Add unique ID support to light, cover and media player groups (#53225)

pull/53158/head^2
Franck Nijhof 2021-07-20 13:56:23 +02:00 committed by GitHub
parent 51dd95ce35
commit b4a50f5459
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 9 deletions

View File

@ -36,6 +36,7 @@ from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_SUPPORTED_FEATURES,
CONF_ENTITIES, CONF_ENTITIES,
CONF_NAME, CONF_NAME,
CONF_UNIQUE_ID,
STATE_CLOSING, STATE_CLOSING,
STATE_OPEN, STATE_OPEN,
STATE_OPENING, STATE_OPENING,
@ -57,8 +58,9 @@ DEFAULT_NAME = "Cover Group"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{ {
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_ENTITIES): cv.entities_domain(DOMAIN), 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, discovery_info: dict[str, Any] | None = None,
) -> None: ) -> None:
"""Set up the Group Cover platform.""" """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): class CoverGroup(GroupEntity, CoverEntity):
@ -82,7 +90,7 @@ class CoverGroup(GroupEntity, CoverEntity):
_attr_current_cover_position: int | None = 100 _attr_current_cover_position: int | None = 100
_attr_assumed_state: bool = True _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.""" """Initialize a CoverGroup entity."""
self._entities = entities self._entities = entities
self._covers: dict[str, set[str]] = { self._covers: dict[str, set[str]] = {
@ -98,6 +106,7 @@ class CoverGroup(GroupEntity, CoverEntity):
self._attr_name = name self._attr_name = name
self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entities} 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: async def _update_supported_features_event(self, event: Event) -> None:
self.async_set_context(event.context) self.async_set_context(event.context)

View File

@ -39,6 +39,7 @@ from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_SUPPORTED_FEATURES,
CONF_ENTITIES, CONF_ENTITIES,
CONF_NAME, CONF_NAME,
CONF_UNIQUE_ID,
STATE_ON, STATE_ON,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
@ -55,6 +56,7 @@ DEFAULT_NAME = "Light Group"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{ {
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, 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), vol.Required(CONF_ENTITIES): cv.entities_domain(light.DOMAIN),
} }
) )
@ -72,7 +74,11 @@ async def async_setup_platform(
) -> None: ) -> None:
"""Initialize light.group platform.""" """Initialize light.group platform."""
async_add_entities( 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_min_mireds = 154
_attr_should_poll = False _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.""" """Initialize a light group."""
self._entity_ids = entity_ids self._entity_ids = entity_ids
self._white_value: int | None = None self._white_value: int | None = None
self._attr_name = name self._attr_name = name
self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids} self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids}
self._attr_unique_id = unique_id
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Register callbacks.""" """Register callbacks."""

View File

@ -48,6 +48,7 @@ from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_SUPPORTED_FEATURES,
CONF_ENTITIES, CONF_ENTITIES,
CONF_NAME, CONF_NAME,
CONF_UNIQUE_ID,
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
@ -71,8 +72,9 @@ DEFAULT_NAME = "Media Group"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{ {
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_ENTITIES): cv.entities_domain(DOMAIN), 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, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the Media Group platform.""" """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): class MediaGroup(MediaPlayerEntity):
"""Representation of a Media Group.""" """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.""" """Initialize a Media Group entity."""
self._name = name self._name = name
self._state: str | None = None self._state: str | None = None
self._supported_features: int = 0 self._supported_features: int = 0
self._attr_unique_id = unique_id
self._entities = entities self._entities = entities
self._features: dict[str, set[str]] = { self._features: dict[str, set[str]] = {

View File

@ -17,6 +17,7 @@ from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_FRIENDLY_NAME,
ATTR_SUPPORTED_FEATURES, ATTR_SUPPORTED_FEATURES,
CONF_ENTITIES, CONF_ENTITIES,
CONF_UNIQUE_ID,
SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER,
SERVICE_CLOSE_COVER_TILT, SERVICE_CLOSE_COVER_TILT,
SERVICE_OPEN_COVER, SERVICE_OPEN_COVER,
@ -32,6 +33,7 @@ from homeassistant.const import (
STATE_OPEN, STATE_OPEN,
STATE_OPENING, STATE_OPENING,
) )
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -77,6 +79,7 @@ CONFIG_ATTRIBUTES = {
DOMAIN: { DOMAIN: {
"platform": "group", "platform": "group",
CONF_ENTITIES: [DEMO_COVER, DEMO_COVER_POS, DEMO_COVER_TILT, DEMO_TILT], 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) state = hass.states.get(COVER_GROUP)
assert state.attributes[ATTR_ASSUMED_STATE] is True 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)]) @pytest.mark.parametrize("config_count", [(CONFIG_TILT_ONLY, 2)])
async def test_cover_that_only_supports_tilt_removed(hass, setup_comp): async def test_cover_that_only_supports_tilt_removed(hass, setup_comp):

View File

@ -44,6 +44,7 @@ from homeassistant.const import (
STATE_ON, STATE_ON,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -58,6 +59,7 @@ async def test_default_state(hass):
"platform": DOMAIN, "platform": DOMAIN,
"entities": ["light.kitchen", "light.bedroom"], "entities": ["light.kitchen", "light.bedroom"],
"name": "Bedroom Group", "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_LIST) is None
assert state.attributes.get(ATTR_EFFECT) 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): async def test_state_reporting(hass):
"""Test the state reporting.""" """Test the state reporting."""
@ -1064,7 +1071,7 @@ async def test_invalid_service_calls(hass):
"""Test invalid service call arguments get discarded.""" """Test invalid service call arguments get discarded."""
add_entities = MagicMock() add_entities = MagicMock()
await group.async_setup_platform( 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_block_till_done()
await hass.async_start() await hass.async_start()

View File

@ -49,6 +49,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
) )
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -73,6 +74,7 @@ async def test_default_state(hass):
"platform": DOMAIN, "platform": DOMAIN,
"entities": ["media_player.player_1", "media_player.player_2"], "entities": ["media_player.player_1", "media_player.player_2"],
"name": "Media group", "name": "Media group",
"unique_id": "unique_identifier",
} }
}, },
) )
@ -89,6 +91,11 @@ async def test_default_state(hass):
"media_player.player_2", "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): async def test_state_reporting(hass):
"""Test the state reporting.""" """Test the state reporting."""