Ortsnamen aus dem Backend, Verbrauchsfaktor aus dem Tankbeleg, Radzaehler ueber Fahrten

Ortsauflösung serverseitig (geokodierung.py): der Ort steht jetzt in der Fahrt
statt im Zwischenspeicher jedes Geräts. Beide Oberflächen fragen nichts mehr ab.

Verbrauchskorrektur (verbrauchskorrektur.py): Faktor = Beleg-Liter / Summe der
Einzelfahrten, voll zu voll. Statt einer Schranke am Faktor wird der Nenner
geprüft - decken die erkannten Fahrten 90-110 % der Tacho-Spanne ab? Ausgelöst
beim Schreiben eines Tankvorgangs, nicht erst bei der nächsten Fahrt.

Radzähler: summiert die Fahrten des montierten Satzes statt Tacho-Deltas. Der
alte Weg hatte Fahrzeugwechsel als gefahrene Strecke verbucht (375.962 km bei
einem Tacho von 61.823).

Null heißt unbekannt: ein Kilometerstand von 0 oder ein leeres Feld werden zu
None normalisiert, statt an sechs Lesestellen als "Tachostand null" zu gelten.
Momentanwerte der Sensoren gelten nur für einen Tankvorgang von jetzt.

Oberfläche, beide Codebasen: Wertespalte der Fahrtenliste ausgerichtet, Ort ->
Ort in "Zuletzt", "Räder" statt "Reifen", "Termin vereinbart" entfernt,
Markenlogo 20 % größer, Übersicht-Symbol auf volle Rasterbreite.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-02 11:28:37 +02:00
co-authored by Claude Opus 5
parent 78a8017e95
commit 2af5c469d6
27 changed files with 1575 additions and 352 deletions
@@ -53,11 +53,13 @@ from . import (
identitaet,
reifen,
screening,
verbrauchskorrektur,
sicherung,
tankerkennung,
verlauf,
)
from .ablage import Ablage
from .geokodierung import Geokodierung
from .const import (
BUENDEL_INFO,
BUENDEL_ORDNER,
@@ -116,6 +118,9 @@ class Koordinator:
self.version = version
self.ablage = Ablage(hass, hass.config.path("audi_dashboard"))
self.zuordnung = Zuordnung(self.ablage)
# Ortsnamen werden einmal hier aufgeloest und mit der Fahrt
# gespeichert, statt in jedem Geraet erneut - siehe geokodierung.py.
self.geokodierung = Geokodierung(hass, self.ablage.basis)
# Was die Entitäten anzeigen: Schlüssel -> (Zustand, Nutzlast).
self.werte: dict[str, tuple[str, dict | list | None]] = {}
@@ -159,10 +164,10 @@ class Koordinator:
# Veröffentlichung den vorgeladenen Wert trägt.
spannung_cache_vorladen(await self.ablage.batterieverlauf_lesen())
await self.alles_veroeffentlichen()
# Der Reifenzähler schreibt beim Start einmal fort: Kilometer, die
# während eines HA-Ausfalls gefahren wurden, gehören noch dem damals
# aktiven Satz (siehe reifen.py).
await reifen.fortschreiben(self, reifen.aktueller_km(self))
# Der Radzähler hängt an den Fahrten, nicht am Kilometerstand - was
# während eines Ausfalls gefahren wurde, kommt über die dabei
# erkannten Fahrten herein (siehe reifen.py).
await reifen.nachtragen(self)
self._takte_anmelden()
await self.trigger_neu_binden()
# Erst wenn Home Assistant vollständig oben ist: bis dahin haben die
@@ -312,7 +317,8 @@ class Koordinator:
neu, alt = self._zustaende(ereignis)
if neu is None or alt is None:
return
await reifen.fortschreiben(self, reifen.aktueller_km(self))
# Der Radzaehler haengt nicht mehr am Kilometerstand, sondern an den
# Fahrten - das Screening rechnet ihn am Ende selbst nach.
await screening.durchfuehren(self)
async def _tankfuellstand_geaendert(self, ereignis: Event[EventStateChangedData]) -> None:
@@ -477,6 +483,27 @@ class Koordinator:
async def tankvorgaenge_veroeffentlichen(self) -> None:
self.setzen(E_TANKVORGAENGE, "aktuell", await self.ablage.tankvorgaenge_lesen())
async def tankvorgang_nachbereiten(self) -> None:
"""Nach jedem Schreiben eines Tankvorgangs: veroeffentlichen und den
Verbrauchsfaktor rechnen lassen.
Eine Betankung ist der Moment, in dem sich ein Intervall SCHLIESST -
vorher gibt es kein Paar aus zwei Tankvorgaengen und damit keinen
Faktor. Bis hierher haing die Korrektur allein am Screening, das bei
jeder Aenderung des Kilometerstands laeuft: gerechnet wurde also erst
bei der naechsten Fahrt, nicht beim Tanken.
Der Aufruf im Screening bleibt trotzdem bestehen, und zwar aus dem
umgekehrten Grund: eine Fahrt im Intervall kann ihren Rohverbrauch
erst NACH der Betankung bekommen (der Tankstand-Verlauf trifft
verzoegert ein), und dann muss der Faktor sie noch einholen.
Der 60-Sekunden-Takt ruft bewusst weiter nur
tankvorgaenge_veroeffentlichen() - dort aendert sich nichts, was einen
neuen Faktor ergeben koennte."""
await self.tankvorgaenge_veroeffentlichen()
await verbrauchskorrektur.anwenden(self)
async def batterieverlauf_veroeffentlichen(self) -> None:
self.setzen(E_BATTERIEVERLAUF, "aktuell", await self.ablage.batterieverlauf_lesen())