Audit, zweite Runde: der Knopf schrieb ein erfundenes Ende (2026.9.3.14)
_letztes_lebenszeichen() fiel auf "jetzt" zurueck, sobald verlauf_lesen() eine leere Liste lieferte - und die bedeutet sowohl "nichts aufgezeichnet" als auch "nicht lesbar". Am 03.09. trat der zweite Fall ein: der Knopf traf einen Neustart, der recorder war noch nicht bereit, die Abfrage lief 2:47 und kam leer zurueck. Die Fahrt bekam 18:43 statt 17:59 als Ende, 74 Minuten statt 30. Drei Quellen in fester Reihenfolge statt einer: Aufzeichnung, dann hass.states (last_reported/last_updated - weiss dasselbe ohne Datenbank), dann "jetzt", und das nur noch mit Warnung. Vier Regressionstests, drei davon gegen den alten Stand nachweislich rot. Dazu aus der Bestandspruefung: odometer_km liegt in drei alten Tankvorgaengen als Zeichenkette vor, ablage.distanz_seit_letzter_tankung() rechnete damit float - str. Laeuft jetzt durch als_kilometerstand(). Der Eingang war schon zu; das ist Altbestand. Und acht Dienste hatten keinen Klarnamen in translations/de.json - jetzt 28 Dienste, 28 Klarnamen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -702,16 +702,50 @@ async def _nachtraeglich_schliessen(k: Koordinator, sensor: str) -> None:
|
||||
async def _letztes_lebenszeichen(
|
||||
k: Koordinator, sensor: str, seit: datetime.datetime
|
||||
) -> datetime.datetime:
|
||||
"""Der zuletzt aufgezeichnete Zeitpunkt der Zündungs-Entität seit `seit`.
|
||||
"""Der Zeitpunkt, zu dem die Fahrtsignal-Entität zuletzt etwas gemeldet hat.
|
||||
|
||||
Das ist der ehrlichste verfügbare Endzeitpunkt: bis dahin wissen wir, dass
|
||||
das Fahrzeug beobachtet wurde. "Jetzt" zu nehmen wäre falsch, wenn Home
|
||||
Assistant tagelang unten war - daraus würde eine Fahrt über Tage. Gibt es
|
||||
gar keine Aufzeichnung (Sensor nicht zugeordnet, recorder zu kurz), bleibt
|
||||
nur "jetzt"; das Screening trägt die Kilometerstände ohnehin nach."""
|
||||
Assistant tagelang unten war - daraus würde eine Fahrt über Tage.
|
||||
|
||||
DREI QUELLEN, IN DIESER REIHENFOLGE - und die zweite gibt es, weil die
|
||||
erste am 03.09.2026 eine falsche Fahrt erzeugt hat:
|
||||
|
||||
1. Die Aufzeichnung. Der genaueste Wert, wenn der recorder antwortet.
|
||||
2. Der Zustandsspeicher. `verlauf_lesen()` liefert eine leere Liste sowohl
|
||||
für "nichts aufgezeichnet" als auch für "nicht lesbar" - und der zweite
|
||||
Fall trat real ein, als der Knopf genau in einen Neustart hinein
|
||||
gedrückt wurde: der recorder war noch nicht bereit, die Abfrage lief
|
||||
2:47 und kam leer zurück, und die Fahrt bekam "jetzt" als Ende - 74
|
||||
Minuten statt 30. `hass.states` weiss dasselbe ohne Datenbank und ist
|
||||
sofort da.
|
||||
3. "Jetzt", aber nicht mehr stillschweigend: wenn beide Quellen nichts
|
||||
hergeben, ist das eine Meldung wert. Das Screening trägt die
|
||||
Kilometerstände ohnehin nach.
|
||||
"""
|
||||
jetzt = datetime.datetime.now(datetime.UTC)
|
||||
punkte = await verlauf_lesen(k.hass, sensor, seit, jetzt)
|
||||
return punkte[-1][0] if punkte else jetzt
|
||||
if punkte:
|
||||
return punkte[-1][0]
|
||||
|
||||
zustand = k.hass.states.get(sensor) if sensor else None
|
||||
if zustand is not None:
|
||||
# last_reported zählt jede Meldung, last_updated nur einen Wechsel -
|
||||
# für "wann haben wir zuletzt etwas gehört" ist das erste genauer.
|
||||
gemeldet = getattr(zustand, "last_reported", None) or zustand.last_updated
|
||||
if gemeldet is not None and gemeldet >= seit:
|
||||
_LOGGER.info(
|
||||
"Verlauf von %s nicht verfügbar - Ende aus dem Zustandsspeicher: %s",
|
||||
sensor, gemeldet.isoformat(),
|
||||
)
|
||||
return gemeldet
|
||||
|
||||
_LOGGER.warning(
|
||||
"Kein Lebenszeichen von %s seit %s - die Fahrt endet auf \"jetzt\", "
|
||||
"was zu lang sein kann",
|
||||
sensor, seit.isoformat(),
|
||||
)
|
||||
return jetzt
|
||||
|
||||
|
||||
async def fahrt_beenden(
|
||||
|
||||
Reference in New Issue
Block a user