Commit Graph

5 Commits

Author SHA1 Message Date
tobias 99cef7c393 Fahrzustand aus der Zuendung statt aus dem Fahrtstatus, App-Version vergleichbar
BUG "Fahrzeug faehrt" aenderte sich nie. standortZustand() leitete den
Fahrzustand aus TRIPS[0].status === "offen" ab - zwei Fehler
uebereinander, die sich gegenseitig verstaerkt haben.

Erstens heisst "offen" nicht "unterwegs", sondern "Daten noch
unvollstaendig": _fahrt_beenden() legt die Fahrt mit diesem Status an,
wenn sie ENDET, und das Kilometerstand-Screening fuellt sie spaeter. Eine
Fahrt, die nie eine Strecke bekam - etwa weil damals kein KM_SENSOR
zugeordnet war -, bleibt fuer immer "offen". Zweitens ist TRIPS[0] die
AELTESTE Fahrt, nicht die neueste: fahrten_veroeffentlichen() reicht
profil.fahrten_lesen() unveraendert in Dateireihenfolge weiter, und die
ist aufsteigend. Zusammen hat die aelteste jemals unvollstaendig
gebliebene Fahrt das Fahrzeug dauerhaft als fahrend angezeigt; an der
Testinstanz war das eine seit 18 Tagen beendete Fahrt.

Nicht den Index geflickt, sondern die Quelle korrigiert: das Backend
veroeffentlicht jetzt "zuendung" im Fahrzeugstatus, gelesen aus dem
ohnehin zugeordneten ZUENDUNG_SENSOR - demselben Signal, das auch
fahrterkennung.py als massgeblich nimmt. Anzeige und Erfassung koennen
dadurch gar nicht mehr auseinanderlaufen. Ohne zugeordneten Sensor
(null) steht "Fahrzustand unbekannt" statt einer Behauptung.

companion-app hatte denselben Fehler spiegelverkehrt: liveZustandLesen()
las "zuendung" (gab es nie) und "lat"/"lon" (das Backend liefert
standort_lat/standort_lon), und der Typ Fahrzeug kannte keines dieser
Felder. Die Live-Ansicht meldete deshalb dauerhaft "Das Fahrzeug steht"
und zeigte nie eine Position. Felder in Fahrzeugstatus/Fahrzeug und im
Adapter ergaenzt, damit der Typ diese Fehlerklasse kuenftig faengt -
wovor sein eigener Kopfkommentar seit einem frueheren Vorfall warnt.

