Volvo: Skip unsupported API fields (#150285)

This commit is contained in:
Thomas D
2025-08-09 12:24:53 +02:00
committed by GitHub
parent fb64ff1d17
commit f8d3bc1b89
8 changed files with 1096 additions and 114 deletions

View File

@@ -15,6 +15,7 @@ from volvocarsapi.models import (
VolvoAuthException,
VolvoCarsApiBaseModel,
VolvoCarsValue,
VolvoCarsValueStatusField,
VolvoCarsVehicle,
)
@@ -36,6 +37,16 @@ type VolvoConfigEntry = ConfigEntry[tuple[VolvoBaseCoordinator, ...]]
type CoordinatorData = dict[str, VolvoCarsApiBaseModel | None]
def _is_invalid_api_field(field: VolvoCarsApiBaseModel | None) -> bool:
if not field:
return True
if isinstance(field, VolvoCarsValueStatusField) and field.status == "ERROR":
return True
return False
class VolvoBaseCoordinator(DataUpdateCoordinator[CoordinatorData]):
"""Volvo base coordinator."""
@@ -121,7 +132,13 @@ class VolvoBaseCoordinator(DataUpdateCoordinator[CoordinatorData]):
translation_key="update_failed",
) from result
data |= cast(CoordinatorData, result)
api_data = cast(CoordinatorData, result)
data |= {
key: field
for key, field in api_data.items()
if not _is_invalid_api_field(field)
}
valid = True
# Raise an error if not a single API call succeeded

View File

@@ -20,6 +20,12 @@ _MODEL_SPECIFIC_RESPONSES = {
"statistics",
"vehicle",
],
"xc60_phev_2020": [
"energy_capabilities",
"energy_state",
"statistics",
"vehicle",
],
"xc90_petrol_2019": ["commands", "statistics", "vehicle"],
}

View File

@@ -0,0 +1,33 @@
{
"isSupported": true,
"batteryChargeLevel": {
"isSupported": false
},
"electricRange": {
"isSupported": false
},
"chargerConnectionStatus": {
"isSupported": true
},
"chargingSystemStatus": {
"isSupported": true
},
"chargingType": {
"isSupported": false
},
"chargerPowerStatus": {
"isSupported": false
},
"estimatedChargingTimeToTargetBatteryChargeLevel": {
"isSupported": false
},
"targetBatteryChargeLevel": {
"isSupported": true
},
"chargingCurrentLimit": {
"isSupported": false
},
"chargingPower": {
"isSupported": false
}
}

View File

@@ -0,0 +1,52 @@
{
"batteryChargeLevel": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"electricRange": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"chargerConnectionStatus": {
"status": "OK",
"value": "DISCONNECTED",
"updatedAt": "2025-08-07T20:29:18Z"
},
"chargingStatus": {
"status": "OK",
"value": "IDLE",
"updatedAt": "2025-08-07T20:29:18Z"
},
"chargingType": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"chargerPowerStatus": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"estimatedChargingTimeToTargetBatteryChargeLevel": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"chargingCurrentLimit": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"targetBatteryChargeLevel": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
},
"chargingPower": {
"status": "ERROR",
"code": "NOT_SUPPORTED",
"message": "Resource is not supported for this vehicle"
}
}

View File

@@ -0,0 +1,32 @@
{
"averageFuelConsumption": {
"value": 4.0,
"unit": "l/100km",
"timestamp": "2025-08-07T20:29:18.343Z"
},
"averageSpeed": {
"value": 65,
"unit": "km/h",
"timestamp": "2025-08-07T20:29:18.343Z"
},
"tripMeterManual": {
"value": 219.7,
"unit": "km",
"timestamp": "2025-08-07T20:29:18.343Z"
},
"tripMeterAutomatic": {
"value": 0.0,
"unit": "km",
"timestamp": "2025-08-07T20:29:18.343Z"
},
"distanceToEmptyTank": {
"value": 920,
"unit": "km",
"timestamp": "2025-08-07T20:29:18.343Z"
},
"distanceToEmptyBattery": {
"value": 29,
"unit": "km",
"timestamp": "2025-08-07T20:29:18.343Z"
}
}

View File

@@ -0,0 +1,17 @@
{
"vin": "YV1ABCDEFG1234567",
"modelYear": 2020,
"gearbox": "AUTOMATIC",
"fuelType": "PETROL/ELECTRIC",
"externalColour": "Bright Silver",
"batteryCapacityKWH": 11.832,
"images": {
"exteriorImageUrl": "https://cas.volvocars.com/image/dynamic/MY20_0000/123/exterior-v1/_/default.png?market=se&client=public-api-engineering&angle=1&bg=00000000&w=1920",
"internalImageUrl": "https://cas.volvocars.com/image/dynamic/MY20_0000/123/interior-v1/_/default.jpg?market=se&client=public-api-engineering&angle=0&w=1920"
},
"descriptions": {
"model": "XC60",
"upholstery": "CHARCOAL/LEABR3/CHARC/SPO",
"steering": "LEFT"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,13 @@ from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.parametrize(
"full_model",
["ex30_2024", "s90_diesel_2018", "xc40_electric_2024", "xc90_petrol_2019"],
[
"ex30_2024",
"s90_diesel_2018",
"xc40_electric_2024",
"xc60_phev_2020",
"xc90_petrol_2019",
],
)
async def test_sensor(
hass: HomeAssistant,
@@ -46,3 +52,20 @@ async def test_distance_to_empty_battery(
assert await setup_integration()
assert hass.states.get("sensor.volvo_xc40_distance_to_empty_battery").state == "250"
@pytest.mark.parametrize(
("full_model", "short_model"),
[("ex30_2024", "ex30"), ("xc60_phev_2020", "xc60")],
)
async def test_skip_invalid_api_fields(
hass: HomeAssistant,
setup_integration: Callable[[], Awaitable[bool]],
short_model: str,
) -> None:
"""Test if invalid values are not creating a sensor."""
with patch("homeassistant.components.volvo.PLATFORMS", [Platform.SENSOR]):
assert await setup_integration()
assert not hass.states.get(f"sensor.volvo_{short_model}_charging_power")