Files

17 KiB

Lighting Controller REST API - Frontend Documentation

Overview

Complete REST API for controlling the LED lighting system. No WebSocket required - all operations use simple HTTP requests.

Base URL: http://10.42.0.1:8765
Local Testing: http://localhost:8765


Table of Contents

  1. Quick Start
  2. Pattern Control
  3. Color Palette
  4. Parameters
  5. System State
  6. Tempo Control
  7. Complete Examples

Quick Start

Load Initial State

// Get everything in one call
const response = await fetch('http://10.42.0.1:8765/api/state');
const state = await response.json();

console.log(state.pattern);           // Current pattern
console.log(state.parameters);        // All parameters
console.log(state.color_palette);     // 8 colors + 2 selected
console.log(state.beat_index);        // Current beat number

Change Pattern

await fetch('http://10.42.0.1:8765/api/pattern', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({pattern: 'alternating'})
});

Change Color

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]}) // Blue and Cyan
});

Pattern Control

GET /api/pattern

Get the currently active pattern.

Request:

GET /api/pattern HTTP/1.1

Response:

{
  "pattern": "alternating"
}

POST /api/pattern

Change the active pattern.

Request:

POST /api/pattern HTTP/1.1
Content-Type: application/json

{
  "pattern": "alternating"
}

Available Patterns:

  • "on" / "o" - Solid color
  • "off" / "f" - All LEDs off
  • "flicker" / "f" - Flickering effect
  • "fill_range" / "fr" - Fill effect
  • "n_chase" / "nc" - Chase pattern
  • "alternating" / "a" - Alternating on/off
  • "pulse" / "p" - Pulsing effect
  • "rainbow" / "r" - Rainbow cycle
  • "specto" / "s" - Spectograph effect
  • "radiate" / "rd" - Radiate from center
  • "segmented_movement" / "sm" - Moving segments

Response:

{
  "status": "ok",
  "pattern": "alternating"
}

JavaScript Example:

async function setPattern(patternName) {
  const response = await fetch('http://10.42.0.1:8765/api/pattern', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({pattern: patternName})
  });
  return await response.json();
}

// Usage
await setPattern('alternating');
await setPattern('rainbow');

Color Palette

GET /api/color-palette

Get the 8-color palette and selected colors.

Response:

{
  "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]         // [0] = pattern color, [1] = reserved
}

Important: The first selected color (index 0) is used for all patterns!


POST /api/color-palette

Update palette colors and/or selected colors.

Request (Change Selected Colors):

{
  "selected_indices": [2, 5]  // Use slot 2 (Blue) for patterns
}

Request (Update a Color):

{
  "palette": [
    {"r": 255, "g": 0, "b": 0},
    {"r": 0, "g": 255, "b": 0},
    {"r": 128, "g": 0, "b": 128},  // Changed to purple
    // ... all 8 colors (must send complete array)
  ]
}

Response:

{
  "status": "ok",
  "palette": {
    "palette": [...],
    "selected_indices": [2, 5]
  }
}

JavaScript Example:

// Change pattern color to slot 5 (Cyan)
async function selectColor(slotIndex) {
  const response = await fetch('http://10.42.0.1:8765/api/color-palette', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({selected_indices: [slotIndex, 1]})
  });
  return await response.json();
}

// Edit a color in the palette
async function updatePaletteColor(slotIndex, r, g, b) {
  // First 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})
  });
}

// Usage
await selectColor(2);  // Use blue for patterns
await updatePaletteColor(3, 128, 0, 128);  // Change slot 3 to purple

Parameters

GET /api/parameters

Get all current parameter values.

Response:

{
  "brightness": 100,    // 0-100
  "delay": 50,          // milliseconds
  "n1": 10,             // Pattern parameter 1
  "n2": 5,              // Pattern parameter 2
  "n3": 2,              // Pattern parameter 3 (forward movement)
  "n4": 1               // Pattern parameter 4 (backward movement)
}

POST /api/parameters

Update one or more parameters. Only send the parameters you want to change.

Request:

{
  "brightness": 75,
  "n1": 15
}

Response:

{
  "status": "ok",
  "parameters": {
    "brightness": 75,
    "delay": 50,
    "n1": 15,
    "n2": 5,
    "n3": 2,
    "n4": 1
  }
}

Parameter Descriptions:

Parameter Range Description
brightness 0-100 LED brightness percentage
delay 1-1000 Pattern speed (milliseconds)
n1 0-255 Pattern-specific (e.g., segment length)
n2 0-255 Pattern-specific (e.g., spacing)
n3 0-255 Pattern-specific (e.g., forward steps)
n4 0-255 Pattern-specific (e.g., backward steps)

JavaScript Example:

async function setBrightness(value) {
  const response = await fetch('http://10.42.0.1:8765/api/parameters', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({brightness: value})
  });
  return await response.json();
}

