Files
vogelalbum/detector/umgebung.py
T
christian cc9e0786c2 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.
2026-10-14 18:55:23 +00:00

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