Files
audi-app/companion-app
Paul Nothaft 4dbf3cb173 Phase 7a: Uebersicht, Fahrzeugstatus und Mein Audi
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.
2026-08-11 11:07:03 +02:00
..

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.