Support attached meters for Phoenix EV-CC controllers (#416)
This commit is contained in:
parent
c509b1c44e
commit
bbaf8ab092
2 changed files with 215 additions and 3 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package charger
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/andig/evcc/api"
|
||||
|
|
@ -12,8 +13,13 @@ const (
|
|||
phEVCCRegStatus = 24000 // Input
|
||||
phEVCCRegMaxCurrent = 22000 // Holding
|
||||
phEVCCRegEnable = 20000 // Coil
|
||||
|
||||
phRegPower = 337 // power reading
|
||||
phRegEnergy = 341 // energy reading
|
||||
)
|
||||
|
||||
var phRegCurrents = []uint16{334, 335, 336} // current readings
|
||||
|
||||
// PhoenixEVCC is an api.ChargeController implementation for Phoenix EV-CC-AC1-M wallboxes.
|
||||
// It uses Modbus TCP to communicate with the wallbox at modbus client id 255.
|
||||
type PhoenixEVCC struct {
|
||||
|
|
@ -25,18 +31,47 @@ func init() {
|
|||
registry.Add("phoenix-evcc", NewPhoenixEVCCFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -p charger -f decoratePhoenixEVCC -b api.Charger -o phoenix-evcc_decorators -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)"
|
||||
|
||||
// NewPhoenixEVCCFromConfig creates a Phoenix charger from generic config
|
||||
func NewPhoenixEVCCFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
cc := modbus.Settings{ID: 255}
|
||||
cc := struct {
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
Meter struct {
|
||||
Power, Energy, Currents bool
|
||||
}
|
||||
}{
|
||||
Settings: modbus.Settings{
|
||||
ID: 255,
|
||||
},
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPhoenixEVCC(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
|
||||
wb, err := NewPhoenixEVCC(cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
|
||||
|
||||
var currentPower func() (float64, error)
|
||||
if cc.Meter.Power {
|
||||
currentPower = wb.currentPower
|
||||
}
|
||||
|
||||
var totalEnergy func() (float64, error)
|
||||
if cc.Meter.Energy {
|
||||
totalEnergy = wb.totalEnergy
|
||||
}
|
||||
|
||||
var currents func() (float64, float64, float64, error)
|
||||
if cc.Meter.Currents {
|
||||
currents = wb.currents
|
||||
}
|
||||
|
||||
return decoratePhoenixEVCC(wb, currentPower, totalEnergy, currents), err
|
||||
}
|
||||
|
||||
// NewPhoenixEVCC creates a Phoenix charger
|
||||
func NewPhoenixEVCC(uri, device, comset string, baudrate int, id uint8) (api.Charger, error) {
|
||||
func NewPhoenixEVCC(uri, device, comset string, baudrate int, id uint8) (*PhoenixEVCC, error) {
|
||||
log := util.NewLogger("evcc")
|
||||
|
||||
conn, err := modbus.NewConnection(uri, device, comset, baudrate, true, id)
|
||||
|
|
@ -98,3 +133,43 @@ func (wb *PhoenixEVCC) MaxCurrent(current int64) error {
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
func (wb *PhoenixEVCC) decodeReading(b []byte) float64 {
|
||||
v := binary.BigEndian.Uint16(b)
|
||||
return float64(v)
|
||||
}
|
||||
|
||||
// CurrentPower implements the Meter.CurrentPower interface
|
||||
func (wb *PhoenixEVCC) currentPower() (float64, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(phRegPower, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return wb.decodeReading(b), err
|
||||
}
|
||||
|
||||
// totalEnergy implements the Meter.TotalEnergy interface
|
||||
func (wb *PhoenixEVCC) totalEnergy() (float64, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(phRegEnergy, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return wb.decodeReading(b), err
|
||||
}
|
||||
|
||||
// currents implements the Meter.Currents interface
|
||||
func (wb *PhoenixEVCC) currents() (float64, float64, float64, error) {
|
||||
var currents []float64
|
||||
for _, regCurrent := range phRegCurrents {
|
||||
b, err := wb.conn.ReadHoldingRegisters(regCurrent, 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
currents = append(currents, wb.decodeReading(b))
|
||||
}
|
||||
|
||||
return currents[0], currents[1], currents[2], nil
|
||||
}
|
||||
|
|
|
|||
137
charger/phoenix-evcc_decorators.go
Normal file
137
charger/phoenix-evcc_decorators.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package charger
|
||||
|
||||
// Code generated by github.com/andig/cmd/tools/decorate.go. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/andig/evcc/api"
|
||||
)
|
||||
|
||||
func decoratePhoenixEVCC(base api.Charger, meter func() (float64, error), meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error)) api.Charger {
|
||||
switch {
|
||||
case meter == nil && meterCurrent == nil && meterEnergy == nil:
|
||||
return base
|
||||
|
||||
case meter != nil && meterCurrent == nil && meterEnergy == nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.Meter
|
||||
}{
|
||||
Charger: base,
|
||||
Meter: &decoratePhoenixEVCCMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterCurrent == nil && meterEnergy != nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.MeterEnergy
|
||||
}{
|
||||
Charger: base,
|
||||
MeterEnergy: &decoratePhoenixEVCCMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterCurrent == nil && meterEnergy != nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.Meter
|
||||
api.MeterEnergy
|
||||
}{
|
||||
Charger: base,
|
||||
Meter: &decoratePhoenixEVCCMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterEnergy: &decoratePhoenixEVCCMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterCurrent != nil && meterEnergy == nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.MeterCurrent
|
||||
}{
|
||||
Charger: base,
|
||||
MeterCurrent: &decoratePhoenixEVCCMeterCurrentImpl{
|
||||
meterCurrent: meterCurrent,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterCurrent != nil && meterEnergy == nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.Meter
|
||||
api.MeterCurrent
|
||||
}{
|
||||
Charger: base,
|
||||
Meter: &decoratePhoenixEVCCMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterCurrent: &decoratePhoenixEVCCMeterCurrentImpl{
|
||||
meterCurrent: meterCurrent,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterCurrent != nil && meterEnergy != nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.MeterCurrent
|
||||
api.MeterEnergy
|
||||
}{
|
||||
Charger: base,
|
||||
MeterCurrent: &decoratePhoenixEVCCMeterCurrentImpl{
|
||||
meterCurrent: meterCurrent,
|
||||
},
|
||||
MeterEnergy: &decoratePhoenixEVCCMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterCurrent != nil && meterEnergy != nil:
|
||||
return &struct {
|
||||
api.Charger
|
||||
api.Meter
|
||||
api.MeterCurrent
|
||||
api.MeterEnergy
|
||||
}{
|
||||
Charger: base,
|
||||
Meter: &decoratePhoenixEVCCMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterCurrent: &decoratePhoenixEVCCMeterCurrentImpl{
|
||||
meterCurrent: meterCurrent,
|
||||
},
|
||||
MeterEnergy: &decoratePhoenixEVCCMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decoratePhoenixEVCCMeterImpl struct {
|
||||
meter func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decoratePhoenixEVCCMeterImpl) CurrentPower() (float64, error) {
|
||||
return impl.meter()
|
||||
}
|
||||
|
||||
type decoratePhoenixEVCCMeterCurrentImpl struct {
|
||||
meterCurrent func() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
func (impl *decoratePhoenixEVCCMeterCurrentImpl) Currents() (float64, float64, float64, error) {
|
||||
return impl.meterCurrent()
|
||||
}
|
||||
|
||||
type decoratePhoenixEVCCMeterEnergyImpl struct {
|
||||
meterEnergy func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decoratePhoenixEVCCMeterEnergyImpl) TotalEnergy() (float64, error) {
|
||||
return impl.meterEnergy()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue