From 0de4c93a3a6f75565374a0294ca6cda2cb33bead Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 19 Dec 2021 11:01:07 +0100 Subject: [PATCH] Enable modbus meter to read phase currents (#2072) --- meter/modbus.go | 31 ++++++++++++++- meter/modbus_decorators.go | 78 +++++++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 7 deletions(-) diff --git a/meter/modbus.go b/meter/modbus.go index 3206a81cc..5f81b5547 100644 --- a/meter/modbus.go +++ b/meter/modbus.go @@ -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) diff --git a/meter/modbus_decorators.go b/meter/modbus_decorators.go index f3625a301..0a6e23e92 100644 --- a/meter/modbus_decorators.go +++ b/meter/modbus_decorators.go @@ -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) }