Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
pavoni 2015-09-18 08:51:54 +01:00
commit 5369d8c61c
58 changed files with 1935 additions and 766 deletions

View File

@ -3,11 +3,6 @@ language: python
python:
- "3.4"
install:
- pip install -r requirements_all.txt
- pip install flake8 pylint coveralls
- script/bootstrap_server
script:
- flake8 homeassistant
- pylint homeassistant
- coverage run -m unittest discover tests
after_success:
- coveralls
- script/cibuild

View File

@ -103,6 +103,10 @@ def get_arguments():
'--uninstall-osx',
action='store_true',
help='Uninstalls from OS X.')
parser.add_argument(
'--restart-osx',
action='store_true',
help='Restarts on OS X.')
if os.name != "nt":
parser.add_argument(
'--daemon',
@ -216,6 +220,10 @@ def main():
if args.uninstall_osx:
uninstall_osx()
return
if args.restart_osx:
uninstall_osx()
install_osx()
return
# daemon functions
if args.pid_file:

View File

@ -0,0 +1,108 @@
"""
homeassistant.components.alarm_control_panel
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with a alarm control panel.
"""
import logging
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import verisure
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
DOMAIN = 'alarm_control_panel'
DEPENDENCIES = []
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
verisure.DISCOVER_SENSORS: 'verisure'
}
SERVICE_TO_METHOD = {
SERVICE_ALARM_DISARM: 'alarm_disarm',
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
}
ATTR_CODE = 'code'
ATTR_TO_PROPERTY = [
ATTR_CODE,
]
def setup(hass, config):
""" Track states and offer events for sensors. """
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
component.setup(config)
def alarm_service_handler(service):
""" Maps services to methods on Alarm. """
target_alarms = component.extract_from_service(service)
if ATTR_CODE not in service.data:
return
code = service.data[ATTR_CODE]
method = SERVICE_TO_METHOD[service.service]
for alarm in target_alarms:
getattr(alarm, method)(code)
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, alarm_service_handler)
return True
def alarm_disarm(hass, code, entity_id=None):
""" Send the alarm the command for disarm. """
data = {ATTR_CODE: code}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
def alarm_arm_home(hass, code, entity_id=None):
""" Send the alarm the command for arm home. """
data = {ATTR_CODE: code}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
def alarm_arm_away(hass, code, entity_id=None):
""" Send the alarm the command for arm away. """
data = {ATTR_CODE: code}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
class AlarmControlPanel(Entity):
""" ABC for alarm control devices. """
def alarm_disarm(self, code):
""" Send disarm command. """
raise NotImplementedError()
def alarm_arm_home(self, code):
""" Send arm home command. """
raise NotImplementedError()
def alarm_arm_away(self, code):
""" Send arm away command. """
raise NotImplementedError()

View File

@ -0,0 +1,88 @@
"""
homeassistant.components.alarm_control_panel.verisure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Interfaces with Verisure alarm control panel.
"""
import logging
import homeassistant.components.verisure as verisure
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import (
STATE_UNKNOWN,
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
alarms = []
alarms.extend([
VerisureAlarm(value)
for value in verisure.get_alarm_status().values()
if verisure.SHOW_ALARM
])
add_devices(alarms)
class VerisureAlarm(alarm.AlarmControlPanel):
""" represents a Verisure alarm status within home assistant. """
def __init__(self, alarm_status):
self._id = alarm_status.id
self._device = verisure.MY_PAGES.DEVICE_ALARM
self._state = STATE_UNKNOWN
@property
def name(self):
""" Returns the name of the device. """
return 'Alarm {}'.format(self._id)
@property
def state(self):
""" Returns the state of the device. """
return self._state
def update(self):
''' update alarm status '''
verisure.update()
if verisure.STATUS[self._device][self._id].status == 'unarmed':
self._state = STATE_ALARM_DISARMED
elif verisure.STATUS[self._device][self._id].status == 'armedhome':
self._state = STATE_ALARM_ARMED_HOME
elif verisure.STATUS[self._device][self._id].status == 'armedaway':
self._state = STATE_ALARM_ARMED_AWAY
elif verisure.STATUS[self._device][self._id].status != 'pending':
_LOGGER.error(
'Unknown alarm state %s',
verisure.STATUS[self._device][self._id].status)
def alarm_disarm(self, code):
""" Send disarm command. """
verisure.MY_PAGES.set_alarm_status(
code,
verisure.MY_PAGES.ALARM_DISARMED)
_LOGGER.warning('disarming')
def alarm_arm_home(self, code):
""" Send arm home command. """
verisure.MY_PAGES.set_alarm_status(
code,
verisure.MY_PAGES.ALARM_ARMED_HOME)
_LOGGER.warning('arming home')
def alarm_arm_away(self, code):
""" Send arm away command. """
verisure.MY_PAGES.set_alarm_status(
code,
verisure.MY_PAGES.ALARM_ARMED_AWAY)
_LOGGER.warning('arming away')

View File

@ -7,63 +7,67 @@ Allows to setup simple automation rules via the config file.
import logging
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.helpers import config_per_platform
from homeassistant.util import split_entity_id
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
from homeassistant.components import logbook
DOMAIN = "automation"
DOMAIN = 'automation'
DEPENDENCIES = ["group"]
DEPENDENCIES = ['group']
CONF_ALIAS = "alias"
CONF_SERVICE = "execute_service"
CONF_SERVICE_ENTITY_ID = "service_entity_id"
CONF_SERVICE_DATA = "service_data"
CONF_IF = "if"
CONF_ALIAS = 'alias'
CONF_SERVICE = 'execute_service'
CONF_SERVICE_ENTITY_ID = 'service_entity_id'
CONF_SERVICE_DATA = 'service_data'
CONF_CONDITION = 'condition'
CONF_ACTION = 'action'
CONF_TRIGGER = 'trigger'
CONF_CONDITION_TYPE = 'condition_type'
CONDITION_USE_TRIGGER_VALUES = 'use_trigger_values'
CONDITION_TYPE_AND = 'and'
CONDITION_TYPE_OR = 'or'
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Sets up automation. """
success = False
config_key = DOMAIN
found = 1
for p_type, p_config in config_per_platform(config, DOMAIN, _LOGGER):
platform = prepare_setup_platform(hass, config, DOMAIN, p_type)
while config_key in config:
p_config = _migrate_old_config(config[config_key])
found += 1
config_key = "{} {}".format(DOMAIN, found)
if platform is None:
_LOGGER.error("Unknown automation platform specified: %s", p_type)
continue
action = _get_action(hass, p_config)
name = p_config.get(CONF_ALIAS, config_key)
action = _get_action(hass, p_config.get(CONF_ACTION, {}), name)
if action is None:
return
continue
if CONF_IF in p_config:
action = _process_if(hass, config, p_config[CONF_IF], action)
if CONF_CONDITION in p_config or CONF_CONDITION_TYPE in p_config:
action = _process_if(hass, config, p_config, action)
if platform.trigger(hass, p_config, action):
_LOGGER.info(
"Initialized %s rule %s", p_type, p_config.get(CONF_ALIAS, ""))
success = True
else:
_LOGGER.error(
"Error setting up rule %s", p_config.get(CONF_ALIAS, ""))
if action is None:
continue
return success
_process_trigger(hass, config, p_config.get(CONF_TRIGGER, []), name,
action)
return True
def _get_action(hass, config):
def _get_action(hass, config, name):
""" Return an action based on a config. """
name = config.get(CONF_ALIAS, 'Unnamed automation')
if CONF_SERVICE not in config:
_LOGGER.error('Error setting up %s, no action specified.',
name)
return
_LOGGER.error('Error setting up %s, no action specified.', name)
return None
def action():
""" Action to be executed. """
@ -71,7 +75,6 @@ def _get_action(hass, config):
logbook.log_entry(hass, name, 'has been triggered', DOMAIN)
domain, service = split_entity_id(config[CONF_SERVICE])
service_data = config.get(CONF_SERVICE_DATA, {})
if not isinstance(service_data, dict):
@ -91,26 +94,107 @@ def _get_action(hass, config):
return action
def _process_if(hass, config, if_configs, action):
def _migrate_old_config(config):
""" Migrate old config to new. """
if CONF_PLATFORM not in config:
return config
_LOGGER.warning(
'You are using an old configuration format. Please upgrade: '
'https://home-assistant.io/components/automation.html')
new_conf = {
CONF_TRIGGER: dict(config),
CONF_CONDITION: config.get('if', []),
CONF_ACTION: dict(config),
}
for cat, key, new_key in (('trigger', 'mqtt_topic', 'topic'),
('trigger', 'mqtt_payload', 'payload'),
('trigger', 'state_entity_id', 'entity_id'),
('trigger', 'state_before', 'before'),
('trigger', 'state_after', 'after'),
('trigger', 'state_to', 'to'),
('trigger', 'state_from', 'from'),
('trigger', 'state_hours', 'hours'),
('trigger', 'state_minutes', 'minutes'),
('trigger', 'state_seconds', 'seconds')):
if key in new_conf[cat]:
new_conf[cat][new_key] = new_conf[cat].pop(key)
return new_conf
def _process_if(hass, config, p_config, action):
""" Processes if checks. """
cond_type = p_config.get(CONF_CONDITION_TYPE,
DEFAULT_CONDITION_TYPE).lower()
if_configs = p_config.get(CONF_CONDITION)
use_trigger = if_configs == CONDITION_USE_TRIGGER_VALUES
if use_trigger:
if_configs = p_config[CONF_TRIGGER]
if isinstance(if_configs, dict):
if_configs = [if_configs]
checks = []
for if_config in if_configs:
p_type = if_config.get(CONF_PLATFORM)
if p_type is None:
_LOGGER.error("No platform defined found for if-statement %s",
if_config)
platform = _resolve_platform('if_action', hass, config,
if_config.get(CONF_PLATFORM))
if platform is None:
continue
platform = prepare_setup_platform(hass, config, DOMAIN, p_type)
check = platform.if_action(hass, if_config)
if platform is None or not hasattr(platform, 'if_action'):
_LOGGER.error("Unsupported if-statement platform specified: %s",
p_type)
# Invalid conditions are allowed if we base it on trigger
if check is None and not use_trigger:
return None
checks.append(check)
if cond_type == CONDITION_TYPE_AND:
def if_action():
""" AND all conditions. """
if all(check() for check in checks):
action()
else:
def if_action():
""" OR all conditions. """
if any(check() for check in checks):
action()
return if_action
def _process_trigger(hass, config, trigger_configs, name, action):
""" Setup triggers. """
if isinstance(trigger_configs, dict):
trigger_configs = [trigger_configs]
for conf in trigger_configs:
platform = _resolve_platform('trigger', hass, config,
conf.get(CONF_PLATFORM))
if platform is None:
continue
action = platform.if_action(hass, if_config, action)
if platform.trigger(hass, conf, action):
_LOGGER.info("Initialized rule %s", name)
else:
_LOGGER.error("Error setting up rule %s", name)
return action
def _resolve_platform(method, hass, config, platform):
""" Find automation platform. """
if platform is None:
return None
platform = prepare_setup_platform(hass, config, DOMAIN, platform)
if platform is None or not hasattr(platform, method):
_LOGGER.error("Unknown automation platform specified for %s: %s",
method, platform)
return None
return platform

View File

@ -10,8 +10,8 @@ import homeassistant.components.mqtt as mqtt
DEPENDENCIES = ['mqtt']
CONF_TOPIC = 'mqtt_topic'
CONF_PAYLOAD = 'mqtt_payload'
CONF_TOPIC = 'topic'
CONF_PAYLOAD = 'payload'
def trigger(hass, config, action):

View File

@ -9,9 +9,9 @@ import logging
from homeassistant.helpers.event import track_state_change
CONF_ENTITY_ID = "state_entity_id"
CONF_BELOW = "state_below"
CONF_ABOVE = "state_above"
CONF_ENTITY_ID = "entity_id"
CONF_BELOW = "below"
CONF_ABOVE = "above"
_LOGGER = logging.getLogger(__name__)
@ -48,14 +48,14 @@ def trigger(hass, config, action):
return True
def if_action(hass, config, action):
def if_action(hass, config):
""" Wraps action method with state based condition. """
entity_id = config.get(CONF_ENTITY_ID)
if entity_id is None:
_LOGGER.error("Missing configuration key %s", CONF_ENTITY_ID)
return action
return None
below = config.get(CONF_BELOW)
above = config.get(CONF_ABOVE)
@ -64,16 +64,14 @@ def if_action(hass, config, action):
_LOGGER.error("Missing configuration key."
" One of %s or %s is required",
CONF_BELOW, CONF_ABOVE)
return action
def state_if():
""" Execute action if state matches. """
return None
def if_numeric_state():
""" Test numeric state condition. """
state = hass.states.get(entity_id)
if state is None or _in_range(state.state, above, below):
action()
return state is not None and _in_range(state.state, above, below)
return state_if
return if_numeric_state
def _in_range(value, range_start, range_end):

View File

@ -10,9 +10,9 @@ from homeassistant.helpers.event import track_state_change
from homeassistant.const import MATCH_ALL
CONF_ENTITY_ID = "state_entity_id"
CONF_FROM = "state_from"
CONF_TO = "state_to"
CONF_ENTITY_ID = "entity_id"
CONF_FROM = "from"
CONF_TO = "to"
CONF_STATE = "state"
@ -26,7 +26,7 @@ def trigger(hass, config, action):
return False
from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
def state_automation_listener(entity, from_s, to_s):
""" Listens for state changes and calls action. """
@ -38,7 +38,7 @@ def trigger(hass, config, action):
return True
def if_action(hass, config, action):
def if_action(hass, config):
""" Wraps action method with state based condition. """
entity_id = config.get(CONF_ENTITY_ID)
state = config.get(CONF_STATE)
@ -47,11 +47,12 @@ def if_action(hass, config, action):
logging.getLogger(__name__).error(
"Missing if-condition configuration key %s or %s", CONF_ENTITY_ID,
CONF_STATE)
return action
return None
def state_if():
""" Execute action if state matches. """
if hass.states.is_state(entity_id, state):
action()
state = str(state)
return state_if
def if_state():
""" Test if condition. """
return hass.states.is_state(entity_id, state)
return if_state

View File

@ -0,0 +1,103 @@
"""
homeassistant.components.automation.sun
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Offers sun based automation rules.
"""
import logging
from datetime import timedelta
from homeassistant.components import sun
from homeassistant.helpers.event import track_point_in_utc_time
import homeassistant.util.dt as dt_util
DEPENDENCIES = ['sun']
CONF_OFFSET = 'offset'
CONF_EVENT = 'event'
EVENT_SUNSET = 'sunset'
EVENT_SUNRISE = 'sunrise'
_LOGGER = logging.getLogger(__name__)
def trigger(hass, config, action):
""" Listen for events based on config. """
event = config.get(CONF_EVENT)
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
if CONF_OFFSET in config:
raw_offset = config.get(CONF_OFFSET)
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
else:
offset = timedelta(0)
# Do something to call action
if event == EVENT_SUNRISE:
trigger_sunrise(hass, action, offset)
else:
trigger_sunset(hass, action, offset)
return True
def trigger_sunrise(hass, action, offset):
""" Trigger action at next sun rise. """
def next_rise():
""" Returns next sunrise. """
next_time = sun.next_rising_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunrise_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
action()
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
def trigger_sunset(hass, action, offset):
""" Trigger action at next sun set. """
def next_set():
""" Returns next sunrise. """
next_time = sun.next_setting_utc(hass) + offset
while next_time < dt_util.utcnow():
next_time = next_time + timedelta(days=1)
return next_time
def sunset_automation_listener(now):
""" Called when it's time for action. """
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
action()
track_point_in_utc_time(hass, sunset_automation_listener, next_set())

View File

@ -10,21 +10,35 @@ from homeassistant.util import convert
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_time_change
CONF_HOURS = "time_hours"
CONF_MINUTES = "time_minutes"
CONF_SECONDS = "time_seconds"
CONF_HOURS = "hours"
CONF_MINUTES = "minutes"
CONF_SECONDS = "seconds"
CONF_BEFORE = "before"
CONF_AFTER = "after"
CONF_WEEKDAY = "weekday"
WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
_LOGGER = logging.getLogger(__name__)
def trigger(hass, config, action):
""" Listen for state changes based on `config`. """
if CONF_AFTER in config:
after = dt_util.parse_time_str(config[CONF_AFTER])
if after is None:
_error_time(config[CONF_AFTER], CONF_AFTER)
return False
hours, minutes, seconds = after.hour, after.minute, after.second
elif (CONF_HOURS in config or CONF_MINUTES in config
or CONF_SECONDS in config):
hours = convert(config.get(CONF_HOURS), int)
minutes = convert(config.get(CONF_MINUTES), int)
seconds = convert(config.get(CONF_SECONDS), int)
else:
_LOGGER.error('One of %s, %s, %s OR %s needs to be specified',
CONF_HOURS, CONF_MINUTES, CONF_SECONDS, CONF_AFTER)
return False
def time_automation_listener(now):
""" Listens for time changes and calls action. """
@ -36,7 +50,7 @@ def trigger(hass, config, action):
return True
def if_action(hass, config, action):
def if_action(hass, config):
""" Wraps action method with time based condition. """
before = config.get(CONF_BEFORE)
after = config.get(CONF_AFTER)
@ -46,37 +60,46 @@ def if_action(hass, config, action):
logging.getLogger(__name__).error(
"Missing if-condition configuration key %s, %s or %s",
CONF_BEFORE, CONF_AFTER, CONF_WEEKDAY)
return None
if before is not None:
before = dt_util.parse_time_str(before)
if before is None:
_error_time(before, CONF_BEFORE)
return None
if after is not None:
after = dt_util.parse_time_str(after)
if after is None:
_error_time(after, CONF_AFTER)
return None
def time_if():
""" Validate time based if-condition """
now = dt_util.now()
if before is not None and now > now.replace(hour=before.hour,
minute=before.minute):
return False
if before is not None:
# Strip seconds if given
before_h, before_m = before.split(':')[0:2]
before_point = now.replace(hour=int(before_h),
minute=int(before_m))
if now > before_point:
return
if after is not None:
# Strip seconds if given
after_h, after_m = after.split(':')[0:2]
after_point = now.replace(hour=int(after_h), minute=int(after_m))
if now < after_point:
return
if after is not None and now < now.replace(hour=after.hour,
minute=after.minute):
return False
if weekday is not None:
now_weekday = WEEKDAYS[now.weekday()]
if isinstance(weekday, str) and weekday != now_weekday or \
now_weekday not in weekday:
return
return False
action()
return True
return time_if
def _error_time(value, key):
""" Helper method to print error. """
_LOGGER.error(
"Received invalid value for '%s': %s", key, value)
if isinstance(value, int):
_LOGGER.error('Make sure you wrap time values in quotes')

View File

@ -0,0 +1,105 @@
"""
homeassistant.components.camera.foscam
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This component provides basic support for Foscam IP cameras.
As part of the basic support the following features will be provided:
-MJPEG video streaming
To use this component, add the following to your configuration.yaml file.
camera:
platform: foscam
name: Door Camera
ip: 192.168.0.123
port: 88
username: YOUR_USERNAME
password: YOUR_PASSWORD
Variables:
ip
*Required
The IP address of your Foscam device.
username
*Required
The username of a visitor or operator of your camera. Oddly admin accounts
don't seem to have access to take snapshots.
password
*Required
The password for accessing your camera.
name
*Optional
This parameter allows you to override the name of your camera in homeassistant.
port
*Optional
The port that the camera is running on. The default is 88.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.foscam.html
"""
import logging
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN
from homeassistant.components.camera import Camera
import requests
import re
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Adds a Foscam IP Camera. """
if not validate_config({DOMAIN: config},
{DOMAIN: ['username', 'password', 'ip']}, _LOGGER):
return None
add_devices_callback([FoscamCamera(config)])
# pylint: disable=too-many-instance-attributes
class FoscamCamera(Camera):
""" An implementation of a Foscam IP camera. """
def __init__(self, device_info):
super(FoscamCamera, self).__init__()
ip_address = device_info.get('ip')
port = device_info.get('port', 88)
self._base_url = 'http://' + ip_address + ':' + str(port) + '/'
self._username = device_info.get('username')
self._password = device_info.get('password')
self._snap_picture_url = self._base_url \
+ 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture&usr=' \
+ self._username + '&pwd=' + self._password
self._name = device_info.get('name', 'Foscam Camera')
_LOGGER.info('Using the following URL for %s: %s',
self._name, self._snap_picture_url)
def camera_image(self):
""" Return a still image reponse from the camera. """
# send the request to snap a picture
response = requests.get(self._snap_picture_url)
# parse the response to find the image file name
pattern = re.compile('src="[.][.]/(.*[.]jpg)"')
filename = pattern.search(response.content.decode("utf-8")).group(1)
# send request for the image
response = requests.get(self._base_url + filename)
return response.content
@property
def name(self):
""" Return the name of this device. """
return self._name

View File

@ -176,10 +176,8 @@ class DeviceTracker(object):
self.track_new = track_new
self.lock = threading.Lock()
entity_ids = []
for device in devices:
if device.track:
entity_ids.append(device.entity_id)
device.update_ha_state()
self.group = None
@ -194,9 +192,9 @@ class DeviceTracker(object):
mac = mac.upper()
device = self.mac_to_dev.get(mac)
if not device:
dev_id = util.slugify(host_name or mac)
dev_id = util.slugify(host_name or '') or util.slugify(mac)
else:
dev_id = str(dev_id)
dev_id = str(dev_id).lower()
device = self.devices.get(dev_id)
if device:
@ -234,7 +232,8 @@ class DeviceTracker(object):
""" Update stale devices. """
with self.lock:
for device in self.devices.values():
if device.last_update_home and device.stale(now):
if (device.track and device.last_update_home and
device.stale(now)):
device.update_ha_state(True)
@ -336,7 +335,8 @@ def convert_csv_config(csv_path, yaml_path):
with open(csv_path) as inp:
for row in csv.DictReader(inp):
dev_id = util.ensure_unique_string(
util.slugify(row['name']) or DEVICE_DEFAULT_NAME, used_ids)
(util.slugify(row['name']) or DEVICE_DEFAULT_NAME).lower(),
used_ids)
used_ids.add(dev_id)
device = Device(None, None, row['track'] == '1', dev_id,
row['device'], row['name'], row['picture'])
@ -350,8 +350,9 @@ def load_config(path, hass, consider_home):
return []
return [
Device(hass, consider_home, device.get('track', False),
str(dev_id), device.get('mac'), device.get('name'),
device.get('picture'), device.get(CONF_AWAY_HIDE, False))
str(dev_id).lower(), str(device.get('mac')).upper(),
device.get('name'), device.get('picture'),
device.get(CONF_AWAY_HIDE, False))
for dev_id, device in load_yaml_config_file(path).items()]

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "397aa7c09f4938b1358672c9983f9f32"
VERSION = "0ab148ece11ddde26b95460c2c91da3d"

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 9637d5d26516873b8a04a3c62b9596163c822a2d
Subproject commit 63e039a221ae6771e0d7c6990d9a93b7cc22fc64

View File

@ -484,7 +484,7 @@ class MediaPlayerDevice(Entity):
else:
state_attr = {
attr: getattr(self, attr) for attr
in ATTR_TO_PROPERTY if getattr(self, attr)
in ATTR_TO_PROPERTY if getattr(self, attr) is not None
}
if self.media_image_url:

View File

@ -356,7 +356,8 @@ class AirPlayDevice(MediaPlayerDevice):
self.player_state = state_hash.get('player_state', None)
if 'name' in state_hash:
self.device_name = state_hash.get('name', 'AirPlay')
name = state_hash.get('name', '')
self.device_name = (name + ' AirTunes Speaker').strip()
if 'kind' in state_hash:
self.kind = state_hash.get('kind', None)

View File

@ -28,6 +28,14 @@ REQUIREMENTS = ['SoCo==0.11.1']
_LOGGER = logging.getLogger(__name__)
# The soco library is excessively chatty when it comes to logging and
# causes a LOT of spam in the logs due to making a http connection to each
# speaker every 10 seconds. Quiet it down a bit to just actual problems.
_SOCO_LOGGER = logging.getLogger('soco')
_SOCO_LOGGER.setLevel(logging.ERROR)
_REQUESTS_LOGGER = logging.getLogger('requests')
_REQUESTS_LOGGER.setLevel(logging.ERROR)
SUPPORT_SONOS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK

View File

@ -256,7 +256,7 @@ class Recorder(threading.Thread):
""" Query the database. """
try:
with self.conn, self.lock:
_LOGGER.info("Running query %s", sql_query)
_LOGGER.debug("Running query %s", sql_query)
cur = self.conn.cursor()

View File

@ -1,137 +0,0 @@
"""
homeassistant.components.scheduler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A component that will act as a scheduler and perform actions based
on the events in the schedule.
It will read a json object from schedule.json in the config dir
and create a schedule based on it.
Each schedule is a JSON with the keys id, name, description,
entity_ids, and events.
- days is an array with the weekday number (monday=0) that the schedule
is active
- entity_ids an array with entity ids that the events in the schedule should
effect (can also be groups)
- events is an array of objects that describe the different events that is
supported. Read in the events descriptions for more information.
"""
import logging
import json
from homeassistant import bootstrap
from homeassistant.loader import get_component
from homeassistant.const import ATTR_ENTITY_ID
DOMAIN = 'scheduler'
DEPENDENCIES = []
_LOGGER = logging.getLogger(__name__)
_SCHEDULE_FILE = 'schedule.json'
def setup(hass, config):
""" Create the schedules. """
def setup_listener(schedule, event_data):
""" Creates the event listener based on event_data. """
event_type = event_data['type']
component = event_type
# if the event isn't part of a component
if event_type in ['time']:
component = 'scheduler.{}'.format(event_type)
elif not bootstrap.setup_component(hass, component, config):
_LOGGER.warn("Could setup event listener for %s", component)
return None
return get_component(component).create_event_listener(schedule,
event_data)
def setup_schedule(schedule_data):
""" Setup a schedule based on the description. """
schedule = Schedule(schedule_data['id'],
name=schedule_data['name'],
description=schedule_data['description'],
entity_ids=schedule_data['entity_ids'],
days=schedule_data['days'])
for event_data in schedule_data['events']:
event_listener = setup_listener(schedule, event_data)
if event_listener:
schedule.add_event_listener(event_listener)
schedule.schedule(hass)
return True
with open(hass.config.path(_SCHEDULE_FILE)) as schedule_file:
schedule_descriptions = json.load(schedule_file)
for schedule_description in schedule_descriptions:
if not setup_schedule(schedule_description):
return False
return True
class Schedule(object):
""" A Schedule """
# pylint: disable=too-many-arguments
def __init__(self, schedule_id, name=None, description=None,
entity_ids=None, days=None):
self.schedule_id = schedule_id
self.name = name
self.description = description
self.entity_ids = entity_ids or []
self.days = days or [0, 1, 2, 3, 4, 5, 6]
self.__event_listeners = []
def add_event_listener(self, event_listener):
""" Add a event to the schedule. """
self.__event_listeners.append(event_listener)
def schedule(self, hass):
""" Schedule all the events in the schedule. """
for event in self.__event_listeners:
event.schedule(hass)
class EventListener(object):
""" The base EventListener class that the schedule uses. """
def __init__(self, schedule):
self.my_schedule = schedule
def schedule(self, hass):
""" Schedule the event """
pass
def execute(self, hass):
""" execute the event """
pass
# pylint: disable=too-few-public-methods
class ServiceEventListener(EventListener):
""" A EventListener that calls a service when executed. """
def __init__(self, schdule, service):
EventListener.__init__(self, schdule)
(self.domain, self.service) = service.split('.')
def execute(self, hass):
""" Call the service. """
data = {ATTR_ENTITY_ID: self.my_schedule.entity_ids}
hass.services.call(self.domain, self.service, data)
# Reschedule for next day
self.schedule(hass)

View File

@ -1,70 +0,0 @@
"""
homeassistant.components.scheduler.time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An event in the scheduler component that will call the service
every specified day at the time specified.
A time event need to have the type 'time', which service to call and at
which time.
{
"type": "time",
"service": "switch.turn_off",
"time": "22:00:00"
}
"""
from datetime import timedelta
import logging
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_time
from homeassistant.components.scheduler import ServiceEventListener
_LOGGER = logging.getLogger(__name__)
def create_event_listener(schedule, event_listener_data):
""" Create a TimeEvent based on the description. """
service = event_listener_data['service']
(hour, minute, second) = [int(x) for x in
event_listener_data['time'].split(':', 3)]
return TimeEventListener(schedule, service, hour, minute, second)
# pylint: disable=too-few-public-methods
class TimeEventListener(ServiceEventListener):
""" The time event that the scheduler uses. """
# pylint: disable=too-many-arguments
def __init__(self, schedule, service, hour, minute, second):
ServiceEventListener.__init__(self, schedule, service)
self.hour = hour
self.minute = minute
self.second = second
def schedule(self, hass):
""" Schedule this event so that it will be called. """
next_time = dt_util.now().replace(
hour=self.hour, minute=self.minute, second=self.second)
# Calculate the next time the event should be executed.
# That is the next day that the schedule is configured to run
while next_time < dt_util.now() or \
next_time.weekday() not in self.my_schedule.days:
next_time = next_time + timedelta(days=1)
# pylint: disable=unused-argument
def execute(now):
""" Call the execute method """
self.execute(hass)
track_point_in_time(hass, execute, next_time)
_LOGGER.info(
'TimeEventListener scheduled for %s, will call service %s.%s',
next_time, self.domain, self.service)

View File

@ -3,7 +3,6 @@ homeassistant.components.sensor.glances
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Gathers system information of hosts which running glances.
Configuration:
To use the glances sensor you will need to add something like the following

View File

@ -91,7 +91,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_LOGGER.error(
"Connection error "
"Please check your settings for OpenWeatherMap.")
return None
return False
data = WeatherData(owm, forecast, hass.config.latitude,
hass.config.longitude)

View File

@ -36,12 +36,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
hasattr(value, 'humidity') and value.humidity
])
sensors.extend([
VerisureAlarm(value)
for value in verisure.get_alarm_status().values()
if verisure.SHOW_ALARM
])
add_devices(sensors)
@ -103,25 +97,3 @@ class VerisureHygrometer(Entity):
def update(self):
''' update sensor '''
verisure.update()
class VerisureAlarm(Entity):
""" represents a Verisure alarm status within home assistant. """
def __init__(self, alarm_status):
self._id = alarm_status.id
self._device = verisure.MY_PAGES.DEVICE_ALARM
@property
def name(self):
""" Returns the name of the device. """
return 'Alarm {}'.format(self._id)
@property
def state(self):
""" Returns the state of the device. """
return verisure.STATUS[self._device][self._id].label
def update(self):
''' update sensor '''
verisure.update()

View File

@ -25,10 +25,8 @@ import urllib
import homeassistant.util as util
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import (
track_point_in_utc_time, track_point_in_time)
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.helpers.entity import Entity
from homeassistant.components.scheduler import ServiceEventListener
DEPENDENCIES = []
REQUIREMENTS = ['astral==0.8.1']
@ -214,95 +212,3 @@ class Sun(Entity):
track_point_in_utc_time(
self.hass, self.point_in_time_listener,
self.next_change + timedelta(seconds=1))
def create_event_listener(schedule, event_listener_data):
""" Create a sun event listener based on the description. """
negative_offset = False
service = event_listener_data['service']
offset_str = event_listener_data['offset']
event = event_listener_data['event']
if offset_str.startswith('-'):
negative_offset = True
offset_str = offset_str[1:]
(hour, minute, second) = [int(x) for x in offset_str.split(':')]
offset = timedelta(hours=hour, minutes=minute, seconds=second)
if event == 'sunset':
return SunsetEventListener(schedule, service, offset, negative_offset)
return SunriseEventListener(schedule, service, offset, negative_offset)
# pylint: disable=too-few-public-methods
class SunEventListener(ServiceEventListener):
""" This is the base class for sun event listeners. """
def __init__(self, schedule, service, offset, negative_offset):
ServiceEventListener.__init__(self, schedule, service)
self.offset = offset
self.negative_offset = negative_offset
def __get_next_time(self, next_event):
"""
Returns when the next time the service should be called.
Taking into account the offset and which days the event should execute.
"""
if self.negative_offset:
next_time = next_event - self.offset
else:
next_time = next_event + self.offset
while next_time < dt_util.now() or \
next_time.weekday() not in self.my_schedule.days:
next_time = next_time + timedelta(days=1)
return next_time
def schedule_next_event(self, hass, next_event):
""" Schedule the event. """
next_time = self.__get_next_time(next_event)
# pylint: disable=unused-argument
def execute(now):
""" Call the execute method. """
self.execute(hass)
track_point_in_time(hass, execute, next_time)
return next_time
# pylint: disable=too-few-public-methods
class SunsetEventListener(SunEventListener):
""" This class is used the call a service when the sun sets. """
def schedule(self, hass):
""" Schedule the event """
next_setting_dt = next_setting(hass)
next_time_dt = self.schedule_next_event(hass, next_setting_dt)
_LOGGER.info(
'SunsetEventListener scheduled for %s, will call service %s.%s',
next_time_dt, self.domain, self.service)
# pylint: disable=too-few-public-methods
class SunriseEventListener(SunEventListener):
""" This class is used the call a service when the sun rises. """
def schedule(self, hass):
""" Schedule the event. """
next_rising_dt = next_rising(hass)
next_time_dt = self.schedule_next_event(hass, next_rising_dt)
_LOGGER.info(
'SunriseEventListener scheduled for %s, will call service %s.%s',
next_time_dt, self.domain, self.service)

View File

@ -59,8 +59,9 @@ from homeassistant.const import (
DOMAIN = "verisure"
DISCOVER_SENSORS = 'verisure.sensors'
DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DEPENDENCIES = []
DEPENDENCIES = ['alarm_control_panel']
REQUIREMENTS = [
'https://github.com/persandstrom/python-verisure/archive/'
'9873c4527f01b1ba1f72ae60f7f35854390d59be.zip#python-verisure==0.2.6'
@ -123,7 +124,8 @@ def setup(hass, config):
# Load components for the devices in the ISY controller that we support
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
('switch', DISCOVER_SWITCHES))):
('switch', DISCOVER_SWITCHES),
('alarm_control_panel', DISCOVER_ALARMS))):
component = get_component(comp_name)
_LOGGER.info(config[DOMAIN])
bootstrap.setup_component(hass, component.DOMAIN, config)
@ -166,7 +168,7 @@ def reconnect():
def update():
""" Updates the status of verisure components. """
if WRONG_PASSWORD_GIVEN:
# Is there any way to inform user?
_LOGGER.error('Wrong password')
return
try:

View File

@ -47,6 +47,9 @@ STATE_PLAYING = 'playing'
STATE_PAUSED = 'paused'
STATE_IDLE = 'idle'
STATE_STANDBY = 'standby'
STATE_ALARM_DISARMED = 'disarmed'
STATE_ALARM_ARMED_HOME = 'armed_home'
STATE_ALARM_ARMED_AWAY = 'armed_away'
# #### STATE AND EVENT ATTRIBUTES ####
# Contains current time for a TIME_CHANGED event
@ -114,6 +117,10 @@ SERVICE_MEDIA_NEXT_TRACK = "media_next_track"
SERVICE_MEDIA_PREVIOUS_TRACK = "media_previous_track"
SERVICE_MEDIA_SEEK = "media_seek"
SERVICE_ALARM_DISARM = "alarm_disarm"
SERVICE_ALARM_ARM_HOME = "alarm_arm_home"
SERVICE_ALARM_ARM_AWAY = "alarm_arm_away"
# #### API / REMOTE ####
SERVER_PORT = 8123

View File

@ -51,6 +51,8 @@ def reproduce_state(hass, states, blocking=False):
current_state = hass.states.get(state.entity_id)
if current_state is None:
_LOGGER.warning('reproduce_state: Unable to find entity %s',
state.entity_id)
continue
if state.state == STATE_ON:
@ -58,7 +60,8 @@ def reproduce_state(hass, states, blocking=False):
elif state.state == STATE_OFF:
service = SERVICE_TURN_OFF
else:
_LOGGER.warning("Unable to reproduce state for %s", state)
_LOGGER.warning("reproduce_state: Unable to reproduce state %s",
state)
continue
service_data = dict(state.attributes)

View File

@ -21,7 +21,7 @@ from .dt import datetime_to_local_str, utcnow
RE_SANITIZE_FILENAME = re.compile(r'(~|\.\.|/|\\)')
RE_SANITIZE_PATH = re.compile(r'(~|\.(\.)+)')
RE_SLUGIFY = re.compile(r'[^A-Za-z0-9_]+')
RE_SLUGIFY = re.compile(r'[^a-z0-9_]+')
def sanitize_filename(filename):
@ -36,7 +36,7 @@ def sanitize_path(path):
def slugify(text):
""" Slugifies a given text. """
text = text.replace(" ", "_")
text = text.lower().replace(" ", "_")
return RE_SLUGIFY.sub("", text)

View File

@ -131,3 +131,20 @@ def date_str_to_date(dt_str):
def strip_microseconds(dattim):
""" Returns a copy of dattime object but with microsecond set to 0. """
return dattim.replace(microsecond=0)
def parse_time_str(time_str):
""" Parse a time string (00:20:00) into Time object.
Return None if invalid.
"""
parts = str(time_str).split(':')
if len(parts) < 2:
return None
try:
hour = int(parts[0])
minute = int(parts[1])
second = int(parts[2]) if len(parts) > 2 else 0
return dt.time(hour, minute, second)
except ValueError:
# ValueError if value cannot be converted to an int or not in range
return None

View File

@ -1,6 +1,6 @@
"""Helpers to install PyPi packages."""
import os
import logging
import os
import pkg_resources
import subprocess
import sys
@ -15,25 +15,24 @@ def install_package(package, upgrade=True, target=None):
"""Install a package on PyPi. Accepts pip compatible package strings.
Return boolean if install successfull."""
# Not using 'import pip; pip.main([])' because it breaks the logger
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
if upgrade:
args.append('--upgrade')
if target:
args += ['--target', os.path.abspath(target)]
with INSTALL_LOCK:
if check_package_exists(package, target):
return True
_LOGGER.info('Attempting install of %s', package)
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
if upgrade:
args.append('--upgrade')
if target:
args += ['--target', os.path.abspath(target)]
try:
return 0 == subprocess.call(args)
except subprocess.SubprocessError:
return False
def check_package_exists(package, target=None):
def check_package_exists(package, target):
"""Check if a package exists.
Returns True when the requirement is met.
Returns False when the package is not installed or doesn't meet req."""
@ -43,16 +42,5 @@ def check_package_exists(package, target=None):
# This is a zip file
req = pkg_resources.Requirement.parse(urlparse(package).fragment)
if target:
work_set = pkg_resources.WorkingSet([target])
search_fun = work_set.find
else:
search_fun = pkg_resources.get_distribution
try:
result = search_fun(req)
except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
return False
return bool(result)
return any(dist in req for dist in
pkg_resources.find_distributions(target))

9
script/bootstrap Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
# script/bootstrap: Resolve all dependencies that the application requires to
# run.
cd "$(dirname "$0")/.."
script/bootstrap_server
script/bootstrap_frontend

5
script/bootstrap_frontend Executable file
View File

@ -0,0 +1,5 @@
echo "Bootstrapping frontend..."
cd homeassistant/components/frontend/www_static/home-assistant-polymer
npm install
npm run setup_js_dev
cd ../../../../..

10
script/bootstrap_server Executable file
View File

@ -0,0 +1,10 @@
cd "$(dirname "$0")/.."
echo "Update the submodule to latest version..."
git submodule update
echo "Installing dependencies..."
python3 -m pip install --upgrade -r requirements_all.txt
echo "Installing development dependencies.."
python3 -m pip install --upgrade flake8 pylint coveralls pytest pytest-cov

View File

@ -1,12 +1,8 @@
# Builds the frontend for production
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
cd "$(dirname "$0")/.."
cd homeassistant/components/frontend/www_static/home-assistant-polymer
npm install
npm run frontend_prod
cp bower_components/webcomponentsjs/webcomponents-lite.min.js ..

View File

@ -3,10 +3,7 @@
# apt-get install cython3 libudev-dev python-sphinx python3-setuptools
# pip3 install cython
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
cd "$(dirname "$0")/.."
if [ ! -d build ]; then
mkdir build

7
script/cibuild Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
# script/cibuild: Setup environment for CI to run tests. This is primarily
# designed to run on the continuous integration server.
script/test coverage
coveralls

View File

@ -3,10 +3,7 @@
# Optional: pass in a timezone as first argument
# If not given will attempt to mount /etc/localtime
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
cd "$(dirname "$0")/.."
docker build -t home-assistant-dev .

View File

@ -1,10 +1,7 @@
# Open a docker that can be used to debug/dev python-openzwave
# Pass in a command line argument to build
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
cd "$(dirname "$0")/.."
if [ $# -gt 0 ]
then

0
scripts/hass-daemon → script/hass-daemon Normal file → Executable file
View File

9
script/lint Executable file
View File

@ -0,0 +1,9 @@
# Run style checks
cd "$(dirname "$0")/.."
echo "Checking style with flake8..."
flake8 homeassistant
echo "Checking style with pylint..."
pylint homeassistant

8
script/server Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
# script/server: Launch the application and any extra required processes
# locally.
cd "$(dirname "$0")/.."
python3 -m homeassistant -c config

5
script/setup Executable file
View File

@ -0,0 +1,5 @@
cd "$(dirname "$0")/.."
git submodule init
script/bootstrap
python3 setup.py develop

16
script/test Executable file
View File

@ -0,0 +1,16 @@
#!/bin/sh
# script/test: Run test suite for application. Optionallly pass in a path to an
# individual test file to run a single test.
cd "$(dirname "$0")/.."
script/lint
echo "Running tests..."
if [ "$1" = "coverage" ]; then
py.test --cov homeassistant tests
else
py.test tests
fi

8
script/update Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
# script/update: Update application to run for its current checkout.
cd "$(dirname "$0")/.."
git pull
git submodule update

View File

@ -1,9 +0,0 @@
# Run style checks
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
flake8 homeassistant
pylint homeassistant

View File

@ -1,10 +0,0 @@
# If current pwd is scripts, go 1 up.
if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
if [ "$1" = "coverage" ]; then
coverage run -m unittest discover tests
else
python3 -m unittest discover tests
fi

View File

@ -1,6 +0,0 @@
echo "The update script has been deprecated since Home Assistant v0.7"
echo
echo "Home Assistant is now distributed via PyPi and can be installed and"
echo "upgraded by running: pip3 install --upgrade homeassistant"
echo
echo "If you are developing a new feature for Home Assistant, run: git pull"

View File

@ -1,15 +1,13 @@
"""
tests.test_component_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~
tests.components.automation.test_event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo component.
Tests event automation.
"""
import unittest
import homeassistant.core as ha
import homeassistant.components.automation as automation
import homeassistant.components.automation.event as event
from homeassistant.const import CONF_PLATFORM
class TestAutomationEvent(unittest.TestCase):
@ -28,20 +26,57 @@ class TestAutomationEvent(unittest.TestCase):
""" Stop down stuff we started. """
self.hass.stop()
def test_fails_setup_if_no_event_type(self):
self.assertFalse(automation.setup(self.hass, {
def test_old_config_if_fires_on_event(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
automation.CONF_SERVICE: 'test.automation'
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation'
}
}))
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_event_with_data(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'event_data': {'some_attr': 'some_value'},
'execute_service': 'test.automation'
}
}))
self.hass.bus.fire('test_event', {'some_attr': 'some_value'})
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_not_fires_if_event_data_not_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'event_data': {'some_attr': 'some_value'},
'execute_service': 'test.automation'
}
}))
self.hass.bus.fire('test_event', {'some_attr': 'some_other_value'})
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_fires_on_event(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'action': {
'execute_service': 'test.automation',
}
}
}))
@ -52,10 +87,14 @@ class TestAutomationEvent(unittest.TestCase):
def test_if_fires_on_event_with_data(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
event.CONF_EVENT_DATA: {'some_attr': 'some_value'},
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'event',
'event_type': 'test_event',
'event_data': {'some_attr': 'some_value'}
},
'action': {
'execute_service': 'test.automation',
}
}
}))
@ -66,10 +105,14 @@ class TestAutomationEvent(unittest.TestCase):
def test_if_not_fires_if_event_data_not_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
event.CONF_EVENT_DATA: {'some_attr': 'some_value'},
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'event',
'event_type': 'test_event',
'event_data': {'some_attr': 'some_value'}
},
'action': {
'execute_service': 'test.automation',
}
}
}))

