Listen mit Jahr-Monat-Aufklappung, Detailseiten, manuelles Eintragen und Beleg-Upload. Statistik rechnet ueber die portierten Regeln. Zwei offene Audit-Befunde des alten Panels sind hier gleich mit erledigt: 1. Loeschen war ausschliesslich per Wischgeste erreichbar. Wer mit Sprachausgabe oder Schaltersteuerung bedient, wischt nicht - Fahrten und Tankvorgaenge waren fuer diese Nutzer gar nicht loeschbar. Jede Zeile hat jetzt zusaetzlich ein sichtbares Menue, ein richtiger Knopf mit Tastatur. 2. Leaflet kam per CDN und blieb auf einem Handy ohne eigenen Internetzugang still leer. Es liegt jetzt im Buendel (eigener Abschnitt, erst bei Bedarf geladen). Bewusst NICHT uebernommen: fakeTrack() aus dem alten Panel, das eine erfundene Linie zwischen zwei Punkten zeichnete, wenn keine Route vorlag. Solange das Fahrzeug keine Positionen liefert, sagt die Seite das - statt eine glaubwuerdig aussehende Erfindung zu zeigen.
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.