Changeset 3732074
- Timestamp:
- 10/07/2026 08:07:53 AM (2 weeks ago)
- Location:
- iot-data-widget
- Files:
-
- 6 added
- 40 edited
- 1 copied
-
tags/1.0.2 (copied) (copied from iot-data-widget/trunk )
-
tags/1.0.2/admin/class-diagnostics-page.php (modified) (7 diffs)
-
tags/1.0.2/admin/class-settings-page.php (modified) (2 diffs)
-
tags/1.0.2/admin/class-setup-wizard.php (modified) (6 diffs)
-
tags/1.0.2/admin/views/diagnostics.php (modified) (3 diffs)
-
tags/1.0.2/admin/views/general-settings-page.php (modified) (2 diffs)
-
tags/1.0.2/admin/views/settings-page.php (modified) (1 diff)
-
tags/1.0.2/admin/views/wizard/step-0-location.php (modified) (2 diffs)
-
tags/1.0.2/includes/class-history-recorder.php (modified) (1 diff)
-
tags/1.0.2/includes/class-http-client.php (added)
-
tags/1.0.2/includes/class-local-network.php (added)
-
tags/1.0.2/includes/class-plugin.php (modified) (5 diffs)
-
tags/1.0.2/includes/class-rest-api.php (modified) (1 diff)
-
tags/1.0.2/includes/class-shortcode.php (modified) (7 diffs)
-
tags/1.0.2/includes/class-value-normalizer.php (modified) (1 diff)
-
tags/1.0.2/includes/class-widget-usage-tracker.php (modified) (3 diffs)
-
tags/1.0.2/includes/connectors/abstract-connector.php (modified) (1 diff)
-
tags/1.0.2/includes/connectors/class-ha-connector.php (modified) (2 diffs)
-
tags/1.0.2/includes/connectors/class-rest-connector.php (modified) (1 diff)
-
tags/1.0.2/iot-data-widget.php (modified) (2 diffs)
-
tags/1.0.2/languages/iot-data-widget.pot (modified) (42 diffs)
-
tags/1.0.2/public/css/widget-styles.css (modified) (3 diffs)
-
tags/1.0.2/readme.txt (modified) (5 diffs)
-
tags/1.0.2/vendor/bin (added)
-
trunk/admin/class-diagnostics-page.php (modified) (7 diffs)
-
trunk/admin/class-settings-page.php (modified) (2 diffs)
-
trunk/admin/class-setup-wizard.php (modified) (6 diffs)
-
trunk/admin/views/diagnostics.php (modified) (3 diffs)
-
trunk/admin/views/general-settings-page.php (modified) (2 diffs)
-
trunk/admin/views/settings-page.php (modified) (1 diff)
-
trunk/admin/views/wizard/step-0-location.php (modified) (2 diffs)
-
trunk/includes/class-history-recorder.php (modified) (1 diff)
-
trunk/includes/class-http-client.php (added)
-
trunk/includes/class-local-network.php (added)
-
trunk/includes/class-plugin.php (modified) (5 diffs)
-
trunk/includes/class-rest-api.php (modified) (1 diff)
-
trunk/includes/class-shortcode.php (modified) (7 diffs)
-
trunk/includes/class-value-normalizer.php (modified) (1 diff)
-
trunk/includes/class-widget-usage-tracker.php (modified) (3 diffs)
-
trunk/includes/connectors/abstract-connector.php (modified) (1 diff)
-
trunk/includes/connectors/class-ha-connector.php (modified) (2 diffs)
-
trunk/includes/connectors/class-rest-connector.php (modified) (1 diff)
-
trunk/iot-data-widget.php (modified) (2 diffs)
-
trunk/languages/iot-data-widget.pot (modified) (42 diffs)
-
trunk/public/css/widget-styles.css (modified) (3 diffs)
-
trunk/readme.txt (modified) (5 diffs)
-
trunk/vendor/bin (added)
Legend:
- Unmodified
- Added
- Removed
-
iot-data-widget/tags/1.0.2/admin/class-diagnostics-page.php
r3728389 r3732074 131 131 $cron_jobs = $this->cron_jobs_info(); 132 132 $connections = $this->connections_info(); 133 $alert_rules = $this->alert_manager->get_rules(); 133 // Null = sekcja ukryta: alerty są Pro (klasa fizycznie tylko w paczce Pro). 134 $alert_rules = class_exists( 'IOTDW_Alert_Evaluator' ) ? $this->alert_manager->get_rules() : null; 134 135 $connections_by_id = array_column( $this->connector_manager->get_connections(), null, 'id' ); 135 136 $recent_errors = IOTDW_Diagnostics_Logger::get_recent_errors(); … … 152 153 * @param array $cron_jobs Z cron_jobs_info(). 153 154 * @param array $connections Z connections_info(). 154 * @param array $alert_rules Z IOTDW_Alert_Manager::get_rules().155 * @param array|null $alert_rules Z IOTDW_Alert_Manager::get_rules(); null = sekcja ukryta (wersja darmowa). 155 156 * @param array $connections_by_id Połączenia indeksowane po ID. 156 157 * @param array $recent_errors Z IOTDW_Diagnostics_Logger::get_recent_errors(). … … 158 159 * @return string 159 160 */ 160 private function build_report_text( array $environment, array $cron_jobs, array $connections, array $alert_rules, array $connections_by_id, array $recent_errors, array $data_sizes ): string {161 private function build_report_text( array $environment, array $cron_jobs, array $connections, ?array $alert_rules, array $connections_by_id, array $recent_errors, array $data_sizes ): string { 161 162 // Code review v1.8: wcześniej wszystkie stringi w tej metodzie były 162 163 // gołym angielskim tekstem, niezgodnie z CLAUDE.md ("i18n od … … 264 265 $lines[] = ''; 265 266 266 $lines[] = __( '== Alert rules ==', 'iot-data-widget' ); 267 if ( empty( $alert_rules ) ) { 268 $lines[] = __( 'None configured.', 'iot-data-widget' ); 269 } else { 267 if ( null !== $alert_rules ) { 268 $lines[] = __( '== Alert rules ==', 'iot-data-widget' ); 269 if ( empty( $alert_rules ) ) { 270 $lines[] = __( 'None configured.', 'iot-data-widget' ); 271 } 270 272 foreach ( $alert_rules as $rule ) { 271 273 $rule_connection = $connections_by_id[ $rule['connection_id'] ] ?? null; … … 285 287 ); 286 288 } 287 }288 $lines[] = '';289 $lines[] = ''; 290 } 289 291 290 292 $lines[] = __( '== Recent errors ==', 'iot-data-widget' ); … … 328 330 global $wpdb; 329 331 330 $tables = array( 331 __( 'Recorded value history', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_History_Recorder::TABLE, 332 __( 'Widget usage tracking', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Widget_Usage_Tracker::TABLE, 333 __( 'Alert rules', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Alert_Manager::TABLE, 334 __( 'MQTT cache', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Cron_Value_Cache::CACHE_TABLE, 335 __( 'Error log', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Diagnostics_Logger::TABLE_ERROR_LOG, 336 ); 332 // Tabele funkcji Pro tylko wtedy, gdy klasa Pro istnieje w paczce. 333 $tables = array(); 334 335 if ( class_exists( 'IOTDW_Renderer_Chart' ) ) { 336 $tables[ __( 'Recorded value history', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_History_Recorder::TABLE; 337 } 338 339 $tables[ __( 'Widget usage tracking', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Widget_Usage_Tracker::TABLE; 340 341 if ( class_exists( 'IOTDW_Alert_Evaluator' ) ) { 342 $tables[ __( 'Alert rules', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Alert_Manager::TABLE; 343 } 344 345 if ( class_exists( 'IOTDW_MQTT_Poll_Engine' ) ) { 346 $tables[ __( 'MQTT cache', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Cron_Value_Cache::CACHE_TABLE; 347 } 348 349 $tables[ __( 'Error log', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Diagnostics_Logger::TABLE_ERROR_LOG; 337 350 338 351 $rows = array(); … … 478 491 */ 479 492 private function cron_jobs_info(): array { 493 // 'requires_class' — crony funkcji Pro tylko wtedy, gdy klasa Pro 494 // fizycznie istnieje w paczce (wersja darmowa ich nie ma, więc wiersz 495 // "Poll MQTT brokers" itp. był tam mylący). 480 496 $hooks = array( 481 IOTDW_History_Recorder::SNAPSHOT_HOOK => __( 'Record value history', 'iot-data-widget' ), 482 IOTDW_History_Recorder::CLEANUP_HOOK => __( 'Clean up old history & error log', 'iot-data-widget' ), 483 IOTDW_Cron_Value_Cache::POLL_HOOK => __( 'Poll MQTT brokers', 'iot-data-widget' ), 484 // Dopisane (code review) — brakowało tu odkąd IOTDW_Email_Report_Sender 485 // powstał (v1.3 Tydzień 7); ten cron logował się już wtedy do 486 // IOTDW_Diagnostics_Logger, po prostu nikt nie dodał go na tej liście. 487 IOTDW_Email_Report_Sender::SEND_HOOK => __( 'Send due email reports', 'iot-data-widget' ), 497 IOTDW_History_Recorder::SNAPSHOT_HOOK => array( __( 'Record value history', 'iot-data-widget' ), 'IOTDW_Renderer_Chart' ), 498 IOTDW_History_Recorder::CLEANUP_HOOK => array( __( 'Clean up old history & error log', 'iot-data-widget' ), '' ), 499 IOTDW_Cron_Value_Cache::POLL_HOOK => array( __( 'Poll MQTT brokers', 'iot-data-widget' ), 'IOTDW_MQTT_Poll_Engine' ), 500 IOTDW_Email_Report_Sender::SEND_HOOK => array( __( 'Send due email reports', 'iot-data-widget' ), 'IOTDW_Email_Report_Dispatcher' ), 488 501 ); 489 502 490 503 $jobs = array(); 491 504 492 foreach ( $hooks as $hook => $label ) { 505 foreach ( $hooks as $hook => list( $label, $requires_class ) ) { 506 if ( '' !== $requires_class && ! class_exists( $requires_class ) ) { 507 continue; 508 } 509 493 510 $jobs[] = array( 494 511 'hook' => $hook, -
iot-data-widget/tags/1.0.2/admin/class-settings-page.php
r3728389 r3732074 171 171 $remove_data_on_uninstall = (bool) ( $settings['remove_data_on_uninstall'] ?? false ); 172 172 $stale_threshold_minutes = (int) ( $settings[ IOTDW_Renderer::STALE_THRESHOLD_SETTING_KEY ] ?? IOTDW_Renderer::DEFAULT_STALE_MINUTES ); 173 $allow_local_network = ! empty( $settings[ IOTDW_Http_Client::SETTING_KEY ] ); 174 $can_manage_local_network = IOTDW_Http_Client::can_manage_local_network(); 173 175 $import_result = get_transient( $this->import_result_transient_key() ); 174 176 … … 219 221 $settings['show_branding'] = ! empty( $_POST['show_branding'] ); 220 222 $settings['remove_data_on_uninstall'] = ! empty( $_POST['remove_data_on_uninstall'] ); 223 224 // Na multisite tylko manage_network_options (patrz IOTDW_Http_Client::can_manage_local_network()). 225 if ( IOTDW_Http_Client::can_manage_local_network() ) { 226 $settings[ IOTDW_Http_Client::SETTING_KEY ] = ! empty( $_POST['allow_local_network'] ); 227 } 221 228 222 229 if ( isset( $_POST['stale_threshold_minutes'] ) ) { -
iot-data-widget/tags/1.0.2/admin/class-setup-wizard.php
r3730704 r3732074 898 898 * 899 899 * @param string $push_type Wybrana platforma. 900 * @param array<string,string> $ connection Zapisany kanał (połączenie 'push').900 * @param array<string,string> $iotdw_connection Zapisany kanał (połączenie 'push'). 901 901 */ 902 902 private function render_push_snippet_step( string $push_type, array $iotdw_connection ): void { … … 975 975 // jednego, rzadkiego przypadku brzegowego. 976 976 wp_safe_redirect( 977 add_query_arg( 'iotdw_notice', ' connection_limit', admin_url( 'admin.php?page=iot-data-widget' ) )977 add_query_arg( 'iotdw_notice', 'demo_failed', admin_url( 'admin.php?page=iot-data-widget' ) ) 978 978 ); 979 979 exit; … … 1051 1051 * przed obiema tymi ścieżkami. 1052 1052 * 1053 * @param string $ type_key Wybrany klucz typu (już zweryfikowany przez find_type() w render_page()).1053 * @param string $iotdw_type_key Wybrany klucz typu (już zweryfikowany przez find_type() w render_page()). 1054 1054 * @param string $connection_id ID istniejącego połączenia (powrót z Kroku 3), albo pusty string dla nowego. 1055 1055 * @param string $mode 'pull'/'push'/'' — do linku "Back" (zgłoszenie 2026-09-07: bez tego "Back" gubił mode/group i wracał na Krok 0 zamiast na Krok 1). … … 1098 1098 * pojedynczym entity=..., bez zapisu, bez zmian w tym przepływie. 1099 1099 * 1100 * @param string $ type_key Wybrany klucz typu (do linku "Back").1101 * @param array<string, string> $ connection Zapisane połączenie (Krok 2).1100 * @param string $iotdw_type_key Wybrany klucz typu (do linku "Back"). 1101 * @param array<string, string> $iotdw_connection Zapisane połączenie (Krok 2). 1102 1102 */ 1103 1103 private function render_step_3( string $iotdw_type_key, array $iotdw_connection ): void { … … 1235 1235 * krok 3), nie trzeba tego dublować w kreatorze. 1236 1236 * 1237 * @param string $ type_key Wybrany klucz typu (do linku "Back").1238 * @param array<string, string> $ connection Zapisane połączenie.1237 * @param string $iotdw_type_key Wybrany klucz typu (do linku "Back"). 1238 * @param array<string, string> $iotdw_connection Zapisane połączenie. 1239 1239 * @param string[] $entity_ids Encje zaznaczone w Kroku 3 — jedna dla konektorów nie-Listable 1240 1240 * (bez zmian), kilka dla Listable (kreator multi-entity, v1.9 Tydzień 5). … … 1325 1325 * 1326 1326 * @param string $type_key Wybrany klucz typu. 1327 * @param array<string, string> $ connection Zapisane połączenie — użyte przez wymagany niżej widok (step-5-done.php), nie bezpośrednio w tej metodzie.1327 * @param array<string, string> $iotdw_connection Zapisane połączenie — użyte przez wymagany niżej widok (step-5-done.php), nie bezpośrednio w tej metodzie. 1328 1328 * @param string[] $entity_ids Encje zaznaczone w Kroku 3 (jedna albo kilka, v1.9) — jak wyżej, użyte przez widok. 1329 1329 */ -
iot-data-widget/tags/1.0.2/admin/views/diagnostics.php
r3728389 r3732074 10 10 * @var array<int, array{hook: string, label: string, is_scheduled: bool, next_run: int|false, last_run: array{ran_at: string, success: bool, message: string}|null}> $cron_jobs Zaplanowane zadania. 11 11 * @var array<int, array{name: string, connector_label: string, last_checked_at: string|null, last_status: string|null, consecutive_errors: int, last_error_message: string|null, is_push: bool, push_active: bool|null, last_received_at: string|null}> $connections Skonfigurowane połączenia + ich status. 12 * @var array<int, array<string, mixed>> $alert_rules Zapisane reguły alertów (IOTDW_Alert_Manager::get_rules()).12 * @var array<int, array<string, mixed>>|null $alert_rules Zapisane reguły alertów (IOTDW_Alert_Manager::get_rules()); null = wersja darmowa, sekcja ukryta. 13 13 * @var array<string, array<string, string>> $connections_by_id Połączenia indeksowane po ID (do nazw w tabeli reguł alertów). 14 14 * @var array<int, array{id: int, occurred_at: string, source: string, message: string}> $recent_errors Najnowsze wpisy logu błędów. … … 233 233 ?> 234 234 235 <?php if ( null !== $alert_rules ) : // Alerty są Pro — na wersji darmowej sekcji nie ma. ?> 235 236 <h2><?php esc_html_e( 'Alert rules', 'iot-data-widget' ); ?></h2> 236 237 <?php … … 273 274 require __DIR__ . '/partials/data-table.php'; 274 275 ?> 276 <?php endif; ?> 275 277 276 278 <h2><?php esc_html_e( 'Recent errors', 'iot-data-widget' ); ?></h2> -
iot-data-widget/tags/1.0.2/admin/views/general-settings-page.php
r3728389 r3732074 16 16 * @var string $export_default_to Domyślna data końcowa formularza eksportu CSV (v1.3 Tydzień 6). 17 17 * @var int $stale_threshold_minutes Próg "stare dane" w minutach (v2.0, docs/mvp-v2-0-push-mode-plan.md §5). 18 * @var bool $allow_local_network "Allow local network addresses" (1.0.2, IOTDW_Http_Client). 19 * @var bool $can_manage_local_network Czy bieżący użytkownik może to zmienić (multisite: manage_network_options). 18 20 */ 19 21 … … 119 121 </tr> 120 122 <tr> 123 <th><?php esc_html_e( 'Local network', 'iot-data-widget' ); ?></th> 124 <td> 125 <label> 126 <input type="checkbox" name="allow_local_network" value="1" <?php checked( $allow_local_network ); ?> <?php disabled( ! $can_manage_local_network ); ?> /> 127 <?php esc_html_e( 'Allow local network addresses', 'iot-data-widget' ); ?> 128 </label> 129 <p class="description"> 130 <?php esc_html_e( 'Turn this on only if WordPress runs on the same network as your devices (e.g. a home server) and connects to addresses like 192.168.x.x or homeassistant.local. Off by default: it stops a misconfigured or malicious URL from reaching other services on your server\'s network. Cloud metadata and link-local addresses are always blocked.', 'iot-data-widget' ); ?> 131 </p> 132 <?php if ( ! $can_manage_local_network ) : ?> 133 <p class="description"><?php esc_html_e( 'On a multisite network, only a network administrator can change this.', 'iot-data-widget' ); ?></p> 134 <?php endif; ?> 135 </td> 136 </tr> 137 <tr> 121 138 <th><?php esc_html_e( 'Uninstall', 'iot-data-widget' ); ?></th> 122 139 <td> -
iot-data-widget/tags/1.0.2/admin/views/settings-page.php
r3728389 r3732074 32 32 $iotdw_current_notice = sanitize_key( wp_unslash( $_GET['iotdw_notice'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 33 33 34 if ( 'connection_limit' === $iotdw_current_notice ) { 34 if ( 'demo_failed' === $iotdw_current_notice ) { 35 // Dawniej "The free version supports one connection" — pozostałość 36 // po zniesionym limicie; zapis połączenia demo po prostu się nie udał. 35 37 $iotdw_notice_type = 'error'; 36 $iotdw_notice_message = __( 'The free version supports one connection. Upgrade to Pro for unlimited connections.', 'iot-data-widget' );38 $iotdw_notice_message = __( 'The demo connection could not be saved. Please try again, or add a connection manually below.', 'iot-data-widget' ); 37 39 } else { 38 40 $iotdw_notice_type = 'success'; -
iot-data-widget/tags/1.0.2/admin/views/wizard/step-0-location.php
r3728389 r3732074 28 28 <?php esc_html_e( 'On external/shared hosting (typical) — my device sends data to my site', 'iot-data-widget' ); ?> 29 29 </a> 30 <a class="button button-hero" href="<?php echo esc_url( add_query_arg( 'mode', 'pull', $iotdw_base_url ) ); ?>"> 30 <?php // 1.0.2: ta opcja włącza też "Allow local network addresses" (IOTDW_Local_Network::handle_enable()). ?> 31 <a class="button button-hero" href="<?php echo esc_url( IOTDW_Local_Network::enable_url( add_query_arg( 'mode', 'pull', $iotdw_base_url ) ) ); ?>"> 31 32 <?php esc_html_e( 'Same network as my devices (Raspberry Pi, NAS, home server) — my site fetches data from the device', 'iot-data-widget' ); ?> 32 33 </a> … … 45 46 ?> 46 47 <a class="button button-hero" href="<?php echo esc_url( add_query_arg( 'mode', 'pull', $iotdw_base_url ) ); ?>"> 47 <?php esc_html_e( 'On external/shared hosting, but my device has its own cloud service (Shelly Cloud, Supla, a publicly-reachable Home Assistant) — my site fetches data from there', 'iot-data-widget' ); ?> 48 <?php 49 // Shelly Cloud i preset Supla są Pro — na wersji darmowej oznaczone. 50 if ( class_exists( 'IOTDW_Connector_Presets' ) ) { 51 esc_html_e( 'On external/shared hosting, but my device has its own cloud service (Shelly Cloud, Supla, a publicly-reachable Home Assistant) — my site fetches data from there', 'iot-data-widget' ); 52 } else { 53 esc_html_e( 'On external/shared hosting, but my device has its own cloud service (a publicly-reachable Home Assistant; Shelly Cloud and Supla in Pro) — my site fetches data from there', 'iot-data-widget' ); 54 } 55 ?> 48 56 </a> 49 57 </p> -
iot-data-widget/tags/1.0.2/includes/class-history-recorder.php
r3728389 r3732074 219 219 // phpcs:enable 220 220 221 $keys = array(); 222 221 223 foreach ( $pairs as $pair ) { 222 $this->record_snapshot( $pair->connection_id, $pair->entity_id ); 224 $keys[ $pair->connection_id . '|' . $pair->entity_id ] = array( $pair->connection_id, $pair->entity_id ); 225 } 226 227 // Wykresy w obszarach widgetów motywu (1.0.2) — nie ma ich w tabeli. 228 foreach ( ( new IOTDW_Widget_Usage_Tracker( $this->connector_manager ) )->widget_area_usage() as $pair ) { 229 if ( in_array( $pair['style'], array( 'chart', 'sparkline' ), true ) ) { 230 $keys[ $pair['connection_id'] . '|' . $pair['entity_id'] ] = array( $pair['connection_id'], $pair['entity_id'] ); 231 } 232 } 233 234 foreach ( $keys as list( $connection_id, $entity_id ) ) { 235 $this->record_snapshot( $connection_id, $entity_id ); 223 236 } 224 237 } -
iot-data-widget/tags/1.0.2/includes/class-plugin.php
r3728389 r3732074 266 266 $this->init_modules(); 267 267 $this->maybe_migrate_credentials(); 268 // Osobno od maybe_upgrade_db() (ta nie czyta połączeń) — jednorazowo, 269 // dopóki ustawienie "Allow local network addresses" nie ma wartości. 270 IOTDW_Local_Network::maybe_migrate( $this->connector_manager ); 268 271 269 272 add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_admin_toolkit_css' ) ); … … 327 330 IOTDW_Push_Channel_Manager::create_table(); 328 331 332 // 1.0.2: aktualizacja z wersji darmowej <= 1.0.1 zdejmuje z 333 // harmonogramu crony funkcji Pro (patrz sync_cron_events()). 334 self::sync_cron_events(); 335 329 336 update_option( self::DB_VERSION_OPTION, IOTDW_VERSION ); 337 } 338 339 /** 340 * Planuje crony wtyczki — te funkcji Pro tylko wtedy, gdy plik Pro 341 * fizycznie istnieje w paczce (ten sam test file_exists() co 342 * init_modules()), w przeciwnym razie zdejmuje je z harmonogramu. 343 * Wersja darmowa <= 1.0.1 planowała wszystkie cztery (zgłoszenie 344 * 2026-10-06): MQTT/raporty nic nie robiły, a snapshot historii odpytywał 345 * urządzenia dla wykresów, których wersja darmowa nie wyświetla. 346 * Wołane przy aktywacji i przy każdej zmianie wersji. 347 */ 348 public static function sync_cron_events(): void { 349 $premium = IOTDW_PLUGIN_DIR . 'includes/premium/'; 350 351 // Niestandardowe harmonogramy muszą być znane, zanim wp_schedule_event() ich użyje. 352 add_filter( 'cron_schedules', array( 'IOTDW_History_Recorder', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 353 add_filter( 'cron_schedules', array( 'IOTDW_Cron_Value_Cache', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 354 355 $events = array( 356 // Historia zasila wykresy/sparkline i alerty — obie funkcje Pro. 357 IOTDW_History_Recorder::SNAPSHOT_HOOK => array( IOTDW_History_Recorder::CRON_SCHEDULE, file_exists( $premium . 'class-renderer-chart.php' ) || file_exists( $premium . 'class-alert-evaluator.php' ) ), 358 // Czyści też dziennik błędów — potrzebny na obu wersjach. 359 IOTDW_History_Recorder::CLEANUP_HOOK => array( 'daily', true ), 360 IOTDW_Cron_Value_Cache::POLL_HOOK => array( IOTDW_Cron_Value_Cache::CRON_SCHEDULE, file_exists( $premium . 'class-mqtt-poll-engine.php' ) ), 361 IOTDW_Email_Report_Sender::SEND_HOOK => array( 'hourly', file_exists( $premium . 'class-email-report-dispatcher.php' ) ), 362 ); 363 364 foreach ( $events as $hook => list( $recurrence, $wanted ) ) { 365 if ( ! $wanted ) { 366 wp_clear_scheduled_hook( $hook ); 367 } elseif ( ! wp_next_scheduled( $hook ) ) { 368 wp_schedule_event( time(), $recurrence, $hook ); 369 } 370 } 330 371 } 331 372 … … 373 414 } 374 415 416 require_once IOTDW_PLUGIN_DIR . 'includes/class-http-client.php'; 417 require_once IOTDW_PLUGIN_DIR . 'includes/class-local-network.php'; 375 418 require_once IOTDW_PLUGIN_DIR . 'includes/class-value-normalizer.php'; 376 419 require_once IOTDW_PLUGIN_DIR . 'includes/class-json-path.php'; … … 557 600 $this->mqtt_poller = new IOTDW_Cron_Value_Cache( $this->connector_manager ); 558 601 $this->settings_page = new IOTDW_Settings_Page( $this->connector_manager, $this->widget_usage_tracker ); 602 // 1.0.2 — "Allow local network addresses": powiadomienie + kreator (same hooki). 603 new IOTDW_Local_Network(); 559 604 // alert_manager (skeleton: CRUD + schemat) zostaje zawsze — 560 605 // IOTDW_Diagnostics_Page je wstrzykuje (raport "Alert rules" działa … … 623 668 update_option( self::DB_VERSION_OPTION, IOTDW_VERSION ); 624 669 625 // Rejestrujemy niestandardowe harmonogramy od razu — inaczej 626 // wp_schedule_event() poniżej nie znalazłby ich w wp_get_schedules(). 627 add_filter( 'cron_schedules', array( 'IOTDW_History_Recorder', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 628 add_filter( 'cron_schedules', array( 'IOTDW_Cron_Value_Cache', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 629 630 if ( ! wp_next_scheduled( IOTDW_History_Recorder::SNAPSHOT_HOOK ) ) { 631 wp_schedule_event( time(), IOTDW_History_Recorder::CRON_SCHEDULE, IOTDW_History_Recorder::SNAPSHOT_HOOK ); 632 } 633 634 if ( ! wp_next_scheduled( IOTDW_History_Recorder::CLEANUP_HOOK ) ) { 635 wp_schedule_event( time(), 'daily', IOTDW_History_Recorder::CLEANUP_HOOK ); 636 } 637 638 if ( ! wp_next_scheduled( IOTDW_Cron_Value_Cache::POLL_HOOK ) ) { 639 wp_schedule_event( time(), IOTDW_Cron_Value_Cache::CRON_SCHEDULE, IOTDW_Cron_Value_Cache::POLL_HOOK ); 640 } 641 642 // Co godzinę — częstotliwość dzienna/tygodniowa nie wymaga precyzji 643 // większej niż to (patrz IOTDW_Email_Report_Sender::is_due()), a 644 // wbudowany harmonogram 'hourly' istnieje zawsze, bez rejestrowania 645 // własnego (w przeciwieństwie do History Recordera/MQTT Pollera). 646 if ( ! wp_next_scheduled( IOTDW_Email_Report_Sender::SEND_HOOK ) ) { 647 wp_schedule_event( time(), 'hourly', IOTDW_Email_Report_Sender::SEND_HOOK ); 648 } 670 self::sync_cron_events(); 649 671 650 672 // Sygnał "pokaż kreator" dla IOTDW_Setup_Wizard::maybe_redirect_after_activation() -
iot-data-widget/tags/1.0.2/includes/class-rest-api.php
r3728389 r3732074 618 618 } 619 619 620 // Wynik pokazywany wprost adminowi ("✓ …") — "false" z surowego bool 621 // JSON jako "Off", jak na karcie/liście. 622 if ( ! empty( $result['display_value'] ) ) { 623 $result['display_value'] = IOTDW_Value_Normalizer::human_display( (string) $result['display_value'] ); 624 } 625 620 626 return rest_ensure_response( 621 627 array( -
iot-data-widget/tags/1.0.2/includes/class-shortcode.php
r3730704 r3732074 91 91 */ 92 92 public function render( $atts ): string { 93 $html = $this->render_widget( $atts ); 94 95 // Blokowy wrapper (1.0.2): motywy wyrównują swoje bloki do kolumny 96 // treści przez margin: auto, co nie działa na elementach inline-block 97 // (card/gauge stały przy krawędzi strony, np. w Twenty Twenty-One). 98 // Wrapper dostaje szerokość kolumny, widget stoi w nim do lewej. 99 // Pusty wynik (błąd ukryty przed gościem) — bez wrappera, żeby nie 100 // zostawiał pustego odstępu. 101 return '' === trim( $html ) ? $html : '<div class="iot-dw-widget-wrap">' . $html . '</div>'; 102 } 103 104 /** 105 * Właściwe renderowanie widgetu (bez wrappera, patrz render()). 106 * 107 * @param array|string $atts Atrybuty shortcode. 108 * @return string 109 */ 110 private function render_widget( $atts ): string { 93 111 $this->rendered_widget = true; 94 112 … … 119 137 ); 120 138 139 $this->enqueue_late( (string) $atts['style'] ); 140 121 141 if ( 'list' === $atts['style'] ) { 122 142 return $this->render_list( $atts ); … … 256 276 * 257 277 * @param IOTDW_Abstract_Connector $connector Instancja konektora dla tego połączenia. 258 * @param array<string, mixed> $connection_row Zapisane połączenie (musi zawierać 'id'/'connector', opcjonalnie 'preset') — do wyliczenia $min_ttl, patrz enforce_min_ttl().259 * @param string $entity_id ID/ścieżka encji.260 * @param int $ttl TTL cache dla udanej odpowiedzi (sekundy), zażądany przez shortcode/blok.278 * @param array<string, mixed> $connection_row Zapisane połączenie (musi zawierać 'id'/'connector', opcjonalnie 'preset') — do wyliczenia $min_ttl, patrz enforce_min_ttl(). 279 * @param string $entity_id ID/ścieżka encji. 280 * @param int $ttl TTL cache dla udanej odpowiedzi (sekundy), zażądany przez shortcode/blok. 261 281 * @return array Znormalizowana struktura danych konektora, ze stemplowanymi connection_id/entity_id. 262 282 */ … … 578 598 } 579 599 580 // v1.8 Tydzień 6 — tokeny (typografia/odstępy/cienie z Tygodnia 5) 581 // muszą się załadować przed widget-styles.css, które teraz z nich 582 // korzysta (var(--iot-dw-text-xl) itd.) — stąd jako zależność, nie 583 // tylko osobny enqueue obok. 600 $this->enqueue_styles(); 601 602 // class_exists() (Guideline 5, 2026-09-24) — chart-widget.js/css są 603 // plikami premium-only, w paczce darmowej fizycznie nie istnieją. 604 if ( IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'chart' ) ) ) { 605 $this->enqueue_chart_assets(); 606 } 607 608 // v1.8 Tydzień 3 — hybrydowe doładowanie. "chart" celowo poza listą 609 // stylów (patrz komentarz nagłówkowy public/js/widget-refresh.js). 610 if ( IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'card', 'gauge', 'sparkline', 'list', 'grid' ) ) ) { 611 $this->enqueue_refresh_script(); 612 } 613 } 614 615 /** 616 * Późne ładowanie zasobów dla widgetu wyrenderowanego POZA treścią 617 * pojedynczego posta (1.0.2: obszar widgetów motywu klasycznego, lista 618 * wpisów, szablon) — maybe_enqueue_assets() widzi tylko post_content 619 * strony pojedynczej. WordPress drukuje style/skrypty dodane po wp_head 620 * w stopce. Tylko front-end — podglądy w adminie/REST mają własne. 621 * 622 * @param string $style Styl widgetu. 623 */ 624 private function enqueue_late( string $style ): void { 625 if ( is_admin() || ! did_action( 'wp_enqueue_scripts' ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { 626 return; 627 } 628 629 $this->enqueue_styles(); 630 631 if ( 'chart' === $style ) { 632 $this->enqueue_chart_assets(); 633 } else { 634 $this->enqueue_refresh_script(); 635 } 636 } 637 638 /** 639 * CSS widgetu + zmienne wyglądu (raz na żądanie). 640 */ 641 private function enqueue_styles(): void { 642 if ( wp_style_is( 'iot-data-widget-theme', 'enqueued' ) ) { 643 return; 644 } 645 646 // v1.8 Tydzień 6 — tokeny muszą się załadować przed widget-styles.css. 584 647 wp_enqueue_style( 585 648 'iot-data-widget-tokens', … … 607 670 IOTDW_Style_Settings::build_css_rule( IOTDW_Style_Settings::css_variables( IOTDW_Style_Settings::get_settings() ) ) 608 671 ); 609 610 // class_exists() (Guideline 5, 2026-09-24; dawniej dodatkowo 611 // iotdw_is_premium(), usunięte jako runtime license-check) — bez 612 // tego warunku strona z style="chart" na wersji darmowej ładowała 613 // Chart.js + chart-widget.js/css na próżno (renderer i tak pokazuje 614 // komunikat "wymaga Pro", nigdy nie odwołuje się do tych plików) — 615 // zbędny ciężar, a chart-widget.js/css są plikami premium-only 616 // (patrz @fs_premium_only w iot-data-widget.php), więc w paczce 617 // darmowej fizycznie nie istnieją. 618 if ( class_exists( 'IOTDW_Renderer_Chart' ) && IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'chart' ) ) ) { 619 wp_enqueue_script( 620 'chart-js', 621 IOTDW_PLUGIN_URL . 'public/js/vendor/chart.umd.min.js', 622 array(), 623 '4.5.1', 624 true 625 ); 626 627 wp_enqueue_script( 628 'iot-data-widget-chart', 629 IOTDW_PLUGIN_URL . 'public/js/chart-widget.js', 630 array( 'chart-js' ), 631 IOTDW_VERSION, 632 true 633 ); 634 635 wp_enqueue_style( 636 'iot-data-widget-chart', 637 IOTDW_PLUGIN_URL . 'public/css/chart-widget.css', 638 array( 'iot-data-widget' ), 639 IOTDW_VERSION 640 ); 641 } 642 643 // v1.8 Tydzień 3 — hybrydowe doładowanie. "chart" celowo poza listą 644 // stylów (patrz komentarz nagłówkowy public/js/widget-refresh.js) — 645 // strona z samym wykresem nie ładuje tego skryptu w ogóle. 646 if ( IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'card', 'gauge', 'sparkline', 'list', 'grid' ) ) ) { 647 wp_enqueue_script( 648 'iot-data-widget-widget-refresh', 649 IOTDW_PLUGIN_URL . 'public/js/widget-refresh.js', 650 array(), 651 IOTDW_VERSION, 652 true 653 ); 654 655 wp_localize_script( 656 'iot-data-widget-widget-refresh', 657 'iotdwWidgetRefresh', 658 array( 'restUrl' => esc_url_raw( rest_url( IOTDW_Rest_Api::NAMESPACE ) ) ) 659 ); 660 } 672 } 673 674 /** 675 * Chart.js + chart-widget.js/css (tylko Pro — pliki istnieją tylko tam). 676 */ 677 private function enqueue_chart_assets(): void { 678 if ( ! class_exists( 'IOTDW_Renderer_Chart' ) || wp_script_is( 'iot-data-widget-chart', 'enqueued' ) ) { 679 return; 680 } 681 682 wp_enqueue_script( 683 'chart-js', 684 IOTDW_PLUGIN_URL . 'public/js/vendor/chart.umd.min.js', 685 array(), 686 '4.5.1', 687 true 688 ); 689 690 wp_enqueue_script( 691 'iot-data-widget-chart', 692 IOTDW_PLUGIN_URL . 'public/js/chart-widget.js', 693 array( 'chart-js' ), 694 IOTDW_VERSION, 695 true 696 ); 697 698 wp_enqueue_style( 699 'iot-data-widget-chart', 700 IOTDW_PLUGIN_URL . 'public/css/chart-widget.css', 701 array( 'iot-data-widget' ), 702 IOTDW_VERSION 703 ); 704 } 705 706 /** 707 * Skrypt jednorazowego odświeżenia wartości po załadowaniu strony. 708 */ 709 private function enqueue_refresh_script(): void { 710 if ( wp_script_is( 'iot-data-widget-widget-refresh', 'enqueued' ) ) { 711 return; 712 } 713 714 wp_enqueue_script( 715 'iot-data-widget-widget-refresh', 716 IOTDW_PLUGIN_URL . 'public/js/widget-refresh.js', 717 array(), 718 IOTDW_VERSION, 719 true 720 ); 721 722 wp_localize_script( 723 'iot-data-widget-widget-refresh', 724 'iotdwWidgetRefresh', 725 array( 'restUrl' => esc_url_raw( rest_url( IOTDW_Rest_Api::NAMESPACE ) ) ) 726 ); 661 727 } 662 728 … … 702 768 * 703 769 * @param IOTDW_Abstract_Connector $connector Instancja konektora. 704 * @param string $entity_id ID encji / ścieżka do wartości.770 * @param string $entity_id ID encji / ścieżka do wartości. 705 771 * @return array 706 772 */ … … 738 804 'entities' => $connector->list_entities(), 739 805 'error' => null, 806 ); 807 } catch ( \RuntimeException $e ) { 808 // Celowe błędy konektora (brak połączenia, 401, adres zablokowany) 809 // — komunikat już jest dla użytkownika, bez "Unexpected". 810 return array( 811 'entities' => array(), 812 'error' => $e->getMessage(), 740 813 ); 741 814 } catch ( \Throwable $e ) { -
iot-data-widget/tags/1.0.2/includes/class-value-normalizer.php
r3728389 r3732074 87 87 ); 88 88 } 89 90 /** 91 * Tekst stanu do pokazania człowiekowi: dosłowne "true"/"false" (surowy 92 * bool z JSON, np. Shelly switch:0.output) → przetłumaczone "On"/"Off", 93 * tak jak robi to już IOTDW_Shortcode dla karty/listy. Każdy inny tekst 94 * (HA "home", Tasmota "ON"...) wraca bez zmian. Nie dla webhooków — 95 * tam display_value zostaje surowe (kontrakt dla odbiorców maszynowych). 96 * 97 * @param string $display_value display_value z konektora. 98 * @return string 99 */ 100 public static function human_display( string $display_value ): string { 101 switch ( strtolower( trim( $display_value ) ) ) { 102 case 'true': 103 return __( 'On', 'iot-data-widget' ); 104 case 'false': 105 return __( 'Off', 'iot-data-widget' ); 106 default: 107 return $display_value; 108 } 109 } 89 110 } -
iot-data-widget/tags/1.0.2/includes/class-widget-usage-tracker.php
r3730704 r3732074 51 51 add_action( 'save_post', array( $this, 'sync_post' ) ); 52 52 add_action( 'before_delete_post', array( $this, 'remove_post' ) ); 53 54 foreach ( array( 'sidebars_widgets', 'widget_block', 'widget_text', 'widget_custom_html', IOTDW_Connector_Manager::CONNECTIONS_OPTION ) as $option ) { 55 add_action( 'update_option_' . $option, array( __CLASS__, 'flush_widget_area_usage' ) ); 56 add_action( 'add_option_' . $option, array( __CLASS__, 'flush_widget_area_usage' ) ); 57 } 58 } 59 60 /** 61 * Transient z parami użytymi w obszarach widgetów motywu klasycznego. 62 */ 63 const WIDGET_AREA_TRANSIENT = 'iotdw_widget_area_usage'; 64 65 /** 66 * Typy widgetów, w których treści szukamy [iotdw_widget] (id_base => pole treści). 67 */ 68 const WIDGET_CONTENT_FIELDS = array( 69 'block' => 'content', 70 'text' => 'text', 71 'custom_html' => 'content', 72 ); 73 74 /** 75 * Kasuje cache par z obszarów widgetów (zmiana widgetów/pasków/połączeń). 76 */ 77 public static function flush_widget_area_usage(): void { 78 delete_transient( self::WIDGET_AREA_TRANSIENT ); 79 } 80 81 /** 82 * Użycie widgetów w AKTYWNYCH obszarach widgetów motywu klasycznego — 83 * odpowiednik wierszy tabeli użycia dla postów (to samo: connection_id, 84 * entity_id, style), ale liczony z opcji widgetów zamiast zapisywany, 85 * żeby nie zmieniać schematu tabeli (post_id jest kluczem jej 86 * czyszczenia). Widgety nieaktywne (wp_inactive_widgets) się nie 87 * liczą — nie są nigdzie wyświetlane. Cache w transiencie, kasowany przy 88 * każdej zmianie widgetów/pasków/połączeń (patrz konstruktor). 89 * 90 * Konsumenci: is_publicly_used() (/widget-data), odpytywanie MQTT 91 * (IOTDW_MQTT_Poll_Engine) i zapis historii (IOTDW_History_Recorder). 92 * 93 * @return array<int, array{connection_id: string, entity_id: string, style: string}> 94 */ 95 public function widget_area_usage(): array { 96 $cached = get_transient( self::WIDGET_AREA_TRANSIENT ); 97 98 if ( is_array( $cached ) && ( array() === $cached || isset( $cached[0]['style'] ) ) ) { 99 return $cached; 100 } 101 102 $usage = array(); 103 // Opcja wprost — wp_get_sidebars_widgets() poza adminem zapamiętuje 104 // wynik w globalnej zmiennej na całe żądanie. 105 $sidebars = get_option( 'sidebars_widgets', array() ); 106 $sidebars = is_array( $sidebars ) ? $sidebars : array(); 107 108 foreach ( $sidebars as $sidebar_id => $widget_ids ) { 109 if ( 'wp_inactive_widgets' === $sidebar_id || 'array_version' === $sidebar_id || ! is_array( $widget_ids ) ) { 110 continue; 111 } 112 113 foreach ( $widget_ids as $widget_id ) { 114 if ( ! preg_match( '/^(.+)-(\d+)$/', (string) $widget_id, $m ) || ! isset( self::WIDGET_CONTENT_FIELDS[ $m[1] ] ) ) { 115 continue; 116 } 117 118 $instances = get_option( 'widget_' . $m[1], array() ); 119 $content = $instances[ (int) $m[2] ][ self::WIDGET_CONTENT_FIELDS[ $m[1] ] ] ?? ''; 120 121 if ( ! is_string( $content ) || '' === $content ) { 122 continue; 123 } 124 125 foreach ( $this->extract_tracked_pairs( $content ) as $pair ) { 126 $usage[ $pair['connection_id'] . '|' . $pair['entity_id'] . '|' . $pair['style'] ] = $pair; 127 } 128 } 129 } 130 131 $usage = array_values( $usage ); 132 133 set_transient( self::WIDGET_AREA_TRANSIENT, $usage, DAY_IN_SECONDS ); 134 135 return $usage; 53 136 } 54 137 … … 201 284 * kwalifikują się — nie ma dla nich żadnego posta do zweryfikowania. 202 285 * 286 * 1.0.2: kwalifikuje się też widget w AKTYWNYM obszarze widgetów motywu 287 * klasycznego (patrz widget_area_usage()) — wyświetlany publicznie tak 288 * samo jak opublikowany post. Wcześniej taki widget nigdy się nie 289 * odświeżał (404 z /widget-data). 290 * 203 291 * @param string $connection_id ID połączenia (kanoniczne, nie nazwa). 204 292 * @param string $entity_id ID encji. … … 228 316 // ma, ale żądający je zna (poprawne ciasteczko). 229 317 if ( null !== $post && ! post_password_required( $post ) ) { 318 return true; 319 } 320 } 321 322 foreach ( $this->widget_area_usage() as $pair ) { 323 if ( $pair['connection_id'] === $connection_id && $pair['entity_id'] === $entity_id ) { 230 324 return true; 231 325 } -
iot-data-widget/tags/1.0.2/includes/connectors/abstract-connector.php
r3728389 r3732074 132 132 } 133 133 134 if ( null !== IOTDW_Http_Client::check_blocked( $base_url ) ) { 135 // Odrzucone przed wysłaniem — komunikat IOTDW_Http_Client już mówi dlaczego. 136 return $message; 137 } 138 134 139 return $message . ' ' . sprintf( 135 140 /* translators: %s: the local-network host/IP that was configured */ -
iot-data-widget/tags/1.0.2/includes/connectors/class-ha-connector.php
r3728389 r3732074 54 54 */ 55 55 public function fetch_data( string $entity_id ): array { 56 $response = wp_remote_get(56 $response = IOTDW_Http_Client::get( 57 57 $this->base_url . '/api/states/' . rawurlencode( $entity_id ), 58 58 array( … … 137 137 // zwykle znacznie więcej danych niż pojedynczy odczyt encji, więc 138 138 // dłuższy zapas jest tu uzasadniony bez kosztu dla front-endu. 139 $response = wp_remote_get(139 $response = IOTDW_Http_Client::get( 140 140 $this->base_url . '/api/states', 141 141 array( -
iot-data-widget/tags/1.0.2/includes/connectors/class-rest-connector.php
r3728389 r3732074 153 153 } 154 154 155 $response = wp_remote_get(155 $response = IOTDW_Http_Client::get( 156 156 $request_url, 157 157 array( -
iot-data-widget/tags/1.0.2/iot-data-widget.php
r3730704 r3732074 4 4 * Plugin Name: IoT Data Widget 5 5 * Description: Displays live data from smart home devices (Home Assistant, REST API, MQTT, Shelly/Tasmota/ESPHome presets, Shelly Cloud) on your website via shortcode and Gutenberg block, with a guided setup wizard, a redesigned and auto-refreshing appearance, and a built-in diagnostics page. Pro adds threshold alerts, email reports, and a public embed mode. 6 * Version: 1.0. 16 * Version: 1.0.2 7 7 * Requires at least: 6.0 8 8 * Requires PHP: 8.1 … … 175 175 // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals 176 176 } 177 define( 'IOTDW_VERSION', '1.0. 1' );177 define( 'IOTDW_VERSION', '1.0.2' ); 178 178 define( 'IOTDW_PLUGIN_FILE', __FILE__ ); 179 179 define( 'IOTDW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
iot-data-widget/tags/1.0.2/languages/iot-data-widget.pot
r3730704 r3732074 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: IoT Data Widget 1.0. 1\n"5 "Project-Id-Version: IoT Data Widget 1.0.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/iot-data-widget\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10-06T1 0:46:23+00:00\n"12 "POT-Creation-Date: 2026-10-06T17:55:40+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 82 82 #: admin/class-email-reports-page.php:151 83 83 #: admin/class-email-reports-page.php:230 84 #: admin/class-settings-page.php:21 385 #: admin/class-settings-page.php:2 3986 #: admin/class-settings-page.php:26 187 #: admin/class-settings-page.php:3 6788 #: admin/class-settings-page.php:4 3584 #: admin/class-settings-page.php:215 85 #: admin/class-settings-page.php:246 86 #: admin/class-settings-page.php:268 87 #: admin/class-settings-page.php:374 88 #: admin/class-settings-page.php:442 89 89 #: admin/class-style-settings-page.php:157 90 #: includes/class-local-network.php:140 91 #: includes/class-local-network.php:173 90 92 #: includes/premium/class-history-csv-export.php:59 91 93 msgid "Insufficient permissions." … … 140 142 #: admin/class-alert-rules-page.php:216 141 143 #: admin/class-email-reports-page.php:207 142 #: admin/class-settings-page.php:4 16144 #: admin/class-settings-page.php:423 143 145 msgid "Please choose a valid connection." 144 146 msgstr "" … … 146 148 #: admin/class-alert-rules-page.php:220 147 149 #: admin/class-email-reports-page.php:211 148 #: admin/class-settings-page.php:42 0150 #: admin/class-settings-page.php:427 149 151 msgid "Entity is required." 150 152 msgstr "" … … 172 174 msgstr "" 173 175 174 #: admin/class-diagnostics-page.php:1 69176 #: admin/class-diagnostics-page.php:170 175 177 msgid "IoT Data Widget — Diagnostic Report" 176 178 msgstr "" 177 179 178 180 #. translators: %s: current date/time in UTC, e.g. "2026-08-13 12:00:00" 179 #: admin/class-diagnostics-page.php:17 1181 #: admin/class-diagnostics-page.php:172 180 182 #, php-format 181 183 msgid "Generated: %s UTC" 182 184 msgstr "" 183 185 184 #: admin/class-diagnostics-page.php:17 4186 #: admin/class-diagnostics-page.php:175 185 187 msgid "== Environment ==" 186 188 msgstr "" 187 189 188 190 #. translators: %s: PHP version, e.g. "8.1.34" 189 #: admin/class-diagnostics-page.php:17 6191 #: admin/class-diagnostics-page.php:177 190 192 #, php-format 191 193 msgid "PHP version: %s" … … 193 195 194 196 #. translators: %s: WordPress version, e.g. "7.0.3" 195 #: admin/class-diagnostics-page.php:17 8197 #: admin/class-diagnostics-page.php:179 196 198 #, php-format 197 199 msgid "WordPress version: %s" … … 199 201 200 202 #. translators: %s: active theme name and version, e.g. "Twenty Twenty-Five 1.4" 201 #: admin/class-diagnostics-page.php:18 0203 #: admin/class-diagnostics-page.php:181 202 204 #, php-format 203 205 msgid "Active theme: %s" … … 205 207 206 208 #. translators: %s: PHP memory limit, e.g. "256M" 207 #: admin/class-diagnostics-page.php:18 2209 #: admin/class-diagnostics-page.php:183 208 210 #, php-format 209 211 msgid "Memory limit: %s" … … 211 213 212 214 #. translators: %s: PHP max execution time in seconds, e.g. "30" 213 #: admin/class-diagnostics-page.php:18 4215 #: admin/class-diagnostics-page.php:185 214 216 #, php-format 215 217 msgid "Max execution time: %ss" … … 217 219 218 220 #. translators: %s: comma-separated list of other active plugin names, or "None" 219 #: admin/class-diagnostics-page.php:18 7221 #: admin/class-diagnostics-page.php:188 220 222 #, php-format 221 223 msgid "Other active plugins: %s" 222 224 msgstr "" 223 225 224 #: admin/class-diagnostics-page.php:18 8226 #: admin/class-diagnostics-page.php:189 225 227 #: admin/views/diagnostics.php:76 226 228 msgid "None" 227 229 msgstr "" 228 230 229 #: admin/class-diagnostics-page.php:19 2231 #: admin/class-diagnostics-page.php:193 230 232 msgid "== Scheduled tasks ==" 231 233 msgstr "" 232 234 233 #: admin/class-diagnostics-page.php:19 5235 #: admin/class-diagnostics-page.php:196 234 236 #: admin/views/diagnostics.php:102 235 237 msgid "Not scheduled" 236 238 msgstr "" 237 239 238 #: admin/class-diagnostics-page.php:19 7240 #: admin/class-diagnostics-page.php:198 239 241 #: admin/views/diagnostics.php:104 240 242 msgid "Scheduled, never run yet" 241 243 msgstr "" 242 244 243 #: admin/class-diagnostics-page.php: 199245 #: admin/class-diagnostics-page.php:200 244 246 #: admin/views/diagnostics.php:106 245 247 msgid "Scheduled & running" … … 247 249 248 250 #. translators: %s: reason the last cron run failed 249 #: admin/class-diagnostics-page.php:20 3251 #: admin/class-diagnostics-page.php:204 250 252 #, php-format 251 253 msgid "Scheduled, last run failed: %s" … … 253 255 254 256 #. translators: 1: task label, 2: status text, 3: next scheduled run, 4: last run time 255 #: admin/class-diagnostics-page.php:21 3257 #: admin/class-diagnostics-page.php:214 256 258 #, php-format 257 259 msgid "%1$s: %2$s (next: %3$s, last: %4$s)" 258 260 msgstr "" 259 261 260 #: admin/class-diagnostics-page.php:22 2262 #: admin/class-diagnostics-page.php:223 261 263 msgid "== Connections ==" 262 264 msgstr "" 263 265 264 #: admin/class-diagnostics-page.php:22 4265 #: admin/class-diagnostics-page.php:2 68266 #: admin/class-diagnostics-page.php:225 267 #: admin/class-diagnostics-page.php:270 266 268 msgid "None configured." 267 269 msgstr "" 268 270 269 #: admin/class-diagnostics-page.php:2 29271 #: admin/class-diagnostics-page.php:230 270 272 msgid "no data received yet" 271 273 msgstr "" 272 274 273 #: admin/class-diagnostics-page.php:23 0275 #: admin/class-diagnostics-page.php:231 274 276 msgid "active" 275 277 msgstr "" 276 278 277 #: admin/class-diagnostics-page.php:23 0279 #: admin/class-diagnostics-page.php:231 278 280 msgid "inactive" 279 281 msgstr "" 280 282 281 283 #. translators: 1: connection name, 2: connector type label, 3: push status text, 4: last received time 282 #: admin/class-diagnostics-page.php:23 4284 #: admin/class-diagnostics-page.php:235 283 285 #, php-format 284 286 msgid "%1$s (%2$s): %3$s, last received %4$s" 285 287 msgstr "" 286 288 287 #: admin/class-diagnostics-page.php:24 5289 #: admin/class-diagnostics-page.php:246 288 290 #: admin/views/diagnostics.php:208 289 #: admin/views/diagnostics.php:32 7291 #: admin/views/diagnostics.php:329 290 292 msgid "No data yet" 291 293 msgstr "" 292 294 293 #: admin/class-diagnostics-page.php:24 6295 #: admin/class-diagnostics-page.php:247 294 296 #: admin/views/diagnostics.php:210 295 297 msgid "OK" 296 298 msgstr "" 297 299 298 #: admin/class-diagnostics-page.php:24 6300 #: admin/class-diagnostics-page.php:247 299 301 #: admin/views/diagnostics.php:212 300 302 msgid "Error" … … 302 304 303 305 #. translators: 1: connection name, 2: connector type label, 3: status text, 4: consecutive error count, 5: last checked time 304 #: admin/class-diagnostics-page.php:25 0306 #: admin/class-diagnostics-page.php:251 305 307 #, php-format 306 308 msgid "%1$s (%2$s): %3$s, %4$d consecutive errors, last checked %5$s" … … 308 310 309 311 #. translators: %s: last recorded error message for this connection 310 #: admin/class-diagnostics-page.php:26 0312 #: admin/class-diagnostics-page.php:261 311 313 #, php-format 312 314 msgid " Last error: %s" 313 315 msgstr "" 314 316 315 #: admin/class-diagnostics-page.php:26 6317 #: admin/class-diagnostics-page.php:268 316 318 msgid "== Alert rules ==" 317 319 msgstr "" 318 320 319 321 #. translators: 1: connection name, 2: entity ID, 3: comparison operator (> or <), 4: threshold value, 5: "Triggered" or "Normal", 6: optional " (last triggered: ...)" note or empty string 320 #: admin/class-diagnostics-page.php:27 5322 #: admin/class-diagnostics-page.php:277 321 323 #, php-format 322 324 msgid "%1$s / %2$s: %3$s %4$s — %5$s%6$s" 323 325 msgstr "" 324 326 325 #: admin/class-diagnostics-page.php:28 0326 #: admin/views/diagnostics.php:25 1327 #: admin/class-diagnostics-page.php:282 328 #: admin/views/diagnostics.php:252 327 329 #: includes/premium/admin-views/alert-rules-page.php:54 328 330 msgid "Triggered" 329 331 msgstr "" 330 332 331 #: admin/class-diagnostics-page.php:28 0332 #: admin/views/diagnostics.php:25 2333 #: admin/class-diagnostics-page.php:282 334 #: admin/views/diagnostics.php:253 333 335 #: includes/premium/admin-views/alert-rules-page.php:55 334 336 msgid "Normal" … … 336 338 337 339 #. translators: %s: last-triggered date/time in UTC 338 #: admin/class-diagnostics-page.php:28 3340 #: admin/class-diagnostics-page.php:285 339 341 #, php-format 340 342 msgid " (last triggered: %s UTC)" 341 343 msgstr "" 342 344 343 #: admin/class-diagnostics-page.php:29 0345 #: admin/class-diagnostics-page.php:292 344 346 msgid "== Recent errors ==" 345 347 msgstr "" 346 348 347 #: admin/class-diagnostics-page.php:29 2349 #: admin/class-diagnostics-page.php:294 348 350 msgid "None recorded." 349 351 msgstr "" 350 352 351 353 #. translators: 1: date/time in UTC, 2: error source, 3: error message 352 #: admin/class-diagnostics-page.php:29 7354 #: admin/class-diagnostics-page.php:299 353 355 #, php-format 354 356 msgid "[%1$s UTC] %2$s: %3$s" 355 357 msgstr "" 356 358 357 #: admin/class-diagnostics-page.php:30 6359 #: admin/class-diagnostics-page.php:308 358 360 msgid "== Data size ==" 359 361 msgstr "" 360 362 361 363 #. translators: 1: table label (already translated), 2: row count 362 #: admin/class-diagnostics-page.php:31 0364 #: admin/class-diagnostics-page.php:312 363 365 #, php-format 364 366 msgid "%1$s: %2$d" 365 367 msgstr "" 366 368 367 #: admin/class-diagnostics-page.php:33 1369 #: admin/class-diagnostics-page.php:336 368 370 msgid "Recorded value history" 369 371 msgstr "" 370 372 371 #: admin/class-diagnostics-page.php:33 2373 #: admin/class-diagnostics-page.php:339 372 374 msgid "Widget usage tracking" 373 375 msgstr "" 374 376 375 #: admin/class-diagnostics-page.php:3 33376 #: admin/views/diagnostics.php:23 5377 #: admin/class-diagnostics-page.php:342 378 #: admin/views/diagnostics.php:236 377 379 #: includes/premium/admin-views/alert-rules-page.php:36 378 380 msgid "Alert rules" 379 381 msgstr "" 380 382 381 #: admin/class-diagnostics-page.php:3 34383 #: admin/class-diagnostics-page.php:346 382 384 msgid "MQTT cache" 383 385 msgstr "" 384 386 385 #: admin/class-diagnostics-page.php:3 35387 #: admin/class-diagnostics-page.php:349 386 388 msgid "Error log" 387 389 msgstr "" 388 390 389 #: admin/class-diagnostics-page.php:4 81391 #: admin/class-diagnostics-page.php:497 390 392 msgid "Record value history" 391 393 msgstr "" 392 394 393 #: admin/class-diagnostics-page.php:4 82395 #: admin/class-diagnostics-page.php:498 394 396 msgid "Clean up old history & error log" 395 397 msgstr "" 396 398 397 #: admin/class-diagnostics-page.php:4 83399 #: admin/class-diagnostics-page.php:499 398 400 msgid "Poll MQTT brokers" 399 401 msgstr "" 400 402 401 #: admin/class-diagnostics-page.php: 487403 #: admin/class-diagnostics-page.php:500 402 404 msgid "Send due email reports" 403 405 msgstr "" … … 421 423 422 424 #: admin/class-settings-page.php:101 423 #: admin/views/general-settings-page.php:2 5425 #: admin/views/general-settings-page.php:27 424 426 msgid "IoT Data Widget — Settings" 425 427 msgstr "" … … 429 431 msgstr "" 430 432 431 #: admin/class-settings-page.php:2 88433 #: admin/class-settings-page.php:295 432 434 msgid "No file was uploaded, or the upload failed." 433 435 msgstr "" 434 436 435 #: admin/class-settings-page.php:29 2437 #: admin/class-settings-page.php:299 436 438 msgid "The uploaded file is too large to be a valid export." 437 439 msgstr "" 438 440 439 #: admin/class-settings-page.php: 298441 #: admin/class-settings-page.php:305 440 442 msgid "Invalid upload." 441 443 msgstr "" 442 444 443 #: admin/class-settings-page.php:3 04445 #: admin/class-settings-page.php:311 444 446 msgid "Could not read the uploaded file." 445 447 msgstr "" 446 448 447 #: admin/class-settings-page.php:4 24449 #: admin/class-settings-page.php:431 448 450 msgid "Please choose a valid widget style." 449 451 msgstr "" … … 649 651 650 652 #: admin/views/connector-form.php:18 651 #: admin/views/general-settings-page.php:2 64653 #: admin/views/general-settings-page.php:281 652 654 #: admin/views/wizard/step-push-2-channel.php:35 653 655 #: includes/premium/admin-views/alert-rule-form.php:20 … … 663 665 664 666 #: admin/views/connector-form.php:32 665 #: admin/views/settings-page.php:1 39667 #: admin/views/settings-page.php:141 666 668 msgid "Add connection" 667 669 msgstr "" … … 723 725 #: admin/views/diagnostics.php:93 724 726 #: admin/views/diagnostics.php:154 725 #: admin/views/diagnostics.php:24 1727 #: admin/views/diagnostics.php:242 726 728 #: includes/premium/admin-views/alert-rules-page.php:44 727 729 msgid "Status" … … 750 752 #: admin/views/diagnostics.php:171 751 753 #: admin/views/diagnostics.php:202 752 #: admin/views/diagnostics.php:25 7753 #: admin/views/diagnostics.php:29 0754 #: admin/views/diagnostics.php:258 755 #: admin/views/diagnostics.php:292 754 756 #, php-format 755 757 msgid "%s UTC" … … 761 763 762 764 #: admin/views/diagnostics.php:151 763 #: admin/views/diagnostics.php:23 8764 #: admin/views/general-settings-page.php:1 69765 #: admin/views/general-settings-page.php:2 27766 #: admin/views/general-settings-page.php:2 77765 #: admin/views/diagnostics.php:239 766 #: admin/views/general-settings-page.php:186 767 #: admin/views/general-settings-page.php:244 768 #: admin/views/general-settings-page.php:294 767 769 #: includes/premium/admin-views/alert-rule-form.php:33 768 770 #: includes/premium/admin-views/alert-rules-page.php:40 … … 775 777 776 778 #: admin/views/diagnostics.php:152 777 #: admin/views/settings-page.php:6 7779 #: admin/views/settings-page.php:69 778 780 msgid "Type" 779 781 msgstr "" … … 804 806 805 807 #: admin/views/diagnostics.php:230 806 #: admin/views/settings-page.php:1 28808 #: admin/views/settings-page.php:130 807 809 msgid "No connections configured yet." 808 810 msgstr "" 809 811 810 #: admin/views/diagnostics.php:2 39811 #: admin/views/general-settings-page.php:1 82812 #: admin/views/general-settings-page.php: 193813 #: admin/views/general-settings-page.php:2 28814 #: admin/views/general-settings-page.php: 293815 #: admin/views/general-settings-page.php:3 04812 #: admin/views/diagnostics.php:240 813 #: admin/views/general-settings-page.php:199 814 #: admin/views/general-settings-page.php:210 815 #: admin/views/general-settings-page.php:245 816 #: admin/views/general-settings-page.php:310 817 #: admin/views/general-settings-page.php:321 816 818 #: admin/views/wizard/step-5-done.php:34 817 819 #: admin/views/wizard/step-push-3-snippet.php:117 … … 831 833 msgstr[1] "" 832 834 833 #: admin/views/diagnostics.php:24 0835 #: admin/views/diagnostics.php:241 834 836 #: includes/premium/admin-views/alert-rule-form.php:81 835 837 #: includes/premium/admin-views/alert-rules-page.php:42 … … 837 839 msgstr "" 838 840 839 #: admin/views/diagnostics.php:24 2841 #: admin/views/diagnostics.php:243 840 842 msgid "Last triggered" 841 843 msgstr "" 842 844 843 #: admin/views/diagnostics.php:27 1845 #: admin/views/diagnostics.php:272 844 846 #: includes/premium/admin-views/alert-rules-page.php:81 845 847 msgid "No alert rules configured yet." 846 848 msgstr "" 847 849 848 #: admin/views/diagnostics.php:27 6850 #: admin/views/diagnostics.php:278 849 851 msgid "Recent errors" 850 852 msgstr "" 851 853 852 #: admin/views/diagnostics.php:2 79854 #: admin/views/diagnostics.php:281 853 855 msgid "When" 854 856 msgstr "" 855 857 856 #: admin/views/diagnostics.php:28 0858 #: admin/views/diagnostics.php:282 857 859 msgid "Source" 858 860 msgstr "" 859 861 860 #: admin/views/diagnostics.php:28 1862 #: admin/views/diagnostics.php:283 861 863 msgid "Message" 862 864 msgstr "" 863 865 864 #: admin/views/diagnostics.php:30 0866 #: admin/views/diagnostics.php:302 865 867 msgid "No errors recorded — everything is working." 866 868 msgstr "" 867 869 868 #: admin/views/diagnostics.php:30 5870 #: admin/views/diagnostics.php:307 869 871 msgid "Data size" 870 872 msgstr "" 871 873 872 #: admin/views/diagnostics.php:31 1874 #: admin/views/diagnostics.php:313 873 875 msgid "Table" 874 876 msgstr "" 875 877 876 #: admin/views/diagnostics.php:31 2878 #: admin/views/diagnostics.php:314 877 879 msgid "Rows" 878 880 msgstr "" 879 881 880 #: admin/views/general-settings-page.php: 29882 #: admin/views/general-settings-page.php:31 881 883 msgid "Settings saved." 882 884 msgstr "" 883 885 884 #: admin/views/general-settings-page.php:3 0886 #: admin/views/general-settings-page.php:32 885 887 msgid "Manually tracked entity added." 886 888 msgstr "" 887 889 888 #: admin/views/general-settings-page.php:3 1890 #: admin/views/general-settings-page.php:33 889 891 msgid "Manually tracked entity removed." 890 892 msgstr "" 891 893 892 894 #. translators: %d: number of imported connections 893 #: admin/views/general-settings-page.php:5 0895 #: admin/views/general-settings-page.php:52 894 896 #, php-format 895 897 msgid "Imported %d connection." … … 899 901 900 902 #. translators: %d: number of skipped entries 901 #: admin/views/general-settings-page.php:6 1903 #: admin/views/general-settings-page.php:63 902 904 #, php-format 903 905 msgid "Skipped %d entry:" … … 906 908 msgstr[1] "" 907 909 908 #: admin/views/general-settings-page.php:7 4910 #: admin/views/general-settings-page.php:76 909 911 msgid "Imported connections need their password/authorization re-entered before use — these are never included in an export, by design." 910 912 msgstr "" 911 913 912 #: admin/views/general-settings-page.php: 79914 #: admin/views/general-settings-page.php:81 913 915 msgid "Import failed:" 914 916 msgstr "" 915 917 916 #: admin/views/general-settings-page.php:8 6918 #: admin/views/general-settings-page.php:88 917 919 msgid "Branding" 918 920 msgstr "" 919 921 920 #: admin/views/general-settings-page.php:9 2922 #: admin/views/general-settings-page.php:94 921 923 msgid "Front-end attribution" 922 924 msgstr "" 923 925 924 #: admin/views/general-settings-page.php:9 6926 #: admin/views/general-settings-page.php:98 925 927 msgid "Show \"Powered by IoT Data Widget\" beneath widgets" 926 928 msgstr "" 927 929 928 #: admin/views/general-settings-page.php: 99930 #: admin/views/general-settings-page.php:101 929 931 msgid "Off by default. Turn this on if you'd like to credit the plugin beneath your widgets." 930 932 msgstr "" 931 933 932 #: admin/views/general-settings-page.php:10 4934 #: admin/views/general-settings-page.php:106 933 935 msgid "Stale data threshold" 934 936 msgstr "" 935 937 936 #: admin/views/general-settings-page.php:11 4938 #: admin/views/general-settings-page.php:116 937 939 msgid "minutes" 938 940 msgstr "" 939 941 940 #: admin/views/general-settings-page.php:11 6942 #: admin/views/general-settings-page.php:118 941 943 msgid "Past this age, widgets show the value grayed out with a timestamp instead of implying it's current — mainly relevant for push channels, where a device can stop sending data without any explicit connection error." 942 944 msgstr "" 943 945 944 #: admin/views/general-settings-page.php:121 946 #: admin/views/general-settings-page.php:123 947 msgid "Local network" 948 msgstr "" 949 950 #: admin/views/general-settings-page.php:127 951 msgid "Allow local network addresses" 952 msgstr "" 953 954 #: admin/views/general-settings-page.php:130 955 msgid "Turn this on only if WordPress runs on the same network as your devices (e.g. a home server) and connects to addresses like 192.168.x.x or homeassistant.local. Off by default: it stops a misconfigured or malicious URL from reaching other services on your server's network. Cloud metadata and link-local addresses are always blocked." 956 msgstr "" 957 958 #: admin/views/general-settings-page.php:133 959 msgid "On a multisite network, only a network administrator can change this." 960 msgstr "" 961 962 #: admin/views/general-settings-page.php:138 945 963 msgid "Uninstall" 946 964 msgstr "" 947 965 948 #: admin/views/general-settings-page.php:1 25966 #: admin/views/general-settings-page.php:142 949 967 msgid "Remove all plugin data when the plugin is deleted" 950 968 msgstr "" 951 969 952 #: admin/views/general-settings-page.php:1 28970 #: admin/views/general-settings-page.php:145 953 971 msgid "Off by default. Deactivating the plugin (e.g. during an update) never touches your data either way — only checking this box AND THEN deleting the plugin from the Plugins screen removes connections, history, alerts and every other setting. Leave this unchecked if you might reinstall later." 954 972 msgstr "" 955 973 956 #: admin/views/general-settings-page.php:1 33974 #: admin/views/general-settings-page.php:150 957 975 msgid "Save settings" 958 976 msgstr "" 959 977 960 #: admin/views/general-settings-page.php:1 36978 #: admin/views/general-settings-page.php:153 961 979 msgid "Export connections" 962 980 msgstr "" 963 981 964 #: admin/views/general-settings-page.php:1 37982 #: admin/views/general-settings-page.php:154 965 983 msgid "Downloads a JSON file with all your connections. Passwords and authorization tokens are never included — re-enter them after importing." 966 984 msgstr "" 967 985 968 #: admin/views/general-settings-page.php:1 41986 #: admin/views/general-settings-page.php:158 969 987 msgid "Download export" 970 988 msgstr "" 971 989 972 #: admin/views/general-settings-page.php:1 44990 #: admin/views/general-settings-page.php:161 973 991 msgid "Import connections" 974 992 msgstr "" 975 993 976 #: admin/views/general-settings-page.php:1 45994 #: admin/views/general-settings-page.php:162 977 995 msgid "Imports connections from a previously exported JSON file. Connections with a matching ID are overwritten; others are added. Existing connections not present in the file are left untouched." 978 996 msgstr "" 979 997 980 #: admin/views/general-settings-page.php:1 50998 #: admin/views/general-settings-page.php:167 981 999 msgid "Import" 982 1000 msgstr "" 983 1001 984 #: admin/views/general-settings-page.php:1 531002 #: admin/views/general-settings-page.php:170 985 1003 msgid "Export history data" 986 1004 msgstr "" 987 1005 988 #: admin/views/general-settings-page.php:1 561006 #: admin/views/general-settings-page.php:173 989 1007 #: includes/premium/class-history-csv-export.php:71 990 1008 msgid "CSV export of recorded history requires the Pro version." 991 1009 msgstr "" 992 1010 993 #: admin/views/general-settings-page.php:1 601011 #: admin/views/general-settings-page.php:177 994 1012 msgid "Downloads the raw recorded history for one connection/entity as a CSV file, for further analysis outside the plugin. Only entities used by a Chart/Sparkline widget (or manually tracked below) have history recorded." 995 1013 msgstr "" 996 1014 997 #: admin/views/general-settings-page.php:1 72998 #: admin/views/general-settings-page.php:2 801015 #: admin/views/general-settings-page.php:189 1016 #: admin/views/general-settings-page.php:297 999 1017 #: includes/premium/admin-views/alert-rule-form.php:40 1000 1018 #: includes/premium/admin-views/email-report-form.php:40 … … 1003 1021 msgstr "" 1004 1022 1005 #: admin/views/general-settings-page.php: 1871006 #: admin/views/general-settings-page.php: 2981023 #: admin/views/general-settings-page.php:204 1024 #: admin/views/general-settings-page.php:315 1007 1025 #: includes/premium/admin-views/alert-rule-form.php:58 1008 1026 #: includes/premium/admin-views/email-report-form.php:58 … … 1012 1030 msgstr "" 1013 1031 1014 #: admin/views/general-settings-page.php: 1971032 #: admin/views/general-settings-page.php:214 1015 1033 msgid "Useful if the entity was renamed/removed from the live connection since history was recorded." 1016 1034 msgstr "" 1017 1035 1018 #: admin/views/general-settings-page.php:2 011019 #: admin/views/general-settings-page.php:3 151036 #: admin/views/general-settings-page.php:218 1037 #: admin/views/general-settings-page.php:332 1020 1038 #: includes/premium/admin-views/alert-rule-form.php:75 1021 1039 #: includes/premium/admin-views/email-report-form.php:75 … … 1025 1043 msgstr "" 1026 1044 1027 #: admin/views/general-settings-page.php:2 071045 #: admin/views/general-settings-page.php:224 1028 1046 msgid "From" 1029 1047 msgstr "" 1030 1048 1031 #: admin/views/general-settings-page.php:2 111049 #: admin/views/general-settings-page.php:228 1032 1050 msgid "To" 1033 1051 msgstr "" 1034 1052 1035 #: admin/views/general-settings-page.php:2 161053 #: admin/views/general-settings-page.php:233 1036 1054 msgid "Download CSV" 1037 1055 msgstr "" 1038 1056 1039 #: admin/views/general-settings-page.php:2 201057 #: admin/views/general-settings-page.php:237 1040 1058 msgid "Manually tracked entities" 1041 1059 msgstr "" 1042 1060 1043 #: admin/views/general-settings-page.php:2 221061 #: admin/views/general-settings-page.php:239 1044 1062 msgid "Widgets placed in post/page content are detected automatically. If you output a widget from a theme template instead (e.g. in a widget area or a custom page template), add it here so it still gets polled for history/alerts even though there's no post content to scan." 1045 1063 msgstr "" 1046 1064 1047 #: admin/views/general-settings-page.php:2 291065 #: admin/views/general-settings-page.php:246 1048 1066 #: blocks/iot-widget/src/edit.js:478 1049 1067 msgid "Style" 1050 1068 msgstr "" 1051 1069 1052 #: admin/views/general-settings-page.php:2 411070 #: admin/views/general-settings-page.php:258 1053 1071 msgid "Stop tracking this entity?" 1054 1072 msgstr "" 1055 1073 1056 #: admin/views/general-settings-page.php:2 421074 #: admin/views/general-settings-page.php:259 1057 1075 #: blocks/iot-widget/src/grid-item-editor.js:117 1058 1076 msgid "Remove" 1059 1077 msgstr "" 1060 1078 1061 #: admin/views/general-settings-page.php:2 551079 #: admin/views/general-settings-page.php:272 1062 1080 msgid "No manually tracked entities yet." 1063 1081 msgstr "" 1064 1082 1065 #: admin/views/general-settings-page.php:2 561083 #: admin/views/general-settings-page.php:273 1066 1084 msgid "Track your first entity" 1067 1085 msgstr "" 1068 1086 1069 #: admin/views/general-settings-page.php: 2881087 #: admin/views/general-settings-page.php:305 1070 1088 #: includes/premium/admin-views/alert-rule-form.php:48 1071 1089 #: includes/premium/admin-views/email-report-form.php:48 … … 1073 1091 msgstr "" 1074 1092 1075 #: admin/views/general-settings-page.php:3 211093 #: admin/views/general-settings-page.php:338 1076 1094 msgid "Widget style" 1077 1095 msgstr "" 1078 1096 1079 #: admin/views/general-settings-page.php:3 241097 #: admin/views/general-settings-page.php:341 1080 1098 #: blocks/iot-widget/src/edit.js:75 1081 1099 msgid "Card" 1082 1100 msgstr "" 1083 1101 1084 #: admin/views/general-settings-page.php:3 251102 #: admin/views/general-settings-page.php:342 1085 1103 #: blocks/iot-widget/src/edit.js:76 1086 1104 msgid "Status list" 1087 1105 msgstr "" 1088 1106 1089 #: admin/views/general-settings-page.php:3 261107 #: admin/views/general-settings-page.php:343 1090 1108 #: blocks/iot-widget/src/edit.js:77 1091 1109 msgid "Gauge" 1092 1110 msgstr "" 1093 1111 1094 #: admin/views/general-settings-page.php:3 271112 #: admin/views/general-settings-page.php:344 1095 1113 #: blocks/iot-widget/src/edit.js:78 1096 1114 msgid "Chart" 1097 1115 msgstr "" 1098 1116 1099 #: admin/views/general-settings-page.php:3 281117 #: admin/views/general-settings-page.php:345 1100 1118 #: blocks/iot-widget/src/edit.js:79 1101 1119 msgid "Mini trend (sparkline)" 1102 1120 msgstr "" 1103 1121 1104 #: admin/views/general-settings-page.php:3 311122 #: admin/views/general-settings-page.php:348 1105 1123 msgid "Must match the style actually rendered by your theme template — Chart/Sparkline are what determine whether this entity gets recorded into history." 1106 1124 msgstr "" 1107 1125 1108 #: admin/views/general-settings-page.php:3 371126 #: admin/views/general-settings-page.php:354 1109 1127 msgid "Add manually tracked entity" 1110 1128 msgstr "" 1111 1129 1112 1130 #: admin/views/partials/connector-fields.php:30 1113 #: admin/views/settings-page.php:6 61131 #: admin/views/settings-page.php:68 1114 1132 msgid "Name" 1115 1133 msgstr "" … … 1120 1138 1121 1139 #: admin/views/partials/connector-fields.php:73 1122 #: admin/views/settings-page.php: 681140 #: admin/views/settings-page.php:70 1123 1141 msgid "URL" 1124 1142 msgstr "" … … 1182 1200 msgstr "" 1183 1201 1184 #: admin/views/settings-page.php:3 61185 msgid "The free version supports one connection. Upgrade to Pro for unlimited connections."1186 msgstr "" 1187 1188 #: admin/views/settings-page.php:4 01202 #: admin/views/settings-page.php:38 1203 msgid "The demo connection could not be saved. Please try again, or add a connection manually below." 1204 msgstr "" 1205 1206 #: admin/views/settings-page.php:42 1189 1207 msgid "Connection saved." 1190 1208 msgstr "" 1191 1209 1192 #: admin/views/settings-page.php:4 11210 #: admin/views/settings-page.php:43 1193 1211 msgid "Connection deleted." 1194 1212 msgstr "" 1195 1213 1196 #: admin/views/settings-page.php:5 11214 #: admin/views/settings-page.php:53 1197 1215 msgid "Now that you have a connection, try these next:" 1198 1216 msgstr "" 1199 1217 1200 #: admin/views/settings-page.php:5 71218 #: admin/views/settings-page.php:59 1201 1219 msgid "Dismiss this notice." 1202 1220 msgstr "" 1203 1221 1204 #: admin/views/settings-page.php:6 21222 #: admin/views/settings-page.php:64 1205 1223 msgid "Saved connections" 1206 1224 msgstr "" 1207 1225 1208 #: admin/views/settings-page.php: 691226 #: admin/views/settings-page.php:71 1209 1227 msgid "Shortcode" 1210 1228 msgstr "" 1211 1229 1212 #: admin/views/settings-page.php:1 081230 #: admin/views/settings-page.php:110 1213 1231 #: includes/premium/admin-views/alert-rules-page.php:60 1214 1232 #: includes/premium/admin-views/email-reports-page.php:56 … … 1216 1234 msgstr "" 1217 1235 1218 #: admin/views/settings-page.php:11 41236 #: admin/views/settings-page.php:116 1219 1237 msgid "Are you sure you want to delete this connection?" 1220 1238 msgstr "" 1221 1239 1222 #: admin/views/settings-page.php:1 291240 #: admin/views/settings-page.php:131 1223 1241 msgid "Add your first connection" 1224 1242 msgstr "" 1225 1243 1226 #: admin/views/settings-page.php:1 381244 #: admin/views/settings-page.php:140 1227 1245 msgid "Edit connection" 1228 1246 msgstr "" … … 1290 1308 msgstr "" 1291 1309 1292 #: admin/views/wizard/step-0-location.php:3 11310 #: admin/views/wizard/step-0-location.php:32 1293 1311 msgid "Same network as my devices (Raspberry Pi, NAS, home server) — my site fetches data from the device" 1294 1312 msgstr "" 1295 1313 1296 #: admin/views/wizard/step-0-location.php: 471314 #: admin/views/wizard/step-0-location.php:51 1297 1315 msgid "On external/shared hosting, but my device has its own cloud service (Shelly Cloud, Supla, a publicly-reachable Home Assistant) — my site fetches data from there" 1298 1316 msgstr "" 1299 1317 1300 #: admin/views/wizard/step-0-location.php:52 1318 #: admin/views/wizard/step-0-location.php:53 1319 msgid "On external/shared hosting, but my device has its own cloud service (a publicly-reachable Home Assistant; Shelly Cloud and Supla in Pro) — my site fetches data from there" 1320 msgstr "" 1321 1322 #: admin/views/wizard/step-0-location.php:60 1301 1323 msgid "Not sure? Choose the first option — it's the safer default." 1302 1324 msgstr "" 1303 1325 1304 #: admin/views/wizard/step-0-location.php: 571326 #: admin/views/wizard/step-0-location.php:65 1305 1327 msgid "Devices behind a home router (NAT) almost never accept incoming connections from the internet — if your WordPress is hosted externally, it typically cannot reach a device address like 192.168.x.x at all. Sending data out from the device instead always works, and so does fetching from a cloud service the device already reports to." 1306 1328 msgstr "" 1307 1329 1308 #: admin/views/wizard/step-0-location.php:7 11330 #: admin/views/wizard/step-0-location.php:79 1309 1331 msgid "Security note: in push mode, this plugin never stores any credentials to your devices — the device sends data out, authenticated by a token this plugin generates that only allows writing one entity to one channel. In pull mode, a copy of your device's own credentials (e.g. a Home Assistant access token) is stored in your WordPress database." 1310 1332 msgstr "" 1311 1333 1312 #: admin/views/wizard/step-0-location.php: 751334 #: admin/views/wizard/step-0-location.php:83 1313 1335 #: admin/views/wizard/step-1-connection-type.php:92 1314 1336 #: admin/views/wizard/step-2-configure.php:87 … … 1582 1604 msgstr "" 1583 1605 1606 #: includes/class-http-client.php:98 1607 msgid "The server redirected too many times." 1608 msgstr "" 1609 1610 #: includes/class-http-client.php:133 1611 msgid "The configured URL has no valid host (only http:// and https:// addresses are supported)." 1612 msgstr "" 1613 1614 #. translators: %s: the host that was blocked 1615 #: includes/class-http-client.php:151 1616 #: includes/class-http-client.php:171 1617 #, php-format 1618 msgid "Refusing to connect to \"%s\" — it resolves to a link-local or reserved address (such as a cloud metadata service). This is always blocked." 1619 msgstr "" 1620 1621 #. translators: %s: the host that was blocked 1622 #: includes/class-http-client.php:252 1623 #, php-format 1624 msgid "Refusing to connect to \"%s\" — it is a local network address. If WordPress runs on the same network as the device, enable \"Allow local network addresses\" under IoT Data Widget → Settings. Otherwise, use a push channel instead." 1625 msgstr "" 1626 1627 #: includes/class-local-network.php:205 1628 msgid "Local network addresses are now allowed, so this site can reach devices on your network. You can change this under IoT Data Widget → Settings." 1629 msgstr "" 1630 1631 #: includes/class-local-network.php:210 1632 msgid "Connections to local network addresses are blocked on this site, and only a network administrator can allow them (IoT Data Widget → Settings). Until then, choose push mode instead." 1633 msgstr "" 1634 1635 #: includes/class-local-network.php:222 1636 msgid "Security update: IoT Data Widget now refuses connections to local network addresses unless \"Allow local network addresses\" is turned on. This site already connects to a device on your local network, so the setting was turned on automatically — nothing stopped working." 1637 msgstr "" 1638 1639 #: includes/class-local-network.php:224 1640 msgid "Review settings" 1641 msgstr "" 1642 1643 #: includes/class-local-network.php:226 1644 msgid "Dismiss" 1645 msgstr "" 1646 1584 1647 #: includes/class-push-ingest.php:120 1585 1648 msgid "Request body is too large." … … 1702 1765 1703 1766 #: includes/class-renderer.php:226 1704 #: includes/class-shortcode.php:1 331767 #: includes/class-shortcode.php:153 1705 1768 msgid "This style requires the Pro version." 1706 1769 msgstr "" … … 1773 1836 msgstr[1] "" 1774 1837 1775 #: includes/class-rest-api.php:7 261838 #: includes/class-rest-api.php:732 1776 1839 msgid "Not found." 1777 1840 msgstr "" 1778 1841 1779 #: includes/class-shortcode.php:1 461842 #: includes/class-shortcode.php:166 1780 1843 msgid "Fill in the \"connection\" and \"entity\" attributes in the [iotdw_widget] shortcode." 1781 1844 msgstr "" 1782 1845 1783 1846 #. translators: %s: connection name or ID given in the shortcode 1784 #: includes/class-shortcode.php:1 591785 #: includes/class-shortcode.php:4 611786 #: includes/class-shortcode.php:5 311847 #: includes/class-shortcode.php:179 1848 #: includes/class-shortcode.php:481 1849 #: includes/class-shortcode.php:551 1787 1850 #, php-format 1788 1851 msgid "Connection \"%s\" was not found." 1789 1852 msgstr "" 1790 1853 1791 #: includes/class-shortcode.php:4 341854 #: includes/class-shortcode.php:454 1792 1855 msgid "Powered by IoT Data Widget" 1793 1856 msgstr "" 1794 1857 1795 #: includes/class-shortcode.php:4 481858 #: includes/class-shortcode.php:468 1796 1859 msgid "Fill in the \"connection\" and \"entities\" attributes (comma-separated entity IDs) in the [iotdw_widget style=\"list\"] shortcode." 1797 1860 msgstr "" 1798 1861 1799 #: includes/class-shortcode.php:5 121862 #: includes/class-shortcode.php:532 1800 1863 msgid "Add at least one entity to the [iotdw_widget style=\"grid\"] shortcode via the \"items\" attribute." 1801 1864 msgstr "" 1802 1865 1803 #: includes/class-shortcode.php:5 131866 #: includes/class-shortcode.php:533 1804 1867 msgid "The \"items\" attribute of the [iotdw_widget style=\"grid\"] shortcode must be a comma-separated list of \"connection|entity\" pairs (e.g. items=\"Home|sensor.temperature,Workshop|relay_state\") — none of the given items matched that format." 1805 1868 msgstr "" 1806 1869 1807 1870 #. translators: %s: PHP error message 1808 #: includes/class-shortcode.php:7 141809 #: includes/class-shortcode.php: 7461871 #: includes/class-shortcode.php:780 1872 #: includes/class-shortcode.php:812 1810 1873 #, php-format 1811 1874 msgid "Unexpected connector error: %s" 1812 1875 msgstr "" 1813 1876 1814 #: includes/class-shortcode.php:861 1877 #: includes/class-shortcode.php:927 1878 #: includes/class-value-normalizer.php:103 1815 1879 #: blocks/iot-widget/src/edit.js:641 1816 1880 msgid "On" 1817 1881 msgstr "" 1818 1882 1819 #: includes/class-shortcode.php:862 1883 #: includes/class-shortcode.php:928 1884 #: includes/class-value-normalizer.php:105 1820 1885 #: blocks/iot-widget/src/edit.js:651 1821 1886 msgid "Off" … … 1823 1888 1824 1889 #. translators: %s: the local-network host/IP that was configured 1825 #: includes/connectors/abstract-connector.php:1 361890 #: includes/connectors/abstract-connector.php:141 1826 1891 #, php-format 1827 1892 msgid "This looks like a local network address (%s) — it will only work if your WordPress server is on the same network as the device. Most external/shared hosting can't reach it; consider Shelly Cloud, a publicly reachable Home Assistant instance, or a differently-reachable REST endpoint instead." … … 2337 2402 msgstr "" 2338 2403 2339 #: includes/premium/class-mqtt-poll-engine.php:1 862404 #: includes/premium/class-mqtt-poll-engine.php:194 2340 2405 msgid "Missing MQTT broker host in the connection settings." 2341 2406 msgstr "" 2342 2407 2343 #: includes/premium/class-mqtt-poll-engine.php:26 12408 #: includes/premium/class-mqtt-poll-engine.php:269 2344 2409 msgid "Missing EMQX REST API URL in the connection settings." 2345 2410 msgstr "" 2346 2411 2347 2412 #. translators: %d: HTTP status code 2348 #: includes/premium/class-mqtt-poll-engine.php: 2982413 #: includes/premium/class-mqtt-poll-engine.php:306 2349 2414 #, php-format 2350 2415 msgid "EMQX REST API returned an unexpected response code: %d." 2351 2416 msgstr "" 2352 2417 2353 #: includes/premium/class-mqtt-poll-engine.php:3 082418 #: includes/premium/class-mqtt-poll-engine.php:316 2354 2419 msgid "Invalid or unexpected response shape from the EMQX REST API." 2355 2420 msgstr "" -
iot-data-widget/tags/1.0.2/public/css/widget-styles.css
r3730704 r3732074 1 /* 2 * Wrapper z IOTDW_Shortcode::render() — celowo bez szerokości/marginesów 3 * poziomych: dostaje je od motywu jak każdy inny blok (kolumna treści). 4 * Odstęp między dwoma widgetami pod rząd przez :where() (zerowa 5 * specyficzność) — motywy blokowe (blockGap) i tak go nadpisują swoim. 6 */ 7 :where(.iot-dw-widget-wrap + .iot-dw-widget-wrap) { 8 margin-top: 1em; 9 } 10 1 11 .iot-dw-widget { 2 12 display: inline-block; … … 137 147 display: block; 138 148 list-style: none; 139 /* Tylko poziome — pionowe zostawiamy motywowi/WP (odstęp między blokami). */ 140 margin-left: 0; 141 margin-right: 0; 149 margin: 0; 142 150 padding: 0; 143 151 box-sizing: border-box; … … 351 359 } 352 360 353 /*354 * Motywy blokowe (np. Twenty Twenty-Five): layout "constrained" wyrównuje355 * dzieci do kolumny treści przez `margin-left/right: auto !important`, ale356 * na elementach inline-level `auto` liczy się jako 0 — card/gauge/sparkline357 * lądowały na lewej krawędzi strony zamiast w kolumnie. Tu: blokowe, szerokość358 * wg treści, lewa krawędź = lewa krawędź kolumny. !important, bo reguła WP też359 * go ma (wygrywa wyższa specyficzność). Poza layoutem constrained (motywy360 * klasyczne, grupa Row/flex) bez zmian — dalej inline, obok siebie.361 */362 .is-layout-constrained > .iot-dw-widget--card,363 .is-layout-constrained > .iot-dw-widget--gauge,364 .is-layout-constrained > .iot-dw-widget--sparkline {365 width: fit-content;366 margin-left: max( 0px, calc( ( 100% - var( --wp--style--global--content-size, 100% ) ) / 2 ) ) !important;367 margin-right: auto !important;368 }369 370 .is-layout-constrained > .iot-dw-widget--card {371 display: block;372 }373 374 .is-layout-constrained > .iot-dw-widget--gauge,375 .is-layout-constrained > .iot-dw-widget--sparkline {376 display: flex;377 } -
iot-data-widget/tags/1.0.2/readme.txt
r3730704 r3732074 5 5 Tested up to: 7.1 6 6 Requires PHP: 8.1 7 Stable tag: 1.0. 17 Stable tag: 1.0.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 22 22 2. Activate the plugin through the **Plugins** screen — this opens the **setup wizard** automatically (skip this step by activating several plugins at once, or by clicking "Skip the wizard" on its first screen). 23 23 3. Follow the wizard: pick a connection type, configure and test it live, choose an entity, and copy the ready-made shortcode from the preview screen — or configure a connection manually under **IoT Data Widget** in the admin menu instead: 24 * **Home Assistant**: enter your instance URL (e.g. `http://homeassistant.local:8123`) and a Long-Lived Access Token. Generate one in Home Assistant under your profile page → **Security** → **Long-Lived Access Tokens** → **Create Token**. 24 * **Home Assistant**: enter your instance URL (e.g. `http://homeassistant.local:8123`) and a Long-Lived Access Token. Generate one in Home Assistant under your profile page → **Security** → **Long-Lived Access Tokens** → **Create Token**. If the URL is a local network address, also turn on **Settings → Allow local network addresses** (the wizard's "Same network as my devices" option does this for you). 25 25 * **Push mode**: no URL/credentials to enter — the wizard generates a channel and a ready-to-paste snippet for your device instead. 26 26 * **Generic REST, advanced mode**: enter the full URL of a JSON endpoint. Leave Authorization empty for public APIs, or enter a full header (e.g. `Authorization: Bearer xxx` or `X-API-Key: xxx`) if the API requires one. … … 66 66 Generic REST (entity is a JSONPath into the JSON response): `[iotdw_widget connection="Weather API" entity="current_weather.temperature"]` 67 67 68 Entities with a non-numeric state (a relay's on/off, a presence sensor's home/away...) show their real value ("ON", "true") in Card and Status list styles.68 Entities with a non-numeric state (a relay's on/off, a presence sensor's home/away...) show their real state ("ON", "home") in Card and Status list styles; a raw true/false from a JSON API shows as "On"/"Off". 69 69 70 70 = I don't want to use the setup wizard — can I skip it? = … … 93 93 94 94 Push mode sidesteps this entirely: the plugin never stores any credentials to your devices, because your device sends data to the plugin instead of the other way around. 95 96 = My device is on the same network as WordPress, but the connection is refused = 97 98 For security, the plugin doesn't connect to local network addresses (like 192.168.x.x or homeassistant.local) unless you allow it. If WordPress runs on the same network as your devices — a Raspberry Pi, NAS or home server — turn on **IoT Data Widget → Settings → Allow local network addresses** (on a multisite network, only a network administrator can). Choosing "Same network as my devices" in the setup wizard does this for you. If your site is hosted externally, use push mode instead: it can't reach your home network anyway. 95 99 96 100 = Does this work with any REST API, or only Home Assistant? = … … 133 137 == Changelog == 134 138 139 = 1.0.2 = 140 * Security: requests to local network addresses (192.168.x.x, 10.x.x.x, homeassistant.local…) are now blocked unless "Allow local network addresses" is turned on under Settings; cloud metadata and link-local addresses are always blocked, including via redirects. If this site already connects to a device on your local network, the setting is turned on automatically during the update — nothing stops working. Choosing "Same network as my devices" in the setup wizard turns it on too. 141 * Added: widgets placed in classic theme widget areas (sidebars, footers) now load their styles and refresh live — previously they appeared unstyled and never updated. 142 * Fixed: widgets line up with the content column in classic themes such as Twenty Twenty-One. Widgets placed one after another now stack vertically — use a Row or Columns block to place them side by side. 143 * Fixed: "Test connection" (and alert e-mails in Pro) show "On"/"Off" instead of "true"/"false" for on/off devices such as Shelly relays. 144 * Fixed: the free version no longer schedules or lists Pro-only background tasks (MQTT polling, email reports, value history); updating removes them. 145 * Fixed: the setup wizard labels Shelly Cloud and Supla as Pro, and shows an accurate message if the demo connection can't be saved. 146 135 147 = 1.0.1 = 136 148 * Added: "Upgrade" link in the plugin menu and the plugin list, and an upgrade link next to Pro-only styles (visible to administrators only). -
iot-data-widget/trunk/admin/class-diagnostics-page.php
r3728389 r3732074 131 131 $cron_jobs = $this->cron_jobs_info(); 132 132 $connections = $this->connections_info(); 133 $alert_rules = $this->alert_manager->get_rules(); 133 // Null = sekcja ukryta: alerty są Pro (klasa fizycznie tylko w paczce Pro). 134 $alert_rules = class_exists( 'IOTDW_Alert_Evaluator' ) ? $this->alert_manager->get_rules() : null; 134 135 $connections_by_id = array_column( $this->connector_manager->get_connections(), null, 'id' ); 135 136 $recent_errors = IOTDW_Diagnostics_Logger::get_recent_errors(); … … 152 153 * @param array $cron_jobs Z cron_jobs_info(). 153 154 * @param array $connections Z connections_info(). 154 * @param array $alert_rules Z IOTDW_Alert_Manager::get_rules().155 * @param array|null $alert_rules Z IOTDW_Alert_Manager::get_rules(); null = sekcja ukryta (wersja darmowa). 155 156 * @param array $connections_by_id Połączenia indeksowane po ID. 156 157 * @param array $recent_errors Z IOTDW_Diagnostics_Logger::get_recent_errors(). … … 158 159 * @return string 159 160 */ 160 private function build_report_text( array $environment, array $cron_jobs, array $connections, array $alert_rules, array $connections_by_id, array $recent_errors, array $data_sizes ): string {161 private function build_report_text( array $environment, array $cron_jobs, array $connections, ?array $alert_rules, array $connections_by_id, array $recent_errors, array $data_sizes ): string { 161 162 // Code review v1.8: wcześniej wszystkie stringi w tej metodzie były 162 163 // gołym angielskim tekstem, niezgodnie z CLAUDE.md ("i18n od … … 264 265 $lines[] = ''; 265 266 266 $lines[] = __( '== Alert rules ==', 'iot-data-widget' ); 267 if ( empty( $alert_rules ) ) { 268 $lines[] = __( 'None configured.', 'iot-data-widget' ); 269 } else { 267 if ( null !== $alert_rules ) { 268 $lines[] = __( '== Alert rules ==', 'iot-data-widget' ); 269 if ( empty( $alert_rules ) ) { 270 $lines[] = __( 'None configured.', 'iot-data-widget' ); 271 } 270 272 foreach ( $alert_rules as $rule ) { 271 273 $rule_connection = $connections_by_id[ $rule['connection_id'] ] ?? null; … … 285 287 ); 286 288 } 287 }288 $lines[] = '';289 $lines[] = ''; 290 } 289 291 290 292 $lines[] = __( '== Recent errors ==', 'iot-data-widget' ); … … 328 330 global $wpdb; 329 331 330 $tables = array( 331 __( 'Recorded value history', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_History_Recorder::TABLE, 332 __( 'Widget usage tracking', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Widget_Usage_Tracker::TABLE, 333 __( 'Alert rules', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Alert_Manager::TABLE, 334 __( 'MQTT cache', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Cron_Value_Cache::CACHE_TABLE, 335 __( 'Error log', 'iot-data-widget' ) => $wpdb->prefix . IOTDW_Diagnostics_Logger::TABLE_ERROR_LOG, 336 ); 332 // Tabele funkcji Pro tylko wtedy, gdy klasa Pro istnieje w paczce. 333 $tables = array(); 334 335 if ( class_exists( 'IOTDW_Renderer_Chart' ) ) { 336 $tables[ __( 'Recorded value history', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_History_Recorder::TABLE; 337 } 338 339 $tables[ __( 'Widget usage tracking', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Widget_Usage_Tracker::TABLE; 340 341 if ( class_exists( 'IOTDW_Alert_Evaluator' ) ) { 342 $tables[ __( 'Alert rules', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Alert_Manager::TABLE; 343 } 344 345 if ( class_exists( 'IOTDW_MQTT_Poll_Engine' ) ) { 346 $tables[ __( 'MQTT cache', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Cron_Value_Cache::CACHE_TABLE; 347 } 348 349 $tables[ __( 'Error log', 'iot-data-widget' ) ] = $wpdb->prefix . IOTDW_Diagnostics_Logger::TABLE_ERROR_LOG; 337 350 338 351 $rows = array(); … … 478 491 */ 479 492 private function cron_jobs_info(): array { 493 // 'requires_class' — crony funkcji Pro tylko wtedy, gdy klasa Pro 494 // fizycznie istnieje w paczce (wersja darmowa ich nie ma, więc wiersz 495 // "Poll MQTT brokers" itp. był tam mylący). 480 496 $hooks = array( 481 IOTDW_History_Recorder::SNAPSHOT_HOOK => __( 'Record value history', 'iot-data-widget' ), 482 IOTDW_History_Recorder::CLEANUP_HOOK => __( 'Clean up old history & error log', 'iot-data-widget' ), 483 IOTDW_Cron_Value_Cache::POLL_HOOK => __( 'Poll MQTT brokers', 'iot-data-widget' ), 484 // Dopisane (code review) — brakowało tu odkąd IOTDW_Email_Report_Sender 485 // powstał (v1.3 Tydzień 7); ten cron logował się już wtedy do 486 // IOTDW_Diagnostics_Logger, po prostu nikt nie dodał go na tej liście. 487 IOTDW_Email_Report_Sender::SEND_HOOK => __( 'Send due email reports', 'iot-data-widget' ), 497 IOTDW_History_Recorder::SNAPSHOT_HOOK => array( __( 'Record value history', 'iot-data-widget' ), 'IOTDW_Renderer_Chart' ), 498 IOTDW_History_Recorder::CLEANUP_HOOK => array( __( 'Clean up old history & error log', 'iot-data-widget' ), '' ), 499 IOTDW_Cron_Value_Cache::POLL_HOOK => array( __( 'Poll MQTT brokers', 'iot-data-widget' ), 'IOTDW_MQTT_Poll_Engine' ), 500 IOTDW_Email_Report_Sender::SEND_HOOK => array( __( 'Send due email reports', 'iot-data-widget' ), 'IOTDW_Email_Report_Dispatcher' ), 488 501 ); 489 502 490 503 $jobs = array(); 491 504 492 foreach ( $hooks as $hook => $label ) { 505 foreach ( $hooks as $hook => list( $label, $requires_class ) ) { 506 if ( '' !== $requires_class && ! class_exists( $requires_class ) ) { 507 continue; 508 } 509 493 510 $jobs[] = array( 494 511 'hook' => $hook, -
iot-data-widget/trunk/admin/class-settings-page.php
r3728389 r3732074 171 171 $remove_data_on_uninstall = (bool) ( $settings['remove_data_on_uninstall'] ?? false ); 172 172 $stale_threshold_minutes = (int) ( $settings[ IOTDW_Renderer::STALE_THRESHOLD_SETTING_KEY ] ?? IOTDW_Renderer::DEFAULT_STALE_MINUTES ); 173 $allow_local_network = ! empty( $settings[ IOTDW_Http_Client::SETTING_KEY ] ); 174 $can_manage_local_network = IOTDW_Http_Client::can_manage_local_network(); 173 175 $import_result = get_transient( $this->import_result_transient_key() ); 174 176 … … 219 221 $settings['show_branding'] = ! empty( $_POST['show_branding'] ); 220 222 $settings['remove_data_on_uninstall'] = ! empty( $_POST['remove_data_on_uninstall'] ); 223 224 // Na multisite tylko manage_network_options (patrz IOTDW_Http_Client::can_manage_local_network()). 225 if ( IOTDW_Http_Client::can_manage_local_network() ) { 226 $settings[ IOTDW_Http_Client::SETTING_KEY ] = ! empty( $_POST['allow_local_network'] ); 227 } 221 228 222 229 if ( isset( $_POST['stale_threshold_minutes'] ) ) { -
iot-data-widget/trunk/admin/class-setup-wizard.php
r3730704 r3732074 898 898 * 899 899 * @param string $push_type Wybrana platforma. 900 * @param array<string,string> $ connection Zapisany kanał (połączenie 'push').900 * @param array<string,string> $iotdw_connection Zapisany kanał (połączenie 'push'). 901 901 */ 902 902 private function render_push_snippet_step( string $push_type, array $iotdw_connection ): void { … … 975 975 // jednego, rzadkiego przypadku brzegowego. 976 976 wp_safe_redirect( 977 add_query_arg( 'iotdw_notice', ' connection_limit', admin_url( 'admin.php?page=iot-data-widget' ) )977 add_query_arg( 'iotdw_notice', 'demo_failed', admin_url( 'admin.php?page=iot-data-widget' ) ) 978 978 ); 979 979 exit; … … 1051 1051 * przed obiema tymi ścieżkami. 1052 1052 * 1053 * @param string $ type_key Wybrany klucz typu (już zweryfikowany przez find_type() w render_page()).1053 * @param string $iotdw_type_key Wybrany klucz typu (już zweryfikowany przez find_type() w render_page()). 1054 1054 * @param string $connection_id ID istniejącego połączenia (powrót z Kroku 3), albo pusty string dla nowego. 1055 1055 * @param string $mode 'pull'/'push'/'' — do linku "Back" (zgłoszenie 2026-09-07: bez tego "Back" gubił mode/group i wracał na Krok 0 zamiast na Krok 1). … … 1098 1098 * pojedynczym entity=..., bez zapisu, bez zmian w tym przepływie. 1099 1099 * 1100 * @param string $ type_key Wybrany klucz typu (do linku "Back").1101 * @param array<string, string> $ connection Zapisane połączenie (Krok 2).1100 * @param string $iotdw_type_key Wybrany klucz typu (do linku "Back"). 1101 * @param array<string, string> $iotdw_connection Zapisane połączenie (Krok 2). 1102 1102 */ 1103 1103 private function render_step_3( string $iotdw_type_key, array $iotdw_connection ): void { … … 1235 1235 * krok 3), nie trzeba tego dublować w kreatorze. 1236 1236 * 1237 * @param string $ type_key Wybrany klucz typu (do linku "Back").1238 * @param array<string, string> $ connection Zapisane połączenie.1237 * @param string $iotdw_type_key Wybrany klucz typu (do linku "Back"). 1238 * @param array<string, string> $iotdw_connection Zapisane połączenie. 1239 1239 * @param string[] $entity_ids Encje zaznaczone w Kroku 3 — jedna dla konektorów nie-Listable 1240 1240 * (bez zmian), kilka dla Listable (kreator multi-entity, v1.9 Tydzień 5). … … 1325 1325 * 1326 1326 * @param string $type_key Wybrany klucz typu. 1327 * @param array<string, string> $ connection Zapisane połączenie — użyte przez wymagany niżej widok (step-5-done.php), nie bezpośrednio w tej metodzie.1327 * @param array<string, string> $iotdw_connection Zapisane połączenie — użyte przez wymagany niżej widok (step-5-done.php), nie bezpośrednio w tej metodzie. 1328 1328 * @param string[] $entity_ids Encje zaznaczone w Kroku 3 (jedna albo kilka, v1.9) — jak wyżej, użyte przez widok. 1329 1329 */ -
iot-data-widget/trunk/admin/views/diagnostics.php
r3728389 r3732074 10 10 * @var array<int, array{hook: string, label: string, is_scheduled: bool, next_run: int|false, last_run: array{ran_at: string, success: bool, message: string}|null}> $cron_jobs Zaplanowane zadania. 11 11 * @var array<int, array{name: string, connector_label: string, last_checked_at: string|null, last_status: string|null, consecutive_errors: int, last_error_message: string|null, is_push: bool, push_active: bool|null, last_received_at: string|null}> $connections Skonfigurowane połączenia + ich status. 12 * @var array<int, array<string, mixed>> $alert_rules Zapisane reguły alertów (IOTDW_Alert_Manager::get_rules()).12 * @var array<int, array<string, mixed>>|null $alert_rules Zapisane reguły alertów (IOTDW_Alert_Manager::get_rules()); null = wersja darmowa, sekcja ukryta. 13 13 * @var array<string, array<string, string>> $connections_by_id Połączenia indeksowane po ID (do nazw w tabeli reguł alertów). 14 14 * @var array<int, array{id: int, occurred_at: string, source: string, message: string}> $recent_errors Najnowsze wpisy logu błędów. … … 233 233 ?> 234 234 235 <?php if ( null !== $alert_rules ) : // Alerty są Pro — na wersji darmowej sekcji nie ma. ?> 235 236 <h2><?php esc_html_e( 'Alert rules', 'iot-data-widget' ); ?></h2> 236 237 <?php … … 273 274 require __DIR__ . '/partials/data-table.php'; 274 275 ?> 276 <?php endif; ?> 275 277 276 278 <h2><?php esc_html_e( 'Recent errors', 'iot-data-widget' ); ?></h2> -
iot-data-widget/trunk/admin/views/general-settings-page.php
r3728389 r3732074 16 16 * @var string $export_default_to Domyślna data końcowa formularza eksportu CSV (v1.3 Tydzień 6). 17 17 * @var int $stale_threshold_minutes Próg "stare dane" w minutach (v2.0, docs/mvp-v2-0-push-mode-plan.md §5). 18 * @var bool $allow_local_network "Allow local network addresses" (1.0.2, IOTDW_Http_Client). 19 * @var bool $can_manage_local_network Czy bieżący użytkownik może to zmienić (multisite: manage_network_options). 18 20 */ 19 21 … … 119 121 </tr> 120 122 <tr> 123 <th><?php esc_html_e( 'Local network', 'iot-data-widget' ); ?></th> 124 <td> 125 <label> 126 <input type="checkbox" name="allow_local_network" value="1" <?php checked( $allow_local_network ); ?> <?php disabled( ! $can_manage_local_network ); ?> /> 127 <?php esc_html_e( 'Allow local network addresses', 'iot-data-widget' ); ?> 128 </label> 129 <p class="description"> 130 <?php esc_html_e( 'Turn this on only if WordPress runs on the same network as your devices (e.g. a home server) and connects to addresses like 192.168.x.x or homeassistant.local. Off by default: it stops a misconfigured or malicious URL from reaching other services on your server\'s network. Cloud metadata and link-local addresses are always blocked.', 'iot-data-widget' ); ?> 131 </p> 132 <?php if ( ! $can_manage_local_network ) : ?> 133 <p class="description"><?php esc_html_e( 'On a multisite network, only a network administrator can change this.', 'iot-data-widget' ); ?></p> 134 <?php endif; ?> 135 </td> 136 </tr> 137 <tr> 121 138 <th><?php esc_html_e( 'Uninstall', 'iot-data-widget' ); ?></th> 122 139 <td> -
iot-data-widget/trunk/admin/views/settings-page.php
r3728389 r3732074 32 32 $iotdw_current_notice = sanitize_key( wp_unslash( $_GET['iotdw_notice'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 33 33 34 if ( 'connection_limit' === $iotdw_current_notice ) { 34 if ( 'demo_failed' === $iotdw_current_notice ) { 35 // Dawniej "The free version supports one connection" — pozostałość 36 // po zniesionym limicie; zapis połączenia demo po prostu się nie udał. 35 37 $iotdw_notice_type = 'error'; 36 $iotdw_notice_message = __( 'The free version supports one connection. Upgrade to Pro for unlimited connections.', 'iot-data-widget' );38 $iotdw_notice_message = __( 'The demo connection could not be saved. Please try again, or add a connection manually below.', 'iot-data-widget' ); 37 39 } else { 38 40 $iotdw_notice_type = 'success'; -
iot-data-widget/trunk/admin/views/wizard/step-0-location.php
r3728389 r3732074 28 28 <?php esc_html_e( 'On external/shared hosting (typical) — my device sends data to my site', 'iot-data-widget' ); ?> 29 29 </a> 30 <a class="button button-hero" href="<?php echo esc_url( add_query_arg( 'mode', 'pull', $iotdw_base_url ) ); ?>"> 30 <?php // 1.0.2: ta opcja włącza też "Allow local network addresses" (IOTDW_Local_Network::handle_enable()). ?> 31 <a class="button button-hero" href="<?php echo esc_url( IOTDW_Local_Network::enable_url( add_query_arg( 'mode', 'pull', $iotdw_base_url ) ) ); ?>"> 31 32 <?php esc_html_e( 'Same network as my devices (Raspberry Pi, NAS, home server) — my site fetches data from the device', 'iot-data-widget' ); ?> 32 33 </a> … … 45 46 ?> 46 47 <a class="button button-hero" href="<?php echo esc_url( add_query_arg( 'mode', 'pull', $iotdw_base_url ) ); ?>"> 47 <?php esc_html_e( 'On external/shared hosting, but my device has its own cloud service (Shelly Cloud, Supla, a publicly-reachable Home Assistant) — my site fetches data from there', 'iot-data-widget' ); ?> 48 <?php 49 // Shelly Cloud i preset Supla są Pro — na wersji darmowej oznaczone. 50 if ( class_exists( 'IOTDW_Connector_Presets' ) ) { 51 esc_html_e( 'On external/shared hosting, but my device has its own cloud service (Shelly Cloud, Supla, a publicly-reachable Home Assistant) — my site fetches data from there', 'iot-data-widget' ); 52 } else { 53 esc_html_e( 'On external/shared hosting, but my device has its own cloud service (a publicly-reachable Home Assistant; Shelly Cloud and Supla in Pro) — my site fetches data from there', 'iot-data-widget' ); 54 } 55 ?> 48 56 </a> 49 57 </p> -
iot-data-widget/trunk/includes/class-history-recorder.php
r3728389 r3732074 219 219 // phpcs:enable 220 220 221 $keys = array(); 222 221 223 foreach ( $pairs as $pair ) { 222 $this->record_snapshot( $pair->connection_id, $pair->entity_id ); 224 $keys[ $pair->connection_id . '|' . $pair->entity_id ] = array( $pair->connection_id, $pair->entity_id ); 225 } 226 227 // Wykresy w obszarach widgetów motywu (1.0.2) — nie ma ich w tabeli. 228 foreach ( ( new IOTDW_Widget_Usage_Tracker( $this->connector_manager ) )->widget_area_usage() as $pair ) { 229 if ( in_array( $pair['style'], array( 'chart', 'sparkline' ), true ) ) { 230 $keys[ $pair['connection_id'] . '|' . $pair['entity_id'] ] = array( $pair['connection_id'], $pair['entity_id'] ); 231 } 232 } 233 234 foreach ( $keys as list( $connection_id, $entity_id ) ) { 235 $this->record_snapshot( $connection_id, $entity_id ); 223 236 } 224 237 } -
iot-data-widget/trunk/includes/class-plugin.php
r3728389 r3732074 266 266 $this->init_modules(); 267 267 $this->maybe_migrate_credentials(); 268 // Osobno od maybe_upgrade_db() (ta nie czyta połączeń) — jednorazowo, 269 // dopóki ustawienie "Allow local network addresses" nie ma wartości. 270 IOTDW_Local_Network::maybe_migrate( $this->connector_manager ); 268 271 269 272 add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_admin_toolkit_css' ) ); … … 327 330 IOTDW_Push_Channel_Manager::create_table(); 328 331 332 // 1.0.2: aktualizacja z wersji darmowej <= 1.0.1 zdejmuje z 333 // harmonogramu crony funkcji Pro (patrz sync_cron_events()). 334 self::sync_cron_events(); 335 329 336 update_option( self::DB_VERSION_OPTION, IOTDW_VERSION ); 337 } 338 339 /** 340 * Planuje crony wtyczki — te funkcji Pro tylko wtedy, gdy plik Pro 341 * fizycznie istnieje w paczce (ten sam test file_exists() co 342 * init_modules()), w przeciwnym razie zdejmuje je z harmonogramu. 343 * Wersja darmowa <= 1.0.1 planowała wszystkie cztery (zgłoszenie 344 * 2026-10-06): MQTT/raporty nic nie robiły, a snapshot historii odpytywał 345 * urządzenia dla wykresów, których wersja darmowa nie wyświetla. 346 * Wołane przy aktywacji i przy każdej zmianie wersji. 347 */ 348 public static function sync_cron_events(): void { 349 $premium = IOTDW_PLUGIN_DIR . 'includes/premium/'; 350 351 // Niestandardowe harmonogramy muszą być znane, zanim wp_schedule_event() ich użyje. 352 add_filter( 'cron_schedules', array( 'IOTDW_History_Recorder', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 353 add_filter( 'cron_schedules', array( 'IOTDW_Cron_Value_Cache', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 354 355 $events = array( 356 // Historia zasila wykresy/sparkline i alerty — obie funkcje Pro. 357 IOTDW_History_Recorder::SNAPSHOT_HOOK => array( IOTDW_History_Recorder::CRON_SCHEDULE, file_exists( $premium . 'class-renderer-chart.php' ) || file_exists( $premium . 'class-alert-evaluator.php' ) ), 358 // Czyści też dziennik błędów — potrzebny na obu wersjach. 359 IOTDW_History_Recorder::CLEANUP_HOOK => array( 'daily', true ), 360 IOTDW_Cron_Value_Cache::POLL_HOOK => array( IOTDW_Cron_Value_Cache::CRON_SCHEDULE, file_exists( $premium . 'class-mqtt-poll-engine.php' ) ), 361 IOTDW_Email_Report_Sender::SEND_HOOK => array( 'hourly', file_exists( $premium . 'class-email-report-dispatcher.php' ) ), 362 ); 363 364 foreach ( $events as $hook => list( $recurrence, $wanted ) ) { 365 if ( ! $wanted ) { 366 wp_clear_scheduled_hook( $hook ); 367 } elseif ( ! wp_next_scheduled( $hook ) ) { 368 wp_schedule_event( time(), $recurrence, $hook ); 369 } 370 } 330 371 } 331 372 … … 373 414 } 374 415 416 require_once IOTDW_PLUGIN_DIR . 'includes/class-http-client.php'; 417 require_once IOTDW_PLUGIN_DIR . 'includes/class-local-network.php'; 375 418 require_once IOTDW_PLUGIN_DIR . 'includes/class-value-normalizer.php'; 376 419 require_once IOTDW_PLUGIN_DIR . 'includes/class-json-path.php'; … … 557 600 $this->mqtt_poller = new IOTDW_Cron_Value_Cache( $this->connector_manager ); 558 601 $this->settings_page = new IOTDW_Settings_Page( $this->connector_manager, $this->widget_usage_tracker ); 602 // 1.0.2 — "Allow local network addresses": powiadomienie + kreator (same hooki). 603 new IOTDW_Local_Network(); 559 604 // alert_manager (skeleton: CRUD + schemat) zostaje zawsze — 560 605 // IOTDW_Diagnostics_Page je wstrzykuje (raport "Alert rules" działa … … 623 668 update_option( self::DB_VERSION_OPTION, IOTDW_VERSION ); 624 669 625 // Rejestrujemy niestandardowe harmonogramy od razu — inaczej 626 // wp_schedule_event() poniżej nie znalazłby ich w wp_get_schedules(). 627 add_filter( 'cron_schedules', array( 'IOTDW_History_Recorder', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 628 add_filter( 'cron_schedules', array( 'IOTDW_Cron_Value_Cache', 'register_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected 629 630 if ( ! wp_next_scheduled( IOTDW_History_Recorder::SNAPSHOT_HOOK ) ) { 631 wp_schedule_event( time(), IOTDW_History_Recorder::CRON_SCHEDULE, IOTDW_History_Recorder::SNAPSHOT_HOOK ); 632 } 633 634 if ( ! wp_next_scheduled( IOTDW_History_Recorder::CLEANUP_HOOK ) ) { 635 wp_schedule_event( time(), 'daily', IOTDW_History_Recorder::CLEANUP_HOOK ); 636 } 637 638 if ( ! wp_next_scheduled( IOTDW_Cron_Value_Cache::POLL_HOOK ) ) { 639 wp_schedule_event( time(), IOTDW_Cron_Value_Cache::CRON_SCHEDULE, IOTDW_Cron_Value_Cache::POLL_HOOK ); 640 } 641 642 // Co godzinę — częstotliwość dzienna/tygodniowa nie wymaga precyzji 643 // większej niż to (patrz IOTDW_Email_Report_Sender::is_due()), a 644 // wbudowany harmonogram 'hourly' istnieje zawsze, bez rejestrowania 645 // własnego (w przeciwieństwie do History Recordera/MQTT Pollera). 646 if ( ! wp_next_scheduled( IOTDW_Email_Report_Sender::SEND_HOOK ) ) { 647 wp_schedule_event( time(), 'hourly', IOTDW_Email_Report_Sender::SEND_HOOK ); 648 } 670 self::sync_cron_events(); 649 671 650 672 // Sygnał "pokaż kreator" dla IOTDW_Setup_Wizard::maybe_redirect_after_activation() -
iot-data-widget/trunk/includes/class-rest-api.php
r3728389 r3732074 618 618 } 619 619 620 // Wynik pokazywany wprost adminowi ("✓ …") — "false" z surowego bool 621 // JSON jako "Off", jak na karcie/liście. 622 if ( ! empty( $result['display_value'] ) ) { 623 $result['display_value'] = IOTDW_Value_Normalizer::human_display( (string) $result['display_value'] ); 624 } 625 620 626 return rest_ensure_response( 621 627 array( -
iot-data-widget/trunk/includes/class-shortcode.php
r3730704 r3732074 91 91 */ 92 92 public function render( $atts ): string { 93 $html = $this->render_widget( $atts ); 94 95 // Blokowy wrapper (1.0.2): motywy wyrównują swoje bloki do kolumny 96 // treści przez margin: auto, co nie działa na elementach inline-block 97 // (card/gauge stały przy krawędzi strony, np. w Twenty Twenty-One). 98 // Wrapper dostaje szerokość kolumny, widget stoi w nim do lewej. 99 // Pusty wynik (błąd ukryty przed gościem) — bez wrappera, żeby nie 100 // zostawiał pustego odstępu. 101 return '' === trim( $html ) ? $html : '<div class="iot-dw-widget-wrap">' . $html . '</div>'; 102 } 103 104 /** 105 * Właściwe renderowanie widgetu (bez wrappera, patrz render()). 106 * 107 * @param array|string $atts Atrybuty shortcode. 108 * @return string 109 */ 110 private function render_widget( $atts ): string { 93 111 $this->rendered_widget = true; 94 112 … … 119 137 ); 120 138 139 $this->enqueue_late( (string) $atts['style'] ); 140 121 141 if ( 'list' === $atts['style'] ) { 122 142 return $this->render_list( $atts ); … … 256 276 * 257 277 * @param IOTDW_Abstract_Connector $connector Instancja konektora dla tego połączenia. 258 * @param array<string, mixed> $connection_row Zapisane połączenie (musi zawierać 'id'/'connector', opcjonalnie 'preset') — do wyliczenia $min_ttl, patrz enforce_min_ttl().259 * @param string $entity_id ID/ścieżka encji.260 * @param int $ttl TTL cache dla udanej odpowiedzi (sekundy), zażądany przez shortcode/blok.278 * @param array<string, mixed> $connection_row Zapisane połączenie (musi zawierać 'id'/'connector', opcjonalnie 'preset') — do wyliczenia $min_ttl, patrz enforce_min_ttl(). 279 * @param string $entity_id ID/ścieżka encji. 280 * @param int $ttl TTL cache dla udanej odpowiedzi (sekundy), zażądany przez shortcode/blok. 261 281 * @return array Znormalizowana struktura danych konektora, ze stemplowanymi connection_id/entity_id. 262 282 */ … … 578 598 } 579 599 580 // v1.8 Tydzień 6 — tokeny (typografia/odstępy/cienie z Tygodnia 5) 581 // muszą się załadować przed widget-styles.css, które teraz z nich 582 // korzysta (var(--iot-dw-text-xl) itd.) — stąd jako zależność, nie 583 // tylko osobny enqueue obok. 600 $this->enqueue_styles(); 601 602 // class_exists() (Guideline 5, 2026-09-24) — chart-widget.js/css są 603 // plikami premium-only, w paczce darmowej fizycznie nie istnieją. 604 if ( IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'chart' ) ) ) { 605 $this->enqueue_chart_assets(); 606 } 607 608 // v1.8 Tydzień 3 — hybrydowe doładowanie. "chart" celowo poza listą 609 // stylów (patrz komentarz nagłówkowy public/js/widget-refresh.js). 610 if ( IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'card', 'gauge', 'sparkline', 'list', 'grid' ) ) ) { 611 $this->enqueue_refresh_script(); 612 } 613 } 614 615 /** 616 * Późne ładowanie zasobów dla widgetu wyrenderowanego POZA treścią 617 * pojedynczego posta (1.0.2: obszar widgetów motywu klasycznego, lista 618 * wpisów, szablon) — maybe_enqueue_assets() widzi tylko post_content 619 * strony pojedynczej. WordPress drukuje style/skrypty dodane po wp_head 620 * w stopce. Tylko front-end — podglądy w adminie/REST mają własne. 621 * 622 * @param string $style Styl widgetu. 623 */ 624 private function enqueue_late( string $style ): void { 625 if ( is_admin() || ! did_action( 'wp_enqueue_scripts' ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { 626 return; 627 } 628 629 $this->enqueue_styles(); 630 631 if ( 'chart' === $style ) { 632 $this->enqueue_chart_assets(); 633 } else { 634 $this->enqueue_refresh_script(); 635 } 636 } 637 638 /** 639 * CSS widgetu + zmienne wyglądu (raz na żądanie). 640 */ 641 private function enqueue_styles(): void { 642 if ( wp_style_is( 'iot-data-widget-theme', 'enqueued' ) ) { 643 return; 644 } 645 646 // v1.8 Tydzień 6 — tokeny muszą się załadować przed widget-styles.css. 584 647 wp_enqueue_style( 585 648 'iot-data-widget-tokens', … … 607 670 IOTDW_Style_Settings::build_css_rule( IOTDW_Style_Settings::css_variables( IOTDW_Style_Settings::get_settings() ) ) 608 671 ); 609 610 // class_exists() (Guideline 5, 2026-09-24; dawniej dodatkowo 611 // iotdw_is_premium(), usunięte jako runtime license-check) — bez 612 // tego warunku strona z style="chart" na wersji darmowej ładowała 613 // Chart.js + chart-widget.js/css na próżno (renderer i tak pokazuje 614 // komunikat "wymaga Pro", nigdy nie odwołuje się do tych plików) — 615 // zbędny ciężar, a chart-widget.js/css są plikami premium-only 616 // (patrz @fs_premium_only w iot-data-widget.php), więc w paczce 617 // darmowej fizycznie nie istnieją. 618 if ( class_exists( 'IOTDW_Renderer_Chart' ) && IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'chart' ) ) ) { 619 wp_enqueue_script( 620 'chart-js', 621 IOTDW_PLUGIN_URL . 'public/js/vendor/chart.umd.min.js', 622 array(), 623 '4.5.1', 624 true 625 ); 626 627 wp_enqueue_script( 628 'iot-data-widget-chart', 629 IOTDW_PLUGIN_URL . 'public/js/chart-widget.js', 630 array( 'chart-js' ), 631 IOTDW_VERSION, 632 true 633 ); 634 635 wp_enqueue_style( 636 'iot-data-widget-chart', 637 IOTDW_PLUGIN_URL . 'public/css/chart-widget.css', 638 array( 'iot-data-widget' ), 639 IOTDW_VERSION 640 ); 641 } 642 643 // v1.8 Tydzień 3 — hybrydowe doładowanie. "chart" celowo poza listą 644 // stylów (patrz komentarz nagłówkowy public/js/widget-refresh.js) — 645 // strona z samym wykresem nie ładuje tego skryptu w ogóle. 646 if ( IOTDW_Widget_Usage_Tracker::content_has_style( $post->post_content, array( 'card', 'gauge', 'sparkline', 'list', 'grid' ) ) ) { 647 wp_enqueue_script( 648 'iot-data-widget-widget-refresh', 649 IOTDW_PLUGIN_URL . 'public/js/widget-refresh.js', 650 array(), 651 IOTDW_VERSION, 652 true 653 ); 654 655 wp_localize_script( 656 'iot-data-widget-widget-refresh', 657 'iotdwWidgetRefresh', 658 array( 'restUrl' => esc_url_raw( rest_url( IOTDW_Rest_Api::NAMESPACE ) ) ) 659 ); 660 } 672 } 673 674 /** 675 * Chart.js + chart-widget.js/css (tylko Pro — pliki istnieją tylko tam). 676 */ 677 private function enqueue_chart_assets(): void { 678 if ( ! class_exists( 'IOTDW_Renderer_Chart' ) || wp_script_is( 'iot-data-widget-chart', 'enqueued' ) ) { 679 return; 680 } 681 682 wp_enqueue_script( 683 'chart-js', 684 IOTDW_PLUGIN_URL . 'public/js/vendor/chart.umd.min.js', 685 array(), 686 '4.5.1', 687 true 688 ); 689 690 wp_enqueue_script( 691 'iot-data-widget-chart', 692 IOTDW_PLUGIN_URL . 'public/js/chart-widget.js', 693 array( 'chart-js' ), 694 IOTDW_VERSION, 695 true 696 ); 697 698 wp_enqueue_style( 699 'iot-data-widget-chart', 700 IOTDW_PLUGIN_URL . 'public/css/chart-widget.css', 701 array( 'iot-data-widget' ), 702 IOTDW_VERSION 703 ); 704 } 705 706 /** 707 * Skrypt jednorazowego odświeżenia wartości po załadowaniu strony. 708 */ 709 private function enqueue_refresh_script(): void { 710 if ( wp_script_is( 'iot-data-widget-widget-refresh', 'enqueued' ) ) { 711 return; 712 } 713 714 wp_enqueue_script( 715 'iot-data-widget-widget-refresh', 716 IOTDW_PLUGIN_URL . 'public/js/widget-refresh.js', 717 array(), 718 IOTDW_VERSION, 719 true 720 ); 721 722 wp_localize_script( 723 'iot-data-widget-widget-refresh', 724 'iotdwWidgetRefresh', 725 array( 'restUrl' => esc_url_raw( rest_url( IOTDW_Rest_Api::NAMESPACE ) ) ) 726 ); 661 727 } 662 728 … … 702 768 * 703 769 * @param IOTDW_Abstract_Connector $connector Instancja konektora. 704 * @param string $entity_id ID encji / ścieżka do wartości.770 * @param string $entity_id ID encji / ścieżka do wartości. 705 771 * @return array 706 772 */ … … 738 804 'entities' => $connector->list_entities(), 739 805 'error' => null, 806 ); 807 } catch ( \RuntimeException $e ) { 808 // Celowe błędy konektora (brak połączenia, 401, adres zablokowany) 809 // — komunikat już jest dla użytkownika, bez "Unexpected". 810 return array( 811 'entities' => array(), 812 'error' => $e->getMessage(), 740 813 ); 741 814 } catch ( \Throwable $e ) { -
iot-data-widget/trunk/includes/class-value-normalizer.php
r3728389 r3732074 87 87 ); 88 88 } 89 90 /** 91 * Tekst stanu do pokazania człowiekowi: dosłowne "true"/"false" (surowy 92 * bool z JSON, np. Shelly switch:0.output) → przetłumaczone "On"/"Off", 93 * tak jak robi to już IOTDW_Shortcode dla karty/listy. Każdy inny tekst 94 * (HA "home", Tasmota "ON"...) wraca bez zmian. Nie dla webhooków — 95 * tam display_value zostaje surowe (kontrakt dla odbiorców maszynowych). 96 * 97 * @param string $display_value display_value z konektora. 98 * @return string 99 */ 100 public static function human_display( string $display_value ): string { 101 switch ( strtolower( trim( $display_value ) ) ) { 102 case 'true': 103 return __( 'On', 'iot-data-widget' ); 104 case 'false': 105 return __( 'Off', 'iot-data-widget' ); 106 default: 107 return $display_value; 108 } 109 } 89 110 } -
iot-data-widget/trunk/includes/class-widget-usage-tracker.php
r3730704 r3732074 51 51 add_action( 'save_post', array( $this, 'sync_post' ) ); 52 52 add_action( 'before_delete_post', array( $this, 'remove_post' ) ); 53 54 foreach ( array( 'sidebars_widgets', 'widget_block', 'widget_text', 'widget_custom_html', IOTDW_Connector_Manager::CONNECTIONS_OPTION ) as $option ) { 55 add_action( 'update_option_' . $option, array( __CLASS__, 'flush_widget_area_usage' ) ); 56 add_action( 'add_option_' . $option, array( __CLASS__, 'flush_widget_area_usage' ) ); 57 } 58 } 59 60 /** 61 * Transient z parami użytymi w obszarach widgetów motywu klasycznego. 62 */ 63 const WIDGET_AREA_TRANSIENT = 'iotdw_widget_area_usage'; 64 65 /** 66 * Typy widgetów, w których treści szukamy [iotdw_widget] (id_base => pole treści). 67 */ 68 const WIDGET_CONTENT_FIELDS = array( 69 'block' => 'content', 70 'text' => 'text', 71 'custom_html' => 'content', 72 ); 73 74 /** 75 * Kasuje cache par z obszarów widgetów (zmiana widgetów/pasków/połączeń). 76 */ 77 public static function flush_widget_area_usage(): void { 78 delete_transient( self::WIDGET_AREA_TRANSIENT ); 79 } 80 81 /** 82 * Użycie widgetów w AKTYWNYCH obszarach widgetów motywu klasycznego — 83 * odpowiednik wierszy tabeli użycia dla postów (to samo: connection_id, 84 * entity_id, style), ale liczony z opcji widgetów zamiast zapisywany, 85 * żeby nie zmieniać schematu tabeli (post_id jest kluczem jej 86 * czyszczenia). Widgety nieaktywne (wp_inactive_widgets) się nie 87 * liczą — nie są nigdzie wyświetlane. Cache w transiencie, kasowany przy 88 * każdej zmianie widgetów/pasków/połączeń (patrz konstruktor). 89 * 90 * Konsumenci: is_publicly_used() (/widget-data), odpytywanie MQTT 91 * (IOTDW_MQTT_Poll_Engine) i zapis historii (IOTDW_History_Recorder). 92 * 93 * @return array<int, array{connection_id: string, entity_id: string, style: string}> 94 */ 95 public function widget_area_usage(): array { 96 $cached = get_transient( self::WIDGET_AREA_TRANSIENT ); 97 98 if ( is_array( $cached ) && ( array() === $cached || isset( $cached[0]['style'] ) ) ) { 99 return $cached; 100 } 101 102 $usage = array(); 103 // Opcja wprost — wp_get_sidebars_widgets() poza adminem zapamiętuje 104 // wynik w globalnej zmiennej na całe żądanie. 105 $sidebars = get_option( 'sidebars_widgets', array() ); 106 $sidebars = is_array( $sidebars ) ? $sidebars : array(); 107 108 foreach ( $sidebars as $sidebar_id => $widget_ids ) { 109 if ( 'wp_inactive_widgets' === $sidebar_id || 'array_version' === $sidebar_id || ! is_array( $widget_ids ) ) { 110 continue; 111 } 112 113 foreach ( $widget_ids as $widget_id ) { 114 if ( ! preg_match( '/^(.+)-(\d+)$/', (string) $widget_id, $m ) || ! isset( self::WIDGET_CONTENT_FIELDS[ $m[1] ] ) ) { 115 continue; 116 } 117 118 $instances = get_option( 'widget_' . $m[1], array() ); 119 $content = $instances[ (int) $m[2] ][ self::WIDGET_CONTENT_FIELDS[ $m[1] ] ] ?? ''; 120 121 if ( ! is_string( $content ) || '' === $content ) { 122 continue; 123 } 124 125 foreach ( $this->extract_tracked_pairs( $content ) as $pair ) { 126 $usage[ $pair['connection_id'] . '|' . $pair['entity_id'] . '|' . $pair['style'] ] = $pair; 127 } 128 } 129 } 130 131 $usage = array_values( $usage ); 132 133 set_transient( self::WIDGET_AREA_TRANSIENT, $usage, DAY_IN_SECONDS ); 134 135 return $usage; 53 136 } 54 137 … … 201 284 * kwalifikują się — nie ma dla nich żadnego posta do zweryfikowania. 202 285 * 286 * 1.0.2: kwalifikuje się też widget w AKTYWNYM obszarze widgetów motywu 287 * klasycznego (patrz widget_area_usage()) — wyświetlany publicznie tak 288 * samo jak opublikowany post. Wcześniej taki widget nigdy się nie 289 * odświeżał (404 z /widget-data). 290 * 203 291 * @param string $connection_id ID połączenia (kanoniczne, nie nazwa). 204 292 * @param string $entity_id ID encji. … … 228 316 // ma, ale żądający je zna (poprawne ciasteczko). 229 317 if ( null !== $post && ! post_password_required( $post ) ) { 318 return true; 319 } 320 } 321 322 foreach ( $this->widget_area_usage() as $pair ) { 323 if ( $pair['connection_id'] === $connection_id && $pair['entity_id'] === $entity_id ) { 230 324 return true; 231 325 } -
iot-data-widget/trunk/includes/connectors/abstract-connector.php
r3728389 r3732074 132 132 } 133 133 134 if ( null !== IOTDW_Http_Client::check_blocked( $base_url ) ) { 135 // Odrzucone przed wysłaniem — komunikat IOTDW_Http_Client już mówi dlaczego. 136 return $message; 137 } 138 134 139 return $message . ' ' . sprintf( 135 140 /* translators: %s: the local-network host/IP that was configured */ -
iot-data-widget/trunk/includes/connectors/class-ha-connector.php
r3728389 r3732074 54 54 */ 55 55 public function fetch_data( string $entity_id ): array { 56 $response = wp_remote_get(56 $response = IOTDW_Http_Client::get( 57 57 $this->base_url . '/api/states/' . rawurlencode( $entity_id ), 58 58 array( … … 137 137 // zwykle znacznie więcej danych niż pojedynczy odczyt encji, więc 138 138 // dłuższy zapas jest tu uzasadniony bez kosztu dla front-endu. 139 $response = wp_remote_get(139 $response = IOTDW_Http_Client::get( 140 140 $this->base_url . '/api/states', 141 141 array( -
iot-data-widget/trunk/includes/connectors/class-rest-connector.php
r3728389 r3732074 153 153 } 154 154 155 $response = wp_remote_get(155 $response = IOTDW_Http_Client::get( 156 156 $request_url, 157 157 array( -
iot-data-widget/trunk/iot-data-widget.php
r3730704 r3732074 4 4 * Plugin Name: IoT Data Widget 5 5 * Description: Displays live data from smart home devices (Home Assistant, REST API, MQTT, Shelly/Tasmota/ESPHome presets, Shelly Cloud) on your website via shortcode and Gutenberg block, with a guided setup wizard, a redesigned and auto-refreshing appearance, and a built-in diagnostics page. Pro adds threshold alerts, email reports, and a public embed mode. 6 * Version: 1.0. 16 * Version: 1.0.2 7 7 * Requires at least: 6.0 8 8 * Requires PHP: 8.1 … … 175 175 // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals 176 176 } 177 define( 'IOTDW_VERSION', '1.0. 1' );177 define( 'IOTDW_VERSION', '1.0.2' ); 178 178 define( 'IOTDW_PLUGIN_FILE', __FILE__ ); 179 179 define( 'IOTDW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
iot-data-widget/trunk/languages/iot-data-widget.pot
r3730704 r3732074 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: IoT Data Widget 1.0. 1\n"5 "Project-Id-Version: IoT Data Widget 1.0.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/iot-data-widget\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10-06T1 0:46:23+00:00\n"12 "POT-Creation-Date: 2026-10-06T17:55:40+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 82 82 #: admin/class-email-reports-page.php:151 83 83 #: admin/class-email-reports-page.php:230 84 #: admin/class-settings-page.php:21 385 #: admin/class-settings-page.php:2 3986 #: admin/class-settings-page.php:26 187 #: admin/class-settings-page.php:3 6788 #: admin/class-settings-page.php:4 3584 #: admin/class-settings-page.php:215 85 #: admin/class-settings-page.php:246 86 #: admin/class-settings-page.php:268 87 #: admin/class-settings-page.php:374 88 #: admin/class-settings-page.php:442 89 89 #: admin/class-style-settings-page.php:157 90 #: includes/class-local-network.php:140 91 #: includes/class-local-network.php:173 90 92 #: includes/premium/class-history-csv-export.php:59 91 93 msgid "Insufficient permissions." … … 140 142 #: admin/class-alert-rules-page.php:216 141 143 #: admin/class-email-reports-page.php:207 142 #: admin/class-settings-page.php:4 16144 #: admin/class-settings-page.php:423 143 145 msgid "Please choose a valid connection." 144 146 msgstr "" … … 146 148 #: admin/class-alert-rules-page.php:220 147 149 #: admin/class-email-reports-page.php:211 148 #: admin/class-settings-page.php:42 0150 #: admin/class-settings-page.php:427 149 151 msgid "Entity is required." 150 152 msgstr "" … … 172 174 msgstr "" 173 175 174 #: admin/class-diagnostics-page.php:1 69176 #: admin/class-diagnostics-page.php:170 175 177 msgid "IoT Data Widget — Diagnostic Report" 176 178 msgstr "" 177 179 178 180 #. translators: %s: current date/time in UTC, e.g. "2026-08-13 12:00:00" 179 #: admin/class-diagnostics-page.php:17 1181 #: admin/class-diagnostics-page.php:172 180 182 #, php-format 181 183 msgid "Generated: %s UTC" 182 184 msgstr "" 183 185 184 #: admin/class-diagnostics-page.php:17 4186 #: admin/class-diagnostics-page.php:175 185 187 msgid "== Environment ==" 186 188 msgstr "" 187 189 188 190 #. translators: %s: PHP version, e.g. "8.1.34" 189 #: admin/class-diagnostics-page.php:17 6191 #: admin/class-diagnostics-page.php:177 190 192 #, php-format 191 193 msgid "PHP version: %s" … … 193 195 194 196 #. translators: %s: WordPress version, e.g. "7.0.3" 195 #: admin/class-diagnostics-page.php:17 8197 #: admin/class-diagnostics-page.php:179 196 198 #, php-format 197 199 msgid "WordPress version: %s" … … 199 201 200 202 #. translators: %s: active theme name and version, e.g. "Twenty Twenty-Five 1.4" 201 #: admin/class-diagnostics-page.php:18 0203 #: admin/class-diagnostics-page.php:181 202 204 #, php-format 203 205 msgid "Active theme: %s" … … 205 207 206 208 #. translators: %s: PHP memory limit, e.g. "256M" 207 #: admin/class-diagnostics-page.php:18 2209 #: admin/class-diagnostics-page.php:183 208 210 #, php-format 209 211 msgid "Memory limit: %s" … … 211 213 212 214 #. translators: %s: PHP max execution time in seconds, e.g. "30" 213 #: admin/class-diagnostics-page.php:18 4215 #: admin/class-diagnostics-page.php:185 214 216 #, php-format 215 217 msgid "Max execution time: %ss" … … 217 219 218 220 #. translators: %s: comma-separated list of other active plugin names, or "None" 219 #: admin/class-diagnostics-page.php:18 7221 #: admin/class-diagnostics-page.php:188 220 222 #, php-format 221 223 msgid "Other active plugins: %s" 222 224 msgstr "" 223 225 224 #: admin/class-diagnostics-page.php:18 8226 #: admin/class-diagnostics-page.php:189 225 227 #: admin/views/diagnostics.php:76 226 228 msgid "None" 227 229 msgstr "" 228 230 229 #: admin/class-diagnostics-page.php:19 2231 #: admin/class-diagnostics-page.php:193 230 232 msgid "== Scheduled tasks ==" 231 233 msgstr "" 232 234 233 #: admin/class-diagnostics-page.php:19 5235 #: admin/class-diagnostics-page.php:196 234 236 #: admin/views/diagnostics.php:102 235 237 msgid "Not scheduled" 236 238 msgstr "" 237 239 238 #: admin/class-diagnostics-page.php:19 7240 #: admin/class-diagnostics-page.php:198 239 241 #: admin/views/diagnostics.php:104 240 242 msgid "Scheduled, never run yet" 241 243 msgstr "" 242 244 243 #: admin/class-diagnostics-page.php: 199245 #: admin/class-diagnostics-page.php:200 244 246 #: admin/views/diagnostics.php:106 245 247 msgid "Scheduled & running" … … 247 249 248 250 #. translators: %s: reason the last cron run failed 249 #: admin/class-diagnostics-page.php:20 3251 #: admin/class-diagnostics-page.php:204 250 252 #, php-format 251 253 msgid "Scheduled, last run failed: %s" … … 253 255 254 256 #. translators: 1: task label, 2: status text, 3: next scheduled run, 4: last run time 255 #: admin/class-diagnostics-page.php:21 3257 #: admin/class-diagnostics-page.php:214 256 258 #, php-format 257 259 msgid "%1$s: %2$s (next: %3$s, last: %4$s)" 258 260 msgstr "" 259 261 260 #: admin/class-diagnostics-page.php:22 2262 #: admin/class-diagnostics-page.php:223 261 263 msgid "== Connections ==" 262 264 msgstr "" 263 265 264 #: admin/class-diagnostics-page.php:22 4265 #: admin/class-diagnostics-page.php:2 68266 #: admin/class-diagnostics-page.php:225 267 #: admin/class-diagnostics-page.php:270 266 268 msgid "None configured." 267 269 msgstr "" 268 270 269 #: admin/class-diagnostics-page.php:2 29271 #: admin/class-diagnostics-page.php:230 270 272 msgid "no data received yet" 271 273 msgstr "" 272 274 273 #: admin/class-diagnostics-page.php:23 0275 #: admin/class-diagnostics-page.php:231 274 276 msgid "active" 275 277 msgstr "" 276 278 277 #: admin/class-diagnostics-page.php:23 0279 #: admin/class-diagnostics-page.php:231 278 280 msgid "inactive" 279 281 msgstr "" 280 282 281 283 #. translators: 1: connection name, 2: connector type label, 3: push status text, 4: last received time 282 #: admin/class-diagnostics-page.php:23 4284 #: admin/class-diagnostics-page.php:235 283 285 #, php-format 284 286 msgid "%1$s (%2$s): %3$s, last received %4$s" 285 287 msgstr "" 286 288 287 #: admin/class-diagnostics-page.php:24 5289 #: admin/class-diagnostics-page.php:246 288 290 #: admin/views/diagnostics.php:208 289 #: admin/views/diagnostics.php:32 7291 #: admin/views/diagnostics.php:329 290 292 msgid "No data yet" 291 293 msgstr "" 292 294 293 #: admin/class-diagnostics-page.php:24 6295 #: admin/class-diagnostics-page.php:247 294 296 #: admin/views/diagnostics.php:210 295 297 msgid "OK" 296 298 msgstr "" 297 299 298 #: admin/class-diagnostics-page.php:24 6300 #: admin/class-diagnostics-page.php:247 299 301 #: admin/views/diagnostics.php:212 300 302 msgid "Error" … … 302 304 303 305 #. translators: 1: connection name, 2: connector type label, 3: status text, 4: consecutive error count, 5: last checked time 304 #: admin/class-diagnostics-page.php:25 0306 #: admin/class-diagnostics-page.php:251 305 307 #, php-format 306 308 msgid "%1$s (%2$s): %3$s, %4$d consecutive errors, last checked %5$s" … … 308 310 309 311 #. translators: %s: last recorded error message for this connection 310 #: admin/class-diagnostics-page.php:26 0312 #: admin/class-diagnostics-page.php:261 311 313 #, php-format 312 314 msgid " Last error: %s" 313 315 msgstr "" 314 316 315 #: admin/class-diagnostics-page.php:26 6317 #: admin/class-diagnostics-page.php:268 316 318 msgid "== Alert rules ==" 317 319 msgstr "" 318 320 319 321 #. translators: 1: connection name, 2: entity ID, 3: comparison operator (> or <), 4: threshold value, 5: "Triggered" or "Normal", 6: optional " (last triggered: ...)" note or empty string 320 #: admin/class-diagnostics-page.php:27 5322 #: admin/class-diagnostics-page.php:277 321 323 #, php-format 322 324 msgid "%1$s / %2$s: %3$s %4$s — %5$s%6$s" 323 325 msgstr "" 324 326 325 #: admin/class-diagnostics-page.php:28 0326 #: admin/views/diagnostics.php:25 1327 #: admin/class-diagnostics-page.php:282 328 #: admin/views/diagnostics.php:252 327 329 #: includes/premium/admin-views/alert-rules-page.php:54 328 330 msgid "Triggered" 329 331 msgstr "" 330 332 331 #: admin/class-diagnostics-page.php:28 0332 #: admin/views/diagnostics.php:25 2333 #: admin/class-diagnostics-page.php:282 334 #: admin/views/diagnostics.php:253 333 335 #: includes/premium/admin-views/alert-rules-page.php:55 334 336 msgid "Normal" … … 336 338 337 339 #. translators: %s: last-triggered date/time in UTC 338 #: admin/class-diagnostics-page.php:28 3340 #: admin/class-diagnostics-page.php:285 339 341 #, php-format 340 342 msgid " (last triggered: %s UTC)" 341 343 msgstr "" 342 344 343 #: admin/class-diagnostics-page.php:29 0345 #: admin/class-diagnostics-page.php:292 344 346 msgid "== Recent errors ==" 345 347 msgstr "" 346 348 347 #: admin/class-diagnostics-page.php:29 2349 #: admin/class-diagnostics-page.php:294 348 350 msgid "None recorded." 349 351 msgstr "" 350 352 351 353 #. translators: 1: date/time in UTC, 2: error source, 3: error message 352 #: admin/class-diagnostics-page.php:29 7354 #: admin/class-diagnostics-page.php:299 353 355 #, php-format 354 356 msgid "[%1$s UTC] %2$s: %3$s" 355 357 msgstr "" 356 358 357 #: admin/class-diagnostics-page.php:30 6359 #: admin/class-diagnostics-page.php:308 358 360 msgid "== Data size ==" 359 361 msgstr "" 360 362 361 363 #. translators: 1: table label (already translated), 2: row count 362 #: admin/class-diagnostics-page.php:31 0364 #: admin/class-diagnostics-page.php:312 363 365 #, php-format 364 366 msgid "%1$s: %2$d" 365 367 msgstr "" 366 368 367 #: admin/class-diagnostics-page.php:33 1369 #: admin/class-diagnostics-page.php:336 368 370 msgid "Recorded value history" 369 371 msgstr "" 370 372 371 #: admin/class-diagnostics-page.php:33 2373 #: admin/class-diagnostics-page.php:339 372 374 msgid "Widget usage tracking" 373 375 msgstr "" 374 376 375 #: admin/class-diagnostics-page.php:3 33376 #: admin/views/diagnostics.php:23 5377 #: admin/class-diagnostics-page.php:342 378 #: admin/views/diagnostics.php:236 377 379 #: includes/premium/admin-views/alert-rules-page.php:36 378 380 msgid "Alert rules" 379 381 msgstr "" 380 382 381 #: admin/class-diagnostics-page.php:3 34383 #: admin/class-diagnostics-page.php:346 382 384 msgid "MQTT cache" 383 385 msgstr "" 384 386 385 #: admin/class-diagnostics-page.php:3 35387 #: admin/class-diagnostics-page.php:349 386 388 msgid "Error log" 387 389 msgstr "" 388 390 389 #: admin/class-diagnostics-page.php:4 81391 #: admin/class-diagnostics-page.php:497 390 392 msgid "Record value history" 391 393 msgstr "" 392 394 393 #: admin/class-diagnostics-page.php:4 82395 #: admin/class-diagnostics-page.php:498 394 396 msgid "Clean up old history & error log" 395 397 msgstr "" 396 398 397 #: admin/class-diagnostics-page.php:4 83399 #: admin/class-diagnostics-page.php:499 398 400 msgid "Poll MQTT brokers" 399 401 msgstr "" 400 402 401 #: admin/class-diagnostics-page.php: 487403 #: admin/class-diagnostics-page.php:500 402 404 msgid "Send due email reports" 403 405 msgstr "" … … 421 423 422 424 #: admin/class-settings-page.php:101 423 #: admin/views/general-settings-page.php:2 5425 #: admin/views/general-settings-page.php:27 424 426 msgid "IoT Data Widget — Settings" 425 427 msgstr "" … … 429 431 msgstr "" 430 432 431 #: admin/class-settings-page.php:2 88433 #: admin/class-settings-page.php:295 432 434 msgid "No file was uploaded, or the upload failed." 433 435 msgstr "" 434 436 435 #: admin/class-settings-page.php:29 2437 #: admin/class-settings-page.php:299 436 438 msgid "The uploaded file is too large to be a valid export." 437 439 msgstr "" 438 440 439 #: admin/class-settings-page.php: 298441 #: admin/class-settings-page.php:305 440 442 msgid "Invalid upload." 441 443 msgstr "" 442 444 443 #: admin/class-settings-page.php:3 04445 #: admin/class-settings-page.php:311 444 446 msgid "Could not read the uploaded file." 445 447 msgstr "" 446 448 447 #: admin/class-settings-page.php:4 24449 #: admin/class-settings-page.php:431 448 450 msgid "Please choose a valid widget style." 449 451 msgstr "" … … 649 651 650 652 #: admin/views/connector-form.php:18 651 #: admin/views/general-settings-page.php:2 64653 #: admin/views/general-settings-page.php:281 652 654 #: admin/views/wizard/step-push-2-channel.php:35 653 655 #: includes/premium/admin-views/alert-rule-form.php:20 … … 663 665 664 666 #: admin/views/connector-form.php:32 665 #: admin/views/settings-page.php:1 39667 #: admin/views/settings-page.php:141 666 668 msgid "Add connection" 667 669 msgstr "" … … 723 725 #: admin/views/diagnostics.php:93 724 726 #: admin/views/diagnostics.php:154 725 #: admin/views/diagnostics.php:24 1727 #: admin/views/diagnostics.php:242 726 728 #: includes/premium/admin-views/alert-rules-page.php:44 727 729 msgid "Status" … … 750 752 #: admin/views/diagnostics.php:171 751 753 #: admin/views/diagnostics.php:202 752 #: admin/views/diagnostics.php:25 7753 #: admin/views/diagnostics.php:29 0754 #: admin/views/diagnostics.php:258 755 #: admin/views/diagnostics.php:292 754 756 #, php-format 755 757 msgid "%s UTC" … … 761 763 762 764 #: admin/views/diagnostics.php:151 763 #: admin/views/diagnostics.php:23 8764 #: admin/views/general-settings-page.php:1 69765 #: admin/views/general-settings-page.php:2 27766 #: admin/views/general-settings-page.php:2 77765 #: admin/views/diagnostics.php:239 766 #: admin/views/general-settings-page.php:186 767 #: admin/views/general-settings-page.php:244 768 #: admin/views/general-settings-page.php:294 767 769 #: includes/premium/admin-views/alert-rule-form.php:33 768 770 #: includes/premium/admin-views/alert-rules-page.php:40 … … 775 777 776 778 #: admin/views/diagnostics.php:152 777 #: admin/views/settings-page.php:6 7779 #: admin/views/settings-page.php:69 778 780 msgid "Type" 779 781 msgstr "" … … 804 806 805 807 #: admin/views/diagnostics.php:230 806 #: admin/views/settings-page.php:1 28808 #: admin/views/settings-page.php:130 807 809 msgid "No connections configured yet." 808 810 msgstr "" 809 811 810 #: admin/views/diagnostics.php:2 39811 #: admin/views/general-settings-page.php:1 82812 #: admin/views/general-settings-page.php: 193813 #: admin/views/general-settings-page.php:2 28814 #: admin/views/general-settings-page.php: 293815 #: admin/views/general-settings-page.php:3 04812 #: admin/views/diagnostics.php:240 813 #: admin/views/general-settings-page.php:199 814 #: admin/views/general-settings-page.php:210 815 #: admin/views/general-settings-page.php:245 816 #: admin/views/general-settings-page.php:310 817 #: admin/views/general-settings-page.php:321 816 818 #: admin/views/wizard/step-5-done.php:34 817 819 #: admin/views/wizard/step-push-3-snippet.php:117 … … 831 833 msgstr[1] "" 832 834 833 #: admin/views/diagnostics.php:24 0835 #: admin/views/diagnostics.php:241 834 836 #: includes/premium/admin-views/alert-rule-form.php:81 835 837 #: includes/premium/admin-views/alert-rules-page.php:42 … … 837 839 msgstr "" 838 840 839 #: admin/views/diagnostics.php:24 2841 #: admin/views/diagnostics.php:243 840 842 msgid "Last triggered" 841 843 msgstr "" 842 844 843 #: admin/views/diagnostics.php:27 1845 #: admin/views/diagnostics.php:272 844 846 #: includes/premium/admin-views/alert-rules-page.php:81 845 847 msgid "No alert rules configured yet." 846 848 msgstr "" 847 849 848 #: admin/views/diagnostics.php:27 6850 #: admin/views/diagnostics.php:278 849 851 msgid "Recent errors" 850 852 msgstr "" 851 853 852 #: admin/views/diagnostics.php:2 79854 #: admin/views/diagnostics.php:281 853 855 msgid "When" 854 856 msgstr "" 855 857 856 #: admin/views/diagnostics.php:28 0858 #: admin/views/diagnostics.php:282 857 859 msgid "Source" 858 860 msgstr "" 859 861 860 #: admin/views/diagnostics.php:28 1862 #: admin/views/diagnostics.php:283 861 863 msgid "Message" 862 864 msgstr "" 863 865 864 #: admin/views/diagnostics.php:30 0866 #: admin/views/diagnostics.php:302 865 867 msgid "No errors recorded — everything is working." 866 868 msgstr "" 867 869 868 #: admin/views/diagnostics.php:30 5870 #: admin/views/diagnostics.php:307 869 871 msgid "Data size" 870 872 msgstr "" 871 873 872 #: admin/views/diagnostics.php:31 1874 #: admin/views/diagnostics.php:313 873 875 msgid "Table" 874 876 msgstr "" 875 877 876 #: admin/views/diagnostics.php:31 2878 #: admin/views/diagnostics.php:314 877 879 msgid "Rows" 878 880 msgstr "" 879 881 880 #: admin/views/general-settings-page.php: 29882 #: admin/views/general-settings-page.php:31 881 883 msgid "Settings saved." 882 884 msgstr "" 883 885 884 #: admin/views/general-settings-page.php:3 0886 #: admin/views/general-settings-page.php:32 885 887 msgid "Manually tracked entity added." 886 888 msgstr "" 887 889 888 #: admin/views/general-settings-page.php:3 1890 #: admin/views/general-settings-page.php:33 889 891 msgid "Manually tracked entity removed." 890 892 msgstr "" 891 893 892 894 #. translators: %d: number of imported connections 893 #: admin/views/general-settings-page.php:5 0895 #: admin/views/general-settings-page.php:52 894 896 #, php-format 895 897 msgid "Imported %d connection." … … 899 901 900 902 #. translators: %d: number of skipped entries 901 #: admin/views/general-settings-page.php:6 1903 #: admin/views/general-settings-page.php:63 902 904 #, php-format 903 905 msgid "Skipped %d entry:" … … 906 908 msgstr[1] "" 907 909 908 #: admin/views/general-settings-page.php:7 4910 #: admin/views/general-settings-page.php:76 909 911 msgid "Imported connections need their password/authorization re-entered before use — these are never included in an export, by design." 910 912 msgstr "" 911 913 912 #: admin/views/general-settings-page.php: 79914 #: admin/views/general-settings-page.php:81 913 915 msgid "Import failed:" 914 916 msgstr "" 915 917 916 #: admin/views/general-settings-page.php:8 6918 #: admin/views/general-settings-page.php:88 917 919 msgid "Branding" 918 920 msgstr "" 919 921 920 #: admin/views/general-settings-page.php:9 2922 #: admin/views/general-settings-page.php:94 921 923 msgid "Front-end attribution" 922 924 msgstr "" 923 925 924 #: admin/views/general-settings-page.php:9 6926 #: admin/views/general-settings-page.php:98 925 927 msgid "Show \"Powered by IoT Data Widget\" beneath widgets" 926 928 msgstr "" 927 929 928 #: admin/views/general-settings-page.php: 99930 #: admin/views/general-settings-page.php:101 929 931 msgid "Off by default. Turn this on if you'd like to credit the plugin beneath your widgets." 930 932 msgstr "" 931 933 932 #: admin/views/general-settings-page.php:10 4934 #: admin/views/general-settings-page.php:106 933 935 msgid "Stale data threshold" 934 936 msgstr "" 935 937 936 #: admin/views/general-settings-page.php:11 4938 #: admin/views/general-settings-page.php:116 937 939 msgid "minutes" 938 940 msgstr "" 939 941 940 #: admin/views/general-settings-page.php:11 6942 #: admin/views/general-settings-page.php:118 941 943 msgid "Past this age, widgets show the value grayed out with a timestamp instead of implying it's current — mainly relevant for push channels, where a device can stop sending data without any explicit connection error." 942 944 msgstr "" 943 945 944 #: admin/views/general-settings-page.php:121 946 #: admin/views/general-settings-page.php:123 947 msgid "Local network" 948 msgstr "" 949 950 #: admin/views/general-settings-page.php:127 951 msgid "Allow local network addresses" 952 msgstr "" 953 954 #: admin/views/general-settings-page.php:130 955 msgid "Turn this on only if WordPress runs on the same network as your devices (e.g. a home server) and connects to addresses like 192.168.x.x or homeassistant.local. Off by default: it stops a misconfigured or malicious URL from reaching other services on your server's network. Cloud metadata and link-local addresses are always blocked." 956 msgstr "" 957 958 #: admin/views/general-settings-page.php:133 959 msgid "On a multisite network, only a network administrator can change this." 960 msgstr "" 961 962 #: admin/views/general-settings-page.php:138 945 963 msgid "Uninstall" 946 964 msgstr "" 947 965 948 #: admin/views/general-settings-page.php:1 25966 #: admin/views/general-settings-page.php:142 949 967 msgid "Remove all plugin data when the plugin is deleted" 950 968 msgstr "" 951 969 952 #: admin/views/general-settings-page.php:1 28970 #: admin/views/general-settings-page.php:145 953 971 msgid "Off by default. Deactivating the plugin (e.g. during an update) never touches your data either way — only checking this box AND THEN deleting the plugin from the Plugins screen removes connections, history, alerts and every other setting. Leave this unchecked if you might reinstall later." 954 972 msgstr "" 955 973 956 #: admin/views/general-settings-page.php:1 33974 #: admin/views/general-settings-page.php:150 957 975 msgid "Save settings" 958 976 msgstr "" 959 977 960 #: admin/views/general-settings-page.php:1 36978 #: admin/views/general-settings-page.php:153 961 979 msgid "Export connections" 962 980 msgstr "" 963 981 964 #: admin/views/general-settings-page.php:1 37982 #: admin/views/general-settings-page.php:154 965 983 msgid "Downloads a JSON file with all your connections. Passwords and authorization tokens are never included — re-enter them after importing." 966 984 msgstr "" 967 985 968 #: admin/views/general-settings-page.php:1 41986 #: admin/views/general-settings-page.php:158 969 987 msgid "Download export" 970 988 msgstr "" 971 989 972 #: admin/views/general-settings-page.php:1 44990 #: admin/views/general-settings-page.php:161 973 991 msgid "Import connections" 974 992 msgstr "" 975 993 976 #: admin/views/general-settings-page.php:1 45994 #: admin/views/general-settings-page.php:162 977 995 msgid "Imports connections from a previously exported JSON file. Connections with a matching ID are overwritten; others are added. Existing connections not present in the file are left untouched." 978 996 msgstr "" 979 997 980 #: admin/views/general-settings-page.php:1 50998 #: admin/views/general-settings-page.php:167 981 999 msgid "Import" 982 1000 msgstr "" 983 1001 984 #: admin/views/general-settings-page.php:1 531002 #: admin/views/general-settings-page.php:170 985 1003 msgid "Export history data" 986 1004 msgstr "" 987 1005 988 #: admin/views/general-settings-page.php:1 561006 #: admin/views/general-settings-page.php:173 989 1007 #: includes/premium/class-history-csv-export.php:71 990 1008 msgid "CSV export of recorded history requires the Pro version." 991 1009 msgstr "" 992 1010 993 #: admin/views/general-settings-page.php:1 601011 #: admin/views/general-settings-page.php:177 994 1012 msgid "Downloads the raw recorded history for one connection/entity as a CSV file, for further analysis outside the plugin. Only entities used by a Chart/Sparkline widget (or manually tracked below) have history recorded." 995 1013 msgstr "" 996 1014 997 #: admin/views/general-settings-page.php:1 72998 #: admin/views/general-settings-page.php:2 801015 #: admin/views/general-settings-page.php:189 1016 #: admin/views/general-settings-page.php:297 999 1017 #: includes/premium/admin-views/alert-rule-form.php:40 1000 1018 #: includes/premium/admin-views/email-report-form.php:40 … … 1003 1021 msgstr "" 1004 1022 1005 #: admin/views/general-settings-page.php: 1871006 #: admin/views/general-settings-page.php: 2981023 #: admin/views/general-settings-page.php:204 1024 #: admin/views/general-settings-page.php:315 1007 1025 #: includes/premium/admin-views/alert-rule-form.php:58 1008 1026 #: includes/premium/admin-views/email-report-form.php:58 … … 1012 1030 msgstr "" 1013 1031 1014 #: admin/views/general-settings-page.php: 1971032 #: admin/views/general-settings-page.php:214 1015 1033 msgid "Useful if the entity was renamed/removed from the live connection since history was recorded." 1016 1034 msgstr "" 1017 1035 1018 #: admin/views/general-settings-page.php:2 011019 #: admin/views/general-settings-page.php:3 151036 #: admin/views/general-settings-page.php:218 1037 #: admin/views/general-settings-page.php:332 1020 1038 #: includes/premium/admin-views/alert-rule-form.php:75 1021 1039 #: includes/premium/admin-views/email-report-form.php:75 … … 1025 1043 msgstr "" 1026 1044 1027 #: admin/views/general-settings-page.php:2 071045 #: admin/views/general-settings-page.php:224 1028 1046 msgid "From" 1029 1047 msgstr "" 1030 1048 1031 #: admin/views/general-settings-page.php:2 111049 #: admin/views/general-settings-page.php:228 1032 1050 msgid "To" 1033 1051 msgstr "" 1034 1052 1035 #: admin/views/general-settings-page.php:2 161053 #: admin/views/general-settings-page.php:233 1036 1054 msgid "Download CSV" 1037 1055 msgstr "" 1038 1056 1039 #: admin/views/general-settings-page.php:2 201057 #: admin/views/general-settings-page.php:237 1040 1058 msgid "Manually tracked entities" 1041 1059 msgstr "" 1042 1060 1043 #: admin/views/general-settings-page.php:2 221061 #: admin/views/general-settings-page.php:239 1044 1062 msgid "Widgets placed in post/page content are detected automatically. If you output a widget from a theme template instead (e.g. in a widget area or a custom page template), add it here so it still gets polled for history/alerts even though there's no post content to scan." 1045 1063 msgstr "" 1046 1064 1047 #: admin/views/general-settings-page.php:2 291065 #: admin/views/general-settings-page.php:246 1048 1066 #: blocks/iot-widget/src/edit.js:478 1049 1067 msgid "Style" 1050 1068 msgstr "" 1051 1069 1052 #: admin/views/general-settings-page.php:2 411070 #: admin/views/general-settings-page.php:258 1053 1071 msgid "Stop tracking this entity?" 1054 1072 msgstr "" 1055 1073 1056 #: admin/views/general-settings-page.php:2 421074 #: admin/views/general-settings-page.php:259 1057 1075 #: blocks/iot-widget/src/grid-item-editor.js:117 1058 1076 msgid "Remove" 1059 1077 msgstr "" 1060 1078 1061 #: admin/views/general-settings-page.php:2 551079 #: admin/views/general-settings-page.php:272 1062 1080 msgid "No manually tracked entities yet." 1063 1081 msgstr "" 1064 1082 1065 #: admin/views/general-settings-page.php:2 561083 #: admin/views/general-settings-page.php:273 1066 1084 msgid "Track your first entity" 1067 1085 msgstr "" 1068 1086 1069 #: admin/views/general-settings-page.php: 2881087 #: admin/views/general-settings-page.php:305 1070 1088 #: includes/premium/admin-views/alert-rule-form.php:48 1071 1089 #: includes/premium/admin-views/email-report-form.php:48 … … 1073 1091 msgstr "" 1074 1092 1075 #: admin/views/general-settings-page.php:3 211093 #: admin/views/general-settings-page.php:338 1076 1094 msgid "Widget style" 1077 1095 msgstr "" 1078 1096 1079 #: admin/views/general-settings-page.php:3 241097 #: admin/views/general-settings-page.php:341 1080 1098 #: blocks/iot-widget/src/edit.js:75 1081 1099 msgid "Card" 1082 1100 msgstr "" 1083 1101 1084 #: admin/views/general-settings-page.php:3 251102 #: admin/views/general-settings-page.php:342 1085 1103 #: blocks/iot-widget/src/edit.js:76 1086 1104 msgid "Status list" 1087 1105 msgstr "" 1088 1106 1089 #: admin/views/general-settings-page.php:3 261107 #: admin/views/general-settings-page.php:343 1090 1108 #: blocks/iot-widget/src/edit.js:77 1091 1109 msgid "Gauge" 1092 1110 msgstr "" 1093 1111 1094 #: admin/views/general-settings-page.php:3 271112 #: admin/views/general-settings-page.php:344 1095 1113 #: blocks/iot-widget/src/edit.js:78 1096 1114 msgid "Chart" 1097 1115 msgstr "" 1098 1116 1099 #: admin/views/general-settings-page.php:3 281117 #: admin/views/general-settings-page.php:345 1100 1118 #: blocks/iot-widget/src/edit.js:79 1101 1119 msgid "Mini trend (sparkline)" 1102 1120 msgstr "" 1103 1121 1104 #: admin/views/general-settings-page.php:3 311122 #: admin/views/general-settings-page.php:348 1105 1123 msgid "Must match the style actually rendered by your theme template — Chart/Sparkline are what determine whether this entity gets recorded into history." 1106 1124 msgstr "" 1107 1125 1108 #: admin/views/general-settings-page.php:3 371126 #: admin/views/general-settings-page.php:354 1109 1127 msgid "Add manually tracked entity" 1110 1128 msgstr "" 1111 1129 1112 1130 #: admin/views/partials/connector-fields.php:30 1113 #: admin/views/settings-page.php:6 61131 #: admin/views/settings-page.php:68 1114 1132 msgid "Name" 1115 1133 msgstr "" … … 1120 1138 1121 1139 #: admin/views/partials/connector-fields.php:73 1122 #: admin/views/settings-page.php: 681140 #: admin/views/settings-page.php:70 1123 1141 msgid "URL" 1124 1142 msgstr "" … … 1182 1200 msgstr "" 1183 1201 1184 #: admin/views/settings-page.php:3 61185 msgid "The free version supports one connection. Upgrade to Pro for unlimited connections."1186 msgstr "" 1187 1188 #: admin/views/settings-page.php:4 01202 #: admin/views/settings-page.php:38 1203 msgid "The demo connection could not be saved. Please try again, or add a connection manually below." 1204 msgstr "" 1205 1206 #: admin/views/settings-page.php:42 1189 1207 msgid "Connection saved." 1190 1208 msgstr "" 1191 1209 1192 #: admin/views/settings-page.php:4 11210 #: admin/views/settings-page.php:43 1193 1211 msgid "Connection deleted." 1194 1212 msgstr "" 1195 1213 1196 #: admin/views/settings-page.php:5 11214 #: admin/views/settings-page.php:53 1197 1215 msgid "Now that you have a connection, try these next:" 1198 1216 msgstr "" 1199 1217 1200 #: admin/views/settings-page.php:5 71218 #: admin/views/settings-page.php:59 1201 1219 msgid "Dismiss this notice." 1202 1220 msgstr "" 1203 1221 1204 #: admin/views/settings-page.php:6 21222 #: admin/views/settings-page.php:64 1205 1223 msgid "Saved connections" 1206 1224 msgstr "" 1207 1225 1208 #: admin/views/settings-page.php: 691226 #: admin/views/settings-page.php:71 1209 1227 msgid "Shortcode" 1210 1228 msgstr "" 1211 1229 1212 #: admin/views/settings-page.php:1 081230 #: admin/views/settings-page.php:110 1213 1231 #: includes/premium/admin-views/alert-rules-page.php:60 1214 1232 #: includes/premium/admin-views/email-reports-page.php:56 … … 1216 1234 msgstr "" 1217 1235 1218 #: admin/views/settings-page.php:11 41236 #: admin/views/settings-page.php:116 1219 1237 msgid "Are you sure you want to delete this connection?" 1220 1238 msgstr "" 1221 1239 1222 #: admin/views/settings-page.php:1 291240 #: admin/views/settings-page.php:131 1223 1241 msgid "Add your first connection" 1224 1242 msgstr "" 1225 1243 1226 #: admin/views/settings-page.php:1 381244 #: admin/views/settings-page.php:140 1227 1245 msgid "Edit connection" 1228 1246 msgstr "" … … 1290 1308 msgstr "" 1291 1309 1292 #: admin/views/wizard/step-0-location.php:3 11310 #: admin/views/wizard/step-0-location.php:32 1293 1311 msgid "Same network as my devices (Raspberry Pi, NAS, home server) — my site fetches data from the device" 1294 1312 msgstr "" 1295 1313 1296 #: admin/views/wizard/step-0-location.php: 471314 #: admin/views/wizard/step-0-location.php:51 1297 1315 msgid "On external/shared hosting, but my device has its own cloud service (Shelly Cloud, Supla, a publicly-reachable Home Assistant) — my site fetches data from there" 1298 1316 msgstr "" 1299 1317 1300 #: admin/views/wizard/step-0-location.php:52 1318 #: admin/views/wizard/step-0-location.php:53 1319 msgid "On external/shared hosting, but my device has its own cloud service (a publicly-reachable Home Assistant; Shelly Cloud and Supla in Pro) — my site fetches data from there" 1320 msgstr "" 1321 1322 #: admin/views/wizard/step-0-location.php:60 1301 1323 msgid "Not sure? Choose the first option — it's the safer default." 1302 1324 msgstr "" 1303 1325 1304 #: admin/views/wizard/step-0-location.php: 571326 #: admin/views/wizard/step-0-location.php:65 1305 1327 msgid "Devices behind a home router (NAT) almost never accept incoming connections from the internet — if your WordPress is hosted externally, it typically cannot reach a device address like 192.168.x.x at all. Sending data out from the device instead always works, and so does fetching from a cloud service the device already reports to." 1306 1328 msgstr "" 1307 1329 1308 #: admin/views/wizard/step-0-location.php:7 11330 #: admin/views/wizard/step-0-location.php:79 1309 1331 msgid "Security note: in push mode, this plugin never stores any credentials to your devices — the device sends data out, authenticated by a token this plugin generates that only allows writing one entity to one channel. In pull mode, a copy of your device's own credentials (e.g. a Home Assistant access token) is stored in your WordPress database." 1310 1332 msgstr "" 1311 1333 1312 #: admin/views/wizard/step-0-location.php: 751334 #: admin/views/wizard/step-0-location.php:83 1313 1335 #: admin/views/wizard/step-1-connection-type.php:92 1314 1336 #: admin/views/wizard/step-2-configure.php:87 … … 1582 1604 msgstr "" 1583 1605 1606 #: includes/class-http-client.php:98 1607 msgid "The server redirected too many times." 1608 msgstr "" 1609 1610 #: includes/class-http-client.php:133 1611 msgid "The configured URL has no valid host (only http:// and https:// addresses are supported)." 1612 msgstr "" 1613 1614 #. translators: %s: the host that was blocked 1615 #: includes/class-http-client.php:151 1616 #: includes/class-http-client.php:171 1617 #, php-format 1618 msgid "Refusing to connect to \"%s\" — it resolves to a link-local or reserved address (such as a cloud metadata service). This is always blocked." 1619 msgstr "" 1620 1621 #. translators: %s: the host that was blocked 1622 #: includes/class-http-client.php:252 1623 #, php-format 1624 msgid "Refusing to connect to \"%s\" — it is a local network address. If WordPress runs on the same network as the device, enable \"Allow local network addresses\" under IoT Data Widget → Settings. Otherwise, use a push channel instead." 1625 msgstr "" 1626 1627 #: includes/class-local-network.php:205 1628 msgid "Local network addresses are now allowed, so this site can reach devices on your network. You can change this under IoT Data Widget → Settings." 1629 msgstr "" 1630 1631 #: includes/class-local-network.php:210 1632 msgid "Connections to local network addresses are blocked on this site, and only a network administrator can allow them (IoT Data Widget → Settings). Until then, choose push mode instead." 1633 msgstr "" 1634 1635 #: includes/class-local-network.php:222 1636 msgid "Security update: IoT Data Widget now refuses connections to local network addresses unless \"Allow local network addresses\" is turned on. This site already connects to a device on your local network, so the setting was turned on automatically — nothing stopped working." 1637 msgstr "" 1638 1639 #: includes/class-local-network.php:224 1640 msgid "Review settings" 1641 msgstr "" 1642 1643 #: includes/class-local-network.php:226 1644 msgid "Dismiss" 1645 msgstr "" 1646 1584 1647 #: includes/class-push-ingest.php:120 1585 1648 msgid "Request body is too large." … … 1702 1765 1703 1766 #: includes/class-renderer.php:226 1704 #: includes/class-shortcode.php:1 331767 #: includes/class-shortcode.php:153 1705 1768 msgid "This style requires the Pro version." 1706 1769 msgstr "" … … 1773 1836 msgstr[1] "" 1774 1837 1775 #: includes/class-rest-api.php:7 261838 #: includes/class-rest-api.php:732 1776 1839 msgid "Not found." 1777 1840 msgstr "" 1778 1841 1779 #: includes/class-shortcode.php:1 461842 #: includes/class-shortcode.php:166 1780 1843 msgid "Fill in the \"connection\" and \"entity\" attributes in the [iotdw_widget] shortcode." 1781 1844 msgstr "" 1782 1845 1783 1846 #. translators: %s: connection name or ID given in the shortcode 1784 #: includes/class-shortcode.php:1 591785 #: includes/class-shortcode.php:4 611786 #: includes/class-shortcode.php:5 311847 #: includes/class-shortcode.php:179 1848 #: includes/class-shortcode.php:481 1849 #: includes/class-shortcode.php:551 1787 1850 #, php-format 1788 1851 msgid "Connection \"%s\" was not found." 1789 1852 msgstr "" 1790 1853 1791 #: includes/class-shortcode.php:4 341854 #: includes/class-shortcode.php:454 1792 1855 msgid "Powered by IoT Data Widget" 1793 1856 msgstr "" 1794 1857 1795 #: includes/class-shortcode.php:4 481858 #: includes/class-shortcode.php:468 1796 1859 msgid "Fill in the \"connection\" and \"entities\" attributes (comma-separated entity IDs) in the [iotdw_widget style=\"list\"] shortcode." 1797 1860 msgstr "" 1798 1861 1799 #: includes/class-shortcode.php:5 121862 #: includes/class-shortcode.php:532 1800 1863 msgid "Add at least one entity to the [iotdw_widget style=\"grid\"] shortcode via the \"items\" attribute." 1801 1864 msgstr "" 1802 1865 1803 #: includes/class-shortcode.php:5 131866 #: includes/class-shortcode.php:533 1804 1867 msgid "The \"items\" attribute of the [iotdw_widget style=\"grid\"] shortcode must be a comma-separated list of \"connection|entity\" pairs (e.g. items=\"Home|sensor.temperature,Workshop|relay_state\") — none of the given items matched that format." 1805 1868 msgstr "" 1806 1869 1807 1870 #. translators: %s: PHP error message 1808 #: includes/class-shortcode.php:7 141809 #: includes/class-shortcode.php: 7461871 #: includes/class-shortcode.php:780 1872 #: includes/class-shortcode.php:812 1810 1873 #, php-format 1811 1874 msgid "Unexpected connector error: %s" 1812 1875 msgstr "" 1813 1876 1814 #: includes/class-shortcode.php:861 1877 #: includes/class-shortcode.php:927 1878 #: includes/class-value-normalizer.php:103 1815 1879 #: blocks/iot-widget/src/edit.js:641 1816 1880 msgid "On" 1817 1881 msgstr "" 1818 1882 1819 #: includes/class-shortcode.php:862 1883 #: includes/class-shortcode.php:928 1884 #: includes/class-value-normalizer.php:105 1820 1885 #: blocks/iot-widget/src/edit.js:651 1821 1886 msgid "Off" … … 1823 1888 1824 1889 #. translators: %s: the local-network host/IP that was configured 1825 #: includes/connectors/abstract-connector.php:1 361890 #: includes/connectors/abstract-connector.php:141 1826 1891 #, php-format 1827 1892 msgid "This looks like a local network address (%s) — it will only work if your WordPress server is on the same network as the device. Most external/shared hosting can't reach it; consider Shelly Cloud, a publicly reachable Home Assistant instance, or a differently-reachable REST endpoint instead." … … 2337 2402 msgstr "" 2338 2403 2339 #: includes/premium/class-mqtt-poll-engine.php:1 862404 #: includes/premium/class-mqtt-poll-engine.php:194 2340 2405 msgid "Missing MQTT broker host in the connection settings." 2341 2406 msgstr "" 2342 2407 2343 #: includes/premium/class-mqtt-poll-engine.php:26 12408 #: includes/premium/class-mqtt-poll-engine.php:269 2344 2409 msgid "Missing EMQX REST API URL in the connection settings." 2345 2410 msgstr "" 2346 2411 2347 2412 #. translators: %d: HTTP status code 2348 #: includes/premium/class-mqtt-poll-engine.php: 2982413 #: includes/premium/class-mqtt-poll-engine.php:306 2349 2414 #, php-format 2350 2415 msgid "EMQX REST API returned an unexpected response code: %d." 2351 2416 msgstr "" 2352 2417 2353 #: includes/premium/class-mqtt-poll-engine.php:3 082418 #: includes/premium/class-mqtt-poll-engine.php:316 2354 2419 msgid "Invalid or unexpected response shape from the EMQX REST API." 2355 2420 msgstr "" -
iot-data-widget/trunk/public/css/widget-styles.css
r3730704 r3732074 1 /* 2 * Wrapper z IOTDW_Shortcode::render() — celowo bez szerokości/marginesów 3 * poziomych: dostaje je od motywu jak każdy inny blok (kolumna treści). 4 * Odstęp między dwoma widgetami pod rząd przez :where() (zerowa 5 * specyficzność) — motywy blokowe (blockGap) i tak go nadpisują swoim. 6 */ 7 :where(.iot-dw-widget-wrap + .iot-dw-widget-wrap) { 8 margin-top: 1em; 9 } 10 1 11 .iot-dw-widget { 2 12 display: inline-block; … … 137 147 display: block; 138 148 list-style: none; 139 /* Tylko poziome — pionowe zostawiamy motywowi/WP (odstęp między blokami). */ 140 margin-left: 0; 141 margin-right: 0; 149 margin: 0; 142 150 padding: 0; 143 151 box-sizing: border-box; … … 351 359 } 352 360 353 /*354 * Motywy blokowe (np. Twenty Twenty-Five): layout "constrained" wyrównuje355 * dzieci do kolumny treści przez `margin-left/right: auto !important`, ale356 * na elementach inline-level `auto` liczy się jako 0 — card/gauge/sparkline357 * lądowały na lewej krawędzi strony zamiast w kolumnie. Tu: blokowe, szerokość358 * wg treści, lewa krawędź = lewa krawędź kolumny. !important, bo reguła WP też359 * go ma (wygrywa wyższa specyficzność). Poza layoutem constrained (motywy360 * klasyczne, grupa Row/flex) bez zmian — dalej inline, obok siebie.361 */362 .is-layout-constrained > .iot-dw-widget--card,363 .is-layout-constrained > .iot-dw-widget--gauge,364 .is-layout-constrained > .iot-dw-widget--sparkline {365 width: fit-content;366 margin-left: max( 0px, calc( ( 100% - var( --wp--style--global--content-size, 100% ) ) / 2 ) ) !important;367 margin-right: auto !important;368 }369 370 .is-layout-constrained > .iot-dw-widget--card {371 display: block;372 }373 374 .is-layout-constrained > .iot-dw-widget--gauge,375 .is-layout-constrained > .iot-dw-widget--sparkline {376 display: flex;377 } -
iot-data-widget/trunk/readme.txt
r3730704 r3732074 5 5 Tested up to: 7.1 6 6 Requires PHP: 8.1 7 Stable tag: 1.0. 17 Stable tag: 1.0.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 22 22 2. Activate the plugin through the **Plugins** screen — this opens the **setup wizard** automatically (skip this step by activating several plugins at once, or by clicking "Skip the wizard" on its first screen). 23 23 3. Follow the wizard: pick a connection type, configure and test it live, choose an entity, and copy the ready-made shortcode from the preview screen — or configure a connection manually under **IoT Data Widget** in the admin menu instead: 24 * **Home Assistant**: enter your instance URL (e.g. `http://homeassistant.local:8123`) and a Long-Lived Access Token. Generate one in Home Assistant under your profile page → **Security** → **Long-Lived Access Tokens** → **Create Token**. 24 * **Home Assistant**: enter your instance URL (e.g. `http://homeassistant.local:8123`) and a Long-Lived Access Token. Generate one in Home Assistant under your profile page → **Security** → **Long-Lived Access Tokens** → **Create Token**. If the URL is a local network address, also turn on **Settings → Allow local network addresses** (the wizard's "Same network as my devices" option does this for you). 25 25 * **Push mode**: no URL/credentials to enter — the wizard generates a channel and a ready-to-paste snippet for your device instead. 26 26 * **Generic REST, advanced mode**: enter the full URL of a JSON endpoint. Leave Authorization empty for public APIs, or enter a full header (e.g. `Authorization: Bearer xxx` or `X-API-Key: xxx`) if the API requires one. … … 66 66 Generic REST (entity is a JSONPath into the JSON response): `[iotdw_widget connection="Weather API" entity="current_weather.temperature"]` 67 67 68 Entities with a non-numeric state (a relay's on/off, a presence sensor's home/away...) show their real value ("ON", "true") in Card and Status list styles.68 Entities with a non-numeric state (a relay's on/off, a presence sensor's home/away...) show their real state ("ON", "home") in Card and Status list styles; a raw true/false from a JSON API shows as "On"/"Off". 69 69 70 70 = I don't want to use the setup wizard — can I skip it? = … … 93 93 94 94 Push mode sidesteps this entirely: the plugin never stores any credentials to your devices, because your device sends data to the plugin instead of the other way around. 95 96 = My device is on the same network as WordPress, but the connection is refused = 97 98 For security, the plugin doesn't connect to local network addresses (like 192.168.x.x or homeassistant.local) unless you allow it. If WordPress runs on the same network as your devices — a Raspberry Pi, NAS or home server — turn on **IoT Data Widget → Settings → Allow local network addresses** (on a multisite network, only a network administrator can). Choosing "Same network as my devices" in the setup wizard does this for you. If your site is hosted externally, use push mode instead: it can't reach your home network anyway. 95 99 96 100 = Does this work with any REST API, or only Home Assistant? = … … 133 137 == Changelog == 134 138 139 = 1.0.2 = 140 * Security: requests to local network addresses (192.168.x.x, 10.x.x.x, homeassistant.local…) are now blocked unless "Allow local network addresses" is turned on under Settings; cloud metadata and link-local addresses are always blocked, including via redirects. If this site already connects to a device on your local network, the setting is turned on automatically during the update — nothing stops working. Choosing "Same network as my devices" in the setup wizard turns it on too. 141 * Added: widgets placed in classic theme widget areas (sidebars, footers) now load their styles and refresh live — previously they appeared unstyled and never updated. 142 * Fixed: widgets line up with the content column in classic themes such as Twenty Twenty-One. Widgets placed one after another now stack vertically — use a Row or Columns block to place them side by side. 143 * Fixed: "Test connection" (and alert e-mails in Pro) show "On"/"Off" instead of "true"/"false" for on/off devices such as Shelly relays. 144 * Fixed: the free version no longer schedules or lists Pro-only background tasks (MQTT polling, email reports, value history); updating removes them. 145 * Fixed: the setup wizard labels Shelly Cloud and Supla as Pro, and shows an accurate message if the demo connection can't be saved. 146 135 147 = 1.0.1 = 136 148 * Added: "Upgrade" link in the plugin menu and the plugin list, and an upgrade link next to Pro-only styles (visible to administrators only).
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)