Show charging power as 0 when not charging for the Volvo integration (#150797)

This commit is contained in:
Thomas D
2025-08-19 16:23:30 +02:00
committed by GitHub
parent 76f3397aa0
commit b52a806b36
10 changed files with 168 additions and 16 deletions

View File

@@ -260,6 +260,8 @@ class VolvoMediumIntervalCoordinator(VolvoBaseCoordinator):
"Volvo medium interval coordinator", "Volvo medium interval coordinator",
) )
self._supported_capabilities: list[str] = []
async def _async_determine_api_calls( async def _async_determine_api_calls(
self, self,
) -> list[Callable[[], Coroutine[Any, Any, Any]]]: ) -> list[Callable[[], Coroutine[Any, Any, Any]]]:
@@ -267,6 +269,31 @@ class VolvoMediumIntervalCoordinator(VolvoBaseCoordinator):
capabilities = await self.api.async_get_energy_capabilities() capabilities = await self.api.async_get_energy_capabilities()
if capabilities.get("isSupported", False): if capabilities.get("isSupported", False):
return [self.api.async_get_energy_state] self._supported_capabilities = [
key
for key, value in capabilities.items()
if isinstance(value, dict) and value.get("isSupported", False)
]
return [self._async_get_energy_state]
return [] return []
async def _async_get_energy_state(
self,
) -> dict[str, VolvoCarsValueStatusField | None]:
def _mark_ok(
field: VolvoCarsValueStatusField | None,
) -> VolvoCarsValueStatusField | None:
if field:
field.status = "OK"
return field
energy_state = await self.api.async_get_energy_state()
return {
key: _mark_ok(value)
for key, value in energy_state.items()
if key in self._supported_capabilities
}

View File

@@ -67,8 +67,8 @@ def _calculate_time_to_service(field: VolvoCarsValue) -> int:
def _charging_power_value(field: VolvoCarsValue) -> int: def _charging_power_value(field: VolvoCarsValue) -> int:
return ( return (
int(field.value) field.value
if isinstance(field, VolvoCarsValueStatusField) and field.status == "OK" if isinstance(field, VolvoCarsValueStatusField) and isinstance(field.value, int)
else 0 else 0
) )

View File

@@ -9,7 +9,7 @@ from volvocarsapi.auth import TOKEN_URL
from volvocarsapi.models import ( from volvocarsapi.models import (
VolvoCarsAvailableCommand, VolvoCarsAvailableCommand,
VolvoCarsLocation, VolvoCarsLocation,
VolvoCarsValueField, VolvoCarsValueStatusField,
VolvoCarsVehicle, VolvoCarsVehicle,
) )
@@ -98,7 +98,7 @@ async def mock_api(hass: HomeAssistant, full_model: str) -> AsyncGenerator[Async
hass, "energy_state", full_model hass, "energy_state", full_model
) )
energy_state = { energy_state = {
key: VolvoCarsValueField.from_dict(value) key: VolvoCarsValueStatusField.from_dict(value)
for key, value in energy_state_data.items() for key, value in energy_state_data.items()
} }
engine_status = await async_load_fixture_as_value_field( engine_status = await async_load_fixture_as_value_field(

View File

@@ -9,7 +9,7 @@
"chargerConnectionStatus": { "chargerConnectionStatus": {
"isSupported": true "isSupported": true
}, },
"chargingSystemStatus": { "chargingStatus": {
"isSupported": true "isSupported": true
}, },
"chargingType": { "chargingType": {
@@ -25,7 +25,7 @@
"isSupported": true "isSupported": true
}, },
"chargingCurrentLimit": { "chargingCurrentLimit": {
"isSupported": true "isSupported": false
}, },
"chargingPower": { "chargingPower": {
"isSupported": true "isSupported": true

View File

@@ -50,7 +50,7 @@
}, },
"chargingPower": { "chargingPower": {
"status": "ERROR", "status": "ERROR",
"code": "NOT_SUPPORTED", "code": "PROPERTY_NOT_FOUND",
"message": "Resource is not supported for this vehicle" "message": "No valid value could be found for the requested property"
} }
} }