async function setSpeed(delayMs) {
  await fetch('http://10.42.0.1:8765/api/parameters', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({delay: delayMs})
  });
}

// Usage
await setBrightness(75);  // Set to 75%
await setSpeed(100);       // Slow down pattern

System State

GET /api/state

Get complete system state in a single call. Perfect for initial UI load.

Response:

{
  "pattern": "alternating",
  "parameters": {
    "brightness": 100,
    "delay": 50,
    "n1": 10,
    "n2": 5,
    "n3": 2,
    "n4": 1
  },
  "color_palette": {
    "palette": [...8 colors...],
    "selected_indices": [0, 1]
  },
  "beat_index": 42
}

JavaScript Example:

// Load all state when UI starts
async function loadInitialState() {
  const response = await fetch('http://10.42.0.1:8765/api/state');
  const state = await response.json();
  
  // Update UI with current state
  updatePatternButtons(state.pattern);
  updateColorPalette(state.color_palette);
  updateSliders(state.parameters);
  
  return state;
}

Tempo Control

POST /api/tempo/reset

Reset the tempo/beat detection in the sound system.

Request:

POST /api/tempo/reset HTTP/1.1

Response:

{
  "status": "ok",
  "message": "Tempo reset sent"
}

JavaScript Example:

async function resetTempo() {
  const response = await fetch('http://10.42.0.1:8765/api/tempo/reset', {
    method: 'POST'
  });
  return await response.json();
}

// Usage: Call this when tempo detection seems off
await resetTempo();

Complete Examples

Example 1: Full UI Controller Class

class LightingController {
  constructor(baseUrl = 'http://10.42.0.1:8765') {
    this.baseUrl = baseUrl;
  }
  
  // Load complete state
  async loadState() {
    const response = await fetch(`${this.baseUrl}/api/state`);
    return await response.json();
  }
  
  // Pattern control
  async setPattern(pattern) {
    const response = await fetch(`${this.baseUrl}/api/pattern`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({pattern})
    });
    return await response.json();
  }
  
  // Color selection
  async selectColor(slotIndex) {
    const response = await fetch(`${this.baseUrl}/api/color-palette`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({selected_indices: [slotIndex, 1]})
    });
    return await response.json();
  }
  
  // Brightness control
  async setBrightness(value) {
    const response = await fetch(`${this.baseUrl}/api/parameters`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({brightness: value})
    });
    return await response.json();
  }
  
  // Pattern parameters
  async setParameters(params) {
    const response = await fetch(`${this.baseUrl}/api/parameters`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify(params)
    });
    return await response.json();
  }
}

// Usage
const lights = new LightingController();

// On page load
const state = await lights.loadState();

// User interactions
await lights.setPattern('rainbow');
await lights.selectColor(2);  // Blue
await lights.setBrightness(75);

Example 2: React Component

import { useState, useEffect } from 'react';

function LightingControl() {
  const [state, setState] = useState(null);
  const BASE_URL = 'http://10.42.0.1:8765';
  
  // Load initial state
  useEffect(() => {
    fetch(`${BASE_URL}/api/state`)
      .then(res => res.json())
      .then(setState);
  }, []);
  
  // Change pattern
  const handlePatternChange = async (pattern) => {
    await fetch(`${BASE_URL}/api/pattern`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({pattern})
    });
    // Reload state
    const newState = await fetch(`${BASE_URL}/api/state`).then(r => r.json());
    setState(newState);
  };
  
  // Change color
  const handleColorSelect = async (slotIndex) => {
    await fetch(`${BASE_URL}/api/color-palette`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({selected_indices: [slotIndex, 1]})
    });
    const newState = await fetch(`${BASE_URL}/api/state`).then(r => r.json());
    setState(newState);
  };
  
  // Change brightness
  const handleBrightnessChange = async (value) => {
    await fetch(`${BASE_URL}/api/parameters`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({brightness: value})
    });
  };
  
  if (!state) return <div>Loading...</div>;
  
  return (
    <div>
      <h2>Pattern: {state.pattern}</h2>
      
      <div>
        <button onClick={() => handlePatternChange('alternating')}>Alternating</button>
        <button onClick={() => handlePatternChange('rainbow')}>Rainbow</button>
        <button onClick={() => handlePatternChange('pulse')}>Pulse</button>
      </div>
      
      <div>
        <h3>Colors</h3>
        {state.color_palette.palette.map((color, i) => (
          <div
            key={i}
            onClick={() => handleColorSelect(i)}
            style={{
              background: `rgb(${color.r}, ${color.g}, ${color.b})`,
              border: state.color_palette.selected_indices[0] === i ? '3px solid gold' : '1px solid black',
              width: 50,
              height: 50,
              display: 'inline-block',
              cursor: 'pointer'
            }}
          />
        ))}
      </div>
      
      <div>
        <label>Brightness: {state.parameters.brightness}</label>
        <input
          type="range"
          min="0"
          max="100"
          value={state.parameters.brightness}
          onChange={(e) => handleBrightnessChange(e.target.value)}
        />
      </div>
    </div>
  );
}

