The dropdown allowed switching the video feed but PTT always pointed at this Pi's local speaker, so picking a non-co-located camera could have let a user talk into the wrong room. Each Pi is now hardcoded to the camera at its own location via CAMERA_NAME, with optional PEER_LINKS to render quick-jump buttons to sibling doorbell Pis at other URLs. https://claude.ai/code/session_013XZ1vmgk78k2PEQ5DmJhF3
127 lines
3.8 KiB
Markdown
127 lines
3.8 KiB
Markdown
# Pi doorbell PTT
|
|
|
|
Turns a Raspberry Pi into a network speaker so a phone hitting
|
|
`https://doorbell.yourdomain.com` can see/hear the front-door Frigate feed
|
|
and hold a button to talk through a speaker mounted at the door.
|
|
|
|
## Hardware
|
|
|
|
- Any Raspberry Pi (Zero W 1st gen is enough; Zero 2 W is better for live
|
|
two-way; Pi 3A+ has a 3.5mm jack onboard and skips the OTG adapter)
|
|
- Audio output, one of:
|
|
- USB speaker + micro-USB-to-USB-A OTG adapter (simplest)
|
|
- 3.5mm powered speaker (Pi 3A+ has the jack; Zero W does not)
|
|
- I2S DAC HAT (best quality, requires GPIO header)
|
|
- microSD card, power supply, WiFi or USB ethernet
|
|
|
|
## Install on the Pi
|
|
|
|
```bash
|
|
# From your laptop/desktop:
|
|
scp -r pi/ pi@PI_LAN_IP:~/doorbell-src
|
|
|
|
# SSH to the Pi:
|
|
ssh pi@PI_LAN_IP
|
|
cd ~/doorbell-src
|
|
chmod +x install.sh
|
|
./install.sh
|
|
```
|
|
|
|
The installer apt-installs ffmpeg + alsa-utils + Python deps, creates a
|
|
virtualenv, drops `server.py` into `~/doorbell/`, installs and enables the
|
|
systemd service, runs `speaker-test` to confirm ALSA output works, and
|
|
starts the service.
|
|
|
|
## Verify
|
|
|
|
```bash
|
|
curl http://127.0.0.1:5555/healthz # -> ok
|
|
sudo journalctl -u doorbell -f # live logs
|
|
```
|
|
|
|
## Wire it up
|
|
|
|
1. On the Caddy host, add the `doorbell.yourdomain.com` block from
|
|
`../caddy/Caddyfile` and reload Caddy.
|
|
2. DNS: point `doorbell.yourdomain.com` at the Caddy host's public IP.
|
|
3. Open `https://doorbell.yourdomain.com` on an Android phone.
|
|
4. Grant the one-time microphone permission.
|
|
5. Tap **Unmute camera** if browser autoplay swallowed the audio.
|
|
6. Hold the big green button to talk.
|
|
|
|
Add to home screen (Chrome menu -> Add to home screen) for an app-like
|
|
experience.
|
|
|
|
## Choosing the camera
|
|
|
|
Each Pi is hardcoded to one camera -- the one whose mic and speaker are
|
|
physically co-located with this Pi. The PTT button on this page only
|
|
talks to *this* Pi's speaker, so mixing cameras here would let a misclick
|
|
talk into the wrong room.
|
|
|
|
`server.py` near the top:
|
|
```python
|
|
CAMERA_NAME = "front_door"
|
|
```
|
|
|
|
The name must match a `go2rtc.streams` entry in
|
|
`frigate_config/config.yml` and the camera must be `enabled: true` in
|
|
Frigate. After editing:
|
|
```bash
|
|
sudo systemctl restart doorbell
|
|
```
|
|
|
|
## Multiple doorbell Pis
|
|
|
|
Run one copy of this app per Pi, each on its own subdomain (e.g.
|
|
`frontdoor.yourdomain.com`, `backdoor.yourdomain.com`). Add a Caddy
|
|
block per subdomain pointing at that Pi's LAN IP -- same shape as the
|
|
existing `doorbell.yourdomain.com` block in `../caddy/Caddyfile`.
|
|
|
|
To render quick-jump buttons to the other Pis at the top of the page,
|
|
fill in `PEER_LINKS` near the top of `server.py`:
|
|
```python
|
|
PEER_LINKS = [
|
|
{"label": "Back door", "url": "https://backdoor.yourdomain.com"},
|
|
{"label": "Squirrel", "url": "https://squirrel.yourdomain.com"},
|
|
]
|
|
```
|
|
Leave it as `[]` (the default) and the row is hidden. Restart with
|
|
`sudo systemctl restart doorbell` after editing.
|
|
|
|
## Audio stack
|
|
|
|
ALSA-only -- no PipeWire/PulseAudio. Lighter on the Pi Zero. If you ever
|
|
need PipeWire (e.g., to share the speaker with another app), change
|
|
`'-f', 'alsa'` to `'-f', 'pulse'` in `server.py` and install the
|
|
PipeWire/Pulse compatibility shim.
|
|
|
|
## Troubleshooting
|
|
|
|
### speaker-test fails
|
|
|
|
USB/3.5mm output isn't the default ALSA card. Check:
|
|
```bash
|
|
aplay -l
|
|
```
|
|
If your speaker isn't card 0, create `/etc/asound.conf`:
|
|
```
|
|
defaults.pcm.card 1
|
|
defaults.ctl.card 1
|
|
```
|
|
(Replace `1` with whatever card your speaker is.)
|
|
|
|
### Video plays but talk button stuck on "Disconnected"
|
|
|
|
The WebSocket isn't reaching the Pi. Most common: Caddy not proxying
|
|
`doorbell.yourdomain.com` -> Pi correctly. From the Caddy host:
|
|
```bash
|
|
curl -i http://PI_LAN_IP:5555/healthz # should return 200 ok
|
|
```
|
|
|
|
### Feedback loop when talking
|
|
|
|
The page auto-mutes the camera while the PTT button is held, so this
|
|
should not happen. If it does, increase distance between Pi speaker and
|
|
camera mic, or turn the speaker volume down.
|