hass-core/tests/components/group/test_config_flow.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

728 lines
22 KiB
Python
Raw Normal View History

"""Test the Switch config flow."""
from typing import Any
from unittest.mock import patch
import pytest
from homeassistant import config_entries
from homeassistant.components.group import DOMAIN, async_setup_entry
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import entity_registry as er
2022-03-15 18:39:18 +01:00
from tests.common import MockConfigEntry
from tests.typing import WebSocketGenerator
2022-03-15 18:39:18 +01:00
@pytest.mark.parametrize(
(
"group_type",
"group_state",
"member_state",
"member_attributes",
"extra_input",
"extra_options",
"extra_attrs",
),
[
("binary_sensor", "on", "on", {}, {}, {"all": False}, {}),
("binary_sensor", "on", "on", {}, {"all": True}, {"all": True}, {}),
("cover", "open", "open", {}, {}, {}, {}),
(
"event",
STATE_UNKNOWN,
"2021-01-01T23:59:59.123+00:00",
{
"event_type": "single_press",
"event_types": ["single_press", "double_press"],
},
{},
{},
{},
),
("fan", "on", "on", {}, {}, {}, {}),
("light", "on", "on", {}, {}, {}, {}),
2022-03-29 20:07:23 -04:00
("lock", "locked", "locked", {}, {}, {}, {}),
("media_player", "on", "on", {}, {}, {}, {}),
2023-01-24 20:12:27 +01:00
(
"sensor",
"20.0",
"10",
{},
{"type": "sum"},
{"type": "sum"},
{},
),
2022-03-28 12:27:26 +02:00
("switch", "on", "on", {}, {}, {}, {}),
],
)
async def test_config_flow(
hass: HomeAssistant,
group_type,
group_state,
member_state,
member_attributes,
extra_input,
extra_options,
extra_attrs,
) -> None:
"""Test the config flow."""
members = [f"{group_type}.one", f"{group_type}.two"]
for member in members:
hass.states.async_set(member, member_state, member_attributes)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": group_type},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == group_type
with patch(
"homeassistant.components.group.async_setup_entry", wraps=async_setup_entry
) as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"name": "Living Room",
"entities": members,
**extra_input,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Living Room"
assert result["data"] == {}
assert result["options"] == {
"entities": members,
"group_type": group_type,
"hide_members": False,
"name": "Living Room",
**extra_options,
}
assert len(mock_setup_entry.mock_calls) == 1
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
assert config_entry.data == {}
assert config_entry.options == {
"entities": members,
"group_type": group_type,
"hide_members": False,
"name": "Living Room",
**extra_options,
}
state = hass.states.get(f"{group_type}.living_room")
assert state.state == group_state
assert state.attributes["entity_id"] == members
for key in extra_attrs:
assert state.attributes[key] == extra_attrs[key]
@pytest.mark.parametrize(
("hide_members", "hidden_by"), [(False, None), (True, "integration")]
)
@pytest.mark.parametrize(
("group_type", "extra_input"),
[
("binary_sensor", {"all": False}),
("cover", {}),
("event", {}),
("fan", {}),
("light", {}),
2022-03-29 20:07:23 -04:00
("lock", {}),
("media_player", {}),
2022-03-28 12:27:26 +02:00
("switch", {}),
],
)
async def test_config_flow_hides_members(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
group_type,
extra_input,
hide_members,
hidden_by,
) -> None:
"""Test the config flow hides members if requested."""
fake_uuid = "a266a680b608c32770e6c45bfe6b8411"
entry = entity_registry.async_get_or_create(
group_type, "test", "unique", suggested_object_id="one"
)
assert entry.entity_id == f"{group_type}.one"
assert entry.hidden_by is None
entry = entity_registry.async_get_or_create(
group_type, "test", "unique3", suggested_object_id="three"
)
assert entry.entity_id == f"{group_type}.three"
assert entry.hidden_by is None
members = [f"{group_type}.one", f"{group_type}.two", fake_uuid, entry.id]
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": group_type},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == group_type
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"name": "Living Room",
"entities": members,
"hide_members": hide_members,
**extra_input,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
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
def get_suggested(schema, key):
"""Get suggested value for key in voluptuous schema."""
2023-02-15 12:39:12 +01:00
for k in schema:
if k == key:
if k.description is None or "suggested_value" not in k.description:
return None
return k.description["suggested_value"]
# Wanted key absent from schema
raise Exception
@pytest.mark.parametrize(
("group_type", "member_state", "extra_options", "options_options"),
[
2023-01-24 20:12:27 +01:00
("binary_sensor", "on", {"all": False}, {}),
("cover", "open", {}, {}),
("event", "2021-01-01T23:59:59.123+00:00", {}, {}),
2023-01-24 20:12:27 +01:00
("fan", "on", {}, {}),
("light", "on", {"all": False}, {}),
("lock", "locked", {}, {}),
("media_player", "on", {}, {}),
(
"sensor",
"10",
{"ignore_non_numeric": False, "type": "sum"},
{"ignore_non_numeric": False, "type": "sum"},
),
("switch", "on", {"all": False}, {}),
],
)
async def test_options(
2023-01-24 20:12:27 +01:00
hass: HomeAssistant, group_type, member_state, extra_options, options_options
) -> None:
"""Test reconfiguring."""
members1 = [f"{group_type}.one", f"{group_type}.two"]
members2 = [f"{group_type}.four", f"{group_type}.five"]
for member in members1:
hass.states.async_set(member, member_state, {})
for member in members2:
hass.states.async_set(member, member_state, {})
group_config_entry = MockConfigEntry(
2022-03-15 18:39:18 +01:00
data={},
domain=DOMAIN,
options={
"entities": members1,
2022-03-15 18:39:18 +01:00
"group_type": group_type,
"name": "Bed Room",
**extra_options,
},
2022-03-15 18:39:18 +01:00
title="Bed Room",
)
group_config_entry.add_to_hass(hass)
2022-03-15 18:39:18 +01:00
assert await hass.config_entries.async_setup(group_config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(f"{group_type}.bed_room")
assert state.attributes["entity_id"] == members1
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == group_type
assert get_suggested(result["data_schema"].schema, "entities") == members1
assert "name" not in result["data_schema"].schema
assert result["data_schema"].schema["entities"].config["exclude_entities"] == [
f"{group_type}.bed_room"
]
result = await hass.config_entries.options.async_configure(
result["flow_id"],
2023-01-24 20:12:27 +01:00
user_input={"entities": members2, **options_options},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {
"entities": members2,
"group_type": group_type,
"hide_members": False,
"name": "Bed Room",
**extra_options,
}
assert config_entry.data == {}
assert config_entry.options == {
"entities": members2,
"group_type": group_type,
"hide_members": False,
"name": "Bed Room",
**extra_options,
}
assert config_entry.title == "Bed Room"
# Check config entry is reloaded with new options
await hass.async_block_till_done()
state = hass.states.get(f"{group_type}.bed_room")
assert state.attributes["entity_id"] == members2
# Check we don't get suggestions from another entry
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": group_type},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == group_type
assert get_suggested(result["data_schema"].schema, "entities") is None
assert get_suggested(result["data_schema"].schema, "name") is None
@pytest.mark.parametrize(
("group_type", "extra_options", "extra_options_after", "advanced"),
[
("light", {"all": False}, {"all": False}, False),
("light", {"all": True}, {"all": True}, False),
("light", {"all": False}, {"all": False}, True),
("light", {"all": True}, {"all": False}, True),
2022-03-28 12:27:26 +02:00
("switch", {"all": False}, {"all": False}, False),
("switch", {"all": True}, {"all": True}, False),
("switch", {"all": False}, {"all": False}, True),
("switch", {"all": True}, {"all": False}, True),
],
)
2022-03-28 12:27:26 +02:00
async def test_all_options(
hass: HomeAssistant, group_type, extra_options, extra_options_after, advanced
) -> None:
"""Test reconfiguring."""
members1 = [f"{group_type}.one", f"{group_type}.two"]
members2 = [f"{group_type}.four", f"{group_type}.five"]
group_config_entry = MockConfigEntry(
data={},
domain=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()
assert hass.states.get(f"{group_type}.bed_room")
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
result = await hass.config_entries.options.async_init(
config_entry.entry_id, context={"show_advanced_options": advanced}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == group_type
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"entities": members2,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {
"entities": members2,
"group_type": group_type,
"hide_members": False,
"name": "Bed Room",
**extra_options_after,
}
assert config_entry.data == {}
assert config_entry.options == {
"entities": members2,
"group_type": group_type,
"hide_members": False,
"name": "Bed Room",
**extra_options_after,
}
assert config_entry.title == "Bed Room"
@pytest.mark.parametrize(
("hide_members", "hidden_by_initial", "hidden_by"),
[
(False, er.RegistryEntryHider.INTEGRATION, None),
(True, None, er.RegistryEntryHider.INTEGRATION),
],
)
@pytest.mark.parametrize(
("group_type", "extra_input"),
[
("binary_sensor", {"all": False}),
("cover", {}),
("event", {}),
("fan", {}),
("light", {}),
2022-03-29 20:07:23 -04:00
("lock", {}),
("media_player", {}),
2022-03-28 12:27:26 +02:00
("switch", {}),
],
)
async def test_options_flow_hides_members(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
group_type,
extra_input,
hide_members,
hidden_by_initial,
hidden_by,
) -> None:
"""Test the options flow hides or unhides members if requested."""
fake_uuid = "a266a680b608c32770e6c45bfe6b8411"
entry = entity_registry.async_get_or_create(
group_type,
"test",
"unique1",
suggested_object_id="one",
hidden_by=hidden_by_initial,
)
assert entry.entity_id == f"{group_type}.one"
entry = entity_registry.async_get_or_create(
group_type,
"test",
"unique3",
suggested_object_id="three",
hidden_by=hidden_by_initial,
)
assert entry.entity_id == f"{group_type}.three"
members = [f"{group_type}.one", f"{group_type}.two", fake_uuid, entry.id]
group_config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
"entities": members,
"group_type": group_type,
"hide_members": False,
"name": "Bed Room",
**extra_input,
},
title="Bed Room",
)
group_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(group_config_entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(group_config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"entities": members,
"hide_members": hide_members,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
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
COVER_ATTRS = [{"supported_features": 0}, {}]
EVENT_ATTRS = [{"event_types": []}, {"event_type": None}]
FAN_ATTRS = [{"supported_features": 0}, {}]
LIGHT_ATTRS = [
{
"icon": "mdi:lightbulb-group",
"supported_color_modes": ["onoff"],
"supported_features": 0,
},
{"color_mode": "unknown"},
]
LOCK_ATTRS = [{"supported_features": 1}, {}]
MEDIA_PLAYER_ATTRS = [{"supported_features": 0}, {}]
SENSOR_ATTRS = [{"icon": "mdi:calculator"}, {"max_entity_id": "sensor.input_two"}]
@pytest.mark.parametrize(
("domain", "extra_user_input", "input_states", "group_state", "extra_attributes"),
[
("binary_sensor", {"all": True}, ["on", "off"], "off", [{}, {}]),
("cover", {}, ["open", "closed"], "open", COVER_ATTRS),
("event", {}, ["", ""], "unknown", EVENT_ATTRS),
("fan", {}, ["on", "off"], "on", FAN_ATTRS),
("light", {}, ["on", "off"], "on", LIGHT_ATTRS),
("lock", {}, ["unlocked", "locked"], "unlocked", LOCK_ATTRS),
("media_player", {}, ["on", "off"], "on", MEDIA_PLAYER_ATTRS),
("sensor", {"type": "max"}, ["10", "20"], "20.0", SENSOR_ATTRS),
("switch", {}, ["on", "off"], "on", [{}, {}]),
],
)
async def test_config_flow_preview(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
domain: str,
extra_user_input: dict[str, Any],
input_states: list[str],
group_state: str,
extra_attributes: list[dict[str, Any]],
) -> None:
"""Test the config flow preview."""
client = await hass_ws_client(hass)
input_entities = [f"{domain}.input_one", f"{domain}.input_two"]
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": domain},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == domain
assert result["errors"] is None
assert result["preview"] == "group"
await client.send_json_auto_id(
{
"type": "group/start_preview",
"flow_id": result["flow_id"],
"flow_type": "config_flow",
"user_input": {"name": "My group", "entities": input_entities}
| extra_user_input,
}
)
msg = await client.receive_json()
preview_subscribe_id = msg["id"]
assert msg["success"]
assert msg["result"] is None
msg = await client.receive_json()
assert msg["event"] == {
"attributes": {"friendly_name": "My group"} | extra_attributes[0],
"state": "unavailable",
}
await client.send_json_auto_id(
{
"type": "unsubscribe_events",
"subscription": preview_subscribe_id,
}
)
msg = await client.receive_json()
assert msg["success"]
hass.states.async_set(input_entities[0], input_states[0])
hass.states.async_set(input_entities[1], input_states[1])
await client.send_json_auto_id(
{
"type": "group/start_preview",
"flow_id": result["flow_id"],
"flow_type": "config_flow",
"user_input": {"name": "My group", "entities": input_entities}
| extra_user_input,
}
)
msg = await client.receive_json()
preview_subscribe_id = msg["id"]
assert msg["success"]
assert msg["result"] is None
msg = await client.receive_json()
assert msg["event"] == {
"attributes": {
"entity_id": input_entities,
"friendly_name": "My group",
}
| extra_attributes[0]
| extra_attributes[1],
"state": group_state,
}
assert len(hass.states.async_all()) == 2
@pytest.mark.parametrize(
(
"domain",
"extra_config_flow_data",
"extra_user_input",
"input_states",
"group_state",
"extra_attributes",
),
[
("binary_sensor", {"all": True}, {"all": False}, ["on", "off"], "on", [{}, {}]),
("cover", {}, {}, ["open", "closed"], "open", COVER_ATTRS),
("event", {}, {}, ["", ""], "unknown", EVENT_ATTRS),
("fan", {}, {}, ["on", "off"], "on", FAN_ATTRS),
("light", {}, {}, ["on", "off"], "on", LIGHT_ATTRS),
("lock", {}, {}, ["unlocked", "locked"], "unlocked", LOCK_ATTRS),
("media_player", {}, {}, ["on", "off"], "on", MEDIA_PLAYER_ATTRS),
(
"sensor",
{"type": "min"},
{"type": "max"},
["10", "20"],
"20.0",
SENSOR_ATTRS,
),
("switch", {}, {}, ["on", "off"], "on", [{}, {}]),
],
)
async def test_option_flow_preview(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
domain: str,
extra_config_flow_data: dict[str, Any],
extra_user_input: dict[str, Any],
input_states: list[str],
group_state: str,
extra_attributes: dict[str, Any],
) -> None:
"""Test the option flow preview."""
input_entities = [f"{domain}.input_one", f"{domain}.input_two"]
# Setup the config entry
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
"entities": input_entities,
"group_type": domain,
"hide_members": False,
"name": "My group",
}
| extra_config_flow_data,
title="My group",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
client = await hass_ws_client(hass)
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["errors"] is None
assert result["preview"] == "group"
hass.states.async_set(input_entities[0], input_states[0])
hass.states.async_set(input_entities[1], input_states[1])
await client.send_json_auto_id(
{
"type": "group/start_preview",
"flow_id": result["flow_id"],
"flow_type": "options_flow",
"user_input": {"entities": input_entities} | extra_user_input,
}
)
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] is None
msg = await client.receive_json()
assert msg["event"] == {
"attributes": {"entity_id": input_entities, "friendly_name": "My group"}
| extra_attributes[0]
| extra_attributes[1],
"state": group_state,
}
assert len(hass.states.async_all()) == 3
async def test_option_flow_sensor_preview_config_entry_removed(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test the option flow preview where the config entry is removed."""
client = await hass_ws_client(hass)
input_entities = ["sensor.input_one", "sensor.input_two"]
# Setup the config entry
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
"entities": input_entities,
"group_type": "sensor",
"hide_members": False,
"name": "My sensor group",
"type": "min",
},
title="My min_max",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["errors"] is None
assert result["preview"] == "group"
await hass.config_entries.async_remove(config_entry.entry_id)
await client.send_json_auto_id(
{
"type": "group/start_preview",
"flow_id": result["flow_id"],
"flow_type": "options_flow",
"user_input": {
"entities": input_entities,
"type": "min",
},
}
)
msg = await client.receive_json()
assert not msg["success"]
assert msg["error"] == {"code": "home_assistant_error", "message": "Unknown error"}