a84f7c81f4
Tatsaechliche Ursache der deploy_challenge-Fehlschlaege gefunden: ein ueberfluessiger/falscher aliases-Eintrag (alias: datametric360 ohne .duckdns.org) in der DuckDNS-Add-on-Konfiguration, zusammen mit accept_terms: false. Nach Entfernen von aliases und accept_terms: true lief die Zertifikatsanfrage sofort durch. Der DNS-Server war entgegen der vorherigen Vermutung kein Faktor - Erfolg trat auch mit dem Speedport als DNS ein. configuration.yaml's alter http:-Block entfernt, HA migriert SSL-Pfade und interne/externe URL jetzt ueber die Oberflaeche (Einstellungen > System > Netzwerk). Dabei eine HA-Eigenheit dokumentiert: Netzwerkaenderungen muessen innerhalb 5 Minuten per Dialog bestaetigt werden, sonst automatischer Rollback. HA ist jetzt per HTTPS mit gueltigem Let's-Encrypt-Zertifikat erreichbar.
438 lines
31 KiB
Markdown
438 lines
31 KiB
Markdown
# 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.md` is authoritative for it). Its
|
|
data source today is the HACS integration `TommiG1/HA_VAG-EU-Data-Act` plus 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:
|
|
|
|
1. **Native iOS app** — via Capacitor.
|
|
2. **Native Android app** — via Capacitor.
|
|
3. **Embedded in Home Assistant** — as a plain `panel_iframe` (not `panel_custom`/`hass` injection),
|
|
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-Act` is **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.py` routine to also copy Traccar's `.mv.db`
|
|
file alongside the other data files — same backup pattern already in place, no new mechanism.
|
|
- **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_server` integration 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 und bestätigt möglich:** *GPRS Server Settings → MQTT Settings →
|
|
MQTT Client Type* bietet laut der Hilfe im Configurator (Screenshot vom Besitzer, 2026-08-11) vier
|
|
Werte: `0 = AWS IoT Shadow`, `1 = AWS IoT Custom`, `2 = Azure IoT`, **`3 = Custom server`**. Die
|
|
öffentliche Teltonika-Doku (Wiki, PDF-Guides) kennt nur die ersten drei — „Custom server" ist offenbar
|
|
erst mit einer späteren Firmware/Configurator-Version dazugekommen und deshalb dort nicht beschrieben.
|
|
Für „Custom server" werden IP/Domain und Port des eigenen Brokers (unter *Server Settings*, nicht im
|
|
MQTT-Block selbst) eingetragen; Login und Passwort kommen zum Einsatz, wenn der Broker das verlangt.
|
|
- **Praktisch bestätigt, mit einem offenen Warnsignal:** ein Teltonika-Community-Thread
|
|
([FMC003 - Custom MQTT Broker?](https://community.teltonika.lt/t/fmc003-custom-mqtt-broker/14784),
|
|
Juli 2025) zeigt einen Nutzer, der genau unser Setup (eigener Mosquitto auf einem VPS, eigene CA,
|
|
Client-Zertifikat vom Gerät) über den Modus „AWS IoT Custom" nachgebaut hat — 24 Stunden stabil
|
|
getestet, damals gab es „Custom server" noch nicht als eigene Option. Zwei Monate später berichtet
|
|
ein anderer Nutzer **speziell für den FMM003**, dass bei ihm über MQTT keine Pakete am Server
|
|
ankamen — nach Rückwechsel auf TCP lief es wieder. Keine Auflösung im Thread sichtbar.
|
|
- **Konsequenz:** die MQTT-Anbindung gilt als grundsätzlich machbar, aber **nicht als sicher
|
|
funktionierend, bevor sie mit dem echten Gerät getestet wurde** — TCP/Codec8 (der bisherige,
|
|
bekannt funktionierende Weg über Traccar) bleibt der Rückfallplan, falls MQTT am FMM003 in der
|
|
Praxis Probleme macht.
|
|
- **„Custom server" beim Besitzer nicht auswählbar (2026-08-11):** im eigenen Configurator lässt
|
|
sich Wert 3 nicht anklicken. Ein zweiter, unabhängiger Praxisbericht
|
|
([mdworld.nl, FMC003 + eigener OpenRemote-Server](https://mdworld.nl/asset-tracking), Aug. 2025)
|
|
bestätigt den gleichen Umweg wie der Community-Thread: **„AWS IoT Custom" wählen und dort auf den
|
|
eigenen Server zeigen** — funktioniert nachweislich, unabhängig davon ob „Custom server" gerade
|
|
wählbar ist oder nicht. Das ist damit der empfohlene Standardweg, nicht nur ein Fallback.
|
|
- **Zertifikatsketten-Reihenfolge ist umgekehrt zur Konvention (wichtiger Stolperstein, aus
|
|
demselben Praxisbericht):** Teltonika-Geräte erwarten die hochgeladene Zertifikatskette in
|
|
**umgekehrter Reihenfolge** (Wurzelzertifikat zuerst, nicht zuletzt) — anders als das übliche
|
|
`fullchain.pem`-Format (Server-Cert zuerst). Wird das ignoriert, schlägt der TLS-Handshake mit
|
|
einem kryptischen Fehler fehl (`SSL routines::tlsv1 alert unknown ca`), der nicht offensichtlich
|
|
auf die Reihenfolge hindeutet. **Vor dem Hochladen im Security-Tab die Kette umdrehen.**
|
|
- **Topic-Namensschema (aus demselben Praxisbericht, als Orientierung):** z. B.
|
|
`<name>/teltonika/%imei%/data` und `.../commands` — `%imei%` wird vom Gerät automatisch ersetzt.
|
|
Exakte eigene Werte erst bei der Inbetriebnahme festlegen.
|
|
- **TLS ist Pflicht, nicht optional:** ohne hochgeladene Zertifikate arbeitet MQTT nicht. Zertifikat,
|
|
privater Schlüssel und Wurzelzertifikat werden im Configurator unter *Security* hinterlegt.
|
|
- **Dateinamen-Endung ist strikt geprüft (2026-08-11, live am Configurator beobachtet):** hochgeladene
|
|
Dateien müssen auf `.pem`, `.pem.crt` oder `.pem.key` enden, sonst Fehler „File format is not valid
|
|
or not supported". Reiner Inhalt (PEM-Format) reicht nicht, die Endung muss stimmen. Schema folgt
|
|
offenbar der AWS-IoT-Download-Konvention: Root-Zertifikat `*.pem`, Geräte-Zertifikat `*.pem.crt`,
|
|
privater Schlüssel `*.pem.key`. Eigene CA-Dateien entsprechend umbenennen, nicht nur `.crt`/`.key`.
|
|
- **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_topic` und 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
|
|
|
|
1. **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.
|
|
2. **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_sub` eine echte Nachricht mitschneiden und daraus
|
|
die Zuordnungstabelle bauen (siehe offener Punkt 2 in §5) — nicht vorher raten.
|
|
3. **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.
|
|
4. **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→0` condition 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.py` approach, just re-triggered
|
|
off the FMM003 ignition entity instead of the WLAN sensor): `task.unique()` cancels a pending
|
|
`task.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.
|
|
- `sensor.iphone_wifi_connection` drops out of `pyscript/modules/einstellungen.py` entirely 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 **same** `pyscript.audi_dashboard_*`
|
|
services the current panel already calls via `hass.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)
|
|
|
|
1. **Cloudflared add-on** — runs `cloudflared` as 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.
|
|
2. **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.
|
|
3. **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:
|
|
|
|
1. **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.
|
|
2. **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. ✅ **ERLEDIGT (Entscheidung, 2026-08-11): Portfreigabe 8883**, nicht VPS-Bridge. Begründung: ein
|
|
Broker mit TLS-Zwang und Pflicht-Client-Zertifikat lässt vor dem eigentlichen Login niemanden
|
|
auch nur ansetzen — der Mehraufwand einer dauerhaften VPS-Miete plus zweitem Mosquitto plus
|
|
Bridge-Verbindung stand in keinem Verhältnis zum zusätzlichen Schutz für ein einzelnes
|
|
Privatfahrzeug. Betrifft nur den Weg Fahrzeug→Zuhause und ist unabhängig von der Anbindung der
|
|
App (§4, dort bleibt es bei Cloudflare Tunnel — kein Widerspruch, siehe dort die Begründung, warum
|
|
ein eng begrenzter, zertifikatsgesicherter Port etwas anderes ist als HA selbst offenzulegen).
|
|
**Umsetzungsstand (2026-08-11):** DuckDNS-Hostname `datametric360.duckdns.org` angelegt und als
|
|
Add-on in HA eingerichtet, aktualisiert zuverlässig die öffentliche IP — das ist der für den
|
|
FMM003-Pfad tatsächlich entscheidende Teil. Router bestätigt **Dual-Stack** (kein DS-Lite), die
|
|
Portfreigabe 8883 am Speedport Smart 4 Plus ist eingerichtet, FMM003 sendet bestätigt Daten.
|
|
|
|
**Let's-Encrypt-Zertifikat für die HA-Oberfläche selbst — ✅ ERLEDIGT (2026-08-11), nach
|
|
ausführlicher Fehlersuche.** Der `deploy_challenge`-Hook (DNS-01, TXT-Eintrag via DuckDNS-API)
|
|
scheiterte zunächst wiederholt mit `timeout 120s` beim Warten auf den eigenen TXT-Eintrag, obwohl
|
|
DuckDNS den Eintrag nachweislich korrekt setzte (bestätigt per `nslookup` gegen `8.8.8.8` vom PC
|
|
*und* per `dig` aus der HA-eigenen SSH-Konsole gegen den internen Supervisor-Resolver). Der DNS-
|
|
Server selbst war am Ende kein Faktor (Erfolg trat auch mit dem Speedport als DNS-Server ein) —
|
|
**tatsächliche Ursache war ein fehlerhafter `aliases`-Eintrag** in der DuckDNS-Add-on-Konfiguration
|
|
(`alias: datametric360` ohne `.duckdns.org`, überflüssig und falsch, da keine externe Domain per
|
|
CNAME verwendet wird — `aliases` ist nur für diesen Fall gedacht) zusammen mit `accept_terms:
|
|
false`. Nach Entfernen von `aliases` und Setzen von `accept_terms: true` lief die Zertifikatsanfrage
|
|
beim nächsten Versuch sofort erfolgreich durch (`Challenge is valid! ... Creating fullchain.pem...
|
|
Done!`), Zertifikat gültig bis 2026-11-09.
|
|
|
|
Anschließend `configuration.yaml`'s alter `http:`-Block (der HA-Start blockiert hatte, solange die
|
|
Zertifikatsdateien nicht existierten) entfernt — HA migriert SSL-Zertifikatspfad/-Schlüssel und die
|
|
interne/externe URL inzwischen in die Oberfläche (Einstellungen → System → Netzwerk). **Eigenheit
|
|
dabei:** eine geänderte Netzwerkkonfiguration muss innerhalb von 5 Minuten über einen Dialog in der
|
|
Oberfläche bestätigt werden, sonst macht HA sie automatisch rückgängig und startet mit dem vorigen
|
|
Stand neu (`Pending HTTP config was not confirmed within 0:05:00` im Log) — beim ersten Versuch
|
|
deshalb ungewollt zurückgerollt, beim zweiten Versuch bewusst sofort bestätigt, seitdem stabil.
|
|
Externe URL bewusst leer gelassen (Port 8123 ist nicht freigegeben, HA bleibt nicht öffentlich
|
|
erreichbar); interne URL auf `https://192.168.2.216:8123` gesetzt — das Zertifikat ist auf den
|
|
Hostnamen ausgestellt, nicht auf die IP, der Browser zeigt deshalb bei IP-Zugriff weiterhin eine
|
|
Namensabgleich-Warnung (Verbindung bleibt trotzdem verschlüsselt); ein lokaler DNS-Eintrag, der
|
|
`datametric360.duckdns.org` intern auf die lokale IP auflöst, wäre die sauberere, aber optionale
|
|
Nachbesserung.
|
|
|
|
Nebenbei geklärt: ein Vorschlag, stattdessen komplett auf Cloudflare zu setzen, wurde geprüft und
|
|
**ist keine Alternative** — Cloudflare Tunnel kann kein rohes MQTT/TCP transportieren (nur HTTP),
|
|
der FMM003 kennt außerdem kein Tunnel-Konzept, nur IP/Domain+Port. Bleibt bei der klaren Trennung:
|
|
Cloudflare Tunnel für die App (§4), Portfreigabe+DuckDNS nur für die Fahrzeug-MQTT-Verbindung.
|
|
2b. **TLS-Zertifikate für Mosquitto und das Gerät** — ✅ **ERLEDIGT (2026-08-11):** eigene private CA
|
|
(10 Jahre gültig) erzeugt, Server-Zertifikat (CN `datametric360.duckdns.org`) und Client-Zertifikat
|
|
für das Gerät signiert, beide gegen die CA verifiziert. Mosquitto ist mit `certfile`/`keyfile`/
|
|
`cafile`/`require_certificate: true` konfiguriert. **Noch offen:** die drei Geräte-Dateien (Root,
|
|
Client-Zertifikat, privater Schlüssel) müssen noch in den FMM003-Configurator hochgeladen werden —
|
|
dabei auf die Dateinamen-Endung achten (siehe Anmerkung oben bei „Dateinamen-Endung ist strikt
|
|
geprüft").
|
|
3. **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.
|
|
4. **Cloudflare Tunnel domain** — ✅ **ERLEDIGT: `datametric360.app` ist 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`.
|
|
|
|
`.app` steht 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.
|