# Review: Branch `main`, Commits `c66ed82..d5562c7`

**Datum:** 2026-08-13 · **Umfang:** 18 Commits, 24 Dateien, +2752/−259 Zeilen
**Gegenstand:** Setup-Menü zur Sensor-Zuordnung, Umstellung von WLAN/VAG auf FMM003,
Standort-Kachel mit Live-Position, iOS-/Großbildschirm-Optikauflage, DuckDNS und Zertifikate.

Geprüft wurde gegen eine Arbeitskopie von `origin/main`. Die mit **[belegt]** markierten Befunde
sind am Code nachvollzogen oder nachgestellt; **[plausibel]** heißt: aus dem Code abgeleitet, aber
nicht im Browser reproduziert.

---

> ## ✅ Umgesetzt am 2026-08-13
>
> Alle Befunde dieses Dokuments sind behoben — siehe die fünf Commits von
> `Setup-Zuordnung: Zuruecksetzen wirkt…` bis `Kleinere Befunde und Dokumentation…`.
> Das Dokument bleibt als Befundprotokoll stehen: es begründet, warum die
> Änderungen so aussehen, wie sie aussehen.
>
> **Zwei Punkte wurden bewusst anders gelöst als hier vorgeschlagen:**
>
> 1. *Karte nur bei echtem Ansichtswechsel neu aufbauen* — nicht umgesetzt. `render()` ersetzt den
>    Inhalt per `innerHTML`; eine überlebende Leaflet-Instanz zeigte danach auf ein abgehängtes
>    Element und bliebe leer. Der Neuaufbau hängt am 20-Sekunden-Takt des Backends und ließe sich
>    nur durch einen Umbau der Render-Architektur ändern. Die Begründung steht im Code.
>    Behoben ist dagegen die Standortabfrage, die tatsächlich ungedrosselt lief.
> 2. *`SPECIFICATION.md`* wurde nicht in Teilen umgeschrieben, sondern hat einen datierten
>    Statushinweis am Anfang bekommen. Ein Umschreiben der §4/§5-Tabellen hätte neue
>    Ungenauigkeiten riskiert.
>
> **Drei Fehler kamen beim Umsetzen dazu**, sichtbar erst im laufenden Panel: ein unlesbares Datum
> ergab „NaN/N" statt eines Strichs, ein fehlender Tanksensor „0 %" statt „unbekannt", und neben dem
> Typenschild stand die Ausführung doppelt. Alle drei sind mit behoben.
>
> Und ein pyscript-Fallstrick ist dabei aufgefallen und im Code vermerkt: **Generatorausdrücke sind
> nicht implementiert** (`not implemented ast ast_generatorexp`), Mengen-, Listen- und
> Dict-Comprehensions dagegen schon.

---

## Gesamteindruck

Handwerklich stark. Das Setup-Menü ersetzt „vor der Installation `einstellungen.py` editieren"
durch eine Oberfläche mit Live-Wertvorschau, Unavailable-Warnung, Duplikat-Prüfung und einem
ehrlichen Neustart-Hinweis für die drei triggergebundenen Felder. Der Kniff dahinter — `setattr`
auf dem laufenden Modul-Objekt, weil alle Leser `einstellungen.KM_SENSOR` als lebenden
Attributzugriff machen — ist elegant und im Kopfkommentar sauber begründet.

Positiv außerdem: Escaping ist im neuen Code durchgängig (kein XSS gefunden), Karten-Ebenen werden
vor dem Neuaufbau entfernt, die Haversine-Distanz ist mathematisch korrekt, Listenfelder sind gegen
zu kurze Arrays abgesichert, und die Python-Syntax aller acht geänderten pyscript-Dateien ist in
Ordnung.

Die Fehler unten liegen fast alle im selben Bereich — dem neuen Setup-Menü — und **drei davon
werden erst nach einem Neustart sichtbar**, also mit maximalem zeitlichem Abstand zur Ursache.

## Übersicht

