Ctrl K
ComunidadGuías…

tariff/electricitymaps.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package tariff

import (
	"errors"
	"fmt"
	"slices"
	"strings"
	"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"
	"github.com/evcc-io/evcc/util/transport"
)

type ElectricityMaps struct {
	*request.Helper
	log  *util.Logger
	uri  string
	zone string
	data *util.Monitor[api.Rates]
}

type CarbonIntensity struct {
	Error    string
	Zone     string
	Forecast []CarbonIntensitySlot
}

type CarbonIntensitySlot struct {
	CarbonIntensity float64   // 626,
	Datetime        time.Time // "2022-12-12T16:00:00.000Z"
}

var _ api.Tariff = (*ElectricityMaps)(nil)

func init() {
	registry.Add("electricitymaps", NewElectricityMapsFromConfig)
}

func NewElectricityMapsFromConfig(other map[string]any) (api.Tariff, error) {
	cc := struct {
		Uri   string
		Token string
		Zone  string
	}{
		Zone: "DE",
	}

	if err := util.DecodeOther(other, &cc); err != nil {
		return nil, err
	}

	log := util.NewLogger("em").Redact(cc.Token)

	t := &ElectricityMaps{
		log:    log,
		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{
		Base: t.Client.Transport,
		Decorator: transport.DecorateHeaders(map[string]string{
			"X-BLOBR-KEY": cc.Token,
		}),
	}

	return runOrError(t)
}

func (t *ElectricityMaps) run(done chan error) {
	var once sync.Once

	uri := fmt.Sprintf("%s/carbon-intensity/forecast?zone=%s", t.uri, t.zone)

	for tick := time.Tick(time.Hour); ; <-tick {
		var res CarbonIntensity

		if err := backoff.Retry(func() error {
			return backoffPermanentError(t.GetJSON(uri, &res))
		}, bo()); err != nil {
			if res.Error != "" {
				err = errors.New(res.Error)
			}

			if reportError(&once, done, err) {
				return
			}

			t.log.ERROR.Println(err)
			continue
		}

		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(),
				Value: r.CarbonIntensity,
			}
			data = append(data, ar)
		}

		mergeRates(t.data, data)
		once.Do(func() { close(done) })
	}
}

func (t *ElectricityMaps) Rates() (api.Rates, error) {
	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
func (t *ElectricityMaps) Type() api.TariffType {
	return api.TariffTypeCo2
}