Merge branch 'umsetzung-datametric360': Companion-App-Phasen 1-10 uebernehmen

Bringt die vollstaendig gebaute DataMetric360-App zusammen (React/Vite,
21 Screens, Audi-Assets, PWA + native Capacitor-Huelle, 90+9 Tests) sowie
die profil_lesen()-Haertung gegen fehlende/kaputte Profildatei zusammen.

Konfliktaufloesung:
- AGENTS.md, INSTALL.md, README.md: main-Fassung war jeweils die
  chronologisch neuere, uebernommen und um die durch den Merge tatsaechlich
  erledigten Punkte ergaenzt (profil_lesen()-Haertung, Audit-Reste-Entscheidung).
- fahrterkennung.py: toten WLAN-Zweig vom Branch verworfen, Zuendungs-
  basierte Erkennung von main behalten.
- homeassistant/FMM003_MAPPING.md (MQTT/Mosquitto-Ansatz vom 2026-08-11,
  vor der Umstellung auf flespi) bewusst nicht uebernommen - main nutzt seit
  2026-08-12 flespi als alleinigen FMM003-Datenweg. UMSETZUNGSPLAN.md Phase 13
  entsprechend als ueberholt markiert, verweist auf AGENTS.md als massgeblich.
- REVIEW_main_2026-08-13.md, ha_install.md (add/add): main-Fassung war die
  spaetere Revision derselben Dokumente, uebernommen.

Die von REVIEW_main_2026-08-13.md befuerchtete Merge-Falle (profil_lesen()
gibt jetzt None zurueck, main-seitige Aufrufer pruefen das nicht) wurde
verifiziert als bereits entschaerft: alle 5 Aufrufstellen im gemergten Stand
(backup.py x2, fahrterkennung.py, frontend_veroeffentlichung.py,
reifenzaehler.py, tankerkennung.py) sind None-sicher.

installationspaket/ nicht Teil dieses Commits (gitignored, wird bei Bedarf
neu zusammengestellt).
This commit is contained in:
2026-08-13 19:03:20 +02:00
113 changed files with 17638 additions and 159 deletions
+4
View File
@@ -53,6 +53,8 @@ def _backup_jetzt():
def _letztes_backup_vermerken():
p = profil.profil_lesen()
if p is None:
return
p.setdefault("einstellungen", {})["letztes_backup"] = datetime.datetime.now().isoformat()
profil.profil_schreiben(p)
frontend_veroeffentlichung.profil_veroeffentlichen()
@@ -69,6 +71,8 @@ def audi_dashboard_backup_jetzt():
@time_trigger("cron(0 4 * * *)")
def _automatisches_backup():
p = profil.profil_lesen()
if p is None:
return
intervall = p.get("einstellungen", {}).get("backup_intervall", "aus")
heute = datetime.date.today()
faellig = (