# 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`](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_wechseln` hat `fahrzeugprofil.json` von „sommer" auf „winter" umgestellt, `audi_dashboard_fahrt_manuell_anlegen` hat einen vollständigen Datensatz an `fahrten.jsonl` angehä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: 1. `task.executor()` akzeptiert nur echte externe Python-Funktionen (z. B. `io.open`), keine im pyscript-Ordner selbst definierten. 2. Variablen aus einem `with ... as f:`-Block waren danach außerhalb nicht mehr auffindbar (`NameError`). 3. 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 laut `reifen.aktiv` gerade montiert ist (reifenzaehler.py, reagiert auf `KM_SENSOR`). Beide starten bei `0` — Laufleistung von vor der Installation muss einmalig direkt im Profil nachgetragen werden. - **Kfz-Steuer-Fälligkeit:** `steuer.faellig` steht auf `null`, 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.py` und landet mit dem `data`-Ordner automatisch unter `/config/audi_dashboard/shell_beleg_parser.py` (siehe INSTALL.md Schritt 2 und Schritt 8). Braucht dort einmalig `pip install pypdf` im System-`python3` des 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 unter `data/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 eine `device_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 in `pyscript/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`](INSTALL.md) und [`update.ps1`](update.ps1).