Add LED ring control per client and broadcast over REST and WebSocket.

Solid color mode fills all ring LEDs; master routes UART commands to slaves
via ESPNOW_LED_RING. goTool exposes POST /api/led-ring, WebSocket set_led_ring,
and a dashboard LED panel with master/slave/all targets.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-29 19:24:55 +02:00
co-authored by Cursor
parent 47c75110c9
commit eb67a46158
21 changed files with 759 additions and 133 deletions
+13 -25
View File
@@ -7,17 +7,12 @@ import (
"powerpod/gotool/pb"
)
const (
ledRingModeClear = 0
ledRingModeProgress = 1
ledRingModeDigit = 2
ledRingModeBlink = 3
ledRingModeFindMe = 4
)
func runLedRing(sp *serialPort, args []string) error {
fs := flag.NewFlagSet("led-ring", flag.ExitOnError)
mode := fs.String("mode", "progress", "clear, progress, digit, blink, or find-me")
mode := fs.String("mode", "progress", "clear, color, progress, digit, blink, or find-me")
clientID := fs.Uint("client", 0, "0=master ring, >0=slave via ESP-NOW")
allClients := fs.Bool("all", false, "broadcast to all slaves")
slavesOnly := fs.Bool("slaves-only", false, "with -all: do not change master ring")
progress := fs.Uint("progress", 0, "fill level 0100 (mode=progress)")
digit := fs.Uint("digit", 0, "digit 010 (mode=digit)")
r := fs.Uint("r", 0, "red 0255")
@@ -30,20 +25,9 @@ func runLedRing(sp *serialPort, args []string) error {
return err
}
var modeVal uint32
switch *mode {
case "clear":
modeVal = ledRingModeClear
case "progress":
modeVal = ledRingModeProgress
case "digit":
modeVal = ledRingModeDigit
case "blink":
modeVal = ledRingModeBlink
case "find-me", "find_me", "findme":
modeVal = ledRingModeFindMe
default:
return fmt.Errorf("unknown -mode %q (clear, progress, digit, blink, find-me)", *mode)
modeVal, err := ledRingModeFromString(*mode)
if err != nil {
return err
}
resp, err := sp.ledRingProgress(&pb.LedRingProgressRequest{
@@ -56,11 +40,15 @@ func runLedRing(sp *serialPort, args []string) error {
Intensity: uint32(*intensity),
BlinkMs: uint32(*blinkMs),
BlinkCount: uint32(*blinkCount),
ClientId: uint32(*clientID),
AllClients: *allClients,
SlavesOnly: *slavesOnly,
})
if err != nil {
return err
}
fmt.Printf("success=%v mode=%d progress=%d digit=%d\n",
resp.GetSuccess(), resp.GetMode(), resp.GetProgress(), resp.GetDigit())
fmt.Printf("success=%v mode=%d progress=%d digit=%d client_id=%d slaves_updated=%d\n",
resp.GetSuccess(), resp.GetMode(), resp.GetProgress(), resp.GetDigit(),
resp.GetClientId(), resp.GetSlavesUpdated())
return nil
}