Support async validation of device trigger (#27333)

This commit is contained in:
Erik Montnemery 2019-10-09 21:04:11 +02:00 committed by GitHub
parent 3194dd3456
commit fdf4f398a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 64 deletions

View File

@ -7,9 +7,6 @@ from typing import Any, Awaitable, Callable
import voluptuous as vol import voluptuous as vol
from homeassistant.components.device_automation.exceptions import (
InvalidDeviceAutomationConfig,
)
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_NAME, ATTR_NAME,
@ -476,10 +473,7 @@ async def _async_process_trigger(hass, config, trigger_configs, name, action):
for conf in trigger_configs: for conf in trigger_configs:
platform = importlib.import_module(".{}".format(conf[CONF_PLATFORM]), __name__) platform = importlib.import_module(".{}".format(conf[CONF_PLATFORM]), __name__)
try: remove = await platform.async_attach_trigger(hass, conf, action, info)
remove = await platform.async_attach_trigger(hass, conf, action, info)
except InvalidDeviceAutomationConfig:
remove = False
if not remove: if not remove:
_LOGGER.error("Error setting up trigger %s", name) _LOGGER.error("Error setting up trigger %s", name)

View File

@ -4,6 +4,9 @@ import importlib
import voluptuous as vol import voluptuous as vol
from homeassistant.components.device_automation.exceptions import (
InvalidDeviceAutomationConfig,
)
from homeassistant.const import CONF_PLATFORM from homeassistant.const import CONF_PLATFORM
from homeassistant.config import async_log_exception, config_without_domain from homeassistant.config import async_log_exception, config_without_domain
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -52,7 +55,12 @@ async def _try_async_validate_config_item(hass, config, full_config=None):
"""Validate config item.""" """Validate config item."""
try: try:
config = await async_validate_config_item(hass, config, full_config) config = await async_validate_config_item(hass, config, full_config)
except (vol.Invalid, HomeAssistantError, IntegrationNotFound) as ex: except (
vol.Invalid,
HomeAssistantError,
IntegrationNotFound,
InvalidDeviceAutomationConfig,
) as ex:
async_log_exception(ex, DOMAIN, full_config or config, hass) async_log_exception(ex, DOMAIN, full_config or config, hass)
return None return None

View File

@ -18,6 +18,9 @@ async def async_validate_trigger_config(hass, config):
platform = await async_get_device_automation_platform( platform = await async_get_device_automation_platform(
hass, config[CONF_DOMAIN], "trigger" hass, config[CONF_DOMAIN], "trigger"
) )
if hasattr(platform, "async_validate_trigger_config"):
return await getattr(platform, "async_validate_trigger_config")(hass, config)
return platform.TRIGGER_SCHEMA(config) return platform.TRIGGER_SCHEMA(config)

View File

@ -204,8 +204,10 @@ def _get_deconz_event_from_device_id(hass, device_id):
return None return None
async def async_attach_trigger(hass, config, action, automation_info): async def async_validate_trigger_config(hass, config):
"""Listen for state changes based on configuration.""" """Validate config."""
config = TRIGGER_SCHEMA(config)
device_registry = await hass.helpers.device_registry.async_get_registry() device_registry = await hass.helpers.device_registry.async_get_registry()
device = device_registry.async_get(config[CONF_DEVICE_ID]) device = device_registry.async_get(config[CONF_DEVICE_ID])
@ -214,6 +216,16 @@ async def async_attach_trigger(hass, config, action, automation_info):
if device.model not in REMOTES or trigger not in REMOTES[device.model]: if device.model not in REMOTES or trigger not in REMOTES[device.model]:
raise InvalidDeviceAutomationConfig raise InvalidDeviceAutomationConfig
return config
async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration."""
device_registry = await hass.helpers.device_registry.async_get_registry()
device = device_registry.async_get(config[CONF_DEVICE_ID])
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
trigger = REMOTES[device.model][trigger] trigger = REMOTES[device.model][trigger]
deconz_event = _get_deconz_event_from_device_id(hass, device.id) deconz_event = _get_deconz_event_from_device_id(hass, device.id)

View File

@ -21,8 +21,10 @@ TRIGGER_SCHEMA = TRIGGER_BASE_SCHEMA.extend(
) )
async def async_attach_trigger(hass, config, action, automation_info): async def async_validate_trigger_config(hass, config):
"""Listen for state changes based on configuration.""" """Validate config."""
config = TRIGGER_SCHEMA(config)
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
zha_device = await async_get_zha_device(hass, config[CONF_DEVICE_ID]) zha_device = await async_get_zha_device(hass, config[CONF_DEVICE_ID])
@ -32,6 +34,14 @@ async def async_attach_trigger(hass, config, action, automation_info):
): ):
raise InvalidDeviceAutomationConfig raise InvalidDeviceAutomationConfig
return config
async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration."""
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
zha_device = await async_get_zha_device(hass, config[CONF_DEVICE_ID])
trigger = zha_device.device_automation_triggers[trigger] trigger = zha_device.device_automation_triggers[trigger]
event_config = { event_config = {

View File

@ -1,6 +1,4 @@
"""ZHA device automation trigger tests.""" """ZHA device automation trigger tests."""
from unittest.mock import patch
import pytest import pytest
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -197,7 +195,7 @@ async def test_if_fires_on_event(hass, config_entry, zha_gateway, calls):
assert calls[0].data["message"] == "service called" assert calls[0].data["message"] == "service called"
async def test_exception_no_triggers(hass, config_entry, zha_gateway, calls): async def test_exception_no_triggers(hass, config_entry, zha_gateway, calls, caplog):
"""Test for exception on event triggers firing.""" """Test for exception on event triggers firing."""
from zigpy.zcl.clusters.general import OnOff, Basic from zigpy.zcl.clusters.general import OnOff, Basic
@ -219,33 +217,32 @@ async def test_exception_no_triggers(hass, config_entry, zha_gateway, calls):
ha_device_registry = await async_get_registry(hass) ha_device_registry = await async_get_registry(hass)
reg_device = ha_device_registry.async_get_device({("zha", ieee_address)}, set()) reg_device = ha_device_registry.async_get_device({("zha", ieee_address)}, set())
with patch("logging.Logger.error") as mock: await async_setup_component(
await async_setup_component( hass,
hass, automation.DOMAIN,
automation.DOMAIN, {
{ automation.DOMAIN: [
automation.DOMAIN: [ {
{ "trigger": {
"trigger": { "device_id": reg_device.id,
"device_id": reg_device.id, "domain": "zha",
"domain": "zha", "platform": "device",
"platform": "device", "type": "junk",
"type": "junk", "subtype": "junk",
"subtype": "junk", },
}, "action": {
"action": { "service": "test.automation",
"service": "test.automation", "data": {"message": "service called"},
"data": {"message": "service called"}, },
}, }
} ]
] },
}, )
) await hass.async_block_till_done()
await hass.async_block_till_done() assert "Invalid config for [automation]" in caplog.text
mock.assert_called_with("Error setting up trigger %s", "automation 0")
async def test_exception_bad_trigger(hass, config_entry, zha_gateway, calls): async def test_exception_bad_trigger(hass, config_entry, zha_gateway, calls, caplog):
"""Test for exception on event triggers firing.""" """Test for exception on event triggers firing."""
from zigpy.zcl.clusters.general import OnOff, Basic from zigpy.zcl.clusters.general import OnOff, Basic
@ -275,27 +272,26 @@ async def test_exception_bad_trigger(hass, config_entry, zha_gateway, calls):
ha_device_registry = await async_get_registry(hass) ha_device_registry = await async_get_registry(hass)
reg_device = ha_device_registry.async_get_device({("zha", ieee_address)}, set()) reg_device = ha_device_registry.async_get_device({("zha", ieee_address)}, set())
with patch("logging.Logger.error") as mock: await async_setup_component(
await async_setup_component( hass,
hass, automation.DOMAIN,
automation.DOMAIN, {
{ automation.DOMAIN: [
automation.DOMAIN: [ {
{ "trigger": {
"trigger": { "device_id": reg_device.id,
"device_id": reg_device.id, "domain": "zha",
"domain": "zha", "platform": "device",
"platform": "device", "type": "junk",
"type": "junk", "subtype": "junk",
"subtype": "junk", },
}, "action": {
"action": { "service": "test.automation",
"service": "test.automation", "data": {"message": "service called"},
"data": {"message": "service called"}, },
}, }
} ]
] },
}, )
) await hass.async_block_till_done()
await hass.async_block_till_done() assert "Invalid config for [automation]" in caplog.text
mock.assert_called_with("Error setting up trigger %s", "automation 0")