From 88bc36af616363993b0a58b3d249411f802140b0 Mon Sep 17 00:00:00 2001 From: Cobra Date: Tue, 14 Apr 2026 15:52:26 -0400 Subject: [PATCH] Fix departure detection for AP ARP-proxy networks (passive-only) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARP proxy on WiFi APs causes the kernel neighbor table to show devices as REACHABLE even after they disconnect. This made last_seen refresh continuously from on_arrival() (netlink path), preventing the watchdog from ever detecting departure. Two fixes: 1. Remove _update_last_seen() from on_arrival() — netlink events are not reliable as a genuine-traffic source on ARP-proxy networks. last_seen is now only updated by the raw packet capture path (ARP requests, mDNS, DHCP) where src_mac must be the device itself. 2. Watchdog now covers all is_personal_device() entries in known_devices, not just the personal_devices enrollment set. device_labels entries (like named phones) were previously unmonitored by the watchdog. --- net_alerter/net_alerter.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/net_alerter/net_alerter.py b/net_alerter/net_alerter.py index e594f84..c41a4b5 100644 --- a/net_alerter/net_alerter.py +++ b/net_alerter/net_alerter.py @@ -953,9 +953,6 @@ def update_occupancy_state() -> None: def on_arrival(mac: str, ip: str, hostname: str = "") -> None: """Handle device arrival.""" - # Update passive observation tracking - _update_last_seen(mac) - # Infrastructure IPs never trigger alerts if ip in infrastructure_ips: logging.debug(f"Ignoring infrastructure IP {ip} (MAC:{mac})") @@ -1758,10 +1755,10 @@ def personal_watchdog() -> None: time.sleep(60) # Check every minute now = time.time() - # Get current set of personal devices without holding lock for long - with personal_lock: - tracked = set(personal_devices) - + # Track all personal devices currently in known_devices (not just enrolled set) + with known_lock: + tracked = {mac for mac in known_devices if is_personal_device(mac)} + for mac in tracked: # Get last_seen timestamp with last_seen_lock: