Files
system-control/firmware/README-API.md
T
mars3142 fb00128847 testing OpenThread
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2026-03-29 18:07:03 +02:00

23 KiB

System Control - API Documentation

This document describes all REST API endpoints and WebSocket messages required for the ESP32 firmware implementation.

Table of Contents


REST API Endpoints

Capabilities

Get Device Capabilities

Returns the device capabilities. Used to determine which features are available.

  • URL: /api/capabilities
  • Method: GET
  • Response:
{
  "thread": true
}
Field Type Description
thread boolean Whether Thread/Matter features are enabled

Notes:

  • If thread is true, the UI shows Matter device management and Scenes
  • If thread is false or the endpoint is unavailable, these features are hidden
  • The client can also force-enable features via URL parameter ?thread=true

WiFi

Scan Networks

Scans for available WiFi networks.

  • URL: /api/wifi/scan
  • Method: GET
  • Response:
[
  {
    "ssid": "NetworkName",
    "rssi": -45
  },
  {
    "ssid": "AnotherNetwork",
    "rssi": -72
  }
]
Field Type Description
ssid string Network name (SSID)
rssi number Signal strength in dBm

Save WiFi Configuration

Saves WiFi credentials and initiates connection.

  • URL: /api/wifi/config
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "ssid": "NetworkName",
  "password": "SecretPassword123"
}
Field Type Required Description
ssid string Yes Network name
password string No Network password
  • Response: 200 OK on success, 400 Bad Request on error

Get WiFi Status

Returns current WiFi connection status.

  • URL: /api/wifi/status
  • Method: GET
  • Response:
{
  "connected": true,
  "ssid": "NetworkName",
  "ip": "192.168.1.100",
  "rssi": -45
}
Field Type Description
connected boolean Whether connected to a network
ssid string Connected network name (if connected)
ip string Assigned IP address (if connected)
rssi number Signal strength in dBm (if connected)

Light Control

Set Light Power

Turns the main light on or off.

  • URL: /api/light/power
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "on": true
}
Field Type Required Description
on boolean Yes true = on, false = off
  • Response: 200 OK on success

Set Thunder Effect

Turns the thunder/lightning effect on or off.

  • URL: /api/light/thunder
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "on": true
}
Field Type Required Description
on boolean Yes true = on, false = off
  • Response: 200 OK on success

Notes:

  • When enabled, random lightning flashes are triggered
  • Can be combined with any light mode
  • Thunder effect stops automatically when light is turned off

Set Light Mode

Sets the lighting mode.

  • URL: /api/light/mode
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "mode": "simulation"
}
Field Type Required Description
mode string Yes One of: day, night, simulation
  • Response: 200 OK on success

Set Active Schema

Sets the active schema for simulation mode.

  • URL: /api/light/schema
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "schema": "schema_01.csv"
}
Field Type Required Description
schema string Yes Schema filename: schema_01.csv, schema_02.csv, etc.
  • Response: 200 OK on success

Get Light Status

Returns current light status (alternative to WebSocket).

  • URL: /api/light/status
  • Method: GET
  • Response:
{
  "on": true,
  "thunder": false,
  "mode": "simulation",
  "schema": "schema_01.csv",
  "color": {
    "r": 255,
    "g": 240,
    "b": 220
  }
}
Field Type Description
on boolean Current power state
thunder boolean Current thunder effect state
mode string Current mode (day/night/simulation)
schema string Active schema filename
color object Current RGB color being displayed

LED Configuration

Get LED Configuration

Returns the current LED segment configuration.

  • URL: /api/wled/config
  • Method: GET
  • Response:
{
  "segments": [
    {
      "name": "Main Light",
      "start": 0,
      "leds": 60
    },
    {
      "name": "Accent Light",
      "start": 60,
      "leds": 30
    }
  ]
}
Field Type Description
segments array List of LED segments
segments[].name string Optional segment name
segments[].start number Start LED index (0-based)
segments[].leds number Number of LEDs in this segment

Save LED Configuration

Saves the LED segment configuration.

  • URL: /api/wled/config
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "segments": [
    {
      "name": "Main Light",
      "start": 0,
      "leds": 60
    },
    {
      "name": "Accent Light",
      "start": 60,
      "leds": 30
    }
  ]
}
Field Type Required Description
segments array Yes List of LED segments (can be empty)
segments[].name string No Optional segment name
segments[].start number Yes Start LED index (0-based)
segments[].leds number Yes Number of LEDs in this segment
  • Response: 200 OK on success, 400 Bad Request on validation error

Notes:

  • Segments define how the LED strip is divided into logical groups
  • Changes are persisted to NVS (non-volatile storage)
  • Each segment can be controlled independently in the light schema

Schema

Load Schema

Loads a schema file.

  • URL: /api/schema/{filename}
  • Method: GET
  • URL Parameters:
    • filename: Schema file name (e.g., schema_01.csv)
  • Response: CSV text data
255,240,220,0,100,250
255,230,200,0,120,250
...

The CSV format has 48 rows (one per 30-minute interval) with 6 values per row:

