From bbb251c0cf8abf2c3e5952d7eab094fb6854f2b0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 3 Dec 2016 12:17:16 -0800 Subject: [PATCH 01/89] Version bump to 0.35.0dev0 --- homeassistant/const.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 8e45ec4bb43..d9191eaedf2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,8 +1,8 @@ # coding: utf-8 """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 -MINOR_VERSION = 34 -PATCH_VERSION = '0' +MINOR_VERSION = 35 +PATCH_VERSION = '0.dev0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4, 2) From efdf51b54296020c916ce839c2408d57f6337073 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 4 Dec 2016 00:31:27 +0100 Subject: [PATCH 02/89] Bugfix sonos hosts (#4698) --- homeassistant/components/media_player/sonos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index b5367486e38..1fa1a39633d 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -59,7 +59,7 @@ ATTR_SLEEP_TIME = 'sleep_time' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_ADVERTISE_ADDR): cv.string, vol.Optional(CONF_INTERFACE_ADDR): cv.string, - vol.Optional(CONF_HOSTS): cv.ensure_list(cv.string), + vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list, [cv.string]), }) SONOS_SCHEMA = vol.Schema({ From c89e6ec915abd6681087d3c09c1c6e1532766a64 Mon Sep 17 00:00:00 2001 From: Jacob Minnis Date: Sat, 3 Dec 2016 18:56:42 -0600 Subject: [PATCH 03/89] Updated email message headers to have 'Date' and 'Message-Id' fields (#4693) (#4695) --- homeassistant/components/notify/smtp.py | 4 ++++ tests/components/notify/test_smtp.py | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/notify/smtp.py b/homeassistant/components/notify/smtp.py index 3171509b008..6ef9bc32990 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/notify/smtp.py @@ -9,6 +9,7 @@ import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage +import email.utils import voluptuous as vol @@ -18,6 +19,7 @@ from homeassistant.components.notify import ( from homeassistant.const import ( CONF_USERNAME, CONF_PASSWORD, CONF_PORT, CONF_SENDER, CONF_RECIPIENT) import homeassistant.helpers.config_validation as cv +import homeassistant.util.dt as dt_util _LOGGER = logging.getLogger(__name__) @@ -134,6 +136,8 @@ class MailNotificationService(BaseNotificationService): msg['To'] = self.recipient msg['From'] = self._sender msg['X-Mailer'] = 'HomeAssistant' + msg['Date'] = email.utils.format_datetime(dt_util.now()) + msg['Message-Id'] = email.utils.make_msgid() return self._send_email(msg) diff --git a/tests/components/notify/test_smtp.py b/tests/components/notify/test_smtp.py index bbaca71ee13..6a2f8c7acbf 100644 --- a/tests/components/notify/test_smtp.py +++ b/tests/components/notify/test_smtp.py @@ -34,16 +34,18 @@ class TestNotifySmtp(unittest.TestCase): def test_text_email(self): """Test build of default text email behavior.""" msg = self.mailer.send_message('Test msg') - expected = ('Content-Type: text/plain; charset="us-ascii"\n' + expected = ('^Content-Type: text/plain; charset="us-ascii"\n' 'MIME-Version: 1.0\n' 'Content-Transfer-Encoding: 7bit\n' 'Subject: Home Assistant\n' 'To: testrecip@test.com\n' 'From: test@test.com\n' 'X-Mailer: HomeAssistant\n' + 'Date: [^\n]+\n' + 'Message-Id: <[^@]+@[^>]+>\n' '\n' - 'Test msg') - self.assertEqual(msg, expected) + 'Test msg$') + self.assertRegex(msg, expected) def test_mixed_email(self): """Test build of mixed text email behavior.""" From 97cc76b43e0156d02b723f4e0a24509f946e668a Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie Date: Sun, 4 Dec 2016 02:27:55 +0100 Subject: [PATCH 04/89] Remove global variable from tellstick code (#4700) * Refactor tellstick code for increased readability. Especially highlight if "device" is a telldus core device or a HA entity. * Refactor Tellstick object model for increased clarity. * Update comments. Unify better with sensors. Fix typo bug. Add debug logging. * Refactor tellstick code for increased readability. Especially highlight if "device" is a telldus core device or a HA entity. * Refactor Tellstick object model for increased clarity. * Update comments. Unify better with sensors. Fix typo bug. Add debug logging. * Fix lint issues. * Remove global variable according to hint from balloob. --- homeassistant/components/light/tellstick.py | 7 ++++--- homeassistant/components/switch/tellstick.py | 3 ++- homeassistant/components/tellstick.py | 13 ++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/light/tellstick.py b/homeassistant/components/light/tellstick.py index 9afc826e83c..90add8f012e 100644 --- a/homeassistant/components/light/tellstick.py +++ b/homeassistant/components/light/tellstick.py @@ -29,16 +29,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None): signal_repetitions = discovery_info.get(ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS) - add_devices(TellstickLight(tellcore_id, signal_repetitions) + add_devices(TellstickLight(tellcore_id, hass.data['tellcore_registry'], + signal_repetitions) for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]) class TellstickLight(TellstickDevice, Light): """Representation of a Tellstick light.""" - def __init__(self, tellcore_id, signal_repetitions): + def __init__(self, tellcore_id, tellcore_registry, signal_repetitions): """Initialize the light.""" - super().__init__(tellcore_id, signal_repetitions) + super().__init__(tellcore_id, tellcore_registry, signal_repetitions) self._brightness = 255 diff --git a/homeassistant/components/switch/tellstick.py b/homeassistant/components/switch/tellstick.py index d3660ab36ca..46b1ad0aa49 100644 --- a/homeassistant/components/switch/tellstick.py +++ b/homeassistant/components/switch/tellstick.py @@ -26,7 +26,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): signal_repetitions = discovery_info.get(ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS) - add_devices(TellstickSwitch(tellcore_id, signal_repetitions) + add_devices(TellstickSwitch(tellcore_id, hass.data['tellcore_registry'], + signal_repetitions) for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]) diff --git a/homeassistant/components/tellstick.py b/homeassistant/components/tellstick.py index cbd5ff20583..e957ef5e2a8 100644 --- a/homeassistant/components/tellstick.py +++ b/homeassistant/components/tellstick.py @@ -62,8 +62,6 @@ def setup(hass, config): from tellcore.library import DirectCallbackDispatcher from tellcore.telldus import TelldusCore - global TELLCORE_REGISTRY - try: tellcore_lib = TelldusCore( callback_dispatcher=DirectCallbackDispatcher()) @@ -75,8 +73,9 @@ def setup(hass, config): all_tellcore_devices = tellcore_lib.devices() # Register devices - TELLCORE_REGISTRY = TellstickRegistry(hass, tellcore_lib) - TELLCORE_REGISTRY.register_tellcore_devices(all_tellcore_devices) + tellcore_registry = TellstickRegistry(hass, tellcore_lib) + tellcore_registry.register_tellcore_devices(all_tellcore_devices) + hass.data['tellcore_registry'] = tellcore_registry # Discover the switches _discover(hass, config, 'switch', @@ -153,17 +152,17 @@ class TellstickDevice(Entity): Contains the common logic for all Tellstick devices. """ - def __init__(self, tellcore_id, signal_repetitions): + def __init__(self, tellcore_id, tellcore_registry, signal_repetitions): """Initalize the Tellstick device.""" self._signal_repetitions = signal_repetitions self._state = None # Look up our corresponding tellcore device - self._tellcore_device = TELLCORE_REGISTRY.get_tellcore_device( + self._tellcore_device = tellcore_registry.get_tellcore_device( tellcore_id) # Query tellcore for the current state self.update() # Add ourselves to the mapping - TELLCORE_REGISTRY.register_ha_device(tellcore_id, self) + tellcore_registry.register_ha_device(tellcore_id, self) @property def should_poll(self): From cf0ff54d14ab72c2e614c98ec57de098ce736f10 Mon Sep 17 00:00:00 2001 From: Sebastian von Minckwitz Date: Sun, 4 Dec 2016 02:50:11 +0100 Subject: [PATCH 05/89] Add option to hide the group card switch (#4631) * Add option to hide the group card switch * Disallow control of hidden group switches * Revert "Disallow control of hidden group switches" This reverts commit 75e5ddfe3092327647e2873cb03ccf3b4a75b85a. * Changed hide_switch to control --- homeassistant/components/group.py | 22 ++++++++++++++++------ tests/components/test_group.py | 4 ++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index cbdfef85942..0dfabdd8a35 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -29,11 +29,13 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' CONF_ENTITIES = 'entities' CONF_VIEW = 'view' +CONF_CONTROL = 'control' ATTR_AUTO = 'auto' ATTR_ORDER = 'order' ATTR_VIEW = 'view' ATTR_VISIBLE = 'visible' +ATTR_CONTROL = 'control' SERVICE_SET_VISIBILITY = 'set_visibility' SET_VISIBILITY_SERVICE_SCHEMA = vol.Schema({ @@ -61,6 +63,7 @@ CONFIG_SCHEMA = vol.Schema({ CONF_VIEW: cv.boolean, CONF_NAME: cv.string, CONF_ICON: cv.icon, + CONF_CONTROL: cv.string, }, cv.match_all)) }, extra=vol.ALLOW_EXTRA) @@ -206,11 +209,13 @@ def _async_process_config(hass, config, component): entity_ids = conf.get(CONF_ENTITIES) or [] icon = conf.get(CONF_ICON) view = conf.get(CONF_VIEW) + control = conf.get(CONF_CONTROL) # Don't create tasks and await them all. The order is important as # groups get a number based on creation order. group = yield from Group.async_create_group( - hass, name, entity_ids, icon=icon, view=view, object_id=object_id) + hass, name, entity_ids, icon=icon, view=view, + control=control, object_id=object_id) groups.append(group) if groups: @@ -221,7 +226,7 @@ class Group(Entity): """Track a group of entity ids.""" def __init__(self, hass, name, order=None, user_defined=True, icon=None, - view=False): + view=False, control=None): """Initialize a group. This Object has factory function for creation. @@ -239,20 +244,22 @@ class Group(Entity): self._assumed_state = False self._async_unsub_state_changed = None self._visible = True + self._control = control @staticmethod def create_group(hass, name, entity_ids=None, user_defined=True, - icon=None, view=False, object_id=None): + icon=None, view=False, control=None, object_id=None): """Initialize a group.""" return run_coroutine_threadsafe( Group.async_create_group(hass, name, entity_ids, user_defined, - icon, view, object_id), + icon, view, control, object_id), hass.loop).result() @staticmethod @asyncio.coroutine def async_create_group(hass, name, entity_ids=None, user_defined=True, - icon=None, view=False, object_id=None): + icon=None, view=False, control=None, + object_id=None): """Initialize a group. This method must be run in the event loop. @@ -260,7 +267,8 @@ class Group(Entity): group = Group( hass, name, order=len(hass.states.async_entity_ids(DOMAIN)), - user_defined=user_defined, icon=icon, view=view) + user_defined=user_defined, icon=icon, view=view, + control=control) group.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, object_id or name, hass=hass) @@ -319,6 +327,8 @@ class Group(Entity): data[ATTR_AUTO] = True if self._view: data[ATTR_VIEW] = True + if self._control: + data[ATTR_CONTROL] = self._control return data @property diff --git a/tests/components/test_group.py b/tests/components/test_group.py index c5b705cbc43..00b75c3a854 100644 --- a/tests/components/test_group.py +++ b/tests/components/test_group.py @@ -228,6 +228,7 @@ class TestComponentsGroup(unittest.TestCase): 'entities': 'light.Bowl, ' + test_group.entity_id, 'icon': 'mdi:work', 'view': True, + 'control': 'hidden', } group_conf['test_group'] = 'hello.world,sensor.happy' group_conf['empty_group'] = {'name': 'Empty Group', 'entities': None} @@ -243,6 +244,8 @@ class TestComponentsGroup(unittest.TestCase): self.assertEqual('mdi:work', group_state.attributes.get(ATTR_ICON)) self.assertTrue(group_state.attributes.get(group.ATTR_VIEW)) + self.assertEqual('hidden', + group_state.attributes.get(group.ATTR_CONTROL)) self.assertTrue(group_state.attributes.get(ATTR_HIDDEN)) self.assertEqual(1, group_state.attributes.get(group.ATTR_ORDER)) @@ -254,6 +257,7 @@ class TestComponentsGroup(unittest.TestCase): self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO)) self.assertIsNone(group_state.attributes.get(ATTR_ICON)) self.assertIsNone(group_state.attributes.get(group.ATTR_VIEW)) + self.assertIsNone(group_state.attributes.get(group.ATTR_CONTROL)) self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN)) self.assertEqual(2, group_state.attributes.get(group.ATTR_ORDER)) From 10d1496f5a7d72d9ad8c13d500e67b6aaa648697 Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Sat, 3 Dec 2016 20:55:14 -0500 Subject: [PATCH 06/89] Updated python-nest to fix a camera bug when loading images (#4701) --- homeassistant/components/nest.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nest.py b/homeassistant/components/nest.py index 01f7d6ab287..e19011c47b8 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [ 'http://github.com/technicalpickles/python-nest' - '/archive/0be5c8a6307ee81540f21aac4fcd22cc5d98c988.zip' # nest-cam branch + '/archive/2512973b4b390d3965da43529cd20402ad374bfa.zip' # nest-cam branch '#python-nest==3.0.0'] DOMAIN = 'nest' diff --git a/requirements_all.txt b/requirements_all.txt index 56772281632..aefb21a93c9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -164,7 +164,7 @@ hikvision==0.4 # http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0 # homeassistant.components.nest -http://github.com/technicalpickles/python-nest/archive/0be5c8a6307ee81540f21aac4fcd22cc5d98c988.zip#python-nest==3.0.0 +http://github.com/technicalpickles/python-nest/archive/2512973b4b390d3965da43529cd20402ad374bfa.zip#python-nest==3.0.0 # homeassistant.components.light.flux_led https://github.com/Danielhiversen/flux_led/archive/0.9.zip#flux_led==0.9 From 7746ecd98ea644ecc2e6a70df209d93479c1560a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 4 Dec 2016 02:58:44 +0100 Subject: [PATCH 07/89] Migrate weather to async (#4677) --- homeassistant/components/weather/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/weather/__init__.py b/homeassistant/components/weather/__init__.py index 26a5a41bf10..67dc7924aa3 100644 --- a/homeassistant/components/weather/__init__.py +++ b/homeassistant/components/weather/__init__.py @@ -4,6 +4,7 @@ Weather component that handles meteorological data for your location. For more details about this component, please refer to the documentation at https://home-assistant.io/components/weather/ """ +import asyncio import logging from numbers import Number @@ -30,11 +31,12 @@ ATTR_WEATHER_WIND_BEARING = 'wind_bearing' ATTR_WEATHER_WIND_SPEED = 'wind_speed' -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup the weather component.""" component = EntityComponent(_LOGGER, DOMAIN, hass) - component.setup(config) + yield from component.async_setup(config) return True From a099430834e702ec839fea209098449c0c71a05b Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 3 Dec 2016 23:09:28 -0500 Subject: [PATCH 08/89] Universal source list (#4086) * Add source_list to universal media player * Expanded attirubte and command support for UMP Added support to the universal media player for the following: Volume Set Current Source Set Source Current Volume The goal is to facilitate a single-card media player that includes source selection and setting the volume of the receiver. Example setup: ``` media_player: - platform: universal name: Media Center children: - media_player.kodi - media_player.cast commands: select_source: service: media_player.select_source data: entity_id: media_player.receiver volume_set: service: media_player.volume_set data: entity_id: media_player.receiver volume_mute: service: media_player.volume_mute data: entity_id: media_player.receiver turn_on: service: homeassistant.turn_on data: entity_id: media_player.receiver turn_off: service: homeassistant.turn_off data: entity_id: media_player.receiver attributes: state: media_player.receiver is_volume_muted: media_player.receiver|is_volume_muted volume_level: media_player.receiver|volume_level source: media_player.receiver|source source_list: media_player.receiver|source_list ``` * Remove print statements * Change service call back to use call_from_config * Modified service calls to use template data * linting fixes * Add tests * linting fices * More pylinting --- .../components/media_player/universal.py | 31 ++++++---- .../components/media_player/test_universal.py | 58 ++++++++++++++++++- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/media_player/universal.py b/homeassistant/components/media_player/universal.py index 9923782872a..cedaeed4985 100644 --- a/homeassistant/components/media_player/universal.py +++ b/homeassistant/components/media_player/universal.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player import ( ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION, ATTR_MEDIA_EPISODE, ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION, ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_TRACK, - ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, + ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_INPUT_SOURCE_LIST, ATTR_SUPPORTED_MEDIA_COMMANDS, DOMAIN, SERVICE_PLAY_MEDIA, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST, @@ -38,6 +38,7 @@ CONF_COMMANDS = 'commands' CONF_PLATFORM = 'platform' CONF_SERVICE = 'service' CONF_SERVICE_DATA = 'service_data' +ATTR_DATA = 'data' CONF_STATE = 'state' OFF_STATES = [STATE_IDLE, STATE_OFF] @@ -178,14 +179,15 @@ class UniversalMediaPlayer(MediaPlayerDevice): def _call_service(self, service_name, service_data=None, allow_override=False): """Call either a specified or active child's service.""" - if allow_override and service_name in self._cmds: - call_from_config( - self.hass, self._cmds[service_name], blocking=True) - return - if service_data is None: service_data = {} + if allow_override and service_name in self._cmds: + call_from_config( + self.hass, self._cmds[service_name], + variables=service_data, blocking=True) + return + active_child = self._child_state service_data[ATTR_ENTITY_ID] = active_child.entity_id @@ -233,7 +235,7 @@ class UniversalMediaPlayer(MediaPlayerDevice): @property def volume_level(self): """Volume level of entity specified in attributes or active child.""" - return self._child_attr(ATTR_MEDIA_VOLUME_LEVEL) + return self._override_or_child_attr(ATTR_MEDIA_VOLUME_LEVEL) @property def is_volume_muted(self): @@ -322,9 +324,14 @@ class UniversalMediaPlayer(MediaPlayerDevice): return self._child_attr(ATTR_APP_NAME) @property - def current_source(self): + def source(self): """"Return the current input source of the device.""" - return self._child_attr(ATTR_INPUT_SOURCE) + return self._override_or_child_attr(ATTR_INPUT_SOURCE) + + @property + def source_list(self): + """List of available input sources.""" + return self._override_or_child_attr(ATTR_INPUT_SOURCE_LIST) @property def supported_media_commands(self): @@ -340,6 +347,8 @@ class UniversalMediaPlayer(MediaPlayerDevice): SERVICE_VOLUME_DOWN]]): flags |= SUPPORT_VOLUME_STEP flags &= ~SUPPORT_VOLUME_SET + elif SERVICE_VOLUME_SET in self._cmds: + flags |= SUPPORT_VOLUME_SET if SERVICE_VOLUME_MUTE in self._cmds and \ ATTR_MEDIA_VOLUME_MUTED in self._attrs: @@ -376,7 +385,7 @@ class UniversalMediaPlayer(MediaPlayerDevice): def set_volume_level(self, volume_level): """Set volume level, range 0..1.""" data = {ATTR_MEDIA_VOLUME_LEVEL: volume_level} - self._call_service(SERVICE_VOLUME_SET, data) + self._call_service(SERVICE_VOLUME_SET, data, allow_override=True) def media_play(self): """Send play commmand.""" @@ -424,7 +433,7 @@ class UniversalMediaPlayer(MediaPlayerDevice): def select_source(self, source): """Set the input source.""" data = {ATTR_INPUT_SOURCE: source} - self._call_service(SERVICE_SELECT_SOURCE, data) + self._call_service(SERVICE_SELECT_SOURCE, data, allow_override=True) def clear_playlist(self): """Clear players playlist.""" diff --git a/tests/components/media_player/test_universal.py b/tests/components/media_player/test_universal.py index b7018945551..76e80f8236e 100644 --- a/tests/components/media_player/test_universal.py +++ b/tests/components/media_player/test_universal.py @@ -5,6 +5,8 @@ import unittest from homeassistant.const import ( STATE_OFF, STATE_ON, STATE_UNKNOWN, STATE_PLAYING, STATE_PAUSED) import homeassistant.components.switch as switch +import homeassistant.components.input_slider as input_slider +import homeassistant.components.input_select as input_select import homeassistant.components.media_player as media_player import homeassistant.components.media_player.universal as universal @@ -142,6 +144,17 @@ class TestMediaPlayer(unittest.TestCase): self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state') self.hass.states.set(self.mock_state_switch_id, STATE_OFF) + self.mock_volume_id = input_slider.ENTITY_ID_FORMAT.format( + 'volume_level') + self.hass.states.set(self.mock_volume_id, 0) + + self.mock_source_list_id = input_select.ENTITY_ID_FORMAT.format( + 'source_list') + self.hass.states.set(self.mock_source_list_id, ['dvd', 'htpc']) + + self.mock_source_id = input_select.ENTITY_ID_FORMAT.format('source') + self.hass.states.set(self.mock_source_id, 'dvd') + self.config_children_only = { 'name': 'test', 'platform': 'universal', 'children': [media_player.ENTITY_ID_FORMAT.format('mock1'), @@ -153,6 +166,9 @@ class TestMediaPlayer(unittest.TestCase): media_player.ENTITY_ID_FORMAT.format('mock2')], 'attributes': { 'is_volume_muted': self.mock_mute_switch_id, + 'volume_level': self.mock_volume_id, + 'source': self.mock_source_id, + 'source_list': self.mock_source_list_id, 'state': self.mock_state_switch_id } } @@ -405,6 +421,42 @@ class TestMediaPlayer(unittest.TestCase): ump.update() self.assertTrue(ump.is_volume_muted) + def test_source_list_children_and_attr(self): + """Test source list property w/ children and attrs.""" + config = self.config_children_and_attr + universal.validate_config(config) + + ump = universal.UniversalMediaPlayer(self.hass, **config) + + self.assertEqual("['dvd', 'htpc']", ump.source_list) + + self.hass.states.set(self.mock_source_list_id, ['dvd', 'htpc', 'game']) + self.assertEqual("['dvd', 'htpc', 'game']", ump.source_list) + + def test_source_children_and_attr(self): + """Test source property w/ children and attrs.""" + config = self.config_children_and_attr + universal.validate_config(config) + + ump = universal.UniversalMediaPlayer(self.hass, **config) + + self.assertEqual('dvd', ump.source) + + self.hass.states.set(self.mock_source_id, 'htpc') + self.assertEqual('htpc', ump.source) + + def test_volume_level_children_and_attr(self): + """Test volume level property w/ children and attrs.""" + config = self.config_children_and_attr + universal.validate_config(config) + + ump = universal.UniversalMediaPlayer(self.hass, **config) + + self.assertEqual('0', ump.volume_level) + + self.hass.states.set(self.mock_volume_id, 100) + self.assertEqual('100', ump.volume_level) + def test_is_volume_muted_children_and_attr(self): """Test is volume muted property w/ children and attrs.""" config = self.config_children_and_attr @@ -443,18 +495,20 @@ class TestMediaPlayer(unittest.TestCase): config['commands']['volume_up'] = 'test' config['commands']['volume_down'] = 'test' config['commands']['volume_mute'] = 'test' + config['commands']['volume_set'] = 'test' + config['commands']['select_source'] = 'test' ump = universal.UniversalMediaPlayer(self.hass, **config) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name']) ump.update() - self.mock_mp_1._supported_media_commands = universal.SUPPORT_VOLUME_SET self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1.update_ha_state() ump.update() check_flags = universal.SUPPORT_TURN_ON | universal.SUPPORT_TURN_OFF \ - | universal.SUPPORT_VOLUME_STEP | universal.SUPPORT_VOLUME_MUTE + | universal.SUPPORT_VOLUME_STEP | universal.SUPPORT_VOLUME_MUTE \ + | universal.SUPPORT_SELECT_SOURCE self.assertEqual(check_flags, ump.supported_media_commands) From 776e53a7f096429cc0105650c04c5563ba29ff03 Mon Sep 17 00:00:00 2001 From: Johan Bloemberg Date: Sun, 4 Dec 2016 05:45:42 +0100 Subject: [PATCH 09/89] Dsmr hourly gas usage. (#4609) * Hourly rate of Gas consumption. Use proper unknown state. * Import unknown state constant. * doh * Cleanup device add. * Fix lint. * Add test for derivative calculation. * Remove conflict. * Document and move calculation into update call. --- homeassistant/components/sensor/dsmr.py | 101 +++++++++++++++++++----- tests/components/sensor/test_dsmr.py | 36 +++++++++ 2 files changed, 117 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/sensor/dsmr.py b/homeassistant/components/sensor/dsmr.py index 8d27f7188d2..0c42033006c 100644 --- a/homeassistant/components/sensor/dsmr.py +++ b/homeassistant/components/sensor/dsmr.py @@ -26,15 +26,15 @@ stores/caches the latest telegram and notifies the Entities that the telegram has been updated. """ import asyncio -import logging from datetime import timedelta +import logging -import voluptuous as vol - -import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_PORT, EVENT_HOMEASSISTANT_STOP +from homeassistant.const import ( + CONF_PORT, EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +import voluptuous as vol _LOGGER = logging.getLogger(__name__) @@ -65,30 +65,37 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): # Suppress logging logging.getLogger('dsmr_parser').setLevel(logging.ERROR) - from dsmr_parser import obis_references as obis + from dsmr_parser import obis_references as obis_ref from dsmr_parser.protocol import create_dsmr_reader dsmr_version = config[CONF_DSMR_VERSION] # Define list of name,obis mappings to generate entities obis_mapping = [ - ['Power Consumption', obis.CURRENT_ELECTRICITY_USAGE], - ['Power Production', obis.CURRENT_ELECTRICITY_DELIVERY], - ['Power Tariff', obis.ELECTRICITY_ACTIVE_TARIFF], - ['Power Consumption (low)', obis.ELECTRICITY_USED_TARIFF_1], - ['Power Consumption (normal)', obis.ELECTRICITY_USED_TARIFF_2], - ['Power Production (low)', obis.ELECTRICITY_DELIVERED_TARIFF_1], - ['Power Production (normal)', obis.ELECTRICITY_DELIVERED_TARIFF_2], + ['Power Consumption', obis_ref.CURRENT_ELECTRICITY_USAGE], + ['Power Production', obis_ref.CURRENT_ELECTRICITY_DELIVERY], + ['Power Tariff', obis_ref.ELECTRICITY_ACTIVE_TARIFF], + ['Power Consumption (low)', obis_ref.ELECTRICITY_USED_TARIFF_1], + ['Power Consumption (normal)', obis_ref.ELECTRICITY_USED_TARIFF_2], + ['Power Production (low)', obis_ref.ELECTRICITY_DELIVERED_TARIFF_1], + ['Power Production (normal)', obis_ref.ELECTRICITY_DELIVERED_TARIFF_2], ] - # Protocol version specific obis - if dsmr_version == '4': - obis_mapping.append(['Gas Consumption', obis.HOURLY_GAS_METER_READING]) - else: - obis_mapping.append(['Gas Consumption', obis.GAS_METER_READING]) # Generate device entities devices = [DSMREntity(name, obis) for name, obis in obis_mapping] + # Protocol version specific obis + if dsmr_version == '4': + gas_obis = obis_ref.HOURLY_GAS_METER_READING + else: + gas_obis = obis_ref.GAS_METER_READING + + # add gas meter reading and derivative for usage + devices += [ + DSMREntity('Gas Consumption', gas_obis), + DerivativeDSMREntity('Hourly Gas Consumption', gas_obis), + ] + yield from async_add_devices(devices) def update_entities_telegram(telegram): @@ -151,7 +158,10 @@ class DSMREntity(Entity): if self._obis == obis.ELECTRICITY_ACTIVE_TARIFF: return self.translate_tariff(value) else: - return value + if value: + return value + else: + return STATE_UNKNOWN @property def unit_of_measurement(self): @@ -168,4 +178,55 @@ class DSMREntity(Entity): elif value == '0001': return 'low' else: - return None + return STATE_UNKNOWN + + +class DerivativeDSMREntity(DSMREntity): + """Calculated derivative for values where the DSMR doesn't offer one. + + Gas readings are only reported per hour and don't offer a rate only + the current meter reading. This entity converts subsequents readings + into a hourly rate. + """ + + _previous_reading = None + _previous_timestamp = None + _state = STATE_UNKNOWN + + @property + def state(self): + """Return the calculated current hourly rate.""" + return self._state + + @asyncio.coroutine + def async_update(self): + """Recalculate hourly rate if timestamp has changed. + + DSMR updates gas meter reading every hour. Along with the + new value a timestamp is provided for the reading. Test + if the last known timestamp differs from the current one + then calculate a new rate for the previous hour. + """ + # check if the timestamp for the object differs from the previous one + timestamp = self.get_dsmr_object_attr('datetime') + if timestamp and timestamp != self._previous_timestamp: + current_reading = self.get_dsmr_object_attr('value') + + if self._previous_reading is None: + # can't calculate rate without previous datapoint + # just store current point + pass + else: + # recalculate the rate + diff = current_reading - self._previous_reading + self._state = diff + + self._previous_reading = current_reading + self._previous_timestamp = timestamp + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, per hour, if any.""" + unit = self.get_dsmr_object_attr('unit') + if unit: + return unit + '/h' diff --git a/tests/components/sensor/test_dsmr.py b/tests/components/sensor/test_dsmr.py index 166a4af9657..e76b26a811b 100644 --- a/tests/components/sensor/test_dsmr.py +++ b/tests/components/sensor/test_dsmr.py @@ -9,6 +9,8 @@ from decimal import Decimal from unittest.mock import Mock from homeassistant.bootstrap import async_setup_component +from homeassistant.components.sensor.dsmr import DerivativeDSMREntity +from homeassistant.const import STATE_UNKNOWN from tests.common import assert_setup_component @@ -62,3 +64,37 @@ def test_default_setup(hass, monkeypatch): power_tariff = hass.states.get('sensor.power_tariff') assert power_tariff.state == 'low' assert power_tariff.attributes.get('unit_of_measurement') is None + + +def test_derivative(): + """Test calculation of derivative value.""" + from dsmr_parser.objects import MBusObject + + entity = DerivativeDSMREntity('test', '1.0.0') + yield from entity.async_update() + + assert entity.state == STATE_UNKNOWN, 'initial state not unknown' + + entity.telegram = { + '1.0.0': MBusObject([ + {'value': 1}, + {'value': 1, 'unit': 'm3'}, + ]) + } + yield from entity.async_update() + + assert entity.state == STATE_UNKNOWN, \ + 'state after first update shoudl still be unknown' + + entity.telegram = { + '1.0.0': MBusObject([ + {'value': 2}, + {'value': 2, 'unit': 'm3'}, + ]) + } + yield from entity.async_update() + + assert entity.state == 1, \ + 'state should be difference between first and second update' + + assert entity.unit_of_measurement == 'm3/h' From ca63e4422701d6db8c34fe4db71677f5f63ae39b Mon Sep 17 00:00:00 2001 From: William Scanlon Date: Sun, 4 Dec 2016 00:39:48 -0500 Subject: [PATCH 10/89] Wink hub sensor (#4704) --- .../components/binary_sensor/wink.py | 24 +++++++++++++++++++ homeassistant/components/wink.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index 2d0e3f7226f..b129b5f24d4 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -40,6 +40,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for sensor in pywink.get_smoke_and_co_detectors(): add_devices([WinkBinarySensorDevice(sensor, hass)]) + for hub in pywink.get_hubs(): + add_devices([WinkHub(hub, hass)]) + class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): """Representation of a Wink binary sensor.""" @@ -79,3 +82,24 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): def sensor_class(self): """Return the class of this sensor, from SENSOR_CLASSES.""" return SENSOR_TYPES.get(self.capability) + + +class WinkHub(WinkDevice, BinarySensorDevice, Entity): + """Representation of a Wink Hub.""" + + def __init(self, wink, hass): + """Initialize the hub sensor.""" + WinkDevice.__init__(self, wink, hass) + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return { + 'update needed': self.wink.update_needed(), + 'firmware version': self.wink.firmware_version() + } + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self.wink.state() diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index bb374afaf86..affeb376f5c 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -15,7 +15,7 @@ from homeassistant.const import ( from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-wink==0.10.1', 'pubnubsub-handler==0.0.5'] +REQUIREMENTS = ['python-wink==0.11.0', 'pubnubsub-handler==0.0.5'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index aefb21a93c9..cd16e47d620 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -471,7 +471,7 @@ python-telegram-bot==5.2.0 python-twitch==1.3.0 # homeassistant.components.wink -python-wink==0.10.1 +python-wink==0.11.0 # homeassistant.components.keyboard # pyuserinput==0.1.11 From e8c9dcf0fe6f34352210f18cfcb5e7ee1e94e104 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 4 Dec 2016 07:08:24 +0100 Subject: [PATCH 11/89] Migrate remote to async (#4678) * Migrate remote to async * add coro * remove sync from init since only used in harmony * import ATTR from remote * remove unused sync stuff from tests --- homeassistant/components/remote/__init__.py | 76 +++++++++++---------- homeassistant/components/remote/harmony.py | 7 +- tests/components/remote/test_demo.py | 17 ----- tests/components/remote/test_init.py | 1 - 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index 8223c33d944..d6f534eae5b 100755 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -4,7 +4,9 @@ Component to interface with universal remote control devices. For more details about this component, please refer to the documentation at https://home-assistant.io/components/remote/ """ +import asyncio from datetime import timedelta +import functools as ft import logging import os @@ -80,50 +82,57 @@ def send_command(hass, device, command, entity_id=None): hass.services.call(DOMAIN, SERVICE_SEND_COMMAND, data) -def sync(hass, entity_id=None): - """Sync remote device.""" - data = {ATTR_ENTITY_ID: entity_id} if entity_id else None - hass.services.call(DOMAIN, SERVICE_SYNC, data) - - -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Track states and offer events for remotes.""" component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_REMOTES) - component.setup(config) + yield from component.async_setup(config) - def handle_remote_service(service): + @asyncio.coroutine + def async_handle_remote_service(service): """Handle calls to the remote services.""" - target_remotes = component.extract_from_service(service) + target_remotes = component.async_extract_from_service(service) activity_id = service.data.get(ATTR_ACTIVITY) device = service.data.get(ATTR_DEVICE) command = service.data.get(ATTR_COMMAND) + update_tasks = [] for remote in target_remotes: if service.service == SERVICE_TURN_ON: - remote.turn_on(activity=activity_id) + yield from remote.async_turn_on(activity=activity_id) elif service.service == SERVICE_SEND_COMMAND: - remote.send_command(device=device, command=command) - elif service.service == SERVICE_SYNC: - remote.sync() + yield from remote.async_send_command( + device=device, command=command) else: - remote.turn_off() + yield from remote.async_turn_off() if remote.should_poll: - remote.update_ha_state(True) + update_coro = remote.async_update_ha_state(True) + if hasattr(remote, 'async_update'): + update_tasks.append(hass.loop.create_task(update_coro)) + else: + yield from update_coro - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) - hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_remote_service, - descriptions.get(SERVICE_TURN_OFF), - schema=REMOTE_SERVICE_SCHEMA) - hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_remote_service, - descriptions.get(SERVICE_TURN_ON), - schema=REMOTE_SERVICE_TURN_ON_SCHEMA) - hass.services.register(DOMAIN, SERVICE_SEND_COMMAND, handle_remote_service, - descriptions.get(SERVICE_SEND_COMMAND), - schema=REMOTE_SERVICE_SEND_COMMAND_SCHEMA) + if update_tasks: + yield from asyncio.wait(update_tasks, loop=hass.loop) + + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, os.path.join( + os.path.dirname(__file__), 'services.yaml')) + hass.services.async_register( + DOMAIN, SERVICE_TURN_OFF, async_handle_remote_service, + descriptions.get(SERVICE_TURN_OFF), + schema=REMOTE_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_TURN_ON, async_handle_remote_service, + descriptions.get(SERVICE_TURN_ON), + schema=REMOTE_SERVICE_TURN_ON_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_SEND_COMMAND, async_handle_remote_service, + descriptions.get(SERVICE_SEND_COMMAND), + schema=REMOTE_SERVICE_SEND_COMMAND_SCHEMA) return True @@ -131,14 +140,11 @@ def setup(hass, config): class RemoteDevice(ToggleEntity): """Representation of a remote.""" - def turn_on(self, **kwargs): - """Turn a device on with the remote.""" - raise NotImplementedError() - - def turn_off(self, **kwargs): - """Turn a device off with the remote.""" - raise NotImplementedError() - def send_command(self, **kwargs): """Send a command to a device.""" raise NotImplementedError() + + def async_send_command(self, **kwargs): + """Send a command to a device.""" + yield from self.hass.loop.run_in_executor( + None, ft.partial(self.send_command, **kwargs)) diff --git a/homeassistant/components/remote/harmony.py b/homeassistant/components/remote/harmony.py index 1bd1e1b94cc..60d0b29c51d 100755 --- a/homeassistant/components/remote/harmony.py +++ b/homeassistant/components/remote/harmony.py @@ -14,7 +14,8 @@ import homeassistant.components.remote as remote import homeassistant.helpers.config_validation as cv from homeassistant.const import ( CONF_NAME, CONF_HOST, CONF_PORT, ATTR_ENTITY_ID) -from homeassistant.components.remote import PLATFORM_SCHEMA, DOMAIN +from homeassistant.components.remote import ( + PLATFORM_SCHEMA, DOMAIN, ATTR_DEVICE, ATTR_COMMAND, ATTR_ACTIVITY) from homeassistant.util import slugify from homeassistant.config import load_yaml_config_file @@ -22,10 +23,6 @@ REQUIREMENTS = ['pyharmony==1.0.12'] _LOGGER = logging.getLogger(__name__) -ATTR_DEVICE = 'device' -ATTR_COMMAND = 'command' -ATTR_ACTIVITY = 'activity' - DEFAULT_PORT = 5222 DEVICES = [] diff --git a/tests/components/remote/test_demo.py b/tests/components/remote/test_demo.py index f43f9e8610c..7dc8f2c8976 100755 --- a/tests/components/remote/test_demo.py +++ b/tests/components/remote/test_demo.py @@ -9,7 +9,6 @@ from homeassistant.const import ( SERVICE_TURN_ON, SERVICE_TURN_OFF) from tests.common import get_test_home_assistant, mock_service -SERVICE_SYNC = 'sync' SERVICE_SEND_COMMAND = 'send_command' @@ -83,22 +82,6 @@ class TestDemoRemote(unittest.TestCase): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual('entity_id_val', call.data[ATTR_ENTITY_ID]) - # Test sync - sync_calls = mock_service( - self.hass, remote.DOMAIN, SERVICE_SYNC) - - remote.sync( - self.hass, entity_id='entity_id_val') - - self.hass.block_till_done() - - self.assertEqual(1, len(sync_calls)) - call = sync_calls[-1] - - self.assertEqual(remote.DOMAIN, call.domain) - self.assertEqual(SERVICE_SYNC, call.service) - self.assertEqual('entity_id_val', call.data[ATTR_ENTITY_ID]) - # Test send_command send_command_calls = mock_service( self.hass, remote.DOMAIN, SERVICE_SEND_COMMAND) diff --git a/tests/components/remote/test_init.py b/tests/components/remote/test_init.py index 799ed3b5ea7..a5d711f8680 100755 --- a/tests/components/remote/test_init.py +++ b/tests/components/remote/test_init.py @@ -11,7 +11,6 @@ import homeassistant.components.remote as remote from tests.common import mock_service, get_test_home_assistant TEST_PLATFORM = {remote.DOMAIN: {CONF_PLATFORM: 'test'}} -SERVICE_SYNC = 'sync' SERVICE_SEND_COMMAND = 'send_command' From c25aa56751301a02e49ce64c6f70acd0c6cc4e8e Mon Sep 17 00:00:00 2001 From: Thibault Cohen Date: Sun, 4 Dec 2016 01:09:49 -0500 Subject: [PATCH 12/89] Add Sharp AquosTV component (#4679) --- .coveragerc | 1 + .../components/media_player/aquostv.py | 161 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 165 insertions(+) create mode 100644 homeassistant/components/media_player/aquostv.py diff --git a/.coveragerc b/.coveragerc index d0967918a60..a41a8470cb0 100644 --- a/.coveragerc +++ b/.coveragerc @@ -183,6 +183,7 @@ omit = homeassistant/components/light/x10.py homeassistant/components/light/yeelight.py homeassistant/components/lirc.py + homeassistant/components/media_player/aquostv.py homeassistant/components/media_player/braviatv.py homeassistant/components/media_player/cast.py homeassistant/components/media_player/cmus.py diff --git a/homeassistant/components/media_player/aquostv.py b/homeassistant/components/media_player/aquostv.py new file mode 100644 index 00000000000..c39986d7588 --- /dev/null +++ b/homeassistant/components/media_player/aquostv.py @@ -0,0 +1,161 @@ +""" +Support for interface with an Aquos TV. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.aquostv/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.media_player import ( + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, + SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, + SUPPORT_VOLUME_SET, MediaPlayerDevice, PLATFORM_SCHEMA) + +from homeassistant.const import ( + CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN, + CONF_PORT, CONF_USERNAME, CONF_PASSWORD) + + +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['sharp-aquos-rc==0.2'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Sharp Aquos TV' +DEFAULT_PORT = 10002 +DEFAULT_USERNAME = 'admin' +DEFAULT_PASSWORD = 'password' + +SUPPORT_SHARPTV = SUPPORT_VOLUME_STEP | \ + SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ + SUPPORT_TURN_OFF | SUPPORT_TURN_ON + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string, + vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string, +}) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Sharp Aquos TV platform.""" + import sharp_aquos_rc + + name = config.get(CONF_NAME) + port = config.get(CONF_PORT) + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + if discovery_info: + _LOGGER.debug('%s', discovery_info) + vals = discovery_info.split(':') + if len(vals) > 1: + port = vals[1] + + host = vals[0] + remote = sharp_aquos_rc.TV(host, + port, + username, + password) + add_devices([SharpAquosTVDevice(name, remote)]) + return True + + host = config.get(CONF_HOST) + remote = sharp_aquos_rc.TV(host, + port, + username, + password) + + add_devices([SharpAquosTVDevice(name, remote)]) + return True + + +# pylint: disable=abstract-method +class SharpAquosTVDevice(MediaPlayerDevice): + """Representation of a Aquos TV.""" + + # pylint: disable=too-many-public-methods + def __init__(self, name, remote): + """Initialize the aquos device.""" + # Save a reference to the imported class + self._name = name + # Assume that the TV is not muted + self._muted = False + # Assume that the TV is in Play mode + self._playing = True + self._state = STATE_UNKNOWN + self._remote = remote + self._volume = 0 + + def update(self): + """Retrieve the latest data.""" + try: + if self._remote.power() == 1: + self._state = STATE_ON + else: + self._state = STATE_OFF + + # Set TV to be able to remotely power on + # self._remote.power_on_command_settings(2) + if self._remote.mute() == 2: + self._muted = False + else: + self._muted = True + self._volume = self._remote.volume() / 60 + except OSError: + self._state = STATE_OFF + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self._state + + @property + def volume_level(self): + """Volume level of the media player (0..1).""" + return self._volume + + @property + def is_volume_muted(self): + """Boolean if volume is currently muted.""" + return self._muted + + @property + def supported_media_commands(self): + """Flag of media commands that are supported.""" + return SUPPORT_SHARPTV + + def turn_off(self): + """Turn off tvplayer.""" + self._remote.power(0) + + def volume_up(self): + """Volume up the media player.""" + self._remote.volume(int(self._volume * 60) + 2) + + def volume_down(self): + """Volume down media player.""" + self._remote.volume(int(self._volume * 60) - 2) + + def set_volume_level(self, level): + """Set Volume media player.""" + self._remote.volume(int(level * 60)) + + def mute_volume(self, mute): + """Send mute command.""" + self._remote.mute(0) + + def turn_on(self): + """Turn the media player on.""" + self._remote.power(1) diff --git a/requirements_all.txt b/requirements_all.txt index cd16e47d620..65e96319843 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -509,6 +509,9 @@ scsgate==0.1.0 # homeassistant.components.notify.sendgrid sendgrid==3.6.3 +# homeassistant.components.media_player.aquostv +sharp-aquos-rc==0.2 + # homeassistant.components.notify.slack slacker==0.9.30 From 53c1b93b6139500cdd1dbe4df7c6173de1438f21 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sun, 4 Dec 2016 01:11:52 -0500 Subject: [PATCH 13/89] Added persistent_notification in case of error during Unifi device_tracker setup (#4682) --- homeassistant/components/device_tracker/unifi.py | 14 +++++++++++++- tests/components/device_tracker/test_unifi.py | 15 ++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/device_tracker/unifi.py b/homeassistant/components/device_tracker/unifi.py index d654c3e3eef..e139775e031 100644 --- a/homeassistant/components/device_tracker/unifi.py +++ b/homeassistant/components/device_tracker/unifi.py @@ -9,6 +9,7 @@ import urllib import voluptuous as vol import homeassistant.helpers.config_validation as cv +import homeassistant.loader as loader from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD @@ -19,6 +20,9 @@ _LOGGER = logging.getLogger(__name__) CONF_PORT = 'port' CONF_SITE_ID = 'site_id' +NOTIFICATION_ID = 'unifi_notification' +NOTIFICATION_TITLE = 'Unifi Device Tracker Setup' + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST, default='localhost'): cv.string, vol.Optional(CONF_SITE_ID, default='default'): cv.string, @@ -38,10 +42,18 @@ def get_scanner(hass, config): site_id = config[DOMAIN].get(CONF_SITE_ID) port = config[DOMAIN].get(CONF_PORT) + persistent_notification = loader.get_component('persistent_notification') try: ctrl = Controller(host, username, password, port, 'v4', site_id) except urllib.error.HTTPError as ex: - _LOGGER.error('Failed to connect to unifi: %s', ex) + _LOGGER.error('Failed to connect to Unifi: %s', ex) + persistent_notification.create( + hass, 'Failed to connect to Unifi. ' + 'Error: {}
' + 'You will need to restart hass after fixing.' + ''.format(ex), + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) return False return UnifiScanner(ctrl) diff --git a/tests/components/device_tracker/test_unifi.py b/tests/components/device_tracker/test_unifi.py index 32ef8976196..5482740ce11 100644 --- a/tests/components/device_tracker/test_unifi.py +++ b/tests/components/device_tracker/test_unifi.py @@ -6,6 +6,7 @@ import urllib from unifi import controller import voluptuous as vol +from tests.common import get_test_home_assistant from homeassistant.components.device_tracker import DOMAIN, unifi as unifi from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PLATFORM) @@ -14,6 +15,14 @@ from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, class TestUnifiScanner(unittest.TestCase): """Test the Unifiy platform.""" + def setUp(self): + """Initialize values for this testcase class.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + @mock.patch('homeassistant.components.device_tracker.unifi.UnifiScanner') @mock.patch.object(controller, 'Controller') def test_config_minimal(self, mock_ctrl, mock_scanner): @@ -25,7 +34,7 @@ class TestUnifiScanner(unittest.TestCase): CONF_PASSWORD: 'password', }) } - result = unifi.get_scanner(None, config) + result = unifi.get_scanner(self.hass, config) self.assertEqual(mock_scanner.return_value, result) self.assertEqual(mock_ctrl.call_count, 1) self.assertEqual( @@ -52,7 +61,7 @@ class TestUnifiScanner(unittest.TestCase): 'site_id': 'abcdef01', }) } - result = unifi.get_scanner(None, config) + result = unifi.get_scanner(self.hass, config) self.assertEqual(mock_scanner.return_value, result) self.assertEqual(mock_ctrl.call_count, 1) self.assertEqual( @@ -96,7 +105,7 @@ class TestUnifiScanner(unittest.TestCase): } mock_ctrl.side_effect = urllib.error.HTTPError( '/', 500, 'foo', {}, None) - result = unifi.get_scanner(None, config) + result = unifi.get_scanner(self.hass, config) self.assertFalse(result) def test_scanner_update(self): # pylint: disable=no-self-use From 4d35f2805f6c910876a6d64da579520e50d30023 Mon Sep 17 00:00:00 2001 From: DaveSergeant Date: Sat, 3 Dec 2016 23:40:22 -0800 Subject: [PATCH 14/89] New support Digital Loggers relays (#4684) * initial commit Previous work included with no history. Sorry, I was figuring out how to use git, branches and deal with open source projects. At this point this is a working switch but with the shortcomings of each of the 8 ports causes a network query. This needs to be rewritten so that the SwitchDevice is part of a larger device group that is only queried once, saving traffic and preventing the small device from timing out. * Device polls independent of switches now Used anel_pwrctrl.py as a basis to extract the per-switch polling out to per-device so it can be trottled properly. Likewise, no longer touching the device independently for relay status AND relay name. Getting them both from the same statuslist() return. * Final comments and tweaks Lowered cycle and update time since the device update is working so well now. Effectively no timeouts anymore. * Added dlipower to requirements homeassistant.components.switch.digitalloggers * Tox fixes pydocstyle updates * More tox errors * Yet more tox Removed useful future TODO and helpful details on the structure of the statuslocal list. Good catch on not initializing .update(), though it worked. * Blank line fix * Added file to .coveragerc --- .coveragerc | 1 + .../components/switch/digitalloggers.py | 148 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 152 insertions(+) create mode 100755 homeassistant/components/switch/digitalloggers.py diff --git a/.coveragerc b/.coveragerc index a41a8470cb0..9078b199a3e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -318,6 +318,7 @@ omit = homeassistant/components/switch/acer_projector.py homeassistant/components/switch/anel_pwrctrl.py homeassistant/components/switch/arest.py + homeassistant/components/switch/digitalloggers.py homeassistant/components/switch/dlink.py homeassistant/components/switch/edimax.py homeassistant/components/switch/hikvisioncam.py diff --git a/homeassistant/components/switch/digitalloggers.py b/homeassistant/components/switch/digitalloggers.py new file mode 100755 index 00000000000..a9d30e52ee6 --- /dev/null +++ b/homeassistant/components/switch/digitalloggers.py @@ -0,0 +1,148 @@ +""" +Support for Digital Loggers DIN III Relays. + +Support for Digital Loggers DIN III Relays and possibly other items +through Dwight Hubbard's, python-dlipower. + +For more details about python-dlipower, please see +https://github.com/dwighthubbard/python-dlipower + +Custom ports are NOT supported due to a limitation of the dlipower +library, not the digital loggers switch + +""" +import logging +from datetime import timedelta + +import voluptuous as vol + +from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) +from homeassistant.const import ( + CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT) +import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle + + +REQUIREMENTS = ['dlipower==0.7.165'] + +CONF_CYCLETIME = 'cycletime' + +DEFAULT_NAME = 'DINRelay' +DEFAULT_USERNAME = 'admin' +DEFAULT_PASSWORD = 'admin' +DEFAULT_TIMEOUT = 20 +DEFAULT_CYCLETIME = 2 + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) + +_LOGGER = logging.getLogger(__name__) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string, + vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string, + vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): + vol.All(vol.Coerce(int), vol.Range(min=1, max=600)), + vol.Optional(CONF_CYCLETIME, default=DEFAULT_CYCLETIME): + vol.All(vol.Coerce(int), vol.Range(min=1, max=600)), + +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Find and return DIN III Relay switch.""" + import dlipower + + host = config.get(CONF_HOST) + controllername = config.get(CONF_NAME) + user = config.get(CONF_USERNAME) + pswd = config.get(CONF_PASSWORD) + tout = config.get(CONF_TIMEOUT) + cycl = config.get(CONF_CYCLETIME) + + power_switch = dlipower.PowerSwitch( + hostname=host, userid=user, password=pswd, + timeout=tout, cycletime=cycl + ) + + if not power_switch.verify(): + _LOGGER.error('Could not connect to DIN III Relay') + return False + + devices = [] + parent_device = DINRelayDevice(power_switch) + + devices.extend( + DINRelay(controllername, device.outlet_number, parent_device) + for device in power_switch + ) + + add_devices(devices) + + +class DINRelay(SwitchDevice): + """Representation of a individual DIN III relay port.""" + + def __init__(self, name, outletnumber, parent_device): + """Initialize the DIN III Relay switch.""" + self._parent_device = parent_device + self.controllername = name + self.outletnumber = outletnumber + self.update() + + @property + def name(self): + """Return the display name of this relay.""" + return self._outletname + + @property + def is_on(self): + """Return true if relay is on.""" + return self._is_on + + @property + def should_poll(self): + """Polling is needed.""" + return True + + def turn_on(self, **kwargs): + """Instruct the relay to turn on.""" + self._parent_device.turn_on(outlet=self.outletnumber) + + def turn_off(self, **kwargs): + """Instruct the relay to turn off.""" + self._parent_device.turn_off(outlet=self.outletnumber) + + def update(self): + """Trigger update for all switches on the parent device.""" + self._parent_device.update() + self._is_on = ( + self._parent_device.statuslocal[self.outletnumber - 1][2] == 'ON' + ) + self._outletname = "{}_{}".format( + self.controllername, + self._parent_device.statuslocal[self.outletnumber - 1][1] + ) + + +class DINRelayDevice(object): + """Device representation for per device throttling.""" + + def __init__(self, device): + """Initialize the DINRelay device.""" + self._device = device + self.update() + + def turn_on(self, **kwargs): + """Instruct the relay to turn on.""" + self._device.on(**kwargs) + + def turn_off(self, **kwargs): + """Instruct the relay to turn off.""" + self._device.off(**kwargs) + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Fetch new state data for this device.""" + self.statuslocal = self._device.statuslist() diff --git a/requirements_all.txt b/requirements_all.txt index 65e96319843..9dc2b5f0264 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -84,6 +84,9 @@ directpy==0.1 # homeassistant.components.updater distro==1.0.1 +# homeassistant.components.switch.digitalloggers +dlipower==0.7.165 + # homeassistant.components.notify.xmpp dnspython3==1.15.0 From b5c2be8ffa1dcf4e4bf2f763e7d2cbf6efe50990 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 4 Dec 2016 15:31:24 +0100 Subject: [PATCH 15/89] Protect hm thread for hangs on events (#4717) --- homeassistant/components/homematic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/homematic.py b/homeassistant/components/homematic.py index bf38b12237e..245e6a12cc2 100644 --- a/homeassistant/components/homematic.py +++ b/homeassistant/components/homematic.py @@ -544,19 +544,19 @@ def _hm_event_handler(hass, proxy, device, caller, attribute, value): # keypress event if attribute in HM_PRESS_EVENTS: - hass.bus.fire(EVENT_KEYPRESS, { + hass.add_job(hass.bus.async_fire(EVENT_KEYPRESS, { ATTR_NAME: hmdevice.NAME, ATTR_PARAM: attribute, ATTR_CHANNEL: channel - }) + })) return # impulse event if attribute in HM_IMPULSE_EVENTS: - hass.bus.fire(EVENT_KEYPRESS, { + hass.add_job(hass.bus.async_fire(EVENT_KEYPRESS, { ATTR_NAME: hmdevice.NAME, ATTR_CHANNEL: channel - }) + })) return _LOGGER.warning("Event is unknown and not forwarded to HA") From c8c6bee539273f39e4562b04f6b864845453783b Mon Sep 17 00:00:00 2001 From: hexa- Date: Sun, 4 Dec 2016 18:50:43 +0100 Subject: [PATCH 16/89] Revert "Update reference to correct tplink switch" (#4722) --- homeassistant/components/switch/tplink.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/tplink.py b/homeassistant/components/switch/tplink.py index 41c1d0462b3..bcc1b329fa8 100644 --- a/homeassistant/components/switch/tplink.py +++ b/homeassistant/components/switch/tplink.py @@ -15,7 +15,7 @@ from homeassistant.const import (CONF_HOST, CONF_NAME) import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['https://github.com/GadgetReactor/pyHS100/archive/' - 'fadb76c5a0e04f4995f16055845ffedc6d658316.zip#pyHS100==0.2.1'] + '1f771b7d8090a91c6a58931532e42730b021cbde.zip#pyHS100==0.2.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 9dc2b5f0264..3fdf12c086a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -173,7 +173,7 @@ http://github.com/technicalpickles/python-nest/archive/2512973b4b390d3965da43529 https://github.com/Danielhiversen/flux_led/archive/0.9.zip#flux_led==0.9 # homeassistant.components.switch.tplink -https://github.com/GadgetReactor/pyHS100/archive/fadb76c5a0e04f4995f16055845ffedc6d658316.zip#pyHS100==0.2.1 +https://github.com/GadgetReactor/pyHS100/archive/1f771b7d8090a91c6a58931532e42730b021cbde.zip#pyHS100==0.2.0 # homeassistant.components.switch.dlink https://github.com/LinuxChristian/pyW215/archive/v0.3.7.zip#pyW215==0.3.7 From 9bf13231f705c2a20d1d551cbb535ab5efacd0f8 Mon Sep 17 00:00:00 2001 From: Johan Bloemberg Date: Sun, 4 Dec 2016 18:51:40 +0100 Subject: [PATCH 17/89] Actually test calling async macvendor lookup and fix it. (#4718) --- .../components/device_tracker/__init__.py | 2 +- tests/components/device_tracker/test_init.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 91f0720e927..4c27b4075e3 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -282,7 +282,7 @@ class DeviceTracker(object): list(self.group.tracking) + [device.entity_id]) # lookup mac vendor string to be stored in config - device.set_vendor_for_mac() + yield from device.set_vendor_for_mac() # update known_devices.yaml self.hass.async_add_job( diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index e2ee21ab90d..013920985ff 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -256,6 +256,24 @@ class TestComponentsDeviceTracker(unittest.TestCase): self.assertEqual(device.vendor, 'unknown') + def test_mac_vendor_lookup_on_see(self): + """Test if macvendor is looked up when device is seen.""" + mac = 'B8:27:EB:00:00:00' + vendor_string = 'Raspberry Pi Foundation' + + tracker = device_tracker.DeviceTracker( + self.hass, timedelta(seconds=60), 0, []) + + with mock_aiohttp_client() as aioclient_mock: + aioclient_mock.get('http://api.macvendors.com/b8:27:eb', + text=vendor_string) + + run_coroutine_threadsafe( + tracker.async_see(mac=mac), self.hass.loop).result() + assert aioclient_mock.call_count == 1, \ + 'No http request for macvendor made!' + self.assertEqual(tracker.devices['b827eb000000'].vendor, vendor_string) + def test_discovery(self): """Test discovery.""" scanner = get_component('device_tracker.test').SCANNER From b2a15e17d3fd846fa750b9e319779edcf0b47a99 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 09:53:05 -0800 Subject: [PATCH 18/89] MQTT Automation: parse payload as JSON (#4703) --- homeassistant/components/automation/mqtt.py | 21 +++++++++++++++------ tests/components/automation/test_mqtt.py | 7 ++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/automation/mqtt.py b/homeassistant/components/automation/mqtt.py index 39deae3d66e..4818c02d9ff 100644 --- a/homeassistant/components/automation/mqtt.py +++ b/homeassistant/components/automation/mqtt.py @@ -4,6 +4,8 @@ Offer MQTT listening automation rules. For more details about this automation rule, please refer to the documentation at https://home-assistant.io/components/automation/#mqtt-trigger """ +import json + import voluptuous as vol from homeassistant.core import callback @@ -31,13 +33,20 @@ def async_trigger(hass, config, action): def mqtt_automation_listener(msg_topic, msg_payload, qos): """Listen for MQTT messages.""" if payload is None or payload == msg_payload: + data = { + 'platform': 'mqtt', + 'topic': msg_topic, + 'payload': msg_payload, + 'qos': qos, + } + + try: + data['payload_json'] = json.loads(msg_payload) + except ValueError: + pass + hass.async_run_job(action, { - 'trigger': { - 'platform': 'mqtt', - 'topic': msg_topic, - 'payload': msg_payload, - 'qos': qos, - } + 'trigger': data }) return mqtt.async_subscribe(hass, topic, mqtt_automation_listener) diff --git a/tests/components/automation/test_mqtt.py b/tests/components/automation/test_mqtt.py index e704b9b2d64..7ee2aadeaf6 100644 --- a/tests/components/automation/test_mqtt.py +++ b/tests/components/automation/test_mqtt.py @@ -42,16 +42,17 @@ class TestAutomationMQTT(unittest.TestCase): 'service': 'test.automation', 'data_template': { 'some': '{{ trigger.platform }} - {{ trigger.topic }}' - ' - {{ trigger.payload }}' + ' - {{ trigger.payload }} - ' + '{{ trigger.payload_json.hello }}' }, } } }) - fire_mqtt_message(self.hass, 'test-topic', 'test_payload') + fire_mqtt_message(self.hass, 'test-topic', '{ "hello": "world" }') self.hass.block_till_done() self.assertEqual(1, len(self.calls)) - self.assertEqual('mqtt - test-topic - test_payload', + self.assertEqual('mqtt - test-topic - { "hello": "world" } - world', self.calls[0].data['some']) automation.turn_off(self.hass) From 93872590b67424784c3fe309978ee0bcac935ffa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 09:54:49 -0800 Subject: [PATCH 19/89] Fix synology dsm doing I/O inside loop (#4699) --- .../components/sensor/synologydsm.py | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/sensor/synologydsm.py b/homeassistant/components/sensor/synologydsm.py index 31201879207..6dc23a71d9b 100644 --- a/homeassistant/components/sensor/synologydsm.py +++ b/homeassistant/components/sensor/synologydsm.py @@ -151,15 +151,9 @@ class SynoApi(): except: _LOGGER.error("Error setting up Synology DSM") - def utilisation(self): - """Return utilisation information from API.""" - if self._api is not None: - return self._api.utilisation - - def storage(self): - """Return storage information from API.""" - if self._api is not None: - return self._api.storage + # Will be updated when `update` gets called. + self.utilisation = self._api.utilisation + self.storage = self._api.storage @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): @@ -219,14 +213,14 @@ class SynoNasUtilSensor(SynoNasSensor): 'memory_total_swap', 'memory_total_real'] if self.var_id in network_sensors or self.var_id in memory_sensors: - attr = getattr(self._api.utilisation(), self.var_id)(False) + attr = getattr(self._api.utilisation, self.var_id)(False) if self.var_id in network_sensors: return round(attr / 1024.0, 1) elif self.var_id in memory_sensors: return round(attr / 1024.0 / 1024.0, 1) else: - return getattr(self._api.utilisation(), self.var_id) + return getattr(self._api.utilisation, self.var_id) class SynoNasStorageSensor(SynoNasSensor): @@ -240,7 +234,7 @@ class SynoNasStorageSensor(SynoNasSensor): if self.monitor_device is not None: if self.var_id in temp_sensors: - attr = getattr(self._api.storage(), + attr = getattr(self._api.storage, self.var_id)(self.monitor_device) if self._api.temp_unit == TEMP_CELSIUS: @@ -248,5 +242,5 @@ class SynoNasStorageSensor(SynoNasSensor): else: return round(attr * 1.8 + 32.0, 1) else: - return getattr(self._api.storage(), + return getattr(self._api.storage, self.var_id)(self.monitor_device) From 1b35f0878ee75f1b87587a6f60d6786b9fa6290d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 10:57:24 -0800 Subject: [PATCH 20/89] Fix CORS when static resources registered (#4727) --- homeassistant/components/http/__init__.py | 6 ++++++ tests/components/http/test_init.py | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index dc18dd2481d..11a9e755bb4 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -277,9 +277,15 @@ class HomeAssistantWSGI(object): @asyncio.coroutine def start(self): """Start the wsgi server.""" + cors_added = set() if self.cors is not None: for route in list(self.app.router.routes()): + if hasattr(route, 'resource'): + route = route.resource + if route in cors_added: + continue self.cors.add(route) + cors_added.add(route) if self.ssl_certificate: context = ssl.SSLContext(SSL_VERSION) diff --git a/tests/components/http/test_init.py b/tests/components/http/test_init.py index cd0d4fe1ffa..f50e1fb9dbf 100644 --- a/tests/components/http/test_init.py +++ b/tests/components/http/test_init.py @@ -44,6 +44,10 @@ def setUpModule(): bootstrap.setup_component(hass, 'api') + # Registering static path as it caused CORS to blow up + hass.http.register_static_path( + '/custom_components', hass.config.path('custom_components')) + hass.start() @@ -53,11 +57,12 @@ def tearDownModule(): hass.stop() -class TestHttp: +class TestCors: """Test HTTP component.""" def test_cors_allowed_with_password_in_url(self): """Test cross origin resource sharing with password in url.""" + req = requests.get(_url(const.URL_API), params={'api_password': API_PASSWORD}, headers={const.HTTP_HEADER_ORIGIN: HTTP_BASE_URL}) From a9be6c36f1129eb32377f3fc994606337ce3b929 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 10:57:48 -0800 Subject: [PATCH 21/89] Re-org emulated_hue and fix google home (#4708) --- homeassistant/components/emulated_hue.py | 566 ------------- .../components/emulated_hue/__init__.py | 198 +++++ .../components/emulated_hue/hue_api.py | 275 ++++++ homeassistant/components/emulated_hue/upnp.py | 166 ++++ tests/components/emulated_hue/__init__.py | 1 + .../test_hue_api.py} | 784 ++++++++---------- tests/components/emulated_hue/test_init.py | 55 ++ tests/components/emulated_hue/test_upnp.py | 120 +++ 8 files changed, 1170 insertions(+), 995 deletions(-) delete mode 100644 homeassistant/components/emulated_hue.py create mode 100644 homeassistant/components/emulated_hue/__init__.py create mode 100644 homeassistant/components/emulated_hue/hue_api.py create mode 100644 homeassistant/components/emulated_hue/upnp.py create mode 100644 tests/components/emulated_hue/__init__.py rename tests/components/{test_emulated_hue.py => emulated_hue/test_hue_api.py} (78%) mode change 100755 => 100644 create mode 100755 tests/components/emulated_hue/test_init.py create mode 100644 tests/components/emulated_hue/test_upnp.py diff --git a/homeassistant/components/emulated_hue.py b/homeassistant/components/emulated_hue.py deleted file mode 100644 index 7fd187daa4d..00000000000 --- a/homeassistant/components/emulated_hue.py +++ /dev/null @@ -1,566 +0,0 @@ -""" -Support for local control of entities by emulating the Phillips Hue bridge. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/emulated_hue/ -""" -import asyncio -import threading -import socket -import logging -import os -import select - -from aiohttp import web -import voluptuous as vol - -from homeassistant import util, core -from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, SERVICE_TURN_OFF, SERVICE_TURN_ON, - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - STATE_ON, STATE_OFF, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, -) -from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_SUPPORTED_FEATURES, SUPPORT_BRIGHTNESS -) -from homeassistant.components.http import ( - HomeAssistantView, HomeAssistantWSGI -) -import homeassistant.helpers.config_validation as cv - -DOMAIN = 'emulated_hue' - -_LOGGER = logging.getLogger(__name__) - -CONF_HOST_IP = 'host_ip' -CONF_LISTEN_PORT = 'listen_port' -CONF_OFF_MAPS_TO_ON_DOMAINS = 'off_maps_to_on_domains' -CONF_EXPOSE_BY_DEFAULT = 'expose_by_default' -CONF_EXPOSED_DOMAINS = 'exposed_domains' - -ATTR_EMULATED_HUE = 'emulated_hue' -ATTR_EMULATED_HUE_NAME = 'emulated_hue_name' - -DEFAULT_LISTEN_PORT = 8300 -DEFAULT_OFF_MAPS_TO_ON_DOMAINS = ['script', 'scene'] -DEFAULT_EXPOSE_BY_DEFAULT = True -DEFAULT_EXPOSED_DOMAINS = [ - 'switch', 'light', 'group', 'input_boolean', 'media_player', 'fan' -] - -HUE_API_STATE_ON = 'on' -HUE_API_STATE_BRI = 'bri' - -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Optional(CONF_HOST_IP): cv.string, - vol.Optional(CONF_LISTEN_PORT, default=DEFAULT_LISTEN_PORT): - vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)), - vol.Optional(CONF_OFF_MAPS_TO_ON_DOMAINS): cv.ensure_list, - vol.Optional(CONF_EXPOSE_BY_DEFAULT): cv.boolean, - vol.Optional(CONF_EXPOSED_DOMAINS): cv.ensure_list - }) -}, extra=vol.ALLOW_EXTRA) - - -def setup(hass, yaml_config): - """Activate the emulated_hue component.""" - config = Config(yaml_config) - - server = HomeAssistantWSGI( - hass, - development=False, - server_host=config.host_ip_addr, - server_port=config.listen_port, - api_password=None, - ssl_certificate=None, - ssl_key=None, - cors_origins=None, - use_x_forwarded_for=False, - trusted_networks=[], - login_threshold=0, - is_ban_enabled=False - ) - - server.register_view(DescriptionXmlView(config)) - server.register_view(HueUsernameView) - server.register_view(HueLightsView(config)) - - upnp_listener = UPNPResponderThread( - config.host_ip_addr, config.listen_port) - - @asyncio.coroutine - def stop_emulated_hue_bridge(event): - """Stop the emulated hue bridge.""" - upnp_listener.stop() - yield from server.stop() - - @asyncio.coroutine - def start_emulated_hue_bridge(event): - """Start the emulated hue bridge.""" - upnp_listener.start() - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, - stop_emulated_hue_bridge) - yield from server.start() - - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_emulated_hue_bridge) - - return True - - -class Config(object): - """Holds configuration variables for the emulated hue bridge.""" - - def __init__(self, yaml_config): - """Initialize the instance.""" - conf = yaml_config.get(DOMAIN, {}) - - # Get the IP address that will be passed to the Echo during discovery - self.host_ip_addr = conf.get(CONF_HOST_IP) - if self.host_ip_addr is None: - self.host_ip_addr = util.get_local_ip() - _LOGGER.warning( - "Listen IP address not specified, auto-detected address is %s", - self.host_ip_addr) - - # Get the port that the Hue bridge will listen on - self.listen_port = conf.get(CONF_LISTEN_PORT) - if not isinstance(self.listen_port, int): - self.listen_port = DEFAULT_LISTEN_PORT - _LOGGER.warning( - "Listen port not specified, defaulting to %s", - self.listen_port) - - # Get domains that cause both "on" and "off" commands to map to "on" - # This is primarily useful for things like scenes or scripts, which - # don't really have a concept of being off - self.off_maps_to_on_domains = conf.get(CONF_OFF_MAPS_TO_ON_DOMAINS) - if not isinstance(self.off_maps_to_on_domains, list): - self.off_maps_to_on_domains = DEFAULT_OFF_MAPS_TO_ON_DOMAINS - - # Get whether or not entities should be exposed by default, or if only - # explicitly marked ones will be exposed - self.expose_by_default = conf.get( - CONF_EXPOSE_BY_DEFAULT, DEFAULT_EXPOSE_BY_DEFAULT) - - # Get domains that are exposed by default when expose_by_default is - # True - self.exposed_domains = conf.get( - CONF_EXPOSED_DOMAINS, DEFAULT_EXPOSED_DOMAINS) - - -class DescriptionXmlView(HomeAssistantView): - """Handles requests for the description.xml file.""" - - url = '/description.xml' - name = 'description:xml' - requires_auth = False - - def __init__(self, config): - """Initialize the instance of the view.""" - self.config = config - - @core.callback - def get(self, request): - """Handle a GET request.""" - xml_template = """ - - -1 -0 - -http://{0}:{1}/ - -urn:schemas-upnp-org:device:Basic:1 -HASS Bridge ({0}) -Royal Philips Electronics -http://www.philips.com -Philips hue Personal Wireless Lighting -Philips hue bridge 2015 -BSB002 -http://www.meethue.com -1234 -uuid:2f402f80-da50-11e1-9b23-001788255acc - - -""" - - resp_text = xml_template.format( - self.config.host_ip_addr, self.config.listen_port) - - return web.Response(text=resp_text, content_type='text/xml') - - -class HueUsernameView(HomeAssistantView): - """Handle requests to create a username for the emulated hue bridge.""" - - url = '/api' - name = 'hue:api' - extra_urls = ['/api/'] - requires_auth = False - - @asyncio.coroutine - def post(self, request): - """Handle a POST request.""" - try: - data = yield from request.json() - except ValueError: - return self.json_message('Invalid JSON', HTTP_BAD_REQUEST) - - if 'devicetype' not in data: - return self.json_message('devicetype not specified', - HTTP_BAD_REQUEST) - - return self.json([{'success': {'username': '12345678901234567890'}}]) - - -class HueLightsView(HomeAssistantView): - """Handle requests for getting and setting info about entities.""" - - url = '/api/{username}/lights' - name = 'api:username:lights' - extra_urls = ['/api/{username}/lights/{entity_id}', - '/api/{username}/lights/{entity_id}/state'] - requires_auth = False - - def __init__(self, config): - """Initialize the instance of the view.""" - self.config = config - self.cached_states = {} - - @core.callback - def get(self, request, username, entity_id=None): - """Handle a GET request.""" - hass = request.app['hass'] - - if entity_id is None: - return self.async_get_lights_list(hass) - - if not request.path.endswith('state'): - return self.async_get_light_state(hass, entity_id) - - return web.Response(text="Method not allowed", status=405) - - @asyncio.coroutine - def put(self, request, username, entity_id=None): - """Handle a PUT request.""" - hass = request.app['hass'] - - if not request.path.endswith('state'): - return web.Response(text="Method not allowed", status=405) - - if entity_id and hass.states.get(entity_id) is None: - return self.json_message('Entity not found', HTTP_NOT_FOUND) - - try: - json_data = yield from request.json() - except ValueError: - return self.json_message('Invalid JSON', HTTP_BAD_REQUEST) - - result = yield from self.async_put_light_state(hass, json_data, - entity_id) - return result - - @core.callback - def async_get_lights_list(self, hass): - """Process a request to get the list of available lights.""" - json_response = {} - - for entity in hass.states.async_all(): - if self.is_entity_exposed(entity): - json_response[entity.entity_id] = entity_to_json(entity) - - return self.json(json_response) - - @core.callback - def async_get_light_state(self, hass, entity_id): - """Process a request to get the state of an individual light.""" - entity = hass.states.get(entity_id) - if entity is None or not self.is_entity_exposed(entity): - return web.Response(text="Entity not found", status=404) - - cached_state = self.cached_states.get(entity_id, None) - - if cached_state is None: - final_state = entity.state == STATE_ON - final_brightness = entity.attributes.get( - ATTR_BRIGHTNESS, 255 if final_state else 0) - else: - final_state, final_brightness = cached_state - - json_response = entity_to_json(entity, final_state, final_brightness) - - return self.json(json_response) - - @asyncio.coroutine - def async_put_light_state(self, hass, request_json, entity_id): - """Process a request to set the state of an individual light.""" - config = self.config - - # Retrieve the entity from the state machine - entity = hass.states.get(entity_id) - if entity is None: - return web.Response(text="Entity not found", status=404) - - if not self.is_entity_exposed(entity): - return web.Response(text="Entity not found", status=404) - - # Parse the request into requested "on" status and brightness - parsed = parse_hue_api_put_light_body(request_json, entity) - - if parsed is None: - return web.Response(text="Bad request", status=400) - - result, brightness = parsed - - # Convert the resulting "on" status into the service we need to call - service = SERVICE_TURN_ON if result else SERVICE_TURN_OFF - - # Construct what we need to send to the service - data = {ATTR_ENTITY_ID: entity_id} - - # If the requested entity is a script add some variables - if entity.domain.lower() == "script": - data['variables'] = { - 'requested_state': STATE_ON if result else STATE_OFF - } - - if brightness is not None: - data['variables']['requested_level'] = brightness - - elif brightness is not None: - data[ATTR_BRIGHTNESS] = brightness - - if entity.domain.lower() in config.off_maps_to_on_domains: - # Map the off command to on - service = SERVICE_TURN_ON - - # Caching is required because things like scripts and scenes won't - # report as "off" to Alexa if an "off" command is received, because - # they'll map to "on". Thus, instead of reporting its actual - # status, we report what Alexa will want to see, which is the same - # as the actual requested command. - self.cached_states[entity_id] = (result, brightness) - - # Perform the requested action - yield from hass.services.async_call(core.DOMAIN, service, data, - blocking=True) - - json_response = \ - [create_hue_success_response(entity_id, HUE_API_STATE_ON, result)] - - if brightness is not None: - json_response.append(create_hue_success_response( - entity_id, HUE_API_STATE_BRI, brightness)) - - return self.json(json_response) - - def is_entity_exposed(self, entity): - """Determine if an entity should be exposed on the emulated bridge. - - Async friendly. - """ - config = self.config - - if entity.attributes.get('view') is not None: - # Ignore entities that are views - return False - - domain = entity.domain.lower() - explicit_expose = entity.attributes.get(ATTR_EMULATED_HUE, None) - - domain_exposed_by_default = \ - config.expose_by_default and domain in config.exposed_domains - - # Expose an entity if the entity's domain is exposed by default and - # the configuration doesn't explicitly exclude it from being - # exposed, or if the entity is explicitly exposed - is_default_exposed = \ - domain_exposed_by_default and explicit_expose is not False - - return is_default_exposed or explicit_expose - - -def parse_hue_api_put_light_body(request_json, entity): - """Parse the body of a request to change the state of a light.""" - if HUE_API_STATE_ON in request_json: - if not isinstance(request_json[HUE_API_STATE_ON], bool): - return None - - if request_json['on']: - # Echo requested device be turned on - brightness = None - report_brightness = False - result = True - else: - # Echo requested device be turned off - brightness = None - report_brightness = False - result = False - - if HUE_API_STATE_BRI in request_json: - # Make sure the entity actually supports brightness - entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - - if (entity_features & SUPPORT_BRIGHTNESS) == SUPPORT_BRIGHTNESS: - try: - # Clamp brightness from 0 to 255 - brightness = \ - max(0, min(int(request_json[HUE_API_STATE_BRI]), 255)) - except ValueError: - return None - - report_brightness = True - result = (brightness > 0) - elif entity.domain.lower() == "script": - # Convert 0-255 to 0-100 - level = int(request_json[HUE_API_STATE_BRI]) / 255 * 100 - - brightness = round(level) - report_brightness = True - result = True - - return (result, brightness) if report_brightness else (result, None) - - -def entity_to_json(entity, is_on=None, brightness=None): - """Convert an entity to its Hue bridge JSON representation.""" - if is_on is None: - is_on = entity.state == STATE_ON - - if brightness is None: - brightness = 255 if is_on else 0 - - name = entity.attributes.get( - ATTR_EMULATED_HUE_NAME, entity.attributes[ATTR_FRIENDLY_NAME]) - - return { - 'state': - { - HUE_API_STATE_ON: is_on, - HUE_API_STATE_BRI: brightness, - 'reachable': True - }, - 'type': 'Dimmable light', - 'name': name, - 'modelid': 'HASS123', - 'uniqueid': entity.entity_id, - 'swversion': '123' - } - - -def create_hue_success_response(entity_id, attr, value): - """Create a success response for an attribute set on a light.""" - success_key = '/lights/{}/state/{}'.format(entity_id, attr) - return {'success': {success_key: value}} - - -class UPNPResponderThread(threading.Thread): - """Handle responding to UPNP/SSDP discovery requests.""" - - _interrupted = False - - def __init__(self, host_ip_addr, listen_port): - """Initialize the class.""" - threading.Thread.__init__(self) - - self.host_ip_addr = host_ip_addr - self.listen_port = listen_port - - # Note that the double newline at the end of - # this string is required per the SSDP spec - resp_template = """HTTP/1.1 200 OK -CACHE-CONTROL: max-age=60 -EXT: -LOCATION: http://{0}:{1}/description.xml -SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1 -ST: urn:schemas-upnp-org:device:basic:1 -USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1 - -""" - - self.upnp_response = resp_template.format(host_ip_addr, listen_port) \ - .replace("\n", "\r\n") \ - .encode('utf-8') - - # Set up a pipe for signaling to the receiver that it's time to - # shutdown. Essentially, we place the SSDP socket into nonblocking - # mode and use select() to wait for data to arrive on either the SSDP - # socket or the pipe. If data arrives on either one, select() returns - # and tells us which filenos have data ready to read. - # - # When we want to stop the responder, we write data to the pipe, which - # causes the select() to return and indicate that said pipe has data - # ready to be read, which indicates to us that the responder needs to - # be shutdown. - self._interrupted_read_pipe, self._interrupted_write_pipe = os.pipe() - - def run(self): - """Run the server.""" - # Listen for UDP port 1900 packets sent to SSDP multicast address - ssdp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - ssdp_socket.setblocking(False) - - # Required for receiving multicast - ssdp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - ssdp_socket.setsockopt( - socket.SOL_IP, - socket.IP_MULTICAST_IF, - socket.inet_aton(self.host_ip_addr)) - - ssdp_socket.setsockopt( - socket.SOL_IP, - socket.IP_ADD_MEMBERSHIP, - socket.inet_aton("239.255.255.250") + - socket.inet_aton(self.host_ip_addr)) - - ssdp_socket.bind(("239.255.255.250", 1900)) - - while True: - if self._interrupted: - clean_socket_close(ssdp_socket) - return - - try: - read, _, _ = select.select( - [self._interrupted_read_pipe, ssdp_socket], [], - [ssdp_socket]) - - if self._interrupted_read_pipe in read: - # Implies self._interrupted is True - clean_socket_close(ssdp_socket) - return - elif ssdp_socket in read: - data, addr = ssdp_socket.recvfrom(1024) - else: - continue - except socket.error as ex: - if self._interrupted: - clean_socket_close(ssdp_socket) - return - - _LOGGER.error("UPNP Responder socket exception occured: %s", - ex.__str__) - - if "M-SEARCH" in data.decode('utf-8'): - # SSDP M-SEARCH method received, respond to it with our info - resp_socket = socket.socket( - socket.AF_INET, socket.SOCK_DGRAM) - - resp_socket.sendto(self.upnp_response, addr) - resp_socket.close() - - def stop(self): - """Stop the server.""" - # Request for server - self._interrupted = True - os.write(self._interrupted_write_pipe, bytes([0])) - self.join() - - -def clean_socket_close(sock): - """Close a socket connection and logs its closure.""" - _LOGGER.info("UPNP responder shutting down.") - - sock.close() diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py new file mode 100644 index 00000000000..2efce06528d --- /dev/null +++ b/homeassistant/components/emulated_hue/__init__.py @@ -0,0 +1,198 @@ +""" +Support for local control of entities by emulating the Phillips Hue bridge. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/emulated_hue/ +""" +import asyncio +import logging + +import voluptuous as vol + +from homeassistant import util +from homeassistant.const import ( + EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, +) +from homeassistant.components.http import HomeAssistantWSGI +import homeassistant.helpers.config_validation as cv +from .hue_api import ( + HueUsernameView, HueAllLightsStateView, HueOneLightStateView, + HueOneLightChangeView) +from .upnp import DescriptionXmlView, UPNPResponderThread + +DOMAIN = 'emulated_hue' + +_LOGGER = logging.getLogger(__name__) + +CONF_HOST_IP = 'host_ip' +CONF_LISTEN_PORT = 'listen_port' +CONF_OFF_MAPS_TO_ON_DOMAINS = 'off_maps_to_on_domains' +CONF_EXPOSE_BY_DEFAULT = 'expose_by_default' +CONF_EXPOSED_DOMAINS = 'exposed_domains' +CONF_TYPE = 'type' + +TYPE_ALEXA = 'alexa' +TYPE_GOOGLE = 'google_home' + +DEFAULT_LISTEN_PORT = 8300 +DEFAULT_OFF_MAPS_TO_ON_DOMAINS = ['script', 'scene'] +DEFAULT_EXPOSE_BY_DEFAULT = True +DEFAULT_EXPOSED_DOMAINS = [ + 'switch', 'light', 'group', 'input_boolean', 'media_player', 'fan' +] +DEFAULT_TYPE = TYPE_ALEXA + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Optional(CONF_HOST_IP): cv.string, + vol.Optional(CONF_LISTEN_PORT, default=DEFAULT_LISTEN_PORT): + vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)), + vol.Optional(CONF_OFF_MAPS_TO_ON_DOMAINS): cv.ensure_list, + vol.Optional(CONF_EXPOSE_BY_DEFAULT): cv.boolean, + vol.Optional(CONF_EXPOSED_DOMAINS): cv.ensure_list, + vol.Optional(CONF_TYPE, default=DEFAULT_TYPE): + vol.Any(TYPE_ALEXA, TYPE_GOOGLE) + }) +}, extra=vol.ALLOW_EXTRA) + +ATTR_EMULATED_HUE = 'emulated_hue' + + +def setup(hass, yaml_config): + """Activate the emulated_hue component.""" + config = Config(yaml_config.get(DOMAIN, {})) + + server = HomeAssistantWSGI( + hass, + development=False, + server_host=config.host_ip_addr, + server_port=config.listen_port, + api_password=None, + ssl_certificate=None, + ssl_key=None, + cors_origins=None, + use_x_forwarded_for=False, + trusted_networks=[], + login_threshold=0, + is_ban_enabled=False + ) + + server.register_view(DescriptionXmlView(config)) + server.register_view(HueUsernameView) + server.register_view(HueAllLightsStateView(config)) + server.register_view(HueOneLightStateView(config)) + server.register_view(HueOneLightChangeView(config)) + + upnp_listener = UPNPResponderThread( + config.host_ip_addr, config.listen_port) + + @asyncio.coroutine + def stop_emulated_hue_bridge(event): + """Stop the emulated hue bridge.""" + upnp_listener.stop() + yield from server.stop() + + @asyncio.coroutine + def start_emulated_hue_bridge(event): + """Start the emulated hue bridge.""" + upnp_listener.start() + yield from server.start() + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, + stop_emulated_hue_bridge) + + hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_emulated_hue_bridge) + + return True + + +class Config(object): + """Holds configuration variables for the emulated hue bridge.""" + + def __init__(self, conf): + """Initialize the instance.""" + self.type = conf.get(CONF_TYPE) + self.numbers = {} + self.cached_states = {} + + # Get the IP address that will be passed to the Echo during discovery + self.host_ip_addr = conf.get(CONF_HOST_IP) + if self.host_ip_addr is None: + self.host_ip_addr = util.get_local_ip() + _LOGGER.warning( + "Listen IP address not specified, auto-detected address is %s", + self.host_ip_addr) + + # Get the port that the Hue bridge will listen on + self.listen_port = conf.get(CONF_LISTEN_PORT) + if not isinstance(self.listen_port, int): + self.listen_port = DEFAULT_LISTEN_PORT + _LOGGER.warning( + "Listen port not specified, defaulting to %s", + self.listen_port) + + if self.type == TYPE_GOOGLE and self.listen_port != 80: + _LOGGER.warning('When targetting Google Home, listening port has ' + 'to be port 80') + + # Get domains that cause both "on" and "off" commands to map to "on" + # This is primarily useful for things like scenes or scripts, which + # don't really have a concept of being off + self.off_maps_to_on_domains = conf.get(CONF_OFF_MAPS_TO_ON_DOMAINS) + if not isinstance(self.off_maps_to_on_domains, list): + self.off_maps_to_on_domains = DEFAULT_OFF_MAPS_TO_ON_DOMAINS + + # Get whether or not entities should be exposed by default, or if only + # explicitly marked ones will be exposed + self.expose_by_default = conf.get( + CONF_EXPOSE_BY_DEFAULT, DEFAULT_EXPOSE_BY_DEFAULT) + + # Get domains that are exposed by default when expose_by_default is + # True + self.exposed_domains = conf.get( + CONF_EXPOSED_DOMAINS, DEFAULT_EXPOSED_DOMAINS) + + def entity_id_to_number(self, entity_id): + """Get a unique number for the entity id.""" + if self.type == TYPE_ALEXA: + return entity_id + + # Google Home + for number, ent_id in self.numbers.items(): + if entity_id == ent_id: + return number + + number = str(len(self.numbers) + 1) + self.numbers[number] = entity_id + return number + + def number_to_entity_id(self, number): + """Convert unique number to entity id.""" + if self.type == TYPE_ALEXA: + return number + + # Google Home + assert isinstance(number, str) + return self.numbers.get(number) + + def is_entity_exposed(self, entity): + """Determine if an entity should be exposed on the emulated bridge. + + Async friendly. + """ + if entity.attributes.get('view') is not None: + # Ignore entities that are views + return False + + domain = entity.domain.lower() + explicit_expose = entity.attributes.get(ATTR_EMULATED_HUE, None) + + domain_exposed_by_default = \ + self.expose_by_default and domain in self.exposed_domains + + # Expose an entity if the entity's domain is exposed by default and + # the configuration doesn't explicitly exclude it from being + # exposed, or if the entity is explicitly exposed + is_default_exposed = \ + domain_exposed_by_default and explicit_expose is not False + + return is_default_exposed or explicit_expose diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py new file mode 100644 index 00000000000..ed06da9495b --- /dev/null +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -0,0 +1,275 @@ +"""Provides a Hue API to control Home Assistant.""" +import asyncio +import logging + +from aiohttp import web + +from homeassistant import core +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, SERVICE_TURN_OFF, SERVICE_TURN_ON, + STATE_ON, STATE_OFF, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, +) +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_SUPPORTED_FEATURES, SUPPORT_BRIGHTNESS +) +from homeassistant.components.http import HomeAssistantView + +_LOGGER = logging.getLogger(__name__) + +ATTR_EMULATED_HUE = 'emulated_hue' +ATTR_EMULATED_HUE_NAME = 'emulated_hue_name' + +HUE_API_STATE_ON = 'on' +HUE_API_STATE_BRI = 'bri' + + +class HueUsernameView(HomeAssistantView): + """Handle requests to create a username for the emulated hue bridge.""" + + url = '/api' + name = 'emulated_hue:api:create_username' + extra_urls = ['/api/'] + requires_auth = False + + @asyncio.coroutine + def post(self, request): + """Handle a POST request.""" + try: + data = yield from request.json() + except ValueError: + return self.json_message('Invalid JSON', HTTP_BAD_REQUEST) + + if 'devicetype' not in data: + return self.json_message('devicetype not specified', + HTTP_BAD_REQUEST) + + return self.json([{'success': {'username': '12345678901234567890'}}]) + + +class HueAllLightsStateView(HomeAssistantView): + """Handle requests for getting and setting info about entities.""" + + url = '/api/{username}/lights' + name = 'emulated_hue:lights:state' + requires_auth = False + + def __init__(self, config): + """Initialize the instance of the view.""" + self.config = config + + @core.callback + def get(self, request, username): + """Process a request to get the list of available lights.""" + hass = request.app['hass'] + json_response = {} + + for entity in hass.states.async_all(): + if self.config.is_entity_exposed(entity): + number = self.config.entity_id_to_number(entity.entity_id) + json_response[number] = entity_to_json(entity) + + return self.json(json_response) + + +class HueOneLightStateView(HomeAssistantView): + """Handle requests for getting and setting info about entities.""" + + url = '/api/{username}/lights/{entity_id}' + name = 'emulated_hue:light:state' + requires_auth = False + + def __init__(self, config): + """Initialize the instance of the view.""" + self.config = config + + @core.callback + def get(self, request, username, entity_id=None): + """Process a request to get the state of an individual light.""" + hass = request.app['hass'] + entity_id = self.config.number_to_entity_id(entity_id) + entity = hass.states.get(entity_id) + + if entity is None: + _LOGGER.error('Entity not found: %s', entity_id) + return web.Response(text="Entity not found", status=404) + + if not self.config.is_entity_exposed(entity): + _LOGGER.error('Entity not exposed: %s', entity_id) + return web.Response(text="Entity not exposed", status=404) + + cached_state = self.config.cached_states.get(entity_id, None) + + if cached_state is None: + final_state = entity.state == STATE_ON + final_brightness = entity.attributes.get( + ATTR_BRIGHTNESS, 255 if final_state else 0) + else: + final_state, final_brightness = cached_state + + json_response = entity_to_json(entity, final_state, final_brightness) + + return self.json(json_response) + + +class HueOneLightChangeView(HomeAssistantView): + """Handle requests for getting and setting info about entities.""" + + url = '/api/{username}/lights/{entity_number}/state' + name = 'emulated_hue:light:state' + requires_auth = False + + def __init__(self, config): + """Initialize the instance of the view.""" + self.config = config + + @asyncio.coroutine + def put(self, request, username, entity_number): + """Process a request to set the state of an individual light.""" + config = self.config + hass = request.app['hass'] + entity_id = config.number_to_entity_id(entity_number) + + if entity_id is None: + _LOGGER.error('Unknown entity number: %s', entity_number) + return self.json_message('Entity not found', HTTP_NOT_FOUND) + + entity = hass.states.get(entity_id) + + if entity is None: + _LOGGER.error('Entity not found: %s', entity_id) + return self.json_message('Entity not found', HTTP_NOT_FOUND) + + if not config.is_entity_exposed(entity): + _LOGGER.error('Entity not exposed: %s', entity_id) + return web.Response(text="Entity not exposed", status=404) + + try: + request_json = yield from request.json() + except ValueError: + _LOGGER.error('Received invalid json') + return self.json_message('Invalid JSON', HTTP_BAD_REQUEST) + + # Parse the request into requested "on" status and brightness + parsed = parse_hue_api_put_light_body(request_json, entity) + + if parsed is None: + _LOGGER.error('Unable to parse data: %s', request_json) + return web.Response(text="Bad request", status=400) + + result, brightness = parsed + + # Convert the resulting "on" status into the service we need to call + service = SERVICE_TURN_ON if result else SERVICE_TURN_OFF + + # Construct what we need to send to the service + data = {ATTR_ENTITY_ID: entity_id} + + # If the requested entity is a script add some variables + if entity.domain == "script": + data['variables'] = { + 'requested_state': STATE_ON if result else STATE_OFF + } + + if brightness is not None: + data['variables']['requested_level'] = brightness + + elif brightness is not None: + data[ATTR_BRIGHTNESS] = brightness + + if entity.domain in config.off_maps_to_on_domains: + # Map the off command to on + service = SERVICE_TURN_ON + + # Caching is required because things like scripts and scenes won't + # report as "off" to Alexa if an "off" command is received, because + # they'll map to "on". Thus, instead of reporting its actual + # status, we report what Alexa will want to see, which is the same + # as the actual requested command. + config.cached_states[entity_id] = (result, brightness) + + # Perform the requested action + yield from hass.services.async_call(core.DOMAIN, service, data, + blocking=True) + + json_response = \ + [create_hue_success_response(entity_id, HUE_API_STATE_ON, result)] + + if brightness is not None: + json_response.append(create_hue_success_response( + entity_id, HUE_API_STATE_BRI, brightness)) + + return self.json(json_response) + + +def parse_hue_api_put_light_body(request_json, entity): + """Parse the body of a request to change the state of a light.""" + if HUE_API_STATE_ON in request_json: + if not isinstance(request_json[HUE_API_STATE_ON], bool): + return None + + if request_json['on']: + # Echo requested device be turned on + brightness = None + report_brightness = False + result = True + else: + # Echo requested device be turned off + brightness = None + report_brightness = False + result = False + + if HUE_API_STATE_BRI in request_json: + # Make sure the entity actually supports brightness + entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + + if (entity_features & SUPPORT_BRIGHTNESS) == SUPPORT_BRIGHTNESS: + try: + # Clamp brightness from 0 to 255 + brightness = \ + max(0, min(int(request_json[HUE_API_STATE_BRI]), 255)) + except ValueError: + return None + + report_brightness = True + result = (brightness > 0) + elif entity.domain.lower() == "script": + # Convert 0-255 to 0-100 + level = int(request_json[HUE_API_STATE_BRI]) / 255 * 100 + + brightness = round(level) + report_brightness = True + result = True + + return (result, brightness) if report_brightness else (result, None) + + +def entity_to_json(entity, is_on=None, brightness=None): + """Convert an entity to its Hue bridge JSON representation.""" + if is_on is None: + is_on = entity.state == STATE_ON + + if brightness is None: + brightness = 255 if is_on else 0 + + name = entity.attributes.get( + ATTR_EMULATED_HUE_NAME, entity.attributes[ATTR_FRIENDLY_NAME]) + + return { + 'state': + { + HUE_API_STATE_ON: is_on, + HUE_API_STATE_BRI: brightness, + 'reachable': True + }, + 'type': 'Dimmable light', + 'name': name, + 'modelid': 'HASS123', + 'uniqueid': entity.entity_id, + 'swversion': '123' + } + + +def create_hue_success_response(entity_id, attr, value): + """Create a success response for an attribute set on a light.""" + success_key = '/lights/{}/state/{}'.format(entity_id, attr) + return {'success': {success_key: value}} diff --git a/homeassistant/components/emulated_hue/upnp.py b/homeassistant/components/emulated_hue/upnp.py new file mode 100644 index 00000000000..f81a8c1b68d --- /dev/null +++ b/homeassistant/components/emulated_hue/upnp.py @@ -0,0 +1,166 @@ +"""Provides a UPNP discovery method that mimicks Hue hubs.""" +import threading +import socket +import logging +import os +import select + +from aiohttp import web + +from homeassistant import core +from homeassistant.components.http import HomeAssistantView + +_LOGGER = logging.getLogger(__name__) + + +class DescriptionXmlView(HomeAssistantView): + """Handles requests for the description.xml file.""" + + url = '/description.xml' + name = 'description:xml' + requires_auth = False + + def __init__(self, config): + """Initialize the instance of the view.""" + self.config = config + + @core.callback + def get(self, request): + """Handle a GET request.""" + xml_template = """ + + +1 +0 + +http://{0}:{1}/ + +urn:schemas-upnp-org:device:Basic:1 +HASS Bridge ({0}) +Royal Philips Electronics +http://www.philips.com +Philips hue Personal Wireless Lighting +Philips hue bridge 2015 +BSB002 +http://www.meethue.com +1234 +uuid:2f402f80-da50-11e1-9b23-001788255acc + + +""" + + resp_text = xml_template.format( + self.config.host_ip_addr, self.config.listen_port) + + return web.Response(text=resp_text, content_type='text/xml') + + +class UPNPResponderThread(threading.Thread): + """Handle responding to UPNP/SSDP discovery requests.""" + + _interrupted = False + + def __init__(self, host_ip_addr, listen_port): + """Initialize the class.""" + threading.Thread.__init__(self) + + self.host_ip_addr = host_ip_addr + self.listen_port = listen_port + + # Note that the double newline at the end of + # this string is required per the SSDP spec + resp_template = """HTTP/1.1 200 OK +CACHE-CONTROL: max-age=60 +EXT: +LOCATION: http://{0}:{1}/description.xml +SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1 +ST: urn:schemas-upnp-org:device:basic:1 +USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1 + +""" + + self.upnp_response = resp_template.format(host_ip_addr, listen_port) \ + .replace("\n", "\r\n") \ + .encode('utf-8') + + # Set up a pipe for signaling to the receiver that it's time to + # shutdown. Essentially, we place the SSDP socket into nonblocking + # mode and use select() to wait for data to arrive on either the SSDP + # socket or the pipe. If data arrives on either one, select() returns + # and tells us which filenos have data ready to read. + # + # When we want to stop the responder, we write data to the pipe, which + # causes the select() to return and indicate that said pipe has data + # ready to be read, which indicates to us that the responder needs to + # be shutdown. + self._interrupted_read_pipe, self._interrupted_write_pipe = os.pipe() + + def run(self): + """Run the server.""" + # Listen for UDP port 1900 packets sent to SSDP multicast address + ssdp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + ssdp_socket.setblocking(False) + + # Required for receiving multicast + ssdp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + ssdp_socket.setsockopt( + socket.SOL_IP, + socket.IP_MULTICAST_IF, + socket.inet_aton(self.host_ip_addr)) + + ssdp_socket.setsockopt( + socket.SOL_IP, + socket.IP_ADD_MEMBERSHIP, + socket.inet_aton("239.255.255.250") + + socket.inet_aton(self.host_ip_addr)) + + ssdp_socket.bind(("239.255.255.250", 1900)) + + while True: + if self._interrupted: + clean_socket_close(ssdp_socket) + return + + try: + read, _, _ = select.select( + [self._interrupted_read_pipe, ssdp_socket], [], + [ssdp_socket]) + + if self._interrupted_read_pipe in read: + # Implies self._interrupted is True + clean_socket_close(ssdp_socket) + return + elif ssdp_socket in read: + data, addr = ssdp_socket.recvfrom(1024) + else: + continue + except socket.error as ex: + if self._interrupted: + clean_socket_close(ssdp_socket) + return + + _LOGGER.error("UPNP Responder socket exception occured: %s", + ex.__str__) + + if "M-SEARCH" in data.decode('utf-8'): + # SSDP M-SEARCH method received, respond to it with our info + resp_socket = socket.socket( + socket.AF_INET, socket.SOCK_DGRAM) + + resp_socket.sendto(self.upnp_response, addr) + resp_socket.close() + + def stop(self): + """Stop the server.""" + # Request for server + self._interrupted = True + os.write(self._interrupted_write_pipe, bytes([0])) + self.join() + + +def clean_socket_close(sock): + """Close a socket connection and logs its closure.""" + _LOGGER.info("UPNP responder shutting down.") + + sock.close() diff --git a/tests/components/emulated_hue/__init__.py b/tests/components/emulated_hue/__init__.py new file mode 100644 index 00000000000..b13b95a080a --- /dev/null +++ b/tests/components/emulated_hue/__init__.py @@ -0,0 +1 @@ +"""Tests for emulated_hue.""" diff --git a/tests/components/test_emulated_hue.py b/tests/components/emulated_hue/test_hue_api.py old mode 100755 new mode 100644 similarity index 78% rename from tests/components/test_emulated_hue.py rename to tests/components/emulated_hue/test_hue_api.py index 7bb8da09e47..9cee27f570f --- a/tests/components/test_emulated_hue.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -1,429 +1,355 @@ -"""The tests for the emulated Hue component.""" -import json - -import unittest -import requests - -from homeassistant import bootstrap, const, core -import homeassistant.components as core_components -from homeassistant.components import emulated_hue, http, light, script -from homeassistant.const import STATE_ON, STATE_OFF -from homeassistant.components.emulated_hue import ( - HUE_API_STATE_ON, HUE_API_STATE_BRI) -from homeassistant.util.async import run_coroutine_threadsafe - -from tests.common import get_test_instance_port, get_test_home_assistant - -HTTP_SERVER_PORT = get_test_instance_port() -BRIDGE_SERVER_PORT = get_test_instance_port() - -BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}' -JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON} - - -def setup_hass_instance(emulated_hue_config): - """Set up the Home Assistant instance to test.""" - hass = get_test_home_assistant() - - # We need to do this to get access to homeassistant/turn_(on,off) - run_coroutine_threadsafe( - core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop - ).result() - - bootstrap.setup_component( - hass, http.DOMAIN, - {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) - - bootstrap.setup_component(hass, emulated_hue.DOMAIN, emulated_hue_config) - - return hass - - -def start_hass_instance(hass): - """Start the Home Assistant instance to test.""" - hass.start() - - -class TestEmulatedHue(unittest.TestCase): - """Test the emulated Hue component.""" - - hass = None - - @classmethod - def setUpClass(cls): - """Setup the class.""" - cls.hass = setup_hass_instance({ - emulated_hue.DOMAIN: { - emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT - }}) - - start_hass_instance(cls.hass) - - @classmethod - def tearDownClass(cls): - """Stop the class.""" - cls.hass.stop() - - def test_description_xml(self): - """Test the description.""" - import xml.etree.ElementTree as ET - - result = requests.get( - BRIDGE_URL_BASE.format('/description.xml'), timeout=5) - - self.assertEqual(result.status_code, 200) - self.assertTrue('text/xml' in result.headers['content-type']) - - # Make sure the XML is parsable - # pylint: disable=bare-except - try: - ET.fromstring(result.text) - except: - self.fail('description.xml is not valid XML!') - - def test_create_username(self): - """Test the creation of an username.""" - request_json = {'devicetype': 'my_device'} - - result = requests.post( - BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), - timeout=5) - - self.assertEqual(result.status_code, 200) - self.assertTrue('application/json' in result.headers['content-type']) - - resp_json = result.json() - success_json = resp_json[0] - - self.assertTrue('success' in success_json) - self.assertTrue('username' in success_json['success']) - - def test_valid_username_request(self): - """Test request with a valid username.""" - request_json = {'invalid_key': 'my_device'} - - result = requests.post( - BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), - timeout=5) - - self.assertEqual(result.status_code, 400) - - -class TestEmulatedHueExposedByDefault(unittest.TestCase): - """Test class for emulated hue component.""" - - @classmethod - def setUpClass(cls): - """Setup the class.""" - cls.hass = setup_hass_instance({ - emulated_hue.DOMAIN: { - emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, - emulated_hue.CONF_EXPOSE_BY_DEFAULT: True - } - }) - - bootstrap.setup_component(cls.hass, light.DOMAIN, { - 'light': [ - { - 'platform': 'demo', - } - ] - }) - - bootstrap.setup_component(cls.hass, script.DOMAIN, { - 'script': { - 'set_kitchen_light': { - 'sequence': [ - { - 'service_template': - "light.turn_{{ requested_state }}", - 'data_template': { - 'entity_id': 'light.kitchen_lights', - 'brightness': "{{ requested_level }}" - } - } - ] - } - } - }) - - start_hass_instance(cls.hass) - - # Kitchen light is explicitly excluded from being exposed - kitchen_light_entity = cls.hass.states.get('light.kitchen_lights') - attrs = dict(kitchen_light_entity.attributes) - attrs[emulated_hue.ATTR_EMULATED_HUE] = False - cls.hass.states.set( - kitchen_light_entity.entity_id, kitchen_light_entity.state, - attributes=attrs) - - # Expose the script - script_entity = cls.hass.states.get('script.set_kitchen_light') - attrs = dict(script_entity.attributes) - attrs[emulated_hue.ATTR_EMULATED_HUE] = True - cls.hass.states.set( - script_entity.entity_id, script_entity.state, attributes=attrs - ) - - @classmethod - def tearDownClass(cls): - """Stop the class.""" - cls.hass.stop() - - def test_discover_lights(self): - """Test the discovery of lights.""" - result = requests.get( - BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) - - self.assertEqual(result.status_code, 200) - self.assertTrue('application/json' in result.headers['content-type']) - - result_json = result.json() - - # Make sure the lights we added to the config are there - self.assertTrue('light.ceiling_lights' in result_json) - self.assertTrue('light.bed_light' in result_json) - self.assertTrue('script.set_kitchen_light' in result_json) - self.assertTrue('light.kitchen_lights' not in result_json) - - def test_get_light_state(self): - """Test the getting of light state.""" - # Turn office light on and set to 127 brightness - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_ON, - { - const.ATTR_ENTITY_ID: 'light.ceiling_lights', - light.ATTR_BRIGHTNESS: 127 - }, - blocking=True) - - office_json = self.perform_get_light_state('light.ceiling_lights', 200) - - self.assertEqual(office_json['state'][HUE_API_STATE_ON], True) - self.assertEqual(office_json['state'][HUE_API_STATE_BRI], 127) - - # Turn bedroom light off - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - { - const.ATTR_ENTITY_ID: 'light.bed_light' - }, - blocking=True) - - bedroom_json = self.perform_get_light_state('light.bed_light', 200) - - self.assertEqual(bedroom_json['state'][HUE_API_STATE_ON], False) - self.assertEqual(bedroom_json['state'][HUE_API_STATE_BRI], 0) - - # Make sure kitchen light isn't accessible - kitchen_url = '/api/username/lights/{}'.format('light.kitchen_lights') - kitchen_result = requests.get( - BRIDGE_URL_BASE.format(kitchen_url), timeout=5) - - self.assertEqual(kitchen_result.status_code, 404) - - def test_put_light_state(self): - """Test the seeting of light states.""" - self.perform_put_test_on_ceiling_lights() - - # Turn the bedroom light on first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_ON, - {const.ATTR_ENTITY_ID: 'light.bed_light', - light.ATTR_BRIGHTNESS: 153}, - blocking=True) - - bed_light = self.hass.states.get('light.bed_light') - self.assertEqual(bed_light.state, STATE_ON) - self.assertEqual(bed_light.attributes[light.ATTR_BRIGHTNESS], 153) - - # Go through the API to turn it off - bedroom_result = self.perform_put_light_state( - 'light.bed_light', False) - - bedroom_result_json = bedroom_result.json() - - self.assertEqual(bedroom_result.status_code, 200) - self.assertTrue( - 'application/json' in bedroom_result.headers['content-type']) - - self.assertEqual(len(bedroom_result_json), 1) - - # Check to make sure the state changed - bed_light = self.hass.states.get('light.bed_light') - self.assertEqual(bed_light.state, STATE_OFF) - - # Make sure we can't change the kitchen light state - kitchen_result = self.perform_put_light_state( - 'light.kitchen_light', True) - self.assertEqual(kitchen_result.status_code, 404) - - def test_put_light_state_script(self): - """Test the setting of script variables.""" - # Turn the kitchen light off first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - {const.ATTR_ENTITY_ID: 'light.kitchen_lights'}, - blocking=True) - - # Emulated hue converts 0-100% to 0-255. - level = 23 - brightness = round(level * 255 / 100) - - script_result = self.perform_put_light_state( - 'script.set_kitchen_light', True, brightness) - - script_result_json = script_result.json() - - self.assertEqual(script_result.status_code, 200) - self.assertEqual(len(script_result_json), 2) - - # Wait until script is complete before continuing - self.hass.block_till_done() - - kitchen_light = self.hass.states.get('light.kitchen_lights') - self.assertEqual(kitchen_light.state, 'on') - self.assertEqual( - kitchen_light.attributes[light.ATTR_BRIGHTNESS], - level) - - # pylint: disable=invalid-name - def test_put_with_form_urlencoded_content_type(self): - """Test the form with urlencoded content.""" - # Needed for Alexa - self.perform_put_test_on_ceiling_lights( - 'application/x-www-form-urlencoded') - - # Make sure we fail gracefully when we can't parse the data - data = {'key1': 'value1', 'key2': 'value2'} - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - 'light.ceiling_lights')), data=data) - - self.assertEqual(result.status_code, 400) - - def test_entity_not_found(self): - """Test for entity which are not found.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format("not.existant_entity")), - timeout=5) - - self.assertEqual(result.status_code, 404) - - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format("non.existant_entity")), - timeout=5) - - self.assertEqual(result.status_code, 404) - - def test_allowed_methods(self): - """Test the allowed methods.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - "light.ceiling_lights"))) - - self.assertEqual(result.status_code, 405) - - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format("light.ceiling_lights")), - data={'key1': 'value1'}) - - self.assertEqual(result.status_code, 405) - - result = requests.put( - BRIDGE_URL_BASE.format('/api/username/lights'), - data={'key1': 'value1'}) - - self.assertEqual(result.status_code, 405) - - def test_proper_put_state_request(self): - """Test the request to set the state.""" - # Test proper on value parsing - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - 'light.ceiling_lights')), - data=json.dumps({HUE_API_STATE_ON: 1234})) - - self.assertEqual(result.status_code, 400) - - # Test proper brightness value parsing - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - 'light.ceiling_lights')), data=json.dumps({ - HUE_API_STATE_ON: True, - HUE_API_STATE_BRI: 'Hello world!' - })) - - self.assertEqual(result.status_code, 400) - - # pylint: disable=invalid-name - def perform_put_test_on_ceiling_lights(self, - content_type='application/json'): - """Test the setting of a light.""" - # Turn the office light off first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, - blocking=True) - - ceiling_lights = self.hass.states.get('light.ceiling_lights') - self.assertEqual(ceiling_lights.state, STATE_OFF) - - # Go through the API to turn it on - office_result = self.perform_put_light_state( - 'light.ceiling_lights', True, 56, content_type) - - office_result_json = office_result.json() - - self.assertEqual(office_result.status_code, 200) - self.assertTrue( - 'application/json' in office_result.headers['content-type']) - - self.assertEqual(len(office_result_json), 2) - - # Check to make sure the state changed - ceiling_lights = self.hass.states.get('light.ceiling_lights') - self.assertEqual(ceiling_lights.state, STATE_ON) - self.assertEqual(ceiling_lights.attributes[light.ATTR_BRIGHTNESS], 56) - - def perform_get_light_state(self, entity_id, expected_status): - """Test the gettting of a light state.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format(entity_id)), timeout=5) - - self.assertEqual(result.status_code, expected_status) - - if expected_status == 200: - self.assertTrue( - 'application/json' in result.headers['content-type']) - - return result.json() - - return None - - # pylint: disable=no-self-use - def perform_put_light_state(self, entity_id, is_on, brightness=None, - content_type='application/json'): - """Test the setting of a light state.""" - url = BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format(entity_id)) - - req_headers = {'Content-Type': content_type} - - data = {HUE_API_STATE_ON: is_on} - - if brightness is not None: - data[HUE_API_STATE_BRI] = brightness - - result = requests.put( - url, data=json.dumps(data), timeout=5, headers=req_headers) - - return result +"""The tests for the emulated Hue component.""" +import json + +import unittest +from unittest.mock import patch +import requests + +from homeassistant import bootstrap, const, core +import homeassistant.components as core_components +from homeassistant.components import emulated_hue, http, light, script +from homeassistant.const import STATE_ON, STATE_OFF +from homeassistant.components.emulated_hue.hue_api import ( + HUE_API_STATE_ON, HUE_API_STATE_BRI) +from homeassistant.util.async import run_coroutine_threadsafe + +from tests.common import get_test_instance_port, get_test_home_assistant + +HTTP_SERVER_PORT = get_test_instance_port() +BRIDGE_SERVER_PORT = get_test_instance_port() + +BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}' +JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON} + + +class TestEmulatedHueExposedByDefault(unittest.TestCase): + """Test class for emulated hue component.""" + + @classmethod + def setUpClass(cls): + """Setup the class.""" + cls.hass = hass = get_test_home_assistant() + + # We need to do this to get access to homeassistant/turn_(on,off) + run_coroutine_threadsafe( + core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop + ).result() + + bootstrap.setup_component( + hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) + + with patch('homeassistant.components' + '.emulated_hue.UPNPResponderThread'): + bootstrap.setup_component(hass, emulated_hue.DOMAIN, { + emulated_hue.DOMAIN: { + emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, + emulated_hue.CONF_EXPOSE_BY_DEFAULT: True + } + }) + + bootstrap.setup_component(cls.hass, light.DOMAIN, { + 'light': [ + { + 'platform': 'demo', + } + ] + }) + + bootstrap.setup_component(cls.hass, script.DOMAIN, { + 'script': { + 'set_kitchen_light': { + 'sequence': [ + { + 'service_template': + "light.turn_{{ requested_state }}", + 'data_template': { + 'entity_id': 'light.kitchen_lights', + 'brightness': "{{ requested_level }}" + } + } + ] + } + } + }) + + cls.hass.start() + + # Kitchen light is explicitly excluded from being exposed + kitchen_light_entity = cls.hass.states.get('light.kitchen_lights') + attrs = dict(kitchen_light_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE] = False + cls.hass.states.set( + kitchen_light_entity.entity_id, kitchen_light_entity.state, + attributes=attrs) + + # Expose the script + script_entity = cls.hass.states.get('script.set_kitchen_light') + attrs = dict(script_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE] = True + cls.hass.states.set( + script_entity.entity_id, script_entity.state, attributes=attrs + ) + + @classmethod + def tearDownClass(cls): + """Stop the class.""" + cls.hass.stop() + + def test_discover_lights(self): + """Test the discovery of lights.""" + result = requests.get( + BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) + + self.assertEqual(result.status_code, 200) + self.assertTrue('application/json' in result.headers['content-type']) + + result_json = result.json() + + # Make sure the lights we added to the config are there + self.assertTrue('light.ceiling_lights' in result_json) + self.assertTrue('light.bed_light' in result_json) + self.assertTrue('script.set_kitchen_light' in result_json) + self.assertTrue('light.kitchen_lights' not in result_json) + + def test_get_light_state(self): + """Test the getting of light state.""" + # Turn office light on and set to 127 brightness + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_ON, + { + const.ATTR_ENTITY_ID: 'light.ceiling_lights', + light.ATTR_BRIGHTNESS: 127 + }, + blocking=True) + + office_json = self.perform_get_light_state('light.ceiling_lights', 200) + + self.assertEqual(office_json['state'][HUE_API_STATE_ON], True) + self.assertEqual(office_json['state'][HUE_API_STATE_BRI], 127) + + # Turn bedroom light off + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_OFF, + { + const.ATTR_ENTITY_ID: 'light.bed_light' + }, + blocking=True) + + bedroom_json = self.perform_get_light_state('light.bed_light', 200) + + self.assertEqual(bedroom_json['state'][HUE_API_STATE_ON], False) + self.assertEqual(bedroom_json['state'][HUE_API_STATE_BRI], 0) + + # Make sure kitchen light isn't accessible + kitchen_url = '/api/username/lights/{}'.format('light.kitchen_lights') + kitchen_result = requests.get( + BRIDGE_URL_BASE.format(kitchen_url), timeout=5) + + self.assertEqual(kitchen_result.status_code, 404) + + def test_put_light_state(self): + """Test the seeting of light states.""" + self.perform_put_test_on_ceiling_lights() + + # Turn the bedroom light on first + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_ON, + {const.ATTR_ENTITY_ID: 'light.bed_light', + light.ATTR_BRIGHTNESS: 153}, + blocking=True) + + bed_light = self.hass.states.get('light.bed_light') + self.assertEqual(bed_light.state, STATE_ON) + self.assertEqual(bed_light.attributes[light.ATTR_BRIGHTNESS], 153) + + # Go through the API to turn it off + bedroom_result = self.perform_put_light_state( + 'light.bed_light', False) + + bedroom_result_json = bedroom_result.json() + + self.assertEqual(bedroom_result.status_code, 200) + self.assertTrue( + 'application/json' in bedroom_result.headers['content-type']) + + self.assertEqual(len(bedroom_result_json), 1) + + # Check to make sure the state changed + bed_light = self.hass.states.get('light.bed_light') + self.assertEqual(bed_light.state, STATE_OFF) + + # Make sure we can't change the kitchen light state + kitchen_result = self.perform_put_light_state( + 'light.kitchen_light', True) + self.assertEqual(kitchen_result.status_code, 404) + + def test_put_light_state_script(self): + """Test the setting of script variables.""" + # Turn the kitchen light off first + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'light.kitchen_lights'}, + blocking=True) + + # Emulated hue converts 0-100% to 0-255. + level = 23 + brightness = round(level * 255 / 100) + + script_result = self.perform_put_light_state( + 'script.set_kitchen_light', True, brightness) + + script_result_json = script_result.json() + + self.assertEqual(script_result.status_code, 200) + self.assertEqual(len(script_result_json), 2) + + # Wait until script is complete before continuing + self.hass.block_till_done() + + kitchen_light = self.hass.states.get('light.kitchen_lights') + self.assertEqual(kitchen_light.state, 'on') + self.assertEqual( + kitchen_light.attributes[light.ATTR_BRIGHTNESS], + level) + + # pylint: disable=invalid-name + def test_put_with_form_urlencoded_content_type(self): + """Test the form with urlencoded content.""" + # Needed for Alexa + self.perform_put_test_on_ceiling_lights( + 'application/x-www-form-urlencoded') + + # Make sure we fail gracefully when we can't parse the data + data = {'key1': 'value1', 'key2': 'value2'} + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + 'light.ceiling_lights')), data=data) + + self.assertEqual(result.status_code, 400) + + def test_entity_not_found(self): + """Test for entity which are not found.""" + result = requests.get( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}'.format("not.existant_entity")), + timeout=5) + + self.assertEqual(result.status_code, 404) + + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format("non.existant_entity")), + timeout=5) + + self.assertEqual(result.status_code, 404) + + def test_allowed_methods(self): + """Test the allowed methods.""" + result = requests.get( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + "light.ceiling_lights"))) + + self.assertEqual(result.status_code, 405) + + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}'.format("light.ceiling_lights")), + data={'key1': 'value1'}) + + self.assertEqual(result.status_code, 405) + + result = requests.put( + BRIDGE_URL_BASE.format('/api/username/lights'), + data={'key1': 'value1'}) + + self.assertEqual(result.status_code, 405) + + def test_proper_put_state_request(self): + """Test the request to set the state.""" + # Test proper on value parsing + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + 'light.ceiling_lights')), + data=json.dumps({HUE_API_STATE_ON: 1234})) + + self.assertEqual(result.status_code, 400) + + # Test proper brightness value parsing + result = requests.put( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format( + 'light.ceiling_lights')), data=json.dumps({ + HUE_API_STATE_ON: True, + HUE_API_STATE_BRI: 'Hello world!' + })) + + self.assertEqual(result.status_code, 400) + + # pylint: disable=invalid-name + def perform_put_test_on_ceiling_lights(self, + content_type='application/json'): + """Test the setting of a light.""" + # Turn the office light off first + self.hass.services.call( + light.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, + blocking=True) + + ceiling_lights = self.hass.states.get('light.ceiling_lights') + self.assertEqual(ceiling_lights.state, STATE_OFF) + + # Go through the API to turn it on + office_result = self.perform_put_light_state( + 'light.ceiling_lights', True, 56, content_type) + + office_result_json = office_result.json() + + self.assertEqual(office_result.status_code, 200) + self.assertTrue( + 'application/json' in office_result.headers['content-type']) + + self.assertEqual(len(office_result_json), 2) + + # Check to make sure the state changed + ceiling_lights = self.hass.states.get('light.ceiling_lights') + self.assertEqual(ceiling_lights.state, STATE_ON) + self.assertEqual(ceiling_lights.attributes[light.ATTR_BRIGHTNESS], 56) + + def perform_get_light_state(self, entity_id, expected_status): + """Test the gettting of a light state.""" + result = requests.get( + BRIDGE_URL_BASE.format( + '/api/username/lights/{}'.format(entity_id)), timeout=5) + + self.assertEqual(result.status_code, expected_status) + + if expected_status == 200: + self.assertTrue( + 'application/json' in result.headers['content-type']) + + return result.json() + + return None + + # pylint: disable=no-self-use + def perform_put_light_state(self, entity_id, is_on, brightness=None, + content_type='application/json'): + """Test the setting of a light state.""" + url = BRIDGE_URL_BASE.format( + '/api/username/lights/{}/state'.format(entity_id)) + + req_headers = {'Content-Type': content_type} + + data = {HUE_API_STATE_ON: is_on} + + if brightness is not None: + data[HUE_API_STATE_BRI] = brightness + + result = requests.put( + url, data=json.dumps(data), timeout=5, headers=req_headers) + + return result diff --git a/tests/components/emulated_hue/test_init.py b/tests/components/emulated_hue/test_init.py new file mode 100755 index 00000000000..ec3cc0a11cb --- /dev/null +++ b/tests/components/emulated_hue/test_init.py @@ -0,0 +1,55 @@ +from unittest.mock import patch + +from homeassistant.components.emulated_hue import Config, _LOGGER + + +def test_config_google_home_entity_id_to_number(): + """Test config adheres to the type.""" + conf = Config({ + 'type': 'google_home' + }) + + number = conf.entity_id_to_number('light.test') + assert number == '1' + + number = conf.entity_id_to_number('light.test') + assert number == '1' + + number = conf.entity_id_to_number('light.test2') + assert number == '2' + + entity_id = conf.number_to_entity_id('1') + assert entity_id == 'light.test' + + +def test_config_alexa_entity_id_to_number(): + """Test config adheres to the type.""" + conf = Config({ + 'type': 'alexa' + }) + + number = conf.entity_id_to_number('light.test') + assert number == 'light.test' + + number = conf.entity_id_to_number('light.test') + assert number == 'light.test' + + number = conf.entity_id_to_number('light.test2') + assert number == 'light.test2' + + entity_id = conf.number_to_entity_id('light.test') + assert entity_id == 'light.test' + + +def test_warning_config_google_home_listen_port(): + """Test we warn when non-default port is used for Google Home.""" + with patch.object(_LOGGER, 'warning') as mock_warn: + Config({ + 'type': 'google_home', + 'host_ip': '123.123.123.123', + 'listen_port': 8300 + }) + + assert mock_warn.called + assert mock_warn.mock_calls[0][1][0] == \ + "When targetting Google Home, listening port has to be port 80" diff --git a/tests/components/emulated_hue/test_upnp.py b/tests/components/emulated_hue/test_upnp.py new file mode 100644 index 00000000000..03b9e993a9b --- /dev/null +++ b/tests/components/emulated_hue/test_upnp.py @@ -0,0 +1,120 @@ +"""The tests for the emulated Hue component.""" +import json + +import unittest +from unittest.mock import patch +import requests + +from homeassistant import bootstrap, const, core +import homeassistant.components as core_components +from homeassistant.components import emulated_hue, http +from homeassistant.util.async import run_coroutine_threadsafe + +from tests.common import get_test_instance_port, get_test_home_assistant + +HTTP_SERVER_PORT = get_test_instance_port() +BRIDGE_SERVER_PORT = get_test_instance_port() + +BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}' +JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON} + + +def setup_hass_instance(emulated_hue_config): + """Set up the Home Assistant instance to test.""" + hass = get_test_home_assistant() + + # We need to do this to get access to homeassistant/turn_(on,off) + run_coroutine_threadsafe( + core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop + ).result() + + bootstrap.setup_component( + hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) + + bootstrap.setup_component(hass, emulated_hue.DOMAIN, emulated_hue_config) + + return hass + + +def start_hass_instance(hass): + """Start the Home Assistant instance to test.""" + hass.start() + + +class TestEmulatedHue(unittest.TestCase): + """Test the emulated Hue component.""" + + hass = None + + @classmethod + def setUpClass(cls): + """Setup the class.""" + cls.hass = hass = get_test_home_assistant() + + # We need to do this to get access to homeassistant/turn_(on,off) + run_coroutine_threadsafe( + core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop + ).result() + + bootstrap.setup_component( + hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) + + with patch('homeassistant.components' + '.emulated_hue.UPNPResponderThread'): + bootstrap.setup_component(hass, emulated_hue.DOMAIN, { + emulated_hue.DOMAIN: { + emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT + }}) + + cls.hass.start() + + @classmethod + def tearDownClass(cls): + """Stop the class.""" + cls.hass.stop() + + def test_description_xml(self): + """Test the description.""" + import xml.etree.ElementTree as ET + + result = requests.get( + BRIDGE_URL_BASE.format('/description.xml'), timeout=5) + + self.assertEqual(result.status_code, 200) + self.assertTrue('text/xml' in result.headers['content-type']) + + # Make sure the XML is parsable + # pylint: disable=bare-except + try: + ET.fromstring(result.text) + except: + self.fail('description.xml is not valid XML!') + + def test_create_username(self): + """Test the creation of an username.""" + request_json = {'devicetype': 'my_device'} + + result = requests.post( + BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), + timeout=5) + + self.assertEqual(result.status_code, 200) + self.assertTrue('application/json' in result.headers['content-type']) + + resp_json = result.json() + success_json = resp_json[0] + + self.assertTrue('success' in success_json) + self.assertTrue('username' in success_json['success']) + + def test_valid_username_request(self): + """Test request with a valid username.""" + request_json = {'invalid_key': 'my_device'} + + result = requests.post( + BRIDGE_URL_BASE.format('/api'), data=json.dumps(request_json), + timeout=5) + + self.assertEqual(result.status_code, 400) From cffc7ac4d871fe31f5ee75d0cbbff1f70899476b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 10:59:18 -0800 Subject: [PATCH 22/89] Update netdisco to 0.8 (#4723) --- homeassistant/components/discovery.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 142764ea522..3c4dff6eac5 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -14,7 +14,7 @@ import voluptuous as vol from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.helpers.discovery import load_platform, discover -REQUIREMENTS = ['netdisco==0.7.7'] +REQUIREMENTS = ['netdisco==0.8.0'] DOMAIN = 'discovery' diff --git a/requirements_all.txt b/requirements_all.txt index 3fdf12c086a..622faa3719d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -296,7 +296,7 @@ mficlient==0.3.0 miflora==0.1.13 # homeassistant.components.discovery -netdisco==0.7.7 +netdisco==0.8.0 # homeassistant.components.sensor.neurio_energy neurio==0.2.10 From 87dab37b8a9aad9e63e49c36d1ded3294b4dc10d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 13:49:46 -0800 Subject: [PATCH 23/89] Fix Nest interpreting Celsius temperature as Fahrenheit (#4729) --- homeassistant/components/climate/nest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/climate/nest.py b/homeassistant/components/climate/nest.py index 01c3b3782b1..06ec500f9e2 100644 --- a/homeassistant/components/climate/nest.py +++ b/homeassistant/components/climate/nest.py @@ -229,7 +229,7 @@ class NestThermostat(ClimateDevice): self._eco_temperature = self.device.eco_temperature self._locked_temperature = self.device.locked_temperature self._is_locked = self.device.is_locked - if self.device.temperature == 'C': + if self.device.temperature_scale == 'C': self._temperature_scale = TEMP_CELSIUS else: self._temperature_scale = TEMP_FAHRENHEIT From 64b1179c1309b8d0b93fd80a29a7f508bcbbc082 Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Sun, 4 Dec 2016 17:33:50 -0500 Subject: [PATCH 24/89] Make sure all nest platforms require discovery info (#4734) --- homeassistant/components/binary_sensor/nest.py | 3 +++ homeassistant/components/camera/nest.py | 1 + homeassistant/components/climate/nest.py | 3 ++- homeassistant/components/sensor/nest.py | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/binary_sensor/nest.py b/homeassistant/components/binary_sensor/nest.py index d78e33c9f95..070703df32a 100644 --- a/homeassistant/components/binary_sensor/nest.py +++ b/homeassistant/components/binary_sensor/nest.py @@ -60,6 +60,9 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices, discovery_info=None): """Setup Nest binary sensors.""" + if discovery_info is None: + return + nest = hass.data[DATA_NEST] conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_BINARY_SENSOR_TYPES) diff --git a/homeassistant/components/camera/nest.py b/homeassistant/components/camera/nest.py index 8bda0e8eb9c..aa2041e07a6 100644 --- a/homeassistant/components/camera/nest.py +++ b/homeassistant/components/camera/nest.py @@ -26,6 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up a Nest Cam.""" if discovery_info is None: return + camera_devices = hass.data[nest.DATA_NEST].camera_devices() cameras = [NestCamera(structure, device) for structure, device in camera_devices] diff --git a/homeassistant/components/climate/nest.py b/homeassistant/components/climate/nest.py index 06ec500f9e2..dbc68162579 100644 --- a/homeassistant/components/climate/nest.py +++ b/homeassistant/components/climate/nest.py @@ -31,10 +31,11 @@ STATE_HEAT_COOL = 'heat-cool' def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Nest thermostat.""" - _LOGGER.debug("Setting up nest thermostat") if discovery_info is None: return + _LOGGER.debug("Setting up nest thermostat") + temp_unit = hass.config.units.temperature_unit add_devices( diff --git a/homeassistant/components/sensor/nest.py b/homeassistant/components/sensor/nest.py index b4909aebae3..53f767ab494 100644 --- a/homeassistant/components/sensor/nest.py +++ b/homeassistant/components/sensor/nest.py @@ -68,6 +68,9 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Nest Sensor.""" + if discovery_info is None: + return + nest = hass.data[DATA_NEST] conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_SENSOR_TYPES) From 2099d023ef0b86195adeaa41d3d80e02dfdfa2ec Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 5 Dec 2016 00:08:14 +0100 Subject: [PATCH 25/89] [0.34] bugfix influxdb node_id (#4712) * Bugfix for #4709 - do not convert node_id to float * Update influxdb.py --- homeassistant/components/influxdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/influxdb.py b/homeassistant/components/influxdb.py index 167767bc00e..08296ad65c7 100644 --- a/homeassistant/components/influxdb.py +++ b/homeassistant/components/influxdb.py @@ -120,7 +120,8 @@ def setup(hass, config): for key, value in state.attributes.items(): if key != 'unit_of_measurement': - if isinstance(value, (str, float, bool)): + if isinstance(value, (str, float, bool)) or \ + key.endswith('_id'): json_body[0]['fields'][key] = value elif isinstance(value, int): # Prevent column data errors in influxDB. From 2b3caa716a66075543856382056fafa72d3718df Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2016 15:30:55 -0800 Subject: [PATCH 26/89] Cast progress (#4735) * add progress to google cast * Add progress to media player demo --- homeassistant/components/media_player/cast.py | 27 ++++++++ homeassistant/components/media_player/demo.py | 68 ++++++++++++++----- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index 1d01f0058ec..0f3b98cab46 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -18,6 +18,7 @@ from homeassistant.const import ( CONF_HOST, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv +import homeassistant.util.dt as dt_util REQUIREMENTS = ['pychromecast==0.7.6'] @@ -105,6 +106,7 @@ class CastDevice(MediaPlayerDevice): self.cast_status = self.cast.status self.media_status = self.cast.media_controller.status + self.media_status_received = None @property def should_poll(self): @@ -231,6 +233,30 @@ class CastDevice(MediaPlayerDevice): """Flag of media commands that are supported.""" return SUPPORT_CAST + @property + def media_position(self): + """Position of current playing media in seconds.""" + if self.media_status is None or not ( + self.media_status.player_is_playing or + self.media_status.player_is_idle): + return None + + position = self.media_status.current_time + + if self.media_status.player_is_playing: + position += (dt_util.utcnow() - + self.media_status_received).total_seconds() + + return position + + @property + def media_position_updated_at(self): + """When was the position of the current playing media valid. + + Returns value from homeassistant.util.dt.utcnow(). + """ + return self.media_status_received + def turn_on(self): """Turn on the ChromeCast.""" # The only way we can turn the Chromecast is on is by launching an app @@ -292,4 +318,5 @@ class CastDevice(MediaPlayerDevice): def new_media_status(self, status): """Called when a new media status is received.""" self.media_status = status + self.media_status_received = dt_util.utcnow() self.schedule_update_ha_state() diff --git a/homeassistant/components/media_player/demo.py b/homeassistant/components/media_player/demo.py index 1c1687de319..226ddfe4769 100644 --- a/homeassistant/components/media_player/demo.py +++ b/homeassistant/components/media_player/demo.py @@ -10,6 +10,7 @@ from homeassistant.components.media_player import ( SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST, MediaPlayerDevice) from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING +import homeassistant.util.dt as dt_util # pylint: disable=unused-argument @@ -18,8 +19,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices([ DemoYoutubePlayer( 'Living Room', 'eyU3bRy2x44', - '♥♥ The Best Fireplace Video (3 hours)'), - DemoYoutubePlayer('Bedroom', 'kxopViU98Xo', 'Epic sax guy 10 hours'), + '♥♥ The Best Fireplace Video (3 hours)', 300), + DemoYoutubePlayer('Bedroom', 'kxopViU98Xo', 'Epic sax guy 10 hours', + 360000), DemoMusicPlayer(), DemoTVShowPlayer(), ]) @@ -78,32 +80,32 @@ class AbstractDemoPlayer(MediaPlayerDevice): def turn_on(self): """Turn the media player on.""" self._player_state = STATE_PLAYING - self.update_ha_state() + self.schedule_update_ha_state() def turn_off(self): """Turn the media player off.""" self._player_state = STATE_OFF - self.update_ha_state() + self.schedule_update_ha_state() def mute_volume(self, mute): """Mute the volume.""" self._volume_muted = mute - self.update_ha_state() + self.schedule_update_ha_state() def set_volume_level(self, volume): """Set the volume level, range 0..1.""" self._volume_level = volume - self.update_ha_state() + self.schedule_update_ha_state() def media_play(self): """Send play command.""" self._player_state = STATE_PLAYING - self.update_ha_state() + self.schedule_update_ha_state() def media_pause(self): """Send pause command.""" self._player_state = STATE_PAUSED - self.update_ha_state() + self.schedule_update_ha_state() class DemoYoutubePlayer(AbstractDemoPlayer): @@ -111,11 +113,14 @@ class DemoYoutubePlayer(AbstractDemoPlayer): # We only implement the methods that we support - def __init__(self, name, youtube_id=None, media_title=None): + def __init__(self, name, youtube_id=None, media_title=None, duration=360): """Initialize the demo device.""" super().__init__(name) self.youtube_id = youtube_id self._media_title = media_title + self._duration = duration + self._progress = int(duration * .15) + self._progress_updated_at = dt_util.utcnow() @property def media_content_id(self): @@ -130,7 +135,7 @@ class DemoYoutubePlayer(AbstractDemoPlayer): @property def media_duration(self): """Return the duration of current playing media in seconds.""" - return 360 + return self._duration @property def media_image_url(self): @@ -152,10 +157,39 @@ class DemoYoutubePlayer(AbstractDemoPlayer): """Flag of media commands that are supported.""" return YOUTUBE_PLAYER_SUPPORT + @property + def media_position(self): + """Position of current playing media in seconds.""" + if self._progress is None: + return None + + position = self._progress + + if self._player_state == STATE_PLAYING: + position += (dt_util.utcnow() - + self._progress_updated_at).total_seconds() + + return position + + @property + def media_position_updated_at(self): + """When was the position of the current playing media valid. + + Returns value from homeassistant.util.dt.utcnow(). + """ + if self._player_state == STATE_PLAYING: + return self._progress_updated_at + def play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" self.youtube_id = media_id - self.update_ha_state() + self.schedule_update_ha_state() + + def media_pause(self): + """Send pause command.""" + self._progress = self.media_position + self._progress_updated_at = dt_util.utcnow() + super().media_pause() class DemoMusicPlayer(AbstractDemoPlayer): @@ -249,20 +283,20 @@ class DemoMusicPlayer(AbstractDemoPlayer): """Send previous track command.""" if self._cur_track > 0: self._cur_track -= 1 - self.update_ha_state() + self.schedule_update_ha_state() def media_next_track(self): """Send next track command.""" if self._cur_track < len(self.tracks) - 1: self._cur_track += 1 - self.update_ha_state() + self.schedule_update_ha_state() def clear_playlist(self): """Clear players playlist.""" self.tracks = [] self._cur_track = 0 self._player_state = STATE_OFF - self.update_ha_state() + self.schedule_update_ha_state() class DemoTVShowPlayer(AbstractDemoPlayer): @@ -344,15 +378,15 @@ class DemoTVShowPlayer(AbstractDemoPlayer): """Send previous track command.""" if self._cur_episode > 1: self._cur_episode -= 1 - self.update_ha_state() + self.schedule_update_ha_state() def media_next_track(self): """Send next track command.""" if self._cur_episode < self._episode_count: self._cur_episode += 1 - self.update_ha_state() + self.schedule_update_ha_state() def select_source(self, source): """Set the input source.""" self._source = source - self.update_ha_state() + self.schedule_update_ha_state() From 5a7e44664694d940db4c98b456a6c89b5cc7f694 Mon Sep 17 00:00:00 2001 From: Daniel Hoyer Iversen Date: Sat, 3 Dec 2016 23:44:06 +0100 Subject: [PATCH 27/89] device tracker --- homeassistant/components/device_tracker/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 91f0720e927..eaa0621b04c 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -332,7 +332,6 @@ class Device(Entity): gps = None # type: GPSType gps_accuracy = 0 last_seen = None # type: dt_util.dt.datetime - battery = None # type: str attributes = None # type: dict vendor = None # type: str @@ -396,9 +395,6 @@ class Device(Entity): attr[ATTR_LONGITUDE] = self.gps[1] attr[ATTR_GPS_ACCURACY] = self.gps_accuracy - if self.battery: - attr[ATTR_BATTERY] = self.battery - if self.attributes: for key, value in self.attributes.items(): attr[key] = value @@ -419,8 +415,13 @@ class Device(Entity): self.host_name = host_name self.location_name = location_name self.gps_accuracy = gps_accuracy or 0 - self.battery = battery - self.attributes = attributes + if (battery or attributes) and self.attributes is None: + self.attributes = {} + if battery: + self.attributes[ATTR_BATTERY] = battery + if attributes: + for key, value in attributes.items(): + self.attributes[key] = value self.gps = None if gps is not None: From 03d19ec2f1b58001fdf829d080f0e0e981c4ebd3 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 5 Dec 2016 11:19:20 +0100 Subject: [PATCH 28/89] Netdata sensor (#4743) * Added netdata sensor * Typo * Add netdata sensor * Improvement of the work done by @ezar --- .coveragerc | 1 + homeassistant/components/sensor/netdata.py | 147 +++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 homeassistant/components/sensor/netdata.py diff --git a/.coveragerc b/.coveragerc index 9078b199a3e..6d5abe745f8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -281,6 +281,7 @@ omit = homeassistant/components/sensor/mhz19.py homeassistant/components/sensor/miflora.py homeassistant/components/sensor/mqtt_room.py + homeassistant/components/sensor/netdata.py homeassistant/components/sensor/neurio_energy.py homeassistant/components/sensor/nut.py homeassistant/components/sensor/nzbget.py diff --git a/homeassistant/components/sensor/netdata.py b/homeassistant/components/sensor/netdata.py new file mode 100644 index 00000000000..3a87eeb5ceb --- /dev/null +++ b/homeassistant/components/sensor/netdata.py @@ -0,0 +1,147 @@ +""" +Support gathering system information of hosts which are running netdata. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.netdata/ +""" +import logging +from datetime import timedelta + +import requests +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_HOST, CONF_PORT, STATE_UNKNOWN, CONF_NAME, CONF_RESOURCES) +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +_LOGGER = logging.getLogger(__name__) +_RESOURCE = 'api/v1' +_REALTIME = 'before=0&after=-1&options=seconds' + +DEFAULT_HOST = 'localhost' +DEFAULT_NAME = 'Netdata' +DEFAULT_PORT = '19999' + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + +SENSOR_TYPES = { + 'memory_free': ['RAM Free', 'MiB', 'system.ram', 'free', 1], + 'memory_used': ['RAM Used', 'MiB', 'system.ram', 'used', 1], + 'memory_cached': ['RAM Cached', 'MiB', 'system.ram', 'cached', 1], + 'memory_buffers': ['RAM Buffers', 'MiB', 'system.ram', 'buffers', 1], + 'swap_free': ['Swap Free', 'MiB', 'system.swap', 'free', 1], + 'swap_used': ['Swap Used', 'MiB', 'system.swap', 'used', 1], + 'processes_running': ['Processes Running', 'Count', 'system.processes', + 'running', 0], + 'processes_blocked': ['Processes Blocked', 'Count', 'system.processes', + 'blocked', 0], + 'system_load': ['System Load', '15 min', 'system.processes', 'running', 2], + 'system_io_in': ['System IO In', 'Count', 'system.io', 'in', 0], + 'system_io_out': ['System IO Out', 'Count', 'system.io', 'out', 0], +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_RESOURCES, default=['memory_free']): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), +}) + + +# pylint: disable=unused-variable +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Netdata sensor.""" + name = config.get(CONF_NAME) + host = config.get(CONF_HOST) + port = config.get(CONF_PORT) + url = 'http://{}:{}'.format(host, port) + version_url = '{}/version.txt'.format(url) + data_url = '{}/{}/data?chart='.format(url, _RESOURCE) + resources = config.get(CONF_RESOURCES) + + try: + response = requests.get(version_url, timeout=10) + if not response.ok: + _LOGGER.error("Response status is '%s'", response.status_code) + return False + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to resource/endpoint: %s", url) + return False + + values = {} + for key, value in sorted(SENSOR_TYPES.items()): + if key in resources: + values.setdefault(value[2], []).append(key) + + dev = [] + for chart in values: + rest_url = '{}{}&{}'.format(data_url, chart, _REALTIME) + rest = NetdataData(rest_url) + for sensor_type in values[chart]: + dev.append(NetdataSensor(rest, name, sensor_type)) + + add_devices(dev) + + +class NetdataSensor(Entity): + """Implementation of a Netdata sensor.""" + + def __init__(self, rest, name, sensor_type): + """Initialize the sensor.""" + self.rest = rest + self.type = sensor_type + self._name = '{} {}'.format(name, SENSOR_TYPES[self.type][0]) + self._precision = SENSOR_TYPES[self.type][4] + self._unit_of_measurement = SENSOR_TYPES[self.type][1] + self.update() + + @property + def name(self): + """The name of the sensor.""" + return self._name + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return self._unit_of_measurement + + @property + def state(self): + """Return the state of the resources.""" + value = self.rest.data + + if value is not None: + netdata_id = SENSOR_TYPES[self.type][3] + if netdata_id in value: + return "{0:.{1}f}".format(value[netdata_id], self._precision) + else: + return STATE_UNKNOWN + + def update(self): + """Get the latest data from Netdata REST API.""" + self.rest.update() + + +class NetdataData(object): + """The class for handling the data retrieval.""" + + def __init__(self, resource): + """Initialize the data object.""" + self._resource = resource + self.data = None + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Get the latest data from the Netdata REST API.""" + try: + response = requests.get(self._resource, timeout=5) + det = response.json() + self.data = {k: v for k, v in zip(det['labels'], det['data'][0])} + + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to host/endpoint: %s", self._resource) + self.data = None From e21382cd3e810e152f5f3191fa4fd407d84b3213 Mon Sep 17 00:00:00 2001 From: rubund Date: Mon, 5 Dec 2016 17:15:36 +0100 Subject: [PATCH 29/89] Fix broken EnOcean support (#4710) * ensure_list * CONF_ID is not required configuration for enocean lights * Use vol.All(cv.ensure_list, [vol.Coerce(int)]) as suggested in pull request review * Fix line too long --- homeassistant/components/binary_sensor/enocean.py | 2 +- homeassistant/components/light/enocean.py | 5 +++-- homeassistant/components/sensor/enocean.py | 2 +- homeassistant/components/switch/enocean.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/binary_sensor/enocean.py b/homeassistant/components/binary_sensor/enocean.py index 631ed0021e1..bd68a232f22 100644 --- a/homeassistant/components/binary_sensor/enocean.py +++ b/homeassistant/components/binary_sensor/enocean.py @@ -20,7 +20,7 @@ DEPENDENCIES = ['enocean'] DEFAULT_NAME = 'EnOcean binary sensor' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_ID): cv.string, + vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_SENSOR_CLASS, default=None): SENSOR_CLASSES_SCHEMA, }) diff --git a/homeassistant/components/light/enocean.py b/homeassistant/components/light/enocean.py index ce65d8cc041..e24aca4902d 100644 --- a/homeassistant/components/light/enocean.py +++ b/homeassistant/components/light/enocean.py @@ -26,8 +26,9 @@ DEPENDENCIES = ['enocean'] SUPPORT_ENOCEAN = SUPPORT_BRIGHTNESS PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_ID): cv.string, - vol.Required(CONF_SENDER_ID): cv.string, + vol.Optional(CONF_ID, default=[]): vol.All(cv.ensure_list, + [vol.Coerce(int)]), + vol.Required(CONF_SENDER_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) diff --git a/homeassistant/components/sensor/enocean.py b/homeassistant/components/sensor/enocean.py index e998b5c9c46..009718dd720 100644 --- a/homeassistant/components/sensor/enocean.py +++ b/homeassistant/components/sensor/enocean.py @@ -20,7 +20,7 @@ DEFAULT_NAME = 'EnOcean sensor' DEPENDENCIES = ['enocean'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_ID): cv.string, + vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) diff --git a/homeassistant/components/switch/enocean.py b/homeassistant/components/switch/enocean.py index 71bd180ad10..ead5d789bbd 100644 --- a/homeassistant/components/switch/enocean.py +++ b/homeassistant/components/switch/enocean.py @@ -20,7 +20,7 @@ DEFAULT_NAME = 'EnOcean Switch' DEPENDENCIES = ['enocean'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_ID): cv.string, + vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) From 13006cee685111e7de106f369f6ae8b09c250439 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 5 Dec 2016 11:32:17 -0800 Subject: [PATCH 30/89] Device tracker attributes (#4753) --- .../components/device_tracker/__init__.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index e95b556b998..7c4c74708db 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -332,6 +332,7 @@ class Device(Entity): gps = None # type: GPSType gps_accuracy = 0 last_seen = None # type: dt_util.dt.datetime + battery = None # type: str attributes = None # type: dict vendor = None # type: str @@ -369,6 +370,7 @@ class Device(Entity): self.away_hide = hide_if_away self.vendor = vendor + self._attributes = {} @property def name(self): @@ -395,12 +397,16 @@ class Device(Entity): attr[ATTR_LONGITUDE] = self.gps[1] attr[ATTR_GPS_ACCURACY] = self.gps_accuracy - if self.attributes: - for key, value in self.attributes.items(): - attr[key] = value + if self.battery: + attr[ATTR_BATTERY] = self.battery return attr + @property + def device_state_attributes(self): + """Return device state attributes.""" + return self._attributes + @property def hidden(self): """If device should be hidden.""" @@ -415,13 +421,10 @@ class Device(Entity): self.host_name = host_name self.location_name = location_name self.gps_accuracy = gps_accuracy or 0 - if (battery or attributes) and self.attributes is None: - self.attributes = {} - if battery: - self.attributes[ATTR_BATTERY] = battery + if attributes: - for key, value in attributes.items(): - self.attributes[key] = value + self._attributes.update(attributes) + self.gps = None if gps is not None: From 308744d8a022c7fc25af4f2ef8a6214cdcf014f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Mon, 5 Dec 2016 20:33:51 +0100 Subject: [PATCH 31/89] Add additional attributes to GPSLogger (#4755) --- homeassistant/components/device_tracker/gpslogger.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/gpslogger.py b/homeassistant/components/device_tracker/gpslogger.py index 2e897ccb10c..d8e7ca8b074 100644 --- a/homeassistant/components/device_tracker/gpslogger.py +++ b/homeassistant/components/device_tracker/gpslogger.py @@ -63,10 +63,20 @@ class GPSLoggerView(HomeAssistantView): accuracy = int(float(data['accuracy'])) if 'battery' in data: battery = float(data['battery']) + attrs = {} + if 'speed' in data: + attrs['speed'] = float(data['speed']) + if 'direction' in data: + attrs['direction'] = float(data['direction']) + if 'altitude' in data: + attrs['altitude'] = float(data['altitude']) + if 'provider' in data: + attrs['provider'] = data['provider'] yield from hass.loop.run_in_executor( None, partial(self.see, dev_id=device, gps=gps_location, battery=battery, - gps_accuracy=accuracy)) + gps_accuracy=accuracy, + attributes=attrs)) return 'Setting location for {}'.format(device) From b3253403aae44d47cd624050ee5f39e5a56ccec5 Mon Sep 17 00:00:00 2001 From: Jeff Wilson Date: Mon, 5 Dec 2016 20:39:40 -0500 Subject: [PATCH 32/89] Set hue-bridgeid in UPNP response (#4740) --- homeassistant/components/emulated_hue/upnp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/emulated_hue/upnp.py b/homeassistant/components/emulated_hue/upnp.py index f81a8c1b68d..fd880c40e6e 100644 --- a/homeassistant/components/emulated_hue/upnp.py +++ b/homeassistant/components/emulated_hue/upnp.py @@ -74,6 +74,7 @@ CACHE-CONTROL: max-age=60 EXT: LOCATION: http://{0}:{1}/description.xml SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1 +hue-bridgeid: 1234 ST: urn:schemas-upnp-org:device:basic:1 USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1 From 1ae8256ffd63983738adf7ed7d947e4d689d889d Mon Sep 17 00:00:00 2001 From: "Martin J. Laubach" Date: Tue, 6 Dec 2016 02:50:50 +0100 Subject: [PATCH 33/89] Add sensor for reading Austrian ZAMG weather conditions (#4347) * Add sensor for reading ZAMG weather conditions * Add to coveragerc; Correct some doc style problems * More doc fixes * More doc fixes * Lose license and whatever. * Don't return UNKNOWN for unknown variables * Verify that the configured station id is actually one in the data set. Don't warn about unknown stations, this cannot happen any more as the configuration parser now checks that. This could still happen if the data set is incomplete though ... * Clean up imports * Clarify comment on throttling interval * Base zamg sensor on Entity, not WeatherEntity, and delete unused code * Fix formatting nits from flake8 * Use ATTR_FRIENDLY_NAME, clean up imports, remove unnecessary indirection. * Use {}.format() instead of "" % * Re-add unit of measurement that got lost somehow * Use guard clauses instead of if-matroshka. Wrap requests.get() in try/except for RequestException. * Huh, how did this happen? White space corrections... * Add sensor for reading ZAMG weather conditions * Add to coveragerc; Correct some doc style problems * More doc fixes * More doc fixes * Verify that the configured station id is actually one in the data set. Don't warn about unknown stations, this cannot happen any more as the configuration parser now checks that. This could still happen if the data set is incomplete though ... * Lose license and whatever. * Don't return UNKNOWN for unknown variables * Clean up imports * Clarify comment on throttling interval * Base zamg sensor on Entity, not WeatherEntity, and delete unused code * Fix formatting nits from flake8 * Use ATTR_FRIENDLY_NAME, clean up imports, remove unnecessary indirection. * Use {}.format() instead of "" % * Re-add unit of measurement that got lost somehow * Use guard clauses instead of if-matroshka. Wrap requests.get() in try/except for RequestException. * Huh, how did this happen? White space corrections... * Precipitation actually is a float, good it rained today * Logger needs no module visibility * Do not name sensors with _ to be in line with the other weather sensor platforms. * Remove manually set friendly_name * comment format police * Less comments * Update zamg.py --- .coveragerc | 2 +- homeassistant/components/sensor/zamg.py | 247 ++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/sensor/zamg.py diff --git a/.coveragerc b/.coveragerc index 6d5abe745f8..5dcc37e1402 100644 --- a/.coveragerc +++ b/.coveragerc @@ -315,7 +315,7 @@ omit = homeassistant/components/sensor/waqi.py homeassistant/components/sensor/xbox_live.py homeassistant/components/sensor/yweather.py - homeassistant/components/sensor/waqi.py + homeassistant/components/sensor/zamg.py homeassistant/components/switch/acer_projector.py homeassistant/components/switch/anel_pwrctrl.py homeassistant/components/switch/arest.py diff --git a/homeassistant/components/sensor/zamg.py b/homeassistant/components/sensor/zamg.py new file mode 100644 index 00000000000..d3d64690ef6 --- /dev/null +++ b/homeassistant/components/sensor/zamg.py @@ -0,0 +1,247 @@ +""" +Sensor for data from Austrian "Zentralanstalt für Meteorologie und Geodynamik". + +This is a sensor for the Austrian weather service "Zentralanstalt für +Meteorologie und Geodynamik" (aka ZAMG). + +The configuration should look like this: + + - platform: zamg + station_id: 11035 + monitored_conditions: + - temperature + - humidity + - pressure + - wind_speed + - precipitation + +Recognised conditions are: + + pressure (Pressure at station level) + pressure_sealevel (Pressure at Sea Level) + humidity (Humidity) + wind_speed (Wind Speed) + wind_bearing (Wind Bearing) + wind_max_speed (Top Wind Speed) + wind_max_bearing (Top Wind Bearing) + sun_last_hour (Sun Last Hour Percentage) + temperature (Temperature) + precipitation (Precipitation) + dewpoint (Dew Point) + +The following stations are available in the data set: + + 11010 Linz/Hörsching + 11012 Kremsmünster + 11022 Retz + 11035 Wien/Hohe Warte + 11036 Wien/Schwechat + 11101 Bregenz + 11121 Innsbruck + 11126 Patscherkofel + 11130 Kufstein + 11150 Salzburg + 11155 Feuerkogel + 11157 Aigen im Ennstal + 11171 Mariazell + 11190 Eisenstadt + 11204 Lienz +""" + +import csv +from datetime import timedelta +import logging +import requests + +import voluptuous as vol + +from homeassistant.components.weather import ( + ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_ATTRIBUTION, + ATTR_WEATHER_PRESSURE, ATTR_WEATHER_TEMPERATURE, + ATTR_WEATHER_WIND_BEARING, ATTR_WEATHER_WIND_SPEED, +) +import homeassistant.helpers.config_validation as cv +from homeassistant.const import ( + CONF_MONITORED_CONDITIONS, CONF_NAME, __version__ +) +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +DEFAULT_NAME = 'zamg' +ATTRIBUTION = 'Data provided by ZAMG' + +# Data source only updates once per hour, so throttle to 30min to have +# reasonably recent data +MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) + +CONF_STATION_ID = "station_id" + +VALID_STATION_IDS = ( + '11010', '11012', '11022', '11035', '11036', '11101', '11121', '11126', + '11130', '11150', '11155', '11157', '11171', '11190', '11204' +) + +SENSOR_TYPES = { + ATTR_WEATHER_PRESSURE: ('Pressure', 'hPa', 'LDstat hPa', float), + 'pressure_sealevel': ('Pressure at Sea Level', 'hPa', 'LDred hPa', float), + ATTR_WEATHER_HUMIDITY: ('Humidity', '%', 'RF %', int), + ATTR_WEATHER_WIND_SPEED: ('Wind Speed', 'km/h', 'WG km/h', float), + ATTR_WEATHER_WIND_BEARING: ('Wind Bearing', '°', 'WR °', int), + 'wind_max_speed': ('Top Wind Speed', 'km/h', 'WSG km/h', float), + 'wind_max_bearing': ('Top Wind Bearing', '°', 'WSR °', int), + 'sun_last_hour': ('Sun Last Hour', '%', 'SO %', int), + ATTR_WEATHER_TEMPERATURE: ('Temperature', '°C', 'T °C', float), + 'precipitation': ('Precipitation', 'l/m²', 'N l/m²', float), + 'dewpoint': ('Dew Point', '°C', 'TP °C', float), + # The following probably not useful for general consumption, + # but we need them to fill in internal attributes + 'station_name': ('Station Name', None, 'Name', str), + 'station_elevation': ('Station Elevation', 'm', 'Höhe m', int), + 'update_date': ('Update Date', None, 'Datum', str), + 'update_time': ('Update Time', None, 'Zeit', str), +} + +PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_STATION_ID): + vol.All(cv.string, vol.In(VALID_STATION_IDS)), + vol.Required(CONF_MONITORED_CONDITIONS): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup platform.""" + station_id = config.get(CONF_STATION_ID) + name = config.get(CONF_NAME) + + logger = logging.getLogger(__name__) + probe = ZamgData(station_id=station_id, logger=logger) + + sensors = [ZAMGWeather(probe, variable, name) + for variable in config[CONF_MONITORED_CONDITIONS]] + + add_devices(sensors, True) + + +class ZAMGWeather(Entity): + """ + I am a weather wrapper for a specific station and a specific attribute. + + Multiple instances (one for each condition) will refer to the same + probe, so things will only get fetched once. + """ + + def __init__(self, probe, variable, name): + """Init condition sensor.""" + self.probe = probe + self.client_name = name + self.variable = variable + + def update(self): + """Delegate update to probe.""" + self.probe.update() + + @property + def name(self): + """Build name of sensor.""" + return '{} {}'.format(self.client_name, self.variable) + + @property + def state(self): + """Return state.""" + return self.probe.get_data(self.variable) + + @property + def unit_of_measurement(self): + """Unit of measurement.""" + return SENSOR_TYPES[self.variable][1] + + @property + def state_attributes(self): + """Return the state attributes.""" + return { + ATTR_WEATHER_ATTRIBUTION: ATTRIBUTION, + "station": self.probe.get_data('station_name'), + "updated": "%s %s" % (self.probe.get_data('update_date'), + self.probe.get_data('update_time')) + } + + +class ZamgData(object): + """ + I represent weather data for a specific site. + + From the web site: + + Sie beinhalten neben Stationsnummer, Stationsname, Seehöhe der Station, + Messdatum und Messzeit (Lokalzeit) die meteorologischen Messwerte von + Temperatur, Taupunkt, relative Luftfeuchtigkeit, Richtung und + Geschwindigkeit des Windmittels und der Windspitze, Niederschlagssumme + der letzten Stunde, Luftdruck reduziert auf Meeresniveau und Luftdruck + auf Stationsniveau sowie die Sonnenscheindauer der letzten Stunde (in + Prozent). Die Messstationen, die diese Daten liefern, sind über das + Bundesgebiet verteilt und beinhalten alle Landeshauptstädte sowie + die wichtigsten Bergstationen. + """ + + API_URL = "http://www.zamg.ac.at/ogd/" + + API_FIELDS = { + v[2]: (k, v[3]) + for k, v in SENSOR_TYPES.items() + } + + API_HEADERS = { + 'User-Agent': 'home-assistant.zamg/' + __version__, + } + + def __init__(self, logger, station_id): + """Initialize the probe.""" + self._logger = logger + self._station_id = station_id + self.data = {} + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ + Update data set. + + Fetch a new data set from the zamg server, parse it and + update internal state accordingly + """ + try: + response = requests.get(self.API_URL, + headers=self.API_HEADERS, timeout=15) + except requests.exceptions.RequestException: + self._logger.exception("While fetching data from server") + return + + if response.status_code != 200: + self._logger.error("API call returned with status %s", + response.status_code) + return + + content_type = response.headers.get('Content-Type', 'whatever') + if content_type != 'text/csv': + self._logger.error("Expected text/csv but got %s", + content_type) + return + + response.encoding = 'UTF8' + content = response.text + data = (line for line in content.split('\n')) + reader = csv.DictReader(data, delimiter=';', quotechar='"') + for row in reader: + if row.get("Station", None) == self._station_id: + self.data = { + self.API_FIELDS.get(k)[0]: + self.API_FIELDS.get(k)[1](v.replace(',', '.')) + for k, v in row.items() + if v and k in self.API_FIELDS + } + break + + def get_data(self, variable): + """Generic accessor for data.""" + return self.data.get(variable) From fa8bc0a36ccefdf64b78ab63be9ccd7d4c0f890e Mon Sep 17 00:00:00 2001 From: John Arild Berentsen Date: Tue, 6 Dec 2016 02:51:58 +0100 Subject: [PATCH 34/89] Add Verisure smartcam capture service (#4559) * Add verisure capture as service * docstyle --- homeassistant/components/services.yaml | 9 +++++++++ homeassistant/components/verisure.py | 27 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 46a3a46ced6..54c0e18a3ee 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -144,3 +144,12 @@ openalpr: restart: description: Restart ffmpeg process of device. + +verisure: + capture_smartcam: + description: Capture a new image from a smartcam. + + fields: + device_serial: + description: The serial number of the smartcam you want to capture an image from. + example: '2DEU AT5Z' diff --git a/homeassistant/components/verisure.py b/homeassistant/components/verisure.py index c8241d8fae5..f2b091aa0f1 100644 --- a/homeassistant/components/verisure.py +++ b/homeassistant/components/verisure.py @@ -7,6 +7,7 @@ https://home-assistant.io/components/verisure/ import logging import threading import time +import os.path from datetime import timedelta import voluptuous as vol @@ -14,12 +15,14 @@ import voluptuous as vol from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.helpers import discovery from homeassistant.util import Throttle +import homeassistant.config as conf_util import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['vsure==0.11.1'] _LOGGER = logging.getLogger(__name__) +ATTR_DEVICE_SERIAL = 'device_serial' CONF_ALARM = 'alarm' CONF_CODE_DIGITS = 'code_digits' CONF_HYDROMETERS = 'hygrometers' @@ -29,6 +32,7 @@ CONF_SMARTPLUGS = 'smartplugs' CONF_THERMOMETERS = 'thermometers' CONF_SMARTCAM = 'smartcam' DOMAIN = 'verisure' +SERVICE_CAPTURE_SMARTCAM = 'capture_smartcam' HUB = None @@ -47,6 +51,10 @@ CONFIG_SCHEMA = vol.Schema({ }), }, extra=vol.ALLOW_EXTRA) +CAPTURE_IMAGE_SCHEMA = vol.Schema({ + vol.Required(ATTR_DEVICE_SERIAL): cv.string +}) + def setup(hass, config): """Setup the Verisure component.""" @@ -60,6 +68,20 @@ def setup(hass, config): 'camera'): discovery.load_platform(hass, component, DOMAIN, {}, config) + descriptions = conf_util.load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + + def capture_smartcam(service): + """Capture a new picture from a smartcam.""" + device_id = service.data.get(ATTR_DEVICE_SERIAL) + HUB.smartcam_capture(device_id) + _LOGGER.debug('Capturing new image from %s', ATTR_DEVICE_SERIAL) + + hass.services.register(DOMAIN, SERVICE_CAPTURE_SMARTCAM, + capture_smartcam, + descriptions[DOMAIN][SERVICE_CAPTURE_SMARTCAM], + schema=CAPTURE_IMAGE_SCHEMA) + return True @@ -150,6 +172,11 @@ class VerisureHub(object): self.smartcam_dict = self.my_pages.smartcam.get_imagelist() _LOGGER.debug('New dict: %s', self.smartcam_dict) + @Throttle(timedelta(seconds=30)) + def smartcam_capture(self, device_id): + """Capture a new image from a smartcam.""" + self.my_pages.smartcam.capture(device_id) + @property def available(self): """Return True if hub is available.""" From b60f5714fc088d672322d3c4b6565558cc254c9b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 5 Dec 2016 18:03:06 -0800 Subject: [PATCH 35/89] Fix websocket async (#4752) * Ensure we write to websocket from inside event loop * Inline service call helper --- homeassistant/components/websocket_api.py | 70 ++++++++++++----------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/websocket_api.py b/homeassistant/components/websocket_api.py index 09f8699f5d1..70b35e00247 100644 --- a/homeassistant/components/websocket_api.py +++ b/homeassistant/components/websocket_api.py @@ -204,7 +204,6 @@ class ActiveConnection: self.hass = hass self.request = request self.wsock = None - self.socket_task = None self.event_listeners = {} def debug(self, message1, message2=''): @@ -220,34 +219,6 @@ class ActiveConnection: self.debug('Sending', message) self.wsock.send_json(message, dumps=JSON_DUMP) - @callback - def _cancel_connection(self, event): - """Cancel this connection.""" - self.socket_task.cancel() - - @asyncio.coroutine - def _call_service_helper(self, msg): - """Helper to call a service and fire complete message.""" - yield from self.hass.services.async_call(msg['domain'], msg['service'], - msg['service_data'], True) - try: - self.send_message(result_message(msg['id'])) - except RuntimeError: - # Socket has been closed. - pass - - @callback - def _forward_event(self, iden, event): - """Helper to forward events to websocket.""" - if event.event_type == EVENT_TIME_CHANGED: - return - - try: - self.send_message(event_message(iden, event)) - except RuntimeError: - # Socket has been closed. - pass - @asyncio.coroutine def handle(self): """Handle the websocket connection.""" @@ -255,9 +226,15 @@ class ActiveConnection: yield from wsock.prepare(self.request) # Set up to cancel this connection when Home Assistant shuts down - self.socket_task = asyncio.Task.current_task(loop=self.hass.loop) - self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, - self._cancel_connection) + socket_task = asyncio.Task.current_task(loop=self.hass.loop) + + @callback + def cancel_connection(event): + """Cancel this connection.""" + socket_task.cancel() + + unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, + cancel_connection) self.debug('Connected') @@ -351,6 +328,8 @@ class ActiveConnection: _LOGGER.exception(error) finally: + unsub_stop() + for unsub in self.event_listeners.values(): unsub() @@ -363,8 +342,20 @@ class ActiveConnection: """Handle subscribe events command.""" msg = SUBSCRIBE_EVENTS_MESSAGE_SCHEMA(msg) + @callback + def forward_events(event): + """Helper to forward events to websocket.""" + if event.event_type == EVENT_TIME_CHANGED: + return + + try: + self.send_message(event_message(msg['id'], event)) + except RuntimeError: + # Socket has been closed. + pass + self.event_listeners[msg['id']] = self.hass.bus.async_listen( - msg['event_type'], partial(self._forward_event, msg['id'])) + msg['event_type'], forward_events) self.send_message(result_message(msg['id'])) @@ -386,7 +377,18 @@ class ActiveConnection: """Handle call service command.""" msg = CALL_SERVICE_MESSAGE_SCHEMA(msg) - self.hass.async_add_job(self._call_service_helper(msg)) + @asyncio.coroutine + def call_service_helper(msg): + """Helper to call a service and fire complete message.""" + yield from self.hass.services.async_call( + msg['domain'], msg['service'], msg['service_data'], True) + try: + self.send_message(result_message(msg['id'])) + except RuntimeError: + # Socket has been closed. + pass + + self.hass.async_add_job(call_service_helper(msg)) def handle_get_states(self, msg): """Handle get states command.""" From 8afd30b7d45f1dd6f7c9bbc9a07fd51a51ba1bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 6 Dec 2016 03:04:04 +0100 Subject: [PATCH 36/89] fix setting battery in device_tracker (#4756) --- homeassistant/components/device_tracker/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 7c4c74708db..d497ea4c314 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -421,7 +421,8 @@ class Device(Entity): self.host_name = host_name self.location_name = location_name self.gps_accuracy = gps_accuracy or 0 - + if battery: + self.battery = battery if attributes: self._attributes.update(attributes) From 776455030fd7b2a8321af8842cdd7943325550f1 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 5 Dec 2016 21:07:04 -0500 Subject: [PATCH 37/89] Fix media_image_urls for universal media player (#4765) * Fix media_image_urls for universal media player * Linter fixes --- .../components/media_player/universal.py | 11 ++++++++ .../components/media_player/test_universal.py | 26 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/homeassistant/components/media_player/universal.py b/homeassistant/components/media_player/universal.py index cedaeed4985..85a3eb42aa5 100644 --- a/homeassistant/components/media_player/universal.py +++ b/homeassistant/components/media_player/universal.py @@ -263,6 +263,17 @@ class UniversalMediaPlayer(MediaPlayerDevice): """Image url of current playing media.""" return self._child_attr(ATTR_ENTITY_PICTURE) + @property + def entity_picture(self): + """ + Return image of the media playing. + + The universal media player doesn't use the parent class logic, since + the url is coming from child entity pictures which have already been + sent through the API proxy. + """ + return self.media_image_url + @property def media_title(self): """Title of current playing media.""" diff --git a/tests/components/media_player/test_universal.py b/tests/components/media_player/test_universal.py index 76e80f8236e..ff70fe36a17 100644 --- a/tests/components/media_player/test_universal.py +++ b/tests/components/media_player/test_universal.py @@ -28,6 +28,7 @@ class MockMediaPlayer(media_player.MediaPlayerDevice): self._supported_media_commands = 0 self._source = None self._tracks = 12 + self._media_image_url = None self.service_calls = { 'turn_on': mock_service( @@ -92,6 +93,11 @@ class MockMediaPlayer(media_player.MediaPlayerDevice): """Supported media commands flag.""" return self._supported_media_commands + @property + def media_image_url(self): + """Image url of current playing media.""" + return self._media_image_url + def turn_on(self): """Mock turn_on function.""" self._state = STATE_UNKNOWN @@ -400,6 +406,26 @@ class TestMediaPlayer(unittest.TestCase): ump.update() self.assertEqual(1, ump.volume_level) + def test_media_image_url(self): + """Test media_image_url property.""" + TEST_URL = "test_url" + config = self.config_children_only + universal.validate_config(config) + + ump = universal.UniversalMediaPlayer(self.hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name']) + ump.update() + + self.assertEqual(None, ump.media_image_url) + + self.mock_mp_1._state = STATE_PLAYING + self.mock_mp_1._media_image_url = TEST_URL + self.mock_mp_1.update_ha_state() + ump.update() + # mock_mp_1 will convert the url to the api proxy url. This test + # ensures ump passes through the same url without an additional proxy. + self.assertEqual(self.mock_mp_1.entity_picture, ump.entity_picture) + def test_is_volume_muted_children_only(self): """Test is volume muted property w/ children only.""" config = self.config_children_only From 81d38c34631e1418000c9ed08764985cfab07bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Oldag?= Date: Tue, 6 Dec 2016 06:12:24 +0100 Subject: [PATCH 38/89] Add flic smart button component (#4681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added component for flic smart buttons. * Apply home-assistant coding styles. * Fixed flic configuration. * Made logging for scanning for new buttons less verbose. * Fixed flic event data. * Follow async conventions. * Added new requirements to requirements_all.txt. * Added flic component to .coveragerc * Updated flic threshold configuration key names. * Flic devices are now removed when they disconnect. * Include review feedback. * Fixed stopping of clients in flic component when home assistant is stopped. * Updated flic component by integrating input of #4738. Use library method to determine click type. Merge three click events into single one with click_type parameter. * Use a single client for both handling click events and scanning for new buttons. * Renamed flic ‘auto_scan’ configuration variable to ‘discovery’ using HA constants. --- .coveragerc | 1 + .../components/binary_sensor/flic.py | 196 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 200 insertions(+) create mode 100644 homeassistant/components/binary_sensor/flic.py diff --git a/.coveragerc b/.coveragerc index 5dcc37e1402..0d34382659b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -122,6 +122,7 @@ omit = homeassistant/components/alarm_control_panel/simplisafe.py homeassistant/components/binary_sensor/arest.py homeassistant/components/binary_sensor/concord232.py + homeassistant/components/binary_sensor/flic.py homeassistant/components/binary_sensor/rest.py homeassistant/components/browser.py homeassistant/components/camera/amcrest.py diff --git a/homeassistant/components/binary_sensor/flic.py b/homeassistant/components/binary_sensor/flic.py new file mode 100644 index 00000000000..63323155d31 --- /dev/null +++ b/homeassistant/components/binary_sensor/flic.py @@ -0,0 +1,196 @@ +"""Contains functionality to use flic buttons as a binary sensor.""" +import asyncio +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.const import ( + CONF_HOST, CONF_PORT, CONF_DISCOVERY, EVENT_HOMEASSISTANT_STOP) +from homeassistant.components.binary_sensor import ( + BinarySensorDevice, PLATFORM_SCHEMA) +from homeassistant.util.async import run_callback_threadsafe + + +REQUIREMENTS = ['https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4'] + +_LOGGER = logging.getLogger(__name__) + + +EVENT_NAME = "flic_click" +EVENT_DATA_NAME = "button_name" +EVENT_DATA_ADDRESS = "button_address" +EVENT_DATA_TYPE = "click_type" + +# Validation of the user's configuration +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_HOST, default='localhost'): cv.string, + vol.Optional(CONF_PORT, default=5551): cv.port, + vol.Optional(CONF_DISCOVERY, default=True): cv.boolean +}) + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Setup the flic platform.""" + import pyflic + + # Initialize flic client responsible for + # connecting to buttons and retrieving events + host = config.get(CONF_HOST) + port = config.get(CONF_PORT) + discovery = config.get(CONF_DISCOVERY) + + try: + client = pyflic.FlicClient(host, port) + except ConnectionRefusedError: + _LOGGER.error("Failed to connect to flic server.") + return + + def new_button_callback(address): + """Setup newly verified button as device in home assistant.""" + hass.add_job(async_setup_button(hass, config, async_add_entities, + client, address)) + + client.on_new_verified_button = new_button_callback + if discovery: + start_scanning(hass, config, async_add_entities, client) + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, + lambda event: client.close()) + hass.loop.run_in_executor(None, client.handle_events) + + # Get addresses of already verified buttons + addresses = yield from async_get_verified_addresses(client) + if addresses: + for address in addresses: + yield from async_setup_button(hass, config, async_add_entities, + client, address) + + +def start_scanning(hass, config, async_add_entities, client): + """Start a new flic client for scanning & connceting to new buttons.""" + import pyflic + + scan_wizard = pyflic.ScanWizard() + + def scan_completed_callback(scan_wizard, result, address, name): + """Restart scan wizard to constantly check for new buttons.""" + if result == pyflic.ScanWizardResult.WizardSuccess: + _LOGGER.info("Found new button (%s)", address) + elif result != pyflic.ScanWizardResult.WizardFailedTimeout: + _LOGGER.warning("Failed to connect to button (%s). Reason: %s", + address, result) + + # Restart scan wizard + start_scanning(hass, config, async_add_entities, client) + + scan_wizard.on_completed = scan_completed_callback + client.add_scan_wizard(scan_wizard) + + +@asyncio.coroutine +def async_setup_button(hass, config, async_add_entities, client, address): + """Setup single button device.""" + button = FlicButton(hass, client, address) + _LOGGER.info("Connected to button (%s)", address) + + yield from async_add_entities([button]) + + +@asyncio.coroutine +def async_get_verified_addresses(client): + """Retrieve addresses of verified buttons.""" + future = asyncio.Future() + loop = asyncio.get_event_loop() + + def get_info_callback(items): + """Set the addressed of connected buttons as result of the future.""" + addresses = items["bd_addr_of_verified_buttons"] + run_callback_threadsafe(loop, future.set_result, addresses) + client.get_info(get_info_callback) + + return future + + +class FlicButton(BinarySensorDevice): + """Representation of a flic button.""" + + def __init__(self, hass, client, address): + """Initialize the flic button.""" + import pyflic + + self._hass = hass + self._address = address + self._is_down = False + self._click_types = { + pyflic.ClickType.ButtonSingleClick: "single", + pyflic.ClickType.ButtonDoubleClick: "double", + pyflic.ClickType.ButtonHold: "hold", + } + + # Initialize connection channel + self._channel = pyflic.ButtonConnectionChannel(self._address) + self._channel.on_button_up_or_down = self._on_up_down + self._channel.on_button_single_or_double_click_or_hold = self._on_click + client.add_connection_channel(self._channel) + + @property + def name(self): + """Return the name of the device.""" + return "flic_%s" % self.address.replace(":", "") + + @property + def address(self): + """Return the bluetooth address of the device.""" + return self._address + + @property + def is_on(self): + """Return true if sensor is on.""" + return self._is_down + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def state_attributes(self): + """Return device specific state attributes.""" + attr = super(FlicButton, self).state_attributes + attr["address"] = self.address + + return attr + + def _on_up_down(self, channel, click_type, was_queued, time_diff): + """Update device state, if event was not queued.""" + import pyflic + + if was_queued: + return + + self._is_down = click_type == pyflic.ClickType.ButtonDown + self.schedule_update_ha_state() + + def _on_click(self, channel, click_type, was_queued, time_diff): + """Fire click event, if event was not queued.""" + if was_queued: + return + + self._hass.bus.fire(EVENT_NAME, { + EVENT_DATA_NAME: self.name, + EVENT_DATA_ADDRESS: self.address, + EVENT_DATA_TYPE: self._click_types[click_type] + }) + + def _connection_status_changed(self, channel, + connection_status, disconnect_reason): + """Remove device, if button disconnects.""" + import pyflic + + if connection_status == pyflic.ConnectionStatus.Disconnected: + _LOGGER.info("Button (%s) disconnected. Reason: %s", + self.address, disconnect_reason) + self.remove() diff --git a/requirements_all.txt b/requirements_all.txt index 622faa3719d..0cae8b32af1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -241,6 +241,9 @@ https://github.com/robbiet480/pygtfs/archive/00546724e4bbcb3053110d844ca44e22462 # homeassistant.components.scene.hunterdouglas_powerview https://github.com/sander76/powerviewApi/archive/246e782d60d5c0addcc98d7899a0186f9d5640b0.zip#powerviewApi==0.3.15 +# homeassistant.components.binary_sensor.flic +https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4 + # homeassistant.components.light.osramlightify https://github.com/tfriedel/python-lightify/archive/d6eadcf311e6e21746182d1480e97b350dda2b3e.zip#lightify==1.0.4 From 8c628071f30ae7c6822244011c489b8b648e46ae Mon Sep 17 00:00:00 2001 From: Hugo Dupras Date: Tue, 6 Dec 2016 06:35:33 +0100 Subject: [PATCH 39/89] Add support for Netatmo tags (#4761) * Add support for Netatmo Welcome Tags Signed-off-by: Hugo D. (jabesq) * Add size parameter for WelcomeData * minor fixes * Add Throttling mechanism for update event This will prevent to reach the API limit Signed-off-by: Hugo D. (jabesq) * Change scan interval for Netatmo Binary sensors Signed-off-by: Hugo D. (jabesq) * Minor fixes Signed-off-by: Hugo D. (jabesq) * Update netatmo.py --- .../components/binary_sensor/netatmo.py | 41 +++++++++++++++---- homeassistant/components/netatmo.py | 24 +++++++++-- requirements_all.txt | 2 +- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/binary_sensor/netatmo.py b/homeassistant/components/binary_sensor/netatmo.py index 93b3bb5817c..94ef0faaad0 100644 --- a/homeassistant/components/binary_sensor/netatmo.py +++ b/homeassistant/components/binary_sensor/netatmo.py @@ -23,9 +23,11 @@ _LOGGER = logging.getLogger(__name__) # These are the available sensors mapped to binary_sensor class SENSOR_TYPES = { - "Someone known": "motion", - "Someone unknown": "motion", - "Motion": "motion", + "Someone known": 'occupancy', + "Someone unknown": 'motion', + "Motion": 'motion', + "Tag Vibration": 'vibration', + "Tag Open": 'opening', } CONF_HOME = 'home' @@ -48,6 +50,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): home = config.get(CONF_HOME, None) timeout = config.get(CONF_TIMEOUT, 15) + module_name = None + import lnetatmo try: data = WelcomeData(netatmo.NETATMO_AUTH, home) @@ -64,23 +68,35 @@ def setup_platform(hass, config, add_devices, discovery_info=None): camera_name not in config[CONF_CAMERAS]: continue for variable in sensors: - add_devices([WelcomeBinarySensor(data, camera_name, home, timeout, - variable)]) + if variable in ('Tag Vibration', 'Tag Open'): + continue + add_devices([WelcomeBinarySensor(data, camera_name, module_name, + home, timeout, variable)]) + + for module_name in data.get_module_names(camera_name): + for variable in sensors: + if variable in ('Tag Vibration', 'Tag Open'): + add_devices([WelcomeBinarySensor(data, camera_name, + module_name, home, + timeout, variable)]) class WelcomeBinarySensor(BinarySensorDevice): """Represent a single binary sensor in a Netatmo Welcome device.""" - def __init__(self, data, camera_name, home, timeout, sensor): + def __init__(self, data, camera_name, module_name, home, timeout, sensor): """Setup for access to the Netatmo camera events.""" self._data = data self._camera_name = camera_name + self._module_name = module_name self._home = home self._timeout = timeout if home: self._name = home + ' / ' + camera_name else: self._name = camera_name + if module_name: + self._name += ' / ' + module_name self._sensor_name = sensor self._name += ' ' + sensor camera_id = data.welcomedata.cameraByName(camera=camera_name, @@ -112,7 +128,7 @@ class WelcomeBinarySensor(BinarySensorDevice): def update(self): """Request an update from the Netatmo API.""" self._data.update() - self._data.welcomedata.updateEvent(home=self._data.home) + self._data.update_event() if self._sensor_name == "Someone known": self._state =\ @@ -129,5 +145,16 @@ class WelcomeBinarySensor(BinarySensorDevice): self._data.welcomedata.motionDetected(self._home, self._camera_name, self._timeout*60) + elif self._sensor_name == "Tag Vibration": + self._state =\ + self._data.welcomedata.moduleMotionDetected(self._home, + self._module_name, + self._camera_name, + self._timeout*60) + elif self._sensor_name == "Tag Open": + self._state =\ + self._data.welcomedata.moduleOpened(self._home, + self._module_name, + self._camera_name) else: return None diff --git a/homeassistant/components/netatmo.py b/homeassistant/components/netatmo.py index d6e0101e4e0..3bb98a00b87 100644 --- a/homeassistant/components/netatmo.py +++ b/homeassistant/components/netatmo.py @@ -18,7 +18,7 @@ from homeassistant.util import Throttle REQUIREMENTS = [ 'https://github.com/jabesq/netatmo-api-python/archive/' - 'v0.7.0.zip#lnetatmo==0.7.0'] + 'v0.8.0.zip#lnetatmo==0.8.0'] _LOGGER = logging.getLogger(__name__) @@ -30,6 +30,7 @@ NETATMO_AUTH = None DEFAULT_DISCOVERY = True MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10) +MIN_TIME_BETWEEN_EVENT_UPDATES = timedelta(seconds=10) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -72,10 +73,11 @@ class WelcomeData(object): self.auth = auth self.welcomedata = None self.camera_names = [] + self.module_names = [] self.home = home def get_camera_names(self): - """Return all module available on the API as a list.""" + """Return all camera available on the API as a list.""" self.camera_names = [] self.update() if not self.home: @@ -87,8 +89,24 @@ class WelcomeData(object): self.camera_names.append(camera['name']) return self.camera_names + def get_module_names(self, camera_name): + """Return all module available on the API as a list.""" + self.module_names = [] + self.update() + cam_id = self.welcomedata.cameraByName(camera=camera_name, + home=self.home)['id'] + for module in self.welcomedata.modules.values(): + if cam_id == module['cam_id']: + self.module_names.append(module['name']) + return self.module_names + @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Call the Netatmo API to update the data.""" import lnetatmo - self.welcomedata = lnetatmo.WelcomeData(self.auth) + self.welcomedata = lnetatmo.WelcomeData(self.auth, size=100) + + @Throttle(MIN_TIME_BETWEEN_EVENT_UPDATES) + def update_event(self): + """Call the Netatmo API to update the list of events.""" + self.welcomedata.updateEvent(home=self.home) diff --git a/requirements_all.txt b/requirements_all.txt index 0cae8b32af1..d468f2aa863 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -205,7 +205,7 @@ https://github.com/danieljkemp/onkyo-eiscp/archive/python3.zip#onkyo-eiscp==0.9. # https://github.com/deisi/fritzconnection/archive/b5c14515e1c8e2652b06b6316a7f3913df942841.zip#fritzconnection==0.4.6 # homeassistant.components.netatmo -https://github.com/jabesq/netatmo-api-python/archive/v0.7.0.zip#lnetatmo==0.7.0 +https://github.com/jabesq/netatmo-api-python/archive/v0.8.0.zip#lnetatmo==0.8.0 # homeassistant.components.neato https://github.com/jabesq/pybotvac/archive/v0.0.1.zip#pybotvac==0.0.1 From 8ca2345fd4fde5b6ecc924a03c9338f05474ac51 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 5 Dec 2016 23:35:54 -0600 Subject: [PATCH 40/89] Pyunifi dep (#4754) * change unifi dependency to pyunifi * Change dependency to fix #4336 * Run gen_requirements_all.py script * Changed import statement to reflect new package * Updated test_unifiy.py with different module * Update requirements_all.txt --- homeassistant/components/device_tracker/unifi.py | 4 ++-- requirements_all.txt | 6 +++--- tests/components/device_tracker/test_unifi.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/device_tracker/unifi.py b/homeassistant/components/device_tracker/unifi.py index e139775e031..ab84eb22e04 100644 --- a/homeassistant/components/device_tracker/unifi.py +++ b/homeassistant/components/device_tracker/unifi.py @@ -14,7 +14,7 @@ from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD # Unifi package doesn't list urllib3 as a requirement -REQUIREMENTS = ['urllib3', 'unifi==1.2.5'] +REQUIREMENTS = ['urllib3', 'pyunifi==1.3'] _LOGGER = logging.getLogger(__name__) CONF_PORT = 'port' @@ -34,7 +34,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def get_scanner(hass, config): """Setup Unifi device_tracker.""" - from unifi.controller import Controller + from pyunifi.controller import Controller host = config[DOMAIN].get(CONF_HOST) username = config[DOMAIN].get(CONF_USERNAME) diff --git a/requirements_all.txt b/requirements_all.txt index d468f2aa863..6f1ca5eb4e1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -479,6 +479,9 @@ python-twitch==1.3.0 # homeassistant.components.wink python-wink==0.11.0 +# homeassistant.components.device_tracker.unifi +pyunifi==1.3 + # homeassistant.components.keyboard # pyuserinput==0.1.11 @@ -569,9 +572,6 @@ twilio==5.4.0 # homeassistant.components.sensor.uber uber_rides==0.2.7 -# homeassistant.components.device_tracker.unifi -unifi==1.2.5 - # homeassistant.components.device_tracker.unifi urllib3 diff --git a/tests/components/device_tracker/test_unifi.py b/tests/components/device_tracker/test_unifi.py index 5482740ce11..12d296959dc 100644 --- a/tests/components/device_tracker/test_unifi.py +++ b/tests/components/device_tracker/test_unifi.py @@ -3,7 +3,7 @@ import unittest from unittest import mock import urllib -from unifi import controller +from pyunifi import controller import voluptuous as vol from tests.common import get_test_home_assistant From a11b68c560d91cfb1af3b94b8dd088496d66a77c Mon Sep 17 00:00:00 2001 From: dasos Date: Tue, 6 Dec 2016 05:37:05 +0000 Subject: [PATCH 41/89] Fix connection check (#4732) * Fix connection check * Release instead * Remove if * Update hook.py --- homeassistant/components/switch/hook.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switch/hook.py b/homeassistant/components/switch/hook.py index eba64c6aeb1..689ab675b5f 100644 --- a/homeassistant/components/switch/hook.py +++ b/homeassistant/components/switch/hook.py @@ -50,7 +50,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return False finally: if response is not None: - yield from response.close() + yield from response.release() try: token = data['data']['token'] @@ -72,7 +72,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return False finally: if response is not None: - yield from response.close() + yield from response.release() yield from async_add_devices( HookSmartHome( @@ -127,7 +127,7 @@ class HookSmartHome(SwitchDevice): finally: if response is not None: - yield from response.close() + yield from response.release() _LOGGER.debug("Got: %s", data) return data['return_value'] == '1' From 64290d74f09c9df66f116894dc19dad92d908b0c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 5 Dec 2016 21:40:34 -0800 Subject: [PATCH 42/89] Update frontend --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 4 ++-- .../frontend/www_static/frontend.html.gz | Bin 130456 -> 130476 bytes .../www_static/home-assistant-polymer | 2 +- .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 2325 -> 2326 bytes 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 837fdd0e1fe..14650b47cb7 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -2,7 +2,7 @@ FINGERPRINTS = { "core.js": "5dfb2d3e567fad37af0321d4b29265ed", - "frontend.html": "6a89b74ab2b76c7d28fad2aea9444ec2", + "frontend.html": "d4f164e559944b8abc560d7b46131714", "mdi.html": "46a76f877ac9848899b8ed382427c16f", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "panels/ha-panel-dev-event.html": "c2d5ec676be98d4474d19f94d0262c1e", diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index d0a3e75d8db..54b9cc4d5e4 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,5 +1,5 @@ \ No newline at end of file +},customStyle:null,getComputedStyleValue:function(e){return!i&&this._styleProperties&&this._styleProperties[e]||getComputedStyle(this).getPropertyValue(e)},_setupStyleProperties:function(){this.customStyle={},this._styleCache=null,this._styleProperties=null,this._scopeSelector=null,this._ownStyleProperties=null,this._customStyle=null},_needsStyleProperties:function(){return Boolean(!i&&this._ownStylePropertyNames&&this._ownStylePropertyNames.length)},_validateApplyShim:function(){if(this.__applyShimInvalid){Polymer.ApplyShim.transform(this._styles,this.__proto__);var e=n.elementStyles(this);if(s){var t=this._template.content.querySelector("style");t&&(t.textContent=e)}else{var r=this._scopeStyle&&this._scopeStyle.nextSibling;r&&(r.textContent=e)}}},_beforeAttached:function(){this._scopeSelector&&!this.__stylePropertiesInvalid||!this._needsStyleProperties()||(this.__stylePropertiesInvalid=!1,this._updateStyleProperties())},_findStyleHost:function(){for(var e,t=this;e=Polymer.dom(t).getOwnerRoot();){if(Polymer.isInstance(e.host))return e.host;t=e.host}return r},_updateStyleProperties:function(){var e,n=this._findStyleHost();n._styleProperties||n._computeStyleProperties(),n._styleCache||(n._styleCache=new Polymer.StyleCache);var r=t.propertyDataFromStyles(n._styles,this),i=!this.__notStyleScopeCacheable;i&&(r.key.customStyle=this.customStyle,e=n._styleCache.retrieve(this.is,r.key,this._styles));var a=Boolean(e);a?this._styleProperties=e._styleProperties:this._computeStyleProperties(r.properties),this._computeOwnStyleProperties(),a||(e=o.retrieve(this.is,this._ownStyleProperties,this._styles));var l=Boolean(e)&&!a,c=this._applyStyleProperties(e);a||(c=c&&s?c.cloneNode(!0):c,e={style:c,_scopeSelector:this._scopeSelector,_styleProperties:this._styleProperties},i&&(r.key.customStyle={},this.mixin(r.key.customStyle,this.customStyle),n._styleCache.store(this.is,e,r.key,this._styles)),l||o.store(this.is,Object.create(e),this._ownStyleProperties,this._styles))},_computeStyleProperties:function(e){var n=this._findStyleHost();n._styleProperties||n._computeStyleProperties();var r=Object.create(n._styleProperties),s=t.hostAndRootPropertiesForScope(this);this.mixin(r,s.hostProps),e=e||t.propertyDataFromStyles(n._styles,this).properties,this.mixin(r,e),this.mixin(r,s.rootProps),t.mixinCustomStyle(r,this.customStyle),t.reify(r),this._styleProperties=r},_computeOwnStyleProperties:function(){for(var e,t={},n=0;n0&&l.push(t);return[{removed:a,added:l}]}},Polymer.Collection.get=function(e){return Polymer._collections.get(e)||new Polymer.Collection(e)},Polymer.Collection.applySplices=function(e,t){var n=Polymer._collections.get(e);return n?n._applySplices(t):null},Polymer({is:"dom-repeat",extends:"template",_template:null,properties:{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},sort:{type:Function,observer:"_sortChanged"},filter:{type:Function,observer:"_filterChanged"},observe:{type:String,observer:"_observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number,observer:"_initializeChunking"},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"_computeFrameTime(targetFramerate)"}},behaviors:[Polymer.Templatizer],observers:["_itemsChanged(items.*)"],created:function(){this._instances=[],this._pool=[],this._limit=1/0;var e=this;this._boundRenderChunk=function(){e._renderChunk()}},detached:function(){this.__isDetached=!0;for(var e=0;e=0;t--){var n=this._instances[t];n.isPlaceholder&&t=this._limit&&(n=this._downgradeInstance(t,n.__key__)),e[n.__key__]=t,n.isPlaceholder||n.__setProperty(this.indexAs,t,!0)}this._pool.length=0,this._setRenderedItemCount(this._instances.length),this.fire("dom-change"),this._tryRenderChunk()},_applyFullRefresh:function(){var e,t=this.collection;if(this._sortFn)e=t?t.getKeys():[];else{e=[];var n=this.items;if(n)for(var r=0;r=r;a--)this._detachAndRemoveInstance(a)},_numericSort:function(e,t){return e-t},_applySplicesUserSort:function(e){for(var t,n,r=this.collection,s={},i=0;i=0;i--){var c=a[i];void 0!==c&&this._detachAndRemoveInstance(c)}var h=this;if(l.length){this._filterFn&&(l=l.filter(function(e){return h._filterFn(r.getItem(e))})),l.sort(function(e,t){return h._sortFn(r.getItem(e),r.getItem(t))});var u=0;for(i=0;i>1,a=this._instances[o].__key__,l=this._sortFn(n.getItem(a),r);if(l<0)e=o+1;else{if(!(l>0)){i=o;break}s=o-1}}return i<0&&(i=s+1),this._insertPlaceholder(i,t),i},_applySplicesArrayOrder:function(e){for(var t,n=0;n=0?(e=this.as+"."+e.substring(n+1),i._notifyPath(e,t,!0)):i.__setProperty(this.as,t,!0))}},itemForElement:function(e){var t=this.modelForElement(e);return t&&t[this.as]},keyForElement:function(e){var t=this.modelForElement(e);return t&&t.__key__},indexForElement:function(e){var t=this.modelForElement(e);return t&&t[this.indexAs]}}),Polymer({is:"array-selector",_template:null,properties:{items:{type:Array,observer:"clearSelection"},multi:{type:Boolean,value:!1,observer:"clearSelection"},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}},clearSelection:function(){if(Array.isArray(this.selected))for(var e=0;e \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/frontend.html.gz b/homeassistant/components/frontend/www_static/frontend.html.gz index 2e509569ab2ffe7c98d3aa9a446f426cc3d9bf52..3bf5bab1b5944da1c6375064d0b6d982ab667b5d 100644 GIT binary patch delta 62822 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 fMYlflq?(1h^*w{TS2 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

l>k?vg57L5S$BgBT}qK>?fnPXR+;W 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 diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 2652823d35b..4697af1b0d9 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 2652823d35b77411988751cc74820dcfc3a0e2ac +Subproject commit 4697af1b0d96e7a21454539888c68189668fa80d diff --git a/homeassistant/components/frontend/www_static/service_worker.js b/homeassistant/components/frontend/www_static/service_worker.js index 25535a72081..b125336669c 100644 --- a/homeassistant/components/frontend/www_static/service_worker.js +++ b/homeassistant/components/frontend/www_static/service_worker.js @@ -1 +1 @@ -"use strict";function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}function notificationEventCallback(e,t){firePushCallback({action:t.action,data:t.notification.data,tag:t.notification.tag,type:e},t.notification.data.jwt)}function firePushCallback(e,t){delete e.data.jwt,0===Object.keys(e.data).length&&e.data.constructor===Object&&delete e.data,fetch("/api/notify.html5/callback",{method:"POST",headers:new Headers({"Content-Type":"application/json",Authorization:"Bearer "+t}),body:JSON.stringify(e)})}var precacheConfig=[["/","71255a0807fe2c461c870c5f3aaedc56"],["/frontend/panels/dev-event-c2d5ec676be98d4474d19f94d0262c1e.html","6c55fc819751923ab00c62ae3fbb7222"],["/frontend/panels/dev-info-a9c07bf281fe9791fb15827ec1286825.html","931f9327e368db710fcdf5f7202f2588"],["/frontend/panels/dev-service-b3fe49532c5c03198fafb0c6ed58b76a.html","4194cb43b74108dc6d10354da2fd81fd"],["/frontend/panels/dev-state-65e5f791cc467561719bf591f1386054.html","78158786a6597ef86c3fd6f4985cde92"],["/frontend/panels/dev-template-7d744ab7f7c08b6d6ad42069989de400.html","8a6ee994b1cdb45b081299b8609915ed"],["/frontend/panels/map-1bf6965b24d76db71a1871865cd4a3a2.html","a74c01c2ee68c83c9938af067ec33b81"],["/static/core-5dfb2d3e567fad37af0321d4b29265ed.js","9a50270db7613e3af449f6c773366f4d"],["/static/frontend-6a89b74ab2b76c7d28fad2aea9444ec2.html","db91a94d9dcb88f12188d3d88c1200a2"],["/static/mdi-46a76f877ac9848899b8ed382427c16f.html","a846c4082dd5cffd88ac72cbe943e691"],["static/fonts/roboto/Roboto-Bold.ttf","d329cc8b34667f114a95422aaad1b063"],["static/fonts/roboto/Roboto-Light.ttf","7b5fb88f12bec8143f00e21bc3222124"],["static/fonts/roboto/Roboto-Medium.ttf","fe13e4170719c2fc586501e777bde143"],["static/fonts/roboto/Roboto-Regular.ttf","ac3f799d5bbaf5196fab15ab8de8431c"],["static/icons/favicon-192x192.png","419903b8422586a7e28021bbe9011175"],["static/icons/favicon.ico","04235bda7843ec2fceb1cbe2bc696cf4"],["static/images/card_media_player_bg.png","a34281d1c1835d338a642e90930e61aa"],["static/webcomponents-lite.min.js","b0f32ad3c7749c40d486603f31c9d8b1"]],cacheName="sw-precache-v2--"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var a=new URL(e);return"/"===a.pathname.slice(-1)&&(a.pathname+=t),a.toString()},createCacheKey=function(e,t,a,n){var c=new URL(e);return n&&c.toString().match(n)||(c.search+=(c.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(a)),c.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var a=new URL(t).pathname;return e.some(function(e){return a.match(e)})},stripIgnoredUrlParameters=function(e,t){var a=new URL(e);return a.search=a.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(e){return t.every(function(t){return!t.test(e[0])})}).map(function(e){return e.join("=")}).join("&"),a.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],a=e[1],n=new URL(t,self.location),c=createCacheKey(n,hashParamName,a,!1);return[n.toString(),c]}));self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(e){return setOfCachedUrls(e).then(function(t){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(a){if(!t.has(a))return e.add(new Request(a,{credentials:"same-origin"}))}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var t=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(e){return e.keys().then(function(a){return Promise.all(a.map(function(a){if(!t.has(a.url))return e.delete(a)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(e){if("GET"===e.request.method){var t,a=stripIgnoredUrlParameters(e.request.url,ignoreUrlParametersMatching);t=urlsToCacheKeys.has(a);var n="index.html";!t&&n&&(a=addDirectoryIndex(a,n),t=urlsToCacheKeys.has(a));var c="/";!t&&c&&"navigate"===e.request.mode&&isPathWhitelisted(["^((?!(static|api|local|service_worker.js|manifest.json)).)*$"],e.request.url)&&(a=new URL(c,self.location).toString(),t=urlsToCacheKeys.has(a)),t&&e.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(a)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(t){return console.warn('Couldn\'t serve response for "%s" from cache: %O',e.request.url,t),fetch(e.request)}))}}),self.addEventListener("push",function(e){var t;e.data&&(t=e.data.json(),e.waitUntil(self.registration.showNotification(t.title,t).then(function(e){firePushCallback({type:"received",tag:t.tag,data:t.data},t.data.jwt)})))}),self.addEventListener("notificationclick",function(e){var t;notificationEventCallback("clicked",e),e.notification.close(),e.notification.data&&e.notification.data.url&&(t=e.notification.data.url,t&&e.waitUntil(clients.matchAll({type:"window"}).then(function(e){var a,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 tH^ChnGi<^|Gf{SBukd%>`rHrhyjUreDC-k z;H+90w5BPfaVC0INmJC-pat_zzuT^@JE_l}sm{n6<4IuaqSFP&>W)79R*|7J) zf-fI_v1-a&@AmlC3N8m9!QcvhwzZ|IO`!|P?SEcgKxJO=vJ_l?cfmK_wJsWXziQ{( z)ob4EJ2gT7;45x8{%==KVAp)ZXZP1}$&uYAXO$O}P|eJII9z`!>k1>WlBRC98-~L@ zalQs4=dLs6%VOM_cNNUdqD;neZG+=qFTl)eb?Ur-_wkeC&w(pw+Ea_}Kl*3)+Idk| z2C*D{LT8;*hcB1q+R^d1wyqri`3i+J#eciClWGAke2t%1t4v`ijqm&-kQ&hSh@mz+P(3BAkd6p9`NWy3al2Dd1nrzhM5z%>s zFQSwwf02<;OQjQ?(U5AIFm_x`3(Z9#VI(3Aah^m}CNhjj&NSBoZ3UGuk)?d2DJFR= zMI4DNCLvQYRV0j(SaGTqTB#np0GJDzhw#Qp8H_$=x_GN;%6hd|Xi6Co@Gc)(Ufy=W!f^+*++fPH;`- zN(#m_p@cCNDaJ7S!jRKF=?hhi;*@8pW?9B%&SJ(ah@c`yW12~l>Ma~BPGuZ2s#GF1 z<_+UAqY_Igjv&o@I5t+J(e1dYMQ!Twe~0d8^lM$Jz!+_9jA$+;6H%NZQiQ}jPhv_r z=Zc6hjeaKTbupWpwX94ex_49nmTVm9Fa%13jIdA%jejQV4X9$ZSPRpD5s66_VuDMm zWrA4|63DVlC_vdi6ZQdStCBZsX&kdZ%X5_o!8LZ0RCDYETrdSJj)>fqR@lBhf7blM zK8&#I{J_7#vYPePn}?Wj2oV~@GoUO)4-xs05RxTFi*%Xn4MTv^ACeoOqDR~q^N;uz@ zb_qh(i)CG5_iaaIVUXC1sxJc(f9i;0m0;q>IVP5hS(=8CMqqNq1m@GZ-*LcOzJRII zUXC`TFuI_lk#pjDl=Am05fppZi>?-;j_keO|q)6#SUp%@~%KEjjV2rNPf1UL#*k`mqD)e+k)^2TN|| z6-p0UWI}L9#2XIXyXBLq@q8Yb`eR3Vu6OIp2C(gQW75CiYEPTb{i@d}4>b;|;ZW{K z!imX}dga~RxH4#wY32N6x_x}+3>^!@2~@IH@cF~57n{Ji#yfGQ&O>jw=lOEa+@gJt zzI>TuC`&{c)IEF(?b<;Ff7WA(*BkpJoa_TA<(s!tzxY zOtlxyKNcyJ)*~|AJgj3yyr9MT#kh!+cYOX_e=~LL5UPt3H9z#B32@|SuI^r$&8sKa zxuC@X^(;L1x>5G1u5WcwZB+HoXxhE}ne%quVbGV!*PS|Xd>oeBlMx0Kf1l$IIrpo( z$oXB7m32SpVSY^aQ`oKcZNe;mLN@51RXfJ}^8D8GCY?O2yKXLjZIh}(Oq~MfSR6+k zf7dbyE_q=-SEeZ4&Vx3<`mWq_|H-7{uSr(Rq@Rt{hpFq_ekM?mwE1bW-{N!}4t?A~^y zw+CH#jETI7ncj~=pGI8+t)^HONJ{-oHhF?4yUG&G^nkWp%N_nPVZAgtMcV3w$l=hb zaAug<1ljiyDPcG~==$!N^SkRld*b#)e}gB<8`~1foAuG;>t)@1hX$$sX2GjMTUqu@ zh{fXl^)EOh>_O78f8@5s$a^hjw`Cml$T#)~+(3)erM?tq{^#?wXPNu*XVCHB;Xcd> z*ke5zWQ@KxH)T>fe|9)x)7HzoGp=&%{|I#`G+pl^He@+L#ynDKjHnQXT3Shh3 z6~yt1SgzXn1C3+S-zQ-3kS*3X6|F5jf45&eFqF;f%eOn%A}&&5VMGoJdz&gHn-y_Sq z1@~aNN9}<{cN?BHoDaV1vwWu)uiILey3etJuEDv)Hc?+X58ab>bCF+7FjuhV!U_M| zW>}l>lM@fKx^*~Olkrv1t-0O=cWlhCwf3b`z4`3$jr-@q bf%xr* Date: Tue, 6 Dec 2016 00:20:21 -0600 Subject: [PATCH 43/89] Match uppercase MAC addresses in asuswrt 'arp -n' output (#4742) (#4764) --- homeassistant/components/device_tracker/asuswrt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index 2eced1b4dd4..4e860846f8e 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -286,8 +286,10 @@ class AsusWrtDeviceScanner(object): # match mac addresses to IP addresses in ARP table for arp in result.arp: - if match.group('mac').lower() in arp.decode('utf-8'): - arp_match = _ARP_REGEX.search(arp.decode('utf-8')) + if match.group('mac').lower() in \ + arp.decode('utf-8').lower(): + arp_match = _ARP_REGEX.search( + arp.decode('utf-8').lower()) if not arp_match: _LOGGER.warning('Could not parse arp row: %s', arp) continue From fa0dbaf065af549e5211c350a68cb9017fbdd919 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 5 Dec 2016 23:39:22 -0800 Subject: [PATCH 44/89] Fix default auth influxdb (#4771) --- homeassistant/components/influxdb.py | 44 ++++++++++++++++------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/influxdb.py b/homeassistant/components/influxdb.py index 08296ad65c7..c712cf6a27e 100644 --- a/homeassistant/components/influxdb.py +++ b/homeassistant/components/influxdb.py @@ -24,23 +24,20 @@ CONF_TAGS = 'tags' CONF_DEFAULT_MEASUREMENT = 'default_measurement' DEFAULT_DATABASE = 'home_assistant' -DEFAULT_HOST = 'localhost' -DEFAULT_PORT = 8086 -DEFAULT_SSL = False -DEFAULT_VERIFY_SSL = False +DEFAULT_VERIFY_SSL = True DOMAIN = 'influxdb' TIMEOUT = 5 CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_HOST): cv.string, vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string, vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string, vol.Optional(CONF_BLACKLIST, default=[]): vol.All(cv.ensure_list, [cv.entity_id]), vol.Optional(CONF_DB_NAME, default=DEFAULT_DATABASE): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, + vol.Optional(CONF_PORT): cv.port, + vol.Optional(CONF_SSL): cv.boolean, vol.Optional(CONF_DEFAULT_MEASUREMENT): cv.string, vol.Optional(CONF_TAGS, default={}): vol.Schema({cv.string: cv.string}), @@ -57,23 +54,34 @@ def setup(hass, config): conf = config[DOMAIN] - host = conf.get(CONF_HOST) - port = conf.get(CONF_PORT) - database = conf.get(CONF_DB_NAME) - username = conf.get(CONF_USERNAME) - password = conf.get(CONF_PASSWORD) - ssl = conf.get(CONF_SSL) - verify_ssl = conf.get(CONF_VERIFY_SSL) + kwargs = { + 'database': conf[CONF_DB_NAME], + 'verify_ssl': conf[CONF_VERIFY_SSL], + 'timeout': TIMEOUT + } + + if CONF_HOST in conf: + kwargs['host'] = conf[CONF_HOST] + + if CONF_PORT in conf: + kwargs['port'] = conf[CONF_PORT] + + if CONF_USERNAME in conf: + kwargs['username'] = conf[CONF_USERNAME] + + if CONF_PASSWORD in conf: + kwargs['password'] = conf[CONF_PASSWORD] + + if CONF_SSL in conf: + kwargs['ssl'] = conf[CONF_SSL] + blacklist = conf.get(CONF_BLACKLIST) whitelist = conf.get(CONF_WHITELIST) tags = conf.get(CONF_TAGS) default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT) try: - influx = InfluxDBClient( - host=host, port=port, username=username, password=password, - database=database, ssl=ssl, verify_ssl=verify_ssl, - timeout=TIMEOUT) + influx = InfluxDBClient(**kwargs) influx.query("select * from /.*/ LIMIT 1;") except exceptions.InfluxDBClientError as exc: _LOGGER.error("Database host is not accessible due to '%s', please " From d968e1d0110f949eb43bd4bbfa15dfdfea85b1e6 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Tue, 6 Dec 2016 13:01:24 +0000 Subject: [PATCH 45/89] Add test to ensure device_tracker records state correctly. (#4776) --- tests/components/device_tracker/test_init.py | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 013920985ff..c3087b108e9 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -458,6 +458,45 @@ class TestComponentsDeviceTracker(unittest.TestCase): timedelta(seconds=0)) assert len(config) == 0 + def test_see_state(self): + """Test device tracker see records state correctly.""" + self.assertTrue(setup_component(self.hass, device_tracker.DOMAIN, + TEST_PLATFORM)) + + params = { + 'mac': 'AA:BB:CC:DD:EE:FF', + 'dev_id': 'some_device', + 'host_name': 'example.com', + 'location_name': 'Work', + 'gps': [.3, .8], + 'gps_accuracy': 1, + 'battery': 100, + 'attributes': { + 'test': 'test', + 'number': 1, + }, + } + + device_tracker.see(self.hass, **params) + self.hass.block_till_done() + + config = device_tracker.load_config(self.yaml_devices, self.hass, + timedelta(seconds=0)) + assert len(config) == 1 + + state = self.hass.states.get('device_tracker.examplecom') + attrs = state.attributes + self.assertEqual(state.state, 'Work') + self.assertEqual(state.object_id, 'examplecom') + self.assertEqual(state.name, 'example.com') + self.assertEqual(attrs['friendly_name'], 'example.com') + self.assertEqual(attrs['battery'], 100) + self.assertEqual(attrs['latitude'], 0.3) + self.assertEqual(attrs['longitude'], 0.8) + self.assertEqual(attrs['test'], 'test') + self.assertEqual(attrs['gps_accuracy'], 1) + self.assertEqual(attrs['number'], 1) + @patch('homeassistant.components.device_tracker._LOGGER.warning') def test_see_failures(self, mock_warning): """Test that the device tracker see failures.""" From 76ff934bd34cb04cf3a028add2a7d6bb572b565f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 6 Dec 2016 15:49:59 +0100 Subject: [PATCH 46/89] Move details to docs, update doc strings, and use consts (#4777) --- homeassistant/components/sensor/zamg.py | 145 +++++++----------------- 1 file changed, 38 insertions(+), 107 deletions(-) diff --git a/homeassistant/components/sensor/zamg.py b/homeassistant/components/sensor/zamg.py index d3d64690ef6..6bb9dd0748d 100644 --- a/homeassistant/components/sensor/zamg.py +++ b/homeassistant/components/sensor/zamg.py @@ -1,81 +1,38 @@ """ Sensor for data from Austrian "Zentralanstalt für Meteorologie und Geodynamik". -This is a sensor for the Austrian weather service "Zentralanstalt für -Meteorologie und Geodynamik" (aka ZAMG). - -The configuration should look like this: - - - platform: zamg - station_id: 11035 - monitored_conditions: - - temperature - - humidity - - pressure - - wind_speed - - precipitation - -Recognised conditions are: - - pressure (Pressure at station level) - pressure_sealevel (Pressure at Sea Level) - humidity (Humidity) - wind_speed (Wind Speed) - wind_bearing (Wind Bearing) - wind_max_speed (Top Wind Speed) - wind_max_bearing (Top Wind Bearing) - sun_last_hour (Sun Last Hour Percentage) - temperature (Temperature) - precipitation (Precipitation) - dewpoint (Dew Point) - -The following stations are available in the data set: - - 11010 Linz/Hörsching - 11012 Kremsmünster - 11022 Retz - 11035 Wien/Hohe Warte - 11036 Wien/Schwechat - 11101 Bregenz - 11121 Innsbruck - 11126 Patscherkofel - 11130 Kufstein - 11150 Salzburg - 11155 Feuerkogel - 11157 Aigen im Ennstal - 11171 Mariazell - 11190 Eisenstadt - 11204 Lienz +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.zamg/ """ - import csv -from datetime import timedelta import logging -import requests +from datetime import timedelta +import requests import voluptuous as vol -from homeassistant.components.weather import ( - ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_ATTRIBUTION, - ATTR_WEATHER_PRESSURE, ATTR_WEATHER_TEMPERATURE, - ATTR_WEATHER_WIND_BEARING, ATTR_WEATHER_WIND_SPEED, -) import homeassistant.helpers.config_validation as cv +from homeassistant.components.weather import ( + ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_ATTRIBUTION, ATTR_WEATHER_PRESSURE, + ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_WIND_BEARING, + ATTR_WEATHER_WIND_SPEED) from homeassistant.const import ( - CONF_MONITORED_CONDITIONS, CONF_NAME, __version__ -) + CONF_MONITORED_CONDITIONS, CONF_NAME, __version__) from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -DEFAULT_NAME = 'zamg' +ATTR_STATION = 'station' +ATTR_UPDATED = 'updated' ATTRIBUTION = 'Data provided by ZAMG' -# Data source only updates once per hour, so throttle to 30min to have +CONF_STATION_ID = 'station_id' + +DEFAULT_NAME = 'zamg' + +# Data source only updates once per hour, so throttle to 30 min to have # reasonably recent data MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) -CONF_STATION_ID = "station_id" - VALID_STATION_IDS = ( '11010', '11012', '11022', '11035', '11036', '11101', '11121', '11126', '11130', '11150', '11155', '11157', '11171', '11190', '11204' @@ -102,41 +59,37 @@ SENSOR_TYPES = { } PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONF_STATION_ID): - vol.All(cv.string, vol.In(VALID_STATION_IDS)), vol.Required(CONF_MONITORED_CONDITIONS): vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Required(CONF_STATION_ID): + vol.All(cv.string, vol.In(VALID_STATION_IDS)), + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup platform.""" + """Set up the ZAMG sensor platform.""" station_id = config.get(CONF_STATION_ID) name = config.get(CONF_NAME) logger = logging.getLogger(__name__) probe = ZamgData(station_id=station_id, logger=logger) - sensors = [ZAMGWeather(probe, variable, name) + sensors = [ZamgSensor(probe, variable, name) for variable in config[CONF_MONITORED_CONDITIONS]] add_devices(sensors, True) -class ZAMGWeather(Entity): - """ - I am a weather wrapper for a specific station and a specific attribute. - - Multiple instances (one for each condition) will refer to the same - probe, so things will only get fetched once. - """ +class ZamgSensor(Entity): + """Implementation of a ZAMG sensor.""" def __init__(self, probe, variable, name): - """Init condition sensor.""" + """Initialize the sensor.""" self.probe = probe self.client_name = name self.variable = variable + self.update() def update(self): """Delegate update to probe.""" @@ -144,17 +97,17 @@ class ZAMGWeather(Entity): @property def name(self): - """Build name of sensor.""" + """Return the name of the sensor.""" return '{} {}'.format(self.client_name, self.variable) @property def state(self): - """Return state.""" + """Return the state of the sensor.""" return self.probe.get_data(self.variable) @property def unit_of_measurement(self): - """Unit of measurement.""" + """Return the unit of measurement of this entity, if any.""" return SENSOR_TYPES[self.variable][1] @property @@ -162,38 +115,22 @@ class ZAMGWeather(Entity): """Return the state attributes.""" return { ATTR_WEATHER_ATTRIBUTION: ATTRIBUTION, - "station": self.probe.get_data('station_name'), - "updated": "%s %s" % (self.probe.get_data('update_date'), - self.probe.get_data('update_time')) + ATTR_STATION: self.probe.get_data('station_name'), + ATTR_UPDATED: '{} {}'.format(self.probe.get_data('update_date'), + self.probe.get_data('update_time')), } class ZamgData(object): - """ - I represent weather data for a specific site. - - From the web site: - - Sie beinhalten neben Stationsnummer, Stationsname, Seehöhe der Station, - Messdatum und Messzeit (Lokalzeit) die meteorologischen Messwerte von - Temperatur, Taupunkt, relative Luftfeuchtigkeit, Richtung und - Geschwindigkeit des Windmittels und der Windspitze, Niederschlagssumme - der letzten Stunde, Luftdruck reduziert auf Meeresniveau und Luftdruck - auf Stationsniveau sowie die Sonnenscheindauer der letzten Stunde (in - Prozent). Die Messstationen, die diese Daten liefern, sind über das - Bundesgebiet verteilt und beinhalten alle Landeshauptstädte sowie - die wichtigsten Bergstationen. - """ - - API_URL = "http://www.zamg.ac.at/ogd/" + """The class for handling the data retrieval.""" + API_URL = 'http://www.zamg.ac.at/ogd/' API_FIELDS = { v[2]: (k, v[3]) for k, v in SENSOR_TYPES.items() } - API_HEADERS = { - 'User-Agent': 'home-assistant.zamg/' + __version__, + 'User-Agent': '{} {}'.format('home-assistant.zamg/', __version__), } def __init__(self, logger, station_id): @@ -204,15 +141,10 @@ class ZamgData(object): @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): - """ - Update data set. - - Fetch a new data set from the zamg server, parse it and - update internal state accordingly - """ + """Get the latest data from ZAMG.""" try: - response = requests.get(self.API_URL, - headers=self.API_HEADERS, timeout=15) + response = requests.get( + self.API_URL, headers=self.API_HEADERS, timeout=15) except requests.exceptions.RequestException: self._logger.exception("While fetching data from server") return @@ -224,8 +156,7 @@ class ZamgData(object): content_type = response.headers.get('Content-Type', 'whatever') if content_type != 'text/csv': - self._logger.error("Expected text/csv but got %s", - content_type) + self._logger.error("Expected text/csv but got %s", content_type) return response.encoding = 'UTF8' From 860a12cffbecbfb547dcb3d91f20a162e0445963 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 6 Dec 2016 07:43:11 -0800 Subject: [PATCH 47/89] Fix Kodi auth (#4770) --- homeassistant/components/media_player/kodi.py | 25 ++++++++++++++----- homeassistant/components/notify/kodi.py | 22 +++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index ae9f8c8d721..68161deea2f 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -36,10 +36,10 @@ SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_PASSWORD): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_TURN_OFF_ACTION, default=None): vol.In(TURN_OFF_ACTION), - vol.Optional(CONF_USERNAME): cv.string, + vol.Inclusive(CONF_USERNAME, 'auth'): cv.string, + vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string, }) @@ -51,11 +51,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if jsonrpc_url: url = jsonrpc_url.rstrip('/jsonrpc') + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + if username is not None: + auth = (username, password) + else: + auth = None + add_devices([ KodiDevice( config.get(CONF_NAME), url, - auth=(config.get(CONF_USERNAME), config.get(CONF_PASSWORD)), + auth=auth, turn_off_action=config.get(CONF_TURN_OFF_ACTION)), ]) @@ -68,10 +76,15 @@ class KodiDevice(MediaPlayerDevice): import jsonrpc_requests self._name = name self._url = url + + kwargs = {'timeout': 5} + + if auth is not None: + kwargs['auth'] = auth + self._server = jsonrpc_requests.Server( - '{}/jsonrpc'.format(self._url), - auth=auth, - timeout=5) + '{}/jsonrpc'.format(self._url), **kwargs) + self._turn_off_action = turn_off_action self._players = list() self._properties = None diff --git a/homeassistant/components/notify/kodi.py b/homeassistant/components/notify/kodi.py index 6f725d63d47..1d95920d00b 100644 --- a/homeassistant/components/notify/kodi.py +++ b/homeassistant/components/notify/kodi.py @@ -22,8 +22,8 @@ DEFAULT_PORT = 8080 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_USERNAME): cv.string, - vol.Optional(CONF_PASSWORD): cv.string, + vol.Inclusive(CONF_USERNAME, 'auth'): cv.string, + vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string, }) ATTR_DISPLAYTIME = 'displaytime' @@ -33,7 +33,13 @@ def get_service(hass, config): """Return the notify service.""" url = '{}:{}'.format(config.get(CONF_HOST), config.get(CONF_PORT)) - auth = (config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + if username is not None: + auth = (username, password) + else: + auth = None return KODINotificationService( url, @@ -48,10 +54,14 @@ class KODINotificationService(BaseNotificationService): """Initialize the service.""" import jsonrpc_requests self._url = url + + kwargs = {'timeout': 5} + + if auth is not None: + kwargs['auth'] = auth + self._server = jsonrpc_requests.Server( - '{}/jsonrpc'.format(self._url), - auth=auth, - timeout=5) + '{}/jsonrpc'.format(self._url), **kwargs) def send_message(self, message="", **kwargs): """Send a message to Kodi.""" From 8826e6a8d0613948669a8784aa76270bf6a2df63 Mon Sep 17 00:00:00 2001 From: Audun Ytterdal Date: Tue, 6 Dec 2016 18:01:47 +0100 Subject: [PATCH 48/89] Add support for telldus in the Docker image. (#4680) * Add support for telldus in the Docker image. Start with -v /tmp/TelldusClient:/tmp/TelldusClient -v /tmp/TelldusEvents:/tmp/TelldusEvents * Merged telldus install with the others * Clean up indenting * Stream apt-key --- Dockerfile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index b42d7edcc89..02e3db616ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,9 +8,12 @@ WORKDIR /usr/src/app RUN pip3 install --no-cache-dir colorlog cython -# For the nmap tracker, bluetooth tracker, Z-Wave -RUN apt-get update && \ - apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo libglib2.0-dev bluetooth libbluetooth-dev && \ +# For the nmap tracker, bluetooth tracker, Z-Wave, tellstick +RUN echo "deb http://download.telldus.com/debian/ stable main" >> /etc/apt/sources.list.d/telldus.list && \ + wget -qO - http://download.telldus.se/debian/telldus-public.key | apt-key add - && \ + apt-get update && \ + apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo libglib2.0-dev bluetooth libbluetooth-dev \ + libtelldus-core2 libtelldus-core-dev && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY script/build_python_openzwave script/build_python_openzwave From 9a3fe691b1a52be49dc0da602b7e6a9812da57c7 Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Tue, 6 Dec 2016 23:31:07 -0500 Subject: [PATCH 49/89] Updated python-nest dependency (#4785) * Updated python-nest dependency This sha fixes two issues: - min and max temperatures not being set when temperature isn't locked - fixes error when setting farenheit with a .5 * gen requirements all * Add fix for https://github.com/home-assistant/home-assistant/issues/4731 --- homeassistant/components/climate/nest.py | 14 ++++++-------- homeassistant/components/nest.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/climate/nest.py b/homeassistant/components/climate/nest.py index dbc68162579..ab41c10e6d1 100644 --- a/homeassistant/components/climate/nest.py +++ b/homeassistant/components/climate/nest.py @@ -86,6 +86,8 @@ class NestThermostat(ClimateDevice): self._eco_temperature = None self._is_locked = None self._locked_temperature = None + self._min_temperature = None + self._max_temperature = None @property def name(self): @@ -204,18 +206,12 @@ class NestThermostat(ClimateDevice): @property def min_temp(self): """Identify min_temp in Nest API or defaults if not available.""" - if self._is_locked: - return self._locked_temperature[0] - else: - return None + return self._min_temperature @property def max_temp(self): """Identify max_temp in Nest API or defaults if not available.""" - if self._is_locked: - return self._locked_temperature[1] - else: - return None + return self._max_temperature def update(self): """Cache value from Python-nest.""" @@ -229,6 +225,8 @@ class NestThermostat(ClimateDevice): self._away = self.structure.away == 'away' self._eco_temperature = self.device.eco_temperature self._locked_temperature = self.device.locked_temperature + self._min_temperature = self.device.min_temperature + self._max_temperature = self.device.max_temperature self._is_locked = self.device.is_locked if self.device.temperature_scale == 'C': self._temperature_scale = TEMP_CELSIUS diff --git a/homeassistant/components/nest.py b/homeassistant/components/nest.py index e19011c47b8..0952129c439 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [ 'http://github.com/technicalpickles/python-nest' - '/archive/2512973b4b390d3965da43529cd20402ad374bfa.zip' # nest-cam branch + '/archive/dd628f90772d170b9602f262d5d2e7d61bdd3cf5.zip' # nest-cam branch '#python-nest==3.0.0'] DOMAIN = 'nest' diff --git a/requirements_all.txt b/requirements_all.txt index 6f1ca5eb4e1..bcc9836fdc9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -167,7 +167,7 @@ hikvision==0.4 # http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0 # homeassistant.components.nest -http://github.com/technicalpickles/python-nest/archive/2512973b4b390d3965da43529cd20402ad374bfa.zip#python-nest==3.0.0 +http://github.com/technicalpickles/python-nest/archive/dd628f90772d170b9602f262d5d2e7d61bdd3cf5.zip#python-nest==3.0.0 # homeassistant.components.light.flux_led https://github.com/Danielhiversen/flux_led/archive/0.9.zip#flux_led==0.9 From c40ddf18c79e2149c95a0e9e649869ecd6a80071 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Wed, 7 Dec 2016 00:03:49 -0500 Subject: [PATCH 50/89] Fix incorrect caching of /api/error_log (#4789) --- homeassistant/components/http/__init__.py | 4 ++-- homeassistant/components/http/static.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 11a9e755bb4..de864a0c193 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -32,7 +32,7 @@ from .const import ( KEY_USE_X_FORWARDED_FOR, KEY_TRUSTED_NETWORKS, KEY_BANS_ENABLED, KEY_LOGIN_THRESHOLD, KEY_DEVELOPMENT, KEY_AUTHENTICATED) -from .static import GZIP_FILE_SENDER, staticresource_middleware +from .static import FILE_SENDER, GZIP_FILE_SENDER, staticresource_middleware from .util import get_real_ip DOMAIN = 'http' @@ -344,7 +344,7 @@ class HomeAssistantView(object): def file(self, request, fil): """Return a file.""" assert isinstance(fil, str), 'only string paths allowed' - response = yield from GZIP_FILE_SENDER.send(request, Path(fil)) + response = yield from FILE_SENDER.send(request, Path(fil)) return response def register(self, router): diff --git a/homeassistant/components/http/static.py b/homeassistant/components/http/static.py index c8c55870e0f..0bd68d6136e 100644 --- a/homeassistant/components/http/static.py +++ b/homeassistant/components/http/static.py @@ -63,6 +63,7 @@ class GzipFileSender(FileSender): GZIP_FILE_SENDER = GzipFileSender() +FILE_SENDER = FileSender() @asyncio.coroutine From 76b79019ce8b7535d5a22d472356b9ee68fbddfa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 6 Dec 2016 21:47:58 -0800 Subject: [PATCH 51/89] Add faster reviews link to contributing --- CONTRIBUTING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8621851ffb6..3dbc2a7022c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,14 @@ # Contributing to Home Assistant -Everybody is invited and welcome to contribute to Home Assistant. There is a lot to do...if you are not a developer perhaps you would like to help with the documentation on [home-assistant.io](https://home-assistant.io/)? If you are a developer and have devices in your home which aren't working with Home Assistant yet, why not spent a couple of hours and help to integrate them? +Everybody is invited and welcome to contribute to Home Assistant. There is a lot to do...if you are not a developer perhaps you would like to help with the documentation on [home-assistant.io](https://home-assistant.io/)? If you are a developer and have devices in your home which aren't working with Home Assistant yet, why not spent a couple of hours and help to integrate them? The process is straight-forward. + - Read [How to get faster PR reviews](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md) by Kubernetes - Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant). - Write the code for your device, notification service, sensor, or IoT thing. - Ensure tests work. - Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant. -Still interested? Then you should take a peak at the [developer documentation](https://home-assistant.io/developers/) to get more details. +Still interested? Then you should take a peak at the [developer documentation](https://home-assistant.io/developers/) to get more details. From 37e3c2a1336a0ae31bde9356f0a7cb7c06618081 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 6 Dec 2016 21:48:44 -0800 Subject: [PATCH 52/89] Contributing: add skip step 0 --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3dbc2a7022c..a63c1400723 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Everybody is invited and welcome to contribute to Home Assistant. There is a lot The process is straight-forward. - - Read [How to get faster PR reviews](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md) by Kubernetes + - Read [How to get faster PR reviews](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md) by Kubernetes (but skip step 0) - Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant). - Write the code for your device, notification service, sensor, or IoT thing. - Ensure tests work. From 98fe50d5adea8a4322641ed5c23a745065a8766c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 6 Dec 2016 22:30:47 -0800 Subject: [PATCH 53/89] Update after service calls (#4795) * Update after service calls * Service update: wrap async_update in create_task --- .../alarm_control_panel/__init__.py | 8 ++++++-- homeassistant/components/cover/__init__.py | 17 ++++++++++++----- homeassistant/components/light/__init__.py | 19 ++++++++++++------- homeassistant/components/lock/__init__.py | 7 +++++-- homeassistant/components/remote/__init__.py | 18 +++++++++++------- homeassistant/components/switch/__init__.py | 18 +++++++++++------- 6 files changed, 57 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 2030c8f88d8..49decfc62fe 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -59,8 +59,12 @@ def setup(hass, config): for alarm in target_alarms: getattr(alarm, method)(code) - if alarm.should_poll: - alarm.update_ha_state(True) + + for alarm in target_alarms: + if not alarm.should_poll: + continue + + alarm.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index db517aec978..6c268e49be6 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -135,12 +135,19 @@ def setup(hass, config): params = service.data.copy() params.pop(ATTR_ENTITY_ID, None) - if method: - for cover in component.extract_from_service(service): - getattr(cover, method['method'])(**params) + if not method: + return - if cover.should_poll: - cover.update_ha_state(True) + covers = component.extract_from_service(service) + + for cover in covers: + getattr(cover, method['method'])(**params) + + for cover in covers: + if not cover.should_poll: + continue + + cover.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 04eb8fabc68..869bbd90e7d 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -236,7 +236,6 @@ def async_setup(hass, config): if color_name is not None: params[ATTR_RGB_COLOR] = color_util.color_name_to_rgb(color_name) - update_tasks = [] for light in target_lights: if service.service == SERVICE_TURN_ON: yield from light.async_turn_on(**params) @@ -245,12 +244,18 @@ def async_setup(hass, config): else: yield from light.async_toggle(**params) - if light.should_poll: - update_coro = light.async_update_ha_state(True) - if hasattr(light, 'async_update'): - update_tasks.append(hass.loop.create_task(update_coro)) - else: - yield from update_coro + update_tasks = [] + + for light in target_lights: + if not light.should_poll: + continue + + update_coro = hass.loop.create_task( + light.async_update_ha_state(True)) + if hasattr(light, 'async_update'): + update_tasks.append(hass.loop.create_task(update_coro)) + else: + yield from update_coro if update_tasks: yield from asyncio.wait(update_tasks, loop=hass.loop) diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 95db9d2b33a..e74b675733b 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -85,8 +85,11 @@ def setup(hass, config): else: item.unlock(code=code) - if item.should_poll: - item.update_ha_state(True) + for item in target_locks: + if not item.should_poll: + continue + + item.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index d6f534eae5b..3a481e83830 100755 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -98,7 +98,6 @@ def async_setup(hass, config): device = service.data.get(ATTR_DEVICE) command = service.data.get(ATTR_COMMAND) - update_tasks = [] for remote in target_remotes: if service.service == SERVICE_TURN_ON: yield from remote.async_turn_on(activity=activity_id) @@ -108,12 +107,17 @@ def async_setup(hass, config): else: yield from remote.async_turn_off() - if remote.should_poll: - update_coro = remote.async_update_ha_state(True) - if hasattr(remote, 'async_update'): - update_tasks.append(hass.loop.create_task(update_coro)) - else: - yield from update_coro + update_tasks = [] + for remote in target_remotes: + if not remote.should_poll: + continue + + update_coro = hass.loop.create_task( + remote.async_update_ha_state(True)) + if hasattr(remote, 'async_update'): + update_tasks.append(hass.loop.create_task(update_coro)) + else: + yield from update_coro if update_tasks: yield from asyncio.wait(update_tasks, loop=hass.loop) diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index 02a313c675f..846a87f5067 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -82,7 +82,6 @@ def async_setup(hass, config): """Handle calls to the switch services.""" target_switches = component.async_extract_from_service(service) - update_tasks = [] for switch in target_switches: if service.service == SERVICE_TURN_ON: yield from switch.async_turn_on() @@ -91,12 +90,17 @@ def async_setup(hass, config): else: yield from switch.async_turn_off() - if switch.should_poll: - update_coro = switch.async_update_ha_state(True) - if hasattr(switch, 'async_update'): - update_tasks.append(hass.loop.create_task(update_coro)) - else: - yield from update_coro + update_tasks = [] + for switch in target_switches: + if not switch.should_poll: + continue + + update_coro = hass.loop.create_task( + switch.async_update_ha_state(True)) + if hasattr(switch, 'async_update'): + update_tasks.append(hass.loop.create_task(update_coro)) + else: + yield from update_coro if update_tasks: yield from asyncio.wait(update_tasks, loop=hass.loop) From 82ad8b0a8fbc978693ef63c4a4ad506f353088fa Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 7 Dec 2016 03:15:48 -0500 Subject: [PATCH 54/89] round $ to 2 decimals (#4786) --- homeassistant/components/sensor/coinmarketcap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/coinmarketcap.py b/homeassistant/components/sensor/coinmarketcap.py index 61545fa3944..e041352af58 100644 --- a/homeassistant/components/sensor/coinmarketcap.py +++ b/homeassistant/components/sensor/coinmarketcap.py @@ -77,7 +77,7 @@ class CoinMarketCapSensor(Entity): @property def state(self): """Return the state of the sensor.""" - return self._ticker.get('price_usd') + return round(self._ticker.get('price_usd'), 2) @property def unit_of_measurement(self): From d0dcd1bb732c2b260c6fe7fe96d5ff0d2fbb2aee Mon Sep 17 00:00:00 2001 From: Jan Losinski Date: Wed, 7 Dec 2016 14:33:41 +0100 Subject: [PATCH 55/89] Scene: add support for input_select (#4674) This adds support for the scene component to handle input_select devices and set their options. This fixes bug #4673 Signed-off-by: Jan Losinski --- homeassistant/const.py | 4 ++++ homeassistant/helpers/state.py | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d9191eaedf2..0a014c0b603 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -278,6 +278,8 @@ ATTR_GPS_ACCURACY = 'gps_accuracy' ATTR_ASSUMED_STATE = 'assumed_state' ATTR_STATE = 'state' +ATTR_OPTION = 'option' + # #### SERVICES #### SERVICE_HOMEASSISTANT_STOP = 'stop' SERVICE_HOMEASSISTANT_RESTART = 'restart' @@ -318,6 +320,8 @@ SERVICE_SET_COVER_TILT_POSITION = 'set_cover_tilt_position' SERVICE_STOP_COVER = 'stop_cover' SERVICE_STOP_COVER_TILT = 'stop_cover_tilt' +SERVICE_SELECT_OPTION = 'select_option' + # #### API / REMOTE #### SERVER_PORT = 8123 diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 9980ad11a8d..46b7837dc15 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -32,7 +32,7 @@ from homeassistant.const import ( SERVICE_CLOSE_COVER, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_CLOSED, STATE_LOCKED, STATE_OFF, STATE_ON, STATE_OPEN, STATE_PAUSED, STATE_PLAYING, - STATE_UNKNOWN, STATE_UNLOCKED) + STATE_UNKNOWN, STATE_UNLOCKED, SERVICE_SELECT_OPTION, ATTR_OPTION) from homeassistant.core import State from homeassistant.util.async import run_coroutine_threadsafe @@ -58,7 +58,8 @@ SERVICE_ATTRIBUTES = { SERVICE_SET_OPERATION_MODE: [ATTR_OPERATION_MODE], SERVICE_SET_AUX_HEAT: [ATTR_AUX_HEAT], SERVICE_SELECT_SOURCE: [ATTR_INPUT_SOURCE], - SERVICE_SEND_IR_CODE: [ATTR_IR_CODE] + SERVICE_SEND_IR_CODE: [ATTR_IR_CODE], + SERVICE_SELECT_OPTION: [ATTR_OPTION] } # Update this dict when new services are added to HA. From 8295fc8b4c8098931fba3822ce7e302d505f4e64 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 7 Dec 2016 17:37:35 +0100 Subject: [PATCH 56/89] Pararell execute state restore by domain (#4801) * Pararell execute state restore per domain * fix spell --- homeassistant/helpers/state.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 46b7837dc15..536975c100e 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -164,11 +164,28 @@ def async_reproduce_state(hass, states, blocking=False): json.dumps(dict(state.attributes), sort_keys=True)) to_call[key].append(state.entity_id) + domain_tasks = {} for (service_domain, service, service_data), entity_ids in to_call.items(): data = json.loads(service_data) data[ATTR_ENTITY_ID] = entity_ids - yield from hass.services.async_call( - service_domain, service, data, blocking) + + if service_domain not in domain_tasks: + domain_tasks[service_domain] = [] + + domain_tasks[service_domain].append( + hass.services.async_call(service_domain, service, data, blocking) + ) + + @asyncio.coroutine + def async_handle_service_calls(coro_list): + """Handle service calls by domain sequence.""" + for coro in coro_list: + yield from coro + + execute_tasks = [async_handle_service_calls(coro_list) + for coro_list in domain_tasks.values()] + if execute_tasks: + yield from asyncio.wait(execute_tasks, loop=hass.loop) def state_as_number(state): From 194b268ae3938b1e80cbac5b984815a5e93f823d Mon Sep 17 00:00:00 2001 From: Keaton Taylor Date: Wed, 7 Dec 2016 23:45:18 -0600 Subject: [PATCH 57/89] Get entity name from entity.name (#4798) Grabbing the ATTR_FRIENDLY_NAME directly produces an error. Instead grab from entity.name. --- homeassistant/components/emulated_hue/hue_api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index ed06da9495b..32fb4af071c 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -6,8 +6,8 @@ from aiohttp import web from homeassistant import core from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, SERVICE_TURN_OFF, SERVICE_TURN_ON, - STATE_ON, STATE_OFF, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, + ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON, + STATE_OFF, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, ) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_SUPPORTED_FEATURES, SUPPORT_BRIGHTNESS @@ -251,8 +251,7 @@ def entity_to_json(entity, is_on=None, brightness=None): if brightness is None: brightness = 255 if is_on else 0 - name = entity.attributes.get( - ATTR_EMULATED_HUE_NAME, entity.attributes[ATTR_FRIENDLY_NAME]) + name = entity.attributes.get(ATTR_EMULATED_HUE_NAME, entity.name) return { 'state': From 695f062e298cf58ce81ee3c40d0dea837ad6fbef Mon Sep 17 00:00:00 2001 From: R1chardTM Date: Thu, 8 Dec 2016 06:45:43 +0100 Subject: [PATCH 58/89] Fix python-nest version bump (#4799) * Fix python-nest version bump * Change SHA so version in HASS and dependency are the same --- homeassistant/components/nest.py | 4 ++-- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest.py b/homeassistant/components/nest.py index 0952129c439..fd5627987a3 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -19,8 +19,8 @@ _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [ 'http://github.com/technicalpickles/python-nest' - '/archive/dd628f90772d170b9602f262d5d2e7d61bdd3cf5.zip' # nest-cam branch - '#python-nest==3.0.0'] + '/archive/7a2eb38d391bddeb78079437f001224c370b555a.zip' # nest-cam branch + '#python-nest==3.0.1'] DOMAIN = 'nest' diff --git a/requirements_all.txt b/requirements_all.txt index bcc9836fdc9..27573a2c587 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -167,7 +167,7 @@ hikvision==0.4 # http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0 # homeassistant.components.nest -http://github.com/technicalpickles/python-nest/archive/dd628f90772d170b9602f262d5d2e7d61bdd3cf5.zip#python-nest==3.0.0 +http://github.com/technicalpickles/python-nest/archive/7a2eb38d391bddeb78079437f001224c370b555a.zip#python-nest==3.0.1 # homeassistant.components.light.flux_led https://github.com/Danielhiversen/flux_led/archive/0.9.zip#flux_led==0.9 From 2e2b764dbee5ef598d9ff6907925de94b78831e2 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 7 Dec 2016 23:46:42 -0600 Subject: [PATCH 59/89] Add exception handling when turning on Onkyo receivers (#4813) --- homeassistant/components/media_player/onkyo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/onkyo.py b/homeassistant/components/media_player/onkyo.py index 5829d119f95..28f8dd1bf6b 100644 --- a/homeassistant/components/media_player/onkyo.py +++ b/homeassistant/components/media_player/onkyo.py @@ -89,7 +89,7 @@ class OnkyoDevice(MediaPlayerDevice): except (ValueError, OSError, AttributeError, AssertionError): if self._receiver.command_socket: self._receiver.command_socket = None - _LOGGER.info('Reseting connection to %s.', self._name) + _LOGGER.info('Resetting connection to %s.', self._name) else: _LOGGER.info('%s is disconnected. Attempting to reconnect.', self._name) @@ -173,7 +173,7 @@ class OnkyoDevice(MediaPlayerDevice): def turn_on(self): """Turn the media player on.""" - self._receiver.power_on() + self.command('system-power on') def select_source(self, source): """Set the input source.""" From 14446c5731e788c67af819234cf158cc9f3c6f16 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Thu, 8 Dec 2016 08:36:37 +0000 Subject: [PATCH 60/89] [media_player.sonos] Add stop support. (#4788) --- .../components/media_player/sonos.py | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 1fa1a39633d..c6e9c1e6655 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -15,7 +15,7 @@ from homeassistant.components.media_player import ( ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_CLEAR_PLAYLIST, - SUPPORT_SELECT_SOURCE, MediaPlayerDevice, PLATFORM_SCHEMA) + SUPPORT_SELECT_SOURCE, MediaPlayerDevice, PLATFORM_SCHEMA, SUPPORT_STOP) from homeassistant.const import ( STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_OFF, ATTR_ENTITY_ID, CONF_HOSTS) @@ -36,9 +36,10 @@ _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_PLAY_MEDIA |\ - SUPPORT_SEEK | SUPPORT_CLEAR_PLAYLIST | SUPPORT_SELECT_SOURCE +SUPPORT_SONOS = SUPPORT_STOP | SUPPORT_PAUSE | SUPPORT_VOLUME_SET |\ + SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK |\ + SUPPORT_PLAY_MEDIA | SUPPORT_SEEK | SUPPORT_CLEAR_PLAYLIST |\ + SUPPORT_SELECT_SOURCE SERVICE_GROUP_PLAYERS = 'sonos_group_players' SERVICE_UNJOIN = 'sonos_unjoin' @@ -289,6 +290,7 @@ class SonosDevice(MediaPlayerDevice): self._media_next_title = None self._support_previous_track = False self._support_next_track = False + self._support_stop = False self._support_pause = False self._current_track_uri = None self._current_track_is_radio_stream = False @@ -433,6 +435,7 @@ class SonosDevice(MediaPlayerDevice): support_previous_track = False support_next_track = False + support_stop = False support_pause = False if is_playing_tv: @@ -450,6 +453,7 @@ class SonosDevice(MediaPlayerDevice): ) support_previous_track = False support_next_track = False + support_stop = False support_pause = False # for radio streams we set the radio station name as the @@ -506,6 +510,7 @@ class SonosDevice(MediaPlayerDevice): ) support_previous_track = True support_next_track = True + support_stop = True support_pause = True position_info = self._player.avTransport.GetPositionInfo( @@ -583,6 +588,7 @@ class SonosDevice(MediaPlayerDevice): self._current_track_is_radio_stream = is_radio_stream self._support_previous_track = support_previous_track self._support_next_track = support_next_track + self._support_stop = support_stop self._support_pause = support_pause self._is_playing_tv = is_playing_tv self._is_playing_line_in = is_playing_line_in @@ -614,6 +620,7 @@ class SonosDevice(MediaPlayerDevice): self._current_track_is_radio_stream = False self._support_previous_track = False self._support_next_track = False + self._support_stop = False self._support_pause = False self._is_playing_tv = False self._is_playing_line_in = False @@ -774,6 +781,9 @@ class SonosDevice(MediaPlayerDevice): if not self._support_next_track: supported = supported ^ SUPPORT_NEXT_TRACK + if not self._support_stop: + supported = supported ^ SUPPORT_STOP + if not self._support_pause: supported = supported ^ SUPPORT_PAUSE @@ -836,6 +846,13 @@ class SonosDevice(MediaPlayerDevice): else: self._player.play() + def media_stop(self): + """Send stop command.""" + if self._coordinator: + self._coordinator.media_stop() + else: + self._player.stop() + def media_pause(self): """Send pause command.""" if self._coordinator: From c90a1b97605648f9855926b86b147ec4347151c4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Dec 2016 22:31:23 -0800 Subject: [PATCH 61/89] Update frontend --- homeassistant/components/frontend/version.py | 6 +- .../frontend/www_static/frontend.html | 4 +- .../frontend/www_static/frontend.html.gz | Bin 130476 -> 130643 bytes .../www_static/home-assistant-polymer | 2 +- .../panels/ha-panel-dev-service.html | 2 +- .../panels/ha-panel-dev-service.html.gz | Bin 17702 -> 17764 bytes .../www_static/panels/ha-panel-map.html | 184 +++++++++++++++++- .../www_static/panels/ha-panel-map.html.gz | Bin 42075 -> 43807 bytes .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 2326 -> 2323 bytes .../www_static/webcomponents-lite.min.js | 6 +- .../www_static/webcomponents-lite.min.js.gz | Bin 12355 -> 12360 bytes 12 files changed, 191 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 14650b47cb7..e6211c145e2 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -2,17 +2,17 @@ FINGERPRINTS = { "core.js": "5dfb2d3e567fad37af0321d4b29265ed", - "frontend.html": "d4f164e559944b8abc560d7b46131714", + "frontend.html": "ac15b11435132aab3da592f9e7b05400", "mdi.html": "46a76f877ac9848899b8ed382427c16f", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "panels/ha-panel-dev-event.html": "c2d5ec676be98d4474d19f94d0262c1e", "panels/ha-panel-dev-info.html": "a9c07bf281fe9791fb15827ec1286825", - "panels/ha-panel-dev-service.html": "b3fe49532c5c03198fafb0c6ed58b76a", + "panels/ha-panel-dev-service.html": "20420e2387fd93db53c8d778097e3d59", "panels/ha-panel-dev-state.html": "65e5f791cc467561719bf591f1386054", "panels/ha-panel-dev-template.html": "7d744ab7f7c08b6d6ad42069989de400", "panels/ha-panel-history.html": "efe1bcdd7733b09e55f4f965d171c295", "panels/ha-panel-iframe.html": "d920f0aa3c903680f2f8795e2255daab", "panels/ha-panel-logbook.html": "4bc5c8370a85a4215413fbae8f85addb", - "panels/ha-panel-map.html": "1bf6965b24d76db71a1871865cd4a3a2", + "panels/ha-panel-map.html": "3b0ca63286cbe80f27bd36dbc2434e89", "websocket_test.html": "575de64b431fe11c3785bf96d7813450" } diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 54b9cc4d5e4..dd0eba180ec 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,5 +1,5 @@

\ No newline at end of file +},customStyle:null,getComputedStyleValue:function(e){return!i&&this._styleProperties&&this._styleProperties[e]||getComputedStyle(this).getPropertyValue(e)},_setupStyleProperties:function(){this.customStyle={},this._styleCache=null,this._styleProperties=null,this._scopeSelector=null,this._ownStyleProperties=null,this._customStyle=null},_needsStyleProperties:function(){return Boolean(!i&&this._ownStylePropertyNames&&this._ownStylePropertyNames.length)},_validateApplyShim:function(){if(this.__applyShimInvalid){Polymer.ApplyShim.transform(this._styles,this.__proto__);var e=n.elementStyles(this);if(s){var t=this._template.content.querySelector("style");t&&(t.textContent=e)}else{var r=this._scopeStyle&&this._scopeStyle.nextSibling;r&&(r.textContent=e)}}},_beforeAttached:function(){this._scopeSelector&&!this.__stylePropertiesInvalid||!this._needsStyleProperties()||(this.__stylePropertiesInvalid=!1,this._updateStyleProperties())},_findStyleHost:function(){for(var e,t=this;e=Polymer.dom(t).getOwnerRoot();){if(Polymer.isInstance(e.host))return e.host;t=e.host}return r},_updateStyleProperties:function(){var e,n=this._findStyleHost();n._styleProperties||n._computeStyleProperties(),n._styleCache||(n._styleCache=new Polymer.StyleCache);var r=t.propertyDataFromStyles(n._styles,this),i=!this.__notStyleScopeCacheable;i&&(r.key.customStyle=this.customStyle,e=n._styleCache.retrieve(this.is,r.key,this._styles));var a=Boolean(e);a?this._styleProperties=e._styleProperties:this._computeStyleProperties(r.properties),this._computeOwnStyleProperties(),a||(e=o.retrieve(this.is,this._ownStyleProperties,this._styles));var l=Boolean(e)&&!a,c=this._applyStyleProperties(e);a||(c=c&&s?c.cloneNode(!0):c,e={style:c,_scopeSelector:this._scopeSelector,_styleProperties:this._styleProperties},i&&(r.key.customStyle={},this.mixin(r.key.customStyle,this.customStyle),n._styleCache.store(this.is,e,r.key,this._styles)),l||o.store(this.is,Object.create(e),this._ownStyleProperties,this._styles))},_computeStyleProperties:function(e){var n=this._findStyleHost();n._styleProperties||n._computeStyleProperties();var r=Object.create(n._styleProperties),s=t.hostAndRootPropertiesForScope(this);this.mixin(r,s.hostProps),e=e||t.propertyDataFromStyles(n._styles,this).properties,this.mixin(r,e),this.mixin(r,s.rootProps),t.mixinCustomStyle(r,this.customStyle),t.reify(r),this._styleProperties=r},_computeOwnStyleProperties:function(){for(var e,t={},n=0;n0&&l.push(t);return[{removed:a,added:l}]}},Polymer.Collection.get=function(e){return Polymer._collections.get(e)||new Polymer.Collection(e)},Polymer.Collection.applySplices=function(e,t){var n=Polymer._collections.get(e);return n?n._applySplices(t):null},Polymer({is:"dom-repeat",extends:"template",_template:null,properties:{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},sort:{type:Function,observer:"_sortChanged"},filter:{type:Function,observer:"_filterChanged"},observe:{type:String,observer:"_observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number,observer:"_initializeChunking"},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"_computeFrameTime(targetFramerate)"}},behaviors:[Polymer.Templatizer],observers:["_itemsChanged(items.*)"],created:function(){this._instances=[],this._pool=[],this._limit=1/0;var e=this;this._boundRenderChunk=function(){e._renderChunk()}},detached:function(){this.__isDetached=!0;for(var e=0;e=0;t--){var n=this._instances[t];n.isPlaceholder&&t=this._limit&&(n=this._downgradeInstance(t,n.__key__)),e[n.__key__]=t,n.isPlaceholder||n.__setProperty(this.indexAs,t,!0)}this._pool.length=0,this._setRenderedItemCount(this._instances.length),this.fire("dom-change"),this._tryRenderChunk()},_applyFullRefresh:function(){var e,t=this.collection;if(this._sortFn)e=t?t.getKeys():[];else{e=[];var n=this.items;if(n)for(var r=0;r=r;a--)this._detachAndRemoveInstance(a)},_numericSort:function(e,t){return e-t},_applySplicesUserSort:function(e){for(var t,n,r=this.collection,s={},i=0;i=0;i--){var c=a[i];void 0!==c&&this._detachAndRemoveInstance(c)}var h=this;if(l.length){this._filterFn&&(l=l.filter(function(e){return h._filterFn(r.getItem(e))})),l.sort(function(e,t){return h._sortFn(r.getItem(e),r.getItem(t))});var u=0;for(i=0;i>1,a=this._instances[o].__key__,l=this._sortFn(n.getItem(a),r);if(l<0)e=o+1;else{if(!(l>0)){i=o;break}s=o-1}}return i<0&&(i=s+1),this._insertPlaceholder(i,t),i},_applySplicesArrayOrder:function(e){for(var t,n=0;n=0?(e=this.as+"."+e.substring(n+1),i._notifyPath(e,t,!0)):i.__setProperty(this.as,t,!0))}},itemForElement:function(e){var t=this.modelForElement(e);return t&&t[this.as]},keyForElement:function(e){var t=this.modelForElement(e);return t&&t.__key__},indexForElement:function(e){var t=this.modelForElement(e);return t&&t[this.indexAs]}}),Polymer({is:"array-selector",_template:null,properties:{items:{type:Array,observer:"clearSelection"},multi:{type:Boolean,value:!1,observer:"clearSelection"},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}},clearSelection:function(){if(Array.isArray(this.selected))for(var e=0;e \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/frontend.html.gz b/homeassistant/components/frontend/www_static/frontend.html.gz index 3bf5bab1b5944da1c6375064d0b6d982ab667b5d..b1488e61a11ee39c694803a710a4e6b8eea2e090 100644 GIT binary patch delta 101571 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 Du3c!6 delta 101406 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�WS=#HPIF-l*j9%A-Sy$Jx1|LGCy#pgr4+> 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 diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 4697af1b0d9..336e974fe6f 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 4697af1b0d96e7a21454539888c68189668fa80d +Subproject commit 336e974fe6f4196aaf0b309e805a009ef0fdfd66 diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html index 51805ebd91d..e4fd1211753 100644 --- a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html @@ -1 +1 @@ -

\ No newline at end of file + \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-dev-service.html.gz index f6c74234e4d56e43ca9be97211a2b65756a5e07c..2419c25adbd0c227748e2991b75bdd3295e45bd4 100644 GIT binary patch delta 12168 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 delta 12105 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 diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-map.html b/homeassistant/components/frontend/www_static/panels/ha-panel-map.html index d63acba7549..38dd2b6e961 100644 --- a/homeassistant/components/frontend/www_static/panels/ha-panel-map.html +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-map.html @@ -1,4 +1,180 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-map.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-map.html.gz index e153a5290fbbb505b414fb4b8b7b05b3455437f5..56a98ed3431887e2fdb5c9f724ac501f8244834b 100644 GIT binary patch literal 43807 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 literal 42075 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 diff --git a/homeassistant/components/frontend/www_static/service_worker.js b/homeassistant/components/frontend/www_static/service_worker.js index b125336669c..d2ef4b42439 100644 --- a/homeassistant/components/frontend/www_static/service_worker.js +++ b/homeassistant/components/frontend/www_static/service_worker.js @@ -1 +1 @@ -"use strict";function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}function notificationEventCallback(e,t){firePushCallback({action:t.action,data:t.notification.data,tag:t.notification.tag,type:e},t.notification.data.jwt)}function firePushCallback(e,t){delete e.data.jwt,0===Object.keys(e.data).length&&e.data.constructor===Object&&delete e.data,fetch("/api/notify.html5/callback",{method:"POST",headers:new Headers({"Content-Type":"application/json",Authorization:"Bearer "+t}),body:JSON.stringify(e)})}var precacheConfig=[["/","e14330e71c13caf3a8ad2ec699e46047"],["/frontend/panels/dev-event-c2d5ec676be98d4474d19f94d0262c1e.html","6c55fc819751923ab00c62ae3fbb7222"],["/frontend/panels/dev-info-a9c07bf281fe9791fb15827ec1286825.html","931f9327e368db710fcdf5f7202f2588"],["/frontend/panels/dev-service-b3fe49532c5c03198fafb0c6ed58b76a.html","4194cb43b74108dc6d10354da2fd81fd"],["/frontend/panels/dev-state-65e5f791cc467561719bf591f1386054.html","78158786a6597ef86c3fd6f4985cde92"],["/frontend/panels/dev-template-7d744ab7f7c08b6d6ad42069989de400.html","8a6ee994b1cdb45b081299b8609915ed"],["/frontend/panels/map-1bf6965b24d76db71a1871865cd4a3a2.html","a74c01c2ee68c83c9938af067ec33b81"],["/static/core-5dfb2d3e567fad37af0321d4b29265ed.js","9a50270db7613e3af449f6c773366f4d"],["/static/frontend-d4f164e559944b8abc560d7b46131714.html","fd803353b0ff1844aa4677b8a0f79fea"],["/static/mdi-46a76f877ac9848899b8ed382427c16f.html","a846c4082dd5cffd88ac72cbe943e691"],["static/fonts/roboto/Roboto-Bold.ttf","d329cc8b34667f114a95422aaad1b063"],["static/fonts/roboto/Roboto-Light.ttf","7b5fb88f12bec8143f00e21bc3222124"],["static/fonts/roboto/Roboto-Medium.ttf","fe13e4170719c2fc586501e777bde143"],["static/fonts/roboto/Roboto-Regular.ttf","ac3f799d5bbaf5196fab15ab8de8431c"],["static/icons/favicon-192x192.png","419903b8422586a7e28021bbe9011175"],["static/icons/favicon.ico","04235bda7843ec2fceb1cbe2bc696cf4"],["static/images/card_media_player_bg.png","a34281d1c1835d338a642e90930e61aa"],["static/webcomponents-lite.min.js","b0f32ad3c7749c40d486603f31c9d8b1"]],cacheName="sw-precache-v2--"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var n=new URL(e);return"/"===n.pathname.slice(-1)&&(n.pathname+=t),n.toString()},createCacheKey=function(e,t,n,a){var c=new URL(e);return a&&c.toString().match(a)||(c.search+=(c.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(n)),c.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var n=new URL(t).pathname;return e.some(function(e){return n.match(e)})},stripIgnoredUrlParameters=function(e,t){var n=new URL(e);return n.search=n.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(e){return t.every(function(t){return!t.test(e[0])})}).map(function(e){return e.join("=")}).join("&"),n.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],n=e[1],a=new URL(t,self.location),c=createCacheKey(a,hashParamName,n,!1);return[a.toString(),c]}));self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(e){return setOfCachedUrls(e).then(function(t){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(n){if(!t.has(n))return e.add(new Request(n,{credentials:"same-origin"}))}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var t=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(e){return e.keys().then(function(n){return Promise.all(n.map(function(n){if(!t.has(n.url))return e.delete(n)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(e){if("GET"===e.request.method){var t,n=stripIgnoredUrlParameters(e.request.url,ignoreUrlParametersMatching);t=urlsToCacheKeys.has(n);var a="index.html";!t&&a&&(n=addDirectoryIndex(n,a),t=urlsToCacheKeys.has(n));var c="/";!t&&c&&"navigate"===e.request.mode&&isPathWhitelisted(["^((?!(static|api|local|service_worker.js|manifest.json)).)*$"],e.request.url)&&(n=new URL(c,self.location).toString(),t=urlsToCacheKeys.has(n)),t&&e.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(n)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(t){return console.warn('Couldn\'t serve response for "%s" from cache: %O',e.request.url,t),fetch(e.request)}))}}),self.addEventListener("push",function(e){var t;e.data&&(t=e.data.json(),e.waitUntil(self.registration.showNotification(t.title,t).then(function(e){firePushCallback({type:"received",tag:t.tag,data:t.data},t.data.jwt)})))}),self.addEventListener("notificationclick",function(e){var t;notificationEventCallback("clicked",e),e.notification.close(),e.notification.data&&e.notification.data.url&&(t=e.notification.data.url,t&&e.waitUntil(clients.matchAll({type:"window"}).then(function(e){var n,a;for(n=0;nstP3wArnM37nKG@r^ z;LGDLR!w>9-Jbrmg6qM1Ft~!-wzgEYDRd#Z{m=6&sLV57mV(Q#F8Id#u8RiVuG;x_ z^*isrJ2yf9;45x8{%==KVAp)ZXZP1}$u~DkIEP#R$S1*Y$F z;dDBEBffxtG(CF#D=#Ranwj}$D=%3y1&a=8Qh~?-5I_sP}e7P*wj*h>y zb>;X^S16<@{@bmcJHG;Npcy!4=GOB?t!~c$`Tos+>%eACHA5@1jPhrRx!A zGR%a3ra95TGDfr@Nlr76gyv~ZlZ_e{5zQie5v92j840yiI?)*osisMupH|aCb5%$f z(J-bV&?wKeVv!O_By*KzdB{+xO4wEt(?l`|nGR_f3Wa*JI97^Aie;(d4`?#nz$i@s z?P5gAIL(rjWP}Nwpl2k?(=drQda@in&GM9grwLl1^HfG!r8;JLA{DR)fHSaImR41U z@r=31bSA@Gq$=eKeM}k4nSwYBH>z@;0x%W}B9({}5$1$4CeR$lNCN7(Dr_sGNQ9E7 z5ynCa$U{vtp`ug?N#iJnd<&YQ6aa{fq8zsZ3rU_J0xC+Rh@*`L3s51)vTk6MC@rXe ziXcfd%~h0f9b%HISWre0LKS>zZ7_He(k#S?rzC=i>o{gQm01==DPpxRcjLf_OOgme z;wT{z)OVv1gIMMRiJKNIz`n9a>vRwfcH@>~-t082KGbchu} zgp9CI35|ay>lLVCwO9+&fDwsF7Gi=+s%4U=Nk|~eFhu}m|4i6Bn5|0Qtfg^(%>Ilq zl?cH#c9K+c>;zoo3i3E2a#vbm`}SD#EBi3QuJaB52Fq%Oa$>?*7>OJqLW6h)G!M~3 zVN;TjEIBDBz<+GpjA@i$B4nr)tXqIZBY+B-vQ+9lh>8WDL5oDtsLKUH!!NNcZ=ksp zv$Zumim|gPB1s-43X201jG`%jED8Y|GvAeV4MNt7WnE$SZAWEckl2f=FM}N0U!s)M>9r8&Viu(b32`b6Y5N(7>!f0_hm$$-zp{ z${LB-aXi16Ram1qiM-_vN;F7euQ>7`QqS~a{QJr*F2|pJuGDWx$d*2TZ(dXizD@6D z49m-woT{m%!H@4=B417Vu>csJJSZ+Vmo8)RcG+v>${qgF+01KU&S_AzGe+ZVvUF7_($jZ7O^e{iB z`zh@5eVZ_gACV3E7ktNfUw*#zyh$ey>#m#2U)rRq5L2hXITpuJ$KSOKf@@xwkCiD( zxAUM4u)ZsI+bB+d9}EtUBlos$BQ$v1)Qh5p0M)xsn}**6NW%;F9-6@wFIRxan0pbd zx+=ghBMi2Jw&6q{T?^wo_|M8_K;?f&=oETg@UlI3TC{(J$HZwJo)OoMy= z2vD*tEC=B)FBU(6syiO-eP*#z155h-Cf zJm~uFh4Z@`>ppqp_C)^!Pm(`uODKP=k0zJbb@LS(r1~F|jt3ooq({E7N8kopq%QTT zF!Mj3r#;Kump_A!2M@o)oPcdMPRzWiuLsYYrp65VFo!{>QVn{GS{Rr)H-l^54&d7o z1BI`Oc7TcBqGN&6XRz!nIMR{qR9Ba_3Y*G({H$J;s`~e1Gq7mGoeo{&>H3$}8Q6Z+ zTW~)3<;~-Lw2>Wu*H-}B<*p!3SHyDF&W|*XNq?V!!9%uK-&C}=^!(j^abzf)*VnIi zu0>p=#KM%8whv)GyqoFnRvgTBD6a4rw!VU~w=U~j8vA3fUhZyNyoc(2_l5@hguVVa zboKNNlGA--4FT);-hGcO>lWODvzjC4SIGHRAvOb4>F8RnTI@XozR7qVbZf3R r!7nyu*joG4sorcmeB=JPa3FsB;dn3*xVwwPtvC4}25Xogl@tH~>2Y$H delta 2324 zcmV+v3G4Qg5|$EwABzYGH%~@b0t0hpa&~EBWnXu1a%*LBE^2cCrC9rOo5~gbR}`jK zff^we7FeumGP${#Hf>&$$K(e$*Rzka5(xqoR(4$5|9j7ZBukd%+&i5~A_BYT@twza zcAZrVgVr>KG|oh?Drt(k8nj^E=@(qiL49t@)&=jn0kdjAL-5e&Y;Wm{XS+7!Bw-2Ug~1ytq*FH6DYcNcu)UF)KO_p5fk zeS6KjeWxbqAAH3P$N%lh3G6%H@Y((M_{uj|OE`sF|A8&Rw@b5Q+JWxQA_XNFKz}zo z{BSy*z7yYnK$;%C{*xD!P|eJII9xB4b%l{wNmDo54a4D{IA4R2bJrR3Wijr|y9(xJ zQ6^)#w!!hQ7hvYKI(6Q^`}oQ6=fD*-?WslgAN{j??YyWfgIJC}p|j4Z!>IdmGrysb*+JAG8bJ43-T@n>naK z7qjWvnKO2L2MCSh5Hcc3EV+(3<0=A~<~h(bq?z;8$A!A-%u?eeub^zl3NA)qV?L6R zN^p6W3dotFG^2{-I;Sd((nu2MP(VGYOcE^_$+LvyQOrdc$~58-Yay~IijM0kDqW9w zF2hWJ=!g*wd6p9`NW!8FB#BtcqGY2ckBQD>{1B&1iHw9=DxK&o3L_mQj2&0gLUU0_ z7>QT|&67Bii40?sGtIR?TR|mEWGUZhq9mtM&{$-YgiOg)kuXlE;*nNprFuk@;RZ%& z0%%uGq@-z~J<+(s}@|+}a#3gJiBO-K~ zr-_KD%2JCvCoCf@MH4BHd9=~MGb%$OBY>1i7Rx-38P{QofsA9pNUy;HRLHTc8yF>j zN{dLvkffRBD$a0q91%rDlt&1m3cj^A>^uphEJV#Ii6Q2i(p;x9%i=gitkj;|jRPY^ zHAyKX3HnS0<3c8Bs4_uO2*zU@0t{#v$4M+gtqDUYIfg954Iu_l1K*RrP{oL*JWDmp zGA?sQ8M7dQidjUXOp;V@;b1hCG-Q!~Qi;^4hH;ri5=)83kmfxc8>`XicHGpWHud;J z_cQvnE>&QRMo}t`aw(aJX^Kb@LV2FhDB_$eBEmHOnW)#rY;M-FGLdM(m?n_`ELj@s zFocK*8DpW6i2h908&Jh+u@$3Ht!ERmq!wwKR^| zpXIqqgy0%GNvb(^0xp;WMq?s(r4_bsk2Sxr4wVRR9VM$U=bLaBoWW(5*R$1u+h z-UO|zk%%3~^NU%9HHwqSd)}Z#gB13LBM&0=OwY!@ugv0n{MF}5{f2~pZ0YmrRi)s^ z^lrwmylly-npztC{NXk7)ubOgke-ljt6<5^yh7cu87uJKNPoT>BBm&)^exo2+CzDHla%rTTDq73RDK81GepaSc$#OsZH z5>EDkGu{@H$#%4B>jgYG(ye{#rUTzj3(HqsFx6f(|5&6@T93$d^RSK;@q!lT7vmyQ z-tqZ!y=LmzAygM7YJO;;32@|SuI@gW&8H{WxuC@X^(_4Ab))QmQC;8aqS~nHpV72? z`7`J3yu+X`lk=TAaeN$>+fVf-x!MkmH>hoYygD$zLT9GdK)=Etf@N_RIln8ivhD{x z%#Z1Q3cGyYCd}d|WP|=0-!a~oUvE8c(#gZR>*n&;HmNGa)G2U|#c|Z}cP)e9k{9N4 zWs1`6JZJ-~@5>u_K$E}o)wjYz;u5OPC7gO zAZ+dTV)+FT>65{3@Q=W0&oLJmrhPp7NN>(eM;)B}AGjZXIqX#rqFp@(r9J!Yz}cT^ zaLpe9N|uG?ApGUU;wMmb$D@6m&lXv3WjUE}Hcm)05B ze$`uWI{4+?(|xp&9e>wX0NdAHK^(7$<*J=O&^RXjeF6p#*MvwAA9w3ciZAUR`0tvG}tHX z^~a&B$8V6FZjUtttmAw4J+iD@a1UPhs6Ft~hGz}ugWvir-)-r|>$cXV?sIIQYj7^H zP1KjpL-%CeT;%f!<_gwaIH~@&8P+D%$%%(q&5`pf32&&127>t&&-1==[34,35,60,62,63,96].indexOf(t)?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&127>t&&-1==[34,35,60,62,96].indexOf(t)?e:encodeURIComponent(e)}function a(e,a,s){function c(e){g.push(e)}var d=a||"scheme start",l=0,u="",w=!1,_=!1,g=[];e:for(;(e[l-1]!=p||0==l)&&!this._isInvalid;){var b=e[l];switch(d){case"scheme start":if(!b||!m.test(b)){if(a){c("Invalid scheme.");break e}u="",d="no scheme";continue}u+=b.toLowerCase(),d="scheme";break;case"scheme":if(b&&v.test(b))u+=b.toLowerCase();else{if(":"!=b){if(a){if(p==b)break e;c("Code point not allowed in scheme: "+b);break e}u="",l=0,d="no scheme";continue}if(this._scheme=u,u="",a)break e;t(this._scheme)&&(this._isRelative=!0),d="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==b?(this._query="?",d="query"):"#"==b?(this._fragment="#",d="fragment"):p!=b&&" "!=b&&"\n"!=b&&"\r"!=b&&(this._schemeData+=r(b));break;case"no scheme":if(s&&t(s._scheme)){d="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=b||"/"!=e[l+1]){c("Expected /, got: "+b),d="relative";continue}d="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),p==b){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==b||"\\"==b)"\\"==b&&c("\\ is an invalid code point."),d="relative slash";else if("?"==b)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,d="query";else{if("#"!=b){var y=e[l+1],E=e[l+2];("file"!=this._scheme||!m.test(b)||":"!=y&&"|"!=y||p!=E&&"/"!=E&&"\\"!=E&&"?"!=E&&"#"!=E)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),d="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,d="fragment"}break;case"relative slash":if("/"!=b&&"\\"!=b){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),d="relative path";continue}"\\"==b&&c("\\ is an invalid code point."),d="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=b){c("Expected '/', got: "+b),d="authority ignore slashes";continue}d="authority second slash";break;case"authority second slash":if(d="authority ignore slashes","/"!=b){c("Expected '/', got: "+b);continue}break;case"authority ignore slashes":if("/"!=b&&"\\"!=b){d="authority";continue}c("Expected authority, got: "+b);break;case"authority":if("@"==b){w&&(c("@ already seen."),u+="%40"),w=!0;for(var L=0;L>>0)+(t++ +"__")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return t&&t[0]===e?(t[0]=t[1]=void 0,!0):!1},has:function(e){var t=e[this.name];return t?t[0]===e:!1}},window.WeakMap=n}(),function(e){function t(e){b.push(e),g||(g=!0,m(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){g=!1;var e=b;b=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r0){var r=n[o-1],i=f(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n":return">";case" ":return" "}}function t(t){return t.replace(u,e)}var n="undefined"==typeof HTMLTemplateElement;/Trident/.test(navigator.userAgent)&&!function(){var e=document.importNode;document.importNode=function(){var t=e.apply(document,arguments);if(t.nodeType===Node.DOCUMENT_FRAGMENT_NODE){var n=document.createDocumentFragment();return n.appendChild(t),n}return t}}();var o=function(){if(!n){var e=document.createElement("template"),t=document.createElement("template");t.content.appendChild(document.createElement("div")),e.content.appendChild(t);var o=e.cloneNode(!0);return 0===o.content.childNodes.length||0===o.content.firstChild.content.childNodes.length}}(),r="template",i=function(){};if(n){var a=document.implementation.createHTMLDocument("template"),s=!0,c=document.createElement("style");c.textContent=r+"{display:none;}";var d=document.head;d.insertBefore(c,d.firstElementChild),i.prototype=Object.create(HTMLElement.prototype),i.decorate=function(e){if(!e.content){e.content=a.createDocumentFragment();for(var n;n=e.firstChild;)e.content.appendChild(n);if(e.cloneNode=function(e){return i.cloneNode(this,e)},s)try{Object.defineProperty(e,"innerHTML",{get:function(){for(var e="",n=this.content.firstChild;n;n=n.nextSibling)e+=n.outerHTML||t(n.data);return e},set:function(e){for(a.body.innerHTML=e,i.bootstrap(a);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;a.body.firstChild;)this.content.appendChild(a.body.firstChild)},configurable:!0})}catch(o){s=!1}i.bootstrap(e.content)}},i.bootstrap=function(e){for(var t,n=e.querySelectorAll(r),o=0,a=n.length;a>o&&(t=n[o]);o++)i.decorate(t)},document.addEventListener("DOMContentLoaded",function(){i.bootstrap(document)});var l=document.createElement;document.createElement=function(){"use strict";var e=l.apply(document,arguments);return"template"===e.localName&&i.decorate(e),e};var u=/[&\u00A0<>]/g}if(n||o){var h=Node.prototype.cloneNode;i.cloneNode=function(e,t){var n=h.call(e,!1);return this.decorate&&this.decorate(n),t&&(n.content.appendChild(h.call(e.content,!0)),this.fixClonedDom(n.content,e.content)),n},i.fixClonedDom=function(e,t){if(t.querySelectorAll)for(var n,o,i=t.querySelectorAll(r),a=e.querySelectorAll(r),s=0,c=a.length;c>s;s++)o=i[s],n=a[s],this.decorate&&this.decorate(o),n.parentNode.replaceChild(o.cloneNode(!0),n)};var f=document.importNode;Node.prototype.cloneNode=function(e){var t=h.call(this,e);return e&&i.fixClonedDom(t,this),t},document.importNode=function(e,t){if(e.localName===r)return i.cloneNode(e,t);var n=f.call(document,e,t);return t&&i.fixClonedDom(n,e),n},o&&(HTMLTemplateElement.prototype.cloneNode=function(e){return i.cloneNode(this,e)})}n&&(window.HTMLTemplateElement=i)}(),function(e){"use strict";if(!window.performance){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var o=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(o.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var r=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||r&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||r&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||p,o(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===w}function o(e,t){if(n(t))e&&e();else{var r=function(){"complete"!==t.readyState&&t.readyState!==w||(t.removeEventListener(_,r),o(e,t))};t.addEventListener(_,r)}}function r(e){e.target.__loaded=!0}function i(e,t){function n(){c==d&&e&&e({allImports:s,loadedImports:l,errorImports:u})}function o(e){r(e),l.push(this),c++,n()}function i(e){u.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,d=s.length,l=[],u=[];if(d)for(var h,f=0;d>f&&(h=s[f]);f++)a(h)?(l.push(this),c++,n()):(h.addEventListener("load",o),h.addEventListener("error",i));else n()}function a(e){return u?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,o=e.length;o>n&&(t=e[n]);n++)c(t)&&d(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function d(e){var t=e["import"];t?r({target:e}):(e.addEventListener("load",r),e.addEventListener("error",r))}var l="import",u=Boolean(l in document.createElement("link")),h=Boolean(window.ShadowDOMPolyfill),f=function(e){return h?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},p=f(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return f(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(p,"_currentScript",m);var v=/Trident/.test(navigator.userAgent),w=v?"complete":"interactive",_="readystatechange";u&&(new MutationObserver(function(e){for(var t,n=0,o=e.length;o>n&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,o=t.length;o>n&&(e=t[n]);n++)d(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=p.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),p.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=l,e.useNative=u,e.rootDocument=p,e.whenReady=t,e.isIE=v}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},o=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=o}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,o={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,o=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,o),e},resolveUrlsInCssText:function(e,o,r){var i=this.replaceUrls(e,r,o,t);return i=this.replaceUrls(i,r,o,n)},replaceUrls:function(e,t,n,o){return e.replace(o,function(e,o,r,i){var a=r.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,o+"'"+a+"'"+i})}};e.path=o}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,o,r){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}o.call(r,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,o=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};o.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,o=e.length;o>n&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,o){if(n.load&&console.log("fetch",e,o),e)if(e.match(/^data:/)){var r=e.split(","),i=r[0],a=r[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,o,null,a)}.bind(this),0)}else{var s=function(t,n,r){this.receive(e,o,t,n,r)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,o,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,o,r){this.cache[e]=o;for(var i,a=this.pending[e],s=0,c=a.length;c>s&&(i=a[s]);s++)this.onload(e,i,o,n,r),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=o}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,o=e.length;o>n&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,o=e.length;o>n&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===l}function n(e){var t=o(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function o(e){return e.textContent+r(e)}function r(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,o=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+o+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,d=e.isIE,l=e.IMPORT_LINK_TYPE,u="link[rel="+l+"]",h={documentSelectors:u,importsSelectors:[u,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,o=function(r){e.removeEventListener("load",o),e.removeEventListener("error",o),t&&t(r),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",o),e.addEventListener("error",o),d&&"style"===e.localName){var r=!1;if(-1==e.textContent.indexOf("@import"))r=!0;else if(e.sheet){r=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;s>c&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(r=r&&Boolean(i.styleSheet))}r&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var o=document.createElement("script");o.__importElement=t,o.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(o,function(t){o.parentNode&&o.parentNode.removeChild(o),e.currentScript=null}),this.addElementToDocument(o)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var o,r=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=r.length;a>i&&(o=r[i]);i++)if(!this.isParsed(o))return this.hasResource(o)?t(o)?this.nextToParseInDoc(o.__doc,o):o:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=h,e.IMPORT_SELECTOR=u}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function o(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function r(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var r=n.createElement("base");r.setAttribute("href",t),n.baseURI||o(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(r),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,d=e.Loader,l=e.Observer,u=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){f.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);f.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,o,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=o,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:r(o,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}u.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),u.parseNext()},loadedAll:function(){u.parseNext()}},f=new d(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new l,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(c,"baseURI",p)}e.importer=h,e.importLoader=f}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,o={added:function(e){for(var o,r,i,a,s=0,c=e.length;c>s&&(a=e[s]);s++)o||(o=a.ownerDocument,r=t.isParsed(o)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&r&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&r.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&r.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=o.added.bind(o);var r=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(o)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var o=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],o=function(e){n.push(e)},r=function(){n.forEach(function(t){t(e)})};e.addModule=o,e.initializeModules=r,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return t(e)?!0:void o(e,t)}),o(e,t)}function n(e,t,o){var r=e.firstElementChild;if(!r)for(r=e.firstChild;r&&r.nodeType!==Node.ELEMENT_NODE;)r=r.nextSibling;for(;r;)t(r,o)!==!0&&n(r,t,o),r=r.nextElementSibling;return null}function o(e,n){for(var o=e.shadowRoot;o;)t(o,n),o=o.olderShadowRoot}function r(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var o,r=e.querySelectorAll("link[rel="+a+"]"),s=0,c=r.length;c>s&&(o=r[s]);s++)o["import"]&&i(o["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=r,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||o(e,t)}function n(t,n){return e.upgrade(t,n)?!0:void(n&&a(t))}function o(e,t){g(e,function(e){return n(e,t)?!0:void 0})}function r(e){L.push(e),E||(E=!0,setTimeout(i))}function i(){E=!1;for(var e,t=L,n=0,o=t.length;o>n&&(e=t[n]);n++)e();L=[]}function a(e){y?r(function(){s(e)}):s(e)}function s(e){ -e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){d(e),g(e,function(e){d(e)})}function d(e){y?r(function(){l(e)}):l(e)}function l(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function u(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function h(e){if(e.shadowRoot&&!e.shadowRoot.__watched){_.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function f(e,n){if(_.dom){var o=n[0];if(o&&"childList"===o.type&&o.addedNodes&&o.addedNodes){for(var r=o.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var i=r&&(r.URL||r._URL||r.host&&r.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=u(e);n.forEach(function(e){"childList"===e.type&&(N(e.addedNodes,function(e){e.localName&&t(e,a)}),N(e.removedNodes,function(e){e.localName&&c(e)}))}),_.dom&&console.groupEnd()}function p(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(f(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(f.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),_.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),m(e),_.dom&&console.groupEnd()}function w(e){b(e,v)}var _=e.flags,g=e.forSubtree,b=e.forDocumentTree,y=window.MutationObserver._isPolyfilled&&_["throttle-attached"];e.hasPolyfillMutations=y,e.hasThrottledAttached=y;var E=!1,L=[],N=Array.prototype.forEach.call.bind(Array.prototype.forEach),M=Element.prototype.createShadowRoot;M&&(Element.prototype.createShadowRoot=function(){var e=M.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=h,e.upgradeDocumentTree=w,e.upgradeDocument=v,e.upgradeSubtree=o,e.upgradeAll=t,e.attached=a,e.takeRecords=p}),window.CustomElements.addModule(function(e){function t(t,o){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(r);if(i&&(r&&i.tag==t.localName||!r&&!i["extends"]))return n(t,i,o)}}function n(t,n,r){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),o(t,n),t.__upgraded__=!0,i(t),r&&e.attached(t),e.upgradeSubtree(t,r),a.upgrade&&console.groupEnd(),t}function o(e,t){Object.__proto__?e.__proto__=t.prototype:(r(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function r(e,t,n){for(var o={},r=t;r!==n&&r!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(r),s=0;i=a[s];s++)o[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(r,i)),o[i]=1);r=Object.getPrototypeOf(r)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=o}),window.CustomElements.addModule(function(e){function t(t,o){var c=o||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(r(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(d(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c["extends"]&&(c["extends"]=c["extends"].toLowerCase()),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),l(c.__name,c),c.ctor=u(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&v(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){o.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){o.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function o(e,t,n){e=e.toLowerCase();var o=this.getAttribute(e);n.apply(this,arguments);var r=this.getAttribute(e);this.attributeChangedCallback&&r!==o&&this.attributeChangedCallback(e,o,r)}function r(e){for(var t=0;t=0&&g(o,HTMLElement),o)}function p(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return w(e),e}}var m,v=(e.isIE,e.upgradeDocumentTree),w=e.upgradeAll,_=e.upgradeWithDefinition,g=e.implementPrototype,b=e.useNative,y=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],E={},L="http://www.w3.org/1999/xhtml",N=document.createElement.bind(document),M=document.createElementNS.bind(document);m=Object.__proto__||b?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},p(Node.prototype,"cloneNode"),p(document,"importNode"),document.registerElement=t,document.createElement=f,document.createElementNS=h,e.registry=E,e["instanceof"]=m,e.reservedTagList=y,e.getRegisteredDefinition=d,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e["instanceof"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents); \ No newline at end of file +// @version 0.7.23 +!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents-lite.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,o=e.split("=");o[0]&&(t=o[0].match(/wc-(.+)/))&&(n[t[1]]=o[1]||!0)}),t)for(var o,r=0;o=t.attributes[r];r++)"src"!==o.name&&(n[o.name]=o.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),function(e){"use strict";function t(e){return void 0!==h[e]}function n(){s.call(this),this._isInvalid=!0}function o(e){return""==e&&n.call(this),e.toLowerCase()}function r(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,63,96].indexOf(t)==-1?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,96].indexOf(t)==-1?e:encodeURIComponent(e)}function a(e,a,s){function c(e){g.push(e)}var d=a||"scheme start",l=0,u="",w=!1,_=!1,g=[];e:for(;(e[l-1]!=p||0==l)&&!this._isInvalid;){var b=e[l];switch(d){case"scheme start":if(!b||!m.test(b)){if(a){c("Invalid scheme.");break e}u="",d="no scheme";continue}u+=b.toLowerCase(),d="scheme";break;case"scheme":if(b&&v.test(b))u+=b.toLowerCase();else{if(":"!=b){if(a){if(p==b)break e;c("Code point not allowed in scheme: "+b);break e}u="",l=0,d="no scheme";continue}if(this._scheme=u,u="",a)break e;t(this._scheme)&&(this._isRelative=!0),d="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==b?(this._query="?",d="query"):"#"==b?(this._fragment="#",d="fragment"):p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._schemeData+=r(b));break;case"no scheme":if(s&&t(s._scheme)){d="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=b||"/"!=e[l+1]){c("Expected /, got: "+b),d="relative";continue}d="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),p==b){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==b||"\\"==b)"\\"==b&&c("\\ is an invalid code point."),d="relative slash";else if("?"==b)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,d="query";else{if("#"!=b){var y=e[l+1],E=e[l+2];("file"!=this._scheme||!m.test(b)||":"!=y&&"|"!=y||p!=E&&"/"!=E&&"\\"!=E&&"?"!=E&&"#"!=E)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),d="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,d="fragment"}break;case"relative slash":if("/"!=b&&"\\"!=b){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),d="relative path";continue}"\\"==b&&c("\\ is an invalid code point."),d="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=b){c("Expected '/', got: "+b),d="authority ignore slashes";continue}d="authority second slash";break;case"authority second slash":if(d="authority ignore slashes","/"!=b){c("Expected '/', got: "+b);continue}break;case"authority ignore slashes":if("/"!=b&&"\\"!=b){d="authority";continue}c("Expected authority, got: "+b);break;case"authority":if("@"==b){w&&(c("@ already seen."),u+="%40"),w=!0;for(var L=0;L>>0)+(t++ +"__")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),function(e){function t(e){b.push(e),g||(g=!0,m(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){g=!1;var e=b;b=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r0){var r=n[o-1],i=f(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n":return">";case" ":return" "}}function t(t){return t.replace(u,e)}var n="undefined"==typeof HTMLTemplateElement;/Trident/.test(navigator.userAgent)&&!function(){var e=document.importNode;document.importNode=function(){var t=e.apply(document,arguments);if(t.nodeType===Node.DOCUMENT_FRAGMENT_NODE){var n=document.createDocumentFragment();return n.appendChild(t),n}return t}}();var o=function(){if(!n){var e=document.createElement("template"),t=document.createElement("template");t.content.appendChild(document.createElement("div")),e.content.appendChild(t);var o=e.cloneNode(!0);return 0===o.content.childNodes.length||0===o.content.firstChild.content.childNodes.length}}(),r="template",i=function(){};if(n){var a=document.implementation.createHTMLDocument("template"),s=!0,c=document.createElement("style");c.textContent=r+"{display:none;}";var d=document.head;d.insertBefore(c,d.firstElementChild),i.prototype=Object.create(HTMLElement.prototype),i.decorate=function(e){if(!e.content){e.content=a.createDocumentFragment();for(var n;n=e.firstChild;)e.content.appendChild(n);if(e.cloneNode=function(e){return i.cloneNode(this,e)},s)try{Object.defineProperty(e,"innerHTML",{get:function(){for(var e="",n=this.content.firstChild;n;n=n.nextSibling)e+=n.outerHTML||t(n.data);return e},set:function(e){for(a.body.innerHTML=e,i.bootstrap(a);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;a.body.firstChild;)this.content.appendChild(a.body.firstChild)},configurable:!0})}catch(o){s=!1}i.bootstrap(e.content)}},i.bootstrap=function(e){for(var t,n=e.querySelectorAll(r),o=0,a=n.length;o]/g}if(n||o){var h=Node.prototype.cloneNode;i.cloneNode=function(e,t){var n=h.call(e,!1);return this.decorate&&this.decorate(n),t&&(n.content.appendChild(h.call(e.content,!0)),this.fixClonedDom(n.content,e.content)),n},i.fixClonedDom=function(e,t){if(t.querySelectorAll)for(var n,o,i=t.querySelectorAll(r),a=e.querySelectorAll(r),s=0,c=a.length;s=200&&e.status<300||304===e.status||0===e.status},load:function(n,o,r){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}o.call(r,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,o=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};o.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,o=e.length;n-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,o,null,a)}.bind(this),0)}else{var s=function(t,n,r){this.receive(e,o,t,n,r)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,o,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,o,r){this.cache[e]=o;for(var i,a=this.pending[e],s=0,c=a.length;s=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,o=function(r){e.removeEventListener("load",o),e.removeEventListener("error",o),t&&t(r),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",o),e.addEventListener("error",o),d&&"style"===e.localName){var r=!1;if(e.textContent.indexOf("@import")==-1)r=!0;else if(e.sheet){r=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;c=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=h,e.IMPORT_SELECTOR=u}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function o(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function r(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var r=n.createElement("base");r.setAttribute("href",t),n.baseURI||o(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(r),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,d=e.Loader,l=e.Observer,u=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){f.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);f.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,o,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=o,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:r(o,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}u.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),u.parseNext()},loadedAll:function(){u.parseNext()}},f=new d(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new l,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(c,"baseURI",p)}e.importer=h,e.importLoader=f}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,o={added:function(e){for(var o,r,i,a,s=0,c=e.length;s=0)){n.push(e);for(var o,r=e.querySelectorAll("link[rel="+a+"]"),s=0,c=r.length;s=0&&g(o,HTMLElement),o)}function p(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return w(e),e}}var m,v=(e.isIE,e.upgradeDocumentTree),w=e.upgradeAll,_=e.upgradeWithDefinition,g=e.implementPrototype,b=e.useNative,y=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],E={},L="http://www.w3.org/1999/xhtml",N=document.createElement.bind(document),M=document.createElementNS.bind(document);m=Object.__proto__||b?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},p(Node.prototype,"cloneNode"),p(document,"importNode"),document.registerElement=t,document.createElement=f,document.createElementNS=h,e.registry=E,e["instanceof"]=m,e.reservedTagList=y,e.getRegisteredDefinition=d,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e["instanceof"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents); \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/webcomponents-lite.min.js.gz b/homeassistant/components/frontend/www_static/webcomponents-lite.min.js.gz index 0bd0568f231d72b785de5e0e10c0a21228f6b5c7..c5fe12d2303e5b2d7e1f40f018bf774b42f15b6d 100644 GIT binary patch literal 12360 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 literal 12355 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 Date: Fri, 9 Dec 2016 08:19:14 +0100 Subject: [PATCH 62/89] Pilight receive match fix for bug 4637 (#4639) * Pilight: dont protocol as list in COMMAND_SCHEMA As described in bug #4637 the protocol should not be wrapped in a list in the spec of COMMAND_SCHEMA because this causes the component to never successfully match any received rf code. As pointed ot in PR #4639 the easiest way to do this, is to not derive COMMAND_SCHEMA from RF_CODE_SCHEMA and specify protocol as simple string there. This fixes bug #4637. Signed-off-by: Jan Losinski * Pilight: Add "unitcode" to command schema. This adds "unitcode" to the COMMAND_SCHEMA. It is used for example in the brennenstuhl protocol of pilight. Signed-off-by: Jan Losinski --- homeassistant/components/switch/pilight.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/pilight.py b/homeassistant/components/switch/pilight.py index 6e16c9fa2e5..a052848cb21 100644 --- a/homeassistant/components/switch/pilight.py +++ b/homeassistant/components/switch/pilight.py @@ -11,7 +11,8 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv import homeassistant.components.pilight as pilight from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.const import (CONF_NAME, CONF_ID, CONF_SWITCHES, CONF_STATE) +from homeassistant.const import (CONF_NAME, CONF_ID, CONF_SWITCHES, CONF_STATE, + CONF_PROTOCOL) _LOGGER = logging.getLogger(__name__) @@ -21,13 +22,16 @@ CONF_ON_CODE = 'on_code' CONF_ON_CODE_RECIEVE = 'on_code_receive' CONF_SYSTEMCODE = 'systemcode' CONF_UNIT = 'unit' +CONF_UNITCODE = 'unitcode' DEPENDENCIES = ['pilight'] -COMMAND_SCHEMA = pilight.RF_CODE_SCHEMA.extend({ +COMMAND_SCHEMA = vol.Schema({ + vol.Optional(CONF_PROTOCOL): cv.string, vol.Optional('on'): cv.positive_int, vol.Optional('off'): cv.positive_int, vol.Optional(CONF_UNIT): cv.positive_int, + vol.Optional(CONF_UNITCODE): cv.positive_int, vol.Optional(CONF_ID): cv.positive_int, vol.Optional(CONF_STATE): cv.string, vol.Optional(CONF_SYSTEMCODE): cv.positive_int, From 0bf9e6d4bb1d38a782824fb8af89ec9bb8406247 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 9 Dec 2016 08:24:03 +0100 Subject: [PATCH 63/89] Bugfix error on automation reload (#4823) --- homeassistant/components/automation/state.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/automation/state.py b/homeassistant/components/automation/state.py index fb146991602..65cca462ed9 100644 --- a/homeassistant/components/automation/state.py +++ b/homeassistant/components/automation/state.py @@ -64,10 +64,19 @@ def async_trigger(hass, config, action): call_action() return + @callback + def clear_listener(): + """Clear all unsub listener.""" + nonlocal async_remove_state_for_cancel + nonlocal async_remove_state_for_listener + async_remove_state_for_listener = None + async_remove_state_for_cancel = None + @callback def state_for_listener(now): """Fire on state changes after a delay and calls action.""" async_remove_state_for_cancel() + clear_listener() call_action() @callback @@ -77,6 +86,7 @@ def async_trigger(hass, config, action): return async_remove_state_for_listener() async_remove_state_for_cancel() + clear_listener() async_remove_state_for_listener = async_track_point_in_utc_time( hass, state_for_listener, dt_util.utcnow() + time_delta) From 0aac4d64e1d8336ebe71fe108245409646a82c75 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 9 Dec 2016 01:26:02 -0600 Subject: [PATCH 64/89] Add away mode for Radio Thermostat/3M Filtrete (#4793) --- .../components/climate/radiotherm.py | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/climate/radiotherm.py b/homeassistant/components/climate/radiotherm.py index d06e148cfdd..9a0e5666036 100644 --- a/homeassistant/components/climate/radiotherm.py +++ b/homeassistant/components/climate/radiotherm.py @@ -23,10 +23,19 @@ ATTR_FAN = 'fan' ATTR_MODE = 'mode' CONF_HOLD_TEMP = 'hold_temp' +CONF_AWAY_TEMPERATURE_HEAT = 'away_temperature_heat' +CONF_AWAY_TEMPERATURE_COOL = 'away_temperature_cool' + +DEFAULT_AWAY_TEMPERATURE_HEAT = 60 +DEFAULT_AWAY_TEMPERATURE_COOL = 85 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_HOLD_TEMP, default=False): cv.boolean, + vol.Optional(CONF_AWAY_TEMPERATURE_HEAT, + default=DEFAULT_AWAY_TEMPERATURE_HEAT): vol.Coerce(float), + vol.Optional(CONF_AWAY_TEMPERATURE_COOL, + default=DEFAULT_AWAY_TEMPERATURE_COOL): vol.Coerce(float), }) @@ -45,12 +54,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False hold_temp = config.get(CONF_HOLD_TEMP) + away_temps = [ + config.get(CONF_AWAY_TEMPERATURE_HEAT), + config.get(CONF_AWAY_TEMPERATURE_COOL) + ] tstats = [] for host in hosts: try: tstat = radiotherm.get_thermostat(host) - tstats.append(RadioThermostat(tstat, hold_temp)) + tstats.append(RadioThermostat(tstat, hold_temp, away_temps)) except OSError: _LOGGER.exception("Unable to connect to Radio Thermostat: %s", host) @@ -61,7 +74,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class RadioThermostat(ClimateDevice): """Representation of a Radio Thermostat.""" - def __init__(self, device, hold_temp): + def __init__(self, device, hold_temp, away_temps): """Initialize the thermostat.""" self.device = device self.set_time() @@ -71,7 +84,10 @@ class RadioThermostat(ClimateDevice): self._name = None self._fmode = None self._tmode = None - self.hold_temp = hold_temp + self._hold_temp = hold_temp + self._away = False + self._away_temps = away_temps + self._prev_temp = None self.update() self._operation_list = [STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_OFF] @@ -113,6 +129,11 @@ class RadioThermostat(ClimateDevice): """Return the temperature we try to reach.""" return self._target_temperature + @property + def is_away_mode_on(self): + """Return true if away mode is on.""" + return self._away + def update(self): """Update the data from the thermostat.""" self._current_temperature = self.device.temp['raw'] @@ -138,7 +159,7 @@ class RadioThermostat(ClimateDevice): self.device.t_cool = round(temperature * 2.0) / 2.0 elif self._current_operation == STATE_HEAT: self.device.t_heat = round(temperature * 2.0) / 2.0 - if self.hold_temp: + if self._hold_temp or self._away: self.device.hold = 1 else: self.device.hold = 0 @@ -162,3 +183,23 @@ class RadioThermostat(ClimateDevice): self.device.t_cool = round(self._target_temperature * 2.0) / 2.0 elif operation_mode == STATE_HEAT: self.device.t_heat = round(self._target_temperature * 2.0) / 2.0 + + def turn_away_mode_on(self): + """Turn away on. + + The RTCOA app simulates away mode by using a hold. + """ + away_temp = None + if not self._away: + self._prev_temp = self._target_temperature + if self._current_operation == STATE_HEAT: + away_temp = self._away_temps[0] + elif self._current_operation == STATE_COOL: + away_temp = self._away_temps[1] + self._away = True + self.set_temperature(temperature=away_temp) + + def turn_away_mode_off(self): + """Turn away off.""" + self._away = False + self.set_temperature(temperature=self._prev_temp) From d02899216dae86c0b41d60ceefb4aab3a1be1222 Mon Sep 17 00:00:00 2001 From: Keaton Taylor Date: Fri, 9 Dec 2016 10:45:14 -0600 Subject: [PATCH 65/89] Prevent emulated hue discovery by hue component (#4819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prevent emulated hue discovery Test for “HASS Bridge” in discovery info, pass if found, else try and setup the bridge. * Solved coding error Duplicate commands and return false added for component. --- homeassistant/components/light/hue.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index 8fd8a6ef097..da2158ba78d 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -90,6 +90,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is not None: host = urlparse(discovery_info[1]).hostname + + if "HASS Bridge" in discovery_info[0]: + _LOGGER.info('Emulated hue found, will not add') + return False else: host = config.get(CONF_HOST, None) From 1547045f2c292a3558b9dccac48a16aefc541533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Oldag?= Date: Fri, 9 Dec 2016 17:52:14 +0100 Subject: [PATCH 66/89] Flic: Support ignoring individual click types. (#4827) --- .../components/binary_sensor/flic.py | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/binary_sensor/flic.py b/homeassistant/components/binary_sensor/flic.py index 63323155d31..92301330605 100644 --- a/homeassistant/components/binary_sensor/flic.py +++ b/homeassistant/components/binary_sensor/flic.py @@ -17,6 +17,13 @@ REQUIREMENTS = ['https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4'] _LOGGER = logging.getLogger(__name__) +CLICK_TYPE_SINGLE = "single" +CLICK_TYPE_DOUBLE = "double" +CLICK_TYPE_HOLD = "hold" +CLICK_TYPES = [CLICK_TYPE_SINGLE, CLICK_TYPE_DOUBLE, CLICK_TYPE_HOLD] + +CONF_IGNORED_CLICK_TYPES = "ignored_click_types" + EVENT_NAME = "flic_click" EVENT_DATA_NAME = "button_name" EVENT_DATA_ADDRESS = "button_address" @@ -26,7 +33,9 @@ EVENT_DATA_TYPE = "click_type" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST, default='localhost'): cv.string, vol.Optional(CONF_PORT, default=5551): cv.port, - vol.Optional(CONF_DISCOVERY, default=True): cv.boolean + vol.Optional(CONF_DISCOVERY, default=True): cv.boolean, + vol.Optional(CONF_IGNORED_CLICK_TYPES): vol.All(cv.ensure_list, + [vol.In(CLICK_TYPES)]) }) @@ -93,7 +102,8 @@ def start_scanning(hass, config, async_add_entities, client): @asyncio.coroutine def async_setup_button(hass, config, async_add_entities, client, address): """Setup single button device.""" - button = FlicButton(hass, client, address) + ignored_click_types = config.get(CONF_IGNORED_CLICK_TYPES) + button = FlicButton(hass, client, address, ignored_click_types) _LOGGER.info("Connected to button (%s)", address) yield from async_add_entities([button]) @@ -117,25 +127,47 @@ def async_get_verified_addresses(client): class FlicButton(BinarySensorDevice): """Representation of a flic button.""" - def __init__(self, hass, client, address): + def __init__(self, hass, client, address, ignored_click_types): """Initialize the flic button.""" import pyflic self._hass = hass self._address = address self._is_down = False - self._click_types = { - pyflic.ClickType.ButtonSingleClick: "single", - pyflic.ClickType.ButtonDoubleClick: "double", - pyflic.ClickType.ButtonHold: "hold", + self._ignored_click_types = ignored_click_types or [] + self._hass_click_types = { + pyflic.ClickType.ButtonClick: CLICK_TYPE_SINGLE, + pyflic.ClickType.ButtonSingleClick: CLICK_TYPE_SINGLE, + pyflic.ClickType.ButtonDoubleClick: CLICK_TYPE_DOUBLE, + pyflic.ClickType.ButtonHold: CLICK_TYPE_HOLD, } - # Initialize connection channel - self._channel = pyflic.ButtonConnectionChannel(self._address) - self._channel.on_button_up_or_down = self._on_up_down - self._channel.on_button_single_or_double_click_or_hold = self._on_click + self._channel = self._create_channel() client.add_connection_channel(self._channel) + def _create_channel(self): + """Create a new connection channel to the button.""" + import pyflic + + channel = pyflic.ButtonConnectionChannel(self._address) + channel.on_button_up_or_down = self._on_up_down + + # If all types of clicks should be ignored, skip registering callbacks + if set(self._ignored_click_types) == set(CLICK_TYPES): + return channel + + if CLICK_TYPE_DOUBLE in self._ignored_click_types: + # Listen to all but double click type events + channel.on_button_click_or_hold = self._on_click + elif CLICK_TYPE_HOLD in self._ignored_click_types: + # Listen to all but hold click type events + channel.on_button_single_or_double_click = self._on_click + else: + # Listen to all click type events + channel.on_button_single_or_double_click_or_hold = self._on_click + + return channel + @property def name(self): """Return the name of the device.""" @@ -176,13 +208,14 @@ class FlicButton(BinarySensorDevice): def _on_click(self, channel, click_type, was_queued, time_diff): """Fire click event, if event was not queued.""" - if was_queued: + hass_click_type = self._hass_click_types[click_type] + if was_queued or hass_click_type in self._ignored_click_types: return self._hass.bus.fire(EVENT_NAME, { EVENT_DATA_NAME: self.name, EVENT_DATA_ADDRESS: self.address, - EVENT_DATA_TYPE: self._click_types[click_type] + EVENT_DATA_TYPE: hass_click_type }) def _connection_status_changed(self, channel, From 64de1c9777f09b641694a3c53375bac5db9d4fdf Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Fri, 9 Dec 2016 14:04:40 -0500 Subject: [PATCH 67/89] Bump python-nest to fix issue with Nest Cam without activity zones (#4820) * Bump python-nest to fix issue with Nest Cam without activity zones * bump to include fix python-nest dependency with hvac_state * regenerate requirements_all.txt --- homeassistant/components/nest.py | 4 ++-- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest.py b/homeassistant/components/nest.py index fd5627987a3..cd871c8e039 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -19,8 +19,8 @@ _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [ 'http://github.com/technicalpickles/python-nest' - '/archive/7a2eb38d391bddeb78079437f001224c370b555a.zip' # nest-cam branch - '#python-nest==3.0.1'] + '/archive/b8391d2b3cb8682f8b0c2bdff477179983609f39.zip' # nest-cam branch + '#python-nest==3.0.2'] DOMAIN = 'nest' diff --git a/requirements_all.txt b/requirements_all.txt index 27573a2c587..b67ef6f9d2b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -167,7 +167,7 @@ hikvision==0.4 # http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0 # homeassistant.components.nest -http://github.com/technicalpickles/python-nest/archive/7a2eb38d391bddeb78079437f001224c370b555a.zip#python-nest==3.0.1 +http://github.com/technicalpickles/python-nest/archive/b8391d2b3cb8682f8b0c2bdff477179983609f39.zip#python-nest==3.0.2 # homeassistant.components.light.flux_led https://github.com/Danielhiversen/flux_led/archive/0.9.zip#flux_led==0.9 From 167260bcc65317aa74212bbcb78ecf14c20263e2 Mon Sep 17 00:00:00 2001 From: r-jordan Date: Sat, 10 Dec 2016 11:36:35 +0100 Subject: [PATCH 68/89] [climate.generic_thermostat] Make tolerance work both ways (#4830) --- .../components/climate/generic_thermostat.py | 36 ++++---- .../climate/test_generic_thermostat.py | 84 ++++++++++++------- 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index 1b3d20d8b59..a40795c37c5 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -198,24 +198,30 @@ class GenericThermostat(ClimateDevice): return if self.ac_mode: - too_hot = self._cur_temp - self._target_temp > self._tolerance is_cooling = self._is_device_active - if too_hot and not is_cooling: - _LOGGER.info('Turning on AC %s', self.heater_entity_id) - switch.turn_on(self.hass, self.heater_entity_id) - elif not too_hot and is_cooling: - _LOGGER.info('Turning off AC %s', self.heater_entity_id) - switch.turn_off(self.hass, self.heater_entity_id) + if is_cooling: + too_cold = self._target_temp - self._cur_temp > self._tolerance + if too_cold: + _LOGGER.info('Turning off AC %s', self.heater_entity_id) + switch.turn_off(self.hass, self.heater_entity_id) + else: + too_hot = self._cur_temp - self._target_temp > self._tolerance + if too_hot: + _LOGGER.info('Turning on AC %s', self.heater_entity_id) + switch.turn_on(self.hass, self.heater_entity_id) else: - too_cold = self._target_temp - self._cur_temp > self._tolerance is_heating = self._is_device_active - - if too_cold and not is_heating: - _LOGGER.info('Turning on heater %s', self.heater_entity_id) - switch.turn_on(self.hass, self.heater_entity_id) - elif not too_cold and is_heating: - _LOGGER.info('Turning off heater %s', self.heater_entity_id) - switch.turn_off(self.hass, self.heater_entity_id) + if is_heating: + too_hot = self._cur_temp - self._target_temp > self._tolerance + if too_hot: + _LOGGER.info('Turning off heater %s', + self.heater_entity_id) + switch.turn_off(self.hass, self.heater_entity_id) + else: + too_cold = self._target_temp - self._cur_temp > self._tolerance + if too_cold: + _LOGGER.info('Turning on heater %s', self.heater_entity_id) + switch.turn_on(self.hass, self.heater_entity_id) @property def _is_device_active(self): diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/climate/test_generic_thermostat.py index 1730c3e003b..7c4ee8db58f 100644 --- a/tests/components/climate/test_generic_thermostat.py +++ b/tests/components/climate/test_generic_thermostat.py @@ -181,34 +181,10 @@ class TestClimateGenericThermostat(unittest.TestCase): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def test_set_temp_change_heater_on(self): - """Test if temperature change turn heater on.""" - self._setup_switch(False) - climate.set_temperature(self.hass, 30) - self.hass.block_till_done() - self._setup_sensor(25) - self.hass.block_till_done() - self.assertEqual(1, len(self.calls)) - call = self.calls[0] - self.assertEqual('switch', call.domain) - self.assertEqual(SERVICE_TURN_ON, call.service) - self.assertEqual(ENT_SWITCH, call.data['entity_id']) - - def test_temp_change_heater_off(self): - """Test if temperature change turn heater off.""" - self._setup_switch(True) - climate.set_temperature(self.hass, 25) - self.hass.block_till_done() - self._setup_sensor(30) - self.hass.block_till_done() - self.assertEqual(1, len(self.calls)) - call = self.calls[0] - self.assertEqual('switch', call.domain) - self.assertEqual(SERVICE_TURN_OFF, call.service) - self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def test_temp_change_heater_on_within_tolerance(self): - """Test if temperature change turn heater on within tolerance.""" + """Test if temperature change doesn't turn heater on within + tolerance. + """ self._setup_switch(False) climate.set_temperature(self.hass, 30) self.hass.block_till_done() @@ -217,9 +193,7 @@ class TestClimateGenericThermostat(unittest.TestCase): self.assertEqual(0, len(self.calls)) def test_temp_change_heater_on_outside_tolerance(self): - """Test if temperature change doesn't turn heater on outside - tolerance. - """ + """Test if temperature change turn heater on outside tolerance.""" self._setup_switch(False) climate.set_temperature(self.hass, 30) self.hass.block_till_done() @@ -231,6 +205,30 @@ class TestClimateGenericThermostat(unittest.TestCase): self.assertEqual(SERVICE_TURN_ON, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) + def test_temp_change_heater_off_within_tolerance(self): + """Test if temperature change doesn't turn heater off within + tolerance. + """ + self._setup_switch(True) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + self._setup_sensor(31) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_temp_change_heater_off_outside_tolerance(self): + """Test if temperature change turn heater off outside tolerance.""" + self._setup_switch(True) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + self._setup_sensor(35) + self.hass.block_till_done() + self.assertEqual(1, len(self.calls)) + call = self.calls[0] + self.assertEqual('switch', call.domain) + self.assertEqual(SERVICE_TURN_OFF, call.service) + self.assertEqual(ENT_SWITCH, call.data['entity_id']) + def _setup_sensor(self, temp, unit=TEMP_CELSIUS): """Setup the test sensor.""" self.hass.states.set(ENT_SENSOR, temp, { @@ -297,7 +295,18 @@ class TestClimateGenericThermostatACMode(unittest.TestCase): self.assertEqual(SERVICE_TURN_ON, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def test_set_temp_change_ac_off(self): + def test_temp_change_ac_off_within_tolerance(self): + """Test if temperature change doesn't turn ac off within + tolerance. + """ + self._setup_switch(True) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + self._setup_sensor(29.8) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_set_temp_change_ac_off_outside_tolerance(self): """Test if temperature change turn ac off.""" self._setup_switch(True) climate.set_temperature(self.hass, 30) @@ -310,7 +319,18 @@ class TestClimateGenericThermostatACMode(unittest.TestCase): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) - def test_temp_change_ac_on(self): + def test_temp_change_ac_on_within_tolerance(self): + """Test if temperature change doesn't turn ac on within + tolerance. + """ + self._setup_switch(False) + climate.set_temperature(self.hass, 25) + self.hass.block_till_done() + self._setup_sensor(25.2) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_temp_change_ac_on_outside_tolerance(self): """Test if temperature change turn ac on.""" self._setup_switch(False) climate.set_temperature(self.hass, 25) From ee5b9e72910718843af27a5023e1a022ba7ef435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sat, 10 Dec 2016 18:53:25 +0100 Subject: [PATCH 69/89] Configurable scan options for nmap (#4838) --- .../components/device_tracker/nmap_tracker.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/device_tracker/nmap_tracker.py b/homeassistant/components/device_tracker/nmap_tracker.py index 2e8bbc5d2a1..404492e35cf 100644 --- a/homeassistant/components/device_tracker/nmap_tracker.py +++ b/homeassistant/components/device_tracker/nmap_tracker.py @@ -25,6 +25,8 @@ _LOGGER = logging.getLogger(__name__) CONF_EXCLUDE = 'exclude' # Interval in minutes to exclude devices from a scan while they are home CONF_HOME_INTERVAL = 'home_interval' +CONF_OPTIONS = 'scan_options' +DEFAULT_OPTIONS = '-F --host-timeout 5s' MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) @@ -33,7 +35,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOSTS): cv.ensure_list, vol.Required(CONF_HOME_INTERVAL, default=0): cv.positive_int, vol.Optional(CONF_EXCLUDE, default=[]): - vol.All(cv.ensure_list, vol.Length(min=1)) + vol.All(cv.ensure_list, vol.Length(min=1)), + vol.Optional(CONF_OPTIONS, default=DEFAULT_OPTIONS): + cv.string }) @@ -69,8 +73,9 @@ class NmapDeviceScanner(object): self.last_results = [] self.hosts = config[CONF_HOSTS] - self.exclude = config.get(CONF_EXCLUDE, []) + self.exclude = config[CONF_EXCLUDE] minutes = config[CONF_HOME_INTERVAL] + self._options = config[CONF_OPTIONS] self.home_interval = timedelta(minutes=minutes) self.success_init = self._update_info() @@ -103,7 +108,7 @@ class NmapDeviceScanner(object): from nmap import PortScanner, PortScannerError scanner = PortScanner() - options = '-F --host-timeout 5s ' + options = self._options if self.home_interval: boundary = dt_util.now() - self.home_interval From 7ba25f35260fe6a046c90adbf44fed711a3903dd Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sun, 11 Dec 2016 09:54:50 +0100 Subject: [PATCH 70/89] Fixed crash during light objects initizilation (#4835) * Fixed crash when lights objects was inited --- homeassistant/components/light/tellstick.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/light/tellstick.py b/homeassistant/components/light/tellstick.py index 90add8f012e..d23d5e2c4d6 100644 --- a/homeassistant/components/light/tellstick.py +++ b/homeassistant/components/light/tellstick.py @@ -72,7 +72,11 @@ class TellstickLight(TellstickDevice, Light): if brightness is not None: self._brightness = brightness - self._state = (self._brightness > 0) + # _brightness is not defined when called from super + try: + self._state = (self._brightness > 0) + except AttributeError: + self._state = True else: self._state = False From cdf94646989d23023f3ddf92c1f3045e7ff8032a Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sun, 11 Dec 2016 09:05:56 +0000 Subject: [PATCH 71/89] [device_tracker.gpslogger] Add additional activity attribute. --- homeassistant/components/device_tracker/gpslogger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/device_tracker/gpslogger.py b/homeassistant/components/device_tracker/gpslogger.py index d8e7ca8b074..22099630bd1 100644 --- a/homeassistant/components/device_tracker/gpslogger.py +++ b/homeassistant/components/device_tracker/gpslogger.py @@ -63,6 +63,7 @@ class GPSLoggerView(HomeAssistantView): accuracy = int(float(data['accuracy'])) if 'battery' in data: battery = float(data['battery']) + attrs = {} if 'speed' in data: attrs['speed'] = float(data['speed']) @@ -72,6 +73,8 @@ class GPSLoggerView(HomeAssistantView): attrs['altitude'] = float(data['altitude']) if 'provider' in data: attrs['provider'] = data['provider'] + if 'activity' in data: + attrs['activity'] = data['activity'] yield from hass.loop.run_in_executor( None, partial(self.see, dev_id=device, From e0552ad89918efd95e5ab8f2e2373c8236955b00 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sun, 11 Dec 2016 13:13:43 +0000 Subject: [PATCH 72/89] [device_tracker] Don't clear GPS coordinates if no GPS seen (#4848) --- homeassistant/components/device_tracker/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index d497ea4c314..ba5e28ff48f 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -426,12 +426,11 @@ class Device(Entity): if attributes: self._attributes.update(attributes) - self.gps = None - if gps is not None: try: self.gps = float(gps[0]), float(gps[1]) except (ValueError, TypeError, IndexError): + self.gps = None _LOGGER.warning('Could not parse gps value for %s: %s', self.dev_id, gps) From 46cad514d46480e3aa17f9991fa7f41f14182246 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sun, 11 Dec 2016 19:18:11 +0200 Subject: [PATCH 73/89] Revert "[device_tracker] Don't clear GPS coordinates when using two device trackers." (#4851) --- homeassistant/components/device_tracker/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index ba5e28ff48f..d497ea4c314 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -426,11 +426,12 @@ class Device(Entity): if attributes: self._attributes.update(attributes) + self.gps = None + if gps is not None: try: self.gps = float(gps[0]), float(gps[1]) except (ValueError, TypeError, IndexError): - self.gps = None _LOGGER.warning('Could not parse gps value for %s: %s', self.dev_id, gps) From 99f1ea9b59edf03a1155381677002698f6cede88 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 11 Dec 2016 23:39:20 +0100 Subject: [PATCH 74/89] Migrate alarm control panel to async (#4807) * Merge alarm control panel to async * fix lint --- .../alarm_control_panel/__init__.py | 106 ++++++++++++------ .../alarm_control_panel/alarmdotcom.py | 5 - .../alarm_control_panel/concord232.py | 9 -- .../alarm_control_panel/envisalink.py | 2 +- .../components/alarm_control_panel/manual.py | 8 +- .../components/alarm_control_panel/nx584.py | 9 -- .../alarm_control_panel/simplisafe.py | 8 -- .../alarm_control_panel/verisure.py | 3 - 8 files changed, 77 insertions(+), 73 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 49decfc62fe..1b64431c7a1 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -4,6 +4,7 @@ Component to interface with an alarm control panel. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/alarm_control_panel/ """ +import asyncio import logging import os @@ -42,40 +43,6 @@ ALARM_SERVICE_SCHEMA = vol.Schema({ }) -def setup(hass, config): - """Track states and offer events for sensors.""" - component = EntityComponent( - logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) - - component.setup(config) - - def alarm_service_handler(service): - """Map services to methods on Alarm.""" - target_alarms = component.extract_from_service(service) - - code = service.data.get(ATTR_CODE) - - method = SERVICE_TO_METHOD[service.service] - - for alarm in target_alarms: - getattr(alarm, method)(code) - - for alarm in target_alarms: - if not alarm.should_poll: - continue - - alarm.update_ha_state(True) - - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) - - for service in SERVICE_TO_METHOD: - hass.services.register(DOMAIN, service, alarm_service_handler, - descriptions.get(service), - schema=ALARM_SERVICE_SCHEMA) - return True - - def alarm_disarm(hass, code=None, entity_id=None): """Send the alarm the command for disarm.""" data = {} @@ -120,6 +87,53 @@ def alarm_trigger(hass, code=None, entity_id=None): hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data) +@asyncio.coroutine +def async_setup(hass, config): + """Track states and offer events for sensors.""" + component = EntityComponent( + logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) + + yield from component.async_setup(config) + + @asyncio.coroutine + def async_alarm_service_handler(service): + """Map services to methods on Alarm.""" + target_alarms = component.async_extract_from_service(service) + + code = service.data.get(ATTR_CODE) + + method = "async_{}".format(SERVICE_TO_METHOD[service.service]) + + for alarm in target_alarms: + yield from getattr(alarm, method)(code) + + update_tasks = [] + for alarm in target_alarms: + if not alarm.should_poll: + continue + + update_coro = hass.loop.create_task( + alarm.async_update_ha_state(True)) + if hasattr(alarm, 'async_update'): + update_tasks.append(hass.loop.create_task(update_coro)) + else: + yield from update_coro + + if update_tasks: + yield from asyncio.wait(update_tasks, loop=hass.loop) + + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, os.path.join( + os.path.dirname(__file__), 'services.yaml')) + + for service in SERVICE_TO_METHOD: + hass.services.async_register( + DOMAIN, service, async_alarm_service_handler, + descriptions.get(service), schema=ALARM_SERVICE_SCHEMA) + + return True + + # pylint: disable=no-self-use class AlarmControlPanel(Entity): """An abstract class for alarm control devices.""" @@ -138,18 +152,42 @@ class AlarmControlPanel(Entity): """Send disarm command.""" raise NotImplementedError() + @asyncio.coroutine + def async_alarm_disarm(self, code=None): + """Send disarm command.""" + yield from self.hass.loop.run_in_executor( + None, self.alarm_disarm, code) + def alarm_arm_home(self, code=None): """Send arm home command.""" raise NotImplementedError() + @asyncio.coroutine + def async_alarm_arm_home(self, code=None): + """Send arm home command.""" + yield from self.hass.loop.run_in_executor( + None, self.alarm_arm_home, code) + def alarm_arm_away(self, code=None): """Send arm away command.""" raise NotImplementedError() + @asyncio.coroutine + def async_alarm_arm_away(self, code=None): + """Send arm away command.""" + yield from self.hass.loop.run_in_executor( + None, self.alarm_arm_away, code) + def alarm_trigger(self, code=None): """Send alarm trigger command.""" raise NotImplementedError() + @asyncio.coroutine + def async_alarm_trigger(self, code=None): + """Send alarm trigger command.""" + yield from self.hass.loop.run_in_executor( + None, self.alarm_trigger, code) + @property def state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/alarm_control_panel/alarmdotcom.py b/homeassistant/components/alarm_control_panel/alarmdotcom.py index cd37fc6a828..07f90cf4476 100644 --- a/homeassistant/components/alarm_control_panel/alarmdotcom.py +++ b/homeassistant/components/alarm_control_panel/alarmdotcom.py @@ -56,11 +56,6 @@ class AlarmDotCom(alarm.AlarmControlPanel): self._password = password self._state = STATE_UNKNOWN - @property - def should_poll(self): - """No polling needed.""" - return True - def update(self): """Fetch the latest state.""" self._state = self._alarm.state diff --git a/homeassistant/components/alarm_control_panel/concord232.py b/homeassistant/components/alarm_control_panel/concord232.py index 0bdcf274c08..de153a9e0a5 100755 --- a/homeassistant/components/alarm_control_panel/concord232.py +++ b/homeassistant/components/alarm_control_panel/concord232.py @@ -71,11 +71,6 @@ class Concord232Alarm(alarm.AlarmControlPanel): self._alarm.last_partition_update = datetime.datetime.now() self.update() - @property - def should_poll(self): - """Polling needed.""" - return True - @property def name(self): """Return the name of the device.""" @@ -126,7 +121,3 @@ class Concord232Alarm(alarm.AlarmControlPanel): def alarm_arm_away(self, code=None): """Send arm away command.""" self._alarm.arm('auto') - - def alarm_trigger(self, code=None): - """Alarm trigger command.""" - raise NotImplementedError() diff --git a/homeassistant/components/alarm_control_panel/envisalink.py b/homeassistant/components/alarm_control_panel/envisalink.py index e84320738a2..96b0fc83ea7 100644 --- a/homeassistant/components/alarm_control_panel/envisalink.py +++ b/homeassistant/components/alarm_control_panel/envisalink.py @@ -97,7 +97,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel): def _update_callback(self, partition): """Update HA state, if needed.""" if partition is None or int(partition) == self._partition_number: - self.hass.async_add_job(self.update_ha_state) + self.hass.async_add_job(self.async_update_ha_state()) @property def code_format(self): diff --git a/homeassistant/components/alarm_control_panel/manual.py b/homeassistant/components/alarm_control_panel/manual.py index 073d55508ed..cc67795d713 100644 --- a/homeassistant/components/alarm_control_panel/manual.py +++ b/homeassistant/components/alarm_control_panel/manual.py @@ -116,7 +116,7 @@ class ManualAlarm(alarm.AlarmControlPanel): self._state = STATE_ALARM_DISARMED self._state_ts = dt_util.utcnow() - self.update_ha_state() + self.schedule_update_ha_state() def alarm_arm_home(self, code=None): """Send arm home command.""" @@ -125,7 +125,7 @@ class ManualAlarm(alarm.AlarmControlPanel): self._state = STATE_ALARM_ARMED_HOME self._state_ts = dt_util.utcnow() - self.update_ha_state() + self.schedule_update_ha_state() if self._pending_time: track_point_in_time( @@ -139,7 +139,7 @@ class ManualAlarm(alarm.AlarmControlPanel): self._state = STATE_ALARM_ARMED_AWAY self._state_ts = dt_util.utcnow() - self.update_ha_state() + self.schedule_update_ha_state() if self._pending_time: track_point_in_time( @@ -151,7 +151,7 @@ class ManualAlarm(alarm.AlarmControlPanel): self._pre_trigger_state = self._state self._state = STATE_ALARM_TRIGGERED self._state_ts = dt_util.utcnow() - self.update_ha_state() + self.schedule_update_ha_state() if self._trigger_time: track_point_in_time( diff --git a/homeassistant/components/alarm_control_panel/nx584.py b/homeassistant/components/alarm_control_panel/nx584.py index cb32fc924e6..58ec8d915ab 100644 --- a/homeassistant/components/alarm_control_panel/nx584.py +++ b/homeassistant/components/alarm_control_panel/nx584.py @@ -62,11 +62,6 @@ class NX584Alarm(alarm.AlarmControlPanel): self._alarm.list_zones() self._state = STATE_UNKNOWN - @property - def should_poll(self): - """Polling needed.""" - return True - @property def name(self): """Return the name of the device.""" @@ -122,7 +117,3 @@ class NX584Alarm(alarm.AlarmControlPanel): def alarm_arm_away(self, code=None): """Send arm away command.""" self._alarm.arm('exit') - - def alarm_trigger(self, code=None): - """Alarm trigger command.""" - raise NotImplementedError() diff --git a/homeassistant/components/alarm_control_panel/simplisafe.py b/homeassistant/components/alarm_control_panel/simplisafe.py index 40ebfb2f39f..7a8f8409c59 100644 --- a/homeassistant/components/alarm_control_panel/simplisafe.py +++ b/homeassistant/components/alarm_control_panel/simplisafe.py @@ -61,11 +61,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): else: self._state = STATE_UNKNOWN - @property - def should_poll(self): - """Poll the SimpliSafe API.""" - return True - @property def name(self): """Return the name of the device.""" @@ -104,7 +99,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): return self.simplisafe.set_state('off') _LOGGER.info('SimpliSafe alarm disarming') - self.update() def alarm_arm_home(self, code=None): """Send arm home command.""" @@ -112,7 +106,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): return self.simplisafe.set_state('home') _LOGGER.info('SimpliSafe alarm arming home') - self.update() def alarm_arm_away(self, code=None): """Send arm away command.""" @@ -120,7 +113,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): return self.simplisafe.set_state('away') _LOGGER.info('SimpliSafe alarm arming away') - self.update() def _validate_code(self, code, state): """Validate given code.""" diff --git a/homeassistant/components/alarm_control_panel/verisure.py b/homeassistant/components/alarm_control_panel/verisure.py index 4ef07c68f59..c1a394fe462 100644 --- a/homeassistant/components/alarm_control_panel/verisure.py +++ b/homeassistant/components/alarm_control_panel/verisure.py @@ -84,18 +84,15 @@ class VerisureAlarm(alarm.AlarmControlPanel): hub.my_pages.alarm.set(code, 'DISARMED') _LOGGER.info('verisure alarm disarming') hub.my_pages.alarm.wait_while_pending() - self.update() def alarm_arm_home(self, code=None): """Send arm home command.""" hub.my_pages.alarm.set(code, 'ARMED_HOME') _LOGGER.info('verisure alarm arming home') hub.my_pages.alarm.wait_while_pending() - self.update() def alarm_arm_away(self, code=None): """Send arm away command.""" hub.my_pages.alarm.set(code, 'ARMED_AWAY') _LOGGER.info('verisure alarm arming away') hub.my_pages.alarm.wait_while_pending() - self.update() From 080c4efb000ebc0ea52f314ac4c17b3989858bc9 Mon Sep 17 00:00:00 2001 From: devdelay Date: Sun, 11 Dec 2016 17:46:10 -0500 Subject: [PATCH 75/89] Ecobee detect Smart Away (#4769) * Ecobee autoAway Event * Update ecobee.py Checking if event['running'] true is pointless because if false event['type'] will equal template and when true type will only be 'hold' or 'autoAway' so I've removed this check from the statement --- homeassistant/components/climate/ecobee.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/ecobee.py b/homeassistant/components/climate/ecobee.py index c98ac6d0106..84d8c32f9ff 100644 --- a/homeassistant/components/climate/ecobee.py +++ b/homeassistant/components/climate/ecobee.py @@ -195,8 +195,9 @@ class Thermostat(ClimateDevice): mode = self.mode events = self.thermostat['events'] for event in events: - if event['running']: - mode = event['holdClimateRef'] + if event['holdClimateRef'] == 'away' or \ + event['type'] == 'autoAway': + mode = "away" break return 'away' in mode From c3923b2768b0e544579ac4fd58a4c79112054c5f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bouillot Date: Sun, 11 Dec 2016 23:47:27 +0100 Subject: [PATCH 76/89] Netatmo improving Battery info (#4724) * Improving Battery info Improving battery status info (to reflect NetAtmo API documentation and change deprecated DeviceList for WeatherStationData * Fixes from previous update Fix the hound issue. add battery_lvl, WindAngle_value, GustAngle_value, rf_status_lvl, wifi_status_lvl --- homeassistant/components/sensor/netatmo.py | 73 ++++++++++++++++++---- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/sensor/netatmo.py b/homeassistant/components/sensor/netatmo.py index c3503207d8b..20c0f94a500 100644 --- a/homeassistant/components/sensor/netatmo.py +++ b/homeassistant/components/sensor/netatmo.py @@ -38,14 +38,19 @@ SENSOR_TYPES = { 'sum_rain_1': ['sum_rain_1', 'mm', 'mdi:weather-rainy'], 'sum_rain_24': ['sum_rain_24', 'mm', 'mdi:weather-rainy'], 'battery_vp': ['Battery', '', 'mdi:battery'], + 'battery_lvl': ['Battery_lvl', '', 'mdi:battery'], 'min_temp': ['Min Temp.', TEMP_CELSIUS, 'mdi:thermometer'], 'max_temp': ['Max Temp.', TEMP_CELSIUS, 'mdi:thermometer'], 'WindAngle': ['Angle', '', 'mdi:compass'], + 'WindAngle_value': ['Angle Value', 'º', 'mdi:compass'], 'WindStrength': ['Strength', 'km/h', 'mdi:weather-windy'], 'GustAngle': ['Gust Angle', '', 'mdi:compass'], + 'GustAngle_value': ['Gust Angle Value', 'º', 'mdi:compass'], 'GustStrength': ['Gust Strength', 'km/h', 'mdi:weather-windy'], 'rf_status': ['Radio', '', 'mdi:signal'], - 'wifi_status': ['Wifi', '', 'mdi:wifi'] + 'rf_status_lvl': ['Radio_lvl', '', 'mdi:signal'], + 'wifi_status': ['Wifi', '', 'mdi:wifi'], + 'wifi_status_lvl': ['Wifi_lvl', 'dBm', 'mdi:wifi'] } MODULE_SCHEMA = vol.Schema({ @@ -103,6 +108,7 @@ class NetAtmoSensor(Entity): self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] module_id = self.netatmo_data.\ station_data.moduleByName(module=module_name)['_id'] + self.module_id = module_id[1] self._unique_id = "Netatmo Sensor {0} - {1} ({2})".format(self._name, module_id, self.type) @@ -154,21 +160,58 @@ class NetAtmoSensor(Entity): self._state = data['CO2'] elif self.type == 'pressure': self._state = round(data['Pressure'], 1) - elif self.type == 'battery_vp': + elif self.type == 'battery_lvl': + self._state = data['battery_vp'] + elif self.type == 'battery_vp' and self.module_id == '6': + if data['battery_vp'] >= 5590: + self._state = "Full" + elif data['battery_vp'] >= 5180: + self._state = "High" + elif data['battery_vp'] >= 4770: + self._state = "Medium" + elif data['battery_vp'] >= 4360: + self._state = "Low" + elif data['battery_vp'] < 4360: + self._state = "Very Low" + elif self.type == 'battery_vp' and self.module_id == '5': if data['battery_vp'] >= 5500: self._state = "Full" - elif data['battery_vp'] >= 5100: + elif data['battery_vp'] >= 5000: self._state = "High" - elif data['battery_vp'] >= 4600: + elif data['battery_vp'] >= 4500: self._state = "Medium" - elif data['battery_vp'] >= 4100: + elif data['battery_vp'] >= 4000: self._state = "Low" - elif data['battery_vp'] < 4100: + elif data['battery_vp'] < 4000: + self._state = "Very Low" + elif self.type == 'battery_vp' and self.module_id == '3': + if data['battery_vp'] >= 5640: + self._state = "Full" + elif data['battery_vp'] >= 5280: + self._state = "High" + elif data['battery_vp'] >= 4920: + self._state = "Medium" + elif data['battery_vp'] >= 4560: + self._state = "Low" + elif data['battery_vp'] < 4560: + self._state = "Very Low" + elif self.type == 'battery_vp' and self.module_id == '2': + if data['battery_vp'] >= 5500: + self._state = "Full" + elif data['battery_vp'] >= 5000: + self._state = "High" + elif data['battery_vp'] >= 4500: + self._state = "Medium" + elif data['battery_vp'] >= 4000: + self._state = "Low" + elif data['battery_vp'] < 4000: self._state = "Very Low" elif self.type == 'min_temp': self._state = data['min_temp'] elif self.type == 'max_temp': self._state = data['max_temp'] + elif self.type == 'WindAngle_value': + self._state = data['WindAngle'] elif self.type == 'WindAngle': if data['WindAngle'] >= 330: self._state = "North (%d\xb0)" % data['WindAngle'] @@ -190,6 +233,8 @@ class NetAtmoSensor(Entity): self._state = "North (%d\xb0)" % data['WindAngle'] elif self.type == 'WindStrength': self._state = data['WindStrength'] + elif self.type == 'GustAngle_value': + self._state = data['GustAngle'] elif self.type == 'GustAngle': if data['GustAngle'] >= 330: self._state = "North (%d\xb0)" % data['GustAngle'] @@ -211,6 +256,8 @@ class NetAtmoSensor(Entity): self._state = "North (%d\xb0)" % data['GustAngle'] elif self.type == 'GustStrength': self._state = data['GustStrength'] + elif self.type == 'rf_status_lvl': + self._state = data['rf_status'] elif self.type == 'rf_status': if data['rf_status'] >= 90: self._state = "Low" @@ -220,13 +267,17 @@ class NetAtmoSensor(Entity): self._state = "High" elif data['rf_status'] <= 59: self._state = "Full" + elif self.type == 'wifi_status_lvl': + self._state = data['wifi_status'] elif self.type == 'wifi_status': if data['wifi_status'] >= 86: - self._state = "Bad" + self._state = "Low" elif data['wifi_status'] >= 71: - self._state = "Middle" - elif data['wifi_status'] <= 70: - self._state = "Good" + self._state = "Medium" + elif data['wifi_status'] >= 56: + self._state = "High" + elif data['wifi_status'] <= 55: + self._state = "Full" class NetAtmoData(object): @@ -248,7 +299,7 @@ class NetAtmoData(object): def update(self): """Call the Netatmo API to update the data.""" import lnetatmo - self.station_data = lnetatmo.DeviceList(self.auth) + self.station_data = lnetatmo.WeatherStationData(self.auth) if self.station is not None: self.data = self.station_data.lastData(station=self.station, From 2708e193ec95c70bc434bc7f9510cb0ed0582724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 11 Dec 2016 23:59:12 +0100 Subject: [PATCH 77/89] vlc media player (#4800) * vlc media player * Update vlc.py --- .coveragerc | 1 + homeassistant/components/media_player/vlc.py | 152 +++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 156 insertions(+) create mode 100644 homeassistant/components/media_player/vlc.py diff --git a/.coveragerc b/.coveragerc index 0d34382659b..f993482093d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -212,6 +212,7 @@ omit = homeassistant/components/media_player/snapcast.py homeassistant/components/media_player/sonos.py homeassistant/components/media_player/squeezebox.py + homeassistant/components/media_player/vlc.py homeassistant/components/media_player/yamaha.py homeassistant/components/notify/aws_lambda.py homeassistant/components/notify/aws_sns.py diff --git a/homeassistant/components/media_player/vlc.py b/homeassistant/components/media_player/vlc.py new file mode 100644 index 00000000000..ee4fef3cfde --- /dev/null +++ b/homeassistant/components/media_player/vlc.py @@ -0,0 +1,152 @@ +""" +Provide functionality to interact with vlc devices on the network. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.vlc/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.media_player import ( + SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, + MediaPlayerDevice, PLATFORM_SCHEMA, MEDIA_TYPE_MUSIC) +from homeassistant.const import (CONF_NAME, STATE_IDLE, STATE_PAUSED, + STATE_PLAYING) +import homeassistant.helpers.config_validation as cv +import homeassistant.util.dt as dt_util + +REQUIREMENTS = ['python-vlc==1.1.2'] + +_LOGGER = logging.getLogger(__name__) + + +SUPPORT_VLC = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ + SUPPORT_PLAY_MEDIA + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME): cv.string, +}) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the vlc platform.""" + add_devices([VlcDevice(config.get(CONF_NAME))]) + + +class VlcDevice(MediaPlayerDevice): + """Representation of a vlc player.""" + + def __init__(self, name): + """Initialize the vlc device.""" + import vlc + self._instance = vlc.Instance() + self._vlc = self._instance.media_player_new() + self._name = name + self._volume = None + self._muted = None + self._state = None + self._media_position_updated_at = None + self._media_position = None + self._media_duration = None + + def update(self): + """Get the latest details from the device.""" + import vlc + status = self._vlc.get_state() + if status == vlc.State.Playing: + self._state = STATE_PLAYING + elif status == vlc.State.Paused: + self._state = STATE_PAUSED + else: + self._state = STATE_IDLE + self._media_duration = self._vlc.get_length()/1000 + self._media_position = self._vlc.get_position() * self._media_duration + self._media_position_updated_at = dt_util.utcnow() + + self._volume = self._vlc.audio_get_volume() / 100 + self._muted = (self._vlc.audio_get_mute() == 1) + + return True + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self._state + + @property + def volume_level(self): + """Volume level of the media player (0..1).""" + return self._volume + + @property + def is_volume_muted(self): + """Boolean if volume is currently muted.""" + return self._muted + + @property + def supported_media_commands(self): + """Flag of media commands that are supported.""" + return SUPPORT_VLC + + @property + def media_content_type(self): + """Content type of current playing media.""" + return MEDIA_TYPE_MUSIC + + @property + def media_duration(self): + """Duration of current playing media in seconds.""" + return self._media_duration + + @property + def media_position(self): + """Position of current playing media in seconds.""" + return self._media_position + + @property + def media_position_updated_at(self): + """When was the position of the current playing media valid.""" + return self._media_position_updated_at + + def media_seek(self, position): + """Seek the media to a specific location.""" + track_length = self._vlc.get_length()/1000 + self._vlc.set_position(position/track_length) + + def mute_volume(self, mute): + """Mute the volume.""" + self._vlc.audio_set_mute(mute) + self._muted = mute + + def set_volume_level(self, volume): + """Set volume level, range 0..1.""" + self._vlc.audio_set_volume(int(volume * 100)) + self._volume = volume + + def media_play(self): + """Send play commmand.""" + self._vlc.play() + self._state = STATE_PLAYING + + def media_pause(self): + """Send pause command.""" + self._vlc.pause() + self._state = STATE_PAUSED + + def play_media(self, media_type, media_id, **kwargs): + """Play media from a URL or file.""" + if not media_type == MEDIA_TYPE_MUSIC: + _LOGGER.error( + "Invalid media type %s. Only %s is supported", + media_type, MEDIA_TYPE_MUSIC) + return + self._vlc.set_media(self._instance.media_new(media_id)) + self._vlc.play() + self._state = STATE_PLAYING diff --git a/requirements_all.txt b/requirements_all.txt index b67ef6f9d2b..f4e4bf6dbf3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -476,6 +476,9 @@ python-telegram-bot==5.2.0 # homeassistant.components.sensor.twitch python-twitch==1.3.0 +# homeassistant.components.media_player.vlc +python-vlc==1.1.2 + # homeassistant.components.wink python-wink==0.11.0 From 4d2480bbd19220a6a7135debceb2a25652884632 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sun, 11 Dec 2016 18:43:42 -0500 Subject: [PATCH 78/89] Added support to language codes on Weather Underground (#4815) * Added supported to language codes to Weather Underground * Removed unecessary None assigments --- .../components/sensor/wunderground.py | 35 ++++++++++++++++--- tests/components/sensor/test_wunderground.py | 20 ++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/sensor/wunderground.py b/homeassistant/components/sensor/wunderground.py index 3194afbe94e..2f6558cd9da 100644 --- a/homeassistant/components/sensor/wunderground.py +++ b/homeassistant/components/sensor/wunderground.py @@ -19,12 +19,15 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -_RESOURCE = 'http://api.wunderground.com/api/{}/conditions/q/' -_ALERTS = 'http://api.wunderground.com/api/{}/alerts/q/' +_RESOURCE = 'http://api.wunderground.com/api/{}/conditions/{}/q/' +_ALERTS = 'http://api.wunderground.com/api/{}/alerts/{}/q/' _LOGGER = logging.getLogger(__name__) CONF_ATTRIBUTION = "Data provided by the WUnderground weather service" CONF_PWS_ID = 'pws_id' +CONF_LANG = 'lang' + +DEFAULT_LANG = 'EN' MIN_TIME_BETWEEN_UPDATES_ALERTS = timedelta(minutes=15) MIN_TIME_BETWEEN_UPDATES_OBSERVATION = timedelta(minutes=5) @@ -80,9 +83,29 @@ ALERTS_ATTRS = [ 'message', ] +# Language Supported Codes +LANG_CODES = [ + 'AF', 'AL', 'AR', 'HY', 'AZ', 'EU', + 'BY', 'BU', 'LI', 'MY', 'CA', 'CN', + 'TW', 'CR', 'CZ', 'DK', 'DV', 'NL', + 'EN', 'EO', 'ET', 'FA', 'FI', 'FR', + 'FC', 'GZ', 'DL', 'KA', 'GR', 'GU', + 'HT', 'IL', 'HI', 'HU', 'IS', 'IO', + 'ID', 'IR', 'IT', 'JP', 'JW', 'KM', + 'KR', 'KU', 'LA', 'LV', 'LT', 'ND', + 'MK', 'MT', 'GM', 'MI', 'MR', 'MN', + 'NO', 'OC', 'PS', 'GN', 'PL', 'BR', + 'PA', 'PU', 'RO', 'RU', 'SR', 'SK', + 'SL', 'SP', 'SI', 'SW', 'CH', 'TL', + 'TT', 'TH', 'UA', 'UZ', 'VU', 'CY', + 'SN', 'JI', 'YI', +] + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): cv.string, vol.Optional(CONF_PWS_ID): cv.string, + vol.Optional(CONF_LANG, default=DEFAULT_LANG): + vol.All(vol.In(LANG_CODES)), vol.Required(CONF_MONITORED_CONDITIONS, default=[]): vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), }) @@ -92,7 +115,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the WUnderground sensor.""" rest = WUndergroundData(hass, config.get(CONF_API_KEY), - config.get(CONF_PWS_ID, None)) + config.get(CONF_PWS_ID), + config.get(CONF_LANG)) sensors = [] for variable in config[CONF_MONITORED_CONDITIONS]: sensors.append(WUndergroundSensor(rest, variable)) @@ -192,18 +216,19 @@ class WUndergroundSensor(Entity): class WUndergroundData(object): """Get data from WUnderground.""" - def __init__(self, hass, api_key, pws_id=None): + def __init__(self, hass, api_key, pws_id, lang): """Initialize the data object.""" self._hass = hass self._api_key = api_key self._pws_id = pws_id + self._lang = 'lang:{}'.format(lang) self._latitude = hass.config.latitude self._longitude = hass.config.longitude self.data = None self.alerts = None def _build_url(self, baseurl=_RESOURCE): - url = baseurl.format(self._api_key) + url = baseurl.format(self._api_key, self._lang) if self._pws_id: url = url + 'pws:{}'.format(self._pws_id) else: diff --git a/tests/components/sensor/test_wunderground.py b/tests/components/sensor/test_wunderground.py index 05c7fc93921..7c92ac20424 100644 --- a/tests/components/sensor/test_wunderground.py +++ b/tests/components/sensor/test_wunderground.py @@ -23,6 +23,16 @@ VALID_CONFIG = { ] } +INVALID_CONFIG = { + 'platform': 'wunderground', + 'api_key': 'BOB', + 'pws_id': 'bar', + 'lang': 'foo', + 'monitored_conditions': [ + 'weather', 'feelslike_c', 'alerts' + ] +} + FEELS_LIKE = '40' WEATHER = 'Clear' HTTPS_ICON_URL = 'https://icons.wxug.com/i/c/k/clear.gif' @@ -128,17 +138,9 @@ class TestWundergroundSetup(unittest.TestCase): self.assertTrue( wunderground.setup_platform(self.hass, VALID_CONFIG, self.add_devices, None)) - invalid_config = { - 'platform': 'wunderground', - 'api_key': 'BOB', - 'pws_id': 'bar', - 'monitored_conditions': [ - 'weather', 'feelslike_c', 'alerts' - ] - } self.assertTrue( - wunderground.setup_platform(self.hass, invalid_config, + wunderground.setup_platform(self.hass, INVALID_CONFIG, self.add_devices, None)) @unittest.mock.patch('requests.get', side_effect=mocked_requests_get) From 6edb54052f203961f11bfd7df8b6dd646d540afd Mon Sep 17 00:00:00 2001 From: IoTGuy Date: Mon, 12 Dec 2016 00:46:55 +0100 Subject: [PATCH 79/89] adding sensehat plugin (#4775) * adding sensehat plugin * added * fix PR * requirement updated * Update sensehat.py --- .coveragerc | 1 + homeassistant/components/sensor/sensehat.py | 134 ++++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 138 insertions(+) create mode 100644 homeassistant/components/sensor/sensehat.py diff --git a/.coveragerc b/.coveragerc index f993482093d..4aae4cbc242 100644 --- a/.coveragerc +++ b/.coveragerc @@ -296,6 +296,7 @@ omit = homeassistant/components/sensor/pvoutput.py homeassistant/components/sensor/sabnzbd.py homeassistant/components/sensor/scrape.py + homeassistant/components/sensor/sensehat.py homeassistant/components/sensor/serial_pm.py homeassistant/components/sensor/snmp.py homeassistant/components/sensor/sonarr.py diff --git a/homeassistant/components/sensor/sensehat.py b/homeassistant/components/sensor/sensehat.py new file mode 100644 index 00000000000..50cd233b39b --- /dev/null +++ b/homeassistant/components/sensor/sensehat.py @@ -0,0 +1,134 @@ +""" +Support for Sense HAT sensors. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/sensor.sensehat +""" +import os +import logging +from datetime import timedelta + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import (TEMP_CELSIUS, CONF_DISPLAY_OPTIONS, CONF_NAME) +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +REQUIREMENTS = ['sense-hat==2.2.0'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'sensehat' + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + +SENSOR_TYPES = { + 'temperature': ['temperature', TEMP_CELSIUS], + 'humidity': ['humidity', "%"], + 'pressure': ['pressure', "mb"], +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_DISPLAY_OPTIONS, default=SENSOR_TYPES): + [vol.In(SENSOR_TYPES)], + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, +}) + + +def get_cpu_temp(): + """Get CPU temperature.""" + res = os.popen("vcgencmd measure_temp").readline() + t_cpu = float(res.replace("temp=", "").replace("'C\n", "")) + return t_cpu + + +def get_average(temp_base): + """Use moving average to get better readings.""" + if not hasattr(get_average, "temp"): + get_average.temp = [temp_base, temp_base, temp_base] + get_average.temp[2] = get_average.temp[1] + get_average.temp[1] = get_average.temp[0] + get_average.temp[0] = temp_base + temp_avg = (get_average.temp[0]+get_average.temp[1]+get_average.temp[2])/3 + return temp_avg + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the sensor platform.""" + data = SenseHatData() + dev = [] + + for variable in config[CONF_DISPLAY_OPTIONS]: + dev.append(SenseHatSensor(data, variable)) + + add_devices(dev) + + +class SenseHatSensor(Entity): + """Representation of a sensehat sensor.""" + + def __init__(self, data, sensor_types): + """Initialize the sensor.""" + self.data = data + self._name = SENSOR_TYPES[sensor_types][0] + self._unit_of_measurement = SENSOR_TYPES[sensor_types][1] + self.type = sensor_types + self._state = None + """updating data.""" + self.update() + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return self._unit_of_measurement + + def update(self): + """Get the latest data and updates the states.""" + self.data.update() + if not self.data.humidity: + _LOGGER.error("Don't receive data!") + return + + if self.type == 'temperature': + self._state = self.data.temperature + if self.type == 'humidity': + self._state = self.data.humidity + if self.type == 'pressure': + self._state = self.data.pressure + + +class SenseHatData(object): + """Get the latest data and update.""" + + def __init__(self): + """Initialize the data object.""" + self.temperature = None + self.humidity = None + self.pressure = None + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Get the latest data from sensehat.""" + from sense_hat import SenseHat + sense = SenseHat() + temp_from_h = sense.get_temperature_from_humidity() + temp_from_p = sense.get_temperature_from_pressure() + t_cpu = get_cpu_temp() + t_total = (temp_from_h+temp_from_p)/2 + t_correct = t_total - ((t_cpu-t_total)/1.5) + t_correct = get_average(t_correct) + self.temperature = t_correct + self.humidity = sense.get_humidity() + self.pressure = sense.get_pressure() diff --git a/requirements_all.txt b/requirements_all.txt index f4e4bf6dbf3..4b2763f26bb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -521,6 +521,9 @@ scsgate==0.1.0 # homeassistant.components.notify.sendgrid sendgrid==3.6.3 +# homeassistant.components.sensor.sensehat +sense-hat==2.2.0 + # homeassistant.components.media_player.aquostv sharp-aquos-rc==0.2 From ecc514b7e487e5dc4a9f740070c21c21d3dae04d Mon Sep 17 00:00:00 2001 From: Jeff Wilson Date: Sun, 11 Dec 2016 18:48:47 -0500 Subject: [PATCH 80/89] Use current mode to determine which temperature attributes to use (#4858) --- homeassistant/components/climate/nest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/nest.py b/homeassistant/components/climate/nest.py index ab41c10e6d1..e098c3c3709 100644 --- a/homeassistant/components/climate/nest.py +++ b/homeassistant/components/climate/nest.py @@ -155,8 +155,8 @@ class NestThermostat(ClimateDevice): """Set new target temperature.""" target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW) target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH) - if target_temp_low is not None and target_temp_high is not None: - if self._mode == STATE_HEAT_COOL: + if self._mode == STATE_HEAT_COOL: + if target_temp_low is not None and target_temp_high is not None: temp = (target_temp_low, target_temp_high) else: temp = kwargs.get(ATTR_TEMPERATURE) From f4b5c439a1365ed79dc8740742c5fb4b969f3221 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 11 Dec 2016 17:34:13 -0800 Subject: [PATCH 81/89] Fix Plex from doing I/O inside event loop (#4857) --- homeassistant/components/media_player/plex.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index 76278722291..5aaee6a341e 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -200,6 +200,7 @@ class PlexClient(MediaPlayerDevice): self.update_devices = update_devices self.update_sessions = update_sessions self.set_device(device) + self._season = None def set_device(self, device): """Set the device property.""" @@ -240,9 +241,15 @@ class PlexClient(MediaPlayerDevice): def update(self): """Get the latest details.""" + from plexapi.video import Show + self.update_devices(no_throttle=True) self.update_sessions(no_throttle=True) + if isinstance(self.session, Show): + self._season = self._convert_na_to_none( + self.session.seasons()[0].index) + # pylint: disable=no-self-use, singleton-comparison def _convert_na_to_none(self, value): """Convert PlexAPI _NA() instances to None.""" @@ -310,9 +317,7 @@ class PlexClient(MediaPlayerDevice): @property def media_season(self): """Season of curent playing media (TV Show only).""" - from plexapi.video import Show - if isinstance(self.session, Show): - return self._convert_na_to_none(self.session.seasons()[0].index) + return self._season @property def media_series_title(self): From df98d5b3c12206c858d3dd7c2943d3bad6228a39 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 11 Dec 2016 17:34:26 -0800 Subject: [PATCH 82/89] Fix Nest doing I/O inside event loop (#4855) --- homeassistant/components/sensor/nest.py | 31 +++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/sensor/nest.py b/homeassistant/components/sensor/nest.py index 53f767ab494..b9dde96a98c 100644 --- a/homeassistant/components/sensor/nest.py +++ b/homeassistant/components/sensor/nest.py @@ -128,13 +128,20 @@ class NestSensor(Entity): # device specific self._location = self.device.where - self._name = self.device.name_long + self._name = "{} {}".format(self.device.name_long, + self.variable.replace("_", " ")) self._state = None + self._unit = None @property def name(self): """Return the name of the nest, if any.""" - return "{} {}".format(self._name, self.variable.replace("_", " ")) + return self._name + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return self._unit class NestBasicSensor(NestSensor): @@ -145,13 +152,10 @@ class NestBasicSensor(NestSensor): """Return the state of the sensor.""" return self._state - @property - def unit_of_measurement(self): - """Return the unit the value is expressed in.""" - return SENSOR_UNITS.get(self.variable, None) - def update(self): """Retrieve latest state.""" + self._unit = SENSOR_UNITS.get(self.variable, None) + if self.variable == 'operation_mode': self._state = getattr(self.device, "mode") else: @@ -161,14 +165,6 @@ class NestBasicSensor(NestSensor): class NestTempSensor(NestSensor): """Representation of a Nest Temperature sensor.""" - @property - def unit_of_measurement(self): - """Return the unit the value is expressed in.""" - if self.device.temperature_scale == 'C': - return TEMP_CELSIUS - else: - return TEMP_FAHRENHEIT - @property def state(self): """Return the state of the sensor.""" @@ -176,6 +172,11 @@ class NestTempSensor(NestSensor): def update(self): """Retrieve latest state.""" + if self.device.temperature_scale == 'C': + self._unit = TEMP_CELSIUS + else: + self._unit = TEMP_FAHRENHEIT + temp = getattr(self.device, self.variable) if temp is None: self._state = None From 48928d1f9e128b63ef7f5d96e07f4ee8bde7b422 Mon Sep 17 00:00:00 2001 From: David-Leon Pohl Date: Mon, 12 Dec 2016 02:38:33 +0100 Subject: [PATCH 83/89] Fix config validation (#4853) (#4854) --- homeassistant/components/switch/pilight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/switch/pilight.py b/homeassistant/components/switch/pilight.py index a052848cb21..84cbbd9fb0e 100644 --- a/homeassistant/components/switch/pilight.py +++ b/homeassistant/components/switch/pilight.py @@ -35,7 +35,7 @@ COMMAND_SCHEMA = vol.Schema({ vol.Optional(CONF_ID): cv.positive_int, vol.Optional(CONF_STATE): cv.string, vol.Optional(CONF_SYSTEMCODE): cv.positive_int, -}) +}, extra=vol.ALLOW_EXTRA) SWITCHES_SCHEMA = vol.Schema({ vol.Required(CONF_ON_CODE): COMMAND_SCHEMA, From b156ae7812251c064af23e6a3548b5f0ddc08a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?= Date: Mon, 12 Dec 2016 02:59:30 +0100 Subject: [PATCH 84/89] Add support for Hue LightGroups (#4744) * Add support for Hue LightGroup entity * Don't filter on LightGroup and add properties for a group * Reuse code from HueLight in HueLightGroup * Remove HueLightGroup and add is_group variable to HueLight * Make linter happy * Update light or lightgroup state when a new state is available * Use schedule_update_ha_state() to schedule the state update. Drop new_lightgroups and use new_lights instead. * code style fix --- homeassistant/components/light/hue.py | 66 ++++++++++++++++++++------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index da2158ba78d..74247ad24ef 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -142,6 +142,7 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): configurator.request_done(request_id) lights = {} + lightgroups = {} @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_lights(): @@ -153,9 +154,15 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): _LOGGER.exception("Cannot reach the bridge") return - api_states = api.get('lights') + api_lights = api.get('lights') - if not isinstance(api_states, dict): + if not isinstance(api_lights, dict): + _LOGGER.error("Got unexpected result from Hue API") + return + + api_groups = api.get('groups') + + if not isinstance(api_groups, dict): _LOGGER.error("Got unexpected result from Hue API") return @@ -167,7 +174,7 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): else: bridge_type = 'hue' - for light_id, info in api_states.items(): + for light_id, info in api_lights.items(): if light_id not in lights: lights[light_id] = HueLight(int(light_id), info, bridge, update_lights, @@ -175,6 +182,17 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): new_lights.append(lights[light_id]) else: lights[light_id].info = info + lights[light_id].schedule_update_ha_state() + + for lightgroup_id, info in api_groups.items(): + if lightgroup_id not in lightgroups: + lightgroups[lightgroup_id] = HueLight( + int(lightgroup_id), info, bridge, update_lights, + bridge_type, allow_unreachable, True) + new_lights.append(lightgroups[lightgroup_id]) + else: + lightgroups[lightgroup_id].info = info + lightgroups[lightgroup_id].schedule_update_ha_state() if new_lights: add_devices(new_lights) @@ -229,15 +247,20 @@ class HueLight(Light): """Representation of a Hue light.""" def __init__(self, light_id, info, bridge, update_lights, - bridge_type, allow_unreachable): + bridge_type, allow_unreachable, is_group=False): """Initialize the light.""" self.light_id = light_id self.info = info self.bridge = bridge self.update_lights = update_lights self.bridge_type = bridge_type - self.allow_unreachable = allow_unreachable + self.is_group = is_group + + if is_group: + self._command_func = self.bridge.set_group + else: + self._command_func = self.bridge.set_light @property def unique_id(self): @@ -247,33 +270,44 @@ class HueLight(Light): @property def name(self): - """Return the mame of the Hue light.""" + """Return the name of the Hue light.""" return self.info.get('name', DEVICE_DEFAULT_NAME) @property def brightness(self): """Return the brightness of this light between 0..255.""" - return self.info['state'].get('bri') + if self.is_group: + return self.info['action'].get('bri') + else: + return self.info['state'].get('bri') @property def xy_color(self): """Return the XY color value.""" - return self.info['state'].get('xy') + if self.is_group: + return self.info['action'].get('xy') + else: + return self.info['state'].get('xy') @property def color_temp(self): """Return the CT color value.""" - return self.info['state'].get('ct') + if self.is_group: + return self.info['action'].get('ct') + else: + return self.info['state'].get('ct') @property def is_on(self): """Return true if device is on.""" - self.update_lights() - - if self.allow_unreachable: - return self.info['state']['on'] + if self.is_group: + return self.info['state']['any_on'] else: - return self.info['state']['reachable'] and self.info['state']['on'] + if self.allow_unreachable: + return self.info['state']['on'] + else: + return self.info['state']['reachable'] and \ + self.info['state']['on'] @property def supported_features(self): @@ -322,7 +356,7 @@ class HueLight(Light): elif self.bridge_type == 'hue': command['effect'] = 'none' - self.bridge.set_light(self.light_id, command) + self._command_func(self.light_id, command) def turn_off(self, **kwargs): """Turn the specified or all lights off.""" @@ -344,7 +378,7 @@ class HueLight(Light): elif self.bridge_type == 'hue': command['alert'] = 'none' - self.bridge.set_light(self.light_id, command) + self._command_func(self.light_id, command) def update(self): """Synchronize state with bridge.""" From 04aa4e898a31e459a4f478989ea13c6d6f3efd97 Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Mon, 12 Dec 2016 06:04:36 +0100 Subject: [PATCH 85/89] Improve denon media_player (#4836) * Add debug level logging of messages in denon * Added media stop for Denon AVR Media Player * Sort source list * Rework input selection for Denon AVR This reworks the input selection, adding more modes and making it so that the media controls are only announced in modes where they actually makes sense. * Added real media info for Denon AVR Media modes * Read more configuration from denon devices This reads network name, and overrides the local name with that. This also reads the source names and reconfigures the input list to those names, and also reads the source deleted list and removes the inputs that are set to deleted in the device. * Discover and handle max volume in Denon media player * Rework source discovery in Denon media player This uses SSFUN as authorative source for which sources that we should present. --- .../components/media_player/denon.py | 113 +++++++++++++++--- 1 file changed, 95 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/media_player/denon.py b/homeassistant/components/media_player/denon.py index b167ee52808..badbae162a2 100755 --- a/homeassistant/components/media_player/denon.py +++ b/homeassistant/components/media_player/denon.py @@ -13,7 +13,7 @@ from homeassistant.components.media_player import ( PLATFORM_SCHEMA, SUPPORT_NEXT_TRACK, SUPPORT_SELECT_SOURCE, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, - MediaPlayerDevice) + SUPPORT_STOP, MediaPlayerDevice) from homeassistant.const import ( CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv @@ -22,16 +22,32 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Music station' -SUPPORT_DENON = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \ - SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | \ - SUPPORT_SELECT_SOURCE | SUPPORT_NEXT_TRACK | \ - SUPPORT_TURN_ON | SUPPORT_TURN_OFF +SUPPORT_DENON = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ + SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE \ + +SUPPORT_MEDIA_MODES = SUPPORT_PAUSE | SUPPORT_STOP | \ + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) +NORMAL_INPUTS = {'Cd': 'CD', 'Dvd': 'DVD', 'Blue ray': 'BD', 'TV': 'TV', + 'Satelite / Cable': 'SAT/CBL', 'Game': 'GAME', + 'Game2': 'GAME2', 'Video Aux': 'V.AUX', 'Dock': 'DOCK'} + +MEDIA_MODES = {'Tuner': 'TUNER', 'Media server': 'SERVER', + 'Ipod dock': 'IPOD', 'Net/USB': 'NET/USB', + 'Rapsody': 'RHAPSODY', 'Napster': 'NAPSTER', + 'Pandora': 'PANDORA', 'LastFM': 'LASTFM', + 'Flickr': 'FLICKR', 'Favorites': 'FAVORITES', + 'Internet Radio': 'IRADIO', 'USB/IPOD': 'USB/IPOD'} + +# Sub-modes of 'NET/USB' +# {'USB': 'USB', 'iPod Direct': 'IPD', 'Internet Radio': 'IRP', +# 'Favorites': 'FVP'} + def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Denon platform.""" @@ -53,14 +69,39 @@ class DenonDevice(MediaPlayerDevice): self._host = host self._pwstate = 'PWSTANDBY' self._volume = 0 - self._source_list = {'TV': 'SITV', 'Tuner': 'SITUNER', - 'Internet Radio': 'SIIRP', 'Favorites': 'SIFVP'} + # Initial value 60dB, changed if we get a MVMAX + self._volume_max = 60 + self._source_list = NORMAL_INPUTS.copy() + self._source_list.update(MEDIA_MODES) self._muted = False self._mediasource = '' + self._mediainfo = '' + + self._should_setup_sources = True + + def _setup_sources(self, telnet): + # NSFRN - Network name + self._name = self.telnet_request(telnet, 'NSFRN ?')[len('NSFRN '):] + + # SSFUN - Configured sources with names + self._source_list = {} + for line in self.telnet_request(telnet, 'SSFUN ?', all_lines=True): + source, configured_name = line[len('SSFUN'):].split(" ", 1) + self._source_list[configured_name] = source + + # SSSOD - Deleted sources + for line in self.telnet_request(telnet, 'SSSOD ?', all_lines=True): + source, status = line[len('SSSOD'):].split(" ", 1) + if status == 'DEL': + for pretty_name, name in self._source_list.items(): + if source == name: + del self._source_list[pretty_name] + break @classmethod - def telnet_request(cls, telnet, command): + def telnet_request(cls, telnet, command, all_lines=False): """Execute `command` and return the response.""" + _LOGGER.debug('Sending: "%s"', command) telnet.write(command.encode('ASCII') + b'\r') lines = [] while True: @@ -68,12 +109,16 @@ class DenonDevice(MediaPlayerDevice): if not line: break lines.append(line.decode('ASCII').strip()) + _LOGGER.debug('Recived: "%s"', line) + if all_lines: + return lines return lines[0] def telnet_command(self, command): """Establish a telnet connection and sends `command`.""" telnet = telnetlib.Telnet(self._host) + _LOGGER.debug('Sending: "%s"', command) telnet.write(command.encode('ASCII') + b'\r') telnet.read_very_eager() # skip response telnet.close() @@ -85,12 +130,30 @@ class DenonDevice(MediaPlayerDevice): except OSError: return False + if self._should_setup_sources: + self._setup_sources(telnet) + self._should_setup_sources = False + self._pwstate = self.telnet_request(telnet, 'PW?') - volume_str = self.telnet_request(telnet, 'MV?')[len('MV'):] - self._volume = int(volume_str) / 60 + for line in self.telnet_request(telnet, 'MV?', all_lines=True): + if line.startswith('MVMAX '): + # only grab two digit max, don't care about any half digit + self._volume_max = int(line[len('MVMAX '):len('MVMAX XX')]) + continue + if line.startswith('MV'): + self._volume = int(line[len('MV'):]) self._muted = (self.telnet_request(telnet, 'MU?') == 'MUON') self._mediasource = self.telnet_request(telnet, 'SI?')[len('SI'):] + if self._mediasource in MEDIA_MODES.values(): + self._mediainfo = "" + answer_codes = ["NSE0", "NSE1X", "NSE2X", "NSE3X", "NSE4", "NSE5", + "NSE6", "NSE7", "NSE8"] + for line in self.telnet_request(telnet, 'NSE', all_lines=True): + self._mediainfo += line[len(answer_codes.pop()):] + '\n' + else: + self._mediainfo = self.source + telnet.close() return True @@ -112,7 +175,7 @@ class DenonDevice(MediaPlayerDevice): @property def volume_level(self): """Volume level of the media player (0..1).""" - return self._volume + return self._volume / self._volume_max @property def is_volume_muted(self): @@ -122,17 +185,27 @@ class DenonDevice(MediaPlayerDevice): @property def source_list(self): """List of available input sources.""" - return list(self._source_list.keys()) + return sorted(list(self._source_list.keys())) @property def media_title(self): - """Current media source.""" - return self._mediasource + """Current media info.""" + return self._mediainfo @property def supported_media_commands(self): """Flag of media commands that are supported.""" - return SUPPORT_DENON + if self._mediasource in MEDIA_MODES.values(): + return SUPPORT_DENON | SUPPORT_MEDIA_MODES + else: + return SUPPORT_DENON + + @property + def source(self): + """Return the current input source.""" + for pretty_name, name in self._source_list.items(): + if self._mediasource == name: + return pretty_name def turn_off(self): """Turn off media player.""" @@ -148,8 +221,8 @@ class DenonDevice(MediaPlayerDevice): def set_volume_level(self, volume): """Set volume level, range 0..1.""" - # 60dB max - self.telnet_command('MV' + str(round(volume * 60)).zfill(2)) + self.telnet_command('MV' + + str(round(volume * self._volume_max)).zfill(2)) def mute_volume(self, mute): """Mute (true) or unmute (false) media player.""" @@ -163,6 +236,10 @@ class DenonDevice(MediaPlayerDevice): """Pause media player.""" self.telnet_command('NS9B') + def media_stop(self): + """Pause media player.""" + self.telnet_command('NS9C') + def media_next_track(self): """Send the next track command.""" self.telnet_command('NS9D') @@ -177,4 +254,4 @@ class DenonDevice(MediaPlayerDevice): def select_source(self, source): """Select input source.""" - self.telnet_command(self._source_list.get(source)) + self.telnet_command('SI' + self._source_list.get(source)) From 2a7fa5afc352ffd7ba9c5542e0d7133e43b9d0f1 Mon Sep 17 00:00:00 2001 From: Erik Eriksson Date: Mon, 12 Dec 2016 06:39:37 +0100 Subject: [PATCH 86/89] Telldus Live: (#4645) - Implemented support for covers and dimmable lights. - Removed global object, use hass.data. - Disabled polling via update. - Inherit from common TelldusLiveEntity device. - Configurable polling interval - Use https API endpoint - Use tellduslive package --- homeassistant/components/cover/tellduslive.py | 46 +++ homeassistant/components/light/tellduslive.py | 59 ++++ .../components/sensor/tellduslive.py | 138 +++----- .../components/switch/tellduslive.py | 46 +-- homeassistant/components/tellduslive.py | 298 +++++++++--------- requirements_all.txt | 2 +- 6 files changed, 314 insertions(+), 275 deletions(-) create mode 100644 homeassistant/components/cover/tellduslive.py create mode 100644 homeassistant/components/light/tellduslive.py diff --git a/homeassistant/components/cover/tellduslive.py b/homeassistant/components/cover/tellduslive.py new file mode 100644 index 00000000000..c48a14e9133 --- /dev/null +++ b/homeassistant/components/cover/tellduslive.py @@ -0,0 +1,46 @@ +""" +Support for Tellstick covers using Tellstick Net. + +This platform uses the Telldus Live online service. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/cover.tellduslive/ + +""" +import logging + +from homeassistant.components.cover import CoverDevice +from homeassistant.components.tellduslive import TelldusLiveEntity + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup covers.""" + if discovery_info is None: + return + add_devices(TelldusLiveCover(hass, cover) for cover in discovery_info) + + +class TelldusLiveCover(TelldusLiveEntity, CoverDevice): + """Representation of a cover.""" + + @property + def is_closed(self): + """Return the current position of the cover.""" + return self.device.is_down + + def close_cover(self, **kwargs): + """Close the cover.""" + self.device.down() + self.changed() + + def open_cover(self, **kwargs): + """Open the cover.""" + self.device.up() + self.changed() + + def stop_cover(self, **kwargs): + """Stop the cover.""" + self.device.stop() + self.changed() diff --git a/homeassistant/components/light/tellduslive.py b/homeassistant/components/light/tellduslive.py new file mode 100644 index 00000000000..f919268c380 --- /dev/null +++ b/homeassistant/components/light/tellduslive.py @@ -0,0 +1,59 @@ +""" +Support for Tellstick switches using Tellstick Net. + +This platform uses the Telldus Live online service. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.tellduslive/ + +""" +import logging + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) +from homeassistant.components.tellduslive import TelldusLiveEntity + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup lights.""" + if discovery_info is None: + return + add_devices(TelldusLiveLight(hass, light) for light in discovery_info) + + +class TelldusLiveLight(TelldusLiveEntity, Light): + """Representation of a light.""" + + def changed(self): + """A property of the device might have changed.""" + # pylint: disable=attribute-defined-outside-init + self._last_brightness = self.brightness + super().changed() + + @property + def brightness(self): + """Return the brightness of this light between 0..255.""" + return self.device.dim_level + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_BRIGHTNESS + + @property + def is_on(self): + """Return true if light is on.""" + return self.device.is_on + + def turn_on(self, **kwargs): + """Turn the light on.""" + brightness = kwargs.get(ATTR_BRIGHTNESS, self._last_brightness) + self.device.dim(level=brightness) + self.changed() + + def turn_off(self, **kwargs): + """Turn the light off.""" + self.device.turn_off() + self.changed() diff --git a/homeassistant/components/sensor/tellduslive.py b/homeassistant/components/sensor/tellduslive.py index 915bbac429f..abc5843ad91 100644 --- a/homeassistant/components/sensor/tellduslive.py +++ b/homeassistant/components/sensor/tellduslive.py @@ -6,37 +6,32 @@ https://home-assistant.io/components/sensor.tellduslive/ """ import logging -from datetime import datetime -from homeassistant.components import tellduslive -from homeassistant.const import ( - ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, TEMP_CELSIUS) -from homeassistant.helpers.entity import Entity - -ATTR_LAST_UPDATED = "time_last_updated" +from homeassistant.components.tellduslive import TelldusLiveEntity +from homeassistant.const import TEMP_CELSIUS _LOGGER = logging.getLogger(__name__) -SENSOR_TYPE_TEMP = "temp" -SENSOR_TYPE_HUMIDITY = "humidity" -SENSOR_TYPE_RAINRATE = "rrate" -SENSOR_TYPE_RAINTOTAL = "rtot" -SENSOR_TYPE_WINDDIRECTION = "wdir" -SENSOR_TYPE_WINDAVERAGE = "wavg" -SENSOR_TYPE_WINDGUST = "wgust" -SENSOR_TYPE_WATT = "watt" -SENSOR_TYPE_LUMINANCE = "lum" +SENSOR_TYPE_TEMP = 'temp' +SENSOR_TYPE_HUMIDITY = 'humidity' +SENSOR_TYPE_RAINRATE = 'rrate' +SENSOR_TYPE_RAINTOTAL = 'rtot' +SENSOR_TYPE_WINDDIRECTION = 'wdir' +SENSOR_TYPE_WINDAVERAGE = 'wavg' +SENSOR_TYPE_WINDGUST = 'wgust' +SENSOR_TYPE_WATT = 'watt' +SENSOR_TYPE_LUMINANCE = 'lum' SENSOR_TYPES = { - SENSOR_TYPE_TEMP: ['Temperature', TEMP_CELSIUS, "mdi:thermometer"], - SENSOR_TYPE_HUMIDITY: ['Humidity', '%', "mdi:water"], - SENSOR_TYPE_RAINRATE: ['Rain rate', 'mm', "mdi:water"], - SENSOR_TYPE_RAINTOTAL: ['Rain total', 'mm', "mdi:water"], - SENSOR_TYPE_WINDDIRECTION: ['Wind direction', '', ""], - SENSOR_TYPE_WINDAVERAGE: ['Wind average', 'm/s', ""], - SENSOR_TYPE_WINDGUST: ['Wind gust', 'm/s', ""], - SENSOR_TYPE_WATT: ['Watt', 'W', ""], - SENSOR_TYPE_LUMINANCE: ['Luminance', 'lx', ""], + SENSOR_TYPE_TEMP: ['Temperature', TEMP_CELSIUS, 'mdi:thermometer'], + SENSOR_TYPE_HUMIDITY: ['Humidity', '%', 'mdi:water'], + SENSOR_TYPE_RAINRATE: ['Rain rate', 'mm', 'mdi:water'], + SENSOR_TYPE_RAINTOTAL: ['Rain total', 'mm', 'mdi:water'], + SENSOR_TYPE_WINDDIRECTION: ['Wind direction', '', ''], + SENSOR_TYPE_WINDAVERAGE: ['Wind average', 'm/s', ''], + SENSOR_TYPE_WINDGUST: ['Wind gust', 'm/s', ''], + SENSOR_TYPE_WATT: ['Watt', 'W', ''], + SENSOR_TYPE_LUMINANCE: ['Luminance', 'lx', ''], } @@ -44,114 +39,75 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup Tellstick sensors.""" if discovery_info is None: return - add_devices(TelldusLiveSensor(sensor) for sensor in discovery_info) + add_devices(TelldusLiveSensor(hass, sensor) for sensor in discovery_info) -class TelldusLiveSensor(Entity): +class TelldusLiveSensor(TelldusLiveEntity): """Representation of a Telldus Live sensor.""" - def __init__(self, sensor_id): - """Initialize the sensor.""" - self._id = sensor_id - self.update() - _LOGGER.debug("created sensor %s", self) - - def update(self): - """Update sensor values.""" - tellduslive.NETWORK.update_sensors() - self._sensor = tellduslive.NETWORK.get_sensor(self._id) + @property + def device_id(self): + """Return id of the device.""" + return self._id[0] @property - def _sensor_name(self): - """Return the name of the sensor.""" - return self._sensor["name"] - - @property - def _sensor_value(self): - """Return the value the sensor.""" - return self._sensor["data"]["value"] - - @property - def _sensor_type(self): + def _type(self): """Return the type of the sensor.""" - return self._sensor["data"]["name"] + return self._id[1] @property - def _battery_level(self): - """Return the battery level of a sensor.""" - sensor_battery_level = self._sensor.get("battery") - return round(sensor_battery_level * 100 / 255) \ - if sensor_battery_level else None - - @property - def _last_updated(self): - """Return the last update.""" - sensor_last_updated = self._sensor.get("lastUpdated") - return str(datetime.fromtimestamp(sensor_last_updated)) \ - if sensor_last_updated else None + def _value(self): + """Return value of the sensor.""" + return self.device.value(self._id[1:]) @property def _value_as_temperature(self): """Return the value as temperature.""" - return round(float(self._sensor_value), 1) + return round(float(self._value), 1) @property def _value_as_luminance(self): """Return the value as luminance.""" - return round(float(self._sensor_value), 1) + return round(float(self._value), 1) @property def _value_as_humidity(self): """Return the value as humidity.""" - return int(round(float(self._sensor_value))) + return int(round(float(self._value))) @property def name(self): """Return the name of the sensor.""" - return "{} {}".format(self._sensor_name or DEVICE_DEFAULT_NAME, - self.quantity_name or "") - - @property - def available(self): - """Return true if the sensor is available.""" - return not self._sensor.get("offline", False) + return '{} {}'.format( + super().name, + self.quantity_name or '') @property def state(self): """Return the state of the sensor.""" - if self._sensor_type == SENSOR_TYPE_TEMP: + if self._type == SENSOR_TYPE_TEMP: return self._value_as_temperature - elif self._sensor_type == SENSOR_TYPE_HUMIDITY: + elif self._type == SENSOR_TYPE_HUMIDITY: return self._value_as_humidity - elif self._sensor_type == SENSOR_TYPE_LUMINANCE: + elif self._type == SENSOR_TYPE_LUMINANCE: return self._value_as_luminance else: - return self._sensor_value - - @property - def device_state_attributes(self): - """Return the state attributes.""" - attrs = {} - if self._battery_level is not None: - attrs[ATTR_BATTERY_LEVEL] = self._battery_level - if self._last_updated is not None: - attrs[ATTR_LAST_UPDATED] = self._last_updated - return attrs + return self._value @property def quantity_name(self): """Name of quantity.""" - return SENSOR_TYPES[self._sensor_type][0] \ - if self._sensor_type in SENSOR_TYPES else None + return SENSOR_TYPES[self._type][0] \ + if self._type in SENSOR_TYPES else None @property def unit_of_measurement(self): """Return the unit of measurement.""" - return SENSOR_TYPES[self._sensor_type][1] \ - if self._sensor_type in SENSOR_TYPES else None + return SENSOR_TYPES[self._type][1] \ + if self._type in SENSOR_TYPES else None @property def icon(self): """Return the icon.""" - return SENSOR_TYPES[self._sensor_type][2] \ - if self._sensor_type in SENSOR_TYPES else None + return SENSOR_TYPES[self._type][2] \ + if self._type in SENSOR_TYPES else None diff --git a/homeassistant/components/switch/tellduslive.py b/homeassistant/components/switch/tellduslive.py index eaa78412c27..b1450de6c5e 100644 --- a/homeassistant/components/switch/tellduslive.py +++ b/homeassistant/components/switch/tellduslive.py @@ -9,7 +9,7 @@ https://home-assistant.io/components/switch.tellduslive/ """ import logging -from homeassistant.components import tellduslive +from homeassistant.components.tellduslive import TelldusLiveEntity from homeassistant.helpers.entity import ToggleEntity _LOGGER = logging.getLogger(__name__) @@ -19,53 +19,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup Tellstick switches.""" if discovery_info is None: return - add_devices(TelldusLiveSwitch(switch) for switch in discovery_info) + add_devices(TelldusLiveSwitch(hass, switch) for switch in discovery_info) -class TelldusLiveSwitch(ToggleEntity): +class TelldusLiveSwitch(TelldusLiveEntity, ToggleEntity): """Representation of a Tellstick switch.""" - def __init__(self, switch_id): - """Initialize the switch.""" - self._id = switch_id - self.update() - _LOGGER.debug("created switch %s", self) - - def update(self): - """Get the latest date and update the state.""" - tellduslive.NETWORK.update_switches() - self._switch = tellduslive.NETWORK.get_switch(self._id) - - @property - def should_poll(self): - """Polling is needed.""" - return True - - @property - def assumed_state(self): - """Return true if unable to access real state of entity.""" - return True - - @property - def name(self): - """Return the name of the switch if any.""" - return self._switch["name"] - - @property - def available(self): - """Return the state of the switch.""" - return not self._switch.get("offline", False) - @property def is_on(self): """Return true if switch is on.""" - from tellive.live import const - return self._switch["state"] == const.TELLSTICK_TURNON + return self.device.is_on() def turn_on(self, **kwargs): """Turn the switch on.""" - tellduslive.NETWORK.turn_switch_on(self._id) + self.device.turn_on() + self.changed() def turn_off(self, **kwargs): """Turn the switch off.""" - tellduslive.NETWORK.turn_switch_off(self._id) + self.device.turn_off() + self.changed() diff --git a/homeassistant/components/tellduslive.py b/homeassistant/components/tellduslive.py index 36e9b01d511..cf62a28b552 100644 --- a/homeassistant/components/tellduslive.py +++ b/homeassistant/components/tellduslive.py @@ -4,18 +4,20 @@ Support for Telldus Live. For more details about this component, please refer to the documentation at https://home-assistant.io/components/tellduslive/ """ +from datetime import datetime, timedelta import logging -from datetime import timedelta - -import voluptuous as vol +from homeassistant.const import ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME from homeassistant.helpers import discovery -from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.event import track_point_in_utc_time +from homeassistant.util.dt import utcnow +import voluptuous as vol DOMAIN = 'tellduslive' -REQUIREMENTS = ['tellive-py==0.5.2'] +REQUIREMENTS = ['tellduslive==0.1.9'] _LOGGER = logging.getLogger(__name__) @@ -23,11 +25,10 @@ CONF_PUBLIC_KEY = 'public_key' CONF_PRIVATE_KEY = 'private_key' CONF_TOKEN = 'token' CONF_TOKEN_SECRET = 'token_secret' +CONF_UPDATE_INTERVAL = 'update_interval' -MIN_TIME_BETWEEN_SWITCH_UPDATES = timedelta(minutes=1) -MIN_TIME_BETWEEN_SENSOR_UPDATES = timedelta(minutes=5) - -NETWORK = None +MIN_UPDATE_INTERVAL = timedelta(seconds=5) +DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -35,183 +36,190 @@ CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_PRIVATE_KEY): cv.string, vol.Required(CONF_TOKEN): cv.string, vol.Required(CONF_TOKEN_SECRET): cv.string, + vol.Optional(CONF_UPDATE_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): ( + vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL))) }), }, extra=vol.ALLOW_EXTRA) +ATTR_LAST_UPDATED = 'time_last_updated' + + def setup(hass, config): """Setup the Telldus Live component.""" - # fixme: aquire app key and provide authentication using username+password + client = TelldusLiveClient(hass, config) - global NETWORK - NETWORK = TelldusLiveData(hass, config) - - if not NETWORK.validate_session(): + if not client.validate_session(): _LOGGER.error( - "Authentication Error: " - "Please make sure you have configured your keys " - "that can be aquired from https://api.telldus.com/keys/index") + 'Authentication Error: ' + 'Please make sure you have configured your keys ' + 'that can be aquired from https://api.telldus.com/keys/index') return False - NETWORK.discover() + hass.data[DOMAIN] = client + client.update(utcnow()) return True -@Throttle(MIN_TIME_BETWEEN_SWITCH_UPDATES) -def request_switches(): - """Make request to online service.""" - _LOGGER.debug("Updating switches from Telldus Live") - switches = NETWORK.request('devices/list') - # Filter out any group of switches. - if switches and 'device' in switches: - return {switch["id"]: switch for switch in switches['device'] - if switch["type"] == "device"} - return None - - -@Throttle(MIN_TIME_BETWEEN_SENSOR_UPDATES) -def request_sensors(): - """Make request to online service.""" - _LOGGER.debug("Updating sensors from Telldus Live") - units = NETWORK.request('sensors/list') - # One unit can contain many sensors. - if units and 'sensor' in units: - return {(unit['id'], sensor['name'], sensor['scale']): - dict(unit, data=sensor) - for unit in units['sensor'] - for sensor in unit['data']} - return None - - -class TelldusLiveData(object): +class TelldusLiveClient(object): """Get the latest data and update the states.""" def __init__(self, hass, config): """Initialize the Tellus data object.""" + from tellduslive import Client + public_key = config[DOMAIN].get(CONF_PUBLIC_KEY) private_key = config[DOMAIN].get(CONF_PRIVATE_KEY) token = config[DOMAIN].get(CONF_TOKEN) token_secret = config[DOMAIN].get(CONF_TOKEN_SECRET) - from tellive.client import LiveClient - - self._switches = {} - self._sensors = {} + self.entities = [] self._hass = hass self._config = config - self._client = LiveClient( - public_key=public_key, private_key=private_key, access_token=token, - access_secret=token_secret) + self._interval = config[DOMAIN].get(CONF_UPDATE_INTERVAL) + _LOGGER.debug('Update interval %s', self._interval) + + self._client = Client(public_key, + private_key, + token, + token_secret) def validate_session(self): - """Make a dummy request to see if the session is valid.""" - response = self.request("user/profile") + """Make a request to see if the session is valid.""" + response = self._client.request_user() return response and 'email' in response - def discover(self): - """Update states, will trigger discover.""" - self.update_sensors() - self.update_switches() - - def _discover(self, found_devices, component_name): - """Send discovery event if component not yet discovered.""" - if not found_devices: - return - - _LOGGER.info("discovered %d new %s devices", - len(found_devices), component_name) - - discovery.load_platform(self._hass, component_name, DOMAIN, - found_devices, self._config) - - def request(self, what, **params): - """Send a request to the Tellstick Live API.""" - from tellive.live import const - - supported_methods = const.TELLSTICK_TURNON \ - | const.TELLSTICK_TURNOFF \ - | const.TELLSTICK_TOGGLE \ - - # Tellstick device methods not yet supported - # | const.TELLSTICK_BELL \ - # | const.TELLSTICK_DIM \ - # | const.TELLSTICK_LEARN \ - # | const.TELLSTICK_EXECUTE \ - # | const.TELLSTICK_UP \ - # | const.TELLSTICK_DOWN \ - # | const.TELLSTICK_STOP - - default_params = {'supportedMethods': supported_methods, - 'includeValues': 1, - 'includeScale': 1, - 'includeIgnored': 0} - params.update(default_params) - - # room for improvement: the telllive library doesn't seem to - # re-use sessions, instead it opens a new session for each request - # this needs to be fixed - + def update(self, now): + """Periodically poll the servers for current state.""" + _LOGGER.debug('Updating') try: - response = self._client.request(what, params) - _LOGGER.debug("got response %s", response) - return response - except OSError as error: - _LOGGER.error("failed to make request to Tellduslive servers: %s", - error) - return None + self._sync() + finally: + track_point_in_utc_time(self._hass, + self.update, + now + self._interval) - def update_devices(self, local_devices, remote_devices, component_name): - """Update local device list and discover new devices.""" - if remote_devices is None: - return local_devices + def _sync(self): + """Update local list of devices.""" + self._client.update() - remote_ids = remote_devices.keys() - local_ids = local_devices.keys() + def identify_device(device): + """Find out what type of HA component to create.""" + from tellduslive import (DIM, UP, TURNON) + if device.methods & DIM: + return 'light' + elif device.methods & UP: + return 'cover' + elif device.methods & TURNON: + return 'switch' + else: + _LOGGER.warning('Unidentified device type (methods: %d)', + device.methods) + return 'switch' - added_devices = list(remote_ids - local_ids) - self._discover(added_devices, - component_name) + def discover(device_id, component): + """Discover the component.""" + discovery.load_platform(self._hass, + component, + DOMAIN, + [device_id], + self._config) - removed_devices = list(local_ids - remote_ids) - remote_devices.update({id: dict(local_devices[id], offline=True) - for id in removed_devices}) + known_ids = set([entity.device_id for entity in self.entities]) + for device in self._client.devices: + if device.device_id in known_ids: + continue + if device.is_sensor: + for item_id in device.items: + discover((device.device_id,) + item_id, + 'sensor') + else: + discover(device.device_id, + identify_device(device)) - return remote_devices + for entity in self.entities: + entity.changed() - def update_sensors(self): - """Update local list of sensors.""" - self._sensors = self.update_devices( - self._sensors, request_sensors(), 'sensor') + def device(self, device_id): + """Return device representation.""" + import tellduslive + return tellduslive.Device(self._client, device_id) - def update_switches(self): - """Update local list of switches.""" - self._switches = self.update_devices( - self._switches, request_switches(), 'switch') + def is_available(self, device_id): + """Return device availability.""" + return device_id in self._client.device_ids - def _check_request(self, what, **params): - """Make request, check result if successful.""" - response = self.request(what, **params) - return response and response.get('status') == 'success' - def get_switch(self, switch_id): - """Return the switch representation.""" - return self._switches[switch_id] +class TelldusLiveEntity(Entity): + """Base class for all Telldus Live entities.""" - def get_sensor(self, sensor_id): - """Return the sensor representation.""" - return self._sensors[sensor_id] + def __init__(self, hass, device_id): + """Initialize the entity.""" + self._id = device_id + self._client = hass.data[DOMAIN] + self._client.entities.append(self) + _LOGGER.debug('Created device %s', self) - def turn_switch_on(self, switch_id): - """Turn switch off.""" - if self._check_request('device/turnOn', id=switch_id): - from tellive.live import const - self.get_switch(switch_id)['state'] = const.TELLSTICK_TURNON + def changed(self): + """A property of the device might have changed.""" + self.schedule_update_ha_state() - def turn_switch_off(self, switch_id): - """Turn switch on.""" - if self._check_request('device/turnOff', id=switch_id): - from tellive.live import const - self.get_switch(switch_id)['state'] = const.TELLSTICK_TURNOFF + @property + def device_id(self): + """Return the id of the device.""" + return self._id + + @property + def device(self): + """Return the representaion of the device.""" + return self._client.device(self.device_id) + + @property + def _state(self): + """Return the state of the device.""" + return self.device.state + + @property + def should_poll(self): + """Polling is not needed.""" + return False + + @property + def assumed_state(self): + """Return true if unable to access real state of entity.""" + return True + + @property + def name(self): + """Return name of device.""" + return self.device.name or DEVICE_DEFAULT_NAME + + @property + def available(self): + """Return true if device is not offline.""" + return self._client.is_available(self.device_id) + + @property + def device_state_attributes(self): + """Return the state attributes.""" + attrs = {} + if self._battery_level: + attrs[ATTR_BATTERY_LEVEL] = self._battery_level + if self._last_updated: + attrs[ATTR_LAST_UPDATED] = self._last_updated + return attrs + + @property + def _battery_level(self): + """Return the battery level of a device.""" + return round(self.device.battery * 100 / 255) \ + if self.device.battery else None + + @property + def _last_updated(self): + """Return the last update of a device.""" + return str(datetime.fromtimestamp(self.device.last_updated)) \ + if self.device.last_updated else None diff --git a/requirements_all.txt b/requirements_all.txt index 4b2763f26bb..fe37b085b2c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -560,7 +560,7 @@ steamodd==4.21 tellcore-py==1.1.2 # homeassistant.components.tellduslive -tellive-py==0.5.2 +tellduslive==0.1.9 # homeassistant.components.sensor.temper temperusb==1.5.1 From d7ccf079228565ec35dffdccd4c9eac4ce6e4588 Mon Sep 17 00:00:00 2001 From: John Mihalic Date: Mon, 12 Dec 2016 00:43:53 -0500 Subject: [PATCH 87/89] Add media position support and trailer type to Emby (#4792) * Add media position support and trailer type to Emby * Adjustments to mitigate TypeError * Simplify media_position property * Update handling when data isn't available * Update emby.py --- homeassistant/components/media_player/emby.py | 41 ++++++++++++++++--- requirements_all.txt | 2 +- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/media_player/emby.py b/homeassistant/components/media_player/emby.py index 5349e74ed40..3fae52dd052 100644 --- a/homeassistant/components/media_player/emby.py +++ b/homeassistant/components/media_player/emby.py @@ -20,12 +20,15 @@ from homeassistant.const import ( STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN) from homeassistant.helpers.event import (track_utc_time_change) from homeassistant.util import Throttle +import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pyemby==0.1'] +REQUIREMENTS = ['pyemby==0.2'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) +MEDIA_TYPE_TRAILER = 'trailer' + DEFAULT_PORT = 8096 _LOGGER = logging.getLogger(__name__) @@ -119,6 +122,8 @@ class EmbyClient(MediaPlayerDevice): self.update_sessions = update_sessions self.client = client self.set_device(device) + self.media_status_last_position = None + self.media_status_received = None def set_device(self, device): """Set the device property.""" @@ -178,6 +183,17 @@ class EmbyClient(MediaPlayerDevice): """Get the latest details.""" self.update_devices(no_throttle=True) self.update_sessions(no_throttle=True) + # Check if we should update progress + try: + position = self.session['PlayState']['PositionTicks'] + except (KeyError, TypeError): + self.media_status_last_position = None + self.media_status_received = None + else: + position = int(position) / 10000000 + if position != self.media_status_last_position: + self.media_status_last_position = position + self.media_status_received = dt_util.utcnow() def play_percent(self): """Return current media percent complete.""" @@ -220,6 +236,8 @@ class EmbyClient(MediaPlayerDevice): return MEDIA_TYPE_TVSHOW elif media_type == 'Movie': return MEDIA_TYPE_VIDEO + elif media_type == 'Trailer': + return MEDIA_TYPE_TRAILER return None except KeyError: return None @@ -233,19 +251,32 @@ class EmbyClient(MediaPlayerDevice): except KeyError: return None + @property + def media_position(self): + """Position of current playing media in seconds.""" + return self.media_status_last_position + + @property + def media_position_updated_at(self): + """ + When was the position of the current playing media valid. + + Returns value from homeassistant.util.dt.utcnow(). + """ + return self.media_status_received + @property def media_image_url(self): """Image url of current playing media.""" if self.now_playing_item is not None: try: return self.client.get_image( - self.now_playing_item['ThumbItemId'], 'Thumb', - self.play_percent()) + self.now_playing_item['ThumbItemId'], 'Thumb', 0) except KeyError: try: return self.client.get_image( - self.now_playing_item['PrimaryImageItemId'], 'Primary', - self.play_percent()) + self.now_playing_item[ + 'PrimaryImageItemId'], 'Primary', 0) except KeyError: return None diff --git a/requirements_all.txt b/requirements_all.txt index fe37b085b2c..f9d1b36fb5f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -390,7 +390,7 @@ pycmus==0.1.0 pydispatcher==2.0.5 # homeassistant.components.media_player.emby -pyemby==0.1 +pyemby==0.2 # homeassistant.components.envisalink pyenvisalink==1.9 From 4114884cdc91a9a238120501fe20c517475b7362 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 12 Dec 2016 00:43:59 -0500 Subject: [PATCH 88/89] Flic: Support use of queued events within timeout (#4822) * Flic: Support use of queued events within timeout * Linter fixes --- .../components/binary_sensor/flic.py | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/binary_sensor/flic.py b/homeassistant/components/binary_sensor/flic.py index 92301330605..980af069f38 100644 --- a/homeassistant/components/binary_sensor/flic.py +++ b/homeassistant/components/binary_sensor/flic.py @@ -6,7 +6,8 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.const import ( - CONF_HOST, CONF_PORT, CONF_DISCOVERY, EVENT_HOMEASSISTANT_STOP) + CONF_HOST, CONF_PORT, CONF_DISCOVERY, CONF_TIMEOUT, + EVENT_HOMEASSISTANT_STOP) from homeassistant.components.binary_sensor import ( BinarySensorDevice, PLATFORM_SCHEMA) from homeassistant.util.async import run_callback_threadsafe @@ -16,6 +17,7 @@ REQUIREMENTS = ['https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4'] _LOGGER = logging.getLogger(__name__) +DEFAULT_TIMEOUT = 3 CLICK_TYPE_SINGLE = "single" CLICK_TYPE_DOUBLE = "double" @@ -28,12 +30,14 @@ EVENT_NAME = "flic_click" EVENT_DATA_NAME = "button_name" EVENT_DATA_ADDRESS = "button_address" EVENT_DATA_TYPE = "click_type" +EVENT_DATA_QUEUED_TIME = "queued_time" # Validation of the user's configuration PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST, default='localhost'): cv.string, vol.Optional(CONF_PORT, default=5551): cv.port, vol.Optional(CONF_DISCOVERY, default=True): cv.boolean, + vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Optional(CONF_IGNORED_CLICK_TYPES): vol.All(cv.ensure_list, [vol.In(CLICK_TYPES)]) }) @@ -102,8 +106,9 @@ def start_scanning(hass, config, async_add_entities, client): @asyncio.coroutine def async_setup_button(hass, config, async_add_entities, client, address): """Setup single button device.""" + timeout = config.get(CONF_TIMEOUT) ignored_click_types = config.get(CONF_IGNORED_CLICK_TYPES) - button = FlicButton(hass, client, address, ignored_click_types) + button = FlicButton(hass, client, address, timeout, ignored_click_types) _LOGGER.info("Connected to button (%s)", address) yield from async_add_entities([button]) @@ -127,12 +132,13 @@ def async_get_verified_addresses(client): class FlicButton(BinarySensorDevice): """Representation of a flic button.""" - def __init__(self, hass, client, address, ignored_click_types): + def __init__(self, hass, client, address, timeout, ignored_click_types): """Initialize the flic button.""" import pyflic self._hass = hass self._address = address + self._timeout = timeout self._is_down = False self._ignored_click_types = ignored_click_types or [] self._hass_click_types = { @@ -196,11 +202,27 @@ class FlicButton(BinarySensorDevice): return attr + def _queued_event_check(self, click_type, time_diff): + """Generate a log message and returns true if timeout exceeded.""" + time_string = "{:d} {}".format( + time_diff, "second" if time_diff == 1 else "seconds") + + if time_diff > self._timeout: + _LOGGER.warning( + "Queued %s dropped for %s. Time in queue was %s.", + click_type, self.address, time_string) + return True + else: + _LOGGER.info( + "Queued %s allowed for %s. Time in queue was %s.", + click_type, self.address, time_string) + return False + def _on_up_down(self, channel, click_type, was_queued, time_diff): """Update device state, if event was not queued.""" import pyflic - if was_queued: + if was_queued and self._queued_event_check(click_type, time_diff): return self._is_down = click_type == pyflic.ClickType.ButtonDown @@ -208,13 +230,19 @@ class FlicButton(BinarySensorDevice): def _on_click(self, channel, click_type, was_queued, time_diff): """Fire click event, if event was not queued.""" + # Return if click event was queued beyond allowed timeout + if was_queued and self._queued_event_check(click_type, time_diff): + return + + # Return if click event is in ignored click types hass_click_type = self._hass_click_types[click_type] - if was_queued or hass_click_type in self._ignored_click_types: + if hass_click_type in self._ignored_click_types: return self._hass.bus.fire(EVENT_NAME, { EVENT_DATA_NAME: self.name, EVENT_DATA_ADDRESS: self.address, + EVENT_DATA_QUEUED_TIME: time_diff, EVENT_DATA_TYPE: hass_click_type }) From 3467020dbf1d4747c07900d8f5fe2c1c0baf5292 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 12 Dec 2016 00:46:19 -0500 Subject: [PATCH 89/89] Added resolution support to Amcrest cameras (#4860) * Added resolution support to Amcrest cameras * Ordered alphabetically DEFAULT_ options --- homeassistant/components/camera/amcrest.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py index 97ed45f21e4..c6568677583 100644 --- a/homeassistant/components/camera/amcrest.py +++ b/homeassistant/components/camera/amcrest.py @@ -18,16 +18,26 @@ REQUIREMENTS = ['amcrest==1.0.0'] _LOGGER = logging.getLogger(__name__) -DEFAULT_PORT = 80 +CONF_RESOLUTION = 'resolution' + DEFAULT_NAME = 'Amcrest Camera' +DEFAULT_PORT = 80 +DEFAULT_RESOLUTION = 'high' NOTIFICATION_ID = 'amcrest_notification' NOTIFICATION_TITLE = 'Amcrest Camera Setup' +RESOLUTION_LIST = { + 'high': 0, + 'low': 1, +} + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION): + vol.All(vol.In(RESOLUTION_LIST)), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, }) @@ -64,13 +74,14 @@ class AmcrestCam(Camera): def __init__(self, device_info, data): """Initialize an Amcrest camera.""" super(AmcrestCam, self).__init__() - self._name = device_info.get(CONF_NAME) self._data = data + self._name = device_info.get(CONF_NAME) + self._resolution = RESOLUTION_LIST[device_info.get(CONF_RESOLUTION)] def camera_image(self): """Return a still image reponse from the camera.""" # Send the request to snap a picture and return raw jpg data - response = self._data.camera.snapshot() + response = self._data.camera.snapshot(channel=self._resolution) return response.data @property