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
@@ -0,0 +1,98 @@
|
||||
{% extends "dex/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Sammlung" %} — Vogelalbum{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Die Sammlung" %}
|
||||
<span class="count">
|
||||
{% blocktrans with a=found_count b=total_count %}{{ a }} von {{ b }} Arten{% endblocktrans %}{% if habitat_label %} · {{ habitat_label }}{% endif %}
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
{% comment %}
|
||||
Lebensraum ist die Hauptachse und bleibt sichtbar. Seltenheit und Gruppe
|
||||
sind Verfeinerungen und gehören in Auswahlfelder — 27 Typen als Pillen
|
||||
waren auf dem Handy zwölf Zeilen, bevor der erste Vogel kam.
|
||||
{% endcomment %}
|
||||
<div class="filterleiste">
|
||||
{% if habitate %}
|
||||
<div class="filtergruppe">
|
||||
<span class="filterlabel">{% trans "Lebensraum" %}</span>
|
||||
<div class="pills">
|
||||
{% for h in habitate %}
|
||||
<a href="?lebensraum={{ h.code }}" {% if habitat == h.code %}class="on"{% endif %}>{{ h.label }} <b>{{ h.n }}</b></a>
|
||||
{% endfor %}
|
||||
<a href="?lebensraum=alle" {% if not habitat %}class="on"{% endif %}>{% trans "Alle" %} <b>{{ gesamt_alle }}</b></a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form class="filtergruppe verfeinern" method="get" action="{% url 'dex' %}">
|
||||
<input type="hidden" name="lebensraum" value="{{ habitat|default:'alle' }}">
|
||||
|
||||
<label>
|
||||
<span class="filterlabel">{% trans "Stufe" %}</span>
|
||||
<select name="seltenheit" onchange="this.form.submit()">
|
||||
<option value="">{% trans "Alle Stufen" %}</option>
|
||||
{% for value, label in rarities %}
|
||||
<option value="{{ value }}" {% if rarity_filter == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span class="filterlabel">{% trans "Gruppe" %}</span>
|
||||
<select name="typ" onchange="this.form.submit()">
|
||||
<option value="">{% trans "Alle Gruppen" %}</option>
|
||||
{% for t in typen %}
|
||||
<option value="{{ t.code }}" {% if typ_filter == t.code %}selected{% endif %}>{{ t.label }} ({{ t.n }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<noscript><button type="submit">{% trans "Anwenden" %}</button></noscript>
|
||||
|
||||
{% if rarity_filter or typ_filter %}
|
||||
<a class="zuruecksetzen" href="?lebensraum={{ habitat|default:'alle' }}">{% trans "Zurücksetzen" %}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if cards %}
|
||||
<div class="grid">
|
||||
{% for card in cards %}
|
||||
{% with s=card.species %}
|
||||
<a class="card {% if not card.found %}locked{% endif %}"
|
||||
href="{% url 'species' s.slug %}"
|
||||
style="{{ s.typ_vars }}"
|
||||
{% if not card.found %}title="{% trans "Noch nicht gesichtet" %}"{% endif %}>
|
||||
<span class="nr">{{ s.dex_nr }}</span>
|
||||
<div class="thumb">
|
||||
{% if card.found and card.entry.best_sighting and card.entry.best_sighting.kachel_url %}
|
||||
<img src="{{ card.entry.best_sighting.kachel_url }}" alt="{{ s.name }}" loading="lazy">
|
||||
{% elif s.reference_image %}
|
||||
<img src="{{ s.reference_image.url }}" alt="{{ s.name }}" loading="lazy">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="name">{{ s.name }}</div>
|
||||
<div class="meta">
|
||||
{% if s.typ %}<span class="pill">{{ s.typ_label }}</span>{% endif %}
|
||||
{% if card.found %}
|
||||
<span class="times">×{{ card.entry.count }}</span>
|
||||
{% else %}
|
||||
<span class="times">+{{ s.xp }} XP</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">{% trans "Keine Art passt zu dieser Auswahl." %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user