TRCC Linux includes a REST API for headless and remote control of LCD and LED devices. Start the server with:
trcc serve # localhost:9876
trcc serve --port 8080 # custom port
trcc serve --token mysecret # require X-API-Token header
trcc serve --tls # HTTPS with auto-generated self-signed cert
trcc serve --host 0.0.0.0 # listen on all interfaces (use with --token)Interactive docs available at http://localhost:9876/docs (Swagger UI) when the server is running.
When the qrcode package is installed (pip install qrcode), startup prints a terminal QR code containing {"host","port","token","tls"} as compact JSON — scan with TRCC Remote to connect instantly.
- Authentication
- Health
- Pairing
- Devices
- Display (LCD)
- Video Playback
- Preview / Live Stream
- Screencast
- Themes
- LED
- System Metrics
- i18n (Internationalization)
- Request/Response Models
When started with --token, all endpoints except /health require the X-API-Token header:
curl -H "X-API-Token: mysecret" http://localhost:9876/devicesWebSocket connections use a query parameter instead:
ws://localhost:9876/display/preview/stream?token=mysecret
Health check. Always accessible, no auth required.
Response:
{"status": "ok", "version": "9.3.2"}Pair a remote client (e.g. TRCC Remote app). Returns connection info and device status.
Response:
{"paired": true, "version": "9.3.2"}List currently known devices.
Response: DeviceResponse[]
[
{
"id": 0,
"name": "FROZEN VISION V2",
"vid": 34765,
"pid": 28891,
"protocol": "scsi",
"resolution": [320, 320],
"path": "/dev/sg2"
}
]Rescan USB for LCD/LED devices. Returns updated device list.
Response: DeviceResponse[]
Get details for a specific device by index.
Response: DeviceResponse
Errors: 404 if device index out of range.
Select a device for control. Initializes LCD or LED dispatcher, mounts static file directories, and restores last theme if available.
If the GUI daemon is running, the API routes commands through IPC automatically.
Response:
{"selected": "FROZEN VISION V2", "resolution": [320, 320]}Upload and send an image directly to the device LCD.
Content-Type: multipart/form-data
| Parameter | Type | Default | Description |
|---|---|---|---|
image |
file | required | Image file (PNG, JPEG, etc.) |
rotation |
int | 0 | Rotation in degrees (0, 90, 180, 270) |
brightness |
int | 100 | Brightness percentage (0-100) |
Limits: 10 MB max upload size. Validated by file extension.
Response:
{"sent": true, "resolution": [320, 320]}Errors: 400 invalid image, 404 device not found, 413 too large, 503 can't discover resolution.
All display endpoints require a device to be selected first (POST /devices/{id}/select). Returns 409 if no LCD device is active.
Send a solid color to the LCD.
Body:
{"hex": "ff0000"}Set display brightness. Persists to config.
Body:
{"level": 3}| Level | Brightness |
|---|---|
| 1 | 25% |
| 2 | 50% |
| 3 | 100% |
Set display rotation. Persists to config.
Body:
{"degrees": 90}Values: 0, 90, 180, 270.
Set split mode (Dynamic Island). Persists to config.
Body:
{"mode": 0}Values: 0 (off), 1-3 (Dynamic Island variants).
Run a color cycle test on the connected LCD. Cycles through 7 colors (red, green, blue, yellow, magenta, cyan, white) with 1-second pauses. Stops any running video/overlay first.
Response:
{"success": true, "message": "Test complete — cycled 7 colors on 320x320"}Reset device by sending a solid red frame. Useful for clearing stuck display state.
Upload and apply a mask overlay (PNG with transparency).
Content-Type: multipart/form-data
| Parameter | Type | Description |
|---|---|---|
image |
file | PNG mask image (max 10 MB) |
Render an overlay from a DC config file path.
| Parameter | Type | Default | Description |
|---|---|---|---|
dc_path |
string | required | Path to config1.dc file |
send |
bool | true | Send rendered frame to device |
Get current display state.
Response:
{
"connected": true,
"resolution": [320, 320],
"device_path": "/dev/sg2"
}Upload an image or video file to the server for use with POST /display/create-theme.
Request: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | yes | Image (.png, .jpg, .gif) or video (.mp4, .webm, .avi, .mkv, .zt) |
Response:
{"path": "/home/user/.trcc/uploads/abc123.png", "filename": "abc123.png", "size": 204800}Returns the server-side path to pass as background or mask in a subsequent POST /display/create-theme call.
Send a custom theme to the LCD device from uploaded files. Accepts a background image or video, optional mask PNG, and optional overlay configuration. Auto-detects animated backgrounds (video, animated GIF).
Request: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
background |
file | yes | Background image or video |
mask |
file | no | Mask PNG overlay |
overlay |
file | no | JSON overlay config ({"elements": [...]}) — takes precedence over metric |
metric |
string (repeatable) | no | Metric spec: key:x,y[:color[:size[:font[:style]]]] |
loop |
bool | no | Loop video (default: true) |
font_size |
int | no | Default font size (default: 14) |
color |
string | no | Default color hex (default: "ffffff") |
font |
string | no | Default font name (default: "Microsoft YaHei") |
font_style |
string | no | Default font style (default: "regular") |
temp_unit |
int | no | Temperature unit: 0=Celsius, 1=Fahrenheit (default: 0) |
time_format |
int | no | Time format: 0=24h, 1=12h (default: 0) |
date_format |
int | no | Date format index (default: 0) |
Response (static):
{"success": true, "animated": false, "resolution": "320x320"}Response (animated):
{"success": true, "animated": true, "loop": true, "resolution": "320x320"}Video playback runs in a background thread, pumping decoded frames to the LCD and updating the preview stream.
Stop background video playback.
Toggle pause on video playback. Returns 409 if no video is playing.
Response:
{"success": true, "paused": true}Get current video playback state.
Response:
{
"playing": true,
"paused": false,
"progress": 0.45,
"current_time": "0:13",
"total_time": "0:30",
"fps": 30.0,
"source": "/path/to/video.mp4",
"loop": true
}Return the current LCD frame as a PNG image. Useful for single-shot screenshots.
Response: image/png binary
Errors: 503 if no image available.
WebSocket live JPEG stream of the LCD. Frames are sent as binary messages at a configurable framerate.
When the GUI daemon is running, frames come via IPC. In standalone mode, frames come from the on_frame_sent capture.
Auth: ?token= query parameter (if token auth is configured).
Client control messages (JSON text frames):
| Message | Default | Range | Description |
|---|---|---|---|
{"fps": N} |
10 | 1-30 | Target framerate |
{"quality": N} |
85 | 10-100 | JPEG quality |
{"pause": bool} |
false | — | Pause/resume stream |
Example (JavaScript):
const ws = new WebSocket('ws://localhost:9876/display/preview/stream');
ws.binaryType = 'arraybuffer';
ws.onmessage = (e) => {
const blob = new Blob([e.data], {type: 'image/jpeg'});
document.getElementById('preview').src = URL.createObjectURL(blob);
};
// Adjust quality
ws.send(JSON.stringify({quality: 70, fps: 15}));Stream screen capture to the LCD device. Auto-detects backend: ffmpeg x11grab on X11/XWayland, PipeWire on pure Wayland. Mutually exclusive with video playback, overlay loops, and keepalive loops.
Start screen capture streaming.
Body:
{"x": 0, "y": 0, "w": 0, "h": 0, "fps": 10}| Parameter | Type | Default | Description |
|---|---|---|---|
x |
int | 0 | Capture region X offset |
y |
int | 0 | Capture region Y offset |
w |
int | 0 | Capture width (0 = full screen) |
h |
int | 0 | Capture height (0 = full screen) |
fps |
int | 10 | Target frame rate |
Response: {"success": true, "backend": "x11"} or {"success": true, "backend": "pipewire"}
Stop screen capture streaming.
Response: {"success": true, "message": "Screencast stopped"}
Check screencast state.
Response:
{"running": true, "backend": "x11", "fps": 10, "region": {"x": 0, "y": 0, "w": 0, "h": 0}, "frames": 142}Initialize the theme system for a given resolution. Downloads theme/mask/web archives if not cached locally.
Body:
{"resolution": "320x320"}Response:
{"initialized": true, "resolution": "320x320"}List available local themes for a given resolution.
| Parameter | Type | Default | Description |
|---|---|---|---|
resolution |
string | "320x320" | Resolution filter (e.g. "480x480") |
Response: ThemeResponse[]
[
{
"name": "CyberPunk",
"category": "Tech",
"is_animated": false,
"has_config": true,
"preview_url": "/static/themes/CyberPunk/Theme.png"
}
]Load a theme by name and send to device. Handles static images, animated themes (video/Theme.zt), and overlay configs (config1.dc) automatically.
Body:
{"name": "CyberPunk", "resolution": "320x320"}resolution is optional — defaults to the connected device's resolution.
Save current device display as a named theme. Saves to ~/.trcc-user/ so custom themes survive uninstall and data re-downloads. Routes through LCDDevice.save() → DisplayService.save_theme().
Body:
{"name": "MyTheme"}Requires: An image loaded on the device (via theme load, send, etc.). Returns 409 if no image.
Import a .tr theme archive. Max 50 MB.
Content-Type: multipart/form-data
| Parameter | Type | Description |
|---|---|---|
file |
file | .tr theme archive |
Export a theme as a downloadable .tr archive.
| Parameter | Type | Default | Description |
|---|---|---|---|
theme_name |
string | required | Theme name (exact or partial match) |
resolution |
string | "320x320" | Resolution filter |
Response: Binary .tr file download (application/octet-stream).
Errors: 400 invalid name, 404 theme not found.
List available cloud theme previews for a given resolution.
| Parameter | Type | Default | Description |
|---|---|---|---|
resolution |
string | "320x320" | Resolution filter |
Response: WebThemeResponse[]
[
{
"id": "a001",
"category": "a",
"preview_url": "/static/web/a001.png",
"has_video": true,
"download_url": "/themes/web/a001/download"
}
]Download a cloud theme to local cache. Optionally starts video playback on the device.
| Parameter | Type | Default | Description |
|---|---|---|---|
resolution |
string | device res | Target resolution |
send |
bool | false | Start playback after download |
Response:
{
"id": "a001",
"cached_path": "/home/user/.trcc/data/web/320320/a001.mp4",
"resolution": "320x320",
"already_cached": false
}List available mask overlays for a given resolution.
| Parameter | Type | Default | Description |
|---|---|---|---|
resolution |
string | "320x320" | Resolution filter |
Response: MaskResponse[]
[
{"name": "mask01", "preview_url": "/static/masks/mask01/Theme.png"}
]All LED endpoints require an LED device to be selected first. Returns 409 if no LED device is active.
Set LED static color.
Body:
{"hex": "00ff88"}Set LED effect mode.
Body:
{"mode": "breathing"}Values: static, breathing, colorful, rainbow (device-dependent).
Set LED brightness (0-100).
Body:
{"level": 80}Turn all LEDs off.
Set CPU/GPU sensor source for temperature/load-linked modes.
Body:
{"source": "cpu"}Set color for a specific LED zone.
Body: {"hex": "ff0000"}
Set effect mode for a specific zone.
Body: {"mode": "breathing"}
Set brightness for a specific zone (0-100).
Body: {"level": 50}
Toggle a specific LED zone on/off.
Body: {"on": true}
Enable/disable zone sync (circulate mode).
Body:
{"enabled": true, "interval": 500}interval is optional (milliseconds between zone rotations).
Toggle a specific LED segment on/off.
Body: {"on": true}
Set segment display clock format.
Body: {"is_24h": true}
Set segment display temperature unit.
Body: {"unit": "C"}
Values: C (Celsius), F (Fahrenheit).
Run LED preview with real system metrics. No device needed — useful for testing without hardware.
| Parameter | Type | Default | Description |
|---|---|---|---|
mode |
string | "static" | LED mode: static, breathing, colorful, rainbow |
segments |
int | 64 | Number of LED segments to simulate |
Response:
{
"success": true,
"mode": "static",
"segments": 10,
"colors": [
{"r": 255, "g": 0, "b": 0},
{"r": 255, "g": 0, "b": 0}
]
}Get current LED state.
Response:
{"connected": true, "status": "..."}All system metrics as JSON — CPU, GPU, memory, disk, network, fans.
Response: Flat dict with prefixed keys:
{
"cpu_temp": 52,
"cpu_usage": 12,
"cpu_freq": 4200,
"gpu_temp": 45,
"gpu_usage": 8,
"mem_used": 8192,
"mem_total": 32768,
"disk_read": 15,
"disk_write": 3,
"net_up": 120,
"net_down": 450,
"fan_speed": 850
}Filtered metrics by category.
| Category | Aliases | Prefix |
|---|---|---|
cpu |
— | cpu_ |
gpu |
— | gpu_ |
mem |
memory |
mem_ |
disk |
— | disk_ |
net |
network |
net_ |
fan |
— | fan_ |
Generate diagnostic report (same as trcc report CLI command).
Response:
{"report": "TRCC Linux v9.3.2\n..."}Run software performance benchmarks (rendering, encoding, compositing).
Response:
{
"benchmarks": [
{"name": "encode_rgb565_320x320", "mean_ms": 0.8, "iterations": 100}
]
}Run hardware USB I/O benchmarks. Requires a connected LCD device. Pauses any running GUI daemon for exclusive access.
Response:
{
"benchmarks": [
{"name": "send_frame_320x320", "mean_ms": 42.1, "iterations": 50}
]
}List all available languages with ISO codes and native names.
Response:
[
{"code": "en", "name": "English"},
{"code": "de", "name": "Deutsch"},
{"code": "ja", "name": "日本語"}
]Get the current application language.
Response:
{"code": "en", "name": "English"}Set the application language by ISO 639-1 code. Persists to config.
Response:
{"code": "de", "name": "Deutsch"}Errors: 400 if language code is not supported.
All request bodies are JSON. All responses are JSON unless noted (preview endpoints return binary).
| Status | Meaning |
|---|---|
| 400 | Invalid request (bad hex color, unknown category, corrupt image) |
| 401 | Invalid or missing API token |
| 404 | Device or theme not found |
| 409 | No device selected — call POST /devices/{id}/select first |
| 413 | Upload exceeds size limit |
| 500 | Device send failed |
| 503 | Device resolution unknown, or no frame available for preview |
After device selection, theme and cloud directories are mounted as static files:
| Mount | Directory | Content |
|---|---|---|
/static/themes/ |
Local theme packs | Theme images, videos, configs |
/static/web/ |
Cloud theme previews | PNG previews, MP4 videos |
/static/masks/ |
Cloud mask overlays | Mask PNGs, overlay configs |
These mounts are resolution-specific and update when a different device is selected.
The API detects whether the GUI daemon is running:
- Daemon mode: Commands route through IPC (Unix socket) to the GUI. Preview stream fetches frames from the daemon. Both GUI and API control the same device simultaneously.
- Standalone mode: API manages the device directly. No GUI required. Preview stream uses the
on_frame_sentcallback to capture outgoing frames.
The mode is selected automatically — no configuration needed.
curl -X POST http://localhost:9876/devices/detect
curl -X POST http://localhost:9876/devices/0/select
curl -X POST -F "image=@photo.png" http://localhost:9876/devices/0/sendcurl -X POST http://localhost:9876/themes/load \
-H "Content-Type: application/json" \
-d '{"name": "CyberPunk"}'curl -X POST http://localhost:9876/led/color \
-H "Content-Type: application/json" \
-d '{"hex": "ff6600"}'# All metrics
curl http://localhost:9876/system/metrics
# CPU only
curl http://localhost:9876/system/metrics/cpu
# Watch metrics (updates every 2s)
watch -n 2 'curl -s http://localhost:9876/system/metrics/cpu | python3 -m json.tool'<img id="preview" />
<script>
const ws = new WebSocket('ws://localhost:9876/display/preview/stream');
ws.binaryType = 'arraybuffer';
ws.onmessage = (e) => {
const blob = new Blob([e.data], {type: 'image/jpeg'});
document.getElementById('preview').src = URL.createObjectURL(blob);
};
</script>