Fix ARRIVED spam — mark departing instead of popping from known_devices

When on_departure popped the device from known_devices, rapid RTM_NEWNEIGH
events during ARP cache rebuild each triggered on_arrival as if the device
was brand new, flooding the Matrix room with 5-10+ duplicate ARRIVED alerts.

Fix: keep device in known_devices with departing=True flag. on_arrival
silently cancels pending departure timers and returns without alerting when
it sees this flag. Timer callback checks flag before sending the alert and
only pops the device when departure is confirmed after 15 minutes.
This commit is contained in:
Cobra
2026-04-09 14:34:46 -04:00
parent b66380f25d
commit 71cd3332d2
2 changed files with 87 additions and 1 deletions
+22 -1
View File
@@ -421,6 +421,19 @@ def on_arrival(mac: str, ip: str, hostname: str = "") -> None:
hostname = hostname or ip
now = time.time()
# Silent re-arrival if device marked departing (rapid flap recovery)
with departure_lock:
if mac in known_devices and known_devices[mac].get('departing'):
# Device returned during departure window — cancel timer, clear flag, no alert
if mac in departure_timers:
departure_timers[mac].cancel()
del departure_timers[mac]
with known_lock:
known_devices[mac]['departing'] = False
last_departed_time.pop(mac, None)
logging.debug(f"Device {mac} re-arrived during departure window — suppressed alert")
return
# Check re-arrival suppression BEFORE acquiring lock (avoid nested locks)
with departure_lock:
# ALWAYS cancel any pending departure timer when device re-arrives
@@ -493,7 +506,8 @@ def on_departure(mac: str) -> None:
return
dev_copy = dev.copy()
known_devices.pop(mac)
# Mark departing instead of popping — allows re-arrival to detect and cancel timer
known_devices[mac]['departing'] = True
# Start 15-minute debounce timer
with departure_lock:
@@ -503,6 +517,13 @@ def on_departure(mac: str) -> None:
def send_departure_alert():
"""Callback to send alert after debounce expires."""
# Check if device re-arrived (departing flag cleared by on_arrival)
with known_lock:
if not known_devices.get(mac, {}).get('departing'):
return # device re-arrived, departure was cancelled
# Device still departed — send alert and remove from tracking
known_devices.pop(mac, None)
duration = fmt_duration(time.time() - dev_copy['first_seen'])
msg = format_departure(dev_copy['hostname'], dev_copy['ip'], dev_copy['vendor'] or "unknown", mac, duration)
logging.info(msg)