Skip to content

Control Server

Griffen Fargo edited this page May 16, 2026 · 9 revisions

Control Server

The custom control server runs on the Pi at port 5000 and provides a richer interface on top of QLC+'s native web UI.

Overview

Access it at http://lights.local:5000

The control server provides:

  • AI Chat — Natural language commands ("make it warmer", "party mode", "fade to black over 5 seconds")
  • Virtual Console — Per-fixture channel sliders with correct labels from .qxf definitions
  • Scene Management — Activate existing scenes, save AI-generated ones permanently
  • Fixture Groups — Organize fixtures into named zones, target commands to specific groups
  • Health Dashboard — Live status of QLC+, WebSocket connection, AI provider, and workspace

Installation

./lightsctl.sh control-install

This creates a Python venv on the Pi, installs dependencies, and sets up lighting-control.service.

Architecture

The server maintains a single persistent WebSocket to QLC+ on a dedicated background thread. All HTTP requests dispatch channel commands through this one connection. This avoids the CLOSE_WAIT socket leak that occurs when each request opens its own short-lived connection (QLC+ 4.14.x has a hard limit of ~50 concurrent WebSocket clients).

See CONTROL_SERVER_ARCHITECTURE.md for the full technical deep-dive.

API Endpoints

Method Path Purpose
POST /api/command AI natural-language command
POST /api/action Structured action dispatch (used by MCP-Server, bypasses AI interpreter)
POST /api/batch Execute an ordered list of actions in one request
POST /api/blackout Instant kill-all — zeroes every channel on targeted fixtures
GET /api/status Multi-service health JSON
GET /api/scenes List workspace scenes
GET /api/scenes/<id> Describe a saved scene (per-fixture channel breakdown)
POST /api/scenes/<id>/activate Apply a scene live
POST /api/scenes/save Save scene XML to workspace permanently
POST /api/scenes/snapshot Capture current state as a new scene
POST /api/scenes/<id>/duplicate Clone a saved scene under a new name
PATCH /api/scenes/<id> Rename a scene (and/or move its Path folder)
DELETE /api/scenes/<id> Delete a saved scene
GET /api/fixtures List fixtures with channel_info
GET /api/fixture_channels/<id> Per-fixture channel breakdown
POST /api/fixtures/<id>/identify Flash a fixture so you can locate it physically
GET /api/groups List fixture groups
POST /api/groups Create a new group
PATCH /api/groups/<name> Rename / re-describe / replace fixture list
DELETE /api/groups/<name> Delete a group
POST /api/groups/<name>/fixtures Append fixtures to a group
DELETE /api/groups/<name>/fixtures Remove fixtures from a group
POST /api/channel Set a single channel value
GET /api/channel_values Live DMX values from QLC+
POST /api/diagnostics/test_dmx R → G → B → restore sweep across fixtures
GET /api/diagnostics/logs/<service> Tail systemd journal for an allowlisted service
GET /api/diagnostics/system Pi-level health JSON
GET /api/chases List chases in the workspace
GET /api/chases/<id> Describe a chase (steps, scene names, timing)
POST /api/chases Create a new chase
DELETE /api/chases/<id> Remove a chase
POST /api/chases/<id>/start Start chase playback
POST /api/chases/<id>/stop Stop chase playback
GET /api/cue_lists List saved cue lists (with runtime status)
GET /api/cue_lists/active Currently-playing cue lists, with elapsed time
GET /api/cue_lists/<id> Describe a cue list
POST /api/cue_lists Create a new cue list
PATCH /api/cue_lists/<id> Rename / re-describe / replace cues array
DELETE /api/cue_lists/<id> Remove (stops playback first if running)
POST /api/cue_lists/<id>/go GO — start playback
POST /api/cue_lists/<id>/stop Halt running cue list

Saving Scenes

The save feature lets you persist any AI-generated scene (or the current live state) into the QLC+ workspace permanently:

# Via API
curl -X POST http://lights.local:5000/api/scenes/save \
  -H "Content-Type: application/json" \
  -d '{"name": "My Scene", "snapshot": true}'

# Or with scene XML from a previous generate
curl -X POST http://lights.local:5000/api/scenes/save \
  -H "Content-Type: application/json" \
  -d '{"name": "Warm Sunset", "scene_xml": "<Function Type=\"Scene\" ...>...</Function>"}'

Saved scenes appear in the Scenes tab immediately and persist through reboots.

Fixture Definitions

The server reads QLC+ .qxf fixture definition files to resolve each channel's semantic role. This means:

  • Channel sliders show correct labels (e.g. "Warm White" not "Ch 2")
  • The AI knows which channels are color vs strobe vs configuration
  • Color commands drive the right channels per fixture type

The parser checks /usr/share/qlcplus/fixtures/ (system) and ~/.qlcplus/fixtures/ (user overrides).

LLM Agent Access

To expose the same control surface to MCP-capable LLM agents (Claude Desktop, ChatGPT, Cursor, custom clients), install the sibling MCP server — see MCP-Server. It runs at port 5001 and calls back into this server's /api/action endpoint.

Management Commands

./lightsctl.sh control-status    # Service status
./lightsctl.sh control-logs      # Recent logs
./lightsctl.sh control-restart   # Restart the server
./lightsctl.sh control-uninstall # Remove the server
./lightsctl.sh env-sync          # Push .env to Pi and restart

Clone this wiki locally