- Migrated from websockets to aiohttp for unified HTTP/WebSocket server - Added REST endpoints: /api/pattern, /api/parameters, /api/state, /api/tempo/reset - Implemented color palette API with 8-slot system and selected colors - First selected color (index 0) is used as primary RGB for patterns - All operations now available via simple HTTP requests (no WebSocket needed) - Added comprehensive documentation: FRONTEND_API.md, COLOR_PALETTE_API.md - Added test scripts: test_rest_api.sh, test_color_patterns.py - Updated test/test_control_server.py for new /ws WebSocket path - Configuration persistence via lighting_config.json - Pattern parameters (n1-n4, brightness, delay) controllable via API - WebSocket still available at /ws for legacy support
9.6 KiB
Color Palette REST API - UI Integration Guide
Overview
The lighting control server provides a REST API for managing an 8-color palette with 2 selected colors. This is designed for UI integration to allow users to:
- View the current 8 colors in the palette
- Edit any of the 8 colors
- Select which 2 colors are active (for patterns that use selected colors)
Configuration is automatically persisted to lighting_config.json
.
Base URLs
The API is available on two ports for flexibility:
Primary (same as WebSocket):
http://<server-ip>:8765/api/color-palette
Backward Compatibility:
http://<server-ip>:8766/api/color-palette
Default IPs:
- Remote:
http://10.42.0.1:8765
(Pi server) - Local:
http://localhost:8765
(testing)
Quick Start for UI Developers
Recommended Usage Pattern
- On UI Load:
GET /api/color-palette
to populate the color picker - When User Edits a Color:
POST /api/color-palette
with updated palette - When User Selects Colors:
POST /api/color-palette
with new selected_indices
API Reference
GET /api/color-palette
Purpose: Get the current palette state (for populating UI on load)
Request
GET /api/color-palette HTTP/1.1
Response (200 OK)
{
"palette": [
{"r": 255, "g": 0, "b": 0}, // Slot 0: Red
{"r": 0, "g": 255, "b": 0}, // Slot 1: Green
{"r": 0, "g": 0, "b": 255}, // Slot 2: Blue
{"r": 255, "g": 255, "b": 0}, // Slot 3: Yellow
{"r": 255, "g": 0, "b": 255}, // Slot 4: Magenta
{"r": 0, "g": 255, "b": 255}, // Slot 5: Cyan
{"r": 255, "g": 128, "b": 0}, // Slot 6: Orange
{"r": 255, "g": 255, "b": 255} // Slot 7: White
],
"selected_indices": [0, 1] // Currently selected: Red and Green
}
Response Fields
palette
: Array of exactly 8 color objects- Each color has
r
,g
,b
(integers 0-255) - Index corresponds to palette slot (0-7)
- Each color has
selected_indices
: Array of exactly 2 integers (0-7)- Indicates which 2 palette slots are currently active
- The first selected color (index 0) is used as the primary RGB color for patterns
- The second selected color (index 1) is available for future pattern features
POST /api/color-palette (or PUT)
Purpose: Update palette colors and/or selected colors
Request Body (JSON)
Both fields are optional - send only what you want to update:
{
"palette": [...], // Optional: Update all 8 colors
"selected_indices": [0, 3] // Optional: Change selected colors
}
Success Response (200 OK)
{
"status": "ok",
"palette": {
"palette": [...],
"selected_indices": [0, 3]
}
}
Error Response (400/500)
{
"status": "error",
"message": "Palette must be an array of 8 colors"
}
Common Use Cases
Use Case 1: Load Palette on UI Startup
async function loadPalette() {
const response = await fetch('http://10.42.0.1:8765/api/color-palette');
const data = await response.json();
// data.palette = array of 8 colors
// data.selected_indices = [index1, index2]
return data;
}
Use Case 2: User Edits a Single Color
When user changes color in slot 3 to purple:
async function updateColor(slotIndex, r, g, b) {
// Get current palette
const current = await fetch('http://10.42.0.1:8765/api/color-palette')
.then(res => res.json());
// Update the specific slot
const newPalette = [...current.palette];
newPalette[slotIndex] = {r, g, b};
// Send updated palette
await fetch('http://10.42.0.1:8765/api/color-palette', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({palette: newPalette})
});
}
// Example: Change slot 3 to purple (128, 0, 128)
updateColor(3, 128, 0, 128);
Use Case 3: User Selects Different Active Colors
When user selects slots 2 and 5:
async function selectColors(index1, index2) {
await fetch('http://10.42.0.1:8765/api/color-palette', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
selected_indices: [index1, index2]
})
});
}
// Example: Select blue (slot 2) and cyan (slot 5)
selectColors(2, 5);
Use Case 4: Reset to Default Palette
async function resetPalette() {
const defaultPalette = [
{r: 255, g: 0, b: 0}, // Red
{r: 0, g: 255, b: 0}, // Green
{r: 0, g: 0, b: 255}, // Blue
{r: 255, g: 255, b: 0}, // Yellow
{r: 255, g: 0, b: 255}, // Magenta
{r: 0, g: 255, b: 255}, // Cyan
{r: 255, g: 128, b: 0}, // Orange
{r: 255, g: 255, b: 255} // White
];
await fetch('http://10.42.0.1:8765/api/color-palette', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
palette: defaultPalette,
selected_indices: [0, 1]
})
});
}
Validation & Error Handling
Validation Rules
Palette Array:
- Must contain exactly 8 color objects
- Each color must have
r
,g
,b
fields - RGB values must be integers between 0-255
Selected Indices:
- Must be an array of exactly 2 integers
- Each index must be between 0-7 (inclusive)
- Can be the same index twice (e.g.,
[3, 3]
)
Error Responses
// Example: Invalid palette length
{
"status": "error",
"message": "Palette must be an array of 8 colors"
}
// Example: Invalid RGB value
{
"status": "error",
"message": "RGB values must be 0-255"
}
// Example: Invalid index
{
"status": "error",
"message": "Selected indices must be 0-7"
}
Data Persistence
- Automatic Save: All changes are immediately saved to
lighting_config.json
- Automatic Load: Server loads saved config on startup
- Default Values: If no config file exists, server initializes with default palette
Default Palette:
[
{r: 255, g: 0, b: 0}, // Slot 0: Red
{r: 0, g: 255, b: 0}, // Slot 1: Green
{r: 0, g: 0, b: 255}, // Slot 2: Blue
{r: 255, g: 255, b: 0}, // Slot 3: Yellow
{r: 255, g: 0, b: 255}, // Slot 4: Magenta
{r: 0, g: 255, b: 255}, // Slot 5: Cyan
{r: 255, g: 128, b: 0}, // Slot 6: Orange
{r: 255, g: 255, b: 255} // Slot 7: White
]
// Default selected: [0, 1] (Red and Green)
Testing & Debugging
Test API Connection
# Quick test - get current palette
curl http://10.42.0.1:8765/api/color-palette
# Pretty print with jq
curl http://10.42.0.1:8765/api/color-palette | jq
# Test update
curl -X POST http://10.42.0.1:8765/api/color-palette \
-H "Content-Type: application/json" \
-d '{"selected_indices": [3, 6]}'
Browser DevTools
// Test in browser console
fetch('http://10.42.0.1:8765/api/color-palette')
.then(r => r.json())
.then(console.log);
Advanced Topics
CORS (Cross-Origin Requests)
Note: The API does not currently include CORS headers.
If accessing from a different origin (e.g., UI on different domain):
- Add CORS middleware to the server (contact backend team)
- Use a proxy server
- Host UI on same origin as API
Helper Function: RGB to Hex
function rgbToHex({r, g, b}) {
return '#' + [r, g, b]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
// Usage
const color = {r: 255, g: 128, b: 0};
const hex = rgbToHex(color); // "#ff8000"
Helper Function: Hex to RGB
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
// Usage
const rgb = hexToRgb('#ff8000'); // {r: 255, g: 128, b: 0}
Environment Configuration
Server Environment Variables (.env
)
# Server host (bind to all interfaces)
CONTROL_SERVER_HOST=0.0.0.0
# Primary port (WebSocket + HTTP API)
CONTROL_SERVER_PORT=8765
# Additional HTTP API port (optional, for backward compatibility)
HTTP_API_PORT=8766
WebSocket Endpoint
Important: WebSocket is on a separate endpoint from the API:
- WebSocket:
ws://10.42.0.1:8765/ws
- HTTP API:
http://10.42.0.1:8765/api/color-palette
Summary for UI Developers
Key Points
✅ 8 color slots, each with RGB values (0-255)
✅ 2 selected colors (indices 0-7)
✅ First selected color is used as the primary RGB for patterns
✅ Auto-persistence to lighting_config.json
✅ Optional updates - send only what changed
✅ Two ports available - 8765 (primary) and 8766 (backup)
Integration Checklist
- Load palette on UI startup with
GET
- Display 8 color slots with current colors
- Highlight the 2 selected colors
- Allow editing individual colors
- Allow selecting which 2 colors are active
- Send updates with
POST
when user makes changes - Handle errors gracefully
- Test with both local and remote server
Quick Reference
// GET - Load palette
const data = await fetch('http://10.42.0.1:8765/api/color-palette')
.then(r => r.json());
// POST - Update color in slot 3
await fetch('http://10.42.0.1:8765/api/color-palette', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
palette: modifiedPaletteArray
})
});
// POST - Change selected colors to slots 2 and 5
await fetch('http://10.42.0.1:8765/api/color-palette', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
selected_indices: [2, 5]
})
});