da85a2c0d6
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.
619 lines
40 KiB
Markdown
619 lines
40 KiB
Markdown
# 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 | 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.
|
||
|
||
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 ~13–15
|
||
- `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 ~106–109): 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 51–55): 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 3–4 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 A–D abgehakt oder begründet gestrichen).
|