Better handle invalid trigger config (#55637)
This commit is contained in:
parent
e0f640c0f8
commit
7111fc47c4
4 changed files with 37 additions and 19 deletions
|
@ -7,6 +7,8 @@ from homeassistant.components.device_automation import (
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DOMAIN
|
from homeassistant.const import CONF_DOMAIN
|
||||||
|
|
||||||
|
from .exceptions import InvalidDeviceAutomationConfig
|
||||||
|
|
||||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
# mypy: allow-untyped-defs, no-check-untyped-defs
|
||||||
|
|
||||||
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
|
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
|
||||||
|
@ -17,10 +19,13 @@ 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"):
|
if not 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)
|
try:
|
||||||
|
return await getattr(platform, "async_validate_trigger_config")(hass, config)
|
||||||
|
except InvalidDeviceAutomationConfig as err:
|
||||||
|
raise vol.Invalid(str(err) or "Invalid trigger configuration") from err
|
||||||
|
|
||||||
|
|
||||||
async def async_attach_trigger(hass, config, action, automation_info):
|
async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
|
|
|
@ -118,12 +118,16 @@ async def async_validate_trigger_config(hass, config):
|
||||||
|
|
||||||
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
|
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
|
||||||
|
|
||||||
if (
|
if not device:
|
||||||
not device
|
raise InvalidDeviceAutomationConfig("Device {config[CONF_DEVICE_ID]} not found")
|
||||||
or device.model not in REMOTES
|
|
||||||
or trigger not in REMOTES[device.model]
|
if device.model not in REMOTES:
|
||||||
):
|
raise InvalidDeviceAutomationConfig(
|
||||||
raise InvalidDeviceAutomationConfig
|
f"Device model {device.model} is not a remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
if trigger not in REMOTES[device.model]:
|
||||||
|
raise InvalidDeviceAutomationConfig("Device does not support trigger {trigger}")
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ from unittest.mock import patch
|
||||||
from homeassistant import core
|
from homeassistant import core
|
||||||
from homeassistant.config import get_default_config_dir
|
from homeassistant.config import get_default_config_dir
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers import area_registry, device_registry, entity_registry
|
||||||
from homeassistant.helpers.check_config import async_check_ha_config_file
|
from homeassistant.helpers.check_config import async_check_ha_config_file
|
||||||
from homeassistant.util.yaml import Secrets
|
from homeassistant.util.yaml import Secrets
|
||||||
import homeassistant.util.yaml.loader as yaml_loader
|
import homeassistant.util.yaml.loader as yaml_loader
|
||||||
|
@ -229,6 +230,9 @@ async def async_check_config(config_dir):
|
||||||
"""Check the HA config."""
|
"""Check the HA config."""
|
||||||
hass = core.HomeAssistant()
|
hass = core.HomeAssistant()
|
||||||
hass.config.config_dir = config_dir
|
hass.config.config_dir = config_dir
|
||||||
|
await area_registry.async_load(hass)
|
||||||
|
await device_registry.async_load(hass)
|
||||||
|
await entity_registry.async_load(hass)
|
||||||
components = await async_check_ha_config_file(hass)
|
components = await async_check_ha_config_file(hass)
|
||||||
await hass.async_stop(force=True)
|
await hass.async_stop(force=True)
|
||||||
return components
|
return components
|
||||||
|
|
|
@ -27,14 +27,23 @@ async def apply_stop_hass(stop_hass):
|
||||||
"""Make sure all hass are stopped."""
|
"""Make sure all hass are stopped."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_is_file():
|
||||||
|
"""Mock is_file."""
|
||||||
|
# All files exist except for the old entity registry file
|
||||||
|
with patch(
|
||||||
|
"os.path.isfile", lambda path: not path.endswith("entity_registry.yaml")
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
def normalize_yaml_files(check_dict):
|
def normalize_yaml_files(check_dict):
|
||||||
"""Remove configuration path from ['yaml_files']."""
|
"""Remove configuration path from ['yaml_files']."""
|
||||||
root = get_test_config_dir()
|
root = get_test_config_dir()
|
||||||
return [key.replace(root, "...") for key in sorted(check_dict["yaml_files"].keys())]
|
return [key.replace(root, "...") for key in sorted(check_dict["yaml_files"].keys())]
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", return_value=True)
|
def test_bad_core_config(mock_is_file, loop):
|
||||||
def test_bad_core_config(isfile_patch, loop):
|
|
||||||
"""Test a bad core config setup."""
|
"""Test a bad core config setup."""
|
||||||
files = {YAML_CONFIG_FILE: BAD_CORE_CONFIG}
|
files = {YAML_CONFIG_FILE: BAD_CORE_CONFIG}
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
|
@ -43,8 +52,7 @@ def test_bad_core_config(isfile_patch, loop):
|
||||||
assert res["except"]["homeassistant"][1] == {"unit_system": "bad"}
|
assert res["except"]["homeassistant"][1] == {"unit_system": "bad"}
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", return_value=True)
|
def test_config_platform_valid(mock_is_file, loop):
|
||||||
def test_config_platform_valid(isfile_patch, loop):
|
|
||||||
"""Test a valid platform setup."""
|
"""Test a valid platform setup."""
|
||||||
files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: demo"}
|
files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: demo"}
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
|
@ -57,8 +65,7 @@ def test_config_platform_valid(isfile_patch, loop):
|
||||||
assert len(res["yaml_files"]) == 1
|
assert len(res["yaml_files"]) == 1
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", return_value=True)
|
def test_component_platform_not_found(mock_is_file, loop):
|
||||||
def test_component_platform_not_found(isfile_patch, loop):
|
|
||||||
"""Test errors if component or platform not found."""
|
"""Test errors if component or platform not found."""
|
||||||
# Make sure they don't exist
|
# Make sure they don't exist
|
||||||
files = {YAML_CONFIG_FILE: BASE_CONFIG + "beer:"}
|
files = {YAML_CONFIG_FILE: BASE_CONFIG + "beer:"}
|
||||||
|
@ -89,8 +96,7 @@ def test_component_platform_not_found(isfile_patch, loop):
|
||||||
assert len(res["yaml_files"]) == 1
|
assert len(res["yaml_files"]) == 1
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", return_value=True)
|
def test_secrets(mock_is_file, loop):
|
||||||
def test_secrets(isfile_patch, loop):
|
|
||||||
"""Test secrets config checking method."""
|
"""Test secrets config checking method."""
|
||||||
secrets_path = get_test_config_dir("secrets.yaml")
|
secrets_path = get_test_config_dir("secrets.yaml")
|
||||||
|
|
||||||
|
@ -121,8 +127,7 @@ def test_secrets(isfile_patch, loop):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", return_value=True)
|
def test_package_invalid(mock_is_file, loop):
|
||||||
def test_package_invalid(isfile_patch, loop):
|
|
||||||
"""Test an invalid package."""
|
"""Test an invalid package."""
|
||||||
files = {
|
files = {
|
||||||
YAML_CONFIG_FILE: BASE_CONFIG + (" packages:\n p1:\n" ' group: ["a"]')
|
YAML_CONFIG_FILE: BASE_CONFIG + (" packages:\n p1:\n" ' group: ["a"]')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue