From b4a50f5459a12edaedde93c05882978606199836 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 20 Jul 2021 13:56:23 +0200 Subject: [PATCH] Add unique ID support to light, cover and media player groups (#53225) --- homeassistant/components/group/cover.py | 15 ++++++++++++--- homeassistant/components/group/light.py | 11 +++++++++-- homeassistant/components/group/media_player.py | 15 ++++++++++++--- tests/components/group/test_cover.py | 8 ++++++++ tests/components/group/test_light.py | 9 ++++++++- tests/components/group/test_media_player.py | 7 +++++++ 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/group/cover.py b/homeassistant/components/group/cover.py index b5022582d9e..397c7e609f3 100644 --- a/homeassistant/components/group/cover.py +++ b/homeassistant/components/group/cover.py @@ -36,6 +36,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, CONF_NAME, + CONF_UNIQUE_ID, STATE_CLOSING, STATE_OPEN, STATE_OPENING, @@ -57,8 +58,9 @@ DEFAULT_NAME = "Cover Group" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_ENTITIES): cv.entities_domain(DOMAIN), + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ) @@ -70,7 +72,13 @@ async def async_setup_platform( discovery_info: dict[str, Any] | None = None, ) -> None: """Set up the Group Cover platform.""" - async_add_entities([CoverGroup(config[CONF_NAME], config[CONF_ENTITIES])]) + async_add_entities( + [ + CoverGroup( + config.get(CONF_UNIQUE_ID), config[CONF_NAME], config[CONF_ENTITIES] + ) + ] + ) class CoverGroup(GroupEntity, CoverEntity): @@ -82,7 +90,7 @@ class CoverGroup(GroupEntity, CoverEntity): _attr_current_cover_position: int | None = 100 _attr_assumed_state: bool = True - def __init__(self, name: str, entities: list[str]) -> None: + def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None: """Initialize a CoverGroup entity.""" self._entities = entities self._covers: dict[str, set[str]] = { @@ -98,6 +106,7 @@ class CoverGroup(GroupEntity, CoverEntity): self._attr_name = name self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entities} + self._attr_unique_id = unique_id async def _update_supported_features_event(self, event: Event) -> None: self.async_set_context(event.context) diff --git a/homeassistant/components/group/light.py b/homeassistant/components/group/light.py index 3f5a6eaf13e..bb0762d2278 100644 --- a/homeassistant/components/group/light.py +++ b/homeassistant/components/group/light.py @@ -39,6 +39,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, CONF_NAME, + CONF_UNIQUE_ID, STATE_ON, STATE_UNAVAILABLE, ) @@ -55,6 +56,7 @@ DEFAULT_NAME = "Light Group" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Required(CONF_ENTITIES): cv.entities_domain(light.DOMAIN), } ) @@ -72,7 +74,11 @@ async def async_setup_platform( ) -> None: """Initialize light.group platform.""" async_add_entities( - [LightGroup(cast(str, config.get(CONF_NAME)), config[CONF_ENTITIES])] + [ + LightGroup( + config.get(CONF_UNIQUE_ID), config[CONF_NAME], config[CONF_ENTITIES] + ) + ] ) @@ -86,13 +92,14 @@ class LightGroup(GroupEntity, light.LightEntity): _attr_min_mireds = 154 _attr_should_poll = False - def __init__(self, name: str, entity_ids: list[str]) -> None: + def __init__(self, unique_id: str | None, name: str, entity_ids: list[str]) -> None: """Initialize a light group.""" self._entity_ids = entity_ids self._white_value: int | None = None self._attr_name = name self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids} + self._attr_unique_id = unique_id async def async_added_to_hass(self) -> None: """Register callbacks.""" diff --git a/homeassistant/components/group/media_player.py b/homeassistant/components/group/media_player.py index 568812fd6e0..810959609b5 100644 --- a/homeassistant/components/group/media_player.py +++ b/homeassistant/components/group/media_player.py @@ -48,6 +48,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, CONF_NAME, + CONF_UNIQUE_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, @@ -71,8 +72,9 @@ DEFAULT_NAME = "Media Group" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_ENTITIES): cv.entities_domain(DOMAIN), + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ) @@ -84,17 +86,24 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Media Group platform.""" - async_add_entities([MediaGroup(config[CONF_NAME], config[CONF_ENTITIES])]) + async_add_entities( + [ + MediaGroup( + config.get(CONF_UNIQUE_ID), config[CONF_NAME], config[CONF_ENTITIES] + ) + ] + ) class MediaGroup(MediaPlayerEntity): """Representation of a Media Group.""" - def __init__(self, name: str, entities: list[str]) -> None: + def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None: """Initialize a Media Group entity.""" self._name = name self._state: str | None = None self._supported_features: int = 0 + self._attr_unique_id = unique_id self._entities = entities self._features: dict[str, set[str]] = { diff --git a/tests/components/group/test_cover.py b/tests/components/group/test_cover.py index 59bde36b46b..8a29274298b 100644 --- a/tests/components/group/test_cover.py +++ b/tests/components/group/test_cover.py @@ -17,6 +17,7 @@ from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, + CONF_UNIQUE_ID, SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER_TILT, SERVICE_OPEN_COVER, @@ -32,6 +33,7 @@ from homeassistant.const import ( STATE_OPEN, STATE_OPENING, ) +from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -77,6 +79,7 @@ CONFIG_ATTRIBUTES = { DOMAIN: { "platform": "group", CONF_ENTITIES: [DEMO_COVER, DEMO_COVER_POS, DEMO_COVER_TILT, DEMO_TILT], + CONF_UNIQUE_ID: "unique_identifier", } } @@ -220,6 +223,11 @@ async def test_attributes(hass, setup_comp): state = hass.states.get(COVER_GROUP) assert state.attributes[ATTR_ASSUMED_STATE] is True + entity_registry = er.async_get(hass) + entry = entity_registry.async_get(COVER_GROUP) + assert entry + assert entry.unique_id == "unique_identifier" + @pytest.mark.parametrize("config_count", [(CONFIG_TILT_ONLY, 2)]) async def test_cover_that_only_supports_tilt_removed(hass, setup_comp): diff --git a/tests/components/group/test_light.py b/tests/components/group/test_light.py index 06ad1b1101b..74275cf0bd2 100644 --- a/tests/components/group/test_light.py +++ b/tests/components/group/test_light.py @@ -44,6 +44,7 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -58,6 +59,7 @@ async def test_default_state(hass): "platform": DOMAIN, "entities": ["light.kitchen", "light.bedroom"], "name": "Bedroom Group", + "unique_id": "unique_identifier", } }, ) @@ -77,6 +79,11 @@ async def test_default_state(hass): assert state.attributes.get(ATTR_EFFECT_LIST) is None assert state.attributes.get(ATTR_EFFECT) is None + entity_registry = er.async_get(hass) + entry = entity_registry.async_get("light.bedroom_group") + assert entry + assert entry.unique_id == "unique_identifier" + async def test_state_reporting(hass): """Test the state reporting.""" @@ -1064,7 +1071,7 @@ async def test_invalid_service_calls(hass): """Test invalid service call arguments get discarded.""" add_entities = MagicMock() await group.async_setup_platform( - hass, {"entities": ["light.test1", "light.test2"]}, add_entities + hass, {"name": "test", "entities": ["light.test1", "light.test2"]}, add_entities ) await hass.async_block_till_done() await hass.async_start() diff --git a/tests/components/group/test_media_player.py b/tests/components/group/test_media_player.py index 5dd5e4225cc..27962297952 100644 --- a/tests/components/group/test_media_player.py +++ b/tests/components/group/test_media_player.py @@ -49,6 +49,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) +from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -73,6 +74,7 @@ async def test_default_state(hass): "platform": DOMAIN, "entities": ["media_player.player_1", "media_player.player_2"], "name": "Media group", + "unique_id": "unique_identifier", } }, ) @@ -89,6 +91,11 @@ async def test_default_state(hass): "media_player.player_2", ] + entity_registry = er.async_get(hass) + entry = entity_registry.async_get("media_player.media_group") + assert entry + assert entry.unique_id == "unique_identifier" + async def test_state_reporting(hass): """Test the state reporting."""