View File

@ -1,15 +1,13 @@
"""
tests.test_component_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo component.
tests.components.automation.test_init
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests automation component.
"""
import unittest
import homeassistant.core as ha
import homeassistant.components.automation as automation
import homeassistant.components.automation.event as event
from homeassistant.const import CONF_PLATFORM, ATTR_ENTITY_ID
from homeassistant.const import ATTR_ENTITY_ID
class TestAutomationEvent(unittest.TestCase):
@ -28,20 +26,13 @@ class TestAutomationEvent(unittest.TestCase):
""" Stop down stuff we started. """
self.hass.stop()
def test_setup_fails_if_unknown_platform(self):
self.assertFalse(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'i_do_not_exist'
}
}))
def test_service_data_not_a_dict(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_SERVICE_DATA: 100
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_data': 100
}
})
@ -49,13 +40,64 @@ class TestAutomationEvent(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_service_specify_data(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_data': {'some': 'data'}
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual('data', self.calls[0].data['some'])
def test_old_config_service_specify_entity_id(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_entity_id': 'hello.world'
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world'],
self.calls[0].data.get(ATTR_ENTITY_ID))
def test_old_config_service_specify_entity_id_list(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'service_entity_id': ['hello.world', 'hello.world2']
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world', 'hello.world2'],
self.calls[0].data.get(ATTR_ENTITY_ID))
def test_service_specify_data(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_SERVICE_DATA: {'some': 'data'}
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'action': {
'execute_service': 'test.automation',
'service_data': {'some': 'data'}
}
}
})
@ -67,29 +109,216 @@ class TestAutomationEvent(unittest.TestCase):
def test_service_specify_entity_id(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_SERVICE_ENTITY_ID: 'hello.world'
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'action': {
'execute_service': 'test.automation',
'service_entity_id': 'hello.world'
}
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world'], self.calls[0].data[ATTR_ENTITY_ID])
self.assertEqual(['hello.world'],
self.calls[0].data.get(ATTR_ENTITY_ID))
def test_service_specify_entity_id_list(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_SERVICE_ENTITY_ID: ['hello.world', 'hello.world2']
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'action': {
'execute_service': 'test.automation',
'service_entity_id': ['hello.world', 'hello.world2']
}
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world', 'hello.world2'],
self.calls[0].data.get(ATTR_ENTITY_ID))
def test_two_triggers(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': [
{
'platform': 'event',
'event_type': 'test_event',
},
{
'platform': 'state',
'entity_id': 'test.entity',
}
],
'action': {
'execute_service': 'test.automation',
}
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set('test.entity', 'hello')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
def test_two_conditions_with_and(self):
entity_id = 'test.entity'
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': [
{
'platform': 'event',
'event_type': 'test_event',
},
],
'condition': [
{
'platform': 'state',
'entity_id': entity_id,
'state': 100
},
{
'platform': 'numeric_state',
'entity_id': entity_id,
'below': 150
}
],
'action': {
'execute_service': 'test.automation',
}
}
})
self.hass.states.set(entity_id, 100)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, 101)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, 151)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_two_conditions_with_or(self):
entity_id = 'test.entity'
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': [
{
'platform': 'event',
'event_type': 'test_event',
},
],
'condition_type': 'OR',
'condition': [
{
'platform': 'state',
'entity_id': entity_id,
'state': 200
},
{
'platform': 'numeric_state',
'entity_id': entity_id,
'below': 150
}
],
'action': {
'execute_service': 'test.automation',
}
}
})
self.hass.states.set(entity_id, 200)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, 100)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
self.hass.states.set(entity_id, 250)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
def test_using_trigger_as_condition(self):
""" """
entity_id = 'test.entity'
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': [
{
'platform': 'state',
'entity_id': entity_id,
'state': 100
},
{
'platform': 'numeric_state',
'entity_id': entity_id,
'below': 150
}
],
'condition': 'use_trigger_values',
'action': {
'execute_service': 'test.automation',
}
}
})
self.hass.states.set(entity_id, 100)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, 120)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, 151)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_using_trigger_as_condition_with_invalid_condition(self):
""" Event is not a valid condition. Will it still work? """
entity_id = 'test.entity'
self.hass.states.set(entity_id, 100)
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': [
{
'platform': 'event',
'event_type': 'test_event',
},
{
'platform': 'numeric_state',
'entity_id': entity_id,
'below': 150
}
],
'condition': 'use_trigger_values',
'action': {
'execute_service': 'test.automation',
}
}
})
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(['hello.world', 'hello.world2'], self.calls[0].data[ATTR_ENTITY_ID])

View File

@ -1,16 +1,13 @@
"""
tests.test_component_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~
tests.components.automation.test_mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo component.
Tests mqtt automation.
"""
import unittest
import homeassistant.core as ha
import homeassistant.components.automation as automation
import homeassistant.components.automation.mqtt as mqtt
from homeassistant.const import CONF_PLATFORM
from tests.common import mock_mqtt_component, fire_mqtt_message
@ -31,20 +28,57 @@ class TestAutomationState(unittest.TestCase):
""" Stop down stuff we started. """
self.hass.stop()
def test_setup_fails_if_no_topic(self):
self.assertFalse(automation.setup(self.hass, {
def test_old_config_if_fires_on_topic_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'mqtt',
automation.CONF_SERVICE: 'test.automation'
'platform': 'mqtt',
'mqtt_topic': 'test-topic',
'execute_service': 'test.automation'
}
}))
fire_mqtt_message(self.hass, 'test-topic', '')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_fires_on_topic_and_payload_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'mqtt',
'mqtt_topic': 'test-topic',
'mqtt_payload': 'hello',
'execute_service': 'test.automation'
}
}))
fire_mqtt_message(self.hass, 'test-topic', 'hello')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_old_config_if_not_fires_on_topic_but_no_payload_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'platform': 'mqtt',
'mqtt_topic': 'test-topic',
'mqtt_payload': 'hello',
'execute_service': 'test.automation'
}
}))
fire_mqtt_message(self.hass, 'test-topic', 'no-hello')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_fires_on_topic_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'mqtt',
mqtt.CONF_TOPIC: 'test-topic',
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'mqtt',
'topic': 'test-topic'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -55,10 +89,14 @@ class TestAutomationState(unittest.TestCase):
def test_if_fires_on_topic_and_payload_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'mqtt',
mqtt.CONF_TOPIC: 'test-topic',
mqtt.CONF_PAYLOAD: 'hello',
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'mqtt',
'topic': 'test-topic',
'payload': 'hello'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -69,10 +107,14 @@ class TestAutomationState(unittest.TestCase):
def test_if_not_fires_on_topic_but_no_payload_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'mqtt',
mqtt.CONF_TOPIC: 'test-topic',
mqtt.CONF_PAYLOAD: 'hello',
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'mqtt',
'topic': 'test-topic',
'payload': 'hello'
},
'action': {
'execute_service': 'test.automation'
}
}
}))

