From 516b7908a64261bf67fe213e08291b4d6f1d4a2f Mon Sep 17 00:00:00 2001 From: n0mad1k Date: Mon, 6 Apr 2026 08:52:30 -0400 Subject: [PATCH] Add absent_count column and commented-out grace period threshold for tripwire resets --- net_alerter/net_alerter.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/net_alerter/net_alerter.py b/net_alerter/net_alerter.py index 2be0341..da3cce3 100644 --- a/net_alerter/net_alerter.py +++ b/net_alerter/net_alerter.py @@ -17,6 +17,10 @@ from datetime import datetime, timezone from pathlib import Path # --- Config (overridable via env) --- +# Minimum consecutive missed scans before a device is considered gone and the +# tripwire resets. Uncomment to enable — set to 3 for 15-min grace at 5-min cron. +# ABSENT_THRESHOLD = int(os.getenv("NET_ALERTER_ABSENT_THRESHOLD", "3")) + MATRIX_HOMESERVER = os.getenv("MATRIX_HOMESERVER", "https://m.example.org") MATRIX_ACCESS_TOKEN = os.getenv("MATRIX_ACCESS_TOKEN", "") MATRIX_ROOM_ID = os.getenv("MATRIX_ROOM_ID", "!REDACTED:example.org") @@ -50,6 +54,9 @@ def get_db() -> sqlite3.Connection: conn.execute("ALTER TABLE devices ADD COLUMN is_present INTEGER NOT NULL DEFAULT 1") if "last_left" not in existing: conn.execute("ALTER TABLE devices ADD COLUMN last_left TEXT") + # absent_count tracks consecutive missed scans (used by ABSENT_THRESHOLD) + if "absent_count" not in existing: + conn.execute("ALTER TABLE devices ADD COLUMN absent_count INTEGER NOT NULL DEFAULT 0") conn.commit() return conn @@ -231,19 +238,29 @@ def run() -> None: if not baseline_mode: alerts.append(("REJOIN", d)) else: - # Already present — just update last_seen + # Already present — just update last_seen, reset absent_count conn.execute( - "UPDATE devices SET ip=?, name=?, last_seen=? WHERE mac=?", + "UPDATE devices SET ip=?, name=?, last_seen=?, absent_count=0 WHERE mac=?", (d["ip"], d["name"], ts, mac), ) - # Mark devices that were present but didn't appear in this scan as gone + # Mark devices that were present but didn't appear in this scan as gone. + # absent_count increments each missed scan; is_present flips to 0 immediately + # (or after ABSENT_THRESHOLD consecutive misses if that option is enabled). present_in_db = { - row[0] for row in conn.execute("SELECT mac FROM devices WHERE is_present=1") + row[0]: row[1] + for row in conn.execute("SELECT mac, absent_count FROM devices WHERE is_present=1") } - for mac in present_in_db - set(scan_results.keys()): + for mac, absent_count in present_in_db.items(): + if mac in scan_results: + continue + new_count = absent_count + 1 + # Uncomment the block below (and ABSENT_THRESHOLD above) to add a grace period: + # if new_count < ABSENT_THRESHOLD: + # conn.execute("UPDATE devices SET absent_count=? WHERE mac=?", (new_count, mac)) + # continue conn.execute( - "UPDATE devices SET is_present=0, last_left=? WHERE mac=?", + "UPDATE devices SET is_present=0, last_left=?, absent_count=0 WHERE mac=?", (ts, mac), ) print(f"[info] Device left: {mac}", file=sys.stderr)