* fix(wifi): make Connect work from the setup AP
Joining a network from LEDMatrix-Setup has to take the AP down first, which
drops the phone that sent the request. The connect endpoint answered only
after the attempt finished, so the browser never got a reply and the WiFi
tab's Connect button appeared to do nothing.
- /wifi/connect answers 202 immediately while the AP is active and connects
in a background thread; the result (never the password) is reported via
/wifi/status as last_connect_attempt. A second connect while one is
pending gets 409.
- connect_to_network holds a /tmp flag for the attempt; the monitor daemon
skips AP management while it is fresh. Previously the daemon's
disconnected counter, accumulated over the whole AP session, re-enabled
the AP on its next tick in the middle of the connect.
- The WiFi tab and captive setup page explain the handoff up front, and on
reopening show why the last attempt failed. The wrong-password message
now works: the route sets the error_type the captive page checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(wifi): serialize connect attempts on both paths
Addresses CodeRabbit review on #571:
- Check for a pending attempt before branching on AP state. A background
attempt takes the AP down long before it finishes, so a second click
used to bypass the 409 and start a competing synchronous connect.
- Record pending for the synchronous (non-AP) path too, so two requests
can't overlap and have the first clear the daemon's in-progress flag
while the second is still connecting.
- Clear the pending state if the background thread fails to start, rather
than refusing every later request until restart.
- Say the setup network returns "within a few minutes": a stale flag plus
the daemon's grace period can take longer than one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* perf(wifi): one status fetch per monitor tick instead of three
The wifi monitor daemon fetched WiFi status + ethernet state before its
AP-mode check, the check internally fetched the same state again (with
retry), and the daemon fetched a third time afterwards — each fetch is
several nmcli subprocess forks, every 30s, forever, even on a perfectly
healthy link.
check_and_manage_ap_mode's decision logic is extracted to
_manage_ap_mode(status, ethernet, ap_active); the new
check_and_manage_ap_mode_with_state() runs the single (retrying) fetch
battery and returns (changed, status, ethernet, ap_active_after) — the
post-state is derivable because state only ever flips via one enable or
one disable. The original bool-returning method delegates, so existing
callers are untouched. The daemon's dead pre-fetch is removed and its
post-check reads use the returned state; the AP-enable retry semantics
(_get_wifi_status_with_retry) are preserved exactly. ~10-15 forks/30s
drops to ~4-6.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam
* fix(wifi): add missing type hints on AP-mode state helpers, fix unused-var lint in tests
- check_and_manage_ap_mode_with_state now declares its
Tuple[bool, WiFiStatus, bool, bool] return type instead of being untyped.
- _manage_ap_mode's status/ethernet_connected/ap_active parameters are now
typed (WiFiStatus, bool, bool), matching its existing -> bool return hint.
- test_wifi_check_state.py: rename unused unpacked variables (status/
ethernet/ap_after) to underscore-prefixed names at the three call sites
that don't use them, leaving the used ones untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
---------
Co-authored-by: Chuck <chuck@example.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>