diff --git a/api/api.go b/api/api.go index 0e5d381c0..634315a49 100644 --- a/api/api.go +++ b/api/api.go @@ -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) diff --git a/charger/abb.go b/charger/abb.go index 122710f31..b4ec3ab2f 100644 --- a/charger/abb.go +++ b/charger/abb.go @@ -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 diff --git a/charger/bender.go b/charger/bender.go index fde0eac65..cdf4bcf8c 100644 --- a/charger/bender.go +++ b/charger/bender.go @@ -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 { diff --git a/charger/bender_decorators.go b/charger/bender_decorators.go index ed1f39714..9d8149337 100644 --- a/charger/bender_decorators.go +++ b/charger/bender_decorators.go @@ -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() +} diff --git a/charger/heidelberg-ec.go b/charger/heidelberg-ec.go index 88fef5894..342afe185 100644 --- a/charger/heidelberg-ec.go +++ b/charger/heidelberg-ec.go @@ -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) diff --git a/cmd/dumper.go b/cmd/dumper.go index ac5428756..49678dd29 100644 --- a/cmd/dumper.go +++ b/cmd/dumper.go @@ -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 diff --git a/core/loadpoint.go b/core/loadpoint.go index fb9266383..90a3069a9 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -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 diff --git a/core/site.go b/core/site.go index f23f7b74b..f240a4f85 100644 --- a/core/site.go +++ b/core/site.go @@ -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) } diff --git a/meter/meter.go b/meter/meter.go index f4bfbed3c..718fc043e 100644 --- a/meter/meter.go +++ b/meter/meter.go @@ -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 diff --git a/meter/meter_average.go b/meter/meter_average.go index d3501669e..a7c601731 100644 --- a/meter/meter_average.go +++ b/meter/meter_average.go @@ -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 } diff --git a/meter/meter_decorators.go b/meter/meter_decorators.go index 334b76e74..ba99e7798 100644 --- a/meter/meter_decorators.go +++ b/meter/meter_decorators.go @@ -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() +} diff --git a/meter/modbus.go b/meter/modbus.go index 1879fbaa4..dc4b68ae0 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.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) diff --git a/meter/modbus_decorators.go b/meter/modbus_decorators.go index b34347ee1..16d5cb49a 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), 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() +} diff --git a/meter/openwb.go b/meter/openwb.go index daece3753..680c1215f 100644 --- a/meter/openwb.go +++ b/meter/openwb.go @@ -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 } diff --git a/meter/sma.go b/meter/sma.go index d0b585aa3..4875b2be3 100644 --- a/meter/sma.go +++ b/meter/sma.go @@ -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() diff --git a/templates/definition/meter/eastron-sdm630.yaml b/templates/definition/meter/eastron-sdm630.yaml index e891f15ba..283ff5d79 100644 --- a/templates/definition/meter/eastron-sdm630.yaml +++ b/templates/definition/meter/eastron-sdm630.yaml @@ -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 diff --git a/templates/definition/meter/fronius-gen24.yaml b/templates/definition/meter/fronius-gen24.yaml index 5992110fb..47c47ba87 100644 --- a/templates/definition/meter/fronius-gen24.yaml +++ b/templates/definition/meter/fronius-gen24.yaml @@ -23,8 +23,8 @@ render: | id: 200 model: sunspec power: Power - energy: Export - currents: + energy: Import + currents: # currents are signed already - CurrentL1 - CurrentL2 - CurrentL3 diff --git a/templates/definition/meter/janitza-b23.yaml b/templates/definition/meter/janitza-b23.yaml index a80a9355e..bc2cc3c9c 100644 --- a/templates/definition/meter/janitza-b23.yaml +++ b/templates/definition/meter/janitza-b23.yaml @@ -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 diff --git a/templates/definition/meter/kostal-ksem.yaml b/templates/definition/meter/kostal-ksem.yaml index c604ece0f..d0bbfc4e4 100644 --- a/templates/definition/meter/kostal-ksem.yaml +++ b/templates/definition/meter/kostal-ksem.yaml @@ -16,6 +16,14 @@ render: | power: Power energy: Import currents: - - CurrentL1 - - CurrentL2 - - CurrentL3 + - CurrentL1 + - CurrentL2 + - CurrentL3 + voltages: + - VoltageL1 + - VoltageL2 + - VoltageL3 + powers: + - PowerL1 + - PowerL2 + - PowerL3 diff --git a/templates/definition/meter/shelly-3em.yaml b/templates/definition/meter/shelly-3em.yaml index 643d1b1c6..56e505753 100644 --- a/templates/definition/meter/shelly-3em.yaml +++ b/templates/definition/meter/shelly-3em.yaml @@ -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 diff --git a/templates/definition/meter/sunspec-hybrid.yaml b/templates/definition/meter/sunspec-hybrid.yaml index 43bb7705b..87e42ebcf 100644 --- a/templates/definition/meter/sunspec-hybrid.yaml +++ b/templates/definition/meter/sunspec-hybrid.yaml @@ -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" }} diff --git a/templates/definition/meter/sunspec-inverter.yaml b/templates/definition/meter/sunspec-inverter.yaml index 16064a8a9..76ce7ba2f 100644 --- a/templates/definition/meter/sunspec-inverter.yaml +++ b/templates/definition/meter/sunspec-inverter.yaml @@ -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 diff --git a/util/metering.go b/util/metering.go new file mode 100644 index 000000000..c054b86f0 --- /dev/null +++ b/util/metering.go @@ -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 +}