Skip to content

Repository files navigation

Context-Aware Home Security Platform

A self-hosted, event-driven home security platform integrating Reolink cameras, ONVIF events, deterministic incident correlation, presence-aware security modes, trusted people/vehicles, topology-aware severity logic, and optional AI-assisted verification. Runs on an always-on machine on the home network and integrates with an existing network-presence data source for away-mode detection.

This project integrates with Reolink cameras via ONVIF; it is an independent, self-hosted project and is not affiliated with or endorsed by Reolink.

A. Why This Exists

A bare camera alert ("motion detected on Camera 3") carries almost no useful information on its own -- it can't tell you whether that motion matters. This platform exists to add the context a human would actually use to judge that: who's home right now, what security mode is active, which camera saw it and whether that camera's coverage genuinely connects to the last one that fired, which zone on the property it was in, whether a known vehicle or trusted person is already associated with it, whether this is a continuation of an incident already underway, how confident the underlying presence/vehicle/AI evidence actually is, and whether there's hard security evidence (like an unexpected indoor person while the house is confirmed away) that should never be second-guessed by softer context. The goal is a system that explains why something is concerning -- or isn't -- instead of just forwarding raw detections.

B. Design Principles

These are enforced in code, not just documentation:

  • UNKNOWN presence must never silently become AWAY. Insufficient signal is reported as uncertain, never collapsed into a confident "away" that would suppress or misjudge an alert.
  • AI may enrich evidence but must not silently override deterministic security rules. A Claude classification of "false positive" can soften a severity score, but it can never pull a result below what hard evidence (e.g. an indoor person while confidently away) already established.
  • Security decisions should be explainable. Every severity result carries human-readable reasons and, where relevant, the softer context that was considered and outweighed -- never a black-box score.
  • One noisy camera should not create duplicate incidents. Related events across time, camera adjacency, zones, and shared context correlate into a single incident rather than a flood of separate alerts.
  • Camera adjacency must reflect real topology, not just camera role. Two cameras sharing a "perimeter" role are not necessarily physically adjacent -- treating them as interchangeable can manufacture a fake continuous approach where none was actually observed (see the Camera Topology section below).
  • Automated behavior should fail conservatively. A missed alert is worse than a spurious one in some paths (e.g. an unreachable presence source defaults to "home") and better than one in others (e.g. an unreachable camera during snapshot verification fails open and alerts anyway) -- chosen deliberately per case, never left to chance.

C. Architecture Overview

flowchart TD
    A[Reolink Cameras] --> B[ONVIF Events]
    B --> C[Event Router]
    C --> D[Detection / Metadata]
    D --> E[Context Layer]
    E --> E1[Presence]
    E --> E2[Security Mode]
    E --> E3[Zones]
    E --> E4[Trusted People]
    E --> E5[Known Vehicles]
    E --> E6[Camera Topology]
    E1 & E2 & E3 & E4 & E5 & E6 --> F[Incident Correlation Engine]
    F --> G[Severity / Verification]
    G --> G1[Deterministic Rules]
    G --> G2[Optional AI Verification]
    G1 & G2 --> H[Alerts + Dashboard + Incident History]
Loading

Every stage above the Alerts box is deterministic, synchronous, and unit-tested without any network, camera hardware, or AI credentials -- the AI verification step is the one narrow exception, and even it is subordinate to the deterministic rules around it (see Design Principles).

