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

28
curtailer/curtailer.go Normal file
View file

@ -0,0 +1,28 @@
package curtailer
import (
"context"
"fmt"
"strings"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
reg "github.com/evcc-io/evcc/util/registry"
)
var registry = reg.New[api.Curtailer]("curtailer")
// NewFromConfig creates curtailer from configuration
func NewFromConfig(ctx context.Context, typ string, other map[string]any) (api.Curtailer, error) {
factory, err := registry.Get(strings.ToLower(typ))
if err != nil {
return nil, err
}
v, err := factory(ctx, other)
if err != nil {
err = fmt.Errorf("cannot create curtailer type '%s': %w", util.TypeWithTemplateName(typ, other), err)
}
return v, err
}

45
curtailer/custom.go Normal file
View file

@ -0,0 +1,45 @@
package curtailer
import (
"context"
"errors"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/meter/measurement"
"github.com/evcc-io/evcc/util"
)
func init() {
registry.AddCtx(api.Custom, NewConfigurableFromConfig)
}
// NewConfigurableFromConfig creates a curtailer from config
func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.Curtailer, error) {
cc := struct {
measurement.Curtailer `mapstructure:",squash"`
}{}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
curtailS, curtailedG, err := cc.Curtailer.Configure(ctx)
if err != nil {
return nil, err
}
if curtailS == nil {
return nil, errors.New("missing curtail")
}
return implement.Curtailer(
func() (int, error) {
percent, err := curtailedG()
return int(percent), err
},
func(percent int) error {
return curtailS(int64(percent))
},
), nil
}

21
curtailer/template.go Normal file
View file

@ -0,0 +1,21 @@
package curtailer
import (
"context"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util/templates"
)
func init() {
registry.AddCtx("template", NewFromTemplateConfig)
}
func NewFromTemplateConfig(ctx context.Context, other map[string]any) (api.Curtailer, error) {
instance, err := templates.RenderInstance(templates.Curtailer, other)
if err != nil {
return nil, err
}
return NewFromConfig(ctx, instance.Type, instance.Other)
}

View file

@ -0,0 +1,21 @@
package curtailer
import (
"testing"
"github.com/evcc-io/evcc/util/templates"
"github.com/evcc-io/evcc/util/test"
)
var acceptable = []string{}
func TestTemplates(t *testing.T) {
templates.TestClass(t, templates.Curtailer, func(t *testing.T, values map[string]any) {
t.Helper()
if _, err := NewFromConfig(t.Context(), "template", values); err != nil && !test.Acceptable(err, acceptable) {
t.Log(values)
t.Error(err)
}
})
}