core/tests/components/group/test_init.py

550 lines
20 KiB
Python

"""The tests for the Group components."""
# pylint: disable=protected-access
from collections import OrderedDict
import unittest
from unittest.mock import patch
import homeassistant.components.group as group
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_FRIENDLY_NAME,
ATTR_HIDDEN,
ATTR_ICON,
STATE_HOME,
STATE_NOT_HOME,
STATE_OFF,
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.setup import async_setup_component, setup_component
from tests.common import assert_setup_component, get_test_home_assistant
from tests.components.group import common
class TestComponentsGroup(unittest.TestCase):
"""Test Group component."""
# pylint: disable=invalid-name
def setUp(self):
"""Set up things to be run when tests are started."""
self.hass = get_test_home_assistant()
# pylint: disable=invalid-name
def tearDown(self):
"""Stop everything that was started."""
self.hass.stop()
def test_setup_group_with_mixed_groupable_states(self):
"""Try to set up a group with mixed groupable states."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("device_tracker.Paulus", STATE_HOME)
group.Group.create_group(
self.hass, "person_and_light", ["light.Bowl", "device_tracker.Paulus"]
)
assert (
STATE_ON
== self.hass.states.get(
group.ENTITY_ID_FORMAT.format("person_and_light")
).state
)
def test_setup_group_with_a_non_existing_state(self):
"""Try to set up a group with a non existing state."""
self.hass.states.set("light.Bowl", STATE_ON)
grp = group.Group.create_group(
self.hass, "light_and_nothing", ["light.Bowl", "non.existing"]
)
assert STATE_ON == grp.state
def test_setup_group_with_non_groupable_states(self):
"""Test setup with groups which are not groupable."""
self.hass.states.set("cast.living_room", "Plex")
self.hass.states.set("cast.bedroom", "Netflix")
grp = group.Group.create_group(
self.hass, "chromecasts", ["cast.living_room", "cast.bedroom"]
)
assert STATE_UNKNOWN == grp.state
def test_setup_empty_group(self):
"""Try to set up an empty group."""
grp = group.Group.create_group(self.hass, "nothing", [])
assert STATE_UNKNOWN == grp.state
def test_monitor_group(self):
"""Test if the group keeps track of states."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
# Test if group setup in our init mode is ok
assert test_group.entity_id in self.hass.states.entity_ids()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_ON == group_state.state
assert group_state.attributes.get(group.ATTR_AUTO)
def test_group_turns_off_if_all_off(self):
"""Test if turn off if the last device that was on turns off."""
self.hass.states.set("light.Bowl", STATE_OFF)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
self.hass.block_till_done()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_OFF == group_state.state
def test_group_turns_on_if_all_are_off_and_one_turns_on(self):
"""Test if turn on if all devices were turned off and one turns on."""
self.hass.states.set("light.Bowl", STATE_OFF)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
# Turn one on
self.hass.states.set("light.Ceiling", STATE_ON)
self.hass.block_till_done()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_ON == group_state.state
def test_allgroup_stays_off_if_all_are_off_and_one_turns_on(self):
"""Group with all: true, stay off if one device turns on."""
self.hass.states.set("light.Bowl", STATE_OFF)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False, mode=True
)
# Turn one on
self.hass.states.set("light.Ceiling", STATE_ON)
self.hass.block_till_done()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_OFF == group_state.state
def test_allgroup_turn_on_if_last_turns_on(self):
"""Group with all: true, turn on if all devices are on."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False, mode=True
)
# Turn one on
self.hass.states.set("light.Ceiling", STATE_ON)
self.hass.block_till_done()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_ON == group_state.state
def test_is_on(self):
"""Test is_on method."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
assert group.is_on(self.hass, test_group.entity_id)
self.hass.states.set("light.Bowl", STATE_OFF)
self.hass.block_till_done()
assert not group.is_on(self.hass, test_group.entity_id)
# Try on non existing state
assert not group.is_on(self.hass, "non.existing")
def test_expand_entity_ids(self):
"""Test expand_entity_ids method."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
assert sorted(["light.ceiling", "light.bowl"]) == sorted(
group.expand_entity_ids(self.hass, [test_group.entity_id])
)
def test_expand_entity_ids_does_not_return_duplicates(self):
"""Test that expand_entity_ids does not return duplicates."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
assert ["light.bowl", "light.ceiling"] == sorted(
group.expand_entity_ids(self.hass, [test_group.entity_id, "light.Ceiling"])
)
assert ["light.bowl", "light.ceiling"] == sorted(
group.expand_entity_ids(self.hass, ["light.bowl", test_group.entity_id])
)
def test_expand_entity_ids_recursive(self):
"""Test expand_entity_ids method with a group that contains itself."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass,
"init_group",
["light.Bowl", "light.Ceiling", "group.init_group"],
False,
)
assert sorted(["light.ceiling", "light.bowl"]) == sorted(
group.expand_entity_ids(self.hass, [test_group.entity_id])
)
def test_expand_entity_ids_ignores_non_strings(self):
"""Test that non string elements in lists are ignored."""
assert [] == group.expand_entity_ids(self.hass, [5, True])
def test_get_entity_ids(self):
"""Test get_entity_ids method."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
assert ["light.bowl", "light.ceiling"] == sorted(
group.get_entity_ids(self.hass, test_group.entity_id)
)
def test_get_entity_ids_with_domain_filter(self):
"""Test if get_entity_ids works with a domain_filter."""
self.hass.states.set("switch.AC", STATE_OFF)
mixed_group = group.Group.create_group(
self.hass, "mixed_group", ["light.Bowl", "switch.AC"], False
)
assert ["switch.ac"] == group.get_entity_ids(
self.hass, mixed_group.entity_id, domain_filter="switch"
)
def test_get_entity_ids_with_non_existing_group_name(self):
"""Test get_entity_ids with a non existing group."""
assert [] == group.get_entity_ids(self.hass, "non_existing")
def test_get_entity_ids_with_non_group_state(self):
"""Test get_entity_ids with a non group state."""
assert [] == group.get_entity_ids(self.hass, "switch.AC")
def test_group_being_init_before_first_tracked_state_is_set_to_on(self):
"""Test if the groups turn on.
If no states existed and now a state it is tracking is being added
as ON.
"""
test_group = group.Group.create_group(
self.hass, "test group", ["light.not_there_1"]
)
self.hass.states.set("light.not_there_1", STATE_ON)
self.hass.block_till_done()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_ON == group_state.state
def test_group_being_init_before_first_tracked_state_is_set_to_off(self):
"""Test if the group turns off.
If no states existed and now a state it is tracking is being added
as OFF.
"""
test_group = group.Group.create_group(
self.hass, "test group", ["light.not_there_1"]
)
self.hass.states.set("light.not_there_1", STATE_OFF)
self.hass.block_till_done()
group_state = self.hass.states.get(test_group.entity_id)
assert STATE_OFF == group_state.state
def test_setup(self):
"""Test setup method."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling"], False
)
group_conf = OrderedDict()
group_conf["second_group"] = {
"entities": "light.Bowl, " + test_group.entity_id,
"icon": "mdi:work",
"view": True,
"control": "hidden",
}
group_conf["test_group"] = "hello.world,sensor.happy"
group_conf["empty_group"] = {"name": "Empty Group", "entities": None}
setup_component(self.hass, "group", {"group": group_conf})
group_state = self.hass.states.get(
group.ENTITY_ID_FORMAT.format("second_group")
)
assert STATE_ON == group_state.state
assert set((test_group.entity_id, "light.bowl")) == set(
group_state.attributes["entity_id"]
)
assert group_state.attributes.get(group.ATTR_AUTO) is None
assert "mdi:work" == group_state.attributes.get(ATTR_ICON)
assert group_state.attributes.get(group.ATTR_VIEW)
assert "hidden" == group_state.attributes.get(group.ATTR_CONTROL)
assert group_state.attributes.get(ATTR_HIDDEN)
assert 1 == group_state.attributes.get(group.ATTR_ORDER)
group_state = self.hass.states.get(group.ENTITY_ID_FORMAT.format("test_group"))
assert STATE_UNKNOWN == group_state.state
assert set(("sensor.happy", "hello.world")) == set(
group_state.attributes["entity_id"]
)
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_VIEW) is None
assert group_state.attributes.get(group.ATTR_CONTROL) is None
assert group_state.attributes.get(ATTR_HIDDEN) is None
assert 2 == group_state.attributes.get(group.ATTR_ORDER)
def test_groups_get_unique_names(self):
"""Two groups with same name should both have a unique entity id."""
grp1 = group.Group.create_group(self.hass, "Je suis Charlie")
grp2 = group.Group.create_group(self.hass, "Je suis Charlie")
assert grp1.entity_id != grp2.entity_id
def test_expand_entity_ids_expands_nested_groups(self):
"""Test if entity ids epands to nested groups."""
group.Group.create_group(self.hass, "light", ["light.test_1", "light.test_2"])
group.Group.create_group(
self.hass, "switch", ["switch.test_1", "switch.test_2"]
)
group.Group.create_group(
self.hass, "group_of_groups", ["group.light", "group.switch"]
)
assert [
"light.test_1",
"light.test_2",
"switch.test_1",
"switch.test_2",
] == sorted(group.expand_entity_ids(self.hass, ["group.group_of_groups"]))
def test_set_assumed_state_based_on_tracked(self):
"""Test assumed state."""
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.states.set("light.Ceiling", STATE_OFF)
test_group = group.Group.create_group(
self.hass, "init_group", ["light.Bowl", "light.Ceiling", "sensor.no_exist"]
)
state = self.hass.states.get(test_group.entity_id)
assert not state.attributes.get(ATTR_ASSUMED_STATE)
self.hass.states.set("light.Bowl", STATE_ON, {ATTR_ASSUMED_STATE: True})
self.hass.block_till_done()
state = self.hass.states.get(test_group.entity_id)
assert state.attributes.get(ATTR_ASSUMED_STATE)
self.hass.states.set("light.Bowl", STATE_ON)
self.hass.block_till_done()
state = self.hass.states.get(test_group.entity_id)
assert not state.attributes.get(ATTR_ASSUMED_STATE)
def test_group_updated_after_device_tracker_zone_change(self):
"""Test group state when device tracker in group changes zone."""
self.hass.states.set("device_tracker.Adam", STATE_HOME)
self.hass.states.set("device_tracker.Eve", STATE_NOT_HOME)
self.hass.block_till_done()
group.Group.create_group(
self.hass, "peeps", ["device_tracker.Adam", "device_tracker.Eve"]
)
self.hass.states.set("device_tracker.Adam", "cool_state_not_home")
self.hass.block_till_done()
assert (
STATE_NOT_HOME
== self.hass.states.get(group.ENTITY_ID_FORMAT.format("peeps")).state
)
def test_reloading_groups(self):
"""Test reloading the group config."""
assert setup_component(
self.hass,
"group",
{
"group": {
"second_group": {
"entities": "light.Bowl",
"icon": "mdi:work",
"view": True,
},
"test_group": "hello.world,sensor.happy",
"empty_group": {"name": "Empty Group", "entities": None},
}
},
)
group.Group.create_group(
self.hass, "all tests", ["test.one", "test.two"], user_defined=False
)
assert sorted(self.hass.states.entity_ids()) == [
"group.all_tests",
"group.empty_group",
"group.second_group",
"group.test_group",
]
assert self.hass.bus.listeners["state_changed"] == 3
with patch(
"homeassistant.config.load_yaml_config_file",
return_value={
"group": {
"hello": {
"entities": "light.Bowl",
"icon": "mdi:work",
"view": True,
}
}
},
):
with patch("homeassistant.config.find_config_file", return_value=""):
common.reload(self.hass)
self.hass.block_till_done()
assert sorted(self.hass.states.entity_ids()) == [
"group.all_tests",
"group.hello",
]
assert self.hass.bus.listeners["state_changed"] == 2
def test_changing_group_visibility(self):
"""Test that a group can be hidden and shown."""
assert setup_component(
self.hass, "group", {"group": {"test_group": "hello.world,sensor.happy"}}
)
group_entity_id = group.ENTITY_ID_FORMAT.format("test_group")
# Hide the group
common.set_visibility(self.hass, group_entity_id, False)
self.hass.block_till_done()
group_state = self.hass.states.get(group_entity_id)
assert group_state.attributes.get(ATTR_HIDDEN)
# Show it again
common.set_visibility(self.hass, group_entity_id, True)
self.hass.block_till_done()
group_state = self.hass.states.get(group_entity_id)
assert group_state.attributes.get(ATTR_HIDDEN) is None
def test_modify_group(self):
"""Test modifying a group."""
group_conf = OrderedDict()
group_conf["modify_group"] = {"name": "friendly_name", "icon": "mdi:work"}
assert setup_component(self.hass, "group", {"group": group_conf})
# The old way would create a new group modify_group1 because
# internally it didn't know anything about those created in the config
common.set_group(self.hass, "modify_group", icon="mdi:play")
self.hass.block_till_done()
group_state = self.hass.states.get(
group.ENTITY_ID_FORMAT.format("modify_group")
)
assert self.hass.states.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_service_group_services(hass):
"""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)
# pylint: disable=invalid-name
async def test_service_group_set_group_remove_group(hass):
"""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",
view=True,
visible=False,
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_VIEW]
assert group_state.attributes[group.ATTR_AUTO]
assert group_state.attributes["hidden"]
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",
control="hidden",
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_VIEW]
assert group_state.attributes[group.ATTR_AUTO]
assert group_state.attributes["hidden"]
assert group_state.attributes["friendly_name"] == "Test2"
assert group_state.attributes["icon"] == "mdi:camera"
assert group_state.attributes[group.ATTR_CONTROL] == "hidden"
assert sorted(list(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