Das Ende einer verschlafenen Fahrt richtig bestimmen
Drei Fehler, die zusammen eine 5-Minuten-Fahrt als 15 Stunden mit 0,1 km/h im Bestand stehen liessen: - nach_neustart_fortsetzen() schloss eine laufende Fahrt bei ALLEM ausser "on" - auch bei Funkstille. Jetzt nur noch bei ausdruecklichem "off". - _letztes_lebenszeichen() las nur die Zuendungsentitaet, die nur bei einem Wechsel schreibt. Zusaetzliche Quelle ist die Meldezeit-Entitaet, deren Werte die Geraetezeit jedes Datensatzes sind. - Strecke und Dauer decken bei einer Luecke verschiedene Zeitraeume ab: der Durchschnitt entfaellt, und die Plausibilitaetspruefung rechnet ohne die verschlafenen Kilometer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -776,8 +776,21 @@ async def nach_neustart_fortsetzen(k: Koordinator) -> None:
|
||||
return
|
||||
|
||||
sensor = k.zuordnung.werte.ZUENDUNG_SENSOR
|
||||
if zustand_oder_none(k.hass, sensor) == "on":
|
||||
return # fährt noch - der Beobachter übernimmt wie sonst auch
|
||||
zustand = zustand_oder_none(k.hass, sensor)
|
||||
if zustand != "off":
|
||||
# "on" heisst: faehrt noch, der Beobachter uebernimmt wie sonst auch.
|
||||
#
|
||||
# Alles ANDERE - None, "unavailable", "unknown" - heisst Funkstille,
|
||||
# nicht "aus". Genau diese Regel steht in zuendung_geaendert() seit
|
||||
# jeher, hier fehlte sie: stand die Entitaet beim Hochfahren noch nicht
|
||||
# bereit (die flespi-Anbindung baut ihre MQTT-Verbindung erst auf),
|
||||
# las diese Funktion "nicht on" und schloss eine laufende Fahrt - mit
|
||||
# einem Ende aus _letztes_lebenszeichen(), im schlimmsten Fall "jetzt".
|
||||
#
|
||||
# Eine wirklich beendete Fahrt geht dadurch nicht verloren: kommt das
|
||||
# gepufferte "aus" spaeter an, schliesst der Beobachter sie, und
|
||||
# _spaetes_aus() faengt sogar den Fall ab, dass sie da schon zu ist.
|
||||
return
|
||||
|
||||
# Dieselbe Sperre wie im Beobachter: der erste Wechsel des Fahrtsignals
|
||||
# kann eintreffen, während dieser Nachlauf noch schreibt.
|
||||
@@ -822,11 +835,30 @@ async def _letztes_lebenszeichen(
|
||||
Kilometerstände ohnehin nach.
|
||||
"""
|
||||
jetzt = datetime.datetime.now(datetime.UTC)
|
||||
kandidaten: list[datetime.datetime] = []
|
||||
|
||||
punkte = await verlauf_lesen(
|
||||
k.hass, sensor, seit, jetzt, k.zuordnung.werte.MELDEZEIT_SENSOR
|
||||
)
|
||||
if punkte:
|
||||
return punkte[-1][0]
|
||||
kandidaten.append(punkte[-1][0])
|
||||
|
||||
# Die Zuendungsentitaet schreibt nur bei einem WECHSEL eine Zeile. Bleibt
|
||||
# die Zuendung stundenlang an, ist ihr letzter Verlaufspunkt der Beginn der
|
||||
# Fahrt - und ein Ende daraus waere die Fahrtdauer null.
|
||||
#
|
||||
# Am 06.09.2026 gemessen: die Fahrt vom Vorabend hatte seit 23:13:08 genau
|
||||
# drei Zeilen, alle aus derselben Sekunde, obwohl das Geraet danach noch
|
||||
# zwoelf Minuten lang Daten schickte.
|
||||
#
|
||||
# Die Meldezeit-Entitaet dagegen bekommt zu JEDEM Datensatz einen neuen
|
||||
# Wert - und dieser Wert IST die Geraetezeit. Sie ist damit die ehrlichste
|
||||
# Antwort auf "bis wann haben wir das Fahrzeug beobachtet".
|
||||
for zeitpunkt in await _meldezeiten(k, seit, jetzt):
|
||||
kandidaten.append(zeitpunkt)
|
||||
|
||||
if kandidaten:
|
||||
return max(kandidaten)
|
||||
|
||||
zustand = k.hass.states.get(sensor) if sensor else None
|
||||
if zustand is not None:
|
||||
@@ -848,6 +880,30 @@ async def _letztes_lebenszeichen(
|
||||
return jetzt
|
||||
|
||||
|
||||
async def _meldezeiten(
|
||||
k: Koordinator, seit: datetime.datetime, bis: datetime.datetime
|
||||
) -> list[datetime.datetime]:
|
||||
"""Die Geraetezeiten aller Datensaetze im Fenster, aufsteigend.
|
||||
|
||||
Die Werte der Meldezeit-Entitaet SIND Unix-Sekunden in UTC (siehe
|
||||
verlauf.geraetezeit()) - hier wird deshalb nicht umgestempelt, sondern der
|
||||
Wert selbst gelesen. Werte ausserhalb des Fensters fallen heraus: ein
|
||||
gepufferter Datensatz kann eine Geraetezeit vor dem Fahrtbeginn tragen."""
|
||||
sensor = k.zuordnung.werte.MELDEZEIT_SENSOR
|
||||
if not sensor:
|
||||
return []
|
||||
zeiten: list[datetime.datetime] = []
|
||||
for _ankunft, roh in await verlauf_lesen(k.hass, sensor, seit, bis):
|
||||
try:
|
||||
wert = datetime.datetime.fromtimestamp(float(roh), datetime.UTC)
|
||||
except (TypeError, ValueError, OSError, OverflowError):
|
||||
continue
|
||||
if seit <= wert <= bis:
|
||||
zeiten.append(wert)
|
||||
zeiten.sort()
|
||||
return zeiten
|
||||
|
||||
|
||||
async def fahrt_beenden(
|
||||
k: Koordinator,
|
||||
start_ts: datetime.datetime,
|
||||
|
||||
Reference in New Issue
Block a user