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 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 10:40:21 +02:00
parent ff5a334e95
commit 4cc91c461b
8 changed files with 363 additions and 24 deletions
@@ -60,6 +60,34 @@ class Sensorzuordnung:
# Kilometerstand der EU-Data-Act-Integration ist der richtige.
KM_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 = ""
@@ -157,8 +185,13 @@ SCHLUESSEL: set[str] = set(STANDARDWERTE)
# "Nur passende Sensoren anzeigen") - eine fehlende oder leere Liste bedeutet
# "keine Einschränkung" bzw. "diese Rolle hat üblicherweise keine Einheit".
FELDER: list[dict] = [
{"key": "ZUENDUNG_SENSOR", "label": "Zündung/ACC-Status", "gruppe": "fahrterkennung",
"hinweis": "on = Fahrt läuft, erkennt Fahrtbeginn/-ende.",
{"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"]},
@@ -167,6 +200,11 @@ FELDER: list[dict] = [
"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,
@@ -26,7 +26,13 @@ from typing import TYPE_CHECKING
from . import screening
from .ablage import neue_id
from .veroeffentlichung import zustand_oder_none
from .verlauf import MINDESTDAUER_S, verlauf_lesen, zeit_normalisiert
from .verlauf import (
MINDESTDAUER_S,
fahrtsignal,
geraetezeit,
verlauf_lesen,
zeit_normalisiert,
)
if TYPE_CHECKING:
from .koordinator import Koordinator
@@ -77,7 +83,23 @@ def leere_fahrt(start_ts: datetime.datetime, ende_ts: datetime.datetime, quelle:
async def zuendung_geaendert(k: Koordinator, neu: str | None, alt: str | None) -> None:
async def _geraetezeit(
k: Koordinator,
ereigniszeit: datetime.datetime | None,
standard: datetime.datetime,
) -> datetime.datetime:
"""Die Meldezeit des Geräts - siehe verlauf.geraetezeit()."""
return await geraetezeit(
k.hass, k.zuordnung.werte.MELDEZEIT_SENSOR, ereigniszeit, standard
)
async def zuendung_geaendert(
k: Koordinator,
neu: str | None,
alt: str | None,
ereigniszeit: datetime.datetime | None = None,
) -> None:
"""Reagiert auf jede Zustandsänderung der Zündungs-Entität.
"unavailable"/"unknown" ist kein Zündungszustand, sondern Funkstille des
@@ -119,9 +141,16 @@ async def zuendung_geaendert(k: Koordinator, neu: str | None, alt: str | None) -
# ganze Zeit lief.
fahrt_laeuft = k.fahrt_start_ts is not None
jetzt = datetime.datetime.now(datetime.UTC)
if an_jetzt and not fahrt_laeuft:
await k.fahrt_start_setzen(datetime.datetime.now(datetime.UTC))
_LOGGER.info("Fahrt gestartet um %s", k.fahrt_start_ts)
beginn = await _geraetezeit(k, ereigniszeit, jetzt)
await k.fahrt_start_setzen(beginn)
_LOGGER.info(
"Fahrt gestartet um %s (%s)",
k.fahrt_start_ts,
"Gerätezeit" if beginn != jetzt else "Ankunft in HA",
)
# Die Anzeige "fährt/steht" hängt an derselben Entität - sofort neu
# veröffentlichen, statt bis zum nächsten 20-Sekunden-Takt zu warten.
await k.fahrzeugstatus_veroeffentlichen()
@@ -134,7 +163,17 @@ async def zuendung_geaendert(k: Koordinator, neu: str | None, alt: str | None) -
# zu warten haette daraus eine halbe Stunde gemacht und zwei
# aufeinanderfolgende Fahrten verschmolzen, die das Geraet sauber
# getrennt hatte.
await fahrt_beenden(k, k.fahrt_start_ts, datetime.datetime.now(datetime.UTC))
ende = await _geraetezeit(k, ereigniszeit, jetzt)
# Ein Ende vor dem Beginn kann es nicht geben. Passiert, wenn die
# Meldezeit aus einem älteren Datensatz stammt als der Fahrtbeginn -
# dann ist die Ankunftszeit die ehrlichere Angabe.
if ende < k.fahrt_start_ts:
_LOGGER.warning(
"Meldezeit %s liegt vor dem Fahrtbeginn %s - nehme die Ankunft",
ende.isoformat(), k.fahrt_start_ts.isoformat(),
)
ende = jetzt
await fahrt_beenden(k, k.fahrt_start_ts, ende)
await k.fahrzeugstatus_veroeffentlichen()
@@ -157,7 +196,7 @@ async def nach_neustart_fortsetzen(k: Koordinator) -> None:
if k.fahrt_start_ts is None:
return
sensor = k.zuordnung.werte.ZUENDUNG_SENSOR
sensor = fahrtsignal(k.zuordnung.werte)
if zustand_oder_none(k.hass, sensor) == "on":
return # fährt noch - der Beobachter übernimmt wie sonst auch
@@ -47,6 +47,7 @@ from .tankerkennung import LITER_SCHWELLE, leerer_tankvorgang, schwelle_prozent
from .verlauf import (
MINDESTDAUER_S,
durchschnitt_kmh,
fahrtsignal,
hoechstwert_im_fenster,
route_aus_verlauf,
UNPLAUSIBLE_KMH,
@@ -440,7 +441,7 @@ async def importieren(k: Koordinator, start: object, ende: object) -> None:
werte = k.zuordnung.werte
verlaeufe = {
"zuendung": await verlauf_lesen(k.hass, werte.ZUENDUNG_SENSOR, von, bis),
"zuendung": await verlauf_lesen(k.hass, fahrtsignal(werte), von, bis),
"km": await verlauf_lesen(k.hass, werte.KM_SENSOR, von, bis),
"tank": await verlauf_lesen(k.hass, werte.TANK_SENSOR, von, bis),
"tank_liter": await verlauf_lesen(k.hass, werte.TANK_LITER_SENSOR, von, bis),
@@ -47,7 +47,16 @@ from homeassistant.helpers.event import (
from homeassistant.helpers.start import async_at_started
from homeassistant.helpers.storage import Store
from . import batterie, fahrterkennung, identitaet, reifen, screening, sicherung, tankerkennung
from . import (
batterie,
fahrterkennung,
identitaet,
reifen,
screening,
sicherung,
tankerkennung,
verlauf,
)
from .ablage import Ablage
from .const import (
BUENDEL_INFO,
@@ -207,7 +216,13 @@ class Koordinator:
self._beobachter.clear()
werte = self.zuordnung.werte
self._beobachten(werte.ZUENDUNG_SENSOR, self._zuendung_geaendert)
# Fahrten löst der Trip-Status aus (oder die Zündung, wenn er fehlt).
self._beobachten(verlauf.fahrtsignal(werte), self._fahrtsignal_geaendert)
# Die Zündung selbst nur noch für die Anzeige "fährt/steht" - sofort
# neu veröffentlichen, statt bis zum nächsten 20-Sekunden-Takt zu
# warten. Entfällt, wenn sie ohnehin schon das Fahrtsignal ist.
if werte.ZUENDUNG_SENSOR != verlauf.fahrtsignal(werte):
self._beobachten(werte.ZUENDUNG_SENSOR, self._zuendung_angezeigt)
self._beobachten(werte.KM_SENSOR, self._kilometerstand_geaendert)
self._beobachten(werte.TANK_SENSOR, self._tankfuellstand_geaendert)
self._beobachten(werte.TANK_LITER_SENSOR, self._tankvolumen_geaendert)
@@ -227,9 +242,18 @@ class Koordinator:
neu = ereignis.data["new_state"]
return (neu.state if neu else None, alt.state if alt else None)
async def _zuendung_geaendert(self, ereignis: Event[EventStateChangedData]) -> None:
async def _zuendung_angezeigt(self, _ereignis: Event[EventStateChangedData]) -> None:
await self.fahrzeugstatus_veroeffentlichen()
async def _fahrtsignal_geaendert(self, ereignis: Event[EventStateChangedData]) -> None:
neu, alt = self._zustaende(ereignis)
await fahrterkennung.zuendung_geaendert(self, neu, alt)
# Der Zeitpunkt, zu dem Home Assistant diesen Wechsel gesehen hat -
# nicht "jetzt". Die Fahrterkennung braucht ihn, um die Meldezeit des
# Geräts demselben Datensatz zuzuordnen (siehe _geraetezeit dort).
zustand = ereignis.data["new_state"]
await fahrterkennung.zuendung_geaendert(
self, neu, alt, zustand.last_updated if zustand else None
)
async def _kilometerstand_geaendert(self, ereignis: Event[EventStateChangedData]) -> None:
neu, alt = self._zustaende(ereignis)
@@ -240,11 +264,17 @@ class Koordinator:
async def _tankfuellstand_geaendert(self, ereignis: Event[EventStateChangedData]) -> None:
neu, _alt = self._zustaende(ereignis)
await tankerkennung.fuellstand_geaendert(self, neu)
zustand = ereignis.data["new_state"]
await tankerkennung.fuellstand_geaendert(
self, neu, zustand.last_updated if zustand else None
)
async def _tankvolumen_geaendert(self, ereignis: Event[EventStateChangedData]) -> None:
neu, _alt = self._zustaende(ereignis)
await tankerkennung.tankvolumen_geaendert(self, neu)
zustand = ereignis.data["new_state"]
await tankerkennung.tankvolumen_geaendert(
self, neu, zustand.last_updated if zustand else None
)
# ------------------------------------------------- Laufende Fahrt merken
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.1.1",
"version": "2026.9.1.3",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": ["@paul"],
@@ -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:
@@ -29,6 +29,8 @@ from __future__ import annotations
import datetime
import logging
import asyncio
from homeassistant.components.recorder import get_instance, history
from homeassistant.core import HomeAssistant
@@ -78,6 +80,19 @@ async def verlauf_lesen(
return []
def fahrtsignal(werte: object) -> str:
"""Die Entität, die Fahrtbeginn und -ende auslöst.
Der Trip-Status des Geräts, wenn er zugeordnet ist - sonst die Zündung,
wie vor der Trennung der beiden Rollen. Damit laufen bestehende
Installationen unverändert weiter, ohne dass jemand etwas zuordnen muss.
Eine gemeinsame Stelle, weil sonst Livepfad (fahrterkennung.py) und
Rückblick (historienimport.py) auseinanderlaufen könnten - dieselbe Regel
wie bei UNPLAUSIBLE_KMH und MINDESTDAUER_S."""
return getattr(werte, "TRIP_SENSOR", "") or getattr(werte, "ZUENDUNG_SENSOR", "")
def zeit_normalisiert(wert: object) -> datetime.datetime | None:
"""Akzeptiert ISO-Zeit mit oder ohne Zeitzone und normalisiert auf UTC.
@@ -139,6 +154,75 @@ def durchschnitt_kmh(distanz_km: float | None, dauer_s: float | None) -> float |
return round(distanz_km / (dauer_s / 3600.0), 1)
# Wie lange auf die Meldezeit desselben Datensatzes gewartet wird.
#
# Die flespi-Integration setzt die Entitäten eines Datensatzes nacheinander:
# am 01.09.2026 gemessen stand die Zündung um 07:35:23.633 und die zugehörige
# Meldezeit 2 ms später um 07:35:23.635. Ohne diese kurze Wartezeit läse man
# den Zeitstempel des VORIGEN Datensatzes - beim Fahren zehn Sekunden daneben,
# im Stand aber möglicherweise Stunden, weil das Gerät dort nur alle paar
# Stunden einen Satz schreibt (Data Acquisition, On Stop: 43200 s).
MELDEZEIT_FRIST_S = 2.0
MELDEZEIT_TAKT_S = 0.05
# Wie weit die Geräteuhr der von Home Assistant vorauslaufen darf, bevor der
# Zeitstempel verworfen wird. Etwas Luft für Drift, aber keine Stunden - eine
# falsch gestellte Geräteuhr darf die Auswertung nicht mitreißen.
MELDEZEIT_VORLAUF = datetime.timedelta(minutes=5)
async def geraetezeit(
hass: HomeAssistant,
sensor: str | None,
ereigniszeit: datetime.datetime | None,
standard: datetime.datetime,
) -> datetime.datetime:
"""Der Zeitstempel, den das Gerät seinem Datensatz mitgegeben hat - oder
`standard`, wenn er fehlt oder unglaubwürdig ist.
Warum das nötig ist: `standard` ist die Uhrzeit, zu der Home Assistant den
Zustandswechsel verarbeitet hat. Hat das Gerät gepuffert - Funkloch,
Tiefgarage, Tiefschlaf -, liegt die Wahrheit beliebig weit davor. 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.
Der Wert ist eine Unix-Sekunde in UTC, nicht Ortszeit - ebenfalls
nachgemessen: der Versatz zur UTC-Uhr von Home Assistant lag bei Sekunden,
nicht bei zwei Stunden. Eine Zeitzonenumrechnung wäre nicht nur unnötig,
sie baute einen Zwei-Stunden-Fehler ein.
Gemeinsam für Fahrterkennung und Tankerkennung: beide legen Datensätze mit
einem Zeitstempel an und dürfen ihn nie unterschiedlich bestimmen - dieselbe
Regel wie bei UNPLAUSIBLE_KMH und MINDESTDAUER_S."""
if not sensor:
return standard
if ereigniszeit is not None:
wartezeit = 0.0
while wartezeit < MELDEZEIT_FRIST_S:
zustand = hass.states.get(sensor)
if zustand is not None and zustand.last_updated >= ereigniszeit:
break
await asyncio.sleep(MELDEZEIT_TAKT_S)
wartezeit += MELDEZEIT_TAKT_S
zustand = hass.states.get(sensor)
try:
gemeldet = datetime.datetime.fromtimestamp(
float(zustand.state), datetime.UTC # type: ignore[union-attr]
)
except (AttributeError, TypeError, ValueError, OSError, OverflowError):
return standard
if gemeldet > standard + MELDEZEIT_VORLAUF:
_LOGGER.warning(
"Meldezeit %s liegt in der Zukunft (jetzt %s) - verworfen",
gemeldet.isoformat(), standard.isoformat(),
)
return standard
return gemeldet
def wert_bei(
verlauf: list[Verlaufspunkt], zeitpunkt: datetime.datetime
) -> float | None: