diff --git a/api/api.go b/api/api.go index a7f749ff8..eb39b9be4 100644 --- a/api/api.go +++ b/api/api.go @@ -9,6 +9,7 @@ type ChargeMode string // Charge modes const ( + ModeEmpty ChargeMode = "" ModeOff ChargeMode = "off" ModeNow ChargeMode = "now" ModeMinPV ChargeMode = "minpv" diff --git a/api/impl.go b/api/impl.go index e2157fcf8..3cc5ad6c0 100644 --- a/api/impl.go +++ b/api/impl.go @@ -1,19 +1,38 @@ package api -import "strings" +import ( + "encoding" + "fmt" + "strings" +) // ChargeModeString converts string to ChargeMode -func ChargeModeString(mode string) ChargeMode { +func ChargeModeString(mode string) (ChargeMode, error) { switch strings.ToLower(mode) { + case string(ModeEmpty): + return ModeEmpty, nil // undefined case string(ModeNow): - return ModeNow + return ModeNow, nil case string(ModeMinPV): - return ModeMinPV + return ModeMinPV, nil case string(ModePV): - return ModePV + return ModePV, nil case string(ModeOff): - return ModeOff + return ModeOff, nil default: - return "" + return "", fmt.Errorf("invalid value: %s", mode) } } + +var _ encoding.TextUnmarshaler = (*ChargeMode)(nil) + +func (c *ChargeMode) UnmarshalText(text []byte) error { + casted, err := ChargeModeString(string(text)) + if err != nil { + return err + } + + *c = casted + + return nil +} diff --git a/core/loadpoint.go b/core/loadpoint.go index f76a9dcce..6f4e705c6 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -142,10 +142,6 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin return nil, err } - // set sane defaults - lp.Mode = api.ChargeModeString(string(lp.Mode)) - lp.OnDisconnect.Mode = api.ChargeModeString(string(lp.OnDisconnect.Mode)) - // set vehicle polling mode switch lp.SoC.Poll.Mode = strings.ToLower(lp.SoC.Poll.Mode); lp.SoC.Poll.Mode { case pollCharging: @@ -401,7 +397,7 @@ func (lp *LoadPoint) evChargeCurrentWrappedMeterHandler(current float64) { // applyAction executes the action func (lp *LoadPoint) applyAction(action ActionConfig) { - if action.Mode != "" && lp.GetMode() != api.ModeOff { + if action.Mode != "" && lp.GetMode() != api.ModeEmpty { lp.SetMode(action.Mode) } if action.TargetSoC != 0 { diff --git a/server/http.go b/server/http.go index de1c9bea5..a6559be63 100644 --- a/server/http.go +++ b/server/http.go @@ -167,11 +167,12 @@ func ChargeModeHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - modeS, ok := vars["mode"] - mode := api.ChargeModeString(modeS) - if mode == "" || string(mode) != modeS || !ok { + modeS := vars["mode"] + + mode, err := api.ChargeModeString(modeS) + if err != nil { w.WriteHeader(http.StatusBadRequest) - jsonResponse(w, r, errorJSON{Error: "invalid mode"}) + jsonResponse(w, r, errorJSON{Error: err.Error()}) return } @@ -195,14 +196,14 @@ func TargetSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - socS, ok := vars["soc"] + socS := vars["soc"] soc, err := strconv.ParseInt(socS, 10, 32) - if ok && err == nil { + if err == nil { err = loadpoint.SetTargetSoC(int(soc)) } - if !ok || err != nil { + if err != nil { w.WriteHeader(http.StatusBadRequest) jsonResponse(w, r, errorJSON{Error: err.Error()}) return @@ -226,14 +227,14 @@ func MinSoCHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - socS, ok := vars["soc"] + socS := vars["soc"] soc, err := strconv.ParseInt(socS, 10, 32) - if ok && err == nil { + if err == nil { err = loadpoint.SetMinSoC(int(soc)) } - if !ok || err != nil { + if err != nil { w.WriteHeader(http.StatusBadRequest) jsonResponse(w, r, errorJSON{Error: err.Error()}) return @@ -257,14 +258,14 @@ func PhasesHandler(loadpoint core.LoadPointAPI) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - phasesS, ok := vars["phases"] + phasesS := vars["phases"] phases, err := strconv.ParseInt(phasesS, 10, 32) - if ok && err == nil { + if err == nil { err = loadpoint.SetPhases(int(phases)) } - if !ok || err != nil { + if err != nil { w.WriteHeader(http.StatusBadRequest) jsonResponse(w, r, errorJSON{Error: err.Error()}) return @@ -418,7 +419,7 @@ func NewHTTPd(url string, site core.SiteAPI, hub *SocketHub, cache *util.Cache) "getphases": {[]string{"GET"}, "/phases", CurrentPhasesHandler(lp)}, "setphases": {[]string{"POST", "OPTIONS"}, "/phases/{phases:[0-9]+}", PhasesHandler(lp)}, "settargetcharge": {[]string{"POST", "OPTIONS"}, "/targetcharge/{soc:[0-9]+}/{time:[0-9TZ:-]+}", TargetChargeHandler(lp)}, - "remotedemand": {[]string{"POST", "OPTIONS"}, "/remotedemand/{demand:[a-z]+}/{source}", RemoteDemandHandler(lp)}, + "remotedemand": {[]string{"POST", "OPTIONS"}, "/remotedemand/{demand:[a-z]+}/{source::[0-9a-zA-Z_-]+}", RemoteDemandHandler(lp)}, } for _, r := range routes { diff --git a/util/decoder.go b/util/decoder.go index 259b86741..7155c45aa 100644 --- a/util/decoder.go +++ b/util/decoder.go @@ -10,7 +10,10 @@ func DecodeOther(other interface{}, cc interface{}) error { Result: cc, ErrorUnused: true, WeaklyTypedInput: true, - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.TextUnmarshallerHookFunc(), + ), } decoder, err := mapstructure.NewDecoder(decoderConfig)