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>
This commit is contained in:
2026-08-31 15:35:56 +02:00
parent 4af38d7e65
commit 90efcc5b00
19 changed files with 565 additions and 161 deletions
+140 -1
View File
@@ -1,6 +1,8 @@
# AGENTS.md — Project state, review findings, open items, and working rules
**Last updated: 2026-08-30** (owner findings batch - vehicle pin, tile chevrons, select arrows,
**Last updated: 2026-08-31** (Neustart-Festhänger der App, OTA-Prüfsumme, Statistik-Kopfzahl,
Setup-Filter und die gemessene Wahrheit über die Dongle-Konfiguration; Manifest `2026.8.31.4`,
siehe Abschnitte AT und AU. Davor: 2026-08-30 (owner findings batch - vehicle pin, tile chevrons, select arrows,
SmartDeal dialog, address cache and pull-to-refresh; manifest `2026.8.30.19`, see sections AO, AP and AQ.
Before that: design-audit follow-through - the five priority items applied to
both codebases, plus the location preview's wrong pin; manifest `2026.8.30.8`, see section AN.
@@ -6050,6 +6052,143 @@ rendering bug in a view.
---
## AT. Neustart-Festhänger, OTA-Prüfsumme, Statistik-Kopfzahl, Setup-Filter (2026.8.31.4)
### Die App blieb nach einem Neustart von Home Assistant hängen
Gemeldet: während Update und Neustart zeigte die App „502 Error" unter einem längst
gedrückten „Jetzt neu starten", und die Meldung blieb stehen, nachdem Home Assistant
wieder lief — erst ein Beenden der App half. Zwei Ursachen, beide in
`DatenKontext.tsx`:
1. **502 galt als harter Ladefehler.** `ApiFehler.istNetzproblem` ist nur `status === null`;
ein Vorschaltserver, der antwortet, während Home Assistant noch hochfährt, liefert
aber 502/503/504. Neu: `istVoruebergehend` fasst beides zusammen (500 bewusst **nicht**
das bessert sich durch Warten nicht). Solange schon Daten da sind, erscheint keine
Fehlerzeile mehr.
2. **Nach dem Wiederverbinden wurde genau einmal nachgeladen.** Die Websocket-Verbindung
steht regelmäßig ein paar Sekunden vor der REST-Schnittstelle. Schlug dieser eine
Versuch fehl, kam nie wieder ein `"verbunden"` — und damit nie wieder ein Versuch; die
App blieb bis zum Beenden auf dem Stand von vor dem Neustart, mitsamt Fehlerzeile.
Neu: `nachladenBisEsKlappt()` mit wachsenden Abständen (2/4/8/15/30/60 s, zusammen gut
zwei Minuten), sauber abgeräumt beim Verlassen.
Dazu ein Fehler, den dieselbe Stelle verdeckt hat: `neuLaden` hing an `bereit`, der Effekt
darunter aber nur an `api` — die dort festgehaltene Fassung sah `bereit` für immer als
`false`. `bereit` liegt jetzt in einem Ref, `neuLaden` ist stabil. Vier Regressionstests
(`DatenKontext.test.tsx`), gegen den alten Stand als fehlschlagend nachgewiesen.
Panel-seitig kein Gegenstück nötig: es hängt an Home Assistants eigener Verbindung, deren
Wiederanlauf schon in Abschnitt N behandelt ist.
### „App-Update: failed to download / checksum failed"
**Das Bündel ist nachweislich in Ordnung.** Geprüft von Ende zu Ende: der Blob bei
`HEAD`, die Datei auf der Platte und die tatsächlich über HTTP ausgelieferten Bytes
haben alle denselben SHA-256, den auch `bundle.json` nennt; git behandelt die Zip als
binär (`i/-text`), `core.autocrlf` kann ihr nichts anhaben. Die Prüfsumme, die das
Plugin vergleicht, ist genau dieser Wert (`CapgoUpdater.swift`, `calcChecksum` über die
heruntergeladene Zip vor dem Entpacken).
Der Fehler fiel zusammen mit dem Neustart — ein Download, der mitten hineinläuft, liefert
je nach Zeitpunkt einen Abbruch („failed to download") oder unvollständige Bytes
(„checksum"). Zwei Änderungen in `ota.ts`:
- Die Fassung hängt jetzt als `?v=` an der Bündel-Adresse. Home Assistant liefert
`/audi_dashboard_static/` **ohne** `Cache-Control`, aber mit `ETag` und
`Last-Modified` (nachgemessen) — genau der Fall, in dem jeder Zwischenspeicher nach
eigenem Ermessen eine Weile die alte Datei behalten darf. Dieselbe Vorsorge trifft das
Panel seit jeher für sein eigenes Bündel.
- Ein Prüfsummenfehler wird nicht mehr wortwörtlich durchgereicht, sondern erklärt: „Das
Update kam unvollständig an … sobald Home Assistant wieder läuft, noch einmal
versuchen."
### Statistik: der Gesamtwert steht in der Kopfzeile
Erst sollte „Absolut" eine fünfte Spalte links werden. Am laufenden Panel gemessen passt
das nicht: die Kachel ist auf dem Telefon innen **307px** breit, ein sechsstelliger
Kilometerstand wie `209.177` misst in Audi Type Wide 300 **67px** bei 16px und 54px bei
13px — fünf Spalten gingen nur mit kleinerer Schrift. Der Eigentümer hat daraufhin
entschieden, den Gesamtwert dorthin zu setzen, wo man ihn zuerst liest: **die große Zahl
oben in „Distanz und Fahrzeit" und „Getankte Menge und Kosten" ist jetzt der Gesamtwert,
nicht mehr der des laufenden Jahres** — die Jahreszahl im Titel entfällt entsprechend.
Darunter bleiben vier Spalten (Jahr/Monat/Woche/Tag), jetzt als `repeat(4, minmax(0,1fr))`
statt `auto-fit`, das auf dem Telefon nur drei Spalten zuließ und die vierte umbrach.
`ZEITRAEUME` rechnet weiter alle fünf, angezeigt wird `ZEITRAUM_SPALTEN`.
### Kein Schatten, kein Beschnitt — und kein Schalter mehr dafür
Gemessen statt geschätzt: das Foto ist 2000×1125 (16:9), der Kasten stand auf 16:8 und
`object-fit: cover` schnitt oben und unten je ein Viertel ab — das Dach lag darin. Statt
das Verhältnis auf 16:9 festzuschreiben (was beim nächsten Foto in einem anderen Format
wieder schneiden würde) bestimmt jetzt das Bild selbst die Höhe; der Platzhalter behält
sein festes Verhältnis, er hat keines von sich aus. Nachgemessen: Kasten 367×207 zu
2000×1125 — kein Beschnitt.
Gleichzeitig sind der Ausblend-Verlauf und der Bodenschatten entfallen (beide Codebasen,
inklusive der `height:220px`-Regel im iOS-Overlay, die den Beschnitt sonst auf breiten
Schirmen wieder erzwungen hätte). Damit hatte der Schalter **„Fahrbahn unter dem
Fahrzeug"** nichts mehr zu schalten und ist ebenfalls weg — samt `bodenAn`/`szeneKlasse()`
im Panel und `useBoden`/`bodenKlasse`/`OHNE_SZENE` in der App. Der Regressionstest für
den geteilten Einstellungsspeicher (`theme.test.tsx`) prüft das jetzt an
`useTabBeschriftung` statt an `useBoden` — dieselbe Aussage, ein noch vorhandener Schalter.
### Setup: „Nur passende Sensoren" sucht nach dem Signalnamen
Domain und Einheit lassen bei einem Fahrzeug mit siebzig Sensoren immer noch Dutzende
übrig. Der Name, der neben der Überschrift als „(z. B. …)" steht, ist der tatsächliche
Signalname der Integration (`beispiel` in `FELDER`) und trifft genau einen. Er filtert
jetzt mit — **mit Rückfall**: findet er nichts, bleibt die ungefilterte Liste stehen, denn
er stammt aus dieser einen Installation. Zusätzlich zählt er in `entitaetScore()` mit 8
Punkten, damit auch der Vorschlag für ein leeres Feld davon profitiert.
## AU. Der Dongle: was die Konfiguration wirklich bewirkt (2026-08-31)
Nachgemessen an der echten Aufzeichnung im Testcontainer (7 bzw. 30 Tage Recorder), nicht
aus der Konfigurationsanzeige abgelesen.
**Was gut funktioniert.** Beim Fahren liefert das Gerät alle **10 s** (p90) einen Satz —
das ist die eingestellte `Min Period` für „Moving", und sie gilt für Geschwindigkeit wie
für Position gleichermaßen (771 bzw. 1165 Punkte in 7 Tagen). Für Strecke, Route,
Durchschnitt und vmax reicht das: in denselben 7 Tagen wurde ein Höchstwert von 118 km/h
erfasst. `can_fuel_volume` löst **0,1 l** auf (Werte wie 18,9 / 19,1 / 19,5 über 30 Tage;
in einzelnen Wochen sieht man nur ganze Liter, das ist die Meldung des Fahrzeugs, nicht
die Auflösung des Sensors) — die Grenze `MIN_STRECKE_VERBRAUCH_KM = 3.0` bleibt damit
richtig hergeleitet. Der Füllstand in Prozent springt in 1-%-Schritten.
**Was nicht funktioniert: das rohe Zündsignal als Fahrterkennung.**
`engine_ignition_or_acc_status` ist der ungefilterte Zündungseingang. In der Aufzeichnung:
am 29.08. drei Wechsel innerhalb von **30 Millisekunden**, danach ein Aus/Ein-Paar mit
**10 Sekunden** Abstand — und am 30./31.08. dreizehn Stunden am Stück „an", ohne Fahrt.
Solange die App eine Pausentoleranz hatte, hat die das aufgefangen. Seit deren Entfernung
(Abschnitt AS) trennt jeder solche Aussetzer eine Fahrt in zwei.
**Die Begründung für das Entfernen war falsch.** Der `Ignition OFF Timeout` von 900 s
unter *Trip \ Odometer* verzögert **nicht** dieses Signal, sondern ausschließlich das
geräteeigene Trip-Element ([Teltonika-Wiki](https://wiki.teltonika-gps.com/view/FMM003_Trip/Odometer_settings):
„timeout value to detect Trip end once the Ignition … is off"). Es gab also nie zwei
Zeitgeber hintereinander — es gab unseren, und daneben einen ungenutzten des Geräts.
**Die Empfehlung, noch nicht umgesetzt (Entscheidung des Eigentümers):**
`ZUENDUNG_SENSOR` auf
`binary_sensor.…_trip_status_true_if_trip_started_false_if_stopped` legen. Dieses Signal
- beginnt erst, wenn Zündung **und** Bewegung **und** `Start Speed` zusammenkommen —
dreizehn Stunden Standlicht-Zündung erzeugen damit keine Fahrt,
- endet erst 900 s nach Zündung aus — die Pausentoleranz kommt vom Gerät, ohne dass wir
einen zweiten Zeitgeber bauen,
- ist millisekundenfestes Prellen los.
Der Preis: das Fahrtende liegt 15 Minuten hinter dem tatsächlichen. Das war mit der
eigenen Pausentoleranz genauso und ist damit kein Rückschritt; wer es genauer will, müsste
das Ende nachträglich auf den letzten Punkt mit Geschwindigkeit > 0 zurückrechnen — eigenes
Arbeitspaket, hier bewusst nicht angefangen.
**Weiter offen, unabhängig davon:** echte Funklöcher. Im gemessenen Zeitraum gab es
mitten in der Fahrt eine Lücke von **2739 s** ohne einen einzigen Satz. Dagegen hilft keine
Einstellung am Gerät; das ist der in Abschnitt P/W beschriebene Fall, den die
Plausibilitätsgrenze (`UNPLAUSIBLE_KMH`) abfängt.
## Working conventions (observed — keep them)
- German is the project language: identifiers, comments, commits, UI texts. Exceptions: