1354ca6b06
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>
142 lines
6.7 KiB
Markdown
142 lines
6.7 KiB
Markdown
# Versionierung — eine Zahl, drei Verbraucher
|
|
|
|
Kurzfassung für den Alltag: **Wenn du etwas änderst, erhöhe `version` in
|
|
`custom_components/audi_dashboard/manifest.json`.** Alles Weitere ergibt sich
|
|
daraus von selbst.
|
|
|
|
---
|
|
|
|
## Warum es diese Zahl braucht
|
|
|
|
Das Panel und die iOS-App werden völlig unterschiedlich ausgeliefert:
|
|
|
|
- **Panel:** gehört zur Integration und wird mit ihr zusammen installiert. Es
|
|
kann gar nicht hinterherhinken — ein Update der Integration ist automatisch
|
|
ein Update des Panels.
|
|
- **iOS-App:** eine Capacitor-Hülle mit **fest gebündelten** Dateien. Sie
|
|
bleibt auf dem Stand, der beim Signieren in Xcode eingebaut wurde —
|
|
unbegrenzt.
|
|
|
|
Die Paritätsregel in `AGENTS.md` sichert, dass beide Codebasen in derselben
|
|
Sitzung geändert werden. Über das, was *läuft*, sagte lange nichts etwas: die
|
|
iOS-App konnte wochenlang zurückliegen, ohne dass es irgendwo sichtbar wurde.
|
|
|
|
Die Versionszahl schließt genau diese Lücke — nicht, indem sie die Abweichung
|
|
verhindert (das kann keine Zahl), sondern indem sie sie **sichtbar** macht.
|
|
|
|
## Wo sie steht
|
|
|
|
In `custom_components/audi_dashboard/manifest.json`, Feld `version`. Format
|
|
`2026.8.23.2` (Datum + laufende Nummer).
|
|
|
|
Genau dort und nirgends sonst. Home Assistant verlangt das Feld für jede
|
|
benutzerdefinierte Integration — eine zweite Quelle für dieselbe Angabe wäre
|
|
eine zu viel. Sie hätten irgendwann auseinandergelegen, und dann wäre der
|
|
Vergleich still falsch geworden statt laut.
|
|
|
|
> HACS würde dieses Feld ebenfalls lesen und danach entscheiden, ob ein
|
|
> Update bereitliegt — spielt hier aber keine Rolle: HACS kann laut eigener
|
|
> Dokumentation grundsätzlich nicht mit privaten GitHub-Repositories
|
|
> arbeiten, und das Repository ist privat (lizenzierte Audi-Assets). Das
|
|
> Manifest-Feld bleibt trotzdem die einzig richtige Stelle - Home Assistant
|
|
> selbst braucht es unabhängig von HACS.
|
|
|
|
> Bis 2026-08-23 gab es dafür eine eigene Datei `VERSION` in der Repo-Wurzel,
|
|
> daneben eine Unix-Sekundenzahl in `www/audi-dashboard-version.json` als
|
|
> Cache-Brecher. Beide sind mit dem Umbau zur Integration entfallen: die
|
|
> Version steht jetzt im Manifest, und die Oberflächen-Dateien tragen sie als
|
|
> `?v=…` in ihrer URL — die Integration hängt sie beim Anmelden des Panels an.
|
|
> Ein Cache-Brecher, der sich nur bei echten Änderungen ändert, ist der
|
|
> bessere: er lädt nichts unnötig neu.
|
|
|
|
## Wie sie durchs System läuft
|
|
|
|
```
|
|
manifest.json { "version": "2026.8.23.2" }
|
|
│
|
|
├─► Home Assistant führt sie als Version der Integration
|
|
│ └─► HACS vergleicht sie gegen das Repository und meldet Updates
|
|
│
|
|
├─► die Integration veröffentlicht sie als
|
|
│ sensor.audi_dashboard_app_version
|
|
│ └─► die iOS-App vergleicht sie mit ihrer eigenen, einkompilierten
|
|
│ Zahl und zeigt bei Abweichung einen Hinweis in der
|
|
│ Hinweisleiste
|
|
│
|
|
├─► sie hängt als ?v=… an der Panel-URL
|
|
│ └─► Browser laden Panel, CSS und Badges nur dann neu, wenn sich
|
|
│ wirklich etwas geändert hat
|
|
│
|
|
└─► companion-app: vite liest das Manifest beim Bauen und setzt sie als
|
|
__APP_VERSION__ ein (bricht ab, wenn das Feld fehlt)
|
|
```
|
|
|
|
Der Vergleich läuft in eine Richtung: **die Integration sagt, welcher Stand
|
|
installiert ist; die App sagt, welchen sie hat.** Stimmen sie nicht überein,
|
|
ist die App zu alt (oder, seltener, die Integration).
|
|
|
|
Bewusst **nur auf Gleichheit**, nie größer/kleiner: die Version ist eine
|
|
Kennung, keine Zahl. Ein Sortierversuch wäre scheingenau und würde bei einem
|
|
Formatwechsel still falsche Antworten geben. Fehlt eine der beiden Seiten, wird
|
|
gar nicht verglichen und nichts gemeldet — ein älteres Backend oder ein Start
|
|
ohne Netz darf keinen Fehlalarm auslösen.
|
|
|
|
## Was du tun musst
|
|
|
|
**Bei einer Änderung an der Oberfläche oder am Backend:**
|
|
|
|
1. `version` in `manifest.json` erhöhen — bei mehreren Änderungen am selben Tag
|
|
die laufende Nummer: `2026.8.23.2` → `2026.8.23.3`, am nächsten Tag
|
|
`2026.8.24.1`.
|
|
2. Integration ausliefern: `homeassistant\installationspaket\Installieren.cmd`
|
|
(HACS scheidet aus — siehe Kasten oben, das Repository ist privat).
|
|
3. iOS-App neu bauen (`npm run build`), damit sie dieselbe Zahl einkompiliert
|
|
bekommt.
|
|
|
|
Vergisst du Schritt 3, ist das kein stiller Fehler mehr: die App meldet selbst,
|
|
dass sie älter ist als der Server.
|
|
|
|
**Bei einer reinen Neuinstallation** ohne Codeänderung: nichts tun.
|
|
|
|
## OTA-Updates (seit 2026-08-24)
|
|
|
|
`@capgo/capacitor-updater` ist eingebaut. Für reine Oberflächen-Änderungen
|
|
(JavaScript, CSS, Bilder) entfällt Schritt 3 oben — die App lädt sich den
|
|
neuen Stand selbst, auf einen Tastendruck des Nutzers in Einstellungen →
|
|
App-Update. Xcode wird nur noch für echte native Änderungen gebraucht
|
|
(neue Plugins, Berechtigungen, `capacitor.config.ts`).
|
|
|
|
**Woher das Bündel kommt.** `companion-app/scripts/ota-paket.ps1` packt
|
|
`dist/` (ohne Sourcemaps) in
|
|
`custom_components/audi_dashboard/frontend/app/bundle.zip`, daneben
|
|
`bundle.json` mit Version, SHA-256 und Größe. Weil das im Integrationsordner
|
|
liegt, reist es bei jeder Auslieferung automatisch mit — kein zweiter Weg, den
|
|
man vergessen könnte. Ausgeliefert wird es unter
|
|
`/audi_dashboard_static/app/bundle.zip`, gemeldet über dasselbe Feld, das
|
|
schon den Versionsvergleich trägt: `sensor.audi_dashboard_app_version`,
|
|
Attribut `daten.buendel`.
|
|
|
|
**Was die App damit macht** (`companion-app/src/daten/ota.ts`): erscheint ein
|
|
Bündel, dessen Version von der eigenen abweicht, zeigt Einstellungen →
|
|
App-Update einen Knopf. Ein Tastendruck lädt die Zip, prüft sie clientseitig
|
|
gegen die mitgelieferte SHA-256 (das Plugin selbst, nicht diese App) und
|
|
tauscht die Oberfläche aus — die App startet dabei neu.
|
|
|
|
**Die Rückfallebene.** `notifyAppReady()` läuft in `App.tsx`, sobald React
|
|
tatsächlich gerendert hat. Bleibt diese Meldung aus, weil das neue Bündel
|
|
die App zerlegt hat, rollt das Plugin nach der eingestellten Frist
|
|
(`appReadyTimeout` in `capacitor.config.ts`) von selbst auf das vorherige
|
|
Bündel zurück — ein kaputtes Update kann das Gerät deshalb nicht dauerhaft
|
|
unbrauchbar machen.
|
|
|
|
**Warum kein Selbstlauf.** `autoUpdate: false` — die App lädt nur auf
|
|
ausdrücklichen Tastendruck, nie im Hintergrund. Ein Update, das während einer
|
|
Fahrteintragung ungefragt die Oberfläche austauscht, wäre die falsche Sorte
|
|
Hilfsbereitschaft.
|
|
|
|
**HACS spielt hier keine Rolle und wird es auch nicht.** Das Update-Bündel
|
|
reist mit `install.ps1` (dem einzigen Installationsweg, siehe oben) genauso
|
|
mit wie mit einem hypothetischen HACS-Download — der Mechanismus hängt nicht
|
|
daran, wie die Integration selbst auf die Instanz kommt, nur daran, dass das
|
|
Bündel im Integrationsordner liegt.
|