Merge remote-tracking branch 'refs/remotes/balloob/dev' into dev

This commit is contained in:
hydreliox 2016-01-11 08:40:07 +01:00
commit 7823fb9788
23 changed files with 280 additions and 108 deletions

View File

@ -87,13 +87,21 @@ def setup(hass, config):
lambda item: util.split_entity_id(item)[0]) lambda item: util.split_entity_id(item)[0])
for domain, ent_ids in by_domain: for domain, ent_ids in by_domain:
# We want to block for all calls and only return when all calls
# have been processed. If a service does not exist it causes a 10
# second delay while we're blocking waiting for a response.
# But services can be registered on other HA instances that are
# listening to the bus too. So as a in between solution, we'll
# block only if the service is defined in the current HA instance.
blocking = hass.services.has_service(domain, service.service)
# Create a new dict for this call # Create a new dict for this call
data = dict(service.data) data = dict(service.data)
# ent_ids is a generator, convert it to a list. # ent_ids is a generator, convert it to a list.
data[ATTR_ENTITY_ID] = list(ent_ids) data[ATTR_ENTITY_ID] = list(ent_ids)
hass.services.call(domain, service.service, data, True) hass.services.call(domain, service.service, data, blocking)
hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service) hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service)
hass.services.register(ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service) hass.services.register(ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service)

View File

