From 047635c30460a8638dc45c7acf5a4be371180047 Mon Sep 17 00:00:00 2001 From: Cobra Date: Wed, 15 Apr 2026 15:25:32 -0400 Subject: [PATCH] Fix arrival/departure gates dropping LAA MACs; halve ARP scan interval The phone/wearable gate used a manual OUI check that missed privately-addressed MACs (iOS 14+/Android 10+ randomize per network). Any phone not pre-configured in device_labels was silently enrolled with no alert, causing ARRIVED/OCCUPIED to never fire. Replacing the gate with is_personal_device() catches OUI, labels, and LAA bit uniformly. ARP scan interval drops from 30s to 15s so worst-case arrival detection is 15s. --- net_alerter/net_alerter.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/net_alerter/net_alerter.py b/net_alerter/net_alerter.py index 533afc9..488e2da 100644 --- a/net_alerter/net_alerter.py +++ b/net_alerter/net_alerter.py @@ -32,7 +32,7 @@ OUI_DB_PATH = "/opt/net_alerter/oui.db" ENV_PATH = Path(os.getenv("NET_ALERTER_ENV", "/opt/net_alerter/.env")) BLE_NAMES_FILE = Path("/opt/net_alerter/ble_names.json") BLE_CORRELATION_WINDOW = 60 # seconds — how far back to look for nearby BLE devices -ARP_SCAN_INTERVAL = int(os.getenv("NET_ALERTER_ARP_SCAN_INTERVAL", "30")) +ARP_SCAN_INTERVAL = int(os.getenv("NET_ALERTER_ARP_SCAN_INTERVAL", "15")) known_devices = {} # {mac: {ip, hostname, vendor, first_seen, last_seen, infrastructure (opt)}} known_lock = threading.Lock() @@ -1111,12 +1111,9 @@ def on_arrival(mac: str, ip: str, hostname: str = "") -> None: 'last_seen': now } - # Only alert for phones/wearables (MOBILE_DEVICE_OUIS) or explicitly labeled devices. - # Everything else is enrolled silently. - oui = mac.replace(":", "").upper()[:6] - normalized = _normalize_mac(mac) - if oui not in MOBILE_DEVICE_OUIS and normalized not in device_labels: - logging.debug(f"Silent enroll {mac} — not a phone/wearable OUI and no label") + # Only alert for phones/wearables. Everything else is enrolled silently. + if not is_personal_device(mac): + logging.debug(f"Silent enroll {mac} — not a phone/wearable") update_occupancy_state() return @@ -1156,11 +1153,10 @@ def on_departure(mac: str) -> None: logging.debug(f"Ignoring departure for infrastructure IP {dev['ip']} (MAC:{mac})") return - # Only alert departure for phones/wearables or labeled devices - oui = mac.replace(":", "").upper()[:6] - if oui not in MOBILE_DEVICE_OUIS and _normalize_mac(mac) not in device_labels: + # Only alert departure for phones/wearables + if not is_personal_device(mac): known_devices.pop(mac, None) - logging.debug(f"Silent depart {mac} — not a phone/wearable OUI and no label") + logging.debug(f"Silent depart {mac} — not a phone/wearable") return # Check if this is part of an interface flap (suppress simultaneous mass-departure)