Fix arrival suppression when device returns during departure debounce
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.
This commit is contained in:
@@ -985,15 +985,23 @@ def on_arrival(mac: str, ip: str, hostname: str = "") -> None:
|
|||||||
|
|
||||||
with known_lock:
|
with known_lock:
|
||||||
with departure_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'):
|
if mac in known_devices and known_devices[mac].get('departing'):
|
||||||
# Device returned during departure window — cancel timer, clear flag, no alert
|
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
|
known_devices[mac]['departing'] = False
|
||||||
with occupancy_lock:
|
with occupancy_lock:
|
||||||
_update_occupancy_state_unlocked()
|
_update_occupancy_state_unlocked()
|
||||||
last_departed_time.pop(mac, None)
|
last_departed_time.pop(mac, None)
|
||||||
logging.debug(f"Device {mac} re-arrived during departure window — suppressed alert")
|
logging.debug(f"Device {mac} rapid-flap suppressed ({int(absence_secs)}s)")
|
||||||
return
|
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
|
# Log if we cancelled a timer
|
||||||
if timers_to_cancel:
|
if timers_to_cancel:
|
||||||
@@ -1079,6 +1087,7 @@ def on_departure(mac: str) -> None:
|
|||||||
dev_copy = dev.copy()
|
dev_copy = dev.copy()
|
||||||
# Mark departing instead of popping — allows re-arrival to detect and cancel timer
|
# Mark departing instead of popping — allows re-arrival to detect and cancel timer
|
||||||
known_devices[mac]['departing'] = True
|
known_devices[mac]['departing'] = True
|
||||||
|
known_devices[mac]['depart_initiated'] = time.time()
|
||||||
|
|
||||||
# Start 15-minute debounce timer
|
# Start 15-minute debounce timer
|
||||||
# Extract timer to cancel outside of lock to avoid deadlock
|
# Extract timer to cancel outside of lock to avoid deadlock
|
||||||
|
|||||||
Reference in New Issue
Block a user