From a799f7ff91029c93737c862c937b88041d28c0fb Mon Sep 17 00:00:00 2001 From: Thomas D <11554546+thomasddn@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:52:12 +0200 Subject: [PATCH] Add service warning sensor to Volvo integration (#154613) --- homeassistant/components/volvo/icons.json | 6 + homeassistant/components/volvo/sensor.py | 19 + homeassistant/components/volvo/strings.json | 16 + .../volvo/snapshots/test_sensor.ambr | 456 ++++++++++++++++++ 4 files changed, 497 insertions(+) diff --git a/homeassistant/components/volvo/icons.json b/homeassistant/components/volvo/icons.json index 94c9375e47a..72f766553c6 100644 --- a/homeassistant/components/volvo/icons.json +++ b/homeassistant/components/volvo/icons.json @@ -348,6 +348,12 @@ "odometer": { "default": "mdi:counter" }, + "service_warning": { + "default": "mdi:wrench-clock", + "state": { + "no_warning": "mdi:car-wrench" + } + }, "target_battery_charge_level": { "default": "mdi:battery-medium" }, diff --git a/homeassistant/components/volvo/sensor.py b/homeassistant/components/volvo/sensor.py index d9d455f1cde..77e3fdfa29d 100644 --- a/homeassistant/components/volvo/sensor.py +++ b/homeassistant/components/volvo/sensor.py @@ -332,6 +332,25 @@ _DESCRIPTIONS: tuple[VolvoSensorDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, suggested_display_precision=1, ), + # diagnostics endpoint + VolvoSensorDescription( + key="service_warning", + api_field="serviceWarning", + device_class=SensorDeviceClass.ENUM, + options=[ + "distance_driven_almost_time_for_service", + "distance_driven_overdue_for_service", + "distance_driven_time_for_service", + "engine_hours_almost_time_for_service", + "engine_hours_overdue_for_service", + "engine_hours_time_for_service", + "no_warning", + "regular_maintenance_almost_time_for_service", + "regular_maintenance_overdue_for_service", + "regular_maintenance_time_for_service", + "unknown_warning", + ], + ), # energy state endpoint VolvoSensorDescription( key="target_battery_charge_level", diff --git a/homeassistant/components/volvo/strings.json b/homeassistant/components/volvo/strings.json index 8cce41a839f..a518fc92514 100644 --- a/homeassistant/components/volvo/strings.json +++ b/homeassistant/components/volvo/strings.json @@ -309,6 +309,22 @@ "odometer": { "name": "Odometer" }, + "service_warning": { + "name": "Service", + "state": { + "distance_driven_almost_time_for_service": "Almost time for distance service", + "distance_driven_overdue_for_service": "Distance service overdue", + "distance_driven_time_for_service": "Time for distance service", + "engine_hours_almost_time_for_service": "Almost time for engine service", + "engine_hours_overdue_for_service": "Engine service overdue", + "engine_hours_time_for_service": "Time for engine service", + "no_warning": "No warning", + "regular_maintenance_almost_time_for_service": "Almost time for service", + "regular_maintenance_overdue_for_service": "Service overdue", + "regular_maintenance_time_for_service": "Time for service", + "unknown_warning": "Unknown warning" + } + }, "target_battery_charge_level": { "name": "Target battery charge level" }, diff --git a/tests/components/volvo/snapshots/test_sensor.ambr b/tests/components/volvo/snapshots/test_sensor.ambr index 6075f7400dc..1e2441f7c07 100644 --- a/tests/components/volvo/snapshots/test_sensor.ambr +++ b/tests/components/volvo/snapshots/test_sensor.ambr @@ -752,6 +752,82 @@ 'state': '30000', }) # --- +# name: test_sensor[ex30_2024][sensor.volvo_ex30_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.volvo_ex30_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service', + 'platform': 'volvo', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_warning', + 'unique_id': 'yv1abcdefg1234567_service_warning', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[ex30_2024][sensor.volvo_ex30_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Volvo EX30 Service', + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'context': , + 'entity_id': 'sensor.volvo_ex30_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_warning', + }) +# --- # name: test_sensor[ex30_2024][sensor.volvo_ex30_target_battery_charge_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -1591,6 +1667,82 @@ 'state': '30000', }) # --- +# name: test_sensor[s90_diesel_2018][sensor.volvo_s90_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.volvo_s90_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service', + 'platform': 'volvo', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_warning', + 'unique_id': 'yv1abcdefg1234567_service_warning', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[s90_diesel_2018][sensor.volvo_s90_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Volvo S90 Service', + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'context': , + 'entity_id': 'sensor.volvo_s90_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_warning', + }) +# --- # name: test_sensor[s90_diesel_2018][sensor.volvo_s90_time_to_engine_service-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -2791,6 +2943,82 @@ 'state': '30000', }) # --- +# name: test_sensor[xc40_electric_2024][sensor.volvo_xc40_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.volvo_xc40_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service', + 'platform': 'volvo', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_warning', + 'unique_id': 'yv1abcdefg1234567_service_warning', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[xc40_electric_2024][sensor.volvo_xc40_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Volvo XC40 Service', + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'context': , + 'entity_id': 'sensor.volvo_xc40_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_warning', + }) +# --- # name: test_sensor[xc40_electric_2024][sensor.volvo_xc40_target_battery_charge_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -3865,6 +4093,82 @@ 'state': '30000', }) # --- +# name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.volvo_xc60_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service', + 'platform': 'volvo', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_warning', + 'unique_id': 'yv1abcdefg1234567_service_warning', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Volvo XC60 Service', + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'context': , + 'entity_id': 'sensor.volvo_xc60_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_warning', + }) +# --- # name: test_sensor[xc60_phev_2020][sensor.volvo_xc60_target_battery_charge_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -4648,6 +4952,82 @@ 'state': '30000', }) # --- +# name: test_sensor[xc90_petrol_2019][sensor.volvo_xc90_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.volvo_xc90_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service', + 'platform': 'volvo', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_warning', + 'unique_id': 'yv1abcdefg1234567_service_warning', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[xc90_petrol_2019][sensor.volvo_xc90_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Volvo XC90 Service', + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'context': , + 'entity_id': 'sensor.volvo_xc90_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_warning', + }) +# --- # name: test_sensor[xc90_petrol_2019][sensor.volvo_xc90_time_to_engine_service-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -5848,6 +6228,82 @@ 'state': '30000', }) # --- +# name: test_sensor[xc90_phev_2024][sensor.volvo_xc90_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.volvo_xc90_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Service', + 'platform': 'volvo', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_warning', + 'unique_id': 'yv1abcdefg1234567_service_warning', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[xc90_phev_2024][sensor.volvo_xc90_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Volvo XC90 Service', + 'options': list([ + 'distance_driven_almost_time_for_service', + 'distance_driven_overdue_for_service', + 'distance_driven_time_for_service', + 'engine_hours_almost_time_for_service', + 'engine_hours_overdue_for_service', + 'engine_hours_time_for_service', + 'no_warning', + 'regular_maintenance_almost_time_for_service', + 'regular_maintenance_overdue_for_service', + 'regular_maintenance_time_for_service', + 'unknown_warning', + ]), + }), + 'context': , + 'entity_id': 'sensor.volvo_xc90_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_warning', + }) +# --- # name: test_sensor[xc90_phev_2024][sensor.volvo_xc90_target_battery_charge_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({