Tariffs: retry download with backoff (#8833)

This commit is contained in:
andig 2023-07-27 09:31:40 +02:00 • committed by GitHub
parent ed7c7c8147
commit 7c92e0daa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 32 deletions

View file

@ -6,6 +6,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/tariff/awattar"
"github.com/evcc-io/evcc/util"
@ -55,11 +56,15 @@ func NewAwattarFromConfig(other map[string]interface{}) (api.Tariff, error) {
func (t *Awattar) run(done chan error) {
var once sync.Once
bo := newBackoff()
client := request.NewHelper(t.log)
for ; true; <-time.Tick(time.Hour) {
var res awattar.Prices
if err := client.GetJSON(t.uri, &res); err != nil {
if err := backoff.Retry(func() error {
return client.GetJSON(t.uri, &res)
}, bo); err != nil {
once.Do(func() { done <- err })
t.log.ERROR.Println(err)
@ -92,7 +97,7 @@ func (t *Awattar) Rates() (api.Rates, error) {
return slices.Clone(t.data), outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *Awattar) Type() api.TariffType {
return api.TariffTypePriceDynamic
}

View file

@ -7,6 +7,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -78,11 +79,15 @@ func NewElectricityMapsFromConfig(other map[string]interface{}) (api.Tariff, err
func (t *ElectricityMaps) run(done chan error) {
var once sync.Once
bo := newBackoff()
uri := fmt.Sprintf("%s/carbon-intensity/forecast?zone=%s", t.uri, t.zone)
for ; true; <-time.Tick(time.Hour) {
var res CarbonIntensity
if err := t.GetJSON(uri, &res); err != nil {
if err := backoff.Retry(func() error {
return t.GetJSON(uri, &res)
}, bo); err != nil {
if res.Error != "" {
err = errors.New(res.Error)
}
@ -119,7 +124,7 @@ func (t *ElectricityMaps) Rates() (api.Rates, error) {
return res, outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *ElectricityMaps) Type() api.TariffType {
return api.TariffTypeCo2
}

View file

@ -8,6 +8,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/tariff/elering"
"github.com/evcc-io/evcc/util"
@ -60,6 +61,7 @@ func NewEleringFromConfig(other map[string]interface{}) (api.Tariff, error) {
func (t *Elering) run(done chan error) {
var once sync.Once
client := request.NewHelper(t.log)
bo := newBackoff()
for ; true; <-time.Tick(time.Hour) {
var res elering.NpsPrice
@ -69,7 +71,9 @@ func (t *Elering) run(done chan error) {
url.QueryEscape(ts.Format(time.RFC3339)),
url.QueryEscape(ts.Add(48*time.Hour).Format(time.RFC3339)))
if err := client.GetJSON(uri, &res); err != nil {
if err := backoff.Retry(func() error {
return client.GetJSON(uri, &res)
}, bo); err != nil {
once.Do(func() { done <- err })
t.log.ERROR.Println(err)
@ -106,7 +110,7 @@ func (t *Elering) Rates() (api.Rates, error) {
return slices.Clone(t.data), outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *Elering) Type() api.TariffType {
return api.TariffTypePriceDynamic
}

View file

@ -7,6 +7,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/tariff/energinet"
"github.com/evcc-io/evcc/util"
@ -59,6 +60,7 @@ func NewEnerginetFromConfig(other map[string]interface{}) (api.Tariff, error) {
func (t *Energinet) run(done chan error) {
var once sync.Once
client := request.NewHelper(t.log)
bo := newBackoff()
for ; true; <-time.Tick(time.Hour) {
var res energinet.Prices
@ -69,7 +71,9 @@ func (t *Energinet) run(done chan error) {
ts.Add(24*time.Hour).Format(time.RFC3339),
t.region)
if err := client.GetJSON(uri, &res); err != nil {
if err := backoff.Retry(func() error {
return client.GetJSON(uri, &res)
}, bo); err != nil {
once.Do(func() { done <- err })
t.log.ERROR.Println(err)
@ -103,7 +107,7 @@ func (t *Energinet) Rates() (api.Rates, error) {
return slices.Clone(t.data), outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *Energinet) Type() api.TariffType {
return api.TariffTypePriceDynamic
}

View file

@ -129,7 +129,7 @@ func (t *Fixed) Rates() (api.Rates, error) {
return res, nil
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *Fixed) Type() api.TariffType {
if t.dynamic {
return api.TariffTypePriceDynamic

View file

@ -6,6 +6,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -13,7 +14,6 @@ import (
)
type GrünStromIndex struct {
*request.Helper
log *util.Logger
mux sync.Mutex
zip string
@ -76,9 +76,8 @@ func NewGrünStromIndexFromConfig(other map[string]interface{}) (api.Tariff, err
log := util.NewLogger("gsi").Redact(cc.Zip)
t := &GrünStromIndex{
log: log,
Helper: request.NewHelper(log),
zip: cc.Zip,
log: log,
zip: cc.Zip,
}
done := make(chan error)
@ -90,11 +89,17 @@ func NewGrünStromIndexFromConfig(other map[string]interface{}) (api.Tariff, err
func (t *GrünStromIndex) run(done chan error) {
var once sync.Once
client := request.NewHelper(t.log)
bo := newBackoff()
uri := fmt.Sprintf("https://api.corrently.io/v2.0/gsi/prediction?zip=%s", t.zip)
for ; true; <-time.Tick(time.Hour) {
var res gsiForecast
err := t.GetJSON(uri, &res)
err := backoff.Retry(func() error {
return client.GetJSON(uri, &res)
}, bo)
if err == nil && res.Err {
if s, ok := res.Message.(string); ok {
err = errors.New(s)
@ -135,7 +140,7 @@ func (t *GrünStromIndex) Rates() (api.Rates, error) {
return slices.Clone(t.data), outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *GrünStromIndex) Type() api.TariffType {
return api.TariffTypeCo2
}

14
tariff/helper.go Normal file
View file

@ -0,0 +1,14 @@
package tariff
import (
"time"
"github.com/cenkalti/backoff/v4"
)
func newBackoff() backoff.BackOff {
bo := backoff.NewExponentialBackOff()
bo.InitialInterval = 5 * time.Second
bo.MaxElapsedTime = time.Minute
return bo
}

View file

@ -5,6 +5,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/tariff/octopus"
"github.com/evcc-io/evcc/util"
@ -60,10 +61,14 @@ func NewOctopusFromConfig(other map[string]interface{}) (api.Tariff, error) {
func (t *Octopus) run(done chan error) {
var once sync.Once
client := request.NewHelper(t.log)
bo := newBackoff()
for ; true; <-time.Tick(time.Hour) {
var res octopus.UnitRates
if err := client.GetJSON(t.uri, &res); err != nil {
if err := backoff.Retry(func() error {
return client.GetJSON(t.uri, &res)
}, bo); err != nil {
once.Do(func() { done <- err })
t.log.ERROR.Println(err)
@ -103,7 +108,7 @@ func (t *Octopus) Rates() (api.Rates, error) {
return slices.Clone(t.data), outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *Octopus) Type() api.TariffType {
return api.TariffTypePriceDynamic
}

View file

@ -6,6 +6,7 @@ import (
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/meter/tibber"
"github.com/evcc-io/evcc/util"
@ -73,27 +74,28 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Tariff, error) {
func (t *Tibber) run(done chan error) {
var once sync.Once
var res struct {
Viewer struct {
Home struct {
ID string
TimeZone string
CurrentSubscription tibber.Subscription
} `graphql:"home(id: $id)"`
}
}
bo := newBackoff()
v := map[string]interface{}{
"id": graphql.ID(t.homeID),
}
for ; true; <-time.Tick(time.Hour) {
ctx, cancel := context.WithTimeout(context.Background(), request.Timeout)
err := t.client.Query(ctx, &res, v)
cancel()
var res struct {
Viewer struct {
Home struct {
ID string
TimeZone string
CurrentSubscription tibber.Subscription
} `graphql:"home(id: $id)"`
}
}
if err != nil {
if err := backoff.Retry(func() error {
ctx, cancel := context.WithTimeout(context.Background(), request.Timeout)
defer cancel()
return t.client.Query(ctx, &res, v)
}, bo); err != nil {
once.Do(func() { done <- err })
t.log.ERROR.Println(err)
@ -137,7 +139,7 @@ func (t *Tibber) Rates() (api.Rates, error) {
return slices.Clone(t.data), outdatedError(t.updated, time.Hour)
}
// Type returns the tariff type
// Type implements the api.Tariff interface
func (t *Tibber) Type() api.TariffType {
return api.TariffTypePriceDynamic
}