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:
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"powerpod/gotool/pb"
|
||||
)
|
||||
|
||||
const (
|
||||
ledRingModeClear = 0
|
||||
ledRingModeProgress = 1
|
||||
ledRingModeDigit = 2
|
||||
ledRingModeBlink = 3
|
||||
ledRingModeFindMe = 4
|
||||
ledRingModeColor = 5
|
||||
)
|
||||
|
||||
type ledRingAPIRequest struct {
|
||||
Mode string `json:"mode"`
|
||||
ClientID uint32 `json:"client_id"`
|
||||
AllClients bool `json:"all_clients"`
|
||||
SlavesOnly bool `json:"slaves_only"`
|
||||
Progress uint32 `json:"progress"`
|
||||
Digit uint32 `json:"digit"`
|
||||
R uint32 `json:"r"`
|
||||
G uint32 `json:"g"`
|
||||
B uint32 `json:"b"`
|
||||
Intensity uint32 `json:"intensity"`
|
||||
BlinkMs uint32 `json:"blink_ms"`
|
||||
BlinkCount uint32 `json:"blink_count"`
|
||||
}
|
||||
|
||||
type ledRingAPIResponse struct {
|
||||
Type string `json:"type,omitempty"` // led_ring_status (WebSocket)
|
||||
Success bool `json:"success"`
|
||||
Mode uint32 `json:"mode,omitempty"`
|
||||
Progress uint32 `json:"progress,omitempty"`
|
||||
Digit uint32 `json:"digit,omitempty"`
|
||||
ClientID uint32 `json:"client_id,omitempty"`
|
||||
SlavesUpdated uint32 `json:"slaves_updated,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func ledRingModeFromString(s string) (uint32, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(s)) {
|
||||
case "clear", "":
|
||||
return ledRingModeClear, nil
|
||||
case "color", "solid", "fill":
|
||||
return ledRingModeColor, nil
|
||||
case "progress":
|
||||
return ledRingModeProgress, nil
|
||||
case "digit":
|
||||
return ledRingModeDigit, nil
|
||||
case "blink":
|
||||
return ledRingModeBlink, nil
|
||||
case "find-me", "find_me", "findme":
|
||||
return ledRingModeFindMe, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown mode %q (clear, color, progress, digit, blink, find-me)", s)
|
||||
}
|
||||
}
|
||||
|
||||
func ledRingPBFromAPI(in ledRingAPIRequest) (*pb.LedRingProgressRequest, error) {
|
||||
mode, err := ledRingModeFromString(in.Mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.LedRingProgressRequest{
|
||||
Mode: mode,
|
||||
Progress: in.Progress,
|
||||
Digit: in.Digit,
|
||||
R: in.R,
|
||||
G: in.G,
|
||||
B: in.B,
|
||||
Intensity: in.Intensity,
|
||||
BlinkMs: in.BlinkMs,
|
||||
BlinkCount: in.BlinkCount,
|
||||
ClientId: in.ClientID,
|
||||
AllClients: in.AllClients,
|
||||
SlavesOnly: in.SlavesOnly,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func applyLedRing(link *managedSerial, in ledRingAPIRequest) ledRingAPIResponse {
|
||||
req, err := ledRingPBFromAPI(in)
|
||||
if err != nil {
|
||||
return ledRingAPIResponse{Error: err.Error()}
|
||||
}
|
||||
resp, err := link.LedRing(req)
|
||||
if err != nil {
|
||||
return ledRingAPIResponse{Error: err.Error()}
|
||||
}
|
||||
out := ledRingAPIResponse{
|
||||
Success: resp.GetSuccess(),
|
||||
Mode: resp.GetMode(),
|
||||
Progress: resp.GetProgress(),
|
||||
Digit: resp.GetDigit(),
|
||||
ClientID: resp.GetClientId(),
|
||||
SlavesUpdated: resp.GetSlavesUpdated(),
|
||||
}
|
||||
if !out.Success && out.Error == "" {
|
||||
out.Error = "led ring command rejected"
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user