Files
ha-ninstrip/README.md
T
Michael Sauer 257838a227 feat(sensor): IP-Adresse, von Haus aus eingeblendet
Michaels Wunsch: die Adresse soll man sehen, ohne erst einen Sensor
einzuschalten - man braucht sie fuer die Firmware-API, zum Anpingen oder
um ein Geraet wiederzufinden. ha-ninlight macht es genauso.

WLAN-Signal, Laufzeit und freier Speicher bleiben aus: die braucht man
erst im Fehlerfall.
2026-09-22 23:17:58 +02:00

7.3 KiB
Raw Blame History

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 und Helligkeit des ganzen Strips
select aufgeschaltete Szene (acht Plätze in der Firmware)
button Neustart
sensor IP-Adresse, WLAN-Signal, Laufzeit, freier Speicher, Leistungsbegrenzung
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 — bei mehreren Segmenten gäbe es keine eine Farbe, die stimmen würde. Die Segmente stellt man über Szenen.

Noch nicht drin

  • Einzelne Segmente als eigene Entitäten

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_type aus system/info mit dem Typ im NINL-Anhänger des Abbildes (ninstrip-rgbc3, ninstrip-k1y). product ist bei allen Platinen ninstrip und taugt dafür nicht: ein K1Y-Abbild invertiert das Datensignal, ein KL-RGBC3-Abbild nicht — die falsche Wahl lässt den Strip dunkel. Firmware vor v0.1.0 meldet kein device_type und 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ält 0.1.0-12-g71a57 und 0.1.0-47-g71a57 für denselben Versionskern. Firmware vor dem Tag v0.1.0 meldete 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.

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.