QR-Code aus gespeichertem Bild, stiller Debug-Knopf, HA-Zugang bei den Zugaengen
Der Scanner liest den QR jetzt auch aus einer gespeicherten Bilddatei - in der Testumgebung ohne Kamera der einzige Weg, auf dem Telefon der kuerzere (der Token entsteht am Rechner, das Bildschirmfoto liegt ohnehin auf dem Geraet). Gleicher Leser, gleiche Deutung, nur eine andere Quelle; ohne Kamera entfaellt das schwarze Quadrat. Dabei ein echter Fehler in der ersten Fassung, gefunden an der Musterseite: img.decode() loest bei einem GIF in Chromium nie auf - das Blatt haette ohne Meldung ewig gewartet. Jetzt createImageBitmap, als Rueckfall bewusst das load-Ereignis statt decode(). Debug-Mode ist nur noch ein stiller Textknopf am Seitenende, ohne Kachel und ohne Erlaeuterung; der Zugang zu Home Assistant steht als Akkordeon in der Kachel "Zugaenge" darin. Beides auf Vorgabe des Eigentuemers. Die Musterseite kann jetzt auch die Ersteinrichtung (?seite=einrichtung) - damit ist der ganze Weg ohne echten Token nachgewiesen: selbst erzeugter QR mit Token-Text landet zeichengleich im Feld, einer mit http-Adresse im Adressfeld, ein Bild ohne Code meldet das sauber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,10 @@ dazu die Kopfmarke statt der Firmierung des Betreibers und ein
|
||||
Cache-Brecher auf der .ipa-Adresse; die .ipa liess sich zuletzt nicht mehr
|
||||
installieren - der Erweiterung fehlte die CFBundleVersion; dazu der QR-Scanner
|
||||
fuer den Zugangs-Token; dazu Debug-Mode, eine Version-Kachel, das Knopfpaar nach
|
||||
HIG und Bilder ohne Verzoegerung, `2026.9.4.11`,
|
||||
Abschnitte CC bis CF. Davor: Geraetezeit statt Ankunftszeit - die Wurzel hinter der 3-km-Fahrt:
|
||||
HIG und Bilder ohne Verzoegerung; dazu eine Musterseite, die die App ohne Token
|
||||
und ohne Netz rendert; dazu der QR-Code aus einem gespeicherten Bild, ein stiller
|
||||
Debug-Knopf und der HA-Zugang bei den Zugaengen, `2026.9.4.12`,
|
||||
Abschnitte CC bis CH. Davor: Geraetezeit statt Ankunftszeit - die Wurzel hinter der 3-km-Fahrt:
|
||||
das Fahrtfenster stand in Geraetezeit, der Verlauf war nach Ankunftszeit sortiert. Dazu die neun
|
||||
Paritaetsbefunde und die verunreinigte Batteriehistorie, `2026.9.4.1`, Abschnitt CB. Davor: Der Regler stellt den Schlaf-Timeout des Dongles, flespi lesend und
|
||||
schreibend, die eigene Warteschlange wieder entfallen, `2026.9.3.10`, Abschnitt BZ. Davor: Zugaenge: die Dienste-Token aus App und Panel bedienbar, der Wert kommt nie
|
||||
@@ -9942,3 +9944,160 @@ ist ausgeschlossen. Abgedeckt sind sie durch `tsc`, den
|
||||
Alle-Seiten-Rendertest und die Tatsache, dass das Panel dieselbe Struktur aus
|
||||
denselben Backend-Daten baut - der Zweig MIT Datensatz ist in der App damit
|
||||
strukturell, nicht optisch belegt.
|
||||
|
||||
## CG. Eine Musterseite loest den wiederkehrenden Pruefblocker (2026.9.4.12)
|
||||
|
||||
**Das Problem, das seit Wochen wiederkehrt:** die App zeigt ohne gespeicherten
|
||||
Zugang gar nichts, jede OPTISCHE Pruefung haengt damit an einem Zugangstoken -
|
||||
und fast jeder Fehler, den dieses Projekt findet, ist nur am gerenderten Bild
|
||||
sichtbar (CSS-Spezifitaet, Flex-Basis in Spalten, ein unsichtbarer Rahmen).
|
||||
`tsc` und die Tests finden diese Klasse nachweislich nicht (Abschnitt S).
|
||||
|
||||
**`companion-app/muster/`** mountet dieselben Bildschirme mit demselben
|
||||
Stylesheet, aber mit `beispielApi()` aus den Tests: kein Netz, kein Token,
|
||||
keine echten Daten - und trotzdem echtes CSS. Gebaut wird sie ueber eine
|
||||
**eigene** Konfiguration (`vite.muster.config.ts`) nach `dist-muster/`; der
|
||||
normale `npm run build` fasst sie nicht an, sie liegt also auch nicht in der
|
||||
`.ipa`. Aufruf mit `?seite=<name>` fuer jeden Namen aus `TITEL`.
|
||||
|
||||
Ausgeliefert wird sie bei Bedarf in den Testcontainer:
|
||||
|
||||
```bash
|
||||
npx vite build --config vite.muster.config.ts
|
||||
MSYS_NO_PATHCONV=1 docker cp companion-app/dist-muster/. audi_ha_test:/config/www/dmmuster/
|
||||
# http://localhost:18123/local/dmmuster/index.html?seite=einst
|
||||
```
|
||||
|
||||
### Was sie sofort gefunden hat
|
||||
|
||||
**Der Zielrahmen des QR-Scanners war unsichtbar.** `border: 2px solid var(--fg)`
|
||||
liegt auf dem Kamerabild - und das ist dunkel. Im Tagmodus ist `--fg` fast
|
||||
schwarz, der Rahmen also nicht zu sehen. Jetzt fest weiss mit einem dunklen
|
||||
Saum darum, damit er auch vor einer hellen Wand ablesbar bleibt. Genau die
|
||||
Sorte Fehler, die kein Test findet.
|
||||
|
||||
### Was sie bestaetigt hat
|
||||
|
||||
- **Letzter Datensatz**: Geraet, Ankunft, Geraetezeit mit Rueckstand
|
||||
(40.686 s), Werte samt Einheit, Fussnote - erst nach dem Aufklappen.
|
||||
- **Version**: „Installiert 2026.9.4.11 / Home-Assistant-Integration" und der
|
||||
Update-Teil in EINER Kachel.
|
||||
- **Knopfpaar**: „Verwerfen" und „Speichern" exakt **148 x 50 px** beide,
|
||||
nebeneinander, der bestaetigende gefuellt und rechts.
|
||||
- Zum ersten Mal live in der App gesehen: der Schalter ist **gruen** und der
|
||||
Nacht/Tag-Umschalter zeigt die gefuellte Bahn - beides Korrekturen aus
|
||||
Abschnitt AL, bis dahin nur strukturell belegt.
|
||||
|
||||
### Die Bild-Verzoegerung, gemessen
|
||||
|
||||
Am selben Bild (`seitenansicht.webp`, 1,2 MB, 2000x1125) in der Musterseite:
|
||||
|
||||
| Fall | Dauer bis das Bild fertig ist |
|
||||
|---|---|
|
||||
| frisch geholt und dekodiert | 28,7 ms |
|
||||
| **im HTTP-Zwischenspeicher, erstes Dekodieren** | **14,3 ms** |
|
||||
| nach `decode()` vorab (was `bilderVorladen()` hinterlaesst) | **0,1 ms** |
|
||||
|
||||
Die mittlere Zeile ist genau der gemeldete Menuewechsel: die Datei liegt im
|
||||
Cache, das Dekodieren kostet trotzdem. Das Vorladen entfernt es vollstaendig.
|
||||
|
||||
**Der Preis, und er gehoert genannt:** die vier vorhandenen Fotos sind zusammen
|
||||
**5,3 MB**, und `bildVersion` haengt an `Date.now()` - die Adresse aendert sich
|
||||
also bei JEDEM App-Start, der HTTP-Zwischenspeicher greift zwischen zwei Starts
|
||||
nie. Bisher zahlte man das nur fuer Bilder, die man ansieht; mit dem Vorladen
|
||||
fuer alle. **Offen, dem Eigentuemer vorgelegt:** entweder so lassen, oder nur
|
||||
das Uebersichtsbild vorladen, oder - die eigentliche Loesung - den
|
||||
Cache-Brecher an eine echte Fotoversion aus dem Backend haengen statt an die
|
||||
Startzeit, dann faellt der wiederholte Abruf ganz weg.
|
||||
|
||||
### Nicht geprueft
|
||||
|
||||
Der Kamerapfad des Scanners (die Vorschau gibt keine Kamera heraus) und das
|
||||
Vorladen in der App selbst - `bildUrl()` braucht dafuer einen gespeicherten
|
||||
Zugang, den die Musterseite bewusst nicht hat. Im Panel ist das Vorladen live
|
||||
belegt (alle neun Plaetze in `performance.getEntriesByType`), und beide
|
||||
Codebasen fuehren denselben Ablauf.
|
||||
|
||||
## CH. Der QR-Code darf auch aus einem gespeicherten Bild kommen (2026.9.4.12)
|
||||
|
||||
### Ohne Kamera war der Scanner eine Sackgasse
|
||||
|
||||
Vorgabe des Eigentuemers: ein Knopf im Scanner-Blatt, der ein **gespeichertes
|
||||
Bild** des QR-Codes einliest. Der Anlass war die Testumgebung ohne Kamera, aber
|
||||
der Fall ist auch auf dem Telefon der haeufigere: der Token entsteht in Home
|
||||
Assistant am **Rechner**, und ein Bildschirmfoto davon liegt danach ohnehin auf
|
||||
dem Geraet. Das Telefon vor den Monitor zu halten ist der Umweg.
|
||||
|
||||
`qrAusDatei()` bekommt dieselben Bilddaten wie der Kamerapfad und benutzt
|
||||
denselben Leser (jsQR) - es ist eine zweite Quelle, kein zweiter Mechanismus.
|
||||
Zwei Unterschiede mit Grund: das Bild wird auf `MAX_KANTE = 2000` verkleinert
|
||||
(ein Handyfoto hat leicht 4000 px, und jsQR laeuft Pixel fuer Pixel darueber),
|
||||
und es wird mit `inversionAttempts: "attemptBoth"` gelesen - ein Bildschirmfoto
|
||||
aus dem Dunkelmodus zeigt den Code hell auf dunkel.
|
||||
|
||||
Ohne Kamera verschwindet ausserdem das schwarze Quadrat: es waere totes Beiwerk,
|
||||
und die Flaeche gehoert dann dem Weg, der noch funktioniert.
|
||||
|
||||
### Der Fund, den erst die Musterseite gezeigt hat: `decode()` bleibt stehen
|
||||
|
||||
Die erste Fassung dekodierte mit `new Image()` plus `await bild.decode()`. Am
|
||||
04.09.2026 an der Musterseite gemessen: bei einem **GIF** loest `decode()` in
|
||||
Chromium **nie** auf - das Bild war laengst geladen (`naturalWidth` 294), das
|
||||
Versprechen blieb offen. Folge: das Blatt haette ohne Fehlermeldung, ohne
|
||||
Kreisel und ohne Absage ewig gewartet.
|
||||
|
||||
Jetzt `createImageBitmap(datei)` (der dafuer vorgesehene Weg, nimmt einen Blob
|
||||
direkt), und als Rueckfall bewusst das **`load`-Ereignis** statt `decode()`.
|
||||
|
||||
Genau die Fehlerklasse, fuer die die Musterseite gebaut wurde: `tsc` und 215
|
||||
Tests waren gruen, der Fehler stand trotzdem drin - er zeigt sich nur beim
|
||||
Ausfuehren, und ausfuehren liess sich der Bildschirm bisher nur mit einem Token.
|
||||
|
||||
### Die Musterseite kann jetzt auch die Ersteinrichtung
|
||||
|
||||
`?seite=einrichtung` mountet den Willkommensbildschirm. Er ist keine Route (er
|
||||
steht vor der Anmeldung) und brauchte deshalb einen eigenen Zweig in
|
||||
`muster/muster.tsx`.
|
||||
|
||||
**Damit ist der ganze Weg nachgewiesen, ohne einen echten Token anzufassen** -
|
||||
gepruefte QR-Codes waren selbst erzeugte (die im Repo liegende MIT-Bibliothek
|
||||
`homeassistant/www/dm360-qrcode-lib.js` erzeugt sie, in den Container kopiert
|
||||
und ueber `/local/` geladen):
|
||||
|
||||
| Eingespieltes Bild | Ergebnis |
|
||||
|---|---|
|
||||
| QR mit einem Text in JWT-Form | steht **zeichengleich** im Token-Feld, Blatt schliesst, Adressfeld unberuehrt |
|
||||
| QR mit `http://muster.local:8123` | landet im **Adressfeld**, nicht im Token-Feld |
|
||||
| graues Bild ohne Code | „In diesem Bild ist kein QR-Code zu erkennen", Blatt bleibt offen |
|
||||
|
||||
### Debug-Mode ist nur noch ein stiller Knopf
|
||||
|
||||
Vorgabe des Eigentuemers: „moeglichst unauffaellig". Kachel, Ueberschrift und
|
||||
der erklaerende Satz sind entfallen; es bleibt ein Textknopf am Seitenende
|
||||
(`.debugknopf` / `.dm-debugknopf`, wortgleich in beiden Codebasen): kein
|
||||
Kachelgrund, kein Rahmen, 12,5 px in der Drittfarbe. Die 44 px Trefferhoehe
|
||||
kommen aus dem Innenabstand - die Flaeche stimmt, ohne dass der Knopf gross
|
||||
aussieht. Live gemessen: Panel 45 px, `rgb(107,107,112)`, durchsichtig, letztes
|
||||
Element der Seite; App 12,5 px, `rgb(142,142,147)`.
|
||||
|
||||
### Der Zugang zu Home Assistant steht jetzt bei den Zugaengen
|
||||
|
||||
Ebenfalls Vorgabe: die eigene Kachel „Zugang" mit „Verbindung trennen" wandert
|
||||
in den Debug-Mode, und zwar **in** die Kachel „Zugaenge". Das ist dieselbe Sorte
|
||||
Angabe wie die Dienste-Token, und eine Verbindung wird ungefaehr so oft getrennt,
|
||||
wie ein Token getauscht wird - also fast nie. Als Akkordeon „Home Assistant" mit
|
||||
der Server-Adresse (aus `zugangLesenSynchron()`) und dem Trennen-Knopf; die
|
||||
Kachel wird deshalb jetzt immer gezeigt, nicht mehr nur bei vorhandenen
|
||||
Dienste-Token.
|
||||
|
||||
Ohne Entsprechung im Panel, und zwar notwendigerweise: es laeuft INNERHALB von
|
||||
Home Assistant und kennt weder Adresse noch Token.
|
||||
|
||||
### Merkposten: die App am Entwicklungsserver anmelden
|
||||
|
||||
Gemeldet als „die Adresse stimmt nicht". Aus dem Dev-Server (`:5173`) heraus ist
|
||||
`http://localhost:18123` **fremder Ursprung**, der Preflight endet in 403 - die
|
||||
App meldet daraufhin voellig zu Recht „Der Server ist unter dieser Adresse nicht
|
||||
erreichbar". Einzutragen ist die **eigene** Adresse `http://localhost:5173`; der
|
||||
Vite-Proxy reicht `/api` serverseitig an `:18123` weiter (Abschnitt AK). Nach der
|
||||
Korrektur stand die Uebersicht mit echten Daten.
|
||||
|
||||
Reference in New Issue
Block a user