pyscript-Backend zur echten HA-Integration umgebaut (HACS-fähig)
Das Backend liegt jetzt als custom_components/audi_dashboard/ vor - eine normale Home-Assistant-Integration mit Config-Flow, einer sensor-Plattform und 18 Diensten. Damit ist die App über HACS installierbar; bis das Repo auf GitHub gespiegelt ist (HACS spricht ausschließlich mit GitHub), installiert homeassistant/installationspaket/install.ps1 denselben Ordner ohne HACS. Fünf Installationsschritte entfallen ersatzlos: der pyscript:-Block, der panel_custom:-Block, das Kopieren der Oberfläche nach www/, das langlebige Zugriffstoken (der Verlauf wird direkt über die recorder-API gelesen) und "pip install pypdf" (steht in manifest.json). Das Fahrzeugprofil legt die Integration beim ersten Start aus ihrer Vorlage an. Drei alte Schwächen sind dabei mit erledigt: - Die Nutzlast landet nicht mehr in der Recorder-Datenbank (_unrecorded_attributes - das kann nur eine echte Entität). - Eine laufende Fahrt überlebt einen Neustart (Store statt Arbeitsspeicher); fiel sie während eines Ausfalls ins Ende, schließt nach_neustart_fortsetzen() sie beim letzten aufgezeichneten Zeitpunkt. - Sensor-Zuordnungen wirken sofort - die Zustandsbeobachter werden neu gebunden, der Neustart-Hinweis und der Neustart-Dienst sind weg. Namensvertrag geändert, beide Oberflächen mitgezogen: pyscript.audi_dashboard_x -> sensor.audi_dashboard_x, pyscript.audi_dashboard_y -> audi_dashboard.y. Eine Companion-App vom alten Stand findet nach dem Umstieg nichts mehr und muss neu gebaut werden; das Panel liegt in der Integration und kann nicht driften. Der selbstgebaute Updater entfällt - HACS ist die Update-Mechanik, die Home Assistant kennt. Die Versionierung schrumpft auf eine Quelle: manifest.json. Geprüft am laufenden Testcontainer (Container byteweise identisch mit dem Repo): alle 18 Dienste, Panel, Config-Entry neu laden, Historienimport, echter Shell-Beleg in-process, Neuinstallation im Wegwerf-Container blank mit automatisch nachinstalliertem pypdf. Companion-App: tsc sauber, 112/112 Tests, beide Rauchtests gegen das laufende Backend grün. Belegparser 8/8. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+28
-155
@@ -1,168 +1,41 @@
|
||||
# Audi-Dashboard — Backend und Frontend, Stand nach dritter Bauphase
|
||||
# homeassistant/ — Installation und Konfiguration
|
||||
|
||||
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.
|
||||
Der Code liegt nicht mehr hier. Seit dem Umbau zur Home-Assistant-Integration
|
||||
(2026-08-23) steht er unter [`../custom_components/audi_dashboard/`](../custom_components/audi_dashboard/):
|
||||
Backend, Panel-Dateien, Vorlage und Belegleser in einem Ordner, der genau so
|
||||
installiert wird, wie HACS ihn installieren würde.
|
||||
|
||||
**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).
|
||||
Was hier bleibt, ist alles, was **um** die Installation herum gebraucht wird.
|
||||
|
||||
## Getestet, nicht nur geprüft
|
||||
| Datei | Wofür |
|
||||
|---|---|
|
||||
| [`INSTALL.md`](INSTALL.md) | Installation Schritt für Schritt, inklusive Umstieg von der pyscript-Fassung |
|
||||
| [`installationspaket/`](installationspaket/) | Ein-Klick-Installation ohne HACS (`Installieren.cmd`) samt Anleitung |
|
||||
| [`recorder_snippet.yaml`](recorder_snippet.yaml) | Datenaufbewahrung von 10 Tagen auf ein Jahr anheben — der einzige YAML-Block, der noch von Hand eingetragen wird |
|
||||
| [`REVERSE_PROXY.md`](REVERSE_PROXY.md) | Zugriff von außen |
|
||||
| `www/dm360-qr.html` | QR-Code-Seite zum Einrichten der Companion-App |
|
||||
|
||||
Beides lief in einer Wegwerf-Testinstanz (Home Assistant in Docker), nicht
|
||||
nur gegen Dokumentation abgeglichen:
|
||||
## Wo die Fahrzeugdaten liegen
|
||||
|
||||
- **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.
|
||||
In Home Assistant selbst, unter `/config/audi_dashboard/`:
|
||||
Fahrzeugprofil, Fahrten, Tankvorgänge, Batterieverlauf, Sensor-Zuordnung,
|
||||
hochgeladene Belege und die automatischen Sicherungen.
|
||||
|
||||
Dabei wurden drei echte pyscript-Eigenheiten gefunden und behoben, die in
|
||||
keiner Dokumentation standen:
|
||||
Dieser Ordner gehört dem Nutzer, nicht der Installation: kein Update — weder
|
||||
über HACS noch über das Installationsskript — fasst ihn an. Dasselbe gilt für
|
||||
die selbst hochgeladenen Fahrzeugfotos unter `/config/www/bilder/`.
|
||||
|
||||
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.
|
||||
Bis 2026-08-23 lag hier ein `data/`-Ordner, aus dem die Erstbefüllung von Hand
|
||||
kopiert werden musste. Den gibt es nicht mehr: die Integration legt das
|
||||
Fahrzeugprofil beim ersten Start selbst aus ihrer Vorlage an
|
||||
([`vorlage/fahrzeugprofil.json`](../custom_components/audi_dashboard/vorlage/fahrzeugprofil.json)).
|
||||
|
||||
## 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.
|
||||
in diesem Datenordner in Home Assistant und steht nirgends im App-Code. Im
|
||||
Repository liegt nur die Vorlage mit Platzhaltern.
|
||||
|
||||
## 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).
|
||||
Das Backup in der App (**Einstellungen → Backup**) exportiert ausschließlich
|
||||
diese Datendateien — nie App-Code oder -Konfiguration.
|
||||
|
||||
Reference in New Issue
Block a user