Elf von dreizehn Phasen sind erledigt oder vorbereitet. AGENTS.md haelt zusaetzlich die drei Fehler fest, die erst der Betrieb gegen eine echte Home-Assistant-Instanz zutage brachte - und den verworfenen selbstgebauten QR-Erzeuger, damit niemand den Versuch wiederholt.
38 KiB
DataMetric360 — Umsetzungsplan
Stand: 2026-08-11. Dieser Plan führt von heute (fertiges HA-Panel, fertige Datenschicht,
halbfertiger Design-Entwurf) bis zur fertigen DataMetric360-App inklusive aller offenen Punkte aus
AGENTS.md. Er ist bewusst kleinteilig geschrieben, damit jede Phase auch von einem schwächeren
KI-Modell oder in einer frischen Session ohne Vorwissen abgearbeitet werden kann.
Beschlossene Rahmenbedingungen (nicht neu diskutieren):
- Alle Daten liegen in Home Assistant. Es gibt kein eigenes Backend — die App spricht direkt
mit der HA-REST-/WebSocket-API und ruft die vorhandenen
pyscript.audi_dashboard_*-Services auf. - Kein Electron. Fürs iPhone wird die Web-App mit Capacitor verpackt (nur Sideload, nie App Store). Optionaler Zwischenschritt: als PWA auf den Homescreen (Phase 10).
- Die App ersetzt später das HA-Panel; bis dahin bleibt das Panel unangetastet in Betrieb.
Für das ausführende Modell: Arbeitsregeln
- Lies zuerst:
AGENTS.md(Regeln + Stand), dann die Phase, die du bearbeitest — nichts anderes. Hole dir Detailwissen erst, wenn ein Schritt es verlangt (die Schritte nennen die Quelldatei). - Eine Phase pro Session. Arbeite die Schritte in Reihenfolge ab. Jeder Schritt hat eine Prüfung („✅ Fertig wenn"). Erfülle sie, bevor du weitergehst. Wenn eine Prüfung fehlschlägt, behebe das, bevor du den nächsten Schritt beginnst.
- Nach jeder abgeschlossenen Phase: Haken in diesem Plan setzen, betroffene Checkboxen in
AGENTS.mdabhaken, „Last updated" inAGENTS.mdaktualisieren (englisch!), committen mit deutscher Commit-Message. - Harte Verbote:
- Audi-Assets (Schriften, Ringe, Typenschilder) niemals nach
design-system/kopieren oder in irgendetwas einbauen, das veröffentlicht/hochgeladen wird. Quelle für die App:homeassistant/www/unddesign/uploads/. - Keine Schriftgewichte ≥ 600, keine Schatten, keine Verläufe. Nur Gewichte 300/400.
homeassistant/nicht umbauen (nur die explizit genannten Pflege-Schritte in Phase 2).- Keine zusätzlichen npm-Pakete einführen, die der Plan nicht nennt, ohne es zu begründen und
in
AGENTS.mdzu dokumentieren. - Home Assistant niemals direkt ins Internet stellen. Nur die in Phase 12 beschriebenen Wege.
- Audi-Assets (Schriften, Ringe, Typenschilder) niemals nach
- Sprache: Code-Bezeichner, Kommentare, UI-Texte, Commits auf Deutsch (Ausnahme:
AGENTS.mdenglisch;design-system/behält englische Props). - Zahlenformat immer
de-DE(1.234,5). Übernimm die Helferde()/eur()aus dem alten Panel (homeassistant/www/audi-dashboard-app.js), statt eigene zu erfinden.
Phasenübersicht und Abhängigkeiten
| Phase | Inhalt | Hängt ab von | Braucht Hardware/Extern? |
|---|---|---|---|
| 1 | Arbeitsumgebung herstellen und verifizieren | — | nein |
| 2 | Pflege HA-Panel (Doku-Drift, Härtung) | — | nein |
| 3 | Design-Entwurf vervollständigen | — | Claude Design (Besitzer) |
| 4 | Workspace + React-Gerüst in companion-app/ |
1 | nein |
| 5 | App-Shell: Theme, Router, responsives Layout | 4 | nein |
| 6 | Onboarding + Datenanbindung + Offline-UX | 5 | nein |
| 7 | Alle Screens umsetzen | 3, 6 | nein |
| 8 | Audi-Assets einbauen | 7 | nein |
| 9 | Tests (authentifiziert + Komponenten) | 6 | nein |
| 10 | PWA + Capacitor/iOS-Sideload | 7 | Mac mit Xcode, iPhone |
| 11 | HA-Einbettung, Ablösung + Archivierung des Panels | 7–10 | HA-Produktivinstanz |
| 12 | Externer Zugriff: Cloudflare Tunnel + Reverse Proxy | teils unabhängig | Domain/DNS, Besitzer |
| 13 | FMM003: MQTT, Zertifikate, Mapping, Fahrterkennung | Hardware | ja: FMM003 verbaut |
Phasen 2, 3 und 12 (Teilschritte) sind unabhängig und können vorgezogen werden. Phase 13 ist die einzige, die zwingend auf Hardware wartet.
Phase 1 — Arbeitsumgebung herstellen und verifizieren ✅ ERLEDIGT (2026-08-11)
Ergebnis: design-system baut und besteht 21/21, companion-app typecheckt und besteht 7/7. Die verlorene Testinstanz wurde als reproduzierbares Skript neu aufgebaut — siehe
testumgebung/(Abweichung vom Plan, bewusst: das Original ging genau deshalb verloren, weil es nur als Anleitung existierte).
Ziel: Beide npm-Pakete bauen, beide Smoke-Tests laufen, die HA-Testinstanz ist erreichbar.
- Node-Version prüfen:
node --version— muss ≥ 20 sein (companion-app nutzt--experimental-strip-types, ab Node 22 stabil; wenn < 20: mit nvm/Volta Node 22 installieren). cd design-system && npm install && npm run build✅ Fertig wenn:design-system/dist/index.jsunddist/styles.cssexistieren, Build ohne Fehler.npm run smoke(im selben Ordner) ✅ Fertig wenn: 21/21 Fälle „ok" melden.cd ../companion-app && npm install && npm run typecheck✅ Fertig wenn:tsc --noEmitfehlerfrei durchläuft.- HA-Testinstanz prüfen:
curl -s -o /dev/null -w '%{http_code}' http://localhost:18123/api/— erwartet:401(API da, Token fehlt — das ist richtig so). Wenn keine Antwort: Der Docker-Container heißtaudi_ha_test(docker start audi_ha_test). Existiert er nicht, siehehomeassistant/README.md(Testbericht-Abschnitt) für den Aufbau; notfalls Phase-1-Schritt 6 überspringen und in Phase 9 nachholen. npm run smokeincompanion-app/✅ Fertig wenn: 7/7 Prüfungen grün.
Abschluss Phase 1: nichts committen (nur node_modules/dist, beides gitignored).
Phase 2 — Pflege des bestehenden HA-Panels ✅ ERLEDIGT (2026-08-11)
Doku-Drift behoben,
profil_lesen()gehärtet und an der Testinstanz belegt. Offen bleibt nur, was Betriebsdaten sind: Fahrzeugfotos undsteuer.faellig.
Ziel: Doku stimmt wieder mit dem Code überein; ein Crash-Risiko ist beseitigt. Reine Pflege — keine Funktionsänderungen.
- Statistik-Behauptung korrigieren (3 Stellen, gleiche Falschaussage „zeigt Beispielzahlen"):
homeassistant/www/audi-dashboard-app.jsKopfkommentar Zeilen ~13–15homeassistant/README.mdZeile ~99homeassistant/INSTALL.mdZeile ~204 Neue Aussage sinngemäß: „Die Statistik-Seite berechnet echte Werte aus Fahrten und Tankvorgängen (Zeiträume, Verbrauch, Tag/Nacht, privat/Arbeitsweg)."
- INSTALL.md Schritt 4 (Zeilen ~106–109): falsche Variablennamen ersetzen.
Falsch:
DOORS_SENSOR,WINDOWS_SENSOR,LOCK_ENTITY,BATTERY_VOLTAGE_SENSOR. Richtig (siehehomeassistant/pyscript/modules/einstellungen.py):TUER_SENSOREN,FENSTER_SENSOREN,TUERSCHLOSS_SENSOREN(je 4er-Listen) undBATTERIE_SENSOR(einzeln). - README.md: Erwähnung der „97-%-Volltankungsregel" (~Zeile 110) streichen (Regel wurde
entfernt, siehe Kommentar
pyscript/belegverarbeitung.py:18-20); in der Dateiübersicht die fehlenden Skripte ergänzen:tankerkennung.py,batterieverlauf.py,bilderverwaltung.py,backup.py,updateverwaltung.py. - Obsoleten Kommentar entfernen:
pyscript/belegverarbeitung.py:41— den Zusatz „TODO: Datei ablegen" streichen (Datei existiert längst), Konstante selbst unverändert lassen. profil_lesen()härten (pyscript/modules/profil.py, ~Zeile 51–55): fehlende oder nicht parsebarefahrzeugprofil.jsonabfangen. Verhalten: Fehler ins Log (log.errorist in pyscript global verfügbar), RückgabeNone; Aufrufer prüfen. Vorher alle Aufrufer suchen (grep -rn "profil_lesen" homeassistant/pyscript/) und sicherstellen, dass jeder mitNoneumgehen kann — wo nicht, dort eine frühe Rückkehr einbauen. pyscript-Eigenheit beachten: Datei-I/O nur übertask.executor(io.open, …)-Muster wie im Bestand, kein nacktesopen(). ✅ Fertig wenn:python3 -c "import ast; ast.parse(open('homeassistant/pyscript/modules/profil.py').read())"fehlerfrei ist und jeder Aufrufer denNone-Fall behandelt.- Bewusst NICHT tun: Swipe-Delete-Fallback, Popup-Tastaturzugang, Leaflet-Selbsthosting (Audit-Reste) — Entscheidung laut Audit: kommt in DataMetric360, nicht ins alte Panel. RAM-only-Zustände (Fahrtstart, Tank-Tiefststand) ebenfalls nicht anfassen — wird mit dem FMM003-Umstieg hinfällig.
- Committen: „Doku an Codestand angleichen und profil_lesen gegen fehlende Datei härten".
AGENTS.md: die erledigten Punkte in Block C abhaken.
Offen bleiben in Block C nur: Fahrzeugfotos hochladen + steuer.faellig setzen (macht der
Besitzer in der App/UI, kein Code).
Phase 3 — Design-Entwurf vervollständigen ⏭️ ÜBERSPRUNGEN (2026-08-11)
Entscheidung des Besitzers: Die Screens wurden direkt aus den Views des alten Panels abgeleitet, statt auf den Claude-Design-Entwurf zu warten. Dieser Abschnitt bleibt stehen, falls der Entwurf später auf den Stand der Umsetzung gebracht werden soll.
Ziel: Der Entwurf zeigt das richtige Fahrzeug und alle Seiten, die das alte Panel hat. Diese Phase läuft im Claude-Design-Projekt (https://claude.ai/design/p/c28a8d4d-ec4e-4178-9e49-ab5b90c02097) — der Besitzer stößt sie an; ein Agent kann den Auftragstext vorbereiten und den Export danach einpflegen.
- Korrektur Modell: überall „Audi RS 4 Avant competition" statt „RS 6 Avant"; Typenschild
rs4(negative/positive) stattrs6. - Fehlende Seiten ergänzen (16 Stück; Vorlage ist jeweils die View im alten Panel —
Funktionsnamen aus
homeassistant/www/audi-dashboard-app.js, Beschreibung inSPECIFICATION.md§3 „Views"):# Seite Vorlage (alte View) 1 Fahrzeugstatus-Detail (12-Punkte Türen/Fenster/Schlösser) vSicherheit()2 Fahrzeugdaten/Technik/Ausstattung vIdent()3 Batterieverlauf (SVG-Kurve, SoC/SoH) vBatterieverlauf()4 Service (Termine, Terminanfrage, Servicebuch-Liste) vService()5 Werkstatt bearbeiten vWerkstatt()6 Servicebuch-Eintrag (ansehen/bearbeiten/neu) vSbuch(i)7 Versicherung & Steuer (Hub) vVers()8 Beitrag bearbeiten vBeitrag()9 Vertragsdetails vVertragsdetails()10 Schutzbrief (nur lesen) vSchutz()11 Notrufnummern bearbeiten vNotrufBearbeiten()12 Kfz-Steuer (Ansicht + Bearbeiten) vSteuer()/vSteuerBearbeiten()13 Reifen (Sätze, km-Zähler, Wechsel, Drehmoment) vReifen()14 Fahrt-Detail (Karte, Daten, Bearbeiten) vTrip(id)15 Tankvorgang-Detail (inkl. Beleg-Upload/-Ersetzen) vFill(id)16 Einstellungen in voller Tiefe (Fahrzeug-Setup, Fotos, SmartDeal, Pausenzeit, Export/Import, Backup, Version/Update) vEinst()Für jede Seite beide Layoutvarianten (schmal mit Tab-Leiste unten, breit ≥1000px mit Seitenleiste) — wie im Brief DESIGN_BRIEF_DATAMETRIC360.mdgefordert. - Export einpflegen: kompletten Ordner
design/durch den neuen Export ersetzen (design/README.mdsagt selbst: „wird beim nächsten Export komplett ersetzt"), README-Abschnitt „Stand dieses Exports" aktualisieren. ✅ Fertig wenn:grep -ci "rs 6\|rs6" design/DM360.dc.html= 0 und alle 16 Seiten im Export auffindbar sind. - Committen;
AGENTS.mdBlock A Punkt 1 abhaken.
Wichtig: Phase 7 kann pro Screen schon vor Abschluss dieser Phase beginnen — die 8 vorhandenen Hauptscreens sind entworfen; die 16 Unterseiten folgen dann dem gleichen Muster.
Phase 4 — Workspace und React-Gerüst ✅ ERLEDIGT (2026-08-11)
Ziel: companion-app/ ist ein lauffähiges Vite+React+TS-Projekt, das @audi-dash/ui als
echte Dependency nutzt. Die bestehende Datenschicht (src/api/) bleibt unverändert liegen.
- npm-Workspace-Root anlegen — neue Datei
package.jsonim Repo-Root:Dazu Repo-Root-{ "name": "audi-app", "private": true, "workspaces": ["design-system", "companion-app"] }.gitignoreumnode_modules/ergänzen (falls nicht abgedeckt). - Dependencies in
companion-app/package.jsonergänzen (bestehende Felder beibehalten!):dependencies:react ^18,react-dom ^18,@audi-dash/ui(Versionsangabe*— kommt über den Workspace);devDependencieszusätzlich:vite,@vitejs/plugin-react,@types/react,@types/react-dom. Scripts ergänzen:"dev": "vite","build": "vite build","preview": "vite preview". npm installim Repo-Root ausführen (verlinkt design-system in die companion-app). Danach einmalnpm run build --workspace design-system(die App importiert ausdist/).- Vite-Konfiguration
companion-app/vite.config.ts:import { defineConfig } from "vite" import react from "@vitejs/plugin-react" export default defineConfig({ plugins: [react()], server: { port: 5173 }, build: { outDir: "dist", target: "es2022" }, }) - tsconfig anpassen: Das bestehende
companion-app/tsconfig.jsonist auf die reine Datenschicht zugeschnitten (noEmit, sehr streng — so lassen). Ergänzen:"jsx": "react-jsx","lib": ["ES2022", "DOM", "DOM.Iterable"]und"moduleResolution": "bundler", falls nicht gesetzt. Die strengen Flags (exactOptionalPropertyTypesusw.) NICHT abschwächen. - Einstieg anlegen:
companion-app/index.html— Minimal-HTML mit<div id="wurzel"></div>und<script type="module" src="/src/main.tsx"></script>;<html lang="de">;<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">.companion-app/src/main.tsx:import { createRoot } from "react-dom/client" import "@audi-dash/ui/styles.css" import { App } from "./App" createRoot(document.getElementById("wurzel")!).render(<App />)companion-app/src/App.tsx— vorerst nur:export function App() { return <div className="ads-root" data-theme="nacht" style={{ minHeight: "100vh" }}>DataMetric360</div> }
className="ads-root"unddata-theme— ohne diese Wurzelklasse greifen die Design-Tokens nicht (Regel ausdesign-system/.design-sync/conventions.md). - ✅ Fertig wenn:
npm run dev --workspace companion-appstartet, http://localhost:5173 zeigt dunklen Hintergrund (#161b23) mit Text;npm run typecheck --workspace companion-appundnpm run build --workspace companion-applaufen fehlerfrei. - Committen: „companion-app: Vite+React-Gerüst und Workspace-Anbindung an @audi-dash/ui".
AGENTS.mdBlock A Punkt 2 abhaken, Statustabelle aktualisieren.
Phase 5 — App-Shell: Theme, Navigation, responsives Layout ✅ ERLEDIGT (2026-08-11)
Ziel: Navigationsgerüst, das beide Layouts beherrscht (schmal: Tab-Leiste unten; breit ≥1000px: Seitenleiste), mit Tag/Nacht-Theme und Zurück-Navigation. Noch ohne echte Daten.
Vorbild für Verhalten: das alte Panel (audi-dashboard-app.js) — flacher Routenzustand,
ZURUECK-Tabelle. Vorbild fürs Aussehen: design/DM360.dc.html (dort existiert der
wide-Umschalter mit 240px-Seitenleiste bereits).
- Kein Router-Paket installieren. Eigener flacher Zustand wie im alten Panel:
src/navigation.tsmittype Route = { name: RouteName; id?: string }, React-State im App-Root,geheZu(name, id?), plusZURUECK-Tabelle (aus dem alten Panel übernehmen und um die neuen Routen ergänzen; 3-stufige Verschachtelungschutz → vertragsdetails → versbeachten). - Theme:
src/theme.ts— Zustand"nacht" | "tag", Persistenz inlocalStorage(Schlüsseldm360.theme), Initialwert: gespeicherter Wert, sonstprefers-color-scheme, Standardnacht. Gesetzt wird ausschließlichdata-themeauf demads-root-Element.localStorage-Zugriffe in try/catch (Muster aus dem alten Panel, Private-Mode-Browser). - Layout:
src/Shell.tsx— CSS-Grid/Flex mit Breakpoint bei 1000px (window.matchMedia("(min-width: 1000px)")+ Listener):- schmal: Inhalt +
TabBar-Komponente (aus@audi-dash/ui) unten,env(safe-area-inset-*)als Padding (iPhone-Notch). - breit: 240px-Seitenleiste links (Navigationspunkte + Ringe-Platzhalter oben), Inhalt rechts; Listen-Detail nebeneinander wo der Entwurf es vorsieht. Die 5 Hauptbereiche: Übersicht, Mein Audi, Fahrten, Statistik, Tanken. Einstellungen über Zahnrad oben rechts (nicht in der Tab-Leiste) — wie im alten Panel und im Entwurf.
- schmal: Inhalt +
- Platzhalter-Screens: je Hauptbereich eine Datei unter
src/screens/(z. B.Uebersicht.tsx), die vorerst nur Titel perads-eyebrow/Tilezeigt. - ✅ Fertig wenn: Im Browser (a) bei < 1000px die Tab-Leiste unten erscheint und alle 5 Bereiche wechselbar sind, (b) bei ≥ 1000px stattdessen die Seitenleiste erscheint, (c) der Theme-Wechsel sichtbar Tag/Nacht umschaltet und einen Reload überlebt.
- Committen: „companion-app: App-Shell mit responsivem Layout, Navigation und Theme".
Phase 6 — Onboarding, Datenanbindung, Offline-UX ✅ ERLEDIGT (2026-08-11)
Ziel: Die App verbindet sich echt mit Home Assistant über die vorhandene Datenschicht.
Die Datenschicht ist fertig — nur benutzen, nicht neu bauen. Einstieg:
companion-app/src/api/index.ts (DataMetricApi). Sie liefert: REST-Reads
(profilLesen, fahrtenLesen, tankvorgaengeLesen, fahrzeugstatusLesen), Live-Updates
(HassLive mit Reconnect), Schreibzugriffe über die Offline-Queue (profilSchreiben,
belegHochladen, fahrtLoeschen, tankvorgangLoeschen, jetztAktualisieren) und
Zugangsdaten-Verwaltung (umgebung.ts).
- Ersteinrichtungs-Screen (
src/screens/Einrichtung.tsx) nach dem Entwurf indesign/DM360.dc.html(dort derisSetup-Block): Felder Server-Adresse + Zugangs-Token, Knopf „Verbinden" ruftverbindungPruefen()aus der Datenschicht; Fehler unterscheiden nachApiFehler.istAnmeldeproblem(Token falsch) vs.istNetzproblem(Adresse/Netz) und in verständlichem Deutsch anzeigen. QR-Knopf: vorerst ausblenden oder deaktiviert mit Hinweis (kommt mit Capacitor, Phase 10). Bei Erfolg Zugangsdaten über dieAblageder Datenschicht speichern und in die App wechseln. - Datenkontext:
src/datenkontext.tsx— ein React-Context, der beim StartDataMetricApiinstanziiert, initial alle vier Reads lädt, sich beiaufZustand()für Live-Updates registriert und{profil, fahrten, tankvorgaenge, status, verbindung}bereitstellt. Übernahme-Pflicht aus dem alten Panel: die UmrechnungenprofilZuConfig()/profilZuCar()(inkl. SmartDeal-Gültigkeit: aktiv wennlaeuft_abfehlt oder ≥ heute, Ablaufdatum einschließlich) ausaudi-dashboard-app.jsnach TypeScript portieren — Logik exakt beibehalten. - Offline-UX (Entwurf: Offline-Marker in der Kopfzeile):
- Verbindungszustand aus
aufVerbindung()der Datenschicht → dezenter „Offline"-Hinweis im Kopf, Daten bleiben sichtbar. - Warteschlange aus
aufAenderung()→ sichtbare Zahl wartender Änderungen („2 Änderungen warten auf Übertragung"), wie im Design-Brief gefordert.
- Verbindungszustand aus
- Stale-Anzeige:
last_updateddes Fahrzeugstatus mitführen (Muster: Staleness-Indikator des alten Panels). - ✅ Fertig wenn: Gegen
audi_ha_test(Token siehe Phase 9 Schritt 1): Einrichtung mit falschem Token zeigt Anmeldefehler, mit richtigem Token lädt die App Profil + Status; HA-Container stoppen → Offline-Marker erscheint; Container starten → verschwindet wieder (Reconnect testet die Backoff-Logik der Datenschicht). - Committen;
AGENTS.mdBlock A Punkte Onboarding/Offline teilweise abhaken (QR bleibt offen).
Phase 7 — Alle Screens umsetzen ✅ ERLEDIGT (2026-08-11)
Alle 21 Seiten stehen. Die Entwurfsvorlage kam für die 16 Unterseiten aus den Views des alten Panels statt aus Claude Design (Entscheidung des Besitzers, 2026-08-11) — Phase 3 ist damit gegenstandslos, solange der Entwurf nicht nachgezogen werden soll.
Ziel: Funktionsgleichheit mit dem alten Panel plus die neuen Live-Ansichten. Pro Screen:
Entwurf aus design/ nachbauen (mit @audi-dash/ui-Komponenten), Logik aus der alten View
portieren, an den Datenkontext anschließen.
Reihenfolge und Zuordnung (Logik-Vorlage = Funktion in audi-dashboard-app.js;
Datenquelle = Feld im Datenkontext):
| Screen | Logik-Vorlage | Datenquelle / Services | Hinweise |
|---|---|---|---|
| Übersicht | vHome() |
status, profil, fahrten, tankvorgaenge | Foto, Status „sicher abgestellt", Reichweite, km, nächster Service, Teaser letzte Fahrt/Tankung; Pull-to-Refresh → jetztAktualisieren() |
| Fahrten-Liste | vTrips() |
fahrten | Jahr→Monat-Akkordeon (Accordion); Lösch-Aktion: nicht nur Swipe — zusätzlich „…"-Menü pro Zeile (behebt Audit-Finding barrierefreies Löschen); manuelles Anlegen (vTripsFormular()) über Service audi_dashboard_fahrt_manuell_anlegen |
| Fahrt-Detail | vTrip(id) |
fahrten | Karte: Leaflet lokal bündeln (npm i leaflet, Audit-Finding CDN), Tile-Layer nach Theme; kein fakeTrack() portieren — ohne echte Route nur Start/Ende-Marker zeigen, mit ehrlichem Leerhinweis |
| Tanken-Liste | vFuel() |
tankvorgaenge | volumengewichteter Durchschnittspreis; Lösch-„…"-Menü wie bei Fahrten |
| Tankvorgang-Detail | vFill(id) |
tankvorgaenge | Beleg-Upload: Datei → Base64 → belegHochladen(); Ergebnis kommt asynchron über die Entität pyscript.audi_dashboard_beleg_ergebnis (Muster im alten Panel: belegErgebnisVerarbeiten()) |
| Statistik | vStat() |
fahrten, tankvorgaenge | Rechenfunktionen (fahrtenSeit, tankSeit, verbrauch, istNachtZeit) 1:1 portieren |
| Mein Audi (Hub) | vAudi() |
profil | Fotogalerie + Kachel-Links auf die Unterseiten |
| Fahrzeugstatus-Detail | vSicherheit() |
status | 16-Punkte-Liste; ok === null heißt „unbekannt", nie grün raten |
| Fahrzeugdaten | vIdent() |
profil (technik, ausstattung) | statisch aus dem Profil |
| Batterieverlauf | vBatterieverlauf() |
eigener Read der Entität pyscript.audi_dashboard_batterieverlauf |
SVG-Chart portieren (Pinch/Pan/Tap); Leerzustand prominent — Sensor ist derzeit unbesetzt |
| Service + Werkstatt + Servicebuch | vService(), vWerkstatt(), vSbuch() |
profil.service | Öl-/Service-Prognose (oelwechselPrognose(), intervalle(), termine()) exakt portieren; ICS-Export (ics()-Helfer) übernehmen |
| Versicherung/Steuer-Gruppe | vVers(), vBeitrag(), vVertragsdetails(), vSchutz(), vNotrufBearbeiten(), vSteuer() |
profil.versicherung, profil.steuer | reine Formular-/Anzeige-Seiten; Schreiben immer als ganzes Profil über profilSchreiben() (es gibt keinen Feld-einzeln-Service!) |
| Reifen | vReifen() |
profil.reifen + Entitäten pyscript.reifen_* |
Wechsel über Service audi_dashboard_reifen_wechseln (nur "sommer"/"winter"); Wechseldatum → ICS |
| Einstellungen | vEinst() |
profil, alle | volle Tiefe: Fahrzeug-Setup, Theme, Fotos (Upload über audi_dashboard_bild_hochladen, nur die 10 erlaubten Dateinamen!), SmartDeal, Pausenzeit, Profil-Export/Import, Backup, CSV-Export, Version |
| Neu: Live-Fahrt | — (neu) | status (künftig FMM003-Felder) | Entwurf screen === 'live': Karte, Geschwindigkeit groß, Strecke/Dauer; bis Phase 13 hinter einem Feature-Schalter LIVE_VERFUEGBAR = false in einer zentralen Konfigurationsdatei — Screen bauen, Schalter erst mit echten Daten umlegen |
| Leerzustände | — | — | für Fahrten/Tanken/Statistik je einen freundlichen „noch keine Daten"-Zustand (Design-Brief), wird real gebraucht: beide JSONL sind aktuell leer |
Regeln für alle Screens:
- Nur
@audi-dash/ui-Komponenten + Tokens (var(--…)), keine neuen Hex-Farben, keine neuen Komponenten ohne sie zuerst indesign-system/anzulegen (dann dort mit CSS+Preview, danach hier verwenden). - Destruktive Aktionen immer mit Bestätigung; Fehler sichtbar in rot inline (Muster altes Panel).
- Jede Zahl durch
de()/eur(). - Nach jedem Screen:
npm run typecheck+ Sichtprüfung schmal UND breit.
✅ Fertig wenn: jeder Screen der Tabelle existiert, mit Testdaten befüllbar ist und in beiden Layouts bedienbar aussieht wie der Entwurf. Committen pro Screen-Gruppe (z. B. „Fahrten-Screens"), nicht ein Riesencommit.
Phase 8 — Audi-Assets einbauen ✅ ERLEDIGT (2026-08-11)
Ziel: Die App sieht aus wie das Original — Schriften, Ringe, Typenschilder.
- Quellen: Schriften aus
design/uploads/*.ttf(oder die woff2-Base64-Blöcke aushomeassistant/www/audi-dashboard.css), Ringedesign/assets/audi-rings-{white,black}.svg, Typenschilder aushomeassistant/www/badges/(alle 14). - Ablage:
companion-app/src/assets/audi/+@font-face-Deklarationen in einer eigenensrc/audi-schrift.css(Familien exakt wie im Bestand:"Audi Type"400,"Audi Type Wide"300/400,"Audi Type Extended"italic). Der--font-stackwird per CSS-Variable auf demads-rootüberschrieben —design-system/-Dateien bleiben unberührt. - Typenschild-Auswahl nach Modell aus dem Profil (Logik aus
vEinst()/Badge-Mapping des alten Panels übernehmen; Theme-abhängig positive/negative-Variante). - Kontrolle:
git log --statdes Commits zeigt KEINE Änderung unterdesign-system/; Gewichte ≥ 600 kommen nirgends vor (grep -rn "font-weight" companion-app/src | grep -vE "300|400"liefert nichts). - Committen;
AGENTS.mdBlock A Assets-Punkt abhaken. Hinweis im Commit: Repo bleibt privat (Lizenz).
Phase 9 — Tests ✅ ERLEDIGT (2026-08-11)
90 Unit- und Rendertests, dazu 9 Prüfungen gegen die laufende Instanz.
Ziel: Die ungeteste Hälfte der Datenschicht ist abgedeckt; die Kern-Rechenlogik hat Unit-Tests.
- Token für die Testinstanz erzeugen:
audi_ha_testim Browser öffnen (http://localhost:18123), anmelden, Profil → Sicherheit → langlebiges Zugriffstoken erstellen. Token NUR incompanion-app/.env.localablegen (VITE_TEST_TOKEN=…), Datei in.gitignoreaufnehmen. Niemals committen. - Authentifizierter Smoke (
scripts/smoke-auth.ts, Aufbau wiescripts/smoke.ts): mit Token (a)zustandLesen("pyscript.audi_dashboard_profil")liefert Daten, (b)dienstAufrufenmit einem harmlosen Service (audi_dashboard_jetzt_aktualisieren) gibt 200, (c) Queue-Round-Trip: Eintrag einstellen bei gestopptem Container, Container starten, Queue leert sich. Script inpackage.jsonalssmoke:auth; ohne gesetztes Token bricht es mit klarer Meldung ab (Exit-Code 0, „übersprungen"). - Unit-Tests mit
vitest(einzige neue Dev-Dependency): portierte Rechenlogik testen — Statistik (verbrauch,istNachtZeit, Zeiträume), Service-Prognose (oelwechselPrognose: Neustart nach Ölwechsel, 180-Tage-Gewichtung, Kappung), SmartDeal-Gültigkeit (Ablauftag einschließlich!),de()/eur()-Formatierung. Testdaten als Fixtures aus realistischen JSONL-Zeilen (Schema:SPECIFICATION.md§5). - ✅ Fertig wenn:
npm run smoke:auth3/3 grün (mit Token),npx vitest rungrün, und beide incompanion-app/README.mddokumentiert sind. - Committen;
AGENTS.mdBlock A Test-Punkt abhaken.
Phase 10 — PWA und Capacitor 🟡 STUFE 1 ERLEDIGT (2026-08-11)
PWA fertig. Capacitor-Konfiguration, Secure-Storage-Adapter und die QR-Seite liegen bereit; die Hülle selbst braucht einen Mac mit Xcode und ein Gerät.
Ziel: Die App läuft auf dem iPhone. Zwei Stufen — erst PWA (sofort nutzbar), dann Capacitor (Keychain + QR-Scan).
Stufe 1 — PWA (kein neues Werkzeug):
companion-app/public/manifest.webmanifest(Name „DataMetric360",display: "standalone", Themenfarbe#161b23, neutrale Icons 192/512px — NICHT die Audi-Ringe als App-Icon, der Homescreen ist „außen"; neutrales DM360-Monogramm erzeugen) + Verweis imindex.html.- Erreichbarkeit fürs Handy: solange Phase 12 nicht fertig ist, über Tailscale
(
npm run build, Ausgabe z. B. über HA/local/odervite preview --hostim LAN testen). - ✅ Fertig wenn: „Zum Home-Bildschirm" auf dem iPhone die App randlos startet und Login + Datenanzeige funktionieren.
Stufe 2 — Capacitor:
4. npm i -D @capacitor/cli && npm i @capacitor/core @capacitor/ios @capacitor/android
(im Workspace für companion-app), npx cap init DataMetric360 app.datametric360 --web-dir dist,
npx cap add ios, npx cap add android.
5. Sichere Token-Ablage: Capacitor-Secure-Storage-Plugin (z. B.
@aparajita/capacitor-secure-storage) als neue Ablage-Implementierung; über den vorhandenen
Hook ablageSetzen() der Datenschicht einhängen, wenn umgebungErkennen() „capacitor" meldet.
Browser-Fall bleibt unverändert BrowserAblage.
6. QR-Scan: Barcode-Plugin (@capacitor-mlkit/barcode-scanning) + Kamera-Berechtigung;
Gegenstück: kleine statische Seite unter HA /local/dm360-qr.html, die den Token clientseitig
(Offline-JS-QR-Bibliothek, keine Netzabfrage) als QR anzeigt. Inhalt des QR: JSON
{"url": "...", "token": "..."}. Wenn das zusammen > 1 Tag Aufwand wird: weglassen —
manuelles Einfügen ist die beschlossene, ausreichende Lösung.
7. iOS-Sideload: npx cap open ios, in Xcode Signing mit eigener Apple-ID, aufs Gerät bauen.
⚠️ Entscheidungspunkt für den Besitzer: mit kostenlosem Apple-Konto läuft die Signatur nach
7 Tagen ab (App neu aufspielen); ein bezahltes Entwicklerkonto (99 €/Jahr) macht 1 Jahr.
Bei 7-Tage-Schmerz ist die PWA-Stufe die Alltagslösung, Capacitor das Extra für Keychain/QR.
8. ✅ Fertig wenn: App startet nativ auf dem iPhone, Token liegt im Keychain (Test: App löschen und
neu installieren → Token weg; Backup/Restore-Verhalten notieren), QR-Einrichtung funktioniert
oder ist dokumentiert entfallen.
9. Committen; AGENTS.md Block A Punkte QR/Secure-Storage abhaken.
Phase 11 — HA-Einbettung und Ablösung des alten Panels
Ziel: Die neue App läuft im HA-Seitenmenü; das alte Panel ist archiviert.
- Build in HA ausliefern:
npm run build, Ausgabe nach/config/www/dm360/der Produktivinstanz (Weg analogupdate.ps1; das Script um diesen Ordner erweitern). configuration.yaml:panel_iframe-Eintrag (Titel „Mein Audi",mdi:car-sports, URL/local/dm360/index.html). Derpanel_custom-Block des alten Panels bleibt zunächst parallel bestehen (zweiter Menüpunkt „Mein Audi (alt)").- Parallelbetrieb mindestens 2 Wochen: beide Panels zeigen dieselben Daten (gleiche Entitäten/Services — Abweichungen sind Portierungsfehler; prüfen: Übersichtszahlen, eine Fahrt anlegen/löschen, ein Beleg-Upload).
- Ablösung: alten
panel_custom-Block entfernen; Dateien NICHT löschen, sondern verschieben:homeassistant/www/audi-dashboard-app.js→homeassistant/archiv/(plus Panel-Stub und CSS), README-Hinweis dort ablegen. Beschlossene Regel: archivieren, nie löschen (COMPANION_APP_ARCHITECTURE.md§1). - ✅ Fertig wenn: Neues Panel im HA-Menü, altes entfernt aber archiviert,
SPECIFICATION.mdbekommt eine Kopfnotiz „beschreibt das archivierte Panel; aktuell ist companion-app/". - Committen;
AGENTS.mdBlock D abhaken, Statustabelle umstellen.
Phase 12 — Externer Zugriff: Cloudflare Tunnel + Reverse Proxy 🟡 VORBEREITET
Fertige Freigabeliste samt Prüfbefehlen:
homeassistant/REVERSE_PROXY.md. Ausführung braucht Cloudflare-Konto und die Nameserver-Umstellung.
Ziel: Die App funktioniert von unterwegs (Mobilfunk), ohne dass HA selbst erreichbar ist.
Referenz: COMPANION_APP_ARCHITECTURE.md §4/§5. Teilweise Besitzer-Aufgaben (Konten/DNS).
- [Besitzer] Nameserver umstellen:
datametric360.appbei all-inkl auf die Cloudflare-Nameserver zeigen lassen (Cloudflare-Konto → Site hinzufügen → „Full setup"; Domain trägt sonst nichts, Umstellung ist folgenlos). ✅ wenndig NS datametric360.appdie Cloudflare-Server liefert. - Hostnamen festlegen (Empfehlung aus dem Architekturdokument übernehmen): App unter
https://datametric360.app, API unterhttps://api.datametric360.app— getrennter API-Hostname hält die Allowlist übersichtlich. Entscheidung inAGENTS.mdprotokollieren. - Cloudflared-Add-on in HA installieren, Tunnel erstellen, zwei öffentliche Hostnamen:
datametric360.app→ Reverse-Proxy-Port (statische App-Dateien + nichts weiter),api.datametric360.app→ Reverse-Proxy-Port (API-Pfade). Kein Router-Port wird geöffnet. - Reverse Proxy: Nginx Proxy Manager-Add-on (Standard-Empfehlung; Traefik nur, falls NPM
an Grenzen stößt — Entscheidung dokumentieren). Allowlist ausschließlich:
GET /api/states/pyscript.audi_dashboard_*undGET /api/states/pyscript.reifen_*POST /api/services/pyscript/audi_dashboard_*GET /api/(Verbindungsprüfung) und der WebSocket-Pfad/api/websocketAlles andere (v. a./auth,/lovelace,/config,/api/config) wird geblockt. ⚠️ Ehrliche Einschränkung dokumentieren:/api/websocketlässt sich nicht pfadgenau beschneiden — nachauth_oksind darüber alle States lesbar. Schutzschicht bleibt der LLAT (ohne Token keine Verbindung); das ist die im Architekturdokument akzeptierte Abwägung.
- Prüfen von außen (Mobilfunk, VPN aus): App lädt, Login klappt,
curlauf einen Nicht-Allowlist-Pfad (z. B./auth/authorize) liefert 403/404; HA-Port 8123 ist von außen nicht erreichbar. - ✅ Fertig wenn: Schritt 5 vollständig besteht. Die endgültige Allowlist wortwörtlich in
COMPANION_APP_ARCHITECTURE.md§5.1 eintragen (dort als offener Punkt vorgemerkt) und inAGENTS.mdBlock B abhaken.
Schritte 2–4 können gegen die bestehende HA-API vor der FMM003-Hardware erledigt werden.
Phase 13 — FMM003-Inbetriebnahme 🟡 VORBEREITET (wartet auf Hardware)
Gerüst der Zuordnungstabelle und alle Schritte:
homeassistant/FMM003_MAPPING.md.
Ziel: Live-Daten vom Fahrzeug fließen nach HA; Fahrterkennung läuft über die Zündung.
Referenz: COMPANION_APP_ARCHITECTURE.md §2b. Nichts hiervon vorab raten oder simulieren.
- Mosquitto-Add-on in HA installieren, Benutzer für das Gerät anlegen.
- Zertifikate: kleine eigene CA (drei
openssl-Befehle: CA-Schlüssel+Zertifikat, Server-Zertifikat für den Broker, Client-Zertifikat fürs Gerät); Broker auf 8883/TLS; CA-/Client-Dateien im Teltonika Configurator unter Security hinterlegen. Ohne Zertifikate verweigert der FMM003 MQTT. - [Besitzer] Broker-Erreichbarkeit entscheiden: Portfreigabe 8883 (einfach; TLS+Login
davor) oder VPS-Broker mit Mosquitto-Bridge über Tailscale (keine Freigabe, mehr Aufwand).
Entscheidung + Begründung in
COMPANION_APP_ARCHITECTURE.md§5.2a undAGENTS.mdnachtragen. - Gerät konfigurieren: Firmware-Version notieren (Codec JSON ist firmwareabhängig!); System → Data Protocol → Codec JSON; GPRS → MQTT mit Broker-IP/Port/Login.
- Erste echte Nachricht mitschneiden:
mosquitto_sub -h <broker> -p 8883 --cafile ca.crt -u <nutzer> -P <pw> -t '#' -v > mitschnitt.txt— mit Zündung an/aus, kurzer Fahrt. Aus dem Mitschnitt die Feldzuordnungstabelle bauen (JSON-Feld → Bedeutung → Einheit) und alshomeassistant/FMM003_MAPPING.mdeinchecken (ohne echte Positionsdaten im Beispiel!). - HA-MQTT-Integration: aus der Tabelle
mqtt:-Sensoren/device_trackerin der HA-Konfiguration definieren (Zündung, Geschwindigkeit, Drehzahl, Tankfüllstand, Position, km). - Fahrterkennung umstellen: neues pyscript
fahrterkennung_fmm003.pynach dem Muster vonfahrterkennung.py— Trigger ist die Zündungs-Entität (1→0 mit Pausentoleranz viatask.unique()/task.sleep(), Ende-Zeitpunkt auf den echten Zündung-aus-Moment rückdatieren;fahrten_pausenzeit_minaus dem Profil weiterverwenden). Erst parallel laufen lassen (schreibt in eine Test-Datei), vergleichen, dann scharf schalten undsensor.iphone_wifi_connectionauseinstellungen.pyentfernen. - Live-Screen aktivieren: Feature-Schalter aus Phase 7 umlegen, Live-Felder anbinden;
GPS-Route der laufenden Fahrt aufzeichnen und im Fahrt-Detail echte Tracks statt der
Start/Ende-Marker zeigen (damit stirbt der letzte Rest von
fakeTrack()endgültig). - ✅ Fertig wenn: eine echte Fahrt automatisch erkannt, mit km und Route gespeichert und in der
App (Liste, Detail mit echter Route, Statistik) korrekt angezeigt wird; VAG-Integration und
WLAN-Trigger sind entfernt;
AGENTS.mdBlock B komplett abgehakt.
Anhang A — Nachschlagereferenz für das ausführende Modell
Entitäten (lesen): pyscript.audi_dashboard_profil, …_fahrten, …_tankvorgaenge,
…_fahrzeugstatus, …_batterieverlauf, …_beleg_ergebnis, …_update_status,
pyscript.reifen_sommer_km, …_winter_km, …_aktiver_satz. Nutzdaten stets im Attribut daten.
Services (schreiben, Domain pyscript): audi_dashboard_profil_schreiben,
…_beleg_hochladen, …_tankvorgang_manuell, …_tankvorgang_aktualisieren,
…_tankvorgang_loeschen, …_fahrt_manuell_anlegen, …_fahrt_loeschen, …_bild_hochladen,
…_bild_loeschen, …_reifen_wechseln, …_backup_jetzt, …_backup_wiederherstellen,
…_jetzt_aktualisieren, …_screening_jetzt, …_update_pruefen, …_update_installieren.
Signaturen: SPECIFICATION.md §5. Service-Aufrufe geben keine Werte zurück — Ergebnisse
kommen asynchron über Entitäten (Muster: Beleg-Upload).
Stolperfallen:
- Profil wird immer als Ganzes geschrieben — nie Teil-Updates erfinden.
edited_fieldseines Datensatzes schützt Felder vor automatischen Überschreibungen — beim Portieren von Bearbeiten-Formularen beibehalten.- Sensorwerte
"unknown"/"unavailable"=Nonebehandeln, nie raten (Musterzustand_oder_none()). - pyscript: kein nacktes
open()/with—task.executor-Muster ausmodules/profil.py. - HA-Attribute haben ~16 KB-Grenze (
frontend_veroeffentlichung.py) — keine großen Blobs in Entitäten stopfen. fahrten.jsonl/tankvorgaenge.jsonlsind aktuell leer — Leerzustände sind der erste Eindruck der App, nicht ein Randfall.
Anhang B — Definition of Done (gesamt)
Die App gilt als fertig, wenn: alle Screens aus Phase 7 in beiden Layouts funktionieren; Onboarding,
Offline-Queue und Beleg-Upload gegen die Produktiv-HA laufen; die App als PWA und (falls Capacitor
umgesetzt) nativ auf dem iPhone installiert ist; der externe Zugriff über datametric360.app
funktioniert, ohne dass HA exponiert ist; das alte Panel archiviert ist; und AGENTS.md den
Endstand widerspiegelt (alle Blöcke A–D abgehakt oder begründet gestrichen).