| # | Befund | Wirkung | Ort |
|---|---|---|---|
| 1 | Speichern bei fehlendem Katalog löscht alle Zuordnungen | Datenverlust | `audi-dashboard-app.js:1773, 2031` |
| 2 | „Zurücksetzen" wirkt bei 15 von 17 Feldern nicht | falscher Zustand | `entitaeten.py:188` |
| 3 | Alle vier Listenpositionen bekommen denselben Sensor | falsche Sicherheitsaussage | `audi-dashboard-app.js:1976` |
| 4 | Vorschlagsschwelle akzeptiert beliebige Sensoren | falscher Fahrt-Trigger | `audi-dashboard-app.js:1980` |
| 5 | Geleerte Listen setzen `["","","",""]` | Status dauerhaft „unbekannt" | `entitaeten.py:188` |
| 6 | Zuordnung wird nirgends gesichert | Verlust bei Wiederherstellung | `backup.py:32` |
| 7 | Kaputte `entitaeten.json` legt das Panel lahm | Totalausfall | `entitaeten.py:159` |
| 8 | Standortabfrage im Dauerfeuer bei abgelehnter Freigabe | Akku, Netz | `audi-dashboard-app.js:553/557` |
| 9 | Kein Tastaturweg in die Einstellungen unter 860 px | Bedienbarkeit | `audi-dashboard-app.js:3849` |
| 10 | `.navmarke` nur in der iOS-Auflage versteckt | Tab-Leiste zerfällt | `audi-dashboard-ios.css:194` |

---

# Schwerwiegend

## 1. Speichern bei fehlendem Katalog löscht die gesamte Zuordnung [belegt]

**Kette:**

1. `ENTITAETEN` wird an genau einer Stelle gefüllt (`audi-dashboard-app.js:3723`), aus `hass.states`.
2. Der Nachlade-Pfad nach einem HA-Neustart (`:3699-3703`) holt Profil, Fahrten, Tankvorgänge,
   Status und Batterieverlauf nach — **nicht** aber `pyscript.audi_dashboard_entitaeten`.
3. `DATEN_GELADEN` hängt allein am Profil. Die App gilt also als geladen, während der Katalog fehlt.
4. Der Knopf „Setup — Sensoren zuordnen" (`:1773`) ist nur an `einrichtenOffen` gekoppelt, nicht an
   den Katalog. `setupKatalog()` liefert `[]`, das Popup öffnet **ohne eine Zeile und ohne
   Fehlermeldung**, `setupZuordnung` bleibt `{}`.
5. „Speichern" schickt `"{}"` (`:2031`). `overrides_schreiben()` ersetzt die Datei vollständig,
   ohne Zusammenführen (`entitaeten.py:166-173`).

**Auswirkung:** `data/entitaeten.json` enthält danach `{}`. Weil die `setattr`-Werte im laufenden
Prozess bestehen bleiben, fällt der Verlust **erst beim nächsten Neustart** auf. Zusammen mit
Befund 6 (keine Sicherung) gibt es dann nichts zurückzuholen.

**Vorschlag:** Setup-Knopf sperren, solange `ENTITAETEN` null ist, und in
`setupSpeichernAusfuehren()` abbrechen, wenn `setupZuordnung` leer ist. Zusätzlich die Entität in
den Nachlade-Pfad aufnehmen.

## 2. „Zurücksetzen" wirkt bei 15 von 17 Feldern nicht [belegt, nachgestellt]

`entitaeten.py:188` überspringt leere Werte:

```python
if wert in (None, "", []):
    continue
```

Der Kopfkommentar derselben Datei beschreibt das Problem exakt richtig — `setattr` verändert das
Modul dauerhaft, „Zurücksetzen" muss den Standardwert *aktiv* zurückschreiben. Das Frontend tut das
auch (`setupStandardwert`). Nur ist der Standardwert bei 15 der 17 Katalogfelder `""` oder `[]` —
und genau die werden übersprungen.

Nachgestellt:

```
nach Zuordnung:     KM_SENSOR = 'sensor.alt_vag_mileage'
nach Zurücksetzen:  KM_SENSOR = 'sensor.alt_vag_mileage'   ← erwartet: ''
```

**Auswirkung:** Für den Nutzer sieht es aus, als hätte das Speichern versagt — `aktueller_stand()`
liest aus dem laufenden Modul, das Feld zeigt beim nächsten Zeichnen wieder den alten Wert.
Funktioniert nur bei `ZUENDUNG_SENSOR`, `BATTERIE_SENSOR` und `STANDORT_TRACKER`, den drei Feldern
mit nicht-leerem Standard.

## 3. Alle vier Listenpositionen bekommen denselben Sensor [belegt]

`audi-dashboard-app.js:1976-1981`:

```js
zuordnung[feld.key] = feld.positionen.map((_, i) => {
  const vorhanden = (werte[feld.key] || [])[i];
  if (vorhanden) return vorhanden;
  const vorschlag = entitaetKandidaten(feld.key, "", "")[0];   // i geht nicht ein
  return vorschlag && entitaetScore(feld, vorschlag, "") >= 3 ? vorschlag.id : "";
});
```

`entitaetKandidaten()` hängt nicht vom Index ab und schließt bereits vergebene IDs nicht aus —
alle vier Positionen bekommen dieselbe Entität. Betrifft `TUER_SENSOREN`, `FENSTER_SENSOREN`,
`TUERSCHLOSS_SENSOREN`.

**Auswirkung:** Bei einer Frischinstallation (genau der Fall, für den das Menü gebaut wurde) prüft
`_sicherheitscheck()` danach viermal dieselbe Tür. **„Sicher abgestellt" meldet gesichert, obwohl
drei Türen nie geprüft wurden.** Die Duplikat-Warnung fängt das beim Speichern ab, ist aber mit
„Trotzdem speichern" wegklickbar.

## 4. Die Vorschlagsschwelle akzeptiert beliebige Sensoren [belegt]

`entitaetScore()` (`:1936`) vergibt: Stichwort +5, Domain +3, device_class +3, Einheit +2. Die
Schwelle ist `>= 3` — **ein Domain-Treffer allein genügt**, ohne dass ein einziges Stichwort passt.
Verschärfend: `setupNurPassend` wird zwei Zeilen vorher auf `true` gesetzt (`:1971`), wodurch
`entitaetKandidaten()` ohnehin hart auf die Domain filtert. Die Schwelle ist damit *immer* erfüllt.
Die Reihenfolge kommt aus `Object.keys(HASS.states)`, ist also faktisch willkürlich.

**Auswirkung:** Ohne passenden Zündungssensor wird `ZUENDUNG_SENSOR` stumm mit dem erstbesten
`binary_sensor.*` vorbelegt — Bewegungsmelder, Fensterkontakt, was zuerst kommt. Das Feld sieht
ausgefüllt aus, wird mitgespeichert, und **die gesamte Fahrterkennung hängt an einem
Zufallssensor**. Bei Einzelfeldern greift keine Duplikat-Warnung. Dasselbe für `TANK_SENSOR` (jeder
%-Sensor, etwa Luftfeuchte) und `REFRESH_BUTTON` (jedes `button.*`).

**Vorschlag:** Schwelle auf `>= 5` — also mindestens ein Stichworttreffer.

## 5. Geleerte Listenfelder setzen `["","","",""]` [belegt]

Die Skip-Regel aus Befund 2 prüft auf `[]`. Ein Array aus vier leeren Zeichenketten ist das nicht:

```python
["","","",""] in (None, "", [])   # False -> wird per setattr gesetzt
```

`_sicherheitscheck()` zippt dann über vier leere Entity-IDs und erzeugt vier „unbekannt"-Zeilen —
mal drei Listen sind das zwölf.

**Auswirkung:** Weil die Gesamtaussage bewusst `None` wird, sobald *eine* Prüfung unbekannt ist,
steht die Übersicht danach **dauerhaft auf „Zustand unbekannt"** statt auf grün.

Kurios ist die Kombination mit Befund 2: Bei Einzelfeldern wirkt „Zurücksetzen" gar nicht, bei
Listenfeldern zu stark. Beides verschwindet mit demselben Fix — statt leere Werte zu überspringen,
immer den Standardwert aus `_STANDARDWERTE` zurückschreiben:

```python
def overrides_anwenden():
    overrides = overrides_lesen()
    for key in _SCHLUESSEL:
        wert = overrides.get(key)
        leer = wert in (None, "", []) or (isinstance(wert, list) and not any(wert))
        setattr(einstellungen, key, _STANDARDWERTE[key] if leer else wert)
```

## 6. Die Sensor-Zuordnung wird nirgends gesichert [belegt]

