Bei all-inkl angelegt, ohne Webseite und ohne Postfach darauf. Geplante Adresse der App: https://datametric360.app Offen bleibt der eigentliche Schritt: die Nameserver muessen bei all-inkl auf Cloudflare umgestellt werden, sonst kann der Tunnel darunter keinen Hostnamen veroeffentlichen. Die Alternative, die DNS bei all-inkl zu belassen, gibt es nur im Business-Tarif fuer 200 USD/Monat. Da auf der Domain nichts liegt, ist die Umstellung folgenlos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
24 KiB
DataMetric360 — Companion App Architecture (design decided, not yet built)
App name: DataMetric360 (decided 2026-08-10). Public hostname: a subdomain of
datametric360.app,
a dedicated domain registered at all-inkl solely for this purpose (see §5.4 for why it must be a
separate domain). Both the app name and the domain are deliberately neutral — they don't advertise
"Audi", "car", or "GPS tracker" to anyone who sees the hostname or the app icon, which is a small but
free privacy win for a hostname that will be publicly resolvable.
Status: architecture decisions locked in during design discussion on 2026-08-10. No code for this
exists yet — this is a separate, not-yet-started project sitting alongside the finished HA panel
described in SPECIFICATION.md. Implementation is explicitly deferred until the FMM003 hardware is
installed and wired (owner's own framing: "Inbetriebnahme findet separat statt, wenn die HW vorrätig
ist"). This file exists so the decisions already made aren't lost before that point.
Relationship to the rest of the repo:
homeassistant/— the finished, in-use HA panel (SPECIFICATION.mdis authoritative for it). Its data source today is the HACS integrationTommiG1/HA_VAG-EU-Data-Actplus the iPhone Companion App's WLAN sensor for trip detection.design-system/— the React component library (no Audi brand assets) built to feed Claude Design. Becomes directly useful here: it's the intended shared UI foundation for the app described below.- This file — describes a new, third piece: a companion app that will eventually supersede the HA panel's role, once built.
1. Concept
One responsive web app, built with the design-system/ component library, shipped three ways:
- Native iOS app — via Capacitor.
- Native Android app — via Capacitor.
- Embedded in Home Assistant — as a plain
panel_iframe(notpanel_custom/hassinjection), so the same build just gets more screen space and reflows via CSS breakpoints instead of floating a fixed phone-card layout in the middle of a bigger screen.
Once this exists, it supersedes homeassistant/www/audi-dashboard-app.js — the current
panel_custom implementation is not meant to be maintained forever in parallel; it gets replaced, not
duplicated. That's an explicit, confirmed decision, not an assumption. audi-dashboard-app.js gets
archived, not deleted, once superseded — same safety-net pattern already used in this project
(.bak-before-audit-merge files).
Distribution: sideload only, no App Store / Play Store. Confirmed decision. This matters beyond
convenience: the Audi Type font, the four-rings SVG, and the model-badge SVGs are licensed only for
this one private, non-published installation (SPECIFICATION.md §2, bauauftrag.md §8/§12) — exactly
why design-system/ strips all of that out, since it gets uploaded to a third party (Claude Design).
A sideloaded app installed directly onto the owner's own two phones, never published to a store, stays
inside that same private-installation scope — so the companion app can use the real Audi assets,
matching the "same look and feel" decision already made, no generic-fallback treatment needed.
Frontend head start via design-system/ + Claude Design: the component-library sync gave Claude
Design the real building blocks (tokens, ~19 components) to design with — it has not yet designed
this app's actual screens (Home, Trips, Fuel, Settings, reworked around the REST/WebSocket data model
instead of hass). That's a future step, following the same pattern as the original Design-Audit task
this session started with: design in Claude Design using the real components, then implement the result
into actual Capacitor/React code.
The iPhone stops being a data source entirely — it becomes purely a display device for this app, same as Android. All vehicle data comes from the FMM003 (see §2), not from the iPhone Companion App.
2. Data source: FMM003 replaces the iPhone + VAG integration
Confirmed, explicit decision (retiring two existing data sources):
- The iPhone Companion App's WLAN sensor (
sensor.iphone_wifi_connection), used today for trip start/end detection, is fully retired. No fallback to it. - The HACS integration
TommiG1/HA_VAG-EU-Data-Actis fully retired as a data source. - All vehicle data comes from the FMM003 (fully wired to the vehicle's CAN/FMS bus), via Codec8, into Home Assistant as one combined, flexibly-mapped entity (no hardcoded field list — see the no-hardcoding mapping system discussed separately, not yet written to a file).
ÜBERHOLT durch eine neue Erkenntnis (2026-08-11): Der FMM003 kann Codec JSON. Damit entfällt Traccar ersatzlos — siehe den Abschnitt „§2b Codec JSON + MQTT" direkt unterhalb. Der folgende Traccar-Abschnitt bleibt als Entscheidungsprotokoll stehen, ist aber nicht mehr der geplante Weg.
Überholte Entscheidung (2026-08-10, nach Recherche): Traccar, minimale Variante.
Research question: is there something more efficient than Traccar for decoding Codec8 from an FMM003? Checked and ruled out:
- HA's official "Teltonika" integration — for Teltonika routers (RUT series: signal/modem/network monitoring), unrelated to GPS/AVL trackers. Not applicable.
- A Teltonika-RMS-based HA integration exists (
derliebemarcus/teltonika_rms) but connects through Teltonika's own cloud (RMS) — same objection as the earlier Flespi rejection: vehicle/GPS data would transit an external vendor cloud, which is exactly what this whole redesign was meant to avoid. - OpenRemote (an alternative fleet platform some Teltonika docs point to) — a full IoT asset-management platform with a rule engine and dashboards; heavier than Traccar, not lighter.
- Minimal standalone Codec8 parser libraries (e.g.
teltonika-go, small Python GPS-server scripts) — genuinely lighter in principle (no bundled platform/Tomcat), but sacrifice exactly what makes Traccar the safer choice: even Traccar itself, mature and community-maintained for years, still has open issues about firmware-specific Codec8 decoding quirks per device generation. A standalone parser would hit the same class of edge cases with none of that accumulated fixing behind it.
Conclusion: no better alternative found — Traccar stays, but in a genuinely minimal shape:
- Database: H2 (embedded, file-based), not MariaDB. This was a wrong assumption in the first draft
of this document — Traccar's own upstream default database is H2, and the HA Community Add-on
(
hassio-addons/addon-traccar) ships with H2 pre-configured (jdbc:h2:/data/traccar) out of the box. MariaDB is only the add-on docs' recommendation for larger/multi-user production deployments, not a hard requirement. For one vehicle, one device, one private user, H2 is the right call — no MariaDB add-on needed at all, one fewer service to run and back up.- Known trade-off (from community reports): occasional H2 corruption/data loss across Traccar version
upgrades. Mitigation: extend the existing daily
backup.pyroutine to also copy Traccar's.mv.dbfile alongside the other data files — same backup pattern already in place, no new mechanism.
- Known trade-off (from community reports): occasional H2 corruption/data loss across Traccar version
upgrades. Mitigation: extend the existing daily
- Disable geofencing and reports/notifications in Traccar's config — none of that applies to a single always-known vehicle; keeps the running feature surface to just device ingestion + Position Forwarding, which is all this project actually uses.
- Position Forwarding → HA webhook (as already planned) remains the integration path for CAN/FMS
attributes (fuel, RPM, etc.), since HA's own official
traccar_serverintegration is device_tracker/geofence-focused and wouldn't surface those extra AVL fields on its own.
Hardware confirmed sufficient for this: Dell OptiPlex 3000 TC, Pentium N6005, 16 GB DDR4, 256 GB NVMe — comfortably enough for Traccar alone, more so without a MariaDB add-on alongside it.
2b. Codec JSON + MQTT — der tatsächlich geplante Weg (2026-08-11)
Neue Information vom Besitzer: der FMM003 beherrscht „Codec JSON". Das ändert die Architektur grundlegend, weil damit die eine Sache entfällt, die Traccar überhaupt nötig gemacht hat: das Entschlüsseln des binären Codec8-Protokolls. Das Gerät liefert die Daten dann bereits als JSON.
Was recherchiert und bestätigt ist
- Codec JSON ist ein Datenprotokoll, kein Transportweg. Eingestellt wird es im Teltonika Configurator unter System → System Settings → Data Protocol → Codec JSON.
- Der zugehörige Transportweg ist MQTT, nicht ein HTTP-Webhook: GPRS Server Settings → MQTT. Das war die wichtigste Überraschung — die erste Vermutung „JSON heißt HTTP-POST an einen HA-Webhook" ist falsch.
- Ein eigener Broker ist vorgesehen: in den MQTT-Einstellungen werden schlicht IP und Port des Brokers eingetragen; Login und Passwort werden verwendet, wenn der Broker Anmeldung verlangt.
- TLS ist Pflicht, nicht optional: ohne hochgeladene Zertifikate arbeitet MQTT nicht. Zertifikat, privater Schlüssel und Wurzelzertifikat werden im Configurator unter Security hinterlegt.
- Firmware-Abhängigkeit: Codec JSON ist nicht in jeder Firmware enthalten (bei mehreren Modellen erst ab 03.28.00 dokumentiert). Der Besitzer bestätigt, dass es auf diesem Gerät verfügbar ist — vor der Inbetriebnahme trotzdem die Firmware-Version notieren.
- Home Assistant kann MQTT von Haus aus: Mosquitto-Broker als offizielles Add-on, dazu die
MQTT-Integration mit
sensor/device_tracker,json_attributes_topicund Templates, um beliebige JSON-Felder herauszuziehen. Es gibt Community-Berichte, die genau das mit Teltonika-Trackern gemacht haben.
Was damit ersatzlos entfällt
| Bisher geplant | Jetzt |
|---|---|
| Traccar-Add-on | entfällt — es gibt nichts mehr zu dekodieren |
Traccar-Datenbank (H2) inkl. Sicherung der .mv.db |
entfällt — kein eigener Datenspeicher mehr |
| Position Forwarding → HA-Webhook | entfällt — HA hört direkt am Broker mit |
| Offener roher TCP-Port für Codec8 | entfällt — stattdessen MQTT über TLS |
| Eigener Codec8-Decoder als Alternative | erledigt sich |
Übrig bleibt: FMM003 → MQTT/TLS → Mosquitto-Add-on → MQTT-Integration → Entitäten in HA. Ein Baustein statt dreien, und alle Teile davon sind offizielle HA-Add-ons beziehungsweise Kernintegrationen statt Fremdsoftware.
Was dadurch neu zu klären ist
- Erreichbarkeit des Brokers von außen. Das Problem verschwindet nicht, es ändert nur die Form:
Das Fahrzeug muss den Broker im Internet erreichen. Cloudflare Tunnel hilft hier nicht — der
ist auf HTTP zugeschnitten, und der FMM003 kann keinen Tunnel-Client mitbringen. Zwei gangbare
Wege:
- Portfreigabe 8883 direkt auf Mosquitto. Verstößt formal gegen den Grundsatz „nichts freigeben", ist aber etwas völlig anderes als Home Assistant selbst freizugeben: ein Port mit TLS-Zwang und Anmeldung, hinter dem nur ein Nachrichtenbroker sitzt.
- Kleiner Broker auf einem VPS, der per Mosquitto-eigenem Bridge-Modus mit dem Broker zuhause gekoppelt wird (die Verbindung baut das Zuhause nach außen auf, über Tailscale). Dann ist zuhause weiterhin nichts erreichbar. Aufwendiger, dafür ohne jede Freigabe.
- Tatsächliche Nutzlast. Wie die CAN/FMS-Werte im JSON heißen, ist noch unbekannt. Sobald das
Gerät läuft: mit MQTT Explorer oder
mosquitto_subeine echte Nachricht mitschneiden und daraus die Zuordnungstabelle bauen (siehe offener Punkt 2 in §5) — nicht vorher raten. - Zertifikate. Für Mosquitto müssen Server-Zertifikat und die passenden Dateien für das Gerät erzeugt werden. Bei eigenem Broker heißt das in der Regel eine eigene kleine CA.
- Datenvolumen. Teltonika weist selbst darauf hin, dass Codec JSON deutlich mehr Bytes braucht als das binäre Codec8E. Bei einem privat genutzten Fahrzeug ist das vernachlässigbar, sollte man bei einem knappen Mobilfunktarif aber im Blick behalten.
Beides — Broker-Erreichbarkeit und Zertifikate — ist erst bei der Inbetriebnahme zu erledigen und blockiert nichts, was jetzt gebaut wird.
Trip detection (substitutes the old WLAN-based logic)
- Start / end trigger: FMM003 ignition state (CAN), not WLAN connectivity.
- Pause-tolerance behavior carries over unchanged from the existing design — this was iterated
three times during design and is settled:
- Ignition never drops during engine start-stop (confirmed technical fact), so no separate hold-time
is needed to filter start-stop out — the
ignition: 1→0condition already excludes it. - The existing user-configurable pause-tolerance setting (
fahrten_pausenzeit_min, today's default 15 min, editable in Settings) is a real, intentional product feature and must be preserved: if the vehicle restarts within that window after a genuine ignition-off, it's one trip, not two. - Implementation pattern (unchanged from the current
fahrterkennung.pyapproach, just re-triggered off the FMM003 ignition entity instead of the WLAN sensor):task.unique()cancels a pendingtask.sleep()if ignition comes back on within the pause window; the trip's recorded end timestamp/odometer is backdated to the actual ignition-off moment, not the timer-expiry moment.
- Ignition never drops during engine start-stop (confirmed technical fact), so no separate hold-time
is needed to filter start-stop out — the
sensor.iphone_wifi_connectiondrops out ofpyscript/modules/einstellungen.pyentirely once this ships.
3. Frontend data access: HA REST/WebSocket API, not hass injection
The new app is not a panel_custom element, so it has no injected hass object. Instead:
- Auth: a Home Assistant Long-Lived Access Token (LLAT), generated once per install in the HA user profile. Sufficient for a private single-user install — no need for the full OAuth flow the official Companion App uses.
- Reads:
GET /api/states/<entity_id>per entity, or subscribe over HA's WebSocket API for push updates instead of polling. - Writes:
POST /api/services/<domain>/<service>— calling the samepyscript.audi_dashboard_*services the current panel already calls viahass.callService()(e.g.audi_dashboard_profil_schreiben,audi_dashboard_beleg_hochladen). Same backend function, same file written server-side — a receipt upload or a FIN/plate edit from the phone is a real round-trip to the shared dataset, never a local-only change. The phone only caches for offline viewing and holds the token; it is never the source of truth. - This same REST/WebSocket client is what makes the iframe-in-HA case (§1.3) work identically to the
native apps — one data-access layer for all three targets, no
hass-injection special-casing needed for the iframe case.
LLAT provisioning (decided)
Primary: manual paste into a one-time setup screen, saved to secure on-device storage (iOS Keychain / Android Keystore, via a Capacitor secure-storage plugin). Always available, no permissions, no extra infrastructure — and the guaranteed fallback if anything about the QR path misbehaves.
Secondary (preferred by the owner, build if it stays simple): QR code scan. The one real objection
to QR — that generating it would mean pasting a long-lived token into some third-party online QR
generator — is avoidable: generate the QR locally, in a small self-hosted page under HA's
/local/, using an offline JS QR library, rendering entirely client-side with no network call. The
token then never leaves the local network on its way into the QR. On the app side this needs a
Capacitor barcode/camera plugin and the camera permission. Both halves are small and well-trodden;
if the implementation turns out to be more than a modest amount of work, ship manual paste alone —
it's fully sufficient on its own.
A URL/deep-link bootstrap was considered and rejected: it needs a registered custom URL scheme plus some channel to send yourself the link, which exposes the token in transit at least as much as pasting it, for no gain over the two options above.
Offline behavior (decided)
- The app shows the last cached data when it has no route to HA, with a visible "Offline" marker in the header/title area — never a blank screen or silent staleness.
- All edits made while offline are queued (receipt uploads, profile edits, manual trip/fill-up entries, etc.) and flushed once connectivity returns, rather than rejected outright. Given this is a single-user app, conflict resolution can stay simple (last-write-wins) — there's no concurrent second editor to reconcile against.
4. Connectivity: Option B — narrow gateway, chosen over Tailscale-always-on
Decision: Option B. (Option A — making the phone's Tailscale connection always-on instead of triggered by the car's WLAN SSID — was the lower-effort alternative and remains available if this turns out to be worth revisiting, but Option B is what's being built.)
Goal
The app must be able to read and write data from any network the phone is on (cellular, foreign Wi-Fi, home Wi-Fi) — not just when Tailscale happens to be connected — while Home Assistant itself stays exactly as closed/local-only as it is today: no port-forward, no Funnel, HA's own port/UI/login never reachable from the public internet. Only a narrow, purpose-built slice of the API is ever exposed.
Shape (two existing, maintained HA Community Add-ons — not custom code to maintain)
- Cloudflared add-on — runs
cloudflaredas an HA add-on. Opens an outbound-only encrypted tunnel from the HAOS host (the OptiPlex) to Cloudflare's edge; no router port ever opens. Confirmed capable of routing multiple public hostnames to multiple local ports/services — so it points at the reverse-proxy add-on's port below, not at HA's own port 8123. - Nginx Proxy Manager or Traefik add-on — a path-scoped reverse proxy sitting between the tunnel
and HA's internal API, on the Supervisor's internal Docker network. Configured with an allowlist
of only the specific paths the companion app needs (e.g.
/api/states/sensor.audi_*,/api/services/pyscript/audi_dashboard_*— exact path list still TBD, depends on the final entity/ service names once the FMM003 mapping is built). Everything else —/lovelace,/config,/auth, the HA frontend itself — is blocked at this layer, never reaching the tunnel at all. - Auth stays a second, independent layer: every proxied call still requires the same Bearer LLAT HA's REST API already demands. Even a path that slipped through the allowlist would be useless without that token, and any individual LLAT can be revoked from the HA profile if the phone is ever lost.
Why this over Option A
Feels like a normal app to the user — plain HTTPS, no VPN client running on the phone, no "why is my battery draining" VPN-always-on tradeoff. The cost is two add-ons' worth of configuration (tunnel + path allowlist) instead of a single phone setting, and the honest caveat that a narrow, purpose-built surface is now internet-facing (even though HA itself never is) — accepted as worth it for the UX.
Explicitly not chosen
- Tailscale Funnel — ruled out earlier for the raw FMM003→decoder TCP ingress path (port/protocol restrictions), and not adopted for this gateway either, since it would mean quietly reversing the existing "no Funnel" rule; not adopted without that being an explicit, separate decision.
- Direct port-forward — ruled out, contradicts the standing "Home Assistant is never exposed
publicly" principle from
bauauftrag.md§3, which continues to hold for HA itself under this design.
5. Open items (not yet decided)
Deliberately deferred until hardware arrives / the next architecture review — not blocking anything else in this document:
-
Exact reverse-proxy path allowlist — depends on the final pyscript entity/service names once the FMM003 combined-entity mapping is implemented; write these down here once decided.
-
No-hardcoding mapping system for the FMM003 combined entity — discussed in an earlier session, not yet written to a file or finalized in detail. Kann erst nach der ersten echten MQTT-Nachricht fertig werden (§2b): die Feldnamen im Codec-JSON sind noch unbekannt und werden mitgeschnitten, nicht geraten. 2a. Erreichbarkeit des MQTT-Brokers für das Fahrzeug — Portfreigabe 8883 gegen VPS-Broker mit Mosquitto-Bridge, siehe §2b. Betrifft nur den Weg Fahrzeug→Zuhause und ist unabhängig von der Anbindung der App (§4, dort bleibt es bei Cloudflare Tunnel). 2b. TLS-Zertifikate für Mosquitto und das Gerät — bei der Inbetriebnahme zu erzeugen; ohne sie verweigert der FMM003 die MQTT-Verbindung.
-
Reverse-proxy add-on: Nginx Proxy Manager vs. Traefik — both viable (§4), pick deferred to the next audit pass. Doesn't depend on the FMM003 — could be set up and tested against HA's existing API before the hardware arrives, if worth doing ahead of time.
-
Cloudflare Tunnel domain — ✅ ERLEDIGT:
datametric360.appist bei all-inkl registriert (Stand 2026-08-11). Eigens dafür angelegt, ohne Webseite und ohne Postfach darauf — genau so, wie es die unten stehende Einschränkung verlangt. Vorgesehene Adresse der App später:https://datametric360.app..appsteht auf der HSTS-Preload-Liste: Browser erzwingen dort HTTPS bedingungslos. Das passt zum Cloudflare Tunnel (immer HTTPS) und schließt eine versehentliche Klartextverbindung von vornherein aus.Noch offen, und der eigentliche Knackpunkt: die Nameserver der Domain müssen bei all-inkl auf Cloudflare umgestellt werden („Full setup") — erst dann kann der Tunnel einen Hostnamen darunter veröffentlichen. Die Variante, bei der die DNS-Verwaltung bei all-inkl bliebe („partial/CNAME setup"), gibt es nur im Business-Tarif für 200 $/Monat. Weil auf dieser Domain nichts liegt außer diesem Vorhaben, ist die Umstellung folgenlos.
Noch zu entscheiden (Kleinigkeit, erst bei der Einrichtung): ob die App unter der Domain selbst liegt und die abgesicherte Schnittstelle unter einer Unteradresse (etwa
api.datametric360.app), oder umgekehrt. Beides funktioniert; ein getrennter Hostname für die Schnittstelle macht die Pfad-Freigabeliste im Reverse Proxy übersichtlicher.Die Einschränkung, die zur eigenen Domain geführt hat:
Cloudflare Tunnel needs the domain's DNS hosted at Cloudflare — i.e. "Full setup": the domain's nameservers get repointed to Cloudflare, moving all DNS records for that domain (A, MX/email, everything) out of all-inkl's DNS management. The alternative that avoids this — Cloudflare's "partial / CNAME setup", which keeps DNS at the existing provider — is Business-plan-only ($200/month), i.e. not realistic here.
Therefore: never point this at an existing all-inkl domain carrying live websites or email — hence the dedicated
datametric360.app. Nothing about the owner's existing domains/mail is touched.Note the all-inkl "Neue Domain anlegen" dialog asks for a target (Webspace / Redirect / Webbaukasten) — that choice is irrelevant here, since DNS gets delegated to Cloudflare afterwards and the all-inkl webspace binding then goes unused. The meaningful step is the nameserver change to Cloudflare, done after registration, not anything in that dialog.
6. Explicit non-goals for this piece (carried over from bauauftrag.md)
- Home Assistant's own UI/API is never exposed publicly, under either connectivity option.
- No change to the vehicle being read-only (no remote control) — this app remains a viewer/data-entry tool, same as the existing panel.