View File

@ -1,15 +1,13 @@
"""
tests.test_component_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~
tests.components.automation.test_numeric_state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo component.
Tests numeric state automation.
"""
import unittest
import homeassistant.core as ha
import homeassistant.components.automation as automation
from homeassistant.components.automation import event, numeric_state
from homeassistant.const import CONF_PLATFORM
class TestAutomationNumericState(unittest.TestCase):
@ -28,31 +26,17 @@ class TestAutomationNumericState(unittest.TestCase):
""" Stop down stuff we started. """
self.hass.stop()
def test_setup_fails_if_no_entity_id(self):
self.assertFalse(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
}
}))
def test_setup_fails_if_no_condition(self):
self.assertFalse(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
automation.CONF_SERVICE: 'test.automation'
}
}))
def test_if_fires_on_entity_change_below(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
# 9 is below 10
@ -66,10 +50,14 @@ class TestAutomationNumericState(unittest.TestCase):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -78,17 +66,20 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_not_fires_on_entity_change_below_to_below(self):
self.hass.states.set('test.entity', 9)
self.hass.pool.block_till_done()
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -97,14 +88,17 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_fires_on_entity_change_above(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'above': 10,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
# 11 is above 10
@ -119,10 +113,14 @@ class TestAutomationNumericState(unittest.TestCase):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'above': 10,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -131,7 +129,6 @@ class TestAutomationNumericState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_not_fires_on_entity_change_above_to_above(self):
# set initial state
self.hass.states.set('test.entity', 11)
@ -139,10 +136,14 @@ class TestAutomationNumericState(unittest.TestCase):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'above': 10,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -154,11 +155,15 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_below_range(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 5,
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
'above': 5,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
# 9 is below 10
@ -169,11 +174,15 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_fires_on_entity_change_below_above_range(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 5,
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
'above': 5,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
# 4 is below 5
@ -187,11 +196,15 @@ class TestAutomationNumericState(unittest.TestCase):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 5,
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
'above': 5,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -206,11 +219,15 @@ class TestAutomationNumericState(unittest.TestCase):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.entity',
numeric_state.CONF_ABOVE: 5,
numeric_state.CONF_BELOW: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.entity',
'below': 10,
'above': 5,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -222,10 +239,13 @@ class TestAutomationNumericState(unittest.TestCase):
def test_if_not_fires_if_entity_not_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: 'test.another_entity',
numeric_state.CONF_ABOVE: 10,
automation.CONF_SERVICE: 'test.automation'
'trigger': {
'platform': 'numeric_state',
'entity_id': 'test.another_entity',
},
'action': {
'execute_service': 'test.automation'
}
}
}))
@ -238,19 +258,23 @@ class TestAutomationNumericState(unittest.TestCase):
test_state = 10
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: [{
CONF_PLATFORM: 'numeric_state',
numeric_state.CONF_ENTITY_ID: entity_id,
numeric_state.CONF_ABOVE: test_state,
numeric_state.CONF_BELOW: test_state + 2,
}]
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'condition': {
'platform': 'numeric_state',
'entity_id': entity_id,
'above': test_state,
'below': test_state + 2
},
'action': {
'execute_service': 'test.automation'
}
}
})
self.hass.states.set(entity_id, test_state )
self.hass.states.set(entity_id, test_state)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()

View File

@ -1,15 +1,13 @@
"""
tests.test_component_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~
tests.components.automation.test_state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo component.
Tests state automation.
"""
import unittest
import homeassistant.core as ha
import homeassistant.components.automation as automation
from homeassistant.components.automation import event, state
from homeassistant.const import CONF_PLATFORM
class TestAutomationState(unittest.TestCase):
@ -29,20 +27,12 @@ class TestAutomationState(unittest.TestCase):
""" Stop down stuff we started. """
self.hass.stop()
def test_setup_fails_if_no_entity_id(self):
self.assertFalse(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
automation.CONF_SERVICE: 'test.automation'
}
}))
def test_if_fires_on_entity_change(self):
def test_old_config_if_fires_on_entity_change(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.entity',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.entity',
'execute_service': 'test.automation'
}
}))
@ -50,13 +40,13 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_from_filter(self):
def test_old_config_if_fires_on_entity_change_with_from_filter(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.entity',
state.CONF_FROM: 'hello',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'execute_service': 'test.automation'
}
}))
@ -64,13 +54,13 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_to_filter(self):
def test_old_config_if_fires_on_entity_change_with_to_filter(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.entity',
state.CONF_TO: 'world',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.entity',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
@ -78,14 +68,14 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_both_filters(self):
def test_old_config_if_fires_on_entity_change_with_both_filters(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.entity',
state.CONF_FROM: 'hello',
state.CONF_TO: 'world',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
@ -93,14 +83,14 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_not_fires_if_to_filter_not_match(self):
def test_old_config_if_not_fires_if_to_filter_not_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.entity',
state.CONF_FROM: 'hello',
state.CONF_TO: 'world',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
@ -108,16 +98,16 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_not_fires_if_from_filter_not_match(self):
def test_old_config_if_not_fires_if_from_filter_not_match(self):
self.hass.states.set('test.entity', 'bye')
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.entity',
state.CONF_FROM: 'hello',
state.CONF_TO: 'world',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.entity',
'state_from': 'hello',
'state_to': 'world',
'execute_service': 'test.automation'
}
}))
@ -125,12 +115,12 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_not_fires_if_entity_not_match(self):
def test_old_config_if_not_fires_if_entity_not_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: 'test.another_entity',
automation.CONF_SERVICE: 'test.automation'
'platform': 'state',
'state_entity_id': 'test.another_entity',
'execute_service': 'test.automation'
}
}))
@ -138,18 +128,18 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_action(self):
def test_old_config_if_action(self):
entity_id = 'domain.test_entity'
test_state = 'new_state'
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: [{
CONF_PLATFORM: 'state',
state.CONF_ENTITY_ID: entity_id,
state.CONF_STATE: test_state,
'platform': 'event',
'event_type': 'test_event',
'execute_service': 'test.automation',
'if': [{
'platform': 'state',
'entity_id': entity_id,
'state': test_state,
}]
}
})
@ -165,3 +155,182 @@ class TestAutomationState(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
},
'action': {
'execute_service': 'test.automation'
}
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_from_filter(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
'from': 'hello'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_to_filter(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
'to': 'world'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_state_filter(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
'state': 'world'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_on_entity_change_with_both_filters(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
'from': 'hello',
'to': 'world'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
self.hass.states.set('test.entity', 'world')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_not_fires_if_to_filter_not_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
'from': 'hello',
'to': 'world'
},
'action': {
'execute_service': 'test.automation'
}
}
}))
self.hass.states.set('test.entity', 'moon')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
def test_if_not_fires_if_from_filter_not_match(self):
self.hass.states.set('test.entity', 'bye')
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.entity',
'from': 'hello',
'to': 'world'
},
'action': {
'execute_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_not_fires_if_entity_not_match(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'state',
'entity_id': 'test.anoter_entity',
},
'action': {
'execute_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_action(self):
entity_id = 'domain.test_entity'
test_state = 'new_state'
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'event',
'event_type': 'test_event',
},
'condition': [{
'platform': 'state',
'entity_id': entity_id,
'state': test_state
}],
'action': {
'execute_service': 'test.automation'
}
}
})
self.hass.states.set(entity_id, test_state)
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
self.hass.states.set(entity_id, test_state + 'something')
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))