`backup.py:32` sichert `fahrzeugprofil.json`, `fahrten.jsonl`, `tankvorgaenge.jsonl` —
`entitaeten.json` fehlt. Auch `audi_dashboard_backup_wiederherstellen` kennt es nicht, `update.ps1`
fasst `data/` bewusst nicht an, und in `homeassistant/.gitignore` steht es nicht neben den anderen
Laufzeitdateien.

**Auswirkung:** Nach einer Wiederherstellung ist die komplette Sensor-Zuordnung still weg. In
Verbindung mit Befund 1 gibt es keinen Rückweg.

## 7. Eine beschädigte `entitaeten.json` legt das Panel lahm [belegt]

`overrides_lesen()` (`entitaeten.py:159`) ruft `json.loads` ohne Absicherung. Der Aufruf steht in
`beim_start()` **vor** `alles_veroeffentlichen()` — wirft er, wird gar nichts veröffentlicht, das
Panel bleibt komplett leer.

Geschrieben wird zwar atomar über `.tmp` + `os.replace`, das Risiko ist also gering — der Ausfall
wäre aber total. Dieselbe Fehlerklasse, die im Branch `umsetzung-datametric360` bei `profil_lesen()`
bereits abgefangen wurde.

---

# Mittel

## 8. Standortabfrage im Dauerfeuer, Karte bei jedem Render neu [belegt]

`USER_POS_TS` wird nur im Erfolgs-Callback gesetzt (`:553`), nicht im Fehlerfall (`:557`). Die
30-Sekunden-Drossel in `standortErfassenFallsNoetig()` (`:560`) greift damit nie, wenn der Nutzer
die Standortfreigabe ablehnt oder kein Fix zustande kommt.

Parallel dazu zerstört `render()` die Vorschaukarte und baut sie neu (`:2879-2880`), sobald die
Route `home` ist — bei **jedem** Backend-Update.

**Auswirkung:** Mit einem GPS-Tracker als Quelle (genau der FMM003-Fall) sind das dutzende
`getCurrentPosition({enableHighAccuracy:true})`-Anfragen pro Stunde plus sichtbares Kachelflackern.

**Vorschlag:** `USER_POS_TS = Date.now()` auch im Fehlerpfad; Karte nur bei echtem Ansichtswechsel
neu bauen — die Variable `gleicheAnsicht` gibt es in `render()` bereits.

## 9. Kein Tastaturweg in die Einstellungen unter 860 px [belegt]

`audi-dashboard-ios.css:81` versteckt `.profilbtn` — den echten `<button aria-label="Einstellungen">` —
und holt ihn erst in der Container-Abfrage ab 860 px zurück (`:230`). Der Ersatz ist ein
Klick-Handler auf `#marke` (`:2965`), und das ist ein `<div class="rings" aria-label="Audi">`
(`:3849`): ohne `role`, ohne `tabindex`.

**Auswirkung:** In Telefonbreite gibt es für Tastatur- und Screenreader-Nutzer **keinen** Zugang zu
den Einstellungen. Ein `<div>` mit `aria-label` ohne Rolle wird von den meisten Screenreadern nicht
einmal angesagt. Gleiche Fehlerklasse wie das Swipe-Löschen aus `AUDIT_2026-08-10.md`.

**Vorschlag:** `#marke` zu einem `<button aria-label="Einstellungen">` machen; dann kann auch
`pointer-events:auto` in `audi-dashboard-ios.css:78` entfallen.

## 10. `.navmarke` nur in der iOS-Auflage versteckt [belegt]

`audi-dashboard-app.js:2956-2963` hängt den Ringklon **unbedingt** in `#tabbar`.
`.navmarke{display:none}` steht ausschließlich in `audi-dashboard-ios.css:194`. `.tabbar` ist ein
`display:grid` mit `repeat(5, 1fr)` (`audi-dashboard.css:211`).

**Auswirkung:** Lädt `/local/audi-dashboard-ios.css` nicht (404, Cache), wird der Klon ein sechstes
Grid-Element — alle Tabs rutschen eine Spalte weiter, „Tanken" fällt in eine zweite Zeile, die
Menüleiste zerfällt. Kein theoretischer Fall: Commit `3548f77` dokumentiert genau dieses
Deployment-Loch.

**Vorschlag:** `.navmarke{display:none}` gehört in die Basis-CSS, die Sichtbarkeit in die Auflage.

