Parkbeginn nach einem Neustart aus der Aufzeichnung holen (2026.9.1.7)
Der Zuhoerer bekommt den Zuendungswechsel nur mit, wenn Home Assistant laeuft. Ging die Zuendung aus, waehrend es unten war - ueber Nacht der Normalfall -, feuerte nichts, und die Registrierung beim Hochfahren wird bewusst ignoriert (sonst begaenne die Parkdauer bei jedem Neustart neu). Ergebnis war "Parkdauer unbekannt", obwohl der Zeitpunkt im recorder steht. _parkbeginn_nachholen() laeuft einmal in _nach_start und schlaegt ihn nach - nur, wenn kein Wert gespeichert ist und die Zuendung auf "aus" steht. Genommen wird der letzte echte Wechsel an -> aus im Fenster von sieben Tagen; gibt es keinen, bleibt es bei "unbekannt" statt den Rand des Fensters zu erfinden. Wieder mit der Meldezeit desselben Datensatzes statt seiner Ankunftszeit - dieselbe Begruendung wie in verlauf.geraetezeit(), hier rueckblickend. VERIFIZIERT in audi_ha_test, der Test trennt genau die beiden Zeiten: Speicher geleert, neu gestartet -> "Parkbeginn aus der Aufzeichnung nachgeholt: 09:04:57". Das ist die Geraetezeit; angekommen war der Datensatz um 09:44. Ein zweiter Neustart ruehrt den Wert nicht an, schreibt keine Zeile und erzeugt keinen Fehler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# AGENTS.md — Project state, review findings, open items, and working rules
|
||||
|
||||
**Last updated: 2026-09-01** („Geparkt seit" kommt von der Zündung, Manifest `2026.9.1.6`,
|
||||
**Last updated: 2026-09-01** („Geparkt seit" kommt von der Zündung und wird nach einem Neustart
|
||||
aus der Aufzeichnung nachgeholt, Manifest `2026.9.1.7`,
|
||||
Abschnitt BB. Davor: GNSS-Streckenrollen vorbereitet, `2026.9.1.5`, Abschnitt BA.
|
||||
Davor: Fahrtende ohne den 900-s-Nachlauf des Geräts, Manifest `2026.9.1.4`,
|
||||
siehe Abschnitt AZ. Davor am selben Tag: Fahrtbeginn und -ende kommen vom Gerät (`MELDEZEIT_SENSOR`),
|
||||
@@ -6629,6 +6630,24 @@ einer Gerätezeit von vor 40 Minuten → `09:04:57` statt `jetzt`.
|
||||
Companion-App gleichgezogen (Paritätsregel): `profilAdapter` um `geparktSeit` erweitert,
|
||||
`standortZustand()` braucht die Fahrtenliste nicht mehr. `tsc --noEmit` sauber, 165/165 Tests grün.
|
||||
|
||||
### Nachtrag (2026.9.1.7): der Parkbeginn überlebt jetzt auch einen Neustart über Nacht
|
||||
|
||||
Der Zuhörer bekommt den Wechsel nur mit, wenn Home Assistant läuft. Ging die Zündung aus, während
|
||||
es unten war — über Nacht der Normalfall —, feuerte nichts, und die Registrierung beim Hochfahren
|
||||
wird ja bewusst ignoriert. Ergebnis: „Parkdauer unbekannt", obwohl der Zeitpunkt im recorder steht.
|
||||
|
||||
`_parkbeginn_nachholen()` läuft einmal in `_nach_start` und schlägt ihn dort nach — **nur**, wenn
|
||||
kein Wert gespeichert ist und die Zündung gerade auf „aus" steht. Genommen wird der letzte echte
|
||||
Wechsel **an → aus** im Fenster von sieben Tagen; gibt es keinen, bleibt es bei „unbekannt", statt
|
||||
den Rand des Fensters zu erfinden.
|
||||
|
||||
Und wieder mit der **Meldezeit desselben Datensatzes**, nicht mit seiner Ankunftszeit — dieselbe
|
||||
Begründung wie in `geraetezeit()`, hier nur rückblickend statt live.
|
||||
|
||||
Live nachgewiesen, und der Test trennt genau die beiden Zeiten: Speicher geleert, neu gestartet →
|
||||
`Parkbeginn aus der Aufzeichnung nachgeholt: 09:04:57`. Das ist die Gerätezeit; angekommen war der
|
||||
Datensatz um 09:44. Ein zweiter Neustart rührt den Wert nicht an und schreibt keine Zeile.
|
||||
|
||||
### Genauigkeit hängt am Operanden des Geräts
|
||||
|
||||
Der Zeitpunkt ist nur so genau, wie das Gerät ihn meldet. Stand der Operand des
|
||||
|
||||
@@ -76,11 +76,26 @@ from .const import (
|
||||
E_ZUORDNUNG,
|
||||
SIGNAL_AKTUALISIERT,
|
||||
)
|
||||
from .veroeffentlichung import fahrzeugstatus, spannung_cache_vorladen
|
||||
from .veroeffentlichung import (
|
||||
fahrzeugstatus,
|
||||
spannung_cache_vorladen,
|
||||
zustand_oder_none,
|
||||
)
|
||||
from .zuordnung import Zuordnung
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Wie weit zurück nach dem letzten Zündungswechsel gesucht wird, wenn der
|
||||
# Parkbeginn nach einem Neustart fehlt. Sieben Tage decken jede realistische
|
||||
# Standzeit ab und bleiben eine Abfrage, die der recorder in einem Rutsch
|
||||
# beantwortet; weiter zurück reicht seine Aufbewahrung ohnehin selten.
|
||||
PARKBEGINN_FENSTER = datetime.timedelta(days=7)
|
||||
|
||||
# Wie weit um den Zündungswechsel herum nach der zugehörigen Meldezeit gesucht
|
||||
# wird. Beide stammen aus demselben Datensatz und liegen deshalb
|
||||
# Millisekunden auseinander (gemessen: 2 ms) - eine Minute ist reichlich.
|
||||
MELDEZEIT_FENSTER = datetime.timedelta(minutes=1)
|
||||
|
||||
# Wie oft der Live-Fahrzeugstatus neu aus den Quellsensoren gelesen wird.
|
||||
TAKT_STATUS = datetime.timedelta(seconds=20)
|
||||
# Profil/Fahrten/Tankvorgänge ändern sich normalerweise nur durch Aktionen,
|
||||
@@ -160,6 +175,7 @@ class Koordinator:
|
||||
|
||||
async def _nach_start(self, _hass: HomeAssistant) -> None:
|
||||
await fahrterkennung.nach_neustart_fortsetzen(self)
|
||||
await self._parkbeginn_nachholen()
|
||||
# Erst hier (nicht in starten()): das Geräte-/Entity-Register der
|
||||
# zugeordneten Sensoren ist vor async_at_started oft noch nicht
|
||||
# vollständig - siehe die Begründung von trigger_neu_binden() oben.
|
||||
@@ -349,6 +365,65 @@ class Koordinator:
|
||||
self.fahrt_start_ts = ts
|
||||
await self._laufzeit_sichern()
|
||||
|
||||
async def _parkbeginn_nachholen(self) -> None:
|
||||
"""Holt den Parkbeginn aus der Aufzeichnung, wenn er fehlt.
|
||||
|
||||
Der Zuhörer (_zuendung_angezeigt) bekommt den Wechsel nur mit, wenn Home
|
||||
Assistant läuft. Ging die Zündung aus, während es unten war - über Nacht
|
||||
der Normalfall -, feuert nichts; die Registrierung der Entität beim
|
||||
Hochfahren wird bewusst ignoriert, sonst begänne die Parkdauer bei jedem
|
||||
Neustart neu. Der Zeitpunkt steht aber im recorder, und dort wird er hier
|
||||
einmalig nachgeschlagen.
|
||||
|
||||
Nur wenn WIRKLICH ein Wechsel an -> aus im Fenster liegt. Steht die
|
||||
Zündung schon die ganze Zeit auf aus, wissen wir den Beginn nicht - dann
|
||||
bleibt es bei \"unbekannt\", statt den Rand des Fensters zu erfinden. Eine
|
||||
falsche Zahl ist schlechter als keine (Ansage des Eigentümers,
|
||||
2026-09-01)."""
|
||||
if self.geparkt_seit_ts is not None:
|
||||
return
|
||||
sensor = self.zuordnung.werte.ZUENDUNG_SENSOR
|
||||
if not sensor or zustand_oder_none(self.hass, sensor) != "off":
|
||||
return
|
||||
|
||||
jetzt = datetime.datetime.now(datetime.UTC)
|
||||
punkte = await verlauf.verlauf_lesen(
|
||||
self.hass, sensor, jetzt - PARKBEGINN_FENSTER, jetzt
|
||||
)
|
||||
# verlauf_lesen() filtert unknown/unavailable schon heraus - eine
|
||||
# Funklücke trennt die Reihe also nicht in zwei Wechsel auf.
|
||||
wechsel: datetime.datetime | None = None
|
||||
vorher: str | None = None
|
||||
for ts, wert in punkte:
|
||||
if wert == "off" and vorher == "on":
|
||||
wechsel = ts
|
||||
vorher = wert
|
||||
if wechsel is None:
|
||||
return
|
||||
|
||||
# Die Ankunftszeit des Datensatzes ist nicht seine Entstehungszeit. Die
|
||||
# Meldezeit desselben Datensatzes ist es - dieselbe Begründung wie in
|
||||
# verlauf.geraetezeit(), hier nur rückblickend statt live.
|
||||
seit = wechsel
|
||||
meldesensor = self.zuordnung.werte.MELDEZEIT_SENSOR
|
||||
if meldesensor:
|
||||
reihe = await verlauf.verlauf_lesen(
|
||||
self.hass, meldesensor, wechsel - MELDEZEIT_FENSTER, wechsel + MELDEZEIT_FENSTER
|
||||
)
|
||||
epoche = verlauf.naechster_wert(wechsel, reihe)
|
||||
if epoche is not None:
|
||||
try:
|
||||
gemeldet = datetime.datetime.fromtimestamp(epoche, datetime.UTC)
|
||||
except (ValueError, OSError, OverflowError):
|
||||
gemeldet = None
|
||||
if gemeldet is not None and gemeldet <= jetzt:
|
||||
seit = gemeldet
|
||||
|
||||
await self.geparkt_seit_setzen(seit)
|
||||
_LOGGER.info(
|
||||
"Parkbeginn aus der Aufzeichnung nachgeholt: %s", seit.isoformat()
|
||||
)
|
||||
|
||||
async def geparkt_seit_setzen(self, ts: datetime.datetime | None) -> None:
|
||||
self.geparkt_seit_ts = ts
|
||||
await self._laufzeit_sichern()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.9.1.6",
|
||||
"version": "2026.9.1.7",
|
||||
"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"],
|
||||
|
||||
Reference in New Issue
Block a user