Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to bind the group helper entity to a device #118008

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion homeassistant/components/group/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import asyncio
from collections.abc import Collection
import dataclasses
import logging
from typing import Any

Expand All @@ -14,14 +15,19 @@
ATTR_ENTITY_ID, # noqa: F401
ATTR_ICON,
ATTR_NAME,
CONF_DEVICE_ID,
CONF_ENTITIES,
CONF_ICON,
CONF_NAME,
SERVICE_RELOAD,
Platform,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.group import (
expand_entity_ids as _expand_entity_ids,
Expand Down Expand Up @@ -77,6 +83,16 @@
_LOGGER = logging.getLogger(__name__)


@dataclasses.dataclass
class GroupData:
"""Runtime configuration data."""

device: str | None = None


GroupConfigEntry = ConfigEntry[GroupData]


def _conf_preprocess(value: Any) -> dict[str, Any]:
"""Preprocess alternative configuration formats."""
if not isinstance(value, dict):
Expand Down Expand Up @@ -139,6 +155,11 @@ def groups_with_entity(hass: HomeAssistant, entity_id: str) -> list[str]:

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""

entry.runtime_data = GroupData(
device=entry.options.get(CONF_DEVICE_ID, None),
)

await hass.config_entries.async_forward_entry_setups(
entry, (entry.options["group_type"],)
)
Expand All @@ -148,8 +169,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update listener, called when the config entry options are changed."""

# Device id before reload
old_device = entry.runtime_data.device

await hass.config_entries.async_reload(entry.entry_id)

# If the old device is different from the current device it is removed from the config entry
if old_device != entry.options.get(CONF_DEVICE_ID, None) and old_device is not None:
device_registry = dr.async_get(hass)
device_registry.async_update_device(
old_device, remove_config_entry_id=entry.entry_id
)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
Expand Down
30 changes: 28 additions & 2 deletions homeassistant/components/group/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_DEVICE_CLASS,
CONF_DEVICE_ID,
CONF_ENTITIES,
CONF_NAME,
CONF_UNIQUE_ID,
Expand All @@ -25,7 +26,12 @@
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

Expand Down Expand Up @@ -57,6 +63,7 @@ async def async_setup_platform(
async_add_entities(
[
BinarySensorGroup(
hass,
config.get(CONF_UNIQUE_ID),
config[CONF_NAME],
config.get(CONF_DEVICE_CLASS),
Expand All @@ -82,7 +89,13 @@ async def async_setup_entry(
async_add_entities(
[
BinarySensorGroup(
config_entry.entry_id, config_entry.title, None, entities, mode
hass,
config_entry.entry_id,
config_entry.title,
None,
entities,
mode,
config_entry.options.get(CONF_DEVICE_ID, None),
)
]
)
Expand All @@ -94,6 +107,7 @@ def async_create_preview_binary_sensor(
) -> BinarySensorGroup:
"""Create a preview sensor."""
return BinarySensorGroup(
hass,
None,
name,
None,
Expand All @@ -109,11 +123,13 @@ class BinarySensorGroup(GroupEntity, BinarySensorEntity):

def __init__(
self,
hass: HomeAssistant,
unique_id: str | None,
name: str,
device_class: BinarySensorDeviceClass | None,
entity_ids: list[str],
mode: bool | None,
device_id: str | None = None,
) -> None:
"""Initialize a BinarySensorGroup entity."""
super().__init__()
Expand All @@ -126,6 +142,16 @@ def __init__(
if mode:
self.mode = all

dev_reg = dr.async_get(hass)
if (
device_id is not None
and (device := dev_reg.async_get(device_id)) is not None
):
self._attr_device_info = DeviceInfo(
connections=device.connections,
identifiers=device.identifiers,
)

@callback
def async_update_group_state(self) -> None:
"""Query all members and determine the binary sensor group state."""
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/group/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import voluptuous as vol

from homeassistant.components import websocket_api
from homeassistant.const import CONF_ENTITIES, CONF_TYPE
from homeassistant.const import CONF_DEVICE_ID, CONF_ENTITIES, CONF_TYPE
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er, selector
Expand Down Expand Up @@ -65,6 +65,7 @@ async def basic_group_options_schema(
{
vol.Required(CONF_ENTITIES): entity_selector,
vol.Required(CONF_HIDE_MEMBERS, default=False): selector.BooleanSelector(),
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
}
)

Expand All @@ -78,6 +79,7 @@ def basic_group_config_schema(domain: str | list[str]) -> vol.Schema:
selector.EntitySelectorConfig(domain=domain, multiple=True),
),
vol.Required(CONF_HIDE_MEMBERS, default=False): selector.BooleanSelector(),
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
}
)

Expand Down
43 changes: 39 additions & 4 deletions homeassistant/components/group/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
CONF_DEVICE_ID,
CONF_ENTITIES,
CONF_NAME,
CONF_UNIQUE_ID,
Expand All @@ -39,7 +40,12 @@
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

Expand Down Expand Up @@ -74,7 +80,10 @@ async def async_setup_platform(
async_add_entities(
[
CoverGroup(
config.get(CONF_UNIQUE_ID), config[CONF_NAME], config[CONF_ENTITIES]
hass,
config.get(CONF_UNIQUE_ID),
config[CONF_NAME],
config[CONF_ENTITIES],
)
]
)
Expand All @@ -92,7 +101,15 @@ async def async_setup_entry(
)

async_add_entities(
[CoverGroup(config_entry.entry_id, config_entry.title, entities)]
[
CoverGroup(
hass,
config_entry.entry_id,
config_entry.title,
entities,
config_entry.options.get(CONF_DEVICE_ID, None),
)
]
)


Expand All @@ -102,6 +119,7 @@ def async_create_preview_cover(
) -> CoverGroup:
"""Create a preview sensor."""
return CoverGroup(
hass,
None,
name,
validated_config[CONF_ENTITIES],
Expand All @@ -117,7 +135,14 @@ class CoverGroup(GroupEntity, CoverEntity):
_attr_is_closing: bool | None = False
_attr_current_cover_position: int | None = 100

def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None:
def __init__(
self,
hass: HomeAssistant,
unique_id: str | None,
name: str,
entities: list[str],
device_id: str | None = None,
) -> None:
"""Initialize a CoverGroup entity."""
self._entity_ids = entities
self._covers: dict[str, set[str]] = {
Expand All @@ -135,6 +160,16 @@ def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> Non
self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entities}
self._attr_unique_id = unique_id

dev_reg = dr.async_get(hass)
if (
device_id is not None
and (device := dev_reg.async_get(device_id)) is not None
):
self._attr_device_info = DeviceInfo(
connections=device.connections,
identifiers=device.identifiers,
)

@callback
def async_update_supported_features(
self,
Expand Down
26 changes: 24 additions & 2 deletions homeassistant/components/group/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@
ATTR_DEVICE_CLASS,
ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
CONF_DEVICE_ID,
CONF_ENTITIES,
CONF_NAME,
CONF_UNIQUE_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import Event, EventStateChangedData, HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
Expand All @@ -48,7 +54,7 @@


async def async_setup_platform(
_: HomeAssistant,
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
__: DiscoveryInfoType | None = None,
Expand All @@ -57,6 +63,7 @@ async def async_setup_platform(
async_add_entities(
[
EventGroup(
hass,
config.get(CONF_UNIQUE_ID),
config[CONF_NAME],
config[CONF_ENTITIES],
Expand All @@ -78,9 +85,11 @@ async def async_setup_entry(
async_add_entities(
[
EventGroup(
hass,
config_entry.entry_id,
config_entry.title,
entities,
config_entry.options.get(CONF_DEVICE_ID, None),
)
]
)
Expand All @@ -92,6 +101,7 @@ def async_create_preview_event(
) -> EventGroup:
"""Create a preview sensor."""
return EventGroup(
hass,
None,
name,
validated_config[CONF_ENTITIES],
Expand All @@ -106,9 +116,11 @@ class EventGroup(GroupEntity, EventEntity):

def __init__(
self,
hass: HomeAssistant,
unique_id: str | None,
name: str,
entity_ids: list[str],
device_id: str | None = None,
) -> None:
"""Initialize an event group."""
self._entity_ids = entity_ids
Expand All @@ -117,6 +129,16 @@ def __init__(
self._attr_unique_id = unique_id
self._attr_event_types = []

dev_reg = dr.async_get(hass)
if (
device_id is not None
and (device := dev_reg.async_get(device_id)) is not None
):
self._attr_device_info = DeviceInfo(
connections=device.connections,
identifiers=device.identifiers,
)

async def async_added_to_hass(self) -> None:
"""Register callbacks."""

Expand Down
Loading
Loading