Stabilize Wi-Fi by preventing AP fallback flapping
This commit is contained in:
25
src/main.py
25
src/main.py
@@ -22,6 +22,8 @@ class Supervisor(threading.Thread):
|
|||||||
self.rtc = rtc
|
self.rtc = rtc
|
||||||
self._stop_event = threading.Event()
|
self._stop_event = threading.Event()
|
||||||
self._ntp_synced = False
|
self._ntp_synced = False
|
||||||
|
self._wifi_down_streak = 0
|
||||||
|
self._ap_enable_after_cycles = 4 # 4 * 15s = 60s sustained Wi-Fi down
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self._stop_event.set()
|
self._stop_event.set()
|
||||||
@@ -32,17 +34,26 @@ class Supervisor(threading.Thread):
|
|||||||
self.nm.refresh_state()
|
self.nm.refresh_state()
|
||||||
snapshot = self.state.snapshot()
|
snapshot = self.state.snapshot()
|
||||||
|
|
||||||
should_have_ap = not (snapshot["wifi_connected"] and snapshot["internet_available"])
|
|
||||||
in_grace = time.time() < self.state.ap_grace_until
|
in_grace = time.time() < self.state.ap_grace_until
|
||||||
|
wifi_connected = bool(snapshot["wifi_connected"])
|
||||||
|
|
||||||
if should_have_ap and not snapshot["ap_mode"] and not snapshot["connecting"] and not in_grace:
|
if wifi_connected:
|
||||||
LOG.warning("No active internet, enabling AP fallback")
|
self._wifi_down_streak = 0
|
||||||
|
else:
|
||||||
|
self._wifi_down_streak += 1
|
||||||
|
|
||||||
|
if (
|
||||||
|
self._wifi_down_streak >= self._ap_enable_after_cycles
|
||||||
|
and not snapshot["ap_mode"]
|
||||||
|
and not snapshot["connecting"]
|
||||||
|
and not in_grace
|
||||||
|
):
|
||||||
|
LOG.warning(
|
||||||
|
"Wi-Fi disconnected for %ss, enabling AP fallback",
|
||||||
|
self._wifi_down_streak * 15,
|
||||||
|
)
|
||||||
self.nm.start_ap()
|
self.nm.start_ap()
|
||||||
|
|
||||||
if not should_have_ap and snapshot["ap_mode"]:
|
|
||||||
LOG.info("Internet restored, disabling AP")
|
|
||||||
self.nm.stop_ap()
|
|
||||||
|
|
||||||
if snapshot["internet_available"] and not self._ntp_synced:
|
if snapshot["internet_available"] and not self._ntp_synced:
|
||||||
ok, msg = self.rtc.sync_ntp_and_rtc()
|
ok, msg = self.rtc.sync_ntp_and_rtc()
|
||||||
if ok:
|
if ok:
|
||||||
|
|||||||
@@ -91,16 +91,14 @@ class NetworkManager:
|
|||||||
return False
|
return False
|
||||||
if not self.has_default_route():
|
if not self.has_default_route():
|
||||||
return False
|
return False
|
||||||
|
# Avoid DNS lookups here; transient resolver issues should not trigger AP mode flapping.
|
||||||
|
for target in (("1.1.1.1", 53), ("8.8.8.8", 53)):
|
||||||
try:
|
try:
|
||||||
with socket.create_connection(("1.1.1.1", 53), timeout=2):
|
with socket.create_connection(target, timeout=2):
|
||||||
pass
|
|
||||||
except OSError:
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
socket.gethostbyname("pool.ntp.org")
|
|
||||||
except OSError:
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
return False
|
||||||
|
|
||||||
def _service_action(self, action: str, unit: str) -> bool:
|
def _service_action(self, action: str, unit: str) -> bool:
|
||||||
return self._run_quiet(["systemctl", action, unit], timeout=20)
|
return self._run_quiet(["systemctl", action, unit], timeout=20)
|
||||||
|
|||||||
Reference in New Issue
Block a user