Ext/Aux meters: track energy metrics (#29836)
This commit is contained in:
parent
468dfa69af
commit
a74982acc5
4 changed files with 52 additions and 20 deletions
|
|
@ -14,6 +14,7 @@ const (
|
|||
PV = "pv"
|
||||
Home = "home" // meter and group (virtual measurement)
|
||||
Loadpoint = "loadpoint"
|
||||
Meter = "meter" // generic meter (ext/aux)
|
||||
Forecast = "forecast"
|
||||
)
|
||||
|
||||
|
|
|
|||
66
core/site.go
66
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue