From 2b87fe2b1da173a25e171a08569002a85377e9d0 Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 25 Aug 2026 13:22:49 +0200 Subject: [PATCH] =?UTF-8?q?Gr=C3=BCnstromindex:=20fall=20back=20to=20co2?= =?UTF-8?q?=5Fg=5Foekostrom=20when=20co2=5Fg=5Fstandard=20is=20null=20(#33?= =?UTF-8?q?164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tariff/corrently/types.go | 4 ++-- tariff/gruenstromindex.go | 38 ++++++++++++++++++++++++++-------- tariff/gruenstromindex_test.go | 25 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 tariff/gruenstromindex_test.go diff --git a/tariff/corrently/types.go b/tariff/corrently/types.go index c26e10873..4243c1b35 100644 --- a/tariff/corrently/types.go +++ b/tariff/corrently/types.go @@ -18,8 +18,8 @@ type Forecast struct { Gsi float64 `json:"gsi"` TimeStamp int64 `json:"timeStamp"` Energyprice string `json:"energyprice"` - Co2GStandard int `json:"co2_g_standard"` - Co2GOekostrom int `json:"co2_g_oekostrom"` + Co2GStandard *int `json:"co2_g_standard"` + Co2GOekostrom *int `json:"co2_g_oekostrom"` Timeframe struct { Start int64 `json:"start"` End int64 `json:"end"` diff --git a/tariff/gruenstromindex.go b/tariff/gruenstromindex.go index d47968f03..d3cd7b64d 100644 --- a/tariff/gruenstromindex.go +++ b/tariff/gruenstromindex.go @@ -74,6 +74,13 @@ func (t *GrünStromIndex) run(done chan error) { } } + var data api.Rates + if err == nil { + if data = gsiRates(res); len(data) == 0 { + err = api.ErrNotAvailable + } + } + if err != nil { if reportError(&once, done, err) { return @@ -83,20 +90,33 @@ func (t *GrünStromIndex) run(done chan error) { continue } - data := make(api.Rates, 0, len(res.Forecast)) - for _, r := range res.Forecast { - data = append(data, api.Rate{ - Start: time.UnixMilli(r.Timeframe.Start).Local(), - End: time.UnixMilli(r.Timeframe.End).Local(), - Value: float64(r.Co2GStandard), - }) - } - mergeRates(t.data, data) once.Do(func() { close(done) }) } } +// gsiRates maps the forecast to rates. co2_g_standard is null since mid-2026, +// fall back to co2_g_oekostrom and skip slots without either value. +func gsiRates(res corrently.Forecast) api.Rates { + data := make(api.Rates, 0, len(res.Forecast)) + for _, r := range res.Forecast { + co2 := r.Co2GStandard + if co2 == nil { + co2 = r.Co2GOekostrom + } + if co2 == nil { + continue + } + + data = append(data, api.Rate{ + Start: time.UnixMilli(r.Timeframe.Start).Local(), + End: time.UnixMilli(r.Timeframe.End).Local(), + Value: float64(*co2), + }) + } + return data +} + // Rates implements the api.Tariff interface func (t *GrünStromIndex) Rates() (api.Rates, error) { var res api.Rates diff --git a/tariff/gruenstromindex_test.go b/tariff/gruenstromindex_test.go new file mode 100644 index 000000000..76b6e8b9e --- /dev/null +++ b/tariff/gruenstromindex_test.go @@ -0,0 +1,25 @@ +package tariff + +import ( + "encoding/json" + "testing" + + "github.com/evcc-io/evcc/tariff/corrently" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGsiRates(t *testing.T) { + // co2_g_standard is null since mid-2026, co2_g_oekostrom is the fallback + var res corrently.Forecast + require.NoError(t, json.Unmarshal([]byte(`{"forecast":[ + {"co2_g_standard":348,"co2_g_oekostrom":23,"timeframe":{"start":1787652000000,"end":1787655600000}}, + {"co2_g_standard":null,"co2_g_oekostrom":25,"timeframe":{"start":1787655600000,"end":1787659200000}}, + {"co2_g_standard":null,"co2_g_oekostrom":null,"timeframe":{"start":1787659200000,"end":1787662800000}} + ]}`), &res)) + + rates := gsiRates(res) + require.Len(t, rates, 2) + assert.Equal(t, 348.0, rates[0].Value) + assert.Equal(t, 25.0, rates[1].Value) +}