Drei zusammengehörige Teile in einem Repository: - homeassistant/ Das fertige, im Einsatz befindliche Home-Assistant-Panel (panel_custom Custom Element + pyscript-Backend). Echte Fahrzeug- und Personendaten (fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl, Tankbelege) bleiben per .gitignore außen vor; die anonymisierte Vorlage fahrzeugprofil.example.json ist mit dabei. - design-system/ Eigenständige React-Komponentenbibliothek (@audi-dash/ui), die die visuelle Sprache des Panels nachbildet - ohne Audi-Markenzeichen und ohne die lizenzierte Hausschrift. Dient als Grundlage für Claude Design. War bis hierher ein eigenes Repository und ist in dieses eingeschmolzen worden. - companion-app/ Datenschicht der neuen App DataMetric360 (iOS/Android via Capacitor, zusätzlich als Iframe im HA-Dashboard). Noch ohne Oberfläche: REST- und WebSocket-Zugriff auf Home Assistant plus Warteschlange für Änderungen ohne Netz. Ersetzt das eingespritzte hass-Objekt, das nur innerhalb des HA-Frontends existiert. Dazu die Projektdokumentation: SPECIFICATION.md (Ist-Stand des Panels), COMPANION_APP_ARCHITECTURE.md (Architekturentscheidungen der neuen App), AUDIT_2026-08-10.md, DESIGN_BRIEF_DATAMETRIC360.md und der ursprüngliche Bauauftrag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3.7 KiB
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.