e1d570992e
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>
82 lines
3.7 KiB
Markdown
82 lines
3.7 KiB
Markdown
# 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`](../COMPANION_APP_ARCHITECTURE.md).
|
|
|
|
## Status
|
|
|
|
**Data layer only.** No UI yet — the screens come from the Claude Design draft
|
|
([`../DESIGN_BRIEF_DATAMETRIC360.md`](../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:
|
|
|
|
```bash
|
|
$env:Path = "C:\Program Files\nodejs;" + $env:Path
|
|
```
|
|
|
|
```bash
|
|
npm run typecheck
|
|
```
|
|
|
|
```bash
|
|
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.
|