@ -67,7 +67,7 @@ class VerisureAlarm(alarm.AlarmControlPanel):
self._state = STATE_ALARM_DISARMED self._state = STATE_ALARM_DISARMED
elif verisure.ALARM_STATUS[self._id].status == 'armedhome': elif verisure.ALARM_STATUS[self._id].status == 'armedhome':
self._state = STATE_ALARM_ARMED_HOME self._state = STATE_ALARM_ARMED_HOME
elif verisure.ALARM_STATUS[self._id].status == 'armedaway': elif verisure.ALARM_STATUS[self._id].status == 'armed':
self._state = STATE_ALARM_ARMED_AWAY self._state = STATE_ALARM_ARMED_AWAY
elif verisure.ALARM_STATUS[self._id].status != 'pending': elif verisure.ALARM_STATUS[self._id].status != 'pending':
_LOGGER.error( _LOGGER.error(

View File

@ -11,6 +11,7 @@ import logging
from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY
from homeassistant.util import template from homeassistant.util import template
from homeassistant.helpers.service import call_from_config
DOMAIN = 'alexa' DOMAIN = 'alexa'
DEPENDENCIES = ['http'] DEPENDENCIES = ['http']
@ -23,6 +24,7 @@ API_ENDPOINT = '/api/alexa'
CONF_INTENTS = 'intents' CONF_INTENTS = 'intents'
CONF_CARD = 'card' CONF_CARD = 'card'
CONF_SPEECH = 'speech' CONF_SPEECH = 'speech'
CONF_ACTION = 'action'
def setup(hass, config): def setup(hass, config):
@ -80,6 +82,7 @@ def _handle_alexa(handler, path_match, data):
speech = config.get(CONF_SPEECH) speech = config.get(CONF_SPEECH)
card = config.get(CONF_CARD) card = config.get(CONF_CARD)
action = config.get(CONF_ACTION)
# pylint: disable=unsubscriptable-object # pylint: disable=unsubscriptable-object
if speech is not None: if speech is not None:
@ -89,6 +92,9 @@ def _handle_alexa(handler, path_match, data):
response.add_card(CardType[card['type']], card['title'], response.add_card(CardType[card['type']], card['title'],
card['content']) card['content'])
if action is not None:
call_from_config(handler.server.hass, action, True)
handler.write_json(response.as_dict()) handler.write_json(response.as_dict())

View File

@ -9,9 +9,9 @@ https://home-assistant.io/components/automation/
import logging import logging
from homeassistant.bootstrap import prepare_setup_platform from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.util import split_entity_id from homeassistant.const import CONF_PLATFORM
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
from homeassistant.components import logbook from homeassistant.components import logbook
from homeassistant.helpers.service import call_from_config
DOMAIN = 'automation' DOMAIN = 'automation'
@ -19,8 +19,6 @@ DEPENDENCIES = ['group']
CONF_ALIAS = 'alias' CONF_ALIAS = 'alias'
CONF_SERVICE = 'service' CONF_SERVICE = 'service'
CONF_SERVICE_ENTITY_ID = 'entity_id'
CONF_SERVICE_DATA = 'data'
CONF_CONDITION = 'condition' CONF_CONDITION = 'condition'
CONF_ACTION = 'action' CONF_ACTION = 'action'
@ -96,22 +94,7 @@ def _get_action(hass, config, name):
_LOGGER.info('Executing %s', name) _LOGGER.info('Executing %s', name)
logbook.log_entry(hass, name, 'has been triggered', DOMAIN) logbook.log_entry(hass, name, 'has been triggered', DOMAIN)
domain, service = split_entity_id(config[CONF_SERVICE]) call_from_config(hass, config)
service_data = config.get(CONF_SERVICE_DATA, {})
if not isinstance(service_data, dict):
_LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA)
service_data = {}
if CONF_SERVICE_ENTITY_ID in config:
try:
service_data[ATTR_ENTITY_ID] = \
config[CONF_SERVICE_ENTITY_ID].split(",")
except AttributeError:
service_data[ATTR_ENTITY_ID] = \
config[CONF_SERVICE_ENTITY_ID]
hass.services.call(domain, service, service_data)
return action return action

View File

@ -71,7 +71,7 @@ def setup_scanner(hass, config, see):
location = '' location = ''
if data['event'] == 'enter': if data['event'] == 'enter':
if data['desc'] == 'home': if data['desc'].lower() == 'home':
location = STATE_HOME location = STATE_HOME
else: else:
location = data['desc'] location = data['desc']

View File

@ -7,16 +7,15 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.vera/ https://home-assistant.io/components/light.vera/
""" """
import logging import logging
import time
from requests.exceptions import RequestException from requests.exceptions import RequestException
from homeassistant.components.switch.vera import VeraSwitch from homeassistant.components.switch.vera import VeraSwitch
from homeassistant.components.light import ATTR_BRIGHTNESS from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STOP, STATE_ON
REQUIREMENTS = ['pyvera==0.2.2'] REQUIREMENTS = ['pyvera==0.2.3']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -59,7 +58,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
lights = [] lights = []
for device in devices: for device in devices:
extra_data = device_data.get(device.deviceId, {}) extra_data = device_data.get(device.device_id, {})
exclude = extra_data.get('exclude', False) exclude = extra_data.get('exclude', False)
if exclude is not True: if exclude is not True:
@ -86,5 +85,5 @@ class VeraLight(VeraSwitch):
else: else:
self.vera_device.switch_on() self.vera_device.switch_on()
self.last_command_send = time.time() self._state = STATE_ON
self.is_on_status = True self.update_ha_state()

View File

@ -20,7 +20,7 @@ from homeassistant.components.media_player import (
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK,
MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO) MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
REQUIREMENTS = ['pychromecast==0.6.13'] REQUIREMENTS = ['pychromecast==0.6.14']
CONF_IGNORE_CEC = 'ignore_cec' CONF_IGNORE_CEC = 'ignore_cec'
CAST_SPLASH = 'https://home-assistant.io/images/cast/splash.png' CAST_SPLASH = 'https://home-assistant.io/images/cast/splash.png'
SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \

View File

@ -35,7 +35,7 @@ SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
def config_from_file(filename, config=None): def config_from_file(filename, config=None):
''' Small configuration file management function''' """ Small configuration file management function. """
if config: if config:
# We're writing configuration # We're writing configuration
try: try:
@ -85,7 +85,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
def setup_plexserver(host, token, hass, add_devices_callback): def setup_plexserver(host, token, hass, add_devices_callback):
''' Setup a plexserver based on host parameter''' """ Setup a plexserver based on host parameter. """
import plexapi.server import plexapi.server
import plexapi.exceptions import plexapi.exceptions

View File

@ -149,9 +149,9 @@ class MQTT(object):
} }
if client_id is None: if client_id is None:
self._mqttc = mqtt.Client() self._mqttc = mqtt.Client(protocol=mqtt.MQTTv311)
else: else:
self._mqttc = mqtt.Client(client_id) self._mqttc = mqtt.Client(client_id, protocol=mqtt.MQTTv311)
self._mqttc.user_data_set(self.userdata) self._mqttc.user_data_set(self.userdata)

View File

@ -70,5 +70,8 @@ class EliqSensor(Entity):
def update(self): def update(self):
""" Gets the latest data. """ """ Gets the latest data. """
try:
response = self.api.get_data_now(channelid=self.channel_id) response = self.api.get_data_now(channelid=self.channel_id)
self._state = int(response.power) self._state = int(response.power)
except TypeError: # raised by eliqonline library on any HTTP error
pass

View File

@ -13,14 +13,14 @@ from homeassistant.util import Throttle
from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT) from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['pyowm==2.2.1'] REQUIREMENTS = ['pyowm==2.3.0']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = { SENSOR_TYPES = {
'weather': ['Condition', ''], 'weather': ['Condition', ''],
'temperature': ['Temperature', ''], 'temperature': ['Temperature', ''],
'wind_speed': ['Wind speed', 'm/s'], 'wind_speed': ['Wind speed', 'm/s'],
'humidity': ['Humidity', '%'], 'humidity': ['Humidity', '%'],
'pressure': ['Pressure', 'hPa'], 'pressure': ['Pressure', 'mbar'],
'clouds': ['Cloud coverage', '%'], 'clouds': ['Cloud coverage', '%'],
'rain': ['Rain', 'mm'], 'rain': ['Rain', 'mm'],
'snow': ['Snow', 'mm'] 'snow': ['Snow', 'mm']

View File

@ -15,7 +15,7 @@ from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME, ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME,
TEMP_CELCIUS, TEMP_FAHRENHEIT, EVENT_HOMEASSISTANT_STOP) TEMP_CELCIUS, TEMP_FAHRENHEIT, EVENT_HOMEASSISTANT_STOP)
REQUIREMENTS = ['pyvera==0.2.2'] REQUIREMENTS = ['pyvera==0.2.3']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -56,7 +56,7 @@ def get_devices(hass, config):
vera_sensors = [] vera_sensors = []
for device in devices: for device in devices:
extra_data = device_data.get(device.deviceId, {}) extra_data = device_data.get(device.device_id, {})
exclude = extra_data.get('exclude', False) exclude = extra_data.get('exclude', False)
if exclude is not True: if exclude is not True:
@ -85,18 +85,14 @@ class VeraSensor(Entity):
self.current_value = '' self.current_value = ''
self._temperature_units = None self._temperature_units = None
self.controller.register(vera_device) self.controller.register(vera_device, self._update_callback)
self.controller.on(
vera_device, self._update_callback)
def _update_callback(self, _device): def _update_callback(self, _device):
""" Called by the vera device callback to update state. """ """ Called by the vera device callback to update state. """
_LOGGER.info(
'Subscription update for %s', self.name)
self.update_ha_state(True) self.update_ha_state(True)
def __str__(self): def __str__(self):
return "%s %s %s" % (self.name, self.vera_device.deviceId, self.state) return "%s %s %s" % (self.name, self.vera_device.device_id, self.state)
@property @property
def state(self): def state(self):
@ -119,18 +115,18 @@ class VeraSensor(Entity):
attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level + '%' attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level + '%'
if self.vera_device.is_armable: if self.vera_device.is_armable:
armed = self.vera_device.refresh_value('Armed') armed = self.vera_device.get_value('Armed')
attr[ATTR_ARMED] = 'True' if armed == '1' else 'False' attr[ATTR_ARMED] = 'True' if armed == '1' else 'False'
if self.vera_device.is_trippable: if self.vera_device.is_trippable:
last_tripped = self.vera_device.refresh_value('LastTrip') last_tripped = self.vera_device.get_value('LastTrip')
if last_tripped is not None: if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped)) utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time) utc_time)
else: else:
attr[ATTR_LAST_TRIP_TIME] = None attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.refresh_value('Tripped') tripped = self.vera_device.get_value('Tripped')
attr[ATTR_TRIPPED] = 'True' if tripped == '1' else 'False' attr[ATTR_TRIPPED] = 'True' if tripped == '1' else 'False'
attr['Vera Device Id'] = self.vera_device.vera_device_id attr['Vera Device Id'] = self.vera_device.vera_device_id
@ -143,7 +139,6 @@ class VeraSensor(Entity):
def update(self): def update(self):
if self.vera_device.category == "Temperature Sensor": if self.vera_device.category == "Temperature Sensor":
self.vera_device.refresh_value('CurrentTemperature')
current_temp = self.vera_device.get_value('CurrentTemperature') current_temp = self.vera_device.get_value('CurrentTemperature')
vera_temp_units = self.vera_device.veraController.temperature_units vera_temp_units = self.vera_device.veraController.temperature_units
@ -161,10 +156,9 @@ class VeraSensor(Entity):
self.current_value = current_temp self.current_value = current_temp
elif self.vera_device.category == "Light Sensor": elif self.vera_device.category == "Light Sensor":
self.vera_device.refresh_value('CurrentLevel')
self.current_value = self.vera_device.get_value('CurrentLevel') self.current_value = self.vera_device.get_value('CurrentLevel')
elif self.vera_device.category == "Sensor": elif self.vera_device.category == "Sensor":
tripped = self.vera_device.refresh_value('Tripped') tripped = self.vera_device.get_value('Tripped')
self.current_value = 'Tripped' if tripped == '1' else 'Not Tripped' self.current_value = 'Tripped' if tripped == '1' else 'Not Tripped'
else: else:
self.current_value = 'Unknown' self.current_value = 'Unknown'

