Die App spricht jetzt echt mit Home Assistant. Ein Datenkontext buendelt die vier Datentoepfe, haengt sich an den WebSocket-Ereignisstrom und reicht Verbindungszustand und Warteschlange nach aussen; die Screens kennen weder REST noch WebSocket. Ersteinrichtung prueft die Zugangsdaten, bevor sie sie speichert, und unterscheidet in der Fehlermeldung zwischen abgelehntem Token und nicht erreichbarem Server - zwei Faelle, die voellig verschiedene Reaktionen verlangen. Zwei echte Fehler, die erst der Test gegen die laufende Instanz zutage brachte: 1. Die Typen der Datenschicht deklarierten tank_prozent, sicher_abgestellt und sicherheit. Das Backend schreibt tankprozent, gesichert und sicherheitscheck - die Oberflaeche haette still ueberall undefined gelesen, ohne dass irgendetwas fehlgeschlagen waere. 2. Der Profil-Adapter reichte lebende Verweise ins Rohprofil durch. Ein Formular haette damit den Rohstand mitveraendert und die Zusage gebrochen, den vom Backend fortgeschriebenen Reifen-Kilometerstand nie zu ueberschreiben. Abschnitte werden jetzt kopiert. Ausserdem zwei Parameter-Eigenschaften in der Datenschicht aufgeloest, weil Node sie im Strip-Modus nicht uebersetzt und genau darueber die Rauchtests laufen. Belegt durch neun Pruefungen gegen die laufende Instanz (Lesen, Dienstaufruf, vollstaendiger Warteschlangenumlauf, WebSocket-Anmeldung) und 21 Unit-Tests.
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.