mirror of https://github.com/home-assistant/core
396 lines
12 KiB
Python
396 lines
12 KiB
Python
"""Test Home Assistant scenes."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.homeassistant import scene as ha_scene
|
|
from homeassistant.components.homeassistant.scene import EVENT_SCENE_RELOADED
|
|
from homeassistant.const import STATE_UNKNOWN
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import ServiceValidationError
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import async_capture_events, async_mock_service
|
|
|
|
|
|
async def test_reload_config_service(hass: HomeAssistant) -> None:
|
|
"""Test the reload config service."""
|
|
assert await async_setup_component(hass, "scene", {})
|
|
await hass.async_block_till_done()
|
|
|
|
test_reloaded_event = async_capture_events(hass, EVENT_SCENE_RELOADED)
|
|
|
|
with patch(
|
|
"homeassistant.config.load_yaml_config_file",
|
|
autospec=True,
|
|
return_value={"scene": {"name": "Hallo", "entities": {"light.kitchen": "on"}}},
|
|
):
|
|
await hass.services.async_call("scene", "reload", blocking=True)
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.states.get("scene.hallo") is not None
|
|
assert len(test_reloaded_event) == 1
|
|
|
|
with patch(
|
|
"homeassistant.config.load_yaml_config_file",
|
|
autospec=True,
|
|
return_value={"scene": {"name": "Bye", "entities": {"light.kitchen": "on"}}},
|
|
):
|
|
await hass.services.async_call("scene", "reload", blocking=True)
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(test_reloaded_event) == 2
|
|
assert hass.states.get("scene.hallo") is None
|
|
assert hass.states.get("scene.bye") is not None
|
|
|
|
|
|
async def test_apply_service(hass: HomeAssistant) -> None:
|
|
"""Test the apply service."""
|
|
assert await async_setup_component(hass, "scene", {})
|
|
assert await async_setup_component(hass, "light", {"light": {"platform": "demo"}})
|
|
await hass.async_block_till_done()
|
|
|
|
await hass.services.async_call(
|
|
"scene", "apply", {"entities": {"light.bed_light": "off"}}, blocking=True
|
|
)
|
|
|
|
assert hass.states.get("light.bed_light").state == "off"
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"apply",
|
|
{"entities": {"light.bed_light": {"state": "on", "brightness": 50}}},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get("light.bed_light")
|
|
assert state.state == "on"
|
|
assert state.attributes["brightness"] == 50
|
|
|
|
turn_on_calls = async_mock_service(hass, "light", "turn_on")
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"apply",
|
|
{
|
|
"transition": 42,
|
|
"entities": {"light.bed_light": {"state": "on", "brightness": 50}},
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
assert len(turn_on_calls) == 1
|
|
assert turn_on_calls[0].domain == "light"
|
|
assert turn_on_calls[0].service == "turn_on"
|
|
assert turn_on_calls[0].data.get("transition") == 42
|
|
assert turn_on_calls[0].data.get("entity_id") == "light.bed_light"
|
|
assert turn_on_calls[0].data.get("brightness") == 50
|
|
|
|
|
|
async def test_create_service(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test the create service."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
"scene",
|
|
{"scene": {"name": "hallo_2", "entities": {"light.kitchen": "on"}}},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get("scene.hallo") is None
|
|
assert hass.states.get("scene.hallo_2") is not None
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{"scene_id": "hallo", "entities": {}, "snapshot_entities": []},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert "Empty scenes are not allowed" in caplog.text
|
|
assert hass.states.get("scene.hallo") is None
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{
|
|
"scene_id": "hallo",
|
|
"entities": {"light.bed_light": {"state": "on", "brightness": 50}},
|
|
},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
scene = hass.states.get("scene.hallo")
|
|
assert scene is not None
|
|
assert scene.domain == "scene"
|
|
assert scene.name == "hallo"
|
|
assert scene.state == STATE_UNKNOWN
|
|
assert scene.attributes.get("entity_id") == ["light.bed_light"]
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{
|
|
"scene_id": "hallo",
|
|
"entities": {"light.kitchen_light": {"state": "on", "brightness": 100}},
|
|
},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
scene = hass.states.get("scene.hallo")
|
|
assert scene is not None
|
|
assert scene.domain == "scene"
|
|
assert scene.name == "hallo"
|
|
assert scene.state == STATE_UNKNOWN
|
|
assert scene.attributes.get("entity_id") == ["light.kitchen_light"]
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{
|
|
"scene_id": "hallo_2",
|
|
"entities": {"light.bed_light": {"state": "on", "brightness": 50}},
|
|
},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert "The scene scene.hallo_2 already exists" in caplog.text
|
|
scene = hass.states.get("scene.hallo_2")
|
|
assert scene is not None
|
|
assert scene.domain == "scene"
|
|
assert scene.name == "hallo_2"
|
|
assert scene.state == STATE_UNKNOWN
|
|
assert scene.attributes.get("entity_id") == ["light.kitchen"]
|
|
|
|
|
|
async def test_delete_service(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test the delete service."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
"scene",
|
|
{"scene": {"name": "hallo_2", "entities": {"light.kitchen": "on"}}},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{
|
|
"scene_id": "hallo",
|
|
"entities": {"light.bed_light": {"state": "on", "brightness": 50}},
|
|
},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"delete",
|
|
{
|
|
"entity_id": "scene.hallo_3",
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"delete",
|
|
{
|
|
"entity_id": "scene.hallo_2",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert hass.states.get("scene.hallo_2") is not None
|
|
|
|
assert hass.states.get("scene.hallo") is not None
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"delete",
|
|
{
|
|
"entity_id": "scene.hallo",
|
|
},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.states.get("state.hallo") is None
|
|
|
|
|
|
async def test_snapshot_service(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test the snapshot option."""
|
|
assert await async_setup_component(hass, "scene", {"scene": {}})
|
|
await hass.async_block_till_done()
|
|
hass.states.async_set("light.my_light", "on", {"hs_color": (345, 75)})
|
|
assert hass.states.get("scene.hallo") is None
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{"scene_id": "hallo", "snapshot_entities": ["light.my_light"]},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
scene = hass.states.get("scene.hallo")
|
|
assert scene is not None
|
|
assert scene.attributes.get("entity_id") == ["light.my_light"]
|
|
|
|
hass.states.async_set("light.my_light", "off", {"hs_color": (123, 45)})
|
|
turn_on_calls = async_mock_service(hass, "light", "turn_on")
|
|
await hass.services.async_call(
|
|
"scene", "turn_on", {"entity_id": "scene.hallo"}, blocking=True
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert len(turn_on_calls) == 1
|
|
assert turn_on_calls[0].data.get("entity_id") == "light.my_light"
|
|
assert turn_on_calls[0].data.get("hs_color") == (345, 75)
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{"scene_id": "hallo_2", "snapshot_entities": ["light.not_existent"]},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get("scene.hallo_2") is None
|
|
assert (
|
|
"Entity light.not_existent does not exist and therefore cannot be snapshotted"
|
|
in caplog.text
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{
|
|
"scene_id": "hallo_3",
|
|
"entities": {"light.bed_light": {"state": "on", "brightness": 50}},
|
|
"snapshot_entities": ["light.my_light"],
|
|
},
|
|
blocking=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
scene = hass.states.get("scene.hallo_3")
|
|
assert scene is not None
|
|
assert "light.my_light" in scene.attributes.get("entity_id")
|
|
assert "light.bed_light" in scene.attributes.get("entity_id")
|
|
|
|
|
|
async def test_ensure_no_intersection(hass: HomeAssistant) -> None:
|
|
"""Test that entities and snapshot_entities do not overlap."""
|
|
assert await async_setup_component(hass, "scene", {"scene": {}})
|
|
await hass.async_block_till_done()
|
|
|
|
with pytest.raises(vol.MultipleInvalid) as ex:
|
|
await hass.services.async_call(
|
|
"scene",
|
|
"create",
|
|
{
|
|
"scene_id": "hallo",
|
|
"entities": {"light.my_light": {"state": "on", "brightness": 50}},
|
|
"snapshot_entities": ["light.my_light"],
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert "entities and snapshot_entities must not overlap" in str(ex.value)
|
|
assert hass.states.get("scene.hallo") is None
|
|
|
|
|
|
async def test_scenes_with_entity(hass: HomeAssistant) -> None:
|
|
"""Test finding scenes with a specific entity."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
"scene",
|
|
{
|
|
"scene": [
|
|
{"name": "scene_1", "entities": {"light.kitchen": "on"}},
|
|
{"name": "scene_2", "entities": {"light.living_room": "off"}},
|
|
{
|
|
"name": "scene_3",
|
|
"entities": {"light.kitchen": "on", "light.living_room": "off"},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert sorted(ha_scene.scenes_with_entity(hass, "light.kitchen")) == [
|
|
"scene.scene_1",
|
|
"scene.scene_3",
|
|
]
|
|
|
|
|
|
async def test_entities_in_scene(hass: HomeAssistant) -> None:
|
|
"""Test finding entities in a scene."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
"scene",
|
|
{
|
|
"scene": [
|
|
{"name": "scene_1", "entities": {"light.kitchen": "on"}},
|
|
{"name": "scene_2", "entities": {"light.living_room": "off"}},
|
|
{
|
|
"name": "scene_3",
|
|
"entities": {"light.kitchen": "on", "light.living_room": "off"},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
for scene_id, entities in (
|
|
("scene.scene_1", ["light.kitchen"]),
|
|
("scene.scene_2", ["light.living_room"]),
|
|
("scene.scene_3", ["light.kitchen", "light.living_room"]),
|
|
):
|
|
assert ha_scene.entities_in_scene(hass, scene_id) == entities
|
|
|
|
|
|
async def test_config(hass: HomeAssistant) -> None:
|
|
"""Test passing config in YAML."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
"scene",
|
|
{
|
|
"scene": [
|
|
{
|
|
"id": "scene_id",
|
|
"name": "Scene Icon",
|
|
"icon": "mdi:party",
|
|
"entities": {"light.kitchen": "on"},
|
|
},
|
|
{
|
|
"name": "Scene No Icon",
|
|
"entities": {"light.kitchen": {"state": "on"}},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
icon = hass.states.get("scene.scene_icon")
|
|
assert icon is not None
|
|
assert icon.attributes["icon"] == "mdi:party"
|
|
|
|
no_icon = hass.states.get("scene.scene_no_icon")
|
|
assert no_icon is not None
|
|
assert "icon" not in no_icon.attributes
|
|
|
|
|
|
def test_validator() -> None:
|
|
"""Test validators."""
|
|
parsed = ha_scene.STATES_SCHEMA({"light.Test": {"state": "on"}})
|
|
assert len(parsed) == 1
|
|
assert "light.test" in parsed
|
|
assert parsed["light.test"].entity_id == "light.test"
|
|
assert parsed["light.test"].state == "on"
|