feat: update-Entitaet fuer die Firmware und das Testgeruest

Aufgebaut wie ha-ninlight: die Abbilder liegen unter ninstrip_firmware im
Konfigurationsverzeichnis, Home Assistant liefert sie selbst unter einer
einmaligen Adresse aus, und zwar genau die Bytes, die vorher geprueft
wurden. Dazu firmware.py (Anhaenger, Versionsreihenfolge, WLAN-Pruefung,
Katalog), http.py, urls.py und shared.py.

Drei Unterschiede zu ninlight, alle aus der Firmware heraus:

* Verglichen wird device_type - die Platine -, nicht product. Letzteres ist
  bei allen Platinen 'ninstrip'; die Abbilder unterscheiden sich aber hart,
  die K1Y invertiert das Datensignal und die KL-RGBC3 nicht. Firmware ohne
  dieses Feld bekommt gar kein Angebot.
* Die WLAN-Pruefung sucht die SSID unverpackt: ein ESP32-Abbild ist nicht
  gzip-verpackt wie das des W600. An einem mit sdkconfig.local gebauten
  Abbild gegengeprueft - die SSID steht genau einmal NUL-gerahmt drin.
* Der Fehlerschluessel heisst device_url_too_long statt lamp_url_too_long.

Dazu das Testgeruest, das es hier bisher gar nicht gab: 204 Tests in
tests_unit/ (ohne HA-Instanz) und tests/ (mit), Abdeckung 97 %, dazu
scripts/check.sh als Ersatz fuer die fehlende CI. Die Nutzdaten in
tests_unit/samples.py sind an der Stehlampe abgefragt.

Beim Gegenpruefen per Mutation fiel auf, dass die Platinenpruefung
unmittelbar vor dem Senden ungeprueft war - der Test dazu ist jetzt da und
faellt, wenn man die Pruefung entfernt.
This commit is contained in:
Michael Sauer
2026-09-22 23:10:07 +02:00
parent d0a9487805
commit 2c77b2d325
44 changed files with 3600 additions and 43 deletions
@@ -40,9 +40,17 @@
"init": {
"title": "Einstellungen",
"data": {
"scan_interval": "Abfrageabstand (Sekunden)"
"scan_interval": "Abfrageabstand (Sekunden)",
"device_base_url": "Adresse, unter der die Geräte Home Assistant erreichen"
},
"data_description": {
"device_base_url": "Nur für Firmware-Updates nötig, wenn die Geräte die interne Adresse von Home Assistant nicht über schlichtes http erreichen, z. B. http://192.168.1.50:8123"
}
}
},
"error": {
"invalid_base_url": "Es muss eine schlichte http://-Adresse mit Host und optionalem Port sein, ohne Pfad.",
"base_url_too_long": "Adresse zu lang – das Gerät nimmt für die ganze Firmware-Adresse höchstens 255 Zeichen."
}
},
"entity": {
@@ -97,6 +105,11 @@
"sacn_multicast": {
"name": "sACN-Multicast beigetreten"
}
},
"update": {
"firmware": {
"name": "Firmware"
}
}
},
"exceptions": {
@@ -111,6 +124,36 @@
},
"cannot_connect": {
"message": "Das Gerät ist nicht erreichbar."
},
"update_busy": {
"message": "Auf dem Gerät läuft bereits eine Aktualisierung."
},
"no_update": {
"message": "Es steht keine neuere Firmware bereit."
},
"no_http_url": {
"message": "Die Geräte erreichen Home Assistant nur über schlichtes http. Die Adresse steht in den Einstellungen der Integration."
},
"device_url_too_long": {
"message": "Die Firmware-Adresse ist länger als die 255 Zeichen, die das Gerät annimmt."
},
"image_invalid": {
"message": "Die Firmware-Datei hat sich geändert oder ist nicht mehr gültig."
},
"wifi_guard": {
"message": "Diese Firmware enthält die WLAN-Zugangsdaten für {ssid} nicht; das Gerät verlöre seine Netzverbindung."
},
"update_failed": {
"message": "Die Aktualisierung ist fehlgeschlagen: {reason}"
},
"update_timeout": {
"message": "Das Gerät hat sich innerhalb von 12 Minuten nicht mit der neuen Firmware gemeldet."
}
},
"issues": {
"wifi_guard": {
"title": "Firmware für {name} wird nicht angeboten",
"description": "Im Firmware-Ordner liegt {version} für {device_type}, aber ohne WLAN-Zugangsdaten für {ssid}. Das Gerät kennt sein WLAN nur aus der Firmware und verlöre nach dieser Aktualisierung seine Netzverbindung. Deshalb wird sie nicht angeboten. Wer das Gerät stattdessen über das Onboarding ins WLAN gebracht hat, bekommt das Update ohne diesen Vorbehalt."
}
}
}