From 0178d32302d341a6aeee71219e6575a3200de4ed Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 4 May 2026 17:06:19 +0200 Subject: [PATCH] EEBus meter: clear cached entity refs on disconnect (#29628) --- charger/eebus.go | 24 +++++++++++++++++++++--- meter/eebus.go | 12 ++++++------ meter/eebus_events.go | 20 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/charger/eebus.go b/charger/eebus.go index 269cdac84..e611eca97 100644 --- a/charger/eebus.go +++ b/charger/eebus.go @@ -43,7 +43,7 @@ type EEBus struct { reconnect bool current float64 - *eebus.Connector + connector *eebus.Connector } func init() { @@ -84,14 +84,14 @@ func newEEBus(ctx context.Context, ski, ip string) (*EEBus, error) { cem: eebus.Instance.CustomerEnergyManagement(), } - c.Connector = eebus.NewConnector() + c.connector = eebus.NewConnector() c.minMaxG = util.Cached(c.minMax, time.Second) if err := eebus.Instance.RegisterDevice(ski, ip, c); err != nil { return nil, err } - if err := c.Wait(ctx); err != nil { + if err := c.connector.Wait(ctx); err != nil { eebus.Instance.UnregisterDevice(ski, c) return nil, err } @@ -125,6 +125,24 @@ func NewEEBus(ctx context.Context, ski, ip string, hasMeter, hasChargedEnergy bo var _ eebus.Device = (*EEBus)(nil) +// Connect implements the eebus.Device interface. +// On SHIP/SPINE disconnect we drop the cached EV entity reference. EvDisconnected +// only fires on a SPINE EntityChange/Remove, not on SHIP-level disconnect, so +// without this we could keep querying an orphan entity until the next reconnect +// re-fires EvConnected. +func (c *EEBus) Connect(connected bool) { + c.connector.Connect(connected) + + if connected { + return + } + + c.mux.Lock() + defer c.mux.Unlock() + + c.ev = nil +} + // UseCaseEvent implements the eebus.Device interface func (c *EEBus) UseCaseEvent(device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event eebusapi.EventType) { c.mux.Lock() diff --git a/meter/eebus.go b/meter/eebus.go index aa14e29c9..3ad07771b 100644 --- a/meter/eebus.go +++ b/meter/eebus.go @@ -25,10 +25,10 @@ import ( type EEBus struct { log *util.Logger - *eebus.Connector - ma *eebus.MonitoringAppliance - eg *eebus.EnergyGuard - mm measurements + connector *eebus.Connector + ma *eebus.MonitoringAppliance + eg *eebus.EnergyGuard + mm measurements mu sync.Mutex maEntity spineapi.EntityRemoteInterface @@ -87,14 +87,14 @@ func NewEEBus(ctx context.Context, ski, ip string, usage *templates.Usage) (api. ma: ma, eg: eebus.Instance.EnergyGuard(), mm: mm, - Connector: eebus.NewConnector(), + connector: eebus.NewConnector(), } if err := eebus.Instance.RegisterDevice(ski, ip, c); err != nil { return nil, err } - if err := c.Wait(ctx); err != nil { + if err := c.connector.Wait(ctx); err != nil { eebus.Instance.UnregisterDevice(ski, c) return nil, err } diff --git a/meter/eebus_events.go b/meter/eebus_events.go index e002dd440..6e1dc032f 100644 --- a/meter/eebus_events.go +++ b/meter/eebus_events.go @@ -12,6 +12,26 @@ import ( var _ eebus.Device = (*EEBus)(nil) +// Connect implements the eebus.Device interface. +// On SHIP/SPINE disconnect we drop cached remote-entity references so a +// subsequent re-pair re-populates them from fresh UseCaseSupportUpdate events. +// Without this, Power/Currents/Voltages would keep serving the last value of +// an orphaned entity (see https://github.com/evcc-io/evcc/issues/28518). +func (c *EEBus) Connect(connected bool) { + c.connector.Connect(connected) + + if connected { + return + } + + c.mu.Lock() + defer c.mu.Unlock() + + c.maEntity = nil + c.egLpcEntity = nil + c.egLppEntity = nil +} + // UseCaseEvent implements the eebus.Device interface func (c *EEBus) UseCaseEvent(_ spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event eebusapi.EventType) { switch event {