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, printer192.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:
- Presence poller (~30s) — reads WiFi associations from telep-router.
- Telegram receiver —
getUpdateslong-poll for enrollment confirmations + commands. - 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 totrusted phone added: <name> – <ip> – <mac> ✅. - On Ignore → add to ignore list (no re-prompt).
/untrust <name>removes a trusted device;/statusprints armed state + trusted list; optional/arm/disarmmanual override (see Open questions).
State machine
trusted_present = trusted ∩ present.len(trusted) == 0→ dormant (never arm, no intruder watch).trusted_present > 0→ disarmed; recordlast_seenper device.trusted_present == 0→ start/continue an “all-gone” timer; at 10 min → armed.- 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).
- Arm:
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
getUpdatesconsumer 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/disarmoverride — 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
- Enroll a test device (join WiFi → Trust prompt → confirm
trusted phone added). - Take it off WiFi → after 10 min →
🔴 ÉLESmessage naming it. - While armed, walk in front of a camera (person) and confirm
⚠️ BETÖRŐ+ snapshot + correct day/night repeat count; drive-by (car) likewise. - Rejoin the device → instant
🟢 KIKAPCSOLVA; in-flight repeats cancelled. - Restart the service → state re-derived from live presence, no phantom arm.
/untrustand/statusbehave.