D. Feature Summary

  • Security modes: HOME / AWAY / NIGHT / GUEST / VACATION, with AUTO (derived from presence) and MANUAL override handling
  • Presence tracking: house-level home/away with debounced transitions (to absorb momentary signal loss) plus optional per-person tracking via a pluggable provider architecture
  • Camera topology / adjacency: explicit relationship modeling between cameras (continuously-observed adjacency, a known coverage gap, an exterior-to-interior transition, or no established relationship) instead of assuming any two same-role cameras are interchangeable
  • Zones: rectangular/polygonal zone definitions per camera with a zone-editor UI, used as one signal (never the only one) for correlation and severity
  • Incident lifecycle: OPEN -> QUIET -> RESOLVED, with automatic transitions plus manual acknowledge / resolve / reopen
  • Incident correlation: groups related events across time, camera adjacency, zone progression, and shared context into one incident instead of separate alerts
  • Trusted people & known vehicles: soft context (never "trusted = safe") that can reduce nuisance severity but can never suppress hard security evidence
  • AI-assisted verification: Claude Haiku classification for perimeter camera events (reducing false positives) and a narrow, infrequent second-opinion check before an indoor away+person rule fires an alert
  • Dashboard and camera status: live camera tiles, house-state badge, recent-events table, per-camera cooldown/pause state, and a running Claude cost estimate
  • Persistent SQLite storage: events, incidents, zones, known vehicles, and trusted people, with snapshot retention/cleanup
  • REST API routes: status, events, incidents, zones, vehicles, trusted people, presence, and security-mode endpoints backing the dashboard

E. Tech Stack

  • Python 3 / aiohttp (async webhook + dashboard server)
  • SQLite (via aiosqlite) for event/incident/zone/vehicle storage -- this app's own primary datastore
  • PostgreSQL (via asyncpg) -- read-only integration used specifically by the WiFi/MAC presence provider, reading device online/offline status from an existing network-presence database; not required if you don't use that provider (e.g. the manual per-person override provider works standalone)
  • Reolink cameras via reolink_aio / ONVIF push events
  • Anthropic Claude (Haiku) for optional AI-assisted classification
  • HTML/CSS/JavaScript dashboard (no frontend framework/build step)
  • systemd unit files for persistent deployment
  • pytest / pytest-asyncio for the test suite

F. Limitations / Not Implemented

  • No automatic alarm dispatch, siren activation, or door locking -- this system alerts a human, it does not take physical action
  • No facial recognition or biometric identification of any kind -- trusted people are identified only via a linked known vehicle or an explicit manual correction, never from camera imagery
  • No BLE presence detection (designed for as a pluggable provider, not built -- no real hardware to build it against yet)
  • No guarantee against camera, network, or power outages -- camera health monitoring detects and alerts on connectivity loss, but cannot prevent it
  • AI verification is optional and deliberately subordinate to deterministic rules -- it can never override hard security evidence, and the system functions (more conservatively) with no AI credentials configured at all

G. Security & Privacy

  • All credentials (camera passwords, API keys, database credentials, alert tokens) are supplied at runtime via environment variables (.env, git-ignored) -- never hardcoded, never committed
  • This public repository contains only synthetic example data -- no real camera credentials, network addresses, household identities, or incident history
  • Camera streams, snapshots, and credentials should never be committed to version control; .gitignore excludes runtime data, databases, and snapshot images by default
  • This project is intended for a trusted, self-hosted home network -- the webhook/dashboard server is meant to stay on your LAN and should not be port-forwarded to the public internet; secure any remote access (VPN, reverse proxy with auth, etc.) appropriately on your own network
  • Setting API_TOKEN is recommended -- it gates the endpoints that change state (away-mode override, camera role, etc.) behind a shared secret

Status

Built in stages, with later phases building on earlier ones without breaking them (the full test suite runs after every change). This public repository is a curated, sanitized snapshot of the working project. Development occurred incrementally in the private working repository, while the public release uses a clean history to avoid exposing household-specific deployment data.

Core pipeline:

  • 1. Camera connection (reolink_aio) -- connect, list devices, pull snapshot
  • 2. ONVIF event subscription -- webhook listener, log raw events
  • 3. Presence / away-mode integration
  • 4. Event router + SQLite storage
  • 5. Claude Haiku classification
  • 6. Alerting (ntfy push / Twilio SMS)
  • 7. Web dashboard

