Bringt die vollstaendig gebaute DataMetric360-App zusammen (React/Vite, 21 Screens, Audi-Assets, PWA + native Capacitor-Huelle, 90+9 Tests) sowie die profil_lesen()-Haertung gegen fehlende/kaputte Profildatei zusammen. Konfliktaufloesung: - AGENTS.md, INSTALL.md, README.md: main-Fassung war jeweils die chronologisch neuere, uebernommen und um die durch den Merge tatsaechlich erledigten Punkte ergaenzt (profil_lesen()-Haertung, Audit-Reste-Entscheidung). - fahrterkennung.py: toten WLAN-Zweig vom Branch verworfen, Zuendungs- basierte Erkennung von main behalten. - homeassistant/FMM003_MAPPING.md (MQTT/Mosquitto-Ansatz vom 2026-08-11, vor der Umstellung auf flespi) bewusst nicht uebernommen - main nutzt seit 2026-08-12 flespi als alleinigen FMM003-Datenweg. UMSETZUNGSPLAN.md Phase 13 entsprechend als ueberholt markiert, verweist auf AGENTS.md als massgeblich. - REVIEW_main_2026-08-13.md, ha_install.md (add/add): main-Fassung war die spaetere Revision derselben Dokumente, uebernommen. Die von REVIEW_main_2026-08-13.md befuerchtete Merge-Falle (profil_lesen() gibt jetzt None zurueck, main-seitige Aufrufer pruefen das nicht) wurde verifiziert als bereits entschaerft: alle 5 Aufrufstellen im gemergten Stand (backup.py x2, fahrterkennung.py, frontend_veroeffentlichung.py, reifenzaehler.py, tankerkennung.py) sind None-sicher. installationspaket/ nicht Teil dieses Commits (gitignored, wird bei Bedarf neu zusammengestellt).
40 KiB
AGENTS.md — Project state, review findings, open items, and working rules
Last updated: 2026-08-13 (merged the umsetzung-datametric360 branch — companion app phases
1–10 done, see UMSETZUNGSPLAN.md). 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,
behavioral rules derived from Andrej Karpathy's observations on LLM coding failures. They bias
toward caution over speed — for trivial tasks, use judgment.
- 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.
- 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.
- 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.
- 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. - This file (plus the session's task list) is the source of truth for what's done — not a leftover
plan-mode file. A
~/.claude/plans/*.mdfile surfaced by the system as "not yet complete" can be stale: plan files aren't marked complete when their work finishes. Before claiming a feature is unbuilt (e.g. "the Setup menu isn't implemented yet"), check the[x]/[ ]items below and/or the task list — trust those over an old plan file's own claim about its status. (Caused a real mistake 2026-08-12: told the user the Setup menu wasn't built yet, when it had shipped hours earlier in the same project — see the "Setup menu"[x]entry further down.)
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:
- This file (overview + open items)
UMSETZUNGSPLAN.md— the step-by-step execution plan for all open items (13 phases with commands and acceptance criteria); when working on an open item, follow the plan's phaseSPECIFICATION.md— authoritative for the HA panel, incl. §7 "Known Gaps"COMPANION_APP_ARCHITECTURE.md— authoritative for DataMetric360 (decided, phases 1–10 built)AUDIT_2026-08-10.md— accessibility/platform audit of the panel (partly done, rest below)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 — to be replaced by the app |
testumgebung/ |
Script that rebuilds a throwaway Home Assistant with the real backend | ✅ new, reproducible |
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 (web, PWA, native iOS/Android via Capacitor, HA iframe); will replace the panel | ✅ all 21 screens built and tested; runs natively on iOS with real data |
design/ |
Export of the Claude Design draft for DataMetric360 | 🚧 interim and now behind the code — screens were derived from the old panel instead (owner's decision) |
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 + 5 modules (pyscript/modules/); trip
detection via the FMM003 ignition sensor with pause tolerance (the iPhone WLAN sensor is gone — see
section B), 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
(~4,000 lines, custom element, no framework/bundler), 5 tabs + ~19 detail routes, cache-busting via
audi-dashboard-version.json + loader stub. Entity IDs are assigned in the UI (Settings →
Fahrzeug einrichten → Setup), stored as overrides in data/entitaeten.json;
pyscript/modules/einstellungen.py holds only the built-in defaults. Deploy: update.ps1
(robocopy to Samba share) or — still inactive — self-update from git.
companion-app/ — what exists: the full app. Data layer (src/api/: REST, WebSocket with
reconnect backoff, persistent offline write queue, credential storage), domain logic
(src/daten/: profile adapter, statistics, service forecast, data context), all 21 screens
(src/screens/), Audi assets (src/assets/audi/), PWA manifest and icons. Verified by 90 unit
and render tests plus 9 checks against a live Home Assistant. npm run dev in the repo root
starts it; testumgebung/aufsetzen.sh provides the server side.
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-12): FMM003 → flespi (native Codec8/TCP, IMEI auth) → HA. Both earlier directions are dropped: Traccar first, then the self-hosted MQTT/TLS broker — the router's port-forward turned out to be unusable. The decision logs deliberately remain in the architecture doc, marked ÜBERHOLT. Details and open steps in section B below.
- Frontend access: HA REST + WebSocket with a long-lived access token (no
hassobject). - External access: Cloudflare Tunnel + reverse proxy with a path allowlist; HA itself stays
unreachable. Domain
datametric360.appregistered (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)
Design/CI review of main (2026-08-13) — see DESIGN_REVIEW_2026-08-13.md
Code review of all UI layers plus headless-browser screenshots of the panel (mock hass),
checked against the repo's own Audi-CI rules. Headlines: the iOS overlay
(audi-dashboard-ios.css) overrides the documented palette/radii, adds shadows/blur and turns
red into a fill color without any recorded decision (visible e.g. as a red-filled "Abbrechen"
next to a black "Speichern" in the setup popup); design-system/ still carries pre-audit state
(old low-contrast --fg3 #657081, no focus styles at all, 13.5px inputs, 34px icon buttons);
the design/ export's RS-6 error extends into the technical sample data (73 l tank, 285/30 R22,
FIN series 4G). Four browser-verified rendering defects: switches stretched by
.feld label{flex:1 1 auto} (state display becomes ambiguous), double "offen" in the trip list,
empty oil-change tile when the service book is empty, and fragmented Leaflet rendering in all
three maps (verify on a real device). Positives: the 300/400 font-weight rule and de-DE
formatting hold everywhere; design-system/ is verifiably brand-free.
HA panel — known gaps (most also in SPECIFICATION.md §7)
- GPS is dead schema for trip records specifically:
start_lat/lon, addresses,route,avg_speed_kmhon individual trips never populated. The trip-detail map draws a fabricated line viafakeTrack()(audi-dashboard-app.js:414) — not a real track. (Unrelated: the Übersicht's live Standort-Kachel is no longer in this state —STANDORT_TRACKERnow points at a realdevice_tracker, see section B/C. This item is only about per-trip route data, which nothing currently populates.) - 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:
UPDATE_REPO_URL = ""(self-update inactive) inpyscript/modules/einstellungen.py.www/bilder/has no vehicle photos (all slots show placeholders);steuer.faelligunset. (BATTERIE_SENSORwas the same kind of gap until 2026-08-12 — now wired to the FMM003'sexternal_power_voltage, see section B/C below. Several other fields are newly inactive since the same date for a different reason — theTommiG1/HA_VAG-EU-Data-Actintegration they depended on was dropped:KM_SENSOR,TANK_SENSOR,RANGE_SENSOR,REFRESH_BUTTON, all door/window/lock sensors, oil-change/ inspection sensors. All are configurable again via the new Setup menu, see section C.) - Robustness:
profil_lesen()inmodules/profil.pydoes not handle a missing/corruptfahrzeugprofil.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 ✅ FIXED 2026-08-11 (kept as a record of what was wrong)
homeassistant/README.md:99,INSTALL.md:204, and the header commentaudi-dashboard-app.js:13-15claim the statistics view shows sample numbers — false;vStat()computes real values fromTRIPS/FILLS.homeassistant/README.md:110mentions the removed 97% full-tank rule (removal documented inbelegverarbeitung.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 — two of three now fixed in the new app
- ✅ Swipe-to-delete without a gesture-free fallback — fixed in the app: every list row also
carries an always-visible "…" menu (
companion-app/src/screens/Zeilenmenue.tsx). Still open in the old panel, which is being replaced anyway. - ✅ Leaflet from a CDN — fixed in the app: bundled from node_modules as a lazy chunk.
- 🟡 Popup close-by-tap-outside in the old panel: still unchecked (the app uses the library's Popup, which is keyboard-reachable).
- Audit's own note: these three may be better done in DataMetric360 than retrofitted — the panel gets replaced anyway.
companion-app / design-system ✅ RESOLVED 2026-08-11
- Not wired together — fixed: an npm workspace in the repo root links
@audi-dash/uiinto the app as a real dependency. - Both packages: no
node_modules, nodist— smoke tests neednpm installfirst (design-system additionallynpm run build;scripts/smoke.mjsimports 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 indesign/README.md).
Found while building (2026-08-11) — all fixed
Three defects that only surfaced by running against a real Home Assistant, not by reading code:
- The data layer declared field names the backend never sends.
Fahrzeugstatushadtank_prozent/sicher_abgestellt/sicherheit; the backend writestankprozent/gesichert/sicherheitscheck. Every screen would have readundefinedwithout anything failing.technik/ausstattungwere typed as objects but are arrays. - The profile adapter handed out live references into the raw profile. Editing a form would have silently mutated the baseline and broken the promise never to overwrite the backend-maintained tire odometer. Sections are copied now.
design-systemstill carried the pre-audit--fg3: #657081(3.0:1 on--tile, fails WCAG AA) that the panel had already fixed to#8a94a3. The new app would have inherited a already-solved contrast defect.
Also: two TypeScript parameter properties in the data layer broke Node's strip-only mode, which is what the smoke scripts run on — rewritten as plain fields.
Three more that only screenshots revealed — nothing failed, the pixels were simply wrong:
- The vehicle block on the home screen was invisible.
overflow: hiddensets a flex item's automatic minimum size to 0, so once the page was taller than the screen, flex-shrink squashed the block to zero height and took model name, badge and plate with it. Fixed with.dm-inhalt > * { flex: none }. - The model name appeared twice — once as the badge image, once as text beside it.
- Filenames overflowed the gallery thumbnails.
And one in the backend, found by watching the log against a current Home Assistant: the odometer
screening called urlopen directly, which HA aborts as a blocking call since 2026.8. Every trip
stayed without a distance, visible only as a warning. Now runs through task.executor.
CORS is a real constraint for this app. cors_allowed_origins did not take effect on HA
2026.8 (preflight 403 even same-origin). Two consequences, both handled: the web build is served
from Home Assistant itself (/local/dm360/, same origin — the planned deployment anyway), and the
native hull enables CapacitorHttp, which routes fetch through native HTTP where CORS does not
apply. Verify this again at commissioning if the app ever moves to a separate hostname.
A self-written QR encoder was discarded. It disagreed with a reference implementation on 1239
of 3249 modules — the code would have been unreadable. homeassistant/www/dm360-qr.html now uses a
vendored MIT library served from Home Assistant itself, which satisfies the actual requirement
(no network call, token never leaves the local network) and round-trips correctly.
Pending: this branch has diverged from main (noted 2026-08-13)
ha_install.md (root) plans the move from pyscript to a native HA integration with a config
flow — UI-only setup, no YAML, and updates via a self-reporting UpdateEntity against the Gitea
repo (HACS is GitHub-only, so it is not an option). All APIs in it were verified against the
running 2026.8.1 instance. It also records which review findings that move eliminates by design.
A full review of main's 18 new commits is in REVIEW_main_2026-08-13.md — 15 findings, the
three most serious in the new setup menu (saving with an unloaded catalogue wipes the whole
mapping; "reset" has no effect on 15 of 17 fields; all four list positions get the same sensor,
which makes "securely parked" report safe while three doors were never checked).
main has moved 18 commits ahead of umsetzung-datametric360 (FMM003 switch, sensor-mapping setup
menu, iOS/large-screen overlay from Claude Design). Deliberate decision: do not merge yet — the
owner keeps working on main first.
Three files conflict (AGENTS.md, homeassistant/INSTALL.md,
homeassistant/pyscript/fahrterkennung.py); four more are touched by both sides but merge cleanly.
One hazard that a clean merge will not catch. This branch changed profil_lesen() in
homeassistant/pyscript/modules/profil.py to return None when the profile file is missing or
corrupt, and guarded all seven call sites that existed here. profil.py is untouched on main, so
it merges silently — but main's call sites (fahrterkennung.py, tankerkennung.py,
reifenzaehler.py, modules/frontend_veroeffentlichung.py) do not guard against None and
would raise AttributeError on a missing profile instead of logging a clear error.
When merging: take main's FMM003 version of fahrterkennung.py, then re-apply the None guard to
every remaining profil.profil_lesen() call site.
Open items
Execution order, exact steps, and acceptance criteria for every item below live in
UMSETZUNGSPLAN.md (phases 1–13). Additional decisions of 2026-08-11: no Electron (Capacitor
wraps the web app for iPhone; a PWA home-screen install is the accepted intermediate step) and
no separate backend (the app talks to the HA REST/WebSocket API directly).
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/uias a real dependency (possibly add a workspace/monorepo root)- Implement the screens from the design draft on top of the existing
DataMetricApilayer - Add Audi assets (fonts/rings/badges) at implementation time from
homeassistant/www/— never intodesign-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_testwith a real token - Offline UX per design brief (offline marker, visible pending queue)
B) Infrastructure / commissioning (partly waits for FMM003 hardware)
- Switch
datametric360.appnameservers 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)
- 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:8883open 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; aflespicustom 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 hostnamedatametric360.duckdns.orgreliably updating; Let's Encrypt for HA's own local UI works (root cause of the earlier DNS-01 failures was a strayaliasesentry 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), notconfiguration.yaml's oldhttp: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 — done 2026-08-12.
fahrterkennung.pyrewritten: trigger is noweinstellungen.ZUENDUNG_SENSOR(binary_sensor.testzone_fmm003_engine_ignition_or_acc_status, on = trip running) instead of the iPhone WLAN sensor; sametask.unique()pause-tolerance pattern kept unchanged.sensor.iphone_wifi_connectionand thefahrzeug.wlan_nameprofile field are fully removed (backend and frontend). At the same time, all entity IDs sourced from the now-abandonedTommiG1/HA_VAG-EU-Data-ActHACS integration were blanked ineinstellungen.py(KM_SENSOR,TANK_SENSOR,RANGE_SENSOR,REFRESH_BUTTON, door/window/lock sensors, oil-change/inspection sensors) — the consuming features (Übersicht tiles, Reifenzähler, automatic Tankerkennung, Fahrtabschluss-Screening) are untouched and degrade gracefully to "unbekannt"/inactive per the existingzustand_oder_none()convention, rather than being deleted;fahrtabschluss.py/tankerkennung.py/reifenzaehler.pynow guard their@state_trigger(f"{einstellungen.KM_SENSOR}")-style decorators withif einstellungen.KM_SENSOR:(untested previously whether pyscript tolerates an empty trigger string — not worth relying on).STANDORT_TRACKERnow points at the realdevice_tracker.testzone_fmm003andBATTERIE_SENSORatsensor.testzone_fmm003_external_power_voltage(the vehicle's 12V bus voltage as measured by the tracker — NOTsensor.testzone_fmm003_battery_voltage, which is the tracker's own internal backup cell and unrelated to the car). Real entity list for this device: the user'sEntiaeten.csvexport (OneDrive, not in this repo). Verified inaudi_ha_test: pyscript reload (pyscript.reload) produced no errors/warnings for any of the changed files; the Übersicht correctly shows "Status unbekannt" and em-dash placeholders for the now-blank VAG fields instead of crashing (that container itself has nodevice_tracker.testzone_fmm003registered, so Standort still shows "Kein GPS-Signal" there — expected, the real HA instance has it).INSTALL.mdstep 4 and its troubleshooting table have since been rewritten (2026-08-12) to point at the Setup menu instead of manualeinstellungen.pyedits, and to use the current FMM003 field names (ZUENDUNG_SENSOR/STANDORT_TRACKER/BATTERIE_SENSOR) instead ofWLAN_SENSOR/TommiG1 — not live-tested against a real install (INSTALL.md itself was never run end-to-end in this project, per its own opening note), but consistent with the actual code.
C) Maintain the existing HA panel (low priority — being replaced)
- Wide-screen/desktop layout (
@container (min-width:860px)inaudi-dashboard-ios.css, turns the same.tabbarinto a 264px side-nav via CSS only, no JS/markup duplication) — done 2026-08-11 from the Claude Design "DataMetric360 Board" draft (claude.ai/designprojectc28a8d4d-ec4e-4178-9e49-ab5b90c02097). Found and fixed a real regression while verifying it live inaudi_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@containerblock, which broke the normal mobile layout too — HA doesn't reliably propagate a real height down throughpanel_custom, soheight:100%collapsedmain#viewto ~0px (tabbar rendered right under the header, content invisible). Fix: kept the outer wrapper viewport-anchored (min(880px, calc(100dvh - 24px)), matchingaudi-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. - Desktop-Sidebar-Markenklon (
.navmarke) nachgezogen — done 2026-08-12. The user compared the deployed app against the live Claude Design "Board" (the two-viewport comparison page, which genuinely iframes this project's ownaudi-dashboard-app.js/-ios.css, not a static mockup) and asked why the desktop Audi-rings placement differed: Design showed the rings pinned above the sidebar nav list, ours showed them in the content header next to the title. Root cause, confirmed viaDesignSync get_file+ byte-diff:audi-dashboard-app.jswas already fully in sync (identical), butaudi-dashboard-ios.csshad drifted — the Design project had since added a.navmarkesidebar clone of the rings (only visible in the@container (min-width:860px)block;.rings{display:none!important}hides the header copy there so the mark appears once) plus a filled-pill active-tab style (.tabpille), neither pulled back locally. Diffed the two full files first to confirm every change was additive/CSS-only, then replaced the local file wholesale. Verified inaudi_ha_testat both 402px and 1400px: rings now sit above the nav list on desktop, gear icon alone in the content header, mobile view unaffected, no console errors beyond the pre-existing missing-vehicle-photo 404 gap. - 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_TRACKERentity-ID setting ineinstellungen.py(device_ tracker with lat/lon attributes), published via a new_standort()helper infrontend_veroeffentlichung.py. The built-in default points at the author's test device (device_tracker.testzone_fmm003); until the FMM003 actually delivers positions (section B), that entity does not exist and the tile shows "Kein GPS-Signal vom Fahrzeug". Verified end-to-end inaudi_ha_testwith a manually createddevice_tracker.test_fahrzeugtest 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/.tilestyling); Claude Design's job is polish, not structure. One known rough edge: reverse-geocoded address didn't resolve within a few seconds in theaudi_ha_testcontainer (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. - Setup menu (Einstellungen → Fahrzeug einrichten → last item when expanded) — done
2026-08-12. Lets the user map every sensor role the app uses (~18 roles, 3 of them 4-item
door/window/lock lists) to real HA entities from a searchable dropdown, instead of hand-
editing
einstellungen.py. New backend modulepyscript/modules/entitaeten.py: aFELDERcatalog (label, hint, expected domain/device_class/unit, keywords, whether it's one of the trigger-bound fields) plus JSON override I/O (data/entitaeten.json, same atomic-write pattern asprofil.py) andoverrides_anwenden(), which doessetattr(einstellungen, key, value)on the already-importedeinstellungenmodule — every existing consumer doesimport einstellungen+ live attribute access (verified: none usefrom einstellungen import X), so this needs no changes to any consumer file. New serviceaudi_dashboard_entitaeten_schreiben(frontend_api.py) and publicationentitaeten_veroeffentlichen()(frontend_veroeffentlichung.py) follow the project's existing read-via-state/write-via-service pattern. Frontend: new popup (vSetupPopup(), modeled on the existing.sheetpattern) with a from-scratch searchable-combobox component (none existed in the repo before) — client-side only, built directly fromHASS.states, no new backend read service; a global "Nur passende Sensoren anzeigen" switch hard-filters candidates by domain/unit, a keyword+domain+unit score ranks and auto-suggests for empty fields. Deliberately does NOT auto-apply changes for the three trigger-bound fields (ZUENDUNG_SENSOR/KM_SENSOR/TANK_SENSOR—@state_triggerbakes the entity ID in at module-load time) — the UI shows an inline "wirkt erst nach Neustart" hint instead of attempting a risky self-pyscript.reload()from inside a running service call. Two real bugs found and fixed during live verification, both now also pushed to the Claude Design project: (1) a full popup re-render on every keystroke fought the search input for focus — fixed by patching just the candidate-list DOM node oninput, matching the same fix the existingdata-tankpreisfeldhandler already uses for the identical reason; (2) the popup background used--tile-2, which Design had since changed to a translucentrgba(255,255,255,.1)in the night theme (for the frosted-glass tile look elsewhere) — made the whole popup partially see-through against the app content behind it. Fixed by switching to--tile-deckend(Design's own opaque-surface token, already used by.standortmenufor the same reason), withvar(--canvas)as a defensive fallback. - Fresh-install packaging +
update.ps1fix — done 2026-08-12. While preparing a short quick-install checklist, found thatupdate.ps1never copiedaudi-dashboard-ios.cssorwww/badges/*— both are loaded by the running app (the ios overlay is injected by the app itself via JS, badges by the model picker since item #51), so everyupdate.ps1run since those were added silently left the deployed copy stale on those two. Fixed (script now copies both). Also removed two stray*.bak-before-audit-mergefiles fromwww/(already gitignored at repo root, never committed, just local clutter from an earlier merge). Newinstallationspaket/— a generated, gitignored bundle (pyscript/, www/ minus the two backup files, data/ with only the example profile + receipt parser,configuration_snippet.yaml,update.ps1) plus a shortANLEITUNG.mdchecklist for copying to a fresh HA instance;INSTALL.mdstays the authoritative, detailed reference. Regenerate on demand, don't keep it permanently in sync — it's a deployment snapshot, not a second source of truth. - Setup-menu polish: 5 additions on top of the existing Setup popup — done 2026-08-12.
Backend (
pyscript/modules/entitaeten.py): a_STANDARDWERTEsnapshot taken at module-load time (beforeoverrides_anwenden()ever runs), exposed viaaktueller_stand()asstandardwerte— needed becausesetattr()on the liveeinstellungenmodule is permanent for the process, so "reset to default" has to write the original value back explicitly rather than just omitting the override. New serviceaudi_dashboard_neustart(frontend_api.py) wrappinghomeassistant.restart(), called only from an explicit user-confirmed button, never automatically. Frontend (audi-dashboard-app.js): (1) live current-value preview next to each candidate in the search dropdown (entitaetZeilenMarkup, readsHASS.states[e.id].state); (2) inline warning under a field if its currently assigned entity isunavailable/unknown/ missing (entitaetStatusWarnung); (3) duplicate-assignment check on save (setupDuplikate) — blocks with a confirm-sheet ("trotzdem speichern?") rather than silently allowing the same entity in two roles; (4) per-field reset button (only shown when the current value differs fromstandardwerte), reverts to the built-in default; (5) after saving, if any of the 3 trigger-bound fields actually changed (setupGeaenderteTriggerFelder, compares against a snapshot taken when the popup opened), a confirm-sheet offers "Jetzt neu starten" calling the new service. Verified live inaudi_ha_test: value preview and unavailable-warning confirmed correct (ZUENDUNG_SENSOR's configured entity genuinely doesn't exist in this test container → "Entität nicht gefunden." shown as designed); duplicate-check correctly caught both a deliberately-forced KM/TANK collision and pre-existing door/window/lock auto-suggestion collisions (a real side effect of this container's limited matching entities, not a bug); reset button appeared/disappeared correctly as values diverged from/matched the default; save round-tripped through the real service intodata/entitaeten.json(byte-inspected) and into the publishedpyscript.audi_dashboard_fahrzeugstatusstate; restart-needed sheet named exactly the changed trigger-bound field(s) and no others. Did not actually trigger a restart during verification (would have restarted the shared test container) — the service call itself was left unexercised beyond confirming it registers without error at pyscript load time. - Fix remaining documentation drift (statistics claim, README gaps, obsolete TODO comment) —
text-only changes; INSTALL.md's WLAN/TommiG1 drift and stale variable names were fixed
2026-08-12 (see section B);
DESIGN_REVIEW_2026-08-13.mdandREVIEW_main_2026-08-13.mdlist further drift not yet fixed (WLAN references still inSPECIFICATION.md,homeassistant/README.md, the profile template) - Harden
profil_lesen()against missing/corruptfahrzeugprofil.json— done on theumsetzung-datametric360branch (2026-08-11), brought over by the 2026-08-13 merge. ReturnsNoneon a missing or malformed file instead of raising; all 5 call sites (backup.py×2,fahrterkennung.py,frontend_veroeffentlichung.py,reifenzaehler.py,tankerkennung.py) already guard forNone— verified by grep across the merged tree, no caller left unguarded (this was flagged as the merge's main risk inREVIEW_main_2026-08-13.md§"Was den Branch betrifft"). - Decide whether the 3 audit leftovers get fixed here or only in DataMetric360 — decided
2026-08-11 (
UMSETZUNGSPLAN.mdPhase 2): swipe-delete keyboard fallback, popup keyboard access, and self-hosting Leaflet stay unfixed in this panel and are addressed 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/, setsteuer.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-DEformat; 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.