b4d44b2731
Phase 10 Schritt 7 ist damit erledigt. auslieferung/App.ipa, 2,4 MB, Ad-hoc signiert mit "Apple Distribution: Paul Nothaft", Profil gueltig bis 29.08.2027, genau ein eingetragenes Geraet. Zwei Annahmen von heute frueh waren falsch und sind korrigiert: die bezahlte Mitgliedschaft stuft das bestehende Team hoch, statt ein neues anzulegen (die Kennung bleibt RMACS9VLS4), und der Export als Ad-hoc funktioniert einwandfrei. Neue Falle festgehalten: beim ersten Signieren fragt der Schluesselbund per Dialog um Erlaubnis. Bleibt der unbeantwortet, haengt xcodebuild wortlos und endet mit errSecInternalComponent. Die Diagnose steht in AGENTS.md, weil das Symptom von sich aus nirgendwohin zeigt. Neu: scripts/ios-luftweg.sh erzeugt manifest.plist, Installationsseite und Symbole fuer die Uebertragung ueber die Luft. Es verweigert eine Basis-Adresse ohne https, weil iOS sonst erst auf dem Telefon still scheitert. Offen bleibt der Host, der die Dateien ausliefert.
633 lines
42 KiB
Markdown
633 lines
42 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 erledigt.** Der Besitzer hat das bezahlte
|
||
> Entwicklerkonto gelöst und das iPhone im Portal registriert; die Team-Kennung
|
||
> blieb dabei `RMACS9VLS4` (die bezahlte Mitgliedschaft stuft das bestehende
|
||
> Team hoch, sie legt kein neues an). Ergebnis: `companion-app/auslieferung/App.ipa`,
|
||
> 2,4 MB, **Ad-hoc** signiert mit `Apple Distribution: Paul Nothaft`, Profil
|
||
> gültig bis 29.08.2027. Zwei Skripte tragen das:
|
||
> `scripts/ios-signieren.sh` (bauen und signieren) und `scripts/ios-luftweg.sh`
|
||
> (Manifest und Installationsseite für die Übertragung über die Luft).
|
||
> Offen bleibt allein ein HTTPS-Host, der die Dateien ausliefert — siehe
|
||
> Schritt 8. Fallstricke und Diagnosen 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 (erledigt 2026-08-29):** `bash companion-app/scripts/ios-signieren.sh`
|
||
baut Webbündel, synchronisiert die Hülle, archiviert signiert und exportiert die `.ipa`
|
||
nach `companion-app/auslieferung/`. 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.
|
||
Der Entscheidungspunkt Kostenlos-vs-Bezahlt ist entschieden: **bezahltes Konto**, damit
|
||
Signatur ein Jahr gültig statt sieben Tage, und Geräteregistrierung über das Portal ohne
|
||
angeschlossenes Telefon.
|
||
⚠️ **Falle, die viel Zeit kostet:** beim ersten Signieren fragt der Schlüsselbund per Dialog
|
||
um Erlaubnis. Wird der nicht beantwortet, hängt `xcodebuild` wortlos minutenlang und endet
|
||
mit `errSecInternalComponent`. Im Dialog **„Immer erlauben"** wählen — ein Archiv signiert
|
||
über 25 Binärdateien und würde sonst jedes Mal erneut fragen.
|
||
8. **Übertragung über die Luft** (statt Kabel): `bash companion-app/scripts/ios-luftweg.sh
|
||
https://<host>` erzeugt `manifest.plist`, Installationsseite und Symbole in
|
||
`ios/build/luftweg/`. Der Ordner muss über **HTTPS mit öffentlich vertrauenswürdigem
|
||
Zertifikat** ausgeliefert werden — iOS lehnt einfaches HTTP und selbstsignierte Zertifikate
|
||
ab, Home Assistant unter `/local/` genügt also **nicht**. Passend: `tailscale serve --bg
|
||
<ordner>` (echtes Let's-Encrypt-Zertifikat auf `*.ts.net`, kein offener Port, iPhone ohnehin
|
||
im Tailnet); der Cloudflare-Tunnel aus Phase 12 täte es später ebenso. Link auf dem iPhone in
|
||
**Safari** öffnen — andere Browser reichen `itms-services://` nicht ans System weiter.
|
||
⏳ Offen: dieser Mac ist bei Tailscale abgemeldet, es läuft also noch kein Host.
|
||
9. ✅ 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).
|