Improve SmartThings sensor deprecation (#142070)

* Improve SmartThings sensor deprecation

* Improve SmartThings sensor deprecation

* Improve SmartThings sensor deprecation
This commit is contained in:
Joost Lekkerkerker 2025-04-02 13:56:23 +02:00 committed by GitHub
parent ca48b07858
commit 93ea88f3de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 240 additions and 933 deletions

View File

@ -9,9 +9,8 @@ from typing import Any, cast
from pysmartthings import Attribute, Capability, ComponentStatus, 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
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN,
SensorDeviceClass, SensorDeviceClass,
SensorEntity, SensorEntity,
SensorEntityDescription, SensorEntityDescription,
@ -33,16 +32,12 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import FullDevice, SmartThingsConfigEntry from . import FullDevice, SmartThingsConfigEntry
from .const import DOMAIN, MAIN from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
from .util import deprecate_entity
THERMOSTAT_CAPABILITIES = { THERMOSTAT_CAPABILITIES = {
Capability.TEMPERATURE_MEASUREMENT, Capability.TEMPERATURE_MEASUREMENT,
@ -1021,31 +1016,67 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Add sensors for a config entry.""" """Add sensors for a config entry."""
entry_data = entry.runtime_data entry_data = entry.runtime_data
async_add_entities( entities = []
SmartThingsSensor(
entry_data.client, entity_registry = er.async_get(hass)
device,
description, for device in entry_data.devices.values(): # pylint: disable=too-many-nested-blocks
capability, for capability, attributes in CAPABILITY_TO_SENSORS.items():
attribute, if capability in device.status[MAIN]:
) for attribute, descriptions in attributes.items():
for device in entry_data.devices.values() for description in descriptions:
for capability, attributes in CAPABILITY_TO_SENSORS.items() if (
if capability in device.status[MAIN] not description.capability_ignore_list
for attribute, descriptions in attributes.items() or not any(
for description in descriptions all(
if ( capability in device.status[MAIN]
not description.capability_ignore_list for capability in capability_list
or not any( )
all(capability in device.status[MAIN] for capability in capability_list) for capability_list in description.capability_ignore_list
for capability_list in description.capability_ignore_list )
) ) and (
) not description.exists_fn
and ( or description.exists_fn(
not description.exists_fn device.status[MAIN][capability][attribute]
or description.exists_fn(device.status[MAIN][capability][attribute]) )
) ):
) if (
description.deprecated
and (
reason := description.deprecated(
device.status[MAIN]
)
)
is not None
):
if deprecate_entity(
hass,
entity_registry,
SENSOR_DOMAIN,
f"{device.device.device_id}_{MAIN}_{capability}_{attribute}_{description.key}",
f"deprecated_{reason}",
):
entities.append(
SmartThingsSensor(
entry_data.client,
device,
description,
capability,
attribute,
)
)
continue
entities.append(
SmartThingsSensor(
entry_data.client,
device,
description,
capability,
attribute,
)
)
async_add_entities(entities)
class SmartThingsSensor(SmartThingsEntity, SensorEntity): class SmartThingsSensor(SmartThingsEntity, SensorEntity):
@ -1113,53 +1144,3 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity):
return [] return []
return [option.lower() for option in options] return [option.lower() for option in options]
return super().options return super().options
async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass."""
await super().async_added_to_hass()
if (
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)
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))
]
async_create_issue(
self.hass,
DOMAIN,
f"deprecated_{reason}_{self.entity_id}",
breaks_in_ha_version="2025.10.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=f"deprecated_{reason}",
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.entity_description.deprecated
or (reason := self.entity_description.deprecated(self.device.status[MAIN]))
is None
):
return
async_delete_issue(self.hass, DOMAIN, f"deprecated_{reason}_{self.entity_id}")

View File

