Per-pod NVS enable (default off) via UART/ESP-NOW/dashboard, and skip UV latch when LiPo ADC reads ~0 mV so USB/external supply still boots. Co-authored-by: Cursor <cursoragent@cursor.com>
141 lines
3.6 KiB
Go
141 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"powerpod/gotool/pb"
|
|
)
|
|
|
|
type batteryUvAPIRequest struct {
|
|
Write bool `json:"write"`
|
|
Enabled bool `json:"enabled"`
|
|
ClientID uint32 `json:"client_id"`
|
|
AllClients bool `json:"all_clients"`
|
|
// SlavesOnly: with all_clients, push to ESP-NOW slaves only (master NVS unchanged).
|
|
SlavesOnly bool `json:"slaves_only"`
|
|
}
|
|
|
|
type batteryUvAPIResponse struct {
|
|
ClientID uint32 `json:"client_id"`
|
|
Success bool `json:"success"`
|
|
Enabled bool `json:"enabled"`
|
|
Latched bool `json:"latched"`
|
|
SlavesUpdated uint32 `json:"slaves_updated"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
func mountBatteryUvAPI(mux *http.ServeMux, link *managedSerial) {
|
|
mux.HandleFunc("/api/battery-uv", func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
serveBatteryUvGet(w, r, link)
|
|
case http.MethodPost:
|
|
serveBatteryUvPost(w, r, link)
|
|
default:
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
}
|
|
|
|
func serveBatteryUvGet(w http.ResponseWriter, r *http.Request, link *managedSerial) {
|
|
clientID, err := parseUintQuery(r, "client_id", 0)
|
|
if err != nil {
|
|
writeJSON(w, http.StatusBadRequest, batteryUvAPIResponse{Error: err.Error()})
|
|
return
|
|
}
|
|
resp, err := link.BatteryUv(&pb.BatteryUvRequest{
|
|
Write: false,
|
|
ClientId: clientID,
|
|
})
|
|
if err != nil {
|
|
writeJSON(w, http.StatusServiceUnavailable, batteryUvAPIResponse{
|
|
ClientID: clientID,
|
|
Error: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, batteryUvAPIResponse{
|
|
ClientID: resp.GetClientId(),
|
|
Success: resp.GetSuccess(),
|
|
Enabled: resp.GetEnabled(),
|
|
Latched: resp.GetLatched(),
|
|
SlavesUpdated: resp.GetSlavesUpdated(),
|
|
})
|
|
}
|
|
|
|
func serveBatteryUvPost(w http.ResponseWriter, r *http.Request, link *managedSerial) {
|
|
var body batteryUvAPIRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, batteryUvAPIResponse{Error: "invalid JSON"})
|
|
return
|
|
}
|
|
if body.AllClients && body.SlavesOnly {
|
|
updated, err := applyBatteryUvToSlaves(link, body.Enabled)
|
|
if err != nil {
|
|
writeJSON(w, http.StatusServiceUnavailable, batteryUvAPIResponse{
|
|
Enabled: body.Enabled,
|
|
Error: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, batteryUvAPIResponse{
|
|
Success: updated > 0,
|
|
Enabled: body.Enabled,
|
|
SlavesUpdated: updated,
|
|
})
|
|
return
|
|
}
|
|
|
|
resp, err := link.BatteryUv(&pb.BatteryUvRequest{
|
|
Write: true,
|
|
Enabled: body.Enabled,
|
|
ClientId: body.ClientID,
|
|
AllClients: body.AllClients,
|
|
})
|
|
if err != nil {
|
|
writeJSON(w, http.StatusServiceUnavailable, batteryUvAPIResponse{
|
|
ClientID: body.ClientID,
|
|
Enabled: body.Enabled,
|
|
Error: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, batteryUvAPIResponse{
|
|
ClientID: resp.GetClientId(),
|
|
Success: resp.GetSuccess(),
|
|
Enabled: resp.GetEnabled(),
|
|
Latched: resp.GetLatched(),
|
|
SlavesUpdated: resp.GetSlavesUpdated(),
|
|
})
|
|
}
|
|
|
|
func applyBatteryUvToSlaves(link *managedSerial, enabled bool) (uint32, error) {
|
|
clients, err := link.listClients()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var updated uint32
|
|
for _, c := range clients {
|
|
resp, err := link.BatteryUv(&pb.BatteryUvRequest{
|
|
Write: true,
|
|
Enabled: enabled,
|
|
ClientId: c.GetId(),
|
|
})
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if resp.GetSuccess() {
|
|
updated++
|
|
}
|
|
}
|
|
if len(clients) == 0 {
|
|
return 0, fmt.Errorf("no slaves registered")
|
|
}
|
|
if updated == 0 {
|
|
return 0, fmt.Errorf("UV protection not applied to any slave")
|
|
}
|
|
return updated, nil
|
|
}
|