Innerscene Developer API
Once an Innerscene fixture - Circadian Sky or Virtual Sun - is connected to your local Wi-Fi network, it exposes a small HTTP server on the LAN. You can read its status and drive its color and brightness from any device on the same network using ordinary GET and POST requests - no SDK, no cloud round-trip, no account.
This guide walks through the four prerequisites - updating the firmware, joining Wi-Fi, finding the IP address, and choosing a command - and then provides a complete reference for every publicly-supported endpoint along with copy-and-paste examples in Node.js and Python.
Who this guide is for
- System integrators - AV/control installers, lighting designers, and building-management system vendors who need to drive fixtures from a Crestron, Lutron, Savant, or custom controller.
- Home automation enthusiasts - Home Assistant, Node-RED, openHAB, and HomeKit-via-Homebridge users who want fixtures to participate in scenes, schedules, and routines beyond what Innerscene Studio provides natively.
- Researchers - circadian-rhythm, vision-science, and chronobiology labs that need precise, scriptable control of color temperature, brightness, and individual color channels for stimulus delivery and spectral tuning.
- Show-control and demo developers - anyone scripting time-lapse demos, retail experiences, or art installations from Python, JavaScript, TouchDesigner, or any other HTTP-capable environment.
The HTTP server is unauthenticated and LAN-only
Any device on the same network can issue commands - treat your fixtures the way you would treat any other smart-home device on your LAN. The fixture does not accept connections from the public internet, and Innerscene does not recommend exposing it through a port forward. Endpoints that could corrupt calibration data, network credentials, or other sensitive state are intentionally omitted from this guide.
1. Update the fixture firmware
The HTTP endpoints described in this guide require recent firmware. Before you start, update each fixture using Innerscene Studio (the iOS app):
- Install Innerscene Studio from the iOS App Store.
- Open Innerscene Studio within Bluetooth range of the fixture and tap it in the device list to connect.
- Open Settings → Firmware Updates. If a newer build is available the app will prompt you to install it; the fixture reboots automatically when the update finishes.
You can confirm the running version at any time with GET /getVersion after Wi-Fi is configured.
Updating older fixtures (no Bluetooth)
If Innerscene Studio cannot find your fixture, it likely predates Bluetooth provisioning. From the app's device list, tap Have a device with older firmware? and follow the on-screen instructions:
- Open Settings → Wi-Fi on your phone and join the network whose name starts with
Innerscene-- the password isinnerscene. - Important: if a captive-portal screen appears showing fixture controls, do not interact with it - close it (tap the X), then tap Use Without Internet when prompted, and return to Innerscene Studio. Tapping anything in the captive-portal sheet can interrupt the upgrade.
- The app reaches the fixture at
172.30.0.1, uploads the latest firmware, and reboots the device. Once the upgrade completes, the fixture will appear in the regular Bluetooth device list.
2. Connect the fixture to Wi-Fi
Wi-Fi is configured from the fixture's built-in web interface, which Innerscene Studio reaches over Bluetooth. The full step-by-step instructions - including SSID selection, password entry, and the captive-portal flow - are documented in the User Guide (the same guide applies to Virtual Sun - append ?product=virtual-sun to the URL).
Use a 2.4 GHz network
The fixture supports 2.4 GHz Wi-Fi only. If your router broadcasts both bands under one SSID with band-steering enabled, consider creating a 2.4 GHz-only SSID for the fixtures.
3. Find the fixture's IP address
Once joined to Wi-Fi, every fixture is assigned an IP address by your router. There are several ways to find it:
- From Innerscene Studio. Connect to the fixture, open Settings → Wi-Fi. The current IP address is shown alongside the connected SSID. The fixture's MAC address is shown on the same screen and on the device-info page.
- From your router's client list. Look it up by MAC address - copy the MAC from Innerscene Studio and match it against the connected-clients table in your router's admin UI. Each fixture also broadcasts its own Wi-Fi network with the SSID
Innerscene-<type>-<mac>(for exampleInnerscene-pri-a4cf12ab34cd), wheretypereflects the fixture's role or daughterboard:pri(primary),sub(secondary),cas(Casambi),dmx,dali,0-10,cal,hub. This SSID can be used to identify nearby fixtures from your phone's Wi-Fi scan. - By scanning your subnet. Every Circadian Sky fixture responds to
/getVersion, so the quickest way to enumerate fixtures from a Mac, Linux, or Windows-with-WSL machine is:
# Replace 192.168.1 with your subnet. Returns IPs that respond to /getVersion.
for n in $(seq 1 254); do
ip="192.168.1.$n"
curl -s -m 0.4 "http://$ip/getVersion" \
| grep -q '"version"' && echo "$ip"
doneOnce you have an IP, verify the fixture is reachable:
$ curl http://192.168.1.42/getVersion
{"version": "2.4.1", "build_date": "Apr 30 2026", "build_time": "14:22:08", ...}Open the fixture's built-in web UI
Pointing a browser at http://<fixture-ip>/ loads the same control interface that Innerscene Studio shows over Bluetooth. From any phone, tablet, or computer on the same network you can adjust CCT and brightness, edit the schedule, configure daughterboards, and view diagnostics - no app needed. It is also the quickest visual confirmation that you have the right IP.
If you plan to control a fixture from a long-running script, reserve its IP in your router's DHCP settings so it does not change.
4. Send your first command
Every fixture interaction is a single HTTP request. Most endpoints are GET requests with parameters in the query string and return JSON. The example below disables the circadian schedule and sets the fixture to 4500 K at 50% brightness:
curl "http://192.168.1.42/setSchedule?mode=0"
curl "http://192.168.1.42/setCCT?cct=4500&i=0.5"The first call exits the automatic schedule so manual commands are honored, and the second sets the color temperature and brightness. Skip ahead to Examples for full Node.js and Python programs.
API Reference
All endpoints are served from the fixture's IP on port 80. JSON is the default response format. Parameters are passed as URL query parameters (for both GET and POST requests).
Every write endpoint listed below takes effect immediately - no reboot is required. Configuration changes are applied to the running fixture and saved to non-volatile storage in the same call. The only way to reboot is the explicit /restart endpoint.
Status & Identity
Tap any endpoint to expand its parameters, example request, and example response.
GET/getVersionFirmware version, build date, and ESP-IDF SDK version.
Example request
curl http://192.168.1.42/getVersionExample response
{
"version": "2.4.1",
"build_date": "Apr 30 2026",
"build_time": "14:22:08",
"build_timestamp": "6814a3e0",
"sdk": "5.1.2"
}Response fields
| Field | Description |
|---|---|
version | Application firmware version string. |
build_date | Compile-time date in __DATE__ format. |
build_time | Compile-time time in __TIME__ format. |
build_timestamp | Hex-encoded compile timestamp; useful for OTA freshness checks. |
sdk | ESP-IDF version (major.minor.patch). |
GET/getStatusCompact JSON snapshot of the fixture state. The recommended polling endpoint.
Returns a single flat JSON object containing time, CCT, lux, MAC, and a number of compact mode/state fields used by Innerscene Studio and the web UI. Field names are short to keep responses small.
Parameters
| Name | Type | Description |
|---|---|---|
hc | 0 | 1 | Optional. When set to 1, also includes a home-card bitmask (hc field) summarising peer/sensor presence. |
Example request
curl "http://192.168.1.42/getStatus"Example response
{
"timezone": "America/Los_Angeles",
"utc_time": "2026-05-07T18:42:12Z",
"cct": 4500, "lux": 1280, "nlux": 0.45000000,
"mac": "a4:cf:12:ab:34:cd",
"cs": 0, "toff": 0.00, "flags": 1, "puc": 7, "ls": 1,
"m": 0, "wx": 0,
"ssc": 0, "msc": 0, "esc": 0,
"as": 0, "dv1": 0.0, "dv2": 0.0,
"gc": 0, "ir": -1, "sss": 0,
"fr": 0, "ddb": 0, "rc": 6
}Response fields
| Field | Description |
|---|---|
timezone | IANA timezone string set on the fixture (or null). |
utc_time | Current UTC time (ISO-8601). |
cct | Current target color temperature (Kelvin). |
lux | Current absolute target lux at the surface. |
nlux | Normalised intensity 0–1 (matches setCCT i parameter). |
mac | Fixture's AP MAC address. |
cs | Last control source (0=schedule, 1=Web UI, 2=BLE, 3=peer, 4=preview, 5=external). |
toff | Schedule time offset in hours (used when previewing a schedule). |
flags | Bitmask: 1=primary, 2=internet OK, 4=primary search, 8=primary found, 16=reboot pending, 32=manufacturing, 256=calibrating. |
puc | Peer update counter; increments when peer set changes. |
ls | Schedule mode (1=schedule active, 0=manual). |
m | Message count in the user-facing message buffer. |
wx | Weather modulation lux offset (cd offset percent). |
ssc / msc / esc | Counters for SkySync, motion sensor, and EnOcean dimmer events. |
gc | Wireless-group radio channel (0 if no group). |
ir | Idle resume: ms until schedule resumes (-1 = not pending). |
sss | SkySync state (0=idle, 1=actively overriding). |
fr | Fixture role: 0=auto, 1=force primary, 2=force secondary. |
ddb | Detected daughterboard type (hardware). |
rc | Actual Wi-Fi radio channel reported by esp_wifi_get_channel(). |
di object is appended with type-specific fields (0–10 V, DALI, DMX, or Casambi). When SkySync is actively overriding, stc/stl (target CCT and lux) are also appended.GET/getDeviceCapabilitiesMin/max CCT and lux supported by this fixture.
Use this when building a UI to clamp slider ranges to what the hardware actually supports.
Example request
curl http://192.168.1.42/getDeviceCapabilitiesExample response
{
"cct": { "min": 1800, "max": 20000 },
"lux": { "min": 0, "max": 4000 }
}GET/getLuxCurvePer-CCT maximum lux output curve.
Sampled at ~30 points across the supported CCT range. Useful for plotting the fixture's photometric envelope or warning users when a target lux is unreachable at a given color temperature.
Example request
curl http://192.168.1.42/getLuxCurveExample response
{
"curve": [
[1800, 1200],
[2400, 2400],
[3000, 3200],
[4000, 3900],
[5000, 4000],
[6500, 3800],
[10000, 3100],
[20000, 1900]
]
}Response fields
| Field | Description |
|---|---|
curve | Array of [cct, maxLux] tuples. cct is in Kelvin; maxLux is the maximum surface lux deliverable at that CCT. |
GET/getTempDriver-board internal temperature.
Example request
curl http://192.168.1.42/getTempExample response
{
"adc_raw": 1843,
"adc_cal": 1521,
"degC": 42.18
}Response fields
| Field | Description |
|---|---|
adc_raw | Raw ADC reading from the on-board thermistor. |
adc_cal | Calibrated voltage in millivolts. |
degC | Temperature in degrees Celsius (typically 30–55 °C in normal operation). |
GET/getUpTimeSeconds since the last boot.
Plain integer response (not JSON) representing whole seconds. Useful for detecting unexpected reboots in long-running automations.
Example request
curl http://192.168.1.42/getUpTimeExample response
84326GET/getDBInfoDaughterboard type and live readings.
Reports which optional control daughterboard (if any) is installed: 0–10 V dimmer, DALI, DMX, or Casambi.
Example request
curl http://192.168.1.42/getDBInfoExample response
{
"type": 1,
"longDescription": "Dual 0-10V",
"wifiDescription": "0-10",
"adc": 2147,
"dbInt": 0
}Response fields
| Field | Description |
|---|---|
type | Numeric daughterboard type (e.g. 1 = 0–10 V, 2 = DALI, 3 = DMX, 4 = Casambi, 5 = Calibration). 0 indicates no daughterboard. |
longDescription | Human-readable description shown in Innerscene Studio - e.g. Dual 0-10V, DALI DT8, Casambi, Secondary. |
wifiDescription | Short tag (≤4 chars) used inside the SoftAP SSID. One of pri, sub, cas, dmx, dali, 0-10, cal, hub, unk. |
adc | Raw ADC reading on the daughterboard detect pin. |
dbInt | Optional integer value reported by the daughterboard firmware. |
Light Control
GET/getCCTCurrent target color temperature and brightness.
Example request
curl http://192.168.1.42/getCCTExample response
{
"cct": 4500,
"i": 0.450000,
"lux": 1280.000000
}Response fields
| Field | Description |
|---|---|
cct | Current target color temperature in Kelvin. |
i | Normalised brightness (0–1) - same scale as the setCCT i parameter. |
lux | Absolute target surface lux. |
GET/setCCTSet color temperature and brightness. Exits the schedule.
Primary control endpoint. Calling this sets a new target and switches the fixture out of automatic schedule mode. Values outside the device's supported range are clamped silently.
Parameters
| Name | Type | Description |
|---|---|---|
cct* | integer (Kelvin) | Target color temperature. Pass -1 to keep the current CCT and only change brightness. |
i | float 0–1 | Normalised brightness. Required if lux is not supplied.-1 keeps current,0 turns the fixture off. |
lux | float | Absolute target lux. Use instead of i when you want a specific lux value. |
pct | 0 | 1 | When 1, i is interpreted as a percentage of the per-CCT maximum (not the global max). Useful for keeping perceived brightness constant when changing CCT. |
fade | 0 | 1 | Override fade for this call (0 = instant, 1 = fade as configured). |
* required
Example request
# Set 4500 K at 50% brightness
curl "http://192.168.1.42/setCCT?cct=4500&i=0.5"
# Set 3000 K at an absolute 800 lux
curl "http://192.168.1.42/setCCT?cct=3000&lux=800"Example response
11 on success, or a JSON-encoded string starting with error: if a required parameter is missing. Calls during an active calibration return {"error":"blocked - calibration active"}.GET/previewCCTLike /setCCT, but auto-reverts to the schedule after a few seconds.
Drives the LEDs immediately without exiting schedule mode. The fixture returns to its scheduled output after a short timeout with no further preview commands. Use this when a user is dragging a slider and you do not want to permanently disable the schedule.
Parameters
| Name | Type | Description |
|---|---|---|
cct* | integer (Kelvin) | Target color temperature. |
i* | float 0–1 | Normalised brightness. |
* required
Example request
curl "http://192.168.1.42/previewCCT?cct=2700&i=0.3"Example response
1GET/setScheduleEnable or disable the automatic circadian schedule.
Parameters
| Name | Type | Description |
|---|---|---|
mode* | 0 | 1 | 1 = enable schedule; 0 = disable (manual mode). Manual setCCT calls automatically set this to 0. |
src | string | Optional. Set to ext when the call originates from an external source (0–10 V, DMX, DALI, Casambi) so that the external input is treated as the active control source. |
* required
Example request
# Resume automatic schedule
curl "http://192.168.1.42/setSchedule?mode=1"
# Switch to manual control
curl "http://192.168.1.42/setSchedule?mode=0"Example response
1GET/setFadeEnable or disable LED fade transitions.
Parameters
| Name | Type | Description |
|---|---|---|
on* | 0 | 1 | 1 = fade between values smoothly; 0 = jump instantly on every setCCT call. |
* required
Example request
curl "http://192.168.1.42/setFade?on=1"Example response
1GET/identifyBlink the fixture so you can identify it visually.
Calling without parameters causes this fixture to blink. Optionally, pass a target MAC and the call broadcasts an identify command to that fixture instead, which is useful when you have multiple fixtures on the same network.
Parameters
| Name | Type | Description |
|---|---|---|
mac | 12-char hex | Optional. Lowercase hex MAC (no separators) of a target fixture, e.g. a4cf12ab34cd. |
wg | integer | Optional. Wireless group number to scope the broadcast (defaults to this fixture's group). |
Example request
# Blink this fixture
curl http://192.168.1.42/identify
# Blink a different fixture by MAC
curl "http://192.168.1.42/identify?mac=a4cf12ab34cd"Example response
1GET/restartSoft-reboot the fixture.
Parameters
| Name | Type | Description |
|---|---|---|
delay | integer (ms) | Delay before rebooting. Defaults to 1000 ms. |
Example request
curl "http://192.168.1.42/restart?delay=2000"Example response
trueDirect Channel Control
Circadian Sky only - for research and calibration use.
The endpoints in this section are supported on Circadian Sky fixtures only. Virtual Sun does not expose direct PWM channel control.
These endpoints write raw 12-bit PWM duty values to each LED channel and bypass the fixture's photometric calibration. Output spectrum and luminous flux will not match the factory CCT/lux mapping, channel-specific maxima are not enforced beyond the 0–4095 range, and excessive duty on any channel may exceed thermal limits over long runs. Use /setCCT for normal control. Use these endpoints when you need bench control of individual channels for spectral research or sensor calibration.
The fixture exposes four PWM-driven channels, addressed as c0 through c3. Each accepts a 12-bit duty value (0–4095). The spectral mapping of each channel is product-specific and not published — if you need precise spectral control, characterise the output with a spectrometer.
| Param | Channel |
|---|---|
c0 | Channel 1 |
c1 | Channel 2 |
c2 | Channel 3 |
c3 | Channel 4 |
GET/getChannelsRead the raw PWM duty for every LED channel. Circadian Sky only.
Returns a JSON array of the current 12-bit duty values (0–4095) for each channel in the order shown above: [c0, c1, c2, c3]. Useful for verifying the result of a /setChannels call or capturing the channel mix produced by the standard CCT calibration at a given operating point.
Example request
curl http://192.168.1.42/getChannelsExample response
[1820, 1145, 0, 312]0.GET/setChannelsWrite raw PWM duty per channel. Circadian Sky only - bypasses photometric calibration.
Sets one or more LED channels to a 12-bit PWM duty value. Only the parameters you pass are changed; channels you omit retain their current value. The fixture leaves schedule mode but does not apply any color or brightness correction - what you write is what is driven to the LED driver.
Parameters
| Name | Type | Description |
|---|---|---|
c0 | integer 0–4095 | Channel 1 duty. |
c1 | integer 0–4095 | Channel 2 duty. |
c2 | integer 0–4095 | Channel 3 duty. |
c3 | integer 0–4095 | Channel 4 duty. |
bypass | 0 | 1 | When 1, the call is permitted even while the fixture's calibration LED-lock is active (used by external sensor-calibration tools). Defaults to 0. |
Example request
# Drive only c3 at ~25%, all others off
curl "http://192.168.1.42/setChannels?c0=0&c1=0&c2=0&c3=1024"
# Tune just one channel without touching the others
curl "http://192.168.1.42/setChannels?c2=512"Example response
0The fixture will not protect itself against unsafe channel mixes. Sustained operation at full duty across multiple channels can exceed the thermal envelope - monitor /getTemp during long runs.
Returns the literal string 0 (ESP_OK) on success or a non-zero error code if any value was outside 0–4095. Use /getChannels to verify what the driver actually applied.
Time & Schedule
GET/setTimezoneSet the IANA timezone used by the schedule.
The fixture computes sunrise and sunset locally and runs the circadian schedule against the configured timezone.
Parameters
| Name | Type | Description |
|---|---|---|
tz* | string (IANA) | IANA timezone identifier such as America/Los_Angeles or Europe/London. |
space | string | Optional. Set to persistent to keep the timezone across factory reset (defaults to the resettable key space). |
* required
Example request
curl "http://192.168.1.42/setTimezone?tz=America/Los_Angeles"Example response
1GET/readClockRead the on-board real-time clock and battery health.
Example request
curl http://192.168.1.42/readClockExample response
{
"time": "2026-05-07T18:42:12Z",
"bat": 1
}Response fields
| Field | Description |
|---|---|
time | Current RTC time as ISO-8601 (assumed UTC). |
bat | RTC coin-cell battery health: 1 = OK, 0 = depleted/missing. |
0 if the RTC chip cannot be read.External Inputs (Daughterboards)
These endpoints are only meaningful on fixtures fitted with a 0–10 V, DALI, DMX, or Casambi daughterboard. Use /getDBInfo to detect what is installed before calling them.
GET/read010VRead 0–10 V input voltages and current 0–10 V configuration.
Returns the live voltage on both 0–10 V channels along with the calibrated output values and the saved daughterboard configuration.
Example request
curl http://192.168.1.42/read010VExample response
{
"vCCT": 7.42, "vDIM": 5.10,
"aCCT": 1834, "aDIM": 1265,
"nCCT": 0.74, "nDIM": 0.51,
"oCCT": 4500, "oDIM": 0.50,
"cct_min": 0.5, "cct_max": 9.5,
"dim_min": 0.5, "dim_max": 9.5,
"dim_pow": 2.0,
"mode": 0, "filter": 0,
"cctSource": 0, "fixedCCT": 4500,
"schedExitV": 9.0,
"refCCT": 7.40, "refDIM": 5.05
}Response fields
| Field | Description |
|---|---|
vCCT / vDIM | Live measured voltages on the CCT and dim inputs. |
aCCT / aDIM | Raw ADC counts on each input. |
nCCT / nDIM | Normalised 0–1 inputs after calibration. |
oCCT / oDIM | Mapped output CCT (Kelvin) and dim (0–1). |
cct_min / cct_max | Voltages mapping to lowest / highest CCT. |
dim_min / dim_max | Voltages mapping to off / full brightness. |
dim_pow | Exponent applied to dim input (1 = linear, 2 = squared). |
mode | 0–10 V mode (0 = dual normal, 1 = swapped, etc.). |
filter | Input filter mode index. |
cctSource | When dim-only modes are active, where CCT comes from. |
fixedCCT | CCT to use in fixed-CCT modes. |
schedExitV | Voltage threshold that exits schedule mode. |
GET/set010VParamsConfigure 0–10 V calibration and behaviour.
All parameters are optional - only the keys you pass are changed. Values are written to the resettable key space by default; pass persistent=1 to make them survive a factory reset.
Parameters
| Name | Type | Description |
|---|---|---|
cct_min | float (V) | Voltage that maps to the lowest CCT. |
cct_max | float (V) | Voltage that maps to the highest CCT. |
dim_min | float (V) | Voltage that maps to off (0%). |
dim_max | float (V) | Voltage that maps to full brightness. |
dim_pow | float | Power curve exponent applied to the dim input (1 = linear). |
mode | integer | 0–10 V mode index. See Innerscene Studio for human-readable names. |
filter | integer | Input filter mode index. |
cctSource | integer | CCT source for dim-only modes. |
fixedCCT | integer (Kelvin) | CCT used when in fixed-CCT mode. |
schedExit | float 0.05–10 V | Voltage delta required to exit schedule mode. |
persistent | 0 | 1 | 1 = save to persistent key space (kept across factory reset). |
Example request
# Linear-dim calibration with full range 0.5–9.5 V
curl "http://192.168.1.42/set010VParams?dim_min=0.5&dim_max=9.5&dim_pow=1&persistent=1"Example response
1GET/setDaliParamsConfigure DALI CCT mapping.
Parameters
| Name | Type | Description |
|---|---|---|
cct_min | integer (Kelvin) | DALI level 0 maps to this CCT. |
cct_max | integer (Kelvin) | DALI level 254 maps to this CCT. |
persistent | 0 | 1 | 1 = save to persistent key space. |
Example request
curl "http://192.168.1.42/setDaliParams?cct_min=2700&cct_max=6500&persistent=1"Example response
1GET/setDMXParamsSet the DMX base address and mode flags.
Parameters
| Name | Type | Description |
|---|---|---|
mode | integer (bitmask) | DMX mode flags. Refer to Innerscene Studio for bit definitions; sent verbatim to the daughterboard. |
baseAddress | integer 0–511 | DMX base address. Set to -1 to disable DMX control. |
persistent | 0 | 1 | 1 = save to persistent key space. |
Example request
curl "http://192.168.1.42/setDMXParams?baseAddress=24&mode=1&persistent=1"Example response
1GET/read_DMXLive DMX channel values and recent change record.
Example request
curl http://192.168.1.42/read_DMXExample response
{
"baseAddress": 24,
"dip_addr": 24,
"mode_flags": 1,
"changeTime": 142,
"index": 1,
"oldValue": 128,
"newValue": 200,
"cct": [180, 4500],
"dim": [200, 0.78],
"schedule": 0,
"inSchedule": 0
}Response fields
| Field | Description |
|---|---|
baseAddress | Configured DMX base address. |
dip_addr | Address read from the on-board DIP switches. |
mode_flags | Active mode bitmask. |
changeTime | Milliseconds since the most recent DMX change. |
index / oldValue / newValue | Channel index and old/new values from the most recent change. |
cct | [raw DMX value, mapped CCT in Kelvin]. |
dim | [raw DMX value, normalised dim 0–1]. |
schedule | Current value of the schedule channel. |
inSchedule | 1 if the fixture is currently in schedule mode. |
GET/casambiInfoCasambi daughterboard state and live readings.
Example request
curl http://192.168.1.42/casambiInfoExample response
{
"ua": "AA:BB:CC:DD:EE:FF",
"nu": "11:22:33:44:55:66",
"fid": 12345,
"fwv": "37.20",
"uid": 12, "sm": 0, "smn": "Idle", "ir": 1,
"cmin": 2200, "cmax": 6500,
"fmin": 1800, "fmax": 20000,
"lon": -118.2437, "lat": 34.0522, "tzo": -480,
"rt": "2026-05-07T11:42:12",
"d": 0.500, "k": 4500, "y": 1
}Response fields
| Field | Description |
|---|---|
ua | Casambi unit MAC address. |
nu | Casambi network UUID (first 6 bytes). |
fid | Numeric fixture ID assigned by Casambi. |
fwv | Casambi module firmware version (major.minor). |
uid / sm / smn | Unit ID, state-mode integer, and state-mode name. |
ir | 1 once the Casambi network init has been received. |
cmin / cmax | Casambi-configured CCT range. |
fmin / fmax | Fixture's hardware CCT range (for clamping). |
lon / lat / tzo | Geo-position and timezone offset (minutes) reported by Casambi. |
rt | Real-time clock from the Casambi network. |
d | Current dim level (0–1). |
k | Current CCT (Kelvin). |
y | 1 if Casambi is actively driving the fixture. |
Network & Peers
GET/getIPsAll network interface addresses, plus MAC and SoftAP SSID.
Returns one entry per active esp_netif (typically the station and the SoftAP), followed by the AP MAC address and the fixture's SoftAP SSID.
Example request
curl http://192.168.1.42/getIPsExample response
[
["sta", "WIFI_STA_DEF",
"192.168.1.42", "255.255.255.0", "192.168.1.1"],
["ap", "WIFI_AP_DEF",
"172.30.0.1", "255.255.255.0", "172.30.0.1"],
"a4:cf:12:ab:34:cd",
"Innerscene-pri-a4cf12ab34cd"
]Response fields
| Field | Description |
|---|---|
Each interface entry | [description, key, ip, netmask, gateway]. |
Trailing string 1 | Fixture's AP MAC address. |
Trailing string 2 | SoftAP SSID currently being broadcast. |
GET/getAPInfoConnected Wi-Fi station details and SoftAP configuration.
Example request
curl http://192.168.1.42/getAPInfoExample response
{
"sta": {
"ssid": "MyHomeNetwork",
"passwd": "********",
"bssid": "f0:9f:c2:11:22:33",
"channel": 6,
"authmode": 3,
"rssi": -54
},
"ap": {
"ssid": "Innerscene-pri-a4cf12ab34cd",
"passwd": "innerscene",
"channel": 6,
"authmode": 3,
"ssid_hidden": 0,
"max_connection": 4,
"beacon_interval": 1000
}
}Response fields
| Field | Description |
|---|---|
sta.ssid / sta.bssid | Connected Wi-Fi network details. |
sta.rssi / sta.channel | Signal strength (dBm) and Wi-Fi channel. |
sta.authmode | Auth mode (3 = WPA2-PSK). |
ap.ssid | The fixture's own SoftAP SSID. |
ap.ssid_hidden | 1 if the SoftAP is hidden during startup channel scan. |
GET/getPeerInfoOther Innerscene fixtures discovered on the local network.
Each fixture in a wireless group periodically beacons its state to the others over ESP-NOW. This endpoint returns the most recent record this fixture is holding for every peer in its group.
The response is a single JSON object containing a peer-update counter and an array p of strings - one string per peer, with nine comma-separated fields per string. The fields appear in fixed order:
apMac,ipv4,buildTimestamp,flags,wirelessGroup,channel,tempScaled,upTime,rssiExample request
curl http://192.168.1.42/getPeerInfoExample response
{
"puc": 7,
"p": [
"a4cf12ab34cd,2a01a8c0,68142b00,1,3,6,33825,84326,-46",
"a4cf12ab35ef,2b01a8c0,68142b00,0,3,6,33279,84102,-58"
]
}Response fields
| Field | Description |
|---|---|
puc | Peer update counter - increments each time the local peer table changes. Matches the puc field in /getStatus, so you can detect changes without re-fetching the full list. |
p[i] field 1 - apMac | 12-character lowercase hex of the peer's SoftAP MAC, with no separators. Example: a4cf12ab34cd ↔ a4:cf:12:ab:34:cd. |
p[i] field 2 - ipv4 | 32-bit IPv4 address printed as lowercase hex with no leading zeros, in the order ESP-IDF stores it (octet 4 first). To reconstruct the dotted address, read the value as a uint32 and extract bytes LSB-first. Example: 2a01a8c0 → bytes 0xc0,0xa8,0x01,0x2a → 192.168.1.42. |
p[i] field 3 - buildTimestamp | Lowercase hex of the peer's firmware build Unix timestamp (the same value returned in the build_timestamp field of /getVersion). Useful for detecting peers that need an OTA update. |
p[i] field 4 - flags | Decimal bitmask. bit 0 (value 1) = peer is the wireless-group primary. Other bits are reserved. |
p[i] field 5 - wirelessGroup | Decimal 0–255. The wireless group this peer belongs to. Always matches the local fixture's group, since cross-group peers are filtered out of the table. |
p[i] field 6 - channel | Decimal Wi-Fi channel the peer is operating on (1–13 for 2.4 GHz). The whole group converges on the primary's channel. |
p[i] field 7 - tempScaled | Decimal 0–65535. Temperature mapped linearly so that 0 = -20 °C and 65535 = +100 °C (120 °C span). Decode with degC = -20 + (tempScaled / 65535) * 120. Example: 33825 → ~42 °C. |
p[i] field 8 - upTime | Decimal seconds since the peer last booted. Resets to 0 on every reboot. |
p[i] field 9 - rssi | Signed dBm (typically -30 to -90). 0 indicates ESP-NOW has not yet measured the link. Lower (more negative) values mean weaker signal. |
Because the format is positional, peers must be parsed by splitting on the comma. Example Python:
for s in r.json()["p"]:
mac, ipv4_hex, build, flags, group, ch, t, up, rssi = s.split(",")
n = int(ipv4_hex, 16)
ip = f"{n & 0xff}.{(n>>8) & 0xff}.{(n>>16) & 0xff}.{(n>>24) & 0xff}"
degC = -20 + (int(t) / 65535) * 120
is_primary = (int(flags) & 1) == 1Messages
GET/getMessagesUser-facing event messages (mode changes, schedule transitions, errors).
Mirrors the Messages view in Innerscene Studio. Each message is encoded as a single string with three comma-separated fields: type, age in seconds, and the human text.
Example request
curl http://192.168.1.42/getMessagesExample response
[
"12,-15,Schedule mode turned on via WebUI",
"5,-94,Power on : Last CCT, DIM = 4500K, 50%"
]Response fields
| Field | Description |
|---|---|
Each entry | type,seconds_ago,message - type is the numeric MESSAGE_TYPE enum, seconds_ago is negative (relative to now), and the rest is the rendered message. |
GET/clearMessagesClear messages of a given numeric type.
Parameters
| Name | Type | Description |
|---|---|---|
type* | integer | MESSAGE_TYPE enum value to clear. Match this against the first field returned by /getMessages. |
* required
Example request
curl "http://192.168.1.42/clearMessages?type=12"Example response
{"status":"success"}{"status":"failed"} if no message of that type was found.Examples
The fixture is just an HTTP server
Anything that can issue HTTP requests can drive a fixture. The examples below cover Node.js, Python, and Home Assistant because those are the most common, but the same calls work from a shell script with curl, an Arduino with WiFiClient, or any other language that can issue HTTP requests.
Node.js
Discover capabilities, read state, and adjust the fixture:
// fixture.mjs - control an Innerscene fixture from Node.js
// npm install node-fetch
// node fixture.mjs 192.168.1.42
import fetch from "node-fetch";
const ip = process.argv[2];
if (!ip) throw new Error("Usage: node fixture.mjs <fixture-ip>");
const base = `http://${ip}`;
async function get(path) {
const res = await fetch(`${base}${path}`);
if (!res.ok) throw new Error(`${path} -> HTTP ${res.status}`);
const text = await res.text();
try { return JSON.parse(text); } catch { return text; }
}
// 1. Discover what the fixture supports
const caps = await get("/getDeviceCapabilities");
console.log("CCT range :", caps.cct);
console.log("Lux range :", caps.lux);
// 2. Read its current state
const status = await get("/getStatus");
console.log("Now :", status.cct, "K ", status.lux, "lux");
// 3. Disable the schedule and set 4500 K at 50% brightness
await get("/setSchedule?mode=0");
await get("/setCCT?cct=4500&i=0.5");
// 4. Blink it to confirm which physical fixture you just controlled
await get("/identify");
A simple sunset routine that fades the fixture from daylight to warm dim over ten minutes:
// Sunset routine: drop from 5000 K / 100% to 2700 K / 10% over 10 minutes.
// Each fade takes ~2 seconds on the fixture; we issue 30 steps over 600 seconds.
import fetch from "node-fetch";
const ip = "192.168.1.42";
const steps = 30;
const totalSeconds = 600;
const startCCT = 5000, endCCT = 2700;
const startI = 1.0, endI = 0.10;
await fetch(`http://${ip}/setSchedule?mode=0`);
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const cct = Math.round(startCCT + (endCCT - startCCT) * t);
const intensity = (startI + (endI - startI) * t).toFixed(3);
await fetch(`http://${ip}/setCCT?cct=${cct}&i=${intensity}`);
await new Promise(r => setTimeout(r, (totalSeconds * 1000) / steps));
}
Python
Same flow as the Node.js example, using requests:
# fixture.py - control an Innerscene fixture from Python
# pip install requests
# python fixture.py 192.168.1.42
import sys, json, requests
if len(sys.argv) < 2:
raise SystemExit("Usage: python fixture.py <fixture-ip>")
base = f"http://{sys.argv[1]}"
def get(path):
r = requests.get(base + path, timeout=5)
r.raise_for_status()
try:
return r.json()
except ValueError:
return r.text
# 1. Discover what the fixture supports
caps = get("/getDeviceCapabilities")
print("CCT range :", caps["cct"])
print("Lux range :", caps["lux"])
# 2. Read its current state
status = get("/getStatus")
print("Now :", status["cct"], "K ", status["lux"], "lux")
# 3. Disable the schedule and set 4500 K at 50% brightness
get("/setSchedule?mode=0")
get("/setCCT?cct=4500&i=0.5")
# 4. Blink it to confirm which physical fixture you just controlled
get("/identify")
Continuously print the fixture's state once per second - useful for logging or driving a dashboard:
# Poll the fixture once per second and print CCT + lux
import time, requests
ip = "192.168.1.42"
while True:
s = requests.get(f"http://{ip}/getStatus", timeout=2).json()
print(f"{s['utc_time']} {s['cct']:>5} K {s['lux']:>5} lux flags={s['flags']}")
time.sleep(1)
Home Assistant
Home Assistant can talk to the fixture using its built-in rest_command and rest sensor integrations - no custom integration is required. Add the snippets below to your Home Assistant configuration to expose the fixture as both a callable service and a live sensor.
sensor.office_fixture
Office Fixture
4500
K
Brightness
Surface lux
1,280
lux
sensor.office_fixture_temperature
Driver Temp
42
°C
binary_sensor.office_fixture_schedule
Schedule
Active
Office Fixture
4500 K · 50% · schedule active
/getStatus and /getTemp, action buttons bound to rest_command services.Outbound commands (set CCT, switch modes, identify, restart):
# configuration.yaml - define outbound commands
rest_command:
fixture_set_cct:
url: "http://192.168.1.42/setCCT?cct={{ cct }}&i={{ intensity }}"
method: GET
fixture_preview_cct:
url: "http://192.168.1.42/previewCCT?cct={{ cct }}&i={{ intensity }}"
method: GET
fixture_schedule:
url: "http://192.168.1.42/setSchedule?mode={{ mode }}"
method: GET
fixture_identify:
url: "http://192.168.1.42/identify"
method: GET
fixture_restart:
url: "http://192.168.1.42/restart"
method: GET
Once defined, call them from automations, scripts, or dashboards:
- service: rest_command.fixture_set_cct
data: { cct: 4500, intensity: 0.5 }Pull live state into Home Assistant entities so you can graph CCT, lux, and temperature over time, or use them as automation triggers:
# configuration.yaml - pull live state into Home Assistant
sensor:
- platform: rest
name: Office Fixture
resource: "http://192.168.1.42/getStatus"
scan_interval: 10
value_template: "{{ value_json.cct }}"
unit_of_measurement: "K"
json_attributes:
- cct
- lux
- nlux
- flags
- ls # 1 = schedule active, 0 = manual
- utc_time
- mac
- platform: rest
name: Office Fixture Temperature
resource: "http://192.168.1.42/getTemp"
scan_interval: 60
value_template: "{{ value_json.degC }}"
unit_of_measurement: "°C"
device_class: temperature
Example automations - a sunset fade and an auto-resume on motion:
# automations.yaml - fade to warm dim 30 min before sunset
- alias: Office fixture sunset fade
trigger:
- platform: sun
event: sunset
offset: "-00:30:00"
action:
- service: rest_command.fixture_schedule
data: { mode: 0 } # exit automatic schedule
- service: rest_command.fixture_set_cct
data: { cct: 2700, intensity: 0.15 }
# Resume the schedule when motion is detected after a manual override
- alias: Office fixture resume schedule on motion
trigger:
- platform: state
entity_id: binary_sensor.office_motion
to: "on"
condition:
- condition: state
entity_id: sensor.office_fixture
attribute: ls
state: 0
action:
- service: rest_command.fixture_schedule
data: { mode: 1 }
Reserve a static IP
Hard-coding the fixture's IP in your Home Assistant config means the integration breaks if DHCP reassigns the address. Bind the fixture's MAC to a fixed IP in your router's DHCP reservation table, or use a hostname your DNS resolves consistently.
Notes & Caveats
- Manual overrides exit the schedule. Calling
/setCCTtakes the fixture out of automatic mode. Re-enable the schedule with/setSchedule?mode=1when you are done. - Use
previewCCTfor sliders. If a user is dragging a UI control,previewCCTavoids permanently disabling the schedule and self-clears after a few seconds of inactivity. - Poll at most a few times per second.
/getStatusis cheap, but the fixture is a small embedded device. One poll every 1–2 seconds is plenty for a dashboard. - Reserve the IP in your router. Long-running automations break if DHCP reassigns the address. Most routers support binding an IP to a MAC.
- Endpoints not in this guide are private. The fixture exposes additional URIs used internally by Innerscene Studio for provisioning, calibration, and OTA updates. They are not part of the supported API and may change between firmware releases. Calling them can corrupt the fixture's calibration state.
Need an endpoint that is not listed? Have a question about integrating with a building-management system? Email [email protected].