opencollection: 1.0.0 info: name: ninlight config: environments: - name: EVK (mDNS) variables: - name: device_address value: ninlight-681f.local - name: device_port value: '3333' - name: device_token value: aabbccddeeff0011223344 - name: ota_url value: http://192.168.1.12:8000/ninlight_evk_ota.img - name: Onboarding-AP variables: - name: device_address value: 192.168.4.1 - name: device_port value: '3333' - name: device_token value: aabbccddeeff0011223344 - name: ota_url value: http://192.168.1.12:8000/ninlight_evk_ota.img items: - info: name: Device type: folder seq: 1 items: - info: name: Get State type: http seq: 1 http: method: GET url: '{{device_address}}:{{device_port}}/api/v1/device/state' settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Power On type: http seq: 2 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "on": true } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Power Off type: http seq: 3 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "on": false } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Brightness 50 type: http seq: 4 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "brightness": 50 } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Color Red + Transition type: http seq: 5 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "color": { "r": 255, "g": 0, "b": 0 }, "transition": 1500 } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Color Temp 2700 + Transition type: http seq: 6 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "color_temp": 2700, "transition": 1500 } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Fade to 100 % in 3 s type: http seq: 7 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "on": true, "brightness": 100, "transition": 3000 } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Power-On Behaviour type: http seq: 8 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "power_on_behaviour": "last" } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Fade Times type: http seq: 9 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "fade_in": 500, "fade_out": 800 } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Default Transition + Wake Switch type: http seq: 10 http: method: POST url: '{{device_address}}:{{device_port}}/api/v1/device/state' body: type: json data: |- { "transition_default": 300, "wake_on_change": false } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: System type: folder seq: 2 items: - info: name: Get System Info type: http seq: 1 http: method: GET url: '{{device_address}}:{{device_port}}/api/v1/system/info' settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 docs: |- Enthält `last_ota_error`: `null`, solange kein Update fehlgeschlagen ist, sonst einer von `download`, `crc`, `trailer`, `device_mismatch`, `hash`, `busy` (`busy` heißt: es läuft bereits ein Update). Kein `reset_reason` mehr im Feld-Satz. `wifi.standalone` unterscheidet den dauerhaften eigenen Zugangspunkt vom Onboarding-AP - `wifi.mode` ist bei beiden `"ap"`. Im AP-Betrieb steht unter `wifi.ssid` der **laufende** AP-Name, nicht die gespeicherte Stations-SSID; der Name des Heimnetzes steht damit nicht in dieser tokenfrei lesbaren Antwort. `sacn.running` sagt, ob der Empfangs-Task lebt und einen Socket haelt - zusammen mit `sacn.active` (es kommt gerade ein gueltiger Strom an) und `sacn.multicast` (der letzte Gruppenbeitritt hat geklappt) ist damit unterscheidbar, ob niemand sendet oder ob der Empfang tot ist. - info: name: Get System Settings type: http seq: 2 http: method: GET url: '{{device_address}}:{{device_port}}/api/v1/system/settings' settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 docs: |- `wifi.source` ist `"stored"`, solange ein per PUT onboardetes WLAN gespeichert ist, sonst `"compiled"` - dann laeuft die Lampe auf den einkompilierten NINLIGHT_WIFI_SSID/-PASSWORD, `wifi.ssid` ist leer, und `GET system/info` kann trotzdem eine verbundene SSID zeigen. Der `standalone`-Block zeigt `enabled`, den **gewuenschten** AP-Namen (`ssid`: der gespeicherte, ohne eigenen der Hostname), `channel` und `pulse`. Das AP-Passwort steht nie darin. Der gewuenschte Name kann vom laufenden abweichen, den `GET system/info` meldet - genau das ist das Erkennungsmerkmal fuer einen Rueckfall auf die Vorgaben nach mehrfach gescheitertem AP-Aufbau. - info: name: Set WiFi type: http seq: 3 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "wifi", "ssid": "MeinWLAN", "password": "geheim123" } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Set Device Name type: http seq: 4 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "device_name", "name": "Wohnzimmer" } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Set Token type: http seq: 5 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "token", "token": "neues-token-123" } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Require Auth for All type: http seq: 6 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "auth", "all": true } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Set sACN type: http seq: 7 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "sacn", "enabled": true, "universe": 1, "start_slot": 1, "on_timeout": "release" } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Standalone einschalten type: http seq: 8 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "standalone", "enabled": true, "password": "geheim123", "ssid": "Buehne", "channel": 6, "pulse": true } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 docs: |- Die Lampe spannt dauerhaft einen eigenen Zugangspunkt auf, statt sich in ein WLAN einzuwaehlen - danach ist sie unter `192.168.4.1` erreichbar, per mDNS als `.local`. Antwort **202** `{"status":"standalone"}`, nicht 200: die Antwort muss das Geraet verlassen haben, bevor das Netz unter der Verbindung weggezogen wird. `password` ist beim Einschalten Pflicht, solange keines gespeichert ist (8..63 Zeichen). `ssid` (1..32), `channel` (1..13, Vorgabe 6) und `pulse` sind optional und behalten sonst ihren gespeicherten Wert; ein leerer `ssid`-String wird mit 400 `invalid ssid` abgelehnt. **Achtung beim Ausprobieren:** Aenderst du Name oder Passwort, waehrend du ueber genau diesen AP verbunden bist, baut die Lampe ihn neu auf und wirft dich hinaus - die 202 kommt noch an, danach musst du dem neuen Netz beitreten. Anders als der unkonfigurierte Onboarding-AP ist der Standalone-AP **nicht** token-frei: ab da gilt der Tokenschutz wie im Normalbetrieb. - info: name: Standalone ausschalten type: http seq: 9 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/settings' body: type: json data: |- { "type": "standalone", "enabled": false } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 docs: |- Braucht kein Passwort. Hat die Lampe gespeicherte WLAN-Daten, waehlt sie sich damit wieder ein; ohne solche oeffnet sie den unkonfigurierten Onboarding-AP `ninlight-` - der ist dann wieder token-frei. Der gespeicherte AP-Name bleibt dabei in den Einstellungen stehen und greift beim naechsten Einschalten des Modus erneut. - info: name: Commands type: folder seq: 10 items: - info: name: Ping type: http seq: 1 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/command' body: type: json data: |- { "command": { "type": "ping" } } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Reboot type: http seq: 2 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/command' body: type: json data: |- { "command": { "type": "reboot" } } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Factory Reset type: http seq: 3 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/command' body: type: json data: |- { "command": { "type": "factory_reset" } } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 - info: name: Firmware Update type: http seq: 4 http: method: PUT url: '{{device_address}}:{{device_port}}/api/v1/system/command' body: type: json data: |- { "command": { "type": "fw_update", "url": "{{ota_url}}" } } settings: encodeUrl: true timeout: 0 followRedirects: true maxRedirects: 5 docs: |- Erfolg: `202 {"status":"updating"}`, Download startet im Hintergrund. Ungültige URL (kein `http://`-Präfix, zu kurz/lang): `400`. Läuft bereits ein Update oder scheitert der Start sonstwie: `409` mit dem Grund im Body (gleicher Wert wie `last_ota_error`, z.B. `"busy"`). request: headers: - name: Authorization value: '{{device_token}}' bundled: true extensions: bruno: ignore: - node_modules - .git presets: requestType: http requestUrl: '{{device_address}}:{{device_port}}/api/v1/'