diff --git a/vehicle/vw/eudataact/eudataact_test.go b/vehicle/vw/eudataact/eudataact_test.go index 1c0cd8482..1cb73adf4 100644 --- a/vehicle/vw/eudataact/eudataact_test.go +++ b/vehicle/vw/eudataact/eudataact_test.go @@ -239,15 +239,34 @@ func TestSocBatteryStateReportOnlyFallbackField(t *testing.T) { // TestGetLimitSocBcamThreshold reproduces issue #32241: the Audi Q4 e-tron // reports its battery care mode SoC limit only under the battery care mode -// field, not under the generic target soc field. +// field, not under the generic target soc field, while battery care mode is +// active. func TestGetLimitSocBcamThreshold(t *testing.T) { - data := []point{{Name: FieldChargeBcamThreshold, Value: "80"}} + data := []point{ + {Name: FieldChargeBcamThreshold, Value: "80"}, + {Name: FieldBcamActivation, Value: BcamActivationActivated}, + } limit, err := testProvider(data).GetLimitSoc() require.NoError(t, err) assert.Equal(t, int64(80), limit) } +// TestGetLimitSocBcamThresholdInactive reproduces issue #32520: with battery +// care mode inactive, the care mode threshold is unrelated to the actual +// target soc and must not shadow it. +func TestGetLimitSocBcamThresholdInactive(t *testing.T) { + data := []point{ + {Name: FieldTargetSoc, Value: "100"}, + {Name: FieldChargeBcamThreshold, Value: "80"}, + {Name: FieldBcamActivation, Value: "BCAM_ACTIVATION_DEACTIVATED"}, + } + + limit, err := testProvider(data).GetLimitSoc() + require.NoError(t, err) + assert.Equal(t, int64(100), limit) +} + // TestPoints guards that a data point with a generic field name ("value") is // stored once yet found by both its unique key and its name. func TestPoints(t *testing.T) { diff --git a/vehicle/vw/eudataact/provider.go b/vehicle/vw/eudataact/provider.go index fcc65c310..08df1f8e8 100644 --- a/vehicle/vw/eudataact/provider.go +++ b/vehicle/vw/eudataact/provider.go @@ -188,7 +188,14 @@ func (v *Provider) GetLimitSoc() (int64, error) { return 0, err } - if p := lookup(data, FieldTargetSoc, FieldChargeBcamThreshold); p != nil { + // the battery care mode threshold only reflects the target soc while care + // mode is active; otherwise it is unrelated and must not be used (see #32520) + fields := []string{FieldTargetSoc} + if p := find(data, FieldBcamActivation); p != nil && p.Value == BcamActivationActivated { + fields = append(fields, FieldChargeBcamThreshold) + } + + if p := lookup(data, fields...); p != nil { f, err := strconv.ParseFloat(p.Value, 64) return int64(f), err } diff --git a/vehicle/vw/eudataact/types.go b/vehicle/vw/eudataact/types.go index 8b5ffa5ab..b382e1dce 100644 --- a/vehicle/vw/eudataact/types.go +++ b/vehicle/vw/eudataact/types.go @@ -132,6 +132,10 @@ const ( // target soc FieldTargetSoc = "settings.target_soc" FieldChargeBcamThreshold = "battery_care_mode.charge_bcam_threshold" // Audi Q4 e-tron battery care mode target soc + FieldBcamActivation = "setting.bcam_activation" // only equals target soc while battery care mode is active + + // battery care mode activation values (see FieldBcamActivation) + BcamActivationActivated = "BCAM_ACTIVATION_ACTIVATED" // range FieldRangeCombined = "cruising_range_combined"