From 1fbc28a628185869a24565bdd3e0a8ce4ae77250 Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Thu, 15 Jan 2026 00:36:19 +0100 Subject: [PATCH] optimize AP mode - save wifi data - show status led Signed-off-by: Peter Siegmund --- firmware/components/api-server/CMakeLists.txt | 1 + .../components/api-server/src/api_handlers.c | 30 +- .../api-server/src/websocket_handler.c | 12 +- .../connectivity-manager/src/dns_hijack.c | 1 - .../connectivity-manager/src/wifi_manager.c | 270 ++++++------------ .../components/led-manager/include/color.h | 6 +- firmware/components/led-manager/src/color.c | 6 +- .../include/persistence_manager.h | 5 +- .../src/persistence_manager.c | 14 +- firmware/main/app_task.cpp | 3 - firmware/storage/www/captive.html | 32 +-- firmware/storage/www/css/captive.css | 9 + firmware/storage/www/index.html | 2 +- firmware/storage/www/js/wifi-shared.js | 79 ++--- 14 files changed, 213 insertions(+), 257 deletions(-) diff --git a/firmware/components/api-server/CMakeLists.txt b/firmware/components/api-server/CMakeLists.txt index 13dee36..0d289d1 100644 --- a/firmware/components/api-server/CMakeLists.txt +++ b/firmware/components/api-server/CMakeLists.txt @@ -12,4 +12,5 @@ idf_component_register(SRCS esp_event json simulator + persistence-manager ) diff --git a/firmware/components/api-server/src/api_handlers.c b/firmware/components/api-server/src/api_handlers.c index 1305498..043e220 100644 --- a/firmware/components/api-server/src/api_handlers.c +++ b/firmware/components/api-server/src/api_handlers.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,17 @@ esp_err_t api_wifi_scan_handler(httpd_req_t *req) return res; } +static void reboot_task(void *param) +{ + vTaskDelay(pdMS_TO_TICKS(100)); + esp_restart(); +} + +static bool is_valid(const cJSON *string) +{ + return string && cJSON_IsString(string) && string->valuestring && strlen(string->valuestring) > 0; +} + esp_err_t api_wifi_config_handler(httpd_req_t *req) { ESP_LOGI(TAG, "POST /api/wifi/config"); @@ -128,12 +140,22 @@ esp_err_t api_wifi_config_handler(httpd_req_t *req) } buf[ret] = '\0'; - // Passwort maskieren cJSON *json = cJSON_Parse(buf); if (json) { + cJSON *ssid = cJSON_GetObjectItem(json, "ssid"); cJSON *pw = cJSON_GetObjectItem(json, "password"); - if (pw && cJSON_IsString(pw) && pw->valuestring) + if (is_valid(ssid) && is_valid(pw)) + { + persistence_manager_t pm; + if (persistence_manager_init(&pm, "wifi_config") == ESP_OK) + { + persistence_manager_set_string(&pm, "ssid", ssid->valuestring); + persistence_manager_set_string(&pm, "password", pw->valuestring); + persistence_manager_deinit(&pm); + } + } + if (is_valid(pw)) { size_t pwlen = strlen(pw->valuestring); char *masked = malloc(pwlen + 1); @@ -163,7 +185,9 @@ esp_err_t api_wifi_config_handler(httpd_req_t *req) ESP_LOGI(TAG, "Received WiFi config: %s", buf); } - // TODO: Parse JSON and connect to WiFi + // Define a reboot task function + xTaskCreate(reboot_task, "reboot_task", 2048, NULL, 5, NULL); + set_cors_headers(req); httpd_resp_set_status(req, "200 OK"); return httpd_resp_sendstr(req, "{\"status\":\"ok\"}"); diff --git a/firmware/components/api-server/src/websocket_handler.c b/firmware/components/api-server/src/websocket_handler.c index 6f9023b..8fdc693 100644 --- a/firmware/components/api-server/src/websocket_handler.c +++ b/firmware/components/api-server/src/websocket_handler.c @@ -18,7 +18,7 @@ static void ws_clients_init(void) } // Add a client to the list -static void add_client(int fd) +static bool add_client(int fd) { for (int i = 0; i < WS_MAX_CLIENTS; i++) { @@ -27,10 +27,11 @@ static void add_client(int fd) ws_clients[i] = fd; ws_client_count++; ESP_LOGI(TAG, "WebSocket client connected: fd=%d (total: %d)", fd, ws_client_count); - return; + return true; } } ESP_LOGW(TAG, "Max WebSocket clients reached, cannot add fd=%d", fd); + return false; } // Remove a client from the list @@ -83,6 +84,13 @@ esp_err_t websocket_handler(httpd_req_t *req) { // This is the handshake ESP_LOGI(TAG, "WebSocket handshake"); + int fd = httpd_req_to_sockfd(req); + if (!add_client(fd)) + { + // Zu viele Clients, Verbindung schließen + httpd_sess_trigger_close(req->handle, fd); + return ESP_FAIL; + } return ESP_OK; } diff --git a/firmware/components/connectivity-manager/src/dns_hijack.c b/firmware/components/connectivity-manager/src/dns_hijack.c index 9408451..eb5657b 100644 --- a/firmware/components/connectivity-manager/src/dns_hijack.c +++ b/firmware/components/connectivity-manager/src/dns_hijack.c @@ -51,7 +51,6 @@ static void dns_server_task(void *pvParameters) // Fragen: 1, Antworten: 1 buf[7] = 1; // Antwort anhängen (Name Pointer auf Frage) - int qlen = len - 12; int pos = len; buf[pos++] = 0xC0; buf[pos++] = 0x0C; // Name pointer diff --git a/firmware/components/connectivity-manager/src/wifi_manager.c b/firmware/components/connectivity-manager/src/wifi_manager.c index f08d156..22fa15c 100644 --- a/firmware/components/connectivity-manager/src/wifi_manager.c +++ b/firmware/components/connectivity-manager/src/wifi_manager.c @@ -16,204 +16,120 @@ #include #include #include +#include #include #include -// Event group to signal when we are connected +// Event group to signal WiFi connection status static EventGroupHandle_t s_wifi_event_group; -// The bits for the event group +// Event group bits #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 static const char *TAG = "wifi_manager"; -static int s_retry_num = 0; -static int s_current_network_index = 0; - -// WiFi network configuration structure -typedef struct +static void wifi_create_ap() { - const char *ssid; - const char *password; -} wifi_network_config_t; - -// Array of configured WiFi networks -static const wifi_network_config_t s_wifi_networks[] = { -#if CONFIG_WIFI_ENABLED - {CONFIG_WIFI_SSID_1, CONFIG_WIFI_PASSWORD_1}, -#if CONFIG_WIFI_NETWORK_COUNT >= 2 - {CONFIG_WIFI_SSID_2, CONFIG_WIFI_PASSWORD_2}, -#endif -#if CONFIG_WIFI_NETWORK_COUNT >= 3 - {CONFIG_WIFI_SSID_3, CONFIG_WIFI_PASSWORD_3}, -#endif -#if CONFIG_WIFI_NETWORK_COUNT >= 4 - {CONFIG_WIFI_SSID_4, CONFIG_WIFI_PASSWORD_4}, -#endif -#if CONFIG_WIFI_NETWORK_COUNT >= 5 - {CONFIG_WIFI_SSID_5, CONFIG_WIFI_PASSWORD_5}, -#endif -#endif -}; - -static const int s_wifi_network_count = sizeof(s_wifi_networks) / sizeof(s_wifi_networks[0]); - -static void try_next_network(void); - -static void connect_to_network(int index) -{ -#if CONFIG_WIFI_ENABLED - if (index >= s_wifi_network_count) - { - ESP_LOGE(TAG, "No more networks to try"); - xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); - return; - } - - const wifi_network_config_t *network = &s_wifi_networks[index]; - - // Skip empty SSIDs - if (network->ssid == NULL || strlen(network->ssid) == 0) - { - ESP_LOGW(TAG, "Skipping empty SSID at index %d", index); - s_current_network_index++; - s_retry_num = 0; - try_next_network(); - return; - } - - ESP_DIAG_EVENT(TAG, "Trying to connect to network %d: %s", index + 1, network->ssid); - - wifi_config_t wifi_config = { - .sta = - { - .threshold.authmode = WIFI_AUTH_WPA2_PSK, - }, - }; - - strncpy((char *)wifi_config.sta.ssid, network->ssid, sizeof(wifi_config.sta.ssid) - 1); - strncpy((char *)wifi_config.sta.password, network->password, sizeof(wifi_config.sta.password) - 1); - - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); - esp_wifi_connect(); -#endif -} - -static void try_next_network(void) -{ -#if CONFIG_WIFI_ENABLED - s_current_network_index++; - s_retry_num = 0; - - if (s_current_network_index < s_wifi_network_count) - { - connect_to_network(s_current_network_index); - } - else - { - ESP_LOGE(TAG, "Failed to connect to any configured network"); - led_behavior_t led0_behavior = { - .index = 0, - .mode = LED_MODE_BLINK, - .color = {.red = 50, .green = 0, .blue = 0}, - .on_time_ms = 1000, - .off_time_ms = 500, - }; - led_status_set_behavior(led0_behavior); - xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); - } -#endif -} - -static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ -#if CONFIG_WIFI_ENABLED - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) - { - led_behavior_t led0_behavior = { - .index = 0, - .mode = LED_MODE_BLINK, - .color = {.red = 50, .green = 50, .blue = 0}, - .on_time_ms = 200, - .off_time_ms = 200, - }; - led_status_set_behavior(led0_behavior); - - connect_to_network(s_current_network_index); - } - else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) - { - if (s_retry_num < CONFIG_WIFI_CONNECT_RETRIES) - { - led_behavior_t led0_behavior = { - .index = 0, - .mode = LED_MODE_BLINK, - .color = {.red = 50, .green = 50, .blue = 0}, - .on_time_ms = 200, - .off_time_ms = 200, - }; - led_status_set_behavior(led0_behavior); - - s_retry_num++; - ESP_DIAG_EVENT(TAG, "Retrying network %d (%d/%d)", s_current_network_index + 1, s_retry_num, - CONFIG_WIFI_CONNECT_RETRIES); - esp_wifi_connect(); - return; - } - - // Retries exhausted for current network, try next one - ESP_LOGW(TAG, "Failed to connect to network %d after %d retries, trying next...", s_current_network_index + 1, - CONFIG_WIFI_CONNECT_RETRIES); - try_next_network(); - } - else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) - { - led_behavior_t led0_behavior = { - .index = 0, - .mode = LED_MODE_SOLID, - .color = {.red = 0, .green = 50, .blue = 0}, - }; - led_status_set_behavior(led0_behavior); - - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - ESP_DIAG_EVENT(TAG, "Got IP address:" IPSTR " (network %d: %s)", IP2STR(&event->ip_info.ip), - s_current_network_index + 1, s_wifi_networks[s_current_network_index].ssid); - s_retry_num = 0; - xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); - } -#endif -} - -void wifi_manager_init() -{ - s_wifi_event_group = xEventGroupCreate(); - s_retry_num = 0; - - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - // Access Point erstellen - esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - + ESP_ERROR_CHECK(esp_wifi_stop()); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); wifi_config_t ap_config = {.ap = {.ssid = "system-control", .ssid_len = strlen("system-control"), .password = "", .max_connection = 4, .authmode = WIFI_AUTH_OPEN}}; - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); ESP_ERROR_CHECK(esp_wifi_start()); + ESP_LOGI(TAG, "Access Point 'system-control' started"); + dns_server_start("192.168.4.1"); - ESP_LOGI(TAG, "Access Point 'system-control' gestartet"); + led_behavior_t led_behavior = { + .color = {.red = 50, .green = 0, .blue = 0}, + .index = 0, + .mode = LED_MODE_SOLID, + }; + led_status_set_behavior(led_behavior); +} - // DNS Hijack Server starten (alle DNS-Anfragen auf AP-IP umleiten) - dns_server_start("192.168.4.1"); // ggf. dynamisch ermitteln +void wifi_manager_init() +{ + s_wifi_event_group = xEventGroupCreate(); - // API-Server starten + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // Try to load stored WiFi configuration + persistence_manager_t pm; + char ssid[33] = {0}; + char password[65] = {0}; + bool have_ssid = false, have_password = false; + if (persistence_manager_init(&pm, "wifi_config") == ESP_OK) + { + persistence_manager_get_string(&pm, "ssid", ssid, sizeof(ssid), ""); + persistence_manager_get_string(&pm, "password", password, sizeof(password), ""); + have_ssid = strlen(ssid) > 0; + have_password = strlen(password) > 0; + } + + if (have_ssid && have_password) + { + led_behavior_t led_behavior = { + .on_time_ms = 250, + .off_time_ms = 100, + .color = {.red = 50, .green = 50, .blue = 0}, + .index = 0, + .mode = LED_MODE_BLINK, + }; + led_status_set_behavior(led_behavior); + + ESP_LOGI(TAG, "Found WiFi configuration: SSID='%s'", ssid); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + wifi_config_t wifi_config = {0}; + strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1); + strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1); + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + int retries = 0; + EventBits_t bits; + do + { + esp_wifi_connect(); + bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, + 5000 / portTICK_PERIOD_MS); + if (bits & WIFI_CONNECTED_BIT) + { + led_behavior_t led_behavior = { + .index = 0, + .color = {.red = 0, .green = 50, .blue = 0}, + .mode = LED_MODE_SOLID, + }; + led_status_set_behavior(led_behavior); + ESP_LOGI(TAG, "WiFi connection established successfully"); + break; + } + retries++; + } while (!(bits & WIFI_CONNECTED_BIT) && retries < CONFIG_WIFI_CONNECT_RETRIES); + + if (!(bits & WIFI_CONNECTED_BIT)) + { + ESP_LOGW(TAG, "WiFi connection failed, switching to Access Point mode"); + wifi_create_ap(); + } + } + else + { + // Create Access Point + esp_netif_create_default_wifi_ap(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + wifi_create_ap(); + } + + // API server start api_server_config_t s_config = API_SERVER_CONFIG_DEFAULT(); ESP_ERROR_CHECK(api_server_start(&s_config)); } diff --git a/firmware/components/led-manager/include/color.h b/firmware/components/led-manager/include/color.h index 6de1694..fee62f1 100644 --- a/firmware/components/led-manager/include/color.h +++ b/firmware/components/led-manager/include/color.h @@ -12,9 +12,9 @@ typedef struct typedef struct { - uint8_t h; - uint8_t s; - uint8_t v; + float h; + float s; + float v; } hsv_t; __BEGIN_DECLS diff --git a/firmware/components/led-manager/src/color.c b/firmware/components/led-manager/src/color.c index 28ae1ac..771a517 100644 --- a/firmware/components/led-manager/src/color.c +++ b/firmware/components/led-manager/src/color.c @@ -45,10 +45,10 @@ rgb_t interpolate_color_hsv(rgb_t start, rgb_t end, float factor) // Interpolate HSV values hsv_t interpolated_hsv; - interpolated_hsv.h = fmod(h1 + (h2 - h1) * factor, 360.0); - if (interpolated_hsv.h < 0) + interpolated_hsv.h = fmodf(h1 + (h2 - h1) * factor, 360.0f); + if (interpolated_hsv.h < 0.0f) { - interpolated_hsv.h += 360.0; + interpolated_hsv.h += 360.0f; } interpolated_hsv.s = start_hsv.s + (end_hsv.s - start_hsv.s) * factor; interpolated_hsv.v = start_hsv.v + (end_hsv.v - start_hsv.v) * factor; diff --git a/firmware/components/persistence-manager/include/persistence_manager.h b/firmware/components/persistence-manager/include/persistence_manager.h index 97ad802..e4305bd 100644 --- a/firmware/components/persistence-manager/include/persistence_manager.h +++ b/firmware/components/persistence-manager/include/persistence_manager.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -33,14 +34,14 @@ extern "C" * @param pm Pointer to the persistence manager structure. * @param nvs_namespace Namespace to use for NVS operations. */ - void persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace); + esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace); /** * @brief Deinitialize the persistence manager and release resources. * * @param pm Pointer to the persistence manager structure. */ - void persistence_manager_deinit(persistence_manager_t *pm); + esp_err_t persistence_manager_deinit(persistence_manager_t *pm); /** * @brief Check if the persistence manager is initialized. diff --git a/firmware/components/persistence-manager/src/persistence_manager.c b/firmware/components/persistence-manager/src/persistence_manager.c index 6d9e5e9..af0309e 100644 --- a/firmware/components/persistence-manager/src/persistence_manager.c +++ b/firmware/components/persistence-manager/src/persistence_manager.c @@ -4,10 +4,10 @@ #define TAG "persistence_manager" -void persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace) +esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace) { if (!pm) - return; + return ESP_ERR_INVALID_ARG; strncpy(pm->nvs_namespace, nvs_namespace ? nvs_namespace : "config", sizeof(pm->nvs_namespace) - 1); pm->nvs_namespace[sizeof(pm->nvs_namespace) - 1] = '\0'; pm->initialized = false; @@ -16,20 +16,20 @@ void persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespa { pm->initialized = true; ESP_LOGI(TAG, "Initialized with namespace: %s", pm->nvs_namespace); + return ESP_OK; } - else - { - ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err)); - } + ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err)); + return err; } -void persistence_manager_deinit(persistence_manager_t *pm) +esp_err_t persistence_manager_deinit(persistence_manager_t *pm) { if (pm && pm->initialized) { nvs_close(pm->nvs_handle); pm->initialized = false; } + return ESP_OK; } bool persistence_manager_is_initialized(const persistence_manager_t *pm) diff --git a/firmware/main/app_task.cpp b/firmware/main/app_task.cpp index 81c36f0..65eccf1 100644 --- a/firmware/main/app_task.cpp +++ b/firmware/main/app_task.cpp @@ -186,10 +186,7 @@ void app_task(void *args) setup_buttons(); init_ui(); -#if CONFIG_WIFI_ENABLED wifi_manager_init(); - analytics_init(); -#endif start_simulation(); diff --git a/firmware/storage/www/captive.html b/firmware/storage/www/captive.html index f3d0d39..4e03a2f 100644 --- a/firmware/storage/www/captive.html +++ b/firmware/storage/www/captive.html @@ -27,25 +27,9 @@

