diff --git a/cmd/setup.go b/cmd/setup.go index bc870e3b7..c4d173a43 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -23,6 +23,7 @@ import ( "github.com/evcc-io/evcc/core/circuit" "github.com/evcc-io/evcc/core/keys" "github.com/evcc-io/evcc/core/loadpoint" + "github.com/evcc-io/evcc/core/metrics" coresettings "github.com/evcc-io/evcc/core/settings" "github.com/evcc-io/evcc/hems" hemsapi "github.com/evcc-io/evcc/hems/hems" @@ -1301,14 +1302,25 @@ func configureSite(conf map[string]any, loadpoints []*core.Loadpoint, tariffs *t return site, nil } +func newLoadpoint(idx int, name string, other map[string]any, settingsFn func(*util.Logger) coresettings.Settings) (*core.Loadpoint, error) { + log := util.NewLoggerWithLoadpoint("lp-"+strconv.Itoa(idx), idx) + + collector, err := metrics.NewCollector(metrics.Loadpoint, name) + if err != nil { + return nil, err + } + + return core.NewLoadpointFromConfig(log, settingsFn(log), collector, other) +} + func configureLoadpoints(conf globalconfig.All) error { for id, cc := range conf.Loadpoints { - cc.Name = "lp-" + strconv.Itoa(id+1) + idx := id + 1 + cc.Name = "lp-" + strconv.Itoa(idx) - log := util.NewLoggerWithLoadpoint(cc.Name, id+1) - settings := coresettings.NewDatabaseSettingsAdapter(fmt.Sprintf("lp%d.", id+1)) - - instance, err := core.NewLoadpointFromConfig(log, settings, cc.Other) + instance, err := newLoadpoint(idx, cc.Name, cc.Other, func(*util.Logger) coresettings.Settings { + return coresettings.NewDatabaseSettingsAdapter(fmt.Sprintf("lp%d.", idx)) + }) if err != nil { return &DeviceError{cc.Name, err} } @@ -1326,19 +1338,16 @@ func configureLoadpoints(conf globalconfig.All) error { for _, conf := range configurable { cc := conf.Named() - - id := len(config.Loadpoints().Devices()) - name := "lp-" + strconv.Itoa(id+1) - log := util.NewLoggerWithLoadpoint(name, id+1) - - settings := coresettings.NewConfigSettingsAdapter(log, &conf) + idx := len(config.Loadpoints().Devices()) + 1 dynamic, static, err := loadpoint.SplitConfig(cc.Other) if err != nil { return &DeviceError{cc.Name, err} } - instance, err := core.NewLoadpointFromConfig(log, settings, static) + instance, err := newLoadpoint(idx, cc.Name, static, func(log *util.Logger) coresettings.Settings { + return coresettings.NewConfigSettingsAdapter(log, &conf) + }) if err != nil { err = &DeviceError{cc.Name, err} } diff --git a/core/loadpoint.go b/core/loadpoint.go index a1aabd439..bf46a1df1 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -17,6 +17,7 @@ import ( "github.com/evcc-io/evcc/core/coordinator" "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/planner" "github.com/evcc-io/evcc/core/session" "github.com/evcc-io/evcc/core/settings" @@ -137,10 +138,11 @@ type Loadpoint struct { chargeRater api.ChargeRater chargedAtStartup float64 // session energy at startup - circuit api.Circuit // Circuit - chargeMeter api.Meter // Charger usage meter - vehicle api.Vehicle // Currently active vehicle - defaultVehicle api.Vehicle // Default vehicle (disables detection) + circuit api.Circuit // Circuit + chargeMeter api.Meter // Charger usage meter + chargeEnergy *metrics.Collector // Charger usage collector + vehicle api.Vehicle // Currently active vehicle + defaultVehicle api.Vehicle // Default vehicle (disables detection) coordinator coordinator.API socEstimator *soc.Estimator @@ -183,7 +185,7 @@ type Loadpoint struct { } // NewLoadpointFromConfig creates a new loadpoint -func NewLoadpointFromConfig(log *util.Logger, settings settings.Settings, other map[string]any) (*Loadpoint, error) { +func NewLoadpointFromConfig(log *util.Logger, settings settings.Settings, collector *metrics.Collector, other map[string]any) (*Loadpoint, error) { lp := NewLoadpoint(log, settings) if err := util.DecodeOther(other, lp); err != nil { return lp, err @@ -266,6 +268,10 @@ func NewLoadpointFromConfig(log *util.Logger, settings settings.Settings, other } lp.configureChargerType(lp.charger) + // add collector + if lp.chargeMeter != nil { + lp.chargeEnergy = collector + } // phase switching defaults based on charger capabilities if !lp.hasPhaseSwitching() { @@ -1733,8 +1739,18 @@ func (lp *Loadpoint) publishChargeProgress() { // TODO deprecated: use sessionEnergy instead lp.publish(keys.ChargedEnergy, lp.GetChargedEnergy()) lp.publish(keys.ChargeDuration, lp.chargeDuration) + + // update energy, prefer totals + var importTotal *float64 if api.HasCap[api.MeterEnergy](lp.chargeMeter) { - lp.publish(keys.ChargeTotalImport, lp.chargeMeterTotal()) + if f := lp.chargeMeterTotal(); f > 0 { + lp.publish(keys.ChargeTotalImport, f) + importTotal = &f + } + } + + if lp.chargeEnergy != nil { + lp.chargeEnergy.AddEnergy(importTotal, nil, lp.chargePower) } } diff --git a/core/metrics/collector.go b/core/metrics/collector.go index 5b412ae91..2a18ca9ac 100644 --- a/core/metrics/collector.go +++ b/core/metrics/collector.go @@ -9,9 +9,11 @@ import ( const ( // groups - Grid = "grid" - PV = "pv" - Home = "home" // meter and group (virtual measurement) + Battery = "battery" + Grid = "grid" + PV = "pv" + Home = "home" // meter and group (virtual measurement) + Loadpoint = "loadpoint" ) type Collector struct { diff --git a/core/metrics/db.go b/core/metrics/db.go index e5031a54d..4b341f928 100644 --- a/core/metrics/db.go +++ b/core/metrics/db.go @@ -36,24 +36,13 @@ func SetupSchema() error { m := db.Instance.Migrator() // entites: create entity first to make sure foreign keys for existing data work - hasTable := m.HasTable(new(entity)) if err := db.Instance.AutoMigrate(new(entity)); err != nil { return err } - // entites: add entity id 1 - if hasTable { - var res entity - if err := db.Instance.Where(&entity{Id: 1, Name: Home}).FirstOrCreate(&res).Error; err != nil { - return err - } - - if res.Group == "" { - res.Group = Home - if err := db.Instance.Save(&res).Error; err != nil { - return err - } - } + // ensure home entity exists (reserves id=1 for legacy meter FK references) + if _, err := createEntity(Home, Home); err != nil { + return err } // drop obsolete indexes diff --git a/server/http_config_loadpoint_handler.go b/server/http_config_loadpoint_handler.go index 5f18c50f8..666d75785 100644 --- a/server/http_config_loadpoint_handler.go +++ b/server/http_config_loadpoint_handler.go @@ -165,7 +165,8 @@ func newLoadpointHandler() http.HandlerFunc { } settings := coresettings.NewConfigSettingsAdapter(log, &conf) - instance, err := core.NewLoadpointFromConfig(log, settings, static) + + instance, err := core.NewLoadpointFromConfig(log, settings, nil, static) if err != nil { conf.Delete() jsonError(w, http.StatusBadRequest, err)