Files
audi-app/REVIEW_main_2026-08-13.md
Paul Nothaft 78d70fa4ca Review-Dokument als umgesetzt kennzeichnen
Haelt fest, was bewusst anders geloest wurde als vorgeschlagen, welche drei
Fehler beim Umsetzen dazukamen und welcher pyscript-Fallstrick dabei
aufgefallen ist.
2026-08-13 16:10:38 +02:00

23 KiB
Raw Permalink Blame History

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:

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:

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 >= 3ein 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:

["","","",""] 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:

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.jsonlentitaeten.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. 282401 sind 120 Zeilen Changelog-Prosa für fünf Einträge, und Z. 249279 dupliziert inhaltlich Z. 133153.

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. 108110 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ändigaudi-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. 5759 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.