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-15 14:30:02 +00:00
commit c11fe9ab04
189 files changed
+29806

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
{% extends "dex/base.html" %}
{% load i18n dex_extras %}
{% block title %}{% trans "Abzeichen" %} — Vogelalbum{% endblock %}
{% block kopf %}
<style>
/* ⚠ Eigene Klassen statt der xp-track vom Kopfbereich: Die ist dort
breit und hoch, hier soll der Balken in eine Abzeichenkarte passen
ohne sie auseinanderzuziehen. */
.fortschritt {
margin-top: 8px;
height: 6px;
border-radius: 999px;
background: color-mix(in srgb, var(--text) 12%, transparent);
overflow: hidden;
}
.fortschritt-fuellung {
height: 100%;
border-radius: 999px;
background: var(--accent);
/* Auch bei 0 % ein Hauch sichtbar -- sonst sieht die Karte aus,
als fehlte der Balken. */
min-width: 3px;
}
.fortschritt-text {
margin-top: 4px;
font-variant-numeric: tabular-nums;
}
/* Die Karte ist abgeblendet, solange das Abzeichen fehlt. Der Balken
soll trotzdem lesbar bleiben -- er ist ja der Grund hinzuschauen. */
.badge.locked .fortschritt-fuellung { opacity: .85; }
</style>
{% endblock %}
{% block content %}
<h2>{% trans "Abzeichen" %} <span class="count">{% blocktrans with n=progress.badges %}{{ n }} verdient{% endblocktrans %}</span></h2>
<div class="badges">
{% for row in rows %}
<div class="badge {% if not row.earned %}locked{% endif %}">
{% abzeichen_bild row.badge.code as pin %}
{% if pin %}
<img class="em" src="{{ pin }}" alt="" width="72" height="72" loading="lazy">
{% else %}
<div class="em">{{ row.badge.emoji }}</div>
{% endif %}
<div>
<div class="n">{{ row.badge.name_lokal }}</div>
<div class="d">{{ row.badge.description_lokal }}</div>
<div class="d" style="margin-top:6px; color:var(--accent-dim); font-weight:700">
{% if row.earned %}{{ row.at|date:"SHORT_DATE_FORMAT" }} · +{{ row.badge.xp }} XP{% else %}+{{ row.badge.xp }} XP{% endif %}
</div>
{% comment %}
🔑 Balken nur, wo es etwas zu zaehlen gibt. Bei "Ein Specht war da"
stuende er dauerhaft auf null und waere keine Information.
⚠️ {# … #} ist in Django EINZEILIG — mehrzeilig braucht es comment,
sonst steht der Text sichtbar auf der Seite.
{% endcomment %}
{% if row.ziel %}
<div class="fortschritt" title="{{ row.ist }} von {{ row.ziel }}">
<div class="fortschritt-fuellung" style="width: {{ row.anteil }}%"></div>
</div>
<div class="d fortschritt-text">{{ row.ist }} / {{ row.ziel }}</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endblock %}