## 11. Standort-Menü ohne `pointercancel` [belegt]

`standortMenuVerdrahten()` (`:706-731`) setzt `smY0` nur in `pointerup` zurück. Alle drei anderen
Zeigergesten der Datei haben einen `pointercancel`-Handler (`:2929`, `:3055`, `:3122`), diese nicht.

**Auswirkung:** Übernimmt das System die Berührung (iOS-Randgeste, Multitouch, eingehender Anruf),
bleibt `smY0` gesetzt. Der `pointermove`-Handler ruft dann `preventDefault()` für jede weitere
Bewegung — **Scrollen ist in der ganzen App blockiert**, bis irgendwo ein `pointerup` kommt, der
dann ein `dy` aus einem veralteten Startpunkt auswertet.

## 12. Fehlgeschlagenes Reverse-Geocoding wird dauerhaft gecacht [belegt]

`:572-586` setzt `STANDORT_ADRESSE_KEY` auch im `catch`-Zweig.

**Auswirkung:** Ein einziger fehlgeschlagener Nominatim-Abruf (Rate-Limit, kurzer Netzausfall) für
eine Zelle führt dazu, dass für dieselbe Zelle die **gesamte Sitzung** lang nur Koordinaten
angezeigt werden, auch wenn das Netz längst wieder da ist.

**Vorschlag:** Key nur im Erfolgsfall setzen.

## 13. Setup-Popup hält den `role="dialog"`-Vertrag nicht ein [belegt]

`:2774` deklariert `role="dialog" aria-modal="true"`. Es fehlen: Escape zum Schließen (in der Datei
gibt es keinen `keydown`-Handler), eine Fokusfalle (Tab wandert in den überdeckten Hintergrund),
und der Schalter „Nur passende Sensoren anzeigen" (`:2779`) hat keinen zugänglichen Namen — der
Beschriftungstext steht im Geschwister-`<span>` außerhalb des `<label>`.

Dazu: Die Entitäts-Auswahl öffnet ausschließlich im `click`-Handler (`:3181`). Wer per Tab ins Feld
springt und tippt, löst zwar den `input`-Handler aus, der bricht aber ab, weil `setupSucheOffen`
noch `null` ist — sichtbar passiert nichts. `role="combobox"`, `aria-expanded` und `aria-controls`
fehlen ebenfalls.

## 14. Standort-Menü nicht per Tastatur bedienbar [belegt]

Im eingeklappten Zustand steht das Blatt per `transform` überwiegend außerhalb des Sichtbereichs,
bleibt aber vollständig im DOM und im Tab-Fokus — Adresse, Route-Link und Teilen-Knopf sind
fokussierbar, ohne sichtbar zu sein (kein `inert`, kein `aria-hidden`). Öffnen geht gar nicht per
Tastatur: `.standortmenu-griffzone` ist ein `<div>` mit reinen Pointer-Handlern. Der Umschalter
Straße/Satellit (`:773`) hat kein `aria-pressed`, sein Label bleibt in beiden Zuständen gleich.

## 15. `disconnectedCallback()` räumt nur eine von drei Karten ab [belegt]

`:3815-3817` zerstört `MAP`, nicht aber `SMAP`/`TMAP`. Wird das Panel abgehängt, während Standort-
oder Übersichtsseite offen ist, bleibt eine Leaflet-Instanz samt Resize-Listener und
Kachel-Abrufen am Leben.

---

# Klein

