CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
WifiMenuUi.cpp
Go to the documentation of this file.
1
5
6#include "AppUiInternal.h"
11
12#include <cstdio>
13#include <cstring>
14#include <ctime>
15#include "freertos/FreeRTOS.h"
16#include "freertos/task.h"
17#include "esp_timer.h"
18#include "esp_attr.h"
19
20namespace cdc::ui {
21
22using namespace cdc::ui;
23
25
27static constexpr uint8_t WIFI_MENU_MAX_ITEMS = 16;
28
30struct WifiItem {
31 char ssid[33];
32 int8_t rssi;
34};
35
37
45
55
61
63
64static constexpr uint8_t WIFI_MENU_FIXED_COUNT = WIFI_IDX_COUNT;
65static ListView* s_wifiMainMenu = nullptr;
66static ListView* s_wifiScanView = nullptr;
67static ListView* s_wifiAuthMenu = nullptr;
68static ListView* s_wifiIpMenu = nullptr;
69
72static uint8_t s_wifiModuleCount = 0;
76static char s_wifiManualLabel[48];
78static uint8_t s_wifiScanCount = 0;
79static const char* s_wifiAuthLabels[WIFI_AUTH_COUNT] = {
80 "WPA2", "WPA/WPA2", "WPA3", "WPA", "Open", "WEP"
81};
82
84static constexpr size_t WIFI_DETAILS_BUF_SIZE = 512;
85static EXT_RAM_BSS_ATTR char s_wifiDetailsBuf[WIFI_DETAILS_BUF_SIZE];
86
88static void onWifiMainSelect(uint16_t index, void* userData);
90static void wifiConnect();
92static void wifiSetup();
94static void wifiShowDetails();
96static void wifiDisconnect();
98static void wifiNtpSync();
100static void wifiStartScan();
102static void onWifiScanSelect(uint16_t index, void* userData);
104static void sortWifiScanResults();
106static void wifiShowAuthMenu();
108static void onWifiAuthSelect(uint16_t index, void* userData);
110static void wifiShowPasswordInput();
112static void onWifiPasswordEntered(const char* password);
114static void wifiShowIpModeMenu();
116static void onWifiIpModeSelect(uint16_t index, void* userData);
118static void wifiShowIpInputField(const char* title, char* target, size_t targetSize,
119 T9InputView::SaveCallback onComplete);
121static void onWifiStaticIpEntered(const char* ip);
123static void onWifiGatewayEntered(const char* gateway);
125static void onWifiNetmaskEntered(const char* netmask);
127static void wifiFinishSetup();
128
136static void drawWifiLockIcon(Gdey029T94* gfx, int x, int y, bool inverted) {
137 if (!gfx) return;
138 uint16_t fg = inverted ? EPD_WHITE : EPD_BLACK;
139 uint16_t bg = inverted ? EPD_BLACK : EPD_WHITE;
140
141 gfx->fillRect(x, y + 5, 9, 7, fg);
142 gfx->drawRect(x + 2, y, 5, 6, fg);
143 gfx->fillRect(x + 3, y + 1, 3, 4, bg);
144}
145
159static bool renderWifiRow(Gdey029T94* gfx, const ListItem& item,
160 uint16_t index, int x, int y, int w, int h,
161 bool selected, void* userCtx) {
162 (void)index;
163 (void)h;
164 (void)userCtx;
165 if (!gfx) return true;
166
167 gfx->setFont(nullptr);
168 gfx->setTextSize(1);
169
170 int baseline = y + 6;
171
172 if (item.userData == nullptr) {
173 if (item.label) {
174 gfx->setCursor(x + 22, baseline);
175 render::printText(gfx, item.label);
176 }
177 return true;
178 }
179
180 const auto* net = reinterpret_cast<const WifiItem*>(item.userData);
181
182 drawSignalBars(gfx, x + 4, baseline - 6, net ? net->rssi : -100, selected);
183
184 const char* ssid = item.label ? item.label : "";
185 char ssidDisplay[36];
186 strncpy(ssidDisplay, ssid, sizeof(ssidDisplay) - 1);
187 ssidDisplay[sizeof(ssidDisplay) - 1] = '\0';
188
189 gfx->setCursor(x + 22, baseline);
190 render::printText(gfx, ssidDisplay);
191
192 if (net && net->security != hal::WifiSecurity::OPEN) {
193 drawWifiLockIcon(gfx, x + w - 15, baseline - 5, selected);
194 }
195
196 return true;
197}
198
203 auto* wifi = hal::getWifiControllerInstance();
204 bool connected = wifi && wifi->isConnected();
205 auto& wifiHandlers = WifiHandlers::instance();
206 bool hasConfig = wifiHandlers.config().valid;
207
208 if (connected) {
209 s_wifiMainItems[WIFI_IDX_CONNECT] = {ui::tr("core.wifi_disconnect"), '*', false, nullptr};
210 } else if (hasConfig) {
211 s_wifiMainItems[WIFI_IDX_CONNECT] = {ui::tr("core.wifi_connect"), 0, false, nullptr};
212 } else {
213 s_wifiMainItems[WIFI_IDX_CONNECT] = {ui::tr("core.wifi_no_config"), 0, true, nullptr};
214 }
215
216 s_wifiMainItems[WIFI_IDX_SETUP] = {ui::tr("core.wifi_setup"), 0, false, nullptr};
217 s_wifiMainItems[WIFI_IDX_DETAILS] = {ui::tr("core.wifi_details"), 0, false, nullptr};
218 s_wifiMainItems[WIFI_IDX_NTP_SYNC] = {ui::tr("core.ntp_sync"), 0, !connected && !hasConfig, nullptr};
219
220 auto& moduleReg = core::ModuleRegistry::instance();
221 s_wifiModuleCount = moduleReg.getMenuItems(
225 );
226
227 for (uint8_t i = 0; i < s_wifiModuleCount; i++) {
228 s_wifiMainItems[WIFI_MENU_FIXED_COUNT + i] = {s_wifiModuleItems[i].label, 0, false, nullptr};
229 }
230
231 if (s_wifiMainMenu) {
233 }
234}
235
250
256static void onWifiMainSelect(uint16_t index, void* userData) {
257 (void)userData;
258
259 switch (index) {
260 case WIFI_IDX_CONNECT: {
261 auto* wifi = hal::getWifiControllerInstance();
262 if (wifi && wifi->isConnected()) {
264 } else {
265 wifiConnect();
266 }
267 return;
268 }
269 case WIFI_IDX_SETUP:
270 wifiSetup();
271 return;
272 case WIFI_IDX_DETAILS:
274 return;
276 wifiNtpSync();
277 return;
278 }
279
280 uint8_t moduleIdx = index - WIFI_MENU_FIXED_COUNT;
281 if (moduleIdx < s_wifiModuleCount) {
282 auto& item = s_wifiModuleItems[moduleIdx];
283 if (item.getView) {
284 IView* view = item.getView();
285 if (view) {
287 }
288 }
290 }
291}
292
296static void wifiConnect() {
297 auto& wifiHandlers = WifiHandlers::instance();
298
299 if (!wifiHandlers.config().valid) {
300 showToastError(ui::tr("core.wifi_no_config"));
301 return;
302 }
303
304 char msg[96];
305 snprintf(msg, sizeof(msg), "%s\n%s", ui::tr("core.wifi_connecting"), wifiHandlers.config().ssid);
306 showToastInfo(msg, 0);
307
308 bool connected = wifiHandlers.setUserEnabled(true);
310
311 if (connected) {
312 auto* wifi = hal::getWifiControllerInstance();
313 char ipBuf[20] = {};
314 if (wifi) wifi->getIpAddress(ipBuf, sizeof(ipBuf));
315 snprintf(msg, sizeof(msg), "%s (IP: %s)", ui::tr("core.wifi_connected"), ipBuf);
317 } else {
318 snprintf(msg, sizeof(msg), "%s: %s", ui::tr("core.wifi_failed"), wifiHandlers.getLastError());
320 }
321
323}
324
328static void wifiDisconnect() {
330 showToastInfo(ui::tr("core.wifi_disconnected"));
332}
333
337static void wifiSetup() {
340}
341
345static void sortWifiScanResults() {
346 if (s_wifiScanCount < 2) return;
347 for (uint8_t i = 0; i < s_wifiScanCount - 1; i++) {
348 for (uint8_t j = i + 1; j < s_wifiScanCount; j++) {
349 if (s_wifiScanResults[j].rssi > s_wifiScanResults[i].rssi) {
350 WifiItem temp = s_wifiScanResults[i];
352 s_wifiScanResults[j] = temp;
353 }
354 }
355 }
356}
357
361static void wifiStartScan() {
362 auto* wifi = hal::getWifiControllerInstance();
363 if (!wifi) {
364 showToastError(ui::tr("core.hw_not_available"));
365 return;
366 }
367
368 if (!wifi->isEnabled()) {
369 wifi->enable(hal::WifiMode::STA);
370 }
371
372 showToastInfo(ui::tr("core.wifi_scanning"), 0);
373
374 s_wifiScanCount = 0;
375 if (wifi->startScan()) {
376 uint32_t startMs = esp_timer_get_time() / 1000;
377 while (!wifi->isScanComplete()) {
378 vTaskDelay(pdMS_TO_TICKS(100));
379 if ((esp_timer_get_time() / 1000 - startMs) > WIFI_SCAN_TIMEOUT_MS) break;
380 }
381
383 uint8_t rawCount = wifi->getScanResults(rawResults, WIFI_MAX_NETWORKS);
384
385 for (uint8_t i = 0; i < rawCount && s_wifiScanCount < WIFI_MAX_NETWORKS; i++) {
386 if (rawResults[i].ssid[0] == '\0') continue;
387
388 bool found = false;
389 for (uint8_t j = 0; j < s_wifiScanCount; j++) {
390 if (strcmp(s_wifiScanResults[j].ssid, rawResults[i].ssid) == 0) {
391 if (rawResults[i].rssi > s_wifiScanResults[j].rssi) {
392 s_wifiScanResults[j].rssi = rawResults[i].rssi;
393 s_wifiScanResults[j].security = rawResults[i].security;
394 }
395 found = true;
396 break;
397 }
398 }
399
400 if (!found) {
401 strncpy(s_wifiScanResults[s_wifiScanCount].ssid, rawResults[i].ssid, 32);
402 s_wifiScanResults[s_wifiScanCount].ssid[32] = '\0';
403 s_wifiScanResults[s_wifiScanCount].rssi = rawResults[i].rssi;
404 s_wifiScanResults[s_wifiScanCount].security = rawResults[i].security;
406 }
407 }
408 }
409
411
413
414 if (!s_wifiScanView) {
415 s_wifiScanView = new ListView();
416 s_wifiScanView->setOnSelect(onWifiScanSelect);
417 s_wifiScanView->setItemRenderer(renderWifiRow, nullptr);
418 }
419
420 snprintf(s_wifiManualLabel, sizeof(s_wifiManualLabel), "+ %s", ui::tr("core.wifi_add_manual"));
421 s_wifiScanItems[0] = {s_wifiManualLabel, 0, false, nullptr};
422
423 uint8_t itemCount = s_wifiScanCount + 1;
424 for (uint8_t i = 0; i < s_wifiScanCount; i++) {
425 s_wifiScanItems[i + 1] = {s_wifiScanResults[i].ssid, 0, false, &s_wifiScanResults[i]};
426 }
427
428 s_wifiScanView->init(ui::tr("core.wifi_setup"), s_wifiScanItems, itemCount);
429 s_wifiScanView->setHint(ui::tr("core.hint_select"));
431}
432
438static void onWifiScanSelect(uint16_t index, void* userData) {
439 auto& wizard = WifiHandlers::instance().wizard();
440 const auto* item = reinterpret_cast<const WifiItem*>(userData);
441
442 if (index == 0 || item == nullptr) {
443 wizard.fromScan = false;
444 showT9Input(ui::tr("core.wifi_ssid"), "", [](const char* ssid) {
445 strncpy(WifiHandlers::instance().wizard().ssid, ssid,
446 sizeof(WifiHandlers::instance().wizard().ssid) - 1);
448 }, 32);
449 return;
450 }
451
452 wizard.fromScan = true;
453 strncpy(wizard.ssid, item->ssid, sizeof(wizard.ssid) - 1);
454 wizard.security = item->security;
455
456 if (wizard.security == hal::WifiSecurity::OPEN) {
457 wizard.password[0] = '\0';
459 } else {
461 }
462}
463
467static void wifiShowAuthMenu() {
468 if (!s_wifiAuthMenu) {
469 s_wifiAuthMenu = new ListView();
470 s_wifiAuthMenu->setOnSelect(onWifiAuthSelect);
471
472 for (uint8_t i = 0; i < WIFI_AUTH_COUNT; i++) {
473 s_wifiAuthItems[i] = {s_wifiAuthLabels[i], 0, false, nullptr};
474 }
475 }
476
477 s_wifiAuthMenu->init(ui::tr("core.wifi_encryption"), s_wifiAuthItems, WIFI_AUTH_COUNT);
479}
480
486static void onWifiAuthSelect(uint16_t index, void* userData) {
487 (void)userData;
488 auto& wizard = WifiHandlers::instance().wizard();
489
490 switch (index) {
491 case WIFI_AUTH_WPA2: wizard.security = hal::WifiSecurity::WPA2_PSK; break;
492 case WIFI_AUTH_WPA_WPA2: wizard.security = hal::WifiSecurity::WPA2_PSK; break;
493 case WIFI_AUTH_WPA3: wizard.security = hal::WifiSecurity::WPA3_PSK; break;
494 case WIFI_AUTH_WPA: wizard.security = hal::WifiSecurity::WPA_PSK; break;
495 case WIFI_AUTH_OPEN: wizard.security = hal::WifiSecurity::OPEN; break;
496 case WIFI_AUTH_WEP: wizard.security = hal::WifiSecurity::WEP; break;
497 }
498
499 if (wizard.security == hal::WifiSecurity::OPEN) {
500 wizard.password[0] = '\0';
502 } else {
504 }
505}
506
511 showT9Input(ui::tr("core.wifi_password"), "", onWifiPasswordEntered, 64);
512}
513
518static void onWifiPasswordEntered(const char* password) {
519 auto& wizard = WifiHandlers::instance().wizard();
520 strncpy(wizard.password, password, sizeof(wizard.password) - 1);
522}
523
527static void wifiShowIpModeMenu() {
528 if (!s_wifiIpMenu) {
529 s_wifiIpMenu = new ListView();
530 s_wifiIpMenu->setOnSelect(onWifiIpModeSelect);
531 }
532
533 s_wifiIpItems[WIFI_IP_DHCP] = {ui::tr("core.wifi_dhcp"), 0, false, nullptr};
534 s_wifiIpItems[WIFI_IP_STATIC] = {ui::tr("core.wifi_static"), 0, false, nullptr};
535
536 s_wifiIpMenu->init(ui::tr("core.wifi_ip_mode"), s_wifiIpItems, WIFI_IP_COUNT);
538}
539
545static void onWifiIpModeSelect(uint16_t index, void* userData) {
546 (void)userData;
547 auto& wizard = WifiHandlers::instance().wizard();
548
549 wizard.useDhcp = (index == WIFI_IP_DHCP);
550
551 if (wizard.useDhcp) {
553 } else {
554 wifiShowIpInputField("IP", wizard.staticIp, sizeof(wizard.staticIp), onWifiStaticIpEntered);
555 }
556}
557
565static void wifiShowIpInputField(const char* title, char* target, size_t targetSize,
566 T9InputView::SaveCallback onComplete) {
567 (void)targetSize;
568 showT9Input(title, target, onComplete, 15);
569}
570
575static void onWifiStaticIpEntered(const char* ip) {
576 auto& wizard = WifiHandlers::instance().wizard();
579 wifiShowIpInputField("IP", wizard.staticIp, sizeof(wizard.staticIp), onWifiStaticIpEntered);
580 return;
581 }
582 strncpy(wizard.staticIp, ip, sizeof(wizard.staticIp) - 1);
583 wifiShowIpInputField(ui::tr("core.wifi_gateway"), wizard.gateway, sizeof(wizard.gateway), onWifiGatewayEntered);
584}
585
590static void onWifiGatewayEntered(const char* gateway) {
591 auto& wizard = WifiHandlers::instance().wizard();
592 if (!WifiHandlers::isValidIpAddress(gateway)) {
594 wifiShowIpInputField(ui::tr("core.wifi_gateway"), wizard.gateway, sizeof(wizard.gateway), onWifiGatewayEntered);
595 return;
596 }
597 strncpy(wizard.gateway, gateway, sizeof(wizard.gateway) - 1);
598 wifiShowIpInputField(ui::tr("core.wifi_netmask"), wizard.netmask, sizeof(wizard.netmask), onWifiNetmaskEntered);
599}
600
605static void onWifiNetmaskEntered(const char* netmask) {
606 auto& wizard = WifiHandlers::instance().wizard();
607 if (!WifiHandlers::isValidIpAddress(netmask)) {
609 wifiShowIpInputField(ui::tr("core.wifi_netmask"), wizard.netmask, sizeof(wizard.netmask), onWifiNetmaskEntered);
610 return;
611 }
612 strncpy(wizard.netmask, netmask, sizeof(wizard.netmask) - 1);
614}
615
619static void wifiFinishSetup() {
621
622 while (ViewStack::instance().current() != s_wifiMainMenu &&
623 ViewStack::instance().depth() > 1) {
625 }
626
627 wifiConnect();
628}
629
633static void wifiShowDetails() {
634 auto* wifi = hal::getWifiControllerInstance();
635 auto& wifiHandlers = WifiHandlers::instance();
636 char* info = s_wifiDetailsBuf;
637 memset(info, 0, WIFI_DETAILS_BUF_SIZE);
638 size_t pos = 0;
639
640 auto append = [&](const char* fmt, ...) {
641 if (pos >= WIFI_DETAILS_BUF_SIZE) return;
642 va_list args;
643 va_start(args, fmt);
644 int written = vsnprintf(info + pos, WIFI_DETAILS_BUF_SIZE - pos, fmt, args);
645 va_end(args);
646 if (written > 0) {
647 size_t w = static_cast<size_t>(written);
648 pos += (w < (WIFI_DETAILS_BUF_SIZE - pos)) ? w : (WIFI_DETAILS_BUF_SIZE - pos - 1);
649 }
650 };
651
652 if (wifi && wifi->isConnected()) {
653 char ipBuf[20] = {};
654 uint8_t mac[6] = {};
655
656 append("Status: %s\n\n", ui::tr("core.wifi_connected"));
657 append("SSID: %s\n", wifi->getCurrentSsid());
658
659 if (wifi->getMacAddress(mac)) {
660 append("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
661 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
662 }
663
664 if (wifi->getIpAddress(ipBuf, sizeof(ipBuf))) {
665 append("IP: %s\n", ipBuf);
666 }
667
668 append("%s: %d dBm\n", ui::tr("core.wifi_signal"), wifi->getRssi());
669
670 } else if (wifiHandlers.config().valid) {
671 append("Status: %s\n\n", ui::tr("core.wifi_disconnected"));
672 append("=== %s ===\n", ui::tr("core.wifi_saved_config"));
673 append("SSID: %s\n", wifiHandlers.config().ssid);
674 append("IP: %s\n", wifiHandlers.config().useDhcp ? "DHCP" : "Static");
675 } else {
676 append("%s", ui::tr("core.wifi_no_config"));
677 }
678
679 showInfo(ui::tr("core.wifi_details"), info);
680}
681
685static void wifiNtpSync() {
686 auto& wifiHandlers = WifiHandlers::instance();
687
688 if (!wifiHandlers.config().valid && !wifiHandlers.isConnected()) {
689 showToastError(ui::tr("core.wifi_no_config"));
690 return;
691 }
692
693 bool wasConnected = wifiHandlers.isConnected();
694
695 if (!wasConnected) {
696 showToastTask(ui::tr("core.wifi_connecting"), 0);
697 if (!wifiHandlers.setUserEnabled(true)) {
699 showToastError(ui::tr("core.wifi_failed"));
700 return;
701 }
703 }
704
705 showToastTask(ui::tr("core.ntp_syncing"), 0);
706
707 bool synced = wifiHandlers.syncNtp();
709
710 if (synced) {
711 showToastSuccess(ui::tr("core.ntp_success"));
712 } else {
713 showToastError(ui::tr("core.ntp_timeout"));
714 }
715}
716
717} // namespace cdc::ui
static ModuleRegistry & instance()
Returns the singleton module registry instance.
static constexpr uint8_t MAX_SCAN_RESULTS
void(*)(const char *text) SaveCallback
Definition T9InputView.h:29
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:34
void push(IView *view, void *context=nullptr)
bool setUserEnabled(bool enabled)
Sets the user/system WiFi intent and applies it immediately.
static bool isValidIpAddress(const char *ip)
Validates dotted IPv4 address string.
WifiWizard & wizard()
void saveConfig()
Saves current wizard Wi-Fi configuration to NVS.
static WifiHandlers & instance()
Returns singleton Wi-Fi handlers instance.
void loadConfig()
Loads Wi-Fi configuration from NVS.
IWifiController * getWifiControllerInstance()
Returns the singleton Wi-Fi controller service instance.
void printText(Gdey029T94 *gfx, const char *text)
Draws CP437 text with the built-in 6x8 glyph font, byte-for-byte.
Centralized key-code constants for cdc_views.
Definition IModule.h:8
const char * tr(const char *key)
Look up a translation by string key.
Definition I18n.h:208
static void wifiShowIpInputField(const char *title, char *target, size_t targetSize, T9InputView::SaveCallback onComplete)
Opens generic IP-related text input step.
static void onWifiMainSelect(uint16_t index, void *userData)
Handles top-level Wi-Fi menu selection.
static ListView * s_wifiScanView
InfoView * showInfo(const char *title, const char *text, const char *hint=nullptr)
Shows a shared info view instance and pushes it onto the view stack.
Definition InfoView.cpp:310
static constexpr uint8_t WIFI_MENU_FIXED_COUNT
Static view pointers and wizard/scan state.
static constexpr uint32_t TOAST_DURATION_LONG_MS
static void onWifiIpModeSelect(uint16_t index, void *userData)
Handles IP mode selection.
static bool renderWifiRow(Gdey029T94 *gfx, const ListItem &item, uint16_t index, int x, int y, int w, int h, bool selected, void *userCtx)
Custom list-row renderer for Wi-Fi scan entries.
static void wifiShowAuthMenu()
Opens security/authentication selection menu.
static void wifiSetup()
Starts Wi-Fi setup wizard flow.
static ListItem s_wifiAuthItems[WIFI_AUTH_COUNT]
static ListView * s_wifiMainMenu
void showToastTask(const char *message, uint16_t durationMs=0)
Shows a task/progress toast message.
static const char * s_wifiAuthLabels[WIFI_AUTH_COUNT]
static ListItem s_wifiScanItems[WIFI_MAX_NETWORKS+1]
static constexpr uint32_t TOAST_DURATION_MEDIUM_MS
static void wifiShowPasswordInput()
Opens password input for selected network.
static constexpr size_t WIFI_DETAILS_BUF_SIZE
PSRAM-backed text buffer used for Wi-Fi details view.
static constexpr uint32_t WIFI_SCAN_TIMEOUT_MS
static ListView * s_wifiAuthMenu
static void wifiConnect()
Connects using saved Wi-Fi configuration.
static constexpr uint8_t WIFI_MAX_NETWORKS
Wi-Fi menu size limits.
static void wifiFinishSetup()
Persists wizard config and attempts connection.
@ WIFI_AUTH_WPA2
@ WIFI_AUTH_WEP
@ WIFI_AUTH_OPEN
@ WIFI_AUTH_WPA3
@ WIFI_AUTH_WPA
@ WIFI_AUTH_WPA_WPA2
@ WIFI_AUTH_COUNT
static WifiItem s_wifiScanResults[WIFI_MAX_NETWORKS]
static void onWifiGatewayEntered(const char *gateway)
Validates and stores gateway input.
@ WIFI_IP_COUNT
@ WIFI_IP_STATIC
T9InputView * showT9Input(const char *title, const char *initialText, T9InputView::SaveCallback onSave, uint16_t maxLen=128)
Shows a shared T9 input view instance.
void showWifiMainMenu()
Shows top-level Wi-Fi menu and reloads stored configuration.
static void drawWifiLockIcon(Gdey029T94 *gfx, int x, int y, bool inverted)
Draws padlock icon for secured Wi-Fi networks.
static core::ModuleMenuItem s_wifiModuleItems[12]
static void onWifiNetmaskEntered(const char *netmask)
Validates and stores netmask input.
void drawSignalBars(Gdey029T94 *gfx, int x, int y, int8_t rssi, bool inverted)
Draws RSSI signal bars using the shared lock-screen visual style.
Definition AppUi.cpp:199
void showToastSuccess(const char *message, uint16_t durationMs=1500)
Shows a success toast message.
static ListItem s_wifiIpItems[WIFI_IP_COUNT]
static char s_wifiManualLabel[48]
static constexpr uint8_t WIFI_MENU_MAX_ITEMS
static void onWifiStaticIpEntered(const char *ip)
Validates and stores static IP input.
static void onWifiPasswordEntered(const char *password)
Stores entered password and continues wizard.
static ListItem s_wifiMainItems[WIFI_MENU_MAX_ITEMS]
static void wifiDisconnect()
Disconnects active Wi-Fi session.
static uint8_t s_wifiModuleCount
static uint8_t s_wifiScanCount
WifiMainMenuIdx
Fixed Wi-Fi menu and wizard index enums.
@ WIFI_IDX_NTP_SYNC
@ WIFI_IDX_DETAILS
@ WIFI_IDX_SETUP
@ WIFI_IDX_COUNT
@ WIFI_IDX_CONNECT
static void sortWifiScanResults()
Sorts scan results by RSSI descending.
static void onWifiAuthSelect(uint16_t index, void *userData)
Handles authentication mode selection.
static void onWifiScanSelect(uint16_t index, void *userData)
Handles scan result selection.
static char s_wifiDetailsBuf[WIFI_DETAILS_BUF_SIZE]
void showToastInfo(const char *message, uint16_t durationMs=1500)
Shows an informational toast message.
void rebuildWifiMainMenu()
Rebuilds top-level Wi-Fi menu items and dynamic module extensions.
void showToastError(const char *message, uint16_t durationMs=1500)
Shows an error toast message.
static void wifiShowDetails()
Shows Wi-Fi details/info screen.
static void wifiStartScan()
Starts network scan and opens scan result list.
static void wifiNtpSync()
Runs NTP synchronization via Wi-Fi.
static void wifiShowIpModeMenu()
Opens IP mode selection menu (DHCP/static).
static ListView * s_wifiIpMenu
Menu item registered by a module.
Definition IModule.h:29
const char * label
Definition ListView.h:16
One scanned Wi-Fi network entry displayed in the scan list.
hal::WifiSecurity security
void reset()
Resets Wi-Fi wizard state to defaults.