diff --git a/README.md b/README.md index 0dff849..93f9499 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,22 @@ Serielle Konsole (115200 8N1, lokales Echo): Verfügbare Befehle in der Konsole: `help`. +## Netzwerk & REST + +- WLAN fest einbauen: `NINLIGHT_WIFI_SSID` / `NINLIGHT_WIFI_PASSWORD` in `config.local.cmake` (gespeicherte Einstellungen haben Vorrang). +- Ohne WLAN-Daten (oder nach Werksreset) öffnet die Lampe den AP `ninlight-` (Passwort `NINLIGHT_AP_PASSWORD`, Default `ninlight`) und pulsiert warmweiß. Onboarding: + + curl -X PUT http://192.168.4.1:3333/api/v1/system/settings \ + -d '{"type":"wifi","ssid":"MeinWLAN","password":"geheim123"}' + curl http://192.168.4.1:3333/api/v1/system/info # wifi.sta_ip / wifi.error + + Nach Erfolg schließt der AP nach 60 s. Ohne Verbindung für 5 min öffnet die Lampe den AP zusätzlich (Fallback). +- REST auf Port 3333, Präfix `/api/v1`: `GET|POST device/state`, `GET system/info`, `GET|PUT system/settings` und `PUT system/command` (beide mit Header `Authorization: `, Default-Token `aabbccddeeff0011223344`). +- Beispiel: `curl -X POST http://ninlight-681f.local:3333/api/v1/device/state -d '{"on":true,"brightness":40,"color_temp":2700}'` +- Suchen: `dns-sd -B _ninlight._tcp` (macOS), Details `dns-sd -L ninlight-681f _ninlight._tcp`. +- Bruno: `bruno/ninlight.yml` importieren, Environment „EVK (mDNS)" bzw. „Onboarding-AP" wählen. +- Konsole: `net` zeigt Modus, SSID, IP, RSSI und den letzten Onboarding-Fehler. + ## Werksreset Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden. diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml new file mode 100644 index 0000000..ad9fa1d --- /dev/null +++ b/bruno/ninlight.yml @@ -0,0 +1,395 @@ +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: Onboarding-AP + variables: + - name: device_address + value: 192.168.4.1 + - name: device_port + value: '3333' + - name: device_token + value: aabbccddeeff0011223344 +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 + 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 + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Color Temp 2700 + type: http + seq: 6 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "color_temp": 2700 + } + 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: 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 + - 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 + - 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: Commands + type: folder + seq: 7 + 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": "http://192.168.1.12:8000/ninlight_evk_ota.img" + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 +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/' diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index a6c8076..c79c307 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -22,6 +22,19 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) — getestet 2026-09-11 mit e19ee98: Neustart nach 40,4 s - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) +## Netzwerk & REST (Plan 2) +- [ ] Mit Build-Credentials: Boot → `net` zeigt `mode=sta` und eine IP, RSSI plausibel (−30 … −90) +- [ ] `dns-sd -B _ninlight._tcp` findet `ninlight-`, `dns-sd -L` zeigt TXT type/fw/mac/name +- [ ] Bruno: alle Device-Requests wirken sichtbar, `Get State` spiegelt den Zustand +- [ ] Ungültiger Body (`{"brightness":0}`) → 400, `system/settings` ohne Token → 401 +- [ ] `bri 30` per REST, sofort `Reboot` per REST → nach dem Boot 30 % (Flush) +- [ ] Werksreset → AP `ninlight-` sichtbar, Lampe pulsiert, Beitritt mit `ninlight` +- [ ] Onboarding mit falschem Passwort → `system/info` zeigt `wifi.error = "auth"`, AP bleibt +- [ ] Onboarding korrekt → `202`, Lampe verbindet, Pulsieren stoppt, AP nach 60 s weg +- [ ] Router aus > 5 min → AP erscheint (`mode=apsta`); Router an → Verbindung, AP nach 10 s weg +- [ ] Namensänderung per REST → TXT `name=` aktualisiert (`dns-sd -L`) +- [ ] `auth_all` an → `GET device/state` ohne Token 401, mit Token 200 + ## Später (Lampen) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß)