Selbst-Update-Sicherung, Service-Prognose ohne Servicebuch und Sicherheit neu gestaltet

- aktualisierung.py: Backup-Ordner verliert sein manifest.json beim Sichern,
  sonst laedt HA ihn nach einem Neustart als zweite audi_dashboard-Integration
  (Ursache des raetselhaften "gleicher Pfad"-Umbenennungs-Fehlers).
- service.ts/audi-dashboard-app.js: Uebersicht faellt jetzt wie Service schon
  auf die Fahrzeugmeldung zurueck, wenn kein Servicebucheintrag existiert;
  eigenes (kuerzeres) Oelwechsel-Intervall wird jetzt auch ohne Servicebuch-
  eintrag naeherungsweise aus der Herstellermeldung hochgerechnet, statt
  weiterhin unveraendert die Herstellervorgabe zu zeigen.
- Sicherheit neu gestaltet: vier Sammelzeilen (Fahrzeug verriegelt / Tueren
  und Klappen geschlossen / Fenster und Dach geschlossen / Kein Licht) statt
  einer flachen Liste, mit neuen Tuerschloss-, Dach- und Standlicht-Sensor-
  rollen, Kreis-Haekchen-Symbolen und einer Detailseite je Tuer/Motorhaube/
  Kofferraum in beiden Frontends.

Details, Verifikation und Sensor-Rollen in AGENTS.md (Abschnitte J/M/N).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-24 15:29:37 +02:00
co-authored by Claude Sonnet 5
parent 146f809e9b
commit 5a048488ea
23 changed files with 866 additions and 97 deletions
@@ -94,26 +94,49 @@ def _standort(hass: HomeAssistant, werte: Sensorzuordnung) -> dict:
def _sicherheitscheck(hass: HomeAssistant, werte: Sensorzuordnung) -> list[dict]:
"""Die einzeln geprüften Punkte hinter "Sicher abgestellt", fürs Frontend
(Klick auf den Status öffnet diese Liste mit einem grünen/roten/grauen
Punkt je Zeile).
"""Die einzeln geprüften Punkte hinter "Sicher abgestellt", fürs Frontend -
gruppiert über das Feld "gruppe" in vier Sammelzeilen (Fahrzeug verriegelt
/ Türen und Klappen geschlossen / Fenster und Dach geschlossen / Licht
ausgeschaltet), die die Oberfläche durch Gruppieren dieser flachen Liste
berechnet, statt hier schon vier separate Listen zu bauen - eine flache
Liste bleibt die einfachere, additive Änderung gegenüber dem, was das
Frontend vorher schon kannte.
"ok" ist None, wenn der Sensor fehlt oder nicht verfügbar ist - genau
daraus leitet sich auch die zusammengefasste Kennzahl unten ab, damit
beide nie auseinanderlaufen können."""
beide nie auseinanderlaufen können. Alle binary_sensor-device_classes
hier (door/window/opening/lock/light) folgen HAs eigener Konvention "off
= der gute Zustand" (zu/verriegelt/kein Licht) - ein einziges Prüfmuster
für alle Zeilen, kein Sonderfall je Art."""
eintraege: list[dict] = []
for pos, sensor in zip(POSITIONEN, werte.TUER_SENSOREN):
w = zustand_oder_none(hass, sensor)
eintraege.append({"label": f"Tür {pos}", "ok": None if w is None else w == "off"})
eintraege.append({"label": f"Tür {pos}", "ok": None if w is None else w == "off", "gruppe": "tueren_klappen"})
for pos, sensor in zip(POSITIONEN, werte.FENSTER_SENSOREN):
w = zustand_oder_none(hass, sensor)
eintraege.append({"label": f"Fenster {pos}", "ok": None if w is None else w == "off"})
eintraege.append({"label": f"Fenster {pos}", "ok": None if w is None else w == "off", "gruppe": "fenster_dach"})
# "Kofferraum" statt "Heckklappe": alltagssprachlicher Name für dieselbe
# Öffnung beim Avant/Kombi - das Sensorfeld HECKKLAPPE_SENSOR selbst
# bleibt unverändert, um eine bestehende Zuordnung nicht zu brechen.
for label, sensor in (
("Heckklappe", werte.HECKKLAPPE_SENSOR),
("Kofferraum", werte.HECKKLAPPE_SENSOR),
("Motorhaube", werte.HAUBE_SENSOR),
):
w = zustand_oder_none(hass, sensor)
eintraege.append({"label": label, "ok": None if w is None else w == "off"})
eintraege.append({"label": label, "ok": None if w is None else w == "off", "gruppe": "tueren_klappen"})
for pos, sensor in zip(POSITIONEN, werte.TUERSCHLOSS_SENSOREN):
w = zustand_oder_none(hass, sensor)
eintraege.append({"label": f"Türschloss {pos}", "ok": None if w is None else w == "off", "gruppe": "verriegelt"})
for label, sensor in (
("Kofferraum-Schloss", werte.HECKKLAPPE_SCHLOSS_SENSOR),
("Motorhauben-Schloss", werte.HAUBE_SCHLOSS_SENSOR),
):
w = zustand_oder_none(hass, sensor)
eintraege.append({"label": label, "ok": None if w is None else w == "off", "gruppe": "verriegelt"})
w = zustand_oder_none(hass, werte.DACH_SENSOR)
eintraege.append({"label": "Dach", "ok": None if w is None else w == "off", "gruppe": "fenster_dach"})
w = zustand_oder_none(hass, werte.LICHT_SENSOR)
eintraege.append({"label": "Standlicht", "ok": None if w is None else w == "off", "gruppe": "licht"})
return eintraege