Reorganize trigger code (#38655)
This commit is contained in:
parent
fca071742d
commit
ca9dd0c833
51 changed files with 306 additions and 194 deletions
|
@ -8,8 +8,9 @@ from homeassistant.components.alarm_control_panel.const import (
|
||||||
SUPPORT_ALARM_ARM_HOME,
|
SUPPORT_ALARM_ARM_HOME,
|
||||||
SUPPORT_ALARM_ARM_NIGHT,
|
SUPPORT_ALARM_ARM_NIGHT,
|
||||||
)
|
)
|
||||||
from homeassistant.components.automation import AutomationActionType, state
|
from homeassistant.components.automation import AutomationActionType
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
|
@ -151,13 +152,13 @@ async def async_attach_trigger(
|
||||||
to_state = STATE_ALARM_ARMED_NIGHT
|
to_state = STATE_ALARM_ARMED_NIGHT
|
||||||
|
|
||||||
state_config = {
|
state_config = {
|
||||||
state.CONF_PLATFORM: "state",
|
state_trigger.CONF_PLATFORM: "state",
|
||||||
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
if from_state:
|
if from_state:
|
||||||
state_config[state.CONF_FROM] = from_state
|
state_config[state_trigger.CONF_FROM] = from_state
|
||||||
state_config = state.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Allow to set up simple automation rules via the config file."""
|
"""Allow to set up simple automation rules via the config file."""
|
||||||
import asyncio
|
|
||||||
import importlib
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Awaitable, Callable, List, Optional, Set
|
from typing import Any, Awaitable, Callable, List, Optional, Set, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -46,6 +44,7 @@ from homeassistant.helpers.script import (
|
||||||
make_script_schema,
|
make_script_schema,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.service import async_register_admin_service
|
from homeassistant.helpers.service import async_register_admin_service
|
||||||
|
from homeassistant.helpers.trigger import async_initialize_triggers
|
||||||
from homeassistant.helpers.typing import TemplateVarsType
|
from homeassistant.helpers.typing import TemplateVarsType
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util.dt import parse_datetime, utcnow
|
from homeassistant.util.dt import parse_datetime, utcnow
|
||||||
|
@ -90,26 +89,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
AutomationActionType = Callable[[HomeAssistant, TemplateVarsType], Awaitable[None]]
|
AutomationActionType = Callable[[HomeAssistant, TemplateVarsType], Awaitable[None]]
|
||||||
|
|
||||||
|
|
||||||
def _platform_validator(config):
|
|
||||||
"""Validate it is a valid platform."""
|
|
||||||
try:
|
|
||||||
platform = importlib.import_module(f".{config[CONF_PLATFORM]}", __name__)
|
|
||||||
except ImportError:
|
|
||||||
raise vol.Invalid("Invalid platform specified") from None
|
|
||||||
|
|
||||||
return platform.TRIGGER_SCHEMA(config)
|
|
||||||
|
|
||||||
|
|
||||||
_TRIGGER_SCHEMA = vol.All(
|
|
||||||
cv.ensure_list,
|
|
||||||
[
|
|
||||||
vol.All(
|
|
||||||
vol.Schema({vol.Required(CONF_PLATFORM): str}, extra=vol.ALLOW_EXTRA),
|
|
||||||
_platform_validator,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
|
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
|
||||||
|
|
||||||
PLATFORM_SCHEMA = vol.All(
|
PLATFORM_SCHEMA = vol.All(
|
||||||
|
@ -122,7 +101,7 @@ PLATFORM_SCHEMA = vol.All(
|
||||||
vol.Optional(CONF_DESCRIPTION): cv.string,
|
vol.Optional(CONF_DESCRIPTION): cv.string,
|
||||||
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
|
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
|
||||||
vol.Optional(CONF_HIDE_ENTITY): cv.boolean,
|
vol.Optional(CONF_HIDE_ENTITY): cv.boolean,
|
||||||
vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA,
|
vol.Required(CONF_TRIGGER): cv.TRIGGER_SCHEMA,
|
||||||
vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
|
vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
|
||||||
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
},
|
},
|
||||||
|
@ -485,36 +464,19 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
||||||
self, home_assistant_start: bool
|
self, home_assistant_start: bool
|
||||||
) -> Optional[Callable[[], None]]:
|
) -> Optional[Callable[[], None]]:
|
||||||
"""Set up the triggers."""
|
"""Set up the triggers."""
|
||||||
info = {"name": self._name, "home_assistant_start": home_assistant_start}
|
|
||||||
|
|
||||||
triggers = []
|
def log_cb(level, msg):
|
||||||
for conf in self._trigger_config:
|
self._logger.log(level, "%s %s", msg, self._name)
|
||||||
platform = importlib.import_module(f".{conf[CONF_PLATFORM]}", __name__)
|
|
||||||
|
|
||||||
triggers.append(
|
return await async_initialize_triggers(
|
||||||
platform.async_attach_trigger( # type: ignore
|
cast(HomeAssistant, self.hass),
|
||||||
self.hass, conf, self.async_trigger, info
|
self._trigger_config,
|
||||||
|
self.async_trigger,
|
||||||
|
DOMAIN,
|
||||||
|
self._name,
|
||||||
|
log_cb,
|
||||||
|
home_assistant_start,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
results = await asyncio.gather(*triggers)
|
|
||||||
|
|
||||||
if None in results:
|
|
||||||
self._logger.error("Error setting up trigger %s", self._name)
|
|
||||||
|
|
||||||
removes = [remove for remove in results if remove is not None]
|
|
||||||
if not removes:
|
|
||||||
return None
|
|
||||||
|
|
||||||
self._logger.info("Initialized trigger %s", self._name)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def remove_triggers():
|
|
||||||
"""Remove attached triggers."""
|
|
||||||
for remove in removes:
|
|
||||||
remove()
|
|
||||||
|
|
||||||
return remove_triggers
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""Config validation helper for the automation integration."""
|
"""Config validation helper for the automation integration."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import importlib
|
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -8,10 +7,11 @@ from homeassistant.components.device_automation.exceptions import (
|
||||||
InvalidDeviceAutomationConfig,
|
InvalidDeviceAutomationConfig,
|
||||||
)
|
)
|
||||||
from homeassistant.config import async_log_exception, config_without_domain
|
from homeassistant.config import async_log_exception, config_without_domain
|
||||||
from homeassistant.const import CONF_PLATFORM
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import condition, config_per_platform
|
from homeassistant.helpers import config_per_platform
|
||||||
|
from homeassistant.helpers.condition import async_validate_condition_config
|
||||||
from homeassistant.helpers.script import async_validate_action_config
|
from homeassistant.helpers.script import async_validate_action_config
|
||||||
|
from homeassistant.helpers.trigger import async_validate_trigger_config
|
||||||
from homeassistant.loader import IntegrationNotFound
|
from homeassistant.loader import IntegrationNotFound
|
||||||
|
|
||||||
from . import CONF_ACTION, CONF_CONDITION, CONF_TRIGGER, DOMAIN, PLATFORM_SCHEMA
|
from . import CONF_ACTION, CONF_CONDITION, CONF_TRIGGER, DOMAIN, PLATFORM_SCHEMA
|
||||||
|
@ -24,22 +24,14 @@ async def async_validate_config_item(hass, config, full_config=None):
|
||||||
"""Validate config item."""
|
"""Validate config item."""
|
||||||
config = PLATFORM_SCHEMA(config)
|
config = PLATFORM_SCHEMA(config)
|
||||||
|
|
||||||
triggers = []
|
config[CONF_TRIGGER] = await async_validate_trigger_config(
|
||||||
for trigger in config[CONF_TRIGGER]:
|
hass, config[CONF_TRIGGER]
|
||||||
trigger_platform = importlib.import_module(
|
|
||||||
f"..{trigger[CONF_PLATFORM]}", __name__
|
|
||||||
)
|
)
|
||||||
if hasattr(trigger_platform, "async_validate_trigger_config"):
|
|
||||||
trigger = await trigger_platform.async_validate_trigger_config(
|
|
||||||
hass, trigger
|
|
||||||
)
|
|
||||||
triggers.append(trigger)
|
|
||||||
config[CONF_TRIGGER] = triggers
|
|
||||||
|
|
||||||
if CONF_CONDITION in config:
|
if CONF_CONDITION in config:
|
||||||
config[CONF_CONDITION] = await asyncio.gather(
|
config[CONF_CONDITION] = await asyncio.gather(
|
||||||
*[
|
*[
|
||||||
condition.async_validate_condition_config(hass, cond)
|
async_validate_condition_config(hass, cond)
|
||||||
for cond in config[CONF_CONDITION]
|
for cond in config[CONF_CONDITION]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
"""Provides device triggers for binary sensors."""
|
"""Provides device triggers for binary sensors."""
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import state as state_automation
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.components.device_automation.const import (
|
from homeassistant.components.device_automation.const import (
|
||||||
CONF_TURNED_OFF,
|
CONF_TURNED_OFF,
|
||||||
CONF_TURNED_ON,
|
CONF_TURNED_ON,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, CONF_FOR, CONF_TYPE
|
from homeassistant.const import ATTR_DEVICE_CLASS, CONF_ENTITY_ID, CONF_FOR, CONF_TYPE
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_registry import async_entries_for_device
|
from homeassistant.helpers.entity_registry import async_entries_for_device
|
||||||
|
@ -197,16 +197,16 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
to_state = "off"
|
to_state = "off"
|
||||||
|
|
||||||
state_config = {
|
state_config = {
|
||||||
state_automation.CONF_PLATFORM: "state",
|
state_trigger.CONF_PLATFORM: "state",
|
||||||
state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state_automation.CONF_FROM: from_state,
|
state_trigger.CONF_FROM: from_state,
|
||||||
state_automation.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
if CONF_FOR in config:
|
if CONF_FOR in config:
|
||||||
state_config[CONF_FOR] = config[CONF_FOR]
|
state_config[CONF_FOR] = config[CONF_FOR]
|
||||||
|
|
||||||
state_config = state_automation.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state_automation.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import (
|
from homeassistant.components.automation import AutomationActionType
|
||||||
AutomationActionType,
|
|
||||||
numeric_state as numeric_state_automation,
|
|
||||||
state as state_automation,
|
|
||||||
)
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
|
from homeassistant.components.homeassistant.triggers import (
|
||||||
|
numeric_state as numeric_state_trigger,
|
||||||
|
state as state_trigger,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_ABOVE,
|
CONF_ABOVE,
|
||||||
CONF_BELOW,
|
CONF_BELOW,
|
||||||
|
@ -36,7 +36,7 @@ HVAC_MODE_TRIGGER_SCHEMA = TRIGGER_BASE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_ENTITY_ID): cv.entity_id,
|
vol.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||||
vol.Required(CONF_TYPE): "hvac_mode_changed",
|
vol.Required(CONF_TYPE): "hvac_mode_changed",
|
||||||
vol.Required(state_automation.CONF_TO): vol.In(const.HVAC_MODES),
|
vol.Required(state_trigger.CONF_TO): vol.In(const.HVAC_MODES),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,34 +118,34 @@ async def async_attach_trigger(
|
||||||
|
|
||||||
if trigger_type == "hvac_mode_changed":
|
if trigger_type == "hvac_mode_changed":
|
||||||
state_config = {
|
state_config = {
|
||||||
state_automation.CONF_PLATFORM: "state",
|
state_trigger.CONF_PLATFORM: "state",
|
||||||
state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state_automation.CONF_TO: config[state_automation.CONF_TO],
|
state_trigger.CONF_TO: config[state_trigger.CONF_TO],
|
||||||
state_automation.CONF_FROM: [
|
state_trigger.CONF_FROM: [
|
||||||
mode
|
mode
|
||||||
for mode in const.HVAC_MODES
|
for mode in const.HVAC_MODES
|
||||||
if mode != config[state_automation.CONF_TO]
|
if mode != config[state_trigger.CONF_TO]
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
if CONF_FOR in config:
|
if CONF_FOR in config:
|
||||||
state_config[CONF_FOR] = config[CONF_FOR]
|
state_config[CONF_FOR] = config[CONF_FOR]
|
||||||
state_config = state_automation.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state_automation.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
numeric_state_config = {
|
numeric_state_config = {
|
||||||
numeric_state_automation.CONF_PLATFORM: "numeric_state",
|
numeric_state_trigger.CONF_PLATFORM: "numeric_state",
|
||||||
numeric_state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
numeric_state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
}
|
}
|
||||||
|
|
||||||
if trigger_type == "current_temperature_changed":
|
if trigger_type == "current_temperature_changed":
|
||||||
numeric_state_config[
|
numeric_state_config[
|
||||||
numeric_state_automation.CONF_VALUE_TEMPLATE
|
numeric_state_trigger.CONF_VALUE_TEMPLATE
|
||||||
] = "{{ state.attributes.current_temperature }}"
|
] = "{{ state.attributes.current_temperature }}"
|
||||||
else:
|
else:
|
||||||
numeric_state_config[
|
numeric_state_config[
|
||||||
numeric_state_automation.CONF_VALUE_TEMPLATE
|
numeric_state_trigger.CONF_VALUE_TEMPLATE
|
||||||
] = "{{ state.attributes.current_humidity }}"
|
] = "{{ state.attributes.current_humidity }}"
|
||||||
|
|
||||||
if CONF_ABOVE in config:
|
if CONF_ABOVE in config:
|
||||||
|
@ -155,8 +155,8 @@ async def async_attach_trigger(
|
||||||
if CONF_FOR in config:
|
if CONF_FOR in config:
|
||||||
numeric_state_config[CONF_FOR] = config[CONF_FOR]
|
numeric_state_config[CONF_FOR] = config[CONF_FOR]
|
||||||
|
|
||||||
numeric_state_config = numeric_state_automation.TRIGGER_SCHEMA(numeric_state_config)
|
numeric_state_config = numeric_state_trigger.TRIGGER_SCHEMA(numeric_state_config)
|
||||||
return await numeric_state_automation.async_attach_trigger(
|
return await numeric_state_trigger.async_attach_trigger(
|
||||||
hass, numeric_state_config, action, automation_info, platform_type="device"
|
hass, numeric_state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import (
|
from homeassistant.components.automation import AutomationActionType
|
||||||
AutomationActionType,
|
|
||||||
numeric_state as numeric_state_automation,
|
|
||||||
state as state_automation,
|
|
||||||
)
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
|
from homeassistant.components.homeassistant.triggers import (
|
||||||
|
numeric_state as numeric_state_trigger,
|
||||||
|
state as state_trigger,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_SUPPORTED_FEATURES,
|
ATTR_SUPPORTED_FEATURES,
|
||||||
CONF_ABOVE,
|
CONF_ABOVE,
|
||||||
|
@ -18,6 +18,7 @@ from homeassistant.const import (
|
||||||
CONF_ENTITY_ID,
|
CONF_ENTITY_ID,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
|
CONF_VALUE_TEMPLATE,
|
||||||
STATE_CLOSED,
|
STATE_CLOSED,
|
||||||
STATE_CLOSING,
|
STATE_CLOSING,
|
||||||
STATE_OPEN,
|
STATE_OPEN,
|
||||||
|
@ -182,12 +183,12 @@ async def async_attach_trigger(
|
||||||
to_state = STATE_CLOSING
|
to_state = STATE_CLOSING
|
||||||
|
|
||||||
state_config = {
|
state_config = {
|
||||||
state_automation.CONF_PLATFORM: "state",
|
CONF_PLATFORM: "state",
|
||||||
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state_automation.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
state_config = state_automation.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state_automation.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -200,13 +201,13 @@ async def async_attach_trigger(
|
||||||
value_template = f"{{{{ state.attributes.{position} }}}}"
|
value_template = f"{{{{ state.attributes.{position} }}}}"
|
||||||
|
|
||||||
numeric_state_config = {
|
numeric_state_config = {
|
||||||
numeric_state_automation.CONF_PLATFORM: "numeric_state",
|
CONF_PLATFORM: "numeric_state",
|
||||||
numeric_state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
numeric_state_automation.CONF_BELOW: max_pos,
|
CONF_BELOW: max_pos,
|
||||||
numeric_state_automation.CONF_ABOVE: min_pos,
|
CONF_ABOVE: min_pos,
|
||||||
numeric_state_automation.CONF_VALUE_TEMPLATE: value_template,
|
CONF_VALUE_TEMPLATE: value_template,
|
||||||
}
|
}
|
||||||
numeric_state_config = numeric_state_automation.TRIGGER_SCHEMA(numeric_state_config)
|
numeric_state_config = numeric_state_trigger.TRIGGER_SCHEMA(numeric_state_config)
|
||||||
return await numeric_state_automation.async_attach_trigger(
|
return await numeric_state_trigger.async_attach_trigger(
|
||||||
hass, numeric_state_config, action, automation_info, platform_type="device"
|
hass, numeric_state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
"""Provides device automations for deconz events."""
|
"""Provides device automations for deconz events."""
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.components.automation.event as event
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.components.device_automation.exceptions import (
|
from homeassistant.components.device_automation.exceptions import (
|
||||||
InvalidDeviceAutomationConfig,
|
InvalidDeviceAutomationConfig,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import event as event_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
|
@ -432,13 +432,13 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
event_id = deconz_event.serial
|
event_id = deconz_event.serial
|
||||||
|
|
||||||
event_config = {
|
event_config = {
|
||||||
event.CONF_PLATFORM: "event",
|
event_trigger.CONF_PLATFORM: "event",
|
||||||
event.CONF_EVENT_TYPE: CONF_DECONZ_EVENT,
|
event_trigger.CONF_EVENT_TYPE: CONF_DECONZ_EVENT,
|
||||||
event.CONF_EVENT_DATA: {CONF_UNIQUE_ID: event_id, **trigger},
|
event_trigger.CONF_EVENT_DATA: {CONF_UNIQUE_ID: event_id, **trigger},
|
||||||
}
|
}
|
||||||
|
|
||||||
event_config = event.TRIGGER_SCHEMA(event_config)
|
event_config = event_trigger.TRIGGER_SCHEMA(event_config)
|
||||||
return await event.async_attach_trigger(
|
return await event_trigger.async_attach_trigger(
|
||||||
hass, event_config, action, automation_info, platform_type="device"
|
hass, event_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,7 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import (
|
from homeassistant.components.automation import AutomationActionType
|
||||||
AutomationActionType,
|
|
||||||
state as state_automation,
|
|
||||||
)
|
|
||||||
from homeassistant.components.device_automation.const import (
|
from homeassistant.components.device_automation.const import (
|
||||||
CONF_IS_OFF,
|
CONF_IS_OFF,
|
||||||
CONF_IS_ON,
|
CONF_IS_ON,
|
||||||
|
@ -16,6 +13,7 @@ from homeassistant.components.device_automation.const import (
|
||||||
CONF_TURNED_OFF,
|
CONF_TURNED_OFF,
|
||||||
CONF_TURNED_ON,
|
CONF_TURNED_ON,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
CONF_CONDITION,
|
CONF_CONDITION,
|
||||||
|
@ -157,16 +155,16 @@ async def async_attach_trigger(
|
||||||
from_state = "on"
|
from_state = "on"
|
||||||
to_state = "off"
|
to_state = "off"
|
||||||
state_config = {
|
state_config = {
|
||||||
state_automation.CONF_PLATFORM: "state",
|
CONF_PLATFORM: "state",
|
||||||
state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state_automation.CONF_FROM: from_state,
|
state_trigger.CONF_FROM: from_state,
|
||||||
state_automation.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
if CONF_FOR in config:
|
if CONF_FOR in config:
|
||||||
state_config[CONF_FOR] = config[CONF_FOR]
|
state_config[CONF_FOR] = config[CONF_FOR]
|
||||||
|
|
||||||
state_config = state_automation.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state_automation.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,9 @@ from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import AutomationActionType, state
|
from homeassistant.components.automation import AutomationActionType
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
|
@ -80,12 +81,12 @@ async def async_attach_trigger(
|
||||||
to_state = STATE_OFF
|
to_state = STATE_OFF
|
||||||
|
|
||||||
state_config = {
|
state_config = {
|
||||||
state.CONF_PLATFORM: "state",
|
state_trigger.CONF_PLATFORM: "state",
|
||||||
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state.CONF_FROM: from_state,
|
state_trigger.CONF_FROM: from_state,
|
||||||
state.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
state_config = state.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
23
homeassistant/components/homeassistant/trigger.py
Normal file
23
homeassistant/components/homeassistant/trigger.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
"""Home Assistant trigger dispatcher."""
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_PLATFORM
|
||||||
|
|
||||||
|
|
||||||
|
def _get_trigger_platform(config):
|
||||||
|
return importlib.import_module(f"..triggers.{config[CONF_PLATFORM]}", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_validate_trigger_config(hass, config):
|
||||||
|
"""Validate config."""
|
||||||
|
platform = _get_trigger_platform(config)
|
||||||
|
if hasattr(platform, "async_validate_trigger_config"):
|
||||||
|
return await getattr(platform, "async_validate_trigger_config")(hass, config)
|
||||||
|
|
||||||
|
return platform.TRIGGER_SCHEMA(config)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
|
"""Attach trigger of specified platform."""
|
||||||
|
platform = _get_trigger_platform(config)
|
||||||
|
return await platform.async_attach_trigger(hass, config, action, automation_info)
|
|
@ -0,0 +1 @@
|
||||||
|
"""Home Assistant triggers."""
|
|
@ -3,11 +3,11 @@ import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.components.automation.event as event
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.components.device_automation.exceptions import (
|
from homeassistant.components.device_automation.exceptions import (
|
||||||
InvalidDeviceAutomationConfig,
|
InvalidDeviceAutomationConfig,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import event as event_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
|
@ -139,13 +139,13 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
trigger = REMOTES[device.model][trigger]
|
trigger = REMOTES[device.model][trigger]
|
||||||
|
|
||||||
event_config = {
|
event_config = {
|
||||||
event.CONF_PLATFORM: "event",
|
event_trigger.CONF_PLATFORM: "event",
|
||||||
event.CONF_EVENT_TYPE: CONF_HUE_EVENT,
|
event_trigger.CONF_EVENT_TYPE: CONF_HUE_EVENT,
|
||||||
event.CONF_EVENT_DATA: {CONF_UNIQUE_ID: hue_event.unique_id, **trigger},
|
event_trigger.CONF_EVENT_DATA: {CONF_UNIQUE_ID: hue_event.unique_id, **trigger},
|
||||||
}
|
}
|
||||||
|
|
||||||
event_config = event.TRIGGER_SCHEMA(event_config)
|
event_config = event_trigger.TRIGGER_SCHEMA(event_config)
|
||||||
return await event.async_attach_trigger(
|
return await event_trigger.async_attach_trigger(
|
||||||
hass, event_config, action, automation_info, platform_type="device"
|
hass, event_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@ from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import (
|
from homeassistant.components.automation import AutomationActionType
|
||||||
AutomationActionType,
|
|
||||||
numeric_state as numeric_state_automation,
|
|
||||||
)
|
|
||||||
from homeassistant.components.device_automation import (
|
from homeassistant.components.device_automation import (
|
||||||
TRIGGER_BASE_SCHEMA,
|
TRIGGER_BASE_SCHEMA,
|
||||||
toggle_entity,
|
toggle_entity,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import (
|
||||||
|
numeric_state as numeric_state_trigger,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_ABOVE,
|
CONF_ABOVE,
|
||||||
CONF_BELOW,
|
CONF_BELOW,
|
||||||
|
@ -81,9 +81,9 @@ async def async_attach_trigger(
|
||||||
|
|
||||||
if trigger_type == "target_humidity_changed":
|
if trigger_type == "target_humidity_changed":
|
||||||
numeric_state_config = {
|
numeric_state_config = {
|
||||||
numeric_state_automation.CONF_PLATFORM: "numeric_state",
|
numeric_state_trigger.CONF_PLATFORM: "numeric_state",
|
||||||
numeric_state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
numeric_state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
numeric_state_automation.CONF_VALUE_TEMPLATE: "{{ state.attributes.humidity }}",
|
numeric_state_trigger.CONF_VALUE_TEMPLATE: "{{ state.attributes.humidity }}",
|
||||||
}
|
}
|
||||||
|
|
||||||
if CONF_ABOVE in config:
|
if CONF_ABOVE in config:
|
||||||
|
@ -93,10 +93,10 @@ async def async_attach_trigger(
|
||||||
if CONF_FOR in config:
|
if CONF_FOR in config:
|
||||||
numeric_state_config[CONF_FOR] = config[CONF_FOR]
|
numeric_state_config[CONF_FOR] = config[CONF_FOR]
|
||||||
|
|
||||||
numeric_state_config = numeric_state_automation.TRIGGER_SCHEMA(
|
numeric_state_config = numeric_state_trigger.TRIGGER_SCHEMA(
|
||||||
numeric_state_config
|
numeric_state_config
|
||||||
)
|
)
|
||||||
return await numeric_state_automation.async_attach_trigger(
|
return await numeric_state_trigger.async_attach_trigger(
|
||||||
hass, numeric_state_config, action, automation_info, platform_type="device"
|
hass, numeric_state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,9 @@ from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import AutomationActionType, state
|
from homeassistant.components.automation import AutomationActionType
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
|
@ -80,12 +81,12 @@ async def async_attach_trigger(
|
||||||
to_state = STATE_UNLOCKED
|
to_state = STATE_UNLOCKED
|
||||||
|
|
||||||
state_config = {
|
state_config = {
|
||||||
state.CONF_PLATFORM: "state",
|
CONF_PLATFORM: "state",
|
||||||
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state.CONF_FROM: from_state,
|
state_trigger.CONF_FROM: from_state,
|
||||||
state.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
state_config = state.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,7 +7,6 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import mqtt
|
from homeassistant.components import mqtt
|
||||||
from homeassistant.components.automation import AutomationActionType
|
from homeassistant.components.automation import AutomationActionType
|
||||||
import homeassistant.components.automation.mqtt as automation_mqtt
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
|
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
|
@ -27,6 +26,7 @@ from . import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
cleanup_device_registry,
|
cleanup_device_registry,
|
||||||
debug_info,
|
debug_info,
|
||||||
|
trigger as mqtt_trigger,
|
||||||
)
|
)
|
||||||
from .discovery import MQTT_DISCOVERY_UPDATED, clear_discovery_hash
|
from .discovery import MQTT_DISCOVERY_UPDATED, clear_discovery_hash
|
||||||
|
|
||||||
|
@ -83,16 +83,16 @@ class TriggerInstance:
|
||||||
async def async_attach_trigger(self):
|
async def async_attach_trigger(self):
|
||||||
"""Attach MQTT trigger."""
|
"""Attach MQTT trigger."""
|
||||||
mqtt_config = {
|
mqtt_config = {
|
||||||
automation_mqtt.CONF_TOPIC: self.trigger.topic,
|
mqtt_trigger.CONF_TOPIC: self.trigger.topic,
|
||||||
automation_mqtt.CONF_ENCODING: DEFAULT_ENCODING,
|
mqtt_trigger.CONF_ENCODING: DEFAULT_ENCODING,
|
||||||
automation_mqtt.CONF_QOS: self.trigger.qos,
|
mqtt_trigger.CONF_QOS: self.trigger.qos,
|
||||||
}
|
}
|
||||||
if self.trigger.payload:
|
if self.trigger.payload:
|
||||||
mqtt_config[CONF_PAYLOAD] = self.trigger.payload
|
mqtt_config[CONF_PAYLOAD] = self.trigger.payload
|
||||||
|
|
||||||
if self.remove:
|
if self.remove:
|
||||||
self.remove()
|
self.remove()
|
||||||
self.remove = await automation_mqtt.async_attach_trigger(
|
self.remove = await mqtt_trigger.async_attach_trigger(
|
||||||
self.trigger.hass, mqtt_config, self.action, self.automation_info,
|
self.trigger.hass, mqtt_config, self.action, self.automation_info,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
"""Provides device triggers for sensors."""
|
"""Provides device triggers for sensors."""
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.components.automation.numeric_state as numeric_state_automation
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.components.device_automation.exceptions import (
|
from homeassistant.components.device_automation.exceptions import (
|
||||||
InvalidDeviceAutomationConfig,
|
InvalidDeviceAutomationConfig,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import (
|
||||||
|
numeric_state as numeric_state_trigger,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
|
@ -100,18 +102,18 @@ TRIGGER_SCHEMA = vol.All(
|
||||||
async def async_attach_trigger(hass, config, action, automation_info):
|
async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
"""Listen for state changes based on configuration."""
|
"""Listen for state changes based on configuration."""
|
||||||
numeric_state_config = {
|
numeric_state_config = {
|
||||||
numeric_state_automation.CONF_PLATFORM: "numeric_state",
|
numeric_state_trigger.CONF_PLATFORM: "numeric_state",
|
||||||
numeric_state_automation.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
numeric_state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
}
|
}
|
||||||
if CONF_ABOVE in config:
|
if CONF_ABOVE in config:
|
||||||
numeric_state_config[numeric_state_automation.CONF_ABOVE] = config[CONF_ABOVE]
|
numeric_state_config[numeric_state_trigger.CONF_ABOVE] = config[CONF_ABOVE]
|
||||||
if CONF_BELOW in config:
|
if CONF_BELOW in config:
|
||||||
numeric_state_config[numeric_state_automation.CONF_BELOW] = config[CONF_BELOW]
|
numeric_state_config[numeric_state_trigger.CONF_BELOW] = config[CONF_BELOW]
|
||||||
if CONF_FOR in config:
|
if CONF_FOR in config:
|
||||||
numeric_state_config[CONF_FOR] = config[CONF_FOR]
|
numeric_state_config[CONF_FOR] = config[CONF_FOR]
|
||||||
|
|
||||||
numeric_state_config = numeric_state_automation.TRIGGER_SCHEMA(numeric_state_config)
|
numeric_state_config = numeric_state_trigger.TRIGGER_SCHEMA(numeric_state_config)
|
||||||
return await numeric_state_automation.async_attach_trigger(
|
return await numeric_state_trigger.async_attach_trigger(
|
||||||
hass, numeric_state_config, action, automation_info, platform_type="device"
|
hass, numeric_state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ TRIGGER_SCHEMA = IF_ACTION_SCHEMA = vol.Schema(
|
||||||
|
|
||||||
|
|
||||||
async def async_attach_trigger(
|
async def async_attach_trigger(
|
||||||
hass, config, action, automation_info, *, platform_type="numeric_state"
|
hass, config, action, automation_info, *, platform_type="template"
|
||||||
):
|
):
|
||||||
"""Listen for state changes based on configuration."""
|
"""Listen for state changes based on configuration."""
|
||||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
value_template = config.get(CONF_VALUE_TEMPLATE)
|
|
@ -3,8 +3,9 @@ from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.automation import AutomationActionType, state
|
from homeassistant.components.automation import AutomationActionType
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
|
@ -77,12 +78,12 @@ async def async_attach_trigger(
|
||||||
to_state = STATE_DOCKED
|
to_state = STATE_DOCKED
|
||||||
|
|
||||||
state_config = {
|
state_config = {
|
||||||
state.CONF_PLATFORM: "state",
|
CONF_PLATFORM: "state",
|
||||||
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
|
||||||
state.CONF_FROM: from_state,
|
state_trigger.CONF_FROM: from_state,
|
||||||
state.CONF_TO: to_state,
|
state_trigger.CONF_TO: to_state,
|
||||||
}
|
}
|
||||||
state_config = state.TRIGGER_SCHEMA(state_config)
|
state_config = state_trigger.TRIGGER_SCHEMA(state_config)
|
||||||
return await state.async_attach_trigger(
|
return await state_trigger.async_attach_trigger(
|
||||||
hass, state_config, action, automation_info, platform_type="device"
|
hass, state_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,8 +9,6 @@ from homeassistant.const import CONF_PLATFORM, CONF_WEBHOOK_ID
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from . import DOMAIN as AUTOMATION_DOMAIN
|
|
||||||
|
|
||||||
# mypy: allow-untyped-defs
|
# mypy: allow-untyped-defs
|
||||||
|
|
||||||
DEPENDENCIES = ("webhook",)
|
DEPENDENCIES = ("webhook",)
|
||||||
|
@ -39,7 +37,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
"""Trigger based on incoming webhooks."""
|
"""Trigger based on incoming webhooks."""
|
||||||
webhook_id = config.get(CONF_WEBHOOK_ID)
|
webhook_id = config.get(CONF_WEBHOOK_ID)
|
||||||
hass.components.webhook.async_register(
|
hass.components.webhook.async_register(
|
||||||
AUTOMATION_DOMAIN,
|
automation_info["domain"],
|
||||||
automation_info["name"],
|
automation_info["name"],
|
||||||
webhook_id,
|
webhook_id,
|
||||||
partial(_handle_webhook, action),
|
partial(_handle_webhook, action),
|
|
@ -1,11 +1,11 @@
|
||||||
"""Provides device automations for ZHA devices that emit events."""
|
"""Provides device automations for ZHA devices that emit events."""
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.components.automation.event as event
|
|
||||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||||
from homeassistant.components.device_automation.exceptions import (
|
from homeassistant.components.device_automation.exceptions import (
|
||||||
InvalidDeviceAutomationConfig,
|
InvalidDeviceAutomationConfig,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.homeassistant.triggers import event as event_trigger
|
||||||
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
|
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
|
||||||
|
|
||||||
from . import DOMAIN
|
from . import DOMAIN
|
||||||
|
@ -54,13 +54,13 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
||||||
trigger = zha_device.device_automation_triggers[trigger]
|
trigger = zha_device.device_automation_triggers[trigger]
|
||||||
|
|
||||||
event_config = {
|
event_config = {
|
||||||
event.CONF_PLATFORM: "event",
|
event_trigger.CONF_PLATFORM: "event",
|
||||||
event.CONF_EVENT_TYPE: ZHA_EVENT,
|
event_trigger.CONF_EVENT_TYPE: ZHA_EVENT,
|
||||||
event.CONF_EVENT_DATA: {DEVICE_IEEE: str(zha_device.ieee), **trigger},
|
event_trigger.CONF_EVENT_DATA: {DEVICE_IEEE: str(zha_device.ieee), **trigger},
|
||||||
}
|
}
|
||||||
|
|
||||||
event_config = event.TRIGGER_SCHEMA(event_config)
|
event_config = event_trigger.TRIGGER_SCHEMA(event_config)
|
||||||
return await event.async_attach_trigger(
|
return await event_trigger.async_attach_trigger(
|
||||||
hass, event_config, action, automation_info, platform_type="device"
|
hass, event_config, action, automation_info, platform_type="device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -995,6 +995,10 @@ CONDITION_SCHEMA: vol.Schema = key_value_schemas(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TRIGGER_SCHEMA = vol.All(
|
||||||
|
ensure_list, [vol.Schema({vol.Required(CONF_PLATFORM): str}, extra=vol.ALLOW_EXTRA)]
|
||||||
|
)
|
||||||
|
|
||||||
_SCRIPT_DELAY_SCHEMA = vol.Schema(
|
_SCRIPT_DELAY_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_ALIAS): string,
|
vol.Optional(CONF_ALIAS): string,
|
||||||
|
|
92
homeassistant/helpers/trigger.py
Normal file
92
homeassistant/helpers/trigger.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
"""Triggers."""
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Any, Callable, List, Optional
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_PLATFORM
|
||||||
|
from homeassistant.core import CALLBACK_TYPE, callback
|
||||||
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
|
from homeassistant.loader import IntegrationNotFound, async_get_integration
|
||||||
|
|
||||||
|
_PLATFORM_ALIASES = {
|
||||||
|
"device_automation": ("device",),
|
||||||
|
"homeassistant": ("event", "numeric_state", "state", "time_pattern", "time"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_get_trigger_platform(
|
||||||
|
hass: HomeAssistantType, config: ConfigType
|
||||||
|
) -> Any:
|
||||||
|
platform = config[CONF_PLATFORM]
|
||||||
|
for alias, triggers in _PLATFORM_ALIASES.items():
|
||||||
|
if platform in triggers:
|
||||||
|
platform = alias
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
integration = await async_get_integration(hass, platform)
|
||||||
|
except IntegrationNotFound:
|
||||||
|
raise vol.Invalid(f"Invalid platform '{platform}' specified") from None
|
||||||
|
try:
|
||||||
|
return integration.get_platform("trigger")
|
||||||
|
except ImportError:
|
||||||
|
raise vol.Invalid(
|
||||||
|
f"Integration '{platform}' does not provide trigger support"
|
||||||
|
) from None
|
||||||
|
|
||||||
|
|
||||||
|
async def async_validate_trigger_config(
|
||||||
|
hass: HomeAssistantType, trigger_config: List[ConfigType]
|
||||||
|
) -> List[ConfigType]:
|
||||||
|
"""Validate triggers."""
|
||||||
|
config = []
|
||||||
|
for conf in trigger_config:
|
||||||
|
platform = await _async_get_trigger_platform(hass, conf)
|
||||||
|
if hasattr(platform, "async_validate_trigger_config"):
|
||||||
|
conf = await platform.async_validate_trigger_config(hass, conf)
|
||||||
|
else:
|
||||||
|
conf = platform.TRIGGER_SCHEMA(conf)
|
||||||
|
config.append(conf)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
async def async_initialize_triggers(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
trigger_config: List[ConfigType],
|
||||||
|
action: Callable,
|
||||||
|
domain: str,
|
||||||
|
name: str,
|
||||||
|
log_cb: Callable,
|
||||||
|
home_assistant_start: bool = False,
|
||||||
|
) -> Optional[CALLBACK_TYPE]:
|
||||||
|
"""Initialize triggers."""
|
||||||
|
info = {
|
||||||
|
"domain": domain,
|
||||||
|
"name": name,
|
||||||
|
"home_assistant_start": home_assistant_start,
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers = []
|
||||||
|
for conf in trigger_config:
|
||||||
|
platform = await _async_get_trigger_platform(hass, conf)
|
||||||
|
triggers.append(platform.async_attach_trigger(hass, conf, action, info))
|
||||||
|
|
||||||
|
removes = await asyncio.gather(*triggers)
|
||||||
|
|
||||||
|
if None in removes:
|
||||||
|
log_cb(logging.ERROR, "Error setting up trigger")
|
||||||
|
|
||||||
|
removes = list(filter(None, removes))
|
||||||
|
if not removes:
|
||||||
|
return None
|
||||||
|
|
||||||
|
log_cb(logging.INFO, "Initialized trigger")
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def remove_triggers(): # type: ignore
|
||||||
|
"""Remove triggers."""
|
||||||
|
for remove in removes:
|
||||||
|
remove()
|
||||||
|
|
||||||
|
return remove_triggers
|
|
@ -861,6 +861,22 @@ async def test_automation_not_trigger_on_bootstrap(hass):
|
||||||
assert ["hello.world"] == calls[0].data.get(ATTR_ENTITY_ID)
|
assert ["hello.world"] == calls[0].data.get(ATTR_ENTITY_ID)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_automation_bad_trigger(hass, caplog):
|
||||||
|
"""Test bad trigger configuration."""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: {
|
||||||
|
"alias": "hello",
|
||||||
|
"trigger": {"platform": "automation"},
|
||||||
|
"action": [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert "Integration 'automation' does not provide trigger support." in caplog.text
|
||||||
|
|
||||||
|
|
||||||
async def test_automation_with_error_in_script(hass, caplog):
|
async def test_automation_with_error_in_script(hass, caplog):
|
||||||
"""Test automation with an error in script."""
|
"""Test automation with an error in script."""
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
|
|
1
tests/components/homeassistant/triggers/__init__.py
Normal file
1
tests/components/homeassistant/triggers/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""Test core triggers."""
|
|
@ -5,7 +5,9 @@ import pytest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.components.automation as automation
|
import homeassistant.components.automation as automation
|
||||||
from homeassistant.components.automation import numeric_state
|
from homeassistant.components.homeassistant.triggers import (
|
||||||
|
numeric_state as numeric_state_trigger,
|
||||||
|
)
|
||||||
from homeassistant.core import Context
|
from homeassistant.core import Context
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
@ -776,7 +778,7 @@ async def test_if_fails_setup_bad_for(hass, calls):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(automation.numeric_state, "_LOGGER") as mock_logger:
|
with patch.object(numeric_state_trigger, "_LOGGER") as mock_logger:
|
||||||
hass.states.async_set("test.entity", 9)
|
hass.states.async_set("test.entity", 9)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_logger.error.called
|
assert mock_logger.error.called
|
||||||
|
@ -1164,7 +1166,7 @@ async def test_invalid_for_template(hass, calls):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(automation.numeric_state, "_LOGGER") as mock_logger:
|
with patch.object(numeric_state_trigger, "_LOGGER") as mock_logger:
|
||||||
hass.states.async_set("test.entity", 9)
|
hass.states.async_set("test.entity", 9)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_logger.error.called
|
assert mock_logger.error.called
|
||||||
|
@ -1234,6 +1236,6 @@ async def test_if_fires_on_entities_change_overlap_for_template(hass, calls):
|
||||||
def test_below_above():
|
def test_below_above():
|
||||||
"""Test above cannot be above below."""
|
"""Test above cannot be above below."""
|
||||||
with pytest.raises(vol.Invalid):
|
with pytest.raises(vol.Invalid):
|
||||||
numeric_state.TRIGGER_SCHEMA(
|
numeric_state_trigger.TRIGGER_SCHEMA(
|
||||||
{"platform": "numeric_state", "above": 1200, "below": 1000}
|
{"platform": "numeric_state", "above": 1200, "below": 1000}
|
||||||
)
|
)
|
|
@ -4,6 +4,7 @@ from datetime import timedelta
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import homeassistant.components.automation as automation
|
import homeassistant.components.automation as automation
|
||||||
|
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||||
from homeassistant.core import Context
|
from homeassistant.core import Context
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
@ -327,7 +328,7 @@ async def test_if_fails_setup_bad_for(hass, calls):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(automation.state, "_LOGGER") as mock_logger:
|
with patch.object(state_trigger, "_LOGGER") as mock_logger:
|
||||||
hass.states.async_set("test.entity", "world")
|
hass.states.async_set("test.entity", "world")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_logger.error.called
|
assert mock_logger.error.called
|
||||||
|
@ -942,7 +943,7 @@ async def test_invalid_for_template_1(hass, calls):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(automation.state, "_LOGGER") as mock_logger:
|
with patch.object(state_trigger, "_LOGGER") as mock_logger:
|
||||||
hass.states.async_set("test.entity", "world")
|
hass.states.async_set("test.entity", "world")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_logger.error.called
|
assert mock_logger.error.called
|
|
@ -361,7 +361,7 @@ async def test_untrack_time_change(hass):
|
||||||
"""Test for removing tracked time changes."""
|
"""Test for removing tracked time changes."""
|
||||||
mock_track_time_change = Mock()
|
mock_track_time_change = Mock()
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.automation.time.async_track_time_change",
|
"homeassistant.components.homeassistant.triggers.time.async_track_time_change",
|
||||||
return_value=mock_track_time_change,
|
return_value=mock_track_time_change,
|
||||||
):
|
):
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
|
@ -419,6 +419,8 @@ async def test_missing_discover_abbreviations(hass, mqtt_mock, caplog):
|
||||||
missing = []
|
missing = []
|
||||||
regex = re.compile(r"(CONF_[a-zA-Z\d_]*) *= *[\'\"]([a-zA-Z\d_]*)[\'\"]")
|
regex = re.compile(r"(CONF_[a-zA-Z\d_]*) *= *[\'\"]([a-zA-Z\d_]*)[\'\"]")
|
||||||
for fil in Path(mqtt.__file__).parent.rglob("*.py"):
|
for fil in Path(mqtt.__file__).parent.rglob("*.py"):
|
||||||
|
if fil.name == "trigger.py":
|
||||||
|
continue
|
||||||
with open(fil) as file:
|
with open(fil) as file:
|
||||||
matches = re.findall(regex, file.read())
|
matches = re.findall(regex, file.read())
|
||||||
for match in matches:
|
for match in matches:
|
||||||
|
|
|
@ -5,6 +5,7 @@ from unittest import mock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import homeassistant.components.automation as automation
|
import homeassistant.components.automation as automation
|
||||||
|
from homeassistant.components.template import trigger as template_trigger
|
||||||
from homeassistant.core import Context, callback
|
from homeassistant.core import Context, callback
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
@ -782,7 +783,7 @@ async def test_invalid_for_template_1(hass, calls):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with mock.patch.object(automation.template, "_LOGGER") as mock_logger:
|
with mock.patch.object(template_trigger, "_LOGGER") as mock_logger:
|
||||||
hass.states.async_set("test.entity", "world")
|
hass.states.async_set("test.entity", "world")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_logger.error.called
|
assert mock_logger.error.called
|
12
tests/helpers/test_trigger.py
Normal file
12
tests/helpers/test_trigger.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
"""The tests for the trigger helper."""
|
||||||
|
import pytest
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.helpers.trigger import async_validate_trigger_config
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bad_trigger_platform(hass):
|
||||||
|
"""Test bad trigger platform."""
|
||||||
|
with pytest.raises(vol.Invalid) as ex:
|
||||||
|
await async_validate_trigger_config(hass, [{"platform": "not_a_platform"}])
|
||||||
|
assert "Invalid platform 'not_a_platform' specified" in str(ex)
|
Loading…
Add table
Add a link
Reference in a new issue