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.
43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""Umgebungsvariablen lesen.
|
|
|
|
Alle Einstellungen heißen ``VOGELALBUM_<NAME>``.
|
|
|
|
Diese Datei kennt bewusst **keine** Django-Abhängigkeit — sie wird vom
|
|
Detektor, von der Web-App und von der Home-Assistant-Brücke gleichermaßen
|
|
benutzt, und die drei haben nicht dieselben Pakete installiert.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
PRAEFIX = "VOGELALBUM"
|
|
|
|
|
|
def hole(name: str, default: str | None = None) -> str | None:
|
|
"""Liest ``VOGELALBUM_<name>``.
|
|
|
|
``name`` ist der Teil hinter dem Präfix, also ``"SOURCE"`` für
|
|
``VOGELALBUM_SOURCE``.
|
|
|
|
🔑 **Ein ausdrücklich leerer Wert schlägt den Vorgabewert.** Das ist kein
|
|
Zufall, sondern trägt eine Bedeutung: Ein leerer Ingest-Token heißt
|
|
„Endpunkt zu", nicht „nimm den Standard". Wer hier ``or default``
|
|
schriebe, machte aus einer bewussten Abschaltung stillschweigend wieder
|
|
eine Öffnung.
|
|
"""
|
|
wert = os.environ.get(f"{PRAEFIX}_{name}")
|
|
if wert is None:
|
|
return default
|
|
return wert
|
|
|
|
|
|
def gesetzt(name: str) -> bool:
|
|
"""Ob die Variable überhaupt vorhanden ist.
|
|
|
|
Nötig, weil ein **leerer** Wert etwas anderes bedeutet als ein fehlender:
|
|
Ein leerer Ingest-Token schaltet den Endpunkt ab, ein fehlender lässt den
|
|
Vorgabewert gelten.
|
|
"""
|
|
return os.environ.get(f"{PRAEFIX}_{name}") is not None
|