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:
commit
cc9e0786c2
189 files changed
+29792
No files matched your search
+160
@@ -0,0 +1,160 @@
|
||||
# Betrieb
|
||||
|
||||
<sub>[← zurück zum Überblick](../README.md) · [alle Kapitel](README.md)</sub>
|
||||
|
||||
Live zuschauen, mehrere Kameras, der Detector auf einem eigenen Host und das
|
||||
Deployen der Web-App. Also alles, was nach dem ersten erkannten Vogel kommt.
|
||||
|
||||
## Live zuschauen
|
||||
|
||||
Eine Kamera liefert **einen** Stream. Wer direkt bei ihr zuschaut, nimmt ihn
|
||||
der Erkennung weg — auf einem Pi Zero W ist das keine Einstellung, sondern
|
||||
Physik: die Funkstrecke trägt rund 2 Mbit/s, ein zweiter Client sättigt sie.
|
||||
|
||||
Der Detector liest den Stream aber ohnehin rund um die Uhr. Also verteilt er
|
||||
ihn weiter:
|
||||
|
||||
```bash
|
||||
python -m detector watch --source http://kamera:8080/stream --live-port 8131
|
||||
```
|
||||
|
||||
Damit schauen beliebig viele Leute zu, und die Kamera merkt nichts davon — sie
|
||||
hat weiterhin genau einen Zuschauer. Solange niemand hinsieht, wird auch nichts
|
||||
umkodiert: bei MJPEG-Quellen gehen die JPEG-Bytes unverändert durch, und das
|
||||
Kodieren für andere Quellen passiert im HTTP-Thread, einmal je Bild statt
|
||||
einmal je Zuschauer. Die Erkennung wird davon nicht langsamer.
|
||||
|
||||
| Adresse | wozu |
|
||||
|---|---|
|
||||
| `http://<detector>:8131/live` | MJPEG, für mehrere Zuschauer |
|
||||
| `http://<detector>:8131/bild.jpg` | Einzelbild |
|
||||
| `http://<detector>:8131/` | Seite mit Livebild |
|
||||
|
||||
### In der Web-App
|
||||
|
||||
Eingetragen wird das **im Django-Admin bei der Kamera**: `Live-Adresse` auf
|
||||
`http://<detector>:8131/live`, dann `Live-Bild zeigen` ankreuzen. Danach gibt
|
||||
es die Seite `/live/` und einen Eintrag in der Navigation. Ohne Freigabe
|
||||
existiert beides nicht — kein Link, keine Seite, 404.
|
||||
|
||||
Das Bild lädt der Browser direkt beim Verteiler; die Web-App reicht nichts
|
||||
durch. Sonst hinge an jedem Zuschauer eine offene Verbindung im Webserver.
|
||||
|
||||
> **Für eine öffentliche Instanz: `VOGELALBUM_LIVE_ERLAUBT=0`.** Dann ist Live
|
||||
> nicht einschaltbar, auch nicht mit einem Fehlklick im Admin, und die
|
||||
> Admin-Liste zeigt „gesperrt". Ein Livebild aus einem Privatgarten soll nicht
|
||||
> an einem Häkchen hängen — für einen Showcase ist die Sammlung ohnehin das
|
||||
> Interessantere.
|
||||
|
||||
Der Verteiler selbst kennt keine Anmeldung. Er gehört ins Heimnetz.
|
||||
|
||||
## Mehrere Kameras
|
||||
|
||||
Jede Kamera bekommt einen eigenen Token. Der Token identifiziert sie — die
|
||||
Web-App muss nie wissen, wo sie steht, und ein kompromittierter Standort lässt
|
||||
sich einzeln stilllegen, ohne die anderen anzufassen.
|
||||
|
||||
```bash
|
||||
manage.py camera add futterhaus --name "Futterhaus Garten" --location "Garten"
|
||||
manage.py camera add nistkasten --name "Nistkasten Apfelbaum"
|
||||
manage.py camera list
|
||||
manage.py camera rotate futterhaus # Token erneuern
|
||||
manage.py camera disable nistkasten
|
||||
```
|
||||
|
||||
Der Token wird **genau einmal** ausgegeben; gespeichert ist nur sein SHA-256.
|
||||
Bei 256 Bit Entropie braucht es kein Salt, und wer die Datenbank in die Hand
|
||||
bekommt, kann damit trotzdem nichts einliefern.
|
||||
|
||||
Pro Kamera läuft eine eigene `detector watch`-Instanz. Sie können auf
|
||||
demselben Host laufen; rechne mit rund 40 % eines Kerns je Kamera bei
|
||||
`--classify-every 2` und 4 fps.
|
||||
|
||||
Die Sammlung ist dabei **gemeinsam**: Findet die Gartenkamera die erste Amsel,
|
||||
ist sie im Dex freigeschaltet — die Nistkastenkamera bekommt für dieselbe Art
|
||||
keinen zweiten Erstfang. Das ist Absicht, es ist die Sammlung eines Menschen
|
||||
und nicht die einer Kamera. Die Sichtungsliste lässt sich trotzdem nach Kamera
|
||||
filtern.
|
||||
|
||||
`manage.py camera list` markiert Kameras, die sich über eine Stunde nicht
|
||||
gemeldet haben — der schnellste Weg zu „warum kommt da nichts mehr".
|
||||
|
||||
## Betrieb
|
||||
|
||||
### Detector auf einem eigenen Host
|
||||
|
||||
Ein frischer Debian- oder Ubuntu-Container, dann:
|
||||
|
||||
```bash
|
||||
sudo ./scripts/setup_detector_host.sh
|
||||
```
|
||||
|
||||
Das Skript legt den Dienstbenutzer `vogelalbum` an, klont nach `/opt/vogelalbum`,
|
||||
baut das venv, holt die Modelle, schreibt `/etc/vogelalbum.env` und installiert
|
||||
die systemd-Unit. Idempotent — ein zweiter Lauf aktualisiert nur.
|
||||
|
||||
Danach `/etc/vogelalbum.env` ausfüllen und `systemctl enable --now
|
||||
vogelalbum-detector`.
|
||||
|
||||
**Dimensionierung**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| CPU | 1 Kern je Kamera (bei `--classify-every 2` und ~4 fps rund 40 % Last) |
|
||||
| RAM | 512 MB je Instanz, 1 GB für Luft |
|
||||
| Platte | 4 GB fürs System, plus ~150 kB je gespeicherter Sichtung |
|
||||
|
||||
**Netz:** ausgehend zur Kamera und zur Web-App. **Eingehend nichts.**
|
||||
|
||||
Achtung, wenn Kamera und Erkennungs-Host in verschiedenen Netzsegmenten
|
||||
liegen: Die Route muss stehen. Bei uns hängt die Kamera im WLAN-Segment und
|
||||
der Server im LAN — ein Scan des falschen Segments findet sie schlicht nicht.
|
||||
|
||||
**Zweite Kamera:** eigene `/etc/vogelalbum-<name>.env`, die Unit als
|
||||
`vogelalbum-detector@.service` ablegen, dann `systemctl enable --now
|
||||
vogelalbum-detector@nistkasten`.
|
||||
|
||||
## Die Web-App deployen
|
||||
|
||||
Nur die Web-App wird deployt. Der Detector bleibt im Heimnetz — er braucht
|
||||
Sichtverbindung zur Kamera.
|
||||
|
||||
1. DNS auf den Host zeigen lassen.
|
||||
2. Anwendung aus dem Repo anlegen, Build über `docker-compose.yml`.
|
||||
⚠️ **Der Eingang ist `medien` auf Port 8080, nicht `web` auf 8000.** Das
|
||||
compose startet zwei Dienste: Gunicorn und einen nginx davor, der die
|
||||
Vogelfotos vom Volume ausliefert. Zeigt der Reverse Proxy direkt auf
|
||||
`web`, lädt die Seite — aber **jede Karte der Sammlung hat ein kaputtes
|
||||
Bild**, denn Django liefert unter `DJANGO_DEBUG=0` keine Medien aus.
|
||||
Begründung ausführlich in [deploy/nginx-media.conf](../deploy/nginx-media.conf).
|
||||
3. Persistentes Volume auf `/data` mounten. **Das ist der Schritt, den man
|
||||
vergisst** — ohne ihn löscht jeder Deploy Sammlung und Fotos.
|
||||
4. Umgebungsvariablen setzen (siehe [Einstellungen](einstellungen.md)).
|
||||
5. Denselben Token auf dem Erkennungs-Host eintragen und `VOGELALBUM_API` auf die
|
||||
öffentliche Adresse setzen.
|
||||
6. Auf einer öffentlichen Instanz `VOGELALBUM_LIVE_ERLAUBT=0` setzen — steht im
|
||||
`docker-compose.yml` schon drin. Ohne die Angabe wäre das Livebild aus dem
|
||||
eigenen Garten erlaubt, denn der Standard im Code ist `1`.
|
||||
|
||||
Der erste Start dauert ein paar Minuten: Der Entrypoint holt einmalig die
|
||||
Referenzfotos. Danach kostet der Schritt nichts mehr. `migrate`,
|
||||
`collectstatic` und `seed_dex` erledigt er ebenfalls — neue Arten im Katalog
|
||||
landen also mit dem nächsten Deploy automatisch in der Datenbank.
|
||||
|
||||
📘 **Schritt für Schritt mit Coolify, inklusive Umzug einer bestehenden
|
||||
Sammlung: [deploy/COOLIFY.md](../deploy/COOLIFY.md).** Dort steht auch, wie
|
||||
Datenbank und Fotos als 1:1-Kopie auf das Volume kommen — Kamera-Tokens,
|
||||
Handbestimmungen und Abzeichen bleiben dabei erhalten, und die Kameras zu
|
||||
Hause liefern mit ihrem bisherigen Token weiter.
|
||||
|
||||
### Einbinden auf einer anderen Seite
|
||||
|
||||
`/widget/` ist dafür da — keine Navigation, transparenter Hintergrund:
|
||||
|
||||
```html
|
||||
<iframe src="https://vogelalbum.example.org/widget/"
|
||||
style="width:100%;height:110px;border:0" loading="lazy"></iframe>
|
||||
```
|
||||
|
||||
Wer lieber selbst gestaltet, nimmt `/api/stats/` — Level, Artenzahl und die
|
||||
letzte Sichtung als JSON.
|
||||
Reference in new issue
Block a user