core/tests/components/group/test_init.py

2286 lines
69 KiB
Python

"""The tests for the Group components."""
from __future__ import annotations
import asyncio
from collections import OrderedDict
from typing import Any
from unittest.mock import patch
import pytest
from homeassistant.components import group
from homeassistant.components.group.registry import GroupIntegrationRegistry
from homeassistant.components.lock import LockState
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_FRIENDLY_NAME,
ATTR_ICON,
EVENT_HOMEASSISTANT_START,
SERVICE_RELOAD,
STATE_CLOSED,
STATE_HOME,
STATE_NOT_HOME,
STATE_OFF,
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from . import common
from tests.common import (
MockConfigEntry,
MockModule,
MockPlatform,
assert_setup_component,
mock_integration,
mock_platform,
)
async def help_test_mixed_entity_platforms_on_off_state_test(
hass: HomeAssistant,
on_off_states1: tuple[set[str], str, str],
on_off_states2: tuple[set[str], str, str],
entity_and_state1_state_2: tuple[str, str | None, str | None],
group_state1: str,
group_state2: str,
grouped_groups: bool = False,
) -> None:
"""Help test on_off_states on mixed entity platforms."""
class MockGroupPlatform1(MockPlatform):
"""Mock a group platform module for test1 integration."""
def async_describe_on_off_states(
self, hass: HomeAssistant, registry: GroupIntegrationRegistry
) -> None:
"""Describe group on off states."""
registry.on_off_states("test1", *on_off_states1)
class MockGroupPlatform2(MockPlatform):
"""Mock a group platform module for test2 integration."""
def async_describe_on_off_states(
self, hass: HomeAssistant, registry: GroupIntegrationRegistry
) -> None:
"""Describe group on off states."""
registry.on_off_states("test2", *on_off_states2)
mock_integration(hass, MockModule(domain="test1"))
mock_platform(hass, "test1.group", MockGroupPlatform1())
assert await async_setup_component(hass, "test1", {"test1": {}})
mock_integration(hass, MockModule(domain="test2"))
mock_platform(hass, "test2.group", MockGroupPlatform2())
assert await async_setup_component(hass, "test2", {"test2": {}})
if grouped_groups:
assert await async_setup_component(
hass,
"group",
{
"group": {
"test1": {
"entities": [
item[0]
for item in entity_and_state1_state_2
if item[0].startswith("test1.")
]
},
"test2": {
"entities": [
item[0]
for item in entity_and_state1_state_2
if item[0].startswith("test2.")
]
},
"test": {"entities": ["group.test1", "group.test2"]},
}
},
)
else:
assert await async_setup_component(
hass,
"group",
{
"group": {
"test": {
"entities": [item[0] for item in entity_and_state1_state_2]
},
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get("group.test")
assert state is not None
# Set first state
for entity_id, state1, _ in entity_and_state1_state_2:
hass.states.async_set(entity_id, state1)
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get("group.test")
assert state is not None
assert state.state == group_state1
# Set second state
for entity_id, _, state2 in entity_and_state1_state_2:
hass.states.async_set(entity_id, state2)
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get("group.test")
assert state is not None
assert state.state == group_state2
async def test_setup_group_with_mixed_groupable_states(hass: HomeAssistant) -> None:
"""Try to set up a group with mixed groupable states."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("device_tracker.Paulus", STATE_HOME)
assert await async_setup_component(hass, "group", {})
await group.Group.async_create_group(
hass,
"person_and_light",
created_by_service=False,
entity_ids=["light.Bowl", "device_tracker.Paulus"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await hass.async_block_till_done()
assert hass.states.get(f"{group.DOMAIN}.person_and_light").state == STATE_ON
async def test_setup_group_with_a_non_existing_state(hass: HomeAssistant) -> None:
"""Try to set up a group with a non existing state."""
hass.states.async_set("light.Bowl", STATE_ON)
assert await async_setup_component(hass, "group", {})
grp = await group.Group.async_create_group(
hass,
"light_and_nothing",
created_by_service=False,
entity_ids=["light.Bowl", "non.existing"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert grp.state == STATE_ON
async def test_setup_group_with_non_groupable_states(hass: HomeAssistant) -> None:
"""Test setup with groups which are not groupable."""
hass.states.async_set("cast.living_room", "Plex")
hass.states.async_set("cast.bedroom", "Netflix")
assert await async_setup_component(hass, "group", {})
grp = await group.Group.async_create_group(
hass,
"chromecasts",
created_by_service=False,
entity_ids=["cast.living_room", "cast.bedroom"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert grp.state is None
async def test_setup_empty_group(hass: HomeAssistant) -> None:
"""Try to set up an empty group."""
grp = await group.Group.async_create_group(
hass,
"nothing",
created_by_service=False,
entity_ids=[],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert grp.state is None
async def test_monitor_group(hass: HomeAssistant) -> None:
"""Test if the group keeps track of states."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
# Test if group setup in our init mode is ok
assert test_group.entity_id in hass.states.async_entity_ids()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_ON
assert group_state.attributes.get(group.ATTR_AUTO)
async def test_group_turns_off_if_all_off(hass: HomeAssistant) -> None:
"""Test if turn off if the last device that was on turns off."""
hass.states.async_set("light.Bowl", STATE_OFF)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await hass.async_block_till_done()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_OFF
async def test_group_turns_on_if_all_are_off_and_one_turns_on(
hass: HomeAssistant,
) -> None:
"""Test if turn on if all devices were turned off and one turns on."""
hass.states.async_set("light.Bowl", STATE_OFF)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
# Turn one on
hass.states.async_set("light.Ceiling", STATE_ON)
await hass.async_block_till_done()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_ON
async def test_allgroup_stays_off_if_all_are_off_and_one_turns_on(
hass: HomeAssistant,
) -> None:
"""Group with all: true, stay off if one device turns on."""
hass.states.async_set("light.Bowl", STATE_OFF)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=True,
object_id=None,
order=None,
)
# Turn one on
hass.states.async_set("light.Ceiling", STATE_ON)
await hass.async_block_till_done()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_OFF
async def test_allgroup_turn_on_if_last_turns_on(hass: HomeAssistant) -> None:
"""Group with all: true, turn on if all devices are on."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=True,
object_id=None,
order=None,
)
# Turn one on
hass.states.async_set("light.Ceiling", STATE_ON)
await hass.async_block_till_done()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_ON
async def test_expand_entity_ids(hass: HomeAssistant) -> None:
"""Test expand_entity_ids method."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert sorted(["light.ceiling", "light.bowl"]) == sorted(
group.expand_entity_ids(hass, [test_group.entity_id])
)
async def test_expand_entity_ids_does_not_return_duplicates(
hass: HomeAssistant,
) -> None:
"""Test that expand_entity_ids does not return duplicates."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert sorted(
group.expand_entity_ids(hass, [test_group.entity_id, "light.Ceiling"])
) == ["light.bowl", "light.ceiling"]
assert sorted(
group.expand_entity_ids(hass, ["light.bowl", test_group.entity_id])
) == ["light.bowl", "light.ceiling"]
async def test_expand_entity_ids_recursive(hass: HomeAssistant) -> None:
"""Test expand_entity_ids method with a group that contains itself."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling", "group.init_group"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert sorted(["light.ceiling", "light.bowl"]) == sorted(
group.expand_entity_ids(hass, [test_group.entity_id])
)
async def test_expand_entity_ids_ignores_non_strings(hass: HomeAssistant) -> None:
"""Test that non string elements in lists are ignored."""
assert group.expand_entity_ids(hass, [5, True]) == []
async def test_get_entity_ids(hass: HomeAssistant) -> None:
"""Test get_entity_ids method."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert sorted(group.get_entity_ids(hass, test_group.entity_id)) == [
"light.bowl",
"light.ceiling",
]
async def test_get_entity_ids_with_domain_filter(hass: HomeAssistant) -> None:
"""Test if get_entity_ids works with a domain_filter."""
hass.states.async_set("switch.AC", STATE_OFF)
assert await async_setup_component(hass, "group", {})
mixed_group = await group.Group.async_create_group(
hass,
"mixed_group",
created_by_service=True,
entity_ids=["light.Bowl", "switch.AC"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert group.get_entity_ids(
hass, mixed_group.entity_id, domain_filter="switch"
) == ["switch.ac"]
async def test_get_entity_ids_with_non_existing_group_name(hass: HomeAssistant) -> None:
"""Test get_entity_ids with a non existing group."""
assert group.get_entity_ids(hass, "non_existing") == []
async def test_get_entity_ids_with_non_group_state(hass: HomeAssistant) -> None:
"""Test get_entity_ids with a non group state."""
assert group.get_entity_ids(hass, "switch.AC") == []
async def test_group_being_init_before_first_tracked_state_is_set_to_on(
hass: HomeAssistant,
) -> None:
"""Test if the groups turn on.
If no states existed and now a state it is tracking is being added
as ON.
"""
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"test group",
created_by_service=False,
entity_ids=["light.not_there_1"],
icon=None,
mode=None,
object_id=None,
order=None,
)
hass.states.async_set("light.not_there_1", STATE_ON)
await hass.async_block_till_done()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_ON
async def test_group_being_init_before_first_tracked_state_is_set_to_off(
hass: HomeAssistant,
) -> None:
"""Test if the group turns off.
If no states existed and now a state it is tracking is being added
as OFF.
"""
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"test group",
created_by_service=False,
entity_ids=["light.not_there_1"],
icon=None,
mode=None,
object_id=None,
order=None,
)
hass.states.async_set("light.not_there_1", STATE_OFF)
await hass.async_block_till_done()
group_state = hass.states.get(test_group.entity_id)
assert group_state.state == STATE_OFF
async def test_groups_get_unique_names(hass: HomeAssistant) -> None:
"""Two groups with same name should both have a unique entity id."""
assert await async_setup_component(hass, "group", {})
grp1 = await group.Group.async_create_group(
hass,
"Je suis Charlie",
created_by_service=False,
entity_ids=None,
icon=None,
mode=None,
object_id=None,
order=None,
)
grp2 = await group.Group.async_create_group(
hass,
"Je suis Charlie",
created_by_service=False,
entity_ids=None,
icon=None,
mode=None,
object_id=None,
order=None,
)
assert grp1.entity_id != grp2.entity_id
async def test_expand_entity_ids_expands_nested_groups(hass: HomeAssistant) -> None:
"""Test if entity ids epands to nested groups."""
assert await async_setup_component(hass, "group", {})
await group.Group.async_create_group(
hass,
"light",
created_by_service=False,
entity_ids=["light.test_1", "light.test_2"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await group.Group.async_create_group(
hass,
"switch",
created_by_service=False,
entity_ids=["switch.test_1", "switch.test_2"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await group.Group.async_create_group(
hass,
"group_of_groups",
created_by_service=False,
entity_ids=["group.light", "group.switch"],
icon=None,
mode=None,
object_id=None,
order=None,
)
assert sorted(group.expand_entity_ids(hass, ["group.group_of_groups"])) == [
"light.test_1",
"light.test_2",
"switch.test_1",
"switch.test_2",
]
async def test_set_assumed_state_based_on_tracked(hass: HomeAssistant) -> None:
"""Test assumed state."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert await async_setup_component(hass, "group", {})
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=False,
entity_ids=["light.Bowl", "light.Ceiling", "sensor.no_exist"],
icon=None,
mode=None,
object_id=None,
order=None,
)
state = hass.states.get(test_group.entity_id)
assert not state.attributes.get(ATTR_ASSUMED_STATE)
hass.states.async_set("light.Bowl", STATE_ON, {ATTR_ASSUMED_STATE: True})
await hass.async_block_till_done()
state = hass.states.get(test_group.entity_id)
assert state.attributes.get(ATTR_ASSUMED_STATE)
hass.states.async_set("light.Bowl", STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(test_group.entity_id)
assert not state.attributes.get(ATTR_ASSUMED_STATE)
async def test_group_updated_after_device_tracker_zone_change(
hass: HomeAssistant,
) -> None:
"""Test group state when device tracker in group changes zone."""
hass.states.async_set("device_tracker.Adam", STATE_HOME)
hass.states.async_set("device_tracker.Eve", STATE_NOT_HOME)
await hass.async_block_till_done()
assert await async_setup_component(hass, "group", {})
assert await async_setup_component(hass, "device_tracker", {})
await hass.async_block_till_done()
await group.Group.async_create_group(
hass,
"peeps",
created_by_service=False,
entity_ids=["device_tracker.Adam", "device_tracker.Eve"],
icon=None,
mode=None,
object_id=None,
order=None,
)
hass.states.async_set("device_tracker.Adam", "cool_state_not_home")
await hass.async_block_till_done()
assert hass.states.get(f"{group.DOMAIN}.peeps").state == STATE_NOT_HOME
async def test_is_on(hass: HomeAssistant) -> None:
"""Test is_on method."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
assert group.is_on(hass, "group.none") is False
assert await async_setup_component(hass, "light", {})
assert await async_setup_component(hass, "group", {})
await hass.async_block_till_done()
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await hass.async_block_till_done()
assert group.is_on(hass, test_group.entity_id) is True
hass.states.async_set("light.Bowl", STATE_OFF)
await hass.async_block_till_done()
assert group.is_on(hass, test_group.entity_id) is False
# Try on non existing state
assert not group.is_on(hass, "non.existing")
@pytest.mark.parametrize(
(
"domains",
"states_old",
"states_new",
"state_ison_group_old",
"state_ison_group_new",
),
[
(
("light", "light"),
(STATE_ON, STATE_OFF),
(STATE_OFF, STATE_OFF),
(STATE_ON, True),
(STATE_OFF, False),
),
(
("cover", "cover"),
(LockState.OPEN, STATE_CLOSED),
(STATE_CLOSED, STATE_CLOSED),
(LockState.OPEN, True),
(STATE_CLOSED, False),
),
(
("lock", "lock"),
(LockState.UNLOCKED, LockState.LOCKED),
(LockState.LOCKED, LockState.LOCKED),
(LockState.UNLOCKED, True),
(LockState.LOCKED, False),
),
(
("cover", "lock"),
(LockState.OPEN, LockState.LOCKED),
(STATE_CLOSED, LockState.LOCKED),
(STATE_ON, True),
(STATE_OFF, False),
),
(
("cover", "lock"),
(LockState.OPEN, LockState.UNLOCKED),
(STATE_CLOSED, LockState.LOCKED),
(STATE_ON, True),
(STATE_OFF, False),
),
(
("cover", "lock", "light"),
(LockState.OPEN, LockState.LOCKED, STATE_ON),
(STATE_CLOSED, LockState.LOCKED, STATE_OFF),
(STATE_ON, True),
(STATE_OFF, False),
),
(
("lock", "lock"),
(LockState.OPEN, LockState.LOCKED),
(LockState.LOCKED, LockState.LOCKED),
(LockState.UNLOCKED, True),
(LockState.LOCKED, False),
),
(
("lock", "lock"),
(LockState.OPENING, LockState.LOCKED),
(LockState.LOCKED, LockState.LOCKED),
(LockState.UNLOCKED, True),
(LockState.LOCKED, False),
),
(
("lock", "lock"),
(LockState.UNLOCKING, LockState.LOCKED),
(LockState.LOCKED, LockState.LOCKED),
(LockState.UNLOCKED, True),
(LockState.LOCKED, False),
),
(
("lock", "lock"),
(LockState.LOCKING, LockState.LOCKED),
(LockState.LOCKED, LockState.LOCKED),
(LockState.UNLOCKED, True),
(LockState.LOCKED, False),
),
(
("lock", "lock"),
(LockState.JAMMED, LockState.LOCKED),
(LockState.LOCKED, LockState.LOCKED),
(LockState.LOCKED, False),
(LockState.LOCKED, False),
),
(
("cover", "lock"),
(LockState.OPEN, LockState.OPEN),
(STATE_CLOSED, LockState.LOCKED),
(STATE_ON, True),
(STATE_OFF, False),
),
],
)
async def test_is_on_and_state_mixed_domains(
hass: HomeAssistant,
domains: tuple[str, ...],
states_old: tuple[str, ...],
states_new: tuple[str, ...],
state_ison_group_old: tuple[str, bool],
state_ison_group_new: tuple[str, bool],
) -> None:
"""Test is_on method with mixed domains."""
count = len(domains)
entity_ids = [f"{domains[index]}.test_{index}" for index in range(count)]
for index in range(count):
hass.states.async_set(entity_ids[index], states_old[index])
assert not group.is_on(hass, "group.none")
await asyncio.gather(
*[async_setup_component(hass, domain, {}) for domain in set(domains)]
)
assert await async_setup_component(hass, "group", {})
await hass.async_block_till_done()
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=entity_ids,
icon=None,
mode=None,
object_id=None,
order=None,
)
await hass.async_block_till_done()
# Assert on old state
state = hass.states.get(test_group.entity_id)
assert state is not None
assert state.state == state_ison_group_old[0]
assert group.is_on(hass, test_group.entity_id) == state_ison_group_old[1]
# Switch and assert on new state
for index in range(count):
hass.states.async_set(entity_ids[index], states_new[index])
await hass.async_block_till_done()
state = hass.states.get(test_group.entity_id)
assert state is not None
assert state.state == state_ison_group_new[0]
assert group.is_on(hass, test_group.entity_id) == state_ison_group_new[1]
async def test_reloading_groups(hass: HomeAssistant) -> None:
"""Test reloading the group config."""
assert await async_setup_component(
hass,
"group",
{
"group": {
"second_group": {"entities": "light.Bowl", "icon": "mdi:work"},
"test_group": "hello.world,sensor.happy",
"empty_group": {"name": "Empty Group", "entities": None},
}
},
)
await hass.async_block_till_done()
await group.Group.async_create_group(
hass,
"all tests",
created_by_service=True,
entity_ids=["test.one", "test.two"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await hass.async_block_till_done()
assert sorted(hass.states.async_entity_ids()) == [
"group.all_tests",
"group.empty_group",
"group.second_group",
"group.test_group",
]
assert hass.bus.async_listeners()["state_changed"] == 1
with patch(
"homeassistant.config.load_yaml_config_file",
return_value={
"group": {"hello": {"entities": "light.Bowl", "icon": "mdi:work"}}
},
):
await hass.services.async_call(group.DOMAIN, SERVICE_RELOAD)
await hass.async_block_till_done()
assert sorted(hass.states.async_entity_ids()) == [
"group.all_tests",
"group.hello",
]
assert hass.bus.async_listeners()["state_changed"] == 1
async def test_modify_group(hass: HomeAssistant) -> None:
"""Test modifying a group."""
group_conf = OrderedDict()
group_conf["modify_group"] = {
"name": "friendly_name",
"icon": "mdi:work",
"entities": None,
}
assert await async_setup_component(hass, "group", {"group": group_conf})
await hass.async_block_till_done()
assert hass.states.get(f"{group.DOMAIN}.modify_group")
# The old way would create a new group modify_group1 because
# internally it didn't know anything about those created in the config
common.async_set_group(hass, "modify_group", icon="mdi:play")
await hass.async_block_till_done()
group_state = hass.states.get(f"{group.DOMAIN}.modify_group")
assert group_state
assert hass.states.async_entity_ids() == ["group.modify_group"]
assert group_state.attributes.get(ATTR_ICON) == "mdi:play"
assert group_state.attributes.get(ATTR_FRIENDLY_NAME) == "friendly_name"
async def test_setup(hass: HomeAssistant) -> None:
"""Test setup method."""
hass.states.async_set("light.Bowl", STATE_ON)
hass.states.async_set("light.Ceiling", STATE_OFF)
group_conf = OrderedDict()
group_conf["test_group"] = "hello.world,sensor.happy"
group_conf["empty_group"] = {"name": "Empty Group", "entities": None}
assert await async_setup_component(hass, "light", {})
await hass.async_block_till_done()
assert await async_setup_component(hass, "group", {"group": group_conf})
await hass.async_block_till_done()
test_group = await group.Group.async_create_group(
hass,
"init_group",
created_by_service=True,
entity_ids=["light.Bowl", "light.Ceiling"],
icon=None,
mode=None,
object_id=None,
order=None,
)
await group.Group.async_create_group(
hass,
"created_group",
created_by_service=False,
entity_ids=["light.Bowl", f"{test_group.entity_id}"],
icon="mdi:work",
mode=None,
object_id=None,
order=None,
)
await hass.async_block_till_done()
group_state = hass.states.get(f"{group.DOMAIN}.created_group")
assert group_state.state == STATE_ON
assert {test_group.entity_id, "light.bowl"} == set(
group_state.attributes["entity_id"]
)
assert group_state.attributes.get(group.ATTR_AUTO) is None
assert group_state.attributes.get(ATTR_ICON) == "mdi:work"
assert group_state.attributes.get(group.ATTR_ORDER) == 3
group_state = hass.states.get(f"{group.DOMAIN}.test_group")
assert group_state.state == STATE_UNKNOWN
assert set(group_state.attributes["entity_id"]) == {"sensor.happy", "hello.world"}
assert group_state.attributes.get(group.ATTR_AUTO) is None
assert group_state.attributes.get(ATTR_ICON) is None
assert group_state.attributes.get(group.ATTR_ORDER) == 0
async def test_service_group_services(hass: HomeAssistant) -> None:
"""Check if service are available."""
with assert_setup_component(0, "group"):
await async_setup_component(hass, "group", {"group": {}})
assert hass.services.has_service("group", group.SERVICE_SET)
assert hass.services.has_service("group", group.SERVICE_REMOVE)
async def test_service_group_services_add_remove_entities(hass: HomeAssistant) -> None:
"""Check if we can add and remove entities from group."""
hass.states.async_set("person.one", "Work")
hass.states.async_set("person.two", "Work")
hass.states.async_set("person.three", "home")
assert await async_setup_component(hass, "person", {})
with assert_setup_component(0, "group"):
await async_setup_component(hass, "group", {"group": {}})
await hass.async_block_till_done()
assert hass.services.has_service("group", group.SERVICE_SET)
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{
"object_id": "new_group",
"name": "New Group",
"entities": ["person.one", "person.two"],
},
)
await hass.async_block_till_done()
group_state = hass.states.get("group.new_group")
assert group_state.state == "not_home"
assert group_state.attributes["friendly_name"] == "New Group"
assert list(group_state.attributes["entity_id"]) == ["person.one", "person.two"]
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{
"object_id": "new_group",
"add_entities": "person.three",
},
)
await hass.async_block_till_done()
group_state = hass.states.get("group.new_group")
assert group_state.state == "home"
assert "person.three" in list(group_state.attributes["entity_id"])
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{
"object_id": "new_group",
"remove_entities": "person.one",
},
)
await hass.async_block_till_done()
group_state = hass.states.get("group.new_group")
assert group_state.state == "home"
assert "person.one" not in list(group_state.attributes["entity_id"])
async def test_service_group_set_group_remove_group(hass: HomeAssistant) -> None:
"""Check if service are available."""
with assert_setup_component(0, "group"):
await async_setup_component(hass, "group", {"group": {}})
common.async_set_group(hass, "user_test_group", name="Test")
await hass.async_block_till_done()
group_state = hass.states.get("group.user_test_group")
assert group_state
assert group_state.attributes[group.ATTR_AUTO]
assert group_state.attributes["friendly_name"] == "Test"
common.async_set_group(hass, "user_test_group", entity_ids=["test.entity_bla1"])
await hass.async_block_till_done()
group_state = hass.states.get("group.user_test_group")
assert group_state
assert group_state.attributes[group.ATTR_AUTO]
assert group_state.attributes["friendly_name"] == "Test"
assert list(group_state.attributes["entity_id"]) == ["test.entity_bla1"]
common.async_set_group(
hass,
"user_test_group",
icon="mdi:camera",
name="Test2",
add=["test.entity_id2"],
)
await hass.async_block_till_done()
group_state = hass.states.get("group.user_test_group")
assert group_state
assert group_state.attributes[group.ATTR_AUTO]
assert group_state.attributes["friendly_name"] == "Test2"
assert group_state.attributes["icon"] == "mdi:camera"
assert sorted(group_state.attributes["entity_id"]) == sorted(
["test.entity_bla1", "test.entity_id2"]
)
common.async_remove(hass, "user_test_group")
await hass.async_block_till_done()
group_state = hass.states.get("group.user_test_group")
assert group_state is None
async def test_group_order(hass: HomeAssistant) -> None:
"""Test that order gets incremented when creating a new group."""
hass.states.async_set("light.bowl", STATE_ON)
assert await async_setup_component(hass, "light", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "light.Bowl", "icon": "mdi:work"},
"group_one": {"entities": "light.Bowl", "icon": "mdi:work"},
"group_two": {"entities": "light.Bowl", "icon": "mdi:work"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").attributes["order"] == 0
assert hass.states.get("group.group_one").attributes["order"] == 1
assert hass.states.get("group.group_two").attributes["order"] == 2
async def test_group_order_with_dynamic_creation(hass: HomeAssistant) -> None:
"""Test that order gets incremented when creating a new group."""
hass.states.async_set("light.bowl", STATE_ON)
assert await async_setup_component(hass, "light", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "light.Bowl", "icon": "mdi:work"},
"group_one": {"entities": "light.Bowl", "icon": "mdi:work"},
"group_two": {"entities": "light.Bowl", "icon": "mdi:work"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").attributes["order"] == 0
assert hass.states.get("group.group_one").attributes["order"] == 1
assert hass.states.get("group.group_two").attributes["order"] == 2
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{"object_id": "new_group", "name": "New Group", "entities": "light.bowl"},
)
await hass.async_block_till_done()
assert hass.states.get("group.new_group").attributes["order"] == 3
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_REMOVE,
{
"object_id": "new_group",
},
)
await hass.async_block_till_done()
assert not hass.states.get("group.new_group")
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{"object_id": "new_group2", "name": "New Group 2", "entities": "light.bowl"},
)
await hass.async_block_till_done()
assert hass.states.get("group.new_group2").attributes["order"] == 4
async def test_group_persons(hass: HomeAssistant) -> None:
"""Test group of persons."""
hass.states.async_set("person.one", "Work")
hass.states.async_set("person.two", "Work")
hass.states.async_set("person.three", "home")
assert await async_setup_component(hass, "person", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "person.one, person.two, person.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "home"
async def test_group_persons_and_device_trackers(hass: HomeAssistant) -> None:
"""Test group of persons and device_tracker."""
hass.states.async_set("person.one", "Work")
hass.states.async_set("person.two", "Work")
hass.states.async_set("person.three", "Work")
hass.states.async_set("device_tracker.one", "home")
assert await async_setup_component(hass, "person", {})
assert await async_setup_component(hass, "device_tracker", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {
"entities": "device_tracker.one, person.one, person.two, person.three"
},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "home"
async def test_group_mixed_domains_on(hass: HomeAssistant) -> None:
"""Test group of mixed domains that is on."""
hass.states.async_set("lock.alexander_garage_exit_door", "unlocked")
hass.states.async_set("binary_sensor.alexander_garage_side_door_open", "on")
hass.states.async_set("cover.small_garage_door", "open")
for domain in ("lock", "binary_sensor", "cover"):
assert await async_setup_component(hass, domain, {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {
"all": "true",
"entities": "lock.alexander_garage_exit_door, binary_sensor.alexander_garage_side_door_open, cover.small_garage_door",
},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "on"
async def test_group_mixed_domains_off(hass: HomeAssistant) -> None:
"""Test group of mixed domains that is off."""
hass.states.async_set("lock.alexander_garage_exit_door", "locked")
hass.states.async_set("binary_sensor.alexander_garage_side_door_open", "off")
hass.states.async_set("cover.small_garage_door", "closed")
for domain in ("lock", "binary_sensor", "cover"):
assert await async_setup_component(hass, domain, {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {
"all": "true",
"entities": "lock.alexander_garage_exit_door, binary_sensor.alexander_garage_side_door_open, cover.small_garage_door",
},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "off"
@pytest.mark.parametrize(
("states", "group_state"),
[
(("locked", "locked", "unlocked"), "unlocked"),
(("locked", "locked", "locked"), "locked"),
(("locked", "locked", "open"), "unlocked"),
(("locked", "unlocked", "open"), "unlocked"),
],
)
async def test_group_locks(hass: HomeAssistant, states, group_state) -> None:
"""Test group of locks."""
hass.states.async_set("lock.one", states[0])
hass.states.async_set("lock.two", states[1])
hass.states.async_set("lock.three", states[2])
assert await async_setup_component(hass, "lock", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "lock.one, lock.two, lock.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == group_state
async def test_group_sensors(hass: HomeAssistant) -> None:
"""Test group of sensors."""
hass.states.async_set("sensor.one", "locked")
hass.states.async_set("sensor.two", "on")
hass.states.async_set("sensor.three", "closed")
assert await async_setup_component(hass, "sensor", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "sensor.one, sensor.two, sensor.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "unknown"
async def test_group_climate_mixed(hass: HomeAssistant) -> None:
"""Test group of climate with mixed states."""
hass.states.async_set("climate.one", "off")
hass.states.async_set("climate.two", "cool")
hass.states.async_set("climate.three", "heat")
assert await async_setup_component(hass, "climate", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "climate.one, climate.two, climate.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_ON
async def test_group_climate_all_cool(hass: HomeAssistant) -> None:
"""Test group of climate all set to cool."""
hass.states.async_set("climate.one", "cool")
hass.states.async_set("climate.two", "cool")
hass.states.async_set("climate.three", "cool")
assert await async_setup_component(hass, "climate", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "climate.one, climate.two, climate.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_ON
async def test_group_climate_all_off(hass: HomeAssistant) -> None:
"""Test group of climate all set to off."""
hass.states.async_set("climate.one", "off")
hass.states.async_set("climate.two", "off")
hass.states.async_set("climate.three", "off")
assert await async_setup_component(hass, "climate", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "climate.one, climate.two, climate.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_OFF
async def test_group_alarm(hass: HomeAssistant) -> None:
"""Test group of alarm control panels."""
hass.states.async_set("alarm_control_panel.one", "armed_away")
hass.states.async_set("alarm_control_panel.two", "armed_home")
hass.states.async_set("alarm_control_panel.three", "armed_away")
hass.set_state(CoreState.stopped)
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {
"entities": "alarm_control_panel.one, alarm_control_panel.two, alarm_control_panel.three"
},
}
},
)
assert await async_setup_component(hass, "alarm_control_panel", {})
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_ON
async def test_group_alarm_disarmed(hass: HomeAssistant) -> None:
"""Test group of alarm control panels disarmed."""
hass.states.async_set("alarm_control_panel.one", "disarmed")
hass.states.async_set("alarm_control_panel.two", "disarmed")
hass.states.async_set("alarm_control_panel.three", "disarmed")
assert await async_setup_component(hass, "alarm_control_panel", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {
"entities": "alarm_control_panel.one, alarm_control_panel.two, alarm_control_panel.three"
},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_OFF
async def test_group_vacuum_off(hass: HomeAssistant) -> None:
"""Test group of vacuums."""
hass.states.async_set("vacuum.one", "docked")
hass.states.async_set("vacuum.two", "off")
hass.states.async_set("vacuum.three", "off")
hass.set_state(CoreState.stopped)
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "vacuum.one, vacuum.two, vacuum.three"},
}
},
)
assert await async_setup_component(hass, "vacuum", {})
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_OFF
async def test_group_vacuum_on(hass: HomeAssistant) -> None:
"""Test group of vacuums."""
hass.states.async_set("vacuum.one", "cleaning")
hass.states.async_set("vacuum.two", "off")
hass.states.async_set("vacuum.three", "off")
assert await async_setup_component(hass, "vacuum", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "vacuum.one, vacuum.two, vacuum.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == STATE_ON
@pytest.mark.parametrize(
("entity_state_list", "group_state"),
[
(
{
"device_tracker.one": "not_home",
"device_tracker.two": "not_home",
"device_tracker.three": "not_home",
},
"not_home",
),
(
{
"device_tracker.one": "home",
"device_tracker.two": "not_home",
"device_tracker.three": "not_home",
},
"home",
),
(
{
"device_tracker.one": "home",
"device_tracker.two": "elsewhere",
"device_tracker.three": "not_home",
},
"home",
),
(
{
"device_tracker.one": "not_home",
"device_tracker.two": "elsewhere",
"device_tracker.three": "not_home",
},
"not_home",
),
],
)
async def test_device_tracker_or_person_not_home(
hass: HomeAssistant,
entity_state_list: dict[str, str],
group_state: str,
) -> None:
"""Test group of device_tracker not_home."""
await async_setup_component(hass, "device_tracker", {})
await async_setup_component(hass, "person", {})
await hass.async_block_till_done()
for entity_id, state in entity_state_list.items():
hass.states.async_set(entity_id, state)
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": ", ".join(entity_state_list)},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == group_state
async def test_light_removed(hass: HomeAssistant) -> None:
"""Test group of lights when one is removed."""
hass.states.async_set("light.one", "off")
hass.states.async_set("light.two", "off")
hass.states.async_set("light.three", "on")
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "light.one, light.two, light.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "on"
hass.states.async_remove("light.three")
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "off"
async def test_switch_removed(hass: HomeAssistant) -> None:
"""Test group of switches when one is removed."""
hass.states.async_set("switch.one", "off")
hass.states.async_set("switch.two", "off")
hass.states.async_set("switch.three", "on")
hass.set_state(CoreState.stopped)
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "switch.one, switch.two, switch.three"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "unknown"
assert await async_setup_component(hass, "switch", {})
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "on"
hass.states.async_remove("switch.three")
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").state == "off"
async def test_lights_added_after_group(hass: HomeAssistant) -> None:
"""Test lights added after group."""
entity_ids = [
"light.living_front_ri",
"light.living_back_lef",
"light.living_back_cen",
"light.living_front_le",
"light.living_front_ce",
"light.living_back_rig",
]
assert await async_setup_component(
hass,
"group",
{
"group": {
"living_room_downlights": {"entities": entity_ids},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.living_room_downlights").state == "unknown"
for entity_id in entity_ids:
hass.states.async_set(entity_id, "off")
await hass.async_block_till_done()
assert hass.states.get("group.living_room_downlights").state == "off"
async def test_lights_added_before_group(hass: HomeAssistant) -> None:
"""Test lights added before group."""
entity_ids = [
"light.living_front_ri",
"light.living_back_lef",
"light.living_back_cen",
"light.living_front_le",
"light.living_front_ce",
"light.living_back_rig",
]
for entity_id in entity_ids:
hass.states.async_set(entity_id, "off")
await hass.async_block_till_done()
assert await async_setup_component(
hass,
"group",
{
"group": {
"living_room_downlights": {"entities": entity_ids},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.living_room_downlights").state == "off"
async def test_cover_added_after_group(hass: HomeAssistant) -> None:
"""Test cover added after group."""
entity_ids = [
"cover.upstairs",
"cover.downstairs",
]
assert await async_setup_component(hass, "cover", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"shades": {"entities": entity_ids},
}
},
)
await hass.async_block_till_done()
for entity_id in entity_ids:
hass.states.async_set(entity_id, "open")
await hass.async_block_till_done()
await hass.async_block_till_done()
assert hass.states.get("group.shades").state == "open"
for entity_id in entity_ids:
hass.states.async_set(entity_id, "closed")
await hass.async_block_till_done()
assert hass.states.get("group.shades").state == "closed"
async def test_group_that_references_a_group_of_lights(hass: HomeAssistant) -> None:
"""Group that references a group of lights."""
entity_ids = [
"light.living_front_ri",
"light.living_back_lef",
]
hass.set_state(CoreState.stopped)
for entity_id in entity_ids:
hass.states.async_set(entity_id, "off")
await hass.async_block_till_done()
assert await async_setup_component(
hass,
"group",
{
"group": {
"living_room_downlights": {"entities": entity_ids},
"grouped_group": {
"entities": ["group.living_room_downlights", *entity_ids]
},
}
},
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.living_room_downlights").state == "off"
assert hass.states.get("group.grouped_group").state == "off"
async def test_group_that_references_a_group_of_covers(hass: HomeAssistant) -> None:
"""Group that references a group of covers."""
entity_ids = [
"cover.living_front_ri",
"cover.living_back_lef",
]
hass.set_state(CoreState.stopped)
for entity_id in entity_ids:
hass.states.async_set(entity_id, "closed")
await hass.async_block_till_done()
assert await async_setup_component(hass, "cover", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"living_room_downcover": {"entities": entity_ids},
"grouped_group": {
"entities": ["group.living_room_downlights", *entity_ids]
},
}
},
)
assert await async_setup_component(hass, "cover", {})
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.living_room_downcover").state == "closed"
assert hass.states.get("group.grouped_group").state == "closed"
async def test_group_that_references_two_groups_of_covers(hass: HomeAssistant) -> None:
"""Group that references a group of covers."""
entity_ids = [
"cover.living_front_ri",
"cover.living_back_lef",
]
hass.set_state(CoreState.stopped)
for entity_id in entity_ids:
hass.states.async_set(entity_id, "closed")
await hass.async_block_till_done()
assert await async_setup_component(hass, "cover", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"living_room_downcover": {"entities": entity_ids},
"living_room_upcover": {"entities": entity_ids},
"grouped_group": {
"entities": [
"group.living_room_downlights",
"group.living_room_upcover",
]
},
}
},
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.living_room_downcover").state == "closed"
assert hass.states.get("group.living_room_upcover").state == "closed"
assert hass.states.get("group.grouped_group").state == "closed"
async def test_group_that_references_two_types_of_groups(hass: HomeAssistant) -> None:
"""Group that references a group of covers and device_trackers."""
group_1_entity_ids = [
"cover.living_front_ri",
"cover.living_back_lef",
]
group_2_entity_ids = [
"device_tracker.living_front_ri",
"device_tracker.living_back_lef",
]
hass.set_state(CoreState.stopped)
for entity_id in group_1_entity_ids:
hass.states.async_set(entity_id, "closed")
for entity_id in group_2_entity_ids:
hass.states.async_set(entity_id, "home")
await hass.async_block_till_done()
assert await async_setup_component(hass, "cover", {})
assert await async_setup_component(hass, "device_tracker", {})
assert await async_setup_component(
hass,
"group",
{
"group": {
"covers": {"entities": group_1_entity_ids},
"device_trackers": {"entities": group_2_entity_ids},
"grouped_group": {
"entities": ["group.covers", "group.device_trackers"]
},
}
},
)
assert await async_setup_component(hass, "cover", {})
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
assert hass.states.get("group.covers").state == "closed"
assert hass.states.get("group.device_trackers").state == "home"
assert hass.states.get("group.grouped_group").state == "on"
async def test_plant_group(hass: HomeAssistant) -> None:
"""Test plant states can be grouped."""
entity_ids = [
"plant.upstairs",
"plant.downstairs",
]
assert await async_setup_component(
hass,
"plant",
{
"plant": {
"plantname": {
"sensors": {
"moisture": "sensor.mqtt_plant_moisture",
"battery": "sensor.mqtt_plant_battery",
"temperature": "sensor.mqtt_plant_temperature",
"conductivity": "sensor.mqtt_plant_conductivity",
"brightness": "sensor.mqtt_plant_brightness",
},
"min_moisture": 20,
"max_moisture": 60,
"min_battery": 17,
"min_conductivity": 500,
"min_temperature": 15,
"min_brightness": 500,
}
}
},
)
assert await async_setup_component(
hass,
"group",
{
"group": {
"plants": {"entities": entity_ids},
"plant_with_binary_sensors": {
"entities": [*entity_ids, "binary_sensor.planter"]
},
}
},
)
await hass.async_block_till_done()
hass.states.async_set("binary_sensor.planter", "off")
for entity_id in entity_ids:
hass.states.async_set(entity_id, "ok")
await hass.async_block_till_done()
await hass.async_block_till_done()
assert hass.states.get("group.plants").state == "ok"
assert hass.states.get("group.plant_with_binary_sensors").state == "off"
hass.states.async_set("binary_sensor.planter", "on")
for entity_id in entity_ids:
hass.states.async_set(entity_id, "problem")
await hass.async_block_till_done()
assert hass.states.get("group.plants").state == "problem"
assert hass.states.get("group.plant_with_binary_sensors").state == "on"
@pytest.mark.parametrize(
("group_type", "member_state", "extra_options"),
[
("binary_sensor", "on", {"all": False}),
("cover", "open", {}),
("fan", "on", {}),
("light", "on", {"all": False}),
("media_player", "on", {}),
(
"sensor",
"1",
{
"all": True,
"type": "max",
"round_digits": 2.0,
"state_class": "measurement",
},
),
],
)
async def test_setup_and_remove_config_entry(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
group_type: str,
member_state: str,
extra_options: dict[str, Any],
) -> None:
"""Test removing a config entry."""
members1 = [f"{group_type}.one", f"{group_type}.two"]
for member in members1:
hass.states.async_set(member, member_state, {})
# Setup the config entry
group_config_entry = MockConfigEntry(
data={},
domain=group.DOMAIN,
options={
"entities": members1,
"group_type": group_type,
"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 and entity registry entry are present
state = hass.states.get(f"{group_type}.bed_room")
assert state.attributes["entity_id"] == members1
assert entity_registry.async_get(f"{group_type}.bed_room") is not None
# Remove the config entry
assert await hass.config_entries.async_remove(group_config_entry.entry_id)
await hass.async_block_till_done()
# Check the state and entity registry entry are removed
assert hass.states.get(f"{group_type}.bed_room") is None
assert entity_registry.async_get(f"{group_type}.bed_room") is None
@pytest.mark.parametrize(
("hide_members", "hidden_by_initial", "hidden_by"),
[
(False, er.RegistryEntryHider.INTEGRATION, er.RegistryEntryHider.INTEGRATION),
(False, None, None),
(False, er.RegistryEntryHider.USER, er.RegistryEntryHider.USER),
(True, er.RegistryEntryHider.INTEGRATION, None),
(True, None, None),
(True, er.RegistryEntryHider.USER, er.RegistryEntryHider.USER),
],
)
@pytest.mark.parametrize(
("group_type", "extra_options"),
[
("binary_sensor", {"all": False}),
("cover", {}),
("fan", {}),
("light", {"all": False}),
("media_player", {}),
],
)
async def test_unhide_members_on_remove(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
group_type: str,
extra_options: dict[str, Any],
hide_members: bool,
hidden_by_initial: er.RegistryEntryHider,
hidden_by: str,
) -> None:
"""Test removing a config entry."""
entry1 = entity_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 = entity_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 = entity_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
entity_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 entity_registry.async_get(f"{group_type}.one").hidden_by == hidden_by
assert entity_registry.async_get(f"{group_type}.three").hidden_by == hidden_by
@pytest.mark.parametrize("grouped_groups", [False, True])
@pytest.mark.parametrize(
("on_off_states1", "on_off_states2"),
[
(
(
{
"on_beer",
"on_milk",
},
"on_beer", # default ON state test1
"off_water", # default OFF state test1
),
(
{
"on_beer",
"on_milk",
},
"on_milk", # default ON state test2
"off_wine", # default OFF state test2
),
),
],
)
@pytest.mark.parametrize(
("entity_and_state1_state_2", "group_state1", "group_state2"),
[
# All OFF states, no change, so group stays OFF
(
[
("test1.ent1", "off_water", "off_water"),
("test1.ent2", "off_water", "off_water"),
("test2.ent1", "off_wine", "off_wine"),
("test2.ent2", "off_wine", "off_wine"),
],
STATE_OFF,
STATE_OFF,
),
# All entities have state on_milk, but the state groups
# are different so the group status defaults to ON / OFF
(
[
("test1.ent1", "off_water", "on_milk"),
("test1.ent2", "off_water", "on_milk"),
("test2.ent1", "off_wine", "on_milk"),
("test2.ent2", "off_wine", "on_milk"),
],
STATE_OFF,
STATE_ON,
),
# Only test1 entities in group, all at ON state
# group returns the default ON state `on_beer`
(
[
("test1.ent1", "off_water", "on_milk"),
("test1.ent2", "off_water", "on_beer"),
],
"off_water",
"on_beer",
),
# Only test1 entities in group, all at ON state
# group returns the default ON state `on_beer`
(
[
("test1.ent1", "off_water", "on_milk"),
("test1.ent2", "off_water", "on_milk"),
],
"off_water",
"on_beer",
),
# Only test2 entities in group, all at ON state
# group returns the default ON state `on_milk`
(
[
("test2.ent1", "off_wine", "on_milk"),
("test2.ent2", "off_wine", "on_milk"),
],
"off_wine",
"on_milk",
),
],
)
async def test_entity_platforms_with_multiple_on_states_no_state_match(
hass: HomeAssistant,
on_off_states1: tuple[set[str], str, str],
on_off_states2: tuple[set[str], str, str],
entity_and_state1_state_2: tuple[str, str | None, str | None],
group_state1: str,
group_state2: str,
grouped_groups: bool,
) -> None:
"""Test custom entity platforms with multiple ON states without state match.
The test group 1 an 2 non matching (default_state_on, state_off) pairs.
"""
await help_test_mixed_entity_platforms_on_off_state_test(
hass,
on_off_states1,
on_off_states2,
entity_and_state1_state_2,
group_state1,
group_state2,
grouped_groups,
)
@pytest.mark.parametrize("grouped_groups", [False, True])
@pytest.mark.parametrize(
("on_off_states1", "on_off_states2"),
[
(
(
{
"on_beer",
"on_milk",
},
"on_beer", # default ON state test1
"off_water", # default OFF state test1
),
(
{
"on_beer",
"on_wine",
},
"on_beer", # default ON state test2
"off_water", # default OFF state test2
),
),
],
)
@pytest.mark.parametrize(
("entity_and_state1_state_2", "group_state1", "group_state2"),
[
# All OFF states, no change, so group stays OFF
(
[
("test1.ent1", "off_water", "off_water"),
("test1.ent2", "off_water", "off_water"),
("test2.ent1", "off_water", "off_water"),
("test2.ent2", "off_water", "off_water"),
],
"off_water",
"off_water",
),
# All entities have ON state `on_milk`
# but the group state will default to on_beer
# which is the default ON state for both integrations.
(
[
("test1.ent1", "off_water", "on_milk"),
("test1.ent2", "off_water", "on_milk"),
("test2.ent1", "off_water", "on_milk"),
("test2.ent2", "off_water", "on_milk"),
],
"off_water",
"on_beer",
),
# Only test1 entities in group, all at ON state
# group returns the default ON state `on_beer`
(
[
("test1.ent1", "off_water", "on_milk"),
("test1.ent2", "off_water", "on_beer"),
],
"off_water",
"on_beer",
),
# Only test1 entities in group, all at ON state
# group returns the default ON state `on_beer`
(
[
("test1.ent1", "off_water", "on_milk"),
("test1.ent2", "off_water", "on_milk"),
],
"off_water",
"on_beer",
),
# Only test2 entities in group, all at ON state
# group returns the default ON state `on_milk`
(
[
("test2.ent1", "off_water", "on_wine"),
("test2.ent2", "off_water", "on_wine"),
],
"off_water",
"on_beer",
),
],
)
async def test_entity_platforms_with_multiple_on_states_with_state_match(
hass: HomeAssistant,
on_off_states1: tuple[set[str], str, str],
on_off_states2: tuple[set[str], str, str],
entity_and_state1_state_2: tuple[str, str | None, str | None],
group_state1: str,
group_state2: str,
grouped_groups: bool,
) -> None:
"""Test custom entity platforms with multiple ON states with a state match.
The integrations test1 and test2 have matching (default_state_on, state_off) pairs.
"""
await help_test_mixed_entity_platforms_on_off_state_test(
hass,
on_off_states1,
on_off_states2,
entity_and_state1_state_2,
group_state1,
group_state2,
grouped_groups,
)