diff --git a/core/site_optimizer.go b/core/site_optimizer.go index 38088d5a8..f22b5bf2c 100644 --- a/core/site_optimizer.go +++ b/core/site_optimizer.go @@ -680,7 +680,8 @@ type batteryForecastSlot struct { // The Limit flag indicates whether the SOC reached the configured SMax (for // the highest point) or SMin (for the lowest point) boundary - in which case // the battery is forecasted to become fully charged or empty. -// Returns nil for either point when no home battery is present. +// Returns nil for either point when no home battery is present or when the +// battery already is at the respective limit. func batteryForecastSocExtremes(req []optimizer.BatteryConfig, resp []optimizer.BatteryResult) (*batteryForecastSlot, *batteryForecastSlot) { homeIndices := lo.FilterMap(req, func(b optimizer.BatteryConfig, i int) (int, bool) { return i, b.SCapacity > 0 @@ -710,6 +711,14 @@ func batteryForecastSocExtremes(req []optimizer.BatteryConfig, resp []optimizer. } } + // battery is already at the limit - announcing it will become full/empty is pointless + if high != nil && high.limit && high.slot == 0 { + high = nil + } + if low != nil && low.limit && low.slot == 0 { + low = nil + } + return high, low } diff --git a/core/site_optimizer_test.go b/core/site_optimizer_test.go index d5ed31b1f..b4c6627c1 100644 --- a/core/site_optimizer_test.go +++ b/core/site_optimizer_test.go @@ -132,10 +132,24 @@ func TestBatteryForecastSocExtremes(t *testing.T) { { "first slot at SMax wins for highest", []optimizer.BatteryConfig{{SCapacity: 1000, SMax: 1000}}, + [][]float32{{500, 1000, 1000}}, + &batteryForecastSlot{slot: 1, soc: 100, limit: true}, + &batteryForecastSlot{slot: 0, soc: 50, limit: false}, + }, + { + "already full — no highest", + []optimizer.BatteryConfig{{SCapacity: 1000, SMax: 1000}}, [][]float32{{1000, 1000, 500}}, - &batteryForecastSlot{slot: 0, soc: 100, limit: true}, + nil, &batteryForecastSlot{slot: 2, soc: 50, limit: false}, }, + { + "already empty — no lowest", + []optimizer.BatteryConfig{{SCapacity: 1000, SMax: 1000, SMin: 100}}, + [][]float32{{100, 100, 500}}, + &batteryForecastSlot{slot: 2, soc: 50, limit: false}, + nil, + }, { "near SMax is not full", []optimizer.BatteryConfig{{SCapacity: 1000, SMax: 1000}},