From ac98b8a5431f2ab9d00dff90ad4366f799b70495 Mon Sep 17 00:00:00 2001 From: andig Date: Fri, 17 Sep 2021 22:57:47 +0200 Subject: [PATCH] Keba: auto-detect optional meter (#1541) --- charger/keba.go | 45 +++--- charger/keba_decorators.go | 281 +++++++++++++++++++++++++++++++++++++ charger/keba_test.go | 27 ---- 3 files changed, 308 insertions(+), 45 deletions(-) create mode 100644 charger/keba_decorators.go delete mode 100644 charger/keba_test.go diff --git a/charger/keba.go b/charger/keba.go index 977d2149b..c1effdaed 100644 --- a/charger/keba.go +++ b/charger/keba.go @@ -37,6 +37,8 @@ func init() { registry.Add("keba", NewKebaFromConfig) } +//go:generate go run ../cmd/tools/decorate.go -f decorateKeba -b *Keba -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.ChargeRater,ChargedEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" + // NewKebaFromConfig creates a new configurable charger func NewKebaFromConfig(other map[string]interface{}) (api.Charger, error) { cc := struct { @@ -47,15 +49,30 @@ func NewKebaFromConfig(other map[string]interface{}) (api.Charger, error) { }{ Timeout: udpTimeout, } + if err := util.DecodeOther(other, &cc); err != nil { return nil, err } - return NewKeba(cc.URI, cc.Serial, cc.RFID, cc.Timeout) + k, err := NewKeba(cc.URI, cc.Serial, cc.RFID, cc.Timeout) + if err != nil { + return nil, err + } + + energy, err := k.totalEnergy() + if err != nil { + return nil, err + } + + if energy > 0 { + return decorateKeba(k, k.currentPower, k.totalEnergy, k.chargedEnergy, k.currents), nil + } + + return k, err } // NewKeba creates a new charger -func NewKeba(uri, serial string, rfid RFID, timeout time.Duration) (api.Charger, error) { +func NewKeba(uri, serial string, rfid RFID, timeout time.Duration) (*Keba, error) { log := util.NewLogger("keba") if keba.Instance == nil { @@ -264,10 +281,8 @@ func (c *Keba) MaxCurrentMillis(current float64) error { return nil } -var _ api.Meter = (*Keba)(nil) - -// CurrentPower implements the api.Meter interface -func (c *Keba) CurrentPower() (float64, error) { +// currentPower implements the api.Meter interface +func (c *Keba) currentPower() (float64, error) { var kr keba.Report3 err := c.roundtrip("report", 3, &kr) @@ -275,10 +290,8 @@ func (c *Keba) CurrentPower() (float64, error) { return float64(kr.P) / 1e3, err } -var _ api.MeterEnergy = (*Keba)(nil) - -// TotalEnergy implements the api.MeterEnergy interface -func (c *Keba) TotalEnergy() (float64, error) { +// totalEnergy implements the api.MeterEnergy interface +func (c *Keba) totalEnergy() (float64, error) { var kr keba.Report3 err := c.roundtrip("report", 3, &kr) @@ -286,10 +299,8 @@ func (c *Keba) TotalEnergy() (float64, error) { return float64(kr.ETotal) / 1e4, err } -var _ api.ChargeRater = (*Keba)(nil) - -// ChargedEnergy implements the ChargeRater interface -func (c *Keba) ChargedEnergy() (float64, error) { +// chargedEnergy implements the ChargeRater interface +func (c *Keba) chargedEnergy() (float64, error) { var kr keba.Report3 err := c.roundtrip("report", 3, &kr) @@ -297,10 +308,8 @@ func (c *Keba) ChargedEnergy() (float64, error) { return float64(kr.EPres) / 1e4, err } -var _ api.MeterCurrent = (*Keba)(nil) - -// Currents implements the api.MeterCurrent interface -func (c *Keba) Currents() (float64, float64, float64, error) { +// currents implements the api.MeterCurrent interface +func (c *Keba) currents() (float64, float64, float64, error) { var kr keba.Report3 err := c.roundtrip("report", 3, &kr) diff --git a/charger/keba_decorators.go b/charger/keba_decorators.go new file mode 100644 index 000000000..10a95a97a --- /dev/null +++ b/charger/keba_decorators.go @@ -0,0 +1,281 @@ +package charger + +// Code generated by github.com/andig/cmd/tools/decorate.go. DO NOT EDIT. + +import ( + "github.com/evcc-io/evcc/api" +) + +func decorateKeba(base *Keba, meter func() (float64, error), meterEnergy func() (float64, error), chargeRater func() (float64, error), meterCurrent func() (float64, float64, float64, error)) api.Charger { + switch { + case chargeRater == nil && meter == nil && meterCurrent == nil && meterEnergy == nil: + return base + + case chargeRater == nil && meter != nil && meterCurrent == nil && meterEnergy == nil: + return &struct { + *Keba + api.Meter + }{ + Keba: base, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + } + + case chargeRater == nil && meter == nil && meterCurrent == nil && meterEnergy != nil: + return &struct { + *Keba + api.MeterEnergy + }{ + Keba: base, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater == nil && meter != nil && meterCurrent == nil && meterEnergy != nil: + return &struct { + *Keba + api.Meter + api.MeterEnergy + }{ + Keba: base, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater != nil && meter == nil && meterCurrent == nil && meterEnergy == nil: + return &struct { + *Keba + api.ChargeRater + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + } + + case chargeRater != nil && meter != nil && meterCurrent == nil && meterEnergy == nil: + return &struct { + *Keba + api.ChargeRater + api.Meter + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + } + + case chargeRater != nil && meter == nil && meterCurrent == nil && meterEnergy != nil: + return &struct { + *Keba + api.ChargeRater + api.MeterEnergy + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater != nil && meter != nil && meterCurrent == nil && meterEnergy != nil: + return &struct { + *Keba + api.ChargeRater + api.Meter + api.MeterEnergy + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater == nil && meter == nil && meterCurrent != nil && meterEnergy == nil: + return &struct { + *Keba + api.MeterCurrent + }{ + Keba: base, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + } + + case chargeRater == nil && meter != nil && meterCurrent != nil && meterEnergy == nil: + return &struct { + *Keba + api.Meter + api.MeterCurrent + }{ + Keba: base, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + } + + case chargeRater == nil && meter == nil && meterCurrent != nil && meterEnergy != nil: + return &struct { + *Keba + api.MeterCurrent + api.MeterEnergy + }{ + Keba: base, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater == nil && meter != nil && meterCurrent != nil && meterEnergy != nil: + return &struct { + *Keba + api.Meter + api.MeterCurrent + api.MeterEnergy + }{ + Keba: base, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater != nil && meter == nil && meterCurrent != nil && meterEnergy == nil: + return &struct { + *Keba + api.ChargeRater + api.MeterCurrent + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + } + + case chargeRater != nil && meter != nil && meterCurrent != nil && meterEnergy == nil: + return &struct { + *Keba + api.ChargeRater + api.Meter + api.MeterCurrent + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + } + + case chargeRater != nil && meter == nil && meterCurrent != nil && meterEnergy != nil: + return &struct { + *Keba + api.ChargeRater + api.MeterCurrent + api.MeterEnergy + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case chargeRater != nil && meter != nil && meterCurrent != nil && meterEnergy != nil: + return &struct { + *Keba + api.ChargeRater + api.Meter + api.MeterCurrent + api.MeterEnergy + }{ + Keba: base, + ChargeRater: &decorateKebaChargeRaterImpl{ + chargeRater: chargeRater, + }, + Meter: &decorateKebaMeterImpl{ + meter: meter, + }, + MeterCurrent: &decorateKebaMeterCurrentImpl{ + meterCurrent: meterCurrent, + }, + MeterEnergy: &decorateKebaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + } + + return nil +} + +type decorateKebaChargeRaterImpl struct { + chargeRater func() (float64, error) +} + +func (impl *decorateKebaChargeRaterImpl) ChargedEnergy() (float64, error) { + return impl.chargeRater() +} + +type decorateKebaMeterImpl struct { + meter func() (float64, error) +} + +func (impl *decorateKebaMeterImpl) CurrentPower() (float64, error) { + return impl.meter() +} + +type decorateKebaMeterCurrentImpl struct { + meterCurrent func() (float64, float64, float64, error) +} + +func (impl *decorateKebaMeterCurrentImpl) Currents() (float64, float64, float64, error) { + return impl.meterCurrent() +} + +type decorateKebaMeterEnergyImpl struct { + meterEnergy func() (float64, error) +} + +func (impl *decorateKebaMeterEnergyImpl) TotalEnergy() (float64, error) { + return impl.meterEnergy() +} diff --git a/charger/keba_test.go b/charger/keba_test.go deleted file mode 100644 index e748648fb..000000000 --- a/charger/keba_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package charger - -import ( - "testing" - - "github.com/evcc-io/evcc/api" -) - -func TestKeba(t *testing.T) { - var wb api.Charger - wb, err := NewKeba("localhost", "bar", RFID{}, 0) - if err != nil { - t.Error(err) - } - - if _, ok := wb.(api.MeterEnergy); !ok { - t.Error("missing MeterEnergy interface") - } - - if _, ok := wb.(api.MeterCurrent); !ok { - t.Error("missing MeterCurrent interface") - } - - if _, ok := wb.(api.ChargeRater); !ok { - t.Error("missing ChargeRater interface") - } -}