package loadpoint import ( "time" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/util" ) type StaticConfig struct { // static config Charger string `json:"charger,omitempty"` Meter string `json:"meter,omitempty"` Circuit string `json:"circuit,omitempty"` Vehicle string `json:"vehicle,omitempty"` } type DynamicConfig struct { // dynamic config Title string `json:"title"` DefaultMode string `json:"defaultMode"` Priority int `json:"priority"` PhasesConfigured int `json:"phasesConfigured"` MinCurrent float64 `json:"minCurrent"` MaxCurrent float64 `json:"maxCurrent"` SmartCostLimit *float64 `json:"smartCostLimit"` SmartFeedInPriorityLimit *float64 `json:"smartFeedInPriorityLimit"` PlanEnergy float64 `json:"planEnergy"` PlanTime time.Time `json:"planTime"` PlanPrecondition_ int64 `json:"planPrecondition" mapstructure:"planPrecondition"` // TODO deprecated, keep for compatibility BatteryBoostLimit int `json:"batteryBoostLimit"` LimitEnergy float64 `json:"limitEnergy"` LimitSoc int `json:"limitSoc"` PlanStrategy api.PlanStrategy `json:"planStrategy"` Thresholds ThresholdsConfig `json:"thresholds"` Soc SocConfig `json:"soc"` UI UIConfig `json:"ui"` } // UIConfig holds display-only settings. Not used in control logic. type UIConfig struct { MinTemp float64 `json:"minTemp"` MaxTemp float64 `json:"maxTemp"` } func SplitConfig(payload map[string]any) (DynamicConfig, map[string]any, error) { // split static and dynamic config via mapstructure var cc struct { DynamicConfig `mapstructure:",squash"` Other map[string]any `mapstructure:",remain"` } cc.BatteryBoostLimit = 100 // default: disabled if err := util.DecodeOther(payload, &cc); err != nil { return DynamicConfig{}, nil, err } // TODO: proper handling of id/name delete(cc.Other, "id") delete(cc.Other, "name") return cc.DynamicConfig, cc.Other, nil } func (payload DynamicConfig) Apply(lp API) error { lp.SetTitle(payload.Title) lp.SetPriority(payload.Priority) lp.SetSmartCostLimit(payload.SmartCostLimit) lp.SetSmartFeedInPriorityLimit(payload.SmartFeedInPriorityLimit) lp.SetThresholds(payload.Thresholds) lp.SetPlanEnergy(payload.PlanTime, payload.PlanEnergy) lp.SetPlanStrategy(payload.PlanStrategy) lp.SetBatteryBoostLimit(payload.BatteryBoostLimit) lp.SetLimitEnergy(payload.LimitEnergy) lp.SetLimitSoc(payload.LimitSoc) // TODO mode warning lp.SetSocConfig(payload.Soc) lp.SetUI(payload.UI) mode, err := api.ChargeModeString(payload.DefaultMode) if err == nil { lp.SetDefaultMode(mode) } if err == nil { err = lp.SetPhasesConfigured(payload.PhasesConfigured) } if err == nil { // In case both min and max current are set, we need to set them in the correct order to avoid validation errors switch { case payload.MinCurrent != 0 && payload.MaxCurrent != 0: if payload.MaxCurrent > lp.GetMaxCurrent() { if err = lp.SetMaxCurrent(payload.MaxCurrent); err == nil { err = lp.SetMinCurrent(payload.MinCurrent) } } else { if err = lp.SetMinCurrent(payload.MinCurrent); err == nil { err = lp.SetMaxCurrent(payload.MaxCurrent) } } case payload.MinCurrent != 0: err = lp.SetMinCurrent(payload.MinCurrent) case payload.MaxCurrent != 0: err = lp.SetMaxCurrent(payload.MaxCurrent) } } return err }