core/tests/components/template/test_lock.py

1017 lines
29 KiB
Python

"""The tests for the Template lock platform."""
from typing import Any
import pytest
from homeassistant import setup
from homeassistant.components import lock, template
from homeassistant.components.lock import LockEntityFeature, LockState
from homeassistant.const import (
ATTR_CODE,
ATTR_ENTITY_ID,
STATE_OFF,
STATE_ON,
STATE_OPEN,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from .conftest import ConfigurationStyle
from tests.common import assert_setup_component
TEST_OBJECT_ID = "test_template_lock"
TEST_ENTITY_ID = f"lock.{TEST_OBJECT_ID}"
TEST_STATE_ENTITY_ID = "switch.test_state"
LOCK_ACTION = {
"lock": {
"service": "test.automation",
"data_template": {
"action": "lock",
"caller": "{{ this.entity_id }}",
"code": "{{ code if code is defined else None }}",
},
},
}
UNLOCK_ACTION = {
"unlock": {
"service": "test.automation",
"data_template": {
"action": "unlock",
"caller": "{{ this.entity_id }}",
"code": "{{ code if code is defined else None }}",
},
},
}
OPEN_ACTION = {
"open": {
"service": "test.automation",
"data_template": {
"action": "open",
"caller": "{{ this.entity_id }}",
},
},
}
OPTIMISTIC_LOCK = {
**LOCK_ACTION,
**UNLOCK_ACTION,
}
OPTIMISTIC_LOCK_CONFIG = {
"platform": "template",
**LOCK_ACTION,
**UNLOCK_ACTION,
**OPEN_ACTION,
}
OPTIMISTIC_CODED_LOCK_CONFIG = {
"platform": "template",
**LOCK_ACTION,
**UNLOCK_ACTION,
}
async def async_setup_legacy_format(
hass: HomeAssistant, count: int, lock_config: dict[str, Any]
) -> None:
"""Do setup of lock integration via legacy format."""
config = {"lock": {"platform": "template", "name": TEST_OBJECT_ID, **lock_config}}
with assert_setup_component(count, lock.DOMAIN):
assert await async_setup_component(
hass,
lock.DOMAIN,
config,
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
async def async_setup_modern_format(
hass: HomeAssistant, count: int, lock_config: dict[str, Any]
) -> None:
"""Do setup of lock integration via modern format."""
config = {"template": {"lock": {"name": TEST_OBJECT_ID, **lock_config}}}
with assert_setup_component(count, template.DOMAIN):
assert await async_setup_component(
hass,
template.DOMAIN,
config,
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
@pytest.fixture
async def setup_lock(
hass: HomeAssistant,
count: int,
style: ConfigurationStyle,
lock_config: dict[str, Any],
) -> None:
"""Do setup of lock integration."""
if style == ConfigurationStyle.LEGACY:
await async_setup_legacy_format(hass, count, lock_config)
elif style == ConfigurationStyle.MODERN:
await async_setup_modern_format(hass, count, lock_config)
@pytest.fixture
async def setup_base_lock(
hass: HomeAssistant,
count: int,
style: ConfigurationStyle,
state_template: str,
extra_config: dict,
):
"""Do setup of cover integration using a state template."""
if style == ConfigurationStyle.LEGACY:
await async_setup_legacy_format(
hass,
count,
{"value_template": state_template, **extra_config},
)
elif style == ConfigurationStyle.MODERN:
await async_setup_modern_format(
hass,
count,
{"state": state_template, **extra_config},
)
@pytest.fixture
async def setup_state_lock(
hass: HomeAssistant,
count: int,
style: ConfigurationStyle,
state_template: str,
):
"""Do setup of cover integration using a state template."""
if style == ConfigurationStyle.LEGACY:
await async_setup_legacy_format(
hass,
count,
{
**OPTIMISTIC_LOCK,
"value_template": state_template,
},
)
elif style == ConfigurationStyle.MODERN:
await async_setup_modern_format(
hass,
count,
{
**OPTIMISTIC_LOCK,
"state": state_template,
},
)
@pytest.fixture
async def setup_state_lock_with_extra_config(
hass: HomeAssistant,
count: int,
style: ConfigurationStyle,
state_template: str,
extra_config: dict,
):
"""Do setup of cover integration using a state template."""
if style == ConfigurationStyle.LEGACY:
await async_setup_legacy_format(
hass,
count,
{**OPTIMISTIC_LOCK, "value_template": state_template, **extra_config},
)
elif style == ConfigurationStyle.MODERN:
await async_setup_modern_format(
hass,
count,
{**OPTIMISTIC_LOCK, "state": state_template, **extra_config},
)
@pytest.fixture
async def setup_state_lock_with_attribute(
hass: HomeAssistant,
count: int,
style: ConfigurationStyle,
state_template: str,
attribute: str,
attribute_template: str,
):
"""Do setup of cover integration using a state template."""
extra = {attribute: attribute_template} if attribute else {}
if style == ConfigurationStyle.LEGACY:
await async_setup_legacy_format(
hass,
count,
{
**OPTIMISTIC_LOCK,
"value_template": state_template,
**extra,
},
)
elif style == ConfigurationStyle.MODERN:
await async_setup_modern_format(
hass,
count,
{**OPTIMISTIC_LOCK, "state": state_template, **extra},
)
@pytest.mark.parametrize(
("count", "state_template"), [(1, "{{ states.switch.test_state.state }}")]
)
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_template_state(hass: HomeAssistant) -> None:
"""Test template."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get("lock.test_template_lock")
assert state.state == LockState.LOCKED
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get("lock.test_template_lock")
assert state.state == LockState.UNLOCKED
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OPEN)
await hass.async_block_till_done()
state = hass.states.get("lock.test_template_lock")
assert state.state == LockState.OPEN
@pytest.mark.parametrize(
("count", "state_template", "extra_config"),
[(1, "{{ states.switch.test_state.state }}", {"optimistic": True, **OPEN_ACTION})],
)
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock_with_extra_config")
async def test_open_lock_optimistic(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test optimistic open."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.LOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_OPEN,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == "open"
assert calls[0].data["caller"] == TEST_ENTITY_ID
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.OPEN
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ 1 == 1 }}")])
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_template_state_boolean_on(hass: HomeAssistant) -> None:
"""Test the setting of the state with boolean on."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.LOCKED
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ 1 == 2 }}")])
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_template_state_boolean_off(hass: HomeAssistant) -> None:
"""Test the setting of the state with off."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
@pytest.mark.parametrize("count", [0])
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.parametrize(
("state_template", "extra_config"),
[
("{% if rubbish %}", OPTIMISTIC_LOCK),
("{{ rubbish }", OPTIMISTIC_LOCK),
("Invalid", {}),
(
"{{ 1==1 }}",
{
"not_value_template": "{{ states.switch.test_state.state }}",
**OPTIMISTIC_LOCK,
},
),
],
)
@pytest.mark.usefixtures("setup_base_lock")
async def test_template_syntax_error(hass: HomeAssistant) -> None:
"""Test templating syntax errors don't create entities."""
assert hass.states.async_all("lock") == []
@pytest.mark.parametrize(("count", "state_template"), [(0, "{{ 1==1 }}")])
@pytest.mark.parametrize("attribute_template", ["{{ rubbish }", "{% if rubbish %}"])
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_template_code_template_syntax_error(hass: HomeAssistant) -> None:
"""Test templating code_format syntax errors don't create entities."""
assert hass.states.async_all("lock") == []
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ 1 + 1 }}")])
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_template_static(hass: HomeAssistant) -> None:
"""Test that we allow static templates."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
hass.states.async_set(TEST_ENTITY_ID, LockState.LOCKED)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.LOCKED
@pytest.mark.parametrize("count", [1])
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.parametrize(
("state_template", "expected"),
[
("{{ True }}", LockState.LOCKED),
("{{ False }}", LockState.UNLOCKED),
("{{ x - 12 }}", STATE_UNAVAILABLE),
],
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_state_template(hass: HomeAssistant, expected: str) -> None:
"""Test state and value_template template."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == expected
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ 1==1 }}")])
@pytest.mark.parametrize(
"attribute_template",
["{% if states.switch.test_state.state %}/local/switch.png{% endif %}"],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.MODERN, "picture"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_picture_template(hass: HomeAssistant) -> None:
"""Test entity_picture template."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.attributes.get("entity_picture") in ("", None)
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.attributes["entity_picture"] == "/local/switch.png"
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ 1==1 }}")])
@pytest.mark.parametrize(
"attribute_template",
["{% if states.switch.test_state.state %}mdi:eye{% endif %}"],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.MODERN, "icon"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_icon_template(hass: HomeAssistant) -> None:
"""Test entity_picture template."""
state = hass.states.get(TEST_ENTITY_ID)
assert state.attributes.get("icon") in ("", None)
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.attributes["icon"] == "mdi:eye"
@pytest.mark.parametrize(
("count", "state_template"), [(1, "{{ states.switch.test_state.state }}")]
)
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_lock_action(hass: HomeAssistant, calls: list[ServiceCall]) -> None:
"""Test lock action."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_LOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == "lock"
assert calls[0].data["caller"] == TEST_ENTITY_ID
@pytest.mark.parametrize(
("count", "state_template"), [(1, "{{ states.switch.test_state.state }}")]
)
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_unlock_action(hass: HomeAssistant, calls: list[ServiceCall]) -> None:
"""Test unlock action."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.LOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_UNLOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == "unlock"
assert calls[0].data["caller"] == TEST_ENTITY_ID
@pytest.mark.parametrize(
("count", "state_template", "extra_config"),
[(1, "{{ states.switch.test_state.state }}", OPEN_ACTION)],
)
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.usefixtures("setup_state_lock_with_extra_config")
async def test_open_action(hass: HomeAssistant, calls: list[ServiceCall]) -> None:
"""Test open action."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.LOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_OPEN,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == "open"
assert calls[0].data["caller"] == TEST_ENTITY_ID
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ states.switch.test_state.state }}",
"{{ '.+' }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_lock_action_with_code(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test lock action with defined code format and supplied lock code."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_LOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "LOCK_CODE"},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == "lock"
assert calls[0].data["caller"] == TEST_ENTITY_ID
assert calls[0].data["code"] == "LOCK_CODE"
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ states.switch.test_state.state }}",
"{{ '.+' }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_unlock_action_with_code(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test unlock action with code format and supplied unlock code."""
await setup.async_setup_component(hass, "switch", {})
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_ON)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.LOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_UNLOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "UNLOCK_CODE"},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == "unlock"
assert calls[0].data["caller"] == TEST_ENTITY_ID
assert calls[0].data["code"] == "UNLOCK_CODE"
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ 1 == 1 }}",
"{{ '\\\\d+' }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.parametrize(
"test_action",
[
lock.SERVICE_LOCK,
lock.SERVICE_UNLOCK,
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_lock_actions_fail_with_invalid_code(
hass: HomeAssistant, calls: list[ServiceCall], test_action
) -> None:
"""Test invalid lock codes."""
await hass.services.async_call(
lock.DOMAIN,
test_action,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "non-number-value"},
)
await hass.services.async_call(
lock.DOMAIN,
test_action,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.async_block_till_done()
assert len(calls) == 0
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ 1 == 1 }}",
"{{ 1/0 }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_lock_actions_dont_execute_with_code_template_rendering_error(
hass: HomeAssistant, calls: list[ServiceCall]
) -> None:
"""Test lock code format rendering fails block lock/unlock actions."""
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_LOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_UNLOCK,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "any-value"},
)
await hass.async_block_till_done()
assert len(calls) == 0
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ states.switch.test_state.state }}",
"{{ None }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.parametrize("action", [lock.SERVICE_LOCK, lock.SERVICE_UNLOCK])
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_actions_with_none_as_codeformat_ignores_code(
hass: HomeAssistant, action, calls: list[ServiceCall]
) -> None:
"""Test lock actions with supplied lock code."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
await hass.services.async_call(
lock.DOMAIN,
action,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "any code"},
)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["action"] == action
assert calls[0].data["caller"] == TEST_ENTITY_ID
assert calls[0].data["code"] == "any code"
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ states.switch.test_state.state }}",
"[12]{1",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "code_format_template"),
(ConfigurationStyle.MODERN, "code_format"),
],
)
@pytest.mark.parametrize("action", [lock.SERVICE_LOCK, lock.SERVICE_UNLOCK])
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_actions_with_invalid_regexp_as_codeformat_never_execute(
hass: HomeAssistant, action, calls: list[ServiceCall]
) -> None:
"""Test lock actions don't execute with invalid regexp."""
hass.states.async_set(TEST_STATE_ENTITY_ID, STATE_OFF)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == LockState.UNLOCKED
await hass.services.async_call(
lock.DOMAIN,
action,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "1"},
)
await hass.services.async_call(
lock.DOMAIN,
action,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_CODE: "x"},
)
await hass.services.async_call(
lock.DOMAIN,
action,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
)
await hass.async_block_till_done()
assert len(calls) == 0
@pytest.mark.parametrize(
("count", "state_template"), [(1, "{{ states.input_select.test_state.state }}")]
)
@pytest.mark.parametrize(
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
)
@pytest.mark.parametrize(
"test_state", [LockState.UNLOCKING, LockState.LOCKING, LockState.JAMMED]
)
@pytest.mark.usefixtures("setup_state_lock")
async def test_lock_state(hass: HomeAssistant, test_state) -> None:
"""Test value template."""
hass.states.async_set("input_select.test_state", test_state)
await hass.async_block_till_done()
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == test_state
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ states('switch.test_state') }}",
"{{ is_state('availability_state.state', 'on') }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "availability_template"),
(ConfigurationStyle.MODERN, "availability"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_available_template_with_entities(hass: HomeAssistant) -> None:
"""Test availability templates with values from other entities."""
# When template returns true..
hass.states.async_set("availability_state.state", STATE_ON)
await hass.async_block_till_done()
# Device State should not be unavailable
assert hass.states.get(TEST_ENTITY_ID).state != STATE_UNAVAILABLE
# When Availability template returns false
hass.states.async_set("availability_state.state", STATE_OFF)
await hass.async_block_till_done()
# device state should be unavailable
assert hass.states.get(TEST_ENTITY_ID).state == STATE_UNAVAILABLE
@pytest.mark.parametrize(
("count", "state_template", "attribute_template"),
[
(
1,
"{{ 1 + 1 }}",
"{{ x - 12 }}",
)
],
)
@pytest.mark.parametrize(
("style", "attribute"),
[
(ConfigurationStyle.LEGACY, "availability_template"),
(ConfigurationStyle.MODERN, "availability"),
],
)
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
async def test_invalid_availability_template_keeps_component_available(
hass: HomeAssistant, caplog_setup_text
) -> None:
"""Test that an invalid availability keeps the device available."""
assert hass.states.get(TEST_ENTITY_ID).state != STATE_UNAVAILABLE
assert ("UndefinedError: 'x' is undefined") in caplog_setup_text
@pytest.mark.parametrize(("count", "domain"), [(1, lock.DOMAIN)])
@pytest.mark.parametrize(
"config",
[
{
lock.DOMAIN: {
**OPTIMISTIC_LOCK_CONFIG,
"name": "test_template_lock_01",
"unique_id": "not-so-unique-anymore",
"value_template": "{{ true }}",
}
},
],
)
@pytest.mark.usefixtures("start_ha")
async def test_legacy_unique_id(hass: HomeAssistant) -> None:
"""Test unique_id option only creates one lock per id."""
await setup.async_setup_component(
hass,
lock.DOMAIN,
{
"lock": {
**OPTIMISTIC_LOCK_CONFIG,
"name": "test_template_lock_02",
"unique_id": "not-so-unique-anymore",
"value_template": "{{ false }}",
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all("lock")) == 1
async def test_modern_unique_id(hass: HomeAssistant) -> None:
"""Test unique_id option only creates one cover per id."""
config = {
"template": {
"lock": [
{
"name": "test_template_lock_01",
"unique_id": "not-so-unique-anymore",
"state": "{{ false }}",
**OPTIMISTIC_LOCK,
},
{
"name": "test_template_lock_02",
"unique_id": "not-so-unique-anymore",
"state": "{{ false }}",
**OPTIMISTIC_LOCK,
},
]
}
}
with assert_setup_component(1, template.DOMAIN):
assert await async_setup_component(
hass,
template.DOMAIN,
config,
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
async def test_nested_unique_id(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None:
"""Test a template unique_id propagates to lock unique_ids."""
with assert_setup_component(1, template.DOMAIN):
assert await async_setup_component(
hass,
template.DOMAIN,
{
"template": {
"unique_id": "x",
"lock": [
{
**OPTIMISTIC_LOCK,
"name": "test_a",
"unique_id": "a",
"state": "{{ true }}",
},
{
**OPTIMISTIC_LOCK,
"name": "test_b",
"unique_id": "b",
"state": "{{ true }}",
},
],
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert len(hass.states.async_all("lock")) == 2
entry = entity_registry.async_get("lock.test_a")
assert entry
assert entry.unique_id == "x-a"
entry = entity_registry.async_get("lock.test_b")
assert entry
assert entry.unique_id == "x-b"
async def test_emtpy_action_config(hass: HomeAssistant) -> None:
"""Test configuration with empty script."""
with assert_setup_component(1, lock.DOMAIN):
assert await setup.async_setup_component(
hass,
lock.DOMAIN,
{
lock.DOMAIN: {
"platform": "template",
"value_template": "{{ 0 == 1 }}",
"lock": [],
"unlock": [],
"open": [],
"name": "test_template_lock",
"optimistic": True,
},
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
state = hass.states.get("lock.test_template_lock")
assert state.attributes["supported_features"] == LockEntityFeature.OPEN
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_UNLOCK,
{ATTR_ENTITY_ID: "lock.test_template_lock"},
)
await hass.async_block_till_done()
state = hass.states.get("lock.test_template_lock")
assert state.state == LockState.UNLOCKED
await hass.services.async_call(
lock.DOMAIN,
lock.SERVICE_LOCK,
{ATTR_ENTITY_ID: "lock.test_template_lock"},
)
await hass.async_block_till_done()
state = hass.states.get("lock.test_template_lock")
assert state.state == LockState.LOCKED