core/tests/components/group/test_fan.py

590 lines
21 KiB
Python

"""The tests for the group fan platform."""
import asyncio
from typing import Any
from unittest.mock import patch
import pytest
from homeassistant import config as hass_config
from homeassistant.components.fan import (
ATTR_DIRECTION,
ATTR_OSCILLATING,
ATTR_PERCENTAGE,
ATTR_PERCENTAGE_STEP,
DIRECTION_FORWARD,
DIRECTION_REVERSE,
DOMAIN as FAN_DOMAIN,
SERVICE_OSCILLATE,
SERVICE_SET_DIRECTION,
SERVICE_SET_PERCENTAGE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
FanEntityFeature,
)
from homeassistant.components.group import SERVICE_RELOAD
from homeassistant.components.group.fan import DEFAULT_NAME
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
ATTR_SUPPORTED_FEATURES,
CONF_ENTITIES,
CONF_UNIQUE_ID,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from tests.common import assert_setup_component, get_fixture_path
FAN_GROUP = "fan.fan_group"
MISSING_FAN_ENTITY_ID = "fan.missing"
LIVING_ROOM_FAN_ENTITY_ID = "fan.living_room_fan"
PERCENTAGE_FULL_FAN_ENTITY_ID = "fan.percentage_full_fan"
CEILING_FAN_ENTITY_ID = "fan.ceiling_fan"
PERCENTAGE_LIMITED_FAN_ENTITY_ID = "fan.percentage_limited_fan"
FULL_FAN_ENTITY_IDS = [LIVING_ROOM_FAN_ENTITY_ID, PERCENTAGE_FULL_FAN_ENTITY_ID]
LIMITED_FAN_ENTITY_IDS = [CEILING_FAN_ENTITY_ID, PERCENTAGE_LIMITED_FAN_ENTITY_ID]
FULL_SUPPORT_FEATURES = (
FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION | FanEntityFeature.OSCILLATE
)
CONFIG_MISSING_FAN = {
FAN_DOMAIN: [
{"platform": "demo"},
{
"platform": "group",
CONF_ENTITIES: [
MISSING_FAN_ENTITY_ID,
*FULL_FAN_ENTITY_IDS,
*LIMITED_FAN_ENTITY_IDS,
],
},
]
}
CONFIG_FULL_SUPPORT = {
FAN_DOMAIN: [
{"platform": "demo"},
{
"platform": "group",
CONF_ENTITIES: [*FULL_FAN_ENTITY_IDS],
},
]
}
CONFIG_LIMITED_SUPPORT = {
FAN_DOMAIN: [
{
"platform": "group",
CONF_ENTITIES: [*LIMITED_FAN_ENTITY_IDS],
},
]
}
CONFIG_ATTRIBUTES = {
FAN_DOMAIN: {
"platform": "group",
CONF_ENTITIES: [*FULL_FAN_ENTITY_IDS, *LIMITED_FAN_ENTITY_IDS],
CONF_UNIQUE_ID: "unique_identifier",
}
}
@pytest.fixture
async def setup_comp(
hass: HomeAssistant, config_count: tuple[dict[str, Any], int]
) -> None:
"""Set up group fan component."""
config, count = config_count
with assert_setup_component(count, FAN_DOMAIN):
await async_setup_component(hass, FAN_DOMAIN, config)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
@pytest.mark.parametrize("config_count", [(CONFIG_ATTRIBUTES, 1)])
@pytest.mark.usefixtures("setup_comp")
async def test_state(hass: HomeAssistant, entity_registry: er.EntityRegistry) -> None:
"""Test handling of state.
The group state is on if at least one group member is on.
Otherwise, the group state is off.
"""
state = hass.states.get(FAN_GROUP)
# No entity has a valid state -> group state unavailable
assert state.state == STATE_UNAVAILABLE
assert state.attributes[ATTR_FRIENDLY_NAME] == DEFAULT_NAME
assert ATTR_ENTITY_ID not in state.attributes
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
# Test group members exposed as attribute
hass.states.async_set(CEILING_FAN_ENTITY_ID, STATE_UNKNOWN, {})
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.attributes[ATTR_ENTITY_ID] == [
*FULL_FAN_ENTITY_IDS,
*LIMITED_FAN_ENTITY_IDS,
]
# All group members unavailable -> unavailable
hass.states.async_set(CEILING_FAN_ENTITY_ID, STATE_UNAVAILABLE)
hass.states.async_set(LIVING_ROOM_FAN_ENTITY_ID, STATE_UNAVAILABLE)
hass.states.async_set(PERCENTAGE_FULL_FAN_ENTITY_ID, STATE_UNAVAILABLE)
hass.states.async_set(PERCENTAGE_LIMITED_FAN_ENTITY_ID, STATE_UNAVAILABLE)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_UNAVAILABLE
# The group state is unknown if all group members are unknown or unavailable.
for state_1 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
for state_2 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
for state_3 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
hass.states.async_set(CEILING_FAN_ENTITY_ID, state_1, {})
hass.states.async_set(LIVING_ROOM_FAN_ENTITY_ID, state_2, {})
hass.states.async_set(PERCENTAGE_FULL_FAN_ENTITY_ID, state_3, {})
hass.states.async_set(
PERCENTAGE_LIMITED_FAN_ENTITY_ID, STATE_UNKNOWN, {}
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_UNKNOWN
# The group state is off if all group members are off, unknown or unavailable.
for state_1 in (STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN):
for state_2 in (STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN):
for state_3 in (STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN):
hass.states.async_set(CEILING_FAN_ENTITY_ID, state_1, {})
hass.states.async_set(LIVING_ROOM_FAN_ENTITY_ID, state_2, {})
hass.states.async_set(PERCENTAGE_FULL_FAN_ENTITY_ID, state_3, {})
hass.states.async_set(PERCENTAGE_LIMITED_FAN_ENTITY_ID, STATE_OFF, {})
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_OFF
# At least one member on -> group on
for state_1 in (STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN):
for state_2 in (STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN):
for state_3 in (STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN):
hass.states.async_set(CEILING_FAN_ENTITY_ID, state_1, {})
hass.states.async_set(LIVING_ROOM_FAN_ENTITY_ID, state_2, {})
hass.states.async_set(PERCENTAGE_FULL_FAN_ENTITY_ID, state_3, {})
hass.states.async_set(PERCENTAGE_LIMITED_FAN_ENTITY_ID, STATE_ON, {})
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
# now remove an entity
hass.states.async_remove(PERCENTAGE_LIMITED_FAN_ENTITY_ID)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_UNKNOWN
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
# now remove all entities
hass.states.async_remove(CEILING_FAN_ENTITY_ID)
hass.states.async_remove(LIVING_ROOM_FAN_ENTITY_ID)
hass.states.async_remove(PERCENTAGE_FULL_FAN_ENTITY_ID)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_UNAVAILABLE
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
# Test entity registry integration
entry = entity_registry.async_get(FAN_GROUP)
assert entry
assert entry.unique_id == "unique_identifier"
@pytest.mark.parametrize("config_count", [(CONFIG_ATTRIBUTES, 1)])
@pytest.mark.usefixtures("setup_comp")
async def test_attributes(hass: HomeAssistant) -> None:
"""Test handling of state attributes."""
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_UNAVAILABLE
assert state.attributes[ATTR_FRIENDLY_NAME] == DEFAULT_NAME
assert ATTR_ENTITY_ID not in state.attributes
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
hass.states.async_set(CEILING_FAN_ENTITY_ID, STATE_ON, {})
hass.states.async_set(LIVING_ROOM_FAN_ENTITY_ID, STATE_ON, {})
hass.states.async_set(PERCENTAGE_FULL_FAN_ENTITY_ID, STATE_ON, {})
hass.states.async_set(PERCENTAGE_LIMITED_FAN_ENTITY_ID, STATE_ON, {})
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert state.attributes[ATTR_ENTITY_ID] == [
*FULL_FAN_ENTITY_IDS,
*LIMITED_FAN_ENTITY_IDS,
]
# Add Entity that supports speed
hass.states.async_set(
CEILING_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FanEntityFeature.SET_SPEED,
ATTR_PERCENTAGE: 50,
},
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_SUPPORTED_FEATURES] == FanEntityFeature.SET_SPEED
assert ATTR_PERCENTAGE in state.attributes
assert state.attributes[ATTR_PERCENTAGE] == 50
assert ATTR_ASSUMED_STATE not in state.attributes
# Add Entity with a different speed should not set assumed state
hass.states.async_set(
PERCENTAGE_LIMITED_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FanEntityFeature.SET_SPEED,
ATTR_PERCENTAGE: 75,
},
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_PERCENTAGE] == int((50 + 75) / 2)
@pytest.mark.parametrize("config_count", [(CONFIG_FULL_SUPPORT, 2)])
@pytest.mark.usefixtures("setup_comp")
async def test_direction_oscillating(hass: HomeAssistant) -> None:
"""Test handling of direction and oscillating attributes."""
hass.states.async_set(
LIVING_ROOM_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FULL_SUPPORT_FEATURES,
ATTR_OSCILLATING: True,
ATTR_DIRECTION: DIRECTION_FORWARD,
ATTR_PERCENTAGE: 50,
},
)
hass.states.async_set(
PERCENTAGE_FULL_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FULL_SUPPORT_FEATURES,
ATTR_OSCILLATING: True,
ATTR_DIRECTION: DIRECTION_FORWARD,
ATTR_PERCENTAGE: 50,
},
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert state.attributes[ATTR_FRIENDLY_NAME] == DEFAULT_NAME
assert state.attributes[ATTR_ENTITY_ID] == [*FULL_FAN_ENTITY_IDS]
assert ATTR_ASSUMED_STATE not in state.attributes
assert state.attributes[ATTR_SUPPORTED_FEATURES] == FULL_SUPPORT_FEATURES
assert ATTR_PERCENTAGE in state.attributes
assert state.attributes[ATTR_PERCENTAGE] == 50
assert state.attributes[ATTR_OSCILLATING] is True
assert state.attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
assert ATTR_ASSUMED_STATE not in state.attributes
# Add Entity with a different direction should not set assumed state
hass.states.async_set(
PERCENTAGE_FULL_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FULL_SUPPORT_FEATURES,
ATTR_OSCILLATING: True,
ATTR_DIRECTION: DIRECTION_REVERSE,
ATTR_PERCENTAGE: 50,
},
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert ATTR_ASSUMED_STATE not in state.attributes
assert ATTR_PERCENTAGE in state.attributes
assert state.attributes[ATTR_PERCENTAGE] == 50
assert state.attributes[ATTR_OSCILLATING] is True
# Now that everything is the same, no longer assumed state
hass.states.async_set(
LIVING_ROOM_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FULL_SUPPORT_FEATURES,
ATTR_OSCILLATING: True,
ATTR_DIRECTION: DIRECTION_REVERSE,
ATTR_PERCENTAGE: 50,
},
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert ATTR_PERCENTAGE in state.attributes
assert state.attributes[ATTR_PERCENTAGE] == 50
assert state.attributes[ATTR_OSCILLATING] is True
assert state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
assert ATTR_ASSUMED_STATE not in state.attributes
hass.states.async_set(
LIVING_ROOM_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FULL_SUPPORT_FEATURES,
ATTR_OSCILLATING: False,
ATTR_DIRECTION: DIRECTION_FORWARD,
ATTR_PERCENTAGE: 50,
},
)
hass.states.async_set(
PERCENTAGE_FULL_FAN_ENTITY_ID,
STATE_ON,
{
ATTR_SUPPORTED_FEATURES: FULL_SUPPORT_FEATURES,
ATTR_OSCILLATING: False,
ATTR_DIRECTION: DIRECTION_FORWARD,
ATTR_PERCENTAGE: 50,
},
)
await hass.async_block_till_done()
state = hass.states.get(FAN_GROUP)
assert state.state == STATE_ON
assert ATTR_PERCENTAGE in state.attributes
assert state.attributes[ATTR_PERCENTAGE] == 50
assert state.attributes[ATTR_OSCILLATING] is False
assert state.attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
assert ATTR_ASSUMED_STATE not in state.attributes
@pytest.mark.parametrize("config_count", [(CONFIG_MISSING_FAN, 2)])
@pytest.mark.usefixtures("setup_comp")
async def test_state_missing_entity_id(hass: HomeAssistant) -> None:
"""Test we can still setup with a missing entity id."""
state = hass.states.get(FAN_GROUP)
await hass.async_block_till_done()
assert state.state == STATE_OFF
async def test_setup_before_started(hass: HomeAssistant) -> None:
"""Test we can setup before starting."""
hass.set_state(CoreState.stopped)
assert await async_setup_component(hass, FAN_DOMAIN, CONFIG_MISSING_FAN)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get(FAN_GROUP).state == STATE_OFF
@pytest.mark.parametrize("config_count", [(CONFIG_MISSING_FAN, 2)])
@pytest.mark.usefixtures("setup_comp")
async def test_reload(hass: HomeAssistant) -> None:
"""Test the ability to reload fans."""
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get(FAN_GROUP).state == STATE_OFF
yaml_path = get_fixture_path("fan_configuration.yaml", "group")
with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
await hass.services.async_call(
"group",
SERVICE_RELOAD,
{},
blocking=True,
)
await hass.async_block_till_done()
assert hass.states.get(FAN_GROUP) is None
assert hass.states.get("fan.upstairs_fans") is not None
@pytest.mark.parametrize("config_count", [(CONFIG_FULL_SUPPORT, 2)])
@pytest.mark.usefixtures("setup_comp")
async def test_service_calls(hass: HomeAssistant) -> None:
"""Test calling services."""
await hass.services.async_call(
FAN_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: FAN_GROUP}, blocking=True
)
assert hass.states.get(LIVING_ROOM_FAN_ENTITY_ID).state == STATE_ON
assert hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID).state == STATE_ON
assert hass.states.get(FAN_GROUP).state == STATE_ON
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_PERCENTAGE: 66},
blocking=True,
)
living_room_fan_state = hass.states.get(LIVING_ROOM_FAN_ENTITY_ID)
assert living_room_fan_state.attributes[ATTR_PERCENTAGE] == 66
percentage_full_fan_state = hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID)
assert percentage_full_fan_state.attributes[ATTR_PERCENTAGE] == 66
fan_group_state = hass.states.get(FAN_GROUP)
assert fan_group_state.attributes[ATTR_PERCENTAGE] == 66
assert fan_group_state.attributes[ATTR_PERCENTAGE_STEP] == 100 / 3
await hass.services.async_call(
FAN_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: FAN_GROUP}, blocking=True
)
assert hass.states.get(LIVING_ROOM_FAN_ENTITY_ID).state == STATE_OFF
assert hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID).state == STATE_OFF
assert hass.states.get(FAN_GROUP).state == STATE_OFF
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_PERCENTAGE,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_PERCENTAGE: 100},
blocking=True,
)
living_room_fan_state = hass.states.get(LIVING_ROOM_FAN_ENTITY_ID)
assert living_room_fan_state.attributes[ATTR_PERCENTAGE] == 100
percentage_full_fan_state = hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID)
assert percentage_full_fan_state.attributes[ATTR_PERCENTAGE] == 100
fan_group_state = hass.states.get(FAN_GROUP)
assert fan_group_state.attributes[ATTR_PERCENTAGE] == 100
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_PERCENTAGE: 0},
blocking=True,
)
assert hass.states.get(LIVING_ROOM_FAN_ENTITY_ID).state == STATE_OFF
assert hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID).state == STATE_OFF
assert hass.states.get(FAN_GROUP).state == STATE_OFF
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_OSCILLATE,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_OSCILLATING: True},
blocking=True,
)
living_room_fan_state = hass.states.get(LIVING_ROOM_FAN_ENTITY_ID)
assert living_room_fan_state.attributes[ATTR_OSCILLATING] is True
percentage_full_fan_state = hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID)
assert percentage_full_fan_state.attributes[ATTR_OSCILLATING] is True
fan_group_state = hass.states.get(FAN_GROUP)
assert fan_group_state.attributes[ATTR_OSCILLATING] is True
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_OSCILLATE,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_OSCILLATING: False},
blocking=True,
)
living_room_fan_state = hass.states.get(LIVING_ROOM_FAN_ENTITY_ID)
assert living_room_fan_state.attributes[ATTR_OSCILLATING] is False
percentage_full_fan_state = hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID)
assert percentage_full_fan_state.attributes[ATTR_OSCILLATING] is False
fan_group_state = hass.states.get(FAN_GROUP)
assert fan_group_state.attributes[ATTR_OSCILLATING] is False
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_DIRECTION,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_DIRECTION: DIRECTION_FORWARD},
blocking=True,
)
living_room_fan_state = hass.states.get(LIVING_ROOM_FAN_ENTITY_ID)
assert living_room_fan_state.attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
percentage_full_fan_state = hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID)
assert percentage_full_fan_state.attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
fan_group_state = hass.states.get(FAN_GROUP)
assert fan_group_state.attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_DIRECTION,
{ATTR_ENTITY_ID: FAN_GROUP, ATTR_DIRECTION: DIRECTION_REVERSE},
blocking=True,
)
living_room_fan_state = hass.states.get(LIVING_ROOM_FAN_ENTITY_ID)
assert living_room_fan_state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
percentage_full_fan_state = hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID)
assert percentage_full_fan_state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
fan_group_state = hass.states.get(FAN_GROUP)
assert fan_group_state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
async def test_nested_group(hass: HomeAssistant) -> None:
"""Test nested fan group."""
await async_setup_component(
hass,
FAN_DOMAIN,
{
FAN_DOMAIN: [
{"platform": "demo"},
{
"platform": "group",
"entities": ["fan.bedroom_group"],
"name": "Nested Group",
},
{
"platform": "group",
CONF_ENTITIES: [
LIVING_ROOM_FAN_ENTITY_ID,
PERCENTAGE_FULL_FAN_ENTITY_ID,
],
"name": "Bedroom Group",
},
]
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
state = hass.states.get("fan.bedroom_group")
assert state is not None
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_ENTITY_ID) == [
LIVING_ROOM_FAN_ENTITY_ID,
PERCENTAGE_FULL_FAN_ENTITY_ID,
]
state = hass.states.get("fan.nested_group")
assert state is not None
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_ENTITY_ID) == ["fan.bedroom_group"]
# Test controlling the nested group
async with asyncio.timeout(0.5):
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "fan.nested_group"},
blocking=True,
)
assert hass.states.get(LIVING_ROOM_FAN_ENTITY_ID).state == STATE_ON
assert hass.states.get(PERCENTAGE_FULL_FAN_ENTITY_ID).state == STATE_ON
assert hass.states.get("fan.bedroom_group").state == STATE_ON
assert hass.states.get("fan.nested_group").state == STATE_ON