Batteriespannung: 8V-Plausibilitätsgrenze; Fahrten: echte Start-/Zielposition und Streckenlinie

Batteriespannung: Werte unter 8V (Sensor-/Verbindungsfehler, nicht physikalisch
plausibel für eine 12V-Bleibatterie) werden nicht mehr erfasst - live und beim
Import. "Neue Räder anlegen"-Knopf sitzt jetzt auf derselben Zeile wie "Montiert".

Fahrten: start_lat/-lon, end_lat/-lon und route wurden bisher nur beim
rückwirkenden Import befüllt, nie bei live erkannten Fahrten (dem Normalfall) -
Screening trägt sie jetzt aus dem GPS-Verlauf nach, unabhängig vom
Kilometerstand-Status. fakeTrack() (eine erfundene Linie) ist entfernt, die
Karte zeichnet jetzt die echte Route oder eine ehrliche Gerade zwischen den
bekannten Punkten. CARTOs anonyme Kartenkacheln verlangen inzwischen einen
API-Schlüssel ("API key required" auf jeder Karte in beiden Apps) - auf
schlüssellose OpenStreetMap-Kacheln umgestellt. Start/Ziel zeigen jetzt Datum
und Uhrzeit über der Adresse; die fehlerhafte "Status"-Zeile ist entfernt.
Batteriespannungs-Diagramm nutzt auf großen Bildschirmen die volle
Spaltenbreite statt einer festen 400px-Deckelung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 17:23:32 +02:00
parent 17149937a6
commit f337820fc8
13 changed files with 328 additions and 77 deletions
+61 -5
View File
@@ -22,7 +22,7 @@ import datetime
import logging
from typing import TYPE_CHECKING
from .verlauf import naechster_wert, verlauf_lesen
from .verlauf import naechster_wert, route_aus_verlauf, verlauf_lesen
if TYPE_CHECKING:
from .koordinator import Koordinator
@@ -56,18 +56,31 @@ def _vollstaendig(aenderungen: dict, fahrt: dict) -> dict:
async def durchfuehren(k: Koordinator) -> None:
"""Versucht, alle offenen Fahrten zu vervollständigen."""
"""Versucht, alle offenen Fahrten zu vervollständigen, und trägt allen
Fahrten ohne Standortdaten Start-/Zielposition sowie eine Streckenlinie
nach - unabhängig vom Kilometerstand-Status, da beide Datenquellen
unabhängig voneinander verspätet eintreffen können."""
km_sensor = k.zuordnung.werte.KM_SENSOR
fahrten = await k.ablage.fahrten_lesen()
offene = [f for f in fahrten if f.get("status") == "offen"]
if not offene:
if not fahrten:
return
offene.sort(key=lambda f: f.get("ts_start", ""))
offene = [f for f in fahrten if f.get("status") == "offen"]
if km_sensor:
offene.sort(key=lambda f: f.get("ts_start", ""))
for fahrt in offene:
await _fahrt_screenen(k, km_sensor, fahrt)
lat_sensor = k.zuordnung.werte.STANDORT_LAT_SENSOR
lon_sensor = k.zuordnung.werte.STANDORT_LON_SENSOR
if lat_sensor and lon_sensor:
ohne_position = [
f for f in fahrten
if f.get("start_lat") is None or f.get("end_lat") is None or f.get("route") is None
]
for fahrt in ohne_position:
await _position_screenen(k, lat_sensor, lon_sensor, fahrt)
await _lueckenlos_verketten(k)
await k.fahrten_veroeffentlichen()
@@ -105,6 +118,49 @@ async def _fahrt_screenen(k: Koordinator, km_sensor: str, fahrt: dict) -> None:
await k.ablage.fahrt_aktualisieren(fahrt["trip_id"], _vollstaendig(aenderungen, fahrt))
async def _position_screenen(
k: Koordinator, lat_sensor: str, lon_sensor: str, fahrt: dict
) -> None:
"""Trägt Start-/Zielposition und eine echte Streckenlinie nach, sobald der
GPS-Verlauf sie hergibt - unabhängig vom Kilometerstand-Status.
Dieselbe "Fahrt gehört der Vergangenheit an" wie bei den Kilometerständen:
kein Live-Zustand zum Fahrtbeginn/-ende wird herangezogen (der wäre bei
einer Funklücke des Trackers unzuverlässig, siehe fahrterkennung.py), nur
der aufgezeichnete Verlauf im Fahrtfenster."""
start = _als_zeit(fahrt.get("ts_start"))
ende = _als_zeit(fahrt.get("ts_end"))
if start is None or ende is None:
return
lat_verlauf = await verlauf_lesen(k.hass, lat_sensor, start - SUCHFENSTER, ende + SUCHFENSTER)
lon_verlauf = await verlauf_lesen(k.hass, lon_sensor, start - SUCHFENSTER, ende + SUCHFENSTER)
if not lat_verlauf or not lon_verlauf:
return
aenderungen: dict = {}
if fahrt.get("start_lat") is None:
lat = naechster_wert(start, lat_verlauf)
lon = naechster_wert(start, lon_verlauf)
if lat is not None and lon is not None:
aenderungen["start_lat"] = lat
aenderungen["start_lon"] = lon
if fahrt.get("end_lat") is None:
lat = naechster_wert(ende, lat_verlauf)
lon = naechster_wert(ende, lon_verlauf)
if lat is not None and lon is not None:
aenderungen["end_lat"] = lat
aenderungen["end_lon"] = lon
if fahrt.get("route") is None:
route = route_aus_verlauf(lat_verlauf, lon_verlauf, start, ende)
if route is not None:
aenderungen["route"] = route
if aenderungen:
await k.ablage.fahrt_aktualisieren(fahrt["trip_id"], aenderungen)
fahrt.update(aenderungen)
async def _lueckenlos_verketten(k: Koordinator) -> None:
"""Fahrt-Ende(N) == Fahrt-Start(N+1), solange dazwischen nicht gefahren
wurde.