Meter (SunSpec): read max AC power from nameplate (#32732)
This commit is contained in:
parent
fb35c0905f
commit
e2c59fc6e1
17 changed files with 91 additions and 24 deletions
|
|
@ -24,7 +24,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.M
|
|||
measurement.Curtailer `mapstructure:",squash"` // optional
|
||||
|
||||
// pv
|
||||
pvMaxACPower `mapstructure:",squash"`
|
||||
pvMaxACPowerCtx `mapstructure:",squash"`
|
||||
|
||||
// battery
|
||||
batteryCapacityCtx `mapstructure:",squash"`
|
||||
|
|
@ -127,7 +127,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.M
|
|||
implement.May(m, implement.PhaseCurrents(currentsG))
|
||||
implement.May(m, implement.PhaseVoltages(voltagesG))
|
||||
implement.May(m, implement.PhasePowers(powersG))
|
||||
implement.May(m, implement.MaxACPowerGetter(cc.pvMaxACPower.Decorator()))
|
||||
implement.May(m, implement.MaxACPowerGetter(cc.pvMaxACPowerCtx.Decorator(ctx)))
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,27 @@ func TestPV(t *testing.T) {
|
|||
_, ok := api.Cap[api.MaxACPowerGetter](m)
|
||||
assert.True(t, ok, "MaxACPowerGetter")
|
||||
}
|
||||
|
||||
func TestPVMaxACPowerPlugin(t *testing.T) {
|
||||
newMeter := func(maxacpower any) api.Meter {
|
||||
m, err := NewConfigurableFromConfig(t.Context(), map[string]any{
|
||||
"power": map[string]any{"source": "const", "value": 1000},
|
||||
"maxacpower": maxacpower,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return m
|
||||
}
|
||||
|
||||
m := newMeter(map[string]any{"source": "const", "value": 5000})
|
||||
mp, ok := api.Cap[api.MaxACPowerGetter](m)
|
||||
require.True(t, ok, "MaxACPowerGetter")
|
||||
assert.Equal(t, 5000.0, mp.MaxACPower())
|
||||
|
||||
// device without rating must not be decorated
|
||||
m = newMeter(map[string]any{"source": "error", "error": "ErrNotAvailable"})
|
||||
_, ok = api.Cap[api.MaxACPowerGetter](m)
|
||||
assert.False(t, ok, "MaxACPowerGetter")
|
||||
}
|
||||
func TestBattery(t *testing.T) {
|
||||
m, err := NewConfigurableFromConfig(t.Context(), map[string]any{
|
||||
"power": map[string]any{
|
||||
|
|
|
|||
|
|
@ -43,11 +43,11 @@ func resolveFloat(ctx context.Context, v any) (func() float64, error) {
|
|||
case nil:
|
||||
return nil, nil
|
||||
case int:
|
||||
return staticCapacity(float64(v)), nil
|
||||
return staticFloat(float64(v)), nil
|
||||
case int64:
|
||||
return staticCapacity(float64(v)), nil
|
||||
return staticFloat(float64(v)), nil
|
||||
case float64:
|
||||
return staticCapacity(v), nil
|
||||
return staticFloat(v), nil
|
||||
default:
|
||||
var cfg plugin.Config
|
||||
if err := util.DecodeOther(v, &cfg); err != nil {
|
||||
|
|
@ -67,7 +67,7 @@ func resolveFloat(ctx context.Context, v any) (func() float64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func staticCapacity(f float64) func() float64 {
|
||||
func staticFloat(f float64) func() float64 {
|
||||
if f == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
)
|
||||
|
||||
type pvMaxACPower struct {
|
||||
MaxACPower float64
|
||||
}
|
||||
|
|
@ -8,10 +14,23 @@ type pvMaxACPower struct {
|
|||
|
||||
// Decorator returns the max AC power decorator
|
||||
func (m *pvMaxACPower) Decorator() func() float64 {
|
||||
if m.MaxACPower == 0 {
|
||||
return nil
|
||||
}
|
||||
return func() float64 {
|
||||
return m.MaxACPower
|
||||
}
|
||||
return staticFloat(m.MaxACPower)
|
||||
}
|
||||
|
||||
type pvMaxACPowerCtx struct {
|
||||
MaxACPower any // static W value or float plugin
|
||||
}
|
||||
|
||||
// var _ api.MaxACPowerGetter = (*pvMaxACPowerCtx)(nil)
|
||||
|
||||
// Decorator returns the max AC power decorator. MaxACPower may be a static value or a
|
||||
// float plugin like the SunSpec nameplate rating. Since the rating is constant, the
|
||||
// plugin is read once. Unavailable or zero means not configured.
|
||||
func (m *pvMaxACPowerCtx) Decorator(ctx context.Context) func() float64 {
|
||||
get, err := resolveFloat(ctx, m.MaxACPower)
|
||||
if err != nil {
|
||||
// not all devices expose the rating- must not fail configuration
|
||||
util.NewLogger("meter").WARN.Printf("maxacpower: %v", err)
|
||||
}
|
||||
return staticFloat(floatOr0(get))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue