Add phase powers and currents (#5539)

This commit is contained in:
premultiply 2022-12-29 12:43:29 +01:00 • committed by andig
parent b0c03705cb
commit aed17532b0
23 changed files with 2060 additions and 151 deletions

View file

@ -92,21 +92,31 @@ func (a ActionConfig) String() string {
return strings.Join(s, ", ")
}
// Meter is able to provide current power in W
// Meter is able to provide total active power in W
type Meter interface {
CurrentPower() (float64, error)
}
// MeterEnergy is able to provide current energy in kWh
// MeterEnergy is able to provide total energy in kWh
type MeterEnergy interface {
TotalEnergy() (float64, error)
}
// MeterCurrent is able to provide per-line current A
// MeterCurrent is able to provide per-phase current A
type MeterCurrent interface {
Currents() (float64, float64, float64, error)
}
// MeterVoltage is able to provide per-phase voltage V
type MeterVoltage interface {
Voltages() (float64, float64, float64, error)
}
// MeterPower is able to provide signed per-phase power W
type MeterPower interface {
Powers() (float64, float64, float64, error)
}
// Battery is able to provide battery Soc in %
type Battery interface {
Soc() (float64, error)

View file

@ -43,6 +43,7 @@ const (
abbRegStatus = 0x400C // Charging state 2 unsigned RO available
abbRegGetCurrent = 0x400E // Current charging current limit 2 0.001 A unsigned RO
abbRegCurrents = 0x4010 // Charging current phases 6 0.001 A unsigned RO available
abbRegVoltages = 0x4016 // Voltage phases 6 0.1 V unsigned RO available
abbRegPower = 0x401C // Active power 2 1 W unsigned RO available
abbRegEnergy = 0x401E // Energy delivered in charging session 2 1 Wh unsigned RO available
abbRegSetCurrent = 0x4100 // Set charging current limit 2 0.001 A unsigned WO available
@ -225,6 +226,23 @@ func (wb *ABB) Currents() (float64, float64, float64, error) {
return curr[0], curr[1], curr[2], nil
}
var _ api.MeterVoltage = (*ABB)(nil)
// Voltages implements the api.MeterVoltage interface
func (wb *ABB) Voltages() (float64, float64, float64, error) {
b, err := wb.conn.ReadHoldingRegisters(abbRegVoltages, 6)
if err != nil {
return 0, 0, 0, err
}
var volt [3]float64
for l := 0; l < 3; l++ {
volt[l] = float64(binary.BigEndian.Uint32(b[4*l:])) / 10
}
return volt[0], volt[1], volt[2], nil
}
// var _ api.PhaseSwitcher = (*ABB)(nil)
// // Phases1p3p implements the api.PhaseSwitcher interface

View file

@ -49,6 +49,7 @@ const (
bendRegCurrents = 212 // Currents from primary meter (mA)
bendRegTotalEnergy = 218 // Total Energy from primary meter (Wh)
bendRegActivePower = 220 // Active Power from primary meter (W)
bendRegVoltages = 222 // Voltages of the ocpp meter (V)
bendRegChargedEnergyLegacy = 705 // Sum of charged energy for the current session (Wh)
bendRegChargingDurationLegacy = 709 // Duration since beginning of charge (Seconds)
bendRegChargedEnergy = 716 // Sum of charged energy for the current session (Wh)
@ -81,7 +82,7 @@ func NewBenderCCFromConfig(other map[string]interface{}) (api.Charger, error) {
return NewBenderCC(cc.URI, cc.ID)
}
//go:generate go run ../cmd/tools/decorate.go -f decorateBenderCC -b *BenderCC -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.ChargeRater,ChargedEnergy,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Identifier,Identify,func() (string, error)"
//go:generate go run ../cmd/tools/decorate.go -f decorateBenderCC -b *BenderCC -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.MeterVoltage,Voltages,func() (float64, float64, float64, error)" -t "api.ChargeRater,ChargedEnergy,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Identifier,Identify,func() (string, error)"
// NewBenderCC creates BenderCC charger
func NewBenderCC(uri string, id uint8) (api.Charger, error) {
@ -110,6 +111,7 @@ func NewBenderCC(uri string, id uint8) (api.Charger, error) {
var (
currentPower func() (float64, error)
currents func() (float64, float64, float64, error)
voltages func() (float64, float64, float64, error)
chargedEnergy func() (float64, error)
totalEnergy func() (float64, error)
identify func() (string, error)
@ -124,6 +126,7 @@ func NewBenderCC(uri string, id uint8) (api.Charger, error) {
if b, err := wb.conn.ReadHoldingRegisters(reg, 2); err == nil && binary.BigEndian.Uint32(b) != math.MaxUint32 {
currentPower = wb.currentPower
currents = wb.currents
voltages = wb.voltages
chargedEnergy = wb.chargedEnergy
totalEnergy = wb.totalEnergy
}
@ -133,7 +136,7 @@ func NewBenderCC(uri string, id uint8) (api.Charger, error) {
identify = wb.identify
}
return decorateBenderCC(wb, currentPower, currents, chargedEnergy, totalEnergy, identify), nil
return decorateBenderCC(wb, currentPower, currents, voltages, chargedEnergy, totalEnergy, identify), nil
}
// Status implements the api.Charger interface
@ -277,7 +280,7 @@ func (wb *BenderCC) totalEnergy() (float64, error) {
return float64(binary.BigEndian.Uint32(b)) / 1e3, nil
}
// Currents implements the api.MeterCurrent interface
// currents implements the api.MeterCurrent interface
func (wb *BenderCC) currents() (float64, float64, float64, error) {
b, err := wb.conn.ReadHoldingRegisters(bendRegCurrents, 6)
if err != nil {
@ -292,6 +295,21 @@ func (wb *BenderCC) currents() (float64, float64, float64, error) {
return curr[0], curr[1], curr[2], nil
}
// voltages implements the api.MeterVoltage interface
func (wb *BenderCC) voltages() (float64, float64, float64, error) {
b, err := wb.conn.ReadHoldingRegisters(bendRegVoltages, 6)
if err != nil {
return 0, 0, 0, err
}
var volt [3]float64
for l := 0; l < 3; l++ {
volt[l] = float64(binary.BigEndian.Uint32(b[4*l : 4*(l+1)]))
}
return volt[0], volt[1], volt[2], nil
}
// identify implements the api.Identifier interface
func (wb *BenderCC) identify() (string, error) {
if !wb.legacy {

View file

@ -6,12 +6,12 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurrent func() (float64, float64, float64, error), chargeRater func() (float64, error), meterEnergy func() (float64, error), identifier func() (string, error)) api.Charger {
func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurrent func() (float64, float64, float64, error), meterVoltage func() (float64, float64, float64, error), chargeRater func() (float64, error), meterEnergy func() (float64, error), identifier func() (string, error)) api.Charger {
switch {
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return base
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Meter
@ -22,7 +22,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.MeterCurrent
@ -33,7 +33,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Meter
@ -48,7 +48,67 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.MeterVoltage
}{
BenderCC: base,
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Meter
api.MeterVoltage
}{
BenderCC: base,
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Meter
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -59,7 +119,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -74,7 +134,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -89,7 +149,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -108,7 +168,83 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Meter
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Meter
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.MeterEnergy
@ -119,7 +255,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Meter
@ -134,7 +270,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.MeterCurrent
@ -149,7 +285,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Meter
@ -168,7 +304,83 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Meter
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -183,7 +395,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -202,7 +414,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -221,7 +433,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -244,7 +456,99 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Meter
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier == nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -255,7 +559,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -270,7 +574,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -285,7 +589,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -304,7 +608,83 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.Meter
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.Meter
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -319,7 +699,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy == nil:
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -338,7 +718,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -357,7 +737,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy == nil:
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -380,7 +760,99 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.Meter
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy == nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.Meter
api.MeterCurrent
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -395,7 +867,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -414,7 +886,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -433,7 +905,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.Identifier
@ -456,7 +928,99 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.Meter
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater == nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.Identifier
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -475,7 +1039,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy != nil:
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -498,7 +1062,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -521,7 +1085,7 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy != nil:
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage == nil:
return &struct {
*BenderCC
api.ChargeRater
@ -547,6 +1111,114 @@ func decorateBenderCC(base *BenderCC, meter func() (float64, error), meterCurren
meterEnergy: meterEnergy,
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent == nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.Meter
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter == nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case chargeRater != nil && identifier != nil && meter != nil && meterCurrent != nil && meterEnergy != nil && meterVoltage != nil:
return &struct {
*BenderCC
api.ChargeRater
api.Identifier
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
BenderCC: base,
ChargeRater: &decorateBenderCCChargeRaterImpl{
chargeRater: chargeRater,
},
Identifier: &decorateBenderCCIdentifierImpl{
identifier: identifier,
},
Meter: &decorateBenderCCMeterImpl{
meter: meter,
},
MeterCurrent: &decorateBenderCCMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateBenderCCMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateBenderCCMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
}
return nil
@ -591,3 +1263,11 @@ type decorateBenderCCMeterEnergyImpl struct {
func (impl *decorateBenderCCMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateBenderCCMeterVoltageImpl struct {
meterVoltage func() (float64, float64, float64, error)
}
func (impl *decorateBenderCCMeterVoltageImpl) Voltages() (float64, float64, float64, error) {
return impl.meterVoltage()
}

View file

@ -37,7 +37,9 @@ type HeidelbergEC struct {
const (
hecRegVehicleStatus = 5 // Input
hecRegCurrents = 6 // Input 6,7,8
hecRegTemperature = 9 // Input
hecRegVoltages = 10 // Input 10,11,12
hecRegPower = 14 // Input
hecRegEnergy = 17 // Input
hecRegTimeoutConfig = 257 // Holding
@ -49,8 +51,6 @@ const (
hecStandbyDisabled = 4 // disable standby
)
var hecRegCurrents = []uint16{6, 7, 8}
func init() {
registry.Add("heidelberg", NewHeidelbergECFromConfig)
}
@ -244,17 +244,34 @@ var _ api.MeterCurrent = (*HeidelbergEC)(nil)
// Currents implements the api.MeterCurrent interface
func (wb *HeidelbergEC) Currents() (float64, float64, float64, error) {
var currents []float64
for _, regCurrent := range hecRegCurrents {
b, err := wb.conn.ReadInputRegisters(regCurrent, 1)
if err != nil {
return 0, 0, 0, err
}
currents = append(currents, float64(binary.BigEndian.Uint16(b))/10)
b, err := wb.conn.ReadInputRegisters(abbRegCurrents, 3)
if err != nil {
return 0, 0, 0, err
}
return currents[0], currents[1], currents[2], nil
var curr [3]float64
for l := 0; l < 3; l++ {
curr[l] = float64(binary.BigEndian.Uint16(b[2*l:])) / 10
}
return curr[0], curr[1], curr[2], nil
}
var _ api.MeterVoltage = (*HeidelbergEC)(nil)
// Voltages implements the api.MeterVoltage interface
func (wb *HeidelbergEC) Voltages() (float64, float64, float64, error) {
b, err := wb.conn.ReadInputRegisters(abbRegVoltages, 3)
if err != nil {
return 0, 0, 0, err
}
var volt [3]float64
for l := 0; l < 3; l++ {
volt[l] = float64(binary.BigEndian.Uint16(b[2*l:]))
}
return volt[0], volt[1], volt[2], nil
}
var _ api.Diagnosis = (*HeidelbergEC)(nil)

View file

@ -63,6 +63,22 @@ func (d *dumper) Dump(name string, v interface{}) {
}
}
if v, ok := v.(api.MeterVoltage); ok {
if u1, u2, u3, err := v.Voltages(); err != nil {
fmt.Fprintf(w, "Voltage L1..L3:\t%v\n", err)
} else {
fmt.Fprintf(w, "Voltage L1..L3:\t%.3gV %.3gV %.3gV\n", u1, u2, u3)
}
}
if v, ok := v.(api.MeterPower); ok {
if p1, p2, p3, err := v.Powers(); err != nil {
fmt.Fprintf(w, "Power L1..L3:\t%v\n", err)
} else {
fmt.Fprintf(w, "Power L1..L3:\t%.3gW %.3gW %.3gW\n", p1, p2, p3)
}
}
if v, ok := v.(api.Battery); ok {
var soc float64
var err error

View file

@ -48,6 +48,7 @@ const (
timerInactive = "inactive"
minActiveCurrent = 1.0 // minimum current at which a phase is treated as active
minActiveVoltage = 208 // minimum voltage at which a phase is treated as active
vehicleDetectInterval = 1 * time.Minute
vehicleDetectDuration = 10 * time.Minute
@ -1579,7 +1580,7 @@ func (lp *Loadpoint) updateChargeCurrents() {
if lp.charging() {
// Quine-McCluskey for (¬L1∧L2∧¬L3) ∨ (¬L1∧¬L2∧L3) ∨ (L1∧¬L2∧L3) ∨ (¬L1∧L2∧L3) -> ¬L1 ∧ L2 ∨ ¬L2 ∧ L3
if !(i1 > minActiveCurrent) && (i2 > minActiveCurrent) || !(i2 > minActiveCurrent) && (i3 > minActiveCurrent) {
lp.log.WARN.Printf("invalid phase wiring between charge meter and vehicle")
lp.log.WARN.Printf("invalid phase wiring between charge meter and charger")
}
var phases int
@ -1600,6 +1601,42 @@ func (lp *Loadpoint) updateChargeCurrents() {
}
}
// updateChargeVoltages uses MeterVoltage interface to count phases with nominal grid voltage
func (lp *Loadpoint) updateChargeVoltages() {
phaseMeter, ok := lp.chargeMeter.(api.MeterVoltage)
if !ok {
return // don't guess
}
u1, u2, u3, err := phaseMeter.Voltages()
if err != nil {
lp.log.ERROR.Printf("charge meter: %v", err)
return
}
chargeVoltages := []float64{u1, u2, u3}
lp.log.DEBUG.Printf("charge voltages: %.3gV", chargeVoltages)
lp.publish("chargeVoltages", chargeVoltages)
// Quine-McCluskey for (¬L1∧L2∧¬L3) ∨ (L1∧L2∧¬L3) ∨ (¬L1∧¬L2∧L3) ∨ (L1∧¬L2∧L3) ∨ (¬L1∧L2∧L3) -> ¬L1 ∧ L3 ∨ L2 ∧ ¬L3 ∨ ¬L2 ∧ L3
if !(u1 > minActiveVoltage) && (u3 > minActiveVoltage) || (u2 > minActiveVoltage) && !(u3 > minActiveVoltage) || !(u2 > minActiveVoltage) && (u3 > minActiveVoltage) {
lp.log.WARN.Printf("invalid phase wiring between charge meter and charger")
}
var phases int
if (u1 > minActiveVoltage) || (u2 > minActiveVoltage) || (u3 > minActiveVoltage) {
phases = 3
}
if (u1 > minActiveVoltage) && (u2 < minActiveVoltage) && (u3 < minActiveVoltage) {
phases = 1
}
if phases >= 1 {
lp.log.DEBUG.Printf("detected phases: %dp", phases)
lp.setPhases(phases)
}
}
// publish charged energy and duration
func (lp *Loadpoint) publishChargeProgress() {
if f, err := lp.chargeRater.ChargedEnergy(); err == nil {
@ -1747,6 +1784,7 @@ func (lp *Loadpoint) Update(sitePower float64, batteryBuffered bool) {
lp.publish("mode", mode)
// read and publish meters first- charge power has already been updated by the site
lp.updateChargeVoltages()
lp.updateChargeCurrents()
// update ChargeRater here to make sure initial meter update is caught

View file

@ -437,12 +437,35 @@ func (site *Site) updateMeters() error {
err := retryMeter("grid", site.gridMeter, &site.gridPower)
// voltages
if phaseMeter, ok := site.gridMeter.(api.MeterVoltage); err == nil && ok {
u1, u2, u3, err := phaseMeter.Voltages()
if err == nil {
site.log.DEBUG.Printf("grid voltages: %.3gV", []float64{u1, u2, u3})
site.publish("gridVoltages", []float64{u1, u2, u3})
} else {
site.log.ERROR.Printf("grid meter voltages: %v", err)
}
}
// powers
var p1, p2, p3 float64
if phaseMeter, ok := site.gridMeter.(api.MeterPower); err == nil && ok {
p1, p2, p3, err = phaseMeter.Powers()
if err == nil {
site.log.DEBUG.Printf("grid powers: %.3gW", []float64{p1, p2, p3})
site.publish("gridPowers", []float64{p1, p2, p3})
} else {
site.log.ERROR.Printf("grid meter powers: %v", err)
}
}
// currents
if phaseMeter, ok := site.gridMeter.(api.MeterCurrent); err == nil && ok {
i1, i2, i3, err := phaseMeter.Currents()
if err == nil {
site.log.DEBUG.Printf("grid currents: %.3gA", []float64{i1, i2, i3})
site.publish("gridCurrents", []float64{i1, i2, i3})
site.log.DEBUG.Printf("grid currents: %.3gA", []float64{util.SignFromPower(i1, p1), util.SignFromPower(i2, p2), util.SignFromPower(i3, p3)})
site.publish("gridCurrents", []float64{util.SignFromPower(i1, p1), util.SignFromPower(i2, p2), util.SignFromPower(i3, p3)})
} else {
site.log.ERROR.Printf("grid meter currents: %v", err)
}

View file

@ -13,7 +13,7 @@ func init() {
registry.Add(api.Custom, NewConfigurableFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -f decorateMeter -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)"
//go:generate go run ../cmd/tools/decorate.go -f decorateMeter -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.MeterVoltage,Voltages,func() (float64, float64, float64, error)" -t "api.MeterPower,Powers,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)"
// NewConfigurableFromConfig creates api.Meter from config
func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) {
@ -22,6 +22,8 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error)
Energy *provider.Config // optional
Soc *provider.Config // optional
Currents []provider.Config // optional
Voltages []provider.Config // optional
Powers []provider.Config // optional
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -35,7 +37,7 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error)
m, _ := NewConfigurable(power)
// decorate Meter with MeterEnergy
// decorate energy
var totalEnergyG func() (float64, error)
if cc.Energy != nil {
totalEnergyG, err = provider.NewFloatGetterFromConfig(*cc.Energy)
@ -44,27 +46,25 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error)
}
}
// 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 idx, cc := range cc.Currents {
c, err := provider.NewFloatGetterFromConfig(cc)
if err != nil {
return nil, fmt.Errorf("currents[%d]: %w", idx, err)
}
curr = append(curr, c)
}
currentsG = collectCurrentProviders(curr)
// decorate currents
currentsG, err := buildPhaseProviders(cc.Currents)
if err != nil {
return nil, fmt.Errorf("currents: %w", err)
}
// decorate Meter with BatterySoc
// decorate voltages
voltagesG, err := buildPhaseProviders(cc.Voltages)
if err != nil {
return nil, fmt.Errorf("voltages: %w", err)
}
// decorate powers
powersG, err := buildPhaseProviders(cc.Powers)
if err != nil {
return nil, fmt.Errorf("powers: %w", err)
}
// decorate soc
var batterySocG func() (float64, error)
if cc.Soc != nil {
batterySocG, err = provider.NewFloatGetterFromConfig(*cc.Soc)
@ -73,25 +73,48 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error)
}
}
res := m.Decorate(totalEnergyG, currentsG, batterySocG)
res := m.Decorate(totalEnergyG, currentsG, voltagesG, powersG, batterySocG)
return res, nil
}
// collectCurrentProviders combines phase getters into currents api function
func collectCurrentProviders(g []func() (float64, error)) func() (float64, float64, float64, error) {
func buildPhaseProviders(providers []provider.Config) (func() (float64, float64, float64, error), error) {
var res func() (float64, float64, float64, error)
if len(providers) > 0 {
if len(providers) != 3 {
return nil, errors.New("need one per phase, total three")
}
phases := make([]func() (float64, error), 0, 3)
for idx, prov := range providers {
c, err := provider.NewFloatGetterFromConfig(prov)
if err != nil {
return nil, fmt.Errorf("[%d] %w", idx, err)
}
phases = append(phases, c)
}
res = collectPhaseProviders(phases)
}
return res, nil
}
// collectPhaseProviders combines phase getters into currents api function
func collectPhaseProviders(g []func() (float64, error)) func() (float64, float64, float64, error) {
return func() (float64, float64, float64, error) {
var currents []float64
var res []float64
for _, currentG := range g {
c, err := currentG()
if err != nil {
return 0, 0, 0, err
}
currents = append(currents, c)
res = append(res, c)
}
return currents[0], currents[1], currents[2], nil
return res[0], res[1], res[2], nil
}
}
@ -112,9 +135,11 @@ type Meter struct {
func (m *Meter) Decorate(
totalEnergy func() (float64, error),
currents func() (float64, float64, float64, error),
voltages func() (float64, float64, float64, error),
powers func() (float64, float64, float64, error),
batterySoc func() (float64, error),
) api.Meter {
return decorateMeter(m, totalEnergy, currents, batterySoc)
return decorateMeter(m, totalEnergy, currents, voltages, powers, batterySoc)
}
// CurrentPower implements the api.Meter interface

View file

@ -55,7 +55,19 @@ func NewMovingAverageFromConfig(other map[string]interface{}) (api.Meter, error)
currents = m.Currents
}
res := meter.Decorate(totalEnergy, currents, batterySoc)
// decorate voltages reading
var voltages func() (float64, float64, float64, error)
if m, ok := m.(api.MeterVoltage); ok {
voltages = m.Voltages
}
// decorate powers reading
var powers func() (float64, float64, float64, error)
if m, ok := m.(api.MeterPower); ok {
powers = m.Powers
}
res := meter.Decorate(totalEnergy, currents, voltages, powers, batterySoc)
return res, nil
}

View file

@ -6,12 +6,12 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error), battery func() (float64, error)) api.Meter {
func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error), meterVoltage func() (float64, float64, float64, error), meterPower func() (float64, float64, float64, error), battery func() (float64, error)) api.Meter {
switch {
case battery == nil && meterCurrent == nil && meterEnergy == nil:
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return base
case battery == nil && meterCurrent == nil && meterEnergy != nil:
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterEnergy
@ -22,7 +22,7 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil:
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
@ -33,7 +33,7 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil:
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
@ -48,7 +48,203 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil:
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterVoltage
}{
Meter: base,
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterPower
}{
Meter: base,
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterEnergy
api.MeterPower
}{
Meter: base,
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterPower
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterPower
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -59,7 +255,7 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil:
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -74,7 +270,7 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil:
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -89,7 +285,7 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil:
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -107,6 +303,250 @@ func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCur
meterEnergy: meterEnergy,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterPower
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterEnergy
api.MeterPower
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterPower
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
api.MeterPower
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateMeterBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateMeterMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateMeterMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
}
return nil
@ -135,3 +575,19 @@ type decorateMeterMeterEnergyImpl struct {
func (impl *decorateMeterMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateMeterMeterPowerImpl struct {
meterPower func() (float64, float64, float64, error)
}
func (impl *decorateMeterMeterPowerImpl) Powers() (float64, float64, float64, error) {
return impl.meterPower()
}
type decorateMeterMeterVoltageImpl struct {
meterVoltage func() (float64, float64, float64, error)
}
func (impl *decorateMeterMeterVoltageImpl) Voltages() (float64, float64, float64, error) {
return impl.meterVoltage()
}

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.MeterCurrent,Currents,func() (float64, float64, 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.MeterVoltage,Voltages,func() (float64, float64, float64, error)" -t "api.MeterPower,Powers,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) {
@ -35,6 +35,8 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
modbus.Settings `mapstructure:",squash"`
Power, Energy, Soc string
Currents []string
Voltages []string
Powers []string
Delay time.Duration
Timeout time.Duration
}{
@ -97,7 +99,7 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
return nil, fmt.Errorf("invalid measurement for power: %s", cc.Power)
}
// decorate energy reading
// decorate energy
var totalEnergy func() (float64, error)
if cc.Energy != "" {
if err := modbus.ParseOperation(device, cc.Energy, &m.opEnergy); err != nil {
@ -107,32 +109,25 @@ 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
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 currents
currentsG, err := m.buildPhaseProviders(cc.Currents)
if err != nil {
return nil, fmt.Errorf("currents: %w", err)
}
// decorate soc reading
// decorate voltages
voltagesG, err := m.buildPhaseProviders(cc.Voltages)
if err != nil {
return nil, fmt.Errorf("voltages: %w", err)
}
// decorate powers
powersG, err := m.buildPhaseProviders(cc.Powers)
if err != nil {
return nil, fmt.Errorf("powers: %w", err)
}
// decorate soc
var soc func() (float64, error)
if cc.Soc != "" {
if err := modbus.ParseOperation(device, cc.Soc, &m.opSoc); err != nil {
@ -142,7 +137,35 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
soc = m.soc
}
return decorateModbus(m, totalEnergy, currentsG, soc), nil
return decorateModbus(m, totalEnergy, currentsG, voltagesG, powersG, soc), nil
}
func (m *Modbus) buildPhaseProviders(readings []string) (func() (float64, float64, float64, error), error) {
var res func() (float64, float64, float64, error)
if len(readings) > 0 {
if len(readings) != 3 {
return nil, errors.New("need one per phase, total three")
}
phases := make([]func() (float64, error), 0, 3)
for idx, reading := range readings {
var opCurrent modbus.Operation
if err := modbus.ParseOperation(m.device, reading, &opCurrent); err != nil {
return nil, fmt.Errorf("invalid measurement [%d]: %s", idx, reading)
}
c := func() (float64, error) {
return m.floatGetter(opCurrent)
}
phases = append(phases, c)
}
res = collectPhaseProviders(phases)
}
return res, 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), meterCurrent func() (float64, float64, float64, error), battery func() (float64, error)) api.Meter {
func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error), meterVoltage func() (float64, float64, float64, error), meterPower func() (float64, float64, float64, error), battery func() (float64, error)) api.Meter {
switch {
case battery == nil && meterCurrent == nil && meterEnergy == nil:
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return base
case battery == nil && meterCurrent == nil && meterEnergy != nil:
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterEnergy
@ -22,7 +22,7 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil:
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
@ -33,7 +33,7 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil:
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
@ -48,7 +48,203 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil:
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterVoltage
}{
Meter: base,
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterPower
}{
Meter: base,
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterEnergy
api.MeterPower
}{
Meter: base,
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterPower
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterPower
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery == nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery == nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.MeterCurrent
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -59,7 +255,7 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil:
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -74,7 +270,7 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil:
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -89,7 +285,7 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil:
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
@ -107,6 +303,250 @@ func decorateModbus(base api.Meter, meterEnergy func() (float64, error), meterCu
meterEnergy: meterEnergy,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower == nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterPower
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterEnergy
api.MeterPower
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterPower
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage == nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
api.MeterPower
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
}
case battery != nil && meterCurrent == nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent == nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy == nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
case battery != nil && meterCurrent != nil && meterEnergy != nil && meterPower != nil && meterVoltage != nil:
return &struct {
api.Meter
api.Battery
api.MeterCurrent
api.MeterEnergy
api.MeterPower
api.MeterVoltage
}{
Meter: base,
Battery: &decorateModbusBatteryImpl{
battery: battery,
},
MeterCurrent: &decorateModbusMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
MeterPower: &decorateModbusMeterPowerImpl{
meterPower: meterPower,
},
MeterVoltage: &decorateModbusMeterVoltageImpl{
meterVoltage: meterVoltage,
},
}
}
return nil
@ -135,3 +575,19 @@ type decorateModbusMeterEnergyImpl struct {
func (impl *decorateModbusMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateModbusMeterPowerImpl struct {
meterPower func() (float64, float64, float64, error)
}
func (impl *decorateModbusMeterPowerImpl) Powers() (float64, float64, float64, error) {
return impl.meterPower()
}
type decorateModbusMeterVoltageImpl struct {
meterVoltage func() (float64, float64, float64, error)
}
func (impl *decorateModbusMeterVoltageImpl) Voltages() (float64, float64, float64, error) {
return impl.meterVoltage()
}

View file

@ -69,7 +69,7 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
curr = append(curr, current)
}
currents = collectCurrentProviders(curr)
currents = collectPhaseProviders(curr)
case "pv":
configuredG := boolG(fmt.Sprintf("%s/pv/1/%s", cc.Topic, openwb.PvConfigured)) // first pv
@ -111,7 +111,7 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
return nil, err
}
res := m.Decorate(nil, currents, soc)
res := m.Decorate(nil, currents, nil, nil, soc)
return res, nil
}

View file

@ -117,14 +117,52 @@ var _ api.MeterCurrent = (*SMA)(nil)
func (sm *SMA) Currents() (float64, float64, float64, error) {
values, err := sm.device.Values()
var powers [3]float64
for i, id := range []sunny.ValueID{sunny.ActivePowerMinusL1, sunny.ActivePowerMinusL2, sunny.ActivePowerMinusL3} {
if p := sma.AsFloat(values[id]); p > 0 {
powers[i] = -p
}
}
var currents [3]float64
for i, id := range []sunny.ValueID{sunny.CurrentL1, sunny.CurrentL2, sunny.CurrentL3} {
currents[i] = sma.AsFloat(values[id])
currents[i] = util.SignFromPower(sma.AsFloat(values[id]), powers[i])
}
return currents[0], currents[1], currents[2], err
}
var _ api.MeterVoltage = (*SMA)(nil)
// Voltages implements the api.MeterVoltage interface
func (sm *SMA) Voltages() (float64, float64, float64, error) {
values, err := sm.device.Values()
var voltages [3]float64
for i, id := range []sunny.ValueID{sunny.VoltageL1, sunny.VoltageL2, sunny.VoltageL3} {
voltages[i] = sma.AsFloat(values[id])
}
return voltages[0], voltages[1], voltages[2], err
}
var _ api.MeterPower = (*SMA)(nil)
// Powers implements the api.MeterPower interface
func (sm *SMA) Powers() (float64, float64, float64, error) {
values, err := sm.device.Values()
var powers [3]float64
for i, id := range []sunny.ValueID{sunny.ActivePowerPlusL1, sunny.ActivePowerPlusL2, sunny.ActivePowerPlusL3} {
powers[i] = sma.AsFloat(values[id])
}
for i, id := range []sunny.ValueID{sunny.ActivePowerMinusL1, sunny.ActivePowerMinusL2, sunny.ActivePowerMinusL3} {
powers[i] -= sma.AsFloat(values[id])
}
return powers[0], powers[1], powers[2], err
}
// soc implements the api.Battery interface
func (sm *SMA) soc() (float64, error) {
values, err := sm.device.Values()

View file

@ -13,14 +13,19 @@ params:
choice: ["rs485"]
render: |
type: modbus
{{- include "modbus" . }}
model: sdm
{{- if eq .usage "charge" }}
energy: Sum # only required for charge meter usage
{{- end }}
{{- if or (eq .usage "charge") (eq .usage "grid") }}
power: Power
energy: Sum
currents:
- CurrentL1
- CurrentL2
- CurrentL3
{{- end }}
{{- include "modbus" . }}
voltages:
- VoltageL1
- VoltageL2
- VoltageL3
powers:
- PowerL1
- PowerL2
- PowerL3

View file

@ -23,8 +23,8 @@ render: |
id: 200
model: sunspec
power: Power
energy: Export
currents:
energy: Import
currents: # currents are signed already
- CurrentL1
- CurrentL2
- CurrentL3

View file

@ -10,14 +10,19 @@ params:
choice: ["rs485", "tcpip"]
render: |
type: modbus
model: janitza
{{- include "modbus" . }}
{{- if eq .usage "charge" }}
energy: Import # only required for charge meter usage
{{- end }}
{{- if or (eq .usage "charge") (eq .usage "grid") }}
model: janitza
power: Power
energy: Import
currents:
- CurrentL1
- CurrentL2
- CurrentL3
{{- end }}
voltages:
- VoltageL1
- VoltageL2
- VoltageL3
powers:
- PowerL1
- PowerL2
- PowerL3

View file

@ -16,6 +16,14 @@ render: |
power: Power
energy: Import
currents:
- CurrentL1
- CurrentL2
- CurrentL3
- CurrentL1
- CurrentL2
- CurrentL3
voltages:
- VoltageL1
- VoltageL2
- VoltageL3
powers:
- PowerL1
- PowerL2
- PowerL3

View file

@ -13,14 +13,11 @@ render: |
source: http
uri: http://{{ .host }}/status
jq: .emeters | map(.power) | add
{{- if or (eq .usage "charge") (eq .usage "grid") }}
energy:
source: http
uri: http://{{ .host }}/status
jq: .emeters | map(.total) | add
scale: 0.001
{{ end -}}
{{if ne .usage "pv" -}}
currents:
- source: http
uri: http://{{ .host }}/emeter/0
@ -31,4 +28,23 @@ render: |
- source: http
uri: http://{{ .host }}/emeter/2
jq: .current
{{ end -}}
voltages:
- source: http
uri: http://{{ .host }}/emeter/0
jq: .voltage
- source: http
uri: http://{{ .host }}/emeter/1
jq: .voltage
- source: http
uri: http://{{ .host }}/emeter/2
jq: .voltage
powers:
- source: http
uri: http://{{ .host }}/emeter/0
jq: .power
- source: http
uri: http://{{ .host }}/emeter/1
jq: .power
- source: http
uri: http://{{ .host }}/emeter/2
jq: .power

View file

@ -27,17 +27,35 @@ render: |
{{- if eq .integer "true" }}
# sunspec model 203 (int+sf) meter
power: 203:W
energy: 203:TotWhImp
currents:
- 203:AphA
- 203:AphB
- 203:AphC
voltages:
- 203:PhVphA
- 203:PhVphB
- 203:PhVphC
powers:
- 203:WphA
- 203:WphB
- 203:WphC
{{ else }}
# sunspec model 213 (float) meter
power: 213:W
energy: 213:TotWhImp
currents:
- 213:AphA
- 213:AphB
- 213:AphC
voltages:
- 213:PhVphA
- 213:PhVphB
- 213:PhVphC
powers:
- 213:WphA
- 213:WphB
- 213:WphC
{{- end }}
{{- end }}
{{- if eq .usage "pv" }}

View file

@ -27,17 +27,35 @@ render: |
{{- if eq .integer "true" }}
# sunspec model 203 (int+sf) meter
power: 203:W
energy: 203:TotWhImp
currents:
- 203:AphA
- 203:AphB
- 203:AphC
voltages:
- 203:PhVphA
- 203:PhVphB
- 203:PhVphC
powers:
- 203:WphA
- 203:WphB
- 203:WphC
{{ else }}
# sunspec model 213 (float) meter
power: 213:W
energy: 213:TotWhImp
currents:
- 213:AphA
- 213:AphB
- 213:AphC
voltages:
- 213:PhVphA
- 213:PhVphB
- 213:PhVphC
powers:
- 213:WphA
- 213:WphB
- 213:WphC
{{- end }}
{{- else }}
model: sunspec

9
util/metering.go Normal file
View file

@ -0,0 +1,9 @@
package util
// SignFromPower is a helper function to create signed current from signed power bypassing already signed current
func SignFromPower(current float64, power float64) float64 {
if current > 0 && power < 0 {
return -current
}
return current
}