Skip to main content

Nanoleaf Panels

Local-LAN control for Nanoleaf Shapes, Canvas, Lines, Elements, and Light Panels via the documented local OpenAPI (the /api/v1/ interface) on port 16021. Pairing is done on the controller — hold the power button until the LED flashes — after which GEM stores a bearer token on the device and talks to it directly. No cloud account is required.

The whole panel set is controlled as one light: GEM commands the controller, not individual panels. Per-panel pixel control (touch effects, music sync, individual-tile animation) needs Nanoleaf's External Control (UDP streaming) extension, which this driver does not implement.

Prerequisites

  • Firmware 1.5.0 or newer. Older controllers running 1.4 firmware do not expose the local OpenAPI. Update with 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 never pushes state — GEM polls it.
  • Same Layer 2 segment as the GEM server. The Nanoleaf mobile app discovers via mDNS; if you cannot reach the controller from a computer on the same VLAN, neither can GEM.

Setup

  1. On the Devices page choose System → Devices → Add Device and pick the driver nanoleaf (display name Nanoleaf Panels). The Device Type auto-fills to light.
  2. Enter the controller's LAN IP in Controller IP. Find it in the Nanoleaf mobile app under Layout → ⋯ → Controller Info, or in your router's DHCP table (the MAC starts with 00:55:DA). Save.
  3. Put the controller into pairing mode: hold the power button for about 5–7 seconds until the LED flashes. You then have ~30 seconds.
  4. Run the pair_token command from the command tester. On success the returned token is written to the device's auth_token attribute automatically and stored encrypted — you never type it in.
  5. Reload the device from the Devices grid. The driver connects and starts its background state poll only on (re)connect, so after pairing the device will not come up Connected or begin reflecting state until you reload it.
  6. Run get_info to confirm the panel set is reachable and to see the firmware and panel layout. The light is now controllable: on, off, set_level, set_color, set_ct, set_hsv, and set_effect are all available.
Re-pairing

The token survives reboots. You only re-run pair_token if the controller is factory-reset, downgraded, or the token is revoked in the Nanoleaf app — and re-pairing always requires the controller to be put back into pairing mode first.

Device attribute reference

AttributeRequiredDefaultNotes
ipyesController LAN IP (Controller IP in the editor).
portno16021OpenAPI port. Always 16021 on consumer controllers; only change for proxied deployments.
auth_tokenpopulated by pairingBearer token issued by pair_token. Stored encrypted; do not edit by hand.
status_intervalno10000 msState-poll cadence (minimum 2000 ms). The controller pushes nothing, so polling is the only state path.
default_transition_msno500 msDefault fade duration for set_level only (see Commands). set_ct / set_color / set_hsv send no fade.

State attributes

On every poll — and on each command that changes state — the driver writes the controller's current state back onto the device. Build attribute triggers, UI tiles, and history on these names:

AttributeTypeMeaning
power_statestringon / off.
levelintBrightness 0–100.
color_tempintColor temperature in Kelvin (last set_ct value, or the polled state).

An attribute is written only when the polled value differs from what the device already holds, so triggers fire on real changes. Hue/saturation are set when you send a color but are not polled back as separate attributes.

Commands

CommandArgumentsNotes
on / off / togglePower. toggle turns the panels on unless the last cached state was already on.
set_levellevel (0–100); optional duration_msBrightness. A level above 0 also powers the panels on. The fade (duration_ms, or default_transition_ms when omitted) is quantized to tenths of a second; a duration that rounds to zero — roughly under 50 ms — is applied instantly.
set_ctct (Kelvin, clamped 1200–6500)Color temperature. Sent with no fade.
set_colorcolor ("FF8800", "#FF8800", or {h,s,v})Solid color; the value/v channel also sets brightness. Sent with no fade.
set_hsvhue (0–360), saturation (0–100), value (0–100)Lower-level color set. Sent with no fade.
set_effecteffect (name)Activates an installed effect by its exact name. List the names with get_effects.
identifyBriefly flashes the panels white, then restores — useful for picking out one controller among several.
pair_tokenIssue a new auth token. The controller must be in pairing mode.
get_infoFull controller payload (firmware, panel layout, capabilities).
get_stateOne-shot power / brightness / color-temperature read.
get_effectsInstalled effect names, returned as {effects: […]}.

Only level is offered as a fillable field in the command tester; duration_ms and the object form of color are reachable from macros that pass custom arguments.

Known limitations

  • Effects ("scenes") are user-installed. The list from get_effects is per-controller and differs between deployments. GEM does not enumerate effects at boot — run get_effects from the command tester when you need the names.
  • External Control UDP streaming is out of scope. Per-panel pixel control requires switching the controller into extControl v2 mode and streaming UDP packets. Use the Nanoleaf mobile app for those features.
  • No persistent connection. Each command is a fresh HTTP request; the driver does not hold a socket open. The controller is slow to accept a new connection after a long idle, so expect 1–2 s of latency on the first request after a pause.

Troubleshooting

SymptomLikely cause / fix
controller not in pairing mode (403 on pair_token)Hold the power button 5–7 s until the LED flashes white, then retry within 30 seconds.
unauthorized (401) on commandsThe stored auth_token is no longer valid (factory reset, firmware downgrade, or revoked in the app). Re-run pair_token, then Reload the device. The device may still show Connected — the controller answered the request — even though the token is bad.
Device stays offline, or state never updates after pairingThe poll loop only starts on connect. Reload the device from the Devices grid after running pair_token.
ECONNREFUSED / connection errorController is offline or its IP changed. Confirm the DHCP lease and reachability with ping.
Changes made in the Nanoleaf app take a few seconds to appearThe controller never pushes; GEM polls every status_interval. Lower it for faster reflection, but the controller may rate-limit polling below ~2 seconds.
  • Devices — the device editor, secure attributes, and Reload.
  • Commands — the command tester used to run pair_token, get_info, and get_effects.
  • Subsystems — organizing the device under the Lights subsystem.