From 0b419578c265594be164cf82453d3f2423176c5a Mon Sep 17 00:00:00 2001 From: Joostlek Date: Tue, 25 Mar 2025 13:06:35 +0100 Subject: [PATCH] Deprecate SmartThings switch entity --- .../components/smartthings/strings.json | 4 + .../components/smartthings/switch.py | 68 ++++++++++++++++- tests/components/smartthings/test_switch.py | 76 ++++++++++++++++++- 3 files changed, 144 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/smartthings/strings.json b/homeassistant/components/smartthings/strings.json index 2612b49a3ed..2693d172c57 100644 --- a/homeassistant/components/smartthings/strings.json +++ b/homeassistant/components/smartthings/strings.json @@ -477,6 +477,10 @@ "deprecated_binary_fridge_door": { "title": "Deprecated refrigerator door binary sensor detected in some automations or scripts", "description": "The refrigerator door binary sensor `{entity}` is deprecated and 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 to fix this issue." + }, + "deprecated_switch_appliance": { + "title": "Deprecated switch detected in some automations or scripts", + "description": "The switch `{entity}` is deprecated and is used in the following automations or scripts:\n{items}\n\nThe actions of the switch did not work, and it has been replaced with a binary sensor instead. Please use them in the above automations or scripts to fix this issue." } } } diff --git a/homeassistant/components/smartthings/switch.py b/homeassistant/components/smartthings/switch.py index 014b11c5329..5b190c7d372 100644 --- a/homeassistant/components/smartthings/switch.py +++ b/homeassistant/components/smartthings/switch.py @@ -5,14 +5,22 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any -from pysmartthings import Attribute, Capability, Command, SmartThings +from pysmartthings import Attribute, Capability, Category, 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.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 MAIN +from .const import DOMAIN, MAIN from .entity import SmartThingsEntity CAPABILITIES = ( @@ -151,6 +159,62 @@ 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() + if self.entity_description != SWITCH or self.device.device.components[ + MAIN + ].manufacturer_category not in {Category.DRYER, Category.WASHER}: + return + automations = automations_with_entity(self.hass, self.entity_id) + scripts = scripts_with_entity(self.hass, self.entity_id) + items = automations + scripts + if not items: + return + + entity_reg: er.EntityRegistry = er.async_get(self.hass) + entity_automations = [ + automation_entity + for automation_id in automations + if (automation_entity := entity_reg.async_get(automation_id)) + ] + entity_scripts = [ + script_entity + for script_id in scripts + if (script_entity := entity_reg.async_get(script_id)) + ] + + items_list = [ + f"- [{item.original_name}](/config/automation/edit/{item.unique_id})" + for item in entity_automations + ] + [ + f"- [{item.original_name}](/config/script/edit/{item.unique_id})" + for item in entity_scripts + ] + + 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="deprecated_switch_appliance", + 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 self.entity_description != SWITCH or self.device.device.components[ + MAIN + ].manufacturer_category not in {Category.DRYER, Category.WASHER}: + 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/test_switch.py b/tests/components/smartthings/test_switch.py index 28bac49b0b0..705b06303be 100644 --- a/tests/components/smartthings/test_switch.py +++ b/tests/components/smartthings/test_switch.py @@ -6,7 +6,10 @@ from pysmartthings import Attribute, Capability, Command import pytest from syrupy import SnapshotAssertion -from homeassistant.components.smartthings.const import MAIN +from homeassistant.components import automation, script +from homeassistant.components.automation import automations_with_entity +from homeassistant.components.script import scripts_with_entity +from homeassistant.components.smartthings.const import DOMAIN, MAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( ATTR_ENTITY_ID, @@ -17,7 +20,8 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import entity_registry as er, issue_registry as ir +from homeassistant.setup import async_setup_component from . import setup_integration, snapshot_smartthings_entities, trigger_update @@ -120,3 +124,71 @@ 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"), + [ + ("da_wm_wm_000001", "switch.washer"), + ("da_wm_wd_000001", "switch.dryer"), + ], +) +async def test_create_issue( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, + issue_registry: ir.IssueRegistry, + entity_id: str, +) -> None: + """Test we create an issue when an automation or script is using a deprecated entity.""" + issue_id = f"deprecated_switch_{entity_id}" + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "alias": "test", + "trigger": {"platform": "state", "entity_id": entity_id}, + "action": { + "action": "automation.turn_on", + "target": { + "entity_id": "automation.test", + }, + }, + } + }, + ) + assert await async_setup_component( + hass, + script.DOMAIN, + { + script.DOMAIN: { + "test": { + "sequence": [ + { + "condition": "state", + "entity_id": entity_id, + "state": "on", + }, + ], + } + } + }, + ) + + await setup_integration(hass, mock_config_entry) + + 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 + assert issue_registry.async_get_issue(DOMAIN, issue_id) + + await hass.config_entries.async_unload(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