EU Data Act: prefer valid battery_level_HV.value for SoC (#31092)

This commit is contained in:
andig 2026-06-21 13:54:35 +02:00 • committed by GitHub
parent be75289a3b
commit c837c1e281
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 39 additions and 5 deletions

View file

@ -142,16 +142,16 @@ func TestSocFreshestField(t *testing.T) {
// first datasets carry both SoC fields at 57, the high-priority field winning
deliver(map[string]point{
FieldBatteryStateReportSoc: {Value: "57"},
FieldHvBatteryLevel: {Value: "57.0"},
FieldHvBatteryLevelValue: {Value: "57.0"},
})
deliver(map[string]point{
FieldBatteryStateReportSoc: {Value: "57"},
FieldHvBatteryLevel: {Value: "57.0"},
FieldHvBatteryLevelValue: {Value: "57.0"},
})
// later datasets only refresh the fallback field as the car charges
for _, v := range []string{"58.0", "59.0", "61.0"} {
deliver(map[string]point{FieldHvBatteryLevel: {Value: v}})
deliver(map[string]point{FieldHvBatteryLevelValue: {Value: v}})
}
soc, err := testProvider(data).Soc()
@ -159,6 +159,28 @@ func TestSocFreshestField(t *testing.T) {
assert.Equal(t, 61.0, soc, "the still-updating fallback wins over the stale high-priority field")
}
// TestSocHvBatteryLevelValid reproduces #31084: battery_level_HV.value (74) is
// preferred over ambiguous battery_state_report.soc (45) only when state is VALID.
func TestSocHvBatteryLevelValid(t *testing.T) {
raw := []dataPoint{
{DataFieldName: FieldBatteryStateReportSoc, Value: "74"},
{DataFieldName: FieldBatteryStateReportSoc, Value: "45"},
{DataFieldName: FieldBatteryStateReportSoc, Value: "45"},
{DataFieldName: FieldHvBatteryLevelValue, Value: "74.0"},
}
// without a VALID flag the ambiguous battery_state_report.soc still wins (45)
soc, err := testProvider(points(raw)).Soc()
require.NoError(t, err)
assert.Equal(t, 45.0, soc, "prior behaviour kept when HV level is not flagged valid")
// with battery_level_HV.state VALID, battery_level_HV.value (74) is preferred
valid := append(raw, dataPoint{DataFieldName: FieldHvBatteryLevelState, Value: hvBatteryLevelValid})
soc, err = testProvider(points(valid)).Soc()
require.NoError(t, err)
assert.Equal(t, 74.0, soc, "battery_level_HV.value wins when flagged VALID")
}
// TestPoints guards that a data point with a generic field name ("value") is
// indexed by its unique key while the name stays indexed (and thus logged).
func TestPoints(t *testing.T) {

View file

@ -81,7 +81,14 @@ func (v *Provider) Soc() (float64, error) {
return 0, err
}
if p := lookup(data, FieldBatteryStateReportSoc, FieldSoc, FieldHvSoc, FieldHvBatteryLevel); p != nil {
// use battery_level_HV.value when its state reports valid
if s, ok := data[FieldHvBatteryLevelState]; ok && s.Value == hvBatteryLevelValid {
if p, ok := data[FieldHvBatteryLevelValue]; ok {
return strconv.ParseFloat(p.Value, 64)
}
}
if p := lookup(data, FieldBatteryStateReportSoc, FieldSoc, FieldHvSoc, FieldHvBatteryLevelValue); p != nil {
return strconv.ParseFloat(p.Value, 64)
}

View file

@ -110,7 +110,8 @@ const (
FieldBatteryStateReportSoc = "battery_state_report.soc"
FieldSoc = "state_of_charge"
FieldHvSoc = "hv_soc"
FieldHvBatteryLevel = "battery_level_HV.value"
FieldHvBatteryLevelValue = "battery_level_HV.value"
FieldHvBatteryLevelState = "battery_level_HV.state"
// target soc
FieldTargetSoc = "settings.target_soc"
@ -129,6 +130,10 @@ const (
FieldRemainingTime = "remaining_charging_time"
)
// hvBatteryLevelValid is the battery_level_HV.state value that marks
// battery_level_HV.value as a trustworthy SoC reading
const hvBatteryLevelValid = "VALID"
// knownKeys lists data point GUIDs that are indexed by their key instead of the
// generic, non-unique DataFieldName they are delivered with
var knownKeys = map[string]struct{}{