View File

@ -25,7 +25,7 @@ SENSOR_TYPES = {
'precipitation': ['Condition', 'mm'], 'precipitation': ['Condition', 'mm'],
'temperature': ['Temperature', '°C'], 'temperature': ['Temperature', '°C'],
'windSpeed': ['Wind speed', 'm/s'], 'windSpeed': ['Wind speed', 'm/s'],
'pressure': ['Pressure', 'hPa'], 'pressure': ['Pressure', 'mbar'],
'windDirection': ['Wind direction', '°'], 'windDirection': ['Wind direction', '°'],
'humidity': ['Humidity', '%'], 'humidity': ['Humidity', '%'],
'fog': ['Fog', '%'], 'fog': ['Fog', '%'],

View File

@ -7,19 +7,21 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.vera/ https://home-assistant.io/components/switch.vera/
""" """
import logging import logging
import time
from requests.exceptions import RequestException from requests.exceptions import RequestException
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import ToggleEntity from homeassistant.components.switch import SwitchDevice
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_BATTERY_LEVEL,
ATTR_TRIPPED, ATTR_TRIPPED,
ATTR_ARMED, ATTR_ARMED,
ATTR_LAST_TRIP_TIME, ATTR_LAST_TRIP_TIME,
EVENT_HOMEASSISTANT_STOP) EVENT_HOMEASSISTANT_STOP,
STATE_ON,
STATE_OFF)
REQUIREMENTS = ['pyvera==0.2.2'] REQUIREMENTS = ['pyvera==0.2.3']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -60,7 +62,7 @@ def get_devices(hass, config):
vera_switches = [] vera_switches = []
for device in devices: for device in devices:
extra_data = device_data.get(device.deviceId, {}) extra_data = device_data.get(device.device_id, {})
exclude = extra_data.get('exclude', False) exclude = extra_data.get('exclude', False)
if exclude is not True: if exclude is not True:
@ -75,7 +77,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(get_devices(hass, config)) add_devices(get_devices(hass, config))
class VeraSwitch(ToggleEntity): class VeraSwitch(SwitchDevice):
""" Represents a Vera Switch. """ """ Represents a Vera Switch. """
def __init__(self, vera_device, controller, extra_data=None): def __init__(self, vera_device, controller, extra_data=None):
@ -86,19 +88,17 @@ class VeraSwitch(ToggleEntity):
self._name = self.extra_data.get('name') self._name = self.extra_data.get('name')
else: else:
self._name = self.vera_device.name self._name = self.vera_device.name
self.is_on_status = False self._state = STATE_OFF
# for debouncing status check after command is sent
self.last_command_send = 0
self.controller.register(vera_device) self.controller.register(vera_device, self._update_callback)
self.controller.on(
vera_device, self._update_callback)
def _update_callback(self, _device): def _update_callback(self, _device):
""" Called by the vera device callback to update state. """ """ Called by the vera device callback to update state. """
_LOGGER.info( if self.vera_device.is_switched_on():
'Subscription update for %s', self.name) self._state = STATE_ON
self.update_ha_state(True) else:
self._state = STATE_OFF
self.update_ha_state()
@property @property
def name(self): def name(self):
@ -113,18 +113,18 @@ class VeraSwitch(ToggleEntity):
attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level + '%' attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level + '%'
if self.vera_device.is_armable: if self.vera_device.is_armable:
armed = self.vera_device.refresh_value('Armed') armed = self.vera_device.get_value('Armed')
attr[ATTR_ARMED] = 'True' if armed == '1' else 'False' attr[ATTR_ARMED] = 'True' if armed == '1' else 'False'
if self.vera_device.is_trippable: if self.vera_device.is_trippable:
last_tripped = self.vera_device.refresh_value('LastTrip') last_tripped = self.vera_device.get_value('LastTrip')
if last_tripped is not None: if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped)) utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time) utc_time)
else: else:
attr[ATTR_LAST_TRIP_TIME] = None attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.refresh_value('Tripped') tripped = self.vera_device.get_value('Tripped')
attr[ATTR_TRIPPED] = 'True' if tripped == '1' else 'False' attr[ATTR_TRIPPED] = 'True' if tripped == '1' else 'False'
attr['Vera Device Id'] = self.vera_device.vera_device_id attr['Vera Device Id'] = self.vera_device.vera_device_id
@ -132,14 +132,14 @@ class VeraSwitch(ToggleEntity):
return attr return attr
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
self.last_command_send = time.time()
self.vera_device.switch_on() self.vera_device.switch_on()
self.is_on_status = True self._state = STATE_ON
self.update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
self.last_command_send = time.time()
self.vera_device.switch_off() self.vera_device.switch_off()
self.is_on_status = False self._state = STATE_OFF
self.update_ha_state()
@property @property
def should_poll(self): def should_poll(self):
@ -149,13 +149,4 @@ class VeraSwitch(ToggleEntity):
@property @property
def is_on(self): def is_on(self):
""" True if device is on. """ """ True if device is on. """
return self.is_on_status return self._state == STATE_ON
def update(self):
# We need to debounce the status call after turning switch on or off
# because the vera has some lag in updating the device status
try:
if (self.last_command_send + 5) < time.time():
self.is_on_status = self.vera_device.is_switched_on()
except RequestException:
_LOGGER.warning('Could not update status for %s', self.name)

View File

@ -12,7 +12,7 @@ from homeassistant.components.switch import SwitchDevice
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, STATE_OFF, STATE_STANDBY, EVENT_HOMEASSISTANT_STOP) STATE_ON, STATE_OFF, STATE_STANDBY, EVENT_HOMEASSISTANT_STOP)
REQUIREMENTS = ['pywemo==0.3.7'] REQUIREMENTS = ['pywemo==0.3.8']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_WEMO_SUBSCRIPTION_REGISTRY = None _WEMO_SUBSCRIPTION_REGISTRY = None
@ -69,15 +69,14 @@ class WemoSwitch(SwitchDevice):
def _update_callback(self, _device, _params): def _update_callback(self, _device, _params):
""" Called by the wemo device callback to update state. """ """ Called by the wemo device callback to update state. """
_LOGGER.info( _LOGGER.info(
'Subscription update for %s, sevice=%s', 'Subscription update for %s',
self.name, _device) _device)
self.update_ha_state(True) self.update_ha_state(True)
@property @property
def should_poll(self): def should_poll(self):
""" No polling should be needed with subscriptions """ """ No polling needed with subscriptions """
# but leave in for initial version in case of issues. return False
return True
@property @property
def unique_id(self): def unique_id(self):

View File

@ -28,7 +28,7 @@ DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel' DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DEPENDENCIES = ['alarm_control_panel'] DEPENDENCIES = ['alarm_control_panel']
REQUIREMENTS = ['vsure==0.4.3'] REQUIREMENTS = ['vsure==0.4.5']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -36,7 +36,7 @@ def extract_entity_ids(hass, service):
service_ent_id = service.data[ATTR_ENTITY_ID] service_ent_id = service.data[ATTR_ENTITY_ID]
if isinstance(service_ent_id, str): if isinstance(service_ent_id, str):
return group.expand_entity_ids(hass, [service_ent_id.lower()]) return group.expand_entity_ids(hass, [service_ent_id])
return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)] return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)]

