Erste drei Screens auf der fertigen Datenschicht. Uebersicht mit Fahrzeugbild, Sicherungsstatus, Reichweite und Tankfuellstand, Kilometerstand samt Service-Faelligkeit und Vorschau auf letzte Fahrt und Tankung. Fahrzeugstatus zeigt die 16 Einzelpruefungen, wobei ein unbekannter Sensor als hohler Ring erscheint statt als gruen geratener Punkt - dieselbe Vorsicht, die schon das Backend walten laesst. Mein Audi buendelt Fotogalerie und Wege zu den Unterseiten. Dazu die geteilte Rechenlogik der Statistik portiert (Zeitraeume, Langzeitverbrauch mit Plausibilitaetsfenster, Gruppierung nach Jahr und Monat) und Formatierung im deutschen Format. Bild-Komponente kapselt eine Reibung zwischen App und Bibliothek: die App laeuft mit exactOptionalPropertyTypes, ein undefined an einem optionalen Prop waere sonst an jeder Aufrufstelle einzeln zu behandeln.
DataMetric360 — companion app
Private vehicle app for one car. Ships three ways from one codebase: native iOS and Android via
Capacitor, and embedded as a plain iframe in the Home Assistant dashboard. Architecture and the
decisions behind it: ../COMPANION_APP_ARCHITECTURE.md.
Status
Data layer only. No UI yet — the screens come from the Claude Design draft
(../DESIGN_BRIEF_DATAMETRIC360.md), and get implemented on top
of design-system/'s React components once that draft settles. This package was written first on
purpose: how the app talks to Home Assistant doesn't depend on what the screens look like, so it
survives every design iteration untouched.
Not yet added (deliberately, they'd be guesses today): React/Vite, Capacitor, the native secure-storage
adapter, and the Audi brand assets (fonts/rings/badges — those come from homeassistant/www/ at
implementation time, never into design-system/, see the licence note in the architecture doc).
Layout
src/api/
├── types.ts HA state shapes + domain types (Fahrt, Tankvorgang, …) + the entity-ID table
├── umgebung.ts runtime detection (capacitor/iframe/browser), credential storage, URL helpers
├── rest.ts REST client — replaces hass.states / hass.callService
├── live.ts WebSocket client — push updates, auto-reconnect with backoff
├── warteschlange.ts offline queue for writes made without a connection
└── index.ts DataMetricApi — ties the three together, exposes the domain operations
scripts/smoke.ts verification against a running HA instance
Comments are in German, matching the rest of the project.
What replaces what
The old panel got a hass object injected by panel_custom. That object only exists inside the HA
frontend, which is exactly why the old panel can't run as a standalone app. The mapping:
| Old panel | Here |
|---|---|
hass.states[id].attributes.daten |
rest.datenLesen(id) / DataMetricApi.profilLesen() etc. |
hass.callService(...) |
warteschlange.einreihen(...) via the DataMetricApi methods |
| automatic re-render on state push | live.aufZustand(...) |
Same entities, same pyscript services, same backend files written — only the transport changes.
Every write goes through the queue rather than straight to REST. That's what makes offline edits behave the same as online ones, just delayed: a receipt photographed in a dead zone is persisted and sent when the connection returns, surviving an app restart in between.
Commands
Node is installed at C:\Program Files\nodejs but is not on PATH — prefix it:
$env:Path = "C:\Program Files\nodejs;" + $env:Path
npm run typecheck
npm run smoke
smoke targets http://localhost:18123 (the audi_ha_test Docker container) by default; pass a
different base URL as the first argument.
Verification status (2026-08-10)
tsc --noEmit clean under strict plus noUncheckedIndexedAccess and exactOptionalPropertyTypes.
All 7 smoke checks pass against the running test container, covering URL normalisation, the
http→ws/https→wss mapping, that GET /api/ exists and rejects an unauthenticated request with 401,
and that the WebSocket opens with auth_required — which is the message live.ts's whole auth flow
is built around.
Not yet verified — needs a token: authenticated reads, service calls, and the queue's real
round-trip. Those need a Long-Lived Access Token, which is a deliberate manual step (see the LLAT
provisioning decision in the architecture doc). To do it: create a token in the HA profile page, then
extend scripts/smoke.ts with authenticated cases.