# Home camera stack Frigate NVR + Mosquitto MQTT + frigate-notify -> ntfy push notifications, with face recognition and license plate recognition on the Frigate side. Includes a planned Pi Zero W "doorbell speaker" stack (push-to-talk web page fronted by Caddy) that's wired but not yet deployed. ## Architecture ``` Cameras (RTSP) Phone / browser | | v v +------------+ MQTT events +----------------+ +-------------+ | Frigate | <-------------> | Mosquitto | | Pi | | (NVR) | | broker | | (planned) | +-----+------+ +-------+--------+ | speaker | | WebRTC / MSE | +------+------+ v v ^ +------------+ +------------------+ | | Caddy |<--HTTPS--------| frigate-notify | | | proxy | | (event consumer) | | +------------+ +--------+---------+ | | | | | cam.yourdomain.com ntfy push | | doorbell.yourdomain.com | +-------HTTPS---------------> PTT button ------------+ ``` ## What's deployed vs planned | Component | Status | |---|---| | Frigate 0.17 | Deployed | | Mosquitto MQTT broker | Deployed | | frigate-notify -> ntfy | Deployed | | Face recognition | Configured, needs training | | License plate recognition | Configured | | Caddy reverse proxy | Whatever your existing Caddy does | | Pi Zero W doorbell speaker | Planned, not deployed yet | ## Repo layout ``` home-cameras/ |-- docker-compose.yml # frigate + mosquitto + frigate-notify |-- .env.example # template -- copy to .env, fill in |-- .gitignore |-- README.md # this file | |-- frigate_config/ | |-- config.yml # production: main stream for detect | `-- alternatives/ | `-- config-simple.yml # lower-CPU fallback: sub-stream detect | |-- frigate-notify/ | `-- config.yml # MQTT in, ntfy out, face-aware templates | |-- mosquitto/ | |-- config/ | | `-- mosquitto.conf # broker config (allow_anonymous false) | |-- data/.gitkeep | `-- log/.gitkeep | |-- caddy/ | `-- Caddyfile # reverse proxy for both subdomains | `-- pi/ # runs on the Pi, NOT on the Frigate host |-- README.md # Pi-specific setup |-- server.py # Flask + WebSocket PTT receiver |-- doorbell.service # systemd unit `-- install.sh # one-shot installer ``` ## First-run on the Frigate host Prerequisites: - Docker + docker compose v2 - DNS records for any subdomains you intend to use, pointing at your Caddy host - An existing Caddy instance (separate from this stack) handling TLS at the edge, OR adapt for whatever reverse proxy you use - Coral USB stick plugged in (or adjust `detectors:` for a different accel) - A media disk mounted on the host; update the `/media/frigate` path in `docker-compose.yml` Steps: ```bash git clone https://github.com/YOU/home-cameras.git cd home-cameras # 1) Configure secrets cp .env.example .env $EDITOR .env # 2) Make mosquitto dirs writable by the container's mosquitto user (UID 1883) sudo chown -R 1883:1883 mosquitto/ # 3) Bootstrap mosquitto BEFORE applying the production config. # The committed mosquitto.conf has allow_anonymous false + password_file, # which means we need to create the password file first OR temporarily # flip to allow_anonymous true to start. # # Easiest: temporarily edit mosquitto/config/mosquitto.conf: # allow_anonymous false -> allow_anonymous true # comment out: password_file /mosquitto/config/passwd # Then start: docker compose up -d mosquitto docker compose logs mosquitto --tail 10 # expect "running" # 4) Create the MQTT user (use the password from your .env) docker compose exec mosquitto mosquitto_passwd -c -b \ /mosquitto/config/passwd frigate \ "$(grep ^FRIGATE_MQTT_PASSWORD .env | cut -d= -f2)" sudo chown 1883:1883 mosquitto/config/passwd sudo chmod 0640 mosquitto/config/passwd # 5) Restore mosquitto.conf to its committed state: # allow_anonymous true -> allow_anonymous false # uncomment: password_file /mosquitto/config/passwd git checkout mosquitto/config/mosquitto.conf docker compose restart mosquitto # 6) Verify auth works sudo apt install -y mosquitto-clients mosquitto_sub -h 127.0.0.1 -u frigate \ -P "$(grep ^FRIGATE_MQTT_PASSWORD .env | cut -d= -f2)" \ -t 'test/#' -v & mosquitto_pub -h 127.0.0.1 -u frigate \ -P "$(grep ^FRIGATE_MQTT_PASSWORD .env | cut -d= -f2)" \ -t 'test/hello' -m 'ok' # expect: test/hello ok kill %1 # 7) Bring up the rest docker compose up -d docker compose logs -f ``` Healthy startup looks like: - Frigate: `frigate.comms.mqtt INFO : MQTT connected` - frigate-notify: `Successfully connected to http://frigate:5000` then `Connected to MQTT.` then `Subscribed to MQTT topic: frigate/events` - mosquitto: incoming client connections from both ## Caddy On whichever host runs Caddy, copy `caddy/Caddyfile` (or merge the relevant site blocks into your existing one), edit IPs and domains, then: ```bash sudo caddy validate --config /etc/caddy/Caddyfile sudo systemctl reload caddy ``` Until the Pi is deployed, comment out the `doorbell.yourdomain.com` block. ## Train face recognition 1. Let Frigate run with normal foot traffic for a day. Face crops are captured automatically and appear under **Face Library -> Train** in the Frigate UI. 2. For each person, label 5-10 **diverse** clear crops -- different angles, lighting, times of day. Diversity beats quantity; 30 near-identical frames from one event hurts recognition. 3. Walk past the camera again. Events should now show the person's name as the sub-label, and frigate-notify's ntfy push will say their name instead of "person". Training images live on the Frigate host at `/media/frigate/clips/faces//`. Not version-controlled (privacy). ## Switching config profiles This repo ships with two Frigate configs: - `frigate_config/config.yml` -- active. Main 2688x1520 stream for both detect and record. Better face recognition at distance, higher CPU. - `frigate_config/alternatives/config-simple.yml` -- lower-CPU fallback. Sub-stream 640x480 for detect, main for record only. To switch: ```bash cp frigate_config/config.yml frigate_config/config.yml.bak cp frigate_config/alternatives/config-simple.yml frigate_config/config.yml docker compose restart frigate ``` ## Adding a camera with a mic When adding a camera with a built-in microphone (e.g. an Anpviz with mic), see the comment block at the bottom of `frigate_config/config.yml` for the exact edits needed to enable live audio in the Frigate UI. ## Pi doorbell speaker (when you're ready) See `pi/README.md`. Summary: 1. Flash Pi OS Lite, scp `pi/` to the Pi, run `install.sh`. 2. Add the `doorbell.yourdomain.com` site block in Caddy. 3. Open the URL on your phone. ## Security notes - `.env` has RTSP credentials, MQTT password, ntfy URL. Never commit it. This repo's `.gitignore` blocks it; review `git status` before committing. - The Pi's Flask server binds only to `127.0.0.1`. Caddy is what exposes it. Do NOT bind `server.py` to `0.0.0.0` -- it has no auth of its own. - Mosquitto's port 1883 is LAN-only. Use a VPN for any remote MQTT clients. - For the doorbell page, optionally add basic auth in Caddy (`caddy hash-password`). ## Troubleshooting ### Frigate can't reach cameras ```bash docker compose exec frigate ping -c 2 ``` RTSP path varies by camera vendor: - Dahua / Amcrest: `/cam/realmonitor?channel=1&subtype=0` - Hikvision / Anpviz H-series: `/Streaming/Channels/101` If your password contains `%`, `@`, `/`, `?`, `#`, `&`, or `+`, either URL-encode it or change the password to avoid those characters. ### Mosquitto restarts in a loop Almost always permission on `mosquitto/config/passwd`: ```bash sudo chown -R 1883:1883 mosquitto/ sudo chmod 0640 mosquitto/config/passwd docker compose restart mosquitto ``` Or the config file or password file simply doesn't exist yet -- see the "First-run on the Frigate host" section above for the bootstrap flow. ### frigate-notify connects to MQTT but doesn't send pings ```bash docker compose logs frigate-notify --tail 30 ``` - "webapi" in logs but expecting MQTT? Check `webapi.enabled: false` and `mqtt.enabled: true` in `frigate-notify/config.yml`. - MQTT auth fails? Verify `FN_FRIGATE__MQTT__PASSWORD` (note DOUBLE underscores) matches what you set with `mosquitto_passwd`. - Confirm what reached the container: `docker inspect frigate-notify --format '{{range .Config.Env}}{{println .}}{{end}}' | grep FN_` ### Always see "person" instead of trained name - Increase `alerts.general.recheck_delay` in `frigate-notify/config.yml` from 10 to 15 or 20 seconds. - Check the Frigate UI event timeline -- if the event itself doesn't show a sub_label, the face crop was too small / too blurry / too obscured for recognition. ## Hardware reference Current: - NVR host: x86_64 + Docker, USB Coral - Cameras: Amcrest (Dahua RTSP) - Notifier: self-hosted ntfy Planned: - Anpviz 4K camera with built-in mic (front door audio) - Back door + squirrel feeder cameras - Pi Zero W + USB speaker at the door