From fb251154c7247c8cd9d7fb487f0bbebb8e443880 Mon Sep 17 00:00:00 2001 From: Tobi G Date: Mon, 24 Aug 2026 12:01:01 +0200 Subject: [PATCH] =?UTF-8?q?=C3=96lwechsel/Inspektion-Anzeige=20repariert,?= =?UTF-8?q?=20Selbst-Update=20der=20Integration=20gebaut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- AGENTS.md | 165 ++++++++++++ README.md | 14 +- VERSIONIERUNG.md | 18 +- companion-app/src/api/index.ts | 20 ++ companion-app/src/api/types.ts | 16 ++ companion-app/src/daten/DatenKontext.tsx | 9 + companion-app/src/daten/service.test.ts | 114 +++++++- companion-app/src/daten/service.ts | 82 +++++- companion-app/src/screens/Einstellungen.tsx | 81 ++++++ companion-app/src/screens/Service.tsx | 33 ++- companion-app/src/screens/Uebersicht.tsx | 2 +- companion-app/vite.config.ts | 16 +- custom_components/audi_dashboard/__init__.py | 8 +- .../audi_dashboard/aktualisierung.py | 244 ++++++++++++++++++ custom_components/audi_dashboard/bilder.py | 7 +- .../audi_dashboard/config_flow.py | 57 +++- custom_components/audi_dashboard/const.py | 20 +- custom_components/audi_dashboard/dienste.py | 75 +++++- .../audi_dashboard/frontend/app/bundle.json | 2 +- .../audi_dashboard/frontend/app/bundle.zip | Bin 230770 -> 231567 bytes .../frontend/audi-dashboard-app.js | 132 ++++++++-- .../audi_dashboard/koordinator.py | 31 ++- .../audi_dashboard/manifest.json | 2 +- .../audi_dashboard/services.yaml | 2 + custom_components/audi_dashboard/strings.json | 19 ++ .../audi_dashboard/translations/de.json | 19 ++ tests/aktualisierung/test_aktualisierung.py | 162 ++++++++++++ 27 files changed, 1268 insertions(+), 82 deletions(-) create mode 100644 custom_components/audi_dashboard/aktualisierung.py create mode 100644 tests/aktualisierung/test_aktualisierung.py diff --git a/AGENTS.md b/AGENTS.md index 1a8bf19..ba25df8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1926,6 +1926,30 @@ Gitea-vs-mirrored-GitHub changes it: HACS refuses private repos regardless of wh mirrored, and the private/public constraint (licensed Audi assets in the repo) isn't going away. `install.ps1` is the permanent, only install path, not a stand-in. +**Stale HACS claims found and fixed in code comments/UI text (2026-08-24).** The docs-level cleanup +above happened the same day as this section was written, but a full-repo grep afterward (prompted by +the owner spotting live UI text still claiming "Updates laufen über HACS") turned up leftovers the +first pass missed, because it focused on the standalone docs (`README.md`, `INSTALL.md`, etc.) and +not source comments or UI strings. Fixed: the panel's Einstellungen → Version tile literally told the +user HACS would notice and install updates itself — replaced with the actual mechanism +(`install.ps1`, panel ships inside the integration and can't go stale on its own). Also fixed, +all now pointing at `install.ps1`/"jeder Auslieferung" instead of "HACS"/"HACS-Update": comments in +`audi-dashboard-app.js` (the version-tracking block and the retired-updater history note), +`companion-app/vite.config.ts` (manifest-version header comment), `const.py` (`STATIK_URL`, +`BUENDEL_ORDNER`, `BILDER_ORDNER` comments), `bilder.py` (module docstring), `__init__.py` +(`cache_headers=False` rationale), and a diagram branch in `VERSIONIERUNG.md` that stated "HACS +vergleicht sie gegen das Repository und meldet Updates" as flat fact, contradicting the boxed note +one screen above it. Left alone: `install.ps1`'s own header (already correctly explains the +exclusion), `ota-paket.ps1`'s comment (already correct), and one debugging note in +`audi-dashboard-app.js` (~line 4720) that recalls what was actually running in a specific test +container during a past incident — a historical fact about that container, not a claim about how +this project works, so not in scope for this cleanup. Also out of scope, a different topic entirely: +mentions of the *retired vehicle-data-source* HACS integration `TommiG1/HA_VAG-EU-Data-Act` in older +planning docs (`SPECIFICATION.md`, `COMPANION_APP_ARCHITECTURE.md`, `bauauftrag.md/html`, +`testumgebung/`) — that HACS integration is unrelated to whether HACS can install *this* app; it was +FMM003's predecessor as a data source, already documented as retired elsewhere in those same files. +Manifest bumped to `2026.8.24.3` and `npm run ota` rerun (the panel text change is user-visible). + ### The deployed-parity problem, and what was built for it (2026-08-23) The parity rule (binding, above) guarantees *source* parity: both codebases change in the same @@ -2131,6 +2155,147 @@ was 1.00:1, white on white. Both pills now follow the app's own button language as a button fill. No companion-app port: the location sheet's share/route pills do not exist there, which the parity rule exempts as panel-only. +### I) Ölwechsel/Inspektion could vanish entirely with a mapped sensor and no Servicebuch (found and fixed 2026-08-24) + +Owner reported: "no inspektion and ölwechsel shown even if there is the right sensor and +herstellervorgabe." Both were true — sensor mapped correctly, "Herstellervorgabe" selected — and the +row still just said "kein Eintrag im Servicebuch" (no Servicebuch entry). + +**Root cause, identical in both codebases.** The Ölwechsel/Inspektion display combines two +independent sources on purpose (see the header comment in `companion-app/src/screens/Service.tsx`): +the vehicle's own reported due date/km (`fahrzeugMeldungRoh()` / `fahrzeug.oelwechselFaelligTs` etc., +sourced from the mapped HA sensor) is meant to stand *alongside* the app's own Servicebuch-based +forecast, not depend on it. But the actual gating checked only the Servicebuch side: +- Panel (`frontend/audi-dashboard-app.js`, `vService()`): `const iv = intervalle(t); if (!iv) return + ...„kein Eintrag im Servicebuch"...` — and `intervalle()` returns `null` whenever there's no + Servicebuch basis, full stop. The vehicle-reported `fm`/`hatMeldung` was computed *after* this early + return, so a mapped sensor with valid data never even got read for a fahrzeug with an empty + Servicebuch — exactly the state of a freshly set-up car. +- companion-app (`src/screens/Service.tsx`): same shape, `{oelHatEintrag ? ... : "kein Eintrag im + Servicebuch"}` — gated purely on a Servicebuch entry existing, ignoring `oelHatMeldung`/ + `inspHatMeldung` computed one line above it. `src/daten/service.ts`'s `naechsterService()` (feeds + the Home-screen "Nächster Service" tile) had the same gap one level up: it only ever built + candidates from `oelwechselPrognose()`/`inspektionPrognose()` (both Servicebuch-only), never + falling back to `fahrzeug.oelwechselFaelligTs`/`inspektionFaelligTs`. + +**Fixed by reordering, not by adding a new data path** — the sensor data was already being read +correctly by the backend and delivered to both frontends; only the display-gating ignored it. +Panel: `fm`/`hatMeldung` now computed *before* the early-return check, which now reads `if (!iv && +!hatMeldung)`; `anzeige`/`herstellervorgabe` guarded for `iv` possibly being `null` (both remain safe +because the code paths that dereference `iv` only run when `hatMeldung` is false, and the new guard +guarantees `iv` is non-null whenever that's the case). companion-app: `oelHatEintrag || +oelHatMeldung` / `inspHatEintrag || inspHatMeldung` as the render condition; `naechsterService()` +gained an `else if (fahrzeug.oelwechselFaelligTs)` / `...inspektionFaelligTs` fallback branch per +service type, added only when the Servicebuch-based prognose is `null` — a real Servicebuch entry +still wins when both exist (own forecast is finer-grained, factoring actual driven km/day). The +Hauptuntersuchung path is unaffected either way: `fahrzeugMeldungRoh("Hauptuntersuchung")` always +returns `{ts: null, km: null}`, so `hatMeldung` is always `false` for it — same behavior as before. + +Verified: `tsc --noEmit` clean, all 120 companion-app tests pass (including 3 new regression tests in +`service.test.ts` covering the meldung-only fallback for both service types and confirming a real +Servicebuch prognose still takes priority when both are present), panel JS re-checked with `node +--check`. `manifest.json` bumped to `2026.8.24.2` and `npm run ota` rerun (own rule from section H, +enforced by `install.ps1` since the same day) since this touched both the panel and the companion-app. + +### J) Self-update: the integration pulls its own updates from Gitea (built 2026-08-24) + +**Why.** `install.ps1` (section H) stopped being a reliable update path: Windows Smart App Control +blocks its execution, and unlike SmartScreen there is no "run anyway" override — once fully +enabled, Smart App Control can only be turned off by reinstalling Windows. The owner's actual daily +pain wasn't the block itself but the whole cycle it forced: open Gitea, download a zip, unpack it, +run the (now-blocked) installer, re-enter the Samba password every time (see section H's Samba-auth +fix earlier the same day). Owner chose, from three options laid out (free workarounds first / code-sign +the installer / have Home Assistant pull its own updates), the third: **the integration now updates +itself.** `install.ps1` remains necessary for the *first* install only — the integration has to +already be running before it can update itself — which is the rare case; the daily-update tedium is +what this closes. + +**Safety design — stricter than install.ps1, because it runs inside what it replaces.** +`install.ps1` is an external process against a foreign HA instance; this feature's code lives +*inside* `custom_components/audi_dashboard/` and replaces that very folder while its own service +call is still executing. `aktualisierung.py`'s module docstring carries the full reasoning; the +shape: +1. Download and verification happen entirely in a sibling staging folder + (`custom_components/audi_dashboard_update_staging/`) — the live folder is never touched until the + download is confirmed complete and plausible. +2. The swap is `os.rename`, not delete-then-copy: the live folder is renamed to + `audi_dashboard_backup` (kept, not deleted — reversible) before the staging folder is renamed into + its place. If the second rename fails, the first is undone automatically rather than leaving a + half-replaced folder. +3. The downloaded `manifest.json` is checked for `domain == "audi_dashboard"` and a `version` field + before either rename runs — matches install.ps1's own "won't touch a folder that isn't + provably ours" rule (section G/H). +4. **No automatic reload or restart.** A reload triggered from inside the same service call that's + still running would risk being interrupted by its own reload. The UI shows a "restart Home + Assistant" message instead — the same pattern install.ps1's Restliste already uses. +5. Nothing outside `custom_components/` is touched — same invariant as install.ps1. +6. The check step (a Gitea network call) runs only on explicit button press, never on the + coordinator's normal publish tick — same "no unsolicited background action" stance as the OTA + `autoUpdate: false` decision. + +Renaming the very folder the running code was imported from is safe on Linux (this project runs on +Home Assistant OS / Docker): Python holds no open file handles on `.py` sources after import +completes, and `os.rename` only operates on path strings, not live handles. + +**Token storage.** No prior mechanism existed for secrets in this integration — `config_flow.py`'s +`entry.data` was always `{}`, no `OptionsFlow` existed. Added `AudiDashboardOptionsFlow` +(Settings → Devices & Services → Audi Dashboard → Configure), one masked field +(`CONF_GITEA_TOKEN` in `const.py`), stored in `entry.options` — HA's own encrypted config-entry +store, never `configuration.yaml`. A read-only-scoped Gitea token is enough; the form text says so. + +**What was built:** +- `custom_components/audi_dashboard/aktualisierung.py` — `version_pruefen()` (one lightweight Gitea + Contents-API call for just `manifest.json`, no repo download) and `update_installieren()` + (downloads the full repo archive via Gitea's archive endpoint — Gitea has no single-folder + download — delegates to the blocking `entpacken_pruefen_tauschen()` via + `hass.async_add_executor_job`). The blocking function takes all paths as parameters rather than + reading module constants directly, specifically so it's testable against a temp directory without + touching the real integration folder. +- Two new services (`update_pruefen`, `update_installieren`), registered in `dienste.py` following + the existing `SCHEMATA`/`behandler`/`_als_dienst()` pattern; both zero-field, both catch + `aktualisierung.AktualisierungsFehler` and turn it into a user-readable message instead of an + unhandled exception. +- `koordinator.py`: `alles_veroeffentlichen()`'s `E_APP_VERSION` publish was factored out into its + own `app_version_veroeffentlichen()` method and extended with `integration_update`. This holds the + *last* check/install result (`self.letzte_update_pruefung`) — deliberately **not** recomputed on + every publish tick, only ever set by the two new service handlers, so normal operation triggers zero + Gitea calls. +- Both frontends got a new "Integration-Update" tile (companion-app: `Einstellungen.tsx` + + `IntegrationUpdateAngabe` type in `api/types.ts` + `integrationUpdate` in `DatenKontext.tsx`; panel: + new tile next to the existing Version tile in `audi-dashboard-app.js`, backed by a new + `INTEGRATION_UPDATE` global read from `daten.integration_update`, wired to the existing + `data-*`/`serviceRufen()`/`ereignisseVerdrahten()` click-dispatch pattern) — not an extension of the + existing OTA "App-Update" tile, which is a different concept (the Capacitor iOS shell going stale) + that doesn't apply to a browser-rendered panel. + +**Verified, honestly scoped by what this session's sandbox could actually run:** +- The safety-critical pure logic (`entpacken_pruefen_tauschen()`) has 7 unit tests + (`tests/aktualisierung/test_aktualisierung.py`, `unittest`, same style as + `tests/belegparser/test_shell_beleg_parser.py`) covering: successful swap with the old version + preserved in the backup folder, `__pycache__` cleanup, wrong domain rejected, missing `version` + field rejected, missing `manifest.json` rejected, corrupt zip rejected, archive missing the + expected path rejected — and every rejection case asserts the live folder is byte-for-byte + untouched. Run inside the project's existing throwaway `ghcr.io/home-assistant/home-assistant:stable` + test container (`audi_ha_test`) via `docker exec`, since no Python interpreter exists in the + session sandbox itself. +- The complete integration (config flow, both services, koordinator changes, both frontends) was + deployed into that same container and restarted from a clean state, twice, ending on + `Audi Dashboard 2026.8.24.4 eingerichtet` with zero errors or tracebacks — confirms the + `OptionsFlow`/service registration/coordinator wiring is structurally correct against a real, + current (2026.8.0) Home Assistant. +- companion-app: `tsc --noEmit` clean, all 127 tests pass. +- **Not verified**: an actual successful download+swap against the real private Gitea repo, since + that needs the owner's own token — entering it is the owner's action via the HA UI, not something + to hand to an assistant in chat. The failure paths (missing token, bad token, unreachable Gitea) are + covered by the unit tests and code review, not by a live call against Gitea. First real end-to-end + use is the owner's to do once a token is configured — worth a first careful try on the test + container before the real instance, same caution as every other install.ps1-era change in this + project. + +**Docs updated to stop calling install.ps1 "the only way":** `README.md`, `VERSIONIERUNG.md` — both +now say install.ps1 is the only way *to install*; the self-update is now the preferred way *to +update* an already-running instance. + --- ## Working conventions (observed — keep them) diff --git a/README.md b/README.md index 2dba268..bf69296 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,18 @@ Audi-Hausschrift und die Typenschilder, die nur für diese eine private Installation lizenziert sind. Der Installer unten ist deshalb nicht die zweite Wahl, sondern der einzige -Weg. Er ist genauso wenig Handarbeit wie ein HACS-Update — derselbe Aufruf -installiert und aktualisiert. +Weg für die **Erstinstallation** — die Integration muss laufen, bevor sie +sich selbst aktualisieren kann. + +Für **Updates danach** gibt es seit 2026-08-24 einen zweiten, bevorzugten +Weg: die Integration lädt ihre neueste Fassung direkt von Gitea und ersetzt +sich selbst (Einstellungen → Geräte & Dienste → Audi Dashboard → +Konfigurieren, dort ein Gitea-Zugriffstoken eintragen; danach in der App +unter Einstellungen → Integration-Update). Grund: Windows Smart App Control +blockiert die Ausführung von `install.ps1` zuverlässig und lässt sich — +anders als der SmartScreen davor — nicht per „trotzdem ausführen" umgehen. +Der Installer unten bleibt der Weg für die Erstinstallation und für den +seltenen Fall, dass das Selbst-Update selbst nicht mehr erreichbar ist. Von einem Windows-Rechner aus, der das `config`-Verzeichnis der HA-Instanz erreicht (Samba-Add-on oder gemounteter Pfad): diff --git a/VERSIONIERUNG.md b/VERSIONIERUNG.md index 72a04b9..f6d5dc7 100644 --- a/VERSIONIERUNG.md +++ b/VERSIONIERUNG.md @@ -55,7 +55,6 @@ Vergleich still falsch geworden statt laut. 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 @@ -97,7 +96,12 @@ ohne Netz darf keinen Fehlalarm auslösen. Bündel) — ein Widerspruch, aus dem es ohne diesen Schritt keinen Ausweg gibt. `install.ps1` prüft das seit 2026-08-24 selbst und warnt, falls vergessen — aber besser, es passiert gar nicht erst. -3. Integration ausliefern: `homeassistant\installationspaket\Installieren.cmd` +3. Integration ausliefern: nach `git push` entweder in der App selbst unter + Einstellungen → Integration-Update auf „Auf Update prüfen" → „Update + installieren" (seit 2026-08-24, ersetzt install.ps1 als laufenden + Update-Weg — Windows Smart App Control blockiert dessen Ausführung + zuverlässig), danach Home Assistant neu starten; oder weiterhin + `homeassistant\installationspaket\Installieren.cmd` von Hand (HACS scheidet aus — siehe Kasten oben, das Repository ist privat). 4. iOS-App für Xcode neu bauen (`npm run build`), damit eine über App Store Connect / Sideload signierte Fassung dieselbe Zahl einkompiliert bekommt. @@ -148,7 +152,9 @@ 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. +reist mit jedem Weg, der die Integration ausliefert, automatisch mit — +`install.ps1` für die Erstinstallation ebenso wie das Selbst-Update seit +2026-08-24 (aktualisierung.py, ersetzt install.ps1 als laufenden Update-Weg, +siehe Abschnitt J in AGENTS.md) — der Mechanismus hängt nicht daran, wie die +Integration selbst auf die Instanz kommt, nur daran, dass das Bündel im +Integrationsordner liegt. diff --git a/companion-app/src/api/index.ts b/companion-app/src/api/index.ts index b44529a..29d26e3 100644 --- a/companion-app/src/api/index.ts +++ b/companion-app/src/api/index.ts @@ -197,6 +197,26 @@ export class DataMetricApi { } } + /* ------------------------------------------------- Selbst-Update der + Integration (aktualisierung.py) - Ersatz für install.ps1, das Windows + Smart App Control blockiert. Bewusst NICHT über die Warteschlange, wie + historieImportieren() oben: beides sind ausdrückliche, sofortige + Aktionen, kein Zustand, den man im Funkloch absetzt und später + nachgeholt haben will. Ergebnis kommt über + appVersionAngabeLesen()/daten.integration_update zurück, nicht über den + Rückgabewert dieser Aufrufe. */ + + /** Fragt nur nach, ob eine neuere Fassung vorliegt - lädt nichts herunter. */ + updatePruefen(): Promise { + return this.rest.dienstAufrufen(DIENST_DOMAIN, "update_pruefen"); + } + + /** Lädt die neueste Fassung und ersetzt den Integrationsordner. Home + Assistant muss danach neu gestartet werden. */ + updateInstallieren(): Promise { + return this.rest.dienstAufrufen(DIENST_DOMAIN, "update_installieren"); + } + /* ------------------------------------------- Import aus dem HA-Verlauf Bewusst NICHT über die Warteschlange, anders als die übrigen schreibenden Vorgänge: der Import ist keine Eingabe, die man im Funkloch diff --git a/companion-app/src/api/types.ts b/companion-app/src/api/types.ts index 5be5ee6..f8fd40b 100644 --- a/companion-app/src/api/types.ts +++ b/companion-app/src/api/types.ts @@ -172,10 +172,26 @@ export interface Buendelangabe { url: string; } +/** Ergebnis der letzten "Auf Update prüfen"/"Update installieren"-Aktion + (custom_components/audi_dashboard/aktualisierung.py, dienste.py). `null`, + solange noch nie geprüft wurde - ein normaler Zustand, kein Fehler. + Ersetzt install.ps1 als Update-Weg, weil Windows Smart App Control es + zuverlässig blockiert. */ +export interface IntegrationUpdateAngabe { + verfuegbar: boolean; + version: string | null; + geprueft_am: string; + /** true unmittelbar nach einer erfolgreichen Installation - dann ist ein + Neustart von Home Assistant nötig, damit die neue Fassung geladen wird. */ + installiert: boolean; + fehler: string | null; +} + /** Nutzlast von sensor.audi_dashboard_app_version. */ export interface AppVersionAngabe { app: string | null; buendel: Buendelangabe | null; + integration_update: IntegrationUpdateAngabe | null; } /* -------------------------------------------------- Entitäts-Verzeichnis */ diff --git a/companion-app/src/daten/DatenKontext.tsx b/companion-app/src/daten/DatenKontext.tsx index 9f1aff3..b553c7b 100644 --- a/companion-app/src/daten/DatenKontext.tsx +++ b/companion-app/src/daten/DatenKontext.tsx @@ -25,6 +25,7 @@ import { type Buendelangabe, type Fahrt, type Fahrzeugstatus, + type IntegrationUpdateAngabe, type Profil, type Tankvorgang, type Verbindungszustand, @@ -50,6 +51,9 @@ export interface DatenWert { oder null — kein Fehler, sondern der Zustand ohne Bündel oder ohne Verbindung. */ otaBuendel: Buendelangabe | null + /** Ergebnis der letzten Selbst-Update-Prüfung/-Installation der + Integration (aktualisierung.py), oder null vor der ersten Prüfung. */ + integrationUpdate: IntegrationUpdateAngabe | null einstellungen: Einstellungen | null fahrzeug: Fahrzeug | null @@ -93,6 +97,9 @@ export function DatenAnbieter({ const [warteschlange, setzeWarteschlange] = useState([]) const [serverVersion, setzeServerVersion] = useState(null) const [otaBuendel, setzeOtaBuendel] = useState(null) + const [integrationUpdate, setzeIntegrationUpdate] = useState( + null, + ) // Damit profilSpeichern immer gegen den neuesten Rohstand arbeitet, auch // wenn zwischendurch ein Push hereinkam. @@ -121,6 +128,7 @@ export function DatenAnbieter({ const versionsangabe = await api.appVersionAngabeLesen() setzeServerVersion(versionsangabe?.app ?? null) setzeOtaBuendel(versionsangabe?.buendel ?? null) + setzeIntegrationUpdate(versionsangabe?.integration_update ?? null) } catch (fehler) { // Kein Netz ist kein Ladefehler, solange schon Daten da sind — dann // zeigt die App den zwischengespeicherten Stand mit Offline-Hinweis. @@ -219,6 +227,7 @@ export function DatenAnbieter({ statusStand, versionsstand: versionVergleichen(eigeneVersion(), serverVersion).stand, otaBuendel, + integrationUpdate, einstellungen, fahrzeug, fahrten, diff --git a/companion-app/src/daten/service.test.ts b/companion-app/src/daten/service.test.ts index 8d562a4..3e622f1 100644 --- a/companion-app/src/daten/service.test.ts +++ b/companion-app/src/daten/service.test.ts @@ -1,6 +1,13 @@ import { describe, expect, it } from "vitest" -import { inspektionPrognose, letzterOelwechsel, naechsterService, oelwechselPrognose } from "./service" +import { + inspektionPrognose, + kmProTagAusFahrten, + letzterOelwechsel, + meldungsPrognose, + naechsterService, + oelwechselPrognose, +} from "./service" import type { Fahrzeug } from "./profilAdapter" function fahrzeug(odo: number, modus = "hersteller"): Fahrzeug { @@ -128,7 +135,7 @@ describe("naechsterService", () => { const insp = inspektionPrognose(fahrzeug(46000), BUCH, jetzt) const erwarteteArt = oel!.datum <= insp!.datum ? "Ölwechsel" : "Inspektion" - const s = naechsterService(fahrzeug(46000), BUCH, jetzt) + const s = naechsterService(fahrzeug(46000), BUCH, [], jetzt) expect(s).not.toBeNull() expect(s!.art).toBe(erwarteteArt) expect(s!.datum).toBe(erwarteteArt === "Ölwechsel" ? oel!.datum : insp!.datum) @@ -136,7 +143,7 @@ describe("naechsterService", () => { it("nimmt die Hauptuntersuchung auf, obwohl sie keine eigene Prognose hat", () => { const f = { ...fahrzeug(46000), hu: "2026-09-01" } - const s = naechsterService(f, [], jetzt) + const s = naechsterService(f, [], [], jetzt) expect(s).not.toBeNull() expect(s!.art).toBe("Hauptuntersuchung") expect(s!.restKm).toBeNull() @@ -144,6 +151,105 @@ describe("naechsterService", () => { }) it("liefert nichts ohne Servicebuch und ohne gepflegte Hauptuntersuchung", () => { - expect(naechsterService(fahrzeug(46000), [], jetzt)).toBeNull() + expect(naechsterService(fahrzeug(46000), [], [], jetzt)).toBeNull() + }) + + it("fällt ohne Servicebucheintrag auf die vom Fahrzeug gemeldete Ölwechsel-Fälligkeit zurück", () => { + // Ein frisch eingerichtetes Fahrzeug mit zugeordnetem Sensor, aber noch + // ganz ohne Servicebuch, muss die Meldung trotzdem zeigen - siehe + // AGENTS.md, Abschnitt H, "no inspektion and ölwechsel shown ...". + const f = { ...fahrzeug(46000), oelwechselFaelligTs: "2027-03-14T00:00:00+00:00", oelwechselFaelligKm: 11750 } + const s = naechsterService(f, [], [], jetzt) + expect(s).not.toBeNull() + expect(s!.art).toBe("Ölwechsel") + expect(s!.datum).toBe("2027-03-14") + expect(s!.restKm).toBe(11750) + }) + + it("fällt ohne Servicebucheintrag auf die vom Fahrzeug gemeldete Inspektions-Fälligkeit zurück", () => { + const f = { ...fahrzeug(46000), inspektionFaelligTs: "2027-01-01T00:00:00+00:00", inspektionFaelligKm: 5000 } + const s = naechsterService(f, [], [], jetzt) + expect(s).not.toBeNull() + expect(s!.art).toBe("Inspektion") + expect(s!.datum).toBe("2027-01-01") + expect(s!.restKm).toBe(5000) + }) + + it("bevorzugt die eigene Prognose gegenüber der Fahrzeugmeldung, wenn beide vorliegen", () => { + // Servicebucheintrag vorhanden -> oelwechselPrognose() greift; die + // Meldung (weit in der Zukunft) darf sie nicht verdrängen. + const f = { ...fahrzeug(46000), oelwechselFaelligTs: "2099-01-01T00:00:00+00:00", oelwechselFaelligKm: 99999 } + const oel = oelwechselPrognose(f, BUCH, jetzt) + expect(oel).not.toBeNull() + const s = naechsterService(f, BUCH, [], jetzt) + expect(s!.datum).not.toBe("2099-01-01") + }) +}) + +describe("kmProTagAusFahrten", () => { + const jetzt = new Date("2026-08-11T12:00:00") + + it("rechnet aus abgeschlossenen Fahrten seit der ältesten die km/Tag-Rate", () => { + // Exakt 30 Tage vor jetzt (gleiche Uhrzeit), 900 km insgesamt -> 30 km/Tag. + const fahrten = [ + { ts_start: "2026-07-12T12:00:00", distance_km: 400, status: "abgeschlossen" }, + { ts_start: "2026-08-01T08:00:00", distance_km: 500, status: "abgeschlossen" }, + ] + expect(kmProTagAusFahrten(fahrten, jetzt)).toBeCloseTo(30, 5) + }) + + it("ignoriert offene Fahrten und Fahrten ohne bekannte Distanz", () => { + const fahrten = [ + { ts_start: "2026-07-12T08:00:00", distance_km: 400, status: "abgeschlossen" }, + { ts_start: "2026-08-10T08:00:00", distance_km: null, status: "abgeschlossen" }, + { ts_start: "2026-08-11T08:00:00", distance_km: 9999, status: "offen" }, + ] + // Nur die erste Fahrt zählt -> dieselbe Rate wie ohne die beiden anderen. + expect(kmProTagAusFahrten(fahrten, jetzt)).toBe( + kmProTagAusFahrten([fahrten[0]!], jetzt), + ) + }) + + it("liefert nichts ohne Fahrten oder ohne belastbaren Zeitraum", () => { + expect(kmProTagAusFahrten([], jetzt)).toBeNull() + expect(kmProTagAusFahrten([{ ts_start: jetzt.toISOString(), distance_km: 5, status: "abgeschlossen" }], jetzt)) + .toBeNull() + }) +}) + +describe("meldungsPrognose", () => { + const jetzt = new Date("2026-08-11T12:00:00") + // 30 km/Tag, siehe kmProTagAusFahrten-Tests oben. + const fahrten = [ + { ts_start: "2026-07-12T12:00:00", distance_km: 400, status: "abgeschlossen" }, + { ts_start: "2026-08-01T08:00:00", distance_km: 500, status: "abgeschlossen" }, + ] + + it("rechnet die Restkilometer mit der Fahrleistung aus dem Fahrtenlog auf ein Datum hoch", () => { + // 300 km Rest bei 30 km/Tag -> 10 Tage. + const p = meldungsPrognose("2099-01-01T00:00:00+00:00", 300, fahrten, jetzt) + expect(p).not.toBeNull() + expect(p!.datum).toBe("2026-08-21") + expect(p!.durchZeitlimit).toBe(false) + }) + + it("deckelt auf die vom Fahrzeug gemeldete Zeitgrenze, wenn zu wenig gefahren wird", () => { + // 30000 km Rest bei 30 km/Tag läge Jahre in der Zukunft - die gemeldete + // Zeitgrenze (fünf Tage später) greift zuerst. + const p = meldungsPrognose("2026-08-16T00:00:00+00:00", 30000, fahrten, jetzt) + expect(p).not.toBeNull() + expect(p!.datum).toBe("2026-08-16") + expect(p!.durchZeitlimit).toBe(true) + }) + + it("fällt ohne Fahrtenlog auf die reine Zeitgrenze der Meldung zurück", () => { + const p = meldungsPrognose("2027-01-01T00:00:00+00:00", 5000, [], jetzt) + expect(p).not.toBeNull() + expect(p!.datum).toBe("2027-01-01") + expect(p!.durchZeitlimit).toBe(true) + }) + + it("liefert nichts ohne jede brauchbare Angabe", () => { + expect(meldungsPrognose(null, null, fahrten, jetzt)).toBeNull() }) }) diff --git a/companion-app/src/daten/service.ts b/companion-app/src/daten/service.ts index 2d88f16..96a9895 100644 --- a/companion-app/src/daten/service.ts +++ b/companion-app/src/daten/service.ts @@ -126,6 +126,59 @@ export function inspektionPrognose( return servicePrognose(fahrzeug, letzterEintrag(buch, /inspektion/i), 30000, 24, jetzt) } +interface Fahrtdaten { + ts_start: string + distance_km: number | null + status?: string +} + +/** Fahrleistung pro Tag aus dem tatsächlichen Fahrtenlog, unabhängig vom + Servicebuch - Grundlage für meldungsPrognose() unten, die auch ganz ohne + Servicebucheintrag funktionieren soll. Nur abgeschlossene Fahrten mit + bekannter Distanz zählen; die Rate ist der Durchschnitt seit der ältesten + erfassten Fahrt, kein kurzes Zeitfenster - damit bleibt sie robust gegen + einzelne fahrtenarme Tage. null ohne belastbare Datenlage. */ +export function kmProTagAusFahrten(fahrten: readonly Fahrtdaten[], jetzt = new Date()): number | null { + const gueltig = fahrten.filter((f) => f.status !== "offen" && f.distance_km != null) + if (!gueltig.length) return null + const km = gueltig.reduce((s, f) => s + (f.distance_km ?? 0), 0) + const aeltesteZeit = Math.min(...gueltig.map((f) => new Date(f.ts_start).getTime())) + const tage = (jetzt.getTime() - aeltesteZeit) / TAG_MS + return tage >= 1 ? km / tage : null +} + +export interface Meldungsprognose { + /** ISO-Tagesdatum. */ + datum: string + /** Die vom Fahrzeug gemeldete Zeitgrenze hat gegriffen, nicht die + hochgerechnete Kilometergrenze - der Nutzer fährt zu wenig, um die + km-Grenze rechtzeitig zu erreichen. */ + durchZeitlimit: boolean +} + +/** Prognose rein aus der Fahrzeugmeldung, für den Fall ganz ohne + Servicebucheintrag (siehe Aufrufer unten und in Service.tsx) - ein echter + Servicebucheintrag bleibt vorrangig, näher am tatsächlichen letzten + Service. Restkilometer aus der Meldung, hochgerechnet mit der echten + Fahrleistung aus dem Fahrtenlog statt einer Servicebuch-Rate; gedeckelt + auf das vom Fahrzeug selbst gemeldete Datum als Zeitgrenze. */ +export function meldungsPrognose( + faelligTs: string | null, + faelligKm: number | null, + fahrten: readonly Fahrtdaten[], + jetzt = new Date(), +): Meldungsprognose | null { + const zeitDatum = faelligTs ? faelligTs.slice(0, 10) : null + if (faelligKm == null) return zeitDatum ? { datum: zeitDatum, durchZeitlimit: true } : null + const restKm = Math.abs(faelligKm) + if (restKm <= 0) return { datum: jetzt.toISOString().slice(0, 10), durchZeitlimit: false } + const rate = kmProTagAusFahrten(fahrten, jetzt) + if (!rate) return zeitDatum ? { datum: zeitDatum, durchZeitlimit: true } : null + const kmDatum = new Date(jetzt.getTime() + (restKm / rate) * TAG_MS).toISOString().slice(0, 10) + const durchZeitlimit = !!zeitDatum && zeitDatum < kmDatum + return { datum: durchZeitlimit ? (zeitDatum as string) : kmDatum, durchZeitlimit } +} + /* ------------------------------------------------------ Nächster Service */ export type Serviceart = "Ölwechsel" | "Inspektion" | "Hauptuntersuchung" @@ -143,23 +196,40 @@ export interface NaechsterService { * Der zeitlich nächste anstehende Service über alle drei Arten hinweg — * Gegenstück zu `naechsterTermin()` im Panel. * - * Ölwechsel und Inspektion kommen aus der eigenen Prognose (Servicebuch plus - * tatsächliche Fahrleistung), die Hauptuntersuchung aus dem im Profil - * gepflegten Termin: sie hat kein Kilometerziel, das Fahrzeug meldet sie nicht, - * und hochrechnen lässt sie sich auch nicht. + * Ölwechsel und Inspektion kommen vorrangig aus der eigenen Prognose + * (Servicebuch plus tatsächliche Fahrleistung); ohne Servicebucheintrag dazu + * fällt jede der beiden einzeln auf meldungsPrognose() zurück - die vom + * Fahrzeug selbst gemeldete Fälligkeit, hochgerechnet mit der Fahrleistung + * aus dem Fahrtenlog. Ein frisch eingerichtetes Fahrzeug mit zugeordnetem + * Sensor, aber noch ganz ohne Servicebuch, zeigte hier vorher "kein Eintrag + * im Servicebuch", obwohl die Fälligkeit längst bekannt war. Die + * Hauptuntersuchung kommt weiterhin nur aus dem im Profil gepflegten + * Termin: sie hat kein Kilometerziel, das Fahrzeug meldet sie nicht, und + * hochrechnen lässt sie sich auch nicht. */ export function naechsterService( fahrzeug: Fahrzeug, buch: readonly Buchhaltung[], + fahrten: readonly Fahrtdaten[] = [], jetzt = new Date(), ): NaechsterService | null { const kandidaten: NaechsterService[] = [] const oel = oelwechselPrognose(fahrzeug, buch, jetzt) - if (oel) kandidaten.push({ art: "Ölwechsel", restKm: oel.restKm, datum: oel.datum }) + if (oel) { + kandidaten.push({ art: "Ölwechsel", restKm: oel.restKm, datum: oel.datum }) + } else { + const m = meldungsPrognose(fahrzeug.oelwechselFaelligTs, fahrzeug.oelwechselFaelligKm, fahrten, jetzt) + if (m) kandidaten.push({ art: "Ölwechsel", restKm: fahrzeug.oelwechselFaelligKm, datum: m.datum }) + } const insp = inspektionPrognose(fahrzeug, buch, jetzt) - if (insp) kandidaten.push({ art: "Inspektion", restKm: insp.restKm, datum: insp.datum }) + if (insp) { + kandidaten.push({ art: "Inspektion", restKm: insp.restKm, datum: insp.datum }) + } else { + const m = meldungsPrognose(fahrzeug.inspektionFaelligTs, fahrzeug.inspektionFaelligKm, fahrten, jetzt) + if (m) kandidaten.push({ art: "Inspektion", restKm: fahrzeug.inspektionFaelligKm, datum: m.datum }) + } // fahrzeug.hu wird von Hand gepflegt und kann leer oder unlesbar sein. const hu = new Date(fahrzeug.hu) diff --git a/companion-app/src/screens/Einstellungen.tsx b/companion-app/src/screens/Einstellungen.tsx index 49bed5e..14fbb79 100644 --- a/companion-app/src/screens/Einstellungen.tsx +++ b/companion-app/src/screens/Einstellungen.tsx @@ -38,9 +38,11 @@ export function Einstellungen({ tankvorgaenge, rohprofil, otaBuendel, + integrationUpdate, api, profilSpeichern, jetztAktualisieren, + neuLaden, } = useDaten() const [theme, themeSetzen] = useTheme() const [entwurf, setzeEntwurf] = useState(null) @@ -49,6 +51,8 @@ export function Einstellungen({ const [importOffen, setzeImportOffen] = useState(false) const [otaLaeuft, setzeOtaLaeuft] = useState(false) const [otaFehler, setzeOtaFehler] = useState(null) + const [updatePruefenLaeuft, setzeUpdatePruefenLaeuft] = useState(false) + const [updateInstallierenLaeuft, setzeUpdateInstallierenLaeuft] = useState(false) const dateiwahl = useRef(null) const otaUpdateVerfuegbar = otaMoeglich() && buendelPasst(otaBuendel, eigeneVersion()) @@ -72,6 +76,33 @@ export function Einstellungen({ } } + // Selbst-Update der Integration (aktualisierung.py) - Ersatz für + // install.ps1, das Windows Smart App Control blockiert. Beide Aktionen + // laufen sofort (keine Warteschlange, siehe api/index.ts) und laden + // danach über neuLaden() den neuen Stand von + // sensor.audi_dashboard_app_version - der Fehlertext bei fehlendem Token + // kommt von dort mit, eine eigene "ist ein Token eingetragen?"-Prüfung + // gibt es bewusst nicht. + const updatePruefenAusloesen = async () => { + setzeUpdatePruefenLaeuft(true) + try { + await api.updatePruefen() + await neuLaden() + } finally { + setzeUpdatePruefenLaeuft(false) + } + } + + const updateInstallierenAusloesen = async () => { + setzeUpdateInstallierenLaeuft(true) + try { + await api.updateInstallieren() + await neuLaden() + } finally { + setzeUpdateInstallierenLaeuft(false) + } + } + if (!einstellungen || !fahrzeug) return null const werte = entwurf ?? einstellungen @@ -410,6 +441,56 @@ export function Einstellungen({ )} + + Integration-Update + {integrationUpdate?.installiert ? ( + <> +

+ Version {integrationUpdate.version} installiert. Home Assistant neu starten, damit + die neue Fassung geladen wird — Einstellungen → System → Neu starten. +

+
+ void updatePruefenAusloesen()} disabled={updatePruefenLaeuft}> + {updatePruefenLaeuft ? "Prüfe …" : "Erneut prüfen"} + +
+ + ) : integrationUpdate?.verfuegbar ? ( + <> + } + /> +