View File

@@ -9,7 +9,7 @@
"chargerConnectionStatus": { "chargerConnectionStatus": {
"isSupported": true "isSupported": true
}, },
"chargingSystemStatus": { "chargingStatus": {
"isSupported": true "isSupported": true
}, },
"chargingType": { "chargingType": {

View File

@@ -9,7 +9,7 @@
"chargerConnectionStatus": { "chargerConnectionStatus": {
"isSupported": true "isSupported": true
}, },
"chargingSystemStatus": { "chargingStatus": {
"isSupported": true "isSupported": true
}, },
"chargingType": { "chargingType": {

View File

@@ -40,9 +40,10 @@
"message": "Resource is not supported for this vehicle" "message": "Resource is not supported for this vehicle"
}, },
"targetBatteryChargeLevel": { "targetBatteryChargeLevel": {
"status": "ERROR", "status": "OK",
"code": "NOT_SUPPORTED", "value": 80,
"message": "Resource is not supported for this vehicle" "unit": "percentage",
"updatedAt": "2024-09-22T09:40:12Z"
}, },
"chargingPower": { "chargingPower": {
"status": "ERROR", "status": "ERROR",

View File

@@ -232,6 +232,62 @@
'state': 'connected', 'state': 'connected',
}) })
# --- # ---
# name: test_sensor[ex30_2024][sensor.volvo_ex30_charging_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.volvo_ex30_charging_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Charging power',
'platform': 'volvo',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'charging_power',
'unique_id': 'yv1abcdefg1234567_charging_power',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_sensor[ex30_2024][sensor.volvo_ex30_charging_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Volvo EX30 Charging power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.volvo_ex30_charging_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensor[ex30_2024][sensor.volvo_ex30_charging_power_status-entry] # name: test_sensor[ex30_2024][sensor.volvo_ex30_charging_power_status-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@@ -2164,7 +2220,7 @@
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': '0', 'state': '1386',
}) })
# --- # ---
# name: test_sensor[xc40_electric_2024][sensor.volvo_xc40_charging_power_status-entry] # name: test_sensor[xc40_electric_2024][sensor.volvo_xc40_charging_power_status-entry]
@@ -3601,6 +3657,58 @@
'state': '30000', 'state': '30000',
}) })
# --- # ---
# name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_target_battery_charge_level-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.volvo_xc60_target_battery_charge_level',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Target battery charge level',
'platform': 'volvo',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'target_battery_charge_level',
'unique_id': 'yv1abcdefg1234567_target_battery_charge_level',
'unit_of_measurement': '%',
})
# ---
# name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_target_battery_charge_level-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Volvo XC60 Target battery charge level',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.volvo_xc60_target_battery_charge_level',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '80',
})
# ---
# name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_time_to_engine_service-entry] # name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_time_to_engine_service-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@@ -68,4 +68,20 @@ async def test_skip_invalid_api_fields(
with patch("homeassistant.components.volvo.PLATFORMS", [Platform.SENSOR]): with patch("homeassistant.components.volvo.PLATFORMS", [Platform.SENSOR]):
assert await setup_integration() assert await setup_integration()
assert not hass.states.get(f"sensor.volvo_{short_model}_charging_power") assert not hass.states.get(f"sensor.volvo_{short_model}_charging_current_limit")
@pytest.mark.parametrize(
"full_model",
["ex30_2024"],
)
async def test_charging_power_value(
hass: HomeAssistant,
setup_integration: Callable[[], Awaitable[bool]],
) -> None:
"""Test if charging_power_value is zero if supported, but not charging."""
with patch("homeassistant.components.volvo.PLATFORMS", [Platform.SENSOR]):
assert await setup_integration()
assert hass.states.get("sensor.volvo_ex30_charging_power").state == "0"