274 lines
9.4 KiB
Markdown
274 lines
9.4 KiB
Markdown
# 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/<n>/`. 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 <camera_ip>
|
|
```
|
|
|
|
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
|