diff --git a/core/metrics/collector.go b/core/metrics/collector.go index 4814f6774..c584b1280 100644 --- a/core/metrics/collector.go +++ b/core/metrics/collector.go @@ -14,6 +14,7 @@ const ( PV = "pv" Home = "home" // meter and group (virtual measurement) Loadpoint = "loadpoint" + Meter = "meter" // generic meter (ext/aux) Forecast = "forecast" ) diff --git a/core/site.go b/core/site.go index 984fdc4b6..784d274d2 100644 --- a/core/site.go +++ b/core/site.go @@ -85,11 +85,7 @@ type Site struct { prioritizer *prioritizer.Prioritizer // Power budgets stats *Stats // Stats - // metrics - fcstEnergy *metrics.Collector - pvEnergy map[string]*metrics.Collector - homeEnergy, gridEnergy *metrics.Collector - batteryEnergy map[string]*metrics.Collector // per-battery, keyed by meter ref + collectors map[string]*metrics.Collector // keyed by meter ref // cached state gridPower float64 // Grid power @@ -144,7 +140,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif if err != nil { return err } - site.homeEnergy = me + site.collectors[metrics.Home] = me // upload telemetry on shutdown if telemetry.Enabled() { @@ -196,7 +192,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif if err != nil { return err } - site.gridEnergy = me + site.collectors[site.Meters.GridMeterRef] = me } // multiple pv @@ -212,7 +208,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif if err != nil { return err } - site.pvEnergy[ref] = me + site.collectors[ref] = me } // solar forecast collector (mirrors PV history shape, used for scale lookup) @@ -220,7 +216,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif if err != nil { return err } - site.fcstEnergy = fc + site.collectors[metrics.Forecast] = fc // multiple batteries for _, ref := range site.Meters.BatteryMetersRef { @@ -234,7 +230,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif if err != nil { return err } - site.batteryEnergy[ref] = me + site.collectors[ref] = me } // meters used only for monitoring @@ -244,6 +240,12 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif return err } site.extMeters = append(site.extMeters, dev) + + me, err := metrics.NewCollector(metrics.Meter, ref) + if err != nil { + return err + } + site.collectors[ref] = me } // auxiliary meters @@ -253,6 +255,12 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif return err } site.auxMeters = append(site.auxMeters, dev) + + me, err := metrics.NewCollector(metrics.Meter, ref) + if err != nil { + return err + } + site.collectors[ref] = me } // revert battery mode on shutdown @@ -270,10 +278,9 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif // NewSite creates a Site with sane defaults func NewSite() *Site { site := &Site{ - log: util.NewLogger("site"), - Voltage: 230, // V - pvEnergy: make(map[string]*metrics.Collector), - batteryEnergy: make(map[string]*metrics.Collector), + log: util.NewLogger("site"), + Voltage: 230, // V + collectors: make(map[string]*metrics.Collector), } return site @@ -580,7 +587,7 @@ func (site *Site) updatePvMeters() { // persist per-meter PV energy slots (used for history and forecast scaling) for i, dev := range site.pvMeters { - c := site.pvEnergy[dev.Config().Name] + c := site.collectors[dev.Config().Name] var importEnergy *float64 if mm[i].Energy > 0 { @@ -657,7 +664,7 @@ func (site *Site) updateBatteryMeters() { // accumulate per-battery energy (charging = import, discharging = export — from battery POV toward grid root) for i, dev := range site.batteryMeters { ref := dev.Config().Name - c, ok := site.batteryEnergy[ref] + c, ok := site.collectors[ref] if !ok { continue } @@ -692,6 +699,24 @@ func weightedSumOfSocs(mm []types.Measurement) float64 { }) } +// addMeterEnergy persists per-meter energy (positive power = import). +func (site *Site) addMeterEnergy(meters []config.Device[api.Meter], mm []types.Measurement) { + for i, dev := range meters { + ref := dev.Config().Name + c, ok := site.collectors[ref] + if !ok { + continue + } + var importEnergy *float64 + if mm[i].Energy > 0 { + importEnergy = &mm[i].Energy + } + if err := c.AddEnergy(importEnergy, nil, mm[i].Power); err != nil { + site.log.ERROR.Printf("persist meter %s energy: %v", ref, err) + } + } +} + // updateAuxMeters updates aux meters func (site *Site) updateAuxMeters() { if len(site.auxMeters) == 0 { @@ -707,6 +732,8 @@ func (site *Site) updateAuxMeters() { site.log.DEBUG.Printf("aux power: %.0fW", site.auxPower) } + site.addMeterEnergy(site.auxMeters, mm) + site.publish(keys.AuxPower, site.auxPower) site.publish(keys.Aux, mm) } @@ -718,6 +745,9 @@ func (site *Site) updateExtMeters() { } mm := site.collectMeters("ext", site.extMeters) + + site.addMeterEnergy(site.extMeters, mm) + site.publish(keys.Ext, mm) } @@ -770,7 +800,7 @@ func (site *Site) updateGridMeter() error { } } - site.gridEnergy.AddEnergy(importEnergy, nil, mm.Power) + site.collectors[site.Meters.GridMeterRef].AddEnergy(importEnergy, nil, mm.Power) site.publish(keys.Grid, mm) @@ -949,7 +979,7 @@ func (site *Site) update(lp updater) { site.publish(keys.HomePower, homePower) if homePower > 0 { - if err := site.homeEnergy.AddEnergy(nil, nil, homePower); err != nil { + if err := site.collectors[metrics.Home].AddEnergy(nil, nil, homePower); err != nil { site.log.ERROR.Printf("persist home consumption: %v", err) } } diff --git a/core/site_optimizer.go b/core/site_optimizer.go index 8a1d265fe..b886f6689 100644 --- a/core/site_optimizer.go +++ b/core/site_optimizer.go @@ -15,6 +15,7 @@ import ( "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/keys" "github.com/evcc-io/evcc/core/loadpoint" + "github.com/evcc-io/evcc/core/metrics" "github.com/evcc-io/evcc/core/types" "github.com/evcc-io/evcc/tariff" "github.com/evcc-io/evcc/util/config" @@ -550,7 +551,7 @@ func loadpointProfile(lp loadpoint.API, minLen int) []float64 { // homeProfile returns the home base load in Wh func (site *Site) homeProfile(minLen int) ([]float64, error) { // kWh over last 30 days - profile, err := site.homeEnergy.ImportProfile(now.BeginningOfDay().AddDate(0, 0, -30)) + profile, err := site.collectors[metrics.Home].ImportProfile(now.BeginningOfDay().AddDate(0, 0, -30)) if err != nil { return nil, err } diff --git a/core/site_tariffs.go b/core/site_tariffs.go index 50fdbe74f..c5fe351f4 100644 --- a/core/site_tariffs.go +++ b/core/site_tariffs.go @@ -147,7 +147,7 @@ func (site *Site) solarDetails(solar api.Rates) solarDetails { } if r, err := solar.At(time.Now()); err == nil { - if err := site.fcstEnergy.AddEnergy(nil, nil, r.Value); err != nil { + if err := site.collectors[metrics.Forecast].AddEnergy(nil, nil, r.Value); err != nil { site.log.ERROR.Printf("solar forecast collector: %v", err) } }