From 4b3eef6b5ad0a2fa6e5f3be484395a1aec2c7545 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 6 Feb 2025 18:35:51 +0100 Subject: [PATCH 01/17] Loadpoint: cleanup phase configuration and drop deprecations --- core/keys/loadpoint.go | 6 ++- core/loadpoint.go | 58 +++---------------------- core/loadpoint/api.go | 8 ++-- core/loadpoint/config.go | 24 +++++----- core/loadpoint/mock.go | 42 ++++++++++++------ core/loadpoint_api.go | 13 ++++-- core/loadpoint_phases.go | 7 ++- server/http_config_loadpoint_handler.go | 26 +++++------ 8 files changed, 83 insertions(+), 101 deletions(-) diff --git a/core/keys/loadpoint.go b/core/keys/loadpoint.go index 543f914b91..9d74a90a60 100644 --- a/core/keys/loadpoint.go +++ b/core/keys/loadpoint.go @@ -19,10 +19,12 @@ const ( DisableDelay = "disableDelay" BatteryBoost = "batteryBoost" + // TODO FIX WORDING Phases = "phases" // configured phases (1/3, 0 for auto on 1p3p chargers, nil for plain chargers) PhasesConfigured = "phasesConfigured" // TODO mirrors "phases" for UI purposes - PhasesEnabled = "phasesEnabled" // enabled phases (1/3) - PhasesActive = "phasesActive" // active phases as used by vehicle (1/2/3) + // TODO REMOVE + PhasesEnabled = "phasesEnabled" // enabled phases (1/3) + PhasesActive = "phasesActive" // active phases as used by vehicle (1/2/3) ChargerIcon = "chargerIcon" // charger icon for ui ChargerFeature = "chargerFeature" // charger feature diff --git a/core/loadpoint.go b/core/loadpoint.go index 3f761e2571..ebf2c52c5d 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -227,9 +227,6 @@ func NewLoadpointFromConfig(log *util.Logger, settings settings.Settings, other lp.phases = 3 } - // TODO deprecated - lp.migrateSettings() - // validate thresholds if lp.Enable.Threshold > lp.Disable.Threshold { lp.log.WARN.Printf("PV mode enable threshold (%.0fW) is larger than disable threshold (%.0fW)", lp.Enable.Threshold, lp.Disable.Threshold) @@ -275,53 +272,6 @@ func NewLoadpoint(log *util.Logger, settings settings.Settings) *Loadpoint { return lp } -// migrateSettings migrates loadpoint settings -func (lp *Loadpoint) migrateSettings() { - // One-time migrations MUST be mirrored in restoreSettings - if lp.DefaultMode != "" { - lp.log.WARN.Println("deprecated: mode setting is ignored, please remove") - if _, err := lp.settings.String(keys.Mode); err != nil { - lp.settings.SetString(keys.Mode, string(lp.DefaultMode)) - } - } - if lp.Title_ != "" { - lp.log.WARN.Println("deprecated: title setting is ignored, please remove") - if _, err := lp.settings.String(keys.Title); err != nil { - lp.settings.SetString(keys.Title, lp.Title_) - } - } - if lp.Priority_ > 0 { - lp.log.WARN.Println("deprecated: priority setting is ignored, please remove") - if _, err := lp.settings.String(keys.Priority); err != nil { - lp.settings.SetInt(keys.Priority, int64(lp.Priority_)) - } - } - if lp.MinCurrent_ > 0 { - lp.log.WARN.Println("deprecated: mincurrent setting is ignored, please remove") - if _, err := lp.settings.Float(keys.MinCurrent); err != nil { - lp.settings.SetFloat(keys.MinCurrent, lp.MinCurrent_) - } - } - if lp.MaxCurrent_ > 0 { - lp.log.WARN.Println("deprecated: maxcurrent setting is ignored, please remove") - if _, err := lp.settings.Float(keys.MaxCurrent); err != nil { - lp.settings.SetFloat(keys.MaxCurrent, lp.MaxCurrent_) - } - } - if lp.Phases_ > 0 { - lp.log.WARN.Println("deprecated: phases setting is ignored, please remove") - if _, err := lp.settings.Int(keys.Phases); err != nil { - lp.settings.SetInt(keys.Phases, int64(lp.Phases_)) - } - } - if lp.Soc.Estimate != nil || lp.Soc.Poll.Mode != loadpoint.PollCharging || lp.Soc.Poll.Interval != 0 { - lp.log.WARN.Println("deprecated: soc setting is ignored, please remove") - if _, err := lp.settings.String(keys.Soc); err != nil { - lp.settings.SetJson(keys.Soc, lp.Soc) - } - } -} - // restoreSettings restores loadpoint settings func (lp *Loadpoint) restoreSettings() { if testing.Testing() { @@ -336,9 +286,12 @@ func (lp *Loadpoint) restoreSettings() { if v, err := lp.settings.Int(keys.Priority); err == nil && v > 0 { lp.setPriority(int(v)) } - if v, err := lp.settings.Int(keys.Phases); err == nil && (v > 0 || lp.hasPhaseSwitching()) { + if v, err := lp.settings.Int(keys.PhasesConfigured); err == nil && (v > 0 || lp.hasPhaseSwitching()) { lp.setConfiguredPhases(int(v)) - lp.phases = lp.configuredPhases + if !lp.hasPhaseSwitching() { + // TODO FIX PUBLISHING/ use setter? + lp.phases = lp.configuredPhases + } } if v, err := lp.settings.Float(keys.MinCurrent); err == nil && v > 0 { lp.setMinCurrent(v) @@ -657,6 +610,7 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even lp.publish(keys.PhasesConfigured, lp.configuredPhases) lp.publish(keys.ChargerPhases1p3p, lp.hasPhaseSwitching()) lp.publish(keys.ChargerSinglePhase, lp.getChargerPhysicalPhases() == 1) + // TODO REMOVE PhasesEnabled lp.publish(keys.PhasesEnabled, lp.phases) lp.publish(keys.PhasesActive, lp.ActivePhases()) lp.publish(keys.SmartCostLimit, lp.smartCostLimit) diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index be1357286b..4f655778eb 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -66,10 +66,12 @@ type API interface { GetDefaultMode() api.ChargeMode // SetDefaultMode sets the default charge mode (for reset) SetDefaultMode(api.ChargeMode) - // GetPhases returns the enabled phases + // GetPhases returns the currently enabled phases GetPhases() int - // SetPhases sets the enabled phases - SetPhases(int) error + // GetConfiguredPhases returns statically configured phases + GetConfiguredPhases() int + // SetConfiguredPhases sets the statically configured phases + SetConfiguredPhases(int) error // ActivePhases returns the active phases for the current vehicle ActivePhases() int diff --git a/core/loadpoint/config.go b/core/loadpoint/config.go index 6834a222ba..fb5ff46dfd 100644 --- a/core/loadpoint/config.go +++ b/core/loadpoint/config.go @@ -17,17 +17,17 @@ type StaticConfig struct { type DynamicConfig struct { // dynamic config - Title string `json:"title"` - DefaultMode string `json:"defaultMode"` - Priority int `json:"priority"` - Phases int `json:"phases"` - MinCurrent float64 `json:"minCurrent"` - MaxCurrent float64 `json:"maxCurrent"` - SmartCostLimit *float64 `json:"smartCostLimit"` - PlanEnergy float64 `json:"planEnergy"` - PlanTime time.Time `json:"planTime"` - LimitEnergy float64 `json:"limitEnergy"` - LimitSoc int `json:"limitSoc"` + Title string `json:"title"` + DefaultMode string `json:"defaultMode"` + Priority int `json:"priority"` + ConfiguredPhases int `json:"configuredPhases"` + MinCurrent float64 `json:"minCurrent"` + MaxCurrent float64 `json:"maxCurrent"` + SmartCostLimit *float64 `json:"smartCostLimit"` + PlanEnergy float64 `json:"planEnergy"` + PlanTime time.Time `json:"planTime"` + LimitEnergy float64 `json:"limitEnergy"` + LimitSoc int `json:"limitSoc"` Thresholds ThresholdsConfig `json:"thresholds"` Soc SocConfig `json:"soc"` @@ -69,7 +69,7 @@ func (payload DynamicConfig) Apply(lp API) error { } if err == nil { - err = lp.SetPhases(payload.Phases) + err = lp.SetConfiguredPhases(payload.ConfiguredPhases) } if err == nil && payload.MinCurrent != 0 { diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go index 7664457d9b..e5a576a03e 100644 --- a/core/loadpoint/mock.go +++ b/core/loadpoint/mock.go @@ -209,6 +209,20 @@ func (mr *MockAPIMockRecorder) GetCircuitName() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCircuitName", reflect.TypeOf((*MockAPI)(nil).GetCircuitName)) } +// GetConfiguredPhases mocks base method. +func (m *MockAPI) GetConfiguredPhases() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetConfiguredPhases") + ret0, _ := ret[0].(int) + return ret0 +} + +// GetConfiguredPhases indicates an expected call of GetConfiguredPhases. +func (mr *MockAPIMockRecorder) GetConfiguredPhases() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfiguredPhases", reflect.TypeOf((*MockAPI)(nil).GetConfiguredPhases)) +} + // GetDefaultMode mocks base method. func (m *MockAPI) GetDefaultMode() api.ChargeMode { m.ctrl.T.Helper() @@ -656,6 +670,20 @@ func (mr *MockAPIMockRecorder) SetBatteryBoost(enable any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBatteryBoost", reflect.TypeOf((*MockAPI)(nil).SetBatteryBoost), enable) } +// SetConfiguredPhases mocks base method. +func (m *MockAPI) SetConfiguredPhases(arg0 int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetConfiguredPhases", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetConfiguredPhases indicates an expected call of SetConfiguredPhases. +func (mr *MockAPIMockRecorder) SetConfiguredPhases(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetConfiguredPhases", reflect.TypeOf((*MockAPI)(nil).SetConfiguredPhases), arg0) +} + // SetDefaultMode mocks base method. func (m *MockAPI) SetDefaultMode(arg0 api.ChargeMode) { m.ctrl.T.Helper() @@ -780,20 +808,6 @@ func (mr *MockAPIMockRecorder) SetMode(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMode", reflect.TypeOf((*MockAPI)(nil).SetMode), arg0) } -// SetPhases mocks base method. -func (m *MockAPI) SetPhases(arg0 int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetPhases", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetPhases indicates an expected call of SetPhases. -func (mr *MockAPIMockRecorder) SetPhases(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhases", reflect.TypeOf((*MockAPI)(nil).SetPhases), arg0) -} - // SetPlanEnergy mocks base method. func (m *MockAPI) SetPlanEnergy(arg0 time.Time, arg1 float64) error { m.ctrl.T.Helper() diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index f9158bbbf9..085307f804 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -166,15 +166,22 @@ func (lp *Loadpoint) SetPriority(prio int) { } } -// GetPhases returns loadpoint enabled phases +// GetPhases returns the currently enabled phases func (lp *Loadpoint) GetPhases() int { lp.RLock() defer lp.RUnlock() return lp.phases } -// SetPhases sets loadpoint enabled phases -func (lp *Loadpoint) SetPhases(phases int) error { +// GetConfiguredPhases returns statically configured phases +func (lp *Loadpoint) GetConfiguredPhases() int { + lp.RLock() + defer lp.RUnlock() + return lp.configuredPhases +} + +// SetConfiguredPhases sets the statically configured phases +func (lp *Loadpoint) SetConfiguredPhases(phases int) error { // limit auto mode (phases=0) to scalable charger if !lp.hasPhaseSwitching() && phases == 0 { return fmt.Errorf("charger does not support phase switching") diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go index ead7958122..84593b173d 100644 --- a/core/loadpoint_phases.go +++ b/core/loadpoint_phases.go @@ -8,9 +8,12 @@ import ( // setConfiguredPhases sets the default phase configuration func (lp *Loadpoint) setConfiguredPhases(phases int) { lp.configuredPhases = phases + + // TODO CLARIFY lp.publish(keys.Phases, lp.configuredPhases) - lp.publish(keys.PhasesConfigured, lp.configuredPhases) // TODO remove - lp.settings.SetInt(keys.Phases, int64(lp.configuredPhases)) + + lp.publish(keys.PhasesConfigured, lp.configuredPhases) + lp.settings.SetInt(keys.PhasesConfigured, int64(lp.configuredPhases)) } // setPhases sets the number of enabled phases without modifying the charger diff --git a/server/http_config_loadpoint_handler.go b/server/http_config_loadpoint_handler.go index 4896b0f1ce..e659e123eb 100644 --- a/server/http_config_loadpoint_handler.go +++ b/server/http_config_loadpoint_handler.go @@ -28,19 +28,19 @@ func getLoadpointStaticConfig(lp loadpoint.API) loadpoint.StaticConfig { func getLoadpointDynamicConfig(lp loadpoint.API) loadpoint.DynamicConfig { planTime, planEnergy := lp.GetPlanEnergy() return loadpoint.DynamicConfig{ - Title: lp.GetTitle(), - DefaultMode: string(lp.GetDefaultMode()), - Priority: lp.GetPriority(), - Phases: lp.GetPhases(), - MinCurrent: lp.GetMinCurrent(), - MaxCurrent: lp.GetMaxCurrent(), - SmartCostLimit: lp.GetSmartCostLimit(), - Thresholds: lp.GetThresholds(), - Soc: lp.GetSocConfig(), - PlanEnergy: planEnergy, - PlanTime: planTime, - LimitEnergy: lp.GetLimitEnergy(), - LimitSoc: lp.GetLimitSoc(), + Title: lp.GetTitle(), + DefaultMode: string(lp.GetDefaultMode()), + Priority: lp.GetPriority(), + ConfiguredPhases: lp.GetConfiguredPhases(), + MinCurrent: lp.GetMinCurrent(), + MaxCurrent: lp.GetMaxCurrent(), + SmartCostLimit: lp.GetSmartCostLimit(), + Thresholds: lp.GetThresholds(), + Soc: lp.GetSocConfig(), + PlanEnergy: planEnergy, + PlanTime: planTime, + LimitEnergy: lp.GetLimitEnergy(), + LimitSoc: lp.GetLimitSoc(), } } From 770bd604c51b28c3c1594cad498a1642b1ea205b Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 11:49:20 +0100 Subject: [PATCH 02/17] config ui, unify wording: configuredPhases > phasesConfigured, improve lp error handling --- .../js/components/Config/LoadpointModal.vue | 14 +++++----- assets/js/views/Config.vue | 1 + cmd/class_enumer.go | 12 +++++--- cmd/error.go | 5 ++-- cmd/setup.go | 19 +++++++------ core/keys/loadpoint.go | 9 ++---- core/loadpoint.go | 28 +++++++++---------- core/loadpoint/api.go | 8 +++--- core/loadpoint/config.go | 4 +-- core/loadpoint/mock.go | 24 ++++++++-------- core/loadpoint_api.go | 12 ++++---- core/loadpoint_phases.go | 21 ++++++-------- core/loadpoint_phases_test.go | 8 +++--- server/http.go | 2 +- server/http_config_loadpoint_handler.go | 2 +- server/mqtt.go | 2 +- 16 files changed, 85 insertions(+), 86 deletions(-) diff --git a/assets/js/components/Config/LoadpointModal.vue b/assets/js/components/Config/LoadpointModal.vue index f2b5f7c013..3500208a9b 100644 --- a/assets/js/components/Config/LoadpointModal.vue +++ b/assets/js/components/Config/LoadpointModal.vue @@ -382,7 +382,7 @@ > diff --git a/cmd/class_enumer.go b/cmd/class_enumer.go index 68cb935c03..475663f59d 100644 --- a/cmd/class_enumer.go +++ b/cmd/class_enumer.go @@ -7,11 +7,11 @@ import ( "strings" ) -const _ClassName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsinfluxmessengersponsorship" +const _ClassName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsinfluxmessengersponsorshiploadpoint" -var _ClassIndex = [...]uint8{0, 10, 15, 22, 29, 35, 42, 46, 50, 58, 69, 74, 84, 86, 90, 96, 105, 116} +var _ClassIndex = [...]uint8{0, 10, 15, 22, 29, 35, 42, 46, 50, 58, 69, 74, 84, 86, 90, 96, 105, 116, 125} -const _ClassLowerName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsinfluxmessengersponsorship" +const _ClassLowerName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsinfluxmessengersponsorshiploadpoint" func (i Class) String() string { i -= 1 @@ -42,9 +42,10 @@ func _ClassNoOp() { _ = x[ClassInflux-(15)] _ = x[ClassMessenger-(16)] _ = x[ClassSponsorship-(17)] + _ = x[ClassLoadpoint-(18)] } -var _ClassValues = []Class{ClassConfigFile, ClassMeter, ClassCharger, ClassVehicle, ClassTariff, ClassCircuit, ClassSite, ClassMqtt, ClassDatabase, ClassModbusProxy, ClassEEBus, ClassJavascript, ClassGo, ClassHEMS, ClassInflux, ClassMessenger, ClassSponsorship} +var _ClassValues = []Class{ClassConfigFile, ClassMeter, ClassCharger, ClassVehicle, ClassTariff, ClassCircuit, ClassSite, ClassMqtt, ClassDatabase, ClassModbusProxy, ClassEEBus, ClassJavascript, ClassGo, ClassHEMS, ClassInflux, ClassMessenger, ClassSponsorship, ClassLoadpoint} var _ClassNameToValueMap = map[string]Class{ _ClassName[0:10]: ClassConfigFile, @@ -81,6 +82,8 @@ var _ClassNameToValueMap = map[string]Class{ _ClassLowerName[96:105]: ClassMessenger, _ClassName[105:116]: ClassSponsorship, _ClassLowerName[105:116]: ClassSponsorship, + _ClassName[116:125]: ClassLoadpoint, + _ClassLowerName[116:125]: ClassLoadpoint, } var _ClassNames = []string{ @@ -101,6 +104,7 @@ var _ClassNames = []string{ _ClassName[90:96], _ClassName[96:105], _ClassName[105:116], + _ClassName[116:125], } // ClassString retrieves an enum value from the enum constants string name. diff --git a/cmd/error.go b/cmd/error.go index 3d8c78e8ae..a027a97c40 100644 --- a/cmd/error.go +++ b/cmd/error.go @@ -27,6 +27,7 @@ const ( ClassInflux ClassMessenger ClassSponsorship + ClassLoadpoint ) // FatalError is an error that can be marshaled @@ -57,7 +58,7 @@ type DeviceError struct { } func (e *DeviceError) Error() string { - return e.err.Error() + return "[" + e.Name + "] " + e.err.Error() } // ClassError indicates the class of devices that failed @@ -67,7 +68,7 @@ type ClassError struct { } func (e *ClassError) Error() string { - return e.err.Error() + return e.Class.String() + " " + e.err.Error() } func (e ClassError) MarshalJSON() ([]byte, error) { diff --git a/cmd/setup.go b/cmd/setup.go index 994553815d..2e0b8e119b 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -862,7 +862,7 @@ func configureSiteAndLoadpoints(conf *globalconfig.All) (*core.Site, error) { } if err := configureLoadpoints(*conf); err != nil { - return nil, fmt.Errorf("failed configuring loadpoints: %w", err) + return nil, &ClassError{ClassLoadpoint, err} } tariffs, err := configureTariffs(conf.Tariffs) @@ -945,7 +945,7 @@ func configureLoadpoints(conf globalconfig.All) error { instance, err := core.NewLoadpointFromConfig(log, settings, cc.Other) if err != nil { - return fmt.Errorf("failed configuring loadpoint: %w", err) + return &DeviceError{cc.Name, err} } if err := config.Loadpoints().Add(config.NewStaticDevice(cc, loadpoint.API(instance))); err != nil { @@ -970,22 +970,23 @@ func configureLoadpoints(conf globalconfig.All) error { dynamic, static, err := loadpoint.SplitConfig(cc.Other) if err != nil { - return fmt.Errorf("failed configuring loadpoint: %w", err) + return &DeviceError{cc.Name, err} } instance, err := core.NewLoadpointFromConfig(log, settings, static) if err != nil { - return fmt.Errorf("failed configuring loadpoint: %w", err) - } - - if err := dynamic.Apply(instance); err != nil { - return err + return &DeviceError{cc.Name, err} } dev := config.NewConfigurableDevice[loadpoint.API](&conf, instance) if err := config.Loadpoints().Add(dev); err != nil { - return err + return &DeviceError{cc.Name, err} } + + if err := dynamic.Apply(instance); err != nil { + return &DeviceError{cc.Name, err} + } + } return nil diff --git a/core/keys/loadpoint.go b/core/keys/loadpoint.go index 9d74a90a60..8e286b81d9 100644 --- a/core/keys/loadpoint.go +++ b/core/keys/loadpoint.go @@ -19,12 +19,9 @@ const ( DisableDelay = "disableDelay" BatteryBoost = "batteryBoost" - // TODO FIX WORDING - Phases = "phases" // configured phases (1/3, 0 for auto on 1p3p chargers, nil for plain chargers) - PhasesConfigured = "phasesConfigured" // TODO mirrors "phases" for UI purposes - // TODO REMOVE - PhasesEnabled = "phasesEnabled" // enabled phases (1/3) - PhasesActive = "phasesActive" // active phases as used by vehicle (1/2/3) + Phases = "phases" // phase mode of charger (1/3, 0 = unknown) + PhasesConfigured = "phasesConfigured" // desired phase mode (0/1/3, 0 = automatic), user selection + PhasesActive = "phasesActive" // active phases as used by vehicle (1/2/3) ChargerIcon = "chargerIcon" // charger icon for ui ChargerFeature = "chargerFeature" // charger feature diff --git a/core/loadpoint.go b/core/loadpoint.go index 841dfc17cc..47c1c89a80 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -106,7 +106,7 @@ type Loadpoint struct { priority int // Priority minCurrent float64 // PV mode: start current Min+PV mode: min current maxCurrent float64 // Max allowed current. Physically ensured by the charger - configuredPhases int // Charger configured phase mode 0/1/3 + phasesConfigured int // Charger configured phase mode 0/1/3 limitSoc int // Session limit for soc limitEnergy float64 // Session limit for energy smartCostLimit *float64 // always charge if cost is below this value @@ -223,7 +223,7 @@ func NewLoadpointFromConfig(log *util.Logger, settings settings.Settings, other // phase switching defaults based on charger capabilities if !lp.hasPhaseSwitching() { - lp.configuredPhases = 3 + lp.phasesConfigured = 3 lp.phases = 3 } @@ -287,11 +287,11 @@ func (lp *Loadpoint) restoreSettings() { lp.setPriority(int(v)) } if v, err := lp.settings.Int(keys.PhasesConfigured); err == nil && (v > 0 || lp.hasPhaseSwitching()) { - lp.setConfiguredPhases(int(v)) + lp.setPhasesConfigured(int(v)) // for 1p3p charger, we don't know the physical state yet (phases == 0), so don't touch it if !lp.hasPhaseSwitching() { // TODO FIX PUBLISHING/ use setter? - lp.phases = lp.configuredPhases + lp.phases = lp.phasesConfigured } } if v, err := lp.settings.Float(keys.MinCurrent); err == nil && v > 0 { @@ -598,21 +598,19 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even lp.publish(keys.DisableDelay, lp.Disable.Delay) if phases := lp.getChargerPhysicalPhases(); phases != 0 { - if lp.configuredPhases != phases && lp.configuredPhases != 0 { - lp.log.WARN.Printf("configured phases %d do not match physical phases %d", lp.configuredPhases, phases) + if lp.phasesConfigured != phases && lp.phasesConfigured != 0 { + lp.log.WARN.Printf("configured phases %d do not match physical phases %d", lp.phasesConfigured, phases) } lp.phases = phases - lp.configuredPhases = phases + lp.phasesConfigured = phases lp.publish(keys.Phases, phases) } else { lp.publish(keys.Phases, nil) } - lp.publish(keys.PhasesConfigured, lp.configuredPhases) + lp.publish(keys.PhasesConfigured, lp.phasesConfigured) lp.publish(keys.ChargerPhases1p3p, lp.hasPhaseSwitching()) lp.publish(keys.ChargerSinglePhase, lp.getChargerPhysicalPhases() == 1) - // TODO REMOVE PhasesEnabled - lp.publish(keys.PhasesEnabled, lp.phases) lp.publish(keys.PhasesActive, lp.ActivePhases()) lp.publish(keys.SmartCostLimit, lp.smartCostLimit) lp.publishTimer(phaseTimer, 0, timerInactive) @@ -1136,13 +1134,13 @@ func (lp *Loadpoint) resetPhaseTimer() { // scalePhasesRequired validates if fixed phase configuration matches enabled phases func (lp *Loadpoint) scalePhasesRequired() bool { - return lp.hasPhaseSwitching() && lp.configuredPhases != 0 && lp.configuredPhases != lp.GetPhases() + return lp.hasPhaseSwitching() && lp.phasesConfigured != 0 && lp.phasesConfigured != lp.GetPhases() } // scalePhasesIfAvailable scales if api.PhaseSwitcher is available func (lp *Loadpoint) scalePhasesIfAvailable(phases int) error { - if lp.configuredPhases != 0 { - phases = lp.configuredPhases + if lp.phasesConfigured != 0 { + phases = lp.phasesConfigured } if lp.hasPhaseSwitching() { @@ -1206,7 +1204,7 @@ func (lp *Loadpoint) pvScalePhases(sitePower, minCurrent, maxCurrent float64) in var waiting bool activePhases := lp.ActivePhases() availablePower := lp.chargePower - sitePower - scalable := (sitePower > 0 || !lp.enabled) && activePhases > 1 && lp.configuredPhases < 3 + scalable := (sitePower > 0 || !lp.enabled) && activePhases > 1 && lp.phasesConfigured < 3 lp.log.DEBUG.Printf("!! pvScalePhases DOWN activePhases: %d, available power: %.0fW, scalable: %t", activePhases, availablePower, scalable) @@ -1800,7 +1798,7 @@ func (lp *Loadpoint) Update(sitePower, batteryBoostPower float64, rates api.Rate err = lp.setLimit(0) case lp.scalePhasesRequired(): - err = lp.scalePhases(lp.configuredPhases) + err = lp.scalePhases(lp.phasesConfigured) case lp.remoteControlled(loadpoint.RemoteHardDisable): remoteDisabled = loadpoint.RemoteHardDisable diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index 4f655778eb..7bdbb7e7ae 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -68,10 +68,10 @@ type API interface { SetDefaultMode(api.ChargeMode) // GetPhases returns the currently enabled phases GetPhases() int - // GetConfiguredPhases returns statically configured phases - GetConfiguredPhases() int - // SetConfiguredPhases sets the statically configured phases - SetConfiguredPhases(int) error + // GetPhasesConfigured returns statically configured phases + GetPhasesConfigured() int + // SetPhasesConfigured sets the statically configured phases + SetPhasesConfigured(int) error // ActivePhases returns the active phases for the current vehicle ActivePhases() int diff --git a/core/loadpoint/config.go b/core/loadpoint/config.go index fb5ff46dfd..f69936bae0 100644 --- a/core/loadpoint/config.go +++ b/core/loadpoint/config.go @@ -20,7 +20,7 @@ type DynamicConfig struct { Title string `json:"title"` DefaultMode string `json:"defaultMode"` Priority int `json:"priority"` - ConfiguredPhases int `json:"configuredPhases"` + PhasesConfigured int `json:"phasesConfigured"` MinCurrent float64 `json:"minCurrent"` MaxCurrent float64 `json:"maxCurrent"` SmartCostLimit *float64 `json:"smartCostLimit"` @@ -69,7 +69,7 @@ func (payload DynamicConfig) Apply(lp API) error { } if err == nil { - err = lp.SetConfiguredPhases(payload.ConfiguredPhases) + err = lp.SetPhasesConfigured(payload.PhasesConfigured) } if err == nil && payload.MinCurrent != 0 { diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go index e5a576a03e..072ea1b4cb 100644 --- a/core/loadpoint/mock.go +++ b/core/loadpoint/mock.go @@ -209,18 +209,18 @@ func (mr *MockAPIMockRecorder) GetCircuitName() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCircuitName", reflect.TypeOf((*MockAPI)(nil).GetCircuitName)) } -// GetConfiguredPhases mocks base method. -func (m *MockAPI) GetConfiguredPhases() int { +// GetPhasesConfigured mocks base method. +func (m *MockAPI) GetPhasesConfigured() int { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetConfiguredPhases") + ret := m.ctrl.Call(m, "GetPhasesConfigured") ret0, _ := ret[0].(int) return ret0 } -// GetConfiguredPhases indicates an expected call of GetConfiguredPhases. -func (mr *MockAPIMockRecorder) GetConfiguredPhases() *gomock.Call { +// GetPhasesConfigured indicates an expected call of GetPhasesConfigured. +func (mr *MockAPIMockRecorder) GetPhasesConfigured() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfiguredPhases", reflect.TypeOf((*MockAPI)(nil).GetConfiguredPhases)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhasesConfigured", reflect.TypeOf((*MockAPI)(nil).GetPhasesConfigured)) } // GetDefaultMode mocks base method. @@ -670,18 +670,18 @@ func (mr *MockAPIMockRecorder) SetBatteryBoost(enable any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBatteryBoost", reflect.TypeOf((*MockAPI)(nil).SetBatteryBoost), enable) } -// SetConfiguredPhases mocks base method. -func (m *MockAPI) SetConfiguredPhases(arg0 int) error { +// SetPhasesConfigured mocks base method. +func (m *MockAPI) SetPhasesConfigured(arg0 int) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetConfiguredPhases", arg0) + ret := m.ctrl.Call(m, "SetPhasesConfigured", arg0) ret0, _ := ret[0].(error) return ret0 } -// SetConfiguredPhases indicates an expected call of SetConfiguredPhases. -func (mr *MockAPIMockRecorder) SetConfiguredPhases(arg0 any) *gomock.Call { +// SetPhasesConfigured indicates an expected call of SetPhasesConfigured. +func (mr *MockAPIMockRecorder) SetPhasesConfigured(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetConfiguredPhases", reflect.TypeOf((*MockAPI)(nil).SetConfiguredPhases), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhasesConfigured", reflect.TypeOf((*MockAPI)(nil).SetPhasesConfigured), arg0) } // SetDefaultMode mocks base method. diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 085307f804..0c63e611a4 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -173,15 +173,15 @@ func (lp *Loadpoint) GetPhases() int { return lp.phases } -// GetConfiguredPhases returns statically configured phases -func (lp *Loadpoint) GetConfiguredPhases() int { +// GetPhasesConfigured returns statically configured phases +func (lp *Loadpoint) GetPhasesConfigured() int { lp.RLock() defer lp.RUnlock() - return lp.configuredPhases + return lp.phasesConfigured } -// SetConfiguredPhases sets the statically configured phases -func (lp *Loadpoint) SetConfiguredPhases(phases int) error { +// SetPhasesConfigured sets the statically configured phases +func (lp *Loadpoint) SetPhasesConfigured(phases int) error { // limit auto mode (phases=0) to scalable charger if !lp.hasPhaseSwitching() && phases == 0 { return fmt.Errorf("charger does not support phase switching") @@ -195,7 +195,7 @@ func (lp *Loadpoint) SetConfiguredPhases(phases int) error { lp.log.DEBUG.Println("set phases:", phases) lp.Lock() - lp.setConfiguredPhases(phases) + lp.setPhasesConfigured(phases) lp.Unlock() // apply immediately if not 1p3p diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go index 84593b173d..85a37bdfc0 100644 --- a/core/loadpoint_phases.go +++ b/core/loadpoint_phases.go @@ -5,15 +5,15 @@ import ( "github.com/evcc-io/evcc/core/keys" ) -// setConfiguredPhases sets the default phase configuration -func (lp *Loadpoint) setConfiguredPhases(phases int) { - lp.configuredPhases = phases +// setPhasesConfigured sets the default phase configuration +func (lp *Loadpoint) setPhasesConfigured(phases int) { + lp.phasesConfigured = phases // TODO CLARIFY - lp.publish(keys.Phases, lp.configuredPhases) + lp.publish(keys.Phases, lp.phasesConfigured) - lp.publish(keys.PhasesConfigured, lp.configuredPhases) - lp.settings.SetInt(keys.PhasesConfigured, int64(lp.configuredPhases)) + lp.publish(keys.PhasesConfigured, lp.phasesConfigured) + lp.settings.SetInt(keys.PhasesConfigured, int64(lp.phasesConfigured)) } // setPhases sets the number of enabled phases without modifying the charger @@ -23,9 +23,6 @@ func (lp *Loadpoint) setPhases(phases int) { lp.phases = phases lp.Unlock() - // publish updated phase configuration - lp.publish(keys.PhasesEnabled, lp.phases) - // reset timer to disabled state lp.resetPhaseTimer() @@ -81,11 +78,11 @@ func (lp *Loadpoint) ActivePhases() int { // minActivePhases returns the minimum number of active phases for the loadpoint. func (lp *Loadpoint) minActivePhases() int { lp.RLock() - configuredPhases := lp.configuredPhases + phasesConfigured := lp.phasesConfigured lp.RUnlock() // 1p3p supported or limit 1p - if lp.hasPhaseSwitching() || configuredPhases == 1 { + if lp.hasPhaseSwitching() || phasesConfigured == 1 { return 1 } @@ -107,7 +104,7 @@ func (lp *Loadpoint) maxActivePhases() int { // if 1p3p supported then assume configured limit or 3p if lp.hasPhaseSwitching() { lp.RLock() - physical = lp.configuredPhases + physical = lp.phasesConfigured lp.RUnlock() } diff --git a/core/loadpoint_phases_test.go b/core/loadpoint_phases_test.go index 4dbb6b02f4..6fdd78cf5e 100644 --- a/core/loadpoint_phases_test.go +++ b/core/loadpoint_phases_test.go @@ -82,7 +82,7 @@ func TestMaxActivePhases(t *testing.T) { vehicle.EXPECT().Phases().Return(tc.vehicle).MinTimes(1) lp := &Loadpoint{ - configuredPhases: configured, // fixed phases or default + phasesConfigured: configured, // fixed phases or default vehicle: vehicle, phases: tc.physical, measuredPhases: tc.measuredPhases, @@ -134,7 +134,7 @@ func TestMinActivePhases(t *testing.T) { vehicle.EXPECT().Phases().Return(tc.vehicle).AnyTimes() lp := &Loadpoint{ - configuredPhases: configured, // fixed phases or default + phasesConfigured: configured, // fixed phases or default vehicle: vehicle, phases: tc.physical, measuredPhases: tc.measuredPhases, @@ -231,7 +231,7 @@ func TestPvScalePhases(t *testing.T) { minCurrent: minA, maxCurrent: maxA, vehicle: vehicle, - configuredPhases: 0, // allow switching + phasesConfigured: 0, // allow switching phases: tc.physical, status: api.StatusC, } @@ -463,7 +463,7 @@ func TestScalePhasesIfAvailable(t *testing.T) { phaseCharger, }, minCurrent: minA, - configuredPhases: tc.dflt, // fixed phases or default + phasesConfigured: tc.dflt, // fixed phases or default phases: tc.physical, // current phase status } diff --git a/server/http.go b/server/http.go index 4a87b384e2..e93ca55b43 100644 --- a/server/http.go +++ b/server/http.go @@ -166,7 +166,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param) "limitenergy": {"POST", "/limitenergy/{value:[0-9.]+}", floatHandler(pass(lp.SetLimitEnergy), lp.GetLimitEnergy)}, "mincurrent": {"POST", "/mincurrent/{value:[0-9.]+}", floatHandler(lp.SetMinCurrent, lp.GetMinCurrent)}, "maxcurrent": {"POST", "/maxcurrent/{value:[0-9.]+}", floatHandler(lp.SetMaxCurrent, lp.GetMaxCurrent)}, - "phases": {"POST", "/phases/{value:[0-9]+}", intHandler(lp.SetPhases, lp.GetPhases)}, + "phases": {"POST", "/phases/{value:[0-9]+}", intHandler(lp.SetPhasesConfigured, lp.GetPhasesConfigured)}, "plan": {"GET", "/plan", planHandler(lp)}, "staticPlanPreview": {"GET", "/plan/static/preview/{type:(?:soc|energy)}/{value:[0-9.]+}/{time:[0-9TZ:.+-]+}", staticPlanPreviewHandler(lp)}, "repeatingPlanPreview": {"GET", "/plan/repeating/preview/{soc:[0-9]+}/{weekdays:[0-6,]+}/{time:[0-2][0-9]:[0-5][0-9]}/{tz:[a-zA-Z0-9_./:-]+}", repeatingPlanPreviewHandler(lp)}, diff --git a/server/http_config_loadpoint_handler.go b/server/http_config_loadpoint_handler.go index e659e123eb..16e7dc6c39 100644 --- a/server/http_config_loadpoint_handler.go +++ b/server/http_config_loadpoint_handler.go @@ -31,7 +31,7 @@ func getLoadpointDynamicConfig(lp loadpoint.API) loadpoint.DynamicConfig { Title: lp.GetTitle(), DefaultMode: string(lp.GetDefaultMode()), Priority: lp.GetPriority(), - ConfiguredPhases: lp.GetConfiguredPhases(), + PhasesConfigured: lp.GetPhasesConfigured(), MinCurrent: lp.GetMinCurrent(), MaxCurrent: lp.GetMaxCurrent(), SmartCostLimit: lp.GetSmartCostLimit(), diff --git a/server/mqtt.go b/server/mqtt.go index 3e0fd73549..f40b8e8d50 100644 --- a/server/mqtt.go +++ b/server/mqtt.go @@ -201,7 +201,7 @@ func (m *MQTT) listenSiteSetters(topic string, site site.API) error { func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.API) error { for _, s := range []setter{ {"mode", setterFunc(api.ChargeModeString, pass(lp.SetMode))}, - {"phases", intSetter(lp.SetPhases)}, + {"phases", intSetter(lp.SetPhasesConfigured)}, {"limitSoc", intSetter(pass(lp.SetLimitSoc))}, {"priority", intSetter(pass(lp.SetPriority))}, {"minCurrent", floatSetter(lp.SetMinCurrent)}, From b06e58341a8d9b111a44476a723e0a7ceb80cf75 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 11:53:46 +0100 Subject: [PATCH 03/17] generate --- core/loadpoint/mock.go | 56 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go index 072ea1b4cb..8ef2f76a2d 100644 --- a/core/loadpoint/mock.go +++ b/core/loadpoint/mock.go @@ -209,20 +209,6 @@ func (mr *MockAPIMockRecorder) GetCircuitName() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCircuitName", reflect.TypeOf((*MockAPI)(nil).GetCircuitName)) } -// GetPhasesConfigured mocks base method. -func (m *MockAPI) GetPhasesConfigured() int { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPhasesConfigured") - ret0, _ := ret[0].(int) - return ret0 -} - -// GetPhasesConfigured indicates an expected call of GetPhasesConfigured. -func (mr *MockAPIMockRecorder) GetPhasesConfigured() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhasesConfigured", reflect.TypeOf((*MockAPI)(nil).GetPhasesConfigured)) -} - // GetDefaultMode mocks base method. func (m *MockAPI) GetDefaultMode() api.ChargeMode { m.ctrl.T.Helper() @@ -419,6 +405,20 @@ func (mr *MockAPIMockRecorder) GetPhases() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhases", reflect.TypeOf((*MockAPI)(nil).GetPhases)) } +// GetPhasesConfigured mocks base method. +func (m *MockAPI) GetPhasesConfigured() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhasesConfigured") + ret0, _ := ret[0].(int) + return ret0 +} + +// GetPhasesConfigured indicates an expected call of GetPhasesConfigured. +func (mr *MockAPIMockRecorder) GetPhasesConfigured() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhasesConfigured", reflect.TypeOf((*MockAPI)(nil).GetPhasesConfigured)) +} + // GetPlan mocks base method. func (m *MockAPI) GetPlan(targetTime time.Time, requiredDuration time.Duration) (api.Rates, error) { m.ctrl.T.Helper() @@ -670,20 +670,6 @@ func (mr *MockAPIMockRecorder) SetBatteryBoost(enable any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBatteryBoost", reflect.TypeOf((*MockAPI)(nil).SetBatteryBoost), enable) } -// SetPhasesConfigured mocks base method. -func (m *MockAPI) SetPhasesConfigured(arg0 int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetPhasesConfigured", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetPhasesConfigured indicates an expected call of SetPhasesConfigured. -func (mr *MockAPIMockRecorder) SetPhasesConfigured(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhasesConfigured", reflect.TypeOf((*MockAPI)(nil).SetPhasesConfigured), arg0) -} - // SetDefaultMode mocks base method. func (m *MockAPI) SetDefaultMode(arg0 api.ChargeMode) { m.ctrl.T.Helper() @@ -808,6 +794,20 @@ func (mr *MockAPIMockRecorder) SetMode(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMode", reflect.TypeOf((*MockAPI)(nil).SetMode), arg0) } +// SetPhasesConfigured mocks base method. +func (m *MockAPI) SetPhasesConfigured(arg0 int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetPhasesConfigured", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetPhasesConfigured indicates an expected call of SetPhasesConfigured. +func (mr *MockAPIMockRecorder) SetPhasesConfigured(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhasesConfigured", reflect.TypeOf((*MockAPI)(nil).SetPhasesConfigured), arg0) +} + // SetPlanEnergy mocks base method. func (m *MockAPI) SetPlanEnergy(arg0 time.Time, arg1 float64) error { m.ctrl.T.Helper() From 6a0592167620e58498062f93500496494ab2106c Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 11:56:51 +0100 Subject: [PATCH 04/17] fix test/lint --- cmd/error_test.go | 2 +- cmd/setup.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/error_test.go b/cmd/error_test.go index e05045db4c..b67ea4ad1d 100644 --- a/cmd/error_test.go +++ b/cmd/error_test.go @@ -18,5 +18,5 @@ func TestError(t *testing.T) { b, err := res.MarshalJSON() require.NoError(t, err) - require.Equal(t, `{"class":"meter","device":"0815","error":"foo"}`, string(b)) + require.Equal(t, `{"class":"meter","device":"0815","error":"[0815] foo"}`, string(b)) } diff --git a/cmd/setup.go b/cmd/setup.go index 2e0b8e119b..4c35a6adf4 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -986,7 +986,6 @@ func configureLoadpoints(conf globalconfig.All) error { if err := dynamic.Apply(instance); err != nil { return &DeviceError{cc.Name, err} } - } return nil From d3c902290c83728c9fd22afe81aa069d94bb1982 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 14:16:58 +0100 Subject: [PATCH 05/17] deprecate/ignore warning; respect title/prio from yaml --- core/loadpoint.go | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/core/loadpoint.go b/core/loadpoint.go index 44bdf8f995..7f581993d4 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -93,14 +93,16 @@ type Loadpoint struct { Soc loadpoint.SocConfig Enable, Disable loadpoint.ThresholdConfig - // TODO deprecated - DefaultMode api.ChargeMode `mapstructure:"mode"` // Default charge mode, used for disconnect - Title_ string `mapstructure:"title"` // UI title - Priority_ int `mapstructure:"priority"` // Priority - GuardDuration_ time.Duration `mapstructure:"guardduration"` // charger enable/disable minimum holding time - Phases_ int `mapstructure:"phases"` - MinCurrent_ float64 `mapstructure:"minCurrent"` - MaxCurrent_ float64 `mapstructure:"maxCurrent"` + // from yaml + DefaultMode api.ChargeMode `mapstructure:"mode"` // Default charge mode, used for disconnect + Title string `mapstructure:"title"` // UI title + Priority int `mapstructure:"priority"` // Priority + + // from yaml, deprecated + GuardDuration_ time.Duration `mapstructure:"guardduration"` // ignored, present for compatibility + Phases_ int `mapstructure:"phases"` // ignored, present for compatibility + MinCurrent_ float64 `mapstructure:"minCurrent"` // ignored, present for compatibility + MaxCurrent_ float64 `mapstructure:"maxCurrent"` // ignored, present for compatibility title string // UI title priority int // Priority @@ -277,9 +279,30 @@ func (lp *Loadpoint) restoreSettings() { if testing.Testing() { return } - if v, err := lp.settings.String(keys.Title); err == nil && v != "" { - lp.setTitle(v) + + // from yaml + if lp.Title != "" { + lp.setTitle(lp.Title) + } + if lp.Priority > 0 { + lp.setPriority(lp.Priority) + } + + // deprecated yaml properties + if lp.Phases_ > 0 { + lp.log.WARN.Printf("ignoring deprecated phases: %d. please configure via UI", lp.Phases_) } + if lp.MinCurrent_ > 0 { + lp.log.WARN.Printf("ignoring deprecated minCurrent: %f. please configure via UI", lp.MinCurrent_) + } + if lp.MaxCurrent_ > 0 { + lp.log.WARN.Printf("ignoring deprecated maxCurrent: %f. please configure via UI", lp.MaxCurrent_) + } + if lp.GuardDuration_ > 0 { + lp.log.WARN.Printf("ignoring deprecated guardduration: %s. please configure via UI", lp.GuardDuration_) + } + + // restore runtime configuration (database & yaml LPs) if v, err := lp.settings.String(keys.Mode); err == nil && v != "" { lp.setMode(api.ChargeMode(v)) } From 649645bfafe26017c392a4e230e5274177817634 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 14:22:04 +0100 Subject: [PATCH 06/17] fix --- core/loadpoint_api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index bbd8192b0c..027763bdfb 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -166,7 +166,7 @@ func (lp *Loadpoint) SetPriority(prio int) { lp.log.DEBUG.Println("set priority:", prio) - if lp.Priority_ != prio { + if lp.Priority != prio { lp.setPriority(prio) } } From c37a4682e606d209bd5bbf24b6444e5a6871e08d Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 18:21:50 +0100 Subject: [PATCH 07/17] Update core/loadpoint/api.go Co-authored-by: andig --- core/loadpoint/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index 7bdbb7e7ae..1fd3c2cbec 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -66,7 +66,7 @@ type API interface { GetDefaultMode() api.ChargeMode // SetDefaultMode sets the default charge mode (for reset) SetDefaultMode(api.ChargeMode) - // GetPhases returns the currently enabled phases + // GetPhases returns the enabled phases GetPhases() int // GetPhasesConfigured returns statically configured phases GetPhasesConfigured() int From 4b1d889038cb087fa4bcaf6dc62bf1d5bdcc1f3e Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 18:24:54 +0100 Subject: [PATCH 08/17] Update core/loadpoint_api.go Co-authored-by: andig --- core/loadpoint_api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 027763bdfb..70eab3ec47 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -183,14 +183,14 @@ func (lp *Loadpoint) getPhases() int { return lp.phases } -// GetPhasesConfigured returns statically configured phases +// GetPhasesConfigured returns the configured phases func (lp *Loadpoint) GetPhasesConfigured() int { lp.RLock() defer lp.RUnlock() return lp.phasesConfigured } -// SetPhasesConfigured sets the statically configured phases +// SetPhasesConfigured sets the configured phases func (lp *Loadpoint) SetPhasesConfigured(phases int) error { // limit auto mode (phases=0) to scalable charger if !lp.hasPhaseSwitching() && phases == 0 { From 4e45bc88ec9d794038f1a00db88a6c0a2d15b7d2 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 18:25:06 +0100 Subject: [PATCH 09/17] Update core/loadpoint_api.go Co-authored-by: andig --- core/loadpoint_api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 70eab3ec47..8bc6e861ba 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -171,7 +171,7 @@ func (lp *Loadpoint) SetPriority(prio int) { } } -// GetPhases returns the currently enabled phases +// GetPhases returns the enabled phases func (lp *Loadpoint) GetPhases() int { lp.RLock() defer lp.RUnlock() From c5279c7cf98a2cc059693782824e8d17fa3cfe8d Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 18:28:07 +0100 Subject: [PATCH 10/17] Update core/loadpoint/api.go Co-authored-by: andig --- core/loadpoint/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index 1fd3c2cbec..824e7f1135 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -70,7 +70,7 @@ type API interface { GetPhases() int // GetPhasesConfigured returns statically configured phases GetPhasesConfigured() int - // SetPhasesConfigured sets the statically configured phases + // SetPhasesConfigured sets the configured phases SetPhasesConfigured(int) error // ActivePhases returns the active phases for the current vehicle ActivePhases() int From a270d9cb64e3185ddd80678f83cd565db7461edd Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Fri, 7 Feb 2025 18:28:21 +0100 Subject: [PATCH 11/17] Update core/loadpoint/api.go Co-authored-by: andig --- core/loadpoint/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index 824e7f1135..5b2a01b34a 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -68,7 +68,7 @@ type API interface { SetDefaultMode(api.ChargeMode) // GetPhases returns the enabled phases GetPhases() int - // GetPhasesConfigured returns statically configured phases + // GetPhasesConfigured returns the configured phases GetPhasesConfigured() int // SetPhasesConfigured sets the configured phases SetPhasesConfigured(int) error From 20845dfe4731f41c01ec8b106984203c67762e9d Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 8 Feb 2025 14:10:06 +0100 Subject: [PATCH 12/17] Fix update/publish phases when config changes --- core/loadpoint.go | 5 ----- core/loadpoint_phases.go | 11 +++++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/core/loadpoint.go b/core/loadpoint.go index 7f581993d4..5612a3b0d1 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -311,11 +311,6 @@ func (lp *Loadpoint) restoreSettings() { } if v, err := lp.settings.Int(keys.PhasesConfigured); err == nil && (v > 0 || lp.hasPhaseSwitching()) { lp.setPhasesConfigured(int(v)) - // for 1p3p charger, we don't know the physical state yet (phases == 0), so don't touch it - if !lp.hasPhaseSwitching() { - // TODO FIX PUBLISHING/ use setter? - lp.phases = lp.phasesConfigured - } } if v, err := lp.settings.Float(keys.MinCurrent); err == nil && v > 0 { lp.setMinCurrent(v) diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go index 4df97801bb..def7cce55a 100644 --- a/core/loadpoint_phases.go +++ b/core/loadpoint_phases.go @@ -8,12 +8,15 @@ import ( // setPhasesConfigured sets the default phase configuration func (lp *Loadpoint) setPhasesConfigured(phases int) { lp.phasesConfigured = phases - - // TODO CLARIFY - lp.publish(keys.Phases, lp.phasesConfigured) - lp.publish(keys.PhasesConfigured, lp.phasesConfigured) lp.settings.SetInt(keys.PhasesConfigured, int64(lp.phasesConfigured)) + + // configured phases are actual phases for non-1p3p charger + // for 1p3p charger, configuration does not mean that the physical state has changed, so don't touch it + if !lp.hasPhaseSwitching() { + lp.phases = phases + lp.publish(keys.Phases, lp.phases) + } } // setPhases sets the number of enabled phases without modifying the charger From 7da9bf13f838c618f7ec8a0bd17ce586d7f1033a Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Sat, 8 Feb 2025 15:50:11 +0100 Subject: [PATCH 13/17] remove unused phases key --- assets/js/components/Loadpoint.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/js/components/Loadpoint.vue b/assets/js/components/Loadpoint.vue index 6aecbaa05c..2e48b21f33 100644 --- a/assets/js/components/Loadpoint.vue +++ b/assets/js/components/Loadpoint.vue @@ -180,7 +180,6 @@ export default { chargeRemainingDuration: Number, // other information - phases: Number, phasesConfigured: Number, phasesActive: Number, chargerPhases1p3p: Boolean, From 246073108b378be679df1a4ef4434677620297e1 Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 8 Feb 2025 15:53:42 +0100 Subject: [PATCH 14/17] Remove phases from publishing --- core/keys/loadpoint.go | 1 - core/loadpoint.go | 3 --- core/loadpoint_phases.go | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/core/keys/loadpoint.go b/core/keys/loadpoint.go index 8e286b81d9..3892880d46 100644 --- a/core/keys/loadpoint.go +++ b/core/keys/loadpoint.go @@ -19,7 +19,6 @@ const ( DisableDelay = "disableDelay" BatteryBoost = "batteryBoost" - Phases = "phases" // phase mode of charger (1/3, 0 = unknown) PhasesConfigured = "phasesConfigured" // desired phase mode (0/1/3, 0 = automatic), user selection PhasesActive = "phasesActive" // active phases as used by vehicle (1/2/3) diff --git a/core/loadpoint.go b/core/loadpoint.go index 5612a3b0d1..d70a52eede 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -621,9 +621,6 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even } lp.phases = phases lp.phasesConfigured = phases - lp.publish(keys.Phases, phases) - } else { - lp.publish(keys.Phases, nil) } lp.publish(keys.PhasesConfigured, lp.phasesConfigured) diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go index def7cce55a..918a6b2af4 100644 --- a/core/loadpoint_phases.go +++ b/core/loadpoint_phases.go @@ -14,8 +14,7 @@ func (lp *Loadpoint) setPhasesConfigured(phases int) { // configured phases are actual phases for non-1p3p charger // for 1p3p charger, configuration does not mean that the physical state has changed, so don't touch it if !lp.hasPhaseSwitching() { - lp.phases = phases - lp.publish(keys.Phases, lp.phases) + lp.setPhases(phases) } } From a4557d109652177d44359539da4dc2ec20c510a1 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Sat, 8 Feb 2025 15:54:33 +0100 Subject: [PATCH 15/17] use phases configured for ui --- assets/js/components/LoadpointSettingsModal.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/js/components/LoadpointSettingsModal.vue b/assets/js/components/LoadpointSettingsModal.vue index 94271c20ab..0c4fb59c80 100644 --- a/assets/js/components/LoadpointSettingsModal.vue +++ b/assets/js/components/LoadpointSettingsModal.vue @@ -183,7 +183,6 @@ export default { props: { id: [String, Number], phasesConfigured: Number, - phasesActive: Number, chargerPhases1p3p: Boolean, chargerSinglePhase: Boolean, batteryBoost: Boolean, @@ -237,7 +236,7 @@ export default { return this.maxPowerPhases(this.phasesConfigured); } } - return this.fmtW(this.maxCurrent * V * this.phasesActive); + return this.fmtW(this.maxCurrent * V * this.phasesConfigured); }, minPower() { if (this.chargerPhases1p3p) { @@ -248,7 +247,7 @@ export default { return this.minPowerPhases(this.phasesConfigured); } } - return this.fmtW(this.minCurrent * V * this.phasesActive); + return this.fmtW(this.minCurrent * V * this.phasesConfigured); }, minCurrentOptions() { const opt1 = [...range(Math.floor(this.maxCurrent), 1), 0.5, 0.25, 0.125]; From cc064792ee4e14ed90cf976e14cc2d72948dc2af Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 8 Feb 2025 16:27:47 +0100 Subject: [PATCH 16/17] wip --- core/loadpoint_phases.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go index 871ab7ba9d..d8e93232f4 100644 --- a/core/loadpoint_phases.go +++ b/core/loadpoint_phases.go @@ -99,7 +99,7 @@ func (lp *Loadpoint) MinActivePhases() int { // minActivePhases returns the minimum number of active phases for the loadpoint. func (lp *Loadpoint) minActivePhases() int { - if lp.hasPhaseSwitching() || lp.configuredPhases == 1 { + if lp.hasPhaseSwitching() || lp.phasesConfigured == 1 { return 1 } @@ -127,7 +127,7 @@ func (lp *Loadpoint) maxActivePhases() int { // if 1p3p supported then assume configured limit or 3p if lp.hasPhaseSwitching() { - physical = lp.configuredPhases + physical = lp.phasesConfigured } return min(expect(vehicle), expect(physical), expect(measured), expect(charger)) From 579496f1a0c8b7f8220c4eeaa4e213d023cc63ee Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 9 Feb 2025 09:01:49 +0100 Subject: [PATCH 17/17] Fix reentrant locks --- core/loadpoint.go | 12 ++++++------ core/loadpoint_api.go | 7 +------ core/loadpoint_phases.go | 27 ++++++++++++++++++--------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/core/loadpoint.go b/core/loadpoint.go index 9517d83dd1..c85cbd7583 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -503,7 +503,7 @@ func (lp *Loadpoint) evVehicleDisconnectHandler() { lp.clearSession() // phases are unknown when vehicle disconnects - lp.resetMeasuredPhases() + lp.ResetMeasuredPhases() // energy and duration lp.energyMetrics.Publish("session", lp) @@ -780,7 +780,7 @@ func (lp *Loadpoint) syncCharger() error { if chargerPhases, err = pg.GetPhases(); err == nil { if chargerPhases > 0 && chargerPhases != phases { lp.log.WARN.Printf("charger logic error: phases mismatch (got %d, expected %d)", chargerPhases, phases) - lp.setPhases(chargerPhases) + lp.SetPhases(chargerPhases) } } else { if errors.Is(err, api.ErrNotAvailable) { @@ -794,7 +794,7 @@ func (lp *Loadpoint) syncCharger() error { if !isPg || errors.Is(err, api.ErrNotAvailable) { if chargerPhases > phases { lp.log.WARN.Printf("charger logic error: phases mismatch (got %d measured, expected %d)", chargerPhases, phases) - lp.setPhases(chargerPhases) + lp.SetPhases(chargerPhases) } } } @@ -1189,7 +1189,7 @@ func (lp *Loadpoint) scalePhases(phases int) error { lp.phasesSwitched = lp.clock.Now() // update setting and reset timer - lp.setPhases(phases) + lp.SetPhases(phases) } return nil @@ -1217,7 +1217,7 @@ func (lp *Loadpoint) pvScalePhases(sitePower, minCurrent, maxCurrent float64) in if lp.chargerUpdateCompleted() && lp.phaseSwitchCompleted() { lp.log.WARN.Printf("ignoring inconsistent phases: %dp < %dp observed active", phases, measuredPhases) } - lp.resetMeasuredPhases() + lp.ResetMeasuredPhases() } var waiting bool @@ -1569,7 +1569,7 @@ func (lp *Loadpoint) updateChargeVoltages() { if phases >= 1 { lp.log.DEBUG.Printf("detected connected phases: %dp", phases) - lp.setPhases(phases) + lp.SetPhases(phases) } } diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 40781c2eb8..486c835db8 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -175,11 +175,6 @@ func (lp *Loadpoint) SetPriority(prio int) { func (lp *Loadpoint) GetPhases() int { lp.RLock() defer lp.RUnlock() - return lp.getPhases() -} - -// getPhases returns loadpoint enabled phases -func (lp *Loadpoint) getPhases() int { return lp.phases } @@ -206,12 +201,12 @@ func (lp *Loadpoint) SetPhasesConfigured(phases int) error { lp.Lock() lp.setPhasesConfigured(phases) - lp.Unlock() // apply immediately if not 1p3p if !lp.hasPhaseSwitching() { lp.setPhases(phases) } + lp.Unlock() lp.requestUpdate() diff --git a/core/loadpoint_phases.go b/core/loadpoint_phases.go index d8e93232f4..f0e9bbb37e 100644 --- a/core/loadpoint_phases.go +++ b/core/loadpoint_phases.go @@ -18,12 +18,17 @@ func (lp *Loadpoint) setPhasesConfigured(phases int) { } } +// SetPhases sets the number of enabled phases without modifying the charger +func (lp *Loadpoint) SetPhases(phases int) { + lp.Lock() + defer lp.Unlock() + lp.setPhases(phases) +} + // setPhases sets the number of enabled phases without modifying the charger func (lp *Loadpoint) setPhases(phases int) { - if lp.GetPhases() != phases { - lp.Lock() + if lp.phases != phases { lp.phases = phases - lp.Unlock() // reset timer to disabled state lp.resetPhaseTimer() @@ -33,13 +38,17 @@ func (lp *Loadpoint) setPhases(phases int) { } } +// ResetMeasuredPhases resets measured phases to unknown on vehicle disconnect, phase switch or phase api call +func (lp *Loadpoint) ResetMeasuredPhases() { + lp.Lock() + defer lp.Unlock() + lp.resetMeasuredPhases() +} + // resetMeasuredPhases resets measured phases to unknown on vehicle disconnect, phase switch or phase api call func (lp *Loadpoint) resetMeasuredPhases() { - lp.Lock() lp.measuredPhases = 0 - lp.Unlock() - - lp.publish(keys.PhasesActive, lp.ActivePhases()) + lp.publish(keys.PhasesActive, lp.activePhases()) } // GetMeasuredPhases provides synchronized access to measuredPhases @@ -75,7 +84,7 @@ func (lp *Loadpoint) ActivePhases() int { // activePhases returns the number of expectedly active phases for the meter. // If unknown for 1p3p chargers during startup it will assume 3p. func (lp *Loadpoint) activePhases() int { - physical := lp.getPhases() + physical := lp.phases vehicle := lp.getVehiclePhases() measured := lp.getMeasuredPhases() charger := lp.getChargerPhysicalPhases() @@ -115,7 +124,7 @@ func (lp *Loadpoint) MaxActivePhases() int { // maxActivePhases returns the maximum number of active phases for the loadpoint. func (lp *Loadpoint) maxActivePhases() int { - physical := lp.getPhases() + physical := lp.phases measured := lp.getMeasuredPhases() vehicle := lp.getVehiclePhases() charger := lp.getChargerPhysicalPhases()