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>
Beim Antippen eines Feldes auf dem Willkommensbildschirm zoomte iOS die
ganze Seite heran und machte sie verschiebbar. Ursache war nicht eine
fehlende Regel, sondern eine unterlegene: grundlage.css und .dm-eingabe
setzen laengst 16px, aber @audi-dash/ui setzt ".ads-feld input" auf 13.5px -
Klasse plus Typ schlaegt die blosse Klasse. Unterhalb von 16px zoomt iOS
beim Fokus, das ist der ganze Mechanismus.
Behoben mit einem spezifischeren Selektor im App-CSS, gleicher Wert wie
ohnehin beabsichtigt. @audi-dash/ui bleibt unangetastet, wie schon beim
type=date-Fall darunter.
Bewusst nicht genommen: user-scalable=no im Viewport. Das haette das
Symptom in einer Zeile versteckt, nimmt aber allen das Aufziehen - und
dasselbe Buendel wird auch als Web-App aus Home Assistant ausgeliefert.
Phase 10 Schritt 7 ist damit erledigt. auslieferung/App.ipa, 2,4 MB, Ad-hoc
signiert mit "Apple Distribution: Paul Nothaft", Profil gueltig bis
29.08.2027, genau ein eingetragenes Geraet.
Zwei Annahmen von heute frueh waren falsch und sind korrigiert: die bezahlte
Mitgliedschaft stuft das bestehende Team hoch, statt ein neues anzulegen (die
Kennung bleibt RMACS9VLS4), und der Export als Ad-hoc funktioniert einwandfrei.
Neue Falle festgehalten: beim ersten Signieren fragt der Schluesselbund per
Dialog um Erlaubnis. Bleibt der unbeantwortet, haengt xcodebuild wortlos und
endet mit errSecInternalComponent. Die Diagnose steht in AGENTS.md, weil das
Symptom von sich aus nirgendwohin zeigt.
Neu: scripts/ios-luftweg.sh erzeugt manifest.plist, Installationsseite und
Symbole fuer die Uebertragung ueber die Luft. Es verweigert eine Basis-Adresse
ohne https, weil iOS sonst erst auf dem Telefon still scheitert.
Offen bleibt der Host, der die Dateien ausliefert.
Der fest eingetragene Wert RMACS9VLS4 gehoert zum kostenlosen Personal Team.
Mit der bezahlten Mitgliedschaft entsteht ein eigenes Team mit anderer
Kennung; der alte Wert wuerde still weiter mit dem kostenlosen Team signieren
und die App nach 7 Tagen sterben lassen. Ohne APPLE_TEAM_ID bricht das Skript
jetzt mit einem Hinweis ab, wo die Kennung zu finden ist.
Owner-Anfrage 2026-08-28 - war 2026-08-11 bewusst weggelassen, jetzt wieder
gewünscht. Ursprünglicher Plan aus UMSETZUNGSPLAN.md Phase 10 als Referenz.
Der vorige Commit ging von einer bezahlten Mitgliedschaft aus und nannte als
Ausweg, die UDID auf developer.apple.com einzutragen. Das ist falsch.
Xcodes eigener Zwischenspeicher belegt das Gegenteil:
isFreeProvisioningTeam = 1, teamType = "Personal Team". Damit gibt es gar
keine Geräteverwaltung im Portal -- ein Gerät wird ausschließlich dadurch
bekannt, dass es angeschlossen und vertraut ist. Zusätzlich verfallen Profil,
App-ID und Geräteeintrag alle 7 Tage.
Auch die ältere Behauptung weiter oben in AGENTS.md ("Paul has an Apple
Developer Program") ist damit als falsch markiert.
Offen und bewusst als ungeprüft vermerkt: ob -exportArchive mit einem
Personal Team überhaupt eine brauchbare .ipa liefert. Der direkte Weg aufs
angeschlossene Gerät steht als Rückfallebene im Skriptkopf.
Phase 10 Schritt 7 lässt sich nicht abschließen, aber alles bis zur Signatur
ist gebaut und belegt: Simulator- und Gerätebau (arm64, Release) laufen
fehlerfrei durch, 146/146 Tests grün, Typecheck sauber.
Die Signatur scheitert allein daran, dass dem Entwicklerteam kein Gerät
bekannt ist -- Apple erzeugt ein Development-Profil nur für konkrete UDIDs.
Kein iPhone angeschlossen, keins je mit diesem Mac gepaart, kein
App-Store-Connect-Schlüssel zum Nachtragen.
Neu: companion-app/scripts/ios-signieren.sh macht Bauen, Synchronisieren,
Signieren und den .ipa-Export zu einem Befehl. Nötig, weil ios/ absichtlich
gitignored ist und jede in Xcode geklickte Signatureinstellung beim nächsten
npx cap add ios wieder verschwinden würde -- die Team-Kennung braucht eine
versionierte Heimat.
Bewusst nicht getan: eine unsignierte .ipa als Platzhalter einchecken. Sie
wäre nicht installierbar und läge als Binärdatei dauerhaft in der Historie.
Live verifiziert: /api/ liefert jetzt 401 statt der generischen NPM-Fehlerseite - der
Proxy-Vertrauen-Fix (X-Forwarded-For/trusted_proxies, seit Migration über die HA-UI statt
YAML gesetzt) greift. Zusätzlich den include-Datei-Bug beim NPM-Add-on genauer beschrieben
und einen Hinweis ergänzt, dass der abschließende location-/-Block nach Debug-Tests immer
wieder eingefügt werden muss - sonst landet jede Anfrage ungefiltert bei Home Assistant.
Neuer Schritt 6 mit den Cloudflare-/NPM-Sicherheitseinstellungen (SSL-Modus,
HSTS, Bot Fight Mode, DNS-Aufräumen, Token pro Gerät, Router-Portcheck) und
dem größten praktischen Stolperstein (Tunnel-Hostname zeigt versehentlich
direkt auf HA statt auf den Reverse Proxy). Nachfolgende Schritte/Verweise
umnummeriert, ein alter Nummerierungsfehler in der Einleitung mitkorrigiert.
Cloudflared-Add-on jetzt namentlich mit Link genannt und geprüft
(homeassistant-apps/app-cloudflared) - die frühere Bezeichnung "offizielles
Add-on" war ungenau (Community-Add-on, kein Nabu-Casa-Produkt). Festgehalten,
dass der dokumentierte Tunnel-Token-Weg keinen Cloudflare-API-Token braucht.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
REVERSE_PROXY.md verwies auf INTERNET_ZUGRIFF_EINRICHTEN.md, das nie
geschrieben wurde - jetzt vorhanden (Cloudflare-Konto, Nameserver-Umstellung,
Cloudflared/NPM-Add-ons, Pfad-Freigabeliste eintragen, Prüfung).
Dabei einen echten Fehler in der Freigabeliste gefunden: sie ging noch von
einem companion-app-Web-Build unter /local/dm360/ aus (Planungsstand
2026-08-11) - das wurde nie gebaut, die App ist nativ/sideload-only. Ersetzt
durch den tatsächlichen Fernbedarf: das OTA-Bündel unter
/audi_dashboard_static/app/* (@capgo/capacitor-updater). Damit genügt auch
ein einziger Hostname statt der ursprünglich erwogenen App-/API-Trennung.
COMPANION_APP_ARCHITECTURE.md §5 und AGENTS.md entsprechend nachgezogen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erst zwei Zeilen (festes Beispiel + zugeordneter Sensor) versucht, aber die
erfundenen Beispiele waren für Reichweite/Sofort-Aktualisierung schlicht
falsch. Jetzt nur noch eine Zeile mit dem tatsächlich zugeordneten Sensor,
"(z. B. ...)" in Label-Größe/-Farbe, der Sensorname selbst wie die Einheit
daneben ([on/off], [km]); für Listenfelder (Türen/Fenster) ganz entfernt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Die Pfad-Freigabeliste ging noch von pyscript.audi_dashboard_*/
pyscript.reifen_* und /api/services/pyscript/... aus - seit der Umstellung
auf die native Integration (2026-08-23) heißen Entitäten
sensor.audi_dashboard_* und Dienste audi_dashboard.<name>. Ergänzt um zwei
seither neu hinzugekommene Bedarfe, die in der alten Fassung fehlten:
homeassistant.restart (Update-Neustart-Knopf) und /local/dm360/* (die
ausgelieferte App selbst - ohne diesen Pfad hätte sie über den Tunnel gar
nicht geladen).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Neues Profilfeld fahrzeug.fin_automatisch (Standard: an).
- identitaet.py: bei "aus" läuft die Ableitung (samt Mehrfach-Sensor-Abgleich)
gar nicht erst; bei "an" überschreibt der abgeleitete Wert auch einen
vorher von Hand eingetragenen - der Schalter selbst entscheidet jetzt, statt
aus einem leeren Feld zu raten.
- dienste.py: profil_schreiben löst die Ableitung jetzt auch aus, damit das
Umschalten des Schalters selbst sofort wirkt.
- Panel: Schalter "automatisch" zwischen Label und Textfeld in der
Fahrgestellnummer-Zeile, Feld wird bei "an" deaktiviert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Neues identitaet.py: durchsucht das Geräte-Register der aktuell im Setup
zugeordneten Sensoren (cupra_eu_data_act, FMM003, ...) nach einem
VIN-förmigen Geräte-Identifier/Seriennummer, statt ein eigenes Setup-Feld zu
verlangen. Mehrere übereinstimmende Quellen werden akzeptiert, abweichende nur
geloggt statt geraten; eine bereits von Hand eingetragene FIN wird nie
überschrieben. Läuft nach jedem Start und nach jeder Setup-Änderung
(koordinator.py/dienste.py).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- .teaser-Klasse auf die Kachel gesetzt (teaser()).
- .teaser .leaf .v: min-width:72px (gleiche Spaltenbreite je Zeile).
- .teaser .leaf .k: flex:1 1 auto, damit unterschiedlich lange Labels nicht
mehr über justify-content:space-between den ganzen Rest der Zeile
verschieben - vorher blieben .v-Boxen zwar gleich breit, saßen aber an
unterschiedlichen X-Positionen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- distanzVorschlag(): liest jetzt zuerst den Live-Wert von TANK_DISTANZ_SENSOR
(wie backendseitig schon in distanz_seit_tankung()), fällt ohne Zuordnung
auf die bisherige FILLS-Berechnung zurück (jetzt distanzVorschlagBerechnet()).
- .leaf .v: gap:3px ergänzt (fehlte im Gegensatz zu .leaf .k) - die zweizeiligen
Werte in der "Zuletzt"-Kachel (Übersicht) berührten sich sonst.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- FELDER-Katalog: "(Datum)"/"(Restkilometer)"/"(Liter)"/"(Knopf)"/"(Fahrertür)"
aus sieben Labels entfernt - die Unterscheidung übernimmt bereits das
[Einheit]-Label neben der Überschrift (feldEinheitAnzeige()).
- .setup-popup auf max-width:760px verbreitert (eigener Wert, getrennt von
.sdpopup/.sheet/.beleg-popup), damit lange Rohsensornamen nicht mehr
umbrechen müssen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- entitaetIdKurz(): Präfix per Stimmenmehrheit über alle zugeordneten Sensoren statt
des einzelnen nächsten Nachbarn - verhindert Über-Kürzung durch zufällig geteilte
Namensteile zwischen verwandten Feldern (can_fuel_volume->fuel_volume,
oil_change_due->due waren falsch).
- .setup-feld-hinweis: Ellipsis-Kürzung entfernt, voller Sensorname sichtbar.
- entitaetKandidaten(): "Nur passende Sensoren anzeigen" filtert jetzt auch nach
device_class für Datum/Timestamp-Felder, nicht nur nach Domain/Einheit.
- tankFelder(): Kilometerstand/Getankte Liter in .mitEinheit gewrappt (wie alle
anderen km/l-Felder im Rest der App).
- distanzVorschlag(): negative Vorschläge (fehlerhafte km-Basis eines vorherigen
Tankvorgangs) werden nicht mehr angezeigt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Zwei weitere Korrekturrunden, beide vom Nutzer live erkannt:
- .6: paarweise Abstimmung statt fortlaufender Verengung - robuster gegen
einen einzelnen Ausreisser, aber grundsaetzlich noch falsch.
- .7: die eigentliche Ursache war, dass diese Installation zwei etwa
gleich grosse Sensorquellen gleichzeitig hat (9 FMM003-Rollen, 18 Rollen
einer zweiten Integration mit "audi_rs_4_avant_"-Praefix) - ein
EINZELNES globales "Sieger"-Praefix kann immer nur eine der beiden
Quellen richtig kuerzen. entitaetIdKurz() sucht jetzt pro Feld den
naechsten Verwandten unter allen zugeordneten Sensoren und kuerzt nur
dessen gemeinsames Praefix - unabhaengig von Anzahl und Groesse der
Quellen.
Diesmal vor dem Deploy anhand der ECHTEN sensor.audi_dashboard_entitaeten-
Zuordnung durchgerechnet statt mit erfundenen Test-IDs (das hatte den
.6-Fehler faelschlich als behoben erscheinen lassen). Version 2026.8.28.7,
live verifiziert: alle 20 Einzelwert-Felder zeigen die richtige, kurze
Kennung, keine installationsspezifischen Reste aus beiden Quellen mehr.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Korrektur zur letzten Aenderung, vom Nutzer live erkannt:
- "fmm003_" war hart einprogrammiert und liess trotzdem noch
installationsspezifisches Rauschen stehen (Geraete-Slug, Instanzname der
Integration). Jetzt wird das laengste gemeinsame Praefix ueber alle
aktuell zugeordneten Sensoren berechnet (setupGemeinsamesPraefix()) -
nichts mehr hartkodiert. Faellt bei einer einzelnen Entitaet aus
anderer Quelle korrekt auf die volle ID zurueck, statt falsch zu kuerzen
(live bestaetigt: TANK_DISTANZ_SENSOR zeigte den vollen Namen, weil er
auf eine andere Integration zeigte).
- Der Live-Wert des Sensors gehoert nicht mehr neben die ID - stattdessen
zeigt das Label selbst jetzt die ERWARTETE Art des Werts an
("Tankfuellstand [%]", "12V-Batteriespannung [V]"), statisch aus dem
FELDER-Katalog abgeleitet, unabhaengig von einer aktuellen Zuordnung.
Version 2026.8.28.5, live im Testcontainer verifiziert (acht echte
Setup-Zeilen gegen die Nutzerbeispiele geprueft).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
companion-app nannte Servicebuch-Feldnamen betrieb/notiz, waehrend Panel und
Backend (profil["service"]["buch"]) werkstatt/kosten/arbeiten verwenden -
ohne Uebersetzung dazwischen zeigten in einer Oberflaeche angelegte
Eintraege in der anderen leere Werkstatt-/Kosten-Werte. werkstatt/kosten/
arbeiten als kanonisch uebernommen (aeltere, bereits etablierte Panel-
Konvention); ServicebuchEintrag, das Bearbeitungsformular (inkl. neuem
Kosten-Feld) und die Ansicht in Service.tsx angepasst, den Workaround-Cast
im CSV-Export (DatensatzPopup.tsx) sowie die Testfixture entsprechend
bereinigt.
Setup-Popup (Panel, Item 11 aus der letzten Sammel-Rueckmeldung): die
statische Beschreibung unter jedem Feldnamen ist jetzt die Kennung und der
aktuelle Wert des tatsaechlich zugeordneten Sensors.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- "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>
- 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 <noreply@anthropic.com>
- Archiv-Knopf jetzt in Zeile mit "Montiert" (stale margin-top entfernt),
bekommt einen sichtbaren Hintergrund
- companion-app: Wechseltermin-Datumsfeld (appearance-Reset gegen
natives Safari-Chrome, Breite auf Inhalt geschrumpft)
- Mein Audi/Reifen-Box: "montiert: Sommerräder" links statt Marke/Modell,
"offen" durch "-" ersetzt
- "Servicebuch" ueberall in "Wartungsplan" umbenannt (nur Anzeigetext,
beide Codebasen)
- Version-Kachel: tote Fahrzeugdaten-/Position-/Dashboard-Zeilen entfernt
(inkl. der nie aktualisierten CONFIG.version-Konstante)
Version 2026.8.28.1, live im Testcontainer verifiziert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.leaf (Panel) und .dm-listenzeile (companion-app) galten fuer echte
Navigations-Buttons UND fuer die reine <div>-Messwertliste gleichermassen -
Hand-Cursor und Tipp-Rueckmeldung suggerierten dort faelschlich Klickbarkeit,
obwohl die Liste nur Wischen-zum-Loeschen unterstuetzt. Beide Regeln jetzt
auf button.leaf/button.dm-listenzeile beschraenkt - echte Zeilen-Buttons
(Fahrten, Tankvorgaenge, Servicebuch) bleiben unveraendert klickbar.
Version 2026.8.27.22, live verifiziert (Cursor auto statt pointer bei der
Messwertliste, pointer weiterhin bei echten Zeilen-Buttons).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Der RAM-only Fallback fuer die Zustand-Kachel wurde bei jedem Neustart
geleert und zeigte "unbekannt", solange seit dem Neustart keine echte
Live-Messung eintraf (Dongle bei ausgeschaltetem Fahrzeug erwartungsgemaess
immer offline). spannung_cache_vorladen() laedt ihn jetzt beim Start aus
der gespeicherten Historie vor, begrenzt auf den vereinbarten Bereich
10,0-13,0 V (SPANNUNG_MIN_V bis AGM_RUHE_MAX_V) - eine Generatorspannung
(z.B. 15,4 V) soll dort nie als Batteriespannung erscheinen.
Version 2026.8.27.21, im Testcontainer verifiziert (12,149 V statt
"unbekannt", trotz Dongle weiterhin 0 V live).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- companion-app Batterie.tsx: Generatorspannung zeigte sich faelschlich als
Ruhespannung/verzerrte den SOH-Trend, Panel-Filter (AGM_RUHE_MAX_V) fehlte
- Manuell angelegte Fahrten/Tankvorgaenge trugen naive Zeitstempel und wurden
von der Import-Dublettenpruefung stillschweigend uebersprungen; neue
gemeinsame zeit_normalisiert() in verlauf.py schliesst die Luecke
- Batteriespannungsverlauf fehlte im Backup (sicherung.py)
- Tankvorgangs-Import verlor stillschweigend aeltere Tankvorgaenge vor Beginn
der Litersensor-Historie; laeuft jetzt zweigleisig (Prozent + Liter)
- Panel-Statistikseite behauptete faelschlich, der Verbrauch je Fahrt komme
vom Fahrzeug (OBD) - ist eine Naeherung aus dem Tankfuellstand
- companion-app Statistik.tsx: Arbeitsweg-Segment war noch rot statt neutral
Version 2026.8.27.19, OTA-Buendel neu gebaut, im Testcontainer verifiziert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Beim Zurueckbauen von bvPunkte/bvRuhePunkte auf eine einzige gefilterte
Liste (voriger Commit) blieb eine Fundstelle unbeachtet: die SOC-Berechnung
in vBatterieverlauf() las weiterhin aus dem entfernten bvRuhePunkte. Jeder
Klick auf "Batteriespannung" liess render() mit einem ReferenceError
abbrechen, bevor die Zielseite ueberhaupt gezeichnet wurde - von aussen
ununterscheidbar von "Klick tut nichts". Live im Browser gefunden (Konsole
zeigte den echten Fehler), nicht durch Codelesen - node --check haette das
nie gefangen, da es nur Syntax prueft, keine Laufzeitreferenzen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Der Nutzer fuehrte ein Selbst-Update aus, das Gitea's letzten committeten Stand
(80d73e8) zog - alle Fixes dieser Sitzung waren bis dahin nur per docker cp an
audi_ha_test ausgeliefert, nie committet. Manifest fiel auf 2026.8.27.8 zurueck,
alle Symptome (Batteriespannung "unbekannt", Diagramm-Maximalwert 13,99V) passten
exakt zum Vor-Sitzungs-Stand. Container wiederhergestellt, jetzt committet -
Lehre fuer kuenftige Sitzungen im Dokument festgehalten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Statistik-Tab-Icon per zentrierter Stauchung an die Fuellflaeche der
anderen Tab-Icons angeglichen (Panel + companion-app), auf Nutzerwunsch
anschliessend 10% groesser.
- Batteriediagramm neu gezeichnet: dynamische viewBox (1 Einheit = 1 Pixel,
kein preserveAspectRatio-Verzerren mehr), ein Punkt je Ruhespannungsmessung
statt Datumsbeschriftung, Tageshoechstwert nur noch im Tooltip. Generator-
spannungen (> AGM_RUHE_MAX_V = 13,0 V, vom Nutzer festgelegt) bleiben aus
dem Diagramm aussen vor; der SOC-Kurve-Eckwert "100% / voll" bleibt bei den
ursprünglich recherchierten 12,8 V. companion-app auf dieselbe lineare
SOC-Interpolation umgestellt (vorher eine abweichende Stufenkurve).
- Außentemperatur wird jetzt auch beim Batterieverlauf-Import nachgetragen
(vorher nur live) - Kopplung an die naechstgelegene Messung innerhalb
NEBENWERT_MAX_ABSTAND_S (verlauf.py), vom Nutzer nach Live-Daten-Analyse
auf 300s festgelegt. Merge-Logik in ablage.py ergaenzt: ein erneuter Import
kann eine zuvor fehlende Temperatur jetzt tatsaechlich nachtragen, auch
wenn sich der Tagesminimalwert selbst nicht aendert.
- "Mein Audi"/Zustand zeigt bei unplausibler Live-Messung (z. B. Dongle
offline) die zuletzt gemessene Spannung statt "unbekannt".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
Die aspect-ratio-Lösung für die volle Spaltenbreite ließ auf großen Bildschirmen
die gesamte Koordinatenfläche mitwachsen - Achsenbeschriftungen (fester font-size
in SVG-Einheiten) wurden dadurch auf ~26px zu groß, das Diagramm unnötig hoch.
Zurück auf eine feste 170px-Höhe: der Y-Maßstab bleibt immer 1:1, nur die
X-Achse dehnt sich auf die volle Breite - normale Schriftgröße, kompaktes
Diagramm, trotzdem volle Spaltenbreite.
bvSkalaY()/y() klemmen jetzt auf [0,1] statt roh zu extrapolieren - ein Punkt
außerhalb von yMin/yMax (z. B. ein Ausreißer auf einer noch nicht aktualisierten
Instanz) zeichnet sich sonst weit außerhalb der Fläche und reißt die Linie über
den Rand hinaus, statt sichtbar am Achsenrand zu liegen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Die Y-Achse beider Batteriespannungs-Diagramme (Panel und companion-app) war bisher
aus den sichtbaren Punkten berechnet - jetzt fest 10-15V, auf Nutzerwunsch (zunächst
8-15V, dann auf 10-15V korrigiert). SPANNUNG_MIN_V (batterie.py) auf denselben Wert
angehoben, damit die Plausibilitätsgrenze zur Achse passt - historienimport.py
übernimmt den Wert automatisch, da er von dort importiert statt dupliziert wird.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
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>
Die Zündungs-Entität stammt vom FMM003-Tracker; verliert er unterwegs
kurz die Verbindung, meldet Home Assistant "unavailable" statt eines
echten Zündungszustands. Die Live-Erkennung wertete das bisher wie
"aus" - eine Funklücke über der Pausenzeit teilte eine durchgehende
Fahrt in zwei. zuendung_geaendert() ignoriert "unavailable"/"unknown"
jetzt vollständig und leitet "fährt gerade" aus dem eigenen
Zwischenstand (fahrt_start_ts) statt aus dem letzten Rohwert ab.
historienimport.py bekommt zusätzlich eine Plausibilitätsprüfung: eine
errechnete Durchschnittsgeschwindigkeit über 300 km/h deutet auf einen
Kilometerstand-Ausreißer an der Fahrtgrenze hin, nicht auf eine echte
Fahrt - die Strecke wird dann verworfen statt eine unmögliche Fahrt
anzuzeigen.
Tankerkennung: optionaler zweiter Sensor für das Tankvolumen in Litern
(TANK_LITER_SENSOR, direkt vom CAN) neben dem bisherigen
Prozent-Füllstand. Ist er zugeordnet, übernimmt er die automatische
Tankerkennung vollständig - genauer als der Umweg über das im
Fahrzeugprofil hinterlegte Tankvolumen, und der erkannte Anstieg
liefert gleich eine grobe Vorbelegung für die getankte Menge statt
eines leeren Feldes. Ohne den Sensor bleibt alles beim Alten. Der
historische Import zieht mit derselben Präferenz nach.
Manifest auf 2026.8.25.2, beide Änderungsrunden live im Testcontainer
verifiziert (py_compile, Neustart, sauberes Setup-Log).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ohne React-Error-Boundary unmountet ein unbehandelter Renderfehler die
gesamte App; übrig bleibt nur der Seitenhintergrund (im Nacht-Theme
praktisch schwarz) - passt zum gemeldeten "schwarze Seite nach Update
prüfen". Behebt nicht die eigentliche Ursache, macht einen künftigen
Absturz aber sichtbar (Fehlertext, Neu-laden-Knopf) und loggt ihn in
die Konsole statt ihn stumm verschwinden zu lassen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dauerText() zeigte "1 Std. 15 Min." - der Besitzer wollte exakt die Schreibweise,
die companion-app/src/format.ts' dauer() bereits verwendet: "1 h 15 min", Minuten
zweistellig gepolstert. Betrifft sowohl die Einzelfahrt-Detailansicht als auch die
Dauer-Anzeige im Fahrt-Bearbeiten-Formular (gemeinsamer Helfer).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
vTrip() zeigte "75 min" statt der bereits vorhandenen dauerText()-Formatierung
("1 Std. 15 Min."), die jede andere Dauer im Panel schon verwendet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Zwei Meldungen des Besitzers ("Update installieren" braucht manuelles
Neuladen, um den Neustart-Knopf zu zeigen; die "HA startet neu..."-
Anzeige ebenso) auf denselben Grund zurückgeführt: datenLaden() rief
render() nur bei Änderungen an profil/fahrten/tank/status/batt auf,
nie bei einer reinen Änderung des Versions-Sensors. Dazu ein bereits
im Code kommentiertes, bisher nur per Tab-Klick umgangenes Rennen
beim echten HA-Neustart gefunden und behoben: panel_custom baut das
Element neu auf, aber nichts zeichnete es automatisch, bis der Nutzer
irgendwo klickte. companion-app erhielt den passenden Fix für die
Neustart-Anzeige (Panel-Bug 1 betraf sie nicht, siehe AGENTS.md).
Alle drei Fixes live im Testcontainer über einen echten
homeassistant.restart bestätigt, nicht nur simuliert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Auf Update prüfen/Update installieren ließ die Seite im Seitenleisten-Layout
sichtbar nach oben springen - render()s eigene Scroll-Wiederherstellung
reichte hier nicht, vermutlich weil die Zustandsänderung des
Update-Sensors zusätzlich über HAs eigenes set-hass-Durchreichen an das
eingebettete Panel einen zweiten Render-Pfad anstößt. Statt die genaue
Ursache weiter zu jagen: fester Scroll-Merkwert vor dem Klick, per
requestAnimationFrame nach jedem eigenen render()-Aufruf dieses Ablaufs
erneut erzwungen. Live bei 1280x800 verifiziert: kein Sprung mehr.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>