EEBus OHPCF: keep enabled state when enable fails (#32263)

This commit is contained in:
andig 2026-07-29 13:33:30 +02:00 • committed by GitHub
parent 20ce660d7a
commit 6187b965ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 24 additions and 9 deletions

View file

@ -148,7 +148,7 @@ func (c *EEBusOHPCF) UseCaseEvent(_ spineapi.DeviceRemoteInterface, entity spine
// react immediately to a freshly announced schedule/resume opportunity // react immediately to a freshly announced schedule/resume opportunity
// instead of waiting for the next reboost tick, which may miss it (#31549) // instead of waiting for the next reboost tick, which may miss it (#31549)
if c.lastEnabled() { if c.lastEnabled() {
if err := c.apply(); err != nil { if err := c.apply(true); err != nil {
c.log.DEBUG.Printf("apply: %v", err) c.log.DEBUG.Printf("apply: %v", err)
} }
} }
@ -252,13 +252,19 @@ func (c *EEBusOHPCF) Enabled() (bool, error) {
// Enable schedules/resumes the optional consumption when on, pauses/aborts it // Enable schedules/resumes the optional consumption when on, pauses/aborts it
// when off; while on a reboost loop reschedules newly announced consumption. // when off; while on a reboost loop reschedules newly announced consumption.
func (c *EEBusOHPCF) Enable(enable bool) error { func (c *EEBusOHPCF) Enable(enable bool) error {
// record the intent only once accepted, otherwise Enabled() would report a
// state the compressor never reached and the loadpoint runs out of sync
if err := c.apply(enable); err != nil {
return err
}
c.setEnabled(enable) c.setEnabled(enable)
if enable { if enable {
c.startReboost() c.startReboost()
} }
return c.apply() return nil
} }
// startReboost launches the reboost loop, unless one is already running or no // startReboost launches the reboost loop, unless one is already running or no
@ -296,7 +302,7 @@ func (c *EEBusOHPCF) reboostLoop() {
if !c.lastEnabled() { if !c.lastEnabled() {
return return
} }
if err := c.apply(); err != nil { if err := c.apply(true); err != nil {
c.log.DEBUG.Printf("reboost: %v", err) c.log.DEBUG.Printf("reboost: %v", err)
} }
} }
@ -355,7 +361,7 @@ func (c *EEBusOHPCF) stop(entity spineapi.EntityRemoteInterface) error {
// MaxCurrent implements the api.Charger interface. OHPCF is on/off and cannot // MaxCurrent implements the api.Charger interface. OHPCF is on/off and cannot
// be modulated, so the offered current is ignored. // be modulated, so the offered current is ignored.
func (c *EEBusOHPCF) MaxCurrent(int64) error { func (c *EEBusOHPCF) MaxCurrent(int64) error {
return c.apply() return c.apply(c.lastEnabled())
} }
var _ api.Dimmer = (*EEBusOHPCF)(nil) var _ api.Dimmer = (*EEBusOHPCF)(nil)
@ -406,7 +412,7 @@ func (c *EEBusOHPCF) Dim(dim bool) error {
// apply issues the command to align the optional consumption with the on/off // apply issues the command to align the optional consumption with the on/off
// intent. It is idempotent: ohpcfControlAction only acts on a state transition. // intent. It is idempotent: ohpcfControlAction only acts on a state transition.
func (c *EEBusOHPCF) apply() error { func (c *EEBusOHPCF) apply(enable bool) error {
entity, ok := c.connectedCompressor() entity, ok := c.connectedCompressor()
if !ok { if !ok {
return errNotConnected return errNotConnected
@ -418,7 +424,7 @@ func (c *EEBusOHPCF) apply() error {
return nil return nil
} }
switch ohpcfControlAction(state, c.lastEnabled()) { switch ohpcfControlAction(state, enable) {
case ohpcfSchedule: case ohpcfSchedule:
return eebus.Await(func(cb func(model.ResultDataType, model.MsgCounterType)) (*model.MsgCounterType, error) { return eebus.Await(func(cb func(model.ResultDataType, model.MsgCounterType)) (*model.MsgCounterType, error) {
// 0 = start immediately (relative schedule, see SchedulePowerConsumptionProcess) // 0 = start immediately (relative schedule, see SchedulePowerConsumptionProcess)

View file

@ -30,6 +30,15 @@ func TestEEBusOHPCFNotConnected(t *testing.T) {
require.ErrorIs(t, c.Dim(true), api.ErrNotAvailable) require.ErrorIs(t, c.Dim(true), api.ErrNotAvailable)
} }
// a failed enable must not persist the intent, otherwise Enabled() reports a
// state the compressor never accepted and the loadpoint runs out of sync (#32252).
func TestOHPCFEnableFailureKeepsState(t *testing.T) {
c := &EEBusOHPCF{}
require.ErrorIs(t, c.Enable(true), errNotConnected)
assert.False(t, c.lastEnabled())
}
// status mapping: running is C, every other connected state (incl. completed // status mapping: running is C, every other connected state (incl. completed
// and stopped after a boost) is standby B, never disconnected. // and stopped after a boost) is standby B, never disconnected.
func TestOHPCFStatus(t *testing.T) { func TestOHPCFStatus(t *testing.T) {

2
go.mod
View file

@ -268,6 +268,6 @@ replace github.com/grid-x/modbus => github.com/evcc-io/modbus v0.0.0-20250501165
replace github.com/lorenzodonini/ocpp-go => github.com/evcc-io/ocpp-go v0.0.0-20260727074919-195c10b8758d replace github.com/lorenzodonini/ocpp-go => github.com/evcc-io/ocpp-go v0.0.0-20260727074919-195c10b8758d
replace github.com/enbility/spine-go => github.com/andig/spine-go v0.7.1-0.20260725155511-6f83690e6238 replace github.com/enbility/spine-go => github.com/andig/spine-go v0.7.1-0.20260729105813-e3e33d07b702
replace github.com/enbility/eebus-go => github.com/andig/eebus-go v0.0.0-20260725155950-e735091ff165 replace github.com/enbility/eebus-go => github.com/andig/eebus-go v0.0.0-20260725155950-e735091ff165

4
go.sum
View file

@ -30,8 +30,8 @@ github.com/andig/gosunspec v0.0.0-20260705113727-6d585e133512 h1:1y8dS4GaBB9WUu/
github.com/andig/gosunspec v0.0.0-20260705113727-6d585e133512/go.mod h1:c6P6szcR+ROkqZruOR4f6qbDKFjZX6OitPpj+yJ/r8k= github.com/andig/gosunspec v0.0.0-20260705113727-6d585e133512/go.mod h1:c6P6szcR+ROkqZruOR4f6qbDKFjZX6OitPpj+yJ/r8k=
github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e h1:m/NTP3JWpR7M0ljLxiQU4fzR25jjhe1LDtxLMNcoNJQ= github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e h1:m/NTP3JWpR7M0ljLxiQU4fzR25jjhe1LDtxLMNcoNJQ=
github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e/go.mod h1:4VtYzTm//oUipwvO3yh0g/udTE7pYJM+U/kyAuFDsgM= github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e/go.mod h1:4VtYzTm//oUipwvO3yh0g/udTE7pYJM+U/kyAuFDsgM=
github.com/andig/spine-go v0.7.1-0.20260725155511-6f83690e6238 h1:VoAFMoBmdIhC6ki4IirAQVd7OPI3xhJG4wJIQpuDSts= github.com/andig/spine-go v0.7.1-0.20260729105813-e3e33d07b702 h1:aSME6GPyu2LeTp282plM4nrCFBwBiNnnjWxuAIYGnhs=
github.com/andig/spine-go v0.7.1-0.20260725155511-6f83690e6238/go.mod h1:ddWZU5BQGyzRGF20Z7rUKFFzbCp+cfu9mZgYtOMW/fM= github.com/andig/spine-go v0.7.1-0.20260729105813-e3e33d07b702/go.mod h1:ddWZU5BQGyzRGF20Z7rUKFFzbCp+cfu9mZgYtOMW/fM=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=