🚂 System Control

-

WLAN-Einrichtung

-
- - -
-
-

Suche nach Netzwerken...

-
- - -
- -
oder manuell eingeben
-
-
@@ -112,12 +97,21 @@ setTheme(current === 'dark' ? 'light' : 'dark'); } + // Button aktivieren/deaktivieren + function updateConnectBtn() { + const ssid = document.getElementById('ssid').value; + const pw = document.getElementById('password').value; + const btn = document.getElementById('connect-btn'); + btn.disabled = !(ssid.length > 0 && pw.length > 0); + } + // Initialize on load document.addEventListener('DOMContentLoaded', () => { initTheme(); initI18n(); - // Auto-scan on load - setTimeout(scanNetworks, 500); + document.getElementById('ssid').addEventListener('input', updateConnectBtn); + document.getElementById('password').addEventListener('input', updateConnectBtn); + updateConnectBtn(); }); // Toggle password visibility diff --git a/firmware/storage/www/css/captive.css b/firmware/storage/www/css/captive.css index a727980..31c9e33 100644 --- a/firmware/storage/www/css/captive.css +++ b/firmware/storage/www/css/captive.css @@ -39,6 +39,15 @@ body { box-shadow: 0 4px 20px var(--shadow); } +#connect-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; + box-shadow: none; + background-color: #888 !important; +} +} + /* Form Group - More spacing */ .form-group { margin-bottom: 16px; diff --git a/firmware/storage/www/index.html b/firmware/storage/www/index.html index 09ff919..6038ccf 100644 --- a/firmware/storage/www/index.html +++ b/firmware/storage/www/index.html @@ -16,7 +16,6 @@
-

