TANK_DISTANZ_SENSOR + Datensatz sichern/laden mit echtem CSV-Import (Panel)

- Neuer optionaler Sensor TANK_DISTANZ_SENSOR ersetzt die eigene
  Kilometerstand-Subtraktion fuer die Tankvorgang-Strecke, wo zugeordnet
  (Live-Erkennung, manuelle Erfassung, historischer Import)
- "Fahrzeugprofil" und "Daten ausgeben" zu einer Kachel zusammengelegt:
  "Datensatz sichern" (4 Exporte wie bisher) / "Datensatz laden" (neu:
  echter CSV-Import fuer Fahrten/Tankvorgaenge/Wartungsplan)
- Import gleicht per ID ab (Fahrten/Tankvorgaenge) bzw. Datum+Art
  (Wartungsplan, hat keine eigene ID) und aktualisiert nur die in der CSV
  enthaltenen Spalten - alles andere am Datensatz bleibt unangetastet
- Nebenbei gefunden und behoben: belege.tankvorgang_aktualisieren()
  ueberschrieb bisher immer alle Felder, auch mit None, wenn irgendeins
  geaendert wurde - fuer den CSV-Import gefaehrlich, jetzt nur noch
  tatsaechlich uebergebene Felder
- Ebenfalls gefunden und behoben: Panel las das Import-Ergebnis per
  HASS.states direkt nach dem Dienstaufruf - ein Wettlauf mit dem
  state_changed-Push. Nutzt jetzt denselben HASS.callWS(get_states)-Weg
  wie der bestehende historie_importieren-Ablauf.

companion-app-Portierung von "Datensatz sichern/laden" steht noch aus.

Version 2026.8.28.3, live im Testcontainer verifiziert (alle drei
CSV-Datensatztypen: anlegen + aktualisieren per ID/Datum+Art getestet,
Testdaten danach geloescht).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-28 10:19:06 +02:00
co-authored by Claude Sonnet 5
parent edefdd66d8
commit bfb7dec1e9
11 changed files with 521 additions and 76 deletions
@@ -231,9 +231,15 @@ async def _tankvorgaenge_importieren(k: Koordinator, verlaeufe: dict) -> dict:
vorhandenen älteren Tankvorgänge stillschweigend verlieren, obwohl der
Prozentsensor sie längst hätte erkennen können. Beide Läufe teilen sich
`bekannte_zeiten`/`neue`, damit derselbe physische Tankvorgang nicht
doppelt entsteht, falls sich die beiden Verläufe zeitlich überschneiden."""
doppelt entsteht, falls sich die beiden Verläufe zeitlich überschneiden.
distance_km bevorzugt ebenso TANK_DISTANZ_SENSOR (nächstliegender Wert
vor dem erkannten Tankvorgang) vor der eigenen Kilometerstand-Subtraktion
(_distanz_zum_vorherigen()) - dieselbe Präferenz wie im Live-Pfad
(tankerkennung.distanz_seit_tankung())."""
prozent_verlauf = verlaeufe["tank"]
liter_verlauf = verlaeufe["tank_liter"] if k.zuordnung.werte.TANK_LITER_SENSOR else []
distanz_verlauf = verlaeufe["tank_distanz"] if k.zuordnung.werte.TANK_DISTANZ_SENSOR else []
if not prozent_verlauf and not liter_verlauf:
return {"angelegt": 0, "uebersprungen": 0}
@@ -282,8 +288,10 @@ async def _tankvorgaenge_importieren(k: Koordinator, verlaeufe: dict) -> dict:
tankvorgang["odometer_km"] = odometer_km
if mit_liter:
tankvorgang["liters"] = round(anstieg, 1)
tankvorgang["distance_km"] = _distanz_zum_vorherigen(
ts.isoformat(), odometer_km, neue + bestehende
distanz_sensor = wert_bei(distanz_verlauf, ts) if distanz_verlauf else None
tankvorgang["distance_km"] = (
distanz_sensor if distanz_sensor is not None
else _distanz_zum_vorherigen(ts.isoformat(), odometer_km, neue + bestehende)
)
neue.append(tankvorgang)
bekannte_zeiten.append(ts)
@@ -426,6 +434,7 @@ async def importieren(k: Koordinator, start: object, ende: object) -> None:
"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),
"tank_distanz": await verlauf_lesen(k.hass, werte.TANK_DISTANZ_SENSOR, von, bis),
"batterie": await verlauf_lesen(k.hass, werte.BATTERIE_SENSOR, von, bis),
"aussentemp": await verlauf_lesen(k.hass, werte.AUSSENTEMP_SENSOR, von, bis),
"lat": await verlauf_lesen(k.hass, werte.STANDORT_LAT_SENSOR, von, bis),