Kein Rueckschritt mehr als Update, Version-Kachel schlanker

aktualisierung.py pruefte mit `remote_version != eigene_version` - reine
Ungleichheit ohne Richtung. Lief die Instanz einer Veroeffentlichung voraus,
bot die Integration an, sich auf die aeltere Fassung zu aktualisieren. Vom
Eigentuemer gemeldet. ist_neuer() ist jetzt das Gegenstueck zu
versionOrdnung() in appVersion.ts, wo derselbe Fehler am 04.09. behoben wurde:
fehlende Stellen zaehlen als 0, kein int() mit Vorabschnitt, und was sich
nicht in eine Reihenfolge bringen laesst, gilt als verfuegbar statt
verschwiegen. Sechs neue Testfaelle, 79 Python-Tests gruen.

Dazu auf Vorgabe des Eigentuemers: die Zeile benennt jetzt selbst, was sie
zeigt - "Home-Assistant-Integration | 2026.9.5.19" statt "Installiert" mit dem
Gegenstand klein darunter. Damit entfallen die Abschnitts-Ueberschriften aus
dem vorigen Commit, sie sagten dasselbe doppelt.

Live in beiden Oberflaechen gegengeprueft: Gitea auf .18, Instanz auf .19 -
gemeldet wird gruenes "aktuell" (--ok) mit Pruefzeitpunkt statt eines
Rueckschritts. 0 Tracebacks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 11:33:56 +02:00
parent f693a41692
commit 5a65ec9590
8 changed files with 158 additions and 24 deletions
+57
View File
@@ -11797,3 +11797,60 @@ Die Gestaltung des zweiten Abschnitts gegengeprueft, indem einer eingesetzt
wurde: Haarlinie 0.8px, 16px Abstand und 16px Polster darueber, Abschnittskopf
14px ueber seinem Inhalt. Der Abschnitt selbst ist nur in der nativen Huelle zu
sehen - im Browser ist `otaMoeglich()` falsch.
## CX. Die Integration bot Rueckschritte als Update an (2026.9.5.19)
Gemeldet: *"Warum wird eine aeltere Version angeboten? .16 ist aelter als
.18"*. In `aktualisierung.py` stand:
"verfuegbar": remote_version != eigene_version
Reine Ungleichheit, ohne Richtung. Lief die Instanz einer Veroeffentlichung
voraus - waehrend der Entwicklung der Normalfall -, bot die Integration an,
sich auf die AELTERE Fassung zu "aktualisieren".
Es ist derselbe Fehler, der am 04.09.2026 fuer die App behoben wurde
(`appVersion.ts`: *"erkannt werden soll eine NEUERE Fassung und keine
andere"*), nur auf der anderen Seite - und er blieb dort stehen, weil damals
nur die App angesehen wurde.
`ist_neuer()` ist jetzt das Gegenstueck zu `versionOrdnung()`, mit denselben
drei Eigenschaften:
* Fehlende Stellen zaehlen als 0, `2026.9.4` und `2026.9.4.0` sind derselbe
Stand.
* Kein `int()` mit Vorabschnitt - "4b" darf nicht klaglos als 4 gelesen werden,
sonst entsteht genau die scheingenaue Reihenfolge, die vermieden werden soll.
* Laesst sich keine Reihenfolge bilden (Formatwechsel), gilt die Fassung als
verfuegbar: die Oberflaeche sagt dann, dass etwas abweicht, aber nicht in
welche Richtung - besser als eine Aenderung zu verschweigen.
Sechs neue Faelle in `tests/aktualisierung/test_aktualisierung.py`, 79 Tests
gruen. Live gegengeprueft: Gitea auf .18, Instanz auf .19 - gemeldet wird
"aktuell" statt eines Rueckschritts.
### Die Kachel wurde dabei schlanker
Vorgabe des Eigentuemers: *"Statt Installiert 'Home-Assistant-Integration' zu
schreiben. Dann haette der User die Sicht: Was ist in welcher Version vorhanden
auf seinem Geraet."* Die Zeile benennt jetzt selbst, was sie zeigt:
Home-Assistant-Integration 2026.9.5.19
aktuell geprueft 05.09.2026, 11:32 Uhr
Damit entfallen auch die Abschnitts-Ueberschriften "Integration"/"App" aus
CW - sie sagten dasselbe ein zweites Mal. Die beiden Bloecke trennt weiterhin
eine Haarlinie, und benannt sind sie durch ihre eigene Zeile.
**"aktuell" erscheint nur nach einer Pruefung.** Vorher steht dort gar nichts:
gruen "aktuell" ohne Pruefung waere eine Behauptung ohne Grundlage, und der
Knopf "Auf Update pruefen" sagt den Zustand ohnehin. Es gibt keinen
automatischen Takt - geprueft wird nur auf Knopfdruck (nachgesehen, kein
`async_track_time_interval` dafuer).
Gemessen in beiden: Farbe `rgb(52,199,89)` bzw. `rgb(48,209,88)` - das ist
`--ok` im jeweiligen Thema.
**Offen, zur Entscheidung:** "Home-Assistant-Integration" bricht als Beschriftung
auf Telefonbreite in zwei Zeilen um. Lesbar, aber unruhig. "Integration" allein
wuerde einzeilig passen.
+9 -20
View File
@@ -748,21 +748,15 @@ export function Einstellungen({
<Tile>
<span className="ads-eyebrow">Version</span>
{/* Die Zeile benennt selbst, WAS in welcher Fassung auf dem Geraet
liegt - vorher hiess das Label "Installiert" und der Gegenstand
stand klein darunter. Damit braucht der Abschnitt auch keine
Ueberschrift mehr: sie saegte dasselbe ein zweites Mal (Vorgabe
des Eigentuemers, 05.09.2026: "damit wird es nochmal schlanker"). */}
<div className="dm-kachelabschnitt">
{/* Nur wenn darunter wirklich ein zweiter Abschnitt folgt. Ohne den
staende "Integration" allein unter "Version" - eine Unterteilung
ohne zweiten Teil, genau wie im Panel, das den App-Abschnitt gar
nicht kennt. */}
{otaMoeglich() && <span className="ads-eyebrow">Integration</span>}
<Werteliste
kinder={
<Wertzeile
label="Installiert"
wert={serverVersion ?? "unbekannt"}
zusatz="Home-Assistant-Integration"
/>
}
/>
<Werteliste
kinder={<Wertzeile label="Home-Assistant-Integration" wert={serverVersion ?? "unbekannt"} />}
/>
{/* HIG "Loading": Netzwerk zu Gitea plus Entpacken dauert unbestimmt
lang, ohne verlässliche Fortschrittsangabe - ein laufender
Indikator statt eines nur deaktivierten Knopfes verhindert, dass
@@ -865,12 +859,7 @@ export function Einstellungen({
{otaMoeglich() && (
<div className="dm-kachelabschnitt">
<span className="ads-eyebrow">App</span>
<Werteliste
kinder={
<Wertzeile label="Installiert" wert={eigeneVersion() ?? "unbekannt"} zusatz="Diese App" />
}
/>
<Werteliste kinder={<Wertzeile label="App" wert={eigeneVersion() ?? "unbekannt"} />} />
{otaUpdateVerfuegbar && otaBuendel ? (
<>
<Werteliste
@@ -84,6 +84,54 @@ def _header(token: str) -> dict[str, str]:
return {"Authorization": f"token {token}"}
def _versionsteile(version: str) -> list[int] | None:
"""Zerlegt `2026.9.4.17` in [2026, 9, 4, 17].
`None`, sobald ein Teil keine reine Ziffernfolge ist - dann wird nicht
sortiert. Bewusst kein `int()` mit Vorabschnitt: das läse "4b" klaglos als
4 und erzeugte genau die scheingenaue Reihenfolge, die hier vermieden
werden soll. Wortgleich mit `teile()` in
companion-app/src/daten/appVersion.ts."""
if not version:
return None
zahlen: list[int] = []
for stueck in version.split("."):
if not stueck.isdigit():
return None
zahlen.append(int(stueck))
return zahlen
def ist_neuer(kandidat: str, bisher: str) -> bool | None:
"""Ob `kandidat` nachweislich neuer ist als `bisher`.
`None` heißt "nicht zu entscheiden" - verschieden, aber nicht in eine
Reihenfolge zu bringen (Formatwechsel). Fehlende Stellen zählen als 0,
damit `2026.9.4` und `2026.9.4.0` denselben Stand bezeichnen.
Gegenstück zu `versionOrdnung()` in appVersion.ts. Der Grund ist derselbe
und dort ausführlich vermerkt: bis zum 05.09.2026 stand hier
`remote_version != eigene_version`, also reine Ungleichheit ohne Richtung.
Lief die Instanz einer Veröffentlichung voraus - während der Entwicklung
der Normalfall -, bot die Integration an, sich auf die ÄLTERE Fassung zu
"aktualisieren". Vom Eigentümer am 05.09.2026 gemeldet: „Warum wird eine
ältere Version angeboten? .16 ist älter als .18"."""
if not kandidat or not bisher:
return None
if kandidat == bisher:
return False
links = _versionsteile(kandidat)
rechts = _versionsteile(bisher)
if links is None or rechts is None:
return None
for i in range(max(len(links), len(rechts))):
l = links[i] if i < len(links) else 0
r = rechts[i] if i < len(rechts) else 0
if l != r:
return l > r
return False
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.
@@ -120,7 +168,11 @@ async def version_pruefen(hass: HomeAssistant, token: str, eigene_version: str)
raise AktualisierungsFehler(f"Antwort von Gitea nicht lesbar: {fehler}") from fehler
return {
"verfuegbar": remote_version != eigene_version,
# Nur eine NACHWEISLICH neuere Fassung ist ein Update. Ist sie nicht
# in eine Reihenfolge zu bringen (None), gilt sie als verfuegbar -
# dann sagt die Oberflaeche, dass etwas abweicht, aber nicht in
# welche Richtung, statt eine Aenderung zu verschweigen.
"verfuegbar": ist_neuer(remote_version, eigene_version) is not False,
"version": remote_version,
"geprueft_am": datetime.datetime.now(datetime.timezone.utc).isoformat(),
}
@@ -1 +1 @@
{"version":"2026.9.5.18","sha256":"8b03fe0a871e5fc88b421146910832fbd73999ee9844000efbaf1b41a52ab603","bytes":324620,"gebaut":"2026-09-05T09:27:15Z"}
{"version":"2026.9.5.19","sha256":"5490ca4a341da15dd210a13f84fb6941620d2974683b3c4280ea31ed87f0fb7c","bytes":324597,"gebaut":"2026-09-05T09:31:37Z"}
@@ -3719,7 +3719,7 @@ function vEinst() {
(Vorgabe des Eigentuemers, 04.09.2026). -->
<div class="tile"><span class="label">Version</span>
<dl class="rows">
<div class="row" style="border-bottom:0"><dt>Installiert</dt><dd>${esc(APP_VERSION || "unbekannt")}<small>Home-Assistant-Integration</small></dd></div>
<div class="row" style="border-bottom:0"><dt>Home-Assistant-Integration</dt><dd>${esc(APP_VERSION || "unbekannt")}</dd></div>
</dl>
${(() => {
const u = INTEGRATION_UPDATE;
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.5.18",
"version": "2026.9.5.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": [
@@ -188,5 +188,41 @@ class AbgelehnteFaelle(unittest.TestCase):
self._unangetastet()
class VersionsOrdnung(unittest.TestCase):
"""Nur eine NACHWEISLICH neuere Fassung ist ein Update.
Bis zum 05.09.2026 stand in version_pruefen() `remote_version !=
eigene_version` - reine Ungleichheit ohne Richtung. Lief die Instanz einer
Veroeffentlichung voraus, bot die Integration an, sich auf die AELTERE
Fassung zu aktualisieren (vom Eigentuemer gemeldet: .16 wurde angeboten,
waehrend .18 lief)."""
def test_neuere_fassung_ist_ein_update(self):
self.assertIs(a.ist_neuer("2026.9.5.18", "2026.9.5.16"), True)
self.assertIs(a.ist_neuer("2026.10.1.1", "2026.9.5.99"), True)
self.assertIs(a.ist_neuer("2027.1.1.1", "2026.12.31.9"), True)
def test_aeltere_fassung_ist_keins(self):
self.assertIs(a.ist_neuer("2026.9.5.16", "2026.9.5.18"), False)
self.assertIs(a.ist_neuer("2026.9.4.26", "2026.9.5.1"), False)
def test_gleichstand_ist_keins(self):
self.assertIs(a.ist_neuer("2026.9.5.18", "2026.9.5.18"), False)
def test_fehlende_stellen_zaehlen_als_null(self):
self.assertIs(a.ist_neuer("2026.9.4", "2026.9.4.0"), False)
self.assertIs(a.ist_neuer("2026.9.4.1", "2026.9.4"), True)
def test_nicht_sortierbar_bleibt_offen(self):
# Formatwechsel: lieber "nicht entscheidbar" als eine erfundene
# Reihenfolge. "4b" darf nicht klaglos als 4 gelesen werden.
self.assertIsNone(a.ist_neuer("2026.9.4b", "2026.9.4.1"))
self.assertIsNone(a.ist_neuer("v2", "2026.9.5.1"))
self.assertIsNone(a.ist_neuer("", "2026.9.5.1"))
def test_zeichengleich_ist_gleich_auch_bei_fremdem_format(self):
self.assertIs(a.ist_neuer("beliebig", "beliebig"), False)
if __name__ == "__main__":
unittest.main()