diff --git a/meter/battery.go b/meter/battery.go index 6f7bd7ce1f..1a5a87dd20 100644 --- a/meter/battery.go +++ b/meter/battery.go @@ -8,8 +8,8 @@ type battery struct { MinSoc, MaxSoc float64 } -// Decorator returns an api.BatteryController decorator -func (m *battery) BatteryController(socG func() (float64, error), limitSocS func(float64) error) func(api.BatteryMode) error { +// LimitController returns an api.BatteryController decorator +func (m *battery) LimitController(socG func() (float64, error), limitSocS func(float64) error) func(api.BatteryMode) error { return func(mode api.BatteryMode) error { switch mode { case api.BatteryNormal: @@ -30,3 +30,10 @@ func (m *battery) BatteryController(socG func() (float64, error), limitSocS func } } } + +// ModeController returns an api.BatteryController decorator +func (m *battery) ModeController(modeS func(int64) error) func(api.BatteryMode) error { + return func(mode api.BatteryMode) error { + return modeS(int64(mode)) + } +} diff --git a/meter/meter.go b/meter/meter.go index 5bb00eb706..3a2743473c 100644 --- a/meter/meter.go +++ b/meter/meter.go @@ -25,10 +25,11 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) Powers []provider.Config // optional // battery - capacity `mapstructure:",squash"` - battery `mapstructure:",squash"` - Soc *provider.Config // optional - LimitSoc *provider.Config // optional + capacity `mapstructure:",squash"` + battery `mapstructure:",squash"` + Soc *provider.Config // optional + LimitSoc *provider.Config // optional + BatteryMode *provider.Config // optional }{ battery: battery{ MinSoc: 20, @@ -84,13 +85,23 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) } var batModeS func(api.BatteryMode) error - if cc.Soc != nil && cc.LimitSoc != nil { + + switch { + case cc.Soc != nil && cc.LimitSoc != nil: limitSocS, err := provider.NewFloatSetterFromConfig("limitSoc", *cc.LimitSoc) if err != nil { return nil, fmt.Errorf("battery limit soc: %w", err) } - batModeS = cc.battery.BatteryController(socG, limitSocS) + batModeS = cc.battery.LimitController(socG, limitSocS) + + case cc.BatteryMode != nil: + modeS, err := provider.NewIntSetterFromConfig("mode", *cc.BatteryMode) + if err != nil { + return nil, fmt.Errorf("battery mode: %w", err) + } + + batModeS = cc.battery.ModeController(modeS) } res := m.Decorate(totalEnergyG, currentsG, voltagesG, powersG, socG, cc.capacity.Decorator(), batModeS) diff --git a/provider/map.go b/provider/map.go new file mode 100644 index 0000000000..6597efd256 --- /dev/null +++ b/provider/map.go @@ -0,0 +1,53 @@ +package provider + +import ( + "fmt" + + "github.com/evcc-io/evcc/util" +) + +type mapProvider struct { + values map[int64]int64 + set Config +} + +func init() { + registry.Add("map", NewMapFromConfig) +} + +// NewMapFromConfig creates type conversion provider +func NewMapFromConfig(other map[string]interface{}) (Provider, error) { + var cc struct { + Values map[int64]int64 + Set Config + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + if len(cc.Values) == 0 { + return nil, fmt.Errorf("missing values") + } + + o := &mapProvider{ + set: cc.Set, + values: cc.Values, + } + + return o, nil +} + +var _ SetIntProvider = (*mapProvider)(nil) + +func (o *mapProvider) IntSetter(param string) (func(int64) error, error) { + set, err := NewIntSetterFromConfig(param, o.set) + + return func(val int64) error { + m, ok := o.values[val] + if !ok { + return fmt.Errorf("value %d not found", val) + } + return set(m) + }, err +} diff --git a/templates/definition/meter/solaredge-hybrid.yaml b/templates/definition/meter/solaredge-hybrid.yaml index 871f1219fd..7ca2ee4231 100644 --- a/templates/definition/meter/solaredge-hybrid.yaml +++ b/templates/definition/meter/solaredge-hybrid.yaml @@ -20,8 +20,8 @@ params: advanced: true render: | type: custom - power: {{- if eq .usage "grid" }} + power: source: modbus {{- include "modbus" . | indent 2 }} timeout: {{ .timeout }} @@ -31,6 +31,7 @@ render: | scale: -1 {{- end }} {{- if eq .usage "pv" }} + power: source: calc add: - source: modbus @@ -47,6 +48,7 @@ render: | decode: float32s {{- end }} {{- if eq .usage "battery" }} + power: source: modbus {{- include "modbus" . | indent 2 }} timeout: {{ .timeout }} @@ -63,6 +65,20 @@ render: | address: 62852 # Battery 1 State of Energy (SOE) type: holding decode: float32s + batterymode: + source: map + values: + 1: 7 # normal + 2: 1 # hold + 3: 3 # charge + set: + source: modbus + {{- include "modbus" . | indent 4 }} + timeout: {{ .timeout }} + register: + address: 0xE00A # Battery mode + type: writesingle + decode: uint16 {{- if .capacity }} capacity: {{ .capacity }} # kWh {{- end }}