Column Description Range
1 Red (R) 0-255
2 Green (G) 0-255
3 Blue (B) 0-255
4 Value 1 (V1) - custom value 0-255
5 Value 2 (V2) - custom value 0-255
6 Value 3 (V3) - custom value 0-255

Save Schema

Saves a schema file.

  • URL: /api/schema/{filename}
  • Method: POST
  • Content-Type: text/csv
  • URL Parameters:
    • filename: Schema file name (e.g., schema_01.csv)
  • Request Body: CSV text data (same format as above)
  • Response: 200 OK on success

Devices

Thread device management via the Iris component. Only available when CONFIG_IRIS_ENABLED=y. All device identifiers are 16-character hex EUI-64 strings (e.g., "aabbccddeeff0011").

Scan for New Joiners

Returns Thread devices that have completed the Commissioner/Joiner flow but have not yet been paired (i.e., not in iris_devices.bin). These appear in the OLED menu under "neue Geräte" for manual confirmation.

  • URL: /api/devices/scan
  • Method: GET
  • Response:
[
  {
    "id": "aabbccddeeff0011",
    "name": "H2-eeff0011",
    "capabilities": 3
  }
]
Field Type Description
id string EUI-64 (16-char hex)
name string Auto-generated name (H2-<last4>) or from H2
capabilities number IRIS_CAP_* bitmask (see README-thread.md)

Pair Device

Provisions a new joiner into the paired device list. Queries the H2 for its capabilities via CoAP, stores the device in iris_devices.bin. Only available on the Master unit.

  • URL: /api/devices/pair
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "id": "aabbccddeeff0011",
  "name": "Wagen 42"
}
Field Type Required Description
id string Yes EUI-64 from scan
name string No User-defined name (defaults to id)
  • Response: 200 OK with {"status":"ok"} on success
  • Error: 403 if called on Backup unit, 500 if pairing failed

Get Paired Devices

Returns all devices stored in iris_devices.bin with their current runtime state.

  • URL: /api/devices/paired
  • Method: GET
  • Response:
[
  {
    "id": "aabbccddeeff0011",
    "name": "Wagen 42",
    "capabilities": 3,
    "state": 1,
    "online": true
  }
]
Field Type Description
id string EUI-64 (16-char hex)
name string User-defined display name
capabilities number IRIS_CAP_* bitmask
state number IRIS_STATE_* bitmask (last polled value)
online boolean Whether the device responded in the last poll

Update Device Name

Renames a paired device (persisted to iris_devices.bin).

  • URL: /api/devices/update
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "id": "aabbccddeeff0011",
  "name": "Wagen 07"
}
Field Type Required Description
id string Yes EUI-64
name string Yes New display name
  • Response: 200 OK with {"status":"ok"}, 404 if device not found

Unpair Device

Removes a paired device from iris_devices.bin.

  • URL: /api/devices/unpair
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "id": "aabbccddeeff0011"
}
Field Type Required Description
id string Yes EUI-64
  • Response: 200 OK with {"status":"ok"}, 404 if device not found

Toggle Device Capability (Unicast)

Sends a unicast CoAP POST /toggle to one specific device. Intended for individual control from the OLED menu.

  • URL: /api/devices/toggle
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "id": "aabbccddeeff0011",
  "cap": 1
}
Field Type Required Description
id string Yes EUI-64 of the target device
cap number Yes Exactly one IRIS_CAP_* bit (e.g., 1 = inner light)
  • Response: 200 OK with {"status":"ok"}, 404 if device not found

Set All Devices (Multicast)

Sends an explicit on/off state to all devices simultaneously via CoAP multicast (ff03::1). Use this instead of toggle to ensure all devices reach the same state.

  • URL: /api/devices/toggle_all
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "cap": 1,
  "state": 1
}
Field Type Required Description
cap number Yes Exactly one IRIS_CAP_* bit
state number Yes 1 = activate, 0 = deactivate
  • Response: 200 OK with {"status":"ok"}

Note: This is a NON (non-confirmable) CoAP multicast — delivery is best-effort. No per-device acknowledgement.


Scenes

Not yet implemented. All scene endpoints return {"status":"not_implemented"}. The schema below describes the planned API.

Get All Scenes

Returns all configured scenes.

  • URL: /api/scenes
  • Method: GET
  • Response:
[
  {
    "id": "scene-1",
    "name": "Evening Mood",
    "icon": "🌅",
    "actions": {
      "light": "on",
      "mode": "simulation",
      "schema": "schema_02.csv",
      "devices": [
        {
          "id": "matter-001",
          "state": "on"
        }
      ]
    }
  },
  {
    "id": "scene-2",
    "name": "Night Mode",
    "icon": "🌙",
    "actions": {
      "light": "on",
      "mode": "night"
    }
  }
]

Scene Object:

Field Type Description
id string Unique scene identifier
name string Scene name
icon string Emoji icon for the scene
actions object Actions to execute (see below)

Actions Object:

Field Type Optional Description
light string Yes "on" or "off"
mode string Yes "day", "night", or "simulation"
schema string Yes Schema filename (e.g., schema_01.csv)
devices array Yes Array of device actions (see below)