Context-aware layer (later phases):

  • Presence 2.0 -- per-person tracking with pluggable providers
  • Security modes -- HOME/AWAY/NIGHT/GUEST/VACATION, kept independent of raw presence
  • Camera zones -- persistent zone definitions, matching engine, editor UI
  • Zone classification bridge -- AI zone context folded into the existing classification call (no extra AI cost)
  • Incident correlation engine -- related events grouped into one incident
  • Known vehicles -- soft context, never a presence signal on its own
  • Trusted people -- identity only via a linked known vehicle or manual correction, never camera imagery
  • Context-aware severity engine -- deterministic HIGH/MEDIUM/etc. scoring with explainable reasons (advisory/shadow mode -- does not change real alert routing)
  • Camera topology / coverage-gap correction -- explicit adjacency modeling so same-role cameras aren't assumed interchangeable

Setup

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt   # includes pytest
cp .env.example .env
# edit .env: fill in real camera IPs/credentials, and later Anthropic/Twilio keys

Cameras are configured as numbered blocks in .env (CAMERA_1_*, CAMERA_2_*, ...) so the list is arbitrary length -- add a camera by adding the next numbered block. Each camera has a ROLE of either:

  • perimeter -- AI motion events go through the Claude Haiku filter
  • indoor_pet -- Claude is skipped entirely; only a rule-based away-mode "person detected while away" check applies (added in step 3)

See .env.example for the full list of variables and comments.

Step 1: verify camera connectivity

python scripts/test_camera_connection.py

This logs into every camera in .env, prints its model/name/ONVIF status, and saves one snapshot per camera into data/snapshots/ so you can eyeball that the image is right.

Run this on a machine that's actually on the same LAN as the cameras (the mini PC, or your normal dev machine at home) -- it won't work from an isolated cloud/CI environment with no route to your home network.

Step 2: verify ONVIF event subscription

Requires WEBHOOK_BASE_URL in .env -- the URL cameras use to reach this machine, e.g. http://192.168.1.10:8090 (this machine's LAN IP, not 0.0.0.0 or localhost).

python scripts/run_event_listener.py

Connects to every camera, starts a local webhook server, subscribes each camera to ONVIF push events, then logs every raw motion/AI event to the console as it arrives. Trigger motion on a camera (or walk in front of one with AI detection on) and confirm a line shows up. Ctrl+C to stop -- subscriptions are cleanly torn down on shutdown.

Each camera needs ONVIF enabled in the Reolink app (Device Settings -> Network -> ONVIF) for this to work; a camera with ONVIF disabled will log a subscribe failure but won't block the others.

Step 3: verify away-mode / presence

Reads live device status directly from the existing home network monitor app's Postgres database (same machine, devices table). No network calls -- just a local DB connection. Configure PRESENCE_DB_* and PRESENCE_DEVICE_MACS in .env (see .env.example); house is "away" only when every listed MAC shows offline, "home" the moment any of them is online. A manual override (--set-override home|away) always wins, and if the presence database can't be reached at all, it defaults to "home" (a missed alert beats a spammed one). Include every MAC a phone has ever shown, not just one -- MAC randomization means a phone can present a different address per network/reconnect.

The presence database resolves a real change within ~20-30s, fast enough that a momentary WiFi drop (phone sleeps, walks to the edge of range) could otherwise cause a brief false "away" reading. So going to "away" is debounced (AWAY_DEBOUNCE_SECONDS, default 90) -- the raw signal has to hold "away" continuously for that long before it's actually reported; coming back to "home" is always immediate, no debounce. While a transition is pending, the dashboard's house badge shows a "confirming away in Ns" countdown instead of silently sitting on "home."

python scripts/test_away_mode.py
python scripts/test_away_mode.py --set-override away
python scripts/test_away_mode.py --clear-override

Presence 2.0 -- per-person tracking (optional)

The house-level home/away logic above is unchanged and still drives every alerting decision. Optionally, set PERSON_1_NAME/PERSON_1_MACS, PERSON_2_NAME/PERSON_2_MACS, ... in .env (see .env.example) to also track individual household members -- the dashboard shows a "Who's Home" row (e.g. ALEX HOME, JORDAN AWAY) built from app/presence/engine.py. If you don't set any PERSON_N_NAME, this falls back to a single "Household" person built from the existing PRESENCE_DEVICE_MACS, so skipping it changes nothing.

This is a pluggable provider architecture (app/presence/providers.py): today it has a WiFi/MAC provider (the same presence database as above, resolved per person) and a manual per-person override (POST /api/presence/{person}/override). BLE beacon and known-vehicle providers are designed for but not built -- they need real hardware/data this deployment doesn't have yet, so nothing fake is exposed until they're real.

Step 4/5/6/7: run the full pipeline (router + storage + Claude Haiku + alerts + dashboard)

This is the real entry point -- it wires cameras, ONVIF events, the away-mode checker, the event router, Claude Haiku classification, alerting, SQLite storage, and the web dashboard together, all on one aiohttp server (webhook + dashboard share WEBHOOK_PORT). Every event gets logged; perimeter AI-object events (person/vehicle/animal) get a snapshot pulled and sent to Claude Haiku for a real/false-positive verdict

  • short reason; indoor_pet cameras never call Claude and only alert on the rule-based "person detected while away" check. Requires WEBHOOK_BASE_URL, presence config, and ANTHROPIC_API_KEY in .env (see .env.example).

A continued presence re-fires ONVIF notifications every second or two -- not a series of distinct events -- so a per-camera cooldown (ALERT_COOLDOWN_SECONDS, default 300) suppresses repeat Claude calls and alerts from the same camera within that window of the last one. One continuous incident gets one alert (and one Claude call) instead of a flood of both; it resets after the window, so a presence still ongoing later still triggers a fresh alert.

indoor_pet cameras (never billed -- no Claude call involved) use their own, much shorter 15-second cooldown just for snapshots, completely separate from the alert cooldown above: a person walking through the house and out the door still only gets one alert, but gets a fresh snapshot every ~15s along the way instead of just one photo of the first moment they were seen.

