Skip to main content

LED

WhatWhere
Route modulehal/routes/led.py
POST /led/solid — solid RGB colourdef set_led_solid(req: LEDSolidRequest)
POST /led/paint — per-pixel colours, or gradient stops with "gradient": truedef set_led_paint(req: LEDPaintRequest)
POST /led/effect — named animation (breathing, candle, rainbow, notification_flash, pulse, blink)def start_led_effect(req: LEDEffectRequest)
POST /led/status — status-cue overlay (booting, error, ota, …)def set_led_status(req: LEDStatusRequest)
POST /led/offdef turn_off_leds(...)
POST /led/effect/stop — stop the running animation, keep last framedef stop_led_effect()
POST /led/restore — hand strip back to user's saved state (after a transient overlay)def restore_led()
GET /led — current statedef get_led_state()
GET /led/color — ring state: brightest pixel + uniform flag, read across every pixeldef get_led_color()
Named status → colour/effect tableSTATUS_LED_PRESETS, hal/presets.py

Concrete calls, all copy-pasteable:

# 1. Solid colour — cold blue
curl -X POST http://127.0.0.1:5001/led/solid \
-H 'Content-Type: application/json' \
-d '{"color": [0, 135, 255]}'

# 2. Per-pixel paint — colour each of the N LEDs individually
# (useful for progress bars, level meters, tests)
curl -X POST http://127.0.0.1:5001/led/paint \
-H 'Content-Type: application/json' \
-d '{"colors": [[255,0,0],[0,255,0],[0,0,255],[255,255,0]]}'

# Or treat the colours as gradient stops and fade across the whole ring
curl -X POST http://127.0.0.1:5001/led/paint \
-H 'Content-Type: application/json' \
-d '{"colors": [[255,0,0],[0,0,255]], "gradient": true}'

# 3. Named animation with tint + speed
curl -X POST http://127.0.0.1:5001/led/effect \
-H 'Content-Type: application/json' \
-d '{"effect": "breathing", "color": [0, 255, 0], "speed": 1.0}'

# Other effects: candle, rainbow, notification_flash, pulse, blink
curl -X POST http://127.0.0.1:5001/led/effect \
-H 'Content-Type: application/json' \
-d '{"effect": "rainbow", "speed": 0.5}'

# 4. Status overlay — HAL picks the colour/effect from STATUS_LED_PRESETS
# ("booting", "wifi_connecting", "connectivity", "hardware", "agent_down", …)
# An unknown state returns 400 with the list of valid names.
curl -X POST http://127.0.0.1:5001/led/status \
-H 'Content-Type: application/json' \
-d '{"state": "booting"}'

# 5. Stop the animation but keep the last frame lit
curl -X POST http://127.0.0.1:5001/led/effect/stop

# 6. Restore the user's saved LED after a transient overlay
curl -X POST http://127.0.0.1:5001/led/restore

# 7. Off
curl -X POST http://127.0.0.1:5001/led/off

# 8. Read current state
curl http://127.0.0.1:5001/led
curl http://127.0.0.1:5001/led/color

Where the LED is actually driven from in this repo (good places to copy patterns from):

CallerFileWhat it does
Status-cue overlay servicesystem/statusled/service.go — func (s *Service) Set(state State)Priority-stacked overlay: booting / ota / error / connectivity / hal_down / agent_down / hardware / wifi_connecting. Highest priority wins; on Clear the strip is restored.
State constants (booting, wifi_connecting, agent_down, …)system/statusled/service.goThe exact set of states you can pass to /led/status.
Wi-Fi connecting blue-blinksystem/device/setup.go — s.statusLED.Set(statusled.StateWifiConnecting)Fires the blue blink while the STA join is happening during setup.
HAL health watchdogsystem/healthwatch/service.goSets StateHALDown / StateHardware when HAL or a driver stops responding.
Agent-runtime healthruntimes/openclaw/service_ws.go, runtimes/claudecode/client.go, runtimes/codex/client.go, runtimes/opencode/client.go, runtimes/picoclaw/client.go, runtimes/hermes/health.goEvery agent runtime sets StateAgentDown when its socket drops so the user sees the LED go red without having to check logs.
Ambient "breathing" idle behavioursystem/ambient/service.goDrives soft colour drift + breathing while there's no interaction. Uses /led/effect with transient=true so it doesn't clobber the user's saved state.
LLM skill / tool-call bridge ([HW:/led/…:{...}])system/server/agent/delivery/http/handler_hw.goWhen an OpenClaw / Hermes / Claude Code skill emits e.g. [HW:/led/solid:{"color":[255,0,0]}], this handler forwards it to HAL. This is how any skill turns the LED red without wiring plumbing itself.
Go HAL client (helpers you'd call from an os-server service)system/lib/hal/client.go — SetEffect, StopEffect, SetStatus, RestoreLED, GetColorFire-and-forget wrappers around the endpoints above. New Go services should use these instead of hand-rolling HTTP.

The full status preset catalogue (colours, effects, speeds) is STATUS_LED_PRESETS in hal/presets.py. How agents are told to drive the ring is in the led-control skill.