The "grey box around the icons" report turned out not to be a defect: the box on "10.000 km"/"1 Jahr"/"15" is the wanted pattern, and every value the user can change should carry it. All .feld inputs and selects now get the grey iOS fill; inputs with their own visual language (checkbox/radio/range/file) keep the transparent base rule, so the switches are unaffected. Fuel stations are now displayed as "Shell, Pascalstr. 8, Ingolstadt" instead of the bare operator name. New _marke()/_ist_strasse()/_tankstelle() helpers are shared by both parser paths so Shell and non-Shell receipts format the same. The brand is only matched against the receipt header - searching the whole text would let "Total" hit a totals line. Missing parts are dropped instead of leaving empty comma slots, and without a known brand the operator name takes its place. station_address still carries the full street and postcode. test_bekannte_stationen updated to the new format; suite stays green. installationspaket/ is versioned from now on (user request). It contains no real vehicle data - only the example profile with empty FIN/plate placeholders. Keep it in sync whenever pyscript/ or www/ changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Audi-Dashboard — Backend und Frontend, Stand nach dritter Bauphase
Dieser Ordner enthält Backend und Frontend aus dem Lastenheft
(bauauftrag.md): das pyscript-Backend für Fahrterkennung, Fahrtabschluss,
Reifenzähler und Belegverarbeitung, plus der zum panel_custom-Custom-
Element umgebaute Prototyp.
Installation: siehe INSTALL.md — Schritt für Schritt,
inklusive der einzigen Stelle, die inhaltlich angepasst werden muss
(pyscript/modules/einstellungen.py, Entity-IDs).
Getestet, nicht nur geprüft
Beides lief in einer Wegwerf-Testinstanz (Home Assistant in Docker), nicht nur gegen Dokumentation abgeglichen:
- Backend: alle Module laden fehlerfrei, alle Trigger und Services
registrieren sich korrekt. Zwei echte Service-Aufrufe über die HA-API
haben tatsächlich Daten geschrieben —
audi_dashboard_reifen_wechselnhatfahrzeugprofil.jsonvon „sommer" auf „winter" umgestellt,audi_dashboard_fahrt_manuell_anlegenhat einen vollständigen Datensatz anfahrten.jsonlangehängt. - Frontend: das Custom Element wurde mit genau diesen echten Backend-Daten gefüttert (per REST abgerufen) und hat daraus korrekt die Übersicht und die Fahrtenliste gerendert — inklusive der zuvor umgeschalteten Winterbereifung, sichtbar an der Fahrbahn-Szene, und der manuell angelegten Testfahrt in der Fahrten-Ansicht.
Dabei wurden drei echte pyscript-Eigenheiten gefunden und behoben, die in keiner Dokumentation standen:
task.executor()akzeptiert nur echte externe Python-Funktionen (z. B.io.open), keine im pyscript-Ordner selbst definierten.- Variablen aus einem
with ... as f:-Block waren danach außerhalb nicht mehr auffindbar (NameError). - Das eingebaute
open()existiert in pyscript nicht.
Eine Lücke bleibt: die Testinstanz ließ keine WebSocket-Verbindung zu
(Einschränkung der Testumgebung, nicht des Codes). Der letzte Schritt — dass
panel_custom das Element beim normalen Draufklicken in der Sidebar selbst
erzeugt und mit hass versorgt — läuft über genau diese Verbindung und
ließ sich deshalb nicht End-to-End nachstellen. panel_custom ist aber
Home Assistants eigener, langjährig stabiler Mechanismus, nicht eigener
Code — das Risiko dort ist gering, verglichen mit dem, was schon getestet
werden konnte.
Persönliche Daten
Alles Fahrzeug- und Personenspezifische (VIN, Kennzeichen, Versicherung,
Werkstattkontakt, Servicehistorie, Fahrten, Tankvorgänge) liegt ausschließlich
in data/ und wird nie im App-Code (pyscript/, www/) fest hinterlegt —
data/fahrzeugprofil.json, data/fahrten.jsonl, data/tankvorgaenge.jsonl
sowie die echten Tankbeleg-PDFs unter data/tests/belege/ sind deshalb per
.gitignore von der Versionierung ausgeschlossen. data/fahrzeugprofil. example.json ist die getrackte Vorlage ohne echte Werte für eine
Neuinstallation (siehe INSTALL.md Schritt 2); die Kernangaben lassen sich
danach direkt in der App unter Einstellungen → Fahrzeug einrichten
eintragen, ohne die JSON-Datei von Hand zu bearbeiten. Das Backup
(Einstellungen → Backup) exportiert ausschließlich diese drei
Datendateien — nie App-Code oder -Konfiguration.
Danach noch offen
- Reifen-Kilometerstände (
reifen.saetze.sommer/winter.km) laufen seit der Frontend-Anpassung automatisch mit: jeder gefahrene Kilometer zählt auf den Satz, der lautreifen.aktivgerade montiert ist (reifenzaehler.py, reagiert aufKM_SENSOR). Beide starten bei0— Laufleistung von vor der Installation muss einmalig direkt im Profil nachgetragen werden. - Kfz-Steuer-Fälligkeit:
steuer.faelligsteht aufnull, aus §13 noch offen. - shell_beleg_parser.py fehlte im Projektordner beim Bau und wurde
nachträglich neu geschrieben — liegt jetzt unter
data/shell_beleg_parser.pyund landet mit demdata-Ordner automatisch unter/config/audi_dashboard/shell_beleg_parser.py(siehe INSTALL.md Schritt 2 und Schritt 8). Braucht dort einmaligpip install pypdfim System-python3des Containers (eigener Prozess, nicht die pyscript-Sandbox). Geprüft gegen zehn echte Mobile-Payment-Belege aus vier verschiedenen Stationen (Ingolstadt/ Zrenner, Rain am Lech/Bauch, Ansbach/Sengül, Königsbronn/Mogler) — die Erkennung ist bewusst nicht an Markenzeilen-Wortlaut ("SHELL STATION" vs. "Shell-Station") oder Rabatt-Label ("V-Power Smart Deal" vs. "ClubSmartRabatt") gebunden, siehe Regressionstest unterdata/tests/test_shell_beleg_parser.py(Aufruf:python3 data/tests/test_shell_beleg_parser.py). Andere Zahlungswege als Mobile Payment oder Belege mit mehreren Positionen (z. B. zusätzlicher Autowäsche-Posten mit abweichendem MwSt.-Satz) sind nicht durch reale Belege abgedeckt und müssten bei Bedarf nachgezogen werden. - Tailscale: „VPN On Demand" mit Regel „Only On", gebunden an
Audi_MMI_2804_5GHz, muss in der Tailscale-App selbst eingerichtet werden (§9, §10 Punkt 8) — das lässt sich nicht von hier aus mitinstallieren. - GPS-Fallback (§7.2) ist nicht umgesetzt. Fahrten ohne passenden
Kilometerstand bleiben aktuell dauerhaft
offen, statt auf die GPS-Strecke auszuweichen. Erfordert einedevice_tracker-Anbindung und Adressauflösung. - Fahrterkennung übersteht keinen HA-Neustart mitten in einer Fahrt oder
Wartezeit — der Zwischenstand lebt nur im Arbeitsspeicher von
fahrterkennung.py. - Bilder fehlen (§7a) — Ordner
www/bilder/ist noch leer. Layout springt nicht (Maße sind reserviert), aber die Flächen bleiben leer. - Leaflet lädt per CDN, wie im Prototyp — braucht zusätzlich zu Tailscale echten Internetzugang auf dem Gerät.
Was schon funktioniert (getestet)
- Fahrterkennung über den Zündungssensor mit Pausenregel (
task.unique+task.sleep); bis 2026-08-12 lief sie über den WLAN-Sensor des iPhones - Zweistufiger Fahrtabschluss per Recorder-Historie-Screening, inklusive Verkettung direkt anschließender Fahrten
- Reifenzähler per direkter Subtraktion, kein
utility_meter - Belegverarbeitung mit Duplikatserkennung über
receipt_key(die zunächst geplante 97-%-Volltankungsregel wurde per Änderungswunsch entfernt, siehe Kopfkommentar inpyscript/belegverarbeitung.py) - Manuelle Fallback-Wege für Fahrten und Tankvorgänge ohne Beleg
- Frontend: Übersicht, Fahrtenliste, Navigation zwischen allen Ansichten,
Datenadapter Profil→Oberfläche, Schreibaktionen über
hass.callService - Statistik-Seite mit echter Auswertung aus Fahrten und Tankvorgängen (Zeiträume, Verbrauch, Tag/Nacht, privat/Arbeitsweg)
- Setup-Menü für die Sensor-Zuordnung, inklusive Sicherung in
data/entitaeten.json
Dateiübersicht
homeassistant/
├── INSTALL.md Schritt-für-Schritt-Installation
├── README.md diese Datei
├── configuration_snippet.yaml Ergänzung zur configuration.yaml
├── data/
│ ├── fahrzeugprofil.json Fahrzeugprofil (§6.1), mit echten Daten befüllt — gitignored
│ ├── fahrzeugprofil.example.json Vorlage ohne echte Daten, bleibt getrackt (siehe INSTALL.md Schritt 2)
│ ├── fahrten.jsonl Fahrten-Archiv (§6.1) — gitignored
│ └── tankvorgaenge.jsonl Tankvorgänge-Archiv (§6.1) — gitignored
├── pyscript/
│ ├── modules/
│ │ ├── einstellungen.py zentrale Entity-ID-Konfiguration (Schritt 4)
│ │ ├── profil.py Datenzugriff (§6)
│ │ ├── entitaeten.py Sensor-Zuordnung aus dem Setup-Menü
│ │ ├── fahrtabschluss_logik.py Screening-Logik (§7.2)
│ │ └── frontend_veroeffentlichung.py Zustände fürs Frontend (§10 Punkt 7 Ersatz)
│ ├── fahrterkennung.py §7.1 (Zündungssensor, früher WLAN)
│ ├── fahrtabschluss.py §7.2 (Trigger-Registrierung)
│ ├── tankerkennung.py §7.4 (automatische Erkennung am Füllstandsanstieg)
│ ├── reifenzaehler.py §7.5
│ ├── belegverarbeitung.py §7.4, §7.7
│ ├── batterieverlauf.py 12-V-Spannung, Tagesminimum/-maximum
│ ├── bilderverwaltung.py Upload/Löschen der Fahrzeugfotos
│ ├── backup.py tägliche Sicherung und Wiederherstellung
│ ├── updateverwaltung.py Update suchen und installieren
│ └── frontend_api.py Lese-/Schreib-Anbindung fürs Frontend
├── www/
│ ├── audi-dashboard-panel.js Lade-Stub (zeigt configuration.yaml hierher), ändert sich kaum
│ ├── audi-dashboard-app.js das eigentliche Custom Element, hier passiert die Arbeit
│ ├── audi-dashboard-version.json Cache-Buster, wird von update.ps1 automatisch neu geschrieben
│ ├── audi-dashboard.css aus dem Prototyp extrahiert, für Shadow DOM angepasst
│ └── bilder/ noch leer, siehe „Danach noch offen"
└── update.ps1 Updates einspielen ohne Neustart (Schritt 10 in INSTALL.md)
Updates einspielen
pyscript/ lädt sich bei Änderungen selbst neu, ganz ohne HA-Neustart — an
der Testinstanz gemessen (Datei überschrieben, Service erschien innerhalb
von Millisekunden). Fürs Frontend gibt es dafür keinen eingebauten
Mechanismus, dazu kommt ein echtes Cache-Problem: /local/ wird 31 Tage
lang gecacht (Cache-Control: max-age=2678400, ebenfalls gemessen). Gelöst
über einen kleinen Lade-Stub (audi-dashboard-panel.js), der bei jedem
Seitenaufruf ungecacht eine Versionsnummer nachlädt und darüber den
eigentlichen Code cache-sicher nachzieht — siehe Schritt 10 in
INSTALL.md und update.ps1.