dc4c1ff0bf
Neue Kachel oberhalb von "Zuletzt" mit echter Leaflet-Mini-Karte, öffnet per Klick eine Vollbild-Standortansicht (Kartendarstellung wählen, auf Fahrzeug/User zentrieren, beide zeigen) mit einem ausziehbaren myAudi-Stil-Menü: Distanz zum Gerät, Adresse (Reverse- Geocoding via Nominatim), fährt/steht/Letzter-Parkplatz-Status, Tankfüllstand/Reichweite, Route- und Teilen-Aktionen. Backend: STANDORT_TRACKER-Einstellung (device_tracker-Entity) plus _standort()-Veröffentlichung, bewusst leer gelassen bis eine echte GPS-Quelle (FMM003/flespi) angebunden ist - Kachel zeigt bis dahin "kein GPS-Signal". Verifiziert in audi_ha_test mit einer manuell gesetzten Test-Position. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
304 lines
21 KiB
Markdown
304 lines
21 KiB
Markdown
# AGENTS.md — Project state, review findings, open items, and working rules
|
|
|
|
**Last updated: 2026-08-11** (full-repo review). This file is the entry point for every new agent
|
|
session: what this repo is, what is finished, what is missing, and how to work here. Detail lives in
|
|
the linked documents — this file points, it does not duplicate.
|
|
|
|
> **Maintenance rule (binding):** whenever you change this repo in a way that affects anything
|
|
> recorded here — completing an open item, making or reversing a decision, adding/removing a
|
|
> component, discovering a new gap — **update this file in the same session** (tick the checkbox,
|
|
> adjust the status table, bump the "Last updated" date). This file must always be written in
|
|
> **English**, even though the rest of the project is German.
|
|
|
|
**How this file is loaded:** Claude Code does not read `AGENTS.md` natively; the root `CLAUDE.md`
|
|
imports it via `@AGENTS.md` (official recommended pattern). Other agents (Codex, Cursor, Copilot)
|
|
read `AGENTS.md` directly. Edit content here, not in `CLAUDE.md`.
|
|
|
|
---
|
|
|
|
## Working rules
|
|
|
|
### Karpathy guidelines (adopted as project standard)
|
|
|
|
From [`multica-ai/andrej-karpathy-skills`](https://github.com/multica-ai/andrej-karpathy-skills),
|
|
behavioral rules derived from Andrej Karpathy's observations on LLM coding failures. They bias
|
|
toward caution over speed — for trivial tasks, use judgment.
|
|
|
|
1. **Think before coding.** Don't assume. Don't hide confusion. Surface tradeoffs. State
|
|
assumptions explicitly; if multiple interpretations exist, present them — don't pick silently.
|
|
If a simpler approach exists, say so. Push back when warranted. If something is unclear, stop
|
|
and ask.
|
|
2. **Simplicity first.** Minimum code that solves the problem. Nothing speculative: no features
|
|
beyond what was asked, no abstractions for single-use code, no unrequested configurability, no
|
|
error handling for impossible scenarios. If 200 lines could be 50, rewrite.
|
|
3. **Surgical changes.** Touch only what you must. Don't "improve" adjacent code, comments, or
|
|
formatting; don't refactor what isn't broken; match existing style. Remove only orphans **your**
|
|
change created; mention (don't delete) pre-existing dead code. Every changed line should trace
|
|
directly to the request.
|
|
4. **Goal-driven execution.** Define success criteria, loop until verified. "Fix the bug" → write a
|
|
test that reproduces it, then make it pass. For multi-step tasks, state a brief plan with a
|
|
verify step per item.
|
|
|
|
### Claude Code practices (current guidance, 2026)
|
|
|
|
- Keep this file **concise and specific** — instructions concrete enough to verify ("run X before
|
|
Y"), markdown headers/bullets, no dense prose. Context files reduce adherence as they grow;
|
|
target roughly ≤200 lines of actual instruction.
|
|
- Don't record here what the code or git history already shows (directory dumps, dependency
|
|
lists) — record pitfalls, rationale, decisions, and conventions that differ from defaults.
|
|
- Conflicting instructions get picked arbitrarily — when updating, **remove** superseded rules
|
|
rather than stacking corrections.
|
|
- Verify loading with `/context` (file must appear under **Memory files**); edit via `/memory`.
|
|
|
|
---
|
|
|
|
## What this project is
|
|
|
|
Private replacement for the Audi *connect plug & play* app (discontinued end of 2026), for exactly
|
|
one vehicle (**Audi RS 4 Avant competition**), single user, self-hosted on Home Assistant, access
|
|
via Tailscale only. Read-only toward the vehicle — no remote control. Covers: vehicle status, trip
|
|
log, fuel log with Shell receipt parsing, service forecasting, insurance/tax, tire management.
|
|
|
|
**License rule (hard):** Audi Type fonts, four-rings SVG, and model-badge SVGs are cleared **only
|
|
for this one private, unpublished installation**. Therefore `design-system/` is brand-free (it gets
|
|
uploaded to Claude Design), while the HA panel and the future sideload-only app may use the real
|
|
assets. Never mix these the other way around.
|
|
|
|
**Reading order for a new session:**
|
|
1. This file (overview + open items)
|
|
2. `SPECIFICATION.md` — authoritative for the HA panel, incl. §7 "Known Gaps"
|
|
3. `COMPANION_APP_ARCHITECTURE.md` — authoritative for DataMetric360 (decided, barely built)
|
|
4. `AUDIT_2026-08-10.md` — accessibility/platform audit of the panel (partly done, rest below)
|
|
5. `bauauftrag.md` — **historical only**; code has diverged (see SPECIFICATION.md §7)
|
|
|
|
## The three projects in this repo
|
|
|
|
| Area | What | Status |
|
|
|---|---|---|
|
|
| `homeassistant/` | HA panel (`panel_custom`): pyscript backend + vanilla-JS frontend | ✅ **finished, in use** |
|
|
| `design-system/` | React component library `@audi-dash/ui`, brand-free, feeds Claude Design | ✅ done as a kit (20 components, 1,690 lines) |
|
|
| `companion-app/` | **DataMetric360** — successor app (Capacitor iOS/Android + HA iframe); will **replace** the panel | 🚧 data layer only (~930 lines TS), **no UI** |
|
|
| `design/` | Export of the Claude Design draft for DataMetric360 | 🚧 interim (wrong model, main screens only) |
|
|
|
|
Root files: `dashboard-muster*.html` = original static prototype (superseded, reference only),
|
|
`bauauftrag.md`/`.html` = original build brief (historical), `DESIGN_BRIEF_DATAMETRIC360.md` = the
|
|
prompt for the Claude Design project.
|
|
|
|
**`homeassistant/` in one paragraph:** 10 pyscript scripts + 4 modules (`pyscript/modules/`); trip
|
|
detection via the iPhone WLAN sensor with pause tolerance, two-stage trip completion via HA history
|
|
screening (odometer often updates only on the next trip), fill-up detection on fuel-level rise,
|
|
Shell PDF parser (`data/shell_beleg_parser.py`, subprocess, the only tested part of the repo), tire
|
|
km counter, backup, self-update, image management. Frontend: one file `www/audi-dashboard-app.js`
|
|
(3,130 lines, custom element, no framework/bundler), 5 tabs + ~19 detail routes, cache-busting via
|
|
`audi-dashboard-version.json` + loader stub. Entity IDs configured centrally in
|
|
`pyscript/modules/einstellungen.py` (the one file edited before install). Deploy: `update.ps1`
|
|
(robocopy to Samba share) or — still inactive — self-update from git.
|
|
|
|
**`companion-app/` — what exists:** dependency-free, strictly typed TS data layer (`src/api/`):
|
|
REST client (`rest.ts`), WebSocket client with auth flow + reconnect backoff (`live.ts`),
|
|
persistent offline write queue (`warteschlange.ts`, strict FIFO), environment detection
|
|
capacitor/iframe/browser (`umgebung.ts`), types + entity table (`types.ts`), facade
|
|
`DataMetricApi` (`index.ts`). Smoke test ran 7/7 green against Docker HA `audi_ha_test`
|
|
(localhost:18123) — unauthenticated only; token-authenticated reads/writes untested.
|
|
|
|
**DataMetric360 architecture (short — details in `COMPANION_APP_ARCHITECTURE.md`):**
|
|
- Future data source: **Teltonika FMM003** on the CAN bus, fully replacing the iPhone WLAN sensor
|
|
and the VAG integration.
|
|
- **Current data path (since 2026-08-11): Codec JSON → MQTT/TLS → Mosquitto add-on → HA.** Traccar
|
|
is **dropped** (the decision log deliberately remains in the architecture doc, marked ÜBERHOLT).
|
|
- Frontend access: HA REST + WebSocket with a long-lived access token (no `hass` object).
|
|
- External access: Cloudflare Tunnel + reverse proxy with a path allowlist; HA itself stays
|
|
unreachable. Domain **`datametric360.app`** registered (all-inkl, 2026-08-11).
|
|
- Distribution: **sideload only** (license reason) — hence real Audi assets are allowed there.
|
|
- Trip detection moves to FMM003 ignition; the pause-tolerance feature
|
|
(`fahrten_pausenzeit_min`) is a deliberate product decision and must be preserved.
|
|
|
|
---
|
|
|
|
## Review findings (2026-08-11)
|
|
|
|
### HA panel — known gaps (most also in SPECIFICATION.md §7)
|
|
|
|
- **GPS is dead schema:** `start_lat/lon`, addresses, `route`, `avg_speed_kmh` never populated.
|
|
The trip-detail map draws a **fabricated** line via `fakeTrack()`
|
|
(`audi-dashboard-app.js:414`) — not a real track.
|
|
- **No GPS fallback in trip completion:** trips without an odometer match stay `status="offen"`
|
|
forever (`modules/fahrtabschluss_logik.py:16-20`).
|
|
- **RAM-only state:** running trip (`_fahrt_start_ts`) and fuel low-water-mark
|
|
(`_tiefststand_pct`) do not survive an HA restart. Deliberately deferred hardening.
|
|
- **Inactive features:** `BATTERIE_SENSOR = ""` (entire battery-history feature is a no-op) and
|
|
`UPDATE_REPO_URL = ""` (self-update inactive), both in `pyscript/modules/einstellungen.py`.
|
|
`www/bilder/` has no vehicle photos (all slots show placeholders); `steuer.faellig` unset.
|
|
- **Robustness:** `profil_lesen()` in `modules/profil.py` does not handle a missing/corrupt
|
|
`fahrzeugprofil.json` — all callers throw.
|
|
- **Tests:** only the receipt parser is tested (`data/tests/test_shell_beleg_parser.py`, 10 real
|
|
receipts) — and its test PDFs are gitignored, so a fresh clone can't run it. Backend and
|
|
frontend: no tests, no CI.
|
|
- **Leaflet via CDN:** trip map needs public internet in addition to the Tailscale tunnel.
|
|
|
|
### Documentation drift (small fixes; align docs to code)
|
|
|
|
- `homeassistant/README.md:99`, `INSTALL.md:204`, and the header comment
|
|
`audi-dashboard-app.js:13-15` claim the statistics view shows sample numbers — **false**;
|
|
`vStat()` computes real values from `TRIPS`/`FILLS`.
|
|
- `INSTALL.md` step 4 names variables that no longer exist (`DOORS_SENSOR`, `WINDOWS_SENSOR`,
|
|
`LOCK_ENTITY`, `BATTERY_VOLTAGE_SENSOR`) — actual names: `TUER_SENSOREN`/`FENSTER_SENSOREN`/
|
|
`TUERSCHLOSS_SENSOREN`/`BATTERIE_SENSOR`.
|
|
- `homeassistant/README.md:110` mentions the removed 97% full-tank rule (removal documented in
|
|
`belegverarbeitung.py:18-20`); the README file list omits 5 pyscript files.
|
|
- Obsolete comment `belegverarbeitung.py:41` ("TODO: Datei ablegen" — file has long existed).
|
|
|
|
### Audit leftovers (`AUDIT_2026-08-10.md` §4, deliberately left open)
|
|
|
|
- 🟠 Swipe-to-delete has no gesture-free fallback — screen-reader/switch-control users cannot
|
|
delete trips/fill-ups. Needs a design decision (long-press vs. "…" button vs. action sheet).
|
|
- 🟡 Popup close-by-tap-outside is not keyboard-reachable (needs a quick manual check).
|
|
- 🟡 Self-host Leaflet JS/CSS (tiles necessarily stay remote).
|
|
- Audit's own note: these three may be better done in DataMetric360 than retrofitted — the panel
|
|
gets replaced anyway.
|
|
|
|
### companion-app / design-system
|
|
|
|
- **Not wired together:** `companion-app` does not reference `@audi-dash/ui` anywhere (no
|
|
dependency, no import, no workspace root). The link exists only in prose.
|
|
- Both packages: no `node_modules`, no `dist` — smoke tests need `npm install` first
|
|
(design-system additionally `npm run build`; `scripts/smoke.mjs` imports from `../dist/`).
|
|
- No unit tests, no Storybook (substitute: SSR smoke over 21 cases in design-system).
|
|
- `design/` export (2026-08-11) has two known, already-commissioned fixes not yet in the export:
|
|
shows **RS 6 instead of RS 4**, and **16 sub-pages** the HA panel already has are missing
|
|
(list in `design/README.md`).
|
|
|
|
---
|
|
|
|
## Open items
|
|
|
|
### A) Build DataMetric360 (the big block)
|
|
|
|
- [ ] Fix the Claude Design draft (RS 4, not RS 6) and extend it by the 16 missing sub-pages;
|
|
then re-export to `design/`
|
|
- [ ] `companion-app`: set up Vite + React + Capacitor scaffold; wire `@audi-dash/ui` as a real
|
|
dependency (possibly add a workspace/monorepo root)
|
|
- [ ] Implement the screens from the design draft on top of the existing `DataMetricApi` layer
|
|
- [ ] Add Audi assets (fonts/rings/badges) at implementation time from `homeassistant/www/` —
|
|
**never** into `design-system/`
|
|
- [ ] Secure storage for the LLAT (iOS Keychain / Android Keystore via Capacitor plugin; the
|
|
`ablageSetzen()` hook already exists)
|
|
- [ ] Onboarding: manual token paste (required); QR scan only if it stays simple (QR generated
|
|
locally under HA `/local/`, architecture §3)
|
|
- [ ] Authenticated smoke tests of the data layer (reads, service calls, queue round-trip)
|
|
against `audi_ha_test` with a real token
|
|
- [ ] Offline UX per design brief (offline marker, visible pending queue)
|
|
|
|
### B) Infrastructure / commissioning (partly waits for FMM003 hardware)
|
|
|
|
- [ ] Switch `datametric360.app` nameservers at all-inkl to Cloudflare ("full setup") —
|
|
prerequisite for the tunnel; domain carries nothing else, so this is consequence-free
|
|
- [ ] Decide hostname split (app on apex + API on `api.` subdomain, or vice versa)
|
|
- [ ] Choose reverse proxy (Nginx Proxy Manager vs. Traefik) — **can be done before hardware**
|
|
against the existing HA API
|
|
- [ ] Define the reverse-proxy path allowlist (depends on final entity/service names)
|
|
- [ ] Install/wire the FMM003; record firmware version (Codec JSON is firmware-dependent)
|
|
- [x] Generate TLS certificates for Mosquitto + device (small private CA) — done 2026-08-11, 10-year
|
|
validity; Mosquitto configured (`certfile`/`keyfile`/`cafile`/`require_certificate: true`).
|
|
Remaining: upload root/client cert/key to the FMM003 Security tab — filenames must end in
|
|
`.pem`/`.pem.crt`/`.pem.key` (Configurator rejects plain `.crt`/`.key`, content-agnostic check)
|
|
- [ ] Decide broker reachability for the vehicle — **reopened 2026-08-11 evening**: port-forward
|
|
8883 on the Speedport Smart 4 Plus looked correctly configured (rule present, right internal
|
|
IP, right port) and internal reachability was confirmed (`homeassistant.local:8883` open from
|
|
the LAN, Mosquitto TLS listener genuinely up, TLS cert chain end-to-end verified byte-for-byte
|
|
against the FMM003's uploaded client cert), but the port stayed **closed from outside**
|
|
(confirmed via external port checker, both before and after a router reboot that changed the
|
|
dynamic WAN IP — DNS/DuckDNS matched correctly each time, so not a DNS or CGNAT issue).
|
|
Community reports (ComputerBase, Telekom Hilft) describe this as a known, Telekom-acknowledged
|
|
firmware bug on this router model; a full disable-the-firewall workaround doesn't exist on this
|
|
model either. Port-forward rule has been removed again. **New direction: route the FMM003
|
|
through flespi instead** (native Teltonika/Codec8 channel, IMEI-based auth, no certs, no
|
|
inbound port needed at all — flespi has a stable public endpoint; HA pulls data back out via
|
|
flespi's REST API or MQTT, outbound-only). Free flespi tier (10 devices/2 channels) is enough
|
|
for one vehicle. Next step: user creates the flespi account + Teltonika channel; a `flespi`
|
|
custom integration is already present in this HA instance (unconfigured) — check what it
|
|
needs once flespi-side setup exists.
|
|
Unrelated but still valid from the same session: DuckDNS hostname
|
|
`datametric360.duckdns.org` reliably updating; Let's Encrypt for HA's own local UI works (root
|
|
cause of the earlier DNS-01 failures was a stray `aliases` entry in the DuckDNS add-on config,
|
|
not DNS/network — see COMPANION_APP_ARCHITECTURE.md §5 item 2a). HA's SSL config now lives in
|
|
Settings → System → Network (UI), not `configuration.yaml`'s old `http:` block, which was
|
|
removed after HA started migrating/ignoring it. The Mosquitto TLS setup itself (cert chain,
|
|
`require_certificate: true`, `certfile`/`keyfile`/`cafile`) is verified correct and can be
|
|
reused as-is if a self-hosted broker is ever revisited.
|
|
- [ ] MQTT Client Type on the FMM003 ("Custom server" not selectable in practice, "AWS IoT Custom"
|
|
pointed at a self-hosted broker was confirmed working by two independent community reports) —
|
|
**moot for now** given the flespi pivot above: flespi uses the device's native Codec8/TCP
|
|
channel, not MQTT at all, so Server Settings should switch to Protocol: TCP against the flespi
|
|
channel host/port instead, and Codec set to "Codec 8 Extended". Revisit this item only if a
|
|
self-hosted broker is picked back up later.
|
|
- [ ] Capture the first real Codec JSON message (`mosquitto_sub`/MQTT Explorer) and build the
|
|
field mapping from it — **do not guess beforehand** (explicit decision)
|
|
- [ ] Move trip detection to FMM003 ignition (reuse the `fahrterkennung.py` pattern, keep pause
|
|
tolerance); then remove `sensor.iphone_wifi_connection` from `einstellungen.py`
|
|
|
|
### C) Maintain the existing HA panel (low priority — being replaced)
|
|
|
|
- [x] Wide-screen/desktop layout (`@container (min-width:860px)` in `audi-dashboard-ios.css`,
|
|
turns the same `.tabbar` into a 264px side-nav via CSS only, no JS/markup duplication) — done
|
|
2026-08-11 from the Claude Design "DataMetric360 Board" draft (`claude.ai/design` project
|
|
`c28a8d4d-ec4e-4178-9e49-ab5b90c02097`). Found and fixed a real regression while verifying it
|
|
live in `audi_ha_test`: the overlay's `:host > div{height:100%}` (meant to drop the old
|
|
412px-cap for the sidebar layout) was applied unconditionally instead of only inside the
|
|
`@container` block, which broke the **normal mobile layout too** — HA doesn't reliably
|
|
propagate a real height down through `panel_custom`, so `height:100%` collapsed `main#view` to
|
|
~0px (tabbar rendered right under the header, content invisible). Fix: kept the outer wrapper
|
|
viewport-anchored (`min(880px, calc(100dvh - 24px))`, matching `audi-dashboard.css`'s original
|
|
reasoning) at all widths, and only drop the 880px cap (`calc(100dvh - 24px)`, still
|
|
viewport-anchored, not parent-relative) inside `@container (min-width:860px)`. Verified live at
|
|
both narrow (mobile tab bar, full content) and wide (1144px container, grid side-nav,
|
|
navigation clicks, settings back-arrow) — screenshots taken, no regressions found.
|
|
- [x] Standort-Kachel (live vehicle GPS position on the Übersicht, above "Zuletzt") — done
|
|
2026-08-12. New tile with a real (non-fake) Leaflet mini-map, opens a fullscreen "Standort"
|
|
route on tap; fullscreen has 4 floating controls (map style, center-on-vehicle,
|
|
center-on-user, fit-both) and a draggable myAudi-style bottom sheet (peek/expand/close by
|
|
drag or tap, opens on vehicle-marker click) showing distance-to-user, address (client-side
|
|
reverse geocoding via the public Nominatim API — same "public API, no key, low single-
|
|
vehicle volume" reasoning as the existing Leaflet-CDN gap below), fährt/steht/"Letzter
|
|
Parkplatz" state, fuel/range, and Route (Google Maps deep link) / Teilen (Web Share API)
|
|
actions. Backend: new `STANDORT_TRACKER` entity-ID setting in `einstellungen.py` (device_
|
|
tracker with lat/lon attributes), published via a new `_standort()` helper in
|
|
`frontend_veroeffentlichung.py` — **left empty/unconfigured**, no live vehicle GPS source
|
|
exists yet (same FMM003/flespi gap as section B); the tile shows "Kein GPS-Signal vom
|
|
Fahrzeug" until that's wired up. Verified end-to-end in `audi_ha_test` with a manually
|
|
created `device_tracker.test_fahrzeug` test entity (tile, fullscreen map, and menu all
|
|
confirmed rendering/populating correctly); the device's own location (browser Geolocation
|
|
API) and the Route/Teilen deep links work independently of the vehicle-GPS gap. Pushed to
|
|
the Claude Design project (`c28a8d4d-ec4e-4178-9e49-ab5b90c02097`, "DataMetric360 Board")
|
|
for visual refinement — the JS/CSS ship functionally complete but visually plain
|
|
(placeholder vehicle-marker glyph, default `.aktion`/`.tile` styling); Claude Design's job is
|
|
polish, not structure. One known rough edge: reverse-geocoded address didn't resolve within
|
|
a few seconds in the `audi_ha_test` container (no crash, stays on "Adresse wird ermittelt …"
|
|
indefinitely) — likely just that container's outbound network to Nominatim specifically;
|
|
unconfirmed whether this reproduces on the real HA instance.
|
|
- [ ] Fix documentation drift (statistics claim, INSTALL variable names, README gaps, obsolete
|
|
TODO comment) — text-only changes
|
|
- [ ] Harden `profil_lesen()` against missing/corrupt `fahrzeugprofil.json`
|
|
- [ ] Decide whether the 3 audit leftovers get fixed here or only in DataMetric360
|
|
- [ ] Optional: persist the RAM-only states (trip start, fuel low-water-mark) — deliberately
|
|
deferred; may become moot with the FMM003 switch
|
|
- [ ] Upload vehicle photos to `www/bilder/`, set `steuer.faellig` (operational data, not code)
|
|
|
|
### D) Once the panel is superseded
|
|
|
|
- [ ] **Archive** `audi-dashboard-app.js`, don't delete (settled decision, architecture §1)
|
|
|
|
---
|
|
|
|
## Working conventions (observed — keep them)
|
|
|
|
- German is the project language: identifiers, comments, commits, UI texts. Exceptions:
|
|
`design-system/` uses English props/JSDoc (Claude Design audience) — and **this file**, which is
|
|
English by rule.
|
|
- Decisions are logged, including rejected ones (see the Traccar section of the architecture
|
|
doc); superseded sections stay in place marked "ÜBERHOLT" rather than being deleted.
|
|
- Numbers in `de-DE` format; font weights 300/400 only, never ≥600 (only those cuts exist).
|
|
- Real vehicle/movement data stays local (`data/` contents and receipt PDFs are gitignored).
|
|
- Security principle: HA is never publicly exposed; only narrowly scoped surfaces (MQTT broker,
|
|
proxy allowlist) may be exposed, each by explicit decision.
|