diff --git a/homeassistant/components/smartthings/sensor.py b/homeassistant/components/smartthings/sensor.py index 6d2ce6417da..f93b27337e1 100644 --- a/homeassistant/components/smartthings/sensor.py +++ b/homeassistant/components/smartthings/sensor.py @@ -7,7 +7,7 @@ from dataclasses import dataclass from datetime import datetime from typing import Any, cast -from pysmartthings import Attribute, Capability, SmartThings, Status +from pysmartthings import Attribute, Capability, ComponentStatus, SmartThings, Status from homeassistant.components.automation import automations_with_entity from homeassistant.components.script import scripts_with_entity @@ -140,6 +140,7 @@ class SmartThingsSensorEntityDescription(SensorEntityDescription): options_attribute: Attribute | None = None exists_fn: Callable[[Status], bool] | None = None use_temperature_unit: bool = False + deprecated: Callable[[ComponentStatus], str | None] | None = None CAPABILITY_TO_SENSORS: dict[ @@ -196,6 +197,17 @@ CAPABILITY_TO_SENSORS: dict[ key=Attribute.VOLUME, translation_key="audio_volume", native_unit_of_measurement=PERCENTAGE, + deprecated=( + lambda status: "media_player" + if all( + capability in status + for capability in ( + Capability.AUDIO_MUTE, + Capability.MEDIA_PLAYBACK, + ) + ) + else None + ), ) ] }, @@ -319,6 +331,7 @@ CAPABILITY_TO_SENSORS: dict[ translation_key="dryer_machine_state", options=WASHER_OPTIONS, device_class=SensorDeviceClass.ENUM, + deprecated=lambda _: "machine_state", ) ], Attribute.DRYER_JOB_STATE: [ @@ -470,6 +483,7 @@ CAPABILITY_TO_SENSORS: dict[ device_class=SensorDeviceClass.ENUM, options_attribute=Attribute.SUPPORTED_INPUT_SOURCES, value_fn=lambda value: value.lower() if value else None, + deprecated=lambda _: "media_player", ) ] }, @@ -478,6 +492,7 @@ CAPABILITY_TO_SENSORS: dict[ SmartThingsSensorEntityDescription( key=Attribute.PLAYBACK_REPEAT_MODE, translation_key="media_playback_repeat", + deprecated=lambda _: "media_player", ) ] }, @@ -486,6 +501,7 @@ CAPABILITY_TO_SENSORS: dict[ SmartThingsSensorEntityDescription( key=Attribute.PLAYBACK_SHUFFLE, translation_key="media_playback_shuffle", + deprecated=lambda _: "media_player", ) ] }, @@ -504,6 +520,7 @@ CAPABILITY_TO_SENSORS: dict[ ], device_class=SensorDeviceClass.ENUM, value_fn=lambda value: MEDIA_PLAYBACK_STATE_MAP.get(value, value), + deprecated=lambda _: "media_player", ) ] }, @@ -949,6 +966,7 @@ CAPABILITY_TO_SENSORS: dict[ translation_key="washer_machine_state", options=WASHER_OPTIONS, device_class=SensorDeviceClass.ENUM, + deprecated=lambda _: "machine_state", ) ], Attribute.WASHER_JOB_STATE: [ @@ -1102,13 +1120,9 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity): """Call when entity is added to hass.""" await super().async_added_to_hass() if ( - self.capability - not in { - Capability.DISHWASHER_OPERATING_STATE, - Capability.DRYER_OPERATING_STATE, - Capability.WASHER_OPERATING_STATE, - } - or self._attribute is not Attribute.MACHINE_STATE + not self.entity_description.deprecated + or (reason := self.entity_description.deprecated(self.device.status[MAIN])) + is None ): return automations = automations_with_entity(self.hass, self.entity_id) @@ -1130,11 +1144,11 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity): async_create_issue( self.hass, DOMAIN, - f"deprecated_machine_state_{self.entity_id}", + f"deprecated_{reason}_{self.entity_id}", breaks_in_ha_version="2025.10.0", is_fixable=False, severity=IssueSeverity.WARNING, - translation_key="deprecated_machine_state", + translation_key=f"deprecated_{reason}", translation_placeholders={ "entity": self.entity_id, "items": "\n".join(items_list), @@ -1145,15 +1159,9 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity): """Call when entity will be removed from hass.""" await super().async_will_remove_from_hass() if ( - self.capability - not in { - Capability.DISHWASHER_OPERATING_STATE, - Capability.DRYER_OPERATING_STATE, - Capability.WASHER_OPERATING_STATE, - } - or self._attribute is not Attribute.MACHINE_STATE + not self.entity_description.deprecated + or (reason := self.entity_description.deprecated(self.device.status[MAIN])) + is None ): return - async_delete_issue( - self.hass, DOMAIN, f"deprecated_machine_state_{self.entity_id}" - ) + async_delete_issue(self.hass, DOMAIN, f"deprecated_{reason}_{self.entity_id}") diff --git a/homeassistant/components/smartthings/strings.json b/homeassistant/components/smartthings/strings.json index dfba018b8d9..7e812845839 100644 --- a/homeassistant/components/smartthings/strings.json +++ b/homeassistant/components/smartthings/strings.json @@ -494,6 +494,10 @@ "deprecated_switch_appliance": { "title": "Deprecated switch detected in some automations or scripts", "description": "The switch `{entity}` is deprecated because the actions did not work, so it has been replaced with a binary sensor instead.\n\nThe switch was used in the following automations or scripts:\n{items}\n\nPlease use them in the above automations or scripts to fix this issue." + }, + "deprecated_media_player": { + "title": "Deprecated sensor detected in some automations or scripts", + "description": "The sensor `{entity}` is deprecated because it has been replaced with a media player entity.\n\nThe sensor was used in the following automations or scripts:\n{items}\n\nPlease use the media player entity in the above automations or scripts to fix this issue." } } } diff --git a/tests/components/smartthings/test_sensor.py b/tests/components/smartthings/test_sensor.py index 229644e2473..cf49d02b910 100644 --- a/tests/components/smartthings/test_sensor.py +++ b/tests/components/smartthings/test_sensor.py @@ -58,10 +58,23 @@ async def test_state_update( @pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.parametrize( - ("device_fixture", "entity_id"), + ("device_fixture", "entity_id", "translation_key"), [ - ("da_wm_wm_000001", "sensor.washer_machine_state"), - ("da_wm_wd_000001", "sensor.dryer_machine_state"), + ("da_wm_wm_000001", "sensor.washer_machine_state", "machine_state"), + ("da_wm_wd_000001", "sensor.dryer_machine_state", "machine_state"), + ("hw_q80r_soundbar", "sensor.soundbar_volume", "media_player"), + ("hw_q80r_soundbar", "sensor.soundbar_media_playback_status", "media_player"), + ("hw_q80r_soundbar", "sensor.soundbar_media_input_source", "media_player"), + ( + "im_speaker_ai_0001", + "sensor.galaxy_home_mini_media_playback_shuffle", + "media_player", + ), + ( + "im_speaker_ai_0001", + "sensor.galaxy_home_mini_media_playback_repeat", + "media_player", + ), ], ) async def test_create_issue( @@ -70,9 +83,10 @@ async def test_create_issue( mock_config_entry: MockConfigEntry, issue_registry: ir.IssueRegistry, entity_id: str, + translation_key: str, ) -> None: """Test we create an issue when an automation or script is using a deprecated entity.""" - issue_id = f"deprecated_machine_state_{entity_id}" + issue_id = f"deprecated_{translation_key}_{entity_id}" assert await async_setup_component( hass, @@ -117,7 +131,7 @@ async def test_create_issue( assert len(issue_registry.issues) == 1 issue = issue_registry.async_get_issue(DOMAIN, issue_id) assert issue is not None - assert issue.translation_key == "deprecated_machine_state" + assert issue.translation_key == f"deprecated_{translation_key}" assert issue.translation_placeholders == { "entity": entity_id, "items": "- [test](/config/automation/edit/test)\n- [test](/config/script/edit/test)",