# 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).