Example 3: Simple HTML + Vanilla JS

<!DOCTYPE html>
<html>
<head>
  <title>Lighting Controller</title>
</head>
<body>
  <h1>LED Lighting Control</h1>
  
  <div id="patterns"></div>
  <div id="colors"></div>
  
  <label>Brightness: <span id="brightness-value">100</span></label>
  <input type="range" id="brightness" min="0" max="100" value="100">
  
  <script>
    const BASE_URL = 'http://10.42.0.1:8765';
    
    // Load initial state
    async function init() {
      const response = await fetch(`${BASE_URL}/api/state`);
      const state = await response.json();
      
      // Create pattern buttons
      const patterns = ['on', 'alternating', 'rainbow', 'pulse', 'segmented_movement'];
      const patternsDiv = document.getElementById('patterns');
      patterns.forEach(pattern => {
        const btn = document.createElement('button');
        btn.textContent = pattern;
        btn.onclick = () => setPattern(pattern);
        patternsDiv.appendChild(btn);
      });
      
      // Create color palette
      const colorsDiv = document.getElementById('colors');
      state.color_palette.palette.forEach((color, i) => {
        const div = document.createElement('div');
        div.style.cssText = `
          background: rgb(${color.r}, ${color.g}, ${color.b});
          width: 50px;
          height: 50px;
          display: inline-block;
          cursor: pointer;
          border: ${state.color_palette.selected_indices[0] === i ? '3px solid gold' : '1px solid black'};
        `;
        div.onclick = () => selectColor(i);
        colorsDiv.appendChild(div);
      });
      
      // Brightness slider
      document.getElementById('brightness').oninput = (e) => {
        document.getElementById('brightness-value').textContent = e.target.value;
        setBrightness(e.target.value);
      };
    }
    
    async function setPattern(pattern) {
      await fetch(`${BASE_URL}/api/pattern`, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({pattern})
      });
    }
    
    async function selectColor(index) {
      await fetch(`${BASE_URL}/api/color-palette`, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({selected_indices: [index, 1]})
      });
      location.reload();  // Refresh to show updated selection
    }
    
    async function setBrightness(value) {
      await fetch(`${BASE_URL}/api/parameters`, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({brightness: parseInt(value)})
      });
    }
    
    init();
  </script>
</body>
</html>

API Endpoint Summary

Method Endpoint Purpose
GET /api/state Get complete system state
GET /api/pattern Get current pattern
POST /api/pattern Change pattern
GET /api/color-palette Get color palette
POST /api/color-palette Update palette/selection
GET /api/parameters Get all parameters
POST /api/parameters Update parameters
POST /api/tempo/reset Reset tempo detection

Error Handling

All endpoints return standard HTTP status codes:

  • 200 OK - Success
  • 400 Bad Request - Invalid request data
  • 500 Internal Server Error - Server error

Error Response Format:

{
  "status": "error",
  "message": "Pattern name required"
}

JavaScript Error Handling Example:

async function safeApiCall(url, options) {
  try {
    const response = await fetch(url, options);
    const data = await response.json();
    
    if (data.status === 'error') {
      console.error('API Error:', data.message);
      return null;
    }
    
    return data;
  } catch (error) {
    console.error('Network Error:', error);
    return null;
  }
}

// Usage
const result = await safeApiCall('http://10.42.0.1:8765/api/pattern', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({pattern: 'rainbow'})
});

Testing

Test All Endpoints

# Get state
curl http://10.42.0.1:8765/api/state | jq

# Change pattern
curl -X POST http://10.42.0.1:8765/api/pattern \
  -H "Content-Type: application/json" \
  -d '{"pattern": "rainbow"}'

# Change color
curl -X POST http://10.42.0.1:8765/api/color-palette \
  -H "Content-Type: application/json" \
  -d '{"selected_indices": [2, 5]}'

# Update brightness
curl -X POST http://10.42.0.1:8765/api/parameters \
  -H "Content-Type: application/json" \
  -d '{"brightness": 75}'

# Reset tempo
curl -X POST http://10.42.0.1:8765/api/tempo/reset

Notes

  • No WebSocket needed - Everything uses simple HTTP REST API
  • CORS: Not currently enabled. Host UI on same domain or add CORS middleware
  • Persistence: Color palette persists to lighting_config.json
  • Real-time: Changes take effect immediately (within one beat cycle)
  • Pattern Color: First selected color (index 0) is used for all patterns

Support Files

  • Full API details: COLOR_PALETTE_API.md
  • Migration notes: AIOHTTP_MIGRATION.md
  • Test results: PATTERN_COLOR_TEST_RESULTS.md