2019-02-13 20:21:14 +00:00
|
|
|
"""Allow to set up simple automation rules via the config file."""
|
2016-10-01 08:22:13 +00:00
|
|
|
import asyncio
|
2016-08-26 06:25:57 +00:00
|
|
|
from functools import partial
|
2018-05-01 18:57:30 +00:00
|
|
|
import importlib
|
2015-01-16 07:32:27 +00:00
|
|
|
import logging
|
|
|
|
|
2016-04-04 19:18:58 +00:00
|
|
|
import voluptuous as vol
|
|
|
|
|
2019-09-10 23:56:28 +00:00
|
|
|
from homeassistant.components.device_automation.exceptions import (
|
|
|
|
InvalidDeviceAutomationConfig,
|
|
|
|
)
|
2016-08-26 06:25:57 +00:00
|
|
|
from homeassistant.const import (
|
2019-07-31 19:25:30 +00:00
|
|
|
ATTR_ENTITY_ID,
|
|
|
|
ATTR_NAME,
|
|
|
|
CONF_ID,
|
|
|
|
CONF_PLATFORM,
|
|
|
|
EVENT_AUTOMATION_TRIGGERED,
|
|
|
|
EVENT_HOMEASSISTANT_START,
|
|
|
|
SERVICE_RELOAD,
|
|
|
|
SERVICE_TOGGLE,
|
|
|
|
SERVICE_TURN_OFF,
|
|
|
|
SERVICE_TURN_ON,
|
|
|
|
STATE_ON,
|
|
|
|
)
|
2019-03-21 05:56:46 +00:00
|
|
|
from homeassistant.core import Context, CoreState
|
2016-04-28 10:03:57 +00:00
|
|
|
from homeassistant.exceptions import HomeAssistantError
|
2019-03-21 05:56:46 +00:00
|
|
|
from homeassistant.helpers import condition, extract_domain_configs, script
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
2019-07-23 17:07:13 +00:00
|
|
|
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
|
2016-08-26 06:25:57 +00:00
|
|
|
from homeassistant.helpers.entity import ToggleEntity
|
|
|
|
from homeassistant.helpers.entity_component import EntityComponent
|
2018-11-28 12:16:43 +00:00
|
|
|
from homeassistant.helpers.restore_state import RestoreEntity
|
2019-03-21 05:56:46 +00:00
|
|
|
from homeassistant.loader import bind_hass
|
2019-07-11 03:42:38 +00:00
|
|
|
from homeassistant.util.dt import parse_datetime, utcnow
|
2015-01-16 07:32:27 +00:00
|
|
|
|
2019-08-12 03:38:18 +00:00
|
|
|
|
|
|
|
# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs
|
|
|
|
# mypy: no-check-untyped-defs, no-warn-return-any
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
DOMAIN = "automation"
|
|
|
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
2015-01-16 07:32:27 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
GROUP_NAME_ALL_AUTOMATIONS = "all automations"
|
2016-10-05 04:20:48 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_ALIAS = "alias"
|
2019-09-17 19:12:54 +00:00
|
|
|
CONF_DESCRIPTION = "description"
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_HIDE_ENTITY = "hide_entity"
|
2015-09-15 05:05:40 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_CONDITION = "condition"
|
|
|
|
CONF_ACTION = "action"
|
|
|
|
CONF_TRIGGER = "trigger"
|
|
|
|
CONF_CONDITION_TYPE = "condition_type"
|
|
|
|
CONF_INITIAL_STATE = "initial_state"
|
2015-09-15 15:56:06 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
CONDITION_USE_TRIGGER_VALUES = "use_trigger_values"
|
|
|
|
CONDITION_TYPE_AND = "and"
|
|
|
|
CONDITION_TYPE_OR = "or"
|
2015-09-15 05:51:28 +00:00
|
|
|
|
|
|
|
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
|
2016-09-20 06:39:07 +00:00
|
|
|
DEFAULT_HIDE_ENTITY = False
|
2016-10-04 06:41:08 +00:00
|
|
|
DEFAULT_INITIAL_STATE = True
|
2015-01-16 07:32:27 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
ATTR_LAST_TRIGGERED = "last_triggered"
|
|
|
|
ATTR_VARIABLES = "variables"
|
|
|
|
SERVICE_TRIGGER = "trigger"
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2015-01-16 07:32:27 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2016-10-01 08:22:13 +00:00
|
|
|
def _platform_validator(config):
|
2019-06-10 22:36:11 +00:00
|
|
|
"""Validate it is a valid platform."""
|
2018-05-01 18:57:30 +00:00
|
|
|
try:
|
2019-07-31 19:25:30 +00:00
|
|
|
platform = importlib.import_module(
|
|
|
|
".{}".format(config[CONF_PLATFORM]), __name__
|
|
|
|
)
|
2018-05-01 18:57:30 +00:00
|
|
|
except ImportError:
|
2019-07-31 19:25:30 +00:00
|
|
|
raise vol.Invalid("Invalid platform specified") from None
|
2016-04-04 19:18:58 +00:00
|
|
|
|
2018-05-01 18:57:30 +00:00
|
|
|
return platform.TRIGGER_SCHEMA(config)
|
2016-04-04 19:18:58 +00:00
|
|
|
|
2016-11-19 05:47:59 +00:00
|
|
|
|
2016-04-04 19:18:58 +00:00
|
|
|
_TRIGGER_SCHEMA = vol.All(
|
|
|
|
cv.ensure_list,
|
|
|
|
[
|
|
|
|
vol.All(
|
2019-07-31 19:25:30 +00:00
|
|
|
vol.Schema({vol.Required(CONF_PLATFORM): str}, extra=vol.ALLOW_EXTRA),
|
|
|
|
_platform_validator,
|
|
|
|
)
|
|
|
|
],
|
2016-04-04 19:18:58 +00:00
|
|
|
)
|
|
|
|
|
2016-10-01 08:22:13 +00:00
|
|
|
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
|
2016-04-04 19:18:58 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
PLATFORM_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
# str on purpose
|
|
|
|
CONF_ID: str,
|
|
|
|
CONF_ALIAS: cv.string,
|
2019-09-17 19:12:54 +00:00
|
|
|
vol.Optional(CONF_DESCRIPTION): cv.string,
|
2019-07-31 19:25:30 +00:00
|
|
|
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
|
|
|
|
vol.Optional(CONF_HIDE_ENTITY, default=DEFAULT_HIDE_ENTITY): cv.boolean,
|
|
|
|
vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA,
|
|
|
|
vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
|
|
|
|
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
TRIGGER_SERVICE_SCHEMA = ENTITY_SERVICE_SCHEMA.extend(
|
|
|
|
{vol.Optional(ATTR_VARIABLES, default={}): dict}
|
|
|
|
)
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2016-09-04 15:15:52 +00:00
|
|
|
RELOAD_SERVICE_SCHEMA = vol.Schema({})
|
|
|
|
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2017-07-16 17:14:46 +00:00
|
|
|
@bind_hass
|
2017-04-04 06:11:39 +00:00
|
|
|
def is_on(hass, entity_id):
|
2016-08-26 06:25:57 +00:00
|
|
|
"""
|
|
|
|
Return true if specified automation entity_id is on.
|
|
|
|
|
2017-04-04 06:11:39 +00:00
|
|
|
Async friendly.
|
2016-08-26 06:25:57 +00:00
|
|
|
"""
|
2017-04-04 06:11:39 +00:00
|
|
|
return hass.states.is_state(entity_id, STATE_ON)
|
2016-08-26 06:25:57 +00:00
|
|
|
|
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def async_setup(hass, config):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the automation."""
|
2019-07-31 19:25:30 +00:00
|
|
|
component = EntityComponent(
|
|
|
|
_LOGGER, DOMAIN, hass, group_name=GROUP_NAME_ALL_AUTOMATIONS
|
|
|
|
)
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
await _async_process_config(hass, config, component)
|
2015-09-14 05:25:42 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def trigger_service_handler(service_call):
|
2016-08-26 06:25:57 +00:00
|
|
|
"""Handle automation triggers."""
|
2016-10-27 07:16:23 +00:00
|
|
|
tasks = []
|
2019-03-04 17:51:12 +00:00
|
|
|
for entity in await component.async_extract_from_service(service_call):
|
2019-07-31 19:25:30 +00:00
|
|
|
tasks.append(
|
|
|
|
entity.async_trigger(
|
|
|
|
service_call.data.get(ATTR_VARIABLES),
|
|
|
|
skip_condition=True,
|
|
|
|
context=service_call.context,
|
|
|
|
)
|
|
|
|
)
|
2016-12-18 20:57:31 +00:00
|
|
|
|
|
|
|
if tasks:
|
2019-05-23 04:09:59 +00:00
|
|
|
await asyncio.wait(tasks)
|
2015-09-14 07:02:33 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def turn_onoff_service_handler(service_call):
|
2016-10-04 05:39:27 +00:00
|
|
|
"""Handle automation turn on/off service calls."""
|
2016-10-27 07:16:23 +00:00
|
|
|
tasks = []
|
2019-08-23 16:53:33 +00:00
|
|
|
method = f"async_{service_call.service}"
|
2019-03-04 17:51:12 +00:00
|
|
|
for entity in await component.async_extract_from_service(service_call):
|
2016-10-27 07:16:23 +00:00
|
|
|
tasks.append(getattr(entity, method)())
|
2016-12-18 20:57:31 +00:00
|
|
|
|
|
|
|
if tasks:
|
2019-05-23 04:09:59 +00:00
|
|
|
await asyncio.wait(tasks)
|
2016-10-04 05:39:27 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def toggle_service_handler(service_call):
|
2016-10-04 05:39:27 +00:00
|
|
|
"""Handle automation toggle service calls."""
|
2016-10-27 07:16:23 +00:00
|
|
|
tasks = []
|
2019-03-04 17:51:12 +00:00
|
|
|
for entity in await component.async_extract_from_service(service_call):
|
2016-10-04 05:39:27 +00:00
|
|
|
if entity.is_on:
|
2016-10-27 07:16:23 +00:00
|
|
|
tasks.append(entity.async_turn_off())
|
2016-10-04 05:39:27 +00:00
|
|
|
else:
|
2016-10-27 07:16:23 +00:00
|
|
|
tasks.append(entity.async_turn_on())
|
2016-12-18 20:57:31 +00:00
|
|
|
|
|
|
|
if tasks:
|
2019-05-23 04:09:59 +00:00
|
|
|
await asyncio.wait(tasks)
|
2015-09-14 05:25:42 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def reload_service_handler(service_call):
|
2016-09-04 15:15:52 +00:00
|
|
|
"""Remove all automations and load new ones from config."""
|
2018-09-04 19:16:24 +00:00
|
|
|
conf = await component.async_prepare_reload()
|
2016-09-04 15:15:52 +00:00
|
|
|
if conf is None:
|
|
|
|
return
|
2018-09-04 19:16:24 +00:00
|
|
|
await _async_process_config(hass, conf, component)
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2016-10-27 07:16:23 +00:00
|
|
|
hass.services.async_register(
|
2019-07-31 19:25:30 +00:00
|
|
|
DOMAIN, SERVICE_TRIGGER, trigger_service_handler, schema=TRIGGER_SERVICE_SCHEMA
|
|
|
|
)
|
2015-01-16 07:32:27 +00:00
|
|
|
|
2016-10-27 07:16:23 +00:00
|
|
|
hass.services.async_register(
|
2019-07-31 19:25:30 +00:00
|
|
|
DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=RELOAD_SERVICE_SCHEMA
|
|
|
|
)
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2016-10-27 07:16:23 +00:00
|
|
|
hass.services.async_register(
|
2019-07-31 19:25:30 +00:00
|
|
|
DOMAIN, SERVICE_TOGGLE, toggle_service_handler, schema=ENTITY_SERVICE_SCHEMA
|
|
|
|
)
|
2016-10-04 05:39:27 +00:00
|
|
|
|
|
|
|
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF):
|
2016-10-27 07:16:23 +00:00
|
|
|
hass.services.async_register(
|
2019-07-31 19:25:30 +00:00
|
|
|
DOMAIN, service, turn_onoff_service_handler, schema=ENTITY_SERVICE_SCHEMA
|
|
|
|
)
|
2015-01-16 07:32:27 +00:00
|
|
|
|
2015-09-15 05:05:40 +00:00
|
|
|
return True
|
2015-01-16 07:32:27 +00:00
|
|
|
|
|
|
|
|
2018-11-28 12:16:43 +00:00
|
|
|
class AutomationEntity(ToggleEntity, RestoreEntity):
|
2016-08-26 06:25:57 +00:00
|
|
|
"""Entity to show status of entity."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
automation_id,
|
|
|
|
name,
|
|
|
|
async_attach_triggers,
|
|
|
|
cond_func,
|
|
|
|
async_action,
|
|
|
|
hidden,
|
|
|
|
initial_state,
|
|
|
|
):
|
2016-08-26 06:25:57 +00:00
|
|
|
"""Initialize an automation entity."""
|
2017-05-10 01:44:00 +00:00
|
|
|
self._id = automation_id
|
2016-08-26 06:25:57 +00:00
|
|
|
self._name = name
|
2016-10-01 08:22:13 +00:00
|
|
|
self._async_attach_triggers = async_attach_triggers
|
2016-10-01 21:11:07 +00:00
|
|
|
self._async_detach_triggers = None
|
2016-08-26 06:25:57 +00:00
|
|
|
self._cond_func = cond_func
|
2016-10-01 08:22:13 +00:00
|
|
|
self._async_action = async_action
|
2016-08-26 06:25:57 +00:00
|
|
|
self._last_triggered = None
|
2016-09-20 06:39:07 +00:00
|
|
|
self._hidden = hidden
|
2017-03-01 04:33:19 +00:00
|
|
|
self._initial_state = initial_state
|
2019-06-08 06:08:22 +00:00
|
|
|
self._is_enabled = False
|
2016-08-26 06:25:57 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Name of the automation."""
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def should_poll(self):
|
|
|
|
"""No polling needed for automation entities."""
|
|
|
|
return False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def state_attributes(self):
|
|
|
|
"""Return the entity state attributes."""
|
2019-07-31 19:25:30 +00:00
|
|
|
return {ATTR_LAST_TRIGGERED: self._last_triggered}
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2016-09-20 06:39:07 +00:00
|
|
|
@property
|
|
|
|
def hidden(self) -> bool:
|
|
|
|
"""Return True if the automation entity should be hidden from UIs."""
|
|
|
|
return self._hidden
|
|
|
|
|
2016-08-26 06:25:57 +00:00
|
|
|
@property
|
|
|
|
def is_on(self) -> bool:
|
|
|
|
"""Return True if entity is on."""
|
2019-07-31 19:25:30 +00:00
|
|
|
return self._async_detach_triggers is not None or self._is_enabled
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def async_added_to_hass(self) -> None:
|
2017-03-04 23:19:01 +00:00
|
|
|
"""Startup with initial state or previous state."""
|
2018-11-28 12:16:43 +00:00
|
|
|
await super().async_added_to_hass()
|
2019-06-08 19:48:37 +00:00
|
|
|
|
|
|
|
state = await self.async_get_last_state()
|
|
|
|
if state:
|
|
|
|
enable_automation = state.state == STATE_ON
|
2019-07-31 19:25:30 +00:00
|
|
|
last_triggered = state.attributes.get("last_triggered")
|
2019-07-11 03:42:38 +00:00
|
|
|
if last_triggered is not None:
|
|
|
|
self._last_triggered = parse_datetime(last_triggered)
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Loaded automation %s with state %s from state "
|
|
|
|
" storage last state %s",
|
|
|
|
self.entity_id,
|
|
|
|
enable_automation,
|
|
|
|
state,
|
|
|
|
)
|
2019-06-08 19:48:37 +00:00
|
|
|
else:
|
|
|
|
enable_automation = DEFAULT_INITIAL_STATE
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Automation %s not in state storage, state %s from " "default is used.",
|
|
|
|
self.entity_id,
|
|
|
|
enable_automation,
|
|
|
|
)
|
2019-06-08 19:48:37 +00:00
|
|
|
|
2017-04-04 06:11:39 +00:00
|
|
|
if self._initial_state is not None:
|
|
|
|
enable_automation = self._initial_state
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Automation %s initial state %s overridden from "
|
|
|
|
"config initial_state",
|
|
|
|
self.entity_id,
|
|
|
|
enable_automation,
|
|
|
|
)
|
2017-03-24 22:52:14 +00:00
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
if enable_automation:
|
2018-09-04 19:16:24 +00:00
|
|
|
await self.async_enable()
|
2017-03-24 22:52:14 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def async_turn_on(self, **kwargs) -> None:
|
2016-10-01 21:11:07 +00:00
|
|
|
"""Turn the entity on and update the state."""
|
2018-09-04 19:16:24 +00:00
|
|
|
await self.async_enable()
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def async_turn_off(self, **kwargs) -> None:
|
2016-08-26 06:25:57 +00:00
|
|
|
"""Turn the entity off."""
|
2019-06-08 06:08:22 +00:00
|
|
|
await self.async_disable()
|
2016-10-01 08:22:13 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
async def async_trigger(self, variables, skip_condition=False, context=None):
|
2016-10-04 05:39:27 +00:00
|
|
|
"""Trigger automation.
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2016-10-04 05:39:27 +00:00
|
|
|
This method is a coroutine.
|
|
|
|
"""
|
2019-03-01 18:08:38 +00:00
|
|
|
if not skip_condition and not self._cond_func(variables):
|
|
|
|
return
|
|
|
|
|
|
|
|
# Create a new context referring to the old context.
|
|
|
|
parent_id = None if context is None else context.id
|
|
|
|
trigger_context = Context(parent_id=parent_id)
|
|
|
|
|
|
|
|
self.async_set_context(trigger_context)
|
2019-07-31 19:25:30 +00:00
|
|
|
self.hass.bus.async_fire(
|
|
|
|
EVENT_AUTOMATION_TRIGGERED,
|
|
|
|
{ATTR_NAME: self._name, ATTR_ENTITY_ID: self.entity_id},
|
|
|
|
context=trigger_context,
|
|
|
|
)
|
2019-03-01 18:08:38 +00:00
|
|
|
await self._async_action(self.entity_id, variables, trigger_context)
|
|
|
|
self._last_triggered = utcnow()
|
|
|
|
await self.async_update_ha_state()
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def async_will_remove_from_hass(self):
|
2018-01-23 06:54:41 +00:00
|
|
|
"""Remove listeners when removing automation from HASS."""
|
2018-11-28 12:16:43 +00:00
|
|
|
await super().async_will_remove_from_hass()
|
2019-06-08 06:08:22 +00:00
|
|
|
await self.async_disable()
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def async_enable(self):
|
2016-10-04 05:39:27 +00:00
|
|
|
"""Enable this automation entity.
|
|
|
|
|
|
|
|
This method is a coroutine.
|
|
|
|
"""
|
2019-06-08 06:08:22 +00:00
|
|
|
if self._is_enabled:
|
2016-10-01 21:11:07 +00:00
|
|
|
return
|
|
|
|
|
2019-06-08 06:08:22 +00:00
|
|
|
self._is_enabled = True
|
|
|
|
|
|
|
|
# HomeAssistant is starting up
|
|
|
|
if self.hass.state != CoreState.not_running:
|
|
|
|
self._async_detach_triggers = await self._async_attach_triggers(
|
2019-07-31 19:25:30 +00:00
|
|
|
self.async_trigger
|
|
|
|
)
|
2019-06-08 06:08:22 +00:00
|
|
|
self.async_write_ha_state()
|
|
|
|
return
|
|
|
|
|
|
|
|
async def async_enable_automation(event):
|
|
|
|
"""Start automation on startup."""
|
|
|
|
# Don't do anything if no longer enabled or already attached
|
2019-07-31 19:25:30 +00:00
|
|
|
if not self._is_enabled or self._async_detach_triggers is not None:
|
2019-06-08 06:08:22 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
self._async_detach_triggers = await self._async_attach_triggers(
|
2019-07-31 19:25:30 +00:00
|
|
|
self.async_trigger
|
|
|
|
)
|
2019-06-08 06:08:22 +00:00
|
|
|
|
|
|
|
self.hass.bus.async_listen_once(
|
2019-07-31 19:25:30 +00:00
|
|
|
EVENT_HOMEASSISTANT_START, async_enable_automation
|
|
|
|
)
|
2019-06-08 06:08:22 +00:00
|
|
|
self.async_write_ha_state()
|
|
|
|
|
|
|
|
async def async_disable(self):
|
|
|
|
"""Disable the automation entity."""
|
|
|
|
if not self._is_enabled:
|
|
|
|
return
|
|
|
|
|
|
|
|
self._is_enabled = False
|
|
|
|
|
|
|
|
if self._async_detach_triggers is not None:
|
|
|
|
self._async_detach_triggers()
|
|
|
|
self._async_detach_triggers = None
|
|
|
|
|
|
|
|
self.async_write_ha_state()
|
2016-10-01 21:11:07 +00:00
|
|
|
|
2017-05-10 01:44:00 +00:00
|
|
|
@property
|
|
|
|
def device_state_attributes(self):
|
|
|
|
"""Return automation attributes."""
|
|
|
|
if self._id is None:
|
|
|
|
return None
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
return {CONF_ID: self._id}
|
2017-05-10 01:44:00 +00:00
|
|
|
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def _async_process_config(hass, config, component):
|
2016-10-04 05:39:27 +00:00
|
|
|
"""Process config and add automations.
|
|
|
|
|
|
|
|
This method is a coroutine.
|
|
|
|
"""
|
2016-10-01 16:19:20 +00:00
|
|
|
entities = []
|
2016-09-04 15:15:52 +00:00
|
|
|
|
|
|
|
for config_key in extract_domain_configs(config, DOMAIN):
|
|
|
|
conf = config[config_key]
|
|
|
|
|
|
|
|
for list_no, config_block in enumerate(conf):
|
2017-05-10 01:44:00 +00:00
|
|
|
automation_id = config_block.get(CONF_ID)
|
2019-08-23 16:53:33 +00:00
|
|
|
name = config_block.get(CONF_ALIAS) or f"{config_key} {list_no}"
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2016-09-20 06:39:07 +00:00
|
|
|
hidden = config_block[CONF_HIDE_ENTITY]
|
2017-04-04 06:11:39 +00:00
|
|
|
initial_state = config_block.get(CONF_INITIAL_STATE)
|
2016-09-20 06:39:07 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
action = _async_get_action(hass, config_block.get(CONF_ACTION, {}), name)
|
2016-09-04 15:15:52 +00:00
|
|
|
|
|
|
|
if CONF_CONDITION in config_block:
|
2019-09-05 14:49:32 +00:00
|
|
|
cond_func = await _async_process_if(hass, config, config_block)
|
2016-09-04 15:15:52 +00:00
|
|
|
|
|
|
|
if cond_func is None:
|
|
|
|
continue
|
|
|
|
else:
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2016-09-04 15:15:52 +00:00
|
|
|
def cond_func(variables):
|
|
|
|
"""Condition will always pass."""
|
|
|
|
return True
|
|
|
|
|
2016-10-01 08:22:13 +00:00
|
|
|
async_attach_triggers = partial(
|
2019-07-31 19:25:30 +00:00
|
|
|
_async_process_trigger,
|
|
|
|
hass,
|
|
|
|
config,
|
|
|
|
config_block.get(CONF_TRIGGER, []),
|
|
|
|
name,
|
2017-03-01 04:33:19 +00:00
|
|
|
)
|
|
|
|
entity = AutomationEntity(
|
2019-07-31 19:25:30 +00:00
|
|
|
automation_id,
|
|
|
|
name,
|
|
|
|
async_attach_triggers,
|
|
|
|
cond_func,
|
|
|
|
action,
|
|
|
|
hidden,
|
|
|
|
initial_state,
|
|
|
|
)
|
2017-03-01 04:33:19 +00:00
|
|
|
|
2016-10-01 21:11:07 +00:00
|
|
|
entities.append(entity)
|
2016-09-04 15:15:52 +00:00
|
|
|
|
2016-11-06 17:26:40 +00:00
|
|
|
if entities:
|
2018-09-04 19:16:24 +00:00
|
|
|
await component.async_add_entities(entities)
|
2016-10-01 16:19:20 +00:00
|
|
|
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2016-10-01 08:22:13 +00:00
|
|
|
def _async_get_action(hass, config, name):
|
2016-03-07 16:14:55 +00:00
|
|
|
"""Return an action based on a configuration."""
|
2016-04-22 02:36:14 +00:00
|
|
|
script_obj = script.Script(hass, config, name)
|
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def action(entity_id, variables, context):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Execute an action."""
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.info("Executing %s", name)
|
2018-12-13 11:21:16 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
await script_obj.async_run(variables, context)
|
|
|
|
except Exception as err: # pylint: disable=broad-except
|
|
|
|
script_obj.async_log_exception(
|
2019-08-23 16:53:33 +00:00
|
|
|
_LOGGER, f"Error while executing automation {entity_id}", err
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2015-01-16 07:32:27 +00:00
|
|
|
|
|
|
|
return action
|
2015-09-14 05:25:42 +00:00
|
|
|
|
|
|
|
|
2019-09-05 14:49:32 +00:00
|
|
|
async def _async_process_if(hass, config, p_config):
|
2016-03-07 19:20:07 +00:00
|
|
|
"""Process if checks."""
|
2015-09-15 15:56:06 +00:00
|
|
|
if_configs = p_config.get(CONF_CONDITION)
|
|
|
|
|
2015-09-15 05:51:28 +00:00
|
|
|
checks = []
|
2015-09-14 05:25:42 +00:00
|
|
|
for if_config in if_configs:
|
2016-04-28 10:03:57 +00:00
|
|
|
try:
|
2019-09-05 14:49:32 +00:00
|
|
|
checks.append(await condition.async_from_config(hass, if_config, False))
|
2016-04-28 10:03:57 +00:00
|
|
|
except HomeAssistantError as ex:
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.warning("Invalid condition: %s", ex)
|
2016-10-01 08:22:13 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
def if_action(variables=None):
|
|
|
|
"""AND all conditions."""
|
|
|
|
return all(check(hass, variables) for check in checks)
|
2015-09-15 05:51:28 +00:00
|
|
|
|
|
|
|
return if_action
|
2015-09-14 05:25:42 +00:00
|
|
|
|
2015-09-15 05:05:40 +00:00
|
|
|
|
2018-09-04 19:16:24 +00:00
|
|
|
async def _async_process_trigger(hass, config, trigger_configs, name, action):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the triggers.
|
2016-10-04 05:39:27 +00:00
|
|
|
|
|
|
|
This method is a coroutine.
|
|
|
|
"""
|
2016-08-26 06:25:57 +00:00
|
|
|
removes = []
|
2019-07-31 19:25:30 +00:00
|
|
|
info = {"name": name}
|
2016-08-26 06:25:57 +00:00
|
|
|
|
2015-09-15 05:05:40 +00:00
|
|
|
for conf in trigger_configs:
|
2019-07-31 19:25:30 +00:00
|
|
|
platform = importlib.import_module(".{}".format(conf[CONF_PLATFORM]), __name__)
|
2015-09-14 05:25:42 +00:00
|
|
|
|
2019-09-10 23:56:28 +00:00
|
|
|
try:
|
|
|
|
remove = await platform.async_trigger(hass, conf, action, info)
|
|
|
|
except InvalidDeviceAutomationConfig:
|
|
|
|
remove = False
|
2016-08-26 06:25:57 +00:00
|
|
|
|
|
|
|
if not remove:
|
2016-10-01 08:22:13 +00:00
|
|
|
_LOGGER.error("Error setting up trigger %s", name)
|
2016-08-26 06:25:57 +00:00
|
|
|
continue
|
|
|
|
|
2016-10-01 08:22:13 +00:00
|
|
|
_LOGGER.info("Initialized trigger %s", name)
|
2016-08-26 06:25:57 +00:00
|
|
|
removes.append(remove)
|
|
|
|
|
|
|
|
if not removes:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def remove_triggers():
|
|
|
|
"""Remove attached triggers."""
|
|
|
|
for remove in removes:
|
|
|
|
remove()
|
|
|
|
|
|
|
|
return remove_triggers
|