Skip to content

Commit 1eafc45

Browse files
authored
chore: simplify external battery mode (#20554)
1 parent 960a71c commit 1eafc45

File tree

4 files changed

+60
-33
lines changed

4 files changed

+60
-33
lines changed

core/site.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -915,14 +915,7 @@ func (site *Site) update(lp updater) {
915915

916916
batteryGridChargeActive := site.batteryGridChargeActive(rate)
917917
site.publish(keys.BatteryGridChargeActive, batteryGridChargeActive)
918-
919-
if batteryMode := site.requiredBatteryMode(batteryGridChargeActive, rate); batteryMode != api.BatteryUnknown {
920-
if err := site.applyBatteryMode(batteryMode); err == nil {
921-
site.SetBatteryMode(batteryMode)
922-
} else {
923-
site.log.ERROR.Println("battery mode:", err)
924-
}
925-
}
918+
site.updateBatteryMode(batteryGridChargeActive, rate)
926919

927920
if sitePower, batteryBuffered, batteryStart, err := site.sitePower(totalChargePower, flexiblePower); err == nil {
928921
// ignore negative pvPower values as that means it is not an energy source but consumption

core/site_api.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -364,19 +364,15 @@ func (site *Site) SetBatteryModeExternal(mode api.BatteryMode) {
364364
site.batteryModeExternal = mode
365365
site.publish(keys.BatteryModeExternal, mode)
366366

367-
if !disable {
368-
site.setBatteryMode(mode)
369-
370-
// start watchdog if not running
371-
if site.batteryModeExternalTimer.IsZero() {
372-
go func() {
373-
for range time.Tick(time.Second) {
374-
if site.batteryModeWatchdogExpired() {
375-
return
376-
}
367+
// start watchdog if not running
368+
if !disable && site.batteryModeExternalTimer.IsZero() {
369+
go func() {
370+
for range time.Tick(time.Second) {
371+
if site.batteryModeWatchdogExpired() {
372+
return
377373
}
378-
}()
379-
}
374+
}
375+
}()
380376
}
381377
}
382378

core/site_battery.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ func (site *Site) SetBatteryMode(batMode api.BatteryMode) {
3939
}
4040
}
4141

42+
func (site *Site) updateBatteryMode(batteryGridChargeActive bool, rate api.Rate) {
43+
if batteryMode := site.requiredBatteryMode(batteryGridChargeActive, rate); batteryMode != api.BatteryUnknown {
44+
if err := site.applyBatteryMode(batteryMode); err == nil {
45+
site.SetBatteryMode(batteryMode)
46+
} else {
47+
site.log.ERROR.Println("battery mode:", err)
48+
}
49+
}
50+
}
51+
4252
// requiredBatteryMode determines required battery mode based on grid charge and rate
4353
func (site *Site) requiredBatteryMode(batteryGridChargeActive bool, rate api.Rate) api.BatteryMode {
4454
var res api.BatteryMode
@@ -63,7 +73,10 @@ func (site *Site) requiredBatteryMode(batteryGridChargeActive bool, rate api.Rat
6373
// require normal mode to leave external control
6474
res = api.BatteryNormal
6575
case extMode != api.BatteryUnknown:
66-
res = extMode
76+
// require external mode only once
77+
if extMode != batMode {
78+
res = extMode
79+
}
6780
case batteryGridChargeActive:
6881
res = mapper(api.BatteryCharge)
6982
case site.dischargeControlActive(rate):

core/site_battery_test.go

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import (
88
"github.com/evcc-io/evcc/util"
99
"github.com/evcc-io/evcc/util/config"
1010
"github.com/stretchr/testify/assert"
11+
"go.uber.org/mock/gomock"
1112
)
1213

13-
func TestExternalBatteryMode(t *testing.T) {
14+
func TestRequiredExternalBatteryMode(t *testing.T) {
1415
for _, tc := range []struct {
1516
internal, ext, new api.BatteryMode
1617
}{
@@ -19,12 +20,12 @@ func TestExternalBatteryMode(t *testing.T) {
1920
{api.BatteryUnknown, api.BatteryCharge, api.BatteryCharge},
2021

2122
{api.BatteryNormal, api.BatteryUnknown, api.BatteryUnknown},
22-
{api.BatteryNormal, api.BatteryNormal, api.BatteryNormal},
23+
{api.BatteryNormal, api.BatteryNormal, api.BatteryUnknown}, // no change required
2324
{api.BatteryNormal, api.BatteryCharge, api.BatteryCharge},
2425

2526
{api.BatteryCharge, api.BatteryUnknown, api.BatteryNormal},
2627
{api.BatteryCharge, api.BatteryNormal, api.BatteryNormal},
27-
{api.BatteryCharge, api.BatteryCharge, api.BatteryCharge},
28+
{api.BatteryCharge, api.BatteryCharge, api.BatteryUnknown}, // no change required
2829
} {
2930
t.Logf("%+v", tc)
3031

@@ -42,8 +43,20 @@ func TestExternalBatteryMode(t *testing.T) {
4243
}
4344

4445
func TestExternalBatteryModeChange(t *testing.T) {
46+
ctrl := gomock.NewController(t)
47+
48+
var bat api.Meter
49+
batCon := api.NewMockBatteryController(ctrl)
50+
51+
bat = &struct {
52+
api.Meter
53+
api.BatteryController
54+
}{
55+
BatteryController: batCon,
56+
}
57+
4558
for _, tc := range []struct {
46-
internal, ext, expired api.BatteryMode
59+
internal, ext, expected api.BatteryMode
4760
}{
4861
{api.BatteryUnknown, api.BatteryUnknown, api.BatteryNormal},
4962
{api.BatteryUnknown, api.BatteryNormal, api.BatteryNormal},
@@ -61,32 +74,44 @@ func TestExternalBatteryModeChange(t *testing.T) {
6174

6275
site := &Site{
6376
log: util.NewLogger("foo"),
64-
batteryMeters: []config.Device[api.Meter]{nil},
77+
batteryMeters: []config.Device[api.Meter]{config.NewStaticDevice(config.Named{}, bat)},
6578
}
6679

80+
// set initial state, internal mode may already be changed
6781
site.batteryMode = tc.internal
68-
82+
site.batteryModeExternal = api.BatteryUnknown
6983
assert.True(t, site.batteryModeExternalTimer.IsZero())
84+
85+
// 1. set required external mode
7086
site.SetBatteryModeExternal(tc.ext)
87+
assert.Equal(t, site.batteryModeExternal, tc.ext, "external mode expected %s got %s", tc.ext, site.batteryModeExternal)
88+
assert.Equal(t, site.batteryMode, tc.internal, "internal mode expected unchanged %s got %s", tc.ext, site.batteryMode)
7189

72-
// timer started
90+
// 2. verify external mode applied to battery
7391
if tc.ext != api.BatteryUnknown {
74-
assert.False(t, site.batteryModeExternalTimer.IsZero())
92+
batCon.EXPECT().SetBatteryMode(tc.ext).Times(1)
7593
}
94+
site.updateBatteryMode(false, api.Rate{})
95+
ctrl.Finish()
7696

77-
// expire timer
97+
// 3. verify required external mode only applied once
98+
site.updateBatteryMode(false, api.Rate{})
99+
ctrl.Finish()
100+
101+
// 4. verify timer expiry
78102
site.batteryModeExternalTimer = site.batteryModeExternalTimer.Add(-time.Hour)
79103
site.batteryModeWatchdogExpired()
80104

81105
// mode reverted to unknown, timer still active
82106
assert.Equal(t, site.batteryModeExternal, api.BatteryUnknown)
83107
assert.False(t, site.batteryModeExternalTimer.IsZero())
84108

85-
mode := site.requiredBatteryMode(false, api.Rate{})
86-
assert.Equal(t, tc.expired.String(), mode.String(), "external mode expected %s got %s", tc.expired, mode)
109+
// battery switched back to normal mode
110+
batCon.EXPECT().SetBatteryMode(api.BatteryNormal).Times(1)
111+
site.updateBatteryMode(false, api.Rate{})
112+
ctrl.Finish()
87113

88114
// timer disabled
89-
site.SetBatteryMode(mode)
90115
assert.True(t, site.batteryModeExternalTimer.IsZero())
91116
}
92117
}

0 commit comments

Comments
 (0)