Files
audi-app/homeassistant
Paul Nothaft c11c492d86 Installationsskript: eine Zeile statt dreissig Minuten Handarbeit
homeassistant/install.sh richtet das Panel samt Backend auf einer
Home-Assistant-Instanz ein und verdrahtet dabei die Selbstaktualisierung, so
dass danach alles Weitere ueber die Weboberflaeche laeuft: Sensoren zuordnen,
nach Updates suchen, einspielen.

Gedacht fuer das Add-on "Terminal & SSH" direkt auf der Instanz:
  bash <(curl -fsSL <ROH-URL>/homeassistant/install.sh)
Alternativ von einem Rechner aus mit --ziel gegen ein eingebundenes
config-Verzeichnis, oder mit --von gegen eine lokale Arbeitskopie.

Was es tut: pyscript installieren (neueste Version von GitHub, falls nicht
vorhanden), pyscript/ und www/ einspielen, den Belegleser mitliefern (er
liegt in data/, ist aber Code), fehlende Datenbestaende aus der Vorlage
anlegen, den Abschnitt in die configuration.yaml eintragen und
UPDATE_REPO_URL setzen.

Drei Eigenschaften, auf die es dabei ankommt:

- Mehrfach ausfuehrbar. Der Abschnitt in der configuration.yaml steht
  zwischen Markern und wird beim zweiten Lauf ersetzt statt angehaengt.
  Stehen pyscript oder panel_custom bereits ausserhalb dieses Abschnitts,
  weist das Skript darauf hin, statt einen doppelten Schluessel zu erzeugen.
- Vorhandene Daten bleiben unangetastet. Fahrzeugprofil, Fahrten,
  Tankvorgaenge und ein bereits hinterlegtes Token werden nie ueberschrieben,
  nur fehlende Dateien angelegt.
- Die configuration.yaml wird vor jeder Aenderung mit Zeitstempel gesichert.

Geprueft gegen eine frische Home-Assistant-Instanz im Container: HA startet
ohne Konfigurationsfehler, alle neun pyscript-Entitaeten werden
veroeffentlicht, das Panel ist als "Mein Audi" mit mdi:car-sports unter
/audi-dashboard-panel registriert und rendert im Browser mit fuenf Tabs. Ein
zweiter Durchlauf laesst Profil und Token unveraendert und den
Konfigurationsabschnitt genau einmal stehen.

Ausserdem den blockierenden Verlaufsabruf in fahrtabschluss_logik.py
uebernommen: urlopen lief direkt statt ueber task.executor, was Home
Assistant seit 2026.8 abbricht - jede Fahrt waere ohne Strecke geblieben.
Der Fix lag bisher nur im Branch umsetzung-datametric360; eine frische
Installation haette den Fehler sonst mitgebracht.

INSTALL.md nennt den schnellen Weg jetzt vor der ausfuehrlichen Anleitung.
2026-08-13 16:20:34 +02:00
..

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_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 und update.ps1.