From 6cd395d6d9503f673c005295beddf313ee4d87f7 Mon Sep 17 00:00:00 2001 From: andig Date: Fri, 22 Mar 2024 19:09:24 +0100 Subject: [PATCH 1/4] Tesla: validate vehicle current --- vehicle/tesla/controller.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vehicle/tesla/controller.go b/vehicle/tesla/controller.go index 0152ca8ef0..2c3795a55f 100644 --- a/vehicle/tesla/controller.go +++ b/vehicle/tesla/controller.go @@ -3,8 +3,10 @@ package tesla import ( "errors" "slices" + "time" "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/provider" "github.com/evcc-io/evcc/util/sponsor" "github.com/evcc-io/tesla-proxy-client" ) @@ -13,13 +15,19 @@ const ProxyBaseUrl = "https://tesla.evcc.io" type Controller struct { vehicle *tesla.Vehicle + dataG provider.Cacheable[*tesla.VehicleData] } // NewController creates a vehicle current and charge controller func NewController(vehicle *tesla.Vehicle) *Controller { impl := &Controller{ vehicle: vehicle, + dataG: provider.ResettableCached(func() (*tesla.VehicleData, error) { + res, err := vehicle.Data() + return res, apiError(err) + }, time.Minute), } + return impl } @@ -31,9 +39,19 @@ func (v *Controller) MaxCurrent(current int64) error { return api.ErrSponsorRequired } + v.dataG.Reset() + return apiError(v.vehicle.SetChargingAmps(int(current))) } +var _ api.CurrentGetter = (*Controller)(nil) + +// StartCharge implements the api.VehicleChargeController interface +func (v *Controller) GetMaxCurrent() (float64, error) { + res, err := v.dataG.Get() + return float64(res.Response.ChargeState.ChargeRate), err +} + var _ api.ChargeController = (*Controller)(nil) // ChargeEnable implements the api.ChargeController interface From 6b1a58af5061d60a7e79b0f0997ae23dd8d5ddcf Mon Sep 17 00:00:00 2001 From: andig Date: Fri, 22 Mar 2024 19:15:55 +0100 Subject: [PATCH 2/4] wip --- vehicle/tesla.go | 2 +- vehicle/tesla/api_test.go | 2 +- vehicle/tesla/controller.go | 28 +++++++++++++++++----------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/vehicle/tesla.go b/vehicle/tesla.go index 2a9c9429c8..62b9acf58c 100644 --- a/vehicle/tesla.go +++ b/vehicle/tesla.go @@ -112,7 +112,7 @@ func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) { v := &Tesla{ embed: &cc.embed, Provider: tesla.NewProvider(vehicle, cc.Cache), - Controller: tesla.NewController(vehicle.WithClient(tcc)), + Controller: tesla.NewController(vehicle, vehicle.WithClient(tcc)), } if v.Title_ == "" { diff --git a/vehicle/tesla/api_test.go b/vehicle/tesla/api_test.go index f677469254..19b02f359f 100644 --- a/vehicle/tesla/api_test.go +++ b/vehicle/tesla/api_test.go @@ -40,5 +40,5 @@ func TestCommandResponse(t *testing.T) { v, err := client.Vehicle("abc") require.NoError(t, err) - require.ErrorIs(t, NewController(v).ChargeEnable(true), api.ErrAsleep) + require.ErrorIs(t, NewController(v, v).ChargeEnable(true), api.ErrAsleep) } diff --git a/vehicle/tesla/controller.go b/vehicle/tesla/controller.go index 2c3795a55f..ff7619c8db 100644 --- a/vehicle/tesla/controller.go +++ b/vehicle/tesla/controller.go @@ -15,20 +15,26 @@ const ProxyBaseUrl = "https://tesla.evcc.io" type Controller struct { vehicle *tesla.Vehicle - dataG provider.Cacheable[*tesla.VehicleData] + current int64 + dataG provider.Cacheable[float64] } // NewController creates a vehicle current and charge controller -func NewController(vehicle *tesla.Vehicle) *Controller { - impl := &Controller{ - vehicle: vehicle, - dataG: provider.ResettableCached(func() (*tesla.VehicleData, error) { - res, err := vehicle.Data() - return res, apiError(err) - }, time.Minute), +func NewController(ro, rw *tesla.Vehicle) *Controller { + v := &Controller{ + vehicle: rw, } - return impl + v.dataG = provider.ResettableCached(func() (float64, error) { + if v.current >= 6 { + // assume match above 6A to save API requests + return float64(v.current), nil + } + res, err := ro.Data() + return float64(res.Response.ChargeState.ChargeRate), apiError(err) + }, time.Minute) + + return v } var _ api.CurrentController = (*Controller)(nil) @@ -39,6 +45,7 @@ func (v *Controller) MaxCurrent(current int64) error { return api.ErrSponsorRequired } + v.current = current v.dataG.Reset() return apiError(v.vehicle.SetChargingAmps(int(current))) @@ -48,8 +55,7 @@ var _ api.CurrentGetter = (*Controller)(nil) // StartCharge implements the api.VehicleChargeController interface func (v *Controller) GetMaxCurrent() (float64, error) { - res, err := v.dataG.Get() - return float64(res.Response.ChargeState.ChargeRate), err + return v.dataG.Get() } var _ api.ChargeController = (*Controller)(nil) From 50cda3cc88eec12d34e936188e25f9cd0eaa04ec Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 24 Mar 2024 10:29:08 +0100 Subject: [PATCH 3/4] Proxy vehicle.GetMaxCurrent --- charger/twc3.go | 12 ++++++++++++ core/loadpoint.go | 21 ++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/charger/twc3.go b/charger/twc3.go index 1ec03994c3..416e8848b8 100644 --- a/charger/twc3.go +++ b/charger/twc3.go @@ -146,6 +146,18 @@ func (c *Twc3) MaxCurrent(current int64) error { return v.MaxCurrent(current) } +var _ api.CurrentGetter = (*Twc3)(nil) + +// GetMaxCurrent implements the api.CurrentGetter interface +func (c *Twc3) GetMaxCurrent() (float64, error) { + v, ok := c.lp.GetVehicle().(api.CurrentGetter) + if !ok { + return 0, api.ErrNotAvailable + } + + return v.GetMaxCurrent() +} + var _ api.ChargeRater = (*Twc3)(nil) // ChargedEnergy implements the api.ChargeRater interface diff --git a/core/loadpoint.go b/core/loadpoint.go index 399c32bab9..563c247331 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -689,18 +689,17 @@ func (lp *Loadpoint) syncCharger() error { if enabled == lp.enabled { // sync max current if charger, ok := lp.charger.(api.CurrentGetter); ok && enabled { - current, err := charger.GetMaxCurrent() - if err != nil { - return fmt.Errorf("charger get max current: %w", err) - } - - // smallest adjustment most PWM-Controllers can do is: 100%÷256×0,6A = 0.234A - if math.Abs(lp.chargeCurrent-current) > 0.23 { - if lp.chargerUpdateCompleted() { - lp.log.WARN.Printf("charger logic error: current mismatch (got %.3gA, expected %.3gA)", current, lp.chargeCurrent) + if current, err := charger.GetMaxCurrent(); err == nil { + // smallest adjustment most PWM-Controllers can do is: 100%÷256×0,6A = 0.234A + if math.Abs(lp.chargeCurrent-current) > 0.23 { + if lp.chargerUpdateCompleted() { + lp.log.WARN.Printf("charger logic error: current mismatch (got %.3gA, expected %.3gA)", current, lp.chargeCurrent) + } + lp.chargeCurrent = current + lp.bus.Publish(evChargeCurrent, lp.chargeCurrent) } - lp.chargeCurrent = current - lp.bus.Publish(evChargeCurrent, lp.chargeCurrent) + } else if !errors.Is(err, api.ErrNotAvailable) { + return fmt.Errorf("charger get max current: %w", err) } } From b7c5c09299a4292946f766270807abba96d6b07d Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 24 Mar 2024 12:10:18 +0100 Subject: [PATCH 4/4] wip --- vehicle/tesla/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vehicle/tesla/controller.go b/vehicle/tesla/controller.go index ff7619c8db..61cf046d00 100644 --- a/vehicle/tesla/controller.go +++ b/vehicle/tesla/controller.go @@ -31,7 +31,7 @@ func NewController(ro, rw *tesla.Vehicle) *Controller { return float64(v.current), nil } res, err := ro.Data() - return float64(res.Response.ChargeState.ChargeRate), apiError(err) + return float64(res.Response.ChargeState.ChargeAmps), apiError(err) }, time.Minute) return v