2020-08-17 16:54:56 +00:00
|
|
|
"""Triggers."""
|
2021-03-17 17:34:19 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2020-08-17 16:54:56 +00:00
|
|
|
import asyncio
|
2021-09-29 14:32:11 +00:00
|
|
|
from collections.abc import Callable
|
2022-03-18 08:25:22 +00:00
|
|
|
import functools
|
2020-08-17 16:54:56 +00:00
|
|
|
import logging
|
2021-09-29 14:32:11 +00:00
|
|
|
from typing import Any
|
2020-08-17 16:54:56 +00:00
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
2022-04-15 16:33:09 +00:00
|
|
|
from homeassistant.const import CONF_ENABLED, CONF_ID, CONF_PLATFORM, CONF_VARIABLES
|
2022-03-18 08:25:22 +00:00
|
|
|
from homeassistant.core import CALLBACK_TYPE, Context, HomeAssistant, callback
|
2021-02-08 09:50:38 +00:00
|
|
|
from homeassistant.exceptions import HomeAssistantError
|
2020-08-17 16:54:56 +00:00
|
|
|
from homeassistant.loader import IntegrationNotFound, async_get_integration
|
|
|
|
|
2021-12-23 19:14:47 +00:00
|
|
|
from .typing import ConfigType, TemplateVarsType
|
|
|
|
|
2020-08-17 16:54:56 +00:00
|
|
|
_PLATFORM_ALIASES = {
|
|
|
|
"device_automation": ("device",),
|
|
|
|
"homeassistant": ("event", "numeric_state", "state", "time_pattern", "time"),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-27 11:55:24 +00:00
|
|
|
async def _async_get_trigger_platform(hass: HomeAssistant, config: ConfigType) -> Any:
|
2021-08-20 04:43:04 +00:00
|
|
|
platform_and_sub_type = config[CONF_PLATFORM].split(".")
|
|
|
|
platform = platform_and_sub_type[0]
|
2020-08-17 16:54:56 +00:00
|
|
|
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(
|
2021-03-27 11:55:24 +00:00
|
|
|
hass: HomeAssistant, trigger_config: list[ConfigType]
|
2021-03-17 17:34:19 +00:00
|
|
|
) -> list[ConfigType]:
|
2020-08-17 16:54:56 +00:00
|
|
|
"""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
|
|
|
|
|
|
|
|
|
2022-03-18 08:25:22 +00:00
|
|
|
def _trigger_action_wrapper(
|
|
|
|
hass: HomeAssistant, action: Callable, conf: ConfigType
|
|
|
|
) -> Callable:
|
|
|
|
"""Wrap trigger action with extra vars if configured."""
|
|
|
|
if CONF_VARIABLES not in conf:
|
|
|
|
return action
|
|
|
|
|
|
|
|
@functools.wraps(action)
|
|
|
|
async def with_vars(
|
|
|
|
run_variables: dict[str, Any], context: Context | None = None
|
|
|
|
) -> None:
|
|
|
|
"""Wrap action with extra vars."""
|
|
|
|
trigger_variables = conf[CONF_VARIABLES]
|
|
|
|
run_variables.update(trigger_variables.async_render(hass, run_variables))
|
|
|
|
await action(run_variables, context)
|
|
|
|
|
|
|
|
return with_vars
|
|
|
|
|
|
|
|
|
2020-08-17 16:54:56 +00:00
|
|
|
async def async_initialize_triggers(
|
2021-03-27 11:55:24 +00:00
|
|
|
hass: HomeAssistant,
|
2021-03-17 17:34:19 +00:00
|
|
|
trigger_config: list[ConfigType],
|
2020-08-17 16:54:56 +00:00
|
|
|
action: Callable,
|
|
|
|
domain: str,
|
|
|
|
name: str,
|
|
|
|
log_cb: Callable,
|
|
|
|
home_assistant_start: bool = False,
|
2021-09-04 00:25:51 +00:00
|
|
|
variables: TemplateVarsType = None,
|
2021-03-17 17:34:19 +00:00
|
|
|
) -> CALLBACK_TYPE | None:
|
2020-08-17 16:54:56 +00:00
|
|
|
"""Initialize triggers."""
|
|
|
|
|
|
|
|
triggers = []
|
2021-03-31 12:56:04 +00:00
|
|
|
for idx, conf in enumerate(trigger_config):
|
2022-04-15 16:33:09 +00:00
|
|
|
# Skip triggers that are not enabled
|
|
|
|
if not conf.get(CONF_ENABLED, True):
|
|
|
|
continue
|
|
|
|
|
2020-08-17 16:54:56 +00:00
|
|
|
platform = await _async_get_trigger_platform(hass, conf)
|
2021-06-11 13:05:57 +00:00
|
|
|
trigger_id = conf.get(CONF_ID, f"{idx}")
|
2021-06-14 15:09:20 +00:00
|
|
|
trigger_idx = f"{idx}"
|
|
|
|
trigger_data = {"id": trigger_id, "idx": trigger_idx}
|
2021-09-04 00:25:51 +00:00
|
|
|
info = {
|
|
|
|
"domain": domain,
|
|
|
|
"name": name,
|
|
|
|
"home_assistant_start": home_assistant_start,
|
|
|
|
"variables": variables,
|
|
|
|
"trigger_data": trigger_data,
|
|
|
|
}
|
2022-03-18 08:25:22 +00:00
|
|
|
|
|
|
|
triggers.append(
|
|
|
|
platform.async_attach_trigger(
|
|
|
|
hass, conf, _trigger_action_wrapper(hass, action, conf), info
|
|
|
|
)
|
|
|
|
)
|
2020-08-17 16:54:56 +00:00
|
|
|
|
2021-01-20 21:13:21 +00:00
|
|
|
attach_results = await asyncio.gather(*triggers, return_exceptions=True)
|
2022-02-18 17:15:57 +00:00
|
|
|
removes: list[Callable[[], None]] = []
|
2021-01-20 21:13:21 +00:00
|
|
|
|
|
|
|
for result in attach_results:
|
2021-02-08 09:50:38 +00:00
|
|
|
if isinstance(result, HomeAssistantError):
|
|
|
|
log_cb(logging.ERROR, f"Got error '{result}' when setting up triggers for")
|
|
|
|
elif isinstance(result, Exception):
|
2021-01-20 21:13:21 +00:00
|
|
|
log_cb(logging.ERROR, "Error setting up trigger", exc_info=result)
|
|
|
|
elif result is None:
|
|
|
|
log_cb(
|
|
|
|
logging.ERROR, "Unknown error while setting up trigger (empty result)"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
removes.append(result)
|
2020-08-17 16:54:56 +00:00
|
|
|
|
|
|
|
if not removes:
|
|
|
|
return None
|
|
|
|
|
|
|
|
log_cb(logging.INFO, "Initialized trigger")
|
|
|
|
|
|
|
|
@callback
|
2022-02-18 17:15:57 +00:00
|
|
|
def remove_triggers() -> None:
|
2020-08-17 16:54:56 +00:00
|
|
|
"""Remove triggers."""
|
|
|
|
for remove in removes:
|
|
|
|
remove()
|
|
|
|
|
|
|
|
return remove_triggers
|