scan() konnte den SSID-Cache filtern, während contains_ssid() aus einem
anderen Executor-Thread gleichzeitig hineinschrieb ("dictionary changed
size during iteration"). Ein Lock schützt jetzt beide Zugriffe.
README ergänzt: mDNS-Updates auf eine bekannte MAC laufen ungeprüft: Ein
Gerät, das dieselbe MAC ankündigt, übernimmt den Host-Eintrag und könnte
so an Anfragen mit Token gelangen (nur in einem bereits kompromittierten
Netz relevant).
98 lines
4.6 KiB
Markdown
98 lines
4.6 KiB
Markdown
# ninlight für Home Assistant
|
||
|
||
Lokale Home-Assistant-Integration für Lampen mit der [ninlight-Firmware](https://gitea.zukunpht.de/ninharp/ninlight-w600)
|
||
(migrierte Klyqa-Lampen auf WinnerMicro W600: GU10, E27, E14, G95). Keine Cloud, kein Konto –
|
||
HA spricht direkt mit der REST-API der Lampe auf Port 3333.
|
||
|
||
## Voraussetzungen
|
||
|
||
- Home Assistant 2026.9 oder neuer
|
||
- Lampen mit ninlight-Firmware im selben Netz
|
||
|
||
## Installation
|
||
|
||
Das Repo ist privat und nicht über HACS verfügbar. Den Ordner `custom_components/ninlight` nach
|
||
`<config>/custom_components/ninlight` der Home-Assistant-Instanz kopieren (z. B. per
|
||
`/usr/bin/scp -r` oder über das Samba-/SSH-Add-on) und Home Assistant neu starten. Für ein Update
|
||
denselben Ordner ersetzen und erneut neu starten.
|
||
|
||
## Einrichtung
|
||
|
||
Lampen werden per mDNS (`_ninlight._tcp`) gefunden und unter *Einstellungen → Geräte & Dienste*
|
||
angeboten. Alternativ „Integration hinzufügen → ninlight" und IP-Adresse oder
|
||
`ninlight-xxxx.local` eingeben.
|
||
|
||
Das Token-Feld ist mit dem **öffentlichen Vorgabe-Token** `aabbccddeeff0011223344` vorbelegt.
|
||
Solange eine Lampe dieses Token nutzt, kann jeder in deinem Netz sie steuern und neu flashen;
|
||
HA zeigt dafür einen Reparaturhinweis. Eigenes Token auf der Lampe setzen
|
||
(`PUT /api/v1/system/settings` mit `{"type":"token","token":"…"}`) und in HA über
|
||
*Neu konfigurieren* eintragen.
|
||
|
||
Wechselt eine Lampe ihre IP, aktualisiert mDNS den Eintrag selbst (Identität ist die MAC).
|
||
Das gilt ungeprüft: Ein Gerät im selben Netz, das dieselbe MAC per mDNS ankündigt, übernimmt
|
||
damit den Host-Eintrag und kann so an Anfragen mit Token gelangen, die eigentlich an die echte
|
||
Lampe gehen (relevant nur in einem bereits kompromittierten Netz).
|
||
|
||
## Entitäten
|
||
|
||
| Entität | Art | Hinweis |
|
||
|---|---|---|
|
||
| Lampe | Licht | An/Aus, Helligkeit, Farbe (RGB-Lampen), Farbtemperatur, Übergang |
|
||
| Einblenden, Ausblenden, Standardübergang | Zahl (Konfiguration) | Millisekunden |
|
||
| Einschaltverhalten | Auswahl (Konfiguration) | letzter Zustand / ein / aus |
|
||
| Bei Änderung einschalten | Schalter (Konfiguration) | |
|
||
| Neustart | Taste (Konfiguration) | Werksreset wird bewusst nicht angeboten |
|
||
| Signalstärke | Sensor (Diagnose) | |
|
||
| IP-Adresse, freier Speicher, letzter Update-Fehler, letzter Start | Sensor (Diagnose) | standardmäßig deaktiviert |
|
||
| Firmware | Update | siehe unten |
|
||
|
||
## Firmware-Updates
|
||
|
||
Home Assistant bietet Updates aus dem Ordner **`<config>/ninlight_firmware/`** an (wird beim
|
||
ersten Start angelegt). Dort `*_ota.img`-Dateien ablegen; Dateinamen sind egal. HA liest
|
||
Gerätetyp und Version aus dem Trailer des Images, prüft die Prüfsumme und bietet jeder Lampe nur
|
||
Images **ihres** Gerätetyps an, die **neuer** sind als ihre Firmware.
|
||
|
||
Die Lampe lädt das Image über reines `http://` direkt von Home Assistant, unter einer zufälligen
|
||
Adresse, die nur während dieses einen Updates gilt. Erreichen die Lampen die interne URL von HA
|
||
nicht über `http://` (z. B. HA hinter HTTPS oder in Docker auf macOS), in den Optionen der
|
||
Integration **„Adresse, unter der Lampen Home Assistant erreichen"** setzen, etwa
|
||
`http://192.168.1.50:8123`.
|
||
|
||
### WLAN-Sicherung – wichtig
|
||
|
||
Eine Lampe kennt ihr WLAN entweder aus **gespeicherten Einstellungen** oder nur aus der
|
||
**einkompilierten Firmware** (`GET /api/v1/system/settings` → `wifi.source`). Im zweiten Fall
|
||
würde ein Image **ohne** WLAN-Zugangsdaten die Lampe nach dem Update aus dem Netz werfen – sie
|
||
startet dann im Onboarding-Zugangspunkt.
|
||
|
||
Deshalb bietet HA einer solchen Lampe nur Images an, die die SSID ihres aktuellen WLANs enthalten.
|
||
Liegt ein neueres Image ohne diese Zugangsdaten im Ordner, erscheint stattdessen ein
|
||
Reparaturhinweis.
|
||
|
||
**Das Release-Archiv der Firmware enthält Images ohne Zugangsdaten.** Für Lampen, die ihr WLAN
|
||
aus der Firmware beziehen, eigene Images **mit** Zugangsdaten bauen (`config.local.cmake` im
|
||
Firmware-Repo).
|
||
|
||
Grenzen der Prüfung: geprüft wird die SSID, nicht das Passwort; und eine Heim-SSID, die zufällig
|
||
einem Text der Firmware gleicht (z. B. `ninlight-recovery`), würde fälschlich als vorhanden
|
||
erkannt.
|
||
|
||
## Diagnose
|
||
|
||
*Gerät → ⋮ → Diagnose herunterladen* liefert Zustand, Systeminfo und passende Firmware-Dateien.
|
||
Token, WLAN-Name, MAC und IP sind geschwärzt.
|
||
|
||
## Entwicklung
|
||
|
||
```bash
|
||
uv venv --python /opt/homebrew/bin/python3.14 .venv && uv pip install --python .venv/bin/python -r requirements_dev.txt
|
||
scripts/check.sh
|
||
```
|
||
|
||
Home Assistant 2026.9 braucht Python ≥ 3.14.2. Vor jedem Push `scripts/check.sh` laufen lassen –
|
||
das Repo liegt im Gitea ohne CI.
|
||
|
||
`tests_unit/` läuft ohne Home-Assistant-Instanz (HTTP-Client gegen einen lokalen Server,
|
||
Firmware-Logik), `tests/` gegen eine Test-Instanz.
|