mirror of https://github.com/home-assistant/core
514 lines
16 KiB
Python
514 lines
16 KiB
Python
"""deCONZ device automation tests."""
|
|
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
from pytest_unordered import unordered
|
|
|
|
from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN
|
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
|
|
|
# pylint: disable-next=hass-component-root-import
|
|
from homeassistant.components.binary_sensor.device_trigger import (
|
|
CONF_BAT_LOW,
|
|
CONF_NOT_BAT_LOW,
|
|
CONF_NOT_TAMPERED,
|
|
CONF_TAMPERED,
|
|
)
|
|
from homeassistant.components.deconz import device_trigger
|
|
from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN
|
|
from homeassistant.components.deconz.device_trigger import CONF_SUBTYPE
|
|
from homeassistant.components.device_automation import DeviceAutomationType
|
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
|
from homeassistant.const import (
|
|
ATTR_BATTERY_LEVEL,
|
|
ATTR_ENTITY_ID,
|
|
CONF_DEVICE_ID,
|
|
CONF_DOMAIN,
|
|
CONF_PLATFORM,
|
|
CONF_TYPE,
|
|
STATE_UNAVAILABLE,
|
|
)
|
|
from homeassistant.core import HomeAssistant, ServiceCall
|
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|
from homeassistant.helpers.trigger import async_initialize_triggers
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from .conftest import WebsocketDataType
|
|
|
|
from tests.common import MockConfigEntry, async_get_device_automations
|
|
|
|
|
|
@pytest.fixture(autouse=True, name="stub_blueprint_populate")
|
|
def stub_blueprint_populate_autouse(stub_blueprint_populate: None) -> None:
|
|
"""Stub copying the blueprints to the config folder."""
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"sensor_payload",
|
|
[
|
|
{
|
|
"config": {
|
|
"alert": "none",
|
|
"battery": 60,
|
|
"group": "10",
|
|
"on": True,
|
|
"reachable": True,
|
|
},
|
|
"ep": 1,
|
|
"etag": "1b355c0b6d2af28febd7ca9165881952",
|
|
"manufacturername": "IKEA of Sweden",
|
|
"mode": 1,
|
|
"modelid": "TRADFRI on/off switch",
|
|
"name": "TRÅDFRI on/off switch ",
|
|
"state": {"buttonevent": 2002, "lastupdated": "2019-09-07T07:39:39"},
|
|
"swversion": "1.4.018",
|
|
"type": "ZHASwitch",
|
|
"uniqueid": "d0:cf:5e:ff:fe:71:a4:3a-01-1000",
|
|
}
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("config_entry_setup")
|
|
async def test_get_triggers(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Test triggers work."""
|
|
device = device_registry.async_get_device(
|
|
identifiers={(DECONZ_DOMAIN, "d0:cf:5e:ff:fe:71:a4:3a")}
|
|
)
|
|
battery_sensor_entry = entity_registry.async_get(
|
|
"sensor.tradfri_on_off_switch_battery"
|
|
)
|
|
|
|
triggers = await async_get_device_automations(
|
|
hass, DeviceAutomationType.TRIGGER, device.id
|
|
)
|
|
|
|
expected_triggers = [
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: device_trigger.CONF_SHORT_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
"metadata": {},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: device_trigger.CONF_LONG_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
"metadata": {},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: device_trigger.CONF_LONG_RELEASE,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
"metadata": {},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: device_trigger.CONF_SHORT_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_OFF,
|
|
"metadata": {},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: device_trigger.CONF_LONG_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_OFF,
|
|
"metadata": {},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: device_trigger.CONF_LONG_RELEASE,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_OFF,
|
|
"metadata": {},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: SENSOR_DOMAIN,
|
|
ATTR_ENTITY_ID: battery_sensor_entry.id,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: ATTR_BATTERY_LEVEL,
|
|
"metadata": {"secondary": True},
|
|
},
|
|
]
|
|
|
|
assert triggers == unordered(expected_triggers)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"sensor_payload",
|
|
[
|
|
{
|
|
"config": {
|
|
"battery": 95,
|
|
"enrolled": 1,
|
|
"on": True,
|
|
"pending": [],
|
|
"reachable": True,
|
|
},
|
|
"ep": 1,
|
|
"etag": "5aaa1c6bae8501f59929539c6e8f44d6",
|
|
"lastseen": "2021-07-25T18:07Z",
|
|
"manufacturername": "lk",
|
|
"modelid": "ZB-KeypadGeneric-D0002",
|
|
"name": "Keypad",
|
|
"state": {
|
|
"action": "armed_stay",
|
|
"lastupdated": "2021-07-25T18:02:51.172",
|
|
"lowbattery": False,
|
|
"panel": "exit_delay",
|
|
"seconds_remaining": 55,
|
|
"tampered": False,
|
|
},
|
|
"swversion": "3.13",
|
|
"type": "ZHAAncillaryControl",
|
|
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
|
}
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("config_entry_setup")
|
|
async def test_get_triggers_for_alarm_event(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Test triggers work."""
|
|
device = device_registry.async_get_device(
|
|
identifiers={(DECONZ_DOMAIN, "00:00:00:00:00:00:00:00")}
|
|
)
|
|
bat_entity = entity_registry.async_get("sensor.keypad_battery")
|
|
low_bat_entity = entity_registry.async_get("binary_sensor.keypad_low_battery")
|
|
tamper_entity = entity_registry.async_get("binary_sensor.keypad_tampered")
|
|
|
|
triggers = await async_get_device_automations(
|
|
hass, DeviceAutomationType.TRIGGER, device.id
|
|
)
|
|
|
|
expected_triggers = [
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: BINARY_SENSOR_DOMAIN,
|
|
ATTR_ENTITY_ID: low_bat_entity.id,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: CONF_BAT_LOW,
|
|
"metadata": {"secondary": True},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: BINARY_SENSOR_DOMAIN,
|
|
ATTR_ENTITY_ID: low_bat_entity.id,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: CONF_NOT_BAT_LOW,
|
|
"metadata": {"secondary": True},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: BINARY_SENSOR_DOMAIN,
|
|
ATTR_ENTITY_ID: tamper_entity.id,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: CONF_TAMPERED,
|
|
"metadata": {"secondary": True},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: BINARY_SENSOR_DOMAIN,
|
|
ATTR_ENTITY_ID: tamper_entity.id,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: CONF_NOT_TAMPERED,
|
|
"metadata": {"secondary": True},
|
|
},
|
|
{
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_DOMAIN: SENSOR_DOMAIN,
|
|
ATTR_ENTITY_ID: bat_entity.id,
|
|
CONF_PLATFORM: "device",
|
|
CONF_TYPE: ATTR_BATTERY_LEVEL,
|
|
"metadata": {"secondary": True},
|
|
},
|
|
]
|
|
|
|
assert triggers == unordered(expected_triggers)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"sensor_payload",
|
|
[
|
|
{
|
|
"config": {
|
|
"alert": "none",
|
|
"group": "10",
|
|
"on": True,
|
|
"reachable": True,
|
|
},
|
|
"ep": 1,
|
|
"etag": "1b355c0b6d2af28febd7ca9165881952",
|
|
"manufacturername": "IKEA of Sweden",
|
|
"mode": 1,
|
|
"modelid": "Unsupported model",
|
|
"name": "TRÅDFRI on/off switch ",
|
|
"state": {"buttonevent": 2002, "lastupdated": "2019-09-07T07:39:39"},
|
|
"swversion": "1.4.018",
|
|
"type": "ZHASwitch",
|
|
"uniqueid": "d0:cf:5e:ff:fe:71:a4:3a-01-1000",
|
|
}
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("config_entry_setup")
|
|
async def test_get_triggers_manage_unsupported_remotes(
|
|
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
|
) -> None:
|
|
"""Verify no triggers for an unsupported remote."""
|
|
device = device_registry.async_get_device(
|
|
identifiers={(DECONZ_DOMAIN, "d0:cf:5e:ff:fe:71:a4:3a")}
|
|
)
|
|
|
|
triggers = await async_get_device_automations(
|
|
hass, DeviceAutomationType.TRIGGER, device.id
|
|
)
|
|
|
|
expected_triggers = []
|
|
|
|
assert triggers == unordered(expected_triggers)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"sensor_payload",
|
|
[
|
|
{
|
|
"config": {
|
|
"alert": "none",
|
|
"battery": 60,
|
|
"group": "10",
|
|
"on": True,
|
|
"reachable": True,
|
|
},
|
|
"ep": 1,
|
|
"etag": "1b355c0b6d2af28febd7ca9165881952",
|
|
"manufacturername": "IKEA of Sweden",
|
|
"mode": 1,
|
|
"modelid": "TRADFRI on/off switch",
|
|
"name": "TRÅDFRI on/off switch ",
|
|
"state": {"buttonevent": 2002, "lastupdated": "2019-09-07T07:39:39"},
|
|
"swversion": "1.4.018",
|
|
"type": "ZHASwitch",
|
|
"uniqueid": "d0:cf:5e:ff:fe:71:a4:3a-01-1000",
|
|
}
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("config_entry_setup")
|
|
async def test_functional_device_trigger(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
service_calls: list[ServiceCall],
|
|
sensor_ws_data: WebsocketDataType,
|
|
) -> None:
|
|
"""Test proper matching and attachment of device trigger automation."""
|
|
device = device_registry.async_get_device(
|
|
identifiers={(DECONZ_DOMAIN, "d0:cf:5e:ff:fe:71:a4:3a")}
|
|
)
|
|
|
|
assert await async_setup_component(
|
|
hass,
|
|
AUTOMATION_DOMAIN,
|
|
{
|
|
AUTOMATION_DOMAIN: [
|
|
{
|
|
"trigger": {
|
|
CONF_PLATFORM: "device",
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_TYPE: device_trigger.CONF_SHORT_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
},
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"some": "test_trigger_button_press"},
|
|
},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
|
|
assert len(hass.states.async_entity_ids(AUTOMATION_DOMAIN)) == 1
|
|
|
|
await sensor_ws_data({"state": {"buttonevent": 1002}})
|
|
await hass.async_block_till_done()
|
|
assert len(service_calls) == 1
|
|
assert service_calls[0].data["some"] == "test_trigger_button_press"
|
|
|
|
|
|
@pytest.mark.skip(reason="Temporarily disabled until automation validation is improved")
|
|
@pytest.mark.usefixtures("config_entry_setup")
|
|
async def test_validate_trigger_unknown_device(hass: HomeAssistant) -> None:
|
|
"""Test unknown device does not return a trigger config."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
AUTOMATION_DOMAIN,
|
|
{
|
|
AUTOMATION_DOMAIN: [
|
|
{
|
|
"trigger": {
|
|
CONF_PLATFORM: "device",
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_DEVICE_ID: "unknown device",
|
|
CONF_TYPE: device_trigger.CONF_SHORT_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
},
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"some": "test_trigger_button_press"},
|
|
},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(hass.states.async_entity_ids(AUTOMATION_DOMAIN)) == 0
|
|
|
|
|
|
async def test_validate_trigger_unsupported_device(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
config_entry_setup: MockConfigEntry,
|
|
) -> None:
|
|
"""Test unsupported device doesn't return a trigger config."""
|
|
device = device_registry.async_get_or_create(
|
|
config_entry_id=config_entry_setup.entry_id,
|
|
identifiers={(DECONZ_DOMAIN, "d0:cf:5e:ff:fe:71:a4:3a")},
|
|
model="unsupported",
|
|
)
|
|
|
|
assert await async_setup_component(
|
|
hass,
|
|
AUTOMATION_DOMAIN,
|
|
{
|
|
AUTOMATION_DOMAIN: [
|
|
{
|
|
"trigger": {
|
|
CONF_PLATFORM: "device",
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_TYPE: device_trigger.CONF_SHORT_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
},
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"some": "test_trigger_button_press"},
|
|
},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
automations = hass.states.async_entity_ids(AUTOMATION_DOMAIN)
|
|
assert len(automations) == 1
|
|
assert hass.states.get(automations[0]).state == STATE_UNAVAILABLE
|
|
|
|
|
|
async def test_validate_trigger_unsupported_trigger(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
config_entry_setup: MockConfigEntry,
|
|
) -> None:
|
|
"""Test unsupported trigger does not return a trigger config."""
|
|
device = device_registry.async_get_or_create(
|
|
config_entry_id=config_entry_setup.entry_id,
|
|
identifiers={(DECONZ_DOMAIN, "d0:cf:5e:ff:fe:71:a4:3a")},
|
|
model="TRADFRI on/off switch",
|
|
)
|
|
|
|
trigger_config = {
|
|
CONF_PLATFORM: "device",
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_TYPE: "unsupported",
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
}
|
|
|
|
assert await async_setup_component(
|
|
hass,
|
|
AUTOMATION_DOMAIN,
|
|
{
|
|
AUTOMATION_DOMAIN: [
|
|
{
|
|
"trigger": trigger_config,
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"some": "test_trigger_button_press"},
|
|
},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
automations = hass.states.async_entity_ids(AUTOMATION_DOMAIN)
|
|
assert len(automations) == 1
|
|
assert hass.states.get(automations[0]).state == STATE_UNAVAILABLE
|
|
|
|
|
|
async def test_attach_trigger_no_matching_event(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
config_entry_setup: MockConfigEntry,
|
|
) -> None:
|
|
"""Test no matching event for device doesn't return a trigger config."""
|
|
device = device_registry.async_get_or_create(
|
|
config_entry_id=config_entry_setup.entry_id,
|
|
identifiers={(DECONZ_DOMAIN, "d0:cf:5e:ff:fe:71:a4:3a")},
|
|
name="Tradfri switch",
|
|
model="TRADFRI on/off switch",
|
|
)
|
|
|
|
trigger_config = {
|
|
CONF_PLATFORM: "device",
|
|
CONF_DOMAIN: DECONZ_DOMAIN,
|
|
CONF_DEVICE_ID: device.id,
|
|
CONF_TYPE: device_trigger.CONF_SHORT_PRESS,
|
|
CONF_SUBTYPE: device_trigger.CONF_TURN_ON,
|
|
}
|
|
|
|
assert await async_setup_component(
|
|
hass,
|
|
AUTOMATION_DOMAIN,
|
|
{
|
|
AUTOMATION_DOMAIN: [
|
|
{
|
|
"trigger": trigger_config,
|
|
"action": {
|
|
"service": "test.automation",
|
|
"data_template": {"some": "test_trigger_button_press"},
|
|
},
|
|
},
|
|
]
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(hass.states.async_entity_ids(AUTOMATION_DOMAIN)) == 1
|
|
|
|
# Assert that deCONZ async_attach_trigger raises InvalidDeviceAutomationConfig
|
|
assert not await async_initialize_triggers(
|
|
hass,
|
|
[trigger_config],
|
|
action=Mock(),
|
|
domain=AUTOMATION_DOMAIN,
|
|
name="mock-name",
|
|
log_cb=Mock(),
|
|
)
|