| Ort | Befund |
|---|---|
| `audi-dashboard-ios.css:88, 208` | `main{padding:…}` (0,0,1) verliert gegen `main#view` (1,0,1) in `audi-dashboard.css:197`. Auf Großbildschirmen bleibt der Seitenrand bei 20 px statt der beabsichtigten 44 px. **Achtung:** `.standort-vollbild{margin:0 -20px}` (`audi-dashboard.css:704`) passt heute genau dazu — beides gehört zusammen angefasst. |
| `audi-dashboard-app.js:3190/3612` | „Nur passende Sensoren anzeigen" wirkt nicht, solange ein Dropdown offen ist: Der Klick-Handler ersetzt das Overlay-DOM, bevor das `change`-Event des Kontrollkästchens ausgeliefert wird. [plausibel] |
| `audi-dashboard-app.js:2966` | Theme-Umschalten ruft kein `render()` — die Leaflet-Kacheln bleiben bis zum nächsten Backend-Update im alten Stil. Durch die neue Dauerkarte auf der Übersicht deutlich sichtbarer als früher. |
| `audi-dashboard-app.js:804` | `${de(CAR.tankPct)} %` zeigt ohne Tanksensor „0 %“, während direkt daneben korrekt „Reichweite unbekannt“ steht. |
| `audi-dashboard-app.js:496, 518, 117` | Toter Code: `fahrzeugGlyphPfade()` nie aufgerufen, `zustand.parkplatz` nie gelesen, `standortGenauigkeitM`/`standortZeit` gemappt aber ungenutzt. Kommentare behaupten das Gegenteil. |
| `audi-dashboard-app.js:595` | Einzige Interpolation im neuen Code, die ohne `esc()` in ein Attribut geht. Heute nicht ausnutzbar, weil `_zu_zahl()` im Backend nur Zahlen durchlässt — als Härtung trotzdem sinnvoll, ebenso `Number.isFinite` in `standortBekannt()`. |

---

# Dokumentation

## AGENTS.md widerspricht sich an sechs Stellen [belegt]

Die Datei ist auf 427 Zeilen gewachsen; neue Einträge wurden angehängt statt eingearbeitet.

| Stelle | Aussage | Widerspruch |
|---|---|---|
| Z. 94 | „trip detection via the iPhone WLAN sensor" | Z. 249 ff.: läuft über `ZUENDUNG_SENSOR`, WLAN „fully removed" |
| Z. 110 | „Codec JSON → MQTT/TLS → Mosquitto" | Z. 227 ff.: Schwenk auf flespi, „not MQTT at all" |
| Z. 93 | „4 modules" | tatsächlich 5 (`entitaeten.py` neu) |
| Z. 97 | „3.130 lines" | tatsächlich 3.873 |
| Z. 100 | „`einstellungen.py` — the one file edited before install" | genau das ersetzt jetzt das Setup-Menü |
| Z. 320 | `STANDORT_TRACKER` „left empty" | steht auf `device_tracker.testzone_fmm003` |

Dazu veraltete Codeverweise: `fakeTrack()` liegt bei `:421` statt `:414`, die Statistik-Behauptung
in `INSTALL.md` bei Z. 229 statt Z. 204.

Die Datei verlangt selbst „nicht festhalten, was Code und Git-History schon zeigen" (Z. 47) —
Z. 282–401 sind 120 Zeilen Changelog-Prosa für fünf Einträge, und Z. 249–279 dupliziert inhaltlich
Z. 133–153.

## INSTALL.md [belegt]

- **Zirkuläre Schrittfolge:** Schritt 4 verweist auf „siehe Schritt 9 zuerst" (Z. 96), Schritt 7
  setzt Schritt 4 voraus (Z. 176). Die Reihenfolge 4→9→7→4 wird nirgends aufgelöst.
- **Ausgelieferte Default-Entities werden verschwiegen:** `ZUENDUNG_SENSOR`, `BATTERIE_SENSOR` und
  `STANDORT_TRACKER` sind mit den `testzone_fmm003`-Entities der Autoren-Instanz vorbelegt. Bei
  einer Frischinstallation zeigen sie ins Leere. Z. 108–110 behauptet nur für KM/TANK/RANGE
  „unbelegt".
- **Override-Datei nicht benannt:** Z. 104 spricht von „einer Override-Datei" — der Pfad
  `/config/audi_dashboard/entitaeten.json` steht nirgends, und ihre fehlende Sicherung (Befund 6)
  ist nicht erwähnt.
- Schritt 2 verweist auf `fahrzeugprofil.example.json`, deren Z. 2 weiterhin den „WLAN-Namen"
  verlangt, obwohl `fahrzeug.wlan_name` im selben Branch entfernt wurde.

## WLAN-Reste in der übrigen Doku [belegt]

Im Code ist die Entfernung vollständig (keine Fundstelle). In der Doku nicht:
`SPECIFICATION.md` (Z. 16, 147, 181, 196, 217), `homeassistant/README.md:106` und die Profilvorlage
nennen WLAN-Sensor und `TommiG1/HA_VAG-EU-Data-Act` weiterhin als aktuellen Stand.

