diff --git a/core/loadpoint_session.go b/core/loadpoint_session.go index 5f8c677e9..6ebe6251f 100644 --- a/core/loadpoint_session.go +++ b/core/loadpoint_session.go @@ -2,11 +2,13 @@ package core import ( "errors" + "time" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/keys" "github.com/evcc-io/evcc/core/session" "github.com/evcc-io/evcc/core/wrapper" + "github.com/evcc-io/evcc/tariff" "github.com/jinzhu/now" ) @@ -51,6 +53,11 @@ func (lp *Loadpoint) createSession() { } } + if lp.site != nil { + lp.session.ReferencePricePerKWh = tariff.AverageRate(lp.site.GetTariff(api.TariffUsageGrid), 24*time.Hour) + lp.session.ReferenceCo2PerKWh = tariff.AverageRate(lp.site.GetTariff(api.TariffUsageCo2), 24*time.Hour) + } + // energy lp.energyMetrics.Reset() lp.energyMetrics.Publish("session", lp) diff --git a/core/session/session.go b/core/session/session.go index 540bfea96..619c52477 100644 --- a/core/session/session.go +++ b/core/session/session.go @@ -11,21 +11,23 @@ import ( // Session is a single charging session type Session struct { - ID uint `json:"id" csv:"-" gorm:"primarykey"` - Created time.Time `json:"created"` - Finished time.Time `json:"finished"` - Loadpoint string `json:"loadpoint"` - Identifier string `json:"identifier"` - Vehicle string `json:"vehicle"` - Odometer *float64 `json:"odometer" format:"int"` - MeterStart *float64 `json:"meterStart" csv:"Meter Start (kWh)" gorm:"column:meter_start_kwh"` - MeterStop *float64 `json:"meterStop" csv:"Meter Stop (kWh)" gorm:"column:meter_end_kwh"` - ChargedEnergy float64 `json:"chargedEnergy" csv:"Charged Energy (kWh)" gorm:"column:charged_kwh"` - ChargeDuration *time.Duration `json:"chargeDuration" csv:"Charge Duration" gorm:"column:charge_duration"` - SolarPercentage *float64 `json:"solarPercentage" csv:"Solar (%)" gorm:"column:solar_percentage"` - Price *float64 `json:"price" csv:"Price" gorm:"column:price"` - PricePerKWh *float64 `json:"pricePerKWh" csv:"Price/kWh" gorm:"column:price_per_kwh"` - Co2PerKWh *float64 `json:"co2PerKWh" csv:"CO2/kWh (gCO2eq)" gorm:"column:co2_per_kwh"` + ID uint `json:"id" csv:"-" gorm:"primarykey"` + Created time.Time `json:"created"` + Finished time.Time `json:"finished"` + Loadpoint string `json:"loadpoint"` + Identifier string `json:"identifier"` + Vehicle string `json:"vehicle"` + Odometer *float64 `json:"odometer" format:"int"` + MeterStart *float64 `json:"meterStart" csv:"Meter Start (kWh)" gorm:"column:meter_start_kwh"` + MeterStop *float64 `json:"meterStop" csv:"Meter Stop (kWh)" gorm:"column:meter_end_kwh"` + ChargedEnergy float64 `json:"chargedEnergy" csv:"Charged Energy (kWh)" gorm:"column:charged_kwh"` + ChargeDuration *time.Duration `json:"chargeDuration" csv:"Charge Duration" gorm:"column:charge_duration"` + SolarPercentage *float64 `json:"solarPercentage" csv:"Solar (%)" gorm:"column:solar_percentage"` + Price *float64 `json:"price" csv:"Price" gorm:"column:price"` + PricePerKWh *float64 `json:"pricePerKWh" csv:"Price/kWh" gorm:"column:price_per_kwh"` + Co2PerKWh *float64 `json:"co2PerKWh" csv:"CO2/kWh (gCO2eq)" gorm:"column:co2_per_kwh"` + ReferencePricePerKWh *float64 `json:"referencePricePerKWh" csv:"Reference Price/kWh" gorm:"column:reference_price_per_kwh"` + ReferenceCo2PerKWh *float64 `json:"referenceCo2PerKWh" csv:"Reference CO2/kWh (gCO2eq)" gorm:"column:reference_co2_per_kwh"` } // Sessions is a list of sessions diff --git a/tariff/tariffs.go b/tariff/tariffs.go index 3f88a1994..ff12198f4 100644 --- a/tariff/tariffs.go +++ b/tariff/tariffs.go @@ -44,6 +44,32 @@ func Rates(t api.Tariff) api.Rates { return rr } +// AverageRate returns the arithmetic mean of rates in [now, now+d), or nil if unavailable. +func AverageRate(t api.Tariff, d time.Duration) *float64 { + rr := Rates(t) + if rr == nil { + return nil + } + + now := time.Now() + end := now.Add(d) + + var sum float64 + var count int + for _, r := range rr { + if r.Start.Before(end) && r.End.After(now) { + sum += r.Value + count++ + } + } + + if count == 0 { + return nil + } + + return new(sum / float64(count)) +} + func (t *Tariffs) Get(u api.TariffUsage) api.Tariff { // ensure tariff is not a wrapper exists := func(t api.Tariff) bool {