Files
audi-app/UMSETZUNGSPLAN.md
T
tobias 137d32d28d Domain auf datametric360.de umgestellt (war .app)
Alle Referenzen in Doku und Code aktualisiert (REVERSE_PROXY.md,
INTERNET_ZUGRIFF_EINRICHTEN.md, COMPANION_APP_ARCHITECTURE.md, AGENTS.md,
UMSETZUNGSPLAN.md, companion-app/src/api/umgebung.ts-Kommentar). Dabei den
.app-spezifischen HSTS-Preload-Hinweis in COMPANION_APP_ARCHITECTURE.md §5.4
korrigiert - gilt für .de nicht, Force-SSL in Nginx Proxy Manager deckt das
weiterhin ab. UMSETZUNGSPLAN.md Phase 12 zusätzlich mit einem
Aktualisierungshinweis versehen (zwei-Hostnamen-Plan und pyscript-Namen dort
waren ohnehin schon überholt, jetzt klar auf REVERSE_PROXY.md/
INTERNET_ZUGRIFF_EINRICHTEN.md als maßgeblich verwiesen).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 15:06:07 +02:00

39 KiB
Raw Blame History

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

  1. 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).
  2. 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.
  3. Nach jeder abgeschlossenen Phase: Haken in diesem Plan setzen, betroffene Checkboxen in AGENTS.md abhaken, „Last updated" in AGENTS.md aktualisieren (englisch!), committen mit deutscher Commit-Message.
  4. 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/ und design/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.md zu dokumentieren.
    • Home Assistant niemals direkt ins Internet stellen. Nur die in Phase 12 beschriebenen Wege.
  5. Sprache: Code-Bezeichner, Kommentare, UI-Texte, Commits auf Deutsch (Ausnahme: AGENTS.md englisch; design-system/ behält englische Props).
  6. Zahlenformat immer de-DE (1.234,5). Übernimm die Helfer de()/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 erledigt bis auf das Signieren aufs Geraet
11 HA-Einbettung, Ablösung + Archivierung des Panels 710 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.

  1. 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).
  2. cd design-system && npm install && npm run build Fertig wenn: design-system/dist/index.js und dist/styles.css existieren, Build ohne Fehler.
  3. npm run smoke (im selben Ordner) Fertig wenn: 21/21 Fälle „ok" melden.
  4. cd ../companion-app && npm install && npm run typecheck Fertig wenn: tsc --noEmit fehlerfrei durchläuft.
  5. 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ßt audi_ha_test (docker start audi_ha_test). Existiert er nicht, siehe homeassistant/README.md (Testbericht-Abschnitt) für den Aufbau; notfalls Phase-1-Schritt 6 überspringen und in Phase 9 nachholen.
  6. npm run smoke in companion-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 und steuer.faellig.