## `update.ps1` [belegt]

Für `www/` ist das Skript nach dem Fix **vollständig** — `audi-dashboard-ios.css` und `badges/`
sind ergänzt, Cache-Busting greift auch für die neue CSS-Datei.

**Verbleibende Lücke:** `homeassistant/data/shell_beleg_parser.py` ist Code, kein Datenbestand —
`belegverarbeitung.py:41` lädt ihn aus `/config/audi_dashboard/`. `update.ps1` schließt `data/`
pauschal aus, `INSTALL.md` behandelt ihn als Einmal-Kopie. **Änderungen am Beleg-Parser, dem
einzigen getesteten Teil des Repos, kämen auf keiner Instanz an.**

## `design/` [belegt]

`dm360-host.html` lädt `audi-dashboard-app.js` (Z. 12) und `audi-dashboard-ios.css` (Z. 33) — beide
existieren in `design/` nicht. Aus der Repo-Kopie ist das Board funktionslos; es läuft nur im
Claude-Design-Projekt. Nirgends dokumentiert.

`design/README.md:19` führt `audi-dashboard-ios.css` in der Inhaltstabelle des Ordners (die Datei
liegt in `homeassistant/www/`) und behauptet, die Auflage „rührt `audi-dashboard-app.js` nicht an" —
Z. 57–59 derselben Datei beschreibt, dass genau dort der Stylesheet-Loader ergänzt wurde.

**Markenassets:** Der Diff fügt keine neuen Binärdateien hinzu. `DataMetric360 Board.dc.html`
verwendet die vorhandenen Audi-Type-Dateien per `@font-face` — das Lizenzrisiko bleibt unverändert,
nicht erhöht.

## `.gitignore` [belegt]

`installationspaket/` ist korrekt ergänzt und schließt nichts Getracktes aus. Es fehlt
`data/entitaeten.json` neben den anderen drei Laufzeitdateien — heute harmlos, weil die Datei nur
unter `/config/audi_dashboard/` entsteht, aber inkonsistent zum eigenen Grundsatz der Datei.

---

# Was den Branch `umsetzung-datametric360` betrifft

**Der flespi-Schwenk macht `homeassistant/FMM003_MAPPING.md` überholt.** Das Dokument beschreibt
Codec JSON über MQTT/TLS mit eigener CA und einem `mosquitto_sub`-Mitschnitt. Mit flespi läuft es
über den nativen Codec8/TCP-Kanal mit IMEI-Authentifizierung, ohne Zertifikate und ohne eingehenden
Port; die Feldzuordnung entstünde aus der flespi-REST-API. Beim Zusammenführen anpassen oder
ersetzen.

**Das CORS-Rätsel ist damit auch erklärt.** Die Notiz auf `main`, der `http:`-Block sei aus der
`configuration.yaml` entfernt worden, „nachdem HA angefangen hat, ihn zu migrieren beziehungsweise
zu ignorieren", erklärt, warum `cors_allowed_origins` in HA 2026.8 wirkungslos blieb — auch bei
gleichem Ursprung. Gehört in `homeassistant/REVERSE_PROXY.md` nachgetragen.

**Die Falle beim Zusammenführen** steht bereits in `AGENTS.md` dieses Branches: `profil_lesen()`
gibt hier bei fehlender Profildatei `None` zurück, `profil.py` ist auf `main` unverändert und geht
konfliktfrei durch — die dortigen Aufrufstellen prüfen das aber nicht.

---

# Empfehlung

Reihenfolge nach Schaden:

1. **Befund 1** (Datenverlust) — zwei Riegel, wenige Zeilen.
2. **Befunde 2 und 5** (Zurücksetzen) — ein gemeinsamer Fix in `overrides_anwenden()`.
3. **Befunde 3 und 4** (falsche Vorbelegung) — Index in den Vorschlag einbeziehen, Schwelle auf 5.
4. **Befund 6** (Sicherung) — eine Zeile in `backup.py`, plus Wiederherstellung.
5. **Befund 7** (Absicherung beim Lesen), **8** (Akku), **9/10** (Bedienbarkeit, Robustheit).

Die Punkte 1 bis 4 betreffen alle das neue Setup-Menü und lassen sich in einem Zug erledigen.
