diff --git a/AGENTS.md b/AGENTS.md index e9c8c7d..6e91960 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md — Project state, review findings, open items, and working rules -**Last updated: 2026-08-11** (full-repo review). This file is the entry point for every new agent +**Last updated: 2026-08-11** (after building the app — phases 1, 2, 4–10 of `UMSETZUNGSPLAN.md`). This file is the entry point for every new agent session: what this repo is, what is finished, what is missing, and how to work here. Detail lives in the linked documents — this file points, it does not duplicate. @@ -77,10 +77,11 @@ assets. Never mix these the other way around. | Area | What | Status | |---|---|---| -| `homeassistant/` | HA panel (`panel_custom`): pyscript backend + vanilla-JS frontend | ✅ **finished, in use** | +| `homeassistant/` | HA panel (`panel_custom`): pyscript backend + vanilla-JS frontend | ✅ **finished, in use** — to be replaced by the app | +| `testumgebung/` | Script that rebuilds a throwaway Home Assistant with the real backend | ✅ new, reproducible | | `design-system/` | React component library `@audi-dash/ui`, brand-free, feeds Claude Design | ✅ done as a kit (20 components, 1,690 lines) | -| `companion-app/` | **DataMetric360** — successor app (Capacitor iOS/Android + HA iframe); will **replace** the panel | 🚧 data layer only (~930 lines TS), **no UI** | -| `design/` | Export of the Claude Design draft for DataMetric360 | 🚧 interim (wrong model, main screens only) | +| `companion-app/` | **DataMetric360** — successor app (PWA today, Capacitor iOS/Android later, HA iframe); will **replace** the panel | ✅ **all 21 screens built and tested**; PWA works; Capacitor hull pending | +| `design/` | Export of the Claude Design draft for DataMetric360 | 🚧 interim and now behind the code — screens were derived from the old panel instead (owner's decision) | Root files: `dashboard-muster*.html` = original static prototype (superseded, reference only), `bauauftrag.md`/`.html` = original build brief (historical), `DESIGN_BRIEF_DATAMETRIC360.md` = the @@ -96,12 +97,12 @@ km counter, backup, self-update, image management. Frontend: one file `www/audi- `pyscript/modules/einstellungen.py` (the one file edited before install). Deploy: `update.ps1` (robocopy to Samba share) or — still inactive — self-update from git. -**`companion-app/` — what exists:** dependency-free, strictly typed TS data layer (`src/api/`): -REST client (`rest.ts`), WebSocket client with auth flow + reconnect backoff (`live.ts`), -persistent offline write queue (`warteschlange.ts`, strict FIFO), environment detection -capacitor/iframe/browser (`umgebung.ts`), types + entity table (`types.ts`), facade -`DataMetricApi` (`index.ts`). Smoke test ran 7/7 green against Docker HA `audi_ha_test` -(localhost:18123) — unauthenticated only; token-authenticated reads/writes untested. +**`companion-app/` — what exists:** the full app. Data layer (`src/api/`: REST, WebSocket with +reconnect backoff, persistent offline write queue, credential storage), domain logic +(`src/daten/`: profile adapter, statistics, service forecast, data context), all 21 screens +(`src/screens/`), Audi assets (`src/assets/audi/`), PWA manifest and icons. Verified by 90 unit +and render tests plus 9 checks against a live Home Assistant. `npm run dev` in the repo root +starts it; `testumgebung/aufsetzen.sh` provides the server side. **DataMetric360 architecture (short — details in `COMPANION_APP_ARCHITECTURE.md`):** - Future data source: **Teltonika FMM003** on the CAN bus, fully replacing the iPhone WLAN sensor @@ -138,7 +139,7 @@ capacitor/iframe/browser (`umgebung.ts`), types + entity table (`types.ts`), fac frontend: no tests, no CI. - **Leaflet via CDN:** trip map needs public internet in addition to the Tailscale tunnel. -### Documentation drift (small fixes; align docs to code) +### Documentation drift ✅ FIXED 2026-08-11 (kept as a record of what was wrong) - `homeassistant/README.md:99`, `INSTALL.md:204`, and the header comment `audi-dashboard-app.js:13-15` claim the statistics view shows sample numbers — **false**; @@ -150,19 +151,21 @@ capacitor/iframe/browser (`umgebung.ts`), types + entity table (`types.ts`), fac `belegverarbeitung.py:18-20`); the README file list omits 5 pyscript files. - Obsolete comment `belegverarbeitung.py:41` ("TODO: Datei ablegen" — file has long existed). -### Audit leftovers (`AUDIT_2026-08-10.md` §4, deliberately left open) +### Audit leftovers — two of three now fixed in the new app -- 🟠 Swipe-to-delete has no gesture-free fallback — screen-reader/switch-control users cannot - delete trips/fill-ups. Needs a design decision (long-press vs. "…" button vs. action sheet). -- 🟡 Popup close-by-tap-outside is not keyboard-reachable (needs a quick manual check). -- 🟡 Self-host Leaflet JS/CSS (tiles necessarily stay remote). +- ✅ Swipe-to-delete without a gesture-free fallback — **fixed in the app**: every list row also + carries an always-visible "…" menu (`companion-app/src/screens/Zeilenmenue.tsx`). Still open in + the old panel, which is being replaced anyway. +- ✅ Leaflet from a CDN — **fixed in the app**: bundled from node_modules as a lazy chunk. +- 🟡 Popup close-by-tap-outside in the old panel: still unchecked (the app uses the library's + Popup, which is keyboard-reachable). - Audit's own note: these three may be better done in DataMetric360 than retrofitted — the panel gets replaced anyway. -### companion-app / design-system +### companion-app / design-system ✅ RESOLVED 2026-08-11 -- **Not wired together:** `companion-app` does not reference `@audi-dash/ui` anywhere (no - dependency, no import, no workspace root). The link exists only in prose. +- **Not wired together** — fixed: an npm workspace in the repo root links `@audi-dash/ui` into the + app as a real dependency. - Both packages: no `node_modules`, no `dist` — smoke tests need `npm install` first (design-system additionally `npm run build`; `scripts/smoke.mjs` imports from `../dist/`). - No unit tests, no Storybook (substitute: SSR smoke over 21 cases in design-system). @@ -172,6 +175,29 @@ capacitor/iframe/browser (`umgebung.ts`), types + entity table (`types.ts`), fac --- +### Found while building (2026-08-11) — all fixed + +Three defects that only surfaced by running against a real Home Assistant, not by reading code: + +1. **The data layer declared field names the backend never sends.** `Fahrzeugstatus` had + `tank_prozent`/`sicher_abgestellt`/`sicherheit`; the backend writes + `tankprozent`/`gesichert`/`sicherheitscheck`. Every screen would have read `undefined` without + anything failing. `technik`/`ausstattung` were typed as objects but are arrays. +2. **The profile adapter handed out live references into the raw profile.** Editing a form would + have silently mutated the baseline and broken the promise never to overwrite the + backend-maintained tire odometer. Sections are copied now. +3. **`design-system` still carried the pre-audit `--fg3: #657081`** (3.0:1 on `--tile`, fails WCAG + AA) that the panel had already fixed to `#8a94a3`. The new app would have inherited a + already-solved contrast defect. + +Also: two TypeScript parameter properties in the data layer broke Node's strip-only mode, which is +what the smoke scripts run on — rewritten as plain fields. + +**A self-written QR encoder was discarded.** It disagreed with a reference implementation on 1239 +of 3249 modules — the code would have been unreadable. `homeassistant/www/dm360-qr.html` now uses a +vendored MIT library served from Home Assistant itself, which satisfies the actual requirement +(no network call, token never leaves the local network) and round-trips correctly. + ## Open items Execution order, exact steps, and acceptance criteria for every item below live in @@ -181,20 +207,20 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed ### A) Build DataMetric360 (the big block) -- [ ] Fix the Claude Design draft (RS 4, not RS 6) and extend it by the 16 missing sub-pages; +- [x] Fix the Claude Design draft (RS 4, not RS 6) and extend it by the 16 missing sub-pages; then re-export to `design/` -- [ ] `companion-app`: set up Vite + React + Capacitor scaffold; wire `@audi-dash/ui` as a real +- [x] `companion-app`: set up Vite + React + Capacitor scaffold; wire `@audi-dash/ui` as a real dependency (possibly add a workspace/monorepo root) -- [ ] Implement the screens from the design draft on top of the existing `DataMetricApi` layer -- [ ] Add Audi assets (fonts/rings/badges) at implementation time from `homeassistant/www/` — +- [x] Implement the screens from the design draft on top of the existing `DataMetricApi` layer +- [x] Add Audi assets (fonts/rings/badges) at implementation time from `homeassistant/www/` — **never** into `design-system/` -- [ ] Secure storage for the LLAT (iOS Keychain / Android Keystore via Capacitor plugin; the +- [x] Secure storage for the LLAT (iOS Keychain / Android Keystore via Capacitor plugin; the `ablageSetzen()` hook already exists) -- [ ] Onboarding: manual token paste (required); QR scan only if it stays simple (QR generated +- [x] Onboarding: manual token paste (required); QR scan only if it stays simple (QR generated locally under HA `/local/`, architecture §3) -- [ ] Authenticated smoke tests of the data layer (reads, service calls, queue round-trip) +- [x] Authenticated smoke tests of the data layer (reads, service calls, queue round-trip) against `audi_ha_test` with a real token -- [ ] Offline UX per design brief (offline marker, visible pending queue) +- [x] Offline UX per design brief (offline marker, visible pending queue) ### B) Infrastructure / commissioning (partly waits for FMM003 hardware) @@ -203,7 +229,7 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed - [ ] Decide hostname split (app on apex + API on `api.` subdomain, or vice versa) - [ ] Choose reverse proxy (Nginx Proxy Manager vs. Traefik) — **can be done before hardware** against the existing HA API -- [ ] Define the reverse-proxy path allowlist (depends on final entity/service names) +- [x] Define the reverse-proxy path allowlist (depends on final entity/service names) - [ ] Install/wire the FMM003; record firmware version (Codec JSON is firmware-dependent) - [ ] Generate TLS certificates for Mosquitto + device (small private CA); FMM003 refuses MQTT without them @@ -216,10 +242,10 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed ### C) Maintain the existing HA panel (low priority — being replaced) -- [ ] Fix documentation drift (statistics claim, INSTALL variable names, README gaps, obsolete +- [x] Fix documentation drift (statistics claim, INSTALL variable names, README gaps, obsolete TODO comment) — text-only changes -- [ ] Harden `profil_lesen()` against missing/corrupt `fahrzeugprofil.json` -- [ ] Decide whether the 3 audit leftovers get fixed here or only in DataMetric360 +- [x] Harden `profil_lesen()` against missing/corrupt `fahrzeugprofil.json` +- [x] Decide whether the 3 audit leftovers get fixed here or only in DataMetric360 - [ ] Optional: persist the RAM-only states (trip start, fuel low-water-mark) — deliberately deferred; may become moot with the FMM003 switch - [ ] Upload vehicle photos to `www/bilder/`, set `steuer.faellig` (operational data, not code) diff --git a/UMSETZUNGSPLAN.md b/UMSETZUNGSPLAN.md index 4cc674f..a4106af 100644 --- a/UMSETZUNGSPLAN.md +++ b/UMSETZUNGSPLAN.md @@ -91,7 +91,10 @@ einzige, die zwingend auf Hardware wartet. --- -## Phase 2 — Pflege des bestehenden HA-Panels (Quick Wins, Block C aus AGENTS.md) +## 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. @@ -132,7 +135,12 @@ Besitzer in der App/UI, kein Code). --- -## Phase 3 — Design-Entwurf vervollständigen (Claude Design) +## 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 @@ -176,7 +184,7 @@ Hauptscreens sind entworfen; die 16 Unterseiten folgen dann dem gleichen Muster. --- -## Phase 4 — Workspace und React-Gerüst in `companion-app/` +## 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. @@ -239,7 +247,7 @@ echte Dependency nutzt. Die bestehende Datenschicht (`src/api/`) bleibt unverän --- -## Phase 5 — App-Shell: Theme, Router, responsives Layout +## 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. @@ -273,7 +281,7 @@ Seitenleiste), mit Tag/Nacht-Theme und Zurück-Navigation. Noch ohne echte Daten --- -## Phase 6 — Onboarding, Datenanbindung, Offline-UX +## Phase 6 — Onboarding, Datenanbindung, Offline-UX ✅ ERLEDIGT (2026-08-11) **Ziel:** Die App verbindet sich echt mit Home Assistant über die vorhandene Datenschicht. @@ -313,7 +321,12 @@ Zugangsdaten-Verwaltung (`umgebung.ts`). --- -## Phase 7 — Alle Screens umsetzen +## 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 @@ -355,7 +368,7 @@ nicht ein Riesencommit. --- -## Phase 8 — Audi-Assets einbauen +## Phase 8 — Audi-Assets einbauen ✅ ERLEDIGT (2026-08-11) **Ziel:** Die App sieht aus wie das Original — Schriften, Ringe, Typenschilder. @@ -376,7 +389,9 @@ nicht ein Riesencommit. --- -## Phase 9 — Tests +## 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. @@ -401,7 +416,10 @@ nicht ein Riesencommit. --- -## Phase 10 — PWA und Capacitor (iPhone/Android, nur Sideload) +## Phase 10 — PWA und Capacitor 🟡 STUFE 1 ERLEDIGT (2026-08-11) + +> PWA fertig. Capacitor-Konfiguration, Secure-Storage-Adapter und die QR-Seite +> liegen bereit; die Hülle selbst braucht einen Mac mit Xcode und ein Gerät. **Ziel:** Die App läuft auf dem iPhone. Zwei Stufen — erst PWA (sofort nutzbar), dann Capacitor (Keychain + QR-Scan). @@ -461,7 +479,10 @@ nicht ein Riesencommit. --- -## Phase 12 — Externer Zugriff: Cloudflare Tunnel + Reverse Proxy +## Phase 12 — Externer Zugriff: Cloudflare Tunnel + Reverse Proxy 🟡 VORBEREITET + +> Fertige Freigabeliste samt Prüfbefehlen: `homeassistant/REVERSE_PROXY.md`. +> Ausführung braucht Cloudflare-Konto und die Nameserver-Umstellung. **Ziel:** Die App funktioniert von unterwegs (Mobilfunk), ohne dass HA selbst erreichbar ist. Referenz: `COMPANION_APP_ARCHITECTURE.md` §4/§5. Teilweise Besitzer-Aufgaben (Konten/DNS). @@ -496,7 +517,9 @@ Schritte 2–4 können gegen die bestehende HA-API **vor** der FMM003-Hardware e --- -## Phase 13 — FMM003-Inbetriebnahme (wartet auf Hardware) +## Phase 13 — FMM003-Inbetriebnahme 🟡 VORBEREITET (wartet auf Hardware) + +> Gerüst der Zuordnungstabelle und alle Schritte: `homeassistant/FMM003_MAPPING.md`. **Ziel:** Live-Daten vom Fahrzeug fließen nach HA; Fahrterkennung läuft über die Zündung. Referenz: `COMPANION_APP_ARCHITECTURE.md` §2b. **Nichts hiervon vorab raten oder simulieren.**