diff --git a/charger/go-e.go b/charger/go-e.go index 6418ce86d..4f4b3aa04 100644 --- a/charger/go-e.go +++ b/charger/go-e.go @@ -38,13 +38,18 @@ type GoE struct { } func init() { - registry.Add("go-e", NewGoEFromConfig) + registry.Add("go-e", func(other map[string]interface{}) (api.Charger, error) { + return newGoEFromConfig(true, other) + }) + registry.Add("go-e-v3", func(other map[string]interface{}) (api.Charger, error) { + return newGoEFromConfig(false, other) + }) } -//go:generate decorate -f decorateGoE -b *GoE -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) error" +//go:generate decorate -f decorateGoE -b *GoE -r api.Charger -t "api.ChargeRater,ChargedEnergy,func() (float64, error)" -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -// NewGoEFromConfig creates a go-e charger from generic config -func NewGoEFromConfig(other map[string]interface{}) (api.Charger, error) { +// newGoEFromConfig creates a go-e charger from generic config +func newGoEFromConfig(v2 bool, other map[string]interface{}) (api.Charger, error) { cc := struct { Token string URI string @@ -61,11 +66,26 @@ func NewGoEFromConfig(other map[string]interface{}) (api.Charger, error) { return nil, errors.New("must have either uri or token") } - return NewGoE(cc.URI, cc.Token, cc.Cache) + c, err := NewGoE(cc.URI, cc.Token, cc.Cache) + if err != nil { + return nil, err + } + + var chargedEnergy func() (float64, error) + if v2 { + chargedEnergy = c.chargedEnergy + } + + var phases1p3p func(int) error + if c.api.IsV2() { + phases1p3p = c.phases1p3p + } + + return decorateGoE(c, chargedEnergy, phases1p3p), nil } // NewGoE creates GoE charger -func NewGoE(uri, token string, cache time.Duration) (api.Charger, error) { +func NewGoE(uri, token string, cache time.Duration) (*GoE, error) { c := &GoE{} log := util.NewLogger("go-e").Redact(token) @@ -80,10 +100,6 @@ func NewGoE(uri, token string, cache time.Duration) (api.Charger, error) { return nil, api.ErrSponsorRequired } - if c.api.IsV2() { - return decorateGoE(c, c.phases1p3p), nil - } - return c, nil } @@ -203,6 +219,17 @@ func (c *GoE) TotalEnergy() (float64, error) { return resp.TotalEnergy(), err } +// chargedEnergy implements the api.ChargeRater interface - v2 only +// https://github.com/evcc-io/evcc/issues/13726 +func (c *GoE) chargedEnergy() (float64, error) { + resp, err := c.api.Status() + if err != nil { + return 0, err + } + + return resp.ChargedEnergy(), err +} + // phases1p3p implements the api.PhaseSwitcher interface - v2 only func (c *GoE) phases1p3p(phases int) error { if phases == 3 { diff --git a/charger/go-e_decorators.go b/charger/go-e_decorators.go index 43aac409a..ed0a7249b 100644 --- a/charger/go-e_decorators.go +++ b/charger/go-e_decorators.go @@ -6,12 +6,23 @@ import ( "github.com/evcc-io/evcc/api" ) -func decorateGoE(base *GoE, phaseSwitcher func(int) error) api.Charger { +func decorateGoE(base *GoE, chargeRater func() (float64, error), phaseSwitcher func(int) error) api.Charger { switch { - case phaseSwitcher == nil: + case chargeRater == nil && phaseSwitcher == nil: return base - case phaseSwitcher != nil: + case chargeRater != nil && phaseSwitcher == nil: + return &struct { + *GoE + api.ChargeRater + }{ + GoE: base, + ChargeRater: &decorateGoEChargeRaterImpl{ + chargeRater: chargeRater, + }, + } + + case chargeRater == nil && phaseSwitcher != nil: return &struct { *GoE api.PhaseSwitcher @@ -21,11 +32,34 @@ func decorateGoE(base *GoE, phaseSwitcher func(int) error) api.Charger { phaseSwitcher: phaseSwitcher, }, } + + case chargeRater != nil && phaseSwitcher != nil: + return &struct { + *GoE + api.ChargeRater + api.PhaseSwitcher + }{ + GoE: base, + ChargeRater: &decorateGoEChargeRaterImpl{ + chargeRater: chargeRater, + }, + PhaseSwitcher: &decorateGoEPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } } return nil } +type decorateGoEChargeRaterImpl struct { + chargeRater func() (float64, error) +} + +func (impl *decorateGoEChargeRaterImpl) ChargedEnergy() (float64, error) { + return impl.chargeRater() +} + type decorateGoEPhaseSwitcherImpl struct { phaseSwitcher func(int) error } diff --git a/charger/go-e_test.go b/charger/go-e_test.go index 7274e0d04..bb9d30210 100644 --- a/charger/go-e_test.go +++ b/charger/go-e_test.go @@ -33,12 +33,11 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func TestGoEV1(t *testing.T) { - h := &handler{} - srv := httptest.NewServer(h) + srv := httptest.NewServer(new(handler)) sponsor.Subject = "foo" - wb, err := NewGoE(srv.URL, "", 0) + wb, err := newGoEFromConfig(false, map[string]any{"uri": srv.URL}) if err != nil { t.Error(err) } @@ -57,13 +56,12 @@ func TestGoEV1(t *testing.T) { } func TestGoEV2(t *testing.T) { - h := &handler{} - srv := httptest.NewServer(h) + srv := httptest.NewServer(new(handler)) + srv.Config.Handler.(*handler).expect("/api/status?filter=alw") sponsor.Subject = "foo" - h.expect("/api/status?filter=alw") - wb, err := NewGoE(srv.URL, "", 0) + wb, err := newGoEFromConfig(false, map[string]any{"uri": srv.URL}) if err != nil { t.Error(err) } diff --git a/templates/definition/charger/go-e-v3.yaml b/templates/definition/charger/go-e-v3.yaml index af427d079..db523f023 100644 --- a/templates/definition/charger/go-e-v3.yaml +++ b/templates/definition/charger/go-e-v3.yaml @@ -9,19 +9,15 @@ requirements: description: de: | Benötigt mindestens Firmware 052.1 oder neuer. - Für 1P/3P-Phasenumschaltung muss die HTTP API v2 im Charger aktiviert sein. - In der Go-E App (Menüpunkt "Auto") sollte die Option "Ausstecken simulieren" aktiviert sein. en: | Requires firmware 052.1 or later. - For 1P/3P-Phase switching the HTTP API v2 in the charger setup needs to be activated. - The “simulate unplugging” option should be activated in the Go-E app ("Car" menu item). evcc: ["sponsorship"] params: - name: host render: | - type: go-e + type: go-e-v3 uri: http://{{ .host }}