@ -504,8 +504,12 @@
"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}` 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."
}, },
"deprecated_media_player": { "deprecated_media_player": {
"title": "Media player sensors deprecated",
"description": "The sensor {entity_name} (`{entity_id}`) is deprecated because it has been replaced with a media player entity.\n\nPlease update your dashboards, templates to use the new media player entity and disable the entity to fix this issue."
},
"deprecated_media_player_scripts": {
"title": "Deprecated sensor detected in some automations or scripts", "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 new media player entity in the above automations or scripts 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 use them in the above automations or scripts to use the new media player entity and disable the entity to fix this issue."
} }
} }
} }

View File

@ -8366,182 +8366,6 @@
'state': '19.0', 'state': '19.0',
}) })
# --- # ---
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_input_source-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'wifi',
'bluetooth',
'hdmi1',
'hdmi2',
'digital',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.soundbar_media_input_source',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media input source',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_input_source',
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577_main_mediaInputSource_inputSource_inputSource',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_input_source-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Soundbar Media input source',
'options': list([
'wifi',
'bluetooth',
'hdmi1',
'hdmi2',
'digital',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.soundbar_media_input_source',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'wifi',
})
# ---
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_playback_status-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.soundbar_media_playback_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media playback status',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_status',
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577_main_mediaPlayback_playbackStatus_playbackStatus',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_media_playback_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Soundbar Media playback status',
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.soundbar_media_playback_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'playing',
})
# ---
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_volume-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.soundbar_volume',
'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': 'Volume',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'audio_volume',
'unique_id': 'afcf3b91-0000-1111-2222-ddff2a0a6577_main_audioVolume_volume_volume',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[hw_q80r_soundbar][sensor.soundbar_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Soundbar Volume',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.soundbar_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1',
})
# ---
# name: test_all_entities[ikea_kadrilj][sensor.kitchen_ikea_kadrilj_window_blind_battery-entry] # name: test_all_entities[ikea_kadrilj][sensor.kitchen_ikea_kadrilj_window_blind_battery-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -8591,261 +8415,6 @@
'state': '37', 'state': '37',
}) })
# --- # ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_input_source-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.galaxy_home_mini_media_input_source',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media input source',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_input_source',
'unique_id': 'c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_main_mediaInputSource_inputSource_inputSource',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_input_source-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Galaxy Home Mini Media input source',
}),
'context': <ANY>,
'entity_id': 'sensor.galaxy_home_mini_media_input_source',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_playback_repeat-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.galaxy_home_mini_media_playback_repeat',
'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': 'Media playback repeat',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_repeat',
'unique_id': 'c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_main_mediaPlaybackRepeat_playbackRepeatMode_playbackRepeatMode',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_playback_repeat-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Galaxy Home Mini Media playback repeat',
}),
'context': <ANY>,
'entity_id': 'sensor.galaxy_home_mini_media_playback_repeat',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_playback_shuffle-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.galaxy_home_mini_media_playback_shuffle',
'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': 'Media playback shuffle',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_shuffle',
'unique_id': 'c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_main_mediaPlaybackShuffle_playbackShuffle_playbackShuffle',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_playback_shuffle-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Galaxy Home Mini Media playback shuffle',
}),
'context': <ANY>,
'entity_id': 'sensor.galaxy_home_mini_media_playback_shuffle',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'disabled',
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_playback_status-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.galaxy_home_mini_media_playback_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media playback status',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_status',
'unique_id': 'c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_main_mediaPlayback_playbackStatus_playbackStatus',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_media_playback_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Galaxy Home Mini Media playback status',
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.galaxy_home_mini_media_playback_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'stopped',
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_volume-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.galaxy_home_mini_volume',
'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': 'Volume',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'audio_volume',
'unique_id': 'c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_main_audioVolume_volume_volume',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[im_speaker_ai_0001][sensor.galaxy_home_mini_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Galaxy Home Mini Volume',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.galaxy_home_mini_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '52',
})
# ---
# name: test_all_entities[multipurpose_sensor][sensor.deck_door_battery-entry] # name: test_all_entities[multipurpose_sensor][sensor.deck_door_battery-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -9184,119 +8753,6 @@
'state': '20', 'state': '20',
}) })
# --- # ---
# name: test_all_entities[sonos_player][sensor.elliots_rum_media_playback_status-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.elliots_rum_media_playback_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media playback status',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_status',
'unique_id': 'c85fced9-c474-4a47-93c2-037cc7829536_main_mediaPlayback_playbackStatus_playbackStatus',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sonos_player][sensor.elliots_rum_media_playback_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Elliots Rum Media playback status',
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.elliots_rum_media_playback_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'playing',
})
# ---
# name: test_all_entities[sonos_player][sensor.elliots_rum_volume-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.elliots_rum_volume',
'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': 'Volume',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'audio_volume',
'unique_id': 'c85fced9-c474-4a47-93c2-037cc7829536_main_audioVolume_volume_volume',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sonos_player][sensor.elliots_rum_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Elliots Rum Volume',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.elliots_rum_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '15',
})
# ---
# name: test_all_entities[tplink_p110][sensor.spulmaschine_energy-entry] # name: test_all_entities[tplink_p110][sensor.spulmaschine_energy-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -9407,119 +8863,6 @@
'state': '0.0', 'state': '0.0',
}) })
# --- # ---
# name: test_all_entities[vd_network_audio_002s][sensor.soundbar_living_media_playback_status-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.soundbar_living_media_playback_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media playback status',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_status',
'unique_id': '0d94e5db-8501-2355-eb4f-214163702cac_main_mediaPlayback_playbackStatus_playbackStatus',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[vd_network_audio_002s][sensor.soundbar_living_media_playback_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Soundbar Living Media playback status',
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.soundbar_living_media_playback_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'stopped',
})
# ---
# name: test_all_entities[vd_network_audio_002s][sensor.soundbar_living_volume-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.soundbar_living_volume',
'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': 'Volume',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'audio_volume',
'unique_id': '0d94e5db-8501-2355-eb4f-214163702cac_main_audioVolume_volume_volume',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[vd_network_audio_002s][sensor.soundbar_living_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Soundbar Living Volume',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.soundbar_living_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '17',
})
# ---
# name: test_all_entities[vd_sensor_light_2023][sensor.light_sensor_55_the_frame_brightness_intensity-entry] # name: test_all_entities[vd_sensor_light_2023][sensor.light_sensor_55_the_frame_brightness_intensity-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -9571,132 +8914,6 @@
'state': '2', 'state': '2',
}) })
# --- # ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_media_input_source-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'digitaltv',
'hdmi1',
'hdmi4',
'hdmi4',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.tv_samsung_8_series_49_media_input_source',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media input source',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_input_source',
'unique_id': '4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_main_mediaInputSource_inputSource_inputSource',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_media_input_source-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': '[TV] Samsung 8 Series (49) Media input source',
'options': list([
'digitaltv',
'hdmi1',
'hdmi4',
'hdmi4',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.tv_samsung_8_series_49_media_input_source',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'hdmi1',
})
# ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_media_playback_status-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.tv_samsung_8_series_49_media_playback_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Media playback status',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'media_playback_status',
'unique_id': '4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_main_mediaPlayback_playbackStatus_playbackStatus',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_media_playback_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': '[TV] Samsung 8 Series (49) Media playback status',
'options': list([
'paused',
'playing',
'stopped',
'fast_forwarding',
'rewinding',
'buffering',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.tv_samsung_8_series_49_media_playback_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_tv_channel-entry] # name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_tv_channel-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -9791,54 +9008,6 @@
'state': '', 'state': '',
}) })
# --- # ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_volume-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': 'sensor',
'entity_category': None,
'entity_id': 'sensor.tv_samsung_8_series_49_volume',
'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': 'Volume',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'audio_volume',
'unique_id': '4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_main_audioVolume_volume_volume',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[vd_stv_2017_k][sensor.tv_samsung_8_series_49_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': '[TV] Samsung 8 Series (49) Volume',
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.tv_samsung_8_series_49_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '13',
})
# ---
# name: test_all_entities[virtual_thermostat][sensor.asd_battery-entry] # name: test_all_entities[virtual_thermostat][sensor.asd_battery-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@ -60,7 +60,6 @@ async def test_state_update(
assert hass.states.get("binary_sensor.refrigerator_cooler_door").state == STATE_ON assert hass.states.get("binary_sensor.refrigerator_cooler_door").state == STATE_ON
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("device_fixture", "unique_id", "suggested_object_id", "issue_string", "entity_id"), ("device_fixture", "unique_id", "suggested_object_id", "issue_string", "entity_id"),
[ [
@ -167,7 +166,6 @@ async def test_create_issue_with_items(
assert len(issue_registry.issues) == 0 assert len(issue_registry.issues) == 0
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("device_fixture", "unique_id", "suggested_object_id", "issue_string", "entity_id"), ("device_fixture", "unique_id", "suggested_object_id", "issue_string", "entity_id"),
[ [

View File

@ -9,8 +9,9 @@ from syrupy import SnapshotAssertion
from homeassistant.components import automation, script from homeassistant.components import automation, script
from homeassistant.components.automation import automations_with_entity from homeassistant.components.automation import automations_with_entity
from homeassistant.components.script import scripts_with_entity from homeassistant.components.script import scripts_with_entity
from homeassistant.components.smartthings.const import DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import Platform from homeassistant.components.smartthings.const import DOMAIN, MAIN
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er, issue_registry as ir from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -56,35 +57,80 @@ async def test_state_update(
assert hass.states.get("sensor.ac_office_granit_temperature").state == "20" assert hass.states.get("sensor.ac_office_granit_temperature").state == "20"
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("device_fixture", "entity_id", "translation_key"), (
"device_fixture",
"unique_id",
"suggested_object_id",
"issue_string",
"entity_id",
"expected_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", "vd_stv_2017_k",
"sensor.galaxy_home_mini_media_playback_shuffle", f"4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_{MAIN}_{Capability.MEDIA_PLAYBACK}_{Attribute.PLAYBACK_STATUS}_{Attribute.PLAYBACK_STATUS}",
"tv_samsung_8_series_49_media_playback_status",
"media_player", "media_player",
"sensor.tv_samsung_8_series_49_media_playback_status",
STATE_UNKNOWN,
),
(
"vd_stv_2017_k",
f"4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_{MAIN}_{Capability.AUDIO_VOLUME}_{Attribute.VOLUME}_{Attribute.VOLUME}",
"tv_samsung_8_series_49_volume",
"media_player",
"sensor.tv_samsung_8_series_49_volume",
"13",
),
(
"vd_stv_2017_k",
f"4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_{MAIN}_{Capability.MEDIA_INPUT_SOURCE}_{Attribute.INPUT_SOURCE}_{Attribute.INPUT_SOURCE}",
"tv_samsung_8_series_49_media_input_source",
"media_player",
"sensor.tv_samsung_8_series_49_media_input_source",
"hdmi1",
), ),
( (
"im_speaker_ai_0001", "im_speaker_ai_0001",
"sensor.galaxy_home_mini_media_playback_repeat", f"c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_{MAIN}_{Capability.MEDIA_PLAYBACK_REPEAT}_{Attribute.PLAYBACK_REPEAT_MODE}_{Attribute.PLAYBACK_REPEAT_MODE}",
"galaxy_home_mini_media_playback_repeat",
"media_player", "media_player",
"sensor.galaxy_home_mini_media_playback_repeat",
"off",
),
(
"im_speaker_ai_0001",
f"c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_{MAIN}_{Capability.MEDIA_PLAYBACK_SHUFFLE}_{Attribute.PLAYBACK_SHUFFLE}_{Attribute.PLAYBACK_SHUFFLE}",
"galaxy_home_mini_media_playback_shuffle",
"media_player",
"sensor.galaxy_home_mini_media_playback_shuffle",
"disabled",
), ),
], ],
) )
async def test_create_issue( async def test_create_issue_with_items(
hass: HomeAssistant, hass: HomeAssistant,
devices: AsyncMock, devices: AsyncMock,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
unique_id: str,
suggested_object_id: str,
issue_string: str,
entity_id: str, entity_id: str,
translation_key: str, expected_state: str,
) -> None: ) -> None:
"""Test we create an issue when an automation or script is using a deprecated entity.""" """Test we create an issue when an automation or script is using a deprecated entity."""
issue_id = f"deprecated_{translation_key}_{entity_id}" issue_id = f"deprecated_{issue_string}_{entity_id}"
entity_entry = entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
unique_id,
suggested_object_id=suggested_object_id,
original_name=suggested_object_id,
)
assert await async_setup_component( assert await async_setup_component(
hass, hass,
@ -123,19 +169,128 @@ async def test_create_issue(
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
assert hass.states.get(entity_id).state == expected_state
assert automations_with_entity(hass, entity_id)[0] == "automation.test" assert automations_with_entity(hass, entity_id)[0] == "automation.test"
assert scripts_with_entity(hass, entity_id)[0] == "script.test" assert scripts_with_entity(hass, entity_id)[0] == "script.test"
assert len(issue_registry.issues) == 1 assert len(issue_registry.issues) == 1
issue = issue_registry.async_get_issue(DOMAIN, issue_id) issue = issue_registry.async_get_issue(DOMAIN, issue_id)
assert issue is not None assert issue is not None
assert issue.translation_key == f"deprecated_{translation_key}" assert issue.translation_key == f"deprecated_{issue_string}_scripts"
assert issue.translation_placeholders == { 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)", "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",
"unique_id",
"suggested_object_id",
"issue_string",
"entity_id",
"expected_state",
),
[
(
"vd_stv_2017_k",
f"4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_{MAIN}_{Capability.MEDIA_PLAYBACK}_{Attribute.PLAYBACK_STATUS}_{Attribute.PLAYBACK_STATUS}",
"tv_samsung_8_series_49_media_playback_status",
"media_player",
"sensor.tv_samsung_8_series_49_media_playback_status",
STATE_UNKNOWN,
),
(
"vd_stv_2017_k",
f"4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_{MAIN}_{Capability.AUDIO_VOLUME}_{Attribute.VOLUME}_{Attribute.VOLUME}",
"tv_samsung_8_series_49_volume",
"media_player",
"sensor.tv_samsung_8_series_49_volume",
"13",
),
(
"vd_stv_2017_k",
f"4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1_{MAIN}_{Capability.MEDIA_INPUT_SOURCE}_{Attribute.INPUT_SOURCE}_{Attribute.INPUT_SOURCE}",
"tv_samsung_8_series_49_media_input_source",
"media_player",
"sensor.tv_samsung_8_series_49_media_input_source",
"hdmi1",
),
(
"im_speaker_ai_0001",
f"c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_{MAIN}_{Capability.MEDIA_PLAYBACK_REPEAT}_{Attribute.PLAYBACK_REPEAT_MODE}_{Attribute.PLAYBACK_REPEAT_MODE}",
"galaxy_home_mini_media_playback_repeat",
"media_player",
"sensor.galaxy_home_mini_media_playback_repeat",
"off",
),
(
"im_speaker_ai_0001",
f"c9276e43-fe3c-88c3-1dcc-2eb79e292b8c_{MAIN}_{Capability.MEDIA_PLAYBACK_SHUFFLE}_{Attribute.PLAYBACK_SHUFFLE}_{Attribute.PLAYBACK_SHUFFLE}",
"galaxy_home_mini_media_playback_shuffle",
"media_player",
"sensor.galaxy_home_mini_media_playback_shuffle",
"disabled",
),
],
)
async def test_create_issue(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
issue_registry: ir.IssueRegistry,
unique_id: str,
suggested_object_id: str,
issue_string: str,
entity_id: str,
expected_state: str,
) -> None:
"""Test we create an issue when an automation or script is using a deprecated entity."""
issue_id = f"deprecated_{issue_string}_{entity_id}"
entity_entry = entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
unique_id,
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 == expected_state
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_{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() await hass.async_block_till_done()
# Assert the issue is no longer present # Assert the issue is no longer present