Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions homeassistant/components/automation/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from homeassistant.const import CONF_PLATFORM
from homeassistant.config import async_log_exception, config_without_domain
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform
from homeassistant.helpers import config_per_platform, script
from homeassistant.loader import IntegrationNotFound

from . import CONF_TRIGGER, DOMAIN, PLATFORM_SCHEMA
from . import CONF_ACTION, CONF_TRIGGER, DOMAIN, PLATFORM_SCHEMA

# mypy: allow-untyped-calls, allow-untyped-defs
# mypy: no-check-untyped-defs, no-warn-return-any
Expand All @@ -32,6 +32,12 @@ async def async_validate_config_item(hass, config, full_config=None):
)
triggers.append(trigger)
config[CONF_TRIGGER] = triggers

actions = []
for action in config[CONF_ACTION]:
action = await script.async_validate_action_config(hass, action)
actions.append(action)
config[CONF_ACTION] = actions
except (vol.Invalid, HomeAssistantError, IntegrationNotFound) as ex:
async_log_exception(ex, DOMAIN, full_config or config, hass)
return None
Expand Down
29 changes: 20 additions & 9 deletions homeassistant/helpers/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@

import voluptuous as vol

import homeassistant.components.device_automation as device_automation
from homeassistant.core import HomeAssistant, Context, callback, CALLBACK_TYPE
from homeassistant.const import (
CONF_CONDITION,
CONF_DEVICE_ID,
CONF_DOMAIN,
CONF_TIMEOUT,
)
from homeassistant.const import CONF_CONDITION, CONF_DEVICE_ID, CONF_TIMEOUT
from homeassistant import exceptions
from homeassistant.helpers import (
service,
Expand All @@ -27,7 +23,6 @@
async_track_template,
)
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import async_get_integration
import homeassistant.util.dt as date_util
from homeassistant.util.async_ import run_coroutine_threadsafe, run_callback_threadsafe

Expand Down Expand Up @@ -86,6 +81,21 @@ def call_from_config(
Script(hass, cv.SCRIPT_SCHEMA(config)).run(variables, context)


async def async_validate_action_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
action_type = _determine_action(config)

if action_type == ACTION_DEVICE_AUTOMATION:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we validate the other types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure it's needed, other types are still validated through the action schema. We need to validate ACTION_CONDITION because it might include device conditions. That's coming in a follow up PR.

platform = await device_automation.async_get_device_automation_platform(
hass, config, "action"
)
config = platform.ACTION_SCHEMA(config)

return config


class _StopScript(Exception):
"""Throw if script needs to stop."""

Expand Down Expand Up @@ -335,8 +345,9 @@ async def _async_device_automation(self, action, variables, context):
"""
self.last_action = action.get(CONF_ALIAS, "device automation")
self._log("Executing step %s" % self.last_action)
integration = await async_get_integration(self.hass, action[CONF_DOMAIN])
platform = integration.get_platform("device_action")
platform = await device_automation.async_get_device_automation_platform(
self.hass, action, "action"
)
await platform.async_call_action_from_config(
self.hass, action, variables, context
)
Expand Down
36 changes: 36 additions & 0 deletions tests/components/device_automation/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,25 @@ async def test_automation_with_non_existing_integration(hass, caplog):
assert "Integration 'beer' not found" in caplog.text


async def test_automation_with_integration_without_device_action(hass, caplog):
"""Test automation with integration without device action support."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event1"},
"action": {"device_id": "", "domain": "test"},
}
},
)

assert (
"Integration 'test' does not support device automation actions" in caplog.text
)


async def test_automation_with_integration_without_device_trigger(hass, caplog):
"""Test automation with integration without device trigger support."""
assert await async_setup_component(
Expand All @@ -208,6 +227,23 @@ async def test_automation_with_integration_without_device_trigger(hass, caplog):
)


async def test_automation_with_bad_action(hass, caplog):
"""Test automation with bad device action."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "event", "event_type": "test_event1"},
"action": {"device_id": "", "domain": "light"},
}
},
)

assert "required key not provided" in caplog.text


async def test_automation_with_bad_trigger(hass, caplog):
"""Test automation with bad device trigger."""
assert await async_setup_component(
Expand Down