diff --git a/AGENTS.md b/AGENTS.md index 369a861..2d14b57 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10477,3 +10477,100 @@ einer anderen Adresse steht, muss prozentkodiert werden - oder er gehoert dort n hin. Die frueheren, erfolgreichen Installationen liefen alle ueber die blanke Manifest-Adresse; der Zusatz kam am 04.09. dazu und ist seither nie durch eine gelungene Installation gegangen. + +## CL. Der Versionsvergleich kennt jetzt eine Richtung (2026.9.4.19) + +Gemeldet nach dem ersten Start der frisch installierten `.17`: „App laeuft auf +4.17, bereit liegt 4.5. Sie kann sich direkt hier erneuern." Die reale Instanz +stand noch auf `2026.9.4.5` - angeboten wurde also ein **Rueckschritt um zwoelf +Fassungen**, der die Oberflaeche still auf den Stand vom Vormittag zurueckgedreht +haette (ohne QR-Scanner, ohne Bestaetigungsblatt, ohne Debug-Bereich), waehrend +die native Huelle auf `.17` geblieben waere. Danach haetten App und Server +uebereingestimmt und die Meldung waere verschwunden - der Verlust also still. + +**Die alte Pruefung konnte das nicht sehen**, und zwar folgerichtig: sie +verglich nur auf Gleichheit. Das Buendel (`.5`) passte zur Serverfassung (`.5`) +und war ungleich der laufenden (`.17`) - beide Bedingungen erfuellt, Knopf da. +Dass `.5` **aelter** ist als `.17`, war ihr per Konstruktion unzugaenglich. + +**Vorgabe des Eigentuemers:** erkannt wird eine neuere Fassung und keine andere. + +### Eine dokumentierte Entscheidung wurde damit umgedreht + +`appVersion.ts` und `VERSIONIERUNG.md` sagten bis hierher ausdruecklich das +Gegenteil - „bewusst kein groesser/kleiner-Vergleich: die Version ist eine +Kennung, keine Zahl. Ein Sortierversuch waere scheingenau und wuerde bei einem +Formatwechsel still falsche Antworten geben." Beide Stellen sind umgeschrieben, +nicht ergaenzt (Regel dieses Dokuments: ueberholte Regeln entfernen, nicht +stapeln). + +**Der Einwand von damals ist eingebaut, nicht verworfen:** `versionOrdnung()` +sortiert ausschliesslich, was dem Format `JJJJ.M.T.N` entspricht - jede Stelle +muss eine reine Ziffernfolge sein, geprueft mit einem Muster und **nicht** mit +`parseInt`, das `4b` klaglos als 4 gelesen und damit genau die scheingenaue +Antwort erzeugt haette. Passt eine Seite nicht ins Schema, kommt `null` zurueck: +dann meldet die App eine Abweichung **ohne Richtung** und bietet nichts an. +Verglichen wird Stelle fuer Stelle als Zahl - zeichenweise stuende `.17` vor +`.5`, also genau der gemeldete Fall. + +### Was sich wo aendert + +| Lage | Streifen | Startblatt | OTA-Knopf | +|---|---|---|---| +| App aelter | „bitte aktualisieren" | ja | ja | +| **App neuer** | **still** | **nein** | **nein** | +| verschieden, nicht sortierbar | neutraler Hinweis | nein | nur bei Serveruebereinstimmung | +| gleich / nicht vergleichbar | still | nein | nein | + +`Versionsstand` heisst jetzt `gleich | aelter | neuer | abweichend | unbekannt`; +`abweichend` bedeutet nur noch „verschieden, aber nicht sortierbar". Der Streifen +meldet diesen Fall **neutral** statt zu schweigen - der Grundsatz „nie eine +stille Veraltung" gilt auch dort, nur ohne eine Richtung zu behaupten, die nicht +belegt ist. + +`buendelPasst()` behaelt seine bisherigen Bedingungen und bekommt eine dritte: +das Buendel muss **vorwaerts** gehen. Ist die Reihenfolge nicht bestimmbar, gilt +weiter die alte Regel (nur Serveruebereinstimmung) - eine erfundene Reihenfolge +waere schlechter als die Regel von vorher. + +### Kein Panel-Gegenstueck, und das ist geprueft + +Das Panel liest `APP_VERSION` an genau drei Stellen und **zeigt** sie nur an +(`vEinst()`, Zeile 3417). Es vergleicht nichts und kennt kein OTA-Buendel - es +laedt seine Dateien bei jedem Aufruf frisch und kann gar nicht veralten. Echte +Ausnahme von der Paritaetsregel, kein stillschweigend ausgelassener Port. + +### Verifiziert + +`tsc --noEmit` sauber, **244 Tests** (von 225; 6 neue fuer `versionOrdnung()`, +5 fuer den Rueckschritt in `buendelPasst()`, 2 fuer das Startblatt, dazu zwei +geaenderte Erwartungen in `appVersion.test.ts` - die Richtungsangabe ist eine +gewollte Verhaltensaenderung, kein Bruch). `vite build` sauber, Panel als Modul +geparst, `audi_ha_test` auf `2026.9.4.19` sauber gestartet, kein Traceback. + +**Gegen den alten Stand als fehlschlagend nachgewiesen:** mit ausgehaengter +Reihenfolgepruefung fallen drei der neuen Tests um, darunter der gemeldete Fall +wortwoertlich (`buendelPasst(.5, laeuft .17, Server .5)` liefert dann `true`). + +**Live in allen drei Lagen**, ohne den Container anzufassen - dafuer bekam nur +der Entwicklungsserver voruebergehend eine andere einkompilierte Nummer, +waehrend die Instanz durchgehend auf `.19` stand: + +| einkompiliert | Instanz | Streifen | Startblatt | OTA-Knopf | +|---|---|---|---|---| +| `.99` (App voraus) | `.19` | still | nein | **nicht vorhanden** | +| `.18` (App zurueck) | `.19` | „aelter als der Server" | - | - | +| `.19` (gleich) | `.19` | still | nein | nein | + +Das Startblatt erscheint im Browser grundsaetzlich nicht (`otaMoeglich()`, nur +nativ) - seine Entscheidung ist deshalb ueber `hinweisFaellig()` geprueft, nicht +optisch. Das Manifest steht danach wieder auf `2026.9.4.19`, gleichauf mit dem +Buendel (sha256 `58969a38…`). + +### Was der Eigentuemer davon merkt + +Die Meldung auf dem Telefon verschwindet nicht durch dieses Update - die App +laeuft ja auf `.17` und bekommt `.19` erst, wenn die Instanz sie ausliefert. Der +Weg bleibt: **erst die Integration auf der realen Instanz aktualisieren** +(Einstellungen -> Version -> pruefen, installieren, neu starten), danach bietet +die App den Schritt `.17` -> `.19` an, und der geht vorwaerts. diff --git a/VERSIONIERUNG.md b/VERSIONIERUNG.md index f6d5dc7..06dd9c4 100644 --- a/VERSIONIERUNG.md +++ b/VERSIONIERUNG.md @@ -74,11 +74,30 @@ 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. +**Verglichen wird der Reihe nach, nicht nur auf Gleichheit — geändert am +04.09.2026.** Bis dahin galt hier ausdrücklich das Gegenteil (die Version sei +eine Kennung und keine Zahl). Der Anlass war ein echter Fall: nach einem +Xcode-Lauf lief die App auf `2026.9.4.17`, die Instanz stand noch auf +`2026.9.4.5` — und die App bot an, sich auf `.5` zu erneuern. Ein Rückschritt um +zwölf Fassungen, den eine Gleichheitsprüfung gar nicht sehen kann. Vorgabe des +Eigentümers: **erkannt wird eine neuere Fassung und keine andere.** + +Der damalige Einwand ist nicht verworfen, sondern eingebaut: sortiert wird nur, +was dem Format `JJJJ.M.T.N` entspricht, und zwar Stelle für Stelle als Zahl — +zeichenweise stünde `.17` vor `.5`. Passt eine Seite nicht ins Schema, gilt sie +als **nicht sortierbar**; dann meldet die App eine Abweichung ohne Richtung und +bietet nichts an, statt eine Reihenfolge zu erfinden. Fehlt eine der beiden +Seiten ganz, wird gar nicht verglichen und nichts gemeldet — ein älteres Backend +oder ein Start ohne Netz darf keinen Fehlalarm auslösen. + +Daraus folgen vier Verhaltensweisen, entschieden in `src/daten/appVersion.ts`: + +| Lage | Streifen | Startblatt | Update-Knopf | +|---|---|---|---| +| App älter als der Server | bitte aktualisieren | ja | ja | +| App neuer (frisch signiert) | still | nein | **nein** | +| verschieden, nicht sortierbar | neutraler Hinweis | nein | nur bei Serverübereinstimmung | +| gleich / nicht vergleichbar | still | nein | nein | ## Was du tun musst diff --git a/companion-app/src/daten/appVersion.test.ts b/companion-app/src/daten/appVersion.test.ts index 9b58085..0834cad 100644 --- a/companion-app/src/daten/appVersion.test.ts +++ b/companion-app/src/daten/appVersion.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest" -import { versionVergleichen } from "./appVersion" +import { versionOrdnung, versionVergleichen } from "./appVersion" /* Der Vergleich entscheidet, ob die App dem Nutzer sagt „ich bin älter als der Server". Beide Fehlrichtungen sind teuer: ein Fehlalarm schickt ihn grundlos @@ -12,9 +12,15 @@ describe("versionVergleichen", () => { expect(v.stand).toBe("gleich") }) - it("meldet eine Abweichung, sobald die Zeichenketten sich unterscheiden", () => { - expect(versionVergleichen("2026.08.23.1", "2026.08.24.1").stand).toBe("abweichend") - expect(versionVergleichen("2026.08.23.2", "2026.08.23.1").stand).toBe("abweichend") + it("nennt die Richtung, statt nur eine Abweichung zu melden", () => { + expect(versionVergleichen("2026.08.23.1", "2026.08.24.1").stand).toBe("aelter") + expect(versionVergleichen("2026.08.23.2", "2026.08.23.1").stand).toBe("neuer") + }) + + it("meldet ohne erkennbares Format eine Abweichung ohne Richtung", () => { + // Der Einwand, der bis zum 04.09.2026 GEGEN jede Sortierung sprach: bei + // einem Formatwechsel darf nichts erfunden werden. + expect(versionVergleichen("2026.8.23.1", "fruehling-2").stand).toBe("abweichend") }) it("vergleicht nicht, wenn das Backend nichts meldet", () => { @@ -38,3 +44,47 @@ describe("versionVergleichen", () => { expect(v.server).toBe("2026.08.24.1") }) }) + +/* versionOrdnung() ist die Stelle, an der aus zwei Kennungen ein Vorher und + ein Nachher wird. Sie entscheidet, ob der Update-Knopf erscheint - und ein + Vorzeichenfehler hier böte einen Rückschritt an, wie am 04.09.2026 real + geschehen (App .17, Instanz .5). */ +describe("versionOrdnung", () => { + it("erkennt die letzte Stelle, auch zweistellig", () => { + // Genau der gemeldete Fall: 5 gegen 17. Zeichenweise verglichen stuende + // "17" vor "5" - deshalb wird je Stelle als Zahl verglichen. + expect(versionOrdnung("2026.9.4.5", "2026.9.4.17")).toBe(-1) + expect(versionOrdnung("2026.9.4.17", "2026.9.4.5")).toBe(1) + }) + + it("vergleicht Stelle für Stelle von links", () => { + expect(versionOrdnung("2026.8.31.9", "2026.9.1.1")).toBe(-1) + expect(versionOrdnung("2026.9.1.1", "2025.12.31.9")).toBe(1) + }) + + it("behandelt fehlende Stellen als Null", () => { + expect(versionOrdnung("2026.9.4", "2026.9.4.0")).toBe(0) + expect(versionOrdnung("2026.9.4", "2026.9.4.1")).toBe(-1) + }) + + it("stoert sich nicht an fuehrenden Nullen", () => { + expect(versionOrdnung("2026.08.23.1", "2026.8.23.1")).toBe(0) + }) + + it("sortiert nicht, was nicht ins Format passt", () => { + // parseInt haette "4b" klaglos als 4 gelesen - genau die scheingenaue + // Antwort, die hier nicht entstehen darf. + expect(versionOrdnung("2026.9.4b", "2026.9.5")).toBeNull() + expect(versionOrdnung("v2026.9.4", "2026.9.5")).toBeNull() + expect(versionOrdnung("2026..4", "2026.9.4")).toBeNull() + }) + + it("nennt zeichengleiche Kennungen gleich, auch ohne lesbares Format", () => { + expect(versionOrdnung("fruehling-2", "fruehling-2")).toBe(0) + }) + + it("entscheidet nichts, wenn eine Seite fehlt", () => { + expect(versionOrdnung(null, "2026.9.4.1")).toBeNull() + expect(versionOrdnung("2026.9.4.1", "")).toBeNull() + }) +}) diff --git a/companion-app/src/daten/appVersion.ts b/companion-app/src/daten/appVersion.ts index 8f05006..f22dcdc 100644 --- a/companion-app/src/daten/appVersion.ts +++ b/companion-app/src/daten/appVersion.ts @@ -7,17 +7,30 @@ * gebündelt und bleibt auf dem Stand vom Signieren — unbegrenzt und bisher * unbemerkt. Diese Datei macht daraus ein sichtbares Signal. * - * Bewusst kein „größer/kleiner"-Vergleich: die Version ist eine Kennung - * (`2026.8.23.2`), keine Zahl. Ein Sortierversuch wäre nur scheingenau und - * würde bei einem Formatwechsel still falsche Antworten geben. Verglichen wird - * auf Gleichheit — alles andere heißt „stimmt nicht überein", und was davon - * älter ist, entscheidet der Mensch. + * ENTSCHEIDUNG GEÄNDERT AM 04.09.2026 — vorher stand hier ausdrücklich + * „bewusst kein größer/kleiner-Vergleich: die Version ist eine Kennung, keine + * Zahl". Der Anlass war ein echter Fall: nach einem Xcode-Lauf lief die App + * auf `2026.9.4.17`, die Instanz stand noch auf `2026.9.4.5` — und die App bot + * an, sich auf `.5` zu „erneuern". Ein Rückschritt, den die Gleichheitsprüfung + * gar nicht sehen konnte. Vorgabe des Eigentümers: erkannt werden soll eine + * NEUERE Fassung und keine andere. + * + * Der damalige Einwand bleibt gültig und ist deshalb eingebaut, statt + * verworfen: sortiert wird nur, was dem Format `JJJJ.M.T.N` entspricht. Passt + * eine der beiden Zeichenketten nicht ins Schema (Formatwechsel), liefert der + * Vergleich „abweichend" statt einer erfundenen Reihenfolge — dann sagt die + * App, dass etwas nicht übereinstimmt, aber nicht, in welche Richtung. */ export type Versionsstand = /** App und Backend melden denselben Stand. */ | "gleich" - /** Sie weichen ab — die App ist mit hoher Wahrscheinlichkeit älter. */ + /** Die App ist nachweislich älter — nur hier gibt es etwas zu holen. */ + | "aelter" + /** Die App ist nachweislich neuer, etwa direkt nach einem Xcode-Lauf, + solange die Instanz noch nicht nachgezogen hat. Kein Handlungsbedarf. */ + | "neuer" + /** Verschieden, aber nicht in eine Reihenfolge zu bringen (Formatwechsel). */ | "abweichend" /** Kein Vergleich möglich (Backend meldet nichts, offline, altes Backend). */ | "unbekannt" @@ -36,10 +49,51 @@ export function eigeneVersion(): string | null { return typeof __APP_VERSION__ === "string" && __APP_VERSION__ ? __APP_VERSION__ : null } +/** Zerlegt `2026.9.4.17` in [2026, 9, 4, 17]. `null`, sobald ein Teil keine + reine Ziffernfolge ist — dann wird nicht sortiert (siehe Modulkopf). */ +function teile(version: string): number[] | null { + const stuecke = version.split(".") + if (stuecke.length === 0) return null + const zahlen: number[] = [] + for (const stueck of stuecke) { + // Bewusst kein parseInt: das würde "4b" klaglos als 4 lesen und damit + // genau die scheingenaue Sortierung erzeugen, vor der der Modulkopf warnt. + if (!/^[0-9]+$/.test(stueck)) return null + zahlen.push(Number(stueck)) + } + return zahlen +} + +/** + * `-1` wenn `a` älter ist als `b`, `0` bei Gleichstand, `+1` wenn `a` neuer + * ist — und `null`, wenn sich das nicht entscheiden lässt. + * + * Fehlende Stellen zählen als 0, damit `2026.9.4` und `2026.9.4.0` denselben + * Stand bezeichnen. + */ +export function versionOrdnung(a: string | null, b: string | null): number | null { + if (!a || !b) return null + // Zeichengleich ist gleich, auch wenn das Format unbekannt ist. + if (a === b) return 0 + const links = teile(a) + const rechts = teile(b) + if (!links || !rechts) return null + const laenge = Math.max(links.length, rechts.length) + for (let i = 0; i < laenge; i++) { + const l = links[i] ?? 0 + const r = rechts[i] ?? 0 + if (l !== r) return l < r ? -1 : 1 + } + return 0 +} + export function versionVergleichen( app: string | null, server: string | null, ): Versionsvergleich { if (!app || !server) return { stand: "unbekannt", app, server } - return { stand: app === server ? "gleich" : "abweichend", app, server } + const ordnung = versionOrdnung(app, server) + if (ordnung === null) return { stand: "abweichend", app, server } + if (ordnung === 0) return { stand: "gleich", app, server } + return { stand: ordnung < 0 ? "aelter" : "neuer", app, server } } diff --git a/companion-app/src/daten/ota.test.ts b/companion-app/src/daten/ota.test.ts index d819fcc..cfaa88a 100644 --- a/companion-app/src/daten/ota.test.ts +++ b/companion-app/src/daten/ota.test.ts @@ -63,3 +63,42 @@ describe("buendelPasst", () => { expect(buendelPasst(buendel({ version: "2026.9.3.11" }), "2026.9.3.10", null)).toBe(true) }) }) + +/* Der gemeldete Fall vom 04.09.2026: nach einem Xcode-Lauf lief die App auf + 2026.9.4.17, die Instanz stand noch auf 2026.9.4.5 - und bot an, sich auf + .5 zu "erneuern". Das Bündel passte zur Serverfassung, war also nach der + damaligen Prüfung in Ordnung; dass es ein Rückschritt ist, konnte sie nicht + sehen. Vorgabe des Eigentümers: erkannt wird eine NEUERE Fassung, sonst + keine. */ +describe("buendelPasst geht nur vorwaerts", () => { + it("lehnt den gemeldeten Rueckschritt ab, obwohl das Buendel zum Server passt", () => { + expect( + buendelPasst(buendel({ version: "2026.9.4.5" }), "2026.9.4.17", "2026.9.4.5"), + ).toBe(false) + }) + + it("nimmt dieselbe Lage nach dem Nachziehen der Instanz an", () => { + // Sobald die Integration auf .18 steht, geht es wieder vorwaerts. + expect( + buendelPasst(buendel({ version: "2026.9.4.18" }), "2026.9.4.17", "2026.9.4.18"), + ).toBe(true) + }) + + it("lehnt einen Rueckschritt auch ohne bekannte Serverfassung ab", () => { + expect(buendelPasst(buendel({ version: "2026.9.4.5" }), "2026.9.4.17", null)).toBe(false) + }) + + it("vergleicht die letzte Stelle als Zahl, nicht zeichenweise", () => { + // "9" gegen "10": zeichenweise stuende "10" davor. + expect(buendelPasst(buendel({ version: "2026.9.4.10" }), "2026.9.4.9", null)).toBe(true) + expect(buendelPasst(buendel({ version: "2026.9.4.9" }), "2026.9.4.10", null)).toBe(false) + }) + + it("bleibt beim alten Verhalten, wenn sich die Reihenfolge nicht bestimmen laesst", () => { + // Formatwechsel: dann entscheidet weiterhin allein die Serverfassung - + // eine Reihenfolge zu erfinden waere schlechter als die alte Regel. + expect(buendelPasst(buendel({ version: "fruehling-3" }), "2026.9.4.17", "fruehling-3")).toBe( + true, + ) + }) +}) diff --git a/companion-app/src/daten/ota.ts b/companion-app/src/daten/ota.ts index 39bba7a..f80a848 100644 --- a/companion-app/src/daten/ota.ts +++ b/companion-app/src/daten/ota.ts @@ -39,6 +39,7 @@ import { Capacitor } from "@capacitor/core" import { CapacitorUpdater } from "@capgo/capacitor-updater" import type { Buendelangabe } from "../api" +import { versionOrdnung } from "./appVersion" /** Läuft die App in der nativen Hülle? Nur dort gibt es etwas auszutauschen — im Browser und im HA-Panel lädt ohnehin jeder Seitenaufruf den aktuellen @@ -83,12 +84,16 @@ export function buendelPasst( // Es muss etwas ANDERES sein als das, was gerade läuft - sonst gibt es // nichts zu holen. if (buendel.version === eigene) return false - // Und es muss die Fassung sein, die diese Installation ausliefert. Der - // Kommentar hier versprach das schon ("läge dort ein älteres, wäre ein - // Update ein Rückschritt"), geprüft wurde es nie: ein blosses !== laesst - // ein aelteres Buendel genauso durch wie ein neueres. Ohne bekannte - // Serverfassung bleibt es beim alten Verhalten - ein Vergleich, den man - // nicht anstellen kann, darf den Knopf nicht wegnehmen. + // Und es muss VORWAERTS gehen. Am 04.09.2026 lief die App nach einem + // Xcode-Lauf auf 2026.9.4.17, während die Instanz noch 2026.9.4.5 + // auslieferte - angeboten wurde ein Rückschritt auf .5, der die + // Oberfläche still um zwölf Fassungen zurückgedreht hätte. Die + // Gleichheitsprüfung konnte das nicht sehen; siehe appVersion.ts. + const ordnung = versionOrdnung(buendel.version, eigene) + if (ordnung !== null && ordnung <= 0) return false + // Und es muss die Fassung sein, die diese Installation ausliefert. Ohne + // bekannte Serverfassung bleibt es beim alten Verhalten - ein Vergleich, + // den man nicht anstellen kann, darf den Knopf nicht wegnehmen. if (serverVersion == null) return true return buendel.version === serverVersion } diff --git a/companion-app/src/screens/Hinweisleiste.tsx b/companion-app/src/screens/Hinweisleiste.tsx index c81268d..f92f5c7 100644 --- a/companion-app/src/screens/Hinweisleiste.tsx +++ b/companion-app/src/screens/Hinweisleiste.tsx @@ -26,12 +26,21 @@ export function Hinweisleiste({ // Derselbe Grundsatz wie beim Offline-Hinweis oben: keine stille Veraltung. // Nur hier veraltet nicht die Anzeige, sondern die App selbst — sie trägt // ihre Dateien fest gebündelt und merkt eine neuere Fassung sonst nie. - const veraltet = versionsstand === "abweichend" + // + // "neuer" schlägt bewusst NICHT an: direkt nach einem Xcode-Lauf ist die + // App der Instanz voraus, und daraus einen Handlungsbedarf zu machen + // hiesse, den Nutzer zu einem Rückschritt zu drängen (siehe + // daten/appVersion.ts). Nur bei einer nicht sortierbaren Abweichung wird + // neutral gemeldet, statt eine Richtung zu behaupten - der Grundsatz + // 'keine stille Veraltung' gilt auch dann. + const veraltet = versionsstand === "aelter" + const unklar = versionsstand === "abweichend" + const versionshinweis = veraltet || unklar // "verbindet" gleich beim Start nicht als Offline melden — das würde bei // jedem App-Start kurz aufblitzen. - if (!offline && wartend === 0 && !veraltet) return null - if (verbindung === "verbindet" && wartend === 0 && !veraltet) return null + if (!offline && wartend === 0 && !versionshinweis) return null + if (verbindung === "verbindet" && wartend === 0 && !versionshinweis) return null const gescheitert = warteschlange.filter((a) => a.fehlversuche > 0).length @@ -51,8 +60,9 @@ export function Hinweisleiste({ : `${wartend} Änderungen warten auf Übertragung.` : null} {gescheitert > 0 ? ` ${gescheitert} davon mit Fehlversuchen.` : null} - {veraltet && (offline || wartend > 0) ? " " : null} + {versionshinweis && (offline || wartend > 0) ? " " : null} {veraltet ? "Diese App ist älter als der Server — bitte aktualisieren." : null} + {unklar ? "App und Server melden verschiedene Fassungen." : null} ) diff --git a/companion-app/src/screens/Versionshinweis.test.ts b/companion-app/src/screens/Versionshinweis.test.ts index 454b3eb..e0b8f44 100644 --- a/companion-app/src/screens/Versionshinweis.test.ts +++ b/companion-app/src/screens/Versionshinweis.test.ts @@ -8,7 +8,7 @@ import { hinweisFaellig } from "./Versionshinweis" nie jemand erfährt. */ describe("hinweisFaellig", () => { it("meldet, wenn App und Server auseinanderlaufen", () => { - expect(hinweisFaellig("abweichend", "2026.9.3.3", null)).toBe(true) + expect(hinweisFaellig("aelter", "2026.9.3.3", null)).toBe(true) }) it("schweigt bei gleichem Stand", () => { @@ -23,16 +23,31 @@ describe("hinweisFaellig", () => { }) it("schweigt ohne Serverfassung, auch wenn abweichend gemeldet wird", () => { - expect(hinweisFaellig("abweichend", null, null)).toBe(false) + expect(hinweisFaellig("aelter", null, null)).toBe(false) }) it("schweigt zur bereits weggetippten Fassung", () => { - expect(hinweisFaellig("abweichend", "2026.9.3.3", "2026.9.3.3")).toBe(false) + expect(hinweisFaellig("aelter", "2026.9.3.3", "2026.9.3.3")).toBe(false) }) it("meldet erneut, sobald eine andere Fassung bereitliegt", () => { // Der Kern der Merkfunktion: weggetippt gilt für genau eine Fassung, // nicht für "Updates" im Allgemeinen. - expect(hinweisFaellig("abweichend", "2026.9.4.1", "2026.9.3.3")).toBe(true) + expect(hinweisFaellig("aelter", "2026.9.4.1", "2026.9.3.3")).toBe(true) + }) +}) + +/* Nachgetragen am 04.09.2026: die App lief nach einem Xcode-Lauf auf + 2026.9.4.17, die Instanz stand auf 2026.9.4.5 - und das Blatt bot an, sich + auf .5 zu "erneuern". Seither meldet nur noch "aelter". */ +describe("hinweisFaellig, wenn die App der Instanz voraus ist", () => { + it("schweigt, wenn die App neuer ist als der Server", () => { + expect(hinweisFaellig("neuer", "2026.9.4.5", null)).toBe(false) + }) + + it("schweigt bei einer Abweichung ohne erkennbare Richtung", () => { + // Formatwechsel: der Streifen meldet das neutral, ein eigenes Blatt waere + // dafuer zu viel - und "bitte aktualisieren" waere nicht belegbar. + expect(hinweisFaellig("abweichend", "gestern-3", null)).toBe(false) }) }) diff --git a/companion-app/src/screens/Versionshinweis.tsx b/companion-app/src/screens/Versionshinweis.tsx index 38102ba..7ea8243 100644 --- a/companion-app/src/screens/Versionshinweis.tsx +++ b/companion-app/src/screens/Versionshinweis.tsx @@ -56,7 +56,13 @@ export function hinweisFaellig( ): boolean { // "unbekannt" heißt: kein Vergleich möglich (offline, altes Backend). Das // ist kein Grund für eine Meldung — genauso wenig wie "gleich". - if (versionsstand !== "abweichend") return false + // + // Seit dem 04.09.2026 zählt nur noch "aelter". "neuer" tritt nach jedem + // Xcode-Lauf auf, solange die Instanz noch nicht nachgezogen hat - dort + // gibt es nichts zu holen, und die Meldung hätte zu einem Rückschritt + // eingeladen. "abweichend" (nicht sortierbar) meldet der Streifen neutral, + // ein eigenes Blatt wäre dafür zu viel. + if (versionsstand !== "aelter") return false if (!serverVersion) return false return serverVersion !== gesehen } diff --git a/custom_components/audi_dashboard/frontend/app/bundle.json b/custom_components/audi_dashboard/frontend/app/bundle.json index 5669de6..c914aa0 100644 --- a/custom_components/audi_dashboard/frontend/app/bundle.json +++ b/custom_components/audi_dashboard/frontend/app/bundle.json @@ -1 +1 @@ -{"version":"2026.9.4.18","sha256":"d8b50e70f23ee796ac1618a94ee49893c202bed6139863e840fa76456b2694ca","bytes":322373,"gebaut":"2026-09-04T12:14:55Z"} \ No newline at end of file +{"version":"2026.9.4.19","sha256":"58969a387bfa72c3d452c04b689e8ec01440c9a2a89b7a097d43fe0418092ae6","bytes":322568,"gebaut":"2026-09-04T14:06:53Z"} \ 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 c8f4582..0961d06 100644 Binary files a/custom_components/audi_dashboard/frontend/app/bundle.zip and b/custom_components/audi_dashboard/frontend/app/bundle.zip differ diff --git a/custom_components/audi_dashboard/manifest.json b/custom_components/audi_dashboard/manifest.json index 3ac28fd..a836a70 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.9.4.18", + "version": "2026.9.4.19", "documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md", "issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues", "codeowners": [