External API
Control lighting programmatically via HTTP REST API
Overview
The External API allows you to control your Hackers Nightlight lighting from external scripts and applications. It uses a simple HTTP REST interface secured with API key authentication.
Enable the API
- Open the WebUI and navigate to Settings → API
- Toggle Enable External API to on
- Click Generate Key to create an API key
- Copy and save the key immediately - it will only be shown once
Base URL
http://<device-ip>
For devices connected to your network (Station mode), use the device’s IP address (e.g., http://192.168.1.100).
For devices in Access Point mode, use the AP IP address (e.g., http://192.168.4.1).
Authentication
All API requests require an X-API-Key header containing your 64-character hex API key:
X-API-Key: cce9f0dae76bc8dec2f5c2a608a015d45476d8df8b3356a7c26147ba0e5c13e9
Endpoints
GET /api/v1/status
Get the current light state including mode, color values, and brightness.
Response:
{
"success": true,
"state": {
"mode": "rgb",
"r": 255,
"g": 128,
"b": 64,
"warm": 0,
"cold": 0,
"brightness": 200
}
}
Mode values: "rgb" or "pwm"
State fields:
| Field | Type | Description |
|---|---|---|
| mode | string | Current mode: “rgb” (color) or “pwm” (warm/cold white) |
| r | int | Red value (0-255) |
| g | int | Green value (0-255) |
| b | int | Blue value (0-255) |
| warm | int | Warm white PWM value (0-255) |
| cold | int | Cold white PWM value (0-255) |
| brightness | int | Brightness level (0-255) |
POST /api/v1/color
Set RGB color mode with optional brightness.
Request body:
{
"r": 255,
"g": 128,
"b": 64,
"brightness": 200
}
| Parameter | Type | Range | Required | Default |
|---|---|---|---|---|
| r | int | 0-255 | Yes | - |
| g | int | 0-255 | Yes | - |
| b | int | 0-255 | Yes | - |
| brightness | int | 0-255 | No | 255 |
Example response:
{"success": true}
POST /api/v1/pwm
Set warm/cold white PWM channels for color temperature control.
Request body:
{
"warm": 200,
"cold": 50,
"brightness": 255
}
| Parameter | Type | Range | Required | Default |
|---|---|---|---|---|
| warm | int | 0-255 | Yes | - |
| cold | int | 0-255 | Yes | - |
| brightness | int | 0-255 | No | 255 |
Example response:
{"success": true}
POST /api/v1/brightness
Set the overall brightness level (affects both RGB and PWM modes).
Request body:
{
"brightness": 180
}
| Parameter | Type | Range | Required | Default |
|---|---|---|---|---|
| brightness | int | 0-255 | Yes | - |
Example response:
{"success": true}
POST /api/v1/effect
Start or stop a lighting effect.
Request body:
{
"effect": "aurora",
"action": "start"
}
| Parameter | Type | Required | Description |
|---|---|---|---|
| effect | string | Yes | Effect name (see table below) |
| action | string | Yes | “start” or “stop” |
Available Effects:
| Effect | Description |
|---|---|
none | Stop all effects, return to solid color |
rainbow | Classic rainbow color cycle |
rainbow_fade | Smooth rainbow fade transition |
aurora | Aurora borealis / northern lights effect |
strobe | Strobe/flashing light |
disco | Disco ball / music reactive style |
ember_hearth | Warm flickering fireplace embers |
Example response:
{"success": true}
POST /api/v1/off
Turn off all lights immediately (sets all values to 0).
Request body: None required
Example response:
{"success": true}
GET /api/v1/info
Get API version and available endpoints.
Response:
{
"success": true,
"api_version": "1.0",
"endpoints": ["status", "color", "pwm", "brightness", "effect", "off"]
}
GET /api/v1/settings
Get API enable/disable state and key status. Does not require API key.
Response:
{
"enabled": true,
"has_key": true,
"key_shown": false
}
POST /api/v1/key/generate
Generate a new API key. Does not require existing API key.
Response:
{
"success": true,
"key": "cce9f0dae76bc8dec2f5c2a608a015d45476d8df8b3356a7c26147ba0e5c13e9"
}
Warning: Generating a new key invalidates the previous key.
POST /api/v1/settings
Enable or disable the API.
Request body:
{
"enabled": true
}
| Parameter | Type | Required | Description |
|---|---|---|---|
| enabled | bool | Yes | true to enable, false to disable |
Example response:
{"success": true}
Python Example
import requests
class NightlightAPI:
def __init__(self, host, api_key):
self.base_url = f"http://{host}"
self.headers = {"X-API-Key": api_key}
def status(self):
"""Get current light state."""
return requests.get(f"{self.base_url}/api/v1/status", headers=self.headers).json()
def set_color(self, r, g, b, brightness=255):
"""Set RGB color with optional brightness (0-255)."""
return requests.post(
f"{self.base_url}/api/v1/color",
headers=self.headers,
json={"r": r, "g": g, "b": b, "brightness": brightness}
).json()
def set_pwm(self, warm, cold, brightness=255):
"""Set warm/cold white PWM channels (0-255)."""
return requests.post(
f"{self.base_url}/api/v1/pwm",
headers=self.headers,
json={"warm": warm, "cold": cold, "brightness": brightness}
).json()
def set_brightness(self, brightness):
"""Set overall brightness (0-255)."""
return requests.post(
f"{self.base_url}/api/v1/brightness",
headers=self.headers,
json={"brightness": brightness}
).json()
def set_effect(self, effect, action="start"):
"""Start or stop an effect.
Effects: 'none', 'rainbow', 'rainbow_fade', 'aurora',
'strobe', 'disco', 'ember_hearth'
Actions: 'start', 'stop'
"""
return requests.post(
f"{self.base_url}/api/v1/effect",
headers=self.headers,
json={"effect": effect, "action": action}
).json()
def turn_off(self):
"""Turn off all lights."""
return requests.post(f"{self.base_url}/api/v1/off", headers=self.headers).json()
def info(self):
"""Get API version and available endpoints."""
return requests.get(f"{self.base_url}/api/v1/info", headers=self.headers).json()
# Usage Examples
if __name__ == "__main__":
# Connect to device
light = NightlightAPI("192.168.4.1", "your-api-key-here")
# Check current state
print(light.status())
# Set warm orange color
light.set_color(255, 140, 50)
# Set to warm white
light.set_pwm(warm=200, cold=50)
# Start aurora effect
light.set_effect("aurora")
# Stop effect
light.set_effect("none")
# Turn off
light.turn_off()
curl Examples
Get status:
curl -H "X-API-Key: your-key" http://192.168.4.1/api/v1/status
Set color (orange):
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"r":255,"g":140,"b":50}' \
http://192.168.4.1/api/v1/color
Set PWM (warm white):
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"warm":200,"cold":50}' \
http://192.168.4.1/api/v1/pwm
Start effect:
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"effect":"aurora","action":"start"}' \
http://192.168.4.1/api/v1/effect
Stop effect:
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"effect":"none","action":"start"}' \
http://192.168.4.1/api/v1/effect
Turn off:
curl -X POST -H "X-API-Key: your-key" http://192.168.4.1/api/v1/off
Generate new API key:
curl -X POST http://192.168.4.1/api/v1/key/generate
JavaScript / Node.js Example
class NightlightAPI {
constructor(host, apiKey) {
this.baseUrl = `http://${host}`;
this.headers = { "X-API-Key": apiKey };
}
async status() {
const res = await fetch(`${this.baseUrl}/api/v1/status`, { headers: this.headers });
return res.json();
}
async setColor(r, g, b, brightness = 255) {
const res = await fetch(`${this.baseUrl}/api/v1/color`, {
method: "POST",
headers: { ...this.headers, "Content-Type": "application/json" },
body: JSON.stringify({ r, g, b, brightness })
});
return res.json();
}
async setPwm(warm, cold, brightness = 255) {
const res = await fetch(`${this.baseUrl}/api/v1/pwm`, {
method: "POST",
headers: { ...this.headers, "Content-Type": "application/json" },
body: JSON.stringify({ warm, cold, brightness })
});
return res.json();
}
async setEffect(effect, action = "start") {
const res = await fetch(`${this.baseUrl}/api/v1/effect`, {
method: "POST",
headers: { ...this.headers, "Content-Type": "application/json" },
body: JSON.stringify({ effect, action })
});
return res.json();
}
async turnOff() {
const res = await fetch(`${this.baseUrl}/api/v1/off`, {
method: "POST",
headers: this.headers
});
return res.json();
}
}
// Usage
const light = new NightlightAPI("192.168.4.1", "your-api-key-here");
await light.setColor(255, 140, 50);
await light.setEffect("aurora");
await light.turnOff();
Error Responses
| HTTP Status | Meaning | Likely Cause |
|---|---|---|
| 200 | Success | Request completed successfully |
| 400 | Bad Request | Invalid JSON or missing required fields |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | API is disabled in settings |
Error response body:
{
"success": false,
"message": "invalid json"
}
Or for auth errors:
{
"error": "Missing API key"
}
{
"error": "Invalid API key"
}
Security Notes
- Keep your API key secret - anyone with the key can control your lights
- Regenerate the key immediately if you suspect it has been compromised
- Disable the API when not in use via Settings → API
- The API is designed for local network use only - do not expose to the internet
- API keys are stored in non-volatile flash memory and persist across reboots
Troubleshooting
“Missing API key” error:
- Ensure you’re sending the
X-API-Keyheader with every request - Check that the header name is exactly
X-API-Key(case-sensitive)
“Invalid API key” error:
- Verify the key matches exactly (64 hex characters)
- Try generating a new key if the old one is not working
Requests seem to succeed but lights don’t change:
- Ensure the API is enabled in Settings → API
- Check that the device is still connected to the network
Connection refused:
- Verify the device IP address is correct
- Ensure the device is in the same network segment
- Check if the device is in AP mode (use 192.168.4.1)