Device Action Object:

Field Type Description
id string Device ID
state string "on" or "off"

Create/Update Scene

Creates a new scene or updates an existing one.

  • URL: /api/scenes
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "id": "scene-1",
  "name": "Evening Mood",
  "icon": "🌅",
  "actions": {
    "light": "on",
    "mode": "simulation",
    "schema": "schema_02.csv",
    "devices": [
      {
        "id": "matter-001",
        "state": "on"
      }
    ]
  }
}
  • Response: 200 OK on success

Delete Scene

Deletes a scene.

  • URL: /api/scenes
  • Method: DELETE
  • Content-Type: application/json
  • Request Body:
{
  "id": "scene-1"
}
Field Type Required Description
id string Yes Scene ID
  • Response: 200 OK on success

Activate Scene

Executes all actions of a scene.

  • URL: /api/scenes/activate
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "id": "scene-1"
}
Field Type Required Description
id string Yes Scene ID
  • Response: 200 OK on success

Input

Simulate Button Input

Remotely triggers a button action as if pressed on the physical device. Useful for controlling the menu UI via the web interface.

  • URL: /api/input
  • Method: POST
  • Content-Type: application/json
  • Request Body:
{
  "action": "button_up",
  "value": ""
}
Field Type Required Description
action string Yes Action name to execute (see list below)
value string No Optional value passed to the action handler (default: empty)

Available actions:

Action Description
button_up Navigate up in the menu
button_down Navigate down in the menu
button_left Adjust value left (e.g. cycle selection)
button_right Adjust value right
button_select Activate the selected menu item
button_back Go back to the previous screen
  • Response: 200 OK on success, 400 Bad Request if action field is missing

Notes:

  • Actions are dispatched via the heimdall action manager
  • Any registered action can be triggered, not just button actions
  • The value field is passed through to the action handler but is currently unused for button actions

WebSocket

Connection

  • URL: ws://{host}/ws or wss://{host}/ws
  • Protocol: JSON messages

The WebSocket connection is used for real-time status updates. The client should reconnect automatically if the connection is lost (recommended: 3 second delay).


Client to Server Messages

Request Status

Requests the current system status.

{
  "type": "getStatus"
}

Server to Client Messages

Status Update

Sent in response to getStatus or when status changes.

{
  "type": "status",
  "on": true,
  "mode": "simulation",
  "schema": "schema_01.csv",
  "color": {
    "r": 255,
    "g": 240,
    "b": 220
  }
}
Field Type Description
type string Always "status"
on boolean Light power state
mode string Current mode (day/night/simulation)
schema string Active schema filename
color object Current RGB color (optional)

Color Update

Sent when the current color changes (during simulation).

{
  "type": "color",
  "r": 255,
  "g": 200,
  "b": 150
}
Field Type Description
type string Always "color"
r number Red value (0-255)
g number Green value (0-255)
b number Blue value (0-255)

WiFi Status Update

Sent when WiFi connection status changes.

{
  "type": "wifi",
  "connected": true,
  "ip": "192.168.1.100",
  "rssi": -45
}
Field Type Description
type string Always "wifi"
connected boolean Whether connected to a network
ip string Assigned IP address (if connected)
rssi number Signal strength in dBm (if connected)

Static Files

The web interface files should be served from the /spiffs/www/ directory:

Path Description
/ Serves index.html (or captive.html in AP mode)
/index.html Main HTML file (full interface)
/captive.html Captive portal (WiFi setup only)
/css/shared.css Shared styles for all pages
/css/index.css Styles for main interface
/css/captive.css Styles for captive portal
/js/wifi-shared.js Shared WiFi configuration logic
/js/*.js JavaScript modules

Captive Portal

When the ESP32 is in Access Point (AP) mode (no WiFi configured or connection failed), it should serve the captive portal:

Behavior

  1. AP Mode Activation:

    • ESP32 creates an access point (e.g., "marklin-setup")
    • DNS server redirects all requests to the ESP32's IP (captive portal detection)
  2. Captive Portal Detection:

    • Respond to common captive portal detection URLs:
      • /generate_204 (Android)
      • /hotspot-detect.html (Apple)
      • /connecttest.txt (Windows)
    • Return redirect or serve captive.html
  3. Serving Files in AP Mode:

    • /captive.html
    • /captive.html → Captive portal page
    • /js/wifi-shared.js → WiFi functions
    • API endpoints remain the same (/api/wifi/*)
  4. After Successful Configuration:

    • ESP32 attempts to connect to the configured network
    • If successful, switch to Station mode and serve index.html
    • If failed, remain in AP mode
Setting Value
SSID marklin-setup
Password None (open) or marklin
IP Address 192.168.4.1
Gateway 192.168.4.1
Subnet 255.255.255.0

Error Handling

All endpoints should return appropriate HTTP status codes:

Code Description
200 Success
400 Bad Request (invalid input)
404 Not Found (resource doesn't exist)
500 Internal Server Error

Error responses should include a JSON body with an error message:

{
  "error": "Description of what went wrong"
}