View File

@ -0,0 +1,43 @@
"""Service calling related helpers."""
import logging
from homeassistant.util import split_entity_id
from homeassistant.const import ATTR_ENTITY_ID
CONF_SERVICE = 'service'
CONF_SERVICE_ENTITY_ID = 'entity_id'
CONF_SERVICE_DATA = 'data'
_LOGGER = logging.getLogger(__name__)
def call_from_config(hass, config, blocking=False):
"""Call a service based on a config hash."""
if not isinstance(config, dict) or CONF_SERVICE not in config:
_LOGGER.error('Missing key %s: %s', CONF_SERVICE, config)
return
try:
domain, service = split_entity_id(config[CONF_SERVICE])
except ValueError:
_LOGGER.error('Invalid service specified: %s', config[CONF_SERVICE])
return
service_data = config.get(CONF_SERVICE_DATA)
if service_data is None:
service_data = {}
elif isinstance(service_data, dict):
service_data = dict(service_data)
else:
_LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA)
service_data = {}
entity_id = config.get(CONF_SERVICE_ENTITY_ID)
if isinstance(entity_id, str):
service_data[ATTR_ENTITY_ID] = [ent.strip() for ent in
entity_id.split(",")]
elif entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.services.call(domain, service, service_data, blocking)

View File

@ -59,7 +59,7 @@ tellcore-py==1.1.2
# homeassistant.components.light.vera # homeassistant.components.light.vera
# homeassistant.components.sensor.vera # homeassistant.components.sensor.vera
# homeassistant.components.switch.vera # homeassistant.components.switch.vera
pyvera==0.2.2 pyvera==0.2.3
# homeassistant.components.wink # homeassistant.components.wink
# homeassistant.components.light.wink # homeassistant.components.light.wink
@ -69,7 +69,7 @@ pyvera==0.2.2
python-wink==0.3.1 python-wink==0.3.1
# homeassistant.components.media_player.cast # homeassistant.components.media_player.cast
pychromecast==0.6.13 pychromecast==0.6.14
# homeassistant.components.media_player.kodi # homeassistant.components.media_player.kodi
jsonrpc-requests==0.1 jsonrpc-requests==0.1
@ -135,7 +135,7 @@ python-forecastio==1.3.3
https://github.com/theolind/pymysensors/archive/d4b809c2167650691058d1e29bfd2c4b1792b4b0.zip#pymysensors==0.3 https://github.com/theolind/pymysensors/archive/d4b809c2167650691058d1e29bfd2c4b1792b4b0.zip#pymysensors==0.3
# homeassistant.components.sensor.openweathermap # homeassistant.components.sensor.openweathermap
pyowm==2.2.1 pyowm==2.3.0
# homeassistant.components.sensor.rpi_gpio # homeassistant.components.sensor.rpi_gpio
# homeassistant.components.switch.rpi_gpio # homeassistant.components.switch.rpi_gpio
@ -173,7 +173,7 @@ hikvision==0.4
orvibo==1.1.0 orvibo==1.1.0
# homeassistant.components.switch.wemo # homeassistant.components.switch.wemo
pywemo==0.3.7 pywemo==0.3.8
# homeassistant.components.tellduslive # homeassistant.components.tellduslive
tellive-py==0.5.2 tellive-py==0.5.2
@ -191,7 +191,7 @@ python-nest==2.6.0
radiotherm==1.2 radiotherm==1.2
# homeassistant.components.verisure # homeassistant.components.verisure
vsure==0.4.3 vsure==0.4.5
# homeassistant.components.zwave # homeassistant.components.zwave
pydispatcher==2.0.5 pydispatcher==2.0.5

View File

@ -4,12 +4,14 @@ tests.components.sensor.test_yr
Tests Yr sensor. Tests Yr sensor.
""" """
from datetime import datetime
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
import homeassistant.core as ha import homeassistant.core as ha
import homeassistant.components.sensor as sensor import homeassistant.components.sensor as sensor
import homeassistant.util.dt as dt_util
@pytest.mark.usefixtures('betamax_session') @pytest.mark.usefixtures('betamax_session')
@ -26,8 +28,12 @@ class TestSensorYr:
self.hass.stop() self.hass.stop()
def test_default_setup(self, betamax_session): def test_default_setup(self, betamax_session):
now = datetime(2016, 1, 5, 1, tzinfo=dt_util.UTC)
with patch('homeassistant.components.sensor.yr.requests.Session', with patch('homeassistant.components.sensor.yr.requests.Session',
return_value=betamax_session): return_value=betamax_session):
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
return_value=now):
assert sensor.setup(self.hass, { assert sensor.setup(self.hass, {
'sensor': { 'sensor': {
'platform': 'yr', 'platform': 'yr',

View File

@ -27,12 +27,13 @@ API_URL = "http://127.0.0.1:{}{}".format(SERVER_PORT, alexa.API_ENDPOINT)
HA_HEADERS = {const.HTTP_HEADER_HA_AUTH: API_PASSWORD} HA_HEADERS = {const.HTTP_HEADER_HA_AUTH: API_PASSWORD}
hass = None hass = None
calls = []
@patch('homeassistant.components.http.util.get_local_ip', @patch('homeassistant.components.http.util.get_local_ip',
return_value='127.0.0.1') return_value='127.0.0.1')
def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name
""" Initalizes a Home Assistant server. """ """Initalize a Home Assistant server for testing this module."""
global hass global hass
hass = ha.HomeAssistant() hass = ha.HomeAssistant()
@ -42,6 +43,8 @@ def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name
{http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD, {http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD,
http.CONF_SERVER_PORT: SERVER_PORT}}) http.CONF_SERVER_PORT: SERVER_PORT}})
hass.services.register('test', 'alexa', lambda call: calls.append(call))
bootstrap.setup_component(hass, alexa.DOMAIN, { bootstrap.setup_component(hass, alexa.DOMAIN, {
'alexa': { 'alexa': {
'intents': { 'intents': {
@ -61,7 +64,20 @@ def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name
'GetZodiacHoroscopeIntent': { 'GetZodiacHoroscopeIntent': {
'speech': { 'speech': {
'type': 'plaintext', 'type': 'plaintext',
'text': 'You told us your sign is {{ ZodiacSign }}.' 'text': 'You told us your sign is {{ ZodiacSign }}.',
}
},
'CallServiceIntent': {
'speech': {
'type': 'plaintext',
'text': 'Service called',
},
'action': {
'service': 'test.alexa',
'data': {
'hello': 1
},
'entity_id': 'switch.test',
} }
} }
} }
@ -231,6 +247,39 @@ class TestAlexa(unittest.TestCase):
text = req.json().get('response', {}).get('outputSpeech', {}).get('text') text = req.json().get('response', {}).get('outputSpeech', {}).get('text')
self.assertEqual('You are both home, you silly', text) self.assertEqual('You are both home, you silly', text)
def test_intent_request_calling_service(self):
data = {
'version': '1.0',
'session': {
'new': False,
'sessionId': 'amzn1.echo-api.session.0000000-0000-0000-0000-00000000000',
'application': {
'applicationId': 'amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe'
},
'attributes': {},
'user': {
'userId': 'amzn1.account.AM3B00000000000000000000000'
}
},
'request': {
'type': 'IntentRequest',
'requestId': ' amzn1.echo-api.request.0000000-0000-0000-0000-00000000000',
'timestamp': '2015-05-13T12:34:56Z',
'intent': {
'name': 'CallServiceIntent',
}
}
}
call_count = len(calls)
req = _req(data)
self.assertEqual(200, req.status_code)
self.assertEqual(call_count + 1, len(calls))
call = calls[-1]
self.assertEqual('test', call.domain)
self.assertEqual('alexa', call.service)
self.assertEqual(['switch.test'], call.data.get('entity_id'))
self.assertEqual(1, call.data.get('hello'))
def test_session_ended_request(self): def test_session_ended_request(self):
data = { data = {
'version': '1.0', 'version': '1.0',

View File

@ -6,20 +6,22 @@ Tests core compoments.
""" """
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
import unittest import unittest
from unittest.mock import patch
import homeassistant.core as ha import homeassistant.core as ha
import homeassistant.loader as loader
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF) STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF)
import homeassistant.components as comps import homeassistant.components as comps
from tests.common import get_test_home_assistant
class TestComponentsCore(unittest.TestCase): class TestComponentsCore(unittest.TestCase):
""" Tests homeassistant.components module. """ """ Tests homeassistant.components module. """
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
""" Init needed objects. """ """ Init needed objects. """
self.hass = ha.HomeAssistant() self.hass = get_test_home_assistant()
self.assertTrue(comps.setup(self.hass, {})) self.assertTrue(comps.setup(self.hass, {}))
self.hass.states.set('light.Bowl', STATE_ON) self.hass.states.set('light.Bowl', STATE_ON)
@ -58,3 +60,24 @@ class TestComponentsCore(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(runs)) self.assertEqual(1, len(runs))
@patch('homeassistant.core.ServiceRegistry.call')
def test_turn_on_to_not_block_for_domains_without_service(self, mock_call):
self.hass.services.register('light', SERVICE_TURN_ON, lambda x: x)
# We can't test if our service call results in services being called
# because by mocking out the call service method, we mock out all
# So we mimick how the service registry calls services
service_call = ha.ServiceCall('homeassistant', 'turn_on', {
'entity_id': ['light.test', 'sensor.bla', 'light.bla']
})
self.hass.services._services['homeassistant']['turn_on'](service_call)
self.assertEqual(2, mock_call.call_count)
self.assertEqual(
('light', 'turn_on', {'entity_id': ['light.bla', 'light.test']},
True),
mock_call.call_args_list[0][0])
self.assertEqual(
('sensor', 'turn_on', {'entity_id': ['sensor.bla']}, False),
mock_call.call_args_list[1][0])

View File

@ -0,0 +1,68 @@
"""
tests.helpers.test_service
~~~~~~~~~~~~~~~~~~~~~~~~~~
Test service helpers.
"""
import unittest
from unittest.mock import patch
from homeassistant.const import SERVICE_TURN_ON
from homeassistant.helpers import service
from tests.common import get_test_home_assistant, mock_service
class TestServiceHelpers(unittest.TestCase):
"""
Tests the Home Assistant service helpers.
"""
def setUp(self): # pylint: disable=invalid-name
""" things to be run when tests are started. """
self.hass = get_test_home_assistant()
self.calls = mock_service(self.hass, 'test_domain', 'test_service')
def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """
self.hass.stop()
def test_split_entity_string(self):
service.call_from_config(self.hass, {
'service': 'test_domain.test_service',
'entity_id': 'hello.world, sensor.beer'
})
self.hass.pool.block_till_done()
self.assertEqual(['hello.world', 'sensor.beer'],
self.calls[-1].data.get('entity_id'))
def test_not_mutate_input(self):
orig = {
'service': 'test_domain.test_service',
'entity_id': 'hello.world, sensor.beer',
'data': {
'hello': 1,
},
}
service.call_from_config(self.hass, orig)
self.hass.pool.block_till_done()
self.assertEqual({
'service': 'test_domain.test_service',
'entity_id': 'hello.world, sensor.beer',
'data': {
'hello': 1,
},
}, orig)
@patch('homeassistant.helpers.service._LOGGER.error')
def test_fail_silently_if_no_service(self, mock_log):
service.call_from_config(self.hass, None)
self.assertEqual(1, mock_log.call_count)
service.call_from_config(self.hass, {})
self.assertEqual(2, mock_log.call_count)
service.call_from_config(self.hass, {
'service': 'invalid'
})
self.assertEqual(3, mock_log.call_count)