Go-E: re-add session energy for v2 (#18138)

This commit is contained in:
andig 2025-01-08 23:16:12 +01:00 • committed by GitHub
parent 879378dc4f
commit 25902b5fdd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 80 additions and 25 deletions

View file

@ -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 {

View file

@ -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
}

View file

@ -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)
}

View file

@ -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 }}