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
|
|
@ -685,7 +685,9 @@ func (site *Site) updatePvMeters() {
|
|||
}
|
||||
|
||||
if m, ok := api.Cap[api.MaxACPowerGetter](meter); ok {
|
||||
if dc := power - m.MaxACPower(); dc > 0 && power > 0 {
|
||||
// unknown rating (0) must not turn the entire production into excess DC
|
||||
if maxACPower := m.MaxACPower(); maxACPower > 0 && power > maxACPower {
|
||||
dc := power - maxACPower
|
||||
mm[i].ExcessDCPower = dc
|
||||
site.log.DEBUG.Printf("pv %d excess DC: %.0fW", i+1, dc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ params:
|
|||
choice: ["tcpip"]
|
||||
id: 1
|
||||
- name: maxacpower
|
||||
service: modbus/read?address=40227&type=holding&encoding=uint16&scale=1&{modbus}
|
||||
usages: ["pv"]
|
||||
deprecated: true # read from nameplate
|
||||
render: |
|
||||
type: custom
|
||||
# sunspec model 701 (DER AC Measurement)
|
||||
|
|
@ -96,5 +95,5 @@ render: |
|
|||
percent = int(limit)
|
||||
}
|
||||
percent
|
||||
maxacpower: {{ .maxacpower }}
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ params:
|
|||
- name: port
|
||||
default: 502
|
||||
- name: maxacpower
|
||||
deprecated: true # read from nameplate
|
||||
render: |
|
||||
type: custom
|
||||
# Fronius Argeno: AC output via SunSpec inverter model 113 (float, primary)
|
||||
|
|
@ -38,4 +39,4 @@ render: |
|
|||
- 113:WH
|
||||
- 103:WH
|
||||
scale: 0.001
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower-tcp" . }}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ params:
|
|||
generic: Integer
|
||||
deprecated: true
|
||||
- name: maxacpower
|
||||
deprecated: true # read from nameplate
|
||||
- preset: battery-params
|
||||
- name: maxchargerate
|
||||
render: |
|
||||
|
|
@ -167,7 +168,7 @@ render: |
|
|||
id: 1
|
||||
value: 160:2:DCWH # mppt 2
|
||||
scale: 0.001
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower-tcp" . }}
|
||||
curtail:
|
||||
source: sequence
|
||||
set:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ params:
|
|||
de: "Zähleradresse von Primär- oder Sekundärzählern. Auf der Weboberfläche des Wechselrichters kann nur die Adresse des ersten Zählers (z.B. 200) eingestellt werden. Zusätzliche Zähler erhalten eine aufsteigende Nummer (z.B: 201)."
|
||||
usages: ["grid"]
|
||||
- name: maxacpower
|
||||
deprecated: true # read from nameplate
|
||||
- name: maxchargerate
|
||||
- preset: battery-params
|
||||
render: |
|
||||
|
|
@ -163,7 +164,7 @@ render: |
|
|||
id: 1
|
||||
value: 160:3:DCWH # mppt 3
|
||||
scale: 0.001
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower-tcp" . }}
|
||||
{{- end }}
|
||||
{{- if eq .usage "battery" }}
|
||||
power:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ params:
|
|||
default: little
|
||||
advanced: true
|
||||
- name: maxacpower
|
||||
service: modbus/read?address=531&type=holding&encoding=uint16&{modbus}
|
||||
deprecated: true # read from nameplate
|
||||
- preset: battery-params
|
||||
- name: capacity
|
||||
service: modbus/read?address=1068&type=holding&encoding=float32s&scale=0.001&{modbus}
|
||||
|
|
@ -74,7 +74,7 @@ render: |
|
|||
type: holding
|
||||
encoding: {{ if (eq .endianness "big") }}float32{{ else }}float32s{{ end }}
|
||||
scale: 0.001 # Wh -> kWh
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
# feed-in curtailment via active power limit (SunSpec model 123, WMaxLimPct @ 40217).
|
||||
# written as a single register (fn 06); the Plenticore rejects write multiple (fn 16) here.
|
||||
# WMaxLim_Ena (register 40221) is enabled by default and left to the inverter
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ render: |
|
|||
address: 0xE174 # Battery 1 Instantaneous Power
|
||||
type: holding
|
||||
decode: float32nans
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
curtail:
|
||||
source: sequence
|
||||
set:
|
||||
|
|
|
|||
|
|
@ -149,4 +149,5 @@ render: |
|
|||
percent = 100
|
||||
}
|
||||
percent
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ params:
|
|||
- name: modbus
|
||||
choice: ["tcpip", "rs485"]
|
||||
- name: maxacpower
|
||||
deprecated: true # read from nameplate
|
||||
render: |
|
||||
type: custom
|
||||
# sunspec model 203 (int+sf)/ 213 (float) meter
|
||||
|
|
@ -88,4 +89,4 @@ render: |
|
|||
percent = int(limit)
|
||||
}
|
||||
percent
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ params:
|
|||
- name: modbus
|
||||
choice: ["tcpip", "rs485"]
|
||||
- name: maxacpower
|
||||
deprecated: true # read from nameplate
|
||||
- preset: battery-params
|
||||
render: |
|
||||
type: custom
|
||||
|
|
@ -106,7 +107,7 @@ render: |
|
|||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:2:DCWH # mppt 2
|
||||
scale: 0.001
|
||||
maxacpower: {{ .maxacpower }} # W
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
{{- end }}
|
||||
{{- if eq .usage "battery" }}
|
||||
power:
|
||||
|
|
|
|||
|
|
@ -87,3 +87,4 @@ render: |
|
|||
percent = int(limit)
|
||||
}
|
||||
percent
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ render: |
|
|||
- 103:WH
|
||||
- 113:WH
|
||||
scale: 0.001
|
||||
{{- include "sunspec-maxacpower" . }}
|
||||
{{- end }}
|
||||
{{- if eq .usage "battery" }}
|
||||
power:
|
||||
|
|
|
|||
18
templates/definition/meter/sunspec.tpl
Normal file
18
templates/definition/meter/sunspec.tpl
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{{ define "sunspec-maxacpower" }}
|
||||
maxacpower: # nameplate rating
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 120:WRtg
|
||||
- 702:WMaxRtg
|
||||
{{- end }}
|
||||
|
||||
{{ define "sunspec-maxacpower-tcp" }}
|
||||
maxacpower: # nameplate rating
|
||||
source: sunspec
|
||||
uri: {{ joinHostPort .host .port }}
|
||||
id: 1
|
||||
value:
|
||||
- 120:WRtg
|
||||
- 702:WMaxRtg
|
||||
{{- end }}
|
||||
Loading…
Add table
Add a link
Reference in a new issue