LUMA-6CH / Open HTTP API
HTTP API documentation
Technical reference for integrating LUMA-6CH with applications, automation systems and tools running on the local network.
1 / Base URL and access
The API uses HTTP/1.1 on the local network and is available at http://luma.local or the device IP address. Never expose the device's port 80 directly to the Internet.
This page provides copyable examples but never communicates with the device. This avoids mixed-content, CORS and Private Network Access problems and accidental token exposure.
- Requests and responses use JSON with
Content-Type: application/json. - API responses use
Cache-Control: no-store. - Successful commands return
{"ok":true}; errors use an HTTP 4xx/5xx status and, where applicable,{"ok":false,"error":"..."}.
2 / Local authentication
When api.auth_required is true, protected routes require Authorization: Bearer <token>. Only unlock assets, GET /api/auth/status and POST /api/auth/login remain public.
| Method | Route | Description |
|---|---|---|
| GET | /api/auth/status | Public: reports only whether unlock is required. |
| POST | /api/auth/login | Public, 512 B maximum. Exchanges the password for a revocable RAM session. |
| POST | /api/auth/password | Sets, changes or removes the local password and revokes existing sessions. |
| POST | /api/tokens | Creates a persistent integration token; its secret is shown once. |
| DELETE | /api/tokens/<id> | Revokes an integration token. |
The local password must contain at least 8 characters; an empty string removes it. Interactive sessions live in RAM for 12 hours and are revoked after a password change or reboot. After five failed logins the device returns 429 try_later for 30 seconds. The persistent verifier uses PBKDF2-HMAC-SHA256 with a random salt and versioned cost.
Interactive session
read -s LUMA_PASSWORD
LUMA_TOKEN=$(curl -fsS -H 'Content-Type: application/json' \
-d "{\"password\":\"$LUMA_PASSWORD\"}" \
http://luma.local/api/auth/login | jq -er .token)
curl -fsS -H "Authorization: Bearer $LUMA_TOKEN" \
http://luma.local/api/statePersistent integration token
For Home Assistant, Node-RED, backends and automations, create a separate credential using an administrative session. Store the secret field in a secret store, never in a repository.
INTEGRATION=$(curl -fsS -X POST \
-H "Authorization: Bearer $LUMA_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"label":"Home Assistant"}' \
http://luma.local/api/tokens)
LUMA_API_TOKEN=$(printf '%s' "$INTEGRATION" | jq -er .secret)
LUMA_TOKEN_ID=$(printf '%s' "$INTEGRATION" | jq -er .id)3 / State and data model
- Six wired channels
0..5for PWM mono LEDs, with per-channel INA219 measurement. - RGB/RGBW/CCT groups with base
0or3, depending on layout. - Addressable S1/S2 strips as virtual groups
6and7; they use an external supply and have no power measurement. - Scenes
scenarios, wall switches1..2, and zones/sensors.
| Method | Route | Result |
|---|---|---|
| GET | /api/state | Complete state: channels, groups, strips, scenes, switches, Wi-Fi, OTA, temperature, power and api.auth_required. |
| GET | /api/devices | Other LUMA devices discovered on the network. |
| GET | /api/channels/map | Logical-channel to physical-LEDC map. |
| GET | /api/wifi/scan | Asynchronous scan: {scanning, networks[]} or array. |
| GET | /api/config/export | Schema 2 backup without passwords, tokens, cloud identity or energy history. |
/api/state.system exposes nvs_queue_overflows, nvs_queue_pending and nvs_flush_failures. A non-zero error counter indicates an asynchronous commit that did not reach flash.
/api/state.inputEvents keeps the latest 32 SW1/SW2 events in RAM, in order, with sequence, timestamp, switchId and type. The sequence restarts after reboot and the queue is not persistent.
4 / Wired channels 0–5
| Method | Route | Body |
|---|---|---|
| POST | /api/channel/<n> | {state?, pwm?, name?, effect?, gamma_active?, restore_after_power_loss?, dimming_ms?, max_voltage_v?, max_current_a?, max_power_w?} |
| POST | /api/channel/<n>/pwm | {"value": 0..100} |
| POST | /api/channel/<n>/effect | {"effect":"static|breathe|blink|strobe|fade|tube"} |
| POST | /api/channel/<n>/name | {"name":"..."} |
| POST | /api/channel/<n>/identify | {} |
pwm:0 is not off: use {"state":false} so the previous level is retained. dimming_ms is the full 0→100% transition time, integer 0..10000; zero is immediate.
User limits cannot exceed 25.5 V, 7.5 A and 180 W per channel. The effective limit is 90 W at 12 V and 180 W at 24 V; the independent fast trip remains 9 A. The aggregate governor uses min(20.8333 A, 500 W / Vbus): 250 W at 12 V and 500 W at 24 V.
curl -fS -X POST -H "Authorization: Bearer $LUMA_TOKEN" \
-H 'Content-Type: application/json' \
http://luma.local/api/channel/0 -d '{"state":true,"pwm":60}'5 / RGB, RGBW, CCT groups and strips
Analogue groups use base 0 or 3; S1/S2 strips use base 6 and 7. In analogue groups, channel positive terminals must be bridged while PWM returns remain separate. Displayed power is the sum of the INA readings and the most restrictive branch governs group derating.
| Method | Route | Body |
|---|---|---|
| POST | /api/group/<base> | {state?, brightness?, white?, color?, effect?, name?, restore_after_power_loss?} |
| POST | /api/group/<base>/color | {"color":"#rrggbb"} |
| POST | /api/group/<base>/brightness | {"value":0..100} |
| POST | /api/group/<base>/white | 0..100: RGBW W intensity or CCT position, from 2700 K to 6500 K. |
| POST | /api/group/<base>/effect | static, breathe, rainbow, cycle, chase, strobe, warm-mix, blink, fade, tube, cct-cycle |
| POST | /api/group/<base>/name | {"name":"..."} |
| POST | /api/strip/<port>/config | {enabled?, chip? (0..3), led_count? (1..1024), gamma?} |
| POST | /api/all | {"state":true|false} |
For strip configuration, port 0 = S1 and 1 = S2. Chip 0: WS2812/13/15/SK6812 RGB (GRB); 1: WS2811/UCS1903 RGB 800 kHz; 2: SK6812 RGBW; 3: APA102/SK9822. Chip 3 uses S1 for data and S2 for clock, therefore excluding the second strip.
curl -fS -X POST -H "Authorization: Bearer $LUMA_TOKEN" \
-H 'Content-Type: application/json' \
http://luma.local/api/group/6 \
-d '{"state":true,"brightness":40,"color":"#ff0000","effect":"rainbow"}'6 / Scenes and wall switches
| Method | Route | Purpose |
|---|---|---|
| GET | /api/scenarios | Complete list; stable IDs 0..15. |
| POST | /api/scenarios | Creates a scene using the /api/state → scenarios[] schema. |
| POST | /api/scenarios/<id> | Updates the scene. |
| POST | /api/scenarios/<id>/run | Runs the scene. |
| DELETE | /api/scenarios/<id> | Deletes the scene. |
| POST | /api/switch/<id> | {label?, input_type?, long_press_ms?, binding?, long_press_binding?} |
Scene actions are {type:"channel"|"group"|"all", target, state, value, color?}. For switches, channel_mask uses bits 0–5 for channels and 6/7 for S1/S2; zero means all.
7 / Network, zones and configuration
| Method | Route | Body / result |
|---|---|---|
| POST | /api/device/name | {"name":"..."} |
| POST | /api/setup/complete | Atomic wizard commit; requires a valid name and layout. |
| GET | /api/zones | Complete snapshot, excluded from /api/state polling. |
| POST | /api/zones | Creates a lux zone with a complete payload. |
| POST | /api/zones/<id> | Updates the zone; its ID is immutable. |
| POST | /api/zones/<id>/calibrate | Starts calibration when channels and sensors are available. |
| POST | /api/wifi/connect | {"ssid":"...","password":"..."} |
| POST | /api/wifi/disconnect | {} |
| POST | /api/channels/map | [0,1,2,3,4,5] |
| POST | /api/config/import | Complete JSON backup. |
| GET | /api/config/export | Sanitised schema 2 backup. |
| POST | /api/config/factory-reset | Wipes NVS and reboots; 409 during OTA. |
/api/config/layout accepts: mono6, rgb_mono, mono_rgb, rgb2, rgbw_mono, cct_mono4, m_cct_m3, m2_cct_m2, m3_cct_m, mono4_cct, cct2_mono2, mono2_cct2, rgb_cct_m, cct_m_rgb, rgbw_cct.
8 / Energy and pricing
| Method | Route | Description |
|---|---|---|
| GET | /api/energy/history | Today, month, 366 completed days, 12 completed months, tariff and unrounded costs. |
| POST | /api/prefs/kwh-price | {"value":0..1000} |
| POST | /api/prefs/currency | {"currency":"EUR","symbol":"€"} |
| POST | /api/prefs/reset-energy | Resets only the current daily counter. |
curl -fsS -H "Authorization: Bearer $LUMA_TOKEN" \
http://luma.local/api/energy/history | jq '.today, .month'
curl -fS -X POST -H "Authorization: Bearer $LUMA_TOKEN" \
-H 'Content-Type: application/json' -d '{"value":0.2845}' \
http://luma.local/api/prefs/kwh-price9 / Local schedules
| Method | Route | Purpose |
|---|---|---|
| GET | /api/schedules | List with ID, configuration and next execution. |
| POST | /api/schedules | Creates and returns the firmware ID with 201. |
| POST | /api/schedules/<id> | Replaces a schedule. |
| DELETE | /api/schedules/<id> | Deletes a schedule. |
| GET/POST | /api/prefs/location | Private location for sunrise and sunset. |
| POST | /api/prefs/timezone | POSIX timezone from the firmware whitelist. |
| POST | /api/prefs/locale | {"locale":"en|it"} |
Maximum 16 schedules. Channel masks 1..0x3f, weekdays 1..0x7f (bit 0 = Monday), minutes 0..1439, event 0=fixed, 1=sunrise, 2=sunset, solar offset -180..180. Impossible payloads return 422 invalid_schedule.
10 / OTA
| Method | Route | Purpose |
|---|---|---|
| GET | /api/ota/info · /api/ota/log | Partition state and non-sensitive log. |
| POST | /api/ota/check · /api/ota/rollback | Checks for updates and rolls back. Updates are signed and delivered by ota.objex.cloud. |
11 / KNXnet/IP
The current build includes the KNXnet/IP tunnelling client. Configuration and mappings are persistent and every route requires authentication.
| Method | Route | Body |
|---|---|---|
| POST | /api/knx/enabled | {"enabled":true|false} |
| POST | /api/knx/mode | {"mode":"tunnel"} |
| POST | /api/knx/gateway | {"gateway":"192.168.1.53"} |
| POST | /api/knx/port | {"port":3671} |
| POST | /api/knx/physical | {"physical":"1.1.250"} |
| POST | /api/knx/mappings | Array of up to 6 channel and switch/dim/feedback GA mappings. |
Group addresses are main/middle/sub (0..31/0..7/0..255). KNX Secure secrets are never returned.
12 / MQTT
The user-configurable MQTT client is separate from the private OBJEX Cloud connection. Routes require local authentication; passwords and CAs are never returned.
| Method | Route | Body / result |
|---|---|---|
| GET | /api/mqtt/user | Non-secret configuration, connection, base topic and diagnostics. |
| POST | /api/mqtt/user | {enabled, transport, host, port, clientId, username, password, prefix, caPem} |
| POST | /api/mqtt/user/clear-password | Clears the stored password. |
| POST | /api/mqtt/user/clear-ca | Clears the custom CA certificate. |
| POST | /api/mqtt/user/publish-now | Publishes state and telemetry now. |
| POST | /api/mqtt/user/test | Tests the session: 409 if disabled, 503 if disconnected. |
Empty secret fields retain the existing value; use the dedicated routes to clear them. The mqtt transport requires allowPlaintext:true. MQTT commands always pass through canonical state and cannot bypass electrical or thermal protection.
13 / Home Assistant
The direct integration is independent from MQTT: it uses Zeroconf and HTTP REST on the LAN. Inventory follows Channel setup and exposes mono, RGB, RGBW, CCT and strips as logical entities.
| Method | Route | Body / result |
|---|---|---|
| GET | /api/home-assistant | Opt-in, local_http transport, API version, stable identity and inventory. |
| POST | /api/home-assistant | {"enabled":true|false} |
Do not manually create REST switches in YAML. Enable Settings → Protocols → Home Assistant, install the luma component and complete pairing under Settings → Devices & services. The password is used once; the component stores only its dedicated token.
14 / Limits, errors and concurrency
- Ordinary bodies: maximum 8 KB and 4 s; login: 512 B; authenticated import: 64 KB and 10 s.
400invalid JSON/type/URL;401missing or expired authentication;404missing resource;409conflict;422impossible configuration;500persistence failure;503subsystem not ready.- Import, channel map, scenes, schedules and zones restore the previous snapshot after an error.
- Do not send concurrent commands to the same field expecting a merge: the last valid command wins and firmware state remains the source of truth.
15 / Feature availability
- DALI endpoints
/api/dali/*are feature-gated and absent from the current build. - KNX is enabled. A build that excludes it returns
404from/api/knx/*without changing configuration. - Cloud MQTT is managed automatically and exposes no manual HTTP commands. The public MQTT client uses independent credentials and namespace.
- The API can grow with firmware; when in doubt,
/api/stateis the authoritative reflection of available features.
16 / Destructive actions
These operations can erase data, interrupt service or revoke access. Export the configuration and verify the target before running them.
POST /api/config/factory-reset wipes NVS and reboots the device.
POST /api/config/import replaces current configuration with the validated backup.
POST /api/ota/rollback changes the active firmware partition.
DELETE /api/scenarios/<id> / DELETE /api/schedules/<id>
DELETE /api/tokens/<id> immediately stops the integration using that token.
No section matches your search.