Curtailment devices: new device class for feed-in limitation (BC) (#32416)

This commit is contained in:
andig 2026-08-22 13:10:59 +02:00 • committed by GitHub
parent 7c0ddf1828
commit 8661c66fc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 819 additions and 149 deletions

View file

@ -7,11 +7,11 @@ import (
"strings"
)
const _ClassName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsshminfluxmessengersponsorshiploadpoint"
const _ClassName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsshminfluxmessengersponsorshiploadpointcurtailer"
var _ClassIndex = [...]uint8{0, 10, 15, 22, 29, 35, 42, 46, 50, 58, 69, 74, 84, 86, 90, 93, 99, 108, 119, 128}
var _ClassIndex = [...]uint8{0, 10, 15, 22, 29, 35, 42, 46, 50, 58, 69, 74, 84, 86, 90, 93, 99, 108, 119, 128, 137}
const _ClassLowerName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsshminfluxmessengersponsorshiploadpoint"
const _ClassLowerName = "configfilemeterchargervehicletariffcircuitsitemqttdatabasemodbusproxyeebusjavascriptgohemsshminfluxmessengersponsorshiploadpointcurtailer"
func (i Class) String() string {
i -= 1
@ -44,9 +44,10 @@ func _ClassNoOp() {
_ = x[ClassMessenger-(17)]
_ = x[ClassSponsorship-(18)]
_ = x[ClassLoadpoint-(19)]
_ = x[ClassCurtailer-(20)]
}
var _ClassValues = []Class{ClassConfigFile, ClassMeter, ClassCharger, ClassVehicle, ClassTariff, ClassCircuit, ClassSite, ClassMqtt, ClassDatabase, ClassModbusProxy, ClassEEBus, ClassJavascript, ClassGo, ClassHEMS, ClassSHM, ClassInflux, ClassMessenger, ClassSponsorship, ClassLoadpoint}
var _ClassValues = []Class{ClassConfigFile, ClassMeter, ClassCharger, ClassVehicle, ClassTariff, ClassCircuit, ClassSite, ClassMqtt, ClassDatabase, ClassModbusProxy, ClassEEBus, ClassJavascript, ClassGo, ClassHEMS, ClassSHM, ClassInflux, ClassMessenger, ClassSponsorship, ClassLoadpoint, ClassCurtailer}
var _ClassNameToValueMap = map[string]Class{
_ClassName[0:10]: ClassConfigFile,
@ -87,6 +88,8 @@ var _ClassNameToValueMap = map[string]Class{
_ClassLowerName[108:119]: ClassSponsorship,
_ClassName[119:128]: ClassLoadpoint,
_ClassLowerName[119:128]: ClassLoadpoint,
_ClassName[128:137]: ClassCurtailer,
_ClassLowerName[128:137]: ClassCurtailer,
}
var _ClassNames = []string{
@ -109,6 +112,7 @@ var _ClassNames = []string{
_ClassName[99:108],
_ClassName[108:119],
_ClassName[119:128],
_ClassName[128:137],
}
// ClassString retrieves an enum value from the enum constants string name.

View file

@ -57,6 +57,8 @@ func runConfigDelete(cmd *cobra.Command, args []string) {
deleteDevice[api.Tariff](c)
case templates.Circuit:
deleteDevice[api.Circuit](c)
case templates.Curtailer:
deleteDevice[api.Curtailer](c)
case templates.Hems:
deleteDevice[hems.API](c)
case templates.Loadpoint:

View file

@ -29,6 +29,7 @@ const (
ClassMessenger
ClassSponsorship
ClassLoadpoint
ClassCurtailer
)
// FatalError is an error that can be marshaled

View file

@ -15,7 +15,7 @@ import (
)
var references struct {
meter, charger, vehicle, circuit, tariff []string
meter, charger, vehicle, circuit, tariff, curtailer []string
}
func collectRefs(conf globalconfig.All) error {
@ -56,8 +56,9 @@ func collectRefs(conf globalconfig.All) error {
func collectSiteRefs(conf globalconfig.All) error {
var refs struct {
Meters core.MetersConfig `mapstructure:"meters"` // Meter references
Other map[string]any `mapstructure:",remain"`
Meters core.MetersConfig `mapstructure:"meters"` // Meter references
Curtailers []string `mapstructure:"curtailers"` // Curtailment device references
Other map[string]any `mapstructure:",remain"`
}
if err := util.DecodeOther(conf.Site, &refs); err != nil {
@ -70,6 +71,7 @@ func collectSiteRefs(conf globalconfig.All) error {
references.meter = append(references.meter, refs.Meters.ExtMetersRef...)
references.meter = append(references.meter, refs.Meters.AuxMetersRef...)
references.meter = append(references.meter, refs.Meters.ConsumerMetersRef...)
references.curtailer = append(references.curtailer, refs.Curtailers...)
// append devices from settings
if v, err := settings.String(keys.GridMeter); err == nil && v != "" {
@ -82,6 +84,10 @@ func collectSiteRefs(conf globalconfig.All) error {
}
}
if v, err := settings.String(keys.Curtailers); err == nil && v != "" {
references.curtailer = append(references.curtailer, strings.Split(v, ",")...)
}
return nil
}

View file

@ -25,6 +25,7 @@ import (
"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/curtailer"
"github.com/evcc-io/evcc/hems"
hemsapi "github.com/evcc-io/evcc/hems/hems"
"github.com/evcc-io/evcc/hems/shm"
@ -400,6 +401,50 @@ func configureMeters(static []config.Named, names ...string) error {
return eg.Wait()
}
func configureCurtailers(static []config.Named, names ...string) error {
var eg errgroup.Group
for i, cc := range static {
if cc.Name == "" {
return fmt.Errorf("cannot create curtailer %d: missing name", i+1)
}
// configure all, if no name refs are given
if len(names) > 0 && !slices.Contains(names, cc.Name) {
continue
}
if err := nameValid(cc.Name); err != nil {
log.WARN.Printf("create curtailer %d: %v", i+1, err)
}
eg.Go(func() error {
return staticInstance("curtailer", cc, curtailer.NewFromConfig, config.Curtailers())
})
}
// append devices from database
configurable, err := config.ConfigurationsByClass(templates.Curtailer)
if err != nil {
return err
}
for _, conf := range configurable {
eg.Go(func() error {
cc := conf.Named()
// always skip unreferenced db devices
if !slices.Contains(names, cc.Name) {
return nil
}
return configurableInstance("curtailer", &conf, curtailer.NewFromConfig, config.Curtailers())
})
}
return eg.Wait()
}
func configureChargers(static []config.Named, names ...string) error {
var eg errgroup.Group
@ -1287,6 +1332,10 @@ func configureDevices(conf globalconfig.All) error {
errs = append(errs, &ClassError{ClassCircuit, err})
}
if err := configureCurtailers(conf.Curtailers, references.curtailer...); err != nil {
errs = append(errs, &ClassError{ClassCurtailer, err})
}
return joinErrors(errs...)
}