From fe07fa78381ae995b464dc47357a42dfd6301be0 Mon Sep 17 00:00:00 2001 From: andig Date: Fri, 5 Jun 2026 17:39:02 +0200 Subject: [PATCH] DriveSomethingGreater: guard against losing data on error --- vehicle/drivesomethinggreater.go | 2 +- vehicle/vw/eudataact/eudataact_test.go | 16 ---------------- vehicle/vw/eudataact/provider.go | 17 +++++++---------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/vehicle/drivesomethinggreater.go b/vehicle/drivesomethinggreater.go index 6d17bafcb..29ba2499b 100644 --- a/vehicle/drivesomethinggreater.go +++ b/vehicle/drivesomethinggreater.go @@ -60,7 +60,7 @@ func NewDriveSomethingGreaterFromConfig(other map[string]any) (api.Vehicle, erro if err == nil { v.fromVehicle(vehicle.Name(), 0) - v.Provider = eudataact.NewProvider(api, vehicle.Vin(), cc.Cache) + v.Provider = eudataact.NewProvider(log, api, vehicle.Vin(), cc.Cache) } return v, err diff --git a/vehicle/vw/eudataact/eudataact_test.go b/vehicle/vw/eudataact/eudataact_test.go index e36c3d202..663c31b97 100644 --- a/vehicle/vw/eudataact/eudataact_test.go +++ b/vehicle/vw/eudataact/eudataact_test.go @@ -106,19 +106,3 @@ func TestMerge(t *testing.T) { assert.Equal(t, "100", dst[FieldOdometer].Value, "older datapoint ignored") assert.Equal(t, "200", dst[FieldRangeSecondary].Value, "new field added") } - -// TestResetDelay verifies the cache reset is scheduled for when the portal is -// expected to deliver the dataset following the one just read. -func TestResetDelay(t *testing.T) { - now := time.Date(2026, 5, 31, 12, 0, 0, 0, time.UTC) - - // fresh dataset: reset one interval + latency later - assert.Equal(t, portalInterval+portalLatency, resetDelay(now, now)) - - // dataset already 5 min old: reset interval + latency after its timestamp - assert.Equal(t, portalInterval+portalLatency-5*time.Minute, resetDelay(now.Add(-5*time.Minute), now)) - - // next dataset already due: never reset sooner than the latency margin - assert.Equal(t, portalLatency, resetDelay(now.Add(-portalInterval), now)) - assert.Equal(t, portalLatency, resetDelay(now.Add(-time.Hour), now)) -} diff --git a/vehicle/vw/eudataact/provider.go b/vehicle/vw/eudataact/provider.go index 588e08512..76fdf2e1e 100644 --- a/vehicle/vw/eudataact/provider.go +++ b/vehicle/vw/eudataact/provider.go @@ -10,11 +10,9 @@ import ( ) const ( - // portalInterval is the cadence at which the portal delivers a new dataset - portalInterval = 15 * time.Minute // portalLatency is the margin added to a dataset's timestamp before the // following dataset is expected to be available for download - portalLatency = 30 * time.Second + portalLatency = time.Minute ) // Provider implements the vehicle api on top of the EU Data Act dataset. @@ -32,7 +30,7 @@ type Provider struct { } // NewProvider creates a vehicle api provider -func NewProvider(api *API, vin string, cache time.Duration) *Provider { +func NewProvider(log *util.Logger, api *API, vin string, cache time.Duration) *Provider { v := &Provider{} s := sharedStore(api) @@ -40,10 +38,9 @@ func NewProvider(api *API, vin string, cache time.Duration) *Provider { cached = util.ResettableCached(func() (map[string]point, error) { ts, err := s.update(vin) if err != nil { - return nil, err - } - if !ts.IsZero() { - time.AfterFunc(resetDelay(ts, time.Now()), cached.Reset) + log.ERROR.Println(err) + } else if !ts.IsZero() { + time.AfterFunc(resetDelay(ts, cache), cached.Reset) } return s.snapshot(vin), nil }, cache) @@ -56,8 +53,8 @@ func NewProvider(api *API, vin string, cache time.Duration) *Provider { // resetDelay returns the delay until the dataset following the one delivered at // ts is expected to be available. It never returns less than portalLatency so a // late or repeated dataset does not cause immediate re-polling. -func resetDelay(ts, now time.Time) time.Duration { - if d := ts.Add(portalInterval + portalLatency).Sub(now); d > portalLatency { +func resetDelay(ts time.Time, cache time.Duration) time.Duration { + if d := time.Until(ts.Add(cache + portalLatency)); d > portalLatency { return d } return portalLatency