Michaels Befund: die Integration ist nicht dasselbe wie die von ninlight. Verglichen und die Luecken geschlossen - alle Felder gibt es in der Firmware schon, sie waren nur nicht bedienbar: * number: fade_in, fade_out, transition_default in Millisekunden, mit den Grenzen der Firmware (10 s, 10 s, 60 s). * select: Einschaltverhalten (last, on, off). * switch: bei Aenderung einschalten. * light: Uebergangszeit beim Ein- und Ausschalten. HA rechnet in Sekunden, das Geraet in Millisekunden; laengeres als 60 s wird gekappt, sonst wiese das Geraet die ganze Anfrage mit 400 ab. Ohne Angabe wird kein Feld geschickt, sonst ueberschriebe jeder Schaltbefehl die Vorgabe. * sensor: letzter Update-Fehler und Startzeit (Zeitstempel statt einer Laufzeit, die jede Abfrage weiterlaeuft), beide ab Werk aus. * diagnostics.py: Diagnosedaten zum Herunterladen, ohne Token, Adressen, MAC und SSID. * config_flow: Neu-Konfigurieren von Adresse und Token, mit Gegenprobe der MAC - ein Tippfehler in der Adresse soll nicht still den falschen Eintrag umbiegen. * Hinweis in den Reparaturen, wenn ein Geraet noch auf dem oeffentlichen Werks-Token steht. Nicht uebernommen: die Auswahl sacn_layout. ninstrip ist immer rgb, ein Strip hat keinen Masterkanal - eine Auswahl mit einer Option waere nur Zierde. 241 Tests, 97 %. Die Kappung der Uebergangszeit per Mutation gegengeprueft.
9.5 KiB
ha-ninstrip
Home-Assistant-Integration für ninstrip — ESP32-C3-Firmware für IC-getriebene LED-Strips. Schwesterprojekt zu ha-ninlight.
Warum eine eigene Integration
ninlight und ninstrip teilen sich das API-Gerüst (Port 3333, /api/v1,
Authorization: <token>), beschreiben aber verschiedene Geräte: eine Lampe
mit einem Licht gegen einen Strip mit Segmenten und Szenen. Beide Modelle in
eine Integration zu zwingen hieße, in Coordinator und Entitäten dauernd zu
verzweigen — und bei jedem Fehler beide Produkte zu gefährden. Dazu kommt,
dass sich die Geräte unter verschiedenen mDNS-Diensten melden
(_ninlight._tcp gegen _ninstrip._tcp) und ohnehin getrennt gefunden
werden.
Das Gerüst ist aber bewusst eins zu eins von ninlight übernommen: gleiche Fehlerklassen, gleicher Coordinator-Aufbau, gleicher Einrichtungsablauf. Wer dort etwas korrigiert, findet es hier an derselben Stelle.
Einrichtung
Das Gerät meldet sich per mDNS und wird von Home Assistant vorgeschlagen.
Nötig ist nur das API-Token. Im Auslieferungszustand und nach jedem
Werksreset antwortet das Gerät mit dem öffentlichen Werks-Token
aabbccddeeff0011223344 — dasselbe wie bei ninlight —, und der Dialog belegt
es vor. Wer ein eigenes gesetzt hat, trägt es dort ein.
Vertrauensgrenze
Das Werks-Token steht öffentlich im Repo. Wer im selben WLAN ist und es kennt, kann das Gerät steuern, Einstellungen ändern und Firmware aufspielen. Das ist eine bewusste Entscheidung, gleich wie bei ninlight: Ziel ist, die Geräte einfach nutzbar zu halten, nicht ein gehärtetes Sicherheitsmodell.
Wer das nicht will, setzt ein eigenes Token — über die Firmware-API mit
PUT system/settings {"type": "token", "token": "..."} — und trägt es hier
ein. Für einen gehärteten Bau lässt sich das Werks-Token in der Firmware auch
ganz abschalten (NL_DEFAULT_TOKEN leer); dann würfelt das Gerät beim ersten
Start und gibt das Token einmalig seriell aus.
Ohne mDNS geht es auch von Hand über Adresse und Token.
Entitäten
| Entität | |
|---|---|
light |
Hauptschalter, Helligkeit, Farbe, Farbtemperatur, Übergangszeit |
select |
aufgeschaltete Szene (acht Plätze in der Firmware) |
select |
Einschaltverhalten (last / on / off) |
number |
Einblend-, Ausblend- und Übergangszeit in ms |
switch |
bei Änderung einschalten |
button |
Neustart |
sensor |
IP-Adresse, WLAN-Signal, Laufzeit, freier Speicher, Leistungsbegrenzung, letzter Update-Fehler, Startzeit |
switch |
sACN-Empfang an/aus |
number |
sACN-Universe und -Startslot |
select |
Verhalten nach Stromende (release / hold) |
binary_sensor |
sACN aktiv, Multicast beigetreten (letzterer standardmäßig aus) |
update |
Firmware-Aktualisierung aus dem Abbild-Ordner |
Standardmäßig an sind die IP-Adresse — sie ist das, was man im Alltag sucht — und die Leistungsbegrenzung, weil sie verrät, ob die Strombegrenzung gerade herunterregelt (100 % heißt: tut sie nicht). WLAN- Signal, Laufzeit und freier Speicher sind aus; die braucht man erst im Fehlerfall.
Eine Lichtentität je Gerät, nicht je Segment. device/state ist der
Hauptschalter und trägt keine Farbe — die steht je Segment. Die
Lichtentität schreibt eine gewählte Farbe deshalb in alle Segmente und
lässt deren Grenzen, Namen und Helligkeit stehen; die mühsam von Hand gemachte
Einteilung bleibt also erhalten.
Zeigen kann sie nur eine Farbe. Sind sich die Segmente nicht einig, meldet
sie gar keine, statt sich eine auszusuchen — jede Angabe wäre dann gelogen.
Farbtemperatur meldet sie nur, wenn alle Segmente auf white stehen;
sobald eines einen Effekt fährt, ist es RGB. Die Spanne ist 2700–6500 K, das
rechnet die Firmware auf die Weißkanäle um (nl_ct.h); Strips ohne
Weißkanäle bekommen dieselbe Spanne als RGB-Näherung.
Für einzelne Segmente gibt es noch keine Entitäten.
Unterschiede zu ha-ninlight
Gleich sind: Einrichtung samt Neu-Konfigurieren und erneuter Anmeldung, Diagnosedaten, Firmware-Aktualisierung, sACN, die Zeiten, das Einschaltverhalten, „bei Änderung einschalten" und der Hinweis, wenn ein Gerät noch auf dem Werks-Token steht.
Verschieden ist, was die Geräte verschieden macht:
- Kein
sacn_layout. ninlight kanndimmer(Masterkanal plus Farben) undrgb; ein Strip hat keinen Masterkanal, ninstrip ist immerrgb. Die Firmware meldet das Feld trotzdem, damit dieselben Werkzeuge beide bedienen. - Farbe kommt aus den Segmenten, bei ninlight aus
device/state. - Szenen statt Farbmodi je Lampe: acht Plätze in der Firmware, als Auswahl.
- Leistungsbegrenzung als Sensor — bei einer Lampe gibt es nichts zu begrenzen.
Noch nicht drin
- Einzelne Segmente als eigene Entitäten
- Effekte (die Firmware kann sie je Segment,
device/effectslistet sie)
Firmware-Aktualisierung
Aufbau wie ha-ninlight. Die Abbilder liegen im Ordner ninstrip_firmware
unterhalb des Konfigurationsverzeichnisses; tools/release_images.sh der
Firmware baut sie. Alle fünf Minuten wird der Ordner durchgesehen.
Home Assistant liefert das Abbild selbst aus, unter einer einmaligen Adresse, die nur gilt, solange die Aktualisierung läuft. Das Gerät kann sich bei Home Assistant nicht anmelden — der unratbare Schlüssel ist der Schutz. Ausgeliefert werden genau die Bytes, die vorher geprüft wurden; wird die Datei mittendrin überschrieben, ändert das nichts mehr an dem, was ankommt.
Drei Dinge entscheiden, ob ein Abbild angeboten wird:
- Die Platine muss passen. Verglichen wird
device_typeaussystem/infomit dem Typ im NINL-Anhänger des Abbildes (ninstrip-rgbc3,ninstrip-k1y).productist bei allen Platinenninstripund taugt dafür nicht: ein K1Y-Abbild invertiert das Datensignal, ein KL-RGBC3-Abbild nicht — die falsche Wahl lässt den Strip dunkel. Firmware vorv0.1.0meldet keindevice_typeund bekommt deshalb gar kein Angebot. - Die Version muss vergleichbar und neuer sein. Gerechnet wird nach
git describe, nicht mit der Versionslogik von Home Assistant: die hält0.1.0-12-g71a57und0.1.0-47-g71a57für denselben Versionskern. Firmware vor dem Tagv0.1.0meldete nur einen Hash — daran lässt sich keine Reihenfolge ablesen, also gibt es auch kein Angebot. - Die WLAN-Zugangsdaten müssen drin sein, solange das Gerät sein WLAN
nur aus der Firmware kennt (
wifi.source=compiled). Sonst verlöre es nach dem Update seine Netzverbindung. Fehlen sie, erscheint ein Hinweis in den Reparaturen statt eines Angebots. Wer das Gerät über das Onboarding ins WLAN gebracht hat (stored), bekommt das Update ohne diesen Vorbehalt.
Die Geräte laden nur über schlichtes http. Erreichen sie die interne
Adresse von Home Assistant nicht so, trägt man in den Einstellungen der
Integration eine eigene ein (http://…, höchstens so lang, dass die ganze
Firmware-Adresse unter 255 Zeichen bleibt — mehr nimmt das Gerät nicht an).
sACN / E1.31
Aufbau wie ha-ninlight: die Einstellungen kommen aus system/settings, der
Zustand aus system/info, und nach jedem Schreiben wird zurückgelesen
statt angenommen — das Gerät prüft selbst, und eine Entität, die das
Gewünschte statt des Gespeicherten zeigt, lügt genau dann, wenn beides
auseinanderläuft.
Zwei Unterschiede zu ninlight:
- Kein Layout-Feld. ninstrip nutzt fest drei Kanäle je Pixel (R, G, B), so wie LedFx, xLights und Resolume senden.
- Der Startslot gilt in jedem Universe, nicht nur im ersten — ein Strip
verteilt sich über mehrere. Die Obergrenze ist deshalb
513 − 3 = 510: dahinter passt kein ganzes Pixel mehr hinein.
Jede Änderung über Home Assistant holt einem laufenden oder gehaltenen Strom die Kontrolle ab — das erledigt die Firmware.
Multicast über VLAN-Grenzen: „beigetreten" heißt nur, dass das Gerät der Gruppe beigetreten ist. Ob das Netz sie weiterreicht, steht auf einem anderen Blatt; ohne Multicast-Router kommt sie über eine VLAN-Grenze nie an. Unicast geht dann trotzdem.
Entwicklung
Die Integration wird in der gemeinsamen Dev-Instanz ha-dev per Mount
eingebunden, der Quellcode bleibt in diesem Repo.
Markenbilder
Die vier PNGs unter custom_components/ninstrip/brand/ sind dieselben wie
in ha-ninlight: die beiden Integrationen gehören zusammen, und zwei
verschiedene Motive in derselben Geräteübersicht stiften nur Verwirrung. Ab
Home Assistant 2026.3 liefert HA sie für eine eigene Integration von dort
aus; ohne sie bleibt ein Platzhalter.
scripts/make_brand.py zeichnet ein eigenes Motiv (ein Band mit fünf
leuchtenden Pixeln) und liegt als Ausgangspunkt bereit, falls doch einmal
eines gewünscht ist — in Gebrauch ist es nicht.
Tests
uv venv -p /opt/homebrew/opt/python@3.14/bin/python3.14 .venv
uv pip install --python .venv/bin/python -r requirements_dev.txt
./scripts/check.sh
scripts/check.sh ersetzt die CI — das Repo liegt in einem privaten Gitea
ohne Actions — und läuft über ruff, die Tests samt Abdeckung (mindestens 95 %)
und die Übersetzungen.
Die Tests liegen in zwei Ordnern: tests_unit/ kommt ohne
Home-Assistant-Instanz aus (API-Client gegen einen echten lokalen Server,
Anhänger, Versionsreihenfolge, Adressen), tests/ fährt eine
Home-Assistant-Instanz hoch. Die Nutzdaten in tests_unit/samples.py sind an
echten Geräten abgefragt, keine erfundenen.
Die Python-Version ist nicht beliebig: Home Assistant 2026.9 verlangt mindestens 3.14.2, und auf PyPI liegt diese HA-Version nur für uv bereit.