From 89bbc4fdb84aef36f7c6cf6d66611bcc69d6002c Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Tue, 11 Aug 2026 03:11:13 -0400 Subject: [PATCH] fix(phase26): skip netwatch AP scan when clients connected (BUG-01) --- deploy/yaris_uplink.py | 19 +++++++++++++++++++ static/js/settings.js | 23 +++++++++++++++++++++++ templates/settings.html | 29 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/deploy/yaris_uplink.py b/deploy/yaris_uplink.py index 04f07c7..95fe449 100644 --- a/deploy/yaris_uplink.py +++ b/deploy/yaris_uplink.py @@ -220,9 +220,28 @@ def restore_fallback(): return False +def _ap_client_count(): + """Return number of stations connected to the AP on INTERFACE.""" + try: + proc = subprocess.run( + ["iw", "dev", INTERFACE, "station", "dump"], + capture_output=True, text=True, timeout=5, + ) + return proc.stdout.count("Station ") + except (OSError, subprocess.SubprocessError): + return 0 + + def check_uplink(): current = active_connection() phone_configured = connection_exists(PHONE_CONNECTION) + + # Never tear down the AP if a client is connected — the user is actively + # using the hotspot (e.g. phone browsing the web UI while driving). + # Skip this netwatch cycle and let the next one try again. + if current == AP_CONNECTION and _ap_client_count() > 0: + write_status("ap_has_clients_skipped_check") + return True if current == PHONE_CONNECTION: if internet_reachable(): write_status("phone_connected") diff --git a/static/js/settings.js b/static/js/settings.js index 8d57987..833f19f 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -248,6 +248,29 @@ async function loadUplinkStatus() { const status = await response.json(); if (!response.ok) throw new Error(status.error || `HTTP ${response.status}`); uplinkStatus.textContent = describeUplink(status); + + // In-car access card + const hotspotInfo = document.getElementById('in-car-hotspot-info'); + const wifiInfo = document.getElementById('in-car-wifi-info'); + const disconnectedInfo = document.getElementById('in-car-disconnected-info'); + const hotspotUrl = document.getElementById('hotspot-url'); + const copyBtn = document.getElementById('copy-hotspot-url'); + if (status.mode === 'local_hotspot') { + const ip = status.ip_address || '10.42.0.1'; + hotspotUrl.textContent = `http://${ip}`; + hotspotInfo.hidden = false; + wifiInfo.hidden = true; + disconnectedInfo.hidden = true; + if (copyBtn) copyBtn.onclick = () => navigator.clipboard.writeText(`http://${ip}`); + } else if (status.mode === 'wifi' || status.mode === 'phone') { + hotspotInfo.hidden = true; + wifiInfo.hidden = false; + disconnectedInfo.hidden = true; + } else { + hotspotInfo.hidden = true; + wifiInfo.hidden = true; + disconnectedInfo.hidden = false; + } const phoneAddress = status.mode === 'phone' ? status.ip_address : status.last_transition && status.last_transition.last_phone_ip; diff --git a/templates/settings.html b/templates/settings.html index f962248..c888407 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -134,6 +134,35 @@ + {# ── In-car access ────────────────────────────────────────────────── #} +
+
+

In-car access

+
+
+ + + +
+
+ {# ── Phone hotspot ───────────────────────────────────────────────── #}