Presence-based Intruder Alarm

Auto-arming home alarm for the telep-mainframe stack: when no trusted phone is on the network, arm; a person or car seen by Frigate then escalates to an urgent Telegram alert. Disarms the instant a trusted phone reappears. See also SESSION-HANDOVER, telep-router.

Decisions (locked with user)

  • Alert = urgent Telegram bot message (bots can’t voice-call). Snapshot + ⚠️ BETÖRŐ, notifying. Repeat 1× day / 2× night (night = 20:00–08:00), ~45s spacing, stop on disarm.
  • Escalate on person OR car while armed. Existing frigate-notify normal alerts keep running always; this is escalation on top.
  • Arm after ALL trusted devices absent for 10 min. Disarm instantly on any trusted device reappearing.
  • Enrollment = confirm each via Telegram. New device → inline Trust ✅ / Ignore ❌ prompt → added only on Trust.
  • Trusted list starts EMPTY; only devices that newly join get enrolled. Alarm is dormant until ≥1 device enrolled (no baseline = no arming).
  • Infrastructure excluded from prompts: mainframe 192.168.1.123, printer 192.168.1.126. Cameras are off-VLAN (telep-cc), never on this LAN.
  • Arm/disarm Telegram messages name the person (the trusted device that triggered the transition).

Architecture

New systemd service intruder-alarm on telep-mainframe (python3, stdlib + requests or urllib). Three concurrent loops in one process:

  1. Presence poller (~30s) — reads WiFi associations from telep-router.
  2. Telegram receivergetUpdates long-poll for enrollment confirmations + commands.
  3. State machine + intruder watcher — arm/disarm logic; when armed, watches Frigate events.
router (WiFi assoc) --ssh--> presence poller --+
                                               +--> state machine --> Telegram (arm/disarm, intruder)
Frigate /api/events --------> intruder watcher-+          ^
Telegram getUpdates --------> receiver --------> trusted list / commands

Prerequisite: mainframe → router access

Presence must come from WiFi association (phones sleep, so ping is unreliable). The service SSHes to root@telep-router and runs iwinfo <ap> assoclist for phy0-ap0 phy1-ap0 phy1-ap1, cross-referencing /tmp/dhcp.leases for name/ip.

  • Set up a dedicated mainframe→router SSH key (generate on mainframe, add to router authorized_keys, ideally command-restricted). This is a new trust link to document.

Presence poller

  • Every ~30s: union of associated MACs across the 3 APs = “present now”.
  • A device is present if seen associated within the last ~2 polls (grace for one missed poll / transient SSH failure — never flip state on a single failed poll).
  • Emits the present-MAC set to the state machine and the enrollment logic.

Enrollment (confirm-each)

  • A present MAC that is not trusted, not excluded, not already pending/ignored → send a Telegram message with inline keyboard: Trust ✅ (trust:<mac>) / Ignore ❌ (ignore:<mac>), body Új eszköz: <name> – <ip> – <mac> (name = DHCP hostname, else (ismeretlen)).
  • Prompt once per MAC (don’t re-prompt every 30s); re-prompt allowed after ~6h if still unanswered.
  • On Trust callback → add to trusted.json, edit message to trusted phone added: <name> – <ip> – <mac> ✅.
  • On Ignore → add to ignore list (no re-prompt).
  • /untrust <name> removes a trusted device; /status prints armed state + trusted list; optional /arm /disarm manual override (see Open questions).

State machine

  • trusted_present = trusted ∩ present.
  • len(trusted) == 0dormant (never arm, no intruder watch).
  • trusted_present > 0disarmed; record last_seen per device.
  • trusted_present == 0 → start/continue an “all-gone” timer; at 10 minarmed.
  • Any trusted device reappearing → disarmed instantly.
  • Transitions post Telegram, naming the trigger device:
    • Arm: 🔴 ÉLES — <name> telefonja lecsatlakozott (name = last trusted device to leave).
    • Disarm: 🟢 KIKAPCSOLVA — <name> telefonja csatlakozott (name = first trusted device to arrive).

Intruder watcher (only while armed)

  • Poll Frigate GET /api/events?after=<armed_ts>&labels=person,car&has_snapshot=1 (~every 10–15s, mirroring frigate-notify).
  • For each new event id since armed → escalate: send group message with ⚠️ BETÖRŐ: <person|car> – <time> + snapshot from /api/events/<id>/snapshot.jpg?bbox=1.
  • Repeat the alert 1× (day) or 2× (20:00–08:00) at ~45s spacing; cancel remaining repeats if disarmed mid-way.
  • De-dup by event id; cooldown ~60s so a burst of events doesn’t spam.

Persistence (/srv/frigate/alarm/)

  • trusted.json{mac: {name, ip, added_ts}}.
  • ignored.json — MACs to never prompt.
  • state.json{armed, all_gone_since, last_update, telegram_offset} (survive restarts; on boot, re-derive from live presence rather than blindly trusting stale armed state).

Config

Reuse existing secrets: bot token from ~/telep/telegram-bot.env, group chat -1004475187307 (same “Dezsi az őr” bot). Config file for: exclude MACs, arm_delay=600s, night 20:00–08:00, repeat counts, poll intervals, Frigate URL http://127.0.0.1:5000, router ssh target.

  • Note: this service becomes the sole getUpdates consumer for the bot (frigate-notify only sends, so no conflict — verify at build time).

Edge cases / risks

  • Randomized MAC rotation: if a phone rotates its per-network MAC it looks like a new device → re-prompt. iOS “Rotate” is off by default (stable per-SSID), so usually fine; note for the user.
  • Router SSH failure / missed poll: grace window prevents a false disarm→arm flip; log and retry.
  • Bootstrapping while away: user’s phone joins → Trust prompt → enrolled → arm/disarm active from then.
  • Bot can’t force-ring muted chats: “urgent” = a notifying message; truly silencing is the user’s Telegram mute. (Already acknowledged.)
  • Security model: WiFi password is the trust boundary; every enrollment needs the user’s Telegram OK; infra excluded; dormant until enrolled — so no spurious intruder blasts before setup.

Open questions (defaults chosen; override if wanted)

  • Manual /arm /disarm override — include? (Default: include /status, add manual arm/disarm as a nice-to-have.)
  • Repeat spacing 45s and cooldown 60s — OK?
  • Snapshot with bbox on the intruder alert — keep the detection box (yes, helps identify).

Verification plan

  1. Enroll a test device (join WiFi → Trust prompt → confirm trusted phone added).
  2. Take it off WiFi → after 10 min → 🔴 ÉLES message naming it.
  3. While armed, walk in front of a camera (person) and confirm ⚠️ BETÖRŐ + snapshot + correct day/night repeat count; drive-by (car) likewise.
  4. Rejoin the device → instant 🟢 KIKAPCSOLVA; in-flight repeats cancelled.
  5. Restart the service → state re-derived from live presence, no phantom arm.
  6. /untrust and /status behave.