Enable modbus meter to read phase currents (#2072)

This commit is contained in:
andig 2021-12-19 11:01:07 +01:00 • committed by GitHub
parent 52852275ff
commit 0de4c93a3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 7 deletions

View file

@ -26,7 +26,7 @@ func init() {
registry.Add("modbus", NewModbusFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -f decorateModbus -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,SoC,func() (float64, error)"
//go:generate go run ../cmd/tools/decorate.go -f decorateModbus -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.Battery,SoC,func() (float64, error)"
// NewModbusFromConfig creates api.Meter from config
func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
@ -34,6 +34,7 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
Model string
modbus.Settings `mapstructure:",squash"`
Power, Energy, SoC string
Currents []string
Timeout time.Duration
}{
Power: "Power",
@ -107,6 +108,32 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
totalEnergy = m.totalEnergy
}
// decorate Meter with MeterCurrent
var currentsG func() (float64, float64, float64, error)
if len(cc.Currents) > 0 {
if len(cc.Currents) != 3 {
return nil, errors.New("need 3 currents")
}
var curr []func() (float64, error)
for _, cc := range cc.Currents {
var opCurrent modbus.Operation
cc = modbus.ReadingName(cc)
if err := modbus.ParseOperation(device, cc, &opCurrent); err != nil {
return nil, fmt.Errorf("invalid measurement for current: %s", cc)
}
c := func() (float64, error) {
return m.floatGetter(opCurrent)
}
curr = append(curr, c)
}
currentsG = collectCurrentProviders(curr)
}
// decorate soc reading
var soc func() (float64, error)
if cc.SoC != "" {
@ -118,7 +145,7 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
soc = m.soc
}
return decorateModbus(m, totalEnergy, soc), nil
return decorateModbus(m, totalEnergy, currentsG, soc), nil
}
// floatGetter executes configured modbus read operation and implements func() (float64, error)

View file

@ -6,12 +6,12 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateModbus(base api.Meter, meterEnergy func() (float64, error), battery func() (float64, error)) api.Meter {
func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error), battery func() (float64, error)) api.Meter {
switch {
case battery == nil && meterEnergy == nil:
case battery == nil && meterCurrent == nil && meterEnergy == nil:
return base
case battery == nil && meterEnergy != nil:
case battery == nil && meterCurrent == nil && meterEnergy != nil:
return &struct {
api.Meter
api.MeterEnergy
@ -22,7 +22,33 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), battery
},
}
case battery != nil && meterEnergy == nil:
case battery == nil && meterCurrent != nil && meterEnergy == nil:
return &struct {
api.Meter
api.MeterCurrent
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil:
return &struct {
api.Meter
api.Battery
@ -33,7 +59,7 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), battery
},
}
case battery != nil && meterEnergy != nil:
case battery != nil && meterCurrent == nil && meterEnergy != nil:
return &struct {
api.Meter
api.Battery
@ -47,6 +73,40 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), battery
meterEnergy: meterEnergy,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
@ -60,6 +120,14 @@ func (impl *decorateModbusBatteryImpl) SoC() (float64, error) {
return impl.battery()
}
type decorateModbusMeterCurrentImpl struct {
meterCurrent func() (float64, float64, float64, error)
}
func (impl *decorateModbusMeterCurrentImpl) Currents() (float64, float64, float64, error) {
return impl.meterCurrent()
}
type decorateModbusMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}