From 4f978242e5e79bb84c1bbe606e9d1e03b1d5ab54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20He=C3=9F?= Date: Fri, 14 Feb 2025 17:24:49 +0100 Subject: [PATCH] Tibber: resubscribe on clean disconnect (#18643) --- meter/tibber-pulse.go | 53 +++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/meter/tibber-pulse.go b/meter/tibber-pulse.go index 9dedd9c46..be6918d34 100644 --- a/meter/tibber-pulse.go +++ b/meter/tibber-pulse.go @@ -20,7 +20,9 @@ func init() { } type Tibber struct { - data *util.Monitor[tibber.LiveMeasurement] + data *util.Monitor[tibber.LiveMeasurement] + homeID string + timeout time.Duration } func NewTibberFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) { @@ -67,7 +69,9 @@ func NewTibberFromConfig(ctx context.Context, other map[string]interface{}) (api } t := &Tibber{ - data: util.NewMonitor[tibber.LiveMeasurement](cc.Timeout), + data: util.NewMonitor[tibber.LiveMeasurement](cc.Timeout), + homeID: cc.HomeID, + timeout: cc.Timeout, } // subscription client @@ -99,18 +103,8 @@ func NewTibberFromConfig(ctx context.Context, other map[string]interface{}) (api return nil }) - done := make(chan error, 1) - go func(done chan error) { - done <- t.subscribe(client, cc.HomeID) - }(done) - - select { - case err := <-done: - if err != nil { - return nil, err - } - case <-time.After(cc.Timeout): - return nil, api.ErrTimeout + if err := t.ensureSubscribed(client); err != nil { + return nil, err } go func() { @@ -121,11 +115,16 @@ func NewTibberFromConfig(ctx context.Context, other map[string]interface{}) (api }() go func() { - // The pulse sometimes declines valid(!) subscription requests, and asks the client to disconnect. - // Therefore we need to restart the client when exiting gracefully upon server request - // https://github.com/evcc-io/evcc/issues/17925#issuecomment-2621458890 for tick := time.Tick(10 * time.Second); ; { - if err := client.Run(); err != nil { + err := client.Run() + if err == nil { + // The pulse sometimes declines valid(!) subscription requests, and asks the client to disconnect. + // This invalidates the subscription, and therefore we resubscribe when exiting Run() gracefully + // upon server request. + // https://github.com/evcc-io/evcc/issues/17925#issuecomment-2621458890 + err = t.ensureSubscribed(client) + } + if err != nil { log.ERROR.Println(err) } @@ -140,13 +139,27 @@ func NewTibberFromConfig(ctx context.Context, other map[string]interface{}) (api return t, nil } -func (t *Tibber) subscribe(client *graphql.SubscriptionClient, homeID string) error { +func (t *Tibber) ensureSubscribed(client *graphql.SubscriptionClient) error { + done := make(chan error, 1) + go func(done chan error) { + done <- t.subscribe(client) + }(done) + + select { + case err := <-done: + return err + case <-time.After(t.timeout): + return api.ErrTimeout + } +} + +func (t *Tibber) subscribe(client *graphql.SubscriptionClient) error { var query struct { tibber.LiveMeasurement `graphql:"liveMeasurement(homeId: $homeId)"` } _, err := client.Subscribe(&query, map[string]any{ - "homeId": graphql.ID(homeID), + "homeId": graphql.ID(t.homeID), }, func(data []byte, err error) error { if err != nil { return err