Improve SmartThings switch deprecation (#142072)

This commit is contained in:
Joost Lekkerkerker 2025-04-02 15:33:17 +02:00 committed by Franck Nijhof
parent 4e0f581747
commit 02d182239a
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
6 changed files with 272 additions and 626 deletions

View File

@ -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: {

View File

@ -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,
}

View File

@ -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."
}
}
}

View File

@ -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."""

View File

@ -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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.induction_hob',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.induction_hob',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_ks_microwave_0101x][switch.microwave-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': 'switch',
'entity_category': None,
'entity_id': 'switch.microwave',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.microwave',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.dishwasher',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dishwasher',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_sc_000001][switch.airdresser-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': 'switch',
'entity_category': None,
'entity_id': 'switch.airdresser',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.airdresser',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_wd_000001][switch.dryer-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': 'switch',
'entity_category': None,
'entity_id': 'switch.dryer',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dryer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.seca_roupa',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.seca_roupa',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.washer',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.washer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.washing_machine',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.washing_machine',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.soundbar',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.soundbar',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.soundbar_living',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.soundbar_living',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'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': <ANY>,
'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': <ANY>,
'entity_id': 'switch.tv_samsung_8_series_49',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -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