Files
audi-app/UMSETZUNGSPLAN.md
T
Paul Nothaft da85a2c0d6 iOS-Signierung vorbereiten: Skript, Nachweis des Gerätebaus, offener Punkt
Phase 10 Schritt 7 lässt sich nicht abschließen, aber alles bis zur Signatur
ist gebaut und belegt: Simulator- und Gerätebau (arm64, Release) laufen
fehlerfrei durch, 146/146 Tests grün, Typecheck sauber.

Die Signatur scheitert allein daran, dass dem Entwicklerteam kein Gerät
bekannt ist -- Apple erzeugt ein Development-Profil nur für konkrete UDIDs.
Kein iPhone angeschlossen, keins je mit diesem Mac gepaart, kein
App-Store-Connect-Schlüssel zum Nachtragen.

Neu: companion-app/scripts/ios-signieren.sh macht Bauen, Synchronisieren,
Signieren und den .ipa-Export zu einem Befehl. Nötig, weil ios/ absichtlich
gitignored ist und jede in Xcode geklickte Signatureinstellung beim nächsten
npx cap add ios wieder verschwinden würde -- die Team-Kennung braucht eine
versionierte Heimat.

Bewusst nicht getan: eine unsignierte .ipa als Platzhalter einchecken. Sie
wäre nicht installierbar und läge als Binärdatei dauerhaft in der Historie.
2026-08-29 11:17:46 +02:00

619 lines
40 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```json
{
"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`:
```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`:
```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:
```tsx
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.
>
> **2026-08-29:** Schritt 7 vorbereitet, aber nicht abschließbar. Der Gerätebau
> (arm64, Release) läuft fehlerfrei durch, das Signieren scheitert
> ausschließlich daran, dass dem Team `RMACS9VLS4` **kein Gerät** bekannt ist:
> Apple erzeugt ein Development-Profil nur für konkrete UDIDs. Der ganze
> Ablauf steckt jetzt in `companion-app/scripts/ios-signieren.sh` und läuft
> bis genau zu diesem Punkt. Details in `AGENTS.md` Abschnitt AI.
**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:** statt Xcode von Hand jetzt `bash companion-app/scripts/ios-signieren.sh`
(baut Webbündel, synchronisiert die Hülle, archiviert signiert, exportiert die `.ipa`).
Die Team-Kennung steht im Skript, weil `ios/` gitignored ist und jede in Xcode geklickte
Einstellung beim nächsten `npx cap add ios` verschwinden würde.
⚠️ **Einmalige Voraussetzung, die kein Skript herstellen kann:** das iPhone muss dem Team
bekannt sein — Kabel anschließen und vertrauen, oder UDID unter developer.apple.com
eintragen. Sonst: „Your team has no devices from which to generate a provisioning profile".
⚠️ 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.js` → `homeassistant/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`](homeassistant/INTERNET_ZUGRIFF_EINRICHTEN.md)
> (Schritt-für-Schritt-Anleitung) und [`homeassistant/REVERSE_PROXY.md`](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()`/`with` — `task.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).