🚂 System Control

+

🚂 System Control

diff --git a/firmware/storage/www/js/wifi-shared.js b/firmware/storage/www/js/wifi-shared.js index 6de63cb..20e5b71 100644 --- a/firmware/storage/www/js/wifi-shared.js +++ b/firmware/storage/www/js/wifi-shared.js @@ -43,6 +43,13 @@ async function scanNetworks() { try { const response = await fetch('/api/wifi/scan'); + if (!response.ok) { + // Fehlerhafte API-Antwort, aber ESP32 ist erreichbar + const errorText = await response.text(); + showStatus('wifi-status', t('wifi.error.scan') + ': ' + errorText, 'error'); + if (loading) loading.classList.remove('active'); + return; + } const networks = await response.json(); if (loading) { @@ -92,43 +99,43 @@ async function scanNetworks() { if (loading) { loading.classList.remove('active'); } - - // Demo mode for local testing - const demoNetworks = [ - { ssid: 'Demo-Netzwerk', rssi: -45 }, - { ssid: 'Gast-WLAN', rssi: -67 }, - { ssid: 'Nachbar-WiFi', rssi: -82 } - ]; - - if (networkList) { - demoNetworks.forEach(network => { - const signalIcon = getSignalIcon(network.rssi); - const item = document.createElement('div'); - item.className = 'network-item'; - item.onclick = () => selectNetwork(network.ssid, item); - item.innerHTML = ` - - ${signalIcon} - ${escapeHtml(network.ssid)} - - ${network.rssi} dBm - `; - networkList.appendChild(item); - }); - networkList.style.display = 'block'; + // Nur bei Netzwerkfehlern Demo-Daten anzeigen + if (error instanceof TypeError) { + const demoNetworks = [ + { ssid: 'Demo-Netzwerk', rssi: -45 }, + { ssid: 'Gast-WLAN', rssi: -67 }, + { ssid: 'Nachbar-WiFi', rssi: -82 } + ]; + if (networkList) { + demoNetworks.forEach(network => { + const signalIcon = getSignalIcon(network.rssi); + const item = document.createElement('div'); + item.className = 'network-item'; + item.onclick = () => selectNetwork(network.ssid, item); + item.innerHTML = ` + + ${signalIcon} + ${escapeHtml(network.ssid)} + + ${network.rssi} dBm + `; + networkList.appendChild(item); + }); + networkList.style.display = 'block'; + } + if (select) { + select.innerHTML = ``; + demoNetworks.forEach(network => { + const option = document.createElement('option'); + option.value = network.ssid; + option.textContent = `${network.ssid} (${network.rssi} dBm)`; + select.appendChild(option); + }); + } + showStatus('wifi-status', 'Demo: ' + t('wifi.networks.found', { count: demoNetworks.length }), 'info'); + } else { + showStatus('wifi-status', t('wifi.error.scan') + ': ' + error.message, 'error'); } - - if (select) { - select.innerHTML = ``; - demoNetworks.forEach(network => { - const option = document.createElement('option'); - option.value = network.ssid; - option.textContent = `${network.ssid} (${network.rssi} dBm)`; - select.appendChild(option); - }); - } - - showStatus('wifi-status', 'Demo: ' + t('wifi.networks.found', { count: demoNetworks.length }), 'info'); } }