From 674591517fe829e5653748abfa08dc99821848c1 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 5 Mar 2025 11:05:35 +0100 Subject: [PATCH] Smart Cost: treat smart cost charging as inflexible for prioritisation (#19381) --- core/loadpoint/api.go | 2 +- core/loadpoint/mock.go | 8 ++++---- core/loadpoint_api.go | 5 ++--- core/prioritizer/prioritizer.go | 5 +++-- core/prioritizer/prioritizer_test.go | 12 ++++++------ core/site.go | 18 +++++++++--------- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index 83b6ef9d4..8622961b0 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -171,7 +171,7 @@ type API interface { // GetChargePower returns the current charging power GetChargePower() float64 // GetChargePowerFlexibility returns the flexible amount of current charging power - GetChargePowerFlexibility() float64 + GetChargePowerFlexibility(rates api.Rates) float64 // GetMaxPhaseCurrent returns max phase current GetMaxPhaseCurrent() float64 diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go index 620bdc20d..80275e26e 100644 --- a/core/loadpoint/mock.go +++ b/core/loadpoint/mock.go @@ -154,17 +154,17 @@ func (mr *MockAPIMockRecorder) GetChargePower() *gomock.Call { } // GetChargePowerFlexibility mocks base method. -func (m *MockAPI) GetChargePowerFlexibility() float64 { +func (m *MockAPI) GetChargePowerFlexibility(rates api.Rates) float64 { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetChargePowerFlexibility") + ret := m.ctrl.Call(m, "GetChargePowerFlexibility", rates) ret0, _ := ret[0].(float64) return ret0 } // GetChargePowerFlexibility indicates an expected call of GetChargePowerFlexibility. -func (mr *MockAPIMockRecorder) GetChargePowerFlexibility() *gomock.Call { +func (mr *MockAPIMockRecorder) GetChargePowerFlexibility(rates any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChargePowerFlexibility", reflect.TypeOf((*MockAPI)(nil).GetChargePowerFlexibility)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChargePowerFlexibility", reflect.TypeOf((*MockAPI)(nil).GetChargePowerFlexibility), rates) } // GetChargerName mocks base method. diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 486c835db..5838c9726 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -538,10 +538,9 @@ func (lp *Loadpoint) GetChargePower() float64 { } // GetChargePowerFlexibility returns the flexible amount of current charging power -func (lp *Loadpoint) GetChargePowerFlexibility() float64 { - // no locking +func (lp *Loadpoint) GetChargePowerFlexibility(rates api.Rates) float64 { mode := lp.GetMode() - if mode == api.ModeNow || !lp.charging() || lp.minSocNotReached() { + if mode == api.ModeNow || !lp.charging() || lp.minSocNotReached() || lp.smartCostActive(rates) { return 0 } diff --git a/core/prioritizer/prioritizer.go b/core/prioritizer/prioritizer.go index ff47cd5d9..6cdb88866 100644 --- a/core/prioritizer/prioritizer.go +++ b/core/prioritizer/prioritizer.go @@ -4,6 +4,7 @@ import ( "fmt" "sync" + "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/util" ) @@ -21,8 +22,8 @@ func New(log *util.Logger) *Prioritizer { } } -func (p *Prioritizer) UpdateChargePowerFlexibility(lp loadpoint.API) { - if power := lp.GetChargePowerFlexibility(); power >= 0 { +func (p *Prioritizer) UpdateChargePowerFlexibility(lp loadpoint.API, rates api.Rates) { + if power := lp.GetChargePowerFlexibility(rates); power >= 0 { p.mu.Lock() p.demand[lp] = power p.mu.Unlock() diff --git a/core/prioritizer/prioritizer_test.go b/core/prioritizer/prioritizer_test.go index bd36e49de..0d65c87a8 100644 --- a/core/prioritizer/prioritizer_test.go +++ b/core/prioritizer/prioritizer_test.go @@ -24,17 +24,17 @@ func TestPrioritzer(t *testing.T) { hi.EXPECT().EffectivePriority().Return(1).AnyTimes() // prio 1 // no additional power available - lo.EXPECT().GetChargePowerFlexibility().Return(300.0) - p.UpdateChargePowerFlexibility(lo) + lo.EXPECT().GetChargePowerFlexibility(nil).Return(300.0) + p.UpdateChargePowerFlexibility(lo, nil) assert.Equal(t, 0.0, p.GetChargePowerFlexibility(lo)) // additional power available - hi.EXPECT().GetChargePowerFlexibility().Return(1e3) - p.UpdateChargePowerFlexibility(hi) + hi.EXPECT().GetChargePowerFlexibility(nil).Return(1e3) + p.UpdateChargePowerFlexibility(hi, nil) assert.Equal(t, 300.0, p.GetChargePowerFlexibility(hi)) // additional power removed - lo.EXPECT().GetChargePowerFlexibility().Return(0.0) - p.UpdateChargePowerFlexibility(lo) + lo.EXPECT().GetChargePowerFlexibility(nil).Return(0.0) + p.UpdateChargePowerFlexibility(lo, nil) assert.Equal(t, 0.0, p.GetChargePowerFlexibility(hi)) } diff --git a/core/site.go b/core/site.go index 7e17c2058..938671590 100644 --- a/core/site.go +++ b/core/site.go @@ -833,7 +833,7 @@ func (site *Site) publishTariffs(greenShareHome float64, greenShareLoadpoints fl } // updateLoadpoints updates all loadpoints' charge power -func (site *Site) updateLoadpoints() float64 { +func (site *Site) updateLoadpoints(rates api.Rates) float64 { var ( wg sync.WaitGroup mu sync.Mutex @@ -844,7 +844,7 @@ func (site *Site) updateLoadpoints() float64 { for _, lp := range site.loadpoints { go func() { power := lp.UpdateChargePowerAndCurrents() - site.prioritizer.UpdateChargePowerFlexibility(lp) + site.prioritizer.UpdateChargePowerFlexibility(lp, rates) mu.Lock() sum += power @@ -861,8 +861,14 @@ func (site *Site) updateLoadpoints() float64 { func (site *Site) update(lp updater) { site.log.DEBUG.Println("----") + // smart cost and battery mode handling + rates, err := site.plannerRates() + if err != nil { + site.log.WARN.Println("planner:", err) + } + // update loadpoints - totalChargePower := site.updateLoadpoints() + totalChargePower := site.updateLoadpoints(rates) // update all circuits' power and currents if site.circuit != nil { @@ -879,12 +885,6 @@ func (site *Site) update(lp updater) { flexiblePower = site.prioritizer.GetChargePowerFlexibility(lp) } - // battery mode handling - rates, err := site.plannerRates() - if err != nil { - site.log.WARN.Println("planner:", err) - } - rate, err := rates.At(time.Now()) if rates != nil && err != nil { msg := fmt.Sprintf("no matching rate for: %s", time.Now().Format(time.RFC3339))