HEMS: make Dimmed/Curtailed tri-state (once more) (#30712)

This commit is contained in:
andig 2026-06-10 22:16:58 +02:00 • committed by GitHub
parent d859ae9c7c
commit 45cab34ce9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 32 additions and 31 deletions

View file

@ -287,8 +287,8 @@ type Circuit interface {
// HEMS exposes the runtime state of the home energy management system. // HEMS exposes the runtime state of the home energy management system.
type HEMS interface { type HEMS interface {
SetUpdated(func()) SetUpdated(func())
Dimmed() bool Dimmed() *bool // nil = no statement
Curtailed() bool Curtailed() *bool // nil = no statement
MaxConsumptionPower() float64 // 0 = no limit MaxConsumptionPower() float64 // 0 = no limit
MaxProductionPower() *float64 // nil = no limit MaxProductionPower() *float64 // nil = no limit
} }

View file

@ -1173,10 +1173,10 @@ func (m *MockHEMS) EXPECT() *MockHEMSMockRecorder {
} }
// Curtailed mocks base method. // Curtailed mocks base method.
func (m *MockHEMS) Curtailed() bool { func (m *MockHEMS) Curtailed() *bool {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Curtailed") ret := m.ctrl.Call(m, "Curtailed")
ret0, _ := ret[0].(bool) ret0, _ := ret[0].(*bool)
return ret0 return ret0
} }
@ -1187,10 +1187,10 @@ func (mr *MockHEMSMockRecorder) Curtailed() *gomock.Call {
} }
// Dimmed mocks base method. // Dimmed mocks base method.
func (m *MockHEMS) Dimmed() bool { func (m *MockHEMS) Dimmed() *bool {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Dimmed") ret := m.ctrl.Call(m, "Dimmed")
ret0, _ := ret[0].(bool) ret0, _ := ret[0].(*bool)
return ret0 return ret0
} }

View file

@ -42,8 +42,8 @@ export interface HemsConfig {
} }
export interface HemsStatus { export interface HemsStatus {
dimmed: boolean; dimmed?: boolean;
curtailed: boolean; curtailed?: boolean;
maxConsumptionPower?: number; maxConsumptionPower?: number;
maxProductionPower?: number; maxProductionPower?: number;
} }

View file

@ -805,7 +805,7 @@ export default defineComponent({
value: status.maxConsumptionPower, value: status.maxConsumptionPower,
warning: true, warning: true,
}; };
} else { } else if (status.dimmed !== undefined) {
result["dimmed"] = { value: status.dimmed }; result["dimmed"] = { value: status.dimmed };
} }
if (status.curtailed && status.maxProductionPower !== undefined) { if (status.curtailed && status.maxProductionPower !== undefined) {
@ -813,7 +813,7 @@ export default defineComponent({
value: status.maxProductionPower, value: status.maxProductionPower,
warning: true, warning: true,
}; };
} else { } else if (status.curtailed !== undefined) {
result["curtailed"] = { value: status.curtailed }; result["curtailed"] = { value: status.curtailed };
} }

View file

@ -350,8 +350,8 @@ func runRoot(cmd *cobra.Command, args []string) {
Config: conf.HEMS.Redacted(), Config: conf.HEMS.Redacted(),
YamlSource: yamlSource.hems, YamlSource: yamlSource.hems,
Status: struct { Status: struct {
Dimmed bool `json:"dimmed"` Dimmed *bool `json:"dimmed,omitempty"`
Curtailed bool `json:"curtailed"` Curtailed *bool `json:"curtailed,omitempty"`
MaxConsumptionPower float64 `json:"maxConsumptionPower,omitempty"` MaxConsumptionPower float64 `json:"maxConsumptionPower,omitempty"`
MaxProductionPower *float64 `json:"maxProductionPower,omitempty"` MaxProductionPower *float64 `json:"maxProductionPower,omitempty"`
}{ }{

View file

@ -83,7 +83,7 @@ func hemsDimmed(hems api.HEMS) *bool {
return nil return nil
} }
return new(hems.Dimmed()) return hems.Dimmed()
} }
// hemsCurtailed returns the HEMS curtail status, nil-safe // hemsCurtailed returns the HEMS curtail status, nil-safe
@ -92,5 +92,5 @@ func hemsCurtailed(hems api.HEMS) *bool {
return nil return nil
} }
return new(hems.Curtailed()) return hems.Curtailed()
} }

View file

@ -294,17 +294,17 @@ func (c *EEBus) setProductionLimit(limit float64, active bool) {
var _ api.HEMS = (*EEBus)(nil) var _ api.HEMS = (*EEBus)(nil)
// Dimmed implements api.HEMS, derived from consumptionLimitActivated. // Dimmed implements api.HEMS, derived from consumptionLimitActivated.
func (c *EEBus) Dimmed() bool { func (c *EEBus) Dimmed() *bool {
c.mux.RLock() c.mux.RLock()
defer c.mux.RUnlock() defer c.mux.RUnlock()
return !c.consumptionLimitActivated.IsZero() return new(!c.consumptionLimitActivated.IsZero())
} }
// Curtailed implements api.HEMS, derived from productionLimitActivated. // Curtailed implements api.HEMS, derived from productionLimitActivated.
func (c *EEBus) Curtailed() bool { func (c *EEBus) Curtailed() *bool {
c.mux.RLock() c.mux.RLock()
defer c.mux.RUnlock() defer c.mux.RUnlock()
return !c.productionLimitActivated.IsZero() return new(!c.productionLimitActivated.IsZero())
} }
// MaxConsumptionPower implements api.HEMS, returning the consumption cap // MaxConsumptionPower implements api.HEMS, returning the consumption cap

View file

@ -49,14 +49,14 @@ func newTestEEBus(t *testing.T) *EEBus {
// assertConsumptionLimit checks the HEMS consumption state through the api.HEMS surface. // assertConsumptionLimit checks the HEMS consumption state through the api.HEMS surface.
func assertConsumptionLimit(t *testing.T, c *EEBus, limit float64) { func assertConsumptionLimit(t *testing.T, c *EEBus, limit float64) {
t.Helper() t.Helper()
assert.Equal(t, limit > 0, c.Dimmed()) assert.Equal(t, new(limit > 0), c.Dimmed())
assert.Equal(t, limit, c.MaxConsumptionPower()) assert.Equal(t, limit, c.MaxConsumptionPower())
} }
// assertProductionLimit checks the HEMS production state through the api.HEMS surface. // assertProductionLimit checks the HEMS production state through the api.HEMS surface.
func assertProductionLimit(t *testing.T, c *EEBus, active bool) { func assertProductionLimit(t *testing.T, c *EEBus, active bool) {
t.Helper() t.Helper()
assert.Equal(t, active, c.Curtailed()) assert.Equal(t, new(active), c.Curtailed())
} }
// TestRun_HeartbeatLost_EntersFailsafe verifies the LPC-911/LPP-911 transition: // TestRun_HeartbeatLost_EntersFailsafe verifies the LPC-911/LPP-911 transition:

View file

@ -223,25 +223,25 @@ func (c *Fnn) setConsumptionLimit(limit float64) error {
var _ api.HEMS = (*Fnn)(nil) var _ api.HEMS = (*Fnn)(nil)
// Dimmed implements api.HEMS. // Dimmed implements api.HEMS.
func (c *Fnn) Dimmed() bool { func (c *Fnn) Dimmed() *bool {
if c.w4 == nil { if c.w4 == nil {
return false return nil
} }
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
return c.consumptionLimit > 0 return new(c.consumptionLimit > 0)
} }
// Curtailed implements api.HEMS. // Curtailed implements api.HEMS.
func (c *Fnn) Curtailed() bool { func (c *Fnn) Curtailed() *bool {
if c.w3 == nil { if c.w3 == nil {
return false return nil
} }
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
return c.productionLimit != nil return new(c.productionLimit != nil)
} }
// MaxConsumptionPower implements api.HEMS. // MaxConsumptionPower implements api.HEMS.

View file

@ -137,15 +137,16 @@ func (c *Relay) setConsumptionLimit(limit float64) error {
var _ api.HEMS = (*Relay)(nil) var _ api.HEMS = (*Relay)(nil)
// Dimmed implements api.HEMS, derived from the active consumption limit. // Dimmed implements api.HEMS, derived from the active consumption limit.
func (c *Relay) Dimmed() bool { func (c *Relay) Dimmed() *bool {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
return c.limit != nil return new(c.limit != nil)
} }
// Curtailed implements api.HEMS. Relay does not curtail production. // Curtailed implements api.HEMS. Relay does not curtail production and
func (c *Relay) Curtailed() bool { // hence makes no statement.
return false func (c *Relay) Curtailed() *bool {
return nil
} }
// MaxConsumptionPower implements api.HEMS, returning the active wattage cap. // MaxConsumptionPower implements api.HEMS, returning the active wattage cap.