From 5d0caa88d76de353679857ecdd27b5f055a9cda9 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 12 Nov 2023 19:11:17 +0100 Subject: [PATCH] chore: use util.Monitor for tariffs (#10729) --- tariff/awattar.go | 27 ++++++++++++--------------- tariff/electricitymaps.go | 30 ++++++++++++++---------------- tariff/elering.go | 32 ++++++++++++++------------------ tariff/energinet.go | 28 +++++++++++++--------------- tariff/entsoe.go | 29 +++++++++++++---------------- tariff/gruenstromindex.go | 32 +++++++++++++++----------------- tariff/ngeso.go | 24 +++++++++++------------- tariff/octopus.go | 36 ++++++++++++++---------------------- tariff/tariffs.go | 8 -------- tariff/tibber.go | 29 +++++++++++++---------------- 10 files changed, 119 insertions(+), 156 deletions(-) diff --git a/tariff/awattar.go b/tariff/awattar.go index 83f35954b8..5faa792514 100644 --- a/tariff/awattar.go +++ b/tariff/awattar.go @@ -16,11 +16,9 @@ import ( type Awattar struct { *embed - mux sync.Mutex - log *util.Logger - uri string - data api.Rates - updated time.Time + log *util.Logger + uri string + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Awattar)(nil) @@ -73,29 +71,28 @@ func (t *Awattar) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(res.Data)) + data := make(api.Rates, 0, len(res.Data)) for _, r := range res.Data { ar := api.Rate{ Start: r.StartTimestamp.Local(), End: r.EndTimestamp.Local(), Price: t.totalPrice(r.Marketprice / 1e3), } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } // Rates implements the api.Tariff interface func (t *Awattar) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/electricitymaps.go b/tariff/electricitymaps.go index b45a52f252..6be4155ed1 100644 --- a/tariff/electricitymaps.go +++ b/tariff/electricitymaps.go @@ -17,12 +17,10 @@ import ( type ElectricityMaps struct { *request.Helper - log *util.Logger - mux sync.Mutex - uri string - zone string - data api.Rates - updated time.Time + log *util.Logger + uri string + zone string + data *util.Monitor[api.Rates] } type CarbonIntensity struct { @@ -62,6 +60,7 @@ func NewElectricityMapsFromConfig(other map[string]interface{}) (api.Tariff, err Helper: request.NewHelper(log), uri: util.DefaultScheme(strings.TrimRight(cc.Uri, "/"), "https"), zone: strings.ToUpper(cc.Zone), + data: util.NewMonitor[api.Rates](2 * time.Hour), } t.Client.Transport = &transport.Decorator{ @@ -101,28 +100,27 @@ func (t *ElectricityMaps) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(res.Forecast)) + data := make(api.Rates, 0, len(res.Forecast)) for _, r := range res.Forecast { ar := api.Rate{ Start: r.Datetime.Local(), End: r.Datetime.Add(time.Hour).Local(), Price: r.CarbonIntensity, } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } func (t *ElectricityMaps) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/elering.go b/tariff/elering.go index 262e0825b0..3e9580b43e 100644 --- a/tariff/elering.go +++ b/tariff/elering.go @@ -18,11 +18,9 @@ import ( type Elering struct { *embed - mux sync.Mutex - log *util.Logger - region string - data api.Rates - updated time.Time + log *util.Logger + region string + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Elering)(nil) @@ -49,6 +47,7 @@ func NewEleringFromConfig(other map[string]interface{}) (api.Tariff, error) { embed: &cc.embed, log: util.NewLogger("Elering"), region: strings.ToLower(cc.Region), + data: util.NewMonitor[api.Rates](2 * time.Hour), } done := make(chan error) @@ -82,13 +81,8 @@ func (t *Elering) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - data := res.Data[t.region] - - t.data = make(api.Rates, 0, len(data)) - for _, r := range data { + data := make(api.Rates, 0, len(res.Data[t.region])) + for _, r := range res.Data[t.region] { ts := time.Unix(r.Timestamp, 0) ar := api.Rate{ @@ -96,19 +90,21 @@ func (t *Elering) run(done chan error) { End: ts.Add(time.Hour).Local(), Price: t.totalPrice(r.Price / 1e3), } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } // Rates implements the api.Tariff interface func (t *Elering) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/energinet.go b/tariff/energinet.go index 886db485d6..80f981f27c 100644 --- a/tariff/energinet.go +++ b/tariff/energinet.go @@ -17,11 +17,9 @@ import ( type Energinet struct { *embed - mux sync.Mutex - log *util.Logger - region string - data api.Rates - updated time.Time + log *util.Logger + region string + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Energinet)(nil) @@ -48,6 +46,7 @@ func NewEnerginetFromConfig(other map[string]interface{}) (api.Tariff, error) { embed: &cc.embed, log: util.NewLogger("energinet"), region: strings.ToLower(cc.Region), + data: util.NewMonitor[api.Rates](2 * time.Hour), } done := make(chan error) @@ -82,10 +81,7 @@ func (t *Energinet) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(res.Records)) + data := make(api.Rates, 0, len(res.Records)) for _, r := range res.Records { date, _ := time.Parse("2006-01-02T15:04:05", r.HourUTC) ar := api.Rate{ @@ -93,19 +89,21 @@ func (t *Energinet) run(done chan error) { End: date.Add(time.Hour).Local(), Price: t.totalPrice(r.SpotPriceDKK / 1e3), } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } // Rates implements the api.Tariff interface func (t *Energinet) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/entsoe.go b/tariff/entsoe.go index 38c0239fe8..d9cab77e63 100644 --- a/tariff/entsoe.go +++ b/tariff/entsoe.go @@ -21,12 +21,10 @@ import ( type Entsoe struct { *request.Helper *embed - mux sync.Mutex - log *util.Logger - token string - domain string - data api.Rates - updated time.Time + log *util.Logger + token string + domain string + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Entsoe)(nil) @@ -157,29 +155,28 @@ func (t *Entsoe) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(res)) + data := make(api.Rates, 0, len(res)) for _, r := range res { ar := api.Rate{ Start: r.Start, End: r.End, Price: t.totalPrice(r.Value), } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } // Rates implements the api.Tariff interface func (t *Entsoe) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/gruenstromindex.go b/tariff/gruenstromindex.go index 5a64eb58f8..012c11b7c6 100644 --- a/tariff/gruenstromindex.go +++ b/tariff/gruenstromindex.go @@ -14,11 +14,9 @@ import ( ) type GrünStromIndex struct { - log *util.Logger - mux sync.Mutex - zip string - data api.Rates - updated time.Time + log *util.Logger + zip string + data *util.Monitor[api.Rates] } type gsiForecast struct { @@ -76,8 +74,9 @@ func NewGrünStromIndexFromConfig(other map[string]interface{}) (api.Tariff, err log := util.NewLogger("gsi").Redact(cc.Zip) t := &GrünStromIndex{ - log: log, - zip: cc.Zip, + log: log, + zip: cc.Zip, + data: util.NewMonitor[api.Rates](2 * time.Hour), } done := make(chan error) @@ -117,28 +116,27 @@ func (t *GrünStromIndex) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(res.Forecast)) + data := make(api.Rates, 0, len(res.Forecast)) for _, r := range res.Forecast { - t.data = append(t.data, api.Rate{ + data = append(data, api.Rate{ Price: float64(r.Co2GStandard), Start: time.UnixMilli(r.Timeframe.Start).Local(), End: time.UnixMilli(r.Timeframe.End).Local(), }) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } // Rates implements the api.Tariff interface func (t *GrünStromIndex) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/ngeso.go b/tariff/ngeso.go index 19c580180c..e6295c801b 100644 --- a/tariff/ngeso.go +++ b/tariff/ngeso.go @@ -15,12 +15,10 @@ import ( ) type Ngeso struct { - mux sync.Mutex log *util.Logger regionId string regionPostcode string - data api.Rates - updated time.Time + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Ngeso)(nil) @@ -47,6 +45,7 @@ func NewNgesoFromConfig(other map[string]interface{}) (api.Tariff, error) { log: util.NewLogger("ngeso"), regionId: cc.Region, regionPostcode: cc.Postcode, + data: util.NewMonitor[api.Rates](2 * time.Hour), } done := make(chan error) @@ -97,10 +96,7 @@ func (t *Ngeso) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(carbonResponse.Results())) + data := make(api.Rates, 0, len(carbonResponse.Results())) for _, r := range carbonResponse.Results() { ar := api.Rate{ Start: r.ValidityStart.Time, @@ -108,19 +104,21 @@ func (t *Ngeso) run(done chan error) { // Use the forecasted rate, as the actual rate is only available for historical data Price: r.Intensity.Forecast, } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } // Rates implements the api.Tariff interface func (t *Ngeso) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/octopus.go b/tariff/octopus.go index 0dbb61cab2..bc9af13d69 100644 --- a/tariff/octopus.go +++ b/tariff/octopus.go @@ -14,12 +14,10 @@ import ( ) type Octopus struct { - mux sync.Mutex - log *util.Logger - uri string - region string - data api.Rates - updated time.Time + log *util.Logger + uri string + region string + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Octopus)(nil) @@ -49,6 +47,7 @@ func NewOctopusFromConfig(other map[string]interface{}) (api.Tariff, error) { log: util.NewLogger("octopus"), uri: octopus.ConstructRatesAPI(cc.Tariff, cc.Region), region: cc.Tariff, + data: util.NewMonitor[api.Rates](2 * time.Hour), } done := make(chan error) @@ -77,10 +76,7 @@ func (t *Octopus) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - - t.data = make(api.Rates, 0, len(res.Results)) + data := make(api.Rates, 0, len(res.Results)) for _, r := range res.Results { ar := api.Rate{ Start: r.ValidityStart, @@ -88,25 +84,21 @@ func (t *Octopus) run(done chan error) { // UnitRates are supplied inclusive of tax, though this could be flipped easily with a config flag. Price: r.PriceInclusiveTax / 1e2, } - t.data = append(t.data, ar) + data = append(data, ar) } - t.data.Sort() + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } -// Unit implements the api.Tariff interface -// Stubbed because supplier always works in GBP -func (t *Octopus) Unit() string { - return "GBP" -} - // Rates implements the api.Tariff interface func (t *Octopus) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface diff --git a/tariff/tariffs.go b/tariff/tariffs.go index 355fccd534..4a3c4857be 100644 --- a/tariff/tariffs.go +++ b/tariff/tariffs.go @@ -50,11 +50,3 @@ func (t *Tariffs) CurrentCo2() (float64, error) { } return 0, api.ErrNotAvailable } - -// outdatedError returns api.ErrOutdated if t is older than 2*d -func outdatedError(t time.Time, d time.Duration) error { - if time.Since(t) > 2*d { - return api.ErrOutdated - } - return nil -} diff --git a/tariff/tibber.go b/tariff/tibber.go index f2866cb5c8..09f746c108 100644 --- a/tariff/tibber.go +++ b/tariff/tibber.go @@ -17,12 +17,10 @@ import ( type Tibber struct { *embed - mux sync.Mutex - log *util.Logger - homeID string - client *tibber.Client - data api.Rates - updated time.Time + log *util.Logger + homeID string + client *tibber.Client + data *util.Monitor[api.Rates] } var _ api.Tariff = (*Tibber)(nil) @@ -53,6 +51,7 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Tariff, error) { log: log, homeID: cc.HomeID, client: tibber.NewClient(log, cc.Token), + data: util.NewMonitor[api.Rates](2 * time.Hour), } if t.homeID == "" { @@ -103,15 +102,11 @@ func (t *Tibber) run(done chan error) { once.Do(func() { close(done) }) - t.mux.Lock() - t.updated = time.Now() - pi := res.Viewer.Home.CurrentSubscription.PriceInfo - t.data = make(api.Rates, 0, len(pi.Today)+len(pi.Tomorrow)) - t.data = append(t.rates(pi.Today), t.rates(pi.Tomorrow)...) - t.data.Sort() + data := append(t.rates(pi.Today), t.rates(pi.Tomorrow)...) + data.Sort() - t.mux.Unlock() + t.data.Set(data) } } @@ -134,9 +129,11 @@ func (t *Tibber) rates(pi []tibber.Price) api.Rates { // Rates implements the api.Tariff interface func (t *Tibber) Rates() (api.Rates, error) { - t.mux.Lock() - defer t.mux.Unlock() - return slices.Clone(t.data), outdatedError(t.updated, time.Hour) + var res api.Rates + err := t.data.GetFunc(func(val api.Rates) { + res = slices.Clone(val) + }) + return res, err } // Type implements the api.Tariff interface