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:
2026-09-03 21:15:42 +02:00
parent 2736c3d6e0
commit 6ec31b5275
8 changed files with 460 additions and 66 deletions
@@ -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(