Implement composition of optional interfaces using generator (#310)

This commit is contained in:
andig 2020-08-26 19:25:07 +02:00 • committed by GitHub
parent 6d89835c2c
commit e7ee87b19a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 596 additions and 143 deletions

View file

@ -14,7 +14,6 @@ clean:
rm -rf dist/
install:
go install golang.org/x/tools/cmd/stringer
go install github.com/mjibson/esc
go install github.com/golang/mock/mockgen

View file

@ -46,7 +46,7 @@ type EVSEListEntry struct {
// EVSEWifi charger implementation
type EVSEWifi struct {
*util.HTTPHelper
uri string
uri string
alwaysActive bool
}
@ -54,22 +54,45 @@ func init() {
registry.Add("evsewifi", NewEVSEWifiFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -p charger -f decorateEVSE -b api.Charger -o evsewifi_decorators -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)"
// NewEVSEWifiFromConfig creates a EVSEWifi charger from generic config
func NewEVSEWifiFromConfig(other map[string]interface{}) (api.Charger, error) {
cc := struct{ URI string }{}
cc := struct {
URI string
Meter struct {
Power, Energy, Currents bool
}
}{}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewEVSEWifi(cc.URI)
evse, err := NewEVSEWifi(cc.URI)
if err != nil {
return evse, err
}
// decorate Charger with MeterEnergy
var totalEnergy func() (float64, error)
if cc.Meter.Energy {
totalEnergy = evse.totalEnergy
}
// decorate Charger with MeterCurrent
var currents func() (float64, float64, float64, error)
if cc.Meter.Currents {
currents = evse.currents
}
return decorateEVSE(evse, totalEnergy, currents), nil
}
// NewEVSEWifi creates EVSEWifi charger
func NewEVSEWifi(uri string) (api.Charger, error) {
func NewEVSEWifi(uri string) (*EVSEWifi, error) {
evse := &EVSEWifi{
HTTPHelper: util.NewHTTPHelper(util.NewLogger("wifi")),
uri: strings.TrimRight(uri, "/"),
alwaysActive: true,
HTTPHelper: util.NewHTTPHelper(util.NewLogger("wifi")),
uri: strings.TrimRight(uri, "/"),
}
return evse, nil
@ -164,20 +187,20 @@ func (evse *EVSEWifi) ChargingTime() (time.Duration, error) {
return time.Duration(params.Duration) * time.Millisecond, err
}
// // TotalEnergy implements the MeterEnergy interface
// func (evse *EVSEWifi) TotalEnergy() (float64, error) {
// params, err := evse.getParameters()
// return params.MeterReading, err
// }
// TotalEnergy implements the MeterEnergy interface
func (evse *EVSEWifi) totalEnergy() (float64, error) {
params, err := evse.getParameters()
return params.MeterReading, err
}
// Currents implements the MeterCurrents interface
func (evse *EVSEWifi) currents() (float64, float64, float64, error) {
params, err := evse.getParameters()
return float64(params.CurrentP1), float64(params.CurrentP2), float64(params.CurrentP3), err
}
// // ChargedEnergy implements the ChargeRater interface
// func (evse *EVSEWifi) ChargedEnergy() (float64, error) {
// params, err := evse.getParameters()
// return params.Energy, err
// }
// // Currents implements the MeterCurrents interface
// func (evse *EVSEWifi) Currents() (float64, float64, float64, error) {
// params, err := evse.getParameters()
// return float64(params.CurrentP1), float64(params.CurrentP2), float64(params.CurrentP3), err
// }

View file

@ -0,0 +1,69 @@
package charger
// This file has been generated - do not modify
import (
"github.com/andig/evcc/api"
)
func decorateEVSE(base api.Charger, meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error)) api.Charger {
switch {
case meterCurrent == nil && meterEnergy == nil:
return base
case meterCurrent == nil && meterEnergy != nil:
return &struct{
api.Charger
api.MeterEnergy
}{
Charger: base,
MeterEnergy: &decorateEVSEMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case meterCurrent != nil && meterEnergy == nil:
return &struct{
api.Charger
api.MeterCurrent
}{
Charger: base,
MeterCurrent: &decorateEVSEMeterCurrentImpl{
meterCurrent: meterCurrent,
},
}
case meterCurrent != nil && meterEnergy != nil:
return &struct{
api.Charger
api.MeterCurrent
api.MeterEnergy
}{
Charger: base,
MeterCurrent: &decorateEVSEMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateEVSEMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
}
type decorateEVSEMeterCurrentImpl struct {
meterCurrent func() (float64, float64, float64, error)
}
func (impl *decorateEVSEMeterCurrentImpl) Currents() (float64, float64, float64, error) {
return impl.meterCurrent()
}
type decorateEVSEMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateEVSEMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}

29
charger/evsewifi_test.go Normal file
View file

@ -0,0 +1,29 @@
package charger
import (
"testing"
"github.com/andig/evcc/api"
)
func TestEvseWifi(t *testing.T) {
evse, err := NewEVSEWifiFromConfig(map[string]interface{}{
"meter": map[string]interface{}{
"power": true,
"energy": true,
"currents": true,
},
})
if err != nil {
t.Error(err)
}
if _, ok := evse.(api.MeterEnergy); !ok {
t.Error("missing api.MeterEnergy")
}
if _, ok := evse.(api.MeterCurrent); !ok {
t.Error("missing api.MeterCurrent")
}
}

227
cmd/tools/decorate.go Normal file
View file

@ -0,0 +1,227 @@
package main
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"text/template"
combinations "github.com/mxschmitt/golang-combinations"
"github.com/spf13/pflag"
)
var srcTmpl = `
package {{.Package}}
// This file has been generated - do not modify
import (
"{{.API}}"
)
{{define "case"}}
{{- $combo := .Combo}}
{{- $prefix := .Prefix}}
{{- $idx := 0}}
{{- range $typ, $def := .Types}}
{{- if gt $idx 0}} &&{{else}}{{$idx = 1}}{{end}} {{$def.VarName}} {{if contains $combo $typ}}!={{else}}=={{end}} nil
{{- end}}:
return &struct{
{{.BaseType}}
{{- range $typ, $def := .Types}}
{{- if contains $combo $typ}}
{{$typ}}
{{- end}}
{{- end}}
}{
{{.ShortBase}}: base,
{{- range $typ, $def := .Types}}
{{- if contains $combo $typ}}
{{$def.ShortType}}: &{{$prefix}}{{$def.ShortType}}Impl{
{{$def.VarName}}: {{$def.VarName}},
},
{{- end}}
{{- end}}
}
{{- end -}}
func {{.Function}}(base {{.BaseType}}{{range ordered}}, {{.VarName}} func() {{slice .Signature 7}}{{end}}) {{.BaseType}} {
{{- $basetype := .BaseType}}
{{- $shortbase := .ShortBase}}
{{- $prefix := .Function}}
{{- $types := .Types}}
{{- $idx := 0}}
switch {
case {{- range $typ, $def := .Types}}
{{- if gt $idx 0}} &&{{else}}{{$idx = 1}}{{end}} {{$def.VarName}} == nil
{{- end}}:
return base
{{range $combo := .Combinations}}
case {{- template "case" dict "BaseType" $basetype "Prefix" $prefix "ShortBase" $shortbase "Types" $types "Combo" $combo}}
{{end}} }
return nil
}
{{range .Types -}}
type {{$prefix}}{{.ShortType}}Impl struct {
{{.VarName}} {{.Signature}}
}
func (impl *{{$prefix}}{{.ShortType}}Impl) {{.Function}}{{slice .Signature 4}} {
return impl.{{.VarName}}()
}
{{end}}
`
type dynamicType struct {
typ, function, signature string
}
type typeStruct struct {
Type, ShortType, Signature, Function, VarName string
}
func generate(out io.Writer, packageName, functionName, baseType string, dynamicTypes ...dynamicType) {
types := make(map[string]typeStruct, len(dynamicTypes))
combos := make([]string, 0)
tmpl, err := template.New("gen").Funcs(template.FuncMap{
// dict combines key value pairs for passing structs into templates
"dict": func(values ...interface{}) (map[string]interface{}, error) {
if len(values)%2 != 0 {
return nil, errors.New("invalid dict call")
}
dict := make(map[string]interface{}, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].(string)
if !ok {
return nil, errors.New("dict keys must be strings")
}
dict[key] = values[i+1]
}
return dict, nil
},
// contains checks if slice contains string
"contains": func(combo []string, typ string) bool {
for _, v := range combo {
if v == typ {
return true
}
}
return false
},
// ordered checks if slice ordered string
"ordered": func() []typeStruct {
ordered := make([]typeStruct, 0)
for _, k := range dynamicTypes {
ordered = append(ordered, types[k.typ])
}
return ordered
},
}).Parse(srcTmpl)
if err != nil {
panic(err)
}
for _, dt := range dynamicTypes {
parts := strings.SplitN(dt.typ, ".", 2)
types[dt.typ] = typeStruct{
Type: dt.typ,
ShortType: parts[1],
VarName: strings.ToLower(parts[1][:1]) + parts[1][1:],
Signature: dt.signature,
Function: dt.function,
}
combos = append(combos, dt.typ)
}
baseTypeParts := strings.SplitN(baseType, ".", 2)
vars := struct {
API string
Package, Function string
BaseType, ShortBase string
Types map[string]typeStruct
Combinations [][]string
}{
API: "github.com/andig/evcc/api",
Package: packageName,
Function: functionName,
BaseType: baseType,
ShortBase: baseTypeParts[1],
Types: types,
Combinations: combinations.All(combos),
}
if err := tmpl.Execute(out, vars); err != nil {
println(err)
os.Exit(2)
}
}
var (
target = pflag.StringP("out", "o", "", "output file")
pkg = pflag.StringP("package", "p", "", "package name")
function = pflag.StringP("function", "f", "decorate", "function name")
base = pflag.StringP("base", "b", "", "base type")
types = pflag.StringArrayP("type", "t", nil, "comma-separated list of type definitions")
)
// Usage prints flags usage
func Usage() {
fmt.Fprintf(os.Stderr, "Usage of decorate:\n")
fmt.Fprintf(os.Stderr, "\ndecorate [flags] -type interface,interface function,function signature\n")
fmt.Fprintf(os.Stderr, "\nFlags:\n")
pflag.PrintDefaults()
}
func main() {
pflag.Usage = Usage
pflag.Parse()
if *base == "" || *pkg == "" || len(*types) == 0 {
Usage()
os.Exit(2)
}
var dynamicTypes []dynamicType
for _, v := range *types {
split := strings.SplitN(v, ",", 3)
dt := dynamicType{split[0], split[1], split[2]}
dynamicTypes = append(dynamicTypes, dt)
}
var buf bytes.Buffer
generate(&buf, *pkg, *function, *base, dynamicTypes...)
generated := strings.TrimSpace(buf.String()) + "\n"
var out io.Writer = os.Stdout
if *target != "" {
name := *target
if !strings.HasSuffix(name, ".go") {
name += ".go"
}
dst, err := os.Create(name)
if err != nil {
println(err)
os.Exit(2)
}
defer dst.Close()
out = dst
}
if _, err := out.Write([]byte(generated)); err != nil {
println(err)
os.Exit(2)
}
}

3
go.mod
View file

@ -29,14 +29,15 @@ require (
github.com/mitchellh/mapstructure v1.3.2
github.com/mjibson/esc v0.2.0
github.com/muka/go-bluetooth v0.0.0-20200619025933-f6113f7141c5
github.com/mxschmitt/golang-combinations v1.0.0
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da
github.com/spf13/cobra v1.0.0
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
github.com/volkszaehler/mbmd v0.0.0-20200804054214-122815825570
golang.org/x/net v0.0.0-20200707034311-ab3426394381
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
)

2
go.sum
View file

@ -266,6 +266,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/muka/go-bluetooth v0.0.0-20200619025933-f6113f7141c5 h1:xnTS/7y0g28W2SJeWNLMYTiTOmfW2P/YdPByoQnPvVo=
github.com/muka/go-bluetooth v0.0.0-20200619025933-f6113f7141c5/go.mod h1:yV39+EVOWdnoTe75NyKdo9iuyI3Slyh4t7eQvElUbWE=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxschmitt/golang-combinations v1.0.0 h1:NFoO7CSP8MUcFlHpe1YdewKwMa15dgDbaqkVLC5DUPI=
github.com/mxschmitt/golang-combinations v1.0.0/go.mod h1:RbMhWvfCelHR6WROvT2bVfxJvZHoEvBj71SKe+H0MYU=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da h1:qiPWuGGr+1GQE6s9NPSK8iggR/6x/V+0snIoOPYsBgc=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da/go.mod h1:DvuJJ/w1Y59rG8UTDxsMk5U+UJXJwuvUgbiJSm9yhX8=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=

View file

@ -13,6 +13,8 @@ func init() {
registry.Add("default", NewConfigurableFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -p meter -f decorateMeter -b api.Meter -o meter_decorators -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)"
// NewConfigurableFromConfig creates api.Meter from config
func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
@ -39,63 +41,42 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error)
m, _ := NewConfigurable(power)
// decorate Meter with MeterEnergy
var totalEnergy func() (float64, error)
if cc.Energy != nil {
energy, err := NewMeterEnergy(*cc.Energy)
m.totalEnergyG, err = provider.NewFloatGetterFromConfig(*cc.Energy)
if err != nil {
return nil, err
}
type EnergyDecorator struct {
api.Meter
api.MeterEnergy
}
m = &EnergyDecorator{
Meter: m,
MeterEnergy: energy,
}
totalEnergy = m.totalEnergy
}
// decorate Meter with MeterCurrent
var currents func() (float64, float64, float64, error)
if len(cc.Currents) > 0 {
currents, err := NewCurrents(cc.Currents)
if err != nil {
return nil, err
if len(cc.Currents) != 3 {
return nil, errors.New("need 3 currents")
}
type PowerEnergy interface {
api.Meter
api.MeterEnergy
var currentsG []func() (float64, error)
for _, cc := range cc.Currents {
c, err := provider.NewFloatGetterFromConfig(cc)
if err != nil {
return nil, err
}
m.currentsG = append(currentsG, c)
}
if pe, ok := m.(PowerEnergy); ok {
type CurrentDecorator struct {
PowerEnergy
api.MeterCurrent
}
m = &CurrentDecorator{
PowerEnergy: pe,
MeterCurrent: currents,
}
} else {
type CurrentDecorator struct {
api.Meter
api.MeterCurrent
}
m = &CurrentDecorator{
Meter: m,
MeterCurrent: currents,
}
}
currents = m.currents
}
return m, nil
res := decorateMeter(m, totalEnergy, currents)
return res, nil
}
// NewConfigurable creates a new charger
func NewConfigurable(currentPowerG func() (float64, error)) (api.Meter, error) {
// NewConfigurable creates a new meter
func NewConfigurable(currentPowerG func() (float64, error)) (*Meter, error) {
m := &Meter{
currentPowerG: currentPowerG,
}
@ -105,6 +86,8 @@ func NewConfigurable(currentPowerG func() (float64, error)) (api.Meter, error) {
// Meter is an api.Meter implementation with configurable getters and setters.
type Meter struct {
currentPowerG func() (float64, error)
totalEnergyG func() (float64, error)
currentsG []func() (float64, error)
}
// CurrentPower implements the Meter.CurrentPower interface
@ -112,62 +95,15 @@ func (m *Meter) CurrentPower() (float64, error) {
return m.currentPowerG()
}
// MeterEnergy is an api.MeterEnergy implementation with configurable getters and setters.
type MeterEnergy struct {
totalEnergyG func() (float64, error)
}
// NewMeterEnergy creates a new api.MeterEnergy
func NewMeterEnergy(ccEnergy provider.Config) (api.MeterEnergy, error) {
totalEnergyG, err := provider.NewFloatGetterFromConfig(ccEnergy)
if err != nil {
return nil, err
}
e := &MeterEnergy{
totalEnergyG: totalEnergyG,
}
return e, nil
}
// TotalEnergy implements the Meter.TotalEnergy interface
func (m *MeterEnergy) TotalEnergy() (float64, error) {
// totalEnergy implements the Meter.TotalEnergy interface
func (m *Meter) totalEnergy() (float64, error) {
return m.totalEnergyG()
}
// Currents is an api.MeterCurrent implementation
type Currents struct {
currentsG []func() (float64, error)
}
// NewCurrents creates a new api.MeterCurrent
func NewCurrents(ccCurrents []provider.Config) (api.MeterCurrent, error) {
if len(ccCurrents) != 3 {
return nil, errors.New("need 3 currents")
}
var currentsG []func() (float64, error)
for _, cc := range ccCurrents {
c, err := provider.NewFloatGetterFromConfig(cc)
if err != nil {
return nil, err
}
currentsG = append(currentsG, c)
}
c := &Currents{
currentsG: currentsG,
}
return c, nil
}
// Currents implements the api.Currents interface
func (c *Currents) Currents() (float64, float64, float64, error) {
// currents implements the Meter.Currents interface
func (m *Meter) currents() (float64, float64, float64, error) {
var currents []float64
for _, currentG := range c.currentsG {
for _, currentG := range m.currentsG {
c, err := currentG()
if err != nil {
return 0, 0, 0, err

69
meter/meter_decorators.go Normal file
View file

@ -0,0 +1,69 @@
package meter
// This file has been generated - do not modify
import (
"github.com/andig/evcc/api"
)
func decorateMeter(base api.Meter, meterEnergy func() (float64, error), meterCurrent func() (float64, float64, float64, error)) api.Meter {
switch {
case meterCurrent == nil && meterEnergy == nil:
return base
case meterCurrent == nil && meterEnergy != nil:
return &struct{
api.Meter
api.MeterEnergy
}{
Meter: base,
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
case meterCurrent != nil && meterEnergy == nil:
return &struct{
api.Meter
api.MeterCurrent
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
}
case meterCurrent != nil && meterEnergy != nil:
return &struct{
api.Meter
api.MeterCurrent
api.MeterEnergy
}{
Meter: base,
MeterCurrent: &decorateMeterMeterCurrentImpl{
meterCurrent: meterCurrent,
},
MeterEnergy: &decorateMeterMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
}
type decorateMeterMeterCurrentImpl struct {
meterCurrent func() (float64, float64, float64, error)
}
func (impl *decorateMeterMeterCurrentImpl) Currents() (float64, float64, float64, error) {
return impl.meterCurrent()
}
type decorateMeterMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateMeterMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}

View file

@ -25,6 +25,8 @@ func init() {
registry.Add("modbus", NewModbusFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -p meter -f decorateModbus -b api.Meter -o modbus_decorators -t "api.MeterEnergy,TotalEnergy,func() (float64, error)"
// NewModbusFromConfig creates api.Meter from config
func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
@ -88,15 +90,16 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
}
// decorate energy reading
var totalEnergy func() (float64, error)
if cc.Energy != "" {
if err := modbus.ParseOperation(device, cc.Energy, &m.opEnergy); err != nil {
log.FATAL.Fatalf("invalid measurement for energy: %s", cc.Power)
}
return &ModbusEnergy{m}, nil
totalEnergy = m.totalEnergy
}
return m, nil
return decorateModbus(m, totalEnergy), nil
}
// floatGetter executes configured modbus read operation and implements func() (float64, error)
@ -143,12 +146,7 @@ func (m *Modbus) CurrentPower() (float64, error) {
return m.floatGetter(m.opPower)
}
// ModbusEnergy decorates Modbus with api.MeterEnergy interface
type ModbusEnergy struct {
*Modbus
}
// TotalEnergy implements the Meter.TotalEnergy interface
func (m *ModbusEnergy) TotalEnergy() (float64, error) {
// totalEnergy implements the Meter.TotalEnergy interface
func (m *Modbus) totalEnergy() (float64, error) {
return m.floatGetter(m.opEnergy)
}

View file

@ -0,0 +1,35 @@
package meter
// This file has been generated - do not modify
import (
"github.com/andig/evcc/api"
)
func decorateModbus(base api.Meter, meterEnergy func() (float64, error)) api.Meter {
switch {
case meterEnergy == nil:
return base
case meterEnergy != nil:
return &struct{
api.Meter
api.MeterEnergy
}{
Meter: base,
MeterEnergy: &decorateModbusMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
}
type decorateModbusMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateModbusMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}

View file

@ -37,6 +37,8 @@ func init() {
registry.Add("sma", NewSMAFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -p meter -f decorateSMA -b api.Meter -o sma_decorators -t "api.MeterEnergy,TotalEnergy,func() (float64, error)"
// NewSMAFromConfig creates a SMA Meter from generic config
func NewSMAFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
@ -77,14 +79,15 @@ func NewSMA(uri, serial, power, energy string) (api.Meter, error) {
return nil, errors.New("missing uri or serial")
}
go sm.receive()
// decorate api.MeterEnergy
var totalEnergy func() (float64, error)
if energy != "" {
return &SMAEnergy{SMA: sm}, nil
totalEnergy = sm.totalEnergy
}
return sm, nil
go sm.receive()
return decorateSMA(sm, totalEnergy), nil
}
// update the actual meter data
@ -168,13 +171,8 @@ func (sm *SMA) Currents() (float64, float64, float64, error) {
return values.currentL1, sm.values.currentL2, sm.values.currentL3, err
}
// SMAEnergy decorates SMA with api.MeterEnergy interface
type SMAEnergy struct {
*SMA
}
// TotalEnergy implements the api.MeterEnergy interface
func (sm *SMAEnergy) TotalEnergy() (float64, error) {
// totalEnergy implements the api.MeterEnergy interface
func (sm *SMA) totalEnergy() (float64, error) {
values, err := sm.hasValue()
return values.energy, err
}

35
meter/sma_decorators.go Normal file
View file

@ -0,0 +1,35 @@
package meter
// This file has been generated - do not modify
import (
"github.com/andig/evcc/api"
)
func decorateSMA(base api.Meter, meterEnergy func() (float64, error)) api.Meter {
switch {
case meterEnergy == nil:
return base
case meterEnergy != nil:
return &struct{
api.Meter
api.MeterEnergy
}{
Meter: base,
MeterEnergy: &decorateSMAMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
}
type decorateSMAMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateSMAMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}

View file

@ -39,6 +39,8 @@ func init() {
registry.Add("tesla", NewTeslaFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -p meter -f decorateTesla -b api.Meter -o tesla_decorators -t "api.MeterEnergy,TotalEnergy,func() (float64, error)"
// NewTeslaFromConfig creates a Tesla Powerwall Meter from generic config
func NewTeslaFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
@ -80,11 +82,12 @@ func NewTesla(uri, usage string) (api.Meter, error) {
m.HTTPHelper.Client.Transport = customTransport
// decorate api.MeterEnergy
var totalEnergy func() (float64, error)
if m.usage == "load" || m.usage == "solar" {
return &TeslaEnergy{Tesla: m}, nil
totalEnergy = m.totalEnergy
}
return m, nil
return decorateTesla(m, totalEnergy), nil
}
// CurrentPower implements the Meter.CurrentPower interface
@ -101,13 +104,8 @@ func (m *Tesla) CurrentPower() (float64, error) {
return 0, fmt.Errorf("invalid usage: %s", m.usage)
}
// TeslaEnergy decorates Tesla with api.MeterEnergy interface
type TeslaEnergy struct {
*Tesla
}
// TotalEnergy implements the api.MeterEnergy interface
func (m *TeslaEnergy) TotalEnergy() (float64, error) {
// totalEnergy implements the api.MeterEnergy interface
func (m *Tesla) totalEnergy() (float64, error) {
var tr teslaResponse
_, err := m.GetJSON(m.uri, &tr)

35
meter/tesla_decorators.go Normal file
View file

@ -0,0 +1,35 @@
package meter
// This file has been generated - do not modify
import (
"github.com/andig/evcc/api"
)
func decorateTesla(base api.Meter, meterEnergy func() (float64, error)) api.Meter {
switch {
case meterEnergy == nil:
return base
case meterEnergy != nil:
return &struct{
api.Meter
api.MeterEnergy
}{
Meter: base,
MeterEnergy: &decorateTeslaMeterEnergyImpl{
meterEnergy: meterEnergy,
},
}
}
return nil
}
type decorateTeslaMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateTeslaMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}

View file

@ -5,5 +5,4 @@ package main
import (
_ "github.com/golang/mock/mockgen"
_ "github.com/mjibson/esc"
_ "golang.org/x/tools/cmd/stringer"
)