When perimeter cameras alert, by presence + time of day: away, and night (11pm-7am local, regardless of home/away), every perimeter camera alerts normally as described above. The one exception is home during the day (7am-11pm): every perimeter camera goes watch-only -- it still logs an event and takes a snapshot on its own short cooldown (same idea as indoor_pet's, so a visual record still exists), but skips Claude entirely and never alerts, shown as "watching" in the events table's Verdict column. The reasoning: routine daytime activity while you're home (kids, pets, deliveries, coming and going) doesn't need Claude's opinion. If some specific camera should be the exception and keep alerting even during home-daytime hours, set CAMERA_N_ALWAYS_ALERT_WHEN_HOME=true in .env for it -- but that's an opt-in exception, not the default for any camera. indoor_pet cameras are untouched by any of this -- their away+person rule already only fires when away, which daytime-at-home never is.

Indoor away+person alerts get a second opinion before they text you. The on-device "person" tag can misfire (a dog, a shadow, a reflection), and since this is the one alert that fires purely off a rule with no human judgment involved, a single Claude call double-checks the snapshot right before the alert would go out -- confirming it's actually a person, not just relabeling the same tag. This only ever runs in that one narrow case (away, person tag, outside cooldown -- i.e. right when an alert is about to fire), so it stays a small, infrequent cost add-on rather than a new per-event Claude call on every indoor_pet detection. If the camera can't be reached to pull a snapshot to verify, it fails open and alerts anyway rather than silently dropping a possibly-real event. A rejected false alarm shows up in the events table the same as a perimeter false_positive: claude_verdict=false_positive, no alert sent.

Doorbell button presses are their own signal. A press (visitor in the events table's raw tags) is a deliberate action by someone at the door, not ambient motion -- it always gets a snapshot and a shot at a real Claude classification, even with no person/vehicle/animal AI tag alongside it, and bypasses the home-daytime watch-only gate above (same as away/night). It still respects a manual pause and the monthly cost cap -- those are explicit choices you made, not automatic policy.

Dashboard: open http://<this-machine's-LAN-IP>:<WEBHOOK_PORT>/ from any browser on the home network. Shows:

  • A house-state badge (home/away + why) with Force Home / Force Away / Auto buttons -- the manual override from step 3, now with a UI. Hover the badge for a per-device breakdown (each tracked MAC's online/offline status from the presence DB) -- useful for spotting exactly which device is keeping the house "home" when you expect "away" (e.g. a VPN session making a phone reappear on the home network). This app only ever reads that status live from the existing network-monitor app's database -- if a device is slow to flip offline, or a VPN connection makes it look present, that's that other app's detection behavior, not a bug here.

  • A camera tile grid: live snapshot per camera (click to refresh, or wait for the 30s auto-refresh), connection status, and a role toggle (perimeter <-> indoor_pet) -- takes effect immediately, no restart, but doesn't persist past a restart (edit .env for the permanent default). A tile also shows an amber "cooldown, Ns left" line whenever that camera is currently suppressing repeat Claude calls/alerts (see cooldown above) -- so a quiet camera right after an alert reads as "working as intended" instead of "did it stop seeing anything?" A perimeter camera with ALWAYS_ALERT_WHEN_HOME=true shows an "always alerts" badge so it's clear at a glance which camera(s) stay live during home-daytime hours.

  • A recent-events table (auto-refreshes every 8s): a thumbnail of the actual snapshot Claude looked at (click for full size), timestamp, camera, tags, Claude verdict + reason, and whether it alerted -- the raw vs. filtered view per camera. A cooldown-suppressed detection shows "cooldown" (in italic muted text, hover for why) in the Verdict column instead of a bare "-", so it's visibly distinct from an event that just didn't have any relevant tags. Only the latest 50 load automatically; click Load older events at the bottom to page further back (this pauses auto-refresh so it doesn't yank you back to the live tail mid-scroll -- click Back to live to resume).

  • A "Claude: $X.XX this month" badge in the header (hover for today's count/cost too). Computed from the actual input/output token counts each classification call reports -- not a guess -- but it's still an estimate; the Anthropic console is always the source of truth for actual billing. Only perimeter events that reach Claude count toward this -- indoor_pet cameras never call Claude at all, and cooldown-suppressed/motion-only events aren't billed and aren't counted either.

    This badge only counts calls made after token tracking was added -- older classify() calls have no captured token count, so right after upgrading it can read "$0.00" even though the Anthropic console shows real spend for the month. It'll start climbing again as soon as the next perimeter detection fires. To also account for older calls this month, run once:

    python scripts/backfill_claude_token_estimates.py --dry-run   # see how many events would be affected
    python scripts/backfill_claude_token_estimates.py             # apply a flat per-call estimate to them

    This fills in a flat per-call token estimate for historical calls, not their real counts (which were never captured) -- it's meant to make the month-to-date figure less misleading, not to match the console exactly. Safe to re-run; it only touches rows still missing a token count.

  • Pause 30m / 1h / 3h buttons, plus a custom hours field (up to 8) for anything longer, in the header for times you're deliberately in a perimeter camera's view and don't want to pay for it -- mowing the lawn, sitting on the porch, working in the driveway. One pause covers the whole job; no need to keep re-pausing it every hour. While paused, perimeter cameras log events as usual but skip Claude entirely (shown as "paused" in the events table's Verdict column, same treatment as a cooldown-suppressed event) -- no snapshot pull, no API call, no alert. Indoor_pet cameras are untouched: pausing doesn't weaken the away+person rule or its verification check (above) either. A paused state shows a countdown and a Resume now button, and auto-expires on its own (capped at 8 hours) so it can't be forgotten and left off indefinitely.

  • Optional hard monthly spending cap (MONTHLY_COST_CAP_USD in .env, unset by default -- no cap). Once month-to-date estimated cost (the same number on the Claude badge) reaches this amount, perimeter cameras auto-pause -- shown as "cost cap" in the events table's Verdict column -- until the calendar month rolls over or you manually resume. You get one push alert when it trips, not silence. indoor_pet cameras are unaffected either way, same reasoning as the manual pause above.

Meant for your home LAN only -- never port-forward WEBHOOK_PORT to the internet. Setting API_TOKEN in .env (recommended, blank by default) adds a shared-secret check to the ONVIF webhook and the dashboard's away-mode override + camera role endpoints -- without it, anything on your LAN could POST fake camera events (running up Claude costs) or silently force the house to "home" to suppress the away+person alert. The dashboard page itself still loads with no login either way; the token only gates the two requests that change state. Generate one with:

python3 -c "import secrets; print(secrets.token_urlsafe(24))"

Camera health monitoring: a camera going silent (crashed, rebooted, Wi-Fi dropped, ONVIF got disabled in the Reolink app) used to produce no signal at all -- events just stopped showing up for that camera and nobody noticed until they checked. Now:

  • At startup, any camera that fails to connect or fails to subscribe to ONVIF events sends an immediate alert ("Camera(s) offline at startup" / "Camera(s) not receiving events").
  • Every 5 minutes, each camera's ONVIF subscription is renewed (or re-subscribed if renewal fails). A camera that can't be renewed or re-subscribed sends a "Camera offline" alert -- once per outage, not every 5 minutes, and again if it recovers and then drops a second time.
  • /api/status (and the dashboard) now shows each camera's subscribed state and last_event_at (the last time it actually received an ONVIF notification), so you can see at a glance whether a camera that looks "connected" is actually still delivering events.

Database size / disk usage / backups

The SQLite database itself stays small forever -- each event row is a few hundred bytes, so even thousands of events a month only adds up to a few MB a year. Event rows are never deleted, so the full history is always there to page back through in the dashboard.

The real disk usage is the snapshot JPEGs (perimeter events routed to Claude, and indoor_pet person detections) -- these are real camera images, roughly 0.5-1.4MB each. scripts/cleanup_snapshots.py deletes snapshot files older than SNAPSHOT_RETENTION_DAYS (default 30) and clears their reference in the DB -- the event row itself is kept forever, just without an image attached once it ages out.

python scripts/cleanup_snapshots.py            # uses SNAPSHOT_RETENTION_DAYS from .env
python scripts/cleanup_snapshots.py --days 14   # override for this run
python scripts/cleanup_snapshots.py --dry-run   # show what would be deleted, delete nothing

Run it daily via systemd timer so you don't have to remember:

sudo cp deploy/context-aware-home-security-cleanup.service deploy/context-aware-home-security-cleanup.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now context-aware-home-security-cleanup.timer
systemctl list-timers context-aware-home-security-cleanup.timer   # confirm it's scheduled

(Same caveat as the main service: edit WorkingDirectory/ExecStart in deploy/context-aware-home-security-cleanup.service first if the repo isn't at /opt/context-aware-home-security.)

Backups: nothing is backed up automatically. data/events.db is a single file -- sqlite3 data/events.db ".backup /path/to/backup.db" (safe even while the app is running) or a plain cp while the service is stopped. Snapshots are just files under data/snapshots/ if you want to back those up too, though with retention cleanup running they're short-lived by design.

Alerting picks the first of these that's configured, in order:

  1. ntfy (NTFY_TOPIC) -- free push notifications, no account. Install the ntfy app and subscribe to your topic.
  2. Twilio SMS (TWILIO_ACCOUNT_SID/AUTH_TOKEN/FROM_NUMBER + ALERT_TO_NUMBER) -- real texts, but Twilio is paid after the trial.
  3. Stub -- if neither is configured, alerts just get logged to the console instead of sent anywhere.

All three implement the same interface, so switching later needs no code changes -- just edit .env.

Every alert text includes a direct link to the snapshot that triggered it (WEBHOOK_BASE_URL + /snapshots/<file>), so you can see the photo without having to separately open the dashboard.

If a send actually fails (Twilio rejects it, ntfy.sh is unreachable, etc.), that's no longer just a log line -- the event's row gets marked and shows FAILED (in red) in the dashboard's events table Alert column instead of silently doing nothing, so a delivery outage doesn't go unnoticed.

python scripts/run_pipeline.py

Trigger some events, then Ctrl+C -- it prints a per-camera summary, and you can inspect the full log anytime with:

python scripts/view_events.py        # last 20 events
python scripts/view_events.py 100    # last 100

scripts/run_event_listener.py from step 2 still works too, if you just want to watch raw ONVIF events without the routing/storage layer.

Running it persistently (systemd)

Running python scripts/run_pipeline.py directly only lasts as long as that terminal/SSH session stays open -- closing it sends the process a hangup signal and kills it. For an always-on setup, install it as a systemd service instead:

sudo cp deploy/context-aware-home-security.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now context-aware-home-security

The unit file assumes the repo lives at /opt/context-aware-home-security with a venv at .venv in that directory -- edit WorkingDirectory and ExecStart in deploy/context-aware-home-security.service first if either is different on your machine, then sudo systemctl daemon-reload again.

systemctl status context-aware-home-security      # is it running?
journalctl -u context-aware-home-security -f      # live logs (same output as running it directly)
sudo systemctl restart context-aware-home-security  # e.g. after a git pull, or a .env change
sudo systemctl stop context-aware-home-security

Restart=on-failure means it comes back on its own if it crashes, and WantedBy=multi-user.target means it starts on boot -- no manual restart needed after a mini PC reboot.

Tests

pytest

753 tests, no real network, camera hardware, database, or API credentials needed -- safe to run anywhere. Coverage spans:

  • Configuration: env parsing, including phone-number normalization
  • ONVIF / event routing: webhook routing and dispatch against fake camera clients, the router's perimeter/indoor_pet decisions against fake cameras/classifier/presence
  • Presence engine / providers: the pluggable per-person provider architecture, away-mode logic against a fake presence reader
  • Security modes: HOME/AWAY/NIGHT/GUEST/VACATION evaluation and AUTO/MANUAL handling
  • Zones: zone storage and AI zone-match sanitization
  • Incident correlation, lifecycle, and storage: join/create decisions, OPEN/QUIET/RESOLVED transitions, persistence, and summary/title generation
  • Camera topology / adjacency: relationship kinds (observed, coverage-gap, entry-transition, none) and same-role-fallback behavior
  • Severity engine: deterministic scoring, the hard-evidence floor, and topology-aware progression quality
  • Known vehicles / trusted people: matching, storage, and soft-context behavior
  • AI classification / verification: the Haiku classifier's request-building and error handling, and the indoor away+person verification check, all against a mocked Anthropic client
  • Alerting: the SMS alert composer, and the ntfy/Twilio senders against a real local test server / mocked Twilio client
  • Dashboard / API: status, counts, events, incidents, zones, vehicles, trusted people, presence, and security-mode routes against fake cameras and a real SQLite/away-mode backend
  • Persistence / storage: the SQLite storage layer underlying events, incidents, zones, vehicles, and trusted people

About

Self-hosted context-aware security platform integrating Reolink cameras, ONVIF events, deterministic incident correlation, presence-aware modes, topology-aware severity scoring, and optional AI verification.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages