mirror of
https://github.com/home-assistant/core.git
synced 2025-08-05 13:45:12 +02:00
fix tests
This commit is contained in:
@@ -1,486 +0,0 @@
|
|||||||
"""
|
|
||||||
Allow to setup simple automation rules via the config file.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
|
||||||
https://home-assistant.io/components/automation/
|
|
||||||
"""
|
|
||||||
import asyncio
|
|
||||||
from functools import partial
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.setup import async_prepare_setup_platform
|
|
||||||
from homeassistant.core import CoreState
|
|
||||||
from homeassistant import config as conf_util
|
|
||||||
from homeassistant.const import (
|
|
||||||
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
|
||||||
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID)
|
|
||||||
from homeassistant.components import logbook
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
|
||||||
from homeassistant.helpers import extract_domain_configs, script, condition
|
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
|
||||||
from homeassistant.helpers.restore_state import async_get_last_state
|
|
||||||
from homeassistant.loader import get_platform
|
|
||||||
from homeassistant.util.dt import utcnow
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.frontend import register_built_in_panel
|
|
||||||
|
|
||||||
DOMAIN = 'automation'
|
|
||||||
DEPENDENCIES = ['group']
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
|
||||||
|
|
||||||
GROUP_NAME_ALL_AUTOMATIONS = 'all automations'
|
|
||||||
|
|
||||||
CONF_ALIAS = 'alias'
|
|
||||||
CONF_HIDE_ENTITY = 'hide_entity'
|
|
||||||
|
|
||||||
CONF_CONDITION = 'condition'
|
|
||||||
CONF_ACTION = 'action'
|
|
||||||
CONF_TRIGGER = 'trigger'
|
|
||||||
CONF_CONDITION_TYPE = 'condition_type'
|
|
||||||
CONF_INITIAL_STATE = 'initial_state'
|
|
||||||
|
|
||||||
CONDITION_USE_TRIGGER_VALUES = 'use_trigger_values'
|
|
||||||
CONDITION_TYPE_AND = 'and'
|
|
||||||
CONDITION_TYPE_OR = 'or'
|
|
||||||
|
|
||||||
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
|
|
||||||
DEFAULT_HIDE_ENTITY = False
|
|
||||||
DEFAULT_INITIAL_STATE = True
|
|
||||||
|
|
||||||
ATTR_LAST_TRIGGERED = 'last_triggered'
|
|
||||||
ATTR_VARIABLES = 'variables'
|
|
||||||
SERVICE_TRIGGER = 'trigger'
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _platform_validator(config):
|
|
||||||
"""Validate it is a valid platform."""
|
|
||||||
platform = get_platform(DOMAIN, config[CONF_PLATFORM])
|
|
||||||
|
|
||||||
if not hasattr(platform, 'TRIGGER_SCHEMA'):
|
|
||||||
return config
|
|
||||||
|
|
||||||
return getattr(platform, 'TRIGGER_SCHEMA')(config)
|
|
||||||
|
|
||||||
|
|
||||||
_TRIGGER_SCHEMA = vol.All(
|
|
||||||
cv.ensure_list,
|
|
||||||
[
|
|
||||||
vol.All(
|
|
||||||
vol.Schema({
|
|
||||||
vol.Required(CONF_PLATFORM): cv.platform_validator(DOMAIN)
|
|
||||||
}, extra=vol.ALLOW_EXTRA),
|
|
||||||
_platform_validator
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = vol.Schema({
|
|
||||||
# str on purpose
|
|
||||||
CONF_ID: str,
|
|
||||||
CONF_ALIAS: cv.string,
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
|
|
||||||
SERVICE_SCHEMA = vol.Schema({
|
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
||||||
})
|
|
||||||
|
|
||||||
TRIGGER_SERVICE_SCHEMA = vol.Schema({
|
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
||||||
vol.Optional(ATTR_VARIABLES, default={}): dict,
|
|
||||||
})
|
|
||||||
|
|
||||||
RELOAD_SERVICE_SCHEMA = vol.Schema({})
|
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id):
|
|
||||||
"""
|
|
||||||
Return true if specified automation entity_id is on.
|
|
||||||
|
|
||||||
Async friendly.
|
|
||||||
"""
|
|
||||||
return hass.states.is_state(entity_id, STATE_ON)
|
|
||||||
|
|
||||||
|
|
||||||
def turn_on(hass, entity_id=None):
|
|
||||||
"""Turn on specified automation or all."""
|
|
||||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
|
||||||
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
|
|
||||||
|
|
||||||
|
|
||||||
def turn_off(hass, entity_id=None):
|
|
||||||
"""Turn off specified automation or all."""
|
|
||||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
|
||||||
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
|
|
||||||
|
|
||||||
|
|
||||||
def toggle(hass, entity_id=None):
|
|
||||||
"""Toggle specified automation or all."""
|
|
||||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
|
||||||
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
|
|
||||||
|
|
||||||
|
|
||||||
def trigger(hass, entity_id=None):
|
|
||||||
"""Trigger specified automation or all."""
|
|
||||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
|
||||||
hass.services.call(DOMAIN, SERVICE_TRIGGER, data)
|
|
||||||
|
|
||||||
|
|
||||||
def reload(hass):
|
|
||||||
"""Reload the automation from config."""
|
|
||||||
hass.services.call(DOMAIN, SERVICE_RELOAD)
|
|
||||||
|
|
||||||
|
|
||||||
def async_reload(hass):
|
|
||||||
"""Reload the automation from config.
|
|
||||||
|
|
||||||
Returns a coroutine object.
|
|
||||||
"""
|
|
||||||
return hass.services.async_call(DOMAIN, SERVICE_RELOAD)
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_setup(hass, config):
|
|
||||||
"""Set up the automation."""
|
|
||||||
component = EntityComponent(_LOGGER, DOMAIN, hass,
|
|
||||||
group_name=GROUP_NAME_ALL_AUTOMATIONS)
|
|
||||||
|
|
||||||
yield from _async_process_config(hass, config, component)
|
|
||||||
|
|
||||||
descriptions = yield from hass.async_add_job(
|
|
||||||
conf_util.load_yaml_config_file, os.path.join(
|
|
||||||
os.path.dirname(__file__), 'services.yaml')
|
|
||||||
)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def trigger_service_handler(service_call):
|
|
||||||
"""Handle automation triggers."""
|
|
||||||
tasks = []
|
|
||||||
for entity in component.async_extract_from_service(service_call):
|
|
||||||
tasks.append(entity.async_trigger(
|
|
||||||
service_call.data.get(ATTR_VARIABLES), True))
|
|
||||||
|
|
||||||
if tasks:
|
|
||||||
yield from asyncio.wait(tasks, loop=hass.loop)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def turn_onoff_service_handler(service_call):
|
|
||||||
"""Handle automation turn on/off service calls."""
|
|
||||||
tasks = []
|
|
||||||
method = 'async_{}'.format(service_call.service)
|
|
||||||
for entity in component.async_extract_from_service(service_call):
|
|
||||||
tasks.append(getattr(entity, method)())
|
|
||||||
|
|
||||||
if tasks:
|
|
||||||
yield from asyncio.wait(tasks, loop=hass.loop)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def toggle_service_handler(service_call):
|
|
||||||
"""Handle automation toggle service calls."""
|
|
||||||
tasks = []
|
|
||||||
for entity in component.async_extract_from_service(service_call):
|
|
||||||
if entity.is_on:
|
|
||||||
tasks.append(entity.async_turn_off())
|
|
||||||
else:
|
|
||||||
tasks.append(entity.async_turn_on())
|
|
||||||
|
|
||||||
if tasks:
|
|
||||||
yield from asyncio.wait(tasks, loop=hass.loop)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def reload_service_handler(service_call):
|
|
||||||
"""Remove all automations and load new ones from config."""
|
|
||||||
conf = yield from component.async_prepare_reload()
|
|
||||||
if conf is None:
|
|
||||||
return
|
|
||||||
yield from _async_process_config(hass, conf, component)
|
|
||||||
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_TRIGGER, trigger_service_handler,
|
|
||||||
descriptions.get(SERVICE_TRIGGER), schema=TRIGGER_SERVICE_SCHEMA)
|
|
||||||
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_RELOAD, reload_service_handler,
|
|
||||||
descriptions.get(SERVICE_RELOAD), schema=RELOAD_SERVICE_SCHEMA)
|
|
||||||
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_TOGGLE, toggle_service_handler,
|
|
||||||
descriptions.get(SERVICE_TOGGLE), schema=SERVICE_SCHEMA)
|
|
||||||
|
|
||||||
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF):
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, service, turn_onoff_service_handler,
|
|
||||||
descriptions.get(service), schema=SERVICE_SCHEMA)
|
|
||||||
|
|
||||||
if 'frontend' in hass.config.components:
|
|
||||||
register_built_in_panel(hass, 'automation', 'Automations',
|
|
||||||
'mdi:playlist-play')
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class AutomationEntity(ToggleEntity):
|
|
||||||
"""Entity to show status of entity."""
|
|
||||||
|
|
||||||
def __init__(self, automation_id, name, async_attach_triggers, cond_func,
|
|
||||||
async_action, hidden, initial_state):
|
|
||||||
"""Initialize an automation entity."""
|
|
||||||
self._id = automation_id
|
|
||||||
self._name = name
|
|
||||||
self._async_attach_triggers = async_attach_triggers
|
|
||||||
self._async_detach_triggers = None
|
|
||||||
self._cond_func = cond_func
|
|
||||||
self._async_action = async_action
|
|
||||||
self._last_triggered = None
|
|
||||||
self._hidden = hidden
|
|
||||||
self._initial_state = initial_state
|
|
||||||
|
|
||||||
@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."""
|
|
||||||
return {
|
|
||||||
ATTR_LAST_TRIGGERED: self._last_triggered
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hidden(self) -> bool:
|
|
||||||
"""Return True if the automation entity should be hidden from UIs."""
|
|
||||||
return self._hidden
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self) -> bool:
|
|
||||||
"""Return True if entity is on."""
|
|
||||||
return self._async_detach_triggers is not None
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_added_to_hass(self) -> None:
|
|
||||||
"""Startup with initial state or previous state."""
|
|
||||||
if self._initial_state is not None:
|
|
||||||
enable_automation = self._initial_state
|
|
||||||
_LOGGER.debug("Automation %s initial state %s from config "
|
|
||||||
"initial_state", self.entity_id, enable_automation)
|
|
||||||
else:
|
|
||||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
|
||||||
if state:
|
|
||||||
enable_automation = state.state == STATE_ON
|
|
||||||
self._last_triggered = state.attributes.get('last_triggered')
|
|
||||||
_LOGGER.debug("Automation %s initial state %s from recorder "
|
|
||||||
"last state %s", self.entity_id,
|
|
||||||
enable_automation, state)
|
|
||||||
else:
|
|
||||||
enable_automation = DEFAULT_INITIAL_STATE
|
|
||||||
_LOGGER.debug("Automation %s initial state %s from default "
|
|
||||||
"initial state", self.entity_id,
|
|
||||||
enable_automation)
|
|
||||||
|
|
||||||
if not enable_automation:
|
|
||||||
return
|
|
||||||
|
|
||||||
# HomeAssistant is starting up
|
|
||||||
elif self.hass.state == CoreState.not_running:
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_enable_automation(event):
|
|
||||||
"""Start automation on startup."""
|
|
||||||
yield from self.async_enable()
|
|
||||||
|
|
||||||
self.hass.bus.async_listen_once(
|
|
||||||
EVENT_HOMEASSISTANT_START, async_enable_automation)
|
|
||||||
|
|
||||||
# HomeAssistant is running
|
|
||||||
else:
|
|
||||||
yield from self.async_enable()
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_turn_on(self, **kwargs) -> None:
|
|
||||||
"""Turn the entity on and update the state."""
|
|
||||||
if self.is_on:
|
|
||||||
return
|
|
||||||
|
|
||||||
yield from self.async_enable()
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_turn_off(self, **kwargs) -> None:
|
|
||||||
"""Turn the entity off."""
|
|
||||||
if not self.is_on:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._async_detach_triggers()
|
|
||||||
self._async_detach_triggers = None
|
|
||||||
yield from self.async_update_ha_state()
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_trigger(self, variables, skip_condition=False):
|
|
||||||
"""Trigger automation.
|
|
||||||
|
|
||||||
This method is a coroutine.
|
|
||||||
"""
|
|
||||||
if skip_condition or self._cond_func(variables):
|
|
||||||
yield from self._async_action(self.entity_id, variables)
|
|
||||||
self._last_triggered = utcnow()
|
|
||||||
yield from self.async_update_ha_state()
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_remove(self):
|
|
||||||
"""Remove automation from HASS."""
|
|
||||||
yield from self.async_turn_off()
|
|
||||||
yield from super().async_remove()
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_enable(self):
|
|
||||||
"""Enable this automation entity.
|
|
||||||
|
|
||||||
This method is a coroutine.
|
|
||||||
"""
|
|
||||||
if self.is_on:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._async_detach_triggers = yield from self._async_attach_triggers(
|
|
||||||
self.async_trigger)
|
|
||||||
yield from self.async_update_ha_state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return automation attributes."""
|
|
||||||
if self._id is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return {
|
|
||||||
CONF_ID: self._id
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def _async_process_config(hass, config, component):
|
|
||||||
"""Process config and add automations.
|
|
||||||
|
|
||||||
This method is a coroutine.
|
|
||||||
"""
|
|
||||||
entities = []
|
|
||||||
|
|
||||||
for config_key in extract_domain_configs(config, DOMAIN):
|
|
||||||
conf = config[config_key]
|
|
||||||
|
|
||||||
for list_no, config_block in enumerate(conf):
|
|
||||||
automation_id = config_block.get(CONF_ID)
|
|
||||||
name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key,
|
|
||||||
list_no)
|
|
||||||
|
|
||||||
hidden = config_block[CONF_HIDE_ENTITY]
|
|
||||||
initial_state = config_block.get(CONF_INITIAL_STATE)
|
|
||||||
|
|
||||||
action = _async_get_action(hass, config_block.get(CONF_ACTION, {}),
|
|
||||||
name)
|
|
||||||
|
|
||||||
if CONF_CONDITION in config_block:
|
|
||||||
cond_func = _async_process_if(hass, config, config_block)
|
|
||||||
|
|
||||||
if cond_func is None:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
def cond_func(variables):
|
|
||||||
"""Condition will always pass."""
|
|
||||||
return True
|
|
||||||
|
|
||||||
async_attach_triggers = partial(
|
|
||||||
_async_process_trigger, hass, config,
|
|
||||||
config_block.get(CONF_TRIGGER, []), name
|
|
||||||
)
|
|
||||||
entity = AutomationEntity(
|
|
||||||
automation_id, name, async_attach_triggers, cond_func, action,
|
|
||||||
hidden, initial_state)
|
|
||||||
|
|
||||||
entities.append(entity)
|
|
||||||
|
|
||||||
if entities:
|
|
||||||
yield from component.async_add_entities(entities)
|
|
||||||
|
|
||||||
|
|
||||||
def _async_get_action(hass, config, name):
|
|
||||||
"""Return an action based on a configuration."""
|
|
||||||
script_obj = script.Script(hass, config, name)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def action(entity_id, variables):
|
|
||||||
"""Execute an action."""
|
|
||||||
_LOGGER.info('Executing %s', name)
|
|
||||||
logbook.async_log_entry(
|
|
||||||
hass, name, 'has been triggered', DOMAIN, entity_id)
|
|
||||||
yield from script_obj.async_run(variables)
|
|
||||||
|
|
||||||
return action
|
|
||||||
|
|
||||||
|
|
||||||
def _async_process_if(hass, config, p_config):
|
|
||||||
"""Process if checks."""
|
|
||||||
if_configs = p_config.get(CONF_CONDITION)
|
|
||||||
|
|
||||||
checks = []
|
|
||||||
for if_config in if_configs:
|
|
||||||
try:
|
|
||||||
checks.append(condition.async_from_config(if_config, False))
|
|
||||||
except HomeAssistantError as ex:
|
|
||||||
_LOGGER.warning('Invalid condition: %s', ex)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def if_action(variables=None):
|
|
||||||
"""AND all conditions."""
|
|
||||||
return all(check(hass, variables) for check in checks)
|
|
||||||
|
|
||||||
return if_action
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def _async_process_trigger(hass, config, trigger_configs, name, action):
|
|
||||||
"""Set up the triggers.
|
|
||||||
|
|
||||||
This method is a coroutine.
|
|
||||||
"""
|
|
||||||
removes = []
|
|
||||||
|
|
||||||
for conf in trigger_configs:
|
|
||||||
platform = yield from async_prepare_setup_platform(
|
|
||||||
hass, config, DOMAIN, conf.get(CONF_PLATFORM))
|
|
||||||
|
|
||||||
if platform is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
remove = yield from platform.async_trigger(hass, conf, action)
|
|
||||||
|
|
||||||
if not remove:
|
|
||||||
_LOGGER.error("Error setting up trigger %s", name)
|
|
||||||
continue
|
|
||||||
|
|
||||||
_LOGGER.info("Initialized trigger %s", name)
|
|
||||||
removes.append(remove)
|
|
||||||
|
|
||||||
if not removes:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def remove_triggers():
|
|
||||||
"""Remove attached triggers."""
|
|
||||||
for remove in removes:
|
|
||||||
remove()
|
|
||||||
|
|
||||||
return remove_triggers
|
|
@@ -32,7 +32,7 @@ class TestAutomation(unittest.TestCase):
|
|||||||
|
|
||||||
def test_service_data_not_a_dict(self):
|
def test_service_data_not_a_dict(self):
|
||||||
"""Test service data not dict."""
|
"""Test service data not dict."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, automation.DOMAIN):
|
||||||
assert setup_component(self.hass, automation.DOMAIN, {
|
assert setup_component(self.hass, automation.DOMAIN, {
|
||||||
automation.DOMAIN: {
|
automation.DOMAIN: {
|
||||||
'trigger': {
|
'trigger': {
|
||||||
@@ -390,23 +390,7 @@ class TestAutomation(unittest.TestCase):
|
|||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
assert automation.is_on(self.hass, entity_id)
|
assert automation.is_on(self.hass, entity_id)
|
||||||
|
|
||||||
@patch('homeassistant.config.load_yaml_config_file', autospec=True,
|
def test_reload_config_service(self):
|
||||||
return_value={
|
|
||||||
automation.DOMAIN: {
|
|
||||||
'alias': 'bye',
|
|
||||||
'trigger': {
|
|
||||||
'platform': 'event',
|
|
||||||
'event_type': 'test_event2',
|
|
||||||
},
|
|
||||||
'action': {
|
|
||||||
'service': 'test.automation',
|
|
||||||
'data_template': {
|
|
||||||
'event': '{{ trigger.event.event_type }}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
def test_reload_config_service(self, mock_load_yaml):
|
|
||||||
"""Test the reload config service."""
|
"""Test the reload config service."""
|
||||||
assert setup_component(self.hass, automation.DOMAIN, {
|
assert setup_component(self.hass, automation.DOMAIN, {
|
||||||
automation.DOMAIN: {
|
automation.DOMAIN: {
|
||||||
@@ -435,10 +419,25 @@ class TestAutomation(unittest.TestCase):
|
|||||||
assert len(self.calls) == 1
|
assert len(self.calls) == 1
|
||||||
assert self.calls[0].data.get('event') == 'test_event'
|
assert self.calls[0].data.get('event') == 'test_event'
|
||||||
|
|
||||||
automation.reload(self.hass)
|
with patch('homeassistant.config.load_yaml_config_file', autospec=True,
|
||||||
self.hass.block_till_done()
|
return_value={
|
||||||
# De-flake ?!
|
automation.DOMAIN: {
|
||||||
self.hass.block_till_done()
|
'alias': 'bye',
|
||||||
|
'trigger': {
|
||||||
|
'platform': 'event',
|
||||||
|
'event_type': 'test_event2',
|
||||||
|
},
|
||||||
|
'action': {
|
||||||
|
'service': 'test.automation',
|
||||||
|
'data_template': {
|
||||||
|
'event': '{{ trigger.event.event_type }}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}):
|
||||||
|
automation.reload(self.hass)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
# De-flake ?!
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
assert self.hass.states.get('automation.hello') is None
|
assert self.hass.states.get('automation.hello') is None
|
||||||
assert self.hass.states.get('automation.bye') is not None
|
assert self.hass.states.get('automation.bye') is not None
|
||||||
@@ -455,9 +454,7 @@ class TestAutomation(unittest.TestCase):
|
|||||||
assert len(self.calls) == 2
|
assert len(self.calls) == 2
|
||||||
assert self.calls[1].data.get('event') == 'test_event2'
|
assert self.calls[1].data.get('event') == 'test_event2'
|
||||||
|
|
||||||
@patch('homeassistant.config.load_yaml_config_file', autospec=True,
|
def test_reload_config_when_invalid_config(self):
|
||||||
return_value={automation.DOMAIN: 'not valid'})
|
|
||||||
def test_reload_config_when_invalid_config(self, mock_load_yaml):
|
|
||||||
"""Test the reload config service handling invalid config."""
|
"""Test the reload config service handling invalid config."""
|
||||||
with assert_setup_component(1):
|
with assert_setup_component(1):
|
||||||
assert setup_component(self.hass, automation.DOMAIN, {
|
assert setup_component(self.hass, automation.DOMAIN, {
|
||||||
@@ -483,8 +480,10 @@ class TestAutomation(unittest.TestCase):
|
|||||||
assert len(self.calls) == 1
|
assert len(self.calls) == 1
|
||||||
assert self.calls[0].data.get('event') == 'test_event'
|
assert self.calls[0].data.get('event') == 'test_event'
|
||||||
|
|
||||||
automation.reload(self.hass)
|
with patch('homeassistant.config.load_yaml_config_file', autospec=True,
|
||||||
self.hass.block_till_done()
|
return_value={automation.DOMAIN: 'not valid'}):
|
||||||
|
automation.reload(self.hass)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
assert self.hass.states.get('automation.hello') is None
|
assert self.hass.states.get('automation.hello') is None
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_template_state_text(self):
|
def test_template_state_text(self):
|
||||||
""""Test the state text of a template."""
|
""""Test the state text of a template."""
|
||||||
with assert_setup_component(1):
|
with assert_setup_component(1, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -74,7 +74,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_template_state_boolean_on(self):
|
def test_template_state_boolean_on(self):
|
||||||
"""Test the setting of the state with boolean on."""
|
"""Test the setting of the state with boolean on."""
|
||||||
with assert_setup_component(1):
|
with assert_setup_component(1, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -103,7 +103,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_template_state_boolean_off(self):
|
def test_template_state_boolean_off(self):
|
||||||
"""Test the setting of the state with off."""
|
"""Test the setting of the state with off."""
|
||||||
with assert_setup_component(1):
|
with assert_setup_component(1, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -132,7 +132,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_icon_template(self):
|
def test_icon_template(self):
|
||||||
"""Test icon template."""
|
"""Test icon template."""
|
||||||
with assert_setup_component(1):
|
with assert_setup_component(1, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -171,7 +171,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_template_syntax_error(self):
|
def test_template_syntax_error(self):
|
||||||
"""Test templating syntax error."""
|
"""Test templating syntax error."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -199,7 +199,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_invalid_name_does_not_create(self):
|
def test_invalid_name_does_not_create(self):
|
||||||
"""Test invalid name."""
|
"""Test invalid name."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -227,7 +227,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_invalid_switch_does_not_create(self):
|
def test_invalid_switch_does_not_create(self):
|
||||||
"""Test invalid switch."""
|
"""Test invalid switch."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -258,7 +258,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_missing_template_does_not_create(self):
|
def test_missing_template_does_not_create(self):
|
||||||
"""Test missing template."""
|
"""Test missing template."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -286,7 +286,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_missing_on_does_not_create(self):
|
def test_missing_on_does_not_create(self):
|
||||||
"""Test missing on."""
|
"""Test missing on."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
@@ -314,7 +314,7 @@ class TestTemplateSwitch:
|
|||||||
|
|
||||||
def test_missing_off_does_not_create(self):
|
def test_missing_off_does_not_create(self):
|
||||||
"""Test missing off."""
|
"""Test missing off."""
|
||||||
with assert_setup_component(0):
|
with assert_setup_component(0, 'switch'):
|
||||||
assert setup.setup_component(self.hass, 'switch', {
|
assert setup.setup_component(self.hass, 'switch', {
|
||||||
'switch': {
|
'switch': {
|
||||||
'platform': 'template',
|
'platform': 'template',
|
||||||
|
Reference in New Issue
Block a user