From 8106dc8b763799b078a83938d94605cc7b8a9bc5 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 7 Jun 2026 13:22:03 +0200 Subject: [PATCH] Refactor dim/curtail handling- split hems and circuit (BC) (#30284) --- api/api.go | 19 +- api/mock.go | 174 ++++++++++------- assets/js/components/Config/CircuitTags.vue | 13 +- assets/js/components/Config/DeviceCard.vue | 37 +++- assets/js/components/Config/DeviceTags.vue | 7 +- assets/js/components/Config/MeterCard.vue | 4 + .../components/Config/defaultYaml/hems.yaml | 18 ++ assets/js/components/HemsWarning.vue | 63 +++++- assets/js/components/Site/Site.vue | 8 +- assets/js/types/evcc.ts | 13 +- assets/js/views/Config.vue | 54 ++++-- cmd/root.go | 24 ++- cmd/setup.go | 2 + core/circuit/circuit.go | 85 +++----- core/circuit/circuit_test.go | 36 +++- core/helper.go | 16 +- core/loadpoint.go | 4 +- core/loadpoint_phases_test.go | 4 +- core/loadpoint_test.go | 30 +-- core/loadpoint_vehicle_test.go | 4 +- core/site.go | 10 +- core/site/api.go | 2 +- core/site_api.go | 17 +- core/site_battery.go | 6 +- core/site_circuits.go | 16 +- hems/eebus/eebus.go | 95 ++++++--- hems/eebus/eebus_test.go | 110 +++++++---- hems/fnn/fnn.go | 144 ++++++++------ hems/hems/api.go | 6 +- hems/relay/relay.go | 70 +++++-- hems/smartgrid/circuit.go | 42 ---- i18n/bg.json | 7 - i18n/bs.json | 9 - i18n/cs.json | 7 - i18n/da.json | 7 - i18n/de.json | 15 +- i18n/el.json | 7 - i18n/en.json | 15 +- i18n/es.json | 7 - i18n/et.json | 2 - i18n/fi.json | 7 - i18n/fr.json | 7 - i18n/hr.json | 7 - i18n/hu.json | 7 - i18n/it.json | 7 - i18n/ja.json | 7 - i18n/lb.json | 7 - i18n/lt.json | 7 - i18n/nl.json | 7 - i18n/no.json | 7 - i18n/pl.json | 7 - i18n/pt.json | 7 - i18n/ro.json | 7 - i18n/ru.json | 7 - i18n/sl.json | 7 - i18n/sv.json | 7 - i18n/ta.json | 7 - i18n/tr.json | 7 - i18n/uk.json | 7 - i18n/zh-Hans.json | 1 - server/eebus/test/cs_test.go | 6 +- server/http_config_helper.go | 6 +- templates/definition/meter/demo-meter.yaml | 19 ++ tests/config-custom-meter.spec.ts | 2 +- tests/config-loadpoint.spec.ts | 6 +- tests/hems-grid.evcc.yaml | 8 + tests/hems.spec.ts | 181 +++++++++++++++--- tests/simulator/api.ts | 2 +- tests/simulator/src/Simulator.vue | 42 ++-- tests/utils.ts | 16 +- 70 files changed, 960 insertions(+), 671 deletions(-) delete mode 100644 hems/smartgrid/circuit.go diff --git a/api/api.go b/api/api.go index 61768c299..4bfeae8e4 100644 --- a/api/api.go +++ b/api/api.go @@ -9,7 +9,7 @@ import ( "golang.org/x/oauth2" ) -//go:generate go tool mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,MeterReturnEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,Tariff +//go:generate go tool mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,MeterReturnEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,HEMS,Tariff // Meter provides total active power in W type Meter interface { @@ -273,7 +273,7 @@ type Circuit interface { SetTitle(string) GetParent() Circuit RegisterChild(child Circuit) - Wrap(parent Circuit) error + SetHEMS(HEMS) HasMeter() bool GetMaxPower() float64 GetMaxCurrent() float64 @@ -282,14 +282,15 @@ type Circuit interface { Update([]CircuitLoad) error ValidateCurrent(old, new float64) float64 ValidatePower(old, new float64) float64 +} - // EnWG §14a - reduce demand/consumption - Dim(bool) - Dimmed() *bool - - // EEG §9 - reduce feed-in to the grid - Curtail(bool) - Curtailed() *bool +// HEMS exposes the runtime state of the home energy management system. +type HEMS interface { + SetUpdated(func()) + Dimmed() bool + Curtailed() bool + MaxConsumptionPower() float64 // 0 = no limit + MaxProductionPower() *float64 // nil = no limit } // Redactor is an interface to redact sensitive data diff --git a/api/mock.go b/api/mock.go index 6adc62aaf..7042443d3 100644 --- a/api/mock.go +++ b/api/mock.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/evcc-io/evcc/api (interfaces: Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,MeterReturnEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,Tariff) +// Source: github.com/evcc-io/evcc/api (interfaces: Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,MeterReturnEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,HEMS,Tariff) // // Generated by this command: // -// mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,MeterReturnEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,Tariff +// mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,CurrentGetter,PhaseSwitcher,PhaseGetter,FeatureDescriber,Identifier,Meter,MeterEnergy,MeterReturnEnergy,PhaseCurrents,Vehicle,ConnectionTimer,ChargeRater,Battery,BatteryController,BatterySocLimiter,Circuit,Dimmer,HEMS,Tariff // // Package api is a generated GoMock package. @@ -895,58 +895,6 @@ func (m *MockCircuit) EXPECT() *MockCircuitMockRecorder { return m.recorder } -// Curtail mocks base method. -func (m *MockCircuit) Curtail(arg0 bool) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Curtail", arg0) -} - -// Curtail indicates an expected call of Curtail. -func (mr *MockCircuitMockRecorder) Curtail(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Curtail", reflect.TypeOf((*MockCircuit)(nil).Curtail), arg0) -} - -// Curtailed mocks base method. -func (m *MockCircuit) Curtailed() *bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Curtailed") - ret0, _ := ret[0].(*bool) - return ret0 -} - -// Curtailed indicates an expected call of Curtailed. -func (mr *MockCircuitMockRecorder) Curtailed() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Curtailed", reflect.TypeOf((*MockCircuit)(nil).Curtailed)) -} - -// Dim mocks base method. -func (m *MockCircuit) Dim(arg0 bool) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Dim", arg0) -} - -// Dim indicates an expected call of Dim. -func (mr *MockCircuitMockRecorder) Dim(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dim", reflect.TypeOf((*MockCircuit)(nil).Dim), arg0) -} - -// Dimmed mocks base method. -func (m *MockCircuit) Dimmed() *bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Dimmed") - ret0, _ := ret[0].(*bool) - return ret0 -} - -// Dimmed indicates an expected call of Dimmed. -func (mr *MockCircuitMockRecorder) Dimmed() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dimmed", reflect.TypeOf((*MockCircuit)(nil).Dimmed)) -} - // GetChargePower mocks base method. func (m *MockCircuit) GetChargePower() float64 { m.ctrl.T.Helper() @@ -1057,6 +1005,18 @@ func (mr *MockCircuitMockRecorder) RegisterChild(child any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterChild", reflect.TypeOf((*MockCircuit)(nil).RegisterChild), child) } +// SetHEMS mocks base method. +func (m *MockCircuit) SetHEMS(arg0 HEMS) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetHEMS", arg0) +} + +// SetHEMS indicates an expected call of SetHEMS. +func (mr *MockCircuitMockRecorder) SetHEMS(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHEMS", reflect.TypeOf((*MockCircuit)(nil).SetHEMS), arg0) +} + // SetMaxCurrent mocks base method. func (m *MockCircuit) SetMaxCurrent(arg0 float64) { m.ctrl.T.Helper() @@ -1135,20 +1095,6 @@ func (mr *MockCircuitMockRecorder) ValidatePower(old, new any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatePower", reflect.TypeOf((*MockCircuit)(nil).ValidatePower), old, new) } -// Wrap mocks base method. -func (m *MockCircuit) Wrap(parent Circuit) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Wrap", parent) - ret0, _ := ret[0].(error) - return ret0 -} - -// Wrap indicates an expected call of Wrap. -func (mr *MockCircuitMockRecorder) Wrap(parent any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wrap", reflect.TypeOf((*MockCircuit)(nil).Wrap), parent) -} - // MockDimmer is a mock of Dimmer interface. type MockDimmer struct { ctrl *gomock.Controller @@ -1202,6 +1148,98 @@ func (mr *MockDimmerMockRecorder) Dimmed() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dimmed", reflect.TypeOf((*MockDimmer)(nil).Dimmed)) } +// MockHEMS is a mock of HEMS interface. +type MockHEMS struct { + ctrl *gomock.Controller + recorder *MockHEMSMockRecorder + isgomock struct{} +} + +// MockHEMSMockRecorder is the mock recorder for MockHEMS. +type MockHEMSMockRecorder struct { + mock *MockHEMS +} + +// NewMockHEMS creates a new mock instance. +func NewMockHEMS(ctrl *gomock.Controller) *MockHEMS { + mock := &MockHEMS{ctrl: ctrl} + mock.recorder = &MockHEMSMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHEMS) EXPECT() *MockHEMSMockRecorder { + return m.recorder +} + +// Curtailed mocks base method. +func (m *MockHEMS) Curtailed() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Curtailed") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Curtailed indicates an expected call of Curtailed. +func (mr *MockHEMSMockRecorder) Curtailed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Curtailed", reflect.TypeOf((*MockHEMS)(nil).Curtailed)) +} + +// Dimmed mocks base method. +func (m *MockHEMS) Dimmed() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Dimmed") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Dimmed indicates an expected call of Dimmed. +func (mr *MockHEMSMockRecorder) Dimmed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dimmed", reflect.TypeOf((*MockHEMS)(nil).Dimmed)) +} + +// MaxConsumptionPower mocks base method. +func (m *MockHEMS) MaxConsumptionPower() float64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MaxConsumptionPower") + ret0, _ := ret[0].(float64) + return ret0 +} + +// MaxConsumptionPower indicates an expected call of MaxConsumptionPower. +func (mr *MockHEMSMockRecorder) MaxConsumptionPower() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxConsumptionPower", reflect.TypeOf((*MockHEMS)(nil).MaxConsumptionPower)) +} + +// MaxProductionPower mocks base method. +func (m *MockHEMS) MaxProductionPower() *float64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MaxProductionPower") + ret0, _ := ret[0].(*float64) + return ret0 +} + +// MaxProductionPower indicates an expected call of MaxProductionPower. +func (mr *MockHEMSMockRecorder) MaxProductionPower() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxProductionPower", reflect.TypeOf((*MockHEMS)(nil).MaxProductionPower)) +} + +// SetUpdated mocks base method. +func (m *MockHEMS) SetUpdated(arg0 func()) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetUpdated", arg0) +} + +// SetUpdated indicates an expected call of SetUpdated. +func (mr *MockHEMSMockRecorder) SetUpdated(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpdated", reflect.TypeOf((*MockHEMS)(nil).SetUpdated), arg0) +} + // MockTariff is a mock of Tariff interface. type MockTariff struct { ctrl *gomock.Controller diff --git a/assets/js/components/Config/CircuitTags.vue b/assets/js/components/Config/CircuitTags.vue index 92d1fc1d0..efa9bfd31 100644 --- a/assets/js/components/Config/CircuitTags.vue +++ b/assets/js/components/Config/CircuitTags.vue @@ -2,7 +2,7 @@