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 }