Nanoleaf Panels
Local-LAN control for Nanoleaf Shapes, Canvas, Lines, Elements, and Light Panels via the documented OpenAPI v1.7 on port 16021. Pairing is on-device (hold the power button until the LED flashes), then GEM stores a bearer token on the device row. No cloud account required.
Prerequisites
- Firmware 1.5.0 or newer. Older controllers running 1.4 firmware do not expose the OpenAPI. Update via the Nanoleaf mobile app first.
- Static DHCP reservation on the controller is strongly recommended. Nanoleaf does not advertise mDNS reliably after a Wi-Fi drop, and the controller does not push state — GEM polls.
- Same Layer 2 segment as the GEM server. The Nanoleaf mobile app discovers via mDNS; if you cannot find the controller from a desktop on the same VLAN, neither will GEM.
Setup
- Add a device with driver
nanoleafand the controller IP.- Find the IP in the Nanoleaf mobile app: Layout → ... → Controller Info, or in your router's DHCP table (the MAC starts with
00:55:DA).
- Find the IP in the Nanoleaf mobile app: Layout → ... → Controller Info, or in your router's DHCP table (the MAC starts with
- Put the controller into pairing mode. Hold the power button on the controller for 5–7 seconds until the LED begins flashing. You have ~30 seconds.
- Run
pair_tokenfrom the device command panel. On success the returned token is written to the device'sauth_tokenattribute automatically and stored encrypted. - Run
get_infoto confirm the panel set is reachable and to see the firmware / panel layout. - The device is now a single-zone light.
on,off,set_level,set_color,set_ct, andset_effectare immediately available.
Attribute reference
| Attribute | Required | Default | Notes |
|---|---|---|---|
ip | yes | — | Controller LAN IP. |
port | no | 16021 | OpenAPI port. Only change for proxied deployments. |
auth_token | populated | — | Bearer token issued by pair_token. Stored encrypted; do not edit by hand. |
status_interval | no | 10000 ms | Polling cadence. The controller pushes nothing; polling is the only state path. |
default_transition_ms | no | 500 ms | Default fade duration for set_level / set_color / set_ct. |
Zone address
The Nanoleaf controller is treated as a single-zone device — one panel set = one logical light. Per-panel addressing requires the External Control (UDP streaming) extension, which is not implemented in this driver.
Commands
| Command | Arguments | Notes |
|---|---|---|
on / off / toggle | — | Power. toggle flips based on the last cached state. |
set_level | level (0–100), optional duration_ms | Sends brightness.duration as integer tenths of a second; duration_ms < 100 becomes an instant change. |
set_ct | ct (Kelvin, clamped 1200–6500) | Color temperature. |
set_color | color ("FF8800" or {h,s,v}) | Solid color; brightness is the value channel. |
set_hsv | hue (0–360), saturation (0–100), value (0–100) | Lower-level color set. |
set_effect | effect (name) | Activates an installed effect. List via get_effects. |
identify | — | Briefly flashes the panels white, then restores. |
pair_token | — | Issue a new auth token. Controller must be in pairing mode. |
get_info | — | Full controller payload (firmware, panel layout). |
get_state | — | Power, brightness, CT, color state. |
get_effects | — | List of installed effect names. |
Known limitations
- Effects ("scenes") are user-installed. The list returned by
get_effectsis per-device and will differ between deployments. GEM does not enumerate them at boot — callget_effectsfrom the admin UI when needed. - External Control UDP streaming is out of scope. Per-panel pixel control (touch effects, music sync, individual-tile animation) requires switching the controller into
extControl v2mode and sending UDP packets at ~25 Hz. Use the Nanoleaf mobile app for those features. - The controller drops idle TCP sockets aggressively. Expect 1–2s of latency on the first request after a long pause; subsequent requests reuse the socket.
Troubleshooting
controller not in pairing mode(403 onpair_token) — Hold the controller power button 5–7 seconds until the LED flashes white, then retry within 30 seconds.unauthorized (401)on any command — The storedauth_tokenis no longer valid (factory reset, firmware downgrade, or user revoked it in the Nanoleaf app). Re-runpair_token.ECONNREFUSEDon poll — Controller is offline or the IP changed. Verify the DHCP lease and reachability withping.- State changes from the Nanoleaf app are not visible for up to
status_intervalms — The controller does not push, GEM polls. Reducestatus_intervalif you need faster reflection, but the controller may rate-limit polling below ~2 seconds.