Files
audi-app/custom_components/audi_dashboard/einstellungen.py
T
tobias 7e2854fe8d Setup: Erwartungswert je Listenposition, gruen auch dort (2026.9.1.15)
Tuer- und Fenstersensoren haben vier Positionen, aber nur einen Beispielnamen
(front_left_door). Der beschreibt die erste; die anderen drei koennen ihn nie
enthalten. Folge: nur die erste Position konnte gruen werden, und im Kopf
stand ein Erwartungswert, der fuer drei von vier Zeilen nicht galt.

Neu "beispiele" je Position im Feld-Katalog und feldBeispiel(feld, idx) im
Panel. Der Erwartungswert steht jetzt an jeder Position statt im Kopf -
rechtsbuendig, in der kleinen grauen Schrift des Positionslabels. Auch die
Sortierung der Vorschlagsliste nutzt ihn, sonst stuende in allen vier Zeilen
dieselbe Entitaet oben.

Typografie: .setup-feld-hinweis ist fuer die Kopfzeile gebaut (14px, --fg,
rechtsbuendig, max-width 55%). Im 12,5px-Positionslabel wirkt das falsch herum.
Eine Regel im Kontext .setup-unterfeld-label .setup-feld-hinweis laesst Groesse
und Farbe erben; die Rechtsbuendigkeit kommt aus justify-content: space-between
am Label, wie eine Ebene darueber bei .setup-feld-kopf.

Verifiziert: alle acht Positionen tragen ihren eigenen Beispielnamen,
py_compile und Panel als Modul sauber, audi_ha_test ohne Fehler gestartet.
Die Darstellung selbst habe ich nicht im Browser gesehen - das Panel liess
sich in der Testinstanz nicht ansteuern.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 19:00:16 +02:00

384 lines
22 KiB
Python

"""Welche Home-Assistant-Entität welche Rolle im Fahrzeug spielt.
Das ist die einzige technische Konfiguration der App. Alles andere (Pausen-
zeit, Reifendaten, Versicherung, ...) gehört ins Fahrzeugprofil und ist über
die Oberfläche änderbar.
**Im Auslieferstand ist hier alles leer.** Das ist Absicht, kein unfertiger
Zustand: welche Entity-IDs richtig sind, hängt an der jeweiligen Instanz und
ihren Integrationen. Zugeordnet wird nach der Installation im Setup-Menü der
App (Einstellungen -> Fahrzeug einrichten -> Setup), das die Zuordnung nach
audi_dashboard/entitaeten.json schreibt (siehe zuordnung.py).
Ein leeres Feld ist der sichere Zustand: die betroffene Kachel zeigt
"unbekannt" statt eines falschen Werts (siehe zustand_oder_none() in
veroeffentlichung.py), und die trigger-gebundenen Felder registrieren gar
keinen Zustandsbeobachter, statt einen gegen eine nicht existierende Entität
zu registrieren. Eine gesetzte, aber falsche Entity-ID ist deshalb schlechter
als eine leere - genau das ist 2026-08-23 einmal passiert, als hier noch die
Entity-IDs einer längst abgeräumten Testinstanz standen.
Zwei typische Quellen auf dieser Instanz: der Teltonika FMM003 (GPS-Tracker
mit CAN-Anbindung, über flespi angebunden) und eine EU-Data-Act-Integration
des Herstellers. Welche davon welche Rolle bedient, entscheidet das
Setup-Menü - nicht diese Datei.
UNTERSCHIED ZUR PYSCRIPT-FASSUNG: dort waren das Modul-Variablen, die
entitaeten.py zur Laufzeit per setattr() auf dem importierten Modul-Objekt
überschrieb. Das funktionierte, war aber ein globaler, veränderlicher Zustand
quer durch alle Dateien. Hier ist es ein ganz normales Objekt, das der
Koordinator hält und weiterreicht.
"""
from __future__ import annotations
from dataclasses import dataclass, field, fields
# Vier gleich aufgebaute Positionen für Türen und Fenster.
POSITIONEN = ["vorne links", "vorne rechts", "hinten links", "hinten rechts"]
@dataclass
class Sensorzuordnung:
"""Die wirksame Zuordnung. Feldnamen = Schlüssel in entitaeten.json."""
# Fahrterkennung: Start/Ende einer Fahrt über den Zündungs-/ACC-Status
# (on = Fahrt läuft). Dieselbe Entität speist auch die Anzeige
# "fährt/steht" - damit können Erfassung und Anzeige nicht auseinander-
# laufen.
ZUENDUNG_SENSOR: str = ""
# Kilometerstand - für Fahrtabschluss-Screening, Reifenzähler und
# Ölwechsel-Prognose.
#
# Beim FMM003 hier NICHT den selbst berechneten Gesamtkilometerstand
# (*_total_calculated_mileage) zuordnen - dafür gibt es GNSS_KM_SENSOR
# rechnung statt auf dem Tacho und damit auf einer anderen Zählbasis als
# der echte Fahrzeug-Kilometerstand. Wer ihn einträgt, verfälscht alle
# drei genannten Auswertungen mit einem inkonsistenten Basiswert. Der vom
# CAN gelesene Wert (*_total_vehicle_mileage_read_from_can) bzw. der
# Kilometerstand der EU-Data-Act-Integration ist der richtige.
KM_SENSOR: str = ""
# Der geräteeigene Kilometerzähler, wenn er auf GNSS rechnet
# (Trip \ Odometer -> Calculation Source = GNSS). Er löst am 01.09.2026
# nachgemessen auf **einen Meter** auf (0,034 km direkt nach dem
# Zurücksetzen), während der vom CAN gelesene Fahrzeug-Kilometerstand nur
# ganze Kilometer hergibt - über die gesamte Aufzeichnung 147 Schritte von
# exakt 1,0 km, kein einziger feinerer.
#
# Gedacht als FEINAUFLÖSUNG, nicht als Ersatz: KM_SENSOR bleibt der Anker,
# weil er der Tacho des Fahrzeugs ist und nicht driftet. Dieser hier liefert
# die Nachkommastelle dazwischen und verliert Strecke, wo kein GNSS-Empfang
# ist. Beides zusammen ergibt eine Strecke auf 100 m genau, die trotzdem am
# Fahrzeugwert hängt.
#
# NOCH OHNE VERBRAUCHER: die Rolle ist angelegt, damit nach der ersten
# echten GNSS-Fahrt nur noch gemessen und nicht mehr zugeordnet werden muss.
# Solange sie niemand liest, ändert eine Zuordnung nichts.
GNSS_KM_SENSOR: str = ""
# Die Strecke seit dem letzten Datensatz, ebenfalls vom Gerät gerechnet
# (Trip \ Odometer -> Mode = "Between records"; steht die Einstellung auf
# "Continuous", zählt das Feld stattdessen die laufende Fahrt und die Rolle
# ist unbrauchbar - vor dem Zuordnen also nachsehen).
#
# Wozu zusätzlich zu GNSS_KM_SENSOR: der Gesamtzähler ist die robustere
# Quelle für eine Fahrtstrecke, weil ein verlorener Datensatz die Differenz
# nicht verfälscht - er zählt weiter. Die Teilstrecken taugen dafür nur,
# wenn keiner fehlt. Umgekehrt zeigen sie, WO Strecke verloren ging, und
# sind damit die bessere Gegenprobe. Auch diese Rolle hat noch keinen
# Verbraucher.
GNSS_TEILSTRECKE_SENSOR: str = ""
# Das Trip-Signal des Geräts: es beginnt erst, wenn Zündung UND Bewegung
# UND "Start Speed" zusammenkommen, und endet erst nach dem
# "Ignition OFF Timeout" des Geräts. Damit bringt es die Pausentoleranz
# selbst mit, statt dass wir einen zweiten Zeitgeber bauen.
#
# Bewusst getrennt von ZUENDUNG_SENSOR: die rohe Zündung prellt (am
# 29.08.2026 drei Wechsel in 30 ms) und stand am 30./31.08. dreizehn
# Stunden am Stück auf "an", ohne dass gefahren wurde. Als Auslöser für
# Fahrten taugt sie nicht, als Anzeige "Zündung an" sehr wohl.
#
# Leer: dann übernimmt ZUENDUNG_SENSOR die Fahrterkennung wie früher -
# bestehende Installationen laufen ohne Zutun weiter.
TRIP_SENSOR: str = ""
# Der Zeitstempel, den das Gerät dem Datensatz selbst mitgegeben hat
# (Unix-Sekunden, UTC). Optional, aber der einzige Weg zu einem
# ehrlichen Fahrtbeginn: ohne ihn ist der Beginn der Moment, in dem
# Home Assistant den Wechsel verarbeitet - und der liegt hinter dem
# tatsächlichen, sobald das Gerät gepuffert hat. Am 01.09.2026
# gemessen: ein Datensatz mit Fahrtende trug die Gerätezeit 07:29:04
# und kam um 07:35:23 an, sechs Minuten später.
#
# UTC, nicht Ortszeit - ebenfalls nachgemessen, der Versatz zur
# UTC-Uhr von Home Assistant lag bei Sekunden, nicht bei zwei Stunden.
# Eine Zeitzonenumrechnung wäre also nicht nur unnötig, sie baute
# einen Zwei-Stunden-Fehler ein.
MELDEZEIT_SENSOR: str = ""
# Tankfüllstand in Prozent - für die automatische Tankerkennung und die
# Anzeige in der Übersicht.
TANK_SENSOR: str = ""
# Tankvolumen in Litern, direkt vom CAN (nicht aus dem Prozentwert
# umgerechnet). Optional - ist er gesetzt, übernimmt er die automatische
# Tankerkennung von TANK_SENSOR: ein Anstieg misst sich dann direkt in
# Litern statt über den Umweg Prozentpunkte -> Tankvolumen aus dem
# Fahrzeugprofil, und der erkannte Anstieg selbst dient als grobe
# Vorbelegung für die getankte Menge (siehe tankerkennung.py).
TANK_LITER_SENSOR: str = ""
# Vom Fahrzeug selbst berechnete Strecke seit der letzten Tankung
# (FMM003: "calculated distance covered after the latest refueling").
# Optional - ist er gesetzt, übernimmt sein aktueller Wert das Feld
# distance_km eines neu erkannten Tankvorgangs anstelle der eigenen
# Kilometerstand-Subtraktion (ablage.distanz_seit_letzter_tankung()) -
# direkt vom Fahrzeug gemeldet statt selbst gerechnet, und robust gegen
# Lücken im eigenen Bestand. Ohne ihn bleibt die bisherige Berechnung
# unverändert der Rückfall.
TANK_DISTANZ_SENSOR: str = ""
# Reichweite (Übersicht).
GESCHWINDIGKEIT_SENSOR: str = ""
RANGE_SENSOR: str = ""
# 12V-Batteriespannung. Beim FMM003 ist das external_power_voltage - die
# vom Gerät gemessene Bordnetzspannung des Fahrzeugs -, NICHT
# battery_voltage (das ist die interne Pufferbatterie des Trackers selbst
# und hat mit der Fahrzeugbatterie nichts zu tun).
BATTERIE_SENSOR: str = ""
# Außentemperatur (CAN ambient air temperature) - optional, für die
# Batteriespannungs-Messwertliste: zusammen mit dem Tagesminimum
# aufgezeichnet, siehe batterie.py.
AUSSENTEMP_SENSOR: str = ""
# Knopf für eine sofortige Neuabfrage beim Fahrzeug.
REFRESH_BUTTON: str = ""
# Türen/Fenster (je vier Positionen, siehe POSITIONEN).
TUER_SENSOREN: list[str] = field(default_factory=list)
FENSTER_SENSOREN: list[str] = field(default_factory=list)
HECKKLAPPE_SENSOR: str = ""
HAUBE_SENSOR: str = ""
# Türschloss (getrennt von den Öffnungssensoren oben - zu heißt nicht
# verriegelt). Nur die Fahrertür: die Zentralverriegelung schließt immer
# alle Türen gemeinsam, ein einzelnes Türschloss lässt sich nicht separat
# bedienen - ihr Zustand gilt fürs ganze Fahrzeug (Nutzer-Entscheidung).
TUERSCHLOSS_SENSOR: str = ""
# Schiebedach (offen/zu) und Standlicht (an/aus) - ergänzen die
# Fenster-Gruppe bzw. stehen für sich, siehe _sicherheitscheck() in
# veroeffentlichung.py.
DACH_SENSOR: str = ""
LICHT_SENSOR: str = ""
# Vom Fahrzeug selbst gemeldete Service-Fälligkeit (ergänzt die
# App-eigene, aus dem Servicebuch berechnete Prognose).
NAECHSTER_OELWECHSEL_SENSOR: str = ""
OELWECHSEL_STRECKE_SENSOR: str = ""
NAECHSTE_INSPEKTION_SENSOR: str = ""
INSPEKTION_STRECKE_SENSOR: str = ""
# Live-GPS-Position (Übersicht -> Standort-Kachel). Breiten-/Längengrad
# als zwei eigene sensor-Entities: flespi liefert Koordinaten so, nicht
# als Attribute einer device_tracker-Entity.
STANDORT_LAT_SENSOR: str = ""
STANDORT_LON_SENSOR: str = ""
def _standardwerte() -> dict[str, object]:
"""Die eingebauten Werte, einmal eingefroren.
Gebraucht für "Zurücksetzen" im Setup-Menü: die Zuordnung wird bei jedem
Anwenden vollständig neu gesetzt, ein weggelassener Override fällt damit
auf genau diesen Wert zurück."""
leer = Sensorzuordnung()
werte: dict[str, object] = {}
for f in fields(leer):
wert = getattr(leer, f.name)
werte[f.name] = list(wert) if isinstance(wert, list) else wert
return werte
STANDARDWERTE: dict[str, object] = _standardwerte()
SCHLUESSEL: set[str] = set(STANDARDWERTE)
# Katalog aller Rollen fürs Setup-Menü im Frontend. Reihenfolge =
# Anzeige-Reihenfolge innerhalb jeder Gruppe. domains/device_classes/units
# sind Erwartungen fürs Frontend-seitige Filtern und Vorschlagen (Schalter
# "Nur passende Sensoren anzeigen") - eine fehlende oder leere Liste bedeutet
# "keine Einschränkung" bzw. "diese Rolle hat üblicherweise keine Einheit".
FELDER: list[dict] = [
{"key": "GNSS_KM_SENSOR", "label": "Kilometerzähler (GNSS)", "gruppe": "fahrterkennung",
"hinweis": "Der geräteeigene Zähler, auf einen Meter genau. Verfeinert die Fahrtstrecke; der Kilometerstand oben bleibt der maßgebliche Wert.",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km"], "liste": False, "pflicht": False,
"beispiel": "total_calculated_mileage",
"stichworte": ["gnss", "gps", "calculated", "mileage", "kilometer"]},
{"key": "GNSS_TEILSTRECKE_SENSOR", "label": "Teilstrecke je Datensatz (GNSS)", "gruppe": "fahrterkennung",
"hinweis": "Strecke seit dem letzten Datensatz - Gegenprobe zum Kilometerzähler und Hinweis darauf, wo Strecke fehlt.",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km"], "liste": False, "pflicht": False,
"beispiel": "segment_mileage",
"stichworte": ["segment", "teilstrecke", "abschnitt", "mileage"]},
{"key": "TRIP_SENSOR", "label": "Trip-Status", "gruppe": "fahrterkennung",
"hinweis": "on = Fahrt läuft. Das gefilterte Fahrtsignal des Geräts - erkennt Fahrtbeginn und -ende. Ohne Zuordnung übernimmt die Zündung diese Aufgabe.",
"domains": ["binary_sensor"], "device_classes": [], "units": [], "liste": False, "pflicht": False,
"beispiel": "trip_status_true_if_trip_started_false_if_stopped",
"stichworte": ["trip", "fahrt", "trip status", "journey"]},
{"key": "ZUENDUNG_SENSOR", "label": "Zündung", "gruppe": "fahrterkennung",
"hinweis": "on = Zündung an. Für die Anzeige \"fährt/steht\"; ohne zugeordneten Trip-Status erkennt sie zusätzlich Fahrtbeginn und -ende.",
"domains": ["binary_sensor"], "device_classes": [], "units": [], "liste": False, "pflicht": True,
"beispiel": "engine_ignition_or_acc_status",
"stichworte": ["zündung", "ignition", "acc", "motor", "engine"]},
{"key": "KM_SENSOR", "label": "Kilometerstand", "gruppe": "fahrterkennung",
"hinweis": "Gesamt-Kilometerstand des Fahrzeugs - für Fahrtabschluss-Screening, Reifenzähler und Ölwechsel-Prognose.",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km", "mi"], "liste": False, "pflicht": False,
"beispiel": "total_vehicle_mileage_read_from_can",
"stichworte": ["kilometer", "km", "mileage", "odometer", "tacho", "laufleistung"]},
{"key": "MELDEZEIT_SENSOR", "label": "Meldezeit des Geräts", "gruppe": "fahrterkennung",
"hinweis": "Zeitstempel des Datensatzes in Unix-Sekunden (UTC). Damit stammen Fahrtbeginn und -ende vom Gerät statt von der Ankunft in Home Assistant.",
"domains": ["sensor"], "device_classes": [], "units": ["s", "seconds"], "liste": False, "pflicht": False,
"beispiel": "message_timestamp",
"stichworte": ["meldezeit", "zeitstempel", "timestamp", "message", "zeit"]},
{"key": "TANK_SENSOR", "label": "Tankfüllstand", "gruppe": "fahrterkennung",
"hinweis": "Füllstand in Prozent - für die automatische Tankerkennung.",
"domains": ["sensor"], "device_classes": [], "units": ["%"], "liste": False, "pflicht": False,
"beispiel": "fuel_level_in_tank_read_from_can",
"stichworte": ["tank", "fuel", "kraftstoff", "füllstand", "level"]},
{"key": "TANK_LITER_SENSOR", "label": "Tankvolumen", "gruppe": "fahrterkennung",
"hinweis": "Optional, vom CAN statt aus dem Prozentwert umgerechnet - macht die automatische "
"Tankerkennung genauer und liefert eine Vorbelegung für die getankte Menge.",
"domains": ["sensor"], "device_classes": ["volume"], "units": ["L", "l"], "liste": False, "pflicht": False,
"beispiel": "can_fuel_volume",
"stichworte": ["tankvolumen", "liter", "litres", "fuel volume", "kraftstoffvolumen"]},
{"key": "TANK_DISTANZ_SENSOR", "label": "Strecke seit letzter Tankung", "gruppe": "fahrterkennung",
"hinweis": "Optional, vom Fahrzeug selbst berechnet - ersetzt die eigene Kilometerstand-Rechnung "
"für die Strecke eines Tankvorgangs.",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km", "mi"], "liste": False, "pflicht": False,
"beispiel": "calculated_distance_covered_after_the_latest_refueling",
"stichworte": ["strecke tankung", "distance refuel", "distance since refueling", "refuelling"]},
{"key": "GESCHWINDIGKEIT_SENSOR", "label": "Geschwindigkeit", "gruppe": "fahrterkennung",
"hinweis": "Optional - fuer die Hoechstgeschwindigkeit einer Fahrt. Der FMM003 liest sie laut seiner Konfiguration vom OBD/CAN, nicht aus GPS.",
"domains": ["sensor"], "device_classes": ["speed"], "units": ["km/h", "mph"], "liste": False, "pflicht": False,
"beispiel": "vehicle_speed_read_from_can",
"stichworte": ["geschwindigkeit", "speed", "tempo", "kmh"]},
{"key": "RANGE_SENSOR", "label": "Reichweite", "gruppe": "uebersicht",
"hinweis": "Für die Übersicht - bleibt sie leer, zeigt die Oberfläche \"unbekannt\".",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km", "mi"], "liste": False, "pflicht": False,
"beispiel": "vehicle_range_read_from_can",
"stichworte": ["reichweite", "range"]},
{"key": "BATTERIE_SENSOR", "label": "12V-Batteriespannung", "gruppe": "uebersicht",
"hinweis": "Nicht jede Integration liefert diesen Wert - bleibt er leer, zeigt \"Mein Audi\" \"unbekannt\".",
"domains": ["sensor"], "device_classes": ["voltage"], "units": ["V"], "liste": False, "pflicht": False,
"beispiel": "external_power_voltage",
"stichworte": ["batterie", "battery", "spannung", "volt", "12v"]},
{"key": "AUSSENTEMP_SENSOR", "label": "Außentemperatur", "gruppe": "uebersicht",
"hinweis": "CAN-Außentemperatur - optional, ergänzt die Batteriespannungs-Messwertliste um den "
"Temperaturwert je Tag.",
"domains": ["sensor"], "device_classes": ["temperature"], "units": ["°C", "°F"], "liste": False,
"pflicht": False, "beispiel": "can_ambient_air_temperature",
"stichworte": ["außentemperatur", "aussentemperatur", "ambient", "temperatur", "temperature"]},
{"key": "REFRESH_BUTTON", "label": "Sofort-Aktualisierung", "gruppe": "uebersicht",
"hinweis": "Der von der Integration bereitgestellte Knopf für \"Ziehen zum Aktualisieren\".",
"domains": ["button"], "device_classes": [], "units": [], "liste": False, "pflicht": False,
"beispiel": "refresh_now",
"stichworte": ["refresh", "aktualisieren", "update", "abrufen", "poll"]},
{"key": "TUER_SENSOREN", "label": "Türen", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = zu. Für \"Sicher abgestellt\" wichtig.",
"domains": ["binary_sensor"], "device_classes": ["door"], "units": [], "liste": True, "positionen": POSITIONEN,
"pflicht": False, "beispiel": "front_left_door",
# Ein Beispielname je Position. "beispiel" allein beschreibt nur die
# erste; die anderen drei koennen "front_left_door" nie enthalten und
# standen deshalb ohne Erwartungswert da.
"beispiele": [
"front_left_door",
"front_right_door",
"rear_left_door",
"rear_right_door",
],
"stichworte": ["tür", "door"]},
{"key": "FENSTER_SENSOREN", "label": "Fenster", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = zu.",
"domains": ["binary_sensor"], "device_classes": ["window"], "units": [], "liste": True, "positionen": POSITIONEN,
"pflicht": False, "beispiel": "front_left_window",
"beispiele": [
"front_left_window",
"front_right_window",
"rear_left_window",
"rear_right_window",
],
"stichworte": ["fenster", "window"]},
{"key": "HECKKLAPPE_SENSOR", "label": "Heckklappe", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = zu.",
"domains": ["binary_sensor"], "device_classes": ["door", "opening"], "units": [], "liste": False, "pflicht": False,
"beispiel": "tailgate",
"stichworte": ["heckklappe", "tailgate", "kofferraum", "trunk"]},
{"key": "HAUBE_SENSOR", "label": "Motorhaube", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = zu.",
"domains": ["binary_sensor"], "device_classes": ["door", "opening"], "units": [], "liste": False, "pflicht": False,
"beispiel": "hood",
"stichworte": ["haube", "hood", "bonnet", "motorhaube"]},
{"key": "TUERSCHLOSS_SENSOR", "label": "Türschloss", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = verriegelt. Die Zentralverriegelung schließt alle Türen gemeinsam - "
"die Fahrertür genügt für \"Fahrzeug verriegelt\".",
"domains": ["binary_sensor"], "device_classes": ["lock"], "units": [], "liste": False,
"pflicht": False, "beispiel": "front_left_door_lock",
"stichworte": ["türschloss", "schloss", "lock", "verriegelt", "fahrertür"]},
{"key": "DACH_SENSOR", "label": "Schiebedach", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = zu.",
"domains": ["binary_sensor"], "device_classes": ["window", "door", "opening"], "units": [], "liste": False, "pflicht": False,
"beispiel": "sunroof",
"stichworte": ["dach", "schiebedach", "sunroof", "roof"]},
{"key": "LICHT_SENSOR", "label": "Standlicht", "gruppe": "sicherheit",
"hinweis": "\"aus\"/off = kein Licht.",
"domains": ["binary_sensor"], "device_classes": ["light"], "units": [], "liste": False, "pflicht": False,
"beispiel": "parking_lights",
"stichworte": ["licht", "standlicht", "light", "parking lights"]},
{"key": "NAECHSTER_OELWECHSEL_SENSOR", "label": "Nächster Ölwechsel", "gruppe": "uebersicht",
"hinweis": "Vom Fahrzeug selbst gemeldete Fälligkeit, ergänzt die App-eigene Servicebuch-Prognose.",
"domains": ["sensor"], "device_classes": ["date", "timestamp"], "units": [], "liste": False, "pflicht": False,
"beispiel": "oil_change_due",
"stichworte": ["ölwechsel", "oil", "service", "change"]},
{"key": "OELWECHSEL_STRECKE_SENSOR", "label": "Nächster Ölwechsel", "gruppe": "uebersicht",
"hinweis": "Meldet die Integration oft als negativen Countdown - die App zeigt nur den Betrag.",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km", "mi"], "liste": False, "pflicht": False,
"beispiel": "oil_change_distance",
"stichworte": ["ölwechsel", "oil", "distance", "strecke"]},
{"key": "NAECHSTE_INSPEKTION_SENSOR", "label": "Nächste Inspektion", "gruppe": "uebersicht",
"hinweis": "Vom Fahrzeug selbst gemeldete Fälligkeit.",
"domains": ["sensor"], "device_classes": ["date", "timestamp"], "units": [], "liste": False, "pflicht": False,
"beispiel": "inspection_due",
"stichworte": ["inspektion", "inspection", "service"]},
{"key": "INSPEKTION_STRECKE_SENSOR", "label": "Nächste Inspektion", "gruppe": "uebersicht",
"hinweis": "Meldet die Integration oft als negativen Countdown - die App zeigt nur den Betrag.",
"domains": ["sensor"], "device_classes": ["distance"], "units": ["km", "mi"], "liste": False, "pflicht": False,
"beispiel": "inspection_distance",
"stichworte": ["inspektion", "inspection", "distance", "strecke"]},
{"key": "STANDORT_LAT_SENSOR", "label": "GPS-Standort: Breitengrad", "gruppe": "standort",
"hinweis": "Breitengrad als eigener Sensor (z. B. flespi).",
"domains": ["sensor"], "device_classes": [], "units": [], "liste": False, "pflicht": False,
"beispiel": "latitude_coordinate_value",
"stichworte": ["breitengrad", "latitude", "lat", "standort", "gps", "position", "ortung"]},
{"key": "STANDORT_LON_SENSOR", "label": "GPS-Standort: Längengrad", "gruppe": "standort",
"hinweis": "Längengrad als eigener Sensor (z. B. flespi).",
"domains": ["sensor"], "device_classes": [], "units": [], "liste": False, "pflicht": False,
"beispiel": "longitude_coordinate_value",
"stichworte": ["längengrad", "longitude", "lon", "lng", "standort", "gps", "position", "ortung"]},
]
# In der pyscript-Fassung stand hier eine Liste TRIGGER_GEBUNDEN: die drei
# Felder ZUENDUNG/KM/TANK_SENSOR waren beim Modul-Laden einmalig in die
# @state_trigger-Registrierung eingebacken, eine Änderung wirkte deshalb erst
# nach einem HA-Neustart, und das Setup-Menü musste genau davor warnen.
# Diese Integration bindet ihre Zustandsbeobachter bei jeder Änderung neu
# (koordinator.trigger_neu_binden) - die Einschränkung, der Warnhinweis und
# der eigens dafür gebaute Neustart-Dienst sind damit ersatzlos weg.
for _feld in FELDER:
_feld["trigger_gebunden"] = False