From 02d182239ace9b50d55979764f6b2f6c298fa0e4 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Wed, 2 Apr 2025 15:33:17 +0200 Subject: [PATCH] Improve SmartThings switch deprecation (#142072) --- .../components/smartthings/binary_sensor.py | 11 +- homeassistant/components/smartthings/const.py | 11 +- .../components/smartthings/strings.json | 16 +- .../components/smartthings/switch.py | 143 ++--- .../smartthings/snapshots/test_switch.ambr | 517 ------------------ tests/components/smartthings/test_switch.py | 200 ++++++- 6 files changed, 272 insertions(+), 626 deletions(-) diff --git a/homeassistant/components/smartthings/binary_sensor.py b/homeassistant/components/smartthings/binary_sensor.py index 75a080975ea..0fe0e7fe919 100644 --- a/homeassistant/components/smartthings/binary_sensor.py +++ b/homeassistant/components/smartthings/binary_sensor.py @@ -19,7 +19,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import FullDevice, SmartThingsConfigEntry -from .const import MAIN +from .const import INVALID_SWITCH_CATEGORIES, MAIN from .entity import SmartThingsEntity from .util import deprecate_entity @@ -127,14 +127,7 @@ CAPABILITY_TO_SENSORS: dict[ key=Attribute.SWITCH, device_class=BinarySensorDeviceClass.POWER, is_on_key="on", - category={ - Category.CLOTHING_CARE_MACHINE, - Category.COOKTOP, - Category.DISHWASHER, - Category.DRYER, - Category.MICROWAVE, - Category.WASHER, - }, + category=INVALID_SWITCH_CATEGORIES, ) }, Capability.TAMPER_ALERT: { diff --git a/homeassistant/components/smartthings/const.py b/homeassistant/components/smartthings/const.py index a3ec9a38200..8f27b785688 100644 --- a/homeassistant/components/smartthings/const.py +++ b/homeassistant/components/smartthings/const.py @@ -1,6 +1,6 @@ """Constants used by the SmartThings component and platforms.""" -from pysmartthings import Attribute, Capability +from pysmartthings import Attribute, Capability, Category DOMAIN = "smartthings" @@ -109,3 +109,12 @@ SENSOR_ATTRIBUTES_TO_CAPABILITIES: dict[str, str] = { Attribute.WASHER_MODE: Capability.WASHER_MODE, Attribute.WASHER_JOB_STATE: Capability.WASHER_OPERATING_STATE, } + +INVALID_SWITCH_CATEGORIES = { + Category.CLOTHING_CARE_MACHINE, + Category.COOKTOP, + Category.DRYER, + Category.WASHER, + Category.MICROWAVE, + Category.DISHWASHER, +} diff --git a/homeassistant/components/smartthings/strings.json b/homeassistant/components/smartthings/strings.json index ebb5d3ddbe2..0f685a3f48e 100644 --- a/homeassistant/components/smartthings/strings.json +++ b/homeassistant/components/smartthings/strings.json @@ -496,12 +496,20 @@ "description": "The refrigerator door binary sensor {entity_name} (`{entity_id}`) is deprecated and will be removed in the future. The entity is used in the following automations or scripts:\n{items}\n\nSeparate entities for cooler and freezer door are available and should be used going forward. Please use them in the above automations or scripts and disable the entity to fix this issue." }, "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 the new binary sensor in the above automations or scripts to fix this issue." + "title": "Appliance switch deprecated", + "description": "The switch {entity_name} (`{entity_id}`) is deprecated because the actions did not work, so it has been replaced with a binary sensor instead.\n\nPlease update your dashboards, templates accordingly and disable the entity to fix this issue." + }, + "deprecated_switch_appliance_scripts": { + "title": "[%key:component::smartthings::issues::deprecated_switch_appliance::title%]", + "description": "The switch {entity_name} (`{entity_id}`) 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 the new binary sensor in the above automations or scripts and disable the entity to fix this issue." }, "deprecated_switch_media_player": { "title": "[%key:component::smartthings::issues::deprecated_switch_appliance::title%]", - "description": "The switch `{entity}` is deprecated and a media player entity has been added to replace it.\n\nThe switch was used in the following automations or scripts:\n{items}\n\nPlease use the new media player entity in the above automations or scripts to fix this issue." + "description": "The switch {entity_name} (`{entity_id}`) is deprecated and a media player entity has been added to replace it.\n\nPlease use the new media player entity in the above automations or scripts and disable the entity to fix this issue." + }, + "deprecated_switch_media_player_scripts": { + "title": "[%key:component::smartthings::issues::deprecated_switch_appliance::title%]", + "description": "The switch {entity_name} (`{entity_id}`) is deprecated and a media player entity has been added to replace it.\n\nThe switch was used in the following automations or scripts:\n{items}\n\nPlease use the new media player entity in the above automations or scripts and disable the entity to fix this issue." }, "deprecated_media_player": { "title": "Media player sensors deprecated", @@ -509,7 +517,7 @@ }, "deprecated_media_player_scripts": { "title": "Deprecated sensor detected in some automations or scripts", - "description": "The sensor {entity_name} (`{entity_id}`) 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 them in the above automations or scripts to use the new media player entity and disable the entity to fix this issue." + "description": "The sensor {entity_name} (`{entity_id}`) 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 update the above automations or scripts to use the new media player entity and disable the entity to fix this issue." } } } diff --git a/homeassistant/components/smartthings/switch.py b/homeassistant/components/smartthings/switch.py index e5b74de3241..4e62957d3d4 100644 --- a/homeassistant/components/smartthings/switch.py +++ b/homeassistant/components/smartthings/switch.py @@ -5,23 +5,21 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any -from pysmartthings import Attribute, Capability, Category, Command, SmartThings +from pysmartthings import Attribute, Capability, Command, SmartThings -from homeassistant.components.automation import automations_with_entity -from homeassistant.components.script import scripts_with_entity -from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.components.switch import ( + DOMAIN as SWITCH_DOMAIN, + SwitchEntity, + SwitchEntityDescription, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from homeassistant.helpers.issue_registry import ( - IssueSeverity, - async_create_issue, - async_delete_issue, -) from . import FullDevice, SmartThingsConfigEntry -from .const import DOMAIN, MAIN +from .const import INVALID_SWITCH_CATEGORIES, MAIN from .entity import SmartThingsEntity +from .util import deprecate_entity CAPABILITIES = ( Capability.SWITCH_LEVEL, @@ -37,6 +35,12 @@ AC_CAPABILITIES = ( Capability.THERMOSTAT_COOLING_SETPOINT, ) +MEDIA_PLAYER_CAPABILITIES = ( + Capability.AUDIO_MUTE, + Capability.AUDIO_VOLUME, + Capability.MEDIA_PLAYBACK, +) + @dataclass(frozen=True, kw_only=True) class SmartThingsSwitchEntityDescription(SwitchEntityDescription): @@ -92,13 +96,6 @@ async def async_setup_entry( """Add switches for a config entry.""" entry_data = entry.runtime_data entities: list[SmartThingsEntity] = [ - SmartThingsSwitch(entry_data.client, device, SWITCH, Capability.SWITCH) - for device in entry_data.devices.values() - if Capability.SWITCH in device.status[MAIN] - and not any(capability in device.status[MAIN] for capability in CAPABILITIES) - and not all(capability in device.status[MAIN] for capability in AC_CAPABILITIES) - ] - entities.extend( SmartThingsCommandSwitch( entry_data.client, device, @@ -108,7 +105,7 @@ async def async_setup_entry( for device in entry_data.devices.values() for capability, description in CAPABILITY_TO_COMMAND_SWITCHES.items() if capability in device.status[MAIN] - ) + ] entities.extend( SmartThingsSwitch( entry_data.client, @@ -129,6 +126,51 @@ async def async_setup_entry( ) ) ) + entity_registry = er.async_get(hass) + for device in entry_data.devices.values(): + if ( + Capability.SWITCH in device.status[MAIN] + and not any( + capability in device.status[MAIN] for capability in CAPABILITIES + ) + and not all( + capability in device.status[MAIN] for capability in AC_CAPABILITIES + ) + ): + media_player = all( + capability in device.status[MAIN] + for capability in MEDIA_PLAYER_CAPABILITIES + ) + appliance = ( + device.device.components[MAIN].manufacturer_category + in INVALID_SWITCH_CATEGORIES + ) + if media_player or appliance: + issue = "media_player" if media_player else "appliance" + if deprecate_entity( + hass, + entity_registry, + SWITCH_DOMAIN, + f"{device.device.device_id}_{MAIN}_{Capability.SWITCH}_{Attribute.SWITCH}_{Attribute.SWITCH}", + f"deprecated_switch_{issue}", + ): + entities.append( + SmartThingsSwitch( + entry_data.client, + device, + SWITCH, + Capability.SWITCH, + ) + ) + continue + entities.append( + SmartThingsSwitch( + entry_data.client, + device, + SWITCH, + Capability.SWITCH, + ) + ) async_add_entities(entities) @@ -136,7 +178,6 @@ class SmartThingsSwitch(SmartThingsEntity, SwitchEntity): """Define a SmartThings switch.""" entity_description: SmartThingsSwitchEntityDescription - created_issue: bool = False def __init__( self, @@ -182,70 +223,6 @@ class SmartThingsSwitch(SmartThingsEntity, SwitchEntity): == "on" ) - async def async_added_to_hass(self) -> None: - """Call when entity is added to hass.""" - await super().async_added_to_hass() - media_player = all( - capability in self.device.status[MAIN] - for capability in ( - Capability.AUDIO_MUTE, - Capability.AUDIO_VOLUME, - Capability.MEDIA_PLAYBACK, - ) - ) - if ( - self.entity_description != SWITCH - and self.device.device.components[MAIN].manufacturer_category - not in { - Category.CLOTHING_CARE_MACHINE, - Category.COOKTOP, - Category.DRYER, - Category.WASHER, - Category.MICROWAVE, - Category.DISHWASHER, - } - ) or (self.entity_description != SWITCH and not media_player): - return - automations = automations_with_entity(self.hass, self.entity_id) - scripts = scripts_with_entity(self.hass, self.entity_id) - if not automations and not scripts: - return - - entity_reg: er.EntityRegistry = er.async_get(self.hass) - items_list = [ - f"- [{item.original_name}](/config/{integration}/edit/{item.unique_id})" - for integration, entities in ( - ("automation", automations), - ("script", scripts), - ) - for entity_id in entities - if (item := entity_reg.async_get(entity_id)) - ] - - identifier = "media_player" if media_player else "appliance" - - self.created_issue = True - async_create_issue( - self.hass, - DOMAIN, - f"deprecated_switch_{self.entity_id}", - breaks_in_ha_version="2025.10.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key=f"deprecated_switch_{identifier}", - translation_placeholders={ - "entity": self.entity_id, - "items": "\n".join(items_list), - }, - ) - - async def async_will_remove_from_hass(self) -> None: - """Call when entity will be removed from hass.""" - await super().async_will_remove_from_hass() - if not self.created_issue: - return - async_delete_issue(self.hass, DOMAIN, f"deprecated_switch_{self.entity_id}") - class SmartThingsCommandSwitch(SmartThingsSwitch): """Define a SmartThings command switch.""" diff --git a/tests/components/smartthings/snapshots/test_switch.ambr b/tests/components/smartthings/snapshots/test_switch.ambr index 8c95d2f20fc..d14d4d02aa4 100644 --- a/tests/components/smartthings/snapshots/test_switch.ambr +++ b/tests/components/smartthings/snapshots/test_switch.ambr @@ -46,100 +46,6 @@ 'state': 'on', }) # --- -# name: test_all_entities[da_ks_cooktop_31001][switch.induction_hob-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.induction_hob', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '808dbd84-f357-47e2-a0cd-3b66fa22d584_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_ks_cooktop_31001][switch.induction_hob-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Induction Hob', - }), - 'context': , - 'entity_id': 'switch.induction_hob', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- -# name: test_all_entities[da_ks_microwave_0101x][switch.microwave-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.microwave', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '2bad3237-4886-e699-1b90-4a51a3d55c8a_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_ks_microwave_0101x][switch.microwave-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Microwave', - }), - 'context': , - 'entity_id': 'switch.microwave', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- # name: test_all_entities[da_ref_normal_000001][switch.refrigerator_ice_maker-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -281,147 +187,6 @@ 'state': 'off', }) # --- -# name: test_all_entities[da_wm_dw_000001][switch.dishwasher-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.dishwasher', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_wm_dw_000001][switch.dishwasher-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Dishwasher', - }), - 'context': , - 'entity_id': 'switch.dishwasher', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- -# name: test_all_entities[da_wm_sc_000001][switch.airdresser-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.airdresser', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'b93211bf-9d96-bd21-3b2f-964fcc87f5cc_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_wm_sc_000001][switch.airdresser-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'AirDresser', - }), - 'context': , - 'entity_id': 'switch.airdresser', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- -# name: test_all_entities[da_wm_wd_000001][switch.dryer-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.dryer', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '02f7256e-8353-5bdd-547f-bd5b1647e01b_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_wm_wd_000001][switch.dryer-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Dryer', - }), - 'context': , - 'entity_id': 'switch.dryer', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- # name: test_all_entities[da_wm_wd_000001][switch.dryer_wrinkle_prevent-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -469,53 +234,6 @@ 'state': 'off', }) # --- -# name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.seca_roupa', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '3a6c4e05-811d-5041-e956-3d04c424cbcd_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Seca-Roupa', - }), - 'context': , - 'entity_id': 'switch.seca_roupa', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- # name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa_wrinkle_prevent-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -563,100 +281,6 @@ 'state': 'off', }) # --- -# name: test_all_entities[da_wm_wm_000001][switch.washer-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.washer', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'f984b91d-f250-9d42-3436-33f09a422a47_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_wm_wm_000001][switch.washer-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Washer', - }), - 'context': , - 'entity_id': 'switch.washer', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- -# name: test_all_entities[da_wm_wm_000001_1][switch.washing_machine-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.washing_machine', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[da_wm_wm_000001_1][switch.washing_machine-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Washing Machine', - }), - 'context': , - 'entity_id': 'switch.washing_machine', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'on', - }) -# --- # name: test_all_entities[da_wm_wm_000001_1][switch.washing_machine_bubble_soak-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -751,53 +375,6 @@ 'state': 'off', }) # --- -# name: test_all_entities[hw_q80r_soundbar][switch.soundbar-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.soundbar', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[hw_q80r_soundbar][switch.soundbar-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Soundbar', - }), - 'context': , - 'entity_id': 'switch.soundbar', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'on', - }) -# --- # name: test_all_entities[sensibo_airconditioner_1][switch.office-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -939,53 +516,6 @@ 'state': 'on', }) # --- -# name: test_all_entities[vd_network_audio_002s][switch.soundbar_living-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.soundbar_living', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '0d94e5db-8501-2355-eb4f-214163702cac_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[vd_network_audio_002s][switch.soundbar_living-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Soundbar Living', - }), - 'context': , - 'entity_id': 'switch.soundbar_living', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'on', - }) -# --- # name: test_all_entities[vd_sensor_light_2023][switch.light_sensor_55_the_frame-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -1033,50 +563,3 @@ 'state': 'off', }) # --- -# name: test_all_entities[vd_stv_2017_k][switch.tv_samsung_8_series_49-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.tv_samsung_8_series_49', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': None, - 'platform': 'smartthings', - 'previous_unique_id': None, - 'supported_features': 0, - 'translation_key': None, - 'unique_id': '4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_main_switch_switch_switch', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[vd_stv_2017_k][switch.tv_samsung_8_series_49-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': '[TV] Samsung 8 Series (49)', - }), - 'context': , - 'entity_id': 'switch.tv_samsung_8_series_49', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'on', - }) -# --- diff --git a/tests/components/smartthings/test_switch.py b/tests/components/smartthings/test_switch.py index 2e360ff68e3..a47ecde7e0d 100644 --- a/tests/components/smartthings/test_switch.py +++ b/tests/components/smartthings/test_switch.py @@ -126,25 +126,86 @@ async def test_state_update( assert hass.states.get("switch.2nd_floor_hallway").state == STATE_OFF -@pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.parametrize( - ("device_fixture", "entity_id", "translation_key"), + ("device_fixture", "device_id", "suggested_object_id", "issue_string"), [ - ("da_wm_wm_000001", "switch.washer", "deprecated_switch_appliance"), - ("da_wm_wd_000001", "switch.dryer", "deprecated_switch_appliance"), - ("hw_q80r_soundbar", "switch.soundbar", "deprecated_switch_media_player"), + ( + "da_ks_cooktop_31001", + "808dbd84-f357-47e2-a0cd-3b66fa22d584", + "induction_hob", + "appliance", + ), + ( + "da_ks_microwave_0101x", + "2bad3237-4886-e699-1b90-4a51a3d55c8a", + "microwave", + "appliance", + ), + ( + "da_wm_dw_000001", + "f36dc7ce-cac0-0667-dc14-a3704eb5e676", + "dishwasher", + "appliance", + ), + ( + "da_wm_sc_000001", + "b93211bf-9d96-bd21-3b2f-964fcc87f5cc", + "airdresser", + "appliance", + ), + ( + "da_wm_wd_000001", + "02f7256e-8353-5bdd-547f-bd5b1647e01b", + "dryer", + "appliance", + ), + ( + "da_wm_wm_000001", + "f984b91d-f250-9d42-3436-33f09a422a47", + "washer", + "appliance", + ), + ( + "hw_q80r_soundbar", + "afcf3b91-0000-1111-2222-ddff2a0a6577", + "soundbar", + "media_player", + ), + ( + "vd_network_audio_002s", + "0d94e5db-8501-2355-eb4f-214163702cac", + "soundbar_living", + "media_player", + ), + ( + "vd_stv_2017_k", + "4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1", + "tv_samsung_8_series_49", + "media_player", + ), ], ) -async def test_create_issue( +async def test_create_issue_with_items( hass: HomeAssistant, devices: AsyncMock, mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, issue_registry: ir.IssueRegistry, - entity_id: str, - translation_key: str, + device_id: str, + suggested_object_id: str, + issue_string: str, ) -> None: """Test we create an issue when an automation or script is using a deprecated entity.""" - issue_id = f"deprecated_switch_{entity_id}" + entity_id = f"switch.{suggested_object_id}" + issue_id = f"deprecated_switch_{issue_string}_{entity_id}" + + entity_entry = entity_registry.async_get_or_create( + SWITCH_DOMAIN, + DOMAIN, + f"{device_id}_{MAIN}_{Capability.SWITCH}_{Attribute.SWITCH}_{Attribute.SWITCH}", + suggested_object_id=suggested_object_id, + original_name=suggested_object_id, + ) assert await async_setup_component( hass, @@ -183,19 +244,134 @@ async def test_create_issue( await setup_integration(hass, mock_config_entry) + assert hass.states.get(entity_id).state in [STATE_OFF, STATE_ON] + assert automations_with_entity(hass, entity_id)[0] == "automation.test" assert scripts_with_entity(hass, entity_id)[0] == "script.test" assert len(issue_registry.issues) == 1 issue = issue_registry.async_get_issue(DOMAIN, issue_id) assert issue is not None - assert issue.translation_key == translation_key + assert issue.translation_key == f"deprecated_switch_{issue_string}_scripts" assert issue.translation_placeholders == { - "entity": entity_id, + "entity_id": entity_id, + "entity_name": suggested_object_id, "items": "- [test](/config/automation/edit/test)\n- [test](/config/script/edit/test)", } - await hass.config_entries.async_unload(mock_config_entry.entry_id) + entity_registry.async_update_entity( + entity_entry.entity_id, + disabled_by=er.RegistryEntryDisabler.USER, + ) + + await hass.config_entries.async_reload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Assert the issue is no longer present + assert not issue_registry.async_get_issue(DOMAIN, issue_id) + assert len(issue_registry.issues) == 0 + + +@pytest.mark.parametrize( + ("device_fixture", "device_id", "suggested_object_id", "issue_string"), + [ + ( + "da_ks_cooktop_31001", + "808dbd84-f357-47e2-a0cd-3b66fa22d584", + "induction_hob", + "appliance", + ), + ( + "da_ks_microwave_0101x", + "2bad3237-4886-e699-1b90-4a51a3d55c8a", + "microwave", + "appliance", + ), + ( + "da_wm_dw_000001", + "f36dc7ce-cac0-0667-dc14-a3704eb5e676", + "dishwasher", + "appliance", + ), + ( + "da_wm_sc_000001", + "b93211bf-9d96-bd21-3b2f-964fcc87f5cc", + "airdresser", + "appliance", + ), + ( + "da_wm_wd_000001", + "02f7256e-8353-5bdd-547f-bd5b1647e01b", + "dryer", + "appliance", + ), + ( + "da_wm_wm_000001", + "f984b91d-f250-9d42-3436-33f09a422a47", + "washer", + "appliance", + ), + ( + "hw_q80r_soundbar", + "afcf3b91-0000-1111-2222-ddff2a0a6577", + "soundbar", + "media_player", + ), + ( + "vd_network_audio_002s", + "0d94e5db-8501-2355-eb4f-214163702cac", + "soundbar_living", + "media_player", + ), + ( + "vd_stv_2017_k", + "4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1", + "tv_samsung_8_series_49", + "media_player", + ), + ], +) +async def test_create_issue( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, + issue_registry: ir.IssueRegistry, + device_id: str, + suggested_object_id: str, + issue_string: str, +) -> None: + """Test we create an issue when an automation or script is using a deprecated entity.""" + entity_id = f"switch.{suggested_object_id}" + issue_id = f"deprecated_switch_{issue_string}_{entity_id}" + + entity_entry = entity_registry.async_get_or_create( + SWITCH_DOMAIN, + DOMAIN, + f"{device_id}_{MAIN}_{Capability.SWITCH}_{Attribute.SWITCH}_{Attribute.SWITCH}", + suggested_object_id=suggested_object_id, + original_name=suggested_object_id, + ) + + await setup_integration(hass, mock_config_entry) + + assert hass.states.get(entity_id).state in [STATE_OFF, STATE_ON] + + assert len(issue_registry.issues) == 1 + issue = issue_registry.async_get_issue(DOMAIN, issue_id) + assert issue is not None + assert issue.translation_key == f"deprecated_switch_{issue_string}" + assert issue.translation_placeholders == { + "entity_id": entity_id, + "entity_name": suggested_object_id, + } + + entity_registry.async_update_entity( + entity_entry.entity_id, + disabled_by=er.RegistryEntryDisabler.USER, + ) + + await hass.config_entries.async_reload(mock_config_entry.entry_id) await hass.async_block_till_done() # Assert the issue is no longer present