Fahrtzeiten vom Geraet, Trip und Zuendung getrennt (2026.9.1.3)

Enthaelt 2026.9.1.2, das nie einzeln ausgeliefert wurde.

FAHRT- UND TANKZEITEN KOMMEN VOM GERAET

Fahrtbeginn war datetime.now() - die Uhrzeit, zu der Home Assistant den
Wechsel verarbeitet. Hat das Geraet gepuffert (Funkloch, Tiefgarage,
Tiefschlaf), liegt die Wahrheit beliebig weit davor. Gemessen: ein Datensatz
mit Fahrtende trug die Geraetezeit 07:29:04 und kam um 07:35:23 an.

Neue Sensorrolle MELDEZEIT_SENSOR (message_timestamp, Unix-Sekunden). Der
Wert ist UTC, keine Ortszeit - nachgemessen lag der Versatz zur UTC-Uhr von
HA bei Sekunden, nicht bei zwei Stunden; eine Zeitzonenumrechnung baute einen
Zwei-Stunden-Fehler ein.

geraetezeit() liegt in verlauf.py, weil Fahrt- und Tankerkennung denselben
Zeitstempel bestimmen muessen. tankerkennung._automatisch_anlegen() stempelte
bisher ebenfalls mit now(); ein aus einem gepufferten Datensatz erkannter
Tankvorgang trug damit die Uhrzeit des Auftauchens.

Die flespi-Integration setzt die Entitaeten eines Datensatzes nacheinander -
gemessen Zuendung 07:35:23.633, zugehoerige Meldezeit 2 ms spaeter. Wer
sofort liest, bekommt den vorigen Datensatz: beim Fahren zehn Sekunden
daneben, im Stand Stunden (On Stop Min Period = 43200 s). geraetezeit()
wartet deshalb bis zu 2 s auf den passenden Wert. Zwei Notbremsen, beide live
ausgeloest: Zeitstempel in der Zukunft und Ende vor dem Beginn.

TRIP UND ZUENDUNG SIND ZWEI ROLLEN

ZUENDUNG_SENSOR zeigte auf das Trip-Signal; im Setup stand "Zuendung/ACC" ueber
einer Trip-Entitaet und die echte Zuendung war nirgends zugeordnet. Neu:
TRIP_SENSOR loest Fahrten aus, ZUENDUNG_SENSOR liefert nur noch die Anzeige
"faehrt/steht". verlauf.fahrtsignal() ist die eine Stelle, die entscheidet -
Trip-Status wenn zugeordnet, sonst Zuendung, damit bestehende Installationen
unveraendert weiterlaufen und Livepfad und Rueckblick nicht auseinanderlaufen.

VERIFIZIERT in audi_ha_test, jeder Weg einzeln:
- Fahrtbeginn 08:30:03 (Geraetezeit) statt 08:38:27 (Ankunft)
- Fahrtende 08:04:40 -> 08:09:22 statt 08:11:04 -> 08:11:24
- Tankvorgang 08:18:45 statt 08:38:51 - 20 Minuten frueher
- Meldezeit in der Zukunft und Ende vor Beginn: Warnung, Ankunft genommen
- Zuendung schalten legt keine Fahrt mehr an, Trip-Signal schon

Der Livetest fing dabei einen NameError, den py_compile nicht sehen konnte:
der Import von geraetezeit in tankerkennung.py fehlte. Kompiliert ist nicht
verifiziert. Alle Testdatensaetze wurden ueber die eigenen Dienste wieder
entfernt; Bestand unveraendert 11 Fahrten, 13 Tankvorgaenge, keine offene Fahrt.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-01 10:40:21 +02:00
co-authored by Claude Opus 5
parent ff5a334e95
commit 4cc91c461b
8 changed files with 363 additions and 24 deletions
@@ -45,6 +45,7 @@ from typing import TYPE_CHECKING
from .ablage import neue_id
from .veroeffentlichung import zustand_oder_none
from .verlauf import geraetezeit
if TYPE_CHECKING:
from .koordinator import Koordinator
@@ -95,7 +96,9 @@ def leerer_tankvorgang(ts: str, quelle: str) -> dict:
}
async def fuellstand_geaendert(k: Koordinator, neu: str | None) -> None:
async def fuellstand_geaendert(
k: Koordinator, neu: str | None, ereigniszeit: datetime.datetime | None = None
) -> None:
aktuell = _als_zahl(neu)
if aktuell is None:
return
@@ -114,11 +117,15 @@ async def fuellstand_geaendert(k: Koordinator, neu: str | None) -> None:
# Fahrzeugprofil hinterlegte Tankvolumen. Beide gleichzeitig auswerten
# würde denselben Tankvorgang zweimal anlegen.
if not k.zuordnung.werte.TANK_LITER_SENSOR:
await _automatisch_anlegen(k, f"Füllstandsanstieg {round(anstieg, 1)} Prozentpunkte")
await _automatisch_anlegen(
k, f"Füllstandsanstieg {round(anstieg, 1)} Prozentpunkte", ereigniszeit=ereigniszeit
)
await k.tiefststand_setzen(aktuell)
async def tankvolumen_geaendert(k: Koordinator, neu: str | None) -> None:
async def tankvolumen_geaendert(
k: Koordinator, neu: str | None, ereigniszeit: datetime.datetime | None = None
) -> None:
"""Wie fuellstand_geaendert(), aber direkt in Litern statt in Prozent -
braucht deshalb keine Umrechnung über das Fahrzeugprofil und liefert mit
dem gemessenen Anstieg selbst eine sinnvolle Vorbelegung für die getankte
@@ -138,7 +145,10 @@ async def tankvolumen_geaendert(k: Koordinator, neu: str | None) -> None:
return
await _automatisch_anlegen(
k, f"Tankvolumenanstieg {round(anstieg, 1)} l", liter_schaetzung=anstieg
k,
f"Tankvolumenanstieg {round(anstieg, 1)} l",
liter_schaetzung=anstieg,
ereigniszeit=ereigniszeit,
)
await k.tiefststand_liter_setzen(aktuell)
@@ -155,12 +165,24 @@ async def distanz_seit_tankung(k: Koordinator, odometer_km: float | None) -> flo
async def _automatisch_anlegen(
k: Koordinator, anlass: str, liter_schaetzung: float | None = None
k: Koordinator,
anlass: str,
liter_schaetzung: float | None = None,
ereigniszeit: datetime.datetime | None = None,
) -> None:
odometer_km = _als_zahl(zustand_oder_none(k.hass, k.zuordnung.werte.KM_SENSOR))
tankvorgang = leerer_tankvorgang(
datetime.datetime.now(datetime.UTC).isoformat(), "auto"
# Der Zeitstempel des Geräts, nicht die Ankunft in Home Assistant -
# dieselbe Begründung wie bei Fahrtbeginn und -ende, siehe
# verlauf.geraetezeit(). Ein Tankvorgang, der aus einem gepufferten
# Datensatz erkannt wird, trug bisher die Uhrzeit des Auftauchens; bei
# 43200 s Aufzeichnungstakt im Stand konnten das Stunden sein.
ts = await geraetezeit(
k.hass,
k.zuordnung.werte.MELDEZEIT_SENSOR,
ereigniszeit,
datetime.datetime.now(datetime.UTC),
)
tankvorgang = leerer_tankvorgang(ts.isoformat(), "auto")
tankvorgang["odometer_km"] = odometer_km
tankvorgang["distance_km"] = await distanz_seit_tankung(k, odometer_km)
if liter_schaetzung is not None: