diff --git a/src/main.py b/src/main.py index 7d3c357..0da9310 100644 --- a/src/main.py +++ b/src/main.py @@ -22,6 +22,8 @@ class Supervisor(threading.Thread): self.rtc = rtc self._stop_event = threading.Event() 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: self._stop_event.set() @@ -32,17 +34,26 @@ class Supervisor(threading.Thread): self.nm.refresh_state() snapshot = self.state.snapshot() - should_have_ap = not (snapshot["wifi_connected"] and snapshot["internet_available"]) 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: - LOG.warning("No active internet, enabling AP fallback") + if wifi_connected: + 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() - 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: ok, msg = self.rtc.sync_ntp_and_rtc() if ok: diff --git a/src/network_manager.py b/src/network_manager.py index d026d27..e212ddb 100644 --- a/src/network_manager.py +++ b/src/network_manager.py @@ -91,16 +91,14 @@ class NetworkManager: return False if not self.has_default_route(): return False - try: - with socket.create_connection(("1.1.1.1", 53), timeout=2): - pass - except OSError: - return False - try: - socket.gethostbyname("pool.ntp.org") - except OSError: - return False - return True + # 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: + with socket.create_connection(target, timeout=2): + return True + except OSError: + continue + return False def _service_action(self, action: str, unit: str) -> bool: return self._run_quiet(["systemctl", action, unit], timeout=20)