Ziel: Doku stimmt wieder mit dem Code überein; ein Crash-Risiko ist beseitigt. Reine Pflege — keine Funktionsänderungen.

  1. Statistik-Behauptung korrigieren (3 Stellen, gleiche Falschaussage „zeigt Beispielzahlen"):
    • homeassistant/www/audi-dashboard-app.js Kopfkommentar Zeilen ~1315
    • homeassistant/README.md Zeile ~99
    • homeassistant/INSTALL.md Zeile ~204 Neue Aussage sinngemäß: „Die Statistik-Seite berechnet echte Werte aus Fahrten und Tankvorgängen (Zeiträume, Verbrauch, Tag/Nacht, privat/Arbeitsweg)."
  2. INSTALL.md Schritt 4 (Zeilen ~106109): falsche Variablennamen ersetzen. Falsch: DOORS_SENSOR, WINDOWS_SENSOR, LOCK_ENTITY, BATTERY_VOLTAGE_SENSOR. Richtig (siehe homeassistant/pyscript/modules/einstellungen.py): TUER_SENSOREN, FENSTER_SENSOREN, TUERSCHLOSS_SENSOREN (je 4er-Listen) und BATTERIE_SENSOR (einzeln).
  3. 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.
  4. Obsoleten Kommentar entfernen: pyscript/belegverarbeitung.py:41 — den Zusatz „TODO: Datei ablegen" streichen (Datei existiert längst), Konstante selbst unverändert lassen.
  5. profil_lesen() härten (pyscript/modules/profil.py, ~Zeile 5155): fehlende oder nicht parsebare fahrzeugprofil.json abfangen. Verhalten: Fehler ins Log (log.error ist in pyscript global verfügbar), Rückgabe None; Aufrufer prüfen. Vorher alle Aufrufer suchen (grep -rn "profil_lesen" homeassistant/pyscript/) und sicherstellen, dass jeder mit None umgehen kann — wo nicht, dort eine frühe Rückkehr einbauen. pyscript-Eigenheit beachten: Datei-I/O nur über task.executor(io.open, …)-Muster wie im Bestand, kein nacktes open(). Fertig wenn: python3 -c "import ast; ast.parse(open('homeassistant/pyscript/modules/profil.py').read())" fehlerfrei ist und jeder Aufrufer den None-Fall behandelt.
  6. 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.
  7. 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.

  1. Korrektur Modell: überall „Audi RS 4 Avant competition" statt „RS 6 Avant"; Typenschild rs4 (negative/positive) statt rs6.
  2. Fehlende Seiten ergänzen (16 Stück; Vorlage ist jeweils die View im alten Panel — Funktionsnamen aus homeassistant/www/audi-dashboard-app.js, Beschreibung in SPECIFICATION.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.md gefordert.
  3. Export einpflegen: kompletten Ordner design/ durch den neuen Export ersetzen (design/README.md sagt 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.
  4. Committen; AGENTS.md Block 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.

  1. npm-Workspace-Root anlegen — neue Datei package.json im Repo-Root:
    {
      "name": "audi-app",
      "private": true,
      "workspaces": ["design-system", "companion-app"]
    }
    
    Dazu Repo-Root-.gitignore um node_modules/ ergänzen (falls nicht abgedeckt).
  2. Dependencies in companion-app/package.json ergänzen (bestehende Felder beibehalten!): dependencies: react ^18, react-dom ^18, @audi-dash/ui (Versionsangabe * — kommt über den Workspace); devDependencies zusätzlich: vite, @vitejs/plugin-react, @types/react, @types/react-dom. Scripts ergänzen: "dev": "vite", "build": "vite build", "preview": "vite preview".
  3. npm install im Repo-Root ausführen (verlinkt design-system in die companion-app). Danach einmal npm run build --workspace design-system (die App importiert aus dist/).
  4. 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" },
    })
    
  5. tsconfig anpassen: Das bestehende companion-app/tsconfig.json ist 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 (exactOptionalPropertyTypes usw.) NICHT abschwächen.
  6. 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>
      }
      
    Pflicht: Das äußerste Element trägt immer className="ads-root" und data-theme — ohne diese Wurzelklasse greifen die Design-Tokens nicht (Regel aus design-system/.design-sync/conventions.md).
  7. Fertig wenn: npm run dev --workspace companion-app startet, http://localhost:5173 zeigt dunklen Hintergrund (#161b23) mit Text; npm run typecheck --workspace companion-app und npm run build --workspace companion-app laufen fehlerfrei.
  8. Committen: „companion-app: Vite+React-Gerüst und Workspace-Anbindung an @audi-dash/ui". AGENTS.md Block 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).

  1. Kein Router-Paket installieren. Eigener flacher Zustand wie im alten Panel: src/navigation.ts mit type Route = { name: RouteName; id?: string }, React-State im App-Root, geheZu(name, id?), plus ZURUECK-Tabelle (aus dem alten Panel übernehmen und um die neuen Routen ergänzen; 3-stufige Verschachtelung schutz → vertragsdetails → vers beachten).
  2. Theme: src/theme.ts — Zustand "nacht" | "tag", Persistenz in localStorage (Schlüssel dm360.theme), Initialwert: gespeicherter Wert, sonst prefers-color-scheme, Standard nacht. Gesetzt wird ausschließlich data-theme auf dem ads-root-Element. localStorage-Zugriffe in try/catch (Muster aus dem alten Panel, Private-Mode-Browser).
  3. 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.
  4. Platzhalter-Screens: je Hauptbereich eine Datei unter src/screens/ (z. B. Uebersicht.tsx), die vorerst nur Titel per ads-eyebrow/Tile zeigt.
  5. 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.
  6. 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).

  1. Ersteinrichtungs-Screen (src/screens/Einrichtung.tsx) nach dem Entwurf in design/DM360.dc.html (dort der isSetup-Block): Felder Server-Adresse + Zugangs-Token, Knopf „Verbinden" ruft verbindungPruefen() aus der Datenschicht; Fehler unterscheiden nach ApiFehler.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 die Ablage der Datenschicht speichern und in die App wechseln.
  2. Datenkontext: src/datenkontext.tsx — ein React-Context, der beim Start DataMetricApi instanziiert, initial alle vier Reads lädt, sich bei aufZustand() für Live-Updates registriert und {profil, fahrten, tankvorgaenge, status, verbindung} bereitstellt. Übernahme-Pflicht aus dem alten Panel: die Umrechnungen profilZuConfig()/profilZuCar() (inkl. SmartDeal-Gültigkeit: aktiv wenn laeuft_ab fehlt oder ≥ heute, Ablaufdatum einschließlich) aus audi-dashboard-app.js nach TypeScript portieren — Logik exakt beibehalten.
  3. 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.
  4. Stale-Anzeige: last_updated des Fahrzeugstatus mitführen (Muster: Staleness-Indikator des alten Panels).
  5. 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).
  6. Committen; AGENTS.md Block 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 in design-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.

  1. Quellen: Schriften aus design/uploads/*.ttf (oder die woff2-Base64-Blöcke aus homeassistant/www/audi-dashboard.css), Ringe design/assets/audi-rings-{white,black}.svg, Typenschilder aus homeassistant/www/badges/ (alle 14).
  2. Ablage: companion-app/src/assets/audi/ + @font-face-Deklarationen in einer eigenen src/audi-schrift.css (Familien exakt wie im Bestand: "Audi Type" 400, "Audi Type Wide" 300/400, "Audi Type Extended" italic). Der --font-stack wird per CSS-Variable auf dem ads-root überschrieben — design-system/-Dateien bleiben unberührt.
  3. Typenschild-Auswahl nach Modell aus dem Profil (Logik aus vEinst()/Badge-Mapping des alten Panels übernehmen; Theme-abhängig positive/negative-Variante).
  4. Kontrolle: git log --stat des Commits zeigt KEINE Änderung unter design-system/; Gewichte ≥ 600 kommen nirgends vor (grep -rn "font-weight" companion-app/src | grep -vE "300|400" liefert nichts).
  5. Committen; AGENTS.md Block 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.

  1. Token für die Testinstanz erzeugen: audi_ha_test im Browser öffnen (http://localhost:18123), anmelden, Profil → Sicherheit → langlebiges Zugriffstoken erstellen. Token NUR in companion-app/.env.local ablegen (VITE_TEST_TOKEN=…), Datei in .gitignore aufnehmen. Niemals committen.
  2. Authentifizierter Smoke (scripts/smoke-auth.ts, Aufbau wie scripts/smoke.ts): mit Token (a) zustandLesen("pyscript.audi_dashboard_profil") liefert Daten, (b) dienstAufrufen mit einem harmlosen Service (audi_dashboard_jetzt_aktualisieren) gibt 200, (c) Queue-Round-Trip: Eintrag einstellen bei gestopptem Container, Container starten, Queue leert sich. Script in package.json als smoke:auth; ohne gesetztes Token bricht es mit klarer Meldung ab (Exit-Code 0, „übersprungen").
  3. 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).
  4. Fertig wenn: npm run smoke:auth 3/3 grün (mit Token), npx vitest run grün, und beide in companion-app/README.md dokumentiert sind.
  5. Committen; AGENTS.md Block A Test-Punkt abhaken.

Phase 10 — PWA und Capacitor ERLEDIGT (2026-08-11)

PWA fertig. Native Hülle gebaut und in Betrieb: Capacitor 8 für iOS und Android, Build im Simulator (iPhone 17 Pro, iOS 26.4) erfolgreich, App zeigt echte Daten. CapacitorHttp umgeht die CORS-Beschränkung der WebView. Offen bleibt allein das Signieren aufs eigene Gerät — das braucht das angeschlossene iPhone und die Apple-ID des Besitzers.

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):

  1. 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 im index.html.
  2. Erreichbarkeit fürs Handy: solange Phase 12 nicht fertig ist, über Tailscale (npm run build, Ausgabe z. B. über HA /local/ oder vite preview --host im LAN testen).
  3. 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.

  1. Build in HA ausliefern: npm run build, Ausgabe nach /config/www/dm360/ der Produktivinstanz (Weg analog update.ps1; das Script um diesen Ordner erweitern).
  2. configuration.yaml: panel_iframe-Eintrag (Titel „Mein Audi", mdi:car-sports, URL /local/dm360/index.html). Der panel_custom-Block des alten Panels bleibt zunächst parallel bestehen (zweiter Menüpunkt „Mein Audi (alt)").
  3. 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).
  4. Ablösung: alten panel_custom-Block entfernen; Dateien NICHT löschen, sondern verschieben: homeassistant/www/audi-dashboard-app.jshomeassistant/archiv/ (plus Panel-Stub und CSS), README-Hinweis dort ablegen. Beschlossene Regel: archivieren, nie löschen (COMPANION_APP_ARCHITECTURE.md §1).
  5. Fertig wenn: Neues Panel im HA-Menü, altes entfernt aber archiviert, SPECIFICATION.md bekommt eine Kopfnotiz „beschreibt das archivierte Panel; aktuell ist companion-app/".
  6. Committen; AGENTS.md Block D abhaken, Statustabelle umstellen.

Phase 12 — Externer Zugriff: Cloudflare Tunnel + Reverse Proxy 🟡 VORBEREITET

2026-08-28 aktualisiert/teilweise überholt. Die Schritte unten stammen aus der frühen Planungsphase (2026-08-11) und enthalten inzwischen überholte Annahmen: zwei getrennte Hostnamen (App-Domain + API-Subdomain — entfällt, die App wird nie als Web-Build ausgeliefert, siehe COMPANION_APP_ARCHITECTURE.md §5 Punkt 4) und pyscript-Entitäts-/Dienstnamen (seit der Integrations-Umstellung 2026-08-23 sensor.audi_dashboard_*/audi_dashboard.<name>, siehe AGENTS.md Abschnitt H). Reverse-Proxy-Wahl (Nginx Proxy Manager) und die vollständige, aktuelle Freigabeliste stehen bereits fest. Maßgeblich für die tatsächliche Einrichtung sind homeassistant/INTERNET_ZUGRIFF_EINRICHTEN.md (Schritt-für-Schritt-Anleitung) und homeassistant/REVERSE_PROXY.md (die Freigabeliste selbst) - die Liste unten bleibt nur als Entscheidungsprotokoll stehen, nicht als aktuelle Anleitung.

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

  1. [Besitzer] Nameserver umstellen: datametric360.de bei all-inkl auf die Cloudflare-Nameserver zeigen lassen (Cloudflare-Konto → Site hinzufügen → „Full setup"; Domain trägt sonst nichts, Umstellung ist folgenlos). wenn dig NS datametric360.de die Cloudflare-Server liefert.
  2. Hostnamen festlegen (App-Domain + getrennter API-Hostname)überholt, siehe Hinweis oben: ein einziger Hostname (https://datametric360.de) genügt, da die App nativ/sideload-only bleibt und nie als eigener Web-Build ausgeliefert wird.
  3. Cloudflared-Add-on in HA installieren, Tunnel erstellen, ein öffentlicher Hostname (datametric360.de → Reverse-Proxy-Port). Kein Router-Port wird geöffnet. Genaue Klicks: INTERNET_ZUGRIFF_EINRICHTEN.md Schritt 3/5.
  4. Reverse Proxy: Nginx Proxy Manager-Add-on (entschieden, nicht Traefik). Die Allowlist ist umfangreicher als unten ursprünglich skizziert (aktueller Entitäts-/Dienststand plus das OTA-Bündel unter /audi_dashboard_static/app/*) - wortwörtlich in REVERSE_PROXY.md gepflegt, nicht hier dupliziert. ⚠️ Ehrliche Einschränkung bleibt bestehen: /api/websocket lässt sich nicht pfadgenau beschneiden — nach auth_ok sind darüber alle States lesbar. Schutzschicht bleibt der LLAT (ohne Token keine Verbindung).
  5. Prüfen von außen (Mobilfunk, VPN aus): die Prüfbefehle stehen in REVERSE_PROXY.md ("Nach der Einrichtung prüfen"). HA-Port 8123 ist von außen nicht erreichbar.
  6. Fertig wenn: Schritt 5 vollständig besteht und die Server-Adresse in der App eingetragen ist (INTERNET_ZUGRIFF_EINRICHTEN.md Schritt 7). In AGENTS.md Block B abhaken.

Schritte 34 können gegen die bestehende HA-API erledigt werden (die FMM003-Hardware läuft seit 2026-08-13 bereits über flespi, siehe Phase 13 unten — diese Phase hängt nicht mehr daran).


Phase 13 — FMM003-Inbetriebnahme ÜBERHOLT — Hardware lief bereits über flespi

Diese Phase ist gegenstandslos. Die FMM003-Hardware ist seit 2026-08-13 in Betrieb, aber über den in AGENTS.md (§ FMM003/Datenpfad) beschriebenen Weg: flespi (natives Codec8/TCP, IMEI- Auth), nicht MQTT/Mosquitto. Die Schritte unten (eigene CA, Mosquitto-Add-on, Zertifikate, mosquitto_sub-Mitschnitt) beschreiben die zuerst geplante, dann verworfene Variante — stehen nur noch als Entscheidungsprotokoll. homeassistant/FMM003_MAPPING.md (unten referenziert) wurde beim Merge 2026-08-13 deshalb bewusst nicht übernommen. Maßgeblich ist ausschließlich AGENTS.md.

Ziel (nicht mehr aktuell): 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.

  1. Mosquitto-Add-on in HA installieren, Benutzer für das Gerät anlegen.
  2. 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.
  3. [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 und AGENTS.md nachtragen.
  4. Gerät konfigurieren: Firmware-Version notieren (Codec JSON ist firmwareabhängig!); System → Data Protocol → Codec JSON; GPRS → MQTT mit Broker-IP/Port/Login.
  5. 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 als homeassistant/FMM003_MAPPING.md einchecken (ohne echte Positionsdaten im Beispiel!).
  6. HA-MQTT-Integration: aus der Tabelle mqtt:-Sensoren/device_tracker in der HA-Konfiguration definieren (Zündung, Geschwindigkeit, Drehzahl, Tankfüllstand, Position, km).
  7. Fahrterkennung umstellen: neues pyscript fahrterkennung_fmm003.py nach dem Muster von fahrterkennung.py — Trigger ist die Zündungs-Entität (1→0 mit Pausentoleranz via task.unique()/task.sleep(), Ende-Zeitpunkt auf den echten Zündung-aus-Moment rückdatieren; fahrten_pausenzeit_min aus dem Profil weiterverwenden). Erst parallel laufen lassen (schreibt in eine Test-Datei), vergleichen, dann scharf schalten und sensor.iphone_wifi_connection aus einstellungen.py entfernen.
  8. 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).
  9. 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.md Block 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_fields eines Datensatzes schützt Felder vor automatischen Überschreibungen — beim Portieren von Bearbeiten-Formularen beibehalten.
  • Sensorwerte "unknown"/"unavailable" = None behandeln, nie raten (Muster zustand_oder_none()).
  • pyscript: kein nacktes open()/withtask.executor-Muster aus modules/profil.py.
  • HA-Attribute haben ~16 KB-Grenze (frontend_veroeffentlichung.py) — keine großen Blobs in Entitäten stopfen.
  • fahrten.jsonl/tankvorgaenge.jsonl sind 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.de funktioniert, ohne dass HA exponiert ist; das alte Panel archiviert ist; und AGENTS.md den Endstand widerspiegelt (alle Blöcke AD abgehakt oder begründet gestrichen).