BUG "Standortzugriff verweigert" war irrefuehrend. Die Zeile steht
direkt unter dem Fahrzeugnamen, wo sonst der Abstand zum Auto steht,
handelt aber vom Standort DIESES Geraets - sie las sich, als sei der
Standort des Autos nicht abrufbar. Jetzt "GPS offline" wie gewuenscht.
Die verweigerte Freigabe behaelt eine eigene Meldung ("GPS-Freigabe
fehlt"): sie ist der einzige Fall mit anderer Abhilfe, und "GPS offline"
wuerde dort zur Signalsuche statt zum Freigabeschalter schicken.

VERSIONIERUNG: die Zahl, die alle fuer "die Version" hielten, ist keine.
Der Integer in audi-dashboard-version.json ist ein Cache-Brecher, den
install.ps1/update.ps1 bei jedem Deploy mit UtcNow neu setzen -
unabhaengig davon, ob sich Code geaendert hat. Zwei Builds derselben
Quelle bekommen verschiedene Zahlen. Er kann die Frage "ist das derselbe
Stand?" grundsaetzlich nicht beantworten.

Deshalb beide Aufgaben getrennt: neue Datei VERSION im Projektstamm
(2026.08.23.1) als Identitaet, von Hand erhoeht; der Integer bleibt
unveraendert der Cache-Brecher. VERSION fliesst in beide Seiten - als
zweites Feld "app" in audi-dashboard-version.json (alle drei
Deploy-Skripte uebernehmen es jetzt; sie haben die Datei bisher komplett
ueberschrieben und haetten es still zerstoert) und ueber vite define als
__APP_VERSION__ in den Companion-Build. Das Backend veroeffentlicht
pyscript.audi_dashboard_app_version, die App vergleicht und meldet eine
Abweichung in der Hinweisleiste - deren erklaerter Grundsatz "nie eine
stille Veraltung" genau dieser Fall ist, nur dass hier nicht die Anzeige
veraltet, sondern die App selbst.

Bewusst nur Gleichheitsvergleich, nie groesser/kleiner: die Version ist
eine Kennung, keine Zahl; Sortieren waere scheingenau und wuerde bei
einem Formatwechsel still falsch antworten. Fehlt eine der beiden
Seiten, wird nicht verglichen und nichts gemeldet - ein aelteres Backend
oder ein Start ohne Netz darf keinen Fehlalarm ausloesen. Der
vite-Build bricht dagegen hart ab, wenn VERSION fehlt, statt eine App zu
erzeugen, die ihre eigene Veraltung nicht erkennen kann. Das Panel
braucht nichts davon: es laedt bei jedem Seitenaufruf neu.

Geprueft: Backend meldet zuendung: False und app_version 2026.08.23.1 im
Testcontainer, Panel zeigt statt "Fahrzeug faehrt" jetzt "Geparkt seit
13 Tg. 13 Std." und statt der alten Meldung "GPS-Freigabe fehlt";
VERSION landet nachweislich im Build (im Bundle gegriffen) und der Build
bricht ohne die Datei ab (gegengeprueft); tsc sauber, Tests 112/112,
vite build sauber, HA-Start ohne Fehler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 21:59:14 +02:00
tobias 3b99f1f332 Vergangene Daten aus dem HA-Verlauf importierbar, Auslieferstand bereinigt
Datenaufbewahrung (recorder_snippet.yaml, neu): Home Assistant loescht
Sensor-Verlaeufe standardmaessig nach 10 Tagen - das war die stille
Obergrenze dafuer, wie weit sich ueberhaupt je etwas rekonstruieren
laesst, denn geloeschte recorder-Zeilen sind endgueltig weg. Der Block
hebt das auf 365 Tage. Die Bestaende der App selbst (fahrten.jsonl,
tankvorgaenge.jsonl, batteriespannung.jsonl, fahrzeugprofil.json) waren
davon nie betroffen, die kennen ohnehin keine Purge-Logik. Platzbedarf
an der Testinstanz gemessen statt geschaetzt: 90.858 Zeilen in 17 Tagen,
hochgerechnet grob 1,9 Mio. Zeilen und 1-1,5 GB im Jahr, davon 96 % aus
der EU-Data-Act-Integration - dafuer liegt eine auskommentierte
exclude:-Liste bei. Bewusst kein include:-Block, der wuerde jeder
anderen Integration den Verlauf nehmen und dabei kaum Platz sparen.

Historienimport (pyscript/historienimport.py, neu): neuer Dienst
audi_dashboard_historie_importieren(start, ende) liest denselben
Verlauf, den die Live-Trigger in Echtzeit sehen, und leitet rueckwirkend
dieselben Datensaetze ab - Fahrten aus dem Zuendungsverlauf (inklusive
der Pausenregel, kurze Unterbrechungen verschmelzen zu einer Fahrt),
Tankvorgaenge nach derselben Tiefststand-Logik und denselben Schwellen
wie tankerkennung.py, Tagesmin/-max der Batteriespannung. Mehrfach
ausfuehrbar: ueberschneidet sich eine Fahrt mit einer bereits erfassten,
wird sie uebersprungen statt doppelt angelegt. Erzeugte Datensaetze
tragen source: "import".

Gelesen wird ueber HAs eigene recorder-API (get_significant_states),
nicht per direktem SQL auf home-assistant_v2.db: das Schema ist
HA-intern und aendert sich zwischen Versionen, die Funktion ist die
stabile Schnittstelle. Preis dafuer ist hass_is_global: true im
pyscript-Block; ohne die Zeile laeuft alles andere unveraendert weiter,
nur der Import meldet, dass er den Verlauf nicht lesen kann.

Oberflaeche in beiden Codebasen: Knopf "Daten importieren aus Home
Assistant" unter Einstellungen -> Einrichten, dahinter ein Fenster mit
Von/Bis (Vorbelegung: letzte 30 Tage), "Importieren" als Hauptaktion und
"Abbrechen" als zweite Wahl. Das Fenster bleibt offen und zeigt das
Ergebnis, statt optimistisch zu schliessen - hier ist das Ergebnis der
Zweck des Aufrufs. Fortschritt ueber die Entitaet
pyscript.audi_dashboard_import_status, weil pyscript-Dienste sofort
zurueckkehren. In companion-app bewusst NICHT ueber die
Offline-Warteschlange: ein spaeter aus dem Nichts abgefeuerter
Importlauf waere fuer den Nutzer nicht nachvollziehbar.

Beim Testen gefunden und mitgefixt: profil.batterieverlauf_tageswert_
aktualisieren() haengte in Einfuegereihenfolge an. Solange nur die
Live-Aufzeichnung schrieb, war das dasselbe wie sortiert (sie traegt
immer den heutigen Tag ein) - der Import traegt vergangene Tage nach,
die waeren hinter den neueren gelandet und das Diagramm haette zeitlich
rueckwaerts gelaufen. Sortiert jetzt nach Datum, wie der eigene
Docstring es ohnehin versprach.

Auslieferstand bereinigt: einstellungen.py brachte zwei Entity-IDs einer
laengst abgeraeumten Testinstanz mit (testzone_fmm003_...). Beim Testen
unsichtbar, weil die Testinstanz sie ueber entitaeten.json ueberschreibt
- auf einer Neuinstallation waeren sie die wirksamen Werte gewesen. Und
schlimmer als nur wirkungslos: fahrterkennung.py registriert seinen
@state_trigger nur, wenn das Feld belegt ist, ausdruecklich als Schutz
gegen eine leere Entity-ID. Ein gesetzter, aber nicht existierender Wert
hebelt genau diesen Schutz aus. Beide Felder jetzt leer wie alle
anderen; Kopfkommentar der Datei ueberarbeitet, er behauptete noch, die
EU-Data-Act-Integration werde nicht mehr verwendet.

install.ps1 abgesichert, da die erste echte Installation auf HA OS
ansteht: die feste configuration.yaml.bak wurde bei jedem Lauf
ueberschrieben, ausgerechnet das Original war nach dem zweiten Lauf weg
- Sicherungen jetzt zeitgestempelt. Nach dem Schreiben wird die Datei
zurueckgelesen und geprueft (Laenge, genau ein Markerblock, bisheriger
Inhalt unveraendert); bei der kleinsten Abweichung rollt das Skript
automatisch zurueck. Die Sicherheitseigenschaften stehen jetzt im Kopf
der Datei, statt dass man ihnen glauben muss.

Standort-Blatt: "Teilen" war im Tagmodus unsichtbar - .standort-pille
nutzte var(--tile), das Blatt darunter var(--tile-deckend), und die
iOS-Auflage setzt im Tagmodus beide auf #FFFFFF. Gemessener Kontrast
1,00:1, weiss auf weiss. Beide Pillen folgen jetzt der Knopfsprache des
Panels (.aktion / .aktion.primaer): Umriss fuer die Nebenaktion, var(--fg)
gefuellt fuer "Route". Danach 17-21:1 Textkontrast in beiden Themes.
Nebenbei ist damit --line-strong - eine Linienfarbe - nicht laenger als
Knopffuellung im Einsatz.

Geprueft: Import zweimal ueber die echte Oberflaeche im Browser gegen
eine in die recorder-DB eingespielte Kunsthistorie (der Testcontainer
laeuft nicht, waehrend das Auto faehrt, echte Fahrten liegen dort also
nicht vor) - drei Fahrten wie erwartet, die 5-Minuten-Unterbrechung
korrekt zu einer 50-Minuten-Fahrt verschmolzen, die 30-Sekunden-Zuendung
verworfen, Strecken kilometergenau; zweiter Lauf legte 0 an und meldete
3 als vorhanden. Neuinstallation in einem Wegwerf-Container: nur
Profilvorlage und Parser, keine Fahrten-/Tank-/Batteriedatei, null
Fehler im Log. install.ps1 gegen Attrappen: -Pruefen schreibt nichts,
echter Lauf laesst automations.yaml bytegleich (SHA-256) und fremde
Bloecke stehen, Ergebnis parst als gueltiges HA-YAML, zweiter Lauf
idempotent, bei fremdem pyscript:/panel_custom: bleibt die Datei
bytegleich. tsc --noEmit sauber, companion-app-Tests 106/106, vite build
sauber, HA-Configcheck und Start ohne Fehler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 20:42:54 +02:00
tobias 2d76f10c1c Fahrten bearbeitbar, Inspektionsprognose, Bootstrap-Takt reisst nicht mehr ab
Sechs gemeldete Punkte plus ein Fund beim Nachlesen des Ladevorgangs:

- HECKKLAPPENSCHLOSS_SENSOR ueberall entfernt (einstellungen.py,
  entitaeten.py-Katalog, _sicherheitscheck()). HECKKLAPPE_SENSOR bleibt.
- Bild-Platzhalter in den Einstellungen war halb abgeschnitten: die bare
  .carfix-Regel der Desktop-Container-Query traf auch den Platzhalter-Div,
  dessen inset:0 von der festen Hoehe uebersteuert wurde. Auf .bildbox
  eingegrenzt.
- Inspektion bekommt eine eigene "voraussichtlich am ..."-Zeile (nur Datum).
  oelwechselPrognose() dafuer auf den gemeinsamen Kern servicePrognose()
  zurueckgefuehrt.
- "Neue Fahrt" bietet jetzt alle Felder, die die Einzelfahrt anzeigt, und die
  Einzelfahrt laesst sich bearbeiten - gemeinsames fahrtFelder()/
  fahrtFormularWerte() nach dem Muster von tankFelder(). Backend: Service
  audi_dashboard_fahrt_aktualisieren, profil.fahrt_bearbeiten() (edited_fields
  schuetzt gegen die Automatik, nicht gegen den Nutzer). Die Art-Pille
  speichert jetzt ebenfalls statt nur lokal umzuschalten.
- Titelzeile steht auf dem Handy buendig zu den Kacheln: .back:not(.on) gibt
  seine Breite und den Gap auf allen Breiten frei, .topbar links 16px.
- Ladebildschirm: nachladeAnstossen() und datenLaden() stiegen bei fehlendem
  HASS aus, ohne eine naechste Runde zu planen - genau die Sackgasse, aus der
  bisher nur ein Menueklick half. Beide planen jetzt weiter, der Timer wird
  beim Abhaengen gestoppt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 11:40:36 +02:00
tobias 6bf7435434 Add real Audi CI battery/edit icons, manual km-correction for tire sets
Replaced the hand-drawn placeholder battery icon with the real Audi CI
battery-12v-l/-s (user supplied the actual SVGs) next to "Ruhespannung",
matching the existing poi-l/poi-s size-variant convention.

Added a km-correction control to the Sommerräder/Winterräder tiles: a
pencil icon (Audi CI edit-s) opens an inline field to manually correct
the tire-set's accumulated km. The tricky part was persistence -
configCarZuProfil() deliberately never writes km back (a stale browser
copy could otherwise clobber a since-elapsed automatic increment), so
this needed its own backend service, audi_dashboard_reifen_km_setzen,
that overwrites only the stored counter and leaves referenz_odo_km
alone - the existing odometer-delta tracking in reifenzaehler.py then
continues accumulating from the corrected value on its own, no other
backend change required.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:16:08 +02:00
tobias fb38c297bd Station name as "brand, street, city", grey box on all editable fields
The "grey box around the icons" report turned out not to be a defect: the box
on "10.000 km"/"1 Jahr"/"15" is the wanted pattern, and every value the user
can change should carry it. All .feld inputs and selects now get the grey iOS
fill; inputs with their own visual language (checkbox/radio/range/file) keep
the transparent base rule, so the switches are unaffected.

Fuel stations are now displayed as "Shell, Pascalstr. 8, Ingolstadt" instead of
the bare operator name. New _marke()/_ist_strasse()/_tankstelle() helpers are
shared by both parser paths so Shell and non-Shell receipts format the same.
The brand is only matched against the receipt header - searching the whole text
would let "Total" hit a totals line. Missing parts are dropped instead of
leaving empty comma slots, and without a known brand the operator name takes
its place. station_address still carries the full street and postcode.
test_bekannte_stationen updated to the new format; suite stays green.

installationspaket/ is versioned from now on (user request). It contains no
real vehicle data - only the example profile with empty FIN/plate placeholders.
Keep it in sync whenever pyscript/ or www/ changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 12:52:31 +02:00