+ Lädt die neueste Fassung von Gitea und ersetzt den Integrationsordner. Home Assistant + muss danach neu gestartet werden. Fahrzeugprofil, Fahrten und Tankvorgänge sind davon + nicht betroffen. +

+
+ void updateInstallierenAusloesen()} + disabled={updateInstallierenLaeuft} + > + {updateInstallierenLaeuft ? "Installiere …" : "Update installieren"} + +
+ + ) : ( + <> +

+ {integrationUpdate + ? `Integration ist aktuell (${integrationUpdate.geprueft_am ? datumZeit(integrationUpdate.geprueft_am) : ""}).` + : "Noch nicht geprüft."} +

+
+ void updatePruefenAusloesen()} disabled={updatePruefenLaeuft}> + {updatePruefenLaeuft ? "Prüfe …" : "Auf Update prüfen"} + +
+ + )} + {integrationUpdate?.fehler &&

{integrationUpdate.fehler}

} +
+ Zugang void }) { - const { fahrzeug } = useDaten() + const { fahrzeug, fahrten } = useDaten() if (!fahrzeug) return null const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[] @@ -62,6 +68,12 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => : prognose ? `${de(prognose.restKm)} km · ${datum(prognose.datum)}` : null + // Ganz ohne Servicebucheintrag (prognose null) übernimmt die + // Fahrzeugmeldung selbst die Rolle der Prognosequelle, hochgerechnet mit + // der echten Fahrleistung aus dem Fahrtenlog statt einer Servicebuch-Rate. + const oelMeldungProg = oelHatMeldung && !prognose + ? meldungsPrognose(fahrzeug.oelwechselFaelligTs, fahrzeug.oelwechselFaelligKm, fahrten) + : null // Bei Herstellervorgabe rechnet die eigene Prognose ohnehin schon mit dem // Herstellerintervall - die Restkilometerzahl daneben wäre nur eine zweite, // verwirrende km-Angabe neben dem Fahrzeug-Wert oben. Nur beim eigenen @@ -70,7 +82,9 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => ? fahrzeug.oel.modus === "hersteller" ? `voraussichtlich am ${datum(prognose.datum)}` : `${de(prognose.restKm)} km, voraussichtlich am ${datum(prognose.datum)}` - : null + : oelMeldungProg + ? `voraussichtlich am ${datum(oelMeldungProg.datum)}` + : null // Der Ölwechsel folgt in "Einrichten" wahlweise der Herstellervorgabe oder // einem kürzeren eigenen Intervall (fahrzeug.oel.modus) - die Inspektion hat // kein einstellbares Intervall, rechnet also immer mit der Herstellervorgabe @@ -92,7 +106,14 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => : inspektionsprognose ? datum(inspektionsprognose.datum) : null - const inspFussnote = inspektionsprognose ? `voraussichtlich am ${datum(inspektionsprognose.datum)}` : null + const inspMeldungProg = inspHatMeldung && !inspektionsprognose + ? meldungsPrognose(fahrzeug.inspektionFaelligTs, fahrzeug.inspektionFaelligKm, fahrten) + : null + const inspFussnote = inspektionsprognose + ? `voraussichtlich am ${datum(inspektionsprognose.datum)}` + : inspMeldungProg + ? `voraussichtlich am ${datum(inspMeldungProg.datum)}` + : null const huDatum = fahrzeug.hu ? new Date(fahrzeug.hu) : null const huGueltig = !!huDatum && !Number.isNaN(huDatum.getTime()) @@ -107,7 +128,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => Ölwechsel - {oelHatEintrag ? ( + {oelHatEintrag || oelHatMeldung ? ( <> {oelHerstellervorgabe &&
Herstellervorgabe
}
{oelWert}
@@ -123,7 +144,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => Inspektion - {inspHatEintrag ? ( + {inspHatEintrag || inspHatMeldung ? ( <>
Herstellervorgabe
{inspWert}
diff --git a/companion-app/src/screens/Uebersicht.tsx b/companion-app/src/screens/Uebersicht.tsx index 3553777..e741517 100644 --- a/companion-app/src/screens/Uebersicht.tsx +++ b/companion-app/src/screens/Uebersicht.tsx @@ -53,7 +53,7 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string) .trim() const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[] - const service = naechsterService(fahrzeug, buch) + const service = naechsterService(fahrzeug, buch, fahrten) const letzteFahrt = fahrten .slice() diff --git a/companion-app/vite.config.ts b/companion-app/vite.config.ts index d09f043..74482e4 100644 --- a/companion-app/vite.config.ts +++ b/companion-app/vite.config.ts @@ -5,18 +5,20 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" // Die App-Version kommt aus der manifest.json der Integration — derselben -// Zahl, die HACS anzeigt, die Home Assistant als Version der Integration führt -// und die das Backend über sensor.audi_dashboard_app_version meldet (siehe +// Zahl, die Home Assistant als Version der Integration führt und die das +// Backend über sensor.audi_dashboard_app_version meldet (siehe // VERSIONIERUNG.md). Sie wird hier fest einkompiliert, damit die fertige App // weiß, aus welchem Stand sie gebaut wurde, und das gegen den vom Backend // gemeldeten Stand halten kann. // // Bis 2026-08-23 stand die Zahl in einer eigenen Datei VERSION im -// Projektstamm. Die ist mit dem Umbau zur HACS-Integration überflüssig -// geworden: die manifest.json MUSS eine Version tragen, HACS verlangt sie und -// vergleicht danach. Zwei Quellen für dieselbe Angabe wären eine Quelle zu -// viel — sie hätten irgendwann auseinandergelegen, und der Vergleich, den -// diese Zahl trägt, wäre still falsch geworden. +// Projektstamm. Die ist mit dem Umbau zur Integration überflüssig geworden: +// Home Assistant selbst verlangt ein version-Feld in jeder manifest.json, +// unabhängig von HACS (das für dieses private Repository ohnehin dauerhaft +// ausscheidet, siehe AGENTS.md Abschnitt H). Zwei Quellen für dieselbe +// Angabe wären eine Quelle zu viel — sie hätten irgendwann +// auseinandergelegen, und der Vergleich, den diese Zahl trägt, wäre still +// falsch geworden. // // Absichtlich hart: fehlt die Datei oder das Feld, soll der Build abbrechen // statt still eine App ohne Versionsangabe zu erzeugen — die könnte ihre diff --git a/custom_components/audi_dashboard/__init__.py b/custom_components/audi_dashboard/__init__.py index ea90a77..b071d72 100644 --- a/custom_components/audi_dashboard/__init__.py +++ b/custom_components/audi_dashboard/__init__.py @@ -104,10 +104,10 @@ async def _statik_anmelden(hass: HomeAssistant) -> None: scheitern. Das ist unkritisch - der Pfad zeigt auf einen Ordner, dessen Inhalt sich beim Neu-Laden nicht ändert. - cache_headers=False ist Absicht: die Dateien werden von HACS ersetzt, und - ein Browser, der eine alte Fassung 31 Tage lang aus dem Cache bedient - (die HA-Vorgabe für statische Pfade), wäre genau die stille Veraltung, die - das Versions-Verfahren verhindern soll.""" + cache_headers=False ist Absicht: die Dateien werden von install.ps1 + ersetzt, und ein Browser, der eine alte Fassung 31 Tage lang aus dem + Cache bedient (die HA-Vorgabe für statische Pfade), wäre genau die + stille Veraltung, die das Versions-Verfahren verhindern soll.""" if hass.data.get(DOMAIN, {}).get("statik"): return ordner = os.path.join(os.path.dirname(__file__), STATIK_ORDNER) diff --git a/custom_components/audi_dashboard/aktualisierung.py b/custom_components/audi_dashboard/aktualisierung.py new file mode 100644 index 0000000..0a4a7a6 --- /dev/null +++ b/custom_components/audi_dashboard/aktualisierung.py @@ -0,0 +1,244 @@ +"""Selbst-Update der Integration direkt von Gitea. + +Ersetzt install.ps1 als Update-Weg - nicht als Erstinstallations-Weg, die +Integration muss bereits laufen, um sich selbst aktualisieren zu können. +Grund: Windows Smart App Control blockiert die Ausführung von install.ps1 +zuverlässig und lässt sich, anders als der SmartScreen davor, nicht per +"trotzdem ausführen" umgehen. + +SICHERHEIT: dieses Modul läuft, anders als install.ps1, INNERHALB des +Prozesses, den es aktualisiert - der eigene Ordner wird ersetzt, während der +Dienstaufruf noch läuft. Deshalb strenger als install.ps1s +"leeren-dann-kopieren": + + 1. Laden und Prüfen passiert komplett in einem Geschwisterordner + (audi_dashboard_update_staging). Der Live-Ordner bleibt unangetastet, + bis feststeht, dass der Download vollständig und plausibel ist. + 2. Erst danach ein atomarer Tausch per os.rename, nicht Löschen+Kopieren: + der alte Ordner wird zu audi_dashboard_backup umbenannt statt gelöscht - + reversibel, falls das Update Probleme macht. + 3. Schlägt der zweite Rename fehl, wird der erste zurückgenommen, statt + einen halb ersetzten Ordner zurückzulassen. + 4. Kein automatischer Reload oder Neustart. Ein Reload aus dem eigenen, + noch laufenden Dienstaufruf heraus anzustoßen wäre riskant - der + Handler könnte mitten in der Ausführung durch den eigenen Reload + unterbrochen werden. Die Oberfläche zeigt stattdessen "Home Assistant + neu starten" an, genau wie install.ps1 es über seine Restliste tut. + 5. Nichts außerhalb des eigenen Ordners: configuration.yaml, .storage/, + www/ und der separate Fahrzeugdaten-Ordner werden nirgends geöffnet. + +Das Umbenennen des eigenen, gerade ausgeführten Ordners ist auf Linux (Home +Assistant OS/Docker, worauf dieses Projekt läuft) unkritisch: Python hält +nach dem Import keine offenen Dateizugriffe auf die .py-Quelltexte mehr, +und der Verzeichnisname selbst ist nur eine Zeichenkette in os.rename() - +kein Handle, das durch die Umbenennung ungültig würde. +""" + +from __future__ import annotations + +import base64 +import datetime +import json +import logging +import os +import shutil +import zipfile +from io import BytesIO +from typing import Any + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +_LOGGER = logging.getLogger(__name__) + +_REPO_BESITZER = "paul" +_REPO_NAME = "audi-app" +_ZWEIG = "main" +_GITEA_BASIS = "https://gitea.nothaft.cloud/api/v1" +_MANIFEST_PFAD_IM_REPO = "custom_components/audi_dashboard/manifest.json" +_INTEGRATIONS_PFAD_IM_REPO = "custom_components/audi_dashboard/" + +# Absolute Pfade, einmal zur Modul-Ladezeit bestimmt - __file__ liegt selbst +# im Live-Ordner, dessen Elternordner (custom_components/) ist der Ort für +# Staging- und Backup-Geschwisterordner. +INTEGRATIONSORDNER = os.path.dirname(os.path.abspath(__file__)) +_STAGING_ORDNER = os.path.join( + os.path.dirname(INTEGRATIONSORDNER), "audi_dashboard_update_staging" +) +_BACKUP_ORDNER = os.path.join(os.path.dirname(INTEGRATIONSORDNER), "audi_dashboard_backup") + + +class AktualisierungsFehler(Exception): + """Jeder Fehlerfall beim Prüfen oder Installieren - Netzwerk, falscher + Token, kaputtes Zip, falsche Domain, fehlgeschlagener Tausch. Eine + eigene Klasse statt roher Exceptions, damit der Dienstaufruf sie gezielt + abfangen und der Oberfläche eine verständliche Meldung zeigen kann, + statt eines unbehandelten Fehlers.""" + + +def _header(token: str) -> dict[str, str]: + return {"Authorization": f"token {token}"} + + +async def version_pruefen(hass: HomeAssistant, token: str, eigene_version: str) -> dict[str, Any]: + """Fragt nur die manifest.json von Gitea ab (ein kleiner Request, kein + Repo-Download) und vergleicht die Version gegen die installierte. + + Läuft ausschließlich auf Tastendruck aus der Oberfläche - niemals aus + dem Koordinator-Takt heraus, sonst würde jeder normale Veröffentlichungs- + Zyklus einen Netzwerkaufruf zu Gitea auslösen.""" + if not token: + raise AktualisierungsFehler( + "Kein Gitea-Token hinterlegt - unter Einstellungen -> Geräte & Dienste -> " + "Audi Dashboard -> Konfigurieren eintragen." + ) + + url = f"{_GITEA_BASIS}/repos/{_REPO_BESITZER}/{_REPO_NAME}/contents/{_MANIFEST_PFAD_IM_REPO}" + session = async_get_clientsession(hass) + try: + async with session.get(url, headers=_header(token), params={"ref": _ZWEIG}) as antwort: + if antwort.status == 401: + raise AktualisierungsFehler("Token ungültig oder abgelaufen.") + if antwort.status == 404: + raise AktualisierungsFehler("Repository oder Pfad bei Gitea nicht gefunden.") + if antwort.status != 200: + raise AktualisierungsFehler(f"Gitea antwortete mit Status {antwort.status}.") + rumpf = await antwort.json() + except AktualisierungsFehler: + raise + except Exception as fehler: # aiohttp-/Verbindungsfehler aller Art + raise AktualisierungsFehler(f"Gitea nicht erreichbar: {fehler}") from fehler + + try: + roh = base64.b64decode(rumpf["content"]) + remote_version = json.loads(roh)["version"] + except (KeyError, ValueError, TypeError) as fehler: + raise AktualisierungsFehler(f"Antwort von Gitea nicht lesbar: {fehler}") from fehler + + return { + "verfuegbar": remote_version != eigene_version, + "version": remote_version, + "geprueft_am": datetime.datetime.now(datetime.timezone.utc).isoformat(), + } + + +async def update_installieren(hass: HomeAssistant, token: str) -> dict[str, Any]: + """Lädt das komplette Repo-Archiv und delegiert Entpacken/Prüfen/Tauschen + an eine blockierende Funktion im Executor - Datei- und Zip-Operationen + gehören nicht in den Event-Loop.""" + if not token: + raise AktualisierungsFehler( + "Kein Gitea-Token hinterlegt - unter Einstellungen -> Geräte & Dienste -> " + "Audi Dashboard -> Konfigurieren eintragen." + ) + + url = f"{_GITEA_BASIS}/repos/{_REPO_BESITZER}/{_REPO_NAME}/archive/{_ZWEIG}.zip" + session = async_get_clientsession(hass) + try: + async with session.get(url, headers=_header(token)) as antwort: + if antwort.status == 401: + raise AktualisierungsFehler("Token ungültig oder abgelaufen.") + if antwort.status != 200: + raise AktualisierungsFehler(f"Gitea antwortete mit Status {antwort.status}.") + zip_bytes = await antwort.read() + except AktualisierungsFehler: + raise + except Exception as fehler: + raise AktualisierungsFehler(f"Gitea nicht erreichbar: {fehler}") from fehler + + neue_version = await hass.async_add_executor_job( + entpacken_pruefen_tauschen, zip_bytes, INTEGRATIONSORDNER, _STAGING_ORDNER, _BACKUP_ORDNER + ) + return {"version": neue_version} + + +def entpacken_pruefen_tauschen( + zip_bytes: bytes, integrationsordner: str, staging_ordner: str, backup_ordner: str +) -> str: + """Blockierend: entpackt nur custom_components/audi_dashboard/ aus dem + Archiv in staging_ordner, prüft das dortige manifest.json, und tauscht + per os.rename mit integrationsordner. Wirft AktualisierungsFehler bei + jedem Problem - und zwar VOR jedem Tausch, siehe Moduldocstring. + + Bewusst ohne hass-Abhängigkeit und mit allen Pfaden als Parameter statt + fester Modul-Konstanten, damit sie sich isoliert mit einem selbstgebauten + Zip gegen einen Temp-Ordner testen lässt, ohne den echten + Integrationsordner anzufassen.""" + if os.path.isdir(staging_ordner): + shutil.rmtree(staging_ordner) + + try: + with zipfile.ZipFile(BytesIO(zip_bytes)) as archiv: + namen = archiv.namelist() + if not namen: + raise AktualisierungsFehler("Leeres Archiv erhalten.") + # Gitea packt alles unter einem Wurzelordner wie "audi-app-main/" + # - das wird hier gesucht statt geraten. + treffer = [ + n for n in namen if _INTEGRATIONS_PFAD_IM_REPO in n and not n.endswith("/") + ] + if not treffer: + raise AktualisierungsFehler( + f"{_INTEGRATIONS_PFAD_IM_REPO} nicht im heruntergeladenen Archiv gefunden." + ) + wurzel_marker = treffer[0].split(_INTEGRATIONS_PFAD_IM_REPO, 1)[0] \ + + _INTEGRATIONS_PFAD_IM_REPO + os.makedirs(staging_ordner, exist_ok=True) + for name in treffer: + if wurzel_marker not in name: + continue + rel = name.split(wurzel_marker, 1)[1] + if not rel: + continue + ziel = os.path.join(staging_ordner, rel.replace("/", os.sep)) + os.makedirs(os.path.dirname(ziel), exist_ok=True) + with archiv.open(name) as quelle, open(ziel, "wb") as senke: + shutil.copyfileobj(quelle, senke) + except zipfile.BadZipFile as fehler: + shutil.rmtree(staging_ordner, ignore_errors=True) + raise AktualisierungsFehler(f"Kein gültiges Zip-Archiv: {fehler}") from fehler + + manifest_pfad = os.path.join(staging_ordner, "manifest.json") + if not os.path.exists(manifest_pfad): + shutil.rmtree(staging_ordner, ignore_errors=True) + raise AktualisierungsFehler("Entpackter Ordner enthält keine manifest.json.") + with open(manifest_pfad, encoding="utf-8") as datei: + manifest = json.load(datei) + if manifest.get("domain") != "audi_dashboard": + shutil.rmtree(staging_ordner, ignore_errors=True) + raise AktualisierungsFehler( + f"Heruntergeladenes Manifest gehört zu Domain '{manifest.get('domain')}', " + "nicht 'audi_dashboard' - Abbruch, nichts wurde angefasst." + ) + neue_version = manifest.get("version") + if not neue_version: + shutil.rmtree(staging_ordner, ignore_errors=True) + raise AktualisierungsFehler("Heruntergeladenes Manifest hat kein version-Feld.") + + # __pycache__ aus dem Staging-Ordner entfernen - dieselbe Sorgfalt wie + # install.ps1s Aufräumschritt nach dem Kopieren. + for wurzel, ordner, _dateien in os.walk(staging_ordner): + if "__pycache__" in ordner: + shutil.rmtree(os.path.join(wurzel, "__pycache__"), ignore_errors=True) + + if os.path.isdir(backup_ordner): + shutil.rmtree(backup_ordner) + try: + os.rename(integrationsordner, backup_ordner) + except OSError as fehler: + shutil.rmtree(staging_ordner, ignore_errors=True) + raise AktualisierungsFehler( + f"Konnte den bestehenden Ordner nicht sichern: {fehler}" + ) from fehler + + try: + os.rename(staging_ordner, integrationsordner) + except OSError as fehler: + # Rollback: die alte Fassung zurückbenennen, statt HA ohne + # Integrationsordner dastehen zu lassen. + os.rename(backup_ordner, integrationsordner) + raise AktualisierungsFehler( + f"Tausch fehlgeschlagen, alte Fassung wiederhergestellt: {fehler}" + ) from fehler + + return neue_version diff --git a/custom_components/audi_dashboard/bilder.py b/custom_components/audi_dashboard/bilder.py index 9887da1..a29e9b2 100644 --- a/custom_components/audi_dashboard/bilder.py +++ b/custom_components/audi_dashboard/bilder.py @@ -14,9 +14,10 @@ bewusst nicht: jede Fotogröße muss hochladbar sein. WARUM DIESE DATEIEN NICHT IM INTEGRATIONSORDNER LIEGEN: sie sind Nutzerdaten, kein Auslieferbestandteil. Lägen sie unter custom_components/audi_dashboard/, -würde das nächste HACS-Update sie mitlöschen. /config/www/bilder/ gehört -dagegen dem Nutzer und wird von keinem Update angefasst; ausgeliefert wird es -weiterhin unter /local/bilder/. +würde der nächste install.ps1-Lauf sie mitlöschen (er leert den Zielordner +vor dem Kopieren, siehe install.ps1). /config/www/bilder/ gehört dagegen dem +Nutzer und wird von keinem Update angefasst; ausgeliefert wird es weiterhin +unter /local/bilder/. """ from __future__ import annotations diff --git a/custom_components/audi_dashboard/config_flow.py b/custom_components/audi_dashboard/config_flow.py index c17dea7..609278d 100644 --- a/custom_components/audi_dashboard/config_flow.py +++ b/custom_components/audi_dashboard/config_flow.py @@ -1,25 +1,34 @@ """Einrichtung über die Oberfläche (Einstellungen -> Geräte & Dienste -> Integration hinzufügen -> Audi Dashboard). -Bewusst ohne Eingabefelder. Es gäbe genau eine Sache zu fragen - welche -Entität welche Rolle im Fahrzeug spielt -, und die gehört nicht hierher: die -Zuordnung wird im Setup-Menü der App selbst vorgenommen, wo neben jedem Feld -steht, wofür es gebraucht wird und was passiert, wenn es leer bleibt. Sie -hier abzufragen hieße, dieselbe Auswahl an zwei Stellen zu pflegen - und die -schlechtere von beiden zuerst zu zeigen. +Bewusst ohne Eingabefelder im Ersteinrichtungs-Schritt. Es gäbe genau eine +Sache zu fragen - welche Entität welche Rolle im Fahrzeug spielt -, und die +gehört nicht hierher: die Zuordnung wird im Setup-Menü der App selbst +vorgenommen, wo neben jedem Feld steht, wofür es gebraucht wird und was +passiert, wenn es leer bleibt. Sie hier abzufragen hieße, dieselbe Auswahl an +zwei Stellen zu pflegen - und die schlechtere von beiden zuerst zu zeigen. Eine einzige Instanz, erzwungen über single_config_entry in manifest.json: die App gehört zu genau einem Fahrzeug, und alle Bestände liegen unter einem festen Pfad. + +Der OptionsFlow unten ist die einzige Ausnahme: ein Gitea-Zugriffstoken für +das Selbst-Update (siehe aktualisierung.py) - ein echtes Geheimnis, das +deshalb in HAs eigenem, verschlüsseltem Config-Entry-Speicher landet +(entry.options), nicht in configuration.yaml. """ from __future__ import annotations from typing import Any -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +import voluptuous as vol -from .const import DOMAIN +from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult, OptionsFlow +from homeassistant.core import callback +from homeassistant.helpers import selector + +from .const import CONF_GITEA_TOKEN, DOMAIN class AudiDashboardConfigFlow(ConfigFlow, domain=DOMAIN): @@ -35,3 +44,35 @@ class AudiDashboardConfigFlow(ConfigFlow, domain=DOMAIN): if user_input is None: return self.async_show_form(step_id="user") return self.async_create_entry(title="Audi Dashboard", data={}) + + @staticmethod + @callback + def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: + return AudiDashboardOptionsFlow() + + +class AudiDashboardOptionsFlow(OptionsFlow): + """Einstellungen -> Geräte & Dienste -> Audi Dashboard -> Konfigurieren. + + Ein Feld: der Gitea-Token fürs Selbst-Update. Leer lassen deaktiviert + die Update-Prüfung wieder (aktualisierung.py verlangt einen nicht-leeren + Token, sonst gibt es eine verständliche Fehlermeldung statt eines + unbehandelten Fehlers).""" + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + if user_input is not None: + return self.async_create_entry(title="", data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=self.add_suggested_values_to_schema( + vol.Schema({ + vol.Optional(CONF_GITEA_TOKEN, default=""): selector.TextSelector( + selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD) + ), + }), + self.config_entry.options, + ), + ) diff --git a/custom_components/audi_dashboard/const.py b/custom_components/audi_dashboard/const.py index 48def17..fe7952d 100644 --- a/custom_components/audi_dashboard/const.py +++ b/custom_components/audi_dashboard/const.py @@ -32,22 +32,22 @@ PANEL_KOMPONENTE = "audi-dashboard-panel" # Statischer Pfad, unter dem die Oberflächen-Dateien aus frontend/ # ausgeliefert werden. Ersetzt /local/ (also /config/www/): die Dateien -# gehören jetzt zum Installationspaket der Integration und werden von HACS -# mitgeliefert, statt separat nach www/ kopiert zu werden. +# gehören jetzt zum Installationspaket der Integration und werden von +# install.ps1 mitgeliefert, statt separat nach www/ kopiert zu werden. STATIK_URL = "/audi_dashboard_static" STATIK_ORDNER = "frontend" # Das OTA-Bündel der iOS-App: dieselbe Oberfläche, als Zip zum Nachladen. -# Liegt bewusst mit im Integrationsordner - dann liefert HACS es bei jedem -# Update mit aus, und Bündel und Integration können gar nicht auseinander- -# laufen (siehe companion-app/scripts/ota-paket.ps1). +# Liegt bewusst mit im Integrationsordner - dann liefert install.ps1 es bei +# jeder Auslieferung mit aus, und Bündel und Integration können gar nicht +# auseinanderlaufen (siehe companion-app/scripts/ota-paket.ps1). BUENDEL_ORDNER = "frontend/app" BUENDEL_URL = f"{STATIK_URL}/app/bundle.zip" BUENDEL_INFO = "bundle.json" # Vom Nutzer hochgeladene Fahrzeugfotos. Die bleiben in /config/www/bilder, # also unter /local/bilder/: sie sind NUTZERDATEN, kein Auslieferbestandteil. -# Lägen sie im Integrationsordner, würde das nächste HACS-Update sie +# Lägen sie im Integrationsordner, würde der nächste install.ps1-Lauf sie # mitlöschen - genau das darf nicht passieren. BILDER_ORDNER = "www/bilder" @@ -89,3 +89,11 @@ D_REIFEN_KM_SETZEN = "reifen_km_setzen" D_BACKUP_JETZT = "backup_jetzt" D_BACKUP_WIEDERHERSTELLEN = "backup_wiederherstellen" D_HISTORIE_IMPORTIEREN = "historie_importieren" +D_UPDATE_PRUEFEN = "update_pruefen" +D_UPDATE_INSTALLIEREN = "update_installieren" + +# Schlüssel für den Gitea-Zugriffstoken in entry.options (siehe +# config_flow.py's AudiDashboardOptionsFlow und aktualisierung.py). Hier statt +# in config_flow.py definiert, damit aktualisierung.py/dienste.py ihn ohne +# Umweg über den Options-Flow importieren können. +CONF_GITEA_TOKEN = "gitea_token" diff --git a/custom_components/audi_dashboard/dienste.py b/custom_components/audi_dashboard/dienste.py index 9000872..3e8fa72 100644 --- a/custom_components/audi_dashboard/dienste.py +++ b/custom_components/audi_dashboard/dienste.py @@ -15,7 +15,9 @@ nicht die Schema-Prüfung. from __future__ import annotations +import datetime import json +import logging from collections.abc import Callable, Coroutine from typing import Any @@ -24,9 +26,19 @@ import voluptuous as vol from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import config_validation as cv -from . import belege, bilder, fahrterkennung, historienimport, reifen, screening, sicherung +from . import ( + aktualisierung, + belege, + bilder, + fahrterkennung, + historienimport, + reifen, + screening, + sicherung, +) from .const import ( BILDER_ORDNER, + CONF_GITEA_TOKEN, D_BACKUP_JETZT, D_BACKUP_WIEDERHERSTELLEN, D_BELEG_HOCHLADEN, @@ -45,10 +57,14 @@ from .const import ( D_TANKVORGANG_AKTUALISIEREN, D_TANKVORGANG_LOESCHEN, D_TANKVORGANG_MANUELL, + D_UPDATE_INSTALLIEREN, + D_UPDATE_PRUEFEN, DOMAIN, ) from .koordinator import Koordinator +_LOGGER = logging.getLogger(__name__) + # Ein JSON-Feld darf als Zeichenkette oder schon ausgepackt ankommen: das # Panel schickt JSON.stringify(...), ein Aufruf aus einer Automation heraus # reicht dagegen naheliegenderweise die Struktur selbst durch. @@ -126,6 +142,8 @@ SCHEMATA: dict[str, vol.Schema] = { vol.Required("start"): cv.string, vol.Required("ende"): cv.string, }), + D_UPDATE_PRUEFEN: vol.Schema({}), + D_UPDATE_INSTALLIEREN: vol.Schema({}), } Fabrik = Callable[[ServiceCall], Coroutine[Any, Any, None]] @@ -202,6 +220,59 @@ def registrieren(hass: HomeAssistant, k: Koordinator) -> None: "audi_dashboard_historienimport", ) + async def update_pruefen(_aufruf: ServiceCall) -> None: + """Fragt bei Gitea nach, ob eine neuere Fassung vorliegt - ohne + etwas herunterzuladen. Läuft nur auf Tastendruck, siehe + aktualisierung.py's Moduldocstring.""" + token = k.entry.options.get(CONF_GITEA_TOKEN, "") + jetzt = datetime.datetime.now(datetime.timezone.utc).isoformat() + try: + ergebnis = await aktualisierung.version_pruefen(hass, token, k.version) + k.letzte_update_pruefung = { + "verfuegbar": ergebnis["verfuegbar"], + "version": ergebnis["version"], + "geprueft_am": ergebnis["geprueft_am"], + "installiert": False, + "fehler": None, + } + except aktualisierung.AktualisierungsFehler as fehler: + _LOGGER.warning("Update-Prüfung fehlgeschlagen: %s", fehler) + k.letzte_update_pruefung = { + "verfuegbar": False, + "version": None, + "geprueft_am": jetzt, + "installiert": False, + "fehler": str(fehler), + } + await k.app_version_veroeffentlichen() + + async def update_installieren(_aufruf: ServiceCall) -> None: + """Lädt die neueste Fassung von Gitea und ersetzt den + Integrationsordner. Home Assistant muss danach von Hand neu + gestartet werden - kein automatischer Reload aus dem eigenen, + noch laufenden Aufruf heraus, siehe aktualisierung.py.""" + token = k.entry.options.get(CONF_GITEA_TOKEN, "") + jetzt = datetime.datetime.now(datetime.timezone.utc).isoformat() + try: + ergebnis = await aktualisierung.update_installieren(hass, token) + k.letzte_update_pruefung = { + "verfuegbar": False, + "version": ergebnis["version"], + "geprueft_am": jetzt, + "installiert": True, + "fehler": None, + } + except aktualisierung.AktualisierungsFehler as fehler: + _LOGGER.warning("Update-Installation fehlgeschlagen: %s", fehler) + k.letzte_update_pruefung = { + "verfuegbar": (k.letzte_update_pruefung or {}).get("verfuegbar", False), + "version": (k.letzte_update_pruefung or {}).get("version"), + "geprueft_am": jetzt, + "installiert": False, + "fehler": str(fehler), + } + await k.app_version_veroeffentlichen() + bilder_ordner = hass.config.path(BILDER_ORDNER) behandler: dict[str, Callable[[ServiceCall], Coroutine[Any, Any, None]]] = { @@ -245,6 +316,8 @@ def registrieren(hass: HomeAssistant, k: Koordinator) -> None: D_BACKUP_WIEDERHERSTELLEN: _als_dienst( lambda a: sicherung.wiederherstellen(k, dict(a.data)) ), + D_UPDATE_PRUEFEN: update_pruefen, + D_UPDATE_INSTALLIEREN: update_installieren, } for name, funktion in behandler.items(): diff --git a/custom_components/audi_dashboard/frontend/app/bundle.json b/custom_components/audi_dashboard/frontend/app/bundle.json index 1db02b7..33c011f 100644 --- a/custom_components/audi_dashboard/frontend/app/bundle.json +++ b/custom_components/audi_dashboard/frontend/app/bundle.json @@ -1 +1 @@ -{"version":"2026.8.24.1","sha256":"967ab36fc866187cf5fdc03d95a3ec21cb6e42003d8c012f8f67b99c67373324","bytes":230770,"gebaut":"2026-08-24T07:16:44Z"} \ No newline at end of file +{"version":"2026.8.24.4","sha256":"cbce1baa785a3577fc65168b9f8b2094ecd844aadc2ac36f99d6be8ccb4cb8c1","bytes":231567,"gebaut":"2026-08-24T09:39:59Z"} \ No newline at end of file diff --git a/custom_components/audi_dashboard/frontend/app/bundle.zip b/custom_components/audi_dashboard/frontend/app/bundle.zip index f2b81fbb2f4097bae697af66103a75903cf02d74..aa452782beba058c6a717bf53bdf06cad22495ed 100644 GIT binary patch delta 89247 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 t.status !== "offen" && t.distance_km != null); + if (!gueltig.length) return null; + const km = gueltig.reduce((s, t) => s + t.distance_km, 0); + const aeltesteZeit = Math.min(...gueltig.map((t) => new Date(t.ts_start).getTime())); + const tage = (heute().getTime() - aeltesteZeit) / dTag; + return tage >= 1 ? km / tage : null; +} + +/* Prognose rein aus der Fahrzeugmeldung, für den Fall ganz ohne + Servicebucheintrag (siehe Aufrufer in vService()) - ein echter + Servicebucheintrag bleibt vorrangig, näher am tatsächlichen letzten + Service. Restkilometer aus der Meldung, hochgerechnet mit der echten + Fahrleistung aus dem Fahrtenlog statt einer Servicebuch-Rate; gedeckelt + auf das vom Fahrzeug selbst gemeldete Datum als Zeitgrenze - fährt der + Nutzer zu wenig, um die km-Grenze rechtzeitig zu erreichen, ist die + gemeldete Zeitgrenze maßgeblich, nicht die hochgerechnete km-Grenze. */ +function meldungsPrognose(art) { + const fm = fahrzeugMeldungRoh(art); + const zeitDatum = fm.ts && gueltigesDatum(new Date(fm.ts)) ? new Date(fm.ts) : null; + if (fm.km == null) return zeitDatum ? { datum: zeitDatum, durchZeitlimit: true } : null; + const restKm = Math.abs(fm.km); + if (restKm <= 0) return { datum: heute(), durchZeitlimit: false }; + const rate = kmProTagAusFahrten(); + if (!rate) return zeitDatum ? { datum: zeitDatum, durchZeitlimit: true } : null; + const kmDatum = new Date(heute().getTime() + (restKm / rate) * dTag); + const durchZeitlimit = !!(zeitDatum && zeitDatum < kmDatum); + return { datum: durchZeitlimit ? zeitDatum : kmDatum, durchZeitlimit }; +} + function oelwechselPrognose() { const oel = CAR.oel; return servicePrognose("öl", @@ -1776,21 +1812,35 @@ function vService() { const artIcon = ART_ICON[t.art]; const iconMarkup = artIcon ? ciSVG(artIcon, ART_ICON_GROESSE[t.art]) : ""; const kopf = `
${iconMarkup}${t.art}
`; + // Fahrzeugmeldung zuerst prüfen: sie steht für sich, auch ganz ohne + // Servicebucheintrag - siehe fahrzeugMeldungRoh()'s Kommentar. Ein + // frisch eingerichtetes Fahrzeug mit zugeordnetem Sensor und + // "Herstellervorgabe", aber ohne einen einzigen Servicebucheintrag, + // zeigte vorher trotz korrekt gemeldeter Fälligkeit nur "kein Eintrag + // im Servicebuch" - iv war null und der frühe Return griff, bevor fm + // überhaupt gelesen wurde. + const fm = fahrzeugMeldungRoh(t.art); + const fmDatum = fm.ts ? dedat(new Date(fm.ts)) : null; + const hatMeldung = fm.km != null || fmDatum; const iv = intervalle(t); - if (!iv) return `
+ if (!iv && !hatMeldung) return `
${kopf}
kein Eintrag im Servicebuch
`; - const anzeige = iv.hersteller || iv.eigen; - const fm = fahrzeugMeldungRoh(t.art); - const fmDatum = fm.ts ? dedat(new Date(fm.ts)) : null; + const anzeige = iv ? (iv.hersteller || iv.eigen) : null; // Forecast-Zeile aus dem kundeneigenen Intervall in "Einrichten" mit // eigenständiger Reset-nach-Service-Logik (servicePrognose()): beim // Ölwechsel mit Restkilometern, bei der Inspektion auf Wunsch nur mit - // dem Datum. Hauptuntersuchung bleibt bewusst ohne. + // dem Datum. Hauptuntersuchung bleibt bewusst ohne. Beide liefern null + // ohne Servicebucheintrag (servicePrognose() braucht letzterEintrag) - + // dann bleibt nur die Fahrzeugmeldung als Wert übrig, siehe wert unten. const eigenOel = t.art === "Ölwechsel" ? oelwechselPrognose() : null; const eigenInsp = t.art === "Inspektion" ? inspektionPrognose() : null; - const hatMeldung = fm.km != null || fmDatum; + // Ganz ohne Servicebucheintrag (eigenOel/eigenInsp beide null) übernimmt + // die Fahrzeugmeldung selbst die Rolle der Prognosequelle, hochgerechnet + // mit der echten Fahrleistung aus dem Fahrtenlog statt einer + // Servicebuch-Rate - siehe meldungsPrognose(). + const meldungProg = hatMeldung && !eigenOel && !eigenInsp ? meldungsPrognose(t.art) : null; // Bei Herstellervorgabe rechnet die eigene Prognose ohnehin schon mit dem // Herstellerintervall - die Restkilometerzahl daneben waere nur eine // zweite, verwirrende km-Angabe neben dem Fahrzeug-Wert oben. Nur beim @@ -1799,7 +1849,8 @@ function vService() { ? (CAR.oel.modus === "hersteller" ? `voraussichtlich am ${dedat(eigenOel.datum)}` : `${de(eigenOel.restKm)} km, voraussichtlich am ${dedat(eigenOel.datum)}`) - : eigenInsp ? `voraussichtlich am ${dedat(eigenInsp.datum)}` : ""; + : eigenInsp ? `voraussichtlich am ${dedat(eigenInsp.datum)}` + : meldungProg ? `voraussichtlich am ${dedat(meldungProg.datum)}` : ""; const wert = hatMeldung ? `${fm.km != null ? de(fm.km) + " km" : ""}${fm.km != null && fmDatum ? " / " : ""}${fmDatum || ""}` : (eigenOel ? de(eigenOel.restKm) + " km · " + dedat(eigenOel.datum) : (anzeige.km !== null ? de(anzeige.km) + " km · " : "") + dedat(anzeige.datum)); @@ -1808,7 +1859,7 @@ function vService() { // Intervall gar nicht. Deshalb zaehlt sie unabhaengig von iv.hersteller // als Herstellervorgabe; nur der App-eigene Eigen-Intervall-Fallback // (anzeige.eigen/eigenOel bei modus "eigen") ist es nicht. - const herstellervorgabe = hatMeldung || iv.hersteller; + const herstellervorgabe = hatMeldung || (iv && iv.hersteller); return `
${kopf} @@ -2331,11 +2382,37 @@ function vEinst() {
Fahrzeugdaten
FMM003Zündung · Batterie
Position
FMM003GPS-Tracker
- Updates laufen über HACS - - HACS meldet selbst, wenn eine neuere Fassung bereitliegt, und installiert sie. - Fahrzeugprofil, Fahrten, Tankvorgänge und eigene Fotos bleiben dabei unangetastet — - die liegen außerhalb des Integrationsordners. +
+
Integration-Update + ${(() => { + const u = INTEGRATION_UPDATE; + // Ersetzt install.ps1 als laufenden Update-Weg (Windows Smart App + // Control blockiert dessen Ausführung zuverlässig) - install.ps1 + // bleibt nur noch für die Erstinstallation nötig, bevor die + // Integration überhaupt läuft und sich selbst aktualisieren kann. + if (u && u.installiert) { + return ` + Version ${esc(u.version)} installiert. Home Assistant neu starten, damit die neue + Fassung geladen wird - Einstellungen -> System -> Neu starten. + `; + } + if (u && u.verfuegbar) { + return `
Verfügbare Version
${esc(u.version)}
+ + Lädt die neueste Fassung von Gitea und ersetzt den Integrationsordner. Home Assistant + muss danach neu gestartet werden. Fahrzeugprofil, Fahrten, Tankvorgänge und eigene + Fotos bleiben dabei unangetastet - die liegen außerhalb des Integrationsordners. + `; + } + return ` + ${u ? `Integration ist aktuell${u.geprueft_am ? ` (geprüft ${dedat(new Date(u.geprueft_am))})` : ""}.` : "Noch nicht geprüft."} + Ein Gitea-Zugriffstoken muss zuvor unter Einstellungen -> Geräte & Dienste -> Audi + Dashboard -> Konfigurieren eingetragen sein. + `; + })()} + ${INTEGRATION_UPDATE && INTEGRATION_UPDATE.fehler + ? `${esc(INTEGRATION_UPDATE.fehler)}` + : ""}
`; } @@ -3022,18 +3099,25 @@ let entwurfIndex = null; const SERVICE_ARTEN = ["Ölwechsel", "Inspektion", "Hauptuntersuchung", "Weitere"]; /* Welche Fassung der Integration gerade installiert ist (Einstellungen -> - Version). Kommt aus sensor.audi_dashboard_app_version, also aus der - manifest.json der Integration - der Zahl, die auch HACS anzeigt. + Version). Kommt aus sensor.audi_dashboard_app_version, also direkt aus der + manifest.json der Integration. Hier stand bis 2026-08-23 eine eigene Update-Funktion: "Update suchen" klonte ein Git-Repository in einen Zwischenordner und kopierte pyscript/ und www/ von dort über den laufenden Code. Das war nötig, solange die App - keine Installationsform hatte, die Home Assistant selbst kennt. Als - HACS-Integration gibt es die: HACS prüft, lädt, installiert und kann - zurückrollen, mit Versionsanzeige und Änderungsprotokoll. Eine zweite, - selbstgebaute Update-Mechanik daneben wäre mehr Angriffsfläche als - Nutzen - der eigene Updater ist deshalb ersatzlos entfallen. */ + keine Installationsform hatte, die Home Assistant selbst kennt - jetzt + gibt es eine: install.ps1 kopiert genau einen Ordner, das Panel darin + inklusive, und ist damit der einzige Installations- wie Update-Weg (HACS + scheidet für dieses private Repository dauerhaft aus, siehe AGENTS.md + Abschnitt H). Eine zweite, selbstgebaute Update-Mechanik daneben wäre + mehr Angriffsfläche als Nutzen - der eigene Updater ist deshalb + ersatzlos entfallen. */ let APP_VERSION = null; +// Ergebnis der letzten "Auf Update prüfen"/"Update installieren" +// (custom_components/audi_dashboard/aktualisierung.py) - null, solange nie +// geprüft wurde. Ersetzt install.ps1 als Update-Weg, das Windows Smart App +// Control zuverlässig blockiert; siehe vVersion()/Einstellungen-Ansicht. +let INTEGRATION_UPDATE = null; /* Auswahl für "Modell" unter Einrichten - setzt nur CONFIG.fahrzeugtitel (Zeile "Modell" in der Identität, Kopfzeilen, Kalender-Einträge, und den @@ -4392,6 +4476,9 @@ function ereignisseVerdrahten() { a.download = `backup_${new Date().toISOString().slice(0, 10)}.json`; document.body.appendChild(a); a.click(); a.remove(); return; } + const upd = e.target.closest("[data-update]"); + if (upd && upd.dataset.update === "pruefen") { serviceRufen("update_pruefen", {}); return; } + if (upd && upd.dataset.update === "installieren") { serviceRufen("update_installieren", {}); return; } const cs = e.target.closest("[data-csv]"); if (cs) { const w = cs.dataset.csv; @@ -4755,6 +4842,11 @@ async function datenLaden(erzwingen) { const entitaetenState = HASS.states[E.entitaeten] || NACHGELADEN.entitaeten; const versionState = HASS.states[E.appVersion]; if (versionState && versionState.state) APP_VERSION = versionState.state; + if (versionState && versionState.attributes) { + INTEGRATION_UPDATE = versionState.attributes.daten + ? versionState.attributes.daten.integration_update ?? null + : null; + } if (entitaetenState && entitaetenState.attributes) ENTITAETEN = entitaetenState.attributes.daten; if (belegState && belegState !== letzterBelegState) { diff --git a/custom_components/audi_dashboard/koordinator.py b/custom_components/audi_dashboard/koordinator.py index ac6ac2a..f38bee6 100644 --- a/custom_components/audi_dashboard/koordinator.py +++ b/custom_components/audi_dashboard/koordinator.py @@ -99,6 +99,13 @@ class Koordinator: self._store = Store[dict](hass, STORE_VERSION, f"{entry.entry_id}_laufzeit") self.fahrt_start_ts: datetime.datetime | None = None self.tiefststand_pct: float | None = None + # Ergebnis des letzten "Auf Update prüfen"/"Update installieren" - + # nur auf Tastendruck gesetzt (siehe dienste.py), nie im normalen + # Veröffentlichungs-Takt neu berechnet, damit keine automatischen + # Gitea-Abfragen im Hintergrund laufen. app_version_veroeffentlichen() + # liest diesen Wert bei jedem Takt erneut mit, damit er nicht + # zwischen zwei Prüfungen verschwindet. + self.letzte_update_pruefung: dict | None = None self._takte: list[CALLBACK_TYPE] = [] self._beobachter: list[CALLBACK_TYPE] = [] @@ -329,13 +336,25 @@ class Koordinator: return None return {**info, "url": BUENDEL_URL} - async def alles_veroeffentlichen(self) -> None: + async def app_version_veroeffentlichen(self) -> None: buendel = await self.hass.async_add_executor_job(self._buendel_lesen) - # Die App liest beides aus derselben Entität: welchen Stand diese - # Installation ausliefert, und ob ein nachladbares Bündel danebenliegt. - # Zusammen an einer Stelle, damit die App nicht zwei Quellen abgleichen - # muss, die auseinanderlaufen können. - self.setzen(E_APP_VERSION, self.version, {"app": self.version, "buendel": buendel}) + # Die App liest alle drei aus derselben Entität: welchen Stand diese + # Installation ausliefert, ob ein nachladbares OTA-Bündel danebenliegt, + # und ob eine neuere Fassung bei Gitea bereitliegt. Zusammen an einer + # Stelle, damit die App nicht mehrere Quellen abgleichen muss, die + # auseinanderlaufen können. + self.setzen( + E_APP_VERSION, + self.version, + { + "app": self.version, + "buendel": buendel, + "integration_update": self.letzte_update_pruefung, + }, + ) + + async def alles_veroeffentlichen(self) -> None: + await self.app_version_veroeffentlichen() await self.profil_veroeffentlichen() await self.fahrten_veroeffentlichen() await self.tankvorgaenge_veroeffentlichen() diff --git a/custom_components/audi_dashboard/manifest.json b/custom_components/audi_dashboard/manifest.json index 215dd8a..8a90256 100644 --- a/custom_components/audi_dashboard/manifest.json +++ b/custom_components/audi_dashboard/manifest.json @@ -1,7 +1,7 @@ { "domain": "audi_dashboard", "name": "Audi Dashboard", - "version": "2026.8.24.1", + "version": "2026.8.24.4", "documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md", "issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues", "codeowners": ["@paul"], diff --git a/custom_components/audi_dashboard/services.yaml b/custom_components/audi_dashboard/services.yaml index 11a8f89..3a8abd0 100644 --- a/custom_components/audi_dashboard/services.yaml +++ b/custom_components/audi_dashboard/services.yaml @@ -10,6 +10,8 @@ jetzt_aktualisieren: screening_jetzt: backup_jetzt: +update_pruefen: +update_installieren: profil_schreiben: fields: diff --git a/custom_components/audi_dashboard/strings.json b/custom_components/audi_dashboard/strings.json index 8b17676..4506780 100644 --- a/custom_components/audi_dashboard/strings.json +++ b/custom_components/audi_dashboard/strings.json @@ -10,6 +10,17 @@ "single_instance_allowed": "Audi Dashboard ist bereits eingerichtet." } }, + "options": { + "step": { + "init": { + "title": "Selbst-Update", + "description": "Zugriffstoken für gitea.nothaft.cloud, damit die Integration ihre eigene neueste Fassung laden und sich selbst ersetzen kann - ersetzt install.ps1, das Windows Smart App Control zuverlässig blockiert. Ein Token mit reinem Lesezugriff auf das Repository genügt. Leer lassen deaktiviert die Update-Prüfung.", + "data": { + "gitea_token": "Gitea-Zugriffstoken" + } + } + } + }, "services": { "jetzt_aktualisieren": { "name": "Jetzt aktualisieren", @@ -169,6 +180,14 @@ "start": { "name": "Von", "description": "Beginn des Zeitraums (ISO, Ortszeit erlaubt)." }, "ende": { "name": "Bis", "description": "Ende des Zeitraums (ISO, Ortszeit erlaubt)." } } + }, + "update_pruefen": { + "name": "Auf Update prüfen", + "description": "Fragt bei Gitea die neueste manifest.json ab und vergleicht die Version - lädt dabei noch nichts herunter." + }, + "update_installieren": { + "name": "Update installieren", + "description": "Lädt die neueste Fassung von Gitea und ersetzt den Integrationsordner. Home Assistant muss danach neu gestartet werden." } } } diff --git a/custom_components/audi_dashboard/translations/de.json b/custom_components/audi_dashboard/translations/de.json index 8b17676..4506780 100644 --- a/custom_components/audi_dashboard/translations/de.json +++ b/custom_components/audi_dashboard/translations/de.json @@ -10,6 +10,17 @@ "single_instance_allowed": "Audi Dashboard ist bereits eingerichtet." } }, + "options": { + "step": { + "init": { + "title": "Selbst-Update", + "description": "Zugriffstoken für gitea.nothaft.cloud, damit die Integration ihre eigene neueste Fassung laden und sich selbst ersetzen kann - ersetzt install.ps1, das Windows Smart App Control zuverlässig blockiert. Ein Token mit reinem Lesezugriff auf das Repository genügt. Leer lassen deaktiviert die Update-Prüfung.", + "data": { + "gitea_token": "Gitea-Zugriffstoken" + } + } + } + }, "services": { "jetzt_aktualisieren": { "name": "Jetzt aktualisieren", @@ -169,6 +180,14 @@ "start": { "name": "Von", "description": "Beginn des Zeitraums (ISO, Ortszeit erlaubt)." }, "ende": { "name": "Bis", "description": "Ende des Zeitraums (ISO, Ortszeit erlaubt)." } } + }, + "update_pruefen": { + "name": "Auf Update prüfen", + "description": "Fragt bei Gitea die neueste manifest.json ab und vergleicht die Version - lädt dabei noch nichts herunter." + }, + "update_installieren": { + "name": "Update installieren", + "description": "Lädt die neueste Fassung von Gitea und ersetzt den Integrationsordner. Home Assistant muss danach neu gestartet werden." } } } diff --git a/tests/aktualisierung/test_aktualisierung.py b/tests/aktualisierung/test_aktualisierung.py new file mode 100644 index 0000000..d0d7fb4 --- /dev/null +++ b/tests/aktualisierung/test_aktualisierung.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +"""Regressionstest für aktualisierung.py's entpacken_pruefen_tauschen(). + +Testet die Sicherheitslogik des Selbst-Updates komplett isoliert: kein +Gitea-Zugriff, kein Home Assistant, nur ein selbstgebautes Zip gegen einen +Temp-Ordner. Deckt genau die Fälle ab, die vor jedem Tausch abgefangen +werden müssen (siehe Moduldocstring in aktualisierung.py): + - erfolgreicher Tausch inklusive Backup der alten Fassung + - falsche Domain im heruntergeladenen Manifest + - fehlendes version-Feld + - fehlende manifest.json überhaupt + - kaputtes Zip + - der gesuchte Pfad fehlt im Archiv + +Aufruf: python3 tests/aktualisierung/test_aktualisierung.py +(braucht das homeassistant-Paket, weil aktualisierung.py es importiert - +in der Integration selbst immer vorhanden, hier z. B. im Test-Container) +""" + +import io +import os +import shutil +import sys +import tempfile +import unittest +import zipfile + +_HIER = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert( + 0, os.path.join(os.path.dirname(os.path.dirname(_HIER)), "custom_components", "audi_dashboard") +) + +import aktualisierung as a # noqa: E402 + + +def _zip_bauen(dateien: dict[str, bytes], wurzel: str = "audi-app-main") -> bytes: + """Baut ein Zip mit den gegebenen Dateien unter + /custom_components/audi_dashboard/ - genau die Form, in + der Gitea ein Repo-Archiv liefert.""" + puffer = io.BytesIO() + with zipfile.ZipFile(puffer, "w") as archiv: + for pfad, inhalt in dateien.items(): + archiv.writestr(f"{wurzel}/custom_components/audi_dashboard/{pfad}", inhalt) + return puffer.getvalue() + + +_GUELTIGES_MANIFEST = b'{"domain": "audi_dashboard", "version": "2099.1.1.1"}' + + +class ErfolgreicherTausch(unittest.TestCase): + def setUp(self): + self.arbeitsordner = tempfile.mkdtemp() + self.integration = os.path.join(self.arbeitsordner, "audi_dashboard") + self.staging = os.path.join(self.arbeitsordner, "audi_dashboard_update_staging") + self.backup = os.path.join(self.arbeitsordner, "audi_dashboard_backup") + os.makedirs(self.integration) + with open(os.path.join(self.integration, "manifest.json"), "wb") as f: + f.write(b'{"domain": "audi_dashboard", "version": "2026.1.1.1"}') + with open(os.path.join(self.integration, "alte_datei.py"), "wb") as f: + f.write(b"# alte Fassung\n") + + def tearDown(self): + shutil.rmtree(self.arbeitsordner, ignore_errors=True) + + def test_tauscht_und_sichert_die_alte_fassung(self): + zip_bytes = _zip_bauen({ + "manifest.json": _GUELTIGES_MANIFEST, + "neue_datei.py": b"# neue Fassung\n", + "frontend/app.js": b"// panel\n", + }) + version = a.entpacken_pruefen_tauschen( + zip_bytes, self.integration, self.staging, self.backup + ) + self.assertEqual(version, "2099.1.1.1") + + # Live-Ordner trägt jetzt die neue Fassung. + with open(os.path.join(self.integration, "manifest.json"), "rb") as f: + self.assertIn(b'"2099.1.1.1"', f.read()) + self.assertTrue(os.path.exists(os.path.join(self.integration, "neue_datei.py"))) + self.assertTrue(os.path.exists(os.path.join(self.integration, "frontend", "app.js"))) + self.assertFalse(os.path.exists(os.path.join(self.integration, "alte_datei.py"))) + + # Alte Fassung bleibt vollständig und unverändert im Backup-Ordner. + with open(os.path.join(self.backup, "manifest.json"), "rb") as f: + self.assertIn(b'"2026.1.1.1"', f.read()) + self.assertTrue(os.path.exists(os.path.join(self.backup, "alte_datei.py"))) + + # Kein Staging-Rest übrig. + self.assertFalse(os.path.exists(self.staging)) + + def test_entfernt_pycache_aus_dem_staging_ordner(self): + zip_bytes = _zip_bauen({ + "manifest.json": _GUELTIGES_MANIFEST, + "__pycache__/alte.cpython-313.pyc": b"\x00\x00", + }) + a.entpacken_pruefen_tauschen(zip_bytes, self.integration, self.staging, self.backup) + self.assertFalse(os.path.exists(os.path.join(self.integration, "__pycache__"))) + + +class AbgelehnteFaelle(unittest.TestCase): + def setUp(self): + self.arbeitsordner = tempfile.mkdtemp() + self.integration = os.path.join(self.arbeitsordner, "audi_dashboard") + self.staging = os.path.join(self.arbeitsordner, "audi_dashboard_update_staging") + self.backup = os.path.join(self.arbeitsordner, "audi_dashboard_backup") + os.makedirs(self.integration) + with open(os.path.join(self.integration, "manifest.json"), "wb") as f: + f.write(b'{"domain": "audi_dashboard", "version": "2026.1.1.1"}') + + def tearDown(self): + shutil.rmtree(self.arbeitsordner, ignore_errors=True) + + def _unangetastet(self): + """Nach jedem abgelehnten Fall muss der Live-Ordner exakt so + dastehen wie vorher - keiner der Fehlerfälle darf vor dem Tausch + etwas am Live-Ordner verändern (siehe Moduldocstring).""" + with open(os.path.join(self.integration, "manifest.json"), "rb") as f: + self.assertIn(b'"2026.1.1.1"', f.read()) + self.assertFalse(os.path.exists(self.backup)) + + def test_lehnt_falsche_domain_ab(self): + zip_bytes = _zip_bauen({ + "manifest.json": b'{"domain": "hacs", "version": "2099.1.1.1"}', + }) + with self.assertRaises(a.AktualisierungsFehler): + a.entpacken_pruefen_tauschen(zip_bytes, self.integration, self.staging, self.backup) + self._unangetastet() + + def test_lehnt_fehlendes_version_feld_ab(self): + zip_bytes = _zip_bauen({ + "manifest.json": b'{"domain": "audi_dashboard"}', + }) + with self.assertRaises(a.AktualisierungsFehler): + a.entpacken_pruefen_tauschen(zip_bytes, self.integration, self.staging, self.backup) + self._unangetastet() + + def test_lehnt_fehlende_manifest_ab(self): + zip_bytes = _zip_bauen({"irgendeine_datei.py": b"# leer\n"}) + with self.assertRaises(a.AktualisierungsFehler): + a.entpacken_pruefen_tauschen(zip_bytes, self.integration, self.staging, self.backup) + self._unangetastet() + + def test_lehnt_kaputtes_zip_ab(self): + with self.assertRaises(a.AktualisierungsFehler): + a.entpacken_pruefen_tauschen( + b"das ist kein Zip", self.integration, self.staging, self.backup + ) + self._unangetastet() + + def test_lehnt_archiv_ohne_gesuchten_pfad_ab(self): + puffer = io.BytesIO() + with zipfile.ZipFile(puffer, "w") as archiv: + archiv.writestr("irgendein-anderes-repo-main/README.md", b"# anderes Projekt\n") + with self.assertRaises(a.AktualisierungsFehler): + a.entpacken_pruefen_tauschen( + puffer.getvalue(), self.integration, self.staging, self.backup + ) + self._unangetastet() + + +if __name__ == "__main__": + unittest.main()