View File

@ -0,0 +1,141 @@
"""
tests.components.automation.test_sun
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests sun automation.
"""
from datetime import datetime
import unittest
from unittest.mock import patch
import homeassistant.core as ha
from homeassistant.components import sun
import homeassistant.components.automation as automation
import homeassistant.util.dt as dt_util
from tests.common import fire_time_changed
class TestAutomationSun(unittest.TestCase):
""" Test the sun automation. """
def setUp(self): # pylint: disable=invalid-name
self.hass = ha.HomeAssistant()
self.hass.config.components.append('sun')
self.calls = []
def record_call(service):
self.calls.append(service)
self.hass.services.register('test', 'automation', record_call)
def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """
self.hass.stop()
def test_sunset_trigger(self):
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_SETTING: '02:00:00 16-09-2015',
})
now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC)
trigger_time = datetime(2015, 9, 16, 2, tzinfo=dt_util.UTC)
with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'sun',
'event': 'sunset',
},
'action': {
'execute_service': 'test.automation',
}
}
}))
fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_sunrise_trigger(self):
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015',
})
now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC)
trigger_time = datetime(2015, 9, 16, 14, tzinfo=dt_util.UTC)
with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'sun',
'event': 'sunrise',
},
'action': {
'execute_service': 'test.automation',
}
}
}))
fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_sunset_trigger_with_offset(self):
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_SETTING: '02:00:00 16-09-2015',
})
now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC)
trigger_time = datetime(2015, 9, 16, 2, 30, tzinfo=dt_util.UTC)
with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'sun',
'event': 'sunset',
'offset': '0:30:00'
},
'action': {
'execute_service': 'test.automation',
}
}
}))
fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_sunrise_trigger_with_offset(self):
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015',
})
now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC)
trigger_time = datetime(2015, 9, 16, 13, 30, tzinfo=dt_util.UTC)
with patch('homeassistant.components.automation.sun.dt_util.utcnow',
return_value=now):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'sun',
'event': 'sunrise',
'offset': '-0:30:00'
},
'action': {
'execute_service': 'test.automation',
}
}
}))
fire_time_changed(self.hass, trigger_time)
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))

View File

@ -1,8 +1,8 @@
"""
tests.test_component_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~
tests.components.automation.test_time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo component.
Tests time automation.
"""
from datetime import timedelta
import unittest
@ -33,7 +33,7 @@ class TestAutomationTime(unittest.TestCase):
""" Stop down stuff we started. """
self.hass.stop()
def test_if_fires_when_hour_matches(self):
def test_old_config_if_fires_when_hour_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'time',
@ -48,7 +48,7 @@ class TestAutomationTime(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_when_minute_matches(self):
def test_old_config_if_fires_when_minute_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'time',
@ -63,7 +63,7 @@ class TestAutomationTime(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_when_second_matches(self):
def test_old_config_if_fires_when_second_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'time',
@ -78,7 +78,7 @@ class TestAutomationTime(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_when_all_matches(self):
def test_old_config_if_fires_when_all_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'time',
@ -96,13 +96,13 @@ class TestAutomationTime(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_before(self):
def test_old_config_if_action_before(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
'if': {
CONF_PLATFORM: 'time',
time.CONF_BEFORE: '10:00'
}
@ -126,13 +126,13 @@ class TestAutomationTime(unittest.TestCase):
self.assertEqual(1, len(self.calls))
def test_if_action_after(self):
def test_old_config_if_action_after(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
'if': {
CONF_PLATFORM: 'time',
time.CONF_AFTER: '10:00'
}
@ -156,13 +156,13 @@ class TestAutomationTime(unittest.TestCase):
self.assertEqual(1, len(self.calls))
def test_if_action_one_weekday(self):
def test_old_config_if_action_one_weekday(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
'if': {
CONF_PLATFORM: 'time',
time.CONF_WEEKDAY: 'mon',
}
@ -187,13 +187,13 @@ class TestAutomationTime(unittest.TestCase):
self.assertEqual(1, len(self.calls))
def test_if_action_list_weekday(self):
def test_old_config_if_action_list_weekday(self):
automation.setup(self.hass, {
automation.DOMAIN: {
CONF_PLATFORM: 'event',
event.CONF_EVENT_TYPE: 'test_event',
automation.CONF_SERVICE: 'test.automation',
automation.CONF_IF: {
'if': {
CONF_PLATFORM: 'time',
time.CONF_WEEKDAY: ['mon', 'tue'],
}
@ -225,3 +225,267 @@ class TestAutomationTime(unittest.TestCase):
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
def test_if_fires_when_hour_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'time',
'hours': 0,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(hour=0))
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_when_minute_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'time',
'minutes': 0,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(minute=0))
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_when_second_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'time',
'seconds': 0,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(second=0))
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_when_all_matches(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'time',
'hours': 1,
'minutes': 2,
'seconds': 3,
},
'action': {
'execute_service': 'test.automation'
}
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=1, minute=2, second=3))
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_fires_using_after(self):
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'time',
'after': '5:00:00',
},
'action': {
'execute_service': 'test.automation'
}
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=5, minute=0, second=0))
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
@patch('homeassistant.components.automation.time._LOGGER.error')
def test_if_not_fires_using_wrong_after(self, mock_error):
""" YAML translates time values to total seconds. This should break the
before rule. """
self.assertTrue(automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'time',
'after': 3605,
# Total seconds. Hour = 3600 second
},
'action': {
'execute_service': 'test.automation'
}
}
}))
fire_time_changed(self.hass, dt_util.utcnow().replace(
hour=1, minute=0, second=5))
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
self.assertEqual(2, mock_error.call_count)
def test_if_action_before(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'event',
'event_type': 'test_event'
},
'condition': {
'platform': 'time',
'before': '10:00',
},
'action': {
'execute_service': 'test.automation'
}
}
})
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=before_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=after_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_after(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'event',
'event_type': 'test_event'
},
'condition': {
'platform': 'time',
'after': '10:00',
},
'action': {
'execute_service': 'test.automation'
}
}
})
before_10 = dt_util.now().replace(hour=8)
after_10 = dt_util.now().replace(hour=14)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=before_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(0, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=after_10):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_one_weekday(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'event',
'event_type': 'test_event'
},
'condition': {
'platform': 'time',
'weekday': 'mon',
},
'action': {
'execute_service': 'test.automation'
}
}
})
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=monday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=tuesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
def test_if_action_list_weekday(self):
automation.setup(self.hass, {
automation.DOMAIN: {
'trigger': {
'platform': 'event',
'event_type': 'test_event'
},
'condition': {
'platform': 'time',
'weekday': ['mon', 'tue'],
},
'action': {
'execute_service': 'test.automation'
}
}
})
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
tuesday = monday + timedelta(days=1)
wednesday = tuesday + timedelta(days=1)
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=monday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(1, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=tuesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))
with patch('homeassistant.components.automation.time.dt_util.now',
return_value=wednesday):
self.hass.bus.fire('test_event')
self.hass.pool.block_till_done()
self.assertEqual(2, len(self.calls))

View File

@ -7,7 +7,7 @@ Tests the device tracker compoments.
# pylint: disable=protected-access,too-many-public-methods
import unittest
from unittest.mock import patch
from datetime import timedelta
from datetime import datetime, timedelta
import os
from homeassistant.config import load_yaml_config_file
@ -127,7 +127,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}}))
config = device_tracker.load_config(self.yaml_devices, self.hass,
timedelta(seconds=0))[0]
self.assertEqual('DEV1', config.dev_id)
self.assertEqual('dev1', config.dev_id)
self.assertEqual(True, config.track)
def test_discovery(self):
@ -145,17 +145,25 @@ class TestComponentsDeviceTracker(unittest.TestCase):
scanner.reset()
scanner.come_home('DEV1')
register_time = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC)
scan_time = datetime(2015, 9, 15, 23, 1, tzinfo=dt_util.UTC)
with patch('homeassistant.components.device_tracker.dt_util.utcnow',
return_value=register_time):
self.assertTrue(device_tracker.setup(self.hass, {
device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}}))
'device_tracker': {
'platform': 'test',
'consider_home': 59,
}}))
self.assertEqual(STATE_HOME,
self.hass.states.get('device_tracker.dev1').state)
scanner.leave_home('DEV1')
now = dt_util.utcnow().replace(second=0) + timedelta(hours=1)
with patch('homeassistant.util.dt.utcnow', return_value=now):
fire_time_changed(self.hass, now)
with patch('homeassistant.components.device_tracker.dt_util.utcnow',
return_value=scan_time):
fire_time_changed(self.hass, scan_time)
self.hass.pool.block_till_done()
self.assertEqual(STATE_NOT_HOME,

View File

@ -32,9 +32,9 @@ class TestUtil(unittest.TestCase):
def test_slugify(self):
""" Test slugify. """
self.assertEqual("Test", util.slugify("T-!@#$!#@$!$est"))
self.assertEqual("Test_More", util.slugify("Test More"))
self.assertEqual("Test_More", util.slugify("Test_(More)"))
self.assertEqual("test", util.slugify("T-!@#$!#@$!$est"))
self.assertEqual("test_more", util.slugify("Test More"))
self.assertEqual("test_more", util.slugify("Test_(More)"))
def test_split_entity_id(self):
""" Test split_entity_id. """