evcc-io/core/loadpoint/config.go

113 lines
3.5 KiB
Go

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
}