Offline Baby-Monitor für Raspberry Pi — Bewegungserkennung, Schrei-/Geräuscherkennung, Live-Video & Audio, Telegram-Alarme, Home Assistant Integration.
Alles lokal. Keine Cloud. Kein Abo. Kein Internet nötig.
- Features
- Hardware
- Schnellstart
- Konfiguration
- Home Assistant Integration
- Telegram-Benachrichtigungen
- API-Referenz
- Fehlerbehebung
- Update
- Backup & Export
- Projektstruktur
- Lizenz
- 📹 Live-Video — MJPEG-Stream mit Motion Detection (OpenCV Frame-Differencing)
- 🔊 Live-Audio — Web Audio API im Browser, ~500ms Latenz
- 👶 Schrei-Erkennung — Frequenzanalyse im 300–800 Hz-Bereich
- 📢 Geräuscherkennung — Lautstärke-basiert, mit einstellbarer Mindestdauer
- 📸 Snapshots — Automatisch bei anhaltender Bewegung, Schrei oder Geräusch
- 📱 Telegram-Alarme — Push-Benachrichtigung mit Snapshot-Foto
- 🎛️ Kamera-Steuerung live — Helligkeit, Kontrast, Sättigung, Schärfe, Gain (via v4l2-ctl)
- 🔄 Audio-/Kamera-Gerät per Dropdown — Live-Erkennung mit
arecord -lundv4l2-ctl, keine config.json-Editiererei mehr - 🌙 Nachtmodus — Automatische Erkennung + Helligkeitsboost für Snapshots
- 📲 Mobile-optimiertes Dashboard — Touch-freundliche Regler, Dark Mode
- 🏠 Home Assistant Integration — MQTT Auto-Discovery + Generic Camera
- 🔔 Audio-Alarm — Browser-Alarmton bei Ereignissen
- 🐳 Docker — ARM64-Image, separate Compose-Files für Pi 4B und Pi Zero
- 💾 Persistente Konfiguration —
config.json, überlebt Neustarts und Updates - 🔧 Debug-Endpoint —
/api/debugmit Live-Status, MQTT-Info und Ereignis-Log
| Komponente | Empfehlung | Alternativen |
|---|---|---|
| Raspberry Pi | Pi Zero 2 W (sparsam, klein) | Pi 4B, Pi 3B |
| Kamera | Logitech C930e (1080p, Autofokus, gutes Mikro) | ELP 2MP mit IR, jede UVC-Webcam |
| SD-Karte | ≥ 8 GB | — |
Getestete Kombinationen:
- ✅ Pi Zero 2 W + Logitech C930e (5 FPS, ~50% CPU)
- ✅ Pi 4B + Logitech C930e (15 FPS, ~25% CPU)
- ✅ Pi 4B ohne Kamera (reine Audio-Instanz für MQTT-Backup)
- Raspberry Pi mit Raspberry Pi OS Lite (64-bit)
- Docker & Docker Compose installiert:
curl -fsSL https://get.docker.com | sh && sudo apt install -y docker-compose-plugin - Benutzer in
docker- undaudio-Gruppen:sudo usermod -aG docker,audio $USER
git clone https://github.com/tobwil/babycam.git
cd babycam
# MQTT-Broker setzen (für Home Assistant)
# In docker-compose.yml:
# MQTT_BROKER=192.168.178.131
# Bauen & starten
docker build -t babycam:latest .
docker compose up -dNach dem Start im Web-UI (http://<pi-ip>:5000) unter ⚙️ das passende Audiogerät aus dem Dropdown wählen — live erkannt via arecord -l. 💾 Speichern, fertig.
Webinterface: http://<pi-ip>:5000
Der Pi Zero hat eine schwächere CPU. Deshalb gibt es ein spezielles Compose-File, das /dev/video0 als Device-Mount (nicht Volume) einbindet — Volume-Mounts für Device-Dateien funktionieren auf dem Zero nicht zuverlässig.
git clone https://github.com/tobwil/babycam.git
cd babycam
# MQTT-Broker setzen
# In docker-compose.pi-zero.yml:
# MQTT_BROKER=192.168.178.131
# Bauen & starten
docker build -t babycam:latest .
docker compose -f docker-compose.pi-zero.yml up -dNach dem Start im Web-UI (http://<pi-ip>:5000) unter ⚙️ Audio- und Kamera-Gerät per Dropdown wählen — live erkannt. 💾 Speichern, fertig. Am Zero auf 5 FPS stellen (📷 Tab).
⚠️ Wichtig: Immerdocker-compose.pi-zero.ymlverwenden, nicht das Standard-Compose-File! Dieses nutztdevices:statt Volume-Mounts für/dev/video0.
Typisches Setup: Ein Pi Zero 2 W im Kinderzimmer (mit Kamera), ein Pi 4B als Home Assistant + Backup-Instanz.
┌─────────────────────┐ MQTT ┌─────────────────────┐
│ Pi Zero 2 W │◄─────────────►│ Pi 4B │
│ 192.168.178.130 │ │ 192.168.178.131 │
│ │ │ │
│ 📹 Kamera + 🎤 │ │ 🏠 Home Assistant │
│ 5 FPS, 640×480 │ │ 📡 Mosquitto MQTT │
│ MQTT → HA │ │ 🎤 Audio-Backup │
└─────────────────────┘ └─────────────────────┘
Pi 4B Backup-Instanz (ohne Kamera, nur Audio + MQTT):
# docker-compose.pi-one.yml verwendet — keine /dev/video0-Mounts!
docker compose -f docker-compose.pi-one.yml up -dDiese Instanz sendet Audio-Pegel und Geräuscherkennung per MQTT, auch wenn die Zero-Instanz ausfällt. Die Kamera-Entität in Home Assistant bleibt dann ohne Bild, aber die Audio-Sensoren laufen weiter.
Alle Einstellungen im Browser unter:
- 🎛️ Geräte — Audio- und Kamera-Gerät per Dropdown auswählen (live erkannt via
arecord -l/v4l2-ctl) - ⚙️ Alarm — Bewegung, Geräusch, Schrei, Nachtmodus, Telegram
- 📷 Kamera — FPS, Helligkeit, Kontrast, Sättigung, Schärfe, Gain
Änderungen werden sofort übernommen (kein Neustart nötig). 💾 Speichern persistiert in config.json.
💡 Pi Zero 2 W Tipp:
fps: 5,audio_rate: 16000,video_enabled: true(aber Kamera-Automatik an). CPU-Auslastung ~50%.
- Mosquitto MQTT Broker in Home Assistant (Einstellungen → Add-ons → Mosquitto)
- BabyCam und HA im selben Netzwerk
MQTT_BROKERim docker-compose.yml auf die HA-IP gesetzt
Nach dem Start erscheinen automatisch via MQTT Auto-Discovery:
| Sensor | Typ | Topic | Beschreibung |
|---|---|---|---|
binary_sensor.babycam_bewegung |
Motion | babycam/motion |
Bewegung (Frame-Differencing) |
binary_sensor.babycam_schreien |
Sound | babycam/cry |
Schreien (300–800 Hz) |
binary_sensor.babycam_gerausch |
Sound | babycam/noise |
Allgemeines Geräusch |
binary_sensor.babycam_nachtmodus |
Light | babycam/night_mode |
Automatische Nachterkennung |
sensor.babycam_lautstarke |
RMS | babycam/sound_level |
Aktueller Lautstärkepegel |
sensor.babycam_helligkeit |
px | babycam/brightness |
Durchschnittliche Bildhelligkeit |
Keine YAML-Konfiguration nötig — die Sensoren registrieren sich selbst via MQTT Discovery.
Einstellungen → Geräte & Dienste → Integration hinzufügen → Generic Camera
| Feld | Wert |
|---|---|
| Still Image URL | http://<babycam-ip>:5000/api/still |
| Stream Source | leer lassen |
| Framerate | 5 |
| Verify SSL | aus |
Alternativ per REST-API (für Skripte):
HA_URL="http://192.168.178.131:8123"
TOKEN="dein-ha-token"
FLOW=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"$HA_URL/api/config/config_entries/flow" \
-d '{"handler":"generic"}' | jq -r '.flow_id')
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"$HA_URL/api/config/config_entries/flow/$FLOW" \
-d '{"still_image_url":"http://192.168.178.130:5000/api/still",
"advanced":{"framerate":5,"verify_ssl":false}}'
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"$HA_URL/api/config/config_entries/flow/$FLOW" \
-d '{"confirmed_ok":true}'ℹ️ MJPEG-Stream (
/video_feed) kann nicht via go2rtc in HA eingebunden werden. Verwende/api/stillfür periodische Standbilder oder eine Webseiten-Karte mit dem vollen BabyCam-UI.
Bei Schrei: Push-Benachrichtigung aufs Handy
automation:
- alias: "BabyCam – Schrei-Alarm"
trigger:
- platform: state
entity_id: binary_sensor.babycam_schreien
to: "on"
action:
- service: notify.mobile_app_mein_handy
data:
title: "🚼 BabyCam"
message: "Baby weint!"
data:
push:
sound: "alert"Bei Bewegung in der Nacht: Nachtlicht einschalten
automation:
- alias: "BabyCam – Nachtlicht bei Bewegung"
trigger:
- platform: state
entity_id: binary_sensor.babycam_bewegung
to: "on"
condition:
- condition: state
entity_id: binary_sensor.babycam_nachtmodus
state: "on"
action:
- service: light.turn_on
target:
entity_id: light.nachtlicht
- delay: "00:05:00"
- service: light.turn_off
target:
entity_id: light.nachtlichtLautstärke-Überwachung mit Schwellwert
automation:
- alias: "BabyCam – Lautstärke-Warnung"
trigger:
- platform: numeric_state
entity_id: sensor.babycam_lautstarke
above: 0.15
action:
- service: persistent_notification.create
data:
title: "🔊 BabyCam"
message: "Lautstärke: {{ states('sensor.babycam_lautstarke') }}"Kamera-Snapshot bei Ereignis speichern
automation:
- alias: "BabyCam – Snapshot bei Schrei"
trigger:
- platform: state
entity_id: binary_sensor.babycam_schreien
to: "on"
action:
- service: camera.snapshot
target:
entity_id: camera.babycam
data:
filename: "/config/www/babycam_schrei.jpg"BabyCam kann Snapshots direkt per Telegram senden — ohne Home Assistant.
- Neuen Bot erstellen: @BotFather →
/newbot - Bot-Token im Web-UI unter ⚙️ Alarm → Telegram eingeben
- Bot in Telegram öffnen und
/startsenden - 🔍 Chat-ID automatisch erkennen klicken
- 💾 Speichern + 📱 Test
Benachrichtigungen werden bei Bewegung, Schrei und Geräusch gesendet — mit Snapshot-Foto und Cooldown (kein Spam).
| Endpoint | Methode | Beschreibung |
|---|---|---|
/ |
GET | Web-Dashboard (volles UI) |
/video_feed |
GET | MJPEG Live-Video-Stream |
/api/still |
GET | JPEG-Standbild (für HA Generic Camera) |
/api/status |
GET | Sensor-Daten: Bewegung, Lautstärke, FPS, Nachtmodus, Uptime |
/api/config |
GET/POST | Konfiguration lesen/schreiben (JSON) |
/api/debug |
GET | Debug-Status: Info + Ereignis-Log (200 Einträge) |
/api/history |
GET | Bewegungs- und Sound-Historie |
/api/snapshots |
GET | Liste aller Snapshots mit Zeitstempel |
/api/snapshot/<file> |
GET | Einzelnes Snapshot-Bild |
/api/audio/latest |
GET | Letzter Audio-Chunk als WAV |
/api/camera/preview |
POST | Kamera-Einstellungen live setzen (ohne Speichern) |
/api/toggle/audio |
POST | Audio-Stream ein/aus |
/api/toggle/video |
POST | Video-Stream ein/aus |
/api/telegram/test |
POST | Telegram-Verbindung testen |
/api/telegram/discover |
POST | Chat-ID automatisch erkennen |
/api/debug Beispiel-Response:
{
"info": {
"audio_device": "plughw:1,0",
"audio_enabled": true,
"has_video_frame": true,
"sound_level": 0.0027,
"cry_detected": false,
"mqtt_connected": true,
"mqtt_broker": "192.168.178.131",
"client_id": "babycam_abc123"
},
"log": [
"[15:03:23] 🎤 Audio-Thread startet: plughw:1,0 @ 16000Hz",
"[15:03:24] ✅ arecord läuft (PID 17)",
"[15:05:00] 📡 MQTT verbunden mit 192.168.178.131:1883"
]
}# Kamera prüfen
ls /dev/video*
v4l2-ctl --device=/dev/video0 --list-formats
# Docker: device-Mount prüfen
docker inspect babycam | grep -A 5 Devices
# Pi Zero: muss "Devices" zeigen, nicht "Binds"!# Mikrofon-Liste
arecord -l
# → Card-Nummer merken
# In der config.json oder via Web-UI anpassen:
# "audio_device": "plughw:X,0"
# Container neustarten
docker compose restart# MQTT-Verbindung prüfen
curl -s http://<babycam-ip>:5000/api/debug | jq .info.mqtt_connected
# → true = verbunden
# Broker-Erreichbarkeit testen
docker exec babycam python3 -c "
import socket; s=socket.socket(); s.settimeout(3)
s.connect(('192.168.178.131', 1883)); print('OK')
"
# Discovery-Topics prüfen (auf dem HA-Pi):
mosquitto_sub -t "homeassistant/+/babycam_pi/#" -vDas ist normal! Der FPS-Regler setzt die Ziel-FPS. Die tatsächliche FPS (fps_actual im Dashboard) ist durch die CPU begrenzt:
| Plattform | Max. FPS |
|---|---|
| Pi 4B | ~15 FPS |
| Pi Zero 2 W | ~5 FPS |
Ziel-FPS unter dem Limit wirkt sofort (z.B. FPS=3 → fps_actual≈3.8). Ziel-FPS über dem Limit bringt nichts (z.B. FPS=30 → fps_actual≈5).
# Logs prüfen
docker logs babycam --tail 50
# Häufige Ursachen:
# - /dev/video0 existiert nicht → docker-compose.pi-one.yml verwenden
# - audio_device falsch → arecord -l prüfen, config.json anpassen
# - MQTT_BROKER nicht erreichbar → IP und Firewall prüfenNach Updates hart neuladen: Strg+Shift+R (Windows/Linux) oder Cmd+Shift+R (Mac). BabyCam setzt Cache-Control: no-cache-Header, aber manche Browser ignorieren das.
cd ~/babycam
git pull
docker build -t babycam:latest .
docker compose down && docker compose up -d
# Pi Zero:
# docker compose -f docker-compose.pi-zero.yml down && docker compose -f docker-compose.pi-zero.yml up -d💡 Konfiguration in
config.jsonwird durch Updates nicht überschrieben — die Datei ist via Volume-Mount persistent.
# Docker-Image exportieren
docker save babycam:latest -o babycam.tar
# Auf anderem System importieren
docker load -i babycam.tar
# Komplettes Setup sichern (ohne Snapshots)
tar -czf babycam-backup-$(date +%Y%m%d).tar.gz \
babycam/ \
--exclude='babycam/snapshots' \
--exclude='babycam/__pycache__'babycam/
├── app.py # Hauptanwendung (Flask + OpenCV + MQTT)
├── templates/
│ └── dashboard.html # Web-Dashboard (Single Page, Dark Mode)
├── static/ # Statische Assets
├── Dockerfile # ARM64 Docker-Image
├── docker-compose.yml # Standard (Pi 4B mit Kamera)
├── docker-compose.pi-zero.yml # Pi Zero (devices statt volumes)
├── docker-compose.pi-one.yml # Pi 4B ohne Kamera (Backup-Instanz)
├── requirements.txt # Python-Abhängigkeiten
├── config.json # Persistente Konfiguration
├── snapshots/ # Automatische Snapshots (nicht in Git)
├── README.md # Diese Datei
└── PI-ZERO-SETUP.md # Detaillierte Pi-Zero-Anleitung
| Komponente | Technologie |
|---|---|
| Webserver | Flask (Python 3.13) |
| Video | OpenCV (cv2), MJPEG, v4l2-ctl |
| Audio | ALSA (arecord), NumPy FFT/RMS |
| Motion Detection | Frame-Differencing mit Gauß-Filter |
| MQTT | paho-mqtt (MQTT v5, Auto-Discovery) |
| Telegram | Bot API (HTTP, async Queue) |
| Frontend | Vanilla JS, Web Audio API, Dark Mode CSS |
| Container | Docker, ARM64, piwheels |
MIT — siehe LICENSE
Made with ❤️ for parents who want privacy.
{ // ── Bewegungserkennung ── "motion_threshold": 12, // Empfindlichkeit (2–50, niedriger = sensibler) "motion_min_area": 800, // Minimale Bewegungsfläche in px² "motion_duration_min": 8, // Sekunden anhaltende Bewegung für Snapshot "alert_cooldown_motion": 30, // Sekunden Pause zwischen Bewegungs-Alarmen // ── Geräuscherkennung ── "sound_threshold": 0.04, // RMS-Lautstärkeschwelle (0.01 = Flüstern) "cry_freq_low": 300, // Untere Frequenz für Schreien (Hz) "cry_freq_high": 800, // Obere Frequenz für Schreien (Hz) "cry_duration": 1.5, // Sekunden anhaltendes Schreien für Alarm "noise_alert_enabled": true, // Auch auf andere Geräusche alarmieren "noise_duration": 1.0, // Sekunden anhaltendes Geräusch "alert_cooldown_cry": 60, // Sekunden Pause zwischen Schrei-Alarmen // ── Kamera ── "camera_device": 0, // /dev/videoX (über Web-UI-Dropdown wählbar) "frame_width": 640, // Bildbreite "frame_height": 480, // Bildhöhe "fps": 15, // Ziel-FPS (Pi Zero: max 5) // ── Kamera-Bildsteuerung ── "camera_auto": true, // Auto-Modus (ignoriert manuelle Werte) "camera_brightness": 128, // 0–255 "camera_contrast": 128, // 0–255 "camera_saturation": 128, // 0–255 "camera_sharpness": 128, // 0–255 "camera_gain": 0, // 0–100 // ── Audio ── "audio_device": "plughw:3,0", // über Web-UI-Dropdown wählbar "audio_rate": 16000, // Samplerate (8000–48000 Hz) // ── Modi ── "night_mode_auto": true, // Automatischer Nachtmodus "night_brightness_threshold": 40, // Helligkeit < 40 = Nacht "snapshot_quality": 75, // JPEG-Qualität (1–100) "video_enabled": true, // Live-Video-Stream "audio_enabled": true, // Live-Audio & Pegel // ── Telegram ── "telegram_enabled": false, "telegram_bot_token": "", "telegram_chat_id": "" }