45259ae9badcb5080015065b6740330741e60f43
35 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
81808dbc10 |
Update ohne Neustart, wenn nur Oberflaechendateien wechseln
Bisher verlangte jedes Update einen Home-Assistant-Neustart. Noetig ist er aber nur fuer Python-Code: HA importiert die Module einmal beim Start und haengt danach mit lebenden Objekten daran. frontend/ dagegen wird direkt von der Platte ausgeliefert (StaticPathConfig, cache_headers=False) - eine ersetzte .js ist sofort wirksam, es braucht nur ein Neuladen im Browser. aktualisierung.neustart_noetig() vergleicht die alte gegen die neue Fassung ueber sha256 je Datei und meldet "kein Neustart" nur, wenn JEDE Abweichung unter frontend/ liegt oder die manifest.json ist. Alles andere - .py, services.yaml, translations/, vorlage/ - gilt als neustartpflichtig, auch wo es das im Einzelfall nicht waere. Die Schieflage ist Absicht: ein faelschlich ausgelassener Neustart laesst neuen Python-Code nie anlaufen, und der Fehler wird woanders gesucht. manifest.json ist ausgenommen, weil sich seine Versionsnummer bei jeder Veroeffentlichung aendert - sonst waere die Unterscheidung wertlos. Weil HA das Manifest fuer die Laufzeit festhaelt (loader.py: hass.data[ DATA_INTEGRATIONS]), liest version_von_platte() die Nummer direkt von der Platte; koordinator.version_neu_lesen() zieht sie nach einem Update ohne Neustart nach, damit Panel und App die neue Fassung auch anzeigen. Panel und App zeigen im Neustart-freien Fall "Seite neu laden" statt "Installation abschliessen - Jetzt neu starten". Neun neue Testfaelle fuer die Unterscheidung, 23 Tests in der Aktualisierungs-Suite gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5621b818cc |
flespi: echtes Lesen statt Zwischenspeicher, und kein Gleichstand ohne Grundlage
Der Waechter meldete am 04.09.2026 "stimmt ueberein" fuer
trip_scenario.ign_off_timeout: 900 gegen unsere Konstante NACHLAUF_S = 900.
Das Geraet hat dort 0 - das Trip-Szenario ist abgeschaltet. flespis
Zwischenspeicher trug den Stand vom 01.09. 13:15, dreieinhalb Tage alt.
Der Eigentuemer hat es aufgedeckt: "die gelesenen werte sind veraltet".
Zwei Aenderungen:
1. "Jetzt lesen" leert erst den Zwischenspeicher der ueberwachten
Einstellungen (DELETE /gw/devices/{id}/settings/{name} - die
API-Entsprechung des Panel-Knopfes "clear cache and synchronize"; loescht
den gespeicherten Wert, nicht den im Geraet) und holt dann neu.
Einstellungen mit einem AUSSTEHENDEN Wert werden uebersprungen: dasselbe
DELETE wuerde ihn verwerfen, und die Aenderung kaeme nie an - wie
1003/1004, die am 04.09. acht Stunden in der Warteschlange standen.
2. `abweichung` ist dreiwertig: true, false und **null (kein Urteil)**. Null
steht dort, wo ein Vergleich nichts aussagt - der Wert wurde nie vom
Geraet bestaetigt, oder es ist gerade eine Aenderung unterwegs. Dazu
traegt jede Zeile `gemeldet_am` (flespis `updated`), und beide
Oberflaechen zeigen es an: "bestaetigt 01.09.2026 (4 T. alt)" bzw.
"nie bestaetigt".
Ein stiller Gleichstand mit einem veralteten Wert ist schlimmer als gar kein
Vergleich: er behauptet Sicherheit, wo keine ist.
tsc sauber, 271 Tests, vite build sauber, Panel als Modul geparst,
py_compile sauber, audi_ha_test auf 2026.9.4.26 ohne Traceback.
|
||
|
|
bdbdb4a010 |
Tankstellenmarke als eigenes Feld, Belegfelder in der App richtig zugeordnet
Die Marke steht auf jedem Beleg, war aber nirgends gespeichert: der Parser erkennt sie seit dem 16.08.2026, faltete sie aber nur in station_name. Fuer jeden Beleg, den eine aeltere Fassung eingelesen hat, blieb dort der Betreibername stehen und die Marke war danach nicht mehr zu holen. - shell_beleg_parser: station_brand als eigenes Feld aus beiden Parserwegen, dazu marke_aus_text()/marke_aus_pdf() - sie lesen nur den Belegkopf und haengen nicht am vollstaendigen Einlesen. - belege.marken_nachtragen(): traegt die Marke einmal beim Start aus der abgelegten Belegdatei nach. Nur wo das Feld gar nicht vorkommt und die Datei liegt; ein einmal eingetragenes Feld wird nie ueberschrieben. - Panel und App: die gespeicherte Marke geht vor, geraten wird nur, wo keine da ist. Ein Teil, der fuer sich genommen eine Kopfmarke ist, taugt zudem nie als Ort. Dazu Punkt 5 der Meldung vom 04.09.2026: ein eingelesener Beleg fuellte nur Datum und Uhrzeit. Der Parser las ihn immer vollstaendig - die App fragte liter/kosten/station/ersparnis/kraftstoff ab, veroeffentlicht werden aber die Namen des Belegs (liters/fuel_total_eur/station_name/discount/fuel_type). Uebereingestimmt hat allein ts. Die Zuordnung steht jetzt als belegFormularwerte() in daten/belegentwurf.ts und wird gegen die echte Nutzlast dieses Belegs geprueft. tsc sauber, 258 Tests (die drei neuen Marken-Tests gegen den alten Stand als scheiternd nachgewiesen), vite build sauber, Panel als Modul geparst, Parser-Suite 11 Tests gegen elf echte Belege. Panel und App liefern fuer elf Faelle byteweise dasselbe. Live in audi_ha_test auf 2026.9.4.21 ohne Traceback: Uebersicht "Shell, Nuernberger Str., Ansbach", Einzelbeleg zweizeilig mit Marke, und der echte geteilte Beleg fuellt durchs Backend das ganze Formular. |
||
|
|
e237bf1e8b |
Server-Adresse: das Schema richtet sich nach dem Ziel
Beim Neuverbinden stand "localhost:5173" im Feld, die App meldete "nicht erreichbar". Ursache war basisUrlNormalisieren(): ohne Schema setzte es blind https:// davor - womit die eigene Vorlage der App unbrauchbar war, denn im Feld steht als Beispiel 192.168.1.20:8123, und im Heimnetz gibt es kein Zertifikat. Jetzt nach dem Ziel: localhost, 127.x, 10.x, 192.168.x, 172.16-31.x, ::1 und .local/.home.arpa/.localhost bekommen http, alles andere weiter https (datametric360.de laeuft ueber Cloudflare). Steht ein Schema da, wird nichts geraten. Vier Regressionstests, darunter der gemeldete Fall, die Vorlage aus dem Feld und 172.32.0.1 als Gegenprobe zum privaten Bereich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
68732aa10f |
Bilder: der Cache-Brecher haengt am Foto statt an der Startzeit
Beide Oberflaechen haengten Date.now() an jede Bildadresse - richtig gegen ein veraltetes Foto (31 Tage Cache-Vorgabe unter /local/), aber die Adresse aendert sich damit bei JEDEM Start, der Zwischenspeicher greift zwischen zwei Starts also nie. Mit dem Vorladen waeren das 5,3 MB je Start gewesen. bilder.staende() liefert jetzt den Zeitstempel je Datei, veroeffentlicht an sensor.audi_dashboard_app_version; eine Bildadresse aendert sich damit genau dann, wenn das Foto ein anderes ist. Ein fehlendes Foto bekommt 0 (dann loest sich auch ein gespeicherter 404 von selbst auf), ein Backend ohne Staende faellt auf das bisherige Verhalten zurueck, und Upload wie Loeschen veroeffentlichen sofort neu. Die App merkt sich den letzten Stand im Browserspeicher: sie malt ihren ersten Bildschirm, bevor die Versionsangabe eintrifft - ohne das Gedaechtnis trug genau dieser Durchlauf noch die Startzeit und holte das Uebersichtsfoto doch wieder bei jedem Start. Live gemessen: Neuladen ohne Aenderung 5.328.771 Bytes Inhalt bei 0 Bytes uebertragen; ein per touch geaendertes Foto wird neu geholt (513.403 Bytes), die anderen nicht; App-Start ohne eine einzige Adresse mit Startzeit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4ab6b9fca5 |
Debug-Mode, eine Version-Kachel, Knoepfe nach HIG, Bilder ohne Verzoegerung (2026.9.4.11)
Debug-Mode: Zugaenge und Dongle stehen nicht mehr verstreut oben in den Einstellungen, sondern zusammen hinter einem Schalter am Seitenende - dazu neu der letzte Datensatz des Dongles (geraet.py). "Letzter Datensatz" heisst dabei: alle Entitaeten des Geraets, deren Aktualisierung hoechstens zwei Sekunden nach der juengsten liegt - die flespi-Integration setzt einen Datensatz in Millisekunden. Angezeigt mit Ankunft, Geraetezeit und dem Rueckstand zwischen beiden. Beim Verifizieren gefunden: app_version_veroeffentlichen() lief nur beim Start und nach einer Aktion, und beim Start haben die Entitaeten des Geraets noch keinen Zustand - die Kachel waere dauerhaft leer geblieben. Laeuft jetzt im Minutentakt mit, ohne Netzaufruf. Ausserdem: - "Version" und "Integration-Update" sind eine Kachel - Speichern/Verwerfen als zwei gleich breite Knoepfe, der bestaetigende gefuellt und rechts; "Verwerfen" war in der App blosser Text - Bilder werden nach dem ersten Zeichnen in der Leerlaufzeit vorgeladen UND dekodiert. Die Verzoegerung beim Seitenwechsel kam nicht vom Netz (31 Tage Cache), sondern vom asynchronen Dekodieren eines frisch erzeugten <img>. 214/214 Tests, Panel als Modul geparst, live gegen audi_ha_test geprueft. |
||
|
|
4d4890034a |
Regler stellt den Schlaf-Timeout des Dongles (2026.9.3.10)
Der Regler "Fahrt beenden" ist jetzt eine Zahl fuer zwei Dinge: unsere eigene
Wartezeit bis zum Fahrtende und der Schlaf-Timeout des FMM003 (103). So lange
bleibt der Dongle wach und wartet darauf, dass es gleich weitergeht; danach
schlaeft er. Die 180 s Ignition OFF Delay laufen davor und bleiben unsichtbar -
sie werden nur von der Fahrtdauer abgezogen. Der Regler geht deshalb von 1 bis
60 in Einerschritten; das Geraet kennt kein Timeout 0.
flespi.py liest und schreibt die Geraete-Konfiguration. Drei Annahmen sind an
der echten API gefallen und stehen als Messung im Modulkopf: der Wert liegt in
current (nicht value), die Einstellung heisst sleep_mode mit dem Wert eine Ebene
tiefer unter mode, und ein PUT braucht {"properties": ..., "address": ...}.
Die eigene Auftrags-Warteschlange ist wieder entfallen. Sie war fuer "das Geraet
schlaeft" gebaut - das gilt aber nur fuer das Geraet, nicht fuer die
Schnittstelle: die vollstaendige Konfiguration kam herein, waehrend das Fahrzeug
seit dem Vortag stand. flespi puffert selbst (pending + address=connection), ein
zweites Auftragsbuch waere eine zweite Warteschlange fuer dieselbe Aufgabe.
Gegengelesen wird nach jedem Schreiben; als Erfolg zaehlt current ODER pending -
letzteres ist bei stehendem Fahrzeug der Normalfall.
Live gegen den echten Account belegt: Token sieht genau ein Geraet, 127
Einstellungen bei parkendem Fahrzeug gelesen, Regler 15 geschrieben und als
pending bestaetigt (Nachbarfelder unveraendert), zweiter Neustart ohne erneute
Anfrage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8c96d9b78e |
Zugaenge: die Dienste-Token aus App und Panel bedienbar (2026.9.3.8)
Die Token liegen seit jeher in entry.options, geschrieben vom Options-Flow unter Einstellungen -> Geraete & Dienste -> Konfigurieren. Der Weg bleibt und ist der Rueckfall, wenn die App den Server nicht erreicht - genau der Fall, in dem ein abgelaufener Token auffaellt. Erreichbar ist er aber nur ueber HAs eigene Weboberflaeche; die iOS-App zeigt keine Konfigurationsdialoge. Die neue Kachel ist die Bedienflaeche dafuer, kein zweiter Speicherort. Der Wert kommt nie zurueck: veroeffentlicht werden nur "gesetzt/nicht gesetzt" und die letzten vier Zeichen (unter zwoelf Zeichen Laenge gar nichts). Auch das Protokoll bekommt ihn nicht, und das Panel leert das Eingabefeld sofort nach dem Absenden. Bewusst NICHT im Fahrzeugprofil: sicherung.py sichert fahrzeugprofil.json, und "Backup exportieren" laedt es als Datei auf das Geraet - ein Token dort wanderte in jede Sicherung und jede weitergegebene Datei. Ein Dienst statt zwei: zugang_setzen(dienst, token), leerer Text loescht. In der App nicht ueber die Warteschlange - ein Stunden spaeter nachgereichter Token ueberschriebe womoeglich einen inzwischen eingetragenen neuen. CONF_FLESPI_TOKEN ist neu und noch ohne Leser; der Flespi-Weg zum Auslesen der Geraetekonfiguration ist damit vorbereitet, aber nicht gebaut. Verifiziert: py_compile auf sechs Backend-Dateien, Panel als Modul geparst, companion-app tsc sauber und 179/179, audi_ha_test auf 2026.9.3.8 sauber gestartet. Live am laufenden Panel der ganze Kreis, Container hinterher wie vorgefunden: Wegwerf-Token gesetzt -> "gesetzt . ...0000", der Wert selbst taucht in den veroeffentlichten Attributen nirgends auf (darauf geprueft), ueber die Sicherheitsabfrage geloescht -> wieder "nicht gesetzt", der Gitea-Token unberuehrt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5ccbfec92d |
Fahrt beenden auf Knopfdruck, Schieberegler, Zuendungspause (2026.9.3.5-.7)
Vier Themen aus einer Sitzung. 1. Die Fahrt endet wieder bei uns statt im Dongle (Abschnitt BW). Der Ignition-OFF-Timeout des FMM003 (900 s) und sein Schlaf-Timeout (ebenfalls 900 s) starten beide beim Zuendungs-Aus und fallen in derselben Sekunde - am 02.09. zweimal beobachtet, einmal ging das Trip-Ende verloren, einmal kam es eine Sekunde vor dem Schlaf an. Ausloeser ist jetzt die Zuendung, die Wartezeit laeuft in Home Assistant. ZUENDUNG_NACHLAUF_S = 180 wird in beiden Wegen abgezogen (ueber das Trip-Signal 1080 s, weil dessen Ende selbst an der verzoegerten Zuendung haengt). 2. Schieberegler, neu in beiden Codebasen. Schrittweite 5 Minuten; der Daumen war im Tagmodus weiss auf weiss und traegt jetzt einen Ring aus --line-strong - ein Token, das genau dort sichtbar ist, wo es gebraucht wird. 3. Knopf "Fahrt beenden" in der Zuletzt-Kachel (Abschnitt BX). Schliesst auf das letzte Lebenszeichen, nicht auf "jetzt", und kennzeichnet das Ende als vorlaeufig. Ein spaet eintreffendes Zuendungs-Aus zieht es nach - innerhalb von sechs Stunden, nur nach vorn, und nur bei einer vorlaeufigen Fahrt. ts_end wandert bewusst NICHT in edited_fields, sonst blockierte der Schutz fuer Handeingaben genau diese Korrektur. 4. Das OTA-Buendel kann auf dem Mac gar nicht entstehen (Abschnitt BV): npm run ota ist eine Windows-PowerShell-Datei, ios-signieren.sh baut ein frisches dist/ und fasst das Buendel nie an. Ausgeliefert war deshalb eine Oberflaeche ohne den Versionshinweis unter richtiger Nummer. Verifiziert: Backend 27/27 (5 Signalwechsel, 14 Zuendungspause, 8 Fahrtende), companion-app tsc sauber und 179/179, Panel als Modul geparst, audi_ha_test auf 2026.9.3.7 sauber gestartet. Live am laufenden Panel und ohne Rueckstand belegt: Wartezeit samt Abbruch, die 180-s-Rechnung, der Regler im Tagmodus und der Knopf von der laufenden Fahrt bis zum verworfenen Kurzvorgang - Fahrten vorher 16, nachher 16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2af5c469d6 |
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 <noreply@anthropic.com> |
||
|
|
be95b40d79 |
Rueckblick legt keine Fahrten ohne gefahrene Strecke mehr an (2026.9.1.12)
Der Eigentuemer hatte auf der realen Instanz alle Nullfahrten geloescht - und bei JEDEM Import kam eine davon wieder: 31.08.2026, 14:48-15:21, 33 Minuten, 209177 -> 209177 km, vmax 3 km/h. MINDESTDAUER_S liess sie durch, weil sie mit 33 Minuten lang genug war; eine Pruefung auf die Strecke gab es im Rueckblick nicht. Steht der Kilometerstand an beiden Enden gleich, hat sich das Fahrzeug nicht bewegt - Zuendung an, aber nicht gefahren. Solche Fenster werden jetzt uebersprungen und als eigene Zahl gemeldet, damit der Import nicht stillschweigend etwas weglaesst. Nur die GEMESSENE Null wird verworfen, nicht die unbekannte: liegt kein Kilometerstand vor, bleibt distanz None und die Fahrt wird angelegt. Der Preis, bewusst getragen: eine echte Fahrt unter einem Kilometer, bei der der Zaehler nicht umsprang, geht verloren - sie waere aber genau der Nulleintrag, den der Eigentuemer nicht im Bestand haben will. Beide Oberflaechen melden die neue Zahl (Paritaetsregel). VERIFIZIERT im Testcontainer ueber den echten Dienst: 1 Fahrt angelegt, 2 Fenster als "nicht gefahren" uebersprungen, null Fahrten mit 0 km im Bestand. py_compile, Panel als Modul, tsc --noEmit sauber, 165/165 gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
af83805c34 |
Fahrtstrecke auf 100 m: CAN als Anker, GNSS als Nachkommastelle (2026.9.1.10)
Zwei echte Fahrten am 01.09.2026 haben die Datenform geliefert, auf die 2026.9.1.5 gewartet hat. strecke_aus_zaehler() summiert die Zuwaechse des geraeteeigenen Kilometerzaehlers und behandelt einen Ruecksprung als Ruecksetzung. Weder der rohe Zaehler noch die Teilstrecken taugen allein: der Zaehler verliert bei einer Ruecksetzung alles Vorherige (Fahrt 1: 0,415 km), die Teilstrecken verlieren einzelne Datensaetze (Fahrt 2: 4 von 50 null, 0,2 km zu wenig). An jedem Datensatz sind beide identisch - sie gehen nur verschieden kaputt. _gnss_verfeinern() ersetzt die grobe Strecke nur, wenn die feine innerhalb der Rundungsunschaerfe des Ankers liegt (GNSS_TOLERANZ_KM = 1.0; beide Enden des CAN-Werts sind auf ganze Kilometer gerundet). Sonst behaelt der Fahrzeugwert recht. VERIFIZIERT gegen die echten Fahrten: - Fahrt 1 (mit Ruecksetzung): 6,958 km, Google Maps sagt 7,1 - Fahrt 2 (sauber): 6,816 km - live: 'Strecke auf 6.896 km verfeinert (Kilometerstand sagte 7.0 km)' - drei aeltere Fahrten korrekt abgelehnt (209178 km gegen 21 km Anker) - sechs Randfaelle: zwei Ruecksetzungen, ein Punkt, leer, unlesbare Werte Nebenbei bestaetigt: NACHLAUF_S = 900 (Zuendung aus 16:33:39, Trip-Signal aus 16:48:44, gespeichertes Ende 16:33:36); der Rueckwaertssprung-Schutz griff live beim Fahrzeugwechsel (-147361 km verworfen, Fahrt blieb offen); die RPM-Zuendung prellt nicht; das Bewegungstor meldet wieder Stillstand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
90efcc5b00 |
Neustart-Festhaenger, OTA-Pruefsumme, Statistik-Kopfzahl (2026.8.31.4)
Die App blieb nach einem Neustart von Home Assistant auf dem alten Stand stehen, mit einer 502-Fehlerzeile, die erst ein Beenden der App wegbekam. Zwei Ursachen: 502/503/504 galten als harter Ladefehler statt als "Server faehrt gerade hoch", und nach dem Wiederverbinden wurde genau einmal nachgeladen - schlug das fehl, kam nie wieder ein Versuch. Beides behoben, vier Regressionstests dazu. OTA: das Buendel ist von HEAD ueber die Platte bis zu den ausgelieferten Bytes nachweislich deckungsgleich. Die Fassung haengt jetzt trotzdem als ?v= an der Adresse (der statische Pfad kommt ohne Cache-Control, aber mit ETag), und ein Pruefsummenfehler wird erklaert statt durchgereicht. Statistik: der Gesamtwert steht jetzt als grosse Zahl in der Kopfzeile, darunter bleiben vier Spalten - fuenf passen auf dem Telefon nachweislich nicht in eine Zeile. Fahrzeugfoto ohne Beschnitt und ohne Bodenschatten, damit auch ohne den Schalter dafuer. Setup filtert zusaetzlich nach dem Signalnamen aus dem Katalog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4af38d7e65 |
Fahrten, Statistik, Trip-Timer (2026.8.31.3)
Der 15-Minuten-Timer war doppelt: der FMM003 wartet laut Konfiguration (Trip \ Odometer, Ignition OFF Timeout) selbst 900 s, die App noch einmal 15 Minuten. Zusammen eine halbe Stunde. Die Pausenregel ist damit entfallen - in der Live-Erkennung, im Historienimport und in beiden Oberflaechen. Der Schwellwert "ab wann ist es ein Parkplatz" hing an derselben Einstellung, hat damit aber nichts zu tun und steht jetzt als eigene Zahl. Hoechst- und Durchschnittsgeschwindigkeit je Fahrt. Der Durchschnitt folgt aus Strecke und Dauer, vmax aus der neuen Rolle GESCHWINDIGKEIT_SENSOR - laut Konfiguration liest das Geraet die Geschwindigkeit vom OBD/CAN. Es ist der groesste gemeldete Wert, nicht die tatsaechliche Spitze: alle zehn Sekunden ein Datensatz. So ist es auch beschriftet. Der Langzeitverbrauch war um einen ganzen Tankinhalt zu hoch. Gemessen wird die Strecke ZWISCHEN erstem und letztem Tankstop; verbraucht wurde darauf, was ab dem ZWEITEN hineinkam - der erste fuellte die Strecke davor. Bei 13 Tankvorgaengen rund 8 Prozent, im Testfall 16,7 statt 8,3 l/100 km. Der Test hatte die falsche Rechnung festgeschrieben und ist mitkorrigiert. Eine negative Strecke (falscher Kilometerstand an einem Tankvorgang) gilt jetzt als unplausibel statt als Null ohne Erklaerung. Verbrauch je Fahrt bekommt Grenzen: am Sensor nachgemessen loest can_fuel_volume 0,1 l auf, ein Schritt entspricht also 10/Strecke l/100 km Fehler. Unter 3 km sagt die Differenz nichts mehr, ueber 60 l/100 km ist es kein Verbrauch. Statistik: zusaetzlich "Absolut", und die Einheit steht jetzt in eckigen Klammern an der Rubrik statt vier Mal am Zeitraum. Fuenf Spalten passen auf einem Telefon nicht nebeneinander, das Raster bricht um. Radfoto in Reifen war ein reines <Bild> - Ersetzen und Loeschen von dort aus gar nicht erreichbar. Jetzt BildMitMenue wie im Panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d2994ce919 |
Standort: "Zuendung an" statt "Fahrzeug faehrt" (2026.8.30.14)
Es gibt keinen Bewegungssensor am Fahrzeug. Der einzige Anhaltspunkt ist ZUENDUNG_SENSOR, und der meldet on auch bei blosser Zuendung oder in Zubehoerstellung - "Fahrzeug faehrt" behauptete also mehr, als bekannt ist. Mein zwischenzeitlicher Vorschlag, zusaetzlich eine laufende Fahrt zu verlangen, war falsch begruendet: fahrt_start_ts in fahrterkennung.py wird aus genau demselben Signal abgeleitet und haette nur eine zweite Huelle um dieselbe Aussage gelegt. Der umgekehrte Fall bleibt unangetastet: Zuendung aus heisst verlaesslich, dass gerade nicht gefahren wird - "Fahrzeug steht" und "Geparkt seit ..." stehen weiter. Zwei Typ-Kommentare, die dieselbe Gleichsetzung weitertrugen, sind mitkorrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4a58021670 |
Paritaetsrunden 2 und 3, Designpruefung umgesetzt (2026.8.30.8)
Drei zusammenhaengende Runden, alle live bei 375x812 gegen audi_ha_test geprueft und in beiden Codebasen angewandt. Paritaetsrunde 2: der gemeinsame Rahmen war das eigentliche Problem - Seitenrand, Kachelabstaende, Kopfleiste, Tableiste und vier Bausteine des Design-Systems trugen noch den Stand vor der iOS-Entscheidung. Dazu sieben Bildschirme neu aufgebaut. Zwei Panel-Fehler dabei mitbehoben: teaser() zeigte unter "Letzte Fahrt" den aeltesten Datensatz, und "Daten bearbeiten" war ein toter Knopf. Paritaetsrunde 3: das Panel hat nie Audi Type gerendert. @font-face in einem Shadow Root wird ignoriert - Schriftschnitte registriert der Browser pro Dokument, nie pro Shadow Tree. Die drei Schnitte liegen jetzt in audi-dashboard-schriften.css und werden ins Dokument gehaengt. Damit erledigt sich eine ganze Reihe von "die Schrift sieht anders aus"-Eindruecken: die beiden Anwendungen zeigten tatsaechlich verschiedene Schriften. Ausserdem "Daten bearbeiten" im Panel gebaut und portiert, das Zeilenmenue entfernt und die letzten neun Bildschirme angeglichen. Designpruefung: die fuenf Punkte der Reihenfolge. Beim vierten hat das Messen den Befund veraendert - gezaehlt waren die deklarierten Groessen, wirksam war laengst eine saubere Sieben-Schritt-Skala mit 27 Ausreissern; die sind jetzt auf den naechsten Schritt gezogen, keiner verschiebt sich um mehr als 1px. Zuletzt: die Standortvorschau zeichnete die falsche Nadel (das Panel wechselt den Icon-Satz ab 34px, die App nahm immer den grossen), und der Kopfabstand der App ist auf den sicheren Bereich reduziert - die 56px des Panels liegen dort unter der Kopfleiste von Home Assistant, in der nativen Huelle steht darueber nichts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e1ebe6d694 |
Vollständiger Panel-companion-app-Paritätsaudit (11 Kapitel)
Systematischer Bildvergleich der companion-app gegen das HA-Panel (companion-app/AGENTS.md Kapitel 1-11): Farbtoken/Radien aus dem falschen Stylesheet gelesen (iOS-Overlay statt Basis-CSS, betraf fast jede Kachel/Farbe/Radius app-weit), vier app-weite @audi-dash/ui-Bugs (Switch/Seg/Feld rot statt neutral bzw. falsche Feldbreite), Reifen-Seite strukturell neu gebaut (beide Radsätze gleichzeitig statt Umschalter, editierbare Felder, Anzugsmoment-/km-Korrektur), Standort-Feature komplett neu (fehlte bisher ganz), sowie diverse Struktur-/Typografie-/Datenlücken in MeinAudi/Service/Versicherung/Sicherheit/Fahrten/Tanken/Statistik/Batterie/Einstellungen. Manifest auf 2026.8.30.2 angehoben, OTA-Bündel neu gebaut und in audi_ha_test verifiziert (sauberer Neustart, keine Tracebacks). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
137d32d28d |
Domain auf datametric360.de umgestellt (war .app)
Alle Referenzen in Doku und Code aktualisiert (REVERSE_PROXY.md, INTERNET_ZUGRIFF_EINRICHTEN.md, COMPANION_APP_ARCHITECTURE.md, AGENTS.md, UMSETZUNGSPLAN.md, companion-app/src/api/umgebung.ts-Kommentar). Dabei den .app-spezifischen HSTS-Preload-Hinweis in COMPANION_APP_ARCHITECTURE.md §5.4 korrigiert - gilt für .de nicht, Force-SSL in Nginx Proxy Manager deckt das weiterhin ab. UMSETZUNGSPLAN.md Phase 12 zusätzlich mit einem Aktualisierungshinweis versehen (zwei-Hostnamen-Plan und pyscript-Namen dort waren ohnehin schon überholt, jetzt klar auf REVERSE_PROXY.md/ INTERNET_ZUGRIFF_EINRICHTEN.md als maßgeblich verwiesen). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
2be6b99667 |
companion-app: Datensatz sichern/laden portiert (Item 9 Paritaet)
- "Daten ausgeben" zu "Fahrzeugprofil" umgebaut, gleiche zwei Knoepfe wie im Panel: Datensatz sichern/laden, popup mit vier Aktionen - CSV-Spaltennamen jetzt byte-identisch zum Panel-Export (gemeinsamer Backend-Parser, csv_import.py) - vorher eigene, abweichende Kopfzeilen ohne Reimport-Moeglichkeit - neue api.csvImportieren(), Fahrzeugprofil-Import ueber das bestehende api.profilSchreiben() (volles Profil, nicht die teilweise zusammenfuehrende profilSpeichern()-Bequemlichkeitsfunktion) - Blinden Fleck geprueft: importStatusLesen()/zustandLesen() lesen per REST, nicht aus lokalem Cache - dieselbe Racebedingung wie im Panel kann hier strukturell nicht auftreten - Toten dateiwahl-Scaffolding entfernt (nie verdrahtet) Beim Bauen echten, vorbestehenden Fehler gefunden: companion-app und Panel verwenden unterschiedliche Feldnamen fuer Wartungsplan-Eintraege (betrieb/notiz vs. werkstatt/kosten) - als eigene Aufgabe geflaggt statt hier mitgefixt. tsc --noEmit sauber, 146/146 Tests, vite build erfolgreich. Nicht live getestet (kein laufender companion-app-Dev-Server mit Backend-Zugang in dieser Sitzung). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
80d73e8afd |
Fahrten: Verbrauch-Näherung, 0V-Zustandsanzeige gefiltert, Plausibilitätsgrenze für die Live-Vervollständigung
Verbrauch (l/100km) war bislang totes Schema - kein Codepfad füllte verbrauch_l_100km je. Jetzt aus der Literstand-Differenz (TANK_LITER_SENSOR) über die Distanz genähert, live und beim Import, in beiden Frontends. Die "Zustand"-Kachel zeigte die Batteriespannung ungefiltert direkt vom Sensor, unabhängig von der Plausibilitätsgrenze der Verlaufsaufzeichnung - ein Sensorausreißer (0V) zeigte sich dort weiterhin, obwohl die Messwertliste ihn längst verwarf. Dieselbe Grenze gilt jetzt auch für diesen Anzeigepfad. Reale Fahrtendaten zeigten eine Fahrt mit 22km in 67s (~1180 km/h) - die Live-Vervollständigung (screening.py) hatte anders als der Import keine Plausibilitätsprüfung der Durchschnittsgeschwindigkeit. Jetzt gemeinsam in verlauf.py (UNPLAUSIBLE_KMH/durchschnitt_kmh) für beide Pfade. Dabei einen zweiten echten Bug gefunden: _fahrt_screenen() zog sein Ergebnis nie ins In-Memory-Objekt nach (nur in die Ablage) - eine im selben Durchlauf gerade erst ermittelte Distanz blieb für spätere Schritte (z. B. Verbrauch) bis zum nächsten Screening unsichtbar. Beide Stellen jetzt behoben. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
f337820fc8 |
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>
|
||
|
|
17149937a6 |
Batteriespannungs-Messwertliste, Wisch-Löschen, Reifensatz-Archiv, CI-Icons
Fünf vom Nutzer angeforderte Erweiterungen in einer Runde:
- Batteriespannungs-Kachel bekommt eine Messwertliste (Datum, Uhrzeit,
Spannung, optional Außentemperatur über den neuen AUSSENTEMP_SENSOR),
erreichbar über ein neues list-s-Symbol in der Diagramm-Kachel. Werte
über AGM_RUHE_MAX_V (12,8 V) sind keine Ruhespannung, sondern
Lichtmaschinenspannung - die Zeile markiert das jetzt mit
"Generatorspannung" statt es unkommentiert als Messwert auszugeben.
- Jede Zeile der neuen Liste wischbar zum Löschen (neuer Dienst
batterieverlauf_loeschen), über dieselbe Wisch-Mechanik wie Fahrten/
Tankvorgänge in beiden Oberflächen.
- Batteriespannungs-Diagramm im Panel war auf großen Bildschirmen (≥860px,
Spaltenlayout) stark in die Breite gezogen (preserveAspectRatio="none"
bei fester Höhe) - gedeckelt wie die dort bereits vorhandenen Popups.
- Statistik-Tab-Symbol in beiden Oberflächen durch das Audi-CI-Symbol
polls-s ersetzt.
- Neue Möglichkeit, einen Reifensatz zu archivieren ("Neue Räder
anlegen"): der aktuelle Stand (km, Marke, Modell, DOT, Maße, Solldruck,
Kommentar) wandert in ein Archiv, der laufende Satz beginnt bei 0 km neu.
Archiv als aufklappbare Übersicht mit voller Bearbeitungsmöglichkeit,
über drei neue, bewusst nicht über profilSchreiben laufende Dienste
(reifen_archivieren/-aktualisieren/-loeschen) - aus demselben Grund wie
reifen_wechseln: kein im Browser gehaltener Stand darf einen
zwischenzeitlich fortgeschriebenen km-Wert überschreiben.
Live im Testcontainer geprüft (erstmals über :18123 statt :8123 erreichbar)
und dabei zwei echte Layout-Fehler gefunden, die kein Compile-/Testlauf
sehen konnte: die Archiv-Eingabefelder waren durch ein fälschlich
verwendetes .mitEinheit (feste 96px-Breite, eigentlich für Zahl+Einheit
gedacht) abgeschnitten ("Continenta" statt "Continental"), und die
Kilometerzahl in der eingeklappten Archiv-Zeile war klein an das Datum
gequetscht statt wie der Satzname lesbar. Beides behoben, live erneut
bestätigt.
Backend: py_compile clean, services.yaml ergänzt. Panel: node --check
clean. companion-app: tsc/Testsuite (146/146)/Build/OTA-Bündel alle grün.
Manifest 2026.8.25.2 → .8, jeder Neustart im Testcontainer sauber.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
5a048488ea |
Selbst-Update-Sicherung, Service-Prognose ohne Servicebuch und Sicherheit neu gestaltet
- aktualisierung.py: Backup-Ordner verliert sein manifest.json beim Sichern, sonst laedt HA ihn nach einem Neustart als zweite audi_dashboard-Integration (Ursache des raetselhaften "gleicher Pfad"-Umbenennungs-Fehlers). - service.ts/audi-dashboard-app.js: Uebersicht faellt jetzt wie Service schon auf die Fahrzeugmeldung zurueck, wenn kein Servicebucheintrag existiert; eigenes (kuerzeres) Oelwechsel-Intervall wird jetzt auch ohne Servicebuch- eintrag naeherungsweise aus der Herstellermeldung hochgerechnet, statt weiterhin unveraendert die Herstellervorgabe zu zeigen. - Sicherheit neu gestaltet: vier Sammelzeilen (Fahrzeug verriegelt / Tueren und Klappen geschlossen / Fenster und Dach geschlossen / Kein Licht) statt einer flachen Liste, mit neuen Tuerschloss-, Dach- und Standlicht-Sensor- rollen, Kreis-Haekchen-Symbolen und einer Detailseite je Tuer/Motorhaube/ Kofferraum in beiden Frontends. Details, Verifikation und Sensor-Rollen in AGENTS.md (Abschnitte J/M/N). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
146f809e9b |
Selbst-Update: Ladezustand und echter Neustart-Knopf
Der erste echte Live-Test des Selbst-Updates gegen das private Gitea-Repo lief erfolgreich (mehrfach im Test-Container bestätigt) - deckte aber zwei UX-Lücken auf, die der Owner direkt gemeldet hat: - Prüfen/Installieren gaben während der 15-25 Sekunden dauernden Netzwerkaktion keine sichtbare Rückmeldung - nicht von einem Hänger zu unterscheiden. Jetzt ein Ladeindikator (Panel: wiederverwendetes .lade-spinner; companion-app: neues .dm-spinner-Äquivalent, gab es dort noch gar nicht). - Nach erfolgreicher Installation stand nur ein Hinweistext da, kein Weg zum eigentlich nötigen nächsten Schritt. Der Knopf wechselt jetzt zu "Installation abschließen - Jetzt neu starten" und stößt homeassistant.restart direkt an. Das separat gemeldete "Lädt"-Hängenbleiben war kein Bug: reproduziert durch Live-Test des neuen Neustart-Knopfs - eine echte HA-Verbindungsunterbrechung während eines Neustarts zeigt exakt denselben, bereits bestehenden Bootstrap-Ladebildschirm. Vermutlich derselbe Effekt durch den Docker-Neustart früher in dieser Sitzung. Verifiziert im Test-Container per Browser-Automatisierung: Neustart-Knopf ausgelöst, Verbindungsabbruch beobachtet, per docker logs bestätigt, dass Home Assistant tatsächlich neu gestartet ist und die neue Version aktiv wurde. Details in AGENTS.md, Abschnitt L. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
fb251154c7 |
Ölwechsel/Inspektion-Anzeige repariert, Selbst-Update der Integration gebaut
Zwei getrennte Themen in einem Commit, beide in derselben Sitzung entstanden:
1. Ölwechsel/Inspektion wurden komplett ausgeblendet ("kein Eintrag im
Servicebuch"), sobald kein Servicebucheintrag vorlag - selbst wenn der
zugeordnete Sensor eine gültige Fälligkeit meldete. In beiden Frontends
prüfte die Anzeige nur den Servicebuch-Zweig, bevor sie die
Fahrzeugmeldung überhaupt las. Jetzt steht die Fahrzeugmeldung für sich;
fehlt zusätzlich ein Servicebucheintrag, übernimmt eine neue,
fahrtenlog-basierte Prognose (kmProTagAusFahrten()/meldungsPrognose())
die Hochrechnung statt der Servicebuch-Rate - deckelt auf die vom
Fahrzeug selbst gemeldete Zeitgrenze, falls zu wenig gefahren wird.
2. install.ps1 als Update-Weg wird von Windows Smart App Control blockiert,
ohne Umgehungsmöglichkeit. Die Integration lädt sich jetzt auf
Tastendruck selbst von Gitea (aktualisierung.py), verifiziert das
Manifest vor jedem Tausch und tauscht per os.rename mit automatischem
Rollback bei Fehlern - install.ps1 bleibt nur noch für die
Erstinstallation nötig. Zugangstoken über einen neuen OptionsFlow in
entry.options, nie in configuration.yaml.
Nebenbei: mehrere seit der HACS-Ausschluss-Entscheidung liegen gebliebene
falsche HACS-Referenzen in Code-Kommentaren und einem UI-Text korrigiert.
Details, Sicherheitsbegründung und Verifikationsstand in AGENTS.md,
Abschnitte I und J.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
1354ca6b06 |
OTA-Updates für die iOS-App; HACS-Fehlannahme korrigiert
HACS kann laut eigener Dokumentation grundsätzlich nicht mit privaten GitHub-Repositories arbeiten (hacs.xyz/docs/faq/private_repositories) - keine Ausnahme für Tokens oder verbundene Konten. Meine frühere Annahme, HACS käme damit zurecht, wenn es unter dem richtigen Konto angemeldet ist, war falsch. Da das Repository aus Lizenzgründen privat bleiben muss (Audi-Hausschrift, Typenschilder), ist install.ps1 damit nicht die Rückfallebene, sondern der einzige Installationsweg - README, INSTALL.md, ANLEITUNG.md, install.ps1 und VERSIONIERUNG.md korrigiert. Oberflächen-Updates für die iOS-App laufen jetzt ohne Xcode: @capgo/capacitor-updater eingebaut, ein Update-Abschnitt in den Einstellungen lädt ein neues Bündel und tauscht die Oberfläche aus. Kein Selbstlauf (autoUpdate: false) - nur auf Tastendruck, nie während der Benutzung. Das Bündel liegt in der Integration selbst (custom_components/audi_dashboard/frontend/app/), nicht unter /local/: so reist es bei jeder Installation automatisch mit, ohne zweiten Auslieferungsweg. Gebaut von companion-app/scripts/ota-paket.ps1 (neuer Befehl: npm run ota), gemeldet über sensor.audi_dashboard_app_version (neues Feld daten.buendel). Ein echter Bug beim Bauen gefunden: [IO.Compression.ZipFile]::CreateFrom- Directory schreibt unter Windows PowerShell 5.1 Backslashes in die Zip-Einträge - iOS hätte das Archiv falsch entpackt. Behoben, indem die Einträge von Hand mit "/" geschrieben werden. Rückfallebene: notifyAppReady() läuft erst, wenn React nachweislich gerendert hat (App.tsx). Kommt diese Meldung nicht, rollt das Plugin nach 20 Sekunden von selbst auf das vorherige Bündel zurück. Am laufenden Testcontainer verifiziert: die ausgelieferte Zip hasht exakt auf den in bundle.json hinterlegten Wert, 13 Einträge, index.html in der Wurzel, keine Backslashes, keine Beschädigung. tsc sauber, 117/117 Tests (5 davon neu für buendelPasst() - dabei eine echte Lücke gefunden: die Funktion hätte bei unbekannter eigener Version fälschlich ein Update angeboten, jetzt genauso vorsichtig wie versionVergleichen). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d8b12da36d |
pyscript-Backend zur echten HA-Integration umgebaut (HACS-fähig)
Das Backend liegt jetzt als custom_components/audi_dashboard/ vor - eine normale Home-Assistant-Integration mit Config-Flow, einer sensor-Plattform und 18 Diensten. Damit ist die App über HACS installierbar; bis das Repo auf GitHub gespiegelt ist (HACS spricht ausschließlich mit GitHub), installiert homeassistant/installationspaket/install.ps1 denselben Ordner ohne HACS. Fünf Installationsschritte entfallen ersatzlos: der pyscript:-Block, der panel_custom:-Block, das Kopieren der Oberfläche nach www/, das langlebige Zugriffstoken (der Verlauf wird direkt über die recorder-API gelesen) und "pip install pypdf" (steht in manifest.json). Das Fahrzeugprofil legt die Integration beim ersten Start aus ihrer Vorlage an. Drei alte Schwächen sind dabei mit erledigt: - Die Nutzlast landet nicht mehr in der Recorder-Datenbank (_unrecorded_attributes - das kann nur eine echte Entität). - Eine laufende Fahrt überlebt einen Neustart (Store statt Arbeitsspeicher); fiel sie während eines Ausfalls ins Ende, schließt nach_neustart_fortsetzen() sie beim letzten aufgezeichneten Zeitpunkt. - Sensor-Zuordnungen wirken sofort - die Zustandsbeobachter werden neu gebunden, der Neustart-Hinweis und der Neustart-Dienst sind weg. Namensvertrag geändert, beide Oberflächen mitgezogen: pyscript.audi_dashboard_x -> sensor.audi_dashboard_x, pyscript.audi_dashboard_y -> audi_dashboard.y. Eine Companion-App vom alten Stand findet nach dem Umstieg nichts mehr und muss neu gebaut werden; das Panel liegt in der Integration und kann nicht driften. Der selbstgebaute Updater entfällt - HACS ist die Update-Mechanik, die Home Assistant kennt. Die Versionierung schrumpft auf eine Quelle: manifest.json. Geprüft am laufenden Testcontainer (Container byteweise identisch mit dem Repo): alle 18 Dienste, Panel, Config-Entry neu laden, Historienimport, echter Shell-Beleg in-process, Neuinstallation im Wegwerf-Container blank mit automatisch nachinstalliertem pypdf. Companion-App: tsc sauber, 112/112 Tests, beide Rauchtests gegen das laufende Backend grün. Belegparser 8/8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
edf3845375 |
companion-app: catch up six days of panel drift before the native build
The phone-app codebase (companion-app/) hadn't been touched since 2026-08-11 - every panel fix and feature since then (HIG audit rounds, receipt upload, trip editing, Inspektion forecast, ...) existed only in the HA panel. Found while auditing what's needed for a real iPhone build; user decision was to port everything now rather than ship a stale app. Six real, verified gaps (not blind copies of panel CSS/markup, which doesn't transfer to the @audi-dash/ui component set): - Battery voltage cutoff was still 13.2V, not the panel's 12.8V fix. - Dead wlan_name field (WLAN trip detection was fully removed from the backend 2026-08-12) - removed from the adapter and the settings screen instead of leaving a form field that silently does nothing. - No Inspektion forecast - added inspektionPrognose() alongside the existing oelwechselPrognose(), sharing a refactored core. - Arbeitsweg pill used the design-system's "work" variant, which is documented as recoloring to red - stopped passing it, same fix as the panel. - Trip creation only took Beginn/Ende/Art; extended with Startort/ Zielort/Kilometerstand/Distanz via a new shared FahrtFelder.tsx. - FahrtDetail.tsx was read-only - added an edit mode using the same shared fields, backed by a new DataMetricApi.fahrtAktualisieren() calling the backend service built earlier this session. Explicitly checked and found not applicable: price rounding (already 2 decimals here), pull-to-refresh CSS (no native gesture to fix), the panel's drag/paste receipt dialog (solves a desktop-browser problem this native app doesn't have - the plain file picker already gets iOS's native Files integration), and the purely cosmetic panel CSS fixes. npm install run at the repo root (node_modules was incomplete/stale), package-lock.json reflects the real dependency tree. Verified with npm run typecheck (clean), npm run test (95/95, up from 90 - added interaction tests for the new form and inspektionPrognose), and npm run build (succeeds). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f8b877d555 |
Phase 10-13: PWA fertig, blockierte Phasen vorbereitet
PWA laeuft: Manifest, Symbole in allen noetigen Groessen und die iOS-Angaben, die Apple statt des Manifests auswertet. Das Startsymbol ist bewusst neutral - die Vier Ringe auf einem Homescreen waeren nach aussen sichtbar und fielen nicht mehr unter die private Nutzung. Vorbereitet, aber nicht ausgefuehrt: Capacitor-Konfiguration, ein Ablage-Adapter fuer Schluesselbund und Keystore (greift defensiv auf die Laufzeit zu, damit die App ohne das Plugin unveraendert weiterlaeuft), die Pfad-Freigabeliste fuer den Reverse Proxy und das Geruest der FMM003-Zuordnungstabelle. Die QR-Seite fuer die Einrichtung war zunaechst ein Fehlgriff: Ich hatte den QR-Erzeuger selbst geschrieben. Der Vergleich gegen eine erprobte Implementierung zeigte 1239 abweichende Module von 3249 - der Code waere unlesbar gewesen. Jetzt liegt eine bewaehrte Bibliothek (MIT, 57 KB) neben der Seite im eigenen Home Assistant. Das erfuellt die eigentliche Anforderung genauso: kein Netzzugriff, der Token verlaesst das eigene Netz nicht. Geprueft ueber einen Umlauf - 233 Zeichen hinein, identisch wieder heraus. |
||
|
|
571d12310f |
Phase 8: Audi-Schrift, Vier-Ringe und Typenschilder
Die drei Schriftschnitte stammen aus dem Panel, wo sie als base64 im Stylesheet lagen - als eigene woff2-Dateien sind sie zwischenspeicherbar statt bei jedem Laden erneut uebertragen zu werden. Typenschilder und Ringe wechseln mit dem Erscheinungsbild. Lizenzgrenze eingehalten und geprueft: design-system bleibt unveraendert und enthaelt keine Markendateien, weil es nach aussen hochgeladen wird. Die App darf sie nutzen, weil sie nur seitlich installiert wird. Dabei noch eine Typungenauigkeit korrigiert: technik und ausstattung im Profil sind Listen von Gruppen, waren aber wie die uebrigen Abschnitte als Objekt deklariert. |
||
|
|
8b64a808ed |
Phase 7b: Fahrten, Tanken und Statistik
Listen mit Jahr-Monat-Aufklappung, Detailseiten, manuelles Eintragen und Beleg-Upload. Statistik rechnet ueber die portierten Regeln. Zwei offene Audit-Befunde des alten Panels sind hier gleich mit erledigt: 1. Loeschen war ausschliesslich per Wischgeste erreichbar. Wer mit Sprachausgabe oder Schaltersteuerung bedient, wischt nicht - Fahrten und Tankvorgaenge waren fuer diese Nutzer gar nicht loeschbar. Jede Zeile hat jetzt zusaetzlich ein sichtbares Menue, ein richtiger Knopf mit Tastatur. 2. Leaflet kam per CDN und blieb auf einem Handy ohne eigenen Internetzugang still leer. Es liegt jetzt im Buendel (eigener Abschnitt, erst bei Bedarf geladen). Bewusst NICHT uebernommen: fakeTrack() aus dem alten Panel, das eine erfundene Linie zwischen zwei Punkten zeichnete, wenn keine Route vorlag. Solange das Fahrzeug keine Positionen liefert, sagt die Seite das - statt eine glaubwuerdig aussehende Erfindung zu zeigen. |
||
|
|
0103508cea |
Phase 6: Datenanbindung, Ersteinrichtung und Offline-Anzeige
Die App spricht jetzt echt mit Home Assistant. Ein Datenkontext buendelt die vier Datentoepfe, haengt sich an den WebSocket-Ereignisstrom und reicht Verbindungszustand und Warteschlange nach aussen; die Screens kennen weder REST noch WebSocket. Ersteinrichtung prueft die Zugangsdaten, bevor sie sie speichert, und unterscheidet in der Fehlermeldung zwischen abgelehntem Token und nicht erreichbarem Server - zwei Faelle, die voellig verschiedene Reaktionen verlangen. Zwei echte Fehler, die erst der Test gegen die laufende Instanz zutage brachte: 1. Die Typen der Datenschicht deklarierten tank_prozent, sicher_abgestellt und sicherheit. Das Backend schreibt tankprozent, gesichert und sicherheitscheck - die Oberflaeche haette still ueberall undefined gelesen, ohne dass irgendetwas fehlgeschlagen waere. 2. Der Profil-Adapter reichte lebende Verweise ins Rohprofil durch. Ein Formular haette damit den Rohstand mitveraendert und die Zusage gebrochen, den vom Backend fortgeschriebenen Reifen-Kilometerstand nie zu ueberschreiben. Abschnitte werden jetzt kopiert. Ausserdem zwei Parameter-Eigenschaften in der Datenschicht aufgeloest, weil Node sie im Strip-Modus nicht uebersetzt und genau darueber die Rauchtests laufen. Belegt durch neun Pruefungen gegen die laufende Instanz (Lesen, Dienstaufruf, vollstaendiger Warteschlangenumlauf, WebSocket-Anmeldung) und 21 Unit-Tests. |
||
|
|
e1d570992e |
Initialer Import: HA-Panel, Design-System, Companion-App
Drei zusammengehörige Teile in einem Repository: - homeassistant/ Das fertige, im Einsatz befindliche Home-Assistant-Panel (panel_custom Custom Element + pyscript-Backend). Echte Fahrzeug- und Personendaten (fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl, Tankbelege) bleiben per .gitignore außen vor; die anonymisierte Vorlage fahrzeugprofil.example.json ist mit dabei. - design-system/ Eigenständige React-Komponentenbibliothek (@audi-dash/ui), die die visuelle Sprache des Panels nachbildet - ohne Audi-Markenzeichen und ohne die lizenzierte Hausschrift. Dient als Grundlage für Claude Design. War bis hierher ein eigenes Repository und ist in dieses eingeschmolzen worden. - companion-app/ Datenschicht der neuen App DataMetric360 (iOS/Android via Capacitor, zusätzlich als Iframe im HA-Dashboard). Noch ohne Oberfläche: REST- und WebSocket-Zugriff auf Home Assistant plus Warteschlange für Änderungen ohne Netz. Ersetzt das eingespritzte hass-Objekt, das nur innerhalb des HA-Frontends existiert. Dazu die Projektdokumentation: SPECIFICATION.md (Ist-Stand des Panels), COMPANION_APP_ARCHITECTURE.md (Architekturentscheidungen der neuen App), AUDIT_2026-08-10.md, DESIGN_BRIEF_DATAMETRIC360.md und der ursprüngliche Bauauftrag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |