Allow hiding and unhiding group members (#68192)
This commit is contained in:
parent
b5d2c6e43a
commit
1b955970f8
7 changed files with 359 additions and 14 deletions
|
@ -28,8 +28,7 @@ from homeassistant.const import (
|
||||||
Platform,
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, callback, split_entity_id
|
from homeassistant.core import HomeAssistant, ServiceCall, callback, split_entity_id
|
||||||
from homeassistant.helpers import start
|
from homeassistant.helpers import config_validation as cv, entity_registry as er, start
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.event import async_track_state_change_event
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
|
@ -40,6 +39,8 @@ from homeassistant.helpers.reload import async_reload_integration_platforms
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
|
|
||||||
|
from .const import CONF_HIDE_MEMBERS
|
||||||
|
|
||||||
# mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs
|
# mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs
|
||||||
|
|
||||||
DOMAIN = "group"
|
DOMAIN = "group"
|
||||||
|
@ -238,6 +239,25 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
|
"""Remove a config entry."""
|
||||||
|
# Unhide the group members
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
if not entry.options[CONF_HIDE_MEMBERS]:
|
||||||
|
return
|
||||||
|
|
||||||
|
for member in entry.options[CONF_ENTITIES]:
|
||||||
|
if not (entity_id := er.async_resolve_entity_id(registry, member)):
|
||||||
|
continue
|
||||||
|
if (entity_entry := registry.async_get(entity_id)) is None:
|
||||||
|
continue
|
||||||
|
if entity_entry.hidden_by != er.RegistryEntryHider.INTEGRATION:
|
||||||
|
continue
|
||||||
|
|
||||||
|
registry.async_update_entity(entity_id, hidden_by=None)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up all groups found defined in the configuration."""
|
"""Set up all groups found defined in the configuration."""
|
||||||
if DOMAIN not in hass.data:
|
if DOMAIN not in hass.data:
|
||||||
|
|
|
@ -7,8 +7,8 @@ from typing import Any, cast
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import CONF_ENTITIES
|
from homeassistant.const import CONF_ENTITIES
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import selector
|
from homeassistant.helpers import entity_registry as er, selector
|
||||||
from homeassistant.helpers.helper_config_entry_flow import (
|
from homeassistant.helpers.helper_config_entry_flow import (
|
||||||
HelperConfigFlowHandler,
|
HelperConfigFlowHandler,
|
||||||
HelperFlowStep,
|
HelperFlowStep,
|
||||||
|
@ -16,6 +16,7 @@ from homeassistant.helpers.helper_config_entry_flow import (
|
||||||
|
|
||||||
from . import DOMAIN
|
from . import DOMAIN
|
||||||
from .binary_sensor import CONF_ALL
|
from .binary_sensor import CONF_ALL
|
||||||
|
from .const import CONF_HIDE_MEMBERS
|
||||||
|
|
||||||
|
|
||||||
def basic_group_options_schema(domain: str) -> vol.Schema:
|
def basic_group_options_schema(domain: str) -> vol.Schema:
|
||||||
|
@ -25,6 +26,9 @@ def basic_group_options_schema(domain: str) -> vol.Schema:
|
||||||
vol.Required(CONF_ENTITIES): selector.selector(
|
vol.Required(CONF_ENTITIES): selector.selector(
|
||||||
{"entity": {"domain": domain, "multiple": True}}
|
{"entity": {"domain": domain, "multiple": True}}
|
||||||
),
|
),
|
||||||
|
vol.Required(CONF_HIDE_MEMBERS, default=False): selector.selector(
|
||||||
|
{"boolean": {}}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -98,6 +102,39 @@ class GroupConfigFlowHandler(HelperConfigFlowHandler, domain=DOMAIN):
|
||||||
config_flow = CONFIG_FLOW
|
config_flow = CONFIG_FLOW
|
||||||
options_flow = OPTIONS_FLOW
|
options_flow = OPTIONS_FLOW
|
||||||
|
|
||||||
|
@callback
|
||||||
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
||||||
"""Return config entry title."""
|
"""Return config entry title."""
|
||||||
return cast(str, options["name"]) if "name" in options else ""
|
return cast(str, options["name"]) if "name" in options else ""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_config_flow_finished(self, options: Mapping[str, Any]) -> None:
|
||||||
|
"""Hide the group members if requested."""
|
||||||
|
if options[CONF_HIDE_MEMBERS]:
|
||||||
|
_async_hide_members(
|
||||||
|
self.hass, options[CONF_ENTITIES], er.RegistryEntryHider.INTEGRATION
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@staticmethod
|
||||||
|
def async_options_flow_finished(
|
||||||
|
hass: HomeAssistant, options: Mapping[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Hide or unhide the group members as requested."""
|
||||||
|
hidden_by = (
|
||||||
|
er.RegistryEntryHider.INTEGRATION if options[CONF_HIDE_MEMBERS] else None
|
||||||
|
)
|
||||||
|
_async_hide_members(hass, options[CONF_ENTITIES], hidden_by)
|
||||||
|
|
||||||
|
|
||||||
|
def _async_hide_members(
|
||||||
|
hass: HomeAssistant, members: list[str], hidden_by: er.RegistryEntryHider | None
|
||||||
|
) -> None:
|
||||||
|
"""Hide or unhide group members."""
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
for member in members:
|
||||||
|
if not (entity_id := er.async_resolve_entity_id(registry, member)):
|
||||||
|
continue
|
||||||
|
if entity_id not in registry.entities:
|
||||||
|
continue
|
||||||
|
registry.async_update_entity(entity_id, hidden_by=hidden_by)
|
||||||
|
|
3
homeassistant/components/group/const.py
Normal file
3
homeassistant/components/group/const.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
"""Constants for the Group integration."""
|
||||||
|
|
||||||
|
CONF_HIDE_MEMBERS = "hide_members"
|
|
@ -961,6 +961,22 @@ def async_validate_entity_id(registry: EntityRegistry, entity_id_or_uuid: str) -
|
||||||
return entry.entity_id
|
return entry.entity_id
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_resolve_entity_id(
|
||||||
|
registry: EntityRegistry, entity_id_or_uuid: str
|
||||||
|
) -> str | None:
|
||||||
|
"""Validate and resolve an entity id or UUID to an entity id.
|
||||||
|
|
||||||
|
Returns None if the entity or UUID is invalid, or if the UUID is not
|
||||||
|
associated with an entity registry item.
|
||||||
|
"""
|
||||||
|
if valid_entity_id(entity_id_or_uuid):
|
||||||
|
return entity_id_or_uuid
|
||||||
|
if (entry := registry.entities.get_entry(entity_id_or_uuid)) is None:
|
||||||
|
return None
|
||||||
|
return entry.entity_id
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_validate_entity_ids(
|
def async_validate_entity_ids(
|
||||||
registry: EntityRegistry, entity_ids_or_uuids: list[str]
|
registry: EntityRegistry, entity_ids_or_uuids: list[str]
|
||||||
|
|
|
@ -137,7 +137,9 @@ class HelperConfigFlowHandler(config_entries.ConfigFlow):
|
||||||
if cls.options_flow is None:
|
if cls.options_flow is None:
|
||||||
raise UnknownHandler
|
raise UnknownHandler
|
||||||
|
|
||||||
return HelperOptionsFlowHandler(config_entry, cls.options_flow)
|
return HelperOptionsFlowHandler(
|
||||||
|
config_entry, cls.options_flow, cls.async_options_flow_finished
|
||||||
|
)
|
||||||
|
|
||||||
# Create an async_get_options_flow method
|
# Create an async_get_options_flow method
|
||||||
cls.async_get_options_flow = _async_get_options_flow # type: ignore[assignment]
|
cls.async_get_options_flow = _async_get_options_flow # type: ignore[assignment]
|
||||||
|
@ -167,6 +169,7 @@ class HelperConfigFlowHandler(config_entries.ConfigFlow):
|
||||||
|
|
||||||
# pylint: disable-next=no-self-use
|
# pylint: disable-next=no-self-use
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@callback
|
||||||
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
||||||
"""Return config entry title.
|
"""Return config entry title.
|
||||||
|
|
||||||
|
@ -174,6 +177,25 @@ class HelperConfigFlowHandler(config_entries.ConfigFlow):
|
||||||
input from the config flow steps.
|
input from the config flow steps.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_config_flow_finished(self, options: Mapping[str, Any]) -> None:
|
||||||
|
"""Take necessary actions after the config flow is finished, if needed.
|
||||||
|
|
||||||
|
The options parameter contains config entry options, which is the union of user
|
||||||
|
input from the config flow steps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@staticmethod
|
||||||
|
def async_options_flow_finished(
|
||||||
|
hass: HomeAssistant, options: Mapping[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Take necessary actions after the options flow is finished, if needed.
|
||||||
|
|
||||||
|
The options parameter contains config entry options, which is the union of stored
|
||||||
|
options and user input from the options flow steps.
|
||||||
|
"""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_create_entry( # pylint: disable=arguments-differ
|
def async_create_entry( # pylint: disable=arguments-differ
|
||||||
self,
|
self,
|
||||||
|
@ -181,6 +203,7 @@ class HelperConfigFlowHandler(config_entries.ConfigFlow):
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Finish config flow and create a config entry."""
|
"""Finish config flow and create a config entry."""
|
||||||
|
self.async_config_flow_finished(data)
|
||||||
return super().async_create_entry(
|
return super().async_create_entry(
|
||||||
data={}, options=data, title=self.async_config_entry_title(data), **kwargs
|
data={}, options=data, title=self.async_config_entry_title(data), **kwargs
|
||||||
)
|
)
|
||||||
|
@ -193,10 +216,12 @@ class HelperOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
self,
|
self,
|
||||||
config_entry: config_entries.ConfigEntry,
|
config_entry: config_entries.ConfigEntry,
|
||||||
options_flow: dict[str, vol.Schema],
|
options_flow: dict[str, vol.Schema],
|
||||||
|
async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize options flow."""
|
"""Initialize options flow."""
|
||||||
self._common_handler = HelperCommonFlowHandler(self, options_flow, config_entry)
|
self._common_handler = HelperCommonFlowHandler(self, options_flow, config_entry)
|
||||||
self._config_entry = config_entry
|
self._config_entry = config_entry
|
||||||
|
self._async_options_flow_finished = async_options_flow_finished
|
||||||
|
|
||||||
for step in options_flow:
|
for step in options_flow:
|
||||||
setattr(self, f"async_step_{step}", self._async_step)
|
setattr(self, f"async_step_{step}", self._async_step)
|
||||||
|
@ -210,10 +235,12 @@ class HelperOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
@callback
|
@callback
|
||||||
def async_create_entry( # pylint: disable=arguments-differ
|
def async_create_entry( # pylint: disable=arguments-differ
|
||||||
self,
|
self,
|
||||||
|
data: Mapping[str, Any],
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Finish config flow and create a config entry."""
|
"""Finish config flow and create a config entry."""
|
||||||
return super().async_create_entry(title="", **kwargs)
|
self._async_options_flow_finished(self.hass, data)
|
||||||
|
return super().async_create_entry(title="", data=data, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -7,6 +7,7 @@ from homeassistant import config_entries
|
||||||
from homeassistant.components.group import DOMAIN, async_setup_entry
|
from homeassistant.components.group import DOMAIN, async_setup_entry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
@ -68,8 +69,9 @@ async def test_config_flow(
|
||||||
assert result["title"] == "Living Room"
|
assert result["title"] == "Living Room"
|
||||||
assert result["data"] == {}
|
assert result["data"] == {}
|
||||||
assert result["options"] == {
|
assert result["options"] == {
|
||||||
"group_type": group_type,
|
|
||||||
"entities": members,
|
"entities": members,
|
||||||
|
"group_type": group_type,
|
||||||
|
"hide_members": False,
|
||||||
"name": "Living Room",
|
"name": "Living Room",
|
||||||
**extra_options,
|
**extra_options,
|
||||||
}
|
}
|
||||||
|
@ -78,9 +80,10 @@ async def test_config_flow(
|
||||||
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
assert config_entry.data == {}
|
assert config_entry.data == {}
|
||||||
assert config_entry.options == {
|
assert config_entry.options == {
|
||||||
"group_type": group_type,
|
|
||||||
"name": "Living Room",
|
|
||||||
"entities": members,
|
"entities": members,
|
||||||
|
"group_type": group_type,
|
||||||
|
"hide_members": False,
|
||||||
|
"name": "Living Room",
|
||||||
**extra_options,
|
**extra_options,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +94,69 @@ async def test_config_flow(
|
||||||
assert state.attributes[key] == extra_attrs[key]
|
assert state.attributes[key] == extra_attrs[key]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"hide_members,hidden_by", ((False, None), (True, "integration"))
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"group_type,extra_input",
|
||||||
|
(
|
||||||
|
("binary_sensor", {"all": False}),
|
||||||
|
("cover", {}),
|
||||||
|
("fan", {}),
|
||||||
|
("light", {}),
|
||||||
|
("media_player", {}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_config_flow_hides_members(
|
||||||
|
hass: HomeAssistant, group_type, extra_input, hide_members, hidden_by
|
||||||
|
) -> None:
|
||||||
|
"""Test the config flow hides members if requested."""
|
||||||
|
fake_uuid = "a266a680b608c32770e6c45bfe6b8411"
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
entry = registry.async_get_or_create(
|
||||||
|
group_type, "test", "unique", suggested_object_id="one"
|
||||||
|
)
|
||||||
|
assert entry.entity_id == f"{group_type}.one"
|
||||||
|
assert entry.hidden_by is None
|
||||||
|
|
||||||
|
entry = registry.async_get_or_create(
|
||||||
|
group_type, "test", "unique3", suggested_object_id="three"
|
||||||
|
)
|
||||||
|
assert entry.entity_id == f"{group_type}.three"
|
||||||
|
assert entry.hidden_by is None
|
||||||
|
|
||||||
|
members = [f"{group_type}.one", f"{group_type}.two", fake_uuid, entry.id]
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{"group_type": group_type},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == group_type
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"name": "Living Room",
|
||||||
|
"entities": members,
|
||||||
|
"hide_members": hide_members,
|
||||||
|
**extra_input,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
|
|
||||||
|
assert registry.async_get(f"{group_type}.one").hidden_by == hidden_by
|
||||||
|
assert registry.async_get(f"{group_type}.three").hidden_by == hidden_by
|
||||||
|
|
||||||
|
|
||||||
def get_suggested(schema, key):
|
def get_suggested(schema, key):
|
||||||
"""Get suggested value for key in voluptuous schema."""
|
"""Get suggested value for key in voluptuous schema."""
|
||||||
for k in schema.keys():
|
for k in schema.keys():
|
||||||
|
@ -124,7 +190,7 @@ async def test_options(
|
||||||
for member in members2:
|
for member in members2:
|
||||||
hass.states.async_set(member, member_state, {})
|
hass.states.async_set(member, member_state, {})
|
||||||
|
|
||||||
switch_as_x_config_entry = MockConfigEntry(
|
group_config_entry = MockConfigEntry(
|
||||||
data={},
|
data={},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={
|
options={
|
||||||
|
@ -135,9 +201,9 @@ async def test_options(
|
||||||
},
|
},
|
||||||
title="Bed Room",
|
title="Bed Room",
|
||||||
)
|
)
|
||||||
switch_as_x_config_entry.add_to_hass(hass)
|
group_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
assert await hass.config_entries.async_setup(switch_as_x_config_entry.entry_id)
|
assert await hass.config_entries.async_setup(group_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(f"{group_type}.bed_room")
|
state = hass.states.get(f"{group_type}.bed_room")
|
||||||
|
@ -159,15 +225,17 @@ async def test_options(
|
||||||
)
|
)
|
||||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
assert result["data"] == {
|
assert result["data"] == {
|
||||||
"group_type": group_type,
|
|
||||||
"entities": members2,
|
"entities": members2,
|
||||||
|
"group_type": group_type,
|
||||||
|
"hide_members": False,
|
||||||
"name": "Bed Room",
|
"name": "Bed Room",
|
||||||
**extra_options,
|
**extra_options,
|
||||||
}
|
}
|
||||||
assert config_entry.data == {}
|
assert config_entry.data == {}
|
||||||
assert config_entry.options == {
|
assert config_entry.options == {
|
||||||
"group_type": group_type,
|
|
||||||
"entities": members2,
|
"entities": members2,
|
||||||
|
"group_type": group_type,
|
||||||
|
"hide_members": False,
|
||||||
"name": "Bed Room",
|
"name": "Bed Room",
|
||||||
**extra_options,
|
**extra_options,
|
||||||
}
|
}
|
||||||
|
@ -196,3 +264,83 @@ async def test_options(
|
||||||
|
|
||||||
assert get_suggested(result["data_schema"].schema, "entities") is None
|
assert get_suggested(result["data_schema"].schema, "entities") is None
|
||||||
assert get_suggested(result["data_schema"].schema, "name") is None
|
assert get_suggested(result["data_schema"].schema, "name") is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"hide_members,hidden_by_initial,hidden_by",
|
||||||
|
((False, "integration", None), (True, None, "integration")),
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"group_type,extra_input",
|
||||||
|
(
|
||||||
|
("binary_sensor", {"all": False}),
|
||||||
|
("cover", {}),
|
||||||
|
("fan", {}),
|
||||||
|
("light", {}),
|
||||||
|
("media_player", {}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_options_flow_hides_members(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
group_type,
|
||||||
|
extra_input,
|
||||||
|
hide_members,
|
||||||
|
hidden_by_initial,
|
||||||
|
hidden_by,
|
||||||
|
) -> None:
|
||||||
|
"""Test the options flow hides or unhides members if requested."""
|
||||||
|
fake_uuid = "a266a680b608c32770e6c45bfe6b8411"
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
entry = registry.async_get_or_create(
|
||||||
|
group_type,
|
||||||
|
"test",
|
||||||
|
"unique1",
|
||||||
|
suggested_object_id="one",
|
||||||
|
hidden_by=hidden_by_initial,
|
||||||
|
)
|
||||||
|
assert entry.entity_id == f"{group_type}.one"
|
||||||
|
|
||||||
|
entry = registry.async_get_or_create(
|
||||||
|
group_type,
|
||||||
|
"test",
|
||||||
|
"unique3",
|
||||||
|
suggested_object_id="three",
|
||||||
|
hidden_by=hidden_by_initial,
|
||||||
|
)
|
||||||
|
assert entry.entity_id == f"{group_type}.three"
|
||||||
|
|
||||||
|
members = [f"{group_type}.one", f"{group_type}.two", fake_uuid, entry.id]
|
||||||
|
|
||||||
|
group_config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=DOMAIN,
|
||||||
|
options={
|
||||||
|
"entities": members,
|
||||||
|
"group_type": group_type,
|
||||||
|
"hide_members": False,
|
||||||
|
"name": "Bed Room",
|
||||||
|
**extra_input,
|
||||||
|
},
|
||||||
|
title="Bed Room",
|
||||||
|
)
|
||||||
|
group_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(group_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_init(group_config_entry.entry_id)
|
||||||
|
assert result["type"] == RESULT_TYPE_FORM
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
"entities": members,
|
||||||
|
"hide_members": hide_members,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||||
|
|
||||||
|
assert registry.async_get(f"{group_type}.one").hidden_by == hidden_by
|
||||||
|
assert registry.async_get(f"{group_type}.three").hidden_by == hidden_by
|
||||||
|
|
|
@ -1416,3 +1416,97 @@ async def test_setup_and_remove_config_entry(
|
||||||
# Check the state and entity registry entry are removed
|
# Check the state and entity registry entry are removed
|
||||||
assert hass.states.get(f"{group_type}.bed_room") is None
|
assert hass.states.get(f"{group_type}.bed_room") is None
|
||||||
assert registry.async_get(f"{group_type}.bed_room") is None
|
assert registry.async_get(f"{group_type}.bed_room") is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"hide_members,hidden_by_initial,hidden_by",
|
||||||
|
(
|
||||||
|
(False, "integration", "integration"),
|
||||||
|
(False, None, None),
|
||||||
|
(False, "user", "user"),
|
||||||
|
(True, "integration", None),
|
||||||
|
(True, None, None),
|
||||||
|
(True, "user", "user"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"group_type,extra_options",
|
||||||
|
(
|
||||||
|
("binary_sensor", {"all": False}),
|
||||||
|
("cover", {}),
|
||||||
|
("fan", {}),
|
||||||
|
("light", {}),
|
||||||
|
("media_player", {}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_unhide_members_on_remove(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
group_type: str,
|
||||||
|
extra_options: dict[str, Any],
|
||||||
|
hide_members: bool,
|
||||||
|
hidden_by_initial: str,
|
||||||
|
hidden_by: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test removing a config entry."""
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
entry1 = registry.async_get_or_create(
|
||||||
|
group_type,
|
||||||
|
"test",
|
||||||
|
"unique1",
|
||||||
|
suggested_object_id="one",
|
||||||
|
hidden_by=hidden_by_initial,
|
||||||
|
)
|
||||||
|
assert entry1.entity_id == f"{group_type}.one"
|
||||||
|
|
||||||
|
entry3 = registry.async_get_or_create(
|
||||||
|
group_type,
|
||||||
|
"test",
|
||||||
|
"unique3",
|
||||||
|
suggested_object_id="three",
|
||||||
|
hidden_by=hidden_by_initial,
|
||||||
|
)
|
||||||
|
assert entry3.entity_id == f"{group_type}.three"
|
||||||
|
|
||||||
|
entry4 = registry.async_get_or_create(
|
||||||
|
group_type,
|
||||||
|
"test",
|
||||||
|
"unique4",
|
||||||
|
suggested_object_id="four",
|
||||||
|
)
|
||||||
|
assert entry4.entity_id == f"{group_type}.four"
|
||||||
|
|
||||||
|
members = [f"{group_type}.one", f"{group_type}.two", entry3.id, entry4.id]
|
||||||
|
|
||||||
|
# Setup the config entry
|
||||||
|
group_config_entry = MockConfigEntry(
|
||||||
|
data={},
|
||||||
|
domain=group.DOMAIN,
|
||||||
|
options={
|
||||||
|
"entities": members,
|
||||||
|
"group_type": group_type,
|
||||||
|
"hide_members": hide_members,
|
||||||
|
"name": "Bed Room",
|
||||||
|
**extra_options,
|
||||||
|
},
|
||||||
|
title="Bed Room",
|
||||||
|
)
|
||||||
|
group_config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(group_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Check the state is present
|
||||||
|
assert hass.states.get(f"{group_type}.bed_room")
|
||||||
|
|
||||||
|
# Remove one entity registry entry, to make sure this does not trip up config entry
|
||||||
|
# removal
|
||||||
|
registry.async_remove(entry4.entity_id)
|
||||||
|
|
||||||
|
# Remove the config entry
|
||||||
|
assert await hass.config_entries.async_remove(group_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Check the group members are unhidden
|
||||||
|
assert registry.async_get(f"{group_type}.one").hidden_by == hidden_by
|
||||||
|
assert registry.async_get(f"{group_type}.three").hidden_by == hidden_by
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue