Pluggable async home security camera pipeline with detection, VLM analysis, and alerts.
Project description
HomeSec
HomeSec is a pluggable, async pipeline for home security cameras. It records short clips, runs object detection, optionally calls a vision-language model (VLM) for a structured summary, and sends alerts via MQTT or email. The design leans toward reliability: clips land on disk first, state/event writes are best-effort, and non-critical stages can fail without losing the alert.
Highlights
- Bring your own input: RTSP motion detection, FTP uploads, or a watched folder
- Parallel upload + filter (YOLOv8) with frame sampling and early exit
- OpenAI-compatible VLM analysis with structured output
- Policy-driven alerts with per-camera overrides
- Fan-out notifiers (MQTT for Home Assistant, SendGrid email)
- Postgres-backed state + events with graceful degradation
- Health endpoint plus optional Postgres telemetry logging
Pipeline at a glance
ClipSource -> (Upload + Filter) -> VLM (optional) -> Alert Policy -> Notifier(s)
- Upload and filter run in parallel; VLM runs only when trigger classes are detected.
- Upload failures do not block alerts; filter failures stop processing.
- State is stored in Postgres (
clip_states+clip_events) when available.
Quickstart
Requirements
- Python 3.10+ (newest available is best; 3.14 is fine if your deps support it)
- ffmpeg in PATH (required for RTSP source)
- Postgres for state/events (
make db-upstarts a local instance). The pipeline continues if the DB is down, but a DSN is still required. - Optional: MQTT broker, Dropbox credentials, OpenAI-compatible API key
Setup
- Install dependencies:
uv sync - Create a config file:
- Start from
config/example.yamlorconfig/sample.yaml
- Start from
- Set environment variables (use
.env.exampleas a template):cp .env.example .env - Start Postgres:
make db-up - Validate config:
uv run python -m homesec.cli validate --config config/example.yaml - Run:
uv run python -m homesec.cli run --config config/example.yaml --log_level INFO
Tip: make homesec loads .env and runs config/production.yaml.
Configuration
Configs are YAML and validated with Pydantic. Start with any file in config/.
Minimal example (RTSP + Dropbox + MQTT):
version: 1
cameras:
- name: front_door
source:
type: rtsp
config:
rtsp_url_env: FRONT_DOOR_RTSP_URL
output_dir: "./recordings"
storage:
backend: dropbox
dropbox:
root: "/homecam"
token_env: DROPBOX_TOKEN
app_key_env: DROPBOX_APP_KEY
app_secret_env: DROPBOX_APP_SECRET
refresh_token_env: DROPBOX_REFRESH_TOKEN
state_store:
dsn_env: DB_DSN
notifiers:
- backend: mqtt
config:
host: "localhost"
port: 1883
filter:
plugin: yolo
config:
classes: ["person"]
min_confidence: 0.5
vlm:
backend: openai
llm:
api_key_env: OPENAI_API_KEY
model: gpt-4o
alert_policy:
backend: default
enabled: true
config:
min_risk_level: medium
per_camera_alert:
front_door:
min_risk_level: low
notify_on_activity_types: ["person_at_door", "delivery"]
A few things worth knowing:
- Secrets never go in YAML. Use env var names (
*_env) and set values in your shell or.env. - At least one notifier must be enabled (
mqttorsendgrid_email). - Built-in YOLO classes:
person,bird,cat,dog,horse,sheep,cow,elephant,bear,zebra,giraffe. - Local storage for development:
storage:
backend: local
local:
root: "./storage"
- Set
alert_policy.enabled: falseto disable notifications (a noop policy is used). - For a quick local run, pair
local_folderwithlocalstorage and drop a clip intorecordings/.
CLI
- Run the pipeline:
uv run python -m homesec.cli run --config config/example.yaml --log_level INFO - Validate config:
uv run python -m homesec.cli validate --config config/example.yaml - Cleanup (reanalyze and optionally delete empty clips):
uv run python -m homesec.cli cleanup --config config/example.yaml --older_than_days 7 --dry_run True
Built-in plugins
- Filters:
yolo - VLM analyzers:
openai(OpenAI-compatible API) - Storage:
dropbox,local - Notifiers:
mqtt,sendgrid_email - Alert policies:
default,noop
Writing a plugin
HomeSec discovers plugins via entry points in the homesec.plugins group. A plugin
module just needs to import and register itself.
# my_package/filters/custom.py
from pydantic import BaseModel
from homesec.interfaces import ObjectFilter
from homesec.plugins.filters import FilterPlugin, filter_plugin
class CustomConfig(BaseModel):
threshold: float = 0.5
class CustomFilter(ObjectFilter):
...
@filter_plugin(name="custom")
def register() -> FilterPlugin:
return FilterPlugin(
name="custom",
config_model=CustomConfig,
factory=lambda cfg: CustomFilter(cfg),
)
# pyproject.toml
[project.entry-points."homesec.plugins"]
my_filters = "my_package.filters.custom"
Observability
- Health endpoint:
GET /health(configurable inhealth.host/health.port) - Optional telemetry logs to Postgres when
DB_DSNis set:- Start local DB:
make db-up - Run migrations:
make db-migrate
- Start local DB:
Development
- Run tests:
make test - Run type checking (strict):
make typecheck - Run both:
make check - Tests must include Given/When/Then comments.
- Architecture notes:
DESIGN.md
License
Apache 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file homesec-0.1.0.tar.gz.
File metadata
- Download URL: homesec-0.1.0.tar.gz
- Upload date:
- Size: 380.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2eb8c7a0e954073c2fb1172e59912da468972ab6aed986fa4751ac6c26fd543
|
|
| MD5 |
f6adb9b58f06963c2d4e494985d91dd2
|
|
| BLAKE2b-256 |
45584b46273a4f480588d366abe21e03fba67d289752854caafb7b17b5bb4bb6
|
File details
Details for the file homesec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: homesec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 106.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bca5c1b5e22eb84009ac06dcc456cf453cc90abedff18b1470532377a44da7e1
|
|
| MD5 |
b4db1ffd25c763a1c80d6f5a45bda9e0
|
|
| BLAKE2b-256 |
5698a9d8ee2c3674d67616b5bc41916ae05a0a7e7e42aedefc8c9e9a7a576928
|