LED
| What | Where |
|---|---|
| Route module | hal/routes/led.py |
POST /led/solid — solid RGB colour | def set_led_solid(req: LEDSolidRequest) |
POST /led/paint — per-pixel colours, or gradient stops with "gradient": true | def 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/off | def turn_off_leds(...) |
POST /led/effect/stop — stop the running animation, keep last frame | def stop_led_effect() |
POST /led/restore — hand strip back to user's saved state (after a transient overlay) | def restore_led() |
GET /led — current state | def get_led_state() |
GET /led/color — ring state: brightest pixel + uniform flag, read across every pixel | def get_led_color() |
| Named status → colour/effect table | STATUS_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):
| Caller | File | What it does |
|---|---|---|
| Status-cue overlay service | system/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.go | The exact set of states you can pass to /led/status. |
| Wi-Fi connecting blue-blink | system/device/setup.go — s.statusLED.Set(statusled.StateWifiConnecting) | Fires the blue blink while the STA join is happening during setup. |
| HAL health watchdog | system/healthwatch/service.go | Sets StateHALDown / StateHardware when HAL or a driver stops responding. |
| Agent-runtime health | runtimes/openclaw/service_ws.go, runtimes/claudecode/client.go, runtimes/codex/client.go, runtimes/opencode/client.go, runtimes/picoclaw/client.go, runtimes/hermes/health.go | Every agent runtime sets StateAgentDown when its socket drops so the user sees the LED go red without having to check logs. |
| Ambient "breathing" idle behaviour | system/ambient/service.go | Drives 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.go | When 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, GetColor | Fire-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.