From 319f11c1dc4a3b6852b5623187bf4a2dc912bab7 Mon Sep 17 00:00:00 2001 From: Cobra Date: Tue, 14 Apr 2026 19:49:33 -0400 Subject: [PATCH] Fix arrival suppression when device returns during departure debounce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Watchdog-triggered departures set departing=True after 15 min of silence. If the device returned during the 15-min debounce window, on_arrival() treated it as a rapid flap and suppressed the ARRIVED alert — even though the device had been genuinely absent for 15+ minutes. Store depart_initiated timestamp when setting departing=True. On re-arrival, check the gap: < 120s = true rapid flap (transient Netlink/DHCP event), suppress as before. >= 120s = real absence confirmed by watchdog, pop the device from known_devices and let it fall through to the new-device path so the ARRIVED alert fires. Also removes debug traceback instrumentation left in _update_last_seen from the prior investigation session. --- net_alerter/net_alerter.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/net_alerter/net_alerter.py b/net_alerter/net_alerter.py index 89a590d..9c819e6 100644 --- a/net_alerter/net_alerter.py +++ b/net_alerter/net_alerter.py @@ -985,15 +985,23 @@ def on_arrival(mac: str, ip: str, hostname: str = "") -> None: with known_lock: with departure_lock: - # Check if device marked departing (rapid flap recovery) + # Check if device marked departing if mac in known_devices and known_devices[mac].get('departing'): - # Device returned during departure window — cancel timer, clear flag, no alert - known_devices[mac]['departing'] = False - with occupancy_lock: - _update_occupancy_state_unlocked() - last_departed_time.pop(mac, None) - logging.debug(f"Device {mac} re-arrived during departure window — suppressed alert") - return + depart_initiated = known_devices[mac].get('depart_initiated', now) + absence_secs = now - depart_initiated + if absence_secs < 120: + # True rapid flap (transient Netlink/DHCP event) — suppress + known_devices[mac]['departing'] = False + with occupancy_lock: + _update_occupancy_state_unlocked() + last_departed_time.pop(mac, None) + logging.debug(f"Device {mac} rapid-flap suppressed ({int(absence_secs)}s)") + return + else: + # Watchdog-confirmed absence — real re-arrival, let it alert + logging.debug(f"Device {mac} returning after {int(absence_secs)}s absence — treating as arrival") + known_devices.pop(mac) + last_departed_time.pop(mac, None) # Log if we cancelled a timer if timers_to_cancel: @@ -1079,6 +1087,7 @@ def on_departure(mac: str) -> None: dev_copy = dev.copy() # Mark departing instead of popping — allows re-arrival to detect and cancel timer known_devices[mac]['departing'] = True + known_devices[mac]['depart_initiated'] = time.time() # Start 15-minute debounce timer # Extract timer to cancel outside of lock to avoid deadlock