Cfos: add attached meter (#15192)
This commit is contained in:
parent
77a3cf2861
commit
a19782ef11
3 changed files with 310 additions and 10 deletions
|
|
@ -17,7 +17,13 @@ const (
|
|||
cfosRegMaxCurrent = 8093
|
||||
cfosRegEnable = 8094
|
||||
cfosRegLastRfid = 8096
|
||||
cfosRegMeter = 8112
|
||||
cfosRegSolarEnabled = 8113
|
||||
|
||||
cfosRegMeterFlags = 8057
|
||||
cfosRegEnergy = 8058 // 4 rw Aktiver Import [Wh]
|
||||
cfosRegPower = 8062 // 2 r Aktive Leistung [W]
|
||||
cfosRegCurrents = 8064 // 2 r Momentaner Strom L1 [0.1 A]
|
||||
)
|
||||
|
||||
// CfosPowerBrain is an charger implementation for cFos PowerBrain wallboxes.
|
||||
|
|
@ -31,7 +37,7 @@ func init() {
|
|||
registry.Add("cfos", NewCfosPowerBrainFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateCfos -b *CfosPowerBrain -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateCfos -b *CfosPowerBrain -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseSwitcher,Phases1p3p,func(int) error"
|
||||
|
||||
// NewCfosPowerBrainFromConfig creates a cFos charger from generic config
|
||||
func NewCfosPowerBrainFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
|
|
@ -70,15 +76,28 @@ func NewCfosPowerBrain(uri string, id uint8) (api.Charger, error) {
|
|||
conn: conn,
|
||||
}
|
||||
|
||||
// decorate meter
|
||||
var (
|
||||
power func() (float64, error)
|
||||
energy func() (float64, error)
|
||||
currents func() (float64, float64, float64, error)
|
||||
)
|
||||
if b, err := wb.conn.ReadHoldingRegisters(cfosRegMeter, 1); err == nil && binary.BigEndian.Uint16(b) != 0 {
|
||||
power = wb.currentPower
|
||||
energy = wb.totalEnergy
|
||||
|
||||
if b, err := wb.conn.ReadHoldingRegisters(cfosRegMeterFlags, 1); err == nil && binary.BigEndian.Uint16(b) != 0 {
|
||||
currents = wb.currents
|
||||
}
|
||||
}
|
||||
|
||||
// decorate phases
|
||||
var phases1p3p func(int) error
|
||||
|
||||
b, err := wb.conn.ReadHoldingRegisters(cfosRegSolarEnabled, 1)
|
||||
if err == nil && binary.BigEndian.Uint16(b)&(1<<8) != 0 {
|
||||
if b, err := wb.conn.ReadHoldingRegisters(cfosRegSolarEnabled, 1); err == nil && binary.BigEndian.Uint16(b)&(1<<8) != 0 {
|
||||
phases1p3p = wb.phases1p3p
|
||||
}
|
||||
|
||||
return decorateCfos(wb, phases1p3p), nil
|
||||
return decorateCfos(wb, power, energy, currents, phases1p3p), nil
|
||||
}
|
||||
|
||||
// Status implements the api.Charger interface
|
||||
|
|
@ -141,6 +160,41 @@ func (wb *CfosPowerBrain) MaxCurrentMillis(current float64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// currentPower implements the api.Meter interface
|
||||
func (wb *CfosPowerBrain) currentPower() (float64, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(cfosRegPower, 2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return float64(binary.BigEndian.Uint32(b)), nil
|
||||
}
|
||||
|
||||
// totalEnergy implements the api.MeterEnergy interface
|
||||
func (wb *CfosPowerBrain) totalEnergy() (float64, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(cfosRegEnergy, 4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return float64(binary.BigEndian.Uint64(b)) / 1e3, nil
|
||||
}
|
||||
|
||||
// currents implements the api.PhaseCurrents interface
|
||||
func (wb *CfosPowerBrain) currents() (float64, float64, float64, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(cfosRegCurrents, 6)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
var res [3]float64
|
||||
for i := range res {
|
||||
res[i] = float64(binary.BigEndian.Uint32(b[4*i:])) / 10
|
||||
}
|
||||
|
||||
return res[0], res[1], res[2], nil
|
||||
}
|
||||
|
||||
// phases1p3p implements the api.PhaseSwitcher interface
|
||||
func (wb *CfosPowerBrain) phases1p3p(phases int) error {
|
||||
if phases == 3 {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,109 @@ import (
|
|||
"github.com/evcc-io/evcc/api"
|
||||
)
|
||||
|
||||
func decorateCfos(base *CfosPowerBrain, phaseSwitcher func(int) error) api.Charger {
|
||||
func decorateCfos(base *CfosPowerBrain, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseSwitcher func(int) error) api.Charger {
|
||||
switch {
|
||||
case phaseSwitcher == nil:
|
||||
case meter == nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil:
|
||||
return base
|
||||
|
||||
case phaseSwitcher != nil:
|
||||
case meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.MeterEnergy
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.MeterEnergy
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.PhaseCurrents
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.PhaseCurrents
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.MeterEnergy
|
||||
api.PhaseCurrents
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.MeterEnergy
|
||||
api.PhaseCurrents
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.PhaseSwitcher
|
||||
|
|
@ -21,11 +118,160 @@ func decorateCfos(base *CfosPowerBrain, phaseSwitcher func(int) error) api.Charg
|
|||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.MeterEnergy
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.MeterEnergy
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.PhaseCurrents
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.PhaseCurrents
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter == nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.MeterEnergy
|
||||
api.PhaseCurrents
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
|
||||
case meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher != nil:
|
||||
return &struct {
|
||||
*CfosPowerBrain
|
||||
api.Meter
|
||||
api.MeterEnergy
|
||||
api.PhaseCurrents
|
||||
api.PhaseSwitcher
|
||||
}{
|
||||
CfosPowerBrain: base,
|
||||
Meter: &decorateCfosMeterImpl{
|
||||
meter: meter,
|
||||
},
|
||||
MeterEnergy: &decorateCfosMeterEnergyImpl{
|
||||
meterEnergy: meterEnergy,
|
||||
},
|
||||
PhaseCurrents: &decorateCfosPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhaseSwitcher: &decorateCfosPhaseSwitcherImpl{
|
||||
phaseSwitcher: phaseSwitcher,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decorateCfosMeterImpl struct {
|
||||
meter func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateCfosMeterImpl) CurrentPower() (float64, error) {
|
||||
return impl.meter()
|
||||
}
|
||||
|
||||
type decorateCfosMeterEnergyImpl struct {
|
||||
meterEnergy func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateCfosMeterEnergyImpl) TotalEnergy() (float64, error) {
|
||||
return impl.meterEnergy()
|
||||
}
|
||||
|
||||
type decorateCfosPhaseCurrentsImpl struct {
|
||||
phaseCurrents func() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateCfosPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
|
||||
return impl.phaseCurrents()
|
||||
}
|
||||
|
||||
type decorateCfosPhaseSwitcherImpl struct {
|
||||
phaseSwitcher func(int) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ capabilities: ["mA", "rfid", "1p3p"]
|
|||
requirements:
|
||||
description:
|
||||
de: |
|
||||
Der Zähler- falls vorhanden- muss separat als Ladezähler konfiguriert werden.
|
||||
Ein evtl. vorhandener S0 Zähler muss separat als Ladezähler konfiguriert werden.
|
||||
Phasenumschaltung bietet nur die Solar-Variante und muss vom Anwender freigeschaltet werden.
|
||||
en: |
|
||||
The meter- if present- must be configured separately as charge meter.
|
||||
S0 meters must be configured separately as charge meter.
|
||||
Phase switching is only available with the Solar variant and must be enabled by the user.
|
||||
evcc: ["sponsorship"]
|
||||
params:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue