Add automation config validation

* Add automation config validation

* Remove unnecessary dict validator

* Downgrade voluptuous to 0.8.9

* Fix linting

* Address issues
This commit is contained in:
Paulus Schoutsen 2016-04-04 12:18:58 -07:00
parent cbe9a7d2a3
commit 8ef542927f
24 changed files with 570 additions and 443 deletions

View File

@ -72,6 +72,7 @@ omit =
homeassistant/components/camera/foscam.py homeassistant/components/camera/foscam.py
homeassistant/components/camera/generic.py homeassistant/components/camera/generic.py
homeassistant/components/camera/mjpeg.py homeassistant/components/camera/mjpeg.py
homeassistant/components/camera/rpi_camera.py
homeassistant/components/device_tracker/actiontec.py homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py homeassistant/components/device_tracker/asuswrt.py

View File

@ -21,7 +21,7 @@ import homeassistant.util.package as pkg_util
from homeassistant.const import ( from homeassistant.const import (
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED, CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
TEMP_CELCIUS, TEMP_FAHRENHEIT, __version__) TEMP_CELCIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__)
from homeassistant.helpers import ( from homeassistant.helpers import (
event_decorators, service, config_per_platform, extract_domain_configs) event_decorators, service, config_per_platform, extract_domain_configs)
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -32,7 +32,6 @@ _CURRENT_SETUP = []
ATTR_COMPONENT = 'component' ATTR_COMPONENT = 'component'
PLATFORM_FORMAT = '{}.{}'
ERROR_LOG_FILENAME = 'home-assistant.log' ERROR_LOG_FILENAME = 'home-assistant.log'
@ -117,6 +116,13 @@ def _setup_component(hass, domain, config):
domain, ex, p_config) domain, ex, p_config)
return False return False
# Not all platform components follow same pattern for platforms
# Sof if p_name is None we are not going to validate platform
# (the automation component is one of them)
if p_name is None:
platforms.append(p_validated)
continue
platform = prepare_setup_platform(hass, config, domain, platform = prepare_setup_platform(hass, config, domain,
p_name) p_name)
@ -176,7 +182,7 @@ def prepare_setup_platform(hass, config, domain, platform_name):
platform_path = PLATFORM_FORMAT.format(domain, platform_name) platform_path = PLATFORM_FORMAT.format(domain, platform_name)
platform = loader.get_component(platform_path) platform = loader.get_platform(domain, platform_name)
# Not found # Not found
if platform is None: if platform is None:

View File

@ -6,13 +6,15 @@ https://home-assistant.io/components/automation/
""" """
import logging import logging
import voluptuous as vol
from homeassistant.bootstrap import prepare_setup_platform from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.const import CONF_PLATFORM from homeassistant.const import CONF_PLATFORM
from homeassistant.components import logbook from homeassistant.components import logbook
from homeassistant.helpers import extract_domain_configs from homeassistant.helpers import extract_domain_configs
from homeassistant.helpers.service import (call_from_config, from homeassistant.helpers.service import call_from_config
validate_service_call) from homeassistant.loader import get_platform
import homeassistant.helpers.config_validation as cv
DOMAIN = 'automation' DOMAIN = 'automation'
@ -31,17 +33,72 @@ CONDITION_TYPE_OR = 'or'
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
METHOD_TRIGGER = 'trigger'
METHOD_IF_ACTION = 'if_action'
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def _platform_validator(method, schema):
"""Generate platform validator for different steps."""
def validator(config):
"""Validate it is a valid platform."""
platform = get_platform(DOMAIN, config[CONF_PLATFORM])
if not hasattr(platform, method):
raise vol.Invalid('invalid method platform')
if not hasattr(platform, schema):
return config
print('validating config', method, config)
return getattr(platform, schema)(config)
return validator
_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(METHOD_TRIGGER, 'TRIGGER_SCHEMA')
),
]
)
_CONDITION_SCHEMA = vol.Any(
CONDITION_USE_TRIGGER_VALUES,
vol.All(
cv.ensure_list,
[
vol.All(
vol.Schema({
vol.Required(CONF_PLATFORM): cv.platform_validator(DOMAIN),
}, extra=vol.ALLOW_EXTRA),
_platform_validator(METHOD_IF_ACTION, 'IF_ACTION_SCHEMA'),
)
]
)
)
PLATFORM_SCHEMA = vol.Schema({
CONF_ALIAS: cv.string,
vol.Required(CONF_TRIGGER): _TRIGGER_SCHEMA,
vol.Required(CONF_CONDITION_TYPE, default=DEFAULT_CONDITION_TYPE):
vol.All(vol.Lower, vol.Any(CONDITION_TYPE_AND, CONDITION_TYPE_OR)),
CONF_CONDITION: _CONDITION_SCHEMA,
vol.Required(CONF_ACTION): cv.SERVICE_SCHEMA,
})
def setup(hass, config): def setup(hass, config):
"""Setup the automation.""" """Setup the automation."""
for config_key in extract_domain_configs(config, DOMAIN): for config_key in extract_domain_configs(config, DOMAIN):
conf = config[config_key] conf = config[config_key]
if not isinstance(conf, list):
conf = [conf]
for list_no, config_block in enumerate(conf): for list_no, config_block in enumerate(conf):
name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key,
list_no)) list_no))
@ -54,10 +111,7 @@ def _setup_automation(hass, config_block, name, config):
"""Setup one instance of automation.""" """Setup one instance of automation."""
action = _get_action(hass, config_block.get(CONF_ACTION, {}), name) action = _get_action(hass, config_block.get(CONF_ACTION, {}), name)
if action is None: if CONF_CONDITION in config_block:
return False
if CONF_CONDITION in config_block or CONF_CONDITION_TYPE in config_block:
action = _process_if(hass, config, config_block, action) action = _process_if(hass, config, config_block, action)
if action is None: if action is None:
@ -70,11 +124,6 @@ def _setup_automation(hass, config_block, name, config):
def _get_action(hass, config, name): def _get_action(hass, config, name):
"""Return an action based on a configuration.""" """Return an action based on a configuration."""
validation_error = validate_service_call(config)
if validation_error:
_LOGGER.error(validation_error)
return None
def action(): def action():
"""Action to be executed.""" """Action to be executed."""
_LOGGER.info('Executing %s', name) _LOGGER.info('Executing %s', name)
@ -96,12 +145,9 @@ def _process_if(hass, config, p_config, action):
if use_trigger: if use_trigger:
if_configs = p_config[CONF_TRIGGER] if_configs = p_config[CONF_TRIGGER]
if isinstance(if_configs, dict):
if_configs = [if_configs]
checks = [] checks = []
for if_config in if_configs: for if_config in if_configs:
platform = _resolve_platform('if_action', hass, config, platform = _resolve_platform(METHOD_IF_ACTION, hass, config,
if_config.get(CONF_PLATFORM)) if_config.get(CONF_PLATFORM))
if platform is None: if platform is None:
continue continue
@ -134,7 +180,7 @@ def _process_trigger(hass, config, trigger_configs, name, action):
trigger_configs = [trigger_configs] trigger_configs = [trigger_configs]
for conf in trigger_configs: for conf in trigger_configs:
platform = _resolve_platform('trigger', hass, config, platform = _resolve_platform(METHOD_TRIGGER, hass, config,
conf.get(CONF_PLATFORM)) conf.get(CONF_PLATFORM))
if platform is None: if platform is None:
continue continue

View File

@ -4,16 +4,17 @@ Offer state listening automation rules.
For more details about this automation rule, please refer to the documentation For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#state-trigger at https://home-assistant.io/components/automation/#state-trigger
""" """
import logging
from datetime import timedelta from datetime import timedelta
import homeassistant.util.dt as dt_util import voluptuous as vol
import homeassistant.util.dt as dt_util
from homeassistant.const import ( from homeassistant.const import (
EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL) EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL, CONF_PLATFORM)
from homeassistant.components.automation.time import ( from homeassistant.components.automation.time import (
CONF_HOURS, CONF_MINUTES, CONF_SECONDS) CONF_HOURS, CONF_MINUTES, CONF_SECONDS)
from homeassistant.helpers.event import track_state_change, track_point_in_time from homeassistant.helpers.event import track_state_change, track_point_in_time
import homeassistant.helpers.config_validation as cv
CONF_ENTITY_ID = "entity_id" CONF_ENTITY_ID = "entity_id"
CONF_FROM = "from" CONF_FROM = "from"
@ -21,6 +22,33 @@ CONF_TO = "to"
CONF_STATE = "state" CONF_STATE = "state"
CONF_FOR = "for" CONF_FOR = "for"
BASE_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'state',
vol.Required(CONF_ENTITY_ID): cv.entity_id,
# These are str on purpose. Want to catch YAML conversions
CONF_STATE: str,
CONF_FOR: vol.All(vol.Schema({
CONF_HOURS: vol.Coerce(int),
CONF_MINUTES: vol.Coerce(int),
CONF_SECONDS: vol.Coerce(int),
}), cv.has_at_least_one_key(CONF_HOURS, CONF_MINUTES, CONF_SECONDS)),
})
TRIGGER_SCHEMA = vol.Schema(vol.All(
BASE_SCHEMA.extend({
# These are str on purpose. Want to catch YAML conversions
CONF_FROM: str,
CONF_TO: str,
}),
vol.Any(cv.key_dependency(CONF_FOR, CONF_TO),
cv.key_dependency(CONF_FOR, CONF_STATE))
))
IF_ACTION_SCHEMA = vol.Schema(vol.All(
BASE_SCHEMA,
cv.key_dependency(CONF_FOR, CONF_STATE)
))
def get_time_config(config): def get_time_config(config):
"""Helper function to extract the time specified in the configuration.""" """Helper function to extract the time specified in the configuration."""
@ -31,18 +59,6 @@ def get_time_config(config):
minutes = config[CONF_FOR].get(CONF_MINUTES) minutes = config[CONF_FOR].get(CONF_MINUTES)
seconds = config[CONF_FOR].get(CONF_SECONDS) seconds = config[CONF_FOR].get(CONF_SECONDS)
if hours is None and minutes is None and seconds is None:
logging.getLogger(__name__).error(
"Received invalid value for '%s': %s",
config[CONF_FOR], CONF_FOR)
return None
if config.get(CONF_TO) is None and config.get(CONF_STATE) is None:
logging.getLogger(__name__).error(
"For: requires a to: value'%s': %s",
config[CONF_FOR], CONF_FOR)
return None
return timedelta(hours=(hours or 0.0), return timedelta(hours=(hours or 0.0),
minutes=(minutes or 0.0), minutes=(minutes or 0.0),
seconds=(seconds or 0.0)) seconds=(seconds or 0.0))
@ -51,24 +67,10 @@ def get_time_config(config):
def trigger(hass, config, action): def trigger(hass, config, action):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
if entity_id is None:
logging.getLogger(__name__).error(
"Missing trigger configuration key %s", CONF_ENTITY_ID)
return None
from_state = config.get(CONF_FROM, MATCH_ALL) from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
time_delta = get_time_config(config) time_delta = get_time_config(config)
if isinstance(from_state, bool) or isinstance(to_state, bool):
logging.getLogger(__name__).error(
'Config error. Surround to/from values with quotes.')
return None
if CONF_FOR in config and time_delta is None:
return None
def state_automation_listener(entity, from_s, to_s): def state_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action.""" """Listen for state changes and calls action."""
def state_for_listener(now): def state_for_listener(now):
@ -105,18 +107,7 @@ def if_action(hass, config):
"""Wrap action method with state based condition.""" """Wrap action method with state based condition."""
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
state = config.get(CONF_STATE) state = config.get(CONF_STATE)
if entity_id is None or state is None:
logging.getLogger(__name__).error(
"Missing if-condition configuration key %s or %s", CONF_ENTITY_ID,
CONF_STATE)
return None
time_delta = get_time_config(config) time_delta = get_time_config(config)
if CONF_FOR in config and time_delta is None:
return None
state = str(state)
def if_state(): def if_state():
"""Test if condition.""" """Test if condition."""

View File

@ -4,12 +4,16 @@ Offer sun based automation rules.
For more details about this automation rule, please refer to the documentation For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#sun-trigger at https://home-assistant.io/components/automation/#sun-trigger
""" """
import logging
from datetime import timedelta from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.const import CONF_PLATFORM
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.components import sun from homeassistant.components import sun
from homeassistant.helpers.event import track_sunrise, track_sunset from homeassistant.helpers.event import track_sunrise, track_sunset
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['sun'] DEPENDENCIES = ['sun']
@ -26,22 +30,30 @@ EVENT_SUNRISE = 'sunrise'
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_SUN_EVENT = vol.All(vol.Lower, vol.Any(EVENT_SUNRISE, EVENT_SUNSET))
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'sun',
vol.Required(CONF_EVENT): _SUN_EVENT,
vol.Required(CONF_OFFSET, default=timedelta(0)): cv.time_offset,
})
IF_ACTION_SCHEMA = vol.All(
vol.Schema({
vol.Required(CONF_PLATFORM): 'sun',
CONF_BEFORE: _SUN_EVENT,
CONF_AFTER: _SUN_EVENT,
vol.Required(CONF_BEFORE_OFFSET, default=timedelta(0)): cv.time_offset,
vol.Required(CONF_AFTER_OFFSET, default=timedelta(0)): cv.time_offset,
}),
cv.has_at_least_one_key(CONF_BEFORE, CONF_AFTER),
)
def trigger(hass, config, action): def trigger(hass, config, action):
"""Listen for events based on configuration.""" """Listen for events based on configuration."""
event = config.get(CONF_EVENT) event = config.get(CONF_EVENT)
offset = config.get(CONF_OFFSET)
if event is None:
_LOGGER.error("Missing configuration key %s", CONF_EVENT)
return False
event = event.lower()
if event not in (EVENT_SUNRISE, EVENT_SUNSET):
_LOGGER.error("Invalid value for %s: %s", CONF_EVENT, event)
return False
offset = _parse_offset(config.get(CONF_OFFSET))
if offset is False:
return False
# Do something to call action # Do something to call action
if event == EVENT_SUNRISE: if event == EVENT_SUNRISE:
@ -56,26 +68,8 @@ def if_action(hass, config):
"""Wrap action method with sun based condition.""" """Wrap action method with sun based condition."""
before = config.get(CONF_BEFORE) before = config.get(CONF_BEFORE)
after = config.get(CONF_AFTER) after = config.get(CONF_AFTER)
before_offset = config.get(CONF_BEFORE_OFFSET)
# Make sure required configuration keys are present after_offset = config.get(CONF_AFTER_OFFSET)
if before is None and after is None:
logging.getLogger(__name__).error(
"Missing if-condition configuration key %s or %s",
CONF_BEFORE, CONF_AFTER)
return None
# Make sure configuration keys have the right value
if before not in (None, EVENT_SUNRISE, EVENT_SUNSET) or \
after not in (None, EVENT_SUNRISE, EVENT_SUNSET):
logging.getLogger(__name__).error(
"%s and %s can only be set to %s or %s",
CONF_BEFORE, CONF_AFTER, EVENT_SUNRISE, EVENT_SUNSET)
return None
before_offset = _parse_offset(config.get(CONF_BEFORE_OFFSET))
after_offset = _parse_offset(config.get(CONF_AFTER_OFFSET))
if before_offset is False or after_offset is False:
return None
if before is None: if before is None:
def before_func(): def before_func():
@ -120,27 +114,3 @@ def if_action(hass, config):
return True return True
return time_if return time_if
def _parse_offset(raw_offset):
"""Parse the offset."""
if raw_offset is None:
return timedelta(0)
negative_offset = False
if raw_offset.startswith('-'):
negative_offset = True
raw_offset = raw_offset[1:]
try:
(hour, minute, second) = [int(x) for x in raw_offset.split(':')]
except ValueError:
_LOGGER.error('Could not parse offset %s', raw_offset)
return False
offset = timedelta(hours=hour, minutes=minute, seconds=second)
if negative_offset:
offset *= -1
return offset

View File

@ -6,21 +6,27 @@ at https://home-assistant.io/components/automation/#template-trigger
""" """
import logging import logging
from homeassistant.const import CONF_VALUE_TEMPLATE, EVENT_STATE_CHANGED import voluptuous as vol
from homeassistant.const import (
CONF_VALUE_TEMPLATE, EVENT_STATE_CHANGED, CONF_PLATFORM)
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.helpers import template from homeassistant.helpers import template
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = IF_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'template',
vol.Required(CONF_VALUE_TEMPLATE): cv.template,
})
def trigger(hass, config, action): def trigger(hass, config, action):
"""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)
if value_template is None:
_LOGGER.error("Missing configuration key %s", CONF_VALUE_TEMPLATE)
return False
# Local variable to keep track of if the action has already been triggered # Local variable to keep track of if the action has already been triggered
already_triggered = False already_triggered = False
@ -44,10 +50,6 @@ def if_action(hass, config):
"""Wrap action method with state based condition.""" """Wrap action method with state based condition."""
value_template = config.get(CONF_VALUE_TEMPLATE) value_template = config.get(CONF_VALUE_TEMPLATE)
if value_template is None:
_LOGGER.error("Missing configuration key %s", CONF_VALUE_TEMPLATE)
return False
return lambda: _check_template(hass, value_template) return lambda: _check_template(hass, value_template)

View File

@ -4,12 +4,13 @@ Offer zone automation rules.
For more details about this automation rule, please refer to the documentation For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#zone-trigger at https://home-assistant.io/components/automation/#zone-trigger
""" """
import logging import voluptuous as vol
from homeassistant.components import zone from homeassistant.components import zone
from homeassistant.const import ( from homeassistant.const import (
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, MATCH_ALL) ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, MATCH_ALL, CONF_PLATFORM)
from homeassistant.helpers.event import track_state_change from homeassistant.helpers.event import track_state_change
import homeassistant.helpers.config_validation as cv
CONF_ENTITY_ID = "entity_id" CONF_ENTITY_ID = "entity_id"
CONF_ZONE = "zone" CONF_ZONE = "zone"
@ -18,19 +19,26 @@ EVENT_ENTER = "enter"
EVENT_LEAVE = "leave" EVENT_LEAVE = "leave"
DEFAULT_EVENT = EVENT_ENTER DEFAULT_EVENT = EVENT_ENTER
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'zone',
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ZONE): cv.entity_id,
vol.Required(CONF_EVENT, default=DEFAULT_EVENT):
vol.Any(EVENT_ENTER, EVENT_LEAVE),
})
IF_ACTION_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'zone',
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ZONE): cv.entity_id,
})
def trigger(hass, config, action): def trigger(hass, config, action):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
zone_entity_id = config.get(CONF_ZONE) zone_entity_id = config.get(CONF_ZONE)
event = config.get(CONF_EVENT)
if entity_id is None or zone_entity_id is None:
logging.getLogger(__name__).error(
"Missing trigger configuration key %s or %s", CONF_ENTITY_ID,
CONF_ZONE)
return False
event = config.get(CONF_EVENT, DEFAULT_EVENT)
def zone_automation_listener(entity, from_s, to_s): def zone_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action.""" """Listen for state changes and calls action."""
@ -59,12 +67,6 @@ def if_action(hass, config):
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
zone_entity_id = config.get(CONF_ZONE) zone_entity_id = config.get(CONF_ZONE)
if entity_id is None or zone_entity_id is None:
logging.getLogger(__name__).error(
"Missing condition configuration key %s or %s", CONF_ENTITY_ID,
CONF_ZONE)
return False
def if_in_zone(): def if_in_zone():
"""Test if condition.""" """Test if condition."""
return _in_zone(hass, zone_entity_id, hass.states.get(entity_id)) return _in_zone(hass, zone_entity_id, hass.states.get(entity_id))

View File

@ -77,9 +77,9 @@ _DELAY_SCHEMA = {
'minutes': vol.All(vol.Coerce(int), vol.Range(min=0)), 'minutes': vol.All(vol.Coerce(int), vol.Range(min=0)),
'hours': vol.All(vol.Coerce(int), vol.Range(min=0)), 'hours': vol.All(vol.Coerce(int), vol.Range(min=0)),
'weeks': vol.All(vol.Coerce(int), vol.Range(min=0)), 'weeks': vol.All(vol.Coerce(int), vol.Range(min=0)),
}, cv.has_at_least_one_key([ }, cv.has_at_least_one_key(
'days', 'seconds', 'microseconds', 'milliseconds', 'minutes', 'hours', 'days', 'seconds', 'microseconds', 'milliseconds', 'minutes', 'hours',
'weeks'])) 'weeks'))
} }
_EVENT_SCHEMA = cv.EVENT_SCHEMA.extend({ _EVENT_SCHEMA = cv.EVENT_SCHEMA.extend({
@ -97,7 +97,7 @@ _SCRIPT_ENTRY_SCHEMA = vol.Schema({
}) })
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
vol.Required(DOMAIN): cv.DictValidator(_SCRIPT_ENTRY_SCHEMA, cv.slug) vol.Required(DOMAIN): {cv.slug: _SCRIPT_ENTRY_SCHEMA}
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)

View File

@ -4,6 +4,8 @@
__version__ = "0.17.0.dev0" __version__ = "0.17.0.dev0"
REQUIRED_PYTHON_VER = (3, 4) REQUIRED_PYTHON_VER = (3, 4)
PLATFORM_FORMAT = '{}.{}'
# Can be used to specify a catch all when registering state or event listeners. # Can be used to specify a catch all when registering state or event listeners.
MATCH_ALL = '*' MATCH_ALL = '*'

View File

@ -1,7 +1,10 @@
"""Helpers for config validation using voluptuous.""" """Helpers for config validation using voluptuous."""
from datetime import timedelta
import jinja2 import jinja2
import voluptuous as vol import voluptuous as vol
from homeassistant.loader import get_platform
from homeassistant.const import ( from homeassistant.const import (
CONF_PLATFORM, CONF_SCAN_INTERVAL, TEMP_CELCIUS, TEMP_FAHRENHEIT) CONF_PLATFORM, CONF_SCAN_INTERVAL, TEMP_CELCIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import valid_entity_id from homeassistant.helpers.entity import valid_entity_id
@ -11,7 +14,6 @@ from homeassistant.util import slugify
# pylint: disable=invalid-name # pylint: disable=invalid-name
# Home Assistant types # Home Assistant types
byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255)) byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255))
small_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1)) small_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
latitude = vol.All(vol.Coerce(float), vol.Range(min=-90, max=90), latitude = vol.All(vol.Coerce(float), vol.Range(min=-90, max=90),
@ -32,6 +34,11 @@ def boolean(value):
return bool(value) return bool(value)
def ensure_list(value):
"""Wrap value in list if it is not one."""
return value if isinstance(value, list) else [value]
def entity_id(value): def entity_id(value):
"""Validate Entity ID.""" """Validate Entity ID."""
if valid_entity_id(value): if valid_entity_id(value):
@ -61,6 +68,59 @@ def icon(value):
raise vol.Invalid('Icons should start with prefix "mdi:"') raise vol.Invalid('Icons should start with prefix "mdi:"')
def time_offset(value):
"""Validate and transform time offset."""
if not isinstance(value, str):
raise vol.Invalid('offset should be a string')
negative_offset = False
if value.startswith('-'):
negative_offset = True
value = value[1:]
elif value.startswith('+'):
value = value[1:]
try:
parsed = [int(x) for x in value.split(':')]
except ValueError:
raise vol.Invalid(
'offset {} should be format HH:MM or HH:MM:SS'.format(value))
if len(parsed) == 2:
hour, minute = parsed
second = 0
elif len(parsed) == 3:
hour, minute, second = parsed
else:
raise vol.Invalid(
'offset {} should be format HH:MM or HH:MM:SS'.format(value))
offset = timedelta(hours=hour, minutes=minute, seconds=second)
if negative_offset:
offset *= -1
return offset
def match_all(value):
"""Validator that matches all values."""
return value
def platform_validator(domain):
"""Validate if platform exists for given domain."""
def validator(value):
"""Test if platform exists."""
if value is None:
raise vol.Invalid('platform cannot be None')
if get_platform(domain, str(value)):
return value
raise vol.Invalid(
'platform {} does not exist for {}'.format(value, domain))
return validator
def service(value): def service(value):
"""Validate service.""" """Validate service."""
# Services use same format as entities so we can use same helper. # Services use same format as entities so we can use same helper.
@ -122,60 +182,24 @@ def time_zone(value):
# Validator helpers # Validator helpers
# pylint: disable=too-few-public-methods def key_dependency(key, dependency):
"""Validate that all dependencies exist for key."""
def validator(value):
"""Test dependencies."""
if not isinstance(value, dict):
raise vol.Invalid('key dependencies require a dict')
print(key, value)
if key in value and dependency not in value:
raise vol.Invalid('dependency violation - key "{}" requires '
'key "{}" to exist'.format(key, dependency))
class DictValidator(object): return value
"""Validate keys and values in a dictionary.""" return validator
def __init__(self, value_validator=None, key_validator=None):
"""Initialize the dict validator."""
if value_validator is not None:
value_validator = vol.Schema(value_validator)
self.value_validator = value_validator
if key_validator is not None:
key_validator = vol.Schema(key_validator)
self.key_validator = key_validator
def __call__(self, obj):
"""Validate the dict."""
if not isinstance(obj, dict):
raise vol.Invalid('Expected dictionary.')
errors = []
# So we keep it an OrderedDict if it is one
result = obj.__class__()
for key, value in obj.items():
if self.key_validator is not None:
try:
key = self.key_validator(key)
except vol.Invalid as ex:
errors.append('key {} is invalid ({})'.format(key, ex))
if self.value_validator is not None:
try:
value = self.value_validator(value)
except vol.Invalid as ex:
errors.append(
'key {} contains invalid value ({})'.format(key, ex))
if not errors:
result[key] = value
if errors:
raise vol.Invalid(
'invalid dictionary: {}'.format(', '.join(errors)))
return result
# Adapted from: # Adapted from:
# https://github.com/alecthomas/voluptuous/issues/115#issuecomment-144464666 # https://github.com/alecthomas/voluptuous/issues/115#issuecomment-144464666
def has_at_least_one_key(keys): def has_at_least_one_key(*keys):
"""Validator that at least one key exists.""" """Validator that at least one key exists."""
def validate(obj): def validate(obj):
"""Test keys exist in dict.""" """Test keys exist in dict."""
@ -206,5 +230,6 @@ SERVICE_SCHEMA = vol.All(vol.Schema({
vol.Exclusive('service', 'service name'): service, vol.Exclusive('service', 'service name'): service,
vol.Exclusive('service_template', 'service name'): string, vol.Exclusive('service_template', 'service name'): string,
vol.Exclusive('data', 'service data'): dict, vol.Exclusive('data', 'service data'): dict,
vol.Exclusive('data_template', 'service data'): DictValidator(template), vol.Exclusive('data_template', 'service data'): {match_all: template},
}), has_at_least_one_key(['service', 'service_template'])) 'entity_id': entity_ids,
}), has_at_least_one_key('service', 'service_template'))

View File

@ -16,6 +16,7 @@ import os
import pkgutil import pkgutil
import sys import sys
from homeassistant.const import PLATFORM_FORMAT
from homeassistant.util import OrderedSet from homeassistant.util import OrderedSet
PREPARED = False PREPARED = False
@ -77,6 +78,11 @@ def set_component(comp_name, component):
_COMPONENT_CACHE[comp_name] = component _COMPONENT_CACHE[comp_name] = component
def get_platform(domain, platform):
"""Try to load specified platform."""
return get_component(PLATFORM_FORMAT.format(domain, platform))
def get_component(comp_name): def get_component(comp_name):
"""Try to load specified component. """Try to load specified component.

View File

@ -5,7 +5,7 @@ pytz>=2016.3
pip>=7.0.0 pip>=7.0.0
vincenty==0.1.4 vincenty==0.1.4
jinja2>=2.8 jinja2>=2.8
voluptuous==0.8.10 voluptuous==0.8.9
# homeassistant.components.isy994 # homeassistant.components.isy994
PyISY==1.0.5 PyISY==1.0.5

View File

@ -17,7 +17,7 @@ REQUIRES = [
'pip>=7.0.0', 'pip>=7.0.0',
'vincenty==0.1.4', 'vincenty==0.1.4',
'jinja2>=2.8', 'jinja2>=2.8',
'voluptuous==0.8.10', 'voluptuous==0.8.9',
] ]
setup( setup(

View File

@ -1,6 +1,7 @@
"""The tests for the Event automation.""" """The tests for the Event automation."""
import unittest import unittest
from homeassistant.bootstrap import _setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -12,6 +13,7 @@ class TestAutomationEvent(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.calls = [] self.calls = []
def record_call(service): def record_call(service):
@ -26,7 +28,7 @@ class TestAutomationEvent(unittest.TestCase):
def test_if_fires_on_event(self): def test_if_fires_on_event(self):
"""Test the firing of events.""" """Test the firing of events."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -36,7 +38,7 @@ class TestAutomationEvent(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.bus.fire('test_event') self.hass.bus.fire('test_event')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -44,7 +46,7 @@ class TestAutomationEvent(unittest.TestCase):
def test_if_fires_on_event_with_data(self): def test_if_fires_on_event_with_data(self):
"""Test the firing of events with data.""" """Test the firing of events with data."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -55,7 +57,7 @@ class TestAutomationEvent(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.bus.fire('test_event', {'some_attr': 'some_value', self.hass.bus.fire('test_event', {'some_attr': 'some_value',
'another': 'value'}) 'another': 'value'})
@ -64,7 +66,7 @@ class TestAutomationEvent(unittest.TestCase):
def test_if_not_fires_if_event_data_not_matches(self): def test_if_not_fires_if_event_data_not_matches(self):
"""Test firing of event if no match.""" """Test firing of event if no match."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -75,7 +77,7 @@ class TestAutomationEvent(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.bus.fire('test_event', {'some_attr': 'some_other_value'}) self.hass.bus.fire('test_event', {'some_attr': 'some_other_value'})
self.hass.pool.block_till_done() self.hass.pool.block_till_done()

View File

@ -1,6 +1,7 @@
"""The tests for the automation component.""" """The tests for the automation component."""
import unittest import unittest
from homeassistant.bootstrap import _setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
@ -13,6 +14,7 @@ class TestAutomation(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.calls = [] self.calls = []
def record_call(service): def record_call(service):
@ -26,7 +28,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."""
automation.setup(self.hass, { assert not _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -39,13 +41,9 @@ class TestAutomation(unittest.TestCase):
} }
}) })
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_service_specify_data(self): def test_service_specify_data(self):
"""Test service data.""" """Test service data."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -65,7 +63,7 @@ class TestAutomation(unittest.TestCase):
def test_service_specify_entity_id(self): def test_service_specify_entity_id(self):
"""Test service data.""" """Test service data."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -86,7 +84,7 @@ class TestAutomation(unittest.TestCase):
def test_service_specify_entity_id_list(self): def test_service_specify_entity_id_list(self):
"""Test service data.""" """Test service data."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -107,7 +105,7 @@ class TestAutomation(unittest.TestCase):
def test_two_triggers(self): def test_two_triggers(self):
"""Test triggers.""" """Test triggers."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': [ 'trigger': [
{ {
@ -135,7 +133,7 @@ class TestAutomation(unittest.TestCase):
def test_two_conditions_with_and(self): def test_two_conditions_with_and(self):
"""Test two and conditions.""" """Test two and conditions."""
entity_id = 'test.entity' entity_id = 'test.entity'
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': [ 'trigger': [
{ {
@ -147,7 +145,7 @@ class TestAutomation(unittest.TestCase):
{ {
'platform': 'state', 'platform': 'state',
'entity_id': entity_id, 'entity_id': entity_id,
'state': 100 'state': '100'
}, },
{ {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -179,7 +177,7 @@ class TestAutomation(unittest.TestCase):
def test_two_conditions_with_or(self): def test_two_conditions_with_or(self):
"""Test two or conditions.""" """Test two or conditions."""
entity_id = 'test.entity' entity_id = 'test.entity'
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': [ 'trigger': [
{ {
@ -192,7 +190,7 @@ class TestAutomation(unittest.TestCase):
{ {
'platform': 'state', 'platform': 'state',
'entity_id': entity_id, 'entity_id': entity_id,
'state': 200 'state': '200'
}, },
{ {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -224,13 +222,13 @@ class TestAutomation(unittest.TestCase):
def test_using_trigger_as_condition(self): def test_using_trigger_as_condition(self):
"""Test triggers as condition.""" """Test triggers as condition."""
entity_id = 'test.entity' entity_id = 'test.entity'
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': [ 'trigger': [
{ {
'platform': 'state', 'platform': 'state',
'entity_id': entity_id, 'entity_id': entity_id,
'state': 100 'state': '100'
}, },
{ {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -247,21 +245,21 @@ class TestAutomation(unittest.TestCase):
self.hass.states.set(entity_id, 100) self.hass.states.set(entity_id, 100)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(2, len(self.calls))
self.hass.states.set(entity_id, 120) self.hass.states.set(entity_id, 120)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(2, len(self.calls))
self.hass.states.set(entity_id, 151) self.hass.states.set(entity_id, 151)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls)) self.assertEqual(2, len(self.calls))
def test_using_trigger_as_condition_with_invalid_condition(self): def test_using_trigger_as_condition_with_invalid_condition(self):
"""Event is not a valid condition.""" """Event is not a valid condition."""
entity_id = 'test.entity' entity_id = 'test.entity'
self.hass.states.set(entity_id, 100) self.hass.states.set(entity_id, 100)
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': [ 'trigger': [
{ {
@ -287,7 +285,7 @@ class TestAutomation(unittest.TestCase):
def test_automation_list_setting(self): def test_automation_list_setting(self):
"""Event is not a valid condition.""" """Event is not a valid condition."""
self.assertTrue(automation.setup(self.hass, { self.assertTrue(_setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: [{ automation.DOMAIN: [{
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',

View File

@ -1,6 +1,7 @@
"""The tests for the MQTT automation.""" """The tests for the MQTT automation."""
import unittest import unittest
from homeassistant.bootstrap import _setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from tests.common import ( from tests.common import (
mock_mqtt_component, fire_mqtt_message, get_test_home_assistant) mock_mqtt_component, fire_mqtt_message, get_test_home_assistant)
@ -12,6 +13,7 @@ class TestAutomationMQTT(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
mock_mqtt_component(self.hass) mock_mqtt_component(self.hass)
self.calls = [] self.calls = []
@ -26,7 +28,7 @@ class TestAutomationMQTT(unittest.TestCase):
def test_if_fires_on_topic_match(self): def test_if_fires_on_topic_match(self):
"""Test if message is fired on topic match.""" """Test if message is fired on topic match."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'mqtt', 'platform': 'mqtt',
@ -36,7 +38,7 @@ class TestAutomationMQTT(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_mqtt_message(self.hass, 'test-topic', '') fire_mqtt_message(self.hass, 'test-topic', '')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -44,7 +46,7 @@ class TestAutomationMQTT(unittest.TestCase):
def test_if_fires_on_topic_and_payload_match(self): def test_if_fires_on_topic_and_payload_match(self):
"""Test if message is fired on topic and payload match.""" """Test if message is fired on topic and payload match."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'mqtt', 'platform': 'mqtt',
@ -55,7 +57,7 @@ class TestAutomationMQTT(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_mqtt_message(self.hass, 'test-topic', 'hello') fire_mqtt_message(self.hass, 'test-topic', 'hello')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -63,7 +65,7 @@ class TestAutomationMQTT(unittest.TestCase):
def test_if_not_fires_on_topic_but_no_payload_match(self): def test_if_not_fires_on_topic_but_no_payload_match(self):
"""Test if message is not fired on topic but no payload.""" """Test if message is not fired on topic but no payload."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'mqtt', 'platform': 'mqtt',
@ -74,7 +76,7 @@ class TestAutomationMQTT(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_mqtt_message(self.hass, 'test-topic', 'no-hello') fire_mqtt_message(self.hass, 'test-topic', 'no-hello')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()

View File

@ -1,6 +1,7 @@
"""The tests for numeric state automation.""" """The tests for numeric state automation."""
import unittest import unittest
from homeassistant.bootstrap import _setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -12,6 +13,7 @@ class TestAutomationNumericState(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.calls = [] self.calls = []
def record_call(service): def record_call(service):
@ -26,7 +28,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_below(self): def test_if_fires_on_entity_change_below(self):
""""Test the firing with changed entity.""" """"Test the firing with changed entity."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -37,7 +39,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 9) self.hass.states.set('test.entity', 9)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -48,7 +50,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -59,7 +61,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 9) self.hass.states.set('test.entity', 9)
@ -71,7 +73,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.states.set('test.entity', 9) self.hass.states.set('test.entity', 9)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -82,7 +84,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 so this should not fire again # 9 is below 10 so this should not fire again
self.hass.states.set('test.entity', 8) self.hass.states.set('test.entity', 8)
@ -91,7 +93,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_above(self): def test_if_fires_on_entity_change_above(self):
""""Test the firing with changed entity.""" """"Test the firing with changed entity."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -102,7 +104,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is above 10 # 11 is above 10
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -114,7 +116,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.states.set('test.entity', 9) self.hass.states.set('test.entity', 9)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -125,7 +127,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is above 10 and 9 is below # 11 is above 10 and 9 is below
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
@ -138,7 +140,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -149,7 +151,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is above 10 so this should fire again # 11 is above 10 so this should fire again
self.hass.states.set('test.entity', 12) self.hass.states.set('test.entity', 12)
@ -158,7 +160,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_below_range(self): def test_if_fires_on_entity_change_below_range(self):
""""Test the firing with changed entity.""" """"Test the firing with changed entity."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -170,7 +172,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 9) self.hass.states.set('test.entity', 9)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -178,7 +180,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_below_above_range(self): def test_if_fires_on_entity_change_below_above_range(self):
""""Test the firing with changed entity.""" """"Test the firing with changed entity."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -190,7 +192,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 4 is below 5 # 4 is below 5
self.hass.states.set('test.entity', 4) self.hass.states.set('test.entity', 4)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -201,7 +203,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -213,7 +215,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 9) self.hass.states.set('test.entity', 9)
@ -225,7 +227,7 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -237,7 +239,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 4 is below 5 so it should not fire # 4 is below 5 so it should not fire
self.hass.states.set('test.entity', 4) self.hass.states.set('test.entity', 4)
@ -246,7 +248,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_not_fires_if_entity_not_match(self): def test_if_not_fires_if_entity_not_match(self):
""""Test if not fired with non matching entity.""" """"Test if not fired with non matching entity."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -256,7 +258,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 11) self.hass.states.set('test.entity', 11)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -264,7 +266,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_below_with_attribute(self): def test_if_fires_on_entity_change_below_with_attribute(self):
""""Test attributes change.""" """"Test attributes change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -275,7 +277,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 9, {'test_attribute': 11}) self.hass.states.set('test.entity', 9, {'test_attribute': 11})
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -283,7 +285,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_not_fires_on_entity_change_not_below_with_attribute(self): def test_if_not_fires_on_entity_change_not_below_with_attribute(self):
""""Test attributes.""" """"Test attributes."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -294,7 +296,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is not below 10 # 11 is not below 10
self.hass.states.set('test.entity', 11, {'test_attribute': 9}) self.hass.states.set('test.entity', 11, {'test_attribute': 9})
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -302,7 +304,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_attribute_change_with_attribute_below(self): def test_if_fires_on_attribute_change_with_attribute_below(self):
""""Test attributes change.""" """"Test attributes change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -314,7 +316,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 'entity', {'test_attribute': 9}) self.hass.states.set('test.entity', 'entity', {'test_attribute': 9})
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -322,7 +324,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_not_fires_on_attribute_change_with_attribute_not_below(self): def test_if_not_fires_on_attribute_change_with_attribute_not_below(self):
""""Test attributes change.""" """"Test attributes change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -334,7 +336,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is not below 10 # 11 is not below 10
self.hass.states.set('test.entity', 'entity', {'test_attribute': 11}) self.hass.states.set('test.entity', 'entity', {'test_attribute': 11})
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -342,7 +344,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_not_fires_on_entity_change_with_attribute_below(self): def test_if_not_fires_on_entity_change_with_attribute_below(self):
""""Test attributes change.""" """"Test attributes change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -354,7 +356,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is not below 10, entity state value should not be tested # 11 is not below 10, entity state value should not be tested
self.hass.states.set('test.entity', '9', {'test_attribute': 11}) self.hass.states.set('test.entity', '9', {'test_attribute': 11})
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -362,7 +364,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_not_fires_on_entity_change_with_not_attribute_below(self): def test_if_not_fires_on_entity_change_with_not_attribute_below(self):
""""Test attributes change.""" """"Test attributes change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -374,7 +376,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is not below 10, entity state value should not be tested # 11 is not below 10, entity state value should not be tested
self.hass.states.set('test.entity', 'entity') self.hass.states.set('test.entity', 'entity')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -382,7 +384,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_fires_on_attr_change_with_attribute_below_and_multiple_attr(self): def test_fires_on_attr_change_with_attribute_below_and_multiple_attr(self):
""""Test attributes change.""" """"Test attributes change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -394,7 +396,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is not below 10 # 9 is not below 10
self.hass.states.set('test.entity', 'entity', self.hass.states.set('test.entity', 'entity',
{'test_attribute': 9, 'not_test_attribute': 11}) {'test_attribute': 9, 'not_test_attribute': 11})
@ -403,7 +405,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_template_list(self): def test_template_list(self):
""""Test template list.""" """"Test template list."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -416,7 +418,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 3 is below 10 # 3 is below 10
self.hass.states.set('test.entity', 'entity', self.hass.states.set('test.entity', 'entity',
{'test_attribute': [11, 15, 3]}) {'test_attribute': [11, 15, 3]})
@ -425,7 +427,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_template_string(self): def test_template_string(self):
""""Test template string.""" """"Test template string."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -438,7 +440,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 9 is below 10 # 9 is below 10
self.hass.states.set('test.entity', 'entity', self.hass.states.set('test.entity', 'entity',
{'test_attribute': '0.9'}) {'test_attribute': '0.9'})
@ -447,7 +449,7 @@ class TestAutomationNumericState(unittest.TestCase):
def test_not_fires_on_attr_change_with_attr_not_below_multiple_attr(self): def test_not_fires_on_attr_change_with_attr_not_below_multiple_attr(self):
""""Test if not fired changed attributes.""" """"Test if not fired changed attributes."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'numeric_state', 'platform': 'numeric_state',
@ -459,7 +461,7 @@ class TestAutomationNumericState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# 11 is not below 10 # 11 is not below 10
self.hass.states.set('test.entity', 'entity', self.hass.states.set('test.entity', 'entity',
{'test_attribute': 11, 'not_test_attribute': 9}) {'test_attribute': 11, 'not_test_attribute': 9})
@ -470,7 +472,7 @@ class TestAutomationNumericState(unittest.TestCase):
""""Test if action.""" """"Test if action."""
entity_id = 'domain.test_entity' entity_id = 'domain.test_entity'
test_state = 10 test_state = 10
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',

View File

@ -3,9 +3,9 @@ import unittest
from datetime import timedelta from datetime import timedelta
from unittest.mock import patch from unittest.mock import patch
from homeassistant.bootstrap import _setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
import homeassistant.components.automation.state as state
from tests.common import fire_time_changed, get_test_home_assistant from tests.common import fire_time_changed, get_test_home_assistant
@ -16,6 +16,7 @@ class TestAutomationState(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.hass.states.set('test.entity', 'hello') self.hass.states.set('test.entity', 'hello')
self.calls = [] self.calls = []
@ -30,7 +31,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_entity_change(self): def test_if_fires_on_entity_change(self):
"""Test for firing on entity change.""" """Test for firing on entity change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -40,7 +41,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -48,7 +49,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_entity_change_with_from_filter(self): def test_if_fires_on_entity_change_with_from_filter(self):
"""Test for firing on entity change with filter.""" """Test for firing on entity change with filter."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -59,7 +60,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -67,7 +68,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_entity_change_with_to_filter(self): def test_if_fires_on_entity_change_with_to_filter(self):
"""Test for firing on entity change with no filter.""" """Test for firing on entity change with no filter."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -78,7 +79,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -86,7 +87,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_entity_change_with_state_filter(self): def test_if_fires_on_entity_change_with_state_filter(self):
"""Test for firing on entity change with state filter.""" """Test for firing on entity change with state filter."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -97,7 +98,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -105,7 +106,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_entity_change_with_both_filters(self): def test_if_fires_on_entity_change_with_both_filters(self):
"""Test for firing if both filters are a non match.""" """Test for firing if both filters are a non match."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -117,7 +118,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -125,7 +126,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_not_fires_if_to_filter_not_match(self): def test_if_not_fires_if_to_filter_not_match(self):
"""Test for not firing if to filter is not a match.""" """Test for not firing if to filter is not a match."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -137,7 +138,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'moon') self.hass.states.set('test.entity', 'moon')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -147,7 +148,7 @@ class TestAutomationState(unittest.TestCase):
"""Test for not firing if from filter is not a match.""" """Test for not firing if from filter is not a match."""
self.hass.states.set('test.entity', 'bye') self.hass.states.set('test.entity', 'bye')
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -159,7 +160,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -167,7 +168,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_not_fires_if_entity_not_match(self): def test_if_not_fires_if_entity_not_match(self):
"""Test for not firing if entity is not matching.""" """Test for not firing if entity is not matching."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -177,7 +178,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -187,7 +188,7 @@ class TestAutomationState(unittest.TestCase):
"""Test for to action.""" """Test for to action."""
entity_id = 'domain.test_entity' entity_id = 'domain.test_entity'
test_state = 'new_state' test_state = 'new_state'
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -218,48 +219,68 @@ class TestAutomationState(unittest.TestCase):
def test_if_fails_setup_if_to_boolean_value(self): def test_if_fails_setup_if_to_boolean_value(self):
"""Test for setup failure for boolean to.""" """Test for setup failure for boolean to."""
self.assertFalse(state.trigger( assert not _setup_component(self.hass, automation.DOMAIN, {
self.hass, { automation.DOMAIN: {
'trigger': {
'platform': 'state', 'platform': 'state',
'entity_id': 'test.entity', 'entity_id': 'test.entity',
'to': True, 'to': True,
}, lambda x: x)) },
'action': {
'service': 'homeassistant.turn_on',
}
}})
def test_if_fails_setup_if_from_boolean_value(self): def test_if_fails_setup_if_from_boolean_value(self):
"""Test for setup failure for boolean from.""" """Test for setup failure for boolean from."""
self.assertFalse(state.trigger( assert not _setup_component(self.hass, automation.DOMAIN, {
self.hass, { automation.DOMAIN: {
'trigger': {
'platform': 'state', 'platform': 'state',
'entity_id': 'test.entity', 'entity_id': 'test.entity',
'from': True, 'from': True,
}, lambda x: x)) },
'action': {
'service': 'homeassistant.turn_on',
}
}})
def test_if_fails_setup_bad_for(self): def test_if_fails_setup_bad_for(self):
"""Test for setup failure for bad for.""" """Test for setup failure for bad for."""
self.assertFalse(state.trigger( assert not _setup_component(self.hass, automation.DOMAIN, {
self.hass, { automation.DOMAIN: {
'trigger': {
'platform': 'state', 'platform': 'state',
'entity_id': 'test.entity', 'entity_id': 'test.entity',
'to': 'world', 'to': 'world',
'for': { 'for': {
'invalid': 5 'invalid': 5
}, },
}, lambda x: x)) },
'action': {
'service': 'homeassistant.turn_on',
}
}})
def test_if_fails_setup_for_without_to(self): def test_if_fails_setup_for_without_to(self):
"""Test for setup failures for missing to.""" """Test for setup failures for missing to."""
self.assertFalse(state.trigger( assert not _setup_component(self.hass, automation.DOMAIN, {
self.hass, { automation.DOMAIN: {
'trigger': {
'platform': 'state', 'platform': 'state',
'entity_id': 'test.entity', 'entity_id': 'test.entity',
'for': { 'for': {
'seconds': 5 'seconds': 5
}, },
}, lambda x: x)) },
'action': {
'service': 'homeassistant.turn_on',
}
}})
def test_if_not_fires_on_entity_change_with_for(self): def test_if_not_fires_on_entity_change_with_for(self):
"""Test for not firing on entity change with for.""" """Test for not firing on entity change with for."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -273,7 +294,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -285,7 +306,7 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_entity_change_with_for(self): def test_if_fires_on_entity_change_with_for(self):
"""Test for firing on entity change with for.""" """Test for firing on entity change with for."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'state', 'platform': 'state',
@ -299,7 +320,7 @@ class TestAutomationState(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -314,7 +335,7 @@ class TestAutomationState(unittest.TestCase):
with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow:
mock_utcnow.return_value = point1 mock_utcnow.return_value = point1
self.hass.states.set('test.entity', 'on') self.hass.states.set('test.entity', 'on')
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -328,12 +349,9 @@ class TestAutomationState(unittest.TestCase):
'seconds': 5 'seconds': 5
}, },
}, },
'action': {'service': 'test.automation'},
'action': {
'service': 'test.automation'
} }
} })
}))
# not enough time has passed # not enough time has passed
self.hass.bus.fire('test_event') self.hass.bus.fire('test_event')
@ -348,21 +366,32 @@ class TestAutomationState(unittest.TestCase):
def test_if_fails_setup_for_without_time(self): def test_if_fails_setup_for_without_time(self):
"""Test for setup failure if no time is provided.""" """Test for setup failure if no time is provided."""
self.assertIsNone(state.if_action( assert not _setup_component(self.hass, automation.DOMAIN, {
self.hass, { automation.DOMAIN: {
'trigger': {
'platform': 'event',
'event_type': 'bla'
},
'condition': {
'platform': 'state', 'platform': 'state',
'entity_id': 'test.entity', 'entity_id': 'test.entity',
'state': 'on', 'state': 'on',
'for': {}, 'for': {},
})) },
'action': {'service': 'test.automation'},
}})
def test_if_fails_setup_for_without_entity(self): def test_if_fails_setup_for_without_entity(self):
"""Test for setup failure if no entity is provided.""" """Test for setup failure if no entity is provided."""
self.assertIsNone(state.if_action( assert not _setup_component(self.hass, automation.DOMAIN, {
self.hass, { automation.DOMAIN: {
'trigger': {'event_type': 'bla'},
'condition': {
'platform': 'state', 'platform': 'state',
'state': 'on', 'state': 'on',
'for': { 'for': {
'seconds': 5 'seconds': 5
}, },
})) },
'action': {'service': 'test.automation'},
}})

View File

@ -3,6 +3,7 @@ from datetime import datetime
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant.bootstrap import _setup_component
from homeassistant.components import sun from homeassistant.components import sun
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -16,6 +17,7 @@ class TestAutomationSun(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.hass.config.components.append('sun') self.hass.config.components.append('sun')
self.calls = [] self.calls = []
@ -40,7 +42,7 @@ class TestAutomationSun(unittest.TestCase):
with patch('homeassistant.components.automation.sun.dt_util.utcnow', with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now): return_value=now):
self.assertTrue(automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'sun', 'platform': 'sun',
@ -50,7 +52,7 @@ class TestAutomationSun(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
fire_time_changed(self.hass, trigger_time) fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -67,7 +69,7 @@ class TestAutomationSun(unittest.TestCase):
with patch('homeassistant.components.automation.sun.dt_util.utcnow', with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now): return_value=now):
self.assertTrue(automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'sun', 'platform': 'sun',
@ -77,7 +79,7 @@ class TestAutomationSun(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
fire_time_changed(self.hass, trigger_time) fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -94,7 +96,7 @@ class TestAutomationSun(unittest.TestCase):
with patch('homeassistant.components.automation.sun.dt_util.utcnow', with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now): return_value=now):
self.assertTrue(automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'sun', 'platform': 'sun',
@ -105,7 +107,7 @@ class TestAutomationSun(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
fire_time_changed(self.hass, trigger_time) fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -122,7 +124,7 @@ class TestAutomationSun(unittest.TestCase):
with patch('homeassistant.components.automation.sun.dt_util.utcnow', with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now): return_value=now):
self.assertTrue(automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'sun', 'platform': 'sun',
@ -133,7 +135,7 @@ class TestAutomationSun(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
fire_time_changed(self.hass, trigger_time) fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -145,7 +147,7 @@ class TestAutomationSun(unittest.TestCase):
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015',
}) })
automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -181,7 +183,7 @@ class TestAutomationSun(unittest.TestCase):
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015',
}) })
automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -217,7 +219,7 @@ class TestAutomationSun(unittest.TestCase):
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015',
}) })
automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -254,7 +256,7 @@ class TestAutomationSun(unittest.TestCase):
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015',
}) })
automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -292,7 +294,7 @@ class TestAutomationSun(unittest.TestCase):
sun.STATE_ATTR_NEXT_SETTING: '15:00:00 16-09-2015', sun.STATE_ATTR_NEXT_SETTING: '15:00:00 16-09-2015',
}) })
automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -338,7 +340,7 @@ class TestAutomationSun(unittest.TestCase):
sun.STATE_ATTR_NEXT_SETTING: '17:30:00 16-09-2015', sun.STATE_ATTR_NEXT_SETTING: '17:30:00 16-09-2015',
}) })
automation.setup(self.hass, { _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',

View File

@ -1,6 +1,7 @@
"""The tests fr the Template automation.""" """The tests fr the Template automation."""
import unittest import unittest
from homeassistant.bootstrap import _setup_component
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -12,6 +13,7 @@ class TestAutomationTemplate(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.hass.states.set('test.entity', 'hello') self.hass.states.set('test.entity', 'hello')
self.calls = [] self.calls = []
@ -27,7 +29,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_bool(self): def test_if_fires_on_change_bool(self):
"""Test for firing on boolean change.""" """Test for firing on boolean change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -37,7 +39,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -45,7 +47,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_str(self): def test_if_fires_on_change_str(self):
"""Test for firing on change.""" """Test for firing on change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -55,7 +57,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -63,7 +65,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_str_crazy(self): def test_if_fires_on_change_str_crazy(self):
"""Test for firing on change.""" """Test for firing on change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -73,7 +75,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -81,7 +83,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_not_fires_on_change_bool(self): def test_if_not_fires_on_change_bool(self):
"""Test for not firing on boolean change.""" """Test for not firing on boolean change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -91,7 +93,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -99,7 +101,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_not_fires_on_change_str(self): def test_if_not_fires_on_change_str(self):
"""Test for not firing on string change.""" """Test for not firing on string change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -109,7 +111,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -117,7 +119,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_not_fires_on_change_str_crazy(self): def test_if_not_fires_on_change_str_crazy(self):
"""Test for not firing on string change.""" """Test for not firing on string change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -127,7 +129,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -135,7 +137,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_no_change(self): def test_if_fires_on_no_change(self):
"""Test for firing on no change.""" """Test for firing on no change."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -145,7 +147,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'hello') self.hass.states.set('test.entity', 'hello')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -153,7 +155,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_two_change(self): def test_if_fires_on_two_change(self):
"""Test for firing on two changes.""" """Test for firing on two changes."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -163,7 +165,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# Trigger once # Trigger once
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
@ -177,7 +179,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_with_template(self): def test_if_fires_on_change_with_template(self):
"""Test for firing on change with template.""" """Test for firing on change with template."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -187,7 +189,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -195,7 +197,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_not_fires_on_change_with_template(self): def test_if_not_fires_on_change_with_template(self):
"""Test for not firing on change with template.""" """Test for not firing on change with template."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -205,7 +207,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -213,7 +215,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_with_template_advanced(self): def test_if_fires_on_change_with_template_advanced(self):
"""Test for firing on change with template advanced.""" """Test for firing on change with template advanced."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -227,7 +229,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -235,7 +237,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_no_change_with_template_advanced(self): def test_if_fires_on_no_change_with_template_advanced(self):
"""Test for firing on no change with template advanced.""" """Test for firing on no change with template advanced."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -249,7 +251,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
# Different state # Different state
self.hass.states.set('test.entity', 'worldz') self.hass.states.set('test.entity', 'worldz')
@ -263,7 +265,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_with_template_2(self): def test_if_fires_on_change_with_template_2(self):
"""Test for firing on change with template.""" """Test for firing on change with template."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -274,7 +276,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
@ -302,7 +304,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_action(self): def test_if_action(self):
"""Test for firing if action.""" """Test for firing if action."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -335,7 +337,7 @@ class TestAutomationTemplate(unittest.TestCase):
def test_if_fires_on_change_with_bad_template(self): def test_if_fires_on_change_with_bad_template(self):
"""Test for firing on change with bad template.""" """Test for firing on change with bad template."""
self.assertTrue(automation.setup(self.hass, { assert not _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -345,15 +347,11 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_fires_on_change_with_bad_template_2(self): def test_if_fires_on_change_with_bad_template_2(self):
"""Test for firing on change with bad template.""" """Test for firing on change with bad template."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'template', 'platform': 'template',
@ -363,7 +361,7 @@ class TestAutomationTemplate(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
self.hass.states.set('test.entity', 'world') self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()

View File

@ -3,6 +3,7 @@ from datetime import timedelta
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant.bootstrap import _setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
@ -15,6 +16,7 @@ class TestAutomationTime(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
self.calls = [] self.calls = []
def record_call(service): def record_call(service):
@ -28,7 +30,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_when_hour_matches(self): def test_if_fires_when_hour_matches(self):
"""Test for firing if hour is matching.""" """Test for firing if hour is matching."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -38,7 +40,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0)) fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0))
@ -47,7 +49,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_when_minute_matches(self): def test_if_fires_when_minute_matches(self):
"""Test for firing if minutes are matching.""" """Test for firing if minutes are matching."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -57,7 +59,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace(minute=0)) fire_time_changed(self.hass, dt_util.utcnow().replace(minute=0))
@ -66,7 +68,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_when_second_matches(self): def test_if_fires_when_second_matches(self):
"""Test for firing if seconds are matching.""" """Test for firing if seconds are matching."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -76,7 +78,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace(second=0)) fire_time_changed(self.hass, dt_util.utcnow().replace(second=0))
@ -85,7 +87,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_when_all_matches(self): def test_if_fires_when_all_matches(self):
"""Test for firing if everything matches.""" """Test for firing if everything matches."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -97,7 +99,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=1, minute=2, second=3)) hour=1, minute=2, second=3))
@ -107,7 +109,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_periodic_seconds(self): def test_if_fires_periodic_seconds(self):
"""Test for firing periodically every second.""" """Test for firing periodically every second."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -117,7 +119,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=0, minute=0, second=2)) hour=0, minute=0, second=2))
@ -127,7 +129,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_periodic_minutes(self): def test_if_fires_periodic_minutes(self):
"""Test for firing periodically every minute.""" """Test for firing periodically every minute."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -137,7 +139,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=0, minute=2, second=0)) hour=0, minute=2, second=0))
@ -147,7 +149,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_periodic_hours(self): def test_if_fires_periodic_hours(self):
"""Test for firing periodically every hour.""" """Test for firing periodically every hour."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -157,7 +159,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=2, minute=0, second=0)) hour=2, minute=0, second=0))
@ -167,7 +169,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_fires_using_after(self): def test_if_fires_using_after(self):
"""Test for firing after.""" """Test for firing after."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -177,7 +179,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=5, minute=0, second=0)) hour=5, minute=0, second=0))
@ -187,7 +189,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_not_working_if_no_values_in_conf_provided(self): def test_if_not_working_if_no_values_in_conf_provided(self):
"""Test for failure if no configuration.""" """Test for failure if no configuration."""
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -196,7 +198,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=5, minute=0, second=0)) hour=5, minute=0, second=0))
@ -210,7 +212,7 @@ class TestAutomationTime(unittest.TestCase):
This should break the before rule. This should break the before rule.
""" """
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'time', 'platform': 'time',
@ -221,7 +223,7 @@ class TestAutomationTime(unittest.TestCase):
'service': 'test.automation' 'service': 'test.automation'
} }
} }
})) })
fire_time_changed(self.hass, dt_util.utcnow().replace( fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=1, minute=0, second=5)) hour=1, minute=0, second=5))
@ -232,7 +234,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_action_before(self): def test_if_action_before(self):
"""Test for if action before.""" """Test for if action before."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -267,7 +269,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_action_after(self): def test_if_action_after(self):
"""Test for if action after.""" """Test for if action after."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -302,7 +304,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_action_one_weekday(self): def test_if_action_one_weekday(self):
"""Test for if action with one weekday.""" """Test for if action with one weekday."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -338,7 +340,7 @@ class TestAutomationTime(unittest.TestCase):
def test_if_action_list_weekday(self): def test_if_action_list_weekday(self):
"""Test for action with a list of weekdays.""" """Test for action with a list of weekdays."""
automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',

View File

@ -1,6 +1,7 @@
"""The tests for the location automation.""" """The tests for the location automation."""
import unittest import unittest
from homeassistant.bootstrap import _setup_component
from homeassistant.components import automation, zone from homeassistant.components import automation, zone
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -12,6 +13,7 @@ class TestAutomationZone(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.hass.config.components.append('group')
zone.setup(self.hass, { zone.setup(self.hass, {
'zone': { 'zone': {
'name': 'test', 'name': 'test',
@ -40,7 +42,7 @@ class TestAutomationZone(unittest.TestCase):
}) })
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'zone', 'platform': 'zone',
@ -52,7 +54,7 @@ class TestAutomationZone(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.states.set('test.entity', 'hello', { self.hass.states.set('test.entity', 'hello', {
'latitude': 32.880586, 'latitude': 32.880586,
@ -70,7 +72,7 @@ class TestAutomationZone(unittest.TestCase):
}) })
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'zone', 'platform': 'zone',
@ -82,7 +84,7 @@ class TestAutomationZone(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.states.set('test.entity', 'hello', { self.hass.states.set('test.entity', 'hello', {
'latitude': 32.881011, 'latitude': 32.881011,
@ -100,7 +102,7 @@ class TestAutomationZone(unittest.TestCase):
}) })
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'zone', 'platform': 'zone',
@ -112,7 +114,7 @@ class TestAutomationZone(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.states.set('test.entity', 'hello', { self.hass.states.set('test.entity', 'hello', {
'latitude': 32.881011, 'latitude': 32.881011,
@ -130,7 +132,7 @@ class TestAutomationZone(unittest.TestCase):
}) })
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'zone', 'platform': 'zone',
@ -142,7 +144,7 @@ class TestAutomationZone(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.states.set('test.entity', 'hello', { self.hass.states.set('test.entity', 'hello', {
'latitude': 32.880586, 'latitude': 32.880586,
@ -160,7 +162,7 @@ class TestAutomationZone(unittest.TestCase):
}) })
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, { assert _setup_component(self.hass, automation.DOMAIN, {
automation.DOMAIN: { automation.DOMAIN: {
'trigger': { 'trigger': {
'platform': 'event', 'platform': 'event',
@ -175,7 +177,7 @@ class TestAutomationZone(unittest.TestCase):
'service': 'test.automation', 'service': 'test.automation',
} }
} }
})) })
self.hass.bus.fire('test_event') self.hass.bus.fire('test_event')
self.hass.pool.block_till_done() self.hass.pool.block_till_done()

View File

@ -31,7 +31,7 @@ class TestScript(unittest.TestCase):
{'test': {}}, {'test': {}},
{ {
'test hello world': { 'test hello world': {
'sequence': [] 'sequence': [{'event': 'bla'}]
} }
}, },
{ {

View File

@ -1,8 +1,12 @@
from datetime import timedelta
import pytest import pytest
import voluptuous as vol import voluptuous as vol
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from tests.common import get_test_home_assistant
def test_boolean(): def test_boolean():
"""Test boolean validation.""" """Test boolean validation."""
@ -117,6 +121,19 @@ def test_event_schema():
cv.EVENT_SCHEMA(value) cv.EVENT_SCHEMA(value)
def test_platform_validator():
"""Test platform validation."""
# Prepares loading
get_test_home_assistant()
schema = vol.Schema(cv.platform_validator('light'))
with pytest.raises(vol.MultipleInvalid):
schema('platform_that_does_not_exist')
schema('hue')
def test_icon(): def test_icon():
"""Test icon validation.""" """Test icon validation."""
schema = vol.Schema(cv.icon) schema = vol.Schema(cv.icon)
@ -128,6 +145,25 @@ def test_icon():
schema('mdi:work') schema('mdi:work')
def test_time_offset():
"""Test time_offset validation."""
schema = vol.Schema(cv.time_offset)
for value in (
None, '', 1234, 'hello:world', '12:', '12:34:56:78'
):
with pytest.raises(vol.MultipleInvalid):
schema(value)
for value in (
'8:20', '23:59', '-8:20', '-23:59:59', '-48:00'
):
schema(value)
assert timedelta(hours=23, minutes=59) == schema('23:59')
assert -1 * timedelta(hours=1, minutes=15) == schema('-1:15')
def test_service(): def test_service():
"""Test service validation.""" """Test service validation."""
schema = vol.Schema(cv.service) schema = vol.Schema(cv.service)
@ -165,6 +201,14 @@ def test_service_schema():
for value in ( for value in (
{'service': 'homeassistant.turn_on'}, {'service': 'homeassistant.turn_on'},
{
'service': 'homeassistant.turn_on',
'entity_id': 'light.kitchen',
},
{
'service': 'homeassistant.turn_on',
'entity_id': ['light.kitchen', 'light.ceiling'],
},
): ):
cv.SERVICE_SCHEMA(value) cv.SERVICE_SCHEMA(value)
@ -232,31 +276,26 @@ def test_time_zone():
schema('UTC') schema('UTC')
def test_dict_validator(): def test_key_dependency():
"""Test DictValidator.""" """Test key_dependency validator."""
schema = vol.Schema(cv.DictValidator(cv.entity_ids, cv.slug)) schema = vol.Schema(cv.key_dependency('beer', 'soda'))
for value in ( for value in (
None, {'beer': None}
{'invalid slug': 'sensor.temp'},
{'hello world': 'invalid_entity'}
): ):
with pytest.raises(vol.MultipleInvalid): with pytest.raises(vol.MultipleInvalid):
schema(value) schema(value)
for value in ( for value in (
{}, {'beer': None, 'soda': None},
{'hello_world': 'sensor.temp'}, {'soda': None}, {}
): ):
schema(value) schema(value)
assert schema({'hello_world': 'sensor.temp'}) == \
{'hello_world': ['sensor.temp']}
def test_has_at_least_one_key(): def test_has_at_least_one_key():
"""Test has_at_least_one_key validator.""" """Test has_at_least_one_key validator."""
schema = vol.Schema(cv.has_at_least_one_key(['beer', 'soda'])) schema = vol.Schema(cv.has_at_least_one_key('beer', 'soda'))
for value in (None, [], {}, {'wine': None}): for value in (None, [], {}, {'wine': None}):
with pytest.raises(vol.MultipleInvalid): with pytest.raises(vol.MultipleInvalid):