Vogelalbum 1.0.0

Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine
Sammlung, die sich von allein fuellt.

Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die
Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert —
ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische
Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten,
gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte,
Stufen und 15 Abzeichen.

Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto,
keine Telemetrie; die Bilder verlassen das eigene Netz nicht.

Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die
Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und
braucht deshalb weder eine erreichbare Adresse noch einen offenen Port —
er laeuft hinter einem gewoehnlichen Router.

Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home
Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen
je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs.

AGPL-3.0.
This commit is contained in:
christian committed 2026-10-14 18:55:23 +00:00
commit cc9e0786c2
189 files changed
+29792

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
# Vogelalbum in Home Assistant
Zwei Teile:
```
GET /api/zustand/ in der Web-App — alles in einer Antwort
bruecke.py holt das und spiegelt es nach MQTT
```
Home Assistant braucht **kein Integrationspaket**: Die Brücke meldet die
Entitäten über MQTT-Discovery an, HA legt sie von selbst an.
## Voraussetzung: ein Broker
⚠️ **Ohne MQTT-Broker geht nichts.** In Home Assistant:
```
Einstellungen → Add-ons → Add-on Store → "Mosquitto broker"
→ Installieren → Starten
```
Dann einen Benutzer anlegen (Mosquitto nimmt HA-Benutzer als
Zugangsdaten, es hat kein eigenes Benutzersystem):
```
Einstellungen → Personen → Benutzer → Hinzufügen
⚠ "Kann sich anmelden" an, Administrator AUS
```
🔑 **Dass es einen Broker braucht, ist der schwache Punkt dieses Weges.**
Wer Vogelalbum verteilt,
muss damit rechnen, dass viele Nutzer ebenfalls keinen haben. Deshalb ist
MQTT der Weg zum Ausprobieren, nicht der endgültige; eine Integration über
HACS käme ohne aus. Siehe Issue #4.
## Starten
```sh
VOGELALBUM_URL=http://sammlung.local:8000 \
VOGELALBUM_LESE_TOKEN=… \
MQTT_HOST=… MQTT_BENUTZER=vogelalbum MQTT_PASSWORT=… \
python homeassistant/bruecke.py
```
| Variable | Vorgabe | wofür |
|---|---|---|
| `VOGELALBUM_URL` | `http://127.0.0.1:8130` | wo die Web-App steht |
| `VOGELALBUM_LESE_TOKEN` | — | **Pflicht.** Leer heißt: Tür zu |
| `VOGELALBUM_INSTANZ` | `garten` | steckt in jeder Kennung |
| `VOGELALBUM_TAKT` | `10` | Sekunden zwischen zwei Abfragen |
| `MQTT_HOST` / `MQTT_PORT` | `127.0.0.1` / `1883` | der Broker |
| `MQTT_BENUTZER` / `MQTT_PASSWORT` | leer | falls der Broker sie verlangt |
| `VOGELALBUM_LIVE_<KENNUNG>` | leer | Livebild direkt vom Verteiler holen |
## Livebild, wenn die Web-App außer Haus steht
🔑 **Ein Riegel gegen die Öffentlichkeit darf nicht das eigene Haus
aussperren.** Steht die Web-App im Netz, gehört dort
`VOGELALBUM_LIVE_ERLAUBT=0` hin — ein Bild aus dem eigenen Garten ist nichts
für Fremde. Dann liefert `/api/zustand/` aber `bild_url: null`, und die
Brücke bekommt ebenfalls nichts, obwohl sie **im Heimnetz** läuft und den
Live-Verteiler des Detectors direkt erreicht.
Deshalb darf die Brücke die Adresse selbst kennen, je Kamera:
```sh
VOGELALBUM_LIVE_VOGELHAUS=http://erkennung.local:8131/bild.jpg
VOGELALBUM_LIVE_TRAENKE=http://erkennung.local:8132/bild.jpg
```
Der Name entsteht aus der **Kennung** der Kamera in Großbuchstaben; alles,
was kein Buchstabe und keine Ziffer ist, wird zum Unterstrich
(`vogelhaus-2` → `VOGELALBUM_LIVE_VOGELHAUS_2`). Eine Adresse auf `/live`
wird selbst auf `/bild.jpg` umgeschrieben — `/live` ist der endlose
MJPEG-Strom, den darf die Brücke nicht lesen. Ohne Eintrag bleibt alles wie
bisher: Die Adresse kommt aus der Schnittstelle.
⚠️ **So sieht dieser Ausfall aus, wenn man ihn nicht kennt:** In Home
Assistant bleibt das zuletzt empfangene Bild stehen und sieht aus wie ein
Livebild. Es gibt keine Fehlermeldung — weder in der Brücke noch in HA.
Eine Anlage lief so acht Tage lang, ohne dass es jemandem auffiel.
## Was rauskommt
26 Entitäten bei zwei Kameras, **11 davon standardmäßig abgeschaltet**.
Die Linie ist *„möglichst viel anbieten, entscheiden soll der Nutzer"* — `enabled_by_default: false` ist genau dafür da.
**Sammlung:** letzte Art · Arten gesammelt · Sichtungen heute und gesamt ·
Stufe · Rang · Punkte · Abzeichen · letztes Abzeichen · letzte Sichtung
(mit allen Angaben als Attribute)
**Je Kamera:** erreichbar · still seit · letzte Sichtung · Signalstärke ·
Chiptemperatur · Bildrate · Laufzeit · Livebild
## Drei Dinge, die man wissen muss
⚠️ **Die Kennungen sind der Vertrag.** Anzeigenamen darf jeder ändern, die
`unique_id` nicht. Wer sie nachträglich ändert, bekommt in HA eine NEUE
Entität, während die alte als Leiche im Register bleibt — mitsamt den
Automationen, die daran hängen.
⚠️ **Das Livebild kommt vom DETECTOR, nie von der Kamera.** Die Kamera
erlaubt genau einen Zuschauer; klinkt sich HA dort ein, verliert der
Detector sein Bild und die Arterkennung steht. Das Symptom wäre *„seit ich
das Dashboard gebaut habe, erkennt er nichts mehr"* — mit der Ursache
woanders.
⚠️ **Signalstärke und Temperatur holt die Brücke direkt vom Gerät**, nicht
über die Web-App. Sie kennt nur das Gerät selbst; über die Datenbank zu
gehen hieße, sie erst zu speichern, und dann wären sie alt.
Der Port dafür wird **probiert und gemerkt**, nicht geraten: erst
Streamport + 1 (der ESP32 hat einen eigenen Diagnose-Port, weil sein
Webserver einen einzigen Arbeiter hat), dann der Streamport (der Pi hat das
Problem nicht).
📌 **Erledigt:** Der Pi am Futterhaus antwortete am 25.09.2026 noch mit 404
auf `/status` — dort lief ältere Software ohne den Endpunkt. Seit dem
Rollout am 09.10.2026 liefert er ihn; am 11.10.2026 nachgemessen
(4,63 B/s, RSSI −51 dBm, 39 °C). Signalstärke und Temperatur stehen damit
für **beide** Kameras in Home Assistant.