diff --git a/vehicle/seat/cupra/provider.go b/vehicle/seat/cupra/provider.go index a604a6909..9fa3f82b2 100644 --- a/vehicle/seat/cupra/provider.go +++ b/vehicle/seat/cupra/provider.go @@ -66,13 +66,17 @@ func (v *Provider) Status() (api.ChargeStatus, error) { return api.StatusNone, err } + // BatteryCardStatus is the authoritative disconnect signal — the Cupra API + // only emits "notConnected" when the cable is unplugged. When the car is + // plugged the field is omitted and Status alone is unreliable: it reports + // "NotReadyForCharging" both for an unplugged car. // https://github.com/evcc-io/evcc/issues/30045 if strings.ToLower(res.Services.Charging.BatteryCardStatus) == "notconnected" { return api.StatusA, nil } switch strings.ToLower(res.Services.Charging.Status) { - case "connected", "readyforcharging", "error": + case "connected", "readyforcharging", "notreadyforcharging", "error": return api.StatusB, nil case "charging": return api.StatusC, nil diff --git a/vehicle/seat/cupra/provider_test.go b/vehicle/seat/cupra/provider_test.go new file mode 100644 index 000000000..119743c12 --- /dev/null +++ b/vehicle/seat/cupra/provider_test.go @@ -0,0 +1,53 @@ +package cupra + +import ( + "testing" + + "github.com/evcc-io/evcc/api" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestProviderStatus covers the Cupra v5 charging-status payload variants +// captured in issues #30045 and #30118. +func TestProviderStatus(t *testing.T) { + cases := []struct { + name string + statusField string + batteryCardStatus string + want api.ChargeStatus + }{ + // cable not plugged in: API emits batteryCardStatus=notConnected + {"unplugged", "NotReadyForCharging", "notConnected", api.StatusA}, + // charging: API omits batteryCardStatus + {"charging", "Charging", "", api.StatusC}, + // plugged but not charging (e.g. chargeMode=off): API omits + // batteryCardStatus, Status reports NotReadyForCharging — must NOT be + // misread as disconnected (#30118) + {"plugged_paused", "NotReadyForCharging", "", api.StatusB}, + // plugged, ready to charge but not yet started + {"ready", "ReadyForCharging", "", api.StatusB}, + // already-connected pre-v5 payload + {"connected", "Connected", "", api.StatusB}, + // fault / error while plugged + {"error", "error", "", api.StatusB}, + // unknown future status with no card info defaults to disconnected + {"unknown", "SomethingNew", "", api.StatusA}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + var s Status + s.Services.Charging.Status = tc.statusField + s.Services.Charging.BatteryCardStatus = tc.batteryCardStatus + + v := &Provider{ + statusG: func() (Status, error) { return s, nil }, + } + + got, err := v.Status() + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +}