From bcfc30264df4b7678d8ce416fdb6395a5f5e601c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 15 Jan 2017 08:40:20 -0800 Subject: [PATCH 001/191] version bump to 0.37 --- homeassistant/const.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index b5c04c26d0b..3ef79ad1724 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 = 36 -PATCH_VERSION = '0' +MINOR_VERSION = 37 +PATCH_VERSION = '0.dev0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4, 2) From 82b84f480b9b3f30d634298121407345ea29010e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 15 Jan 2017 09:16:46 -0800 Subject: [PATCH 002/191] Fix script release (#5345) --- script/release | 3 --- 1 file changed, 3 deletions(-) diff --git a/script/release b/script/release index 3bcddcfef76..65a6339cedc 100755 --- a/script/release +++ b/script/release @@ -1,9 +1,6 @@ #!/bin/sh # Pushes a new version to PyPi. -# Stop on errors -set -e - cd "$(dirname "$0")/.." head -n 5 homeassistant/const.py | tail -n 1 | grep PATCH_VERSION > /dev/null From 633c1408fbc293e8846e7da02e03970d0fbf2279 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 15 Jan 2017 18:37:17 +0100 Subject: [PATCH 003/191] Upgrade pylast to 1.7.0 (#5344) --- homeassistant/components/sensor/lastfm.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/lastfm.py b/homeassistant/components/sensor/lastfm.py index 5d660f20217..5da512b205d 100644 --- a/homeassistant/components/sensor/lastfm.py +++ b/homeassistant/components/sensor/lastfm.py @@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pylast==1.6.0'] +REQUIREMENTS = ['pylast==1.7.0'] CONF_USERS = 'users' diff --git a/requirements_all.txt b/requirements_all.txt index ca9f9f422a7..3bea17fbead 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -431,7 +431,7 @@ pyicloud==0.9.1 pyiss==1.0.1 # homeassistant.components.sensor.lastfm -pylast==1.6.0 +pylast==1.7.0 # homeassistant.components.litejet pylitejet==0.1 From 8200827a19c43a9d38428b3ec6eae7946208d8b5 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Sun, 15 Jan 2017 14:37:24 -0500 Subject: [PATCH 004/191] Add support for direct MJPEG streams from Amcrest cameras (#5217) * Add support for direct MJPEG streams from Amcrest cameras The previous implementation relied on using snapshots from the camera. However, some Amcrest models cannot keep up with the large number of requests and instead timeout, resulting in no video stream. These cameras do provide MJPEG streams though, so this commit adds the option to use these instead of creating one from snapshots. Unfortunately, some cameras on newer firmwares do not support MJPEG streams at high resolution - only at low resolution. By providing users with both a `resolution` and `stream_source` option, we can allow them to choose whichever combination works for their particular model and firmware version. * Close the stream instead of releasing it * Close stream without creating a task * Handle client aborts * fix lint --- homeassistant/components/camera/amcrest.py | 84 +++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py index c6568677583..a3d8dcf35df 100644 --- a/homeassistant/components/camera/amcrest.py +++ b/homeassistant/components/camera/amcrest.py @@ -4,8 +4,13 @@ This component provides basic support for Amcrest IP cameras. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/camera.amcrest/ """ +import asyncio import logging +import aiohttp +from aiohttp import web +from aiohttp.web_exceptions import HTTPGatewayTimeout +import async_timeout import voluptuous as vol import homeassistant.loader as loader @@ -13,16 +18,19 @@ from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT) from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.aiohttp_client import async_create_clientsession REQUIREMENTS = ['amcrest==1.0.0'] _LOGGER = logging.getLogger(__name__) CONF_RESOLUTION = 'resolution' +CONF_STREAM_SOURCE = 'stream_source' DEFAULT_NAME = 'Amcrest Camera' DEFAULT_PORT = 80 DEFAULT_RESOLUTION = 'high' +DEFAULT_STREAM_SOURCE = 'mjpeg' NOTIFICATION_ID = 'amcrest_notification' NOTIFICATION_TITLE = 'Amcrest Camera Setup' @@ -32,6 +40,14 @@ RESOLUTION_LIST = { 'low': 1, } +STREAM_SOURCE_LIST = { + 'mjpeg': 0, + 'snapshot': 1 +} + +CONTENT_TYPE_HEADER = 'Content-Type' +TIMEOUT = 5 + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_USERNAME): cv.string, @@ -40,6 +56,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.All(vol.In(RESOLUTION_LIST)), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE): + vol.All(vol.In(STREAM_SOURCE_LIST)), }) @@ -64,19 +82,33 @@ def setup_platform(hass, config, add_devices, discovery_info=None): notification_id=NOTIFICATION_ID) return False - add_devices([AmcrestCam(config, data)]) + add_devices([AmcrestCam(hass, config, data)]) return True class AmcrestCam(Camera): """An implementation of an Amcrest IP camera.""" - def __init__(self, device_info, data): + def __init__(self, hass, device_info, data): """Initialize an Amcrest camera.""" super(AmcrestCam, self).__init__() + self._base_url = '%s://%s:%s/cgi-bin' % ( + 'http', + device_info.get(CONF_HOST), + device_info.get(CONF_PORT) + ) self._data = data + self._hass = hass self._name = device_info.get(CONF_NAME) self._resolution = RESOLUTION_LIST[device_info.get(CONF_RESOLUTION)] + self._stream_source = STREAM_SOURCE_LIST[ + device_info.get(CONF_STREAM_SOURCE) + ] + self._token = self._auth = aiohttp.BasicAuth( + device_info.get(CONF_USERNAME), + password=device_info.get(CONF_PASSWORD) + ) + self._websession = async_create_clientsession(hass) def camera_image(self): """Return a still image reponse from the camera.""" @@ -84,6 +116,54 @@ class AmcrestCam(Camera): response = self._data.camera.snapshot(channel=self._resolution) return response.data + @asyncio.coroutine + def handle_async_mjpeg_stream(self, request): + """Return an MJPEG stream.""" + # The snapshot implementation is handled by the parent class + if self._stream_source == STREAM_SOURCE_LIST['snapshot']: + yield from super().handle_async_mjpeg_stream(request) + return + + # Otherwise, stream an MJPEG image stream directly from the camera + streaming_url = '%s/mjpg/video.cgi?channel=0&subtype=%d' % ( + self._base_url, + self._resolution + ) + + stream = None + response = None + try: + with async_timeout.timeout(TIMEOUT, loop=self.hass.loop): + stream = yield from self._websession.get( + streaming_url, + auth=self._token, + timeout=TIMEOUT + ) + response = web.StreamResponse() + response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) + + yield from response.prepare(request) + + while True: + data = yield from stream.content.read(16384) + if not data: + break + response.write(data) + + except (asyncio.TimeoutError, aiohttp.errors.ClientError): + _LOGGER.exception("Error on %s", streaming_url) + raise HTTPGatewayTimeout() + + except asyncio.CancelledError: + _LOGGER.debug("Close stream by frontend.") + response = None + + finally: + if stream is not None: + stream.close() + if response is not None: + yield from response.write_eof() + @property def name(self): """Return the name of this camera.""" From 01d9e6cdfe611ce3eb6477679c15d955aaeda5e9 Mon Sep 17 00:00:00 2001 From: freol35241 Date: Sun, 15 Jan 2017 20:53:46 +0100 Subject: [PATCH 005/191] Removing throttle decorator Removing redundant throttle decorator on update method. This ensures the existing 'cache-value' config option is respected. Also, UPDATE_INTERVAL is renamed to DEFAULT_UPDATE_INTERVAL for clarity. --- homeassistant/components/sensor/miflora.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/miflora.py b/homeassistant/components/sensor/miflora.py index a519d97a855..155808f702f 100644 --- a/homeassistant/components/sensor/miflora.py +++ b/homeassistant/components/sensor/miflora.py @@ -12,7 +12,6 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -from homeassistant.util import Throttle from homeassistant.const import ( CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_MAC) @@ -31,9 +30,7 @@ DEFAULT_MEDIAN = 3 DEFAULT_NAME = 'Mi Flora' DEFAULT_RETRIES = 2 DEFAULT_TIMEOUT = 10 - -UPDATE_INTERVAL = 1200 -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=UPDATE_INTERVAL) +DEFAULT_UPDATE_INTERVAL = 1200 # Sensor types are defined like: Name, units SENSOR_TYPES = { @@ -53,7 +50,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Optional(CONF_RETRIES, default=DEFAULT_RETRIES): cv.positive_int, - vol.Optional(CONF_CACHE, default=UPDATE_INTERVAL): cv.positive_int, + vol.Optional(CONF_CACHE, default=DEFAULT_UPDATE_INTERVAL): cv.positive_int, }) @@ -122,7 +119,6 @@ class MiFloraSensor(Entity): """Force update.""" return self._force_update - @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """ Update current conditions. From c8cf952e2190b70eb1fed2ee89dace23d1f91bd4 Mon Sep 17 00:00:00 2001 From: freol35241 Date: Sun, 15 Jan 2017 21:10:02 +0100 Subject: [PATCH 006/191] Remove import of datetime module --- homeassistant/components/sensor/miflora.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/sensor/miflora.py b/homeassistant/components/sensor/miflora.py index 155808f702f..1922d4832ee 100644 --- a/homeassistant/components/sensor/miflora.py +++ b/homeassistant/components/sensor/miflora.py @@ -4,7 +4,6 @@ Support for Xiaomi Mi Flora BLE plant sensor. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.miflora/ """ -from datetime import timedelta import logging import voluptuous as vol From ad23613cdc541dbc64bc88bc7433ceb8d4e2b55b Mon Sep 17 00:00:00 2001 From: Heiko Rothe Date: Sun, 15 Jan 2017 22:45:54 +0100 Subject: [PATCH 007/191] Fixed the lannouncer platform get_service method (#5352) --- homeassistant/components/notify/lannouncer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/notify/lannouncer.py b/homeassistant/components/notify/lannouncer.py index be1bc636fd6..4d038faeb9a 100644 --- a/homeassistant/components/notify/lannouncer.py +++ b/homeassistant/components/notify/lannouncer.py @@ -29,7 +29,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ _LOGGER = logging.getLogger(__name__) -def get_service(hass, config): +def get_service(hass, config, discovery_info=None): """Get the Lannouncer notification service.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) From 6b91d9a75cdb5ebe7d0ef6a33fa6cda7146585f8 Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Sun, 15 Jan 2017 23:05:41 +0100 Subject: [PATCH 008/191] Update keyboard_remote.py (#5341) Now it fires events in case the keyboard disconnects and/or disconnects --- homeassistant/components/keyboard_remote.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/keyboard_remote.py b/homeassistant/components/keyboard_remote.py index de7eacf96dd..992f9390124 100644 --- a/homeassistant/components/keyboard_remote.py +++ b/homeassistant/components/keyboard_remote.py @@ -48,6 +48,8 @@ REQUIREMENTS = ['evdev==0.6.1'] _LOGGER = logging.getLogger(__name__) ICON = 'mdi:remote' KEYBOARD_REMOTE_COMMAND_RECEIVED = 'keyboard_remote_command_received' +KEYBOARD_REMOTE_CONNECTED = 'keyboard_remote_connected' +KEYBOARD_REMOTE_DISCONNECTED = 'keyboard_remote_disconnected' KEY_CODE = 'key_code' KEY_VALUE = {'key_up': 0, 'key_down': 1, 'key_hold': 2} TYPE = 'type' @@ -151,13 +153,19 @@ class KeyboardRemote(threading.Thread): self.keyboard_connected = True _LOGGER.debug('KeyboardRemote: keyboard re-connected, %s', self.device_descriptor) + self.hass.bus.fire( + KEYBOARD_REMOTE_CONNECTED + ) try: event = self.dev.read_one() except IOError: # Keyboard Disconnected self.keyboard_connected = False - _LOGGER.debug('KeyboardRemote: keyard disconnected, %s', + _LOGGER.debug('KeyboardRemote: keyboard disconnected, %s', self.device_descriptor) + self.hass.bus.fire( + KEYBOARD_REMOTE_DISCONNECTED + ) continue if not event: From 2e3d5302bfe1806a61e1860c089bda69d4318a8d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 15 Jan 2017 23:53:37 +0100 Subject: [PATCH 009/191] Bugfix timedelta v2 (#5349) * Bugfix timedelta v2 * fix volvo * fix lint --- .../device_tracker/bluetooth_le_tracker.py | 14 +++++--------- .../components/device_tracker/bluetooth_tracker.py | 4 +--- .../components/device_tracker/volvooncall.py | 5 ++--- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/device_tracker/bluetooth_le_tracker.py b/homeassistant/components/device_tracker/bluetooth_le_tracker.py index 8c29bc94be5..454ab127af0 100644 --- a/homeassistant/components/device_tracker/bluetooth_le_tracker.py +++ b/homeassistant/components/device_tracker/bluetooth_le_tracker.py @@ -1,14 +1,12 @@ """Tracking for bluetooth low energy devices.""" import logging -from datetime import timedelta import voluptuous as vol from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.components.device_tracker import ( YAML_DEVICES, CONF_TRACK_NEW, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL, - PLATFORM_SCHEMA, load_config, DEFAULT_TRACK_NEW + PLATFORM_SCHEMA, load_config ) -import homeassistant.util as util import homeassistant.util.dt as dt_util import homeassistant.helpers.config_validation as cv @@ -86,14 +84,13 @@ def setup_scanner(hass, config, see): # if track new devices is true discover new devices # on every scan. - track_new = util.convert(config.get(CONF_TRACK_NEW), bool, - DEFAULT_TRACK_NEW) + track_new = config.get(CONF_TRACK_NEW) + if not devs_to_track and not track_new: _LOGGER.warning("No Bluetooth LE devices to track!") return False - interval = util.convert(config.get(CONF_SCAN_INTERVAL), int, - DEFAULT_SCAN_INTERVAL) + interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) def update_ble(now): """Lookup Bluetooth LE devices and update status.""" @@ -113,8 +110,7 @@ def setup_scanner(hass, config, see): _LOGGER.info("Discovered Bluetooth LE device %s", address) see_device(address, devs[address], new_device=True) - track_point_in_utc_time(hass, update_ble, - now + timedelta(seconds=interval)) + track_point_in_utc_time(hass, update_ble, now + interval) update_ble(dt_util.utcnow()) diff --git a/homeassistant/components/device_tracker/bluetooth_tracker.py b/homeassistant/components/device_tracker/bluetooth_tracker.py index 86e115c65c4..a8b3861cdc5 100644 --- a/homeassistant/components/device_tracker/bluetooth_tracker.py +++ b/homeassistant/components/device_tracker/bluetooth_tracker.py @@ -1,6 +1,5 @@ """Tracking for bluetooth devices.""" import logging -from datetime import timedelta import voluptuous as vol @@ -83,8 +82,7 @@ def setup_scanner(hass, config, see): see_device((mac, result)) except bluetooth.BluetoothError: _LOGGER.exception('Error looking up bluetooth device!') - track_point_in_utc_time(hass, update_bluetooth, - now + timedelta(seconds=interval)) + track_point_in_utc_time(hass, update_bluetooth, now + interval) update_bluetooth(dt_util.utcnow()) diff --git a/homeassistant/components/device_tracker/volvooncall.py b/homeassistant/components/device_tracker/volvooncall.py index a7dba230831..1be76d6139c 100644 --- a/homeassistant/components/device_tracker/volvooncall.py +++ b/homeassistant/components/device_tracker/volvooncall.py @@ -37,7 +37,7 @@ def setup_scanner(hass, config, see): config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) - interval = max(MIN_TIME_BETWEEN_SCANS.seconds, + interval = max(MIN_TIME_BETWEEN_SCANS, config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)) def _see_vehicle(vehicle): @@ -91,8 +91,7 @@ def setup_scanner(hass, config, see): return True finally: - track_point_in_utc_time(hass, update, - now + timedelta(seconds=interval)) + track_point_in_utc_time(hass, update, now + interval) _LOGGER.info('Logging in to service') return update(utcnow()) From 7a1d4b96ef4a6a2317dcbc5cf6901e2781fd27c2 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Mon, 16 Jan 2017 06:48:36 +0100 Subject: [PATCH 010/191] Eq3bt bump version, expose away attribute (#5353) * eq3bt: read away ends attr * eq3bt: bump version to fix missing __init__, expose away_ends attribute. --- homeassistant/components/climate/eq3btsmart.py | 4 +++- requirements_all.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/eq3btsmart.py b/homeassistant/components/climate/eq3btsmart.py index 41697f7b31d..a8ab9bd30b2 100644 --- a/homeassistant/components/climate/eq3btsmart.py +++ b/homeassistant/components/climate/eq3btsmart.py @@ -17,7 +17,7 @@ from homeassistant.const import ( import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-eq3bt==0.1.2'] +REQUIREMENTS = ['python-eq3bt==0.1.4'] _LOGGER = logging.getLogger(__name__) @@ -29,6 +29,7 @@ ATTR_STATE_WINDOW_OPEN = "window_open" ATTR_STATE_VALVE = "valve" ATTR_STATE_LOCKED = "is_locked" ATTR_STATE_LOW_BAT = "low_battery" +ATTR_STATE_AWAY_END = "away_end" DEVICE_SCHEMA = vol.Schema({ vol.Required(CONF_MAC): cv.string, @@ -152,6 +153,7 @@ class EQ3BTSmartThermostat(ClimateDevice): ATTR_STATE_LOW_BAT: self._thermostat.low_battery, ATTR_STATE_VALVE: self._thermostat.valve_state, ATTR_STATE_WINDOW_OPEN: self._thermostat.window_open, + ATTR_STATE_AWAY_END: self._thermostat.away_end, } return dev_specific diff --git a/requirements_all.txt b/requirements_all.txt index 3bea17fbead..6d3f254f02a 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -476,7 +476,7 @@ pysnmp==4.3.2 python-digitalocean==1.10.1 # homeassistant.components.climate.eq3btsmart -python-eq3bt==0.1.2 +python-eq3bt==0.1.4 # homeassistant.components.sensor.darksky python-forecastio==1.3.5 From f08e2648aed98f7ba4f3876a9ddd42dd729c0cb3 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 16 Jan 2017 11:06:23 +0100 Subject: [PATCH 011/191] Bugfix upc with aiohttp 1.2 (cookies) (#5362) --- .../components/device_tracker/upc_connect.py | 17 +++++------ tests/test_util/aiohttp.py | 29 ++++++++++++------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/device_tracker/upc_connect.py b/homeassistant/components/device_tracker/upc_connect.py index aafa9824a4e..13336e939a5 100644 --- a/homeassistant/components/device_tracker/upc_connect.py +++ b/homeassistant/components/device_tracker/upc_connect.py @@ -74,8 +74,11 @@ class UPCDeviceScanner(DeviceScanner): return [] raw = yield from self._async_ws_function(CMD_DEVICES) - xml_root = ET.fromstring(raw) + if raw is None: + _LOGGER.warning("Can't read device from %s", self.host) + return + xml_root = ET.fromstring(raw) return [mac.text for mac in xml_root.iter('MACAddr')] @asyncio.coroutine @@ -94,7 +97,8 @@ class UPCDeviceScanner(DeviceScanner): "http://{}/common_page/login.html".format(self.host) ) - self.token = self._async_get_token() + yield from response.text() + self.token = response.cookies['sessionToken'].value # login data = yield from self._async_ws_function(CMD_LOGIN, { @@ -144,7 +148,7 @@ class UPCDeviceScanner(DeviceScanner): # load data, store token for next request raw = yield from response.text() - self.token = self._async_get_token() + self.token = response.cookies['sessionToken'].value return raw @@ -155,10 +159,3 @@ class UPCDeviceScanner(DeviceScanner): finally: if response is not None: yield from response.release() - - def _async_get_token(self): - """Extract token from cookies.""" - cookie_manager = self.websession.cookie_jar.filter_cookies( - "http://{}".format(self.host)) - - return cookie_manager.get('sessionToken') diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index dcdf69395b4..afe2f626de7 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -37,11 +37,9 @@ class AiohttpClientMocker: content = b'' if params: url = str(yarl.URL(url).with_query(params)) - if cookies: - self._cookies.update(cookies) self._mocks.append(AiohttpClientMockResponse( - method, url, status, content, exc)) + method, url, status, content, cookies, exc)) def get(self, *args, **kwargs): """Register a mock get request.""" @@ -68,10 +66,6 @@ class AiohttpClientMocker: """Number of requests made.""" return len(self.mock_calls) - def filter_cookies(self, host): - """Return hosts cookies.""" - return self._cookies - def clear_requests(self): """Reset mock calls.""" self._mocks.clear() @@ -97,7 +91,7 @@ class AiohttpClientMocker: class AiohttpClientMockResponse: """Mock Aiohttp client response.""" - def __init__(self, method, url, status, response, exc=None): + def __init__(self, method, url, status, response, cookies=None, exc=None): """Initialize a fake response.""" self.method = method self._url = url @@ -107,6 +101,14 @@ class AiohttpClientMockResponse: self.response = response self.exc = exc + self._cookies = {} + + if cookies: + for name, data in cookies.items(): + cookie = mock.MagicMock() + cookie.value = data + self._cookies[name] = cookie + def match_request(self, method, url, params=None): """Test if response answers request.""" if method.lower() != self.method.lower(): @@ -140,6 +142,11 @@ class AiohttpClientMockResponse: return True + @property + def cookies(self): + """Return dict of cookies.""" + return self._cookies + @asyncio.coroutine def read(self): """Return mock response.""" @@ -160,6 +167,10 @@ class AiohttpClientMockResponse: """Mock release.""" pass + def close(self): + """Mock close.""" + pass + @contextmanager def mock_aiohttp_client(): @@ -173,6 +184,4 @@ def mock_aiohttp_client(): setattr(instance, method, functools.partial(mocker.match_request, method)) - instance.cookie_jar.filter_cookies = mocker.filter_cookies - yield mocker From 887c586aae436607ec3464bbbea4e7e800b763bb Mon Sep 17 00:00:00 2001 From: whhsw Date: Mon, 16 Jan 2017 19:44:09 +0800 Subject: [PATCH 012/191] Add station parameter to waqi sensor (#5239) * Add station parameter to waqi sensor * Update waqi.py * Update waqi.py * Update waqi.py * add back 'waqi' prefix and add station_name prop * Update waqi.py --- homeassistant/components/sensor/waqi.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/waqi.py b/homeassistant/components/sensor/waqi.py index b893eeaf204..af2d80a0948 100644 --- a/homeassistant/components/sensor/waqi.py +++ b/homeassistant/components/sensor/waqi.py @@ -30,6 +30,7 @@ ATTR_TIME = 'time' ATTRIBUTION = 'Data provided by the World Air Quality Index project' CONF_LOCATIONS = 'locations' +CONF_STATIONS = 'stations' MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) @@ -38,7 +39,8 @@ SENSOR_TYPES = { } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_LOCATIONS): cv.ensure_list + vol.Optional(CONF_STATIONS): cv.ensure_list, + vol.Required(CONF_LOCATIONS): cv.ensure_list, }) @@ -47,11 +49,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None): import pwaqi dev = [] + station_filter = config.get(CONF_STATIONS) for location_name in config.get(CONF_LOCATIONS): station_ids = pwaqi.findStationCodesByCity(location_name) - _LOGGER.error('The following stations were returned: %s', station_ids) + _LOGGER.info('The following stations were returned: %s', station_ids) for station in station_ids: - dev.append(WaqiSensor(WaqiData(station), station)) + waqi_sensor = WaqiSensor(WaqiData(station), station) + if (not station_filter) or \ + (waqi_sensor.station_name in station_filter): + dev.append(WaqiSensor(WaqiData(station), station)) add_devices(dev) @@ -74,6 +80,14 @@ class WaqiSensor(Entity): except (KeyError, TypeError): return 'WAQI {}'.format(self._station_id) + @property + def station_name(self): + """Return the name of the station.""" + try: + return self._details['city']['name'] + except (KeyError, TypeError): + return None + @property def icon(self): """Icon to use in the frontend, if any.""" From 62f26fb701fc4f3bdd14da66cec456da4cb3df93 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 16 Jan 2017 11:36:35 -0500 Subject: [PATCH 013/191] Fixes #5357 which especify absolute path to save cookie used by USPS sensor. (#5358) Traceback (most recent call last): File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/helpers/entity_component.py", line 151, in _async_setup_platform entity_platform.add_entities, discovery_info File "/usr/local/lib/python3.5/asyncio/futures.py", line 361, in __iter__ yield self # This tells Task to wait for completion. File "/usr/local/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup future.result() File "/usr/local/lib/python3.5/asyncio/futures.py", line 274, in result raise self._exception File "/usr/local/lib/python3.5/concurrent/futures/thread.py", line 55, in run result = self.fn(*self.args, **self.kwargs) File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/components/sensor/usps.py", line 48, in setup_platform add_devices([USPSSensor(session, config.get(CONF_UPDATE_INTERVAL))]) File "/home/hass/.virtualenvs/home_assistant/lib/python3.5/site-packages/homeassistant/components/sensor/usps.py", line 58, in __init__ self._profile = myusps.get_profile(session) File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 100, in wrapped _login(*args) File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 90, in _login _save_cookies(session.cookies, session.auth.cookie_path) File "/home/hass/.homeassistant/deps/myusps/__init__.py", line 41, in _save_cookies with open(filename, 'wb') as handle: PermissionError: [Errno 13] Permission denied: './usps_cookies.pickle' --- homeassistant/components/sensor/usps.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/usps.py b/homeassistant/components/sensor/usps.py index e9562667f6d..0bc7f6cbd5a 100644 --- a/homeassistant/components/sensor/usps.py +++ b/homeassistant/components/sensor/usps.py @@ -22,6 +22,7 @@ REQUIREMENTS = ['myusps==1.0.1'] _LOGGER = logging.getLogger(__name__) +COOKIE = 'usps_cookies.pickle' CONF_UPDATE_INTERVAL = 'update_interval' ICON = 'mdi:package-variant-closed' STATUS_DELIVERED = 'delivered' @@ -39,8 +40,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the USPS platform.""" import myusps try: + cookie = hass.config.path(COOKIE) session = myusps.get_session(config.get(CONF_USERNAME), - config.get(CONF_PASSWORD)) + config.get(CONF_PASSWORD), + cookie_path=cookie) except myusps.USPSError: _LOGGER.exception('Could not connect to My USPS') return False From 196897fdfc056fe3a10516efcc07e561ca92af3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Mon, 16 Jan 2017 18:03:26 +0100 Subject: [PATCH 014/191] Fix python-nest release number (#5369) * fix_#5365 * fix_#5365 --- 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 30a256f1c37..337cc8f9160 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -20,7 +20,7 @@ _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [ 'http://github.com/technicalpickles/python-nest' '/archive/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip' # nest-cam branch - '#python-nest==3.0.3'] + '#python-nest==3.0.2'] DOMAIN = 'nest' diff --git a/requirements_all.txt b/requirements_all.txt index 6d3f254f02a..9bf5827d8cd 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -178,7 +178,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/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip#python-nest==3.0.3 +http://github.com/technicalpickles/python-nest/archive/e6c9d56a8df455d4d7746389811f2c1387e8cb33.zip#python-nest==3.0.2 # homeassistant.components.switch.dlink https://github.com/LinuxChristian/pyW215/archive/v0.3.7.zip#pyW215==0.3.7 From bd3117a0e7c4e70bfee5ea5207b9afc12c35eb45 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 16 Jan 2017 15:56:47 -0500 Subject: [PATCH 015/191] Reserve a test port for broken api to fix race (#5371) * Reserve a test port for broken api to fix race * I cheated. --- tests/test_remote.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_remote.py b/tests/test_remote.py index fa2a53a96cb..d20acc88857 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -23,7 +23,7 @@ HTTP_BASE_URL = 'http://127.0.0.1:{}'.format(MASTER_PORT) HA_HEADERS = {HTTP_HEADER_HA_AUTH: API_PASSWORD} -broken_api = remote.API('127.0.0.1', "bladiebla") +broken_api = remote.API('127.0.0.1', "bladybla", port=get_test_instance_port()) hass, slave, master_api = None, None, None From d8560a244cf860c710b2833f4552f6e60375a624 Mon Sep 17 00:00:00 2001 From: Duoxilian Date: Mon, 16 Jan 2017 19:58:34 -0600 Subject: [PATCH 016/191] Made target temperature sensitive to auto mode (#5312) * Made target temperature sensitive to auto mode * Used current_operation instead of operation_mode * When not in auto_mode, the temperature is sent to set_temperature * Low and high targets are switched in the call to set_temperature. * Missed on current_operation. Use STATE_AUTO. * Remove incorrectly checked in directory. * Updated set_temperature based on Martin's feedback. * Use ATTR_TEMPERATURE from const.py --- homeassistant/components/climate/ecobee.py | 69 +++++++++++++++------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/climate/ecobee.py b/homeassistant/components/climate/ecobee.py index bfb11f703d1..f820d69754d 100644 --- a/homeassistant/components/climate/ecobee.py +++ b/homeassistant/components/climate/ecobee.py @@ -11,10 +11,10 @@ import voluptuous as vol from homeassistant.components import ecobee from homeassistant.components.climate import ( - DOMAIN, STATE_COOL, STATE_HEAT, STATE_IDLE, ClimateDevice, + DOMAIN, STATE_COOL, STATE_HEAT, STATE_AUTO, STATE_IDLE, ClimateDevice, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH) from homeassistant.const import ( - ATTR_ENTITY_ID, STATE_OFF, STATE_ON, TEMP_FAHRENHEIT) + ATTR_ENTITY_ID, STATE_OFF, STATE_ON, ATTR_TEMPERATURE, TEMP_FAHRENHEIT) from homeassistant.config import load_yaml_config_file import homeassistant.helpers.config_validation as cv @@ -145,12 +145,30 @@ class Thermostat(ClimateDevice): @property def target_temperature_low(self): """Return the lower bound temperature we try to reach.""" - return int(self.thermostat['runtime']['desiredHeat'] / 10) + if self.current_operation == STATE_AUTO: + return int(self.thermostat['runtime']['desiredHeat'] / 10) + else: + return None @property def target_temperature_high(self): """Return the upper bound temperature we try to reach.""" - return int(self.thermostat['runtime']['desiredCool'] / 10) + if self.current_operation == STATE_AUTO: + return int(self.thermostat['runtime']['desiredCool'] / 10) + else: + return None + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + if self.current_operation == STATE_AUTO: + return None + if self.current_operation == STATE_HEAT: + return int(self.thermostat['runtime']['desiredHeat'] / 10) + elif self.current_operation == STATE_COOL: + return int(self.thermostat['runtime']['desiredCool'] / 10) + else: + return None @property def desired_fan_mode(self): @@ -246,25 +264,36 @@ class Thermostat(ClimateDevice): def set_temperature(self, **kwargs): """Set new target temperature.""" - if kwargs.get(ATTR_TARGET_TEMP_LOW) is not None and \ - kwargs.get(ATTR_TARGET_TEMP_HIGH) is not None: - high_temp = int(kwargs.get(ATTR_TARGET_TEMP_LOW)) - low_temp = int(kwargs.get(ATTR_TARGET_TEMP_HIGH)) + low_temp = kwargs.get(ATTR_TARGET_TEMP_LOW) + high_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) + temp = kwargs.get(ATTR_TEMPERATURE) + + if self.current_operation == STATE_HEAT and temp is not None: + low_temp = temp + high_temp = temp + 20 + elif self.current_operation == STATE_COOL and temp is not None: + low_temp = temp - 20 + high_temp = temp + if low_temp is None and high_temp is None: + _LOGGER.error( + 'Missing valid arguments for set_temperature in %s', kwargs) + return + + low_temp = int(low_temp) + high_temp = int(high_temp) if self.hold_temp: - self.data.ecobee.set_hold_temp(self.thermostat_index, low_temp, - high_temp, "indefinite") - _LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, " - "high=%s, is=%s", low_temp, isinstance( - low_temp, (int, float)), high_temp, - isinstance(high_temp, (int, float))) + self.data.ecobee.set_hold_temp( + self.thermostat_index, high_temp, low_temp, "indefinite") else: - self.data.ecobee.set_hold_temp(self.thermostat_index, low_temp, - high_temp) - _LOGGER.debug("Setting ecobee temp to: low=%s, is=%s, " - "high=%s, is=%s", low_temp, isinstance( - low_temp, (int, float)), high_temp, - isinstance(high_temp, (int, float))) + self.data.ecobee.set_hold_temp( + self.thermostat_index, high_temp, low_temp) + + _LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, " + "high=%s, is=%s", low_temp, isinstance( + low_temp, (int, float)), high_temp, + isinstance(high_temp, (int, float))) + self.update_without_throttle = True def set_operation_mode(self, operation_mode): From 0bbb16626c8c70027e2d9e649e1ba91dca775af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 17 Jan 2017 06:52:12 +0100 Subject: [PATCH 017/191] fix bug in flux_led (#5373) --- homeassistant/components/light/flux_led.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/flux_led.py b/homeassistant/components/light/flux_led.py index 22dd40b30ef..46eec35724a 100644 --- a/homeassistant/components/light/flux_led.py +++ b/homeassistant/components/light/flux_led.py @@ -72,6 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): continue device['name'] = device['id'] + " " + ipaddr device[ATTR_MODE] = 'rgbw' + device[CONF_PROTOCOL] = None light = FluxLight(device) if light.is_valid: lights.append(light) From 7511a5842dad21ec517cdad433acdd904941933d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 16 Jan 2017 22:08:47 -0800 Subject: [PATCH 018/191] Fix load_yaml default value (#5383) --- homeassistant/util/yaml.py | 2 +- tests/util/test_yaml.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/util/yaml.py b/homeassistant/util/yaml.py index 64b63b31ca6..00508862279 100644 --- a/homeassistant/util/yaml.py +++ b/homeassistant/util/yaml.py @@ -60,7 +60,7 @@ def load_yaml(fname: str) -> Union[List, Dict]: with open(fname, encoding='utf-8') as conf_file: # If configuration file is empty YAML returns None # We convert that to an empty dict - return yaml.load(conf_file, Loader=SafeLineLoader) or {} + return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict() except yaml.YAMLError as exc: _LOGGER.error(exc) raise HomeAssistantError(exc) diff --git a/tests/util/test_yaml.py b/tests/util/test_yaml.py index 3305fbea6c9..79fd994ce86 100644 --- a/tests/util/test_yaml.py +++ b/tests/util/test_yaml.py @@ -74,6 +74,12 @@ class TestYaml(unittest.TestCase): doc = yaml.yaml.safe_load(file) assert doc["key"] == "value" + with patch_yaml_files({'test.yaml': None}): + conf = 'key: !include test.yaml' + with io.StringIO(conf) as file: + doc = yaml.yaml.safe_load(file) + assert doc["key"] == {} + @patch('homeassistant.util.yaml.os.walk') def test_include_dir_list(self, mock_walk): """Test include dir list yaml.""" From 51dcd3de6d00a68d3b3586d232068b0d12e1a995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 17 Jan 2017 07:35:09 +0100 Subject: [PATCH 019/191] fix bug #5374 --- homeassistant/components/notify/facebook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/notify/facebook.py b/homeassistant/components/notify/facebook.py index 2acabcf02c0..e598b0e818b 100644 --- a/homeassistant/components/notify/facebook.py +++ b/homeassistant/components/notify/facebook.py @@ -25,7 +25,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) -def get_service(hass, config): +def get_service(hass, config, discovery_info=None): """Get the Facebook notification service.""" return FacebookNotificationService(config[CONF_PAGE_ACCESS_TOKEN]) From 59f74896a097a53d523fb1f4cce87d6daa0e99ac Mon Sep 17 00:00:00 2001 From: Tom Dickman Date: Tue, 17 Jan 2017 00:45:44 -0600 Subject: [PATCH 020/191] Updated abreviation for miles in darksky sensor (#5382) --- homeassistant/components/sensor/darksky.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/darksky.py b/homeassistant/components/sensor/darksky.py index 173990a6a2f..ab79cff2aad 100644 --- a/homeassistant/components/sensor/darksky.py +++ b/homeassistant/components/sensor/darksky.py @@ -38,7 +38,7 @@ SENSOR_TYPES = { 'daily_summary': ['Daily Summary', None, None, None, None, None, None], 'icon': ['Icon', None, None, None, None, None, None], 'nearest_storm_distance': ['Nearest Storm Distance', - 'km', 'm', 'km', 'km', 'm', + 'km', 'mi', 'km', 'km', 'mi', 'mdi:weather-lightning'], 'nearest_storm_bearing': ['Nearest Storm Bearing', '°', '°', '°', '°', '°', @@ -63,7 +63,7 @@ SENSOR_TYPES = { 'humidity': ['Humidity', '%', '%', '%', '%', '%', 'mdi:water-percent'], 'pressure': ['Pressure', 'mbar', 'mbar', 'mbar', 'mbar', 'mbar', 'mdi:gauge'], - 'visibility': ['Visibility', 'km', 'm', 'km', 'km', 'm', 'mdi:eye'], + 'visibility': ['Visibility', 'km', 'mi', 'km', 'km', 'mi', 'mdi:eye'], 'ozone': ['Ozone', 'DU', 'DU', 'DU', 'DU', 'DU', 'mdi:eye'], 'apparent_temperature_max': ['Daily High Apparent Temperature', '°C', '°F', '°C', '°C', '°C', From 8e17bf43e0e1e136845b74bf451288b197b0a4f3 Mon Sep 17 00:00:00 2001 From: Bryce Edwards Date: Tue, 17 Jan 2017 00:52:21 -0600 Subject: [PATCH 021/191] added upnp_bind_multicast option to emulated_hue component (#5381) --- homeassistant/components/emulated_hue/__init__.py | 11 ++++++++++- homeassistant/components/emulated_hue/upnp.py | 8 ++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index 2efce06528d..d412a7af91f 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -26,6 +26,7 @@ _LOGGER = logging.getLogger(__name__) CONF_HOST_IP = 'host_ip' CONF_LISTEN_PORT = 'listen_port' +CONF_UPNP_BIND_MULTICAST = 'upnp_bind_multicast' CONF_OFF_MAPS_TO_ON_DOMAINS = 'off_maps_to_on_domains' CONF_EXPOSE_BY_DEFAULT = 'expose_by_default' CONF_EXPOSED_DOMAINS = 'exposed_domains' @@ -35,6 +36,7 @@ TYPE_ALEXA = 'alexa' TYPE_GOOGLE = 'google_home' DEFAULT_LISTEN_PORT = 8300 +DEFAULT_UPNP_BIND_MULTICAST = True DEFAULT_OFF_MAPS_TO_ON_DOMAINS = ['script', 'scene'] DEFAULT_EXPOSE_BY_DEFAULT = True DEFAULT_EXPOSED_DOMAINS = [ @@ -47,6 +49,7 @@ CONFIG_SCHEMA = 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_UPNP_BIND_MULTICAST): cv.boolean, 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, @@ -84,7 +87,8 @@ def setup(hass, yaml_config): server.register_view(HueOneLightChangeView(config)) upnp_listener = UPNPResponderThread( - config.host_ip_addr, config.listen_port) + config.host_ip_addr, config.listen_port, + config.upnp_bind_multicast) @asyncio.coroutine def stop_emulated_hue_bridge(event): @@ -134,6 +138,11 @@ class Config(object): _LOGGER.warning('When targetting Google Home, listening port has ' 'to be port 80') + # Get whether or not UPNP binds to multicast address (239.255.255.250) + # or to the unicast address (host_ip_addr) + self.upnp_bind_multicast = conf.get( + CONF_UPNP_BIND_MULTICAST, DEFAULT_UPNP_BIND_MULTICAST) + # 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 diff --git a/homeassistant/components/emulated_hue/upnp.py b/homeassistant/components/emulated_hue/upnp.py index fd880c40e6e..de3be34e2de 100644 --- a/homeassistant/components/emulated_hue/upnp.py +++ b/homeassistant/components/emulated_hue/upnp.py @@ -60,12 +60,13 @@ class UPNPResponderThread(threading.Thread): _interrupted = False - def __init__(self, host_ip_addr, listen_port): + def __init__(self, host_ip_addr, listen_port, upnp_bind_multicast): """Initialize the class.""" threading.Thread.__init__(self) self.host_ip_addr = host_ip_addr self.listen_port = listen_port + self.upnp_bind_multicast = upnp_bind_multicast # Note that the double newline at the end of # this string is required per the SSDP spec @@ -116,7 +117,10 @@ USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1 socket.inet_aton("239.255.255.250") + socket.inet_aton(self.host_ip_addr)) - ssdp_socket.bind(("239.255.255.250", 1900)) + if self.upnp_bind_multicast: + ssdp_socket.bind(("239.255.255.250", 1900)) + else: + ssdp_socket.bind((self.host_ip_addr, 1900)) while True: if self._interrupted: From bae38ac17b29097b0b8ce9262fa232c5d91e0165 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Tue, 17 Jan 2017 01:52:53 -0500 Subject: [PATCH 022/191] Include .ignore file for search utilities (#5290) * Include .ignore file for search utilities This instructs search utilities to ignore generated html/js files. * Panels has no js files --- .ignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .ignore diff --git a/.ignore b/.ignore new file mode 100644 index 00000000000..45c6dc5561f --- /dev/null +++ b/.ignore @@ -0,0 +1,6 @@ +# Patterns matched in this file will be ignored by supported search utilities + +# Ignore generated html and javascript files +/homeassistant/components/frontend/www_static/*.html +/homeassistant/components/frontend/www_static/*.js +/homeassistant/components/frontend/www_static/panels/*.html From d62b1fc8085d2d58a4e976a5c74833c9c6f97cf7 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Tue, 17 Jan 2017 07:53:34 +0100 Subject: [PATCH 023/191] Make initial flux update directly when turning on (#5266) --- homeassistant/components/switch/flux.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/switch/flux.py b/homeassistant/components/switch/flux.py index 9fccf75ea4f..75ecc30c823 100644 --- a/homeassistant/components/switch/flux.py +++ b/homeassistant/components/switch/flux.py @@ -134,6 +134,8 @@ class FluxSwitch(SwitchDevice): def turn_on(self, **kwargs): """Turn on flux.""" + if not self._state: # make initial update + self.flux_update() self._state = True self.unsub_tracker = track_utc_time_change(self.hass, self.flux_update, second=[0, 30]) From 65bf30643af56bc81a6be665cb3ca6b14dd67e66 Mon Sep 17 00:00:00 2001 From: Tom Dickman Date: Tue, 17 Jan 2017 00:55:05 -0600 Subject: [PATCH 024/191] Use timezone aware timestamp in flux_update (#5378) --- homeassistant/components/switch/flux.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/switch/flux.py b/homeassistant/components/switch/flux.py index 75ecc30c823..8f3ff769a06 100644 --- a/homeassistant/components/switch/flux.py +++ b/homeassistant/components/switch/flux.py @@ -14,12 +14,11 @@ from homeassistant.components.light import is_on, turn_on from homeassistant.components.sun import next_setting, next_rising from homeassistant.components.switch import DOMAIN, SwitchDevice from homeassistant.const import CONF_NAME, CONF_PLATFORM -from homeassistant.helpers.event import track_utc_time_change +from homeassistant.helpers.event import track_time_change from homeassistant.util.color import ( color_temperature_to_rgb, color_RGB_to_xy, color_temperature_kelvin_to_mired, HASS_COLOR_MIN, HASS_COLOR_MAX) from homeassistant.util.dt import now as dt_now -from homeassistant.util.dt import as_local import homeassistant.helpers.config_validation as cv DEPENDENCIES = ['sun', 'light'] @@ -137,8 +136,8 @@ class FluxSwitch(SwitchDevice): if not self._state: # make initial update self.flux_update() self._state = True - self.unsub_tracker = track_utc_time_change(self.hass, self.flux_update, - second=[0, 30]) + self.unsub_tracker = track_time_change(self.hass, self.flux_update, + second=[0, 30]) self.schedule_update_ha_state() def turn_off(self, **kwargs): @@ -199,8 +198,7 @@ class FluxSwitch(SwitchDevice): _LOGGER.info("Lights updated to x:%s y:%s brightness:%s, %s%%" " of %s cycle complete at %s", x_val, y_val, brightness, round( - percentage_complete * 100), time_state, - as_local(now)) + percentage_complete * 100), time_state, now) else: # Convert to mired and clamp to allowed values mired = color_temperature_kelvin_to_mired(temp) @@ -208,8 +206,7 @@ class FluxSwitch(SwitchDevice): set_lights_temp(self.hass, self._lights, mired, brightness) _LOGGER.info("Lights updated to mired:%s brightness:%s, %s%%" " of %s cycle complete at %s", mired, brightness, - round(percentage_complete * 100), - time_state, as_local(now)) + round(percentage_complete * 100), time_state, now) def find_start_time(self, now): """Return sunrise or start_time if given.""" From 41a6c35ea26ec6cac2e3c122aea3ab0961fbce7d Mon Sep 17 00:00:00 2001 From: Jesse Newland Date: Tue, 17 Jan 2017 00:55:42 -0600 Subject: [PATCH 025/191] Install phantomjs in Docker container (#5368) --- Dockerfile | 2 +- script/install_phantomjs | 15 +++++++++++++++ script/setup_docker_prereqs | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100755 script/install_phantomjs diff --git a/Dockerfile b/Dockerfile index 7522ca9cb64..ecdbbafba66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ RUN mkdir -p /usr/src/app WORKDIR /usr/src/app # Copy build scripts -COPY script/setup_docker_prereqs script/build_python_openzwave script/build_libcec script/ +COPY script/setup_docker_prereqs script/build_python_openzwave script/build_libcec script/install_phantomjs script/ RUN script/setup_docker_prereqs # Install hass component dependencies diff --git a/script/install_phantomjs b/script/install_phantomjs new file mode 100755 index 00000000000..178dfad540e --- /dev/null +++ b/script/install_phantomjs @@ -0,0 +1,15 @@ +#!/bin/bash +# Sets up phantomjs to be used with Home Assistant. + +# Stop on errors +set -e + +PHANTOMJS_VERSION="2.1.1" + +cd "$(dirname "$0")/.." +mkdir -p build && cd build + +curl -LSO https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 +tar -xjf phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 +mv phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin/phantomjs /usr/bin/phantomjs +/usr/bin/phantomjs -v diff --git a/script/setup_docker_prereqs b/script/setup_docker_prereqs index d6ec2789c80..f0c6ddf4cc5 100755 --- a/script/setup_docker_prereqs +++ b/script/setup_docker_prereqs @@ -50,6 +50,9 @@ cp -R /usr/src/app/build/python-openzwave/openzwave/config /usr/local/share/pyth # Build and install libcec script/build_libcec +# Install phantomjs +script/install_phantomjs + # Remove packages apt-get remove -y --purge ${PACKAGES_DEV[@]} apt-get -y --purge autoremove From b915cf776bfc90ac87c71e374c2bce4625ba1c52 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 17 Jan 2017 01:57:25 -0500 Subject: [PATCH 026/191] Introduced Amcrest camera sensors and bump Amcrest module version (#5310) * Introduced Amcrest camera sensors * Makes script/gen_requirements_all.py happy * Bump Amcrest version across all components * - Adjusted scan_interval to 10 seconds - Filtering HTTPError and ConnectTimeout exceptions - Removed @Throttle decorator --- .coveragerc | 1 + homeassistant/components/camera/amcrest.py | 2 +- homeassistant/components/sensor/amcrest.py | 142 +++++++++++++++++++++ requirements_all.txt | 3 +- 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/sensor/amcrest.py diff --git a/.coveragerc b/.coveragerc index 506e51a63d8..91d7e64e79b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -262,6 +262,7 @@ omit = homeassistant/components/openalpr.py homeassistant/components/remote/harmony.py homeassistant/components/scene/hunterdouglas_powerview.py + homeassistant/components/sensor/amcrest.py homeassistant/components/sensor/arest.py homeassistant/components/sensor/arwn.py homeassistant/components/sensor/bbox.py diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py index a3d8dcf35df..bec760dbe10 100644 --- a/homeassistant/components/camera/amcrest.py +++ b/homeassistant/components/camera/amcrest.py @@ -20,7 +20,7 @@ from homeassistant.const import ( from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_create_clientsession -REQUIREMENTS = ['amcrest==1.0.0'] +REQUIREMENTS = ['amcrest==1.1.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sensor/amcrest.py b/homeassistant/components/sensor/amcrest.py new file mode 100644 index 00000000000..7a41bcc6fe4 --- /dev/null +++ b/homeassistant/components/sensor/amcrest.py @@ -0,0 +1,142 @@ +""" +This component provides HA sensor support for Amcrest IP cameras. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.amcrest/ +""" +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_HOST, CONF_NAME, CONF_MONITORED_CONDITIONS, + CONF_SCAN_INTERVAL, CONF_USERNAME, CONF_PASSWORD, + CONF_PORT, STATE_UNKNOWN) +from homeassistant.helpers.entity import Entity +import homeassistant.loader as loader + +from requests.exceptions import HTTPError, ConnectTimeout + +REQUIREMENTS = ['amcrest==1.1.0'] + +_LOGGER = logging.getLogger(__name__) + +NOTIFICATION_ID = 'amcrest_notification' +NOTIFICATION_TITLE = 'Amcrest Sensor Setup' + +DEFAULT_NAME = 'Amcrest' +DEFAULT_PORT = 80 +DEFAULT_SCAN_INTERVAL = timedelta(seconds=10) + +# Sensor types are defined like: Name, units, icon +SENSOR_TYPES = { + 'motion_detector': ['Motion Detected', None, 'run'], + 'sdcard': ['SD Used', '%', 'sd'], + 'ptz_preset': ['PTZ Preset', None, 'camera-iris'], +} + +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_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): + vol.All(vol.Coerce(int), vol.Range(min=1)), + vol.Required(CONF_MONITORED_CONDITIONS, default=[]): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up a sensor for an Amcrest IP Camera.""" + from amcrest import AmcrestCamera + + data = AmcrestCamera( + config.get(CONF_HOST), config.get(CONF_PORT), + config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) + + persistent_notification = loader.get_component('persistent_notification') + try: + data.camera.current_time + except (ConnectTimeout, HTTPError) as ex: + _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex)) + persistent_notification.create( + hass, 'Error: {}
' + 'You will need to restart hass after fixing.' + ''.format(ex), + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) + return False + + sensors = [] + for sensor_type in config.get(CONF_MONITORED_CONDITIONS): + sensors.append(AmcrestSensor(config, data, sensor_type)) + + add_devices(sensors, True) + + return True + + +class AmcrestSensor(Entity): + """A sensor implementation for Amcrest IP camera.""" + + def __init__(self, device_info, data, sensor_type): + """Initialize a sensor for Amcrest camera.""" + super(AmcrestSensor, self).__init__() + self._attrs = {} + self._data = data + self._sensor_type = sensor_type + self._name = '{0}_{1}'.format(device_info.get(CONF_NAME), + SENSOR_TYPES.get(self._sensor_type)[0]) + self._icon = 'mdi:{}'.format(SENSOR_TYPES.get(self._sensor_type)[2]) + self._state = STATE_UNKNOWN + + @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 device_state_attributes(self): + """Return the state attributes.""" + return self._attrs + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return self._icon + + @property + def unit_of_measurement(self): + """Return the units of measurement.""" + return SENSOR_TYPES.get(self._sensor_type)[1] + + def update(self): + """Get the latest data and updates the state.""" + version, build_date = self._data.camera.software_information + self._attrs['Build Date'] = build_date.split('=')[-1] + self._attrs['Serial Number'] = self._data.camera.serial_number + self._attrs['Version'] = version.split('=')[-1] + + if self._sensor_type == 'motion_detector': + self._state = self._data.camera.is_motion_detected + self._attrs['Record Mode'] = self._data.camera.record_mode + + elif self._sensor_type == 'ptz_preset': + self._state = self._data.camera.ptz_presets_count + + elif self._sensor_type == 'sdcard': + sd_used = self._data.camera.storage_used + sd_total = self._data.camera.storage_total + self._attrs['Total'] = '{0} {1}'.format(*sd_total) + self._attrs['Used'] = '{0} {1}'.format(*sd_used) + self._state = self._data.camera.percent(sd_used[0], sd_total[0]) diff --git a/requirements_all.txt b/requirements_all.txt index 9bf5827d8cd..41acf7fcabd 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,8 @@ TwitterAPI==2.4.3 aiohttp_cors==0.5.0 # homeassistant.components.camera.amcrest -amcrest==1.0.0 +# homeassistant.components.sensor.amcrest +amcrest==1.1.0 # homeassistant.components.apcupsd apcaccess==0.0.4 From ef274c691441a228f461fa394cb07e82d2892191 Mon Sep 17 00:00:00 2001 From: Christiaan Blom Date: Tue, 17 Jan 2017 07:58:38 +0100 Subject: [PATCH 027/191] New Discord notification component (#5330) * Initial commit of discord notification component * Fixed error where script added extra entries to .coveragerc * Cleaned up code * Compliance to PEP8 * removed dependencies * readded dependencies * changed name of client id to token for configuration * Changes for Hound * Incorporated Review Feedback * Review feedback * Updated requirements file * Check compliance --- .coveragerc | 1 + homeassistant/components/notify/discord.py | 51 ++++++++++++++++++++++ requirements_all.txt | 3 ++ 3 files changed, 55 insertions(+) create mode 100644 homeassistant/components/notify/discord.py diff --git a/.coveragerc b/.coveragerc index 91d7e64e79b..f1631b4e99f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -231,6 +231,7 @@ omit = homeassistant/components/notify/aws_lambda.py homeassistant/components/notify/aws_sns.py homeassistant/components/notify/aws_sqs.py + homeassistant/components/notify/discord.py homeassistant/components/notify/facebook.py homeassistant/components/notify/free_mobile.py homeassistant/components/notify/gntp.py diff --git a/homeassistant/components/notify/discord.py b/homeassistant/components/notify/discord.py new file mode 100644 index 00000000000..3d426b22645 --- /dev/null +++ b/homeassistant/components/notify/discord.py @@ -0,0 +1,51 @@ +"""Discord platform for notify component.""" +import logging +import asyncio +import voluptuous as vol +import homeassistant.helpers.config_validation as cv +from homeassistant.components.notify import ( + PLATFORM_SCHEMA, BaseNotificationService) + +_LOGGER = logging.getLogger(__name__) + +REQUIREMENTS = ['discord.py==0.16.0'] + +CONF_TOKEN = 'token' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_TOKEN): cv.string +}) + + +def get_service(hass, config, discovery_info=None): + """Get the Discord notification service.""" + token = config.get(CONF_TOKEN) + return DiscordNotificationService(hass, token) + + +class DiscordNotificationService(BaseNotificationService): + """Implement the notification service for Discord.""" + + def __init__(self, hass, token): + """Initialize the service.""" + self.token = token + self.hass = hass + + @asyncio.coroutine + def async_send_message(self, message, target): + """Login to Discord, send message to channel(s) and log out.""" + import discord + discord_bot = discord.Client(loop=self.hass.loop) + + yield from discord_bot.login(self.token) + + for channelid in target: + channel = discord.Object(id=channelid) + yield from discord_bot.send_message(channel, message) + + yield from discord_bot.logout() + yield from discord_bot.close() + + def send_message(self, message=None, target=None, **kwargs): + """Send a message using Discord.""" + self.hass.async_add_job(self.async_send_message(message, target)) diff --git a/requirements_all.txt b/requirements_all.txt index 41acf7fcabd..4c751e03e1f 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -87,6 +87,9 @@ denonavr==0.3.0 # homeassistant.components.media_player.directv directpy==0.1 +# homeassistant.components.notify.discord +discord.py==0.16.0 + # homeassistant.components.updater distro==1.0.2 From 784b87eb2f551f94536af8ce7495c797438c4610 Mon Sep 17 00:00:00 2001 From: Nick Touran Date: Mon, 16 Jan 2017 23:01:57 -0800 Subject: [PATCH 028/191] Add listing and selection of available MPD playlists (#5237) * Add listing and selection of available MPD playlists through input source UI. * MPD support updating playlist list on the fly as well as at turn-on. * Added no_throttle to force playlist update on mpd power cycle. * Added kwargs signature to get Throttle working right. --- homeassistant/components/media_player/mpd.py | 39 +++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py index 015ba2fd0ac..2f16410e783 100644 --- a/homeassistant/components/media_player/mpd.py +++ b/homeassistant/components/media_player/mpd.py @@ -6,6 +6,7 @@ https://home-assistant.io/components/media_player.mpd/ """ import logging import socket +from datetime import timedelta import voluptuous as vol @@ -13,11 +14,12 @@ from homeassistant.components.media_player import ( MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, PLATFORM_SCHEMA, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_SET, SUPPORT_PLAY_MEDIA, SUPPORT_PLAY, MEDIA_TYPE_PLAYLIST, - MediaPlayerDevice) + SUPPORT_SELECT_SOURCE, MediaPlayerDevice) from homeassistant.const import ( STATE_OFF, STATE_PAUSED, STATE_PLAYING, CONF_PORT, CONF_PASSWORD, CONF_HOST) import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle REQUIREMENTS = ['python-mpd2==0.5.5'] @@ -28,9 +30,11 @@ CONF_LOCATION = 'location' DEFAULT_LOCATION = 'MPD' DEFAULT_PORT = 6600 +PLAYLIST_UPDATE_INTERVAL = timedelta(seconds=120) + SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \ SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \ - SUPPORT_PLAY_MEDIA | SUPPORT_PLAY + SUPPORT_PLAY_MEDIA | SUPPORT_PLAY | SUPPORT_SELECT_SOURCE PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, @@ -88,6 +92,8 @@ class MpdDevice(MediaPlayerDevice): self.password = password self.status = None self.currentsong = None + self.playlists = [] + self.currentplaylist = None self.client = mpd.MPDClient() self.client.timeout = 10 @@ -100,6 +106,7 @@ class MpdDevice(MediaPlayerDevice): try: self.status = self.client.status() self.currentsong = self.client.currentsong() + self._update_playlists() except (mpd.ConnectionError, OSError, BrokenPipeError, ValueError): # Cleanly disconnect in case connection is not in valid state try: @@ -181,6 +188,20 @@ class MpdDevice(MediaPlayerDevice): """Flag of media commands that are supported.""" return SUPPORT_MPD + @property + def source(self): + """Name of the current input source.""" + return self.currentplaylist + + @property + def source_list(self): + """List of available input sources.""" + return self.playlists + + def select_source(self, source): + """Choose a different available playlist and play it.""" + self.play_media(MEDIA_TYPE_PLAYLIST, source) + def turn_off(self): """Service to send the MPD the command to stop playing.""" self.client.stop() @@ -188,6 +209,14 @@ class MpdDevice(MediaPlayerDevice): def turn_on(self): """Service to send the MPD the command to start playing.""" self.client.play() + self._update_playlists(no_throttle=True) + + @Throttle(PLAYLIST_UPDATE_INTERVAL) + def _update_playlists(self, **kwargs): + """Update available MPD playlists.""" + self.playlists = [] + for playlist_data in self.client.listplaylists(): + self.playlists.append(playlist_data['playlist']) def set_volume_level(self, volume): """Set volume of media player.""" @@ -227,6 +256,12 @@ class MpdDevice(MediaPlayerDevice): """Send the media player the command for playing a playlist.""" _LOGGER.info(str.format("Playing playlist: {0}", media_id)) if media_type == MEDIA_TYPE_PLAYLIST: + if media_id in self.playlists: + self.currentplaylist = media_id + else: + self.currentplaylist = None + _LOGGER.warning(str.format("Unknown playlist name %s.", + media_id)) self.client.clear() self.client.load(media_id) self.client.play() From fd5c2ad08fcb023d342548cec52ef99c39020341 Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Tue, 17 Jan 2017 08:04:50 +0100 Subject: [PATCH 029/191] Denon improvements (#5251) * denonavr: Expose input as title when in non playing modes Signed-off-by: Anton Lundin * denon: Pop from the intended end of the list Pop from front of list, so we start with NSE0, then NSE1X and so on. Signed-off-by: Anton Lundin * denonavr: Don't provide broken media_url's Only return a media_url if we're in a state that might provide one. Signed-off-by: Anton Lundin * denonavr: Only expose player support when in a player mode This changes so the denonavr only exposes the media player support, when in a mode that supports media playing. Signed-off-by: Anton Lundin --- homeassistant/components/media_player/denon.py | 2 +- .../components/media_player/denonavr.py | 17 ++++++++++++----- requirements_all.txt | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/media_player/denon.py b/homeassistant/components/media_player/denon.py index 1feee79635d..22ccd2f0d56 100755 --- a/homeassistant/components/media_player/denon.py +++ b/homeassistant/components/media_player/denon.py @@ -150,7 +150,7 @@ class DenonDevice(MediaPlayerDevice): 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' + self._mediainfo += line[len(answer_codes.pop(0)):] + '\n' else: self._mediainfo = self.source diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 50b16afc811..e6f0bf99d42 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_NAME, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['denonavr==0.3.0'] +REQUIREMENTS = ['denonavr==0.3.1'] _LOGGER = logging.getLogger(__name__) @@ -28,7 +28,9 @@ KEY_DENON_CACHE = 'denonavr_hosts' SUPPORT_DENON = SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \ - SUPPORT_SELECT_SOURCE | SUPPORT_PLAY_MEDIA | \ + SUPPORT_SELECT_SOURCE | SUPPORT_VOLUME_SET + +SUPPORT_MEDIA_MODES = SUPPORT_PLAY_MEDIA | \ SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | \ SUPPORT_NEXT_TRACK | SUPPORT_VOLUME_SET | SUPPORT_PLAY @@ -167,7 +169,10 @@ class DenonDevice(MediaPlayerDevice): @property def supported_media_commands(self): """Flag of media commands that are supported.""" - return SUPPORT_DENON + if self._current_source in self._receiver.netaudio_func_list: + return SUPPORT_DENON | SUPPORT_MEDIA_MODES + else: + return SUPPORT_DENON @property def media_content_id(self): @@ -190,7 +195,7 @@ class DenonDevice(MediaPlayerDevice): @property def media_image_url(self): """Image url of current playing media.""" - if self._power == "ON": + if self._current_source in self._receiver.playing_func_list: return self._media_image_url else: return None @@ -198,7 +203,9 @@ class DenonDevice(MediaPlayerDevice): @property def media_title(self): """Title of current playing media.""" - if self._title is not None: + if self._current_source not in self._receiver.playing_func_list: + return self._current_source + elif self._title is not None: return self._title else: return self._frequency diff --git a/requirements_all.txt b/requirements_all.txt index 4c751e03e1f..7edbb4bcc70 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -82,7 +82,7 @@ colorlog>2.1,<3 concord232==0.14 # homeassistant.components.media_player.denonavr -denonavr==0.3.0 +denonavr==0.3.1 # homeassistant.components.media_player.directv directpy==0.1 From c72f8b1a06338108d779ba6966ad4dc3c3671607 Mon Sep 17 00:00:00 2001 From: Giel Janssens Date: Tue, 17 Jan 2017 08:10:18 +0100 Subject: [PATCH 030/191] Netatmo Presence (#5122) * Netatmo Presence * Travis * Remove def camera_stream --- .../components/binary_sensor/netatmo.py | 151 +++++++++++++----- homeassistant/components/camera/netatmo.py | 45 ++++-- homeassistant/components/netatmo.py | 33 ++-- requirements_all.txt | 2 +- 4 files changed, 160 insertions(+), 71 deletions(-) diff --git a/homeassistant/components/binary_sensor/netatmo.py b/homeassistant/components/binary_sensor/netatmo.py index 94ef0faaad0..4ef29b9e5f5 100644 --- a/homeassistant/components/binary_sensor/netatmo.py +++ b/homeassistant/components/binary_sensor/netatmo.py @@ -1,19 +1,19 @@ """ Support for the Netatmo binary sensors. -The binary sensors based on events seen by the NetatmoCamera +The binary sensors based on events seen by the Netatmo cameras. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.netatmo/ +https://home-assistant.io/components/binary_sensor.netatmo/. """ import logging import voluptuous as vol from homeassistant.components.binary_sensor import ( BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.netatmo import WelcomeData +from homeassistant.components.netatmo import CameraData from homeassistant.loader import get_component -from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_TIMEOUT +from homeassistant.const import CONF_TIMEOUT, CONF_OFFSET from homeassistant.helpers import config_validation as cv DEPENDENCIES = ["netatmo"] @@ -22,24 +22,37 @@ _LOGGER = logging.getLogger(__name__) # These are the available sensors mapped to binary_sensor class -SENSOR_TYPES = { - "Someone known": 'occupancy', - "Someone unknown": 'motion', - "Motion": 'motion', +WELCOME_SENSOR_TYPES = { + "Someone known": "motion", + "Someone unknown": "motion", + "Motion": "motion", "Tag Vibration": 'vibration', - "Tag Open": 'opening', + "Tag Open": 'opening' +} +PRESENCE_SENSOR_TYPES = { + "Outdoor motion": "motion", + "Outdoor human": "motion", + "Outdoor animal": "motion", + "Outdoor vehicle": "motion" } CONF_HOME = 'home' CONF_CAMERAS = 'cameras' +CONF_WELCOME_SENSORS = 'welcome_sensors' +CONF_PRESENCE_SENSORS = 'presence_sensors' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOME): cv.string, vol.Optional(CONF_TIMEOUT): cv.positive_int, + vol.Optional(CONF_OFFSET): cv.positive_int, vol.Optional(CONF_CAMERAS, default=[]): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(CONF_MONITORED_CONDITIONS, default=SENSOR_TYPES.keys()): - vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Optional( + CONF_WELCOME_SENSORS, default=WELCOME_SENSOR_TYPES.keys()): + vol.All(cv.ensure_list, [vol.In(WELCOME_SENSOR_TYPES)]), + vol.Optional( + CONF_PRESENCE_SENSORS, default=PRESENCE_SENSOR_TYPES.keys()): + vol.All(cv.ensure_list, [vol.In(PRESENCE_SENSOR_TYPES)]), }) @@ -49,48 +62,68 @@ def setup_platform(hass, config, add_devices, discovery_info=None): netatmo = get_component('netatmo') home = config.get(CONF_HOME, None) timeout = config.get(CONF_TIMEOUT, 15) + offset = config.get(CONF_OFFSET, 90) module_name = None import lnetatmo try: - data = WelcomeData(netatmo.NETATMO_AUTH, home) + data = CameraData(netatmo.NETATMO_AUTH, home) if data.get_camera_names() == []: return None except lnetatmo.NoDevice: return None - sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES) + welcome_sensors = config.get( + CONF_WELCOME_SENSORS, WELCOME_SENSOR_TYPES) + presence_sensors = config.get( + CONF_PRESENCE_SENSORS, PRESENCE_SENSOR_TYPES) for camera_name in data.get_camera_names(): - if CONF_CAMERAS in config: - if config[CONF_CAMERAS] != [] and \ - camera_name not in config[CONF_CAMERAS]: - continue - for variable in sensors: - if variable in ('Tag Vibration', 'Tag Open'): - continue - add_devices([WelcomeBinarySensor(data, camera_name, module_name, - home, timeout, variable)]) + camera_type = data.get_camera_type(camera=camera_name, home=home) + if camera_type == "NACamera": + if CONF_CAMERAS in config: + if config[CONF_CAMERAS] != [] and \ + camera_name not in config[CONF_CAMERAS]: + continue + for variable in welcome_sensors: + add_devices([NetatmoBinarySensor(data, camera_name, + module_name, home, timeout, + offset, camera_type, + variable)]) + if camera_type == "NOC": + if CONF_CAMERAS in config: + if config[CONF_CAMERAS] != [] and \ + camera_name not in config[CONF_CAMERAS]: + continue + for variable in presence_sensors: + add_devices([NetatmoBinarySensor(data, camera_name, + module_name, home, timeout, + offset, camera_type, + variable)]) for module_name in data.get_module_names(camera_name): - for variable in sensors: + for variable in welcome_sensors: if variable in ('Tag Vibration', 'Tag Open'): - add_devices([WelcomeBinarySensor(data, camera_name, + add_devices([NetatmoBinarySensor(data, camera_name, module_name, home, - timeout, variable)]) + timeout, offset, + camera_type, + variable)]) -class WelcomeBinarySensor(BinarySensorDevice): - """Represent a single binary sensor in a Netatmo Welcome device.""" +class NetatmoBinarySensor(BinarySensorDevice): + """Represent a single binary sensor in a Netatmo Camera device.""" - def __init__(self, data, camera_name, module_name, home, timeout, sensor): + def __init__(self, data, camera_name, module_name, home, + timeout, offset, camera_type, 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 + self._offset = offset if home: self._name = home + ' / ' + camera_name else: @@ -99,10 +132,11 @@ class WelcomeBinarySensor(BinarySensorDevice): self._name += ' / ' + module_name self._sensor_name = sensor self._name += ' ' + sensor - camera_id = data.welcomedata.cameraByName(camera=camera_name, + camera_id = data.camera_data.cameraByName(camera=camera_name, home=home)['id'] - self._unique_id = "Welcome_binary_sensor {0} - {1}".format(self._name, + self._unique_id = "Netatmo_binary_sensor {0} - {1}".format(self._name, camera_id) + self._cameratype = camera_type self.update() @property @@ -118,7 +152,12 @@ class WelcomeBinarySensor(BinarySensorDevice): @property def sensor_class(self): """Return the class of this sensor, from SENSOR_CLASSES.""" - return SENSOR_TYPES.get(self._sensor_name) + if self._cameratype == "NACamera": + return WELCOME_SENSOR_TYPES.get(self._sensor_name) + elif self._cameratype == "NOC": + return PRESENCE_SENSOR_TYPES.get(self._sensor_name) + else: + return None @property def is_on(self): @@ -130,30 +169,54 @@ class WelcomeBinarySensor(BinarySensorDevice): self._data.update() self._data.update_event() - if self._sensor_name == "Someone known": - self._state =\ - self._data.welcomedata.someoneKnownSeen(self._home, + if self._cameratype == "NACamera": + if self._sensor_name == "Someone known": + self._state =\ + self._data.camera_data.someoneKnownSeen(self._home, self._camera_name, self._timeout*60) - elif self._sensor_name == "Someone unknown": - self._state =\ - self._data.welcomedata.someoneUnknownSeen(self._home, + elif self._sensor_name == "Someone unknown": + self._state =\ + self._data.camera_data.someoneUnknownSeen( + self._home, self._camera_name, self._timeout*60) + elif self._sensor_name == "Motion": + self._state =\ + self._data.camera_data.motionDetected(self._home, self._camera_name, self._timeout*60) - elif self._sensor_name == "Motion": - self._state =\ - self._data.welcomedata.motionDetected(self._home, - self._camera_name, - self._timeout*60) + else: + return None + elif self._cameratype == "NOC": + if self._sensor_name == "Outdoor motion": + self._state =\ + self._data.camera_data.outdoormotionDetected( + self._home, self._camera_name, self._offset) + elif self._sensor_name == "Outdoor human": + self._state =\ + self._data.camera_data.humanDetected(self._home, + self._camera_name, + self._offset) + elif self._sensor_name == "Outdoor animal": + self._state =\ + self._data.camera_data.animalDetected(self._home, + self._camera_name, + self._offset) + elif self._sensor_name == "Outdoor vehicle": + self._state =\ + self._data.camera_data.carDetected(self._home, + self._camera_name, + self._offset) + else: + return None elif self._sensor_name == "Tag Vibration": self._state =\ - self._data.welcomedata.moduleMotionDetected(self._home, + self._data.camera_data.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._data.camera_data.moduleOpened(self._home, self._module_name, self._camera_name) else: diff --git a/homeassistant/components/camera/netatmo.py b/homeassistant/components/camera/netatmo.py index 47808de02b9..563de206dea 100644 --- a/homeassistant/components/camera/netatmo.py +++ b/homeassistant/components/camera/netatmo.py @@ -1,15 +1,15 @@ """ -Support for the Netatmo Welcome camera. +Support for the Netatmo cameras. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.netatmo/ +https://home-assistant.io/components/camera.netatmo/. """ import logging import requests import voluptuous as vol -from homeassistant.components.netatmo import WelcomeData +from homeassistant.components.netatmo import CameraData from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) from homeassistant.loader import get_component from homeassistant.helpers import config_validation as cv @@ -30,41 +30,43 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup access to Netatmo Welcome cameras.""" + """Setup access to Netatmo cameras.""" netatmo = get_component('netatmo') home = config.get(CONF_HOME) import lnetatmo try: - data = WelcomeData(netatmo.NETATMO_AUTH, home) + data = CameraData(netatmo.NETATMO_AUTH, home) for camera_name in data.get_camera_names(): + camera_type = data.get_camera_type(camera=camera_name, home=home) if CONF_CAMERAS in config: if config[CONF_CAMERAS] != [] and \ camera_name not in config[CONF_CAMERAS]: continue - add_devices([WelcomeCamera(data, camera_name, home)]) + add_devices([NetatmoCamera(data, camera_name, home, camera_type)]) except lnetatmo.NoDevice: return None -class WelcomeCamera(Camera): - """Representation of the images published from Welcome camera.""" +class NetatmoCamera(Camera): + """Representation of the images published from a Netatmo camera.""" - def __init__(self, data, camera_name, home): + def __init__(self, data, camera_name, home, camera_type): """Setup for access to the Netatmo camera images.""" - super(WelcomeCamera, self).__init__() + super(NetatmoCamera, self).__init__() self._data = data self._camera_name = camera_name if home: self._name = home + ' / ' + camera_name else: self._name = camera_name - camera_id = data.welcomedata.cameraByName(camera=camera_name, + camera_id = data.camera_data.cameraByName(camera=camera_name, home=home)['id'] self._unique_id = "Welcome_camera {0} - {1}".format(self._name, camera_id) - self._vpnurl, self._localurl = self._data.welcomedata.cameraUrls( + self._vpnurl, self._localurl = self._data.camera_data.cameraUrls( camera=camera_name ) + self._cameratype = camera_type def camera_image(self): """Return a still image response from the camera.""" @@ -79,15 +81,30 @@ class WelcomeCamera(Camera): _LOGGER.error('Welcome VPN url changed: %s', error) self._data.update() (self._vpnurl, self._localurl) = \ - self._data.welcomedata.cameraUrls(camera=self._camera_name) + self._data.camera_data.cameraUrls(camera=self._camera_name) return None return response.content @property def name(self): - """Return the name of this Netatmo Welcome device.""" + """Return the name of this Netatmo camera device.""" return self._name + @property + def brand(self): + """Camera brand.""" + return "Netatmo" + + @property + def model(self): + """Camera model.""" + if self._cameratype == "NOC": + return "Presence" + elif self._cameratype == "NACamera": + return "Welcome" + else: + return None + @property def unique_id(self): """Return the unique ID for this sensor.""" diff --git a/homeassistant/components/netatmo.py b/homeassistant/components/netatmo.py index b4ebbc1d460..ea691ac94f4 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.8.1.zip#lnetatmo==0.8.1'] + 'v0.9.0.zip#lnetatmo==0.9.0'] _LOGGER = logging.getLogger(__name__) @@ -53,7 +53,8 @@ def setup(hass, config): config[DOMAIN][CONF_API_KEY], config[DOMAIN][CONF_SECRET_KEY], config[DOMAIN][CONF_USERNAME], config[DOMAIN][CONF_PASSWORD], 'read_station read_camera access_camera ' - 'read_thermostat write_thermostat') + 'read_thermostat write_thermostat ' + 'read_presence access_presence') except HTTPError: _LOGGER.error("Unable to connect to Netatmo API") return False @@ -65,27 +66,28 @@ def setup(hass, config): return True -class WelcomeData(object): +class CameraData(object): """Get the latest data from Netatmo.""" def __init__(self, auth, home=None): """Initialize the data object.""" self.auth = auth - self.welcomedata = None + self.camera_data = None self.camera_names = [] self.module_names = [] self.home = home + self.camera_type = None def get_camera_names(self): """Return all camera available on the API as a list.""" self.camera_names = [] self.update() if not self.home: - for home in self.welcomedata.cameras: - for camera in self.welcomedata.cameras[home].values(): + for home in self.camera_data.cameras: + for camera in self.camera_data.cameras[home].values(): self.camera_names.append(camera['name']) else: - for camera in self.welcomedata.cameras[self.home].values(): + for camera in self.camera_data.cameras[self.home].values(): self.camera_names.append(camera['name']) return self.camera_names @@ -93,20 +95,27 @@ class WelcomeData(object): """Return all module available on the API as a list.""" self.module_names = [] self.update() - cam_id = self.welcomedata.cameraByName(camera=camera_name, + cam_id = self.camera_data.cameraByName(camera=camera_name, home=self.home)['id'] - for module in self.welcomedata.modules.values(): + for module in self.camera_data.modules.values(): if cam_id == module['cam_id']: self.module_names.append(module['name']) return self.module_names + def get_camera_type(self, camera=None, home=None, cid=None): + """Return all module available on the API as a list.""" + for camera_name in self.camera_names: + self.camera_type = self.camera_data.cameraType(camera_name) + return self.camera_type + @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Call the Netatmo API to update the data.""" import lnetatmo - self.welcomedata = lnetatmo.WelcomeData(self.auth, size=100) + self.camera_data = lnetatmo.CameraData(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) + """Call the Netatmo API to update the events.""" + self.camera_data.updateEvent( + home=self.home, cameratype=self.camera_type) diff --git a/requirements_all.txt b/requirements_all.txt index 7edbb4bcc70..31613054a52 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -214,7 +214,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.8.1.zip#lnetatmo==0.8.1 +https://github.com/jabesq/netatmo-api-python/archive/v0.9.0.zip#lnetatmo==0.9.0 # homeassistant.components.neato https://github.com/jabesq/pybotvac/archive/v0.0.1.zip#pybotvac==0.0.1 From e4a45fa85755ca7abc7374c54d4b793e6caa9763 Mon Sep 17 00:00:00 2001 From: martst Date: Tue, 17 Jan 2017 07:11:02 +0000 Subject: [PATCH 031/191] Improved x10 state monitoring (#5115) * Improved x10 state monitoring * Improved x10 state monitoring * Use update mthod to fetch state change * Use update mthod to fetch state change * Use update function to fetch status * remove temp file * Add doc string to update method --- homeassistant/components/light/x10.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/light/x10.py b/homeassistant/components/light/x10.py index 30ede3eac18..48df8368294 100644 --- a/homeassistant/components/light/x10.py +++ b/homeassistant/components/light/x10.py @@ -33,16 +33,10 @@ def x10_command(command): return check_output(['heyu'] + command.split(' '), stderr=STDOUT) -def get_status(): - """Get on/off status for all x10 units in default housecode.""" - output = check_output('heyu info | grep monitored', shell=True) - return output.decode('utf-8').split(' ')[-1].strip('\n()') - - def get_unit_status(code): """Get on/off status for given unit.""" - unit = int(code[1:]) - return get_status()[16 - int(unit)] == '1' + output = check_output('heyu onstate ' + code, shell=True) + return int(output.decode('utf-8')[0]) def setup_platform(hass, config, add_devices, discovery_info=None): @@ -63,8 +57,8 @@ class X10Light(Light): """Initialize an X10 Light.""" self._name = light['name'] self._id = light['id'] - self._is_on = False self._brightness = 0 + self._state = False @property def name(self): @@ -79,7 +73,7 @@ class X10Light(Light): @property def is_on(self): """Return true if light is on.""" - return self._is_on + return self._state @property def supported_features(self): @@ -90,13 +84,13 @@ class X10Light(Light): """Instruct the light to turn on.""" x10_command('on ' + self._id) self._brightness = kwargs.get(ATTR_BRIGHTNESS, 255) - self._is_on = True + self._state = True def turn_off(self, **kwargs): """Instruct the light to turn off.""" x10_command('off ' + self._id) - self._is_on = False + self._state = False def update(self): - """Fetch new state data for this light.""" - self._is_on = get_unit_status(self._id) + """Fetch update state.""" + self._state = bool(get_unit_status(self._id)) From 40ba4fd872436e223e060afad5f9bb869b86421e Mon Sep 17 00:00:00 2001 From: Job Vermeulen Date: Tue, 17 Jan 2017 08:15:11 +0100 Subject: [PATCH 032/191] Tado device tracker support (#5046) * Added tado device tracker * Added tado device tracker to .converagerc * Updated docs * Code formatting and removed unused import * Code formatting and removed unused import * Respected the lint line length * Respect pydocstyle rules * Respect the lint line limit length * Fixed reviewer feedback * Changed the tracker to support async * Respect the New line end of file rule * Update .coveragerc --- .coveragerc | 1 + .../components/device_tracker/tado.py | 130 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 homeassistant/components/device_tracker/tado.py diff --git a/.coveragerc b/.coveragerc index f1631b4e99f..3ee6d3cb81a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -168,6 +168,7 @@ omit = homeassistant/components/device_tracker/swisscom.py homeassistant/components/device_tracker/thomson.py homeassistant/components/device_tracker/tomato.py + homeassistant/components/device_tracker/tado.py homeassistant/components/device_tracker/tplink.py homeassistant/components/device_tracker/trackr.py homeassistant/components/device_tracker/ubus.py diff --git a/homeassistant/components/device_tracker/tado.py b/homeassistant/components/device_tracker/tado.py new file mode 100644 index 00000000000..5cb1f8fcbd2 --- /dev/null +++ b/homeassistant/components/device_tracker/tado.py @@ -0,0 +1,130 @@ +""" +Support for Tado Smart Thermostat. + +Device tracker platform that supports presence detection. +The detection is based on geofencing enabled devices used with Tado. +""" +import logging +from datetime import timedelta +from collections import namedtuple + +import asyncio +import aiohttp +import async_timeout + +import voluptuous as vol + +from homeassistant.const import CONF_USERNAME, CONF_PASSWORD +import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle +from homeassistant.components.device_tracker import \ + DOMAIN, PLATFORM_SCHEMA, DeviceScanner +from homeassistant.helpers.aiohttp_client import async_create_clientsession + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) + +_LOGGER = logging.getLogger(__name__) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_USERNAME): cv.string +}) + + +def get_scanner(hass, config): + """Return a Tado scanner.""" + scanner = TadoDeviceScanner(hass, config[DOMAIN]) + + return scanner if scanner.success_init else None + + +Device = namedtuple("Device", ["mac", "name"]) + + +class TadoDeviceScanner(DeviceScanner): + """This class gets geofenced devices from Tado.""" + + def __init__(self, hass, config): + """Initialize the scanner.""" + self.last_results = [] + + self.username = config[CONF_USERNAME] + self.password = config[CONF_PASSWORD] + self.tadoapiurl = 'https://my.tado.com/api/v2/me' \ + '?username={}&password={}' + + self.websession = async_create_clientsession( + hass, cookie_jar=aiohttp.CookieJar(unsafe=True, loop=hass.loop)) + + self.success_init = self._update_info() + _LOGGER.info("Tado scanner initialized") + + @asyncio.coroutine + def async_scan_devices(self): + """Scan for devices and return a list containing found device ids.""" + yield from self._update_info() + + return [device.mac for device in self.last_results] + + @asyncio.coroutine + def async_get_device_name(self, mac): + """Return the name of the given device or None if we don't know.""" + filter_named = [device.name for device in self.last_results + if device.mac == mac] + + if filter_named: + return filter_named[0] + else: + return None + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """ + Query Tado for device marked as at home. + + Returns boolean if scanning successful. + """ + _LOGGER.debug("Requesting Tado") + + last_results = [] + + response = None + tadojson = None + try: + # get first token + with async_timeout.timeout(10, loop=self.hass.loop): + url = self.tadoapiurl.format(self.username, self.password) + response = yield from self.websession.get( + url + ) + + # error on Tado webservice + if response.status != 200: + _LOGGER.warning( + "Error %d on %s.", response.status, self.tadoapiurl) + self.token = None + return + + tadojson = yield from response.json() + + except (asyncio.TimeoutError, aiohttp.errors.ClientError): + _LOGGER.error("Can not load Tado data") + return False + + finally: + if response is not None: + yield from response.release() + + # Find devices that have geofencing enabled, and are currently at home + for mobiledevice in tadojson['mobileDevices']: + if 'location' in mobiledevice: + if mobiledevice['location']['atHome']: + deviceid = mobiledevice['id'] + devicename = mobiledevice['name'] + last_results.append(Device(deviceid, devicename)) + + self.last_results = last_results + + _LOGGER.info("Tado presence query successful") + return True From 915a91dc1b6fdca454c3533765b3e272f504ac4f Mon Sep 17 00:00:00 2001 From: Johan Bloemberg Date: Tue, 17 Jan 2017 08:56:00 +0100 Subject: [PATCH 033/191] DSMR: TCP, reconnecting and V4 CRC support (#5164) * Add support for TCP connection. * Implement reconnect logic. * Actually register connect loop task and fix error handling. * Fix lint, configure upstream requirement. * Revert debug logging. * Explicitly catch connection errors. * Test reconnect on setup and reconnect after disconnect. * Style. --- homeassistant/components/sensor/dsmr.py | 79 +++++++++++++--- requirements_all.txt | 2 +- requirements_test.txt | 1 + tests/components/sensor/test_dsmr.py | 120 ++++++++++++++++++++++-- 4 files changed, 177 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/sensor/dsmr.py b/homeassistant/components/sensor/dsmr.py index 0c42033006c..729b435edbc 100644 --- a/homeassistant/components/sensor/dsmr.py +++ b/homeassistant/components/sensor/dsmr.py @@ -1,5 +1,4 @@ -""" -Support for Dutch Smart Meter Requirements. +"""Support for Dutch Smart Meter Requirements. Also known as: Smartmeter or P1 port. @@ -24,23 +23,27 @@ DSMR version the Entities for this component are create during bootstrap. Another loop (DSMR class) is setup which reads the telegram queue, stores/caches the latest telegram and notifies the Entities that the telegram has been updated. + """ import asyncio from datetime import timedelta +from functools import partial import logging from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_PORT, EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN) + CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN) +from homeassistant.core import CoreState import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity import voluptuous as vol _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['dsmr_parser==0.4'] +REQUIREMENTS = ['dsmr_parser==0.6'] CONF_DSMR_VERSION = 'dsmr_version' +CONF_RECONNECT_INTERVAL = 'reconnect_interval' DEFAULT_DSMR_VERSION = '2.2' DEFAULT_PORT = '/dev/ttyUSB0' @@ -51,11 +54,14 @@ ICON_POWER = 'mdi:flash' # Smart meter sends telegram every 10 seconds MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) +RECONNECT_INTERVAL = 5 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.string, + vol.Optional(CONF_HOST, default=None): cv.string, vol.Optional(CONF_DSMR_VERSION, default=DEFAULT_DSMR_VERSION): vol.All( cv.string, vol.In(['4', '2.2'])), + vol.Optional(CONF_RECONNECT_INTERVAL, default=30): int, }) @@ -66,7 +72,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): logging.getLogger('dsmr_parser').setLevel(logging.ERROR) from dsmr_parser import obis_references as obis_ref - from dsmr_parser.protocol import create_dsmr_reader + from dsmr_parser.protocol import create_dsmr_reader, create_tcp_dsmr_reader + import serial dsmr_version = config[CONF_DSMR_VERSION] @@ -105,15 +112,55 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): device.telegram = telegram hass.async_add_job(device.async_update_ha_state) - # Creates a asyncio.Protocol for reading DSMR telegrams from serial + # Creates a asyncio.Protocol factory for reading DSMR telegrams from serial # and calls update_entities_telegram to update entities on arrival - dsmr = create_dsmr_reader(config[CONF_PORT], config[CONF_DSMR_VERSION], - update_entities_telegram, loop=hass.loop) + if config[CONF_HOST]: + reader_factory = partial(create_tcp_dsmr_reader, + config[CONF_HOST], + config[CONF_PORT], + config[CONF_DSMR_VERSION], + update_entities_telegram, + loop=hass.loop) + else: + reader_factory = partial(create_dsmr_reader, + config[CONF_PORT], + config[CONF_DSMR_VERSION], + update_entities_telegram, + loop=hass.loop) - # Start DSMR asycnio.Protocol reader - transport, _ = yield from hass.loop.create_task(dsmr) + @asyncio.coroutine + def connect_and_reconnect(): + """Connect to DSMR and keep reconnecting until HA stops.""" + while hass.state != CoreState.stopping: + # Start DSMR asycnio.Protocol reader + try: + transport, protocol = yield from hass.loop.create_task( + reader_factory()) + except (serial.serialutil.SerialException, ConnectionRefusedError, + TimeoutError): + # log any error while establishing connection and drop to retry + # connection wait + _LOGGER.exception('error connecting to DSMR') + transport = None - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, transport.close) + if transport: + # register listener to close transport on HA shutdown + stop_listerer = hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, transport.close) + + # wait for reader to close + yield from protocol.wait_closed() + + if hass.state != CoreState.stopping: + if transport: + # remove listerer + stop_listerer() + + # throttle reconnect attempts + yield from asyncio.sleep(config[CONF_RECONNECT_INTERVAL], + loop=hass.loop) + + hass.loop.create_task(connect_and_reconnect()) class DSMREntity(Entity): @@ -187,6 +234,7 @@ class DerivativeDSMREntity(DSMREntity): 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 @@ -202,10 +250,11 @@ class DerivativeDSMREntity(DSMREntity): 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. + 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') diff --git a/requirements_all.txt b/requirements_all.txt index 31613054a52..2a7e16844f3 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -103,7 +103,7 @@ dnspython3==1.15.0 dovado==0.1.15 # homeassistant.components.sensor.dsmr -dsmr_parser==0.4 +dsmr_parser==0.6 # homeassistant.components.dweet # homeassistant.components.sensor.dweet diff --git a/requirements_test.txt b/requirements_test.txt index d001c5d1a78..3ce07cff7ef 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -16,3 +16,4 @@ pytest-sugar>=0.7.1 requests_mock>=1.0 mock-open>=1.3.1 flake8-docstrings==1.0.2 +asynctest>=0.8.0 diff --git a/tests/components/sensor/test_dsmr.py b/tests/components/sensor/test_dsmr.py index 35e224253ee..aae8dfddc5b 100644 --- a/tests/components/sensor/test_dsmr.py +++ b/tests/components/sensor/test_dsmr.py @@ -1,22 +1,51 @@ """Test for DSMR components. -Tests setup of the DSMR component and ensure incoming telegrams cause Entity -to be updated with new values. +Tests setup of the DSMR component and ensure incoming telegrams cause +Entity to be updated with new values. + """ import asyncio from decimal import Decimal from unittest.mock import Mock +import asynctest 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, mock_coro +import pytest +from tests.common import assert_setup_component + + +@pytest.fixture +def mock_connection_factory(monkeypatch): + """Mock the create functions for serial and TCP Asyncio connections.""" + from dsmr_parser.protocol import DSMRProtocol + transport = asynctest.Mock(spec=asyncio.Transport) + protocol = asynctest.Mock(spec=DSMRProtocol) + + @asyncio.coroutine + def connection_factory(*args, **kwargs): + """Return mocked out Asyncio classes.""" + return (transport, protocol) + connection_factory = Mock(wraps=connection_factory) + + # apply the mock to both connection factories + monkeypatch.setattr( + 'dsmr_parser.protocol.create_dsmr_reader', + connection_factory) + monkeypatch.setattr( + 'dsmr_parser.protocol.create_tcp_dsmr_reader', + connection_factory) + + return connection_factory, transport, protocol @asyncio.coroutine -def test_default_setup(hass, monkeypatch): +def test_default_setup(hass, mock_connection_factory): """Test the default setup.""" + (connection_factory, transport, protocol) = mock_connection_factory + from dsmr_parser.obis_references import ( CURRENT_ELECTRICITY_USAGE, ELECTRICITY_ACTIVE_TARIFF, @@ -34,15 +63,11 @@ def test_default_setup(hass, monkeypatch): ]), } - # mock for injecting DSMR telegram - dsmr = Mock(return_value=mock_coro([Mock(), None])) - monkeypatch.setattr('dsmr_parser.protocol.create_dsmr_reader', dsmr) - with assert_setup_component(1): yield from async_setup_component(hass, 'sensor', {'sensor': config}) - telegram_callback = dsmr.call_args_list[0][0][2] + telegram_callback = connection_factory.call_args_list[0][0][2] # make sure entities have been created and return 'unknown' state power_consumption = hass.states.get('sensor.power_consumption') @@ -99,3 +124,80 @@ def test_derivative(): 'state should be difference between first and second update' assert entity.unit_of_measurement == 'm3/h' + + +@asyncio.coroutine +def test_tcp(hass, mock_connection_factory): + """If proper config provided TCP connection should be made.""" + (connection_factory, transport, protocol) = mock_connection_factory + + config = { + 'platform': 'dsmr', + 'host': 'localhost', + 'port': 1234, + } + + with assert_setup_component(1): + yield from async_setup_component(hass, 'sensor', + {'sensor': config}) + + assert connection_factory.call_args_list[0][0][0] == 'localhost' + assert connection_factory.call_args_list[0][0][1] == '1234' + + +@asyncio.coroutine +def test_connection_errors_retry(hass, monkeypatch, mock_connection_factory): + """Connection should be retried on error during setup.""" + (connection_factory, transport, protocol) = mock_connection_factory + + config = { + 'platform': 'dsmr', + 'reconnect_interval': 0, + } + + # override the mock to have it fail the first time + first_fail_connection_factory = Mock( + wraps=connection_factory, side_effect=[ + TimeoutError]) + + monkeypatch.setattr( + 'dsmr_parser.protocol.create_dsmr_reader', + first_fail_connection_factory) + yield from async_setup_component(hass, 'sensor', {'sensor': config}) + + # wait for sleep to resolve + yield from hass.async_block_till_done() + assert first_fail_connection_factory.call_count == 2, \ + 'connecting not retried' + + +@asyncio.coroutine +def test_reconnect(hass, monkeypatch, mock_connection_factory): + """If transport disconnects, the connection should be retried.""" + (connection_factory, transport, protocol) = mock_connection_factory + config = { + 'platform': 'dsmr', + 'reconnect_interval': 0, + } + + # mock waiting coroutine while connection lasts + closed = asyncio.Event(loop=hass.loop) + + @asyncio.coroutine + def wait_closed(): + yield from closed.wait() + protocol.wait_closed = wait_closed + + yield from async_setup_component(hass, 'sensor', {'sensor': config}) + + assert connection_factory.call_count == 1 + + # indicate disconnect, release wait lock and allow reconnect to happen + closed.set() + # wait for lock set to resolve + yield from hass.async_block_till_done() + # wait for sleep to resolve + yield from hass.async_block_till_done() + + assert connection_factory.call_count == 2, \ + 'connecting not retried' From d240ea56d8e2f199cf82665ff2bcc2c9ab41adae Mon Sep 17 00:00:00 2001 From: anpetrov Date: Tue, 17 Jan 2017 00:01:11 -0800 Subject: [PATCH 034/191] Add Skybeacon BLE temperature/humidity sensor (#5183) Signed-off-by: Andrey Petrov --- .coveragerc | 1 + homeassistant/components/sensor/skybeacon.py | 195 +++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 199 insertions(+) create mode 100644 homeassistant/components/sensor/skybeacon.py diff --git a/.coveragerc b/.coveragerc index 3ee6d3cb81a..3196003a95a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -321,6 +321,7 @@ omit = homeassistant/components/sensor/scrape.py homeassistant/components/sensor/sensehat.py homeassistant/components/sensor/serial_pm.py + homeassistant/components/sensor/skybeacon.py homeassistant/components/sensor/sma.py homeassistant/components/sensor/snmp.py homeassistant/components/sensor/sonarr.py diff --git a/homeassistant/components/sensor/skybeacon.py b/homeassistant/components/sensor/skybeacon.py new file mode 100644 index 00000000000..45dcb5ca2ee --- /dev/null +++ b/homeassistant/components/sensor/skybeacon.py @@ -0,0 +1,195 @@ +""" +Support for SKYBEACON temperature/humidity Bluetooth LE sensor. + +These are inexpensive CR2477-powered ibeacon/eddystone sensors +that come with temperature/sensor module. +More information: http://cnsky9.en.alibaba.com + +example: +sensor: + - platform: skybeacon + mac: 'F7:BE:12:02:47:31' + name: 'living room' +""" + +import logging +import threading +from uuid import UUID + +import voluptuous as vol +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import (CONF_NAME, + CONF_MAC, + TEMP_CELSIUS, + STATE_UNKNOWN, + EVENT_HOMEASSISTANT_STOP) + +REQUIREMENTS = ['pygatt==3.0.0'] + +CONNECT_LOCK = threading.Lock() + +_LOGGER = logging.getLogger(__name__) + +ATTR_DEVICE = 'device' +ATTR_MODEL = 'model' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_MAC): cv.string, + vol.Optional(CONF_NAME, default=""): cv.string, +}) + +BLE_TEMP_UUID = '0000ff92-0000-1000-8000-00805f9b34fb' +BLE_TEMP_HANDLE = 0x24 +SKIP_HANDLE_LOOKUP = True +CONNECT_TIMEOUT = 30 + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the sensor.""" + name = config.get(CONF_NAME) + mac = config.get(CONF_MAC) + _LOGGER.error("setting up..") + mon = Monitor(hass, mac, name) + add_devices([SkybeaconTemp(name, mon)]) + add_devices([SkybeaconHumid(name, mon)]) + + def monitor_stop(_service_or_event): + """Stop the monitor thread.""" + _LOGGER.info("skybeacon: stopping monitor for %s ", name) + mon.terminate() + + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, monitor_stop) + mon.start() + + +class SkybeaconHumid(Entity): + """Representation of a humidity sensor.""" + + def __init__(self, name, mon): + """Initialize a sensor.""" + self.mon = mon + self._name = name + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self.mon.data['humid'] + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return "%" + + @property + def device_state_attributes(self): + """Return the state attributes of the sensor.""" + return { + ATTR_DEVICE: "SKYBEACON", + ATTR_MODEL: 1, + } + + +class SkybeaconTemp(Entity): + """Representation of a temperature sensor.""" + + def __init__(self, name, mon): + """Initialize a sensor.""" + self.mon = mon + self._name = name + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + return self.mon.data['temp'] + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return TEMP_CELSIUS + + @property + def device_state_attributes(self): + """Return the state attributes of the sensor.""" + return { + ATTR_DEVICE: "SKYBEACON", + ATTR_MODEL: 1, + } + + +class Monitor(threading.Thread): + """Connection handling.""" + + def __init__(self, hass, mac, name): + """Construct interface object.""" + threading.Thread.__init__(self) + self.daemon = False + self.hass = hass + self.mac = mac + self.name = name + self.data = {'temp': STATE_UNKNOWN, 'humid': STATE_UNKNOWN} + self.keep_going = True + self.event = threading.Event() + + def run(self): + """Thread that keeps connection alive.""" + import pygatt + from pygatt.backends import Characteristic + from pygatt.exceptions import (BLEError, + NotConnectedError, + NotificationTimeout) + + cached_char = Characteristic(BLE_TEMP_UUID, BLE_TEMP_HANDLE) + adapter = pygatt.backends.GATTToolBackend() + while True: + try: + _LOGGER.info("connecting to %s", self.name) + # we need concurrent connect, so lets not reset the device + adapter.start(reset_on_start=False) + # seems only one connection can be initiated at a time + with CONNECT_LOCK: + device = adapter.connect(self.mac, + CONNECT_TIMEOUT, + pygatt.BLEAddressType.random) + if SKIP_HANDLE_LOOKUP: + # HACK: inject handle mapping collected offline + # pylint: disable=protected-access + device._characteristics[UUID(BLE_TEMP_UUID)] = cached_char + # magic: writing this makes device happy + device.char_write_handle(0x1b, bytearray([255]), False) + device.subscribe(BLE_TEMP_UUID, self._update) + _LOGGER.info("subscribed to %s", self.name) + while self.keep_going: + # protect against stale connections, just read temperature + device.char_read(BLE_TEMP_UUID, timeout=CONNECT_TIMEOUT) + self.event.wait(60) + break + except (BLEError, NotConnectedError, NotificationTimeout) as ex: + _LOGGER.error("Exception: %s ", str(ex)) + finally: + adapter.stop() + + def _update(self, handle, value): + """Notification callback from pygatt.""" + _LOGGER.info("%s: %15s temperature = %-2d.%-2d, humidity = %3d", + handle, self.name, value[0], value[2], value[1]) + self.data['temp'] = float(("%d.%d" % (value[0], value[2]))) + self.data['humid'] = value[1] + + def terminate(self): + """Signal runner to stop and join thread.""" + self.keep_going = False + self.event.set() + self.join() diff --git a/requirements_all.txt b/requirements_all.txt index 2a7e16844f3..1ebabd8d9e1 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -419,6 +419,9 @@ pyenvisalink==2.0 # homeassistant.components.ifttt pyfttt==0.3 +# homeassistant.components.sensor.skybeacon +pygatt==3.0.0 + # homeassistant.components.remote.harmony pyharmony==1.0.12 From 321a8be33937ccda7bfa9406b7755018a8da315b Mon Sep 17 00:00:00 2001 From: R1chardTM Date: Tue, 17 Jan 2017 09:12:15 +0100 Subject: [PATCH 035/191] Move Nest sensors configuration to Nest component (#4983) * Move Nest sensor config to Nest Component * Ensure Nest Protect sensors are added without specified sensor config * Fix pylint warnings * Remove support for empty monitored condion list * Remove scan interval * Remove scan interval import * Add Nest sensors by default with opt-out --- .../components/binary_sensor/nest.py | 37 +++++------- homeassistant/components/nest.py | 22 ++++++-- homeassistant/components/sensor/nest.py | 56 ++++++------------- homeassistant/const.py | 1 + 4 files changed, 49 insertions(+), 67 deletions(-) diff --git a/homeassistant/components/binary_sensor/nest.py b/homeassistant/components/binary_sensor/nest.py index c66373bc58a..4689bc59082 100644 --- a/homeassistant/components/binary_sensor/nest.py +++ b/homeassistant/components/binary_sensor/nest.py @@ -7,14 +7,10 @@ https://home-assistant.io/components/binary_sensor.nest/ from itertools import chain import logging -import voluptuous as vol - -from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) +from homeassistant.components.binary_sensor import (BinarySensorDevice) from homeassistant.components.sensor.nest import NestSensor -from homeassistant.const import (CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS) +from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.components.nest import DATA_NEST -import homeassistant.helpers.config_validation as cv DEPENDENCIES = ['nest'] @@ -42,17 +38,6 @@ _BINARY_TYPES_DEPRECATED = [ _VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \ + CAMERA_BINARY_TYPES -_VALID_BINARY_SENSOR_TYPES_WITH_DEPRECATED = _VALID_BINARY_SENSOR_TYPES \ - + _BINARY_TYPES_DEPRECATED - - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_SCAN_INTERVAL): - vol.All(vol.Coerce(int), vol.Range(min=1)), - vol.Required(CONF_MONITORED_CONDITIONS): - vol.All(cv.ensure_list, - [vol.In(_VALID_BINARY_SENSOR_TYPES_WITH_DEPRECATED)]) -}) _LOGGER = logging.getLogger(__name__) @@ -63,15 +48,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return nest = hass.data[DATA_NEST] - conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_BINARY_SENSOR_TYPES) - for variable in conf: + # Add all available binary sensors if no Nest binary sensor config is set + if discovery_info == {}: + conditions = _VALID_BINARY_SENSOR_TYPES + else: + conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {}) + + for variable in conditions: if variable in _BINARY_TYPES_DEPRECATED: wstr = (variable + " is no a longer supported " "monitored_conditions. See " "https://home-assistant.io/components/binary_sensor.nest/ " - "for valid options, or remove monitored_conditions " - "entirely to get a reasonable default") + "for valid options.") _LOGGER.error(wstr) sensors = [] @@ -80,16 +69,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None): nest.cameras()) for structure, device in device_chain: sensors += [NestBinarySensor(structure, device, variable) - for variable in conf + for variable in conditions if variable in BINARY_TYPES] sensors += [NestBinarySensor(structure, device, variable) - for variable in conf + for variable in conditions if variable in CLIMATE_BINARY_TYPES and device.is_thermostat] if device.is_camera: sensors += [NestBinarySensor(structure, device, variable) - for variable in conf + for variable in conditions if variable in CAMERA_BINARY_TYPES] for activity_zone in device.activity_zones: sensors += [NestActivityZoneSensor(structure, diff --git a/homeassistant/components/nest.py b/homeassistant/components/nest.py index 337cc8f9160..13c2ddc7bed 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -11,7 +11,9 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.helpers import discovery -from homeassistant.const import (CONF_STRUCTURE, CONF_FILENAME) +from homeassistant.const import (CONF_STRUCTURE, CONF_FILENAME, + CONF_BINARY_SENSORS, CONF_SENSORS, + CONF_MONITORED_CONDITIONS) from homeassistant.loader import get_component _CONFIGURING = {} @@ -30,11 +32,17 @@ NEST_CONFIG_FILE = 'nest.conf' CONF_CLIENT_ID = 'client_id' CONF_CLIENT_SECRET = 'client_secret' +SENSOR_SCHEMA = vol.Schema({ + vol.Optional(CONF_MONITORED_CONDITIONS): vol.All(cv.ensure_list) +}) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_CLIENT_ID): cv.string, vol.Required(CONF_CLIENT_SECRET): cv.string, - vol.Optional(CONF_STRUCTURE): vol.All(cv.ensure_list, cv.string) + vol.Optional(CONF_STRUCTURE): vol.All(cv.ensure_list, cv.string), + vol.Optional(CONF_SENSORS): SENSOR_SCHEMA, + vol.Optional(CONF_BINARY_SENSORS): SENSOR_SCHEMA }) }, extra=vol.ALLOW_EXTRA) @@ -88,9 +96,15 @@ def setup_nest(hass, nest, config, pin=None): _LOGGER.debug("proceeding with discovery") discovery.load_platform(hass, 'climate', DOMAIN, {}, config) - discovery.load_platform(hass, 'sensor', DOMAIN, {}, config) - discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config) discovery.load_platform(hass, 'camera', DOMAIN, {}, config) + + sensor_config = conf.get(CONF_SENSORS, {}) + discovery.load_platform(hass, 'sensor', DOMAIN, sensor_config, config) + + binary_sensor_config = conf.get(CONF_BINARY_SENSORS, {}) + discovery.load_platform(hass, 'binary_sensor', DOMAIN, + binary_sensor_config, config) + _LOGGER.debug("setup done") return True diff --git a/homeassistant/components/sensor/nest.py b/homeassistant/components/sensor/nest.py index a074dcc310d..6305f5265b0 100644 --- a/homeassistant/components/sensor/nest.py +++ b/homeassistant/components/sensor/nest.py @@ -7,15 +7,10 @@ https://home-assistant.io/components/sensor.nest/ from itertools import chain import logging -import voluptuous as vol - -from homeassistant.components.nest import ( - DATA_NEST, DOMAIN) +from homeassistant.components.nest import DATA_NEST from homeassistant.helpers.entity import Entity -from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_PLATFORM, - CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS -) +from homeassistant.const import (TEMP_CELSIUS, TEMP_FAHRENHEIT, + CONF_MONITORED_CONDITIONS) DEPENDENCIES = ['nest'] SENSOR_TYPES = ['humidity', @@ -26,23 +21,15 @@ SENSOR_TYPES_DEPRECATED = ['last_ip', 'local_ip', 'last_connection'] -SENSOR_TYPES_DEPRECATED = ['last_ip', - 'local_ip'] - -WEATHER_VARS = {} - DEPRECATED_WEATHER_VARS = {'weather_humidity': 'humidity', 'weather_temperature': 'temperature', 'weather_condition': 'condition', 'wind_speed': 'kph', 'wind_direction': 'direction'} -SENSOR_UNITS = {'humidity': '%', - 'temperature': '°C'} +SENSOR_UNITS = {'humidity': '%', 'temperature': '°C'} -PROTECT_VARS = ['co_status', - 'smoke_status', - 'battery_health'] +PROTECT_VARS = ['co_status', 'smoke_status', 'battery_health'] PROTECT_VARS_DEPRECATED = ['battery_level'] @@ -51,19 +38,7 @@ SENSOR_TEMP_TYPES = ['temperature', 'target'] _SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED \ + list(DEPRECATED_WEATHER_VARS.keys()) + PROTECT_VARS_DEPRECATED -_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS \ - + list(WEATHER_VARS.keys()) - -_VALID_SENSOR_TYPES_WITH_DEPRECATED = _VALID_SENSOR_TYPES \ - + _SENSOR_TYPES_DEPRECATED - -PLATFORM_SCHEMA = vol.Schema({ - vol.Required(CONF_PLATFORM): DOMAIN, - vol.Optional(CONF_SCAN_INTERVAL): - vol.All(vol.Coerce(int), vol.Range(min=1)), - vol.Required(CONF_MONITORED_CONDITIONS): - [vol.In(_VALID_SENSOR_TYPES_WITH_DEPRECATED)] -}) +_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS _LOGGER = logging.getLogger(__name__) @@ -74,9 +49,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return nest = hass.data[DATA_NEST] - conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_SENSOR_TYPES) - for variable in conf: + # Add all available sensors if no Nest sensor config is set + if discovery_info == {}: + conditions = _VALID_SENSOR_TYPES + else: + conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {}) + + for variable in conditions: if variable in _SENSOR_TYPES_DEPRECATED: if variable in DEPRECATED_WEATHER_VARS: wstr = ("Nest no longer provides weather data like %s. See " @@ -87,22 +67,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None): wstr = (variable + " is no a longer supported " "monitored_conditions. See " "https://home-assistant.io/components/" - "binary_sensor.nest/ " - "for valid options, or remove monitored_conditions " - "entirely to get a reasonable default") + "binary_sensor.nest/ for valid options.") _LOGGER.error(wstr) all_sensors = [] for structure, device in chain(nest.thermostats(), nest.smoke_co_alarms()): sensors = [NestBasicSensor(structure, device, variable) - for variable in conf + for variable in conditions if variable in SENSOR_TYPES and device.is_thermostat] sensors += [NestTempSensor(structure, device, variable) - for variable in conf + for variable in conditions if variable in SENSOR_TEMP_TYPES and device.is_thermostat] sensors += [NestProtectSensor(structure, device, variable) - for variable in conf + for variable in conditions if variable in PROTECT_VARS and device.is_smoke_co_alarm] all_sensors.extend(sensors) diff --git a/homeassistant/const.py b/homeassistant/const.py index 3ef79ad1724..2bdefe6a9fd 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -62,6 +62,7 @@ CONF_AUTHENTICATION = 'authentication' CONF_BASE = 'base' CONF_BEFORE = 'before' CONF_BELOW = 'below' +CONF_BINARY_SENSORS = 'binary_sensors' CONF_BLACKLIST = 'blacklist' CONF_BRIGHTNESS = 'brightness' CONF_CODE = 'code' From 298c1654f83a002436b16f465e77333797c5a41a Mon Sep 17 00:00:00 2001 From: Whytey Date: Tue, 17 Jan 2017 18:41:37 +1000 Subject: [PATCH 036/191] New zabbix (#5297) * Hopefully a clean branch for merging * Remove scan_interval, use defaults for now * Fix code style error --- .coveragerc | 3 + homeassistant/components/sensor/zabbix.py | 174 ++++++++++++++++++++++ homeassistant/components/zabbix.py | 60 ++++++++ requirements_all.txt | 3 + 4 files changed, 240 insertions(+) create mode 100644 homeassistant/components/sensor/zabbix.py create mode 100644 homeassistant/components/zabbix.py diff --git a/.coveragerc b/.coveragerc index 3196003a95a..8f67671a5a2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -122,6 +122,9 @@ omit = homeassistant/components/mochad.py homeassistant/components/*/mochad.py + homeassistant/components/zabbix.py + homeassistant/components/*/zabbix.py + homeassistant/components/alarm_control_panel/alarmdotcom.py homeassistant/components/alarm_control_panel/concord232.py homeassistant/components/alarm_control_panel/nx584.py diff --git a/homeassistant/components/sensor/zabbix.py b/homeassistant/components/sensor/zabbix.py new file mode 100644 index 00000000000..6c3d0a3d653 --- /dev/null +++ b/homeassistant/components/sensor/zabbix.py @@ -0,0 +1,174 @@ +""" +Support for Zabbix Sensors. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.zabbix/ +""" +import logging +import voluptuous as vol + +from homeassistant.helpers.entity import Entity +import homeassistant.components.zabbix as zabbix +from homeassistant.components.sensor import PLATFORM_SCHEMA +import homeassistant.helpers.config_validation as cv + + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['zabbix'] + +_CONF_TRIGGERS = "triggers" +_CONF_HOSTIDS = "hostids" +_CONF_INDIVIDUAL = "individual" +_CONF_NAME = "name" + +_ZABBIX_ID_LIST_SCHEMA = vol.Schema([int]) +_ZABBIX_TRIGGER_SCHEMA = vol.Schema({ + vol.Optional(_CONF_HOSTIDS, default=[]): _ZABBIX_ID_LIST_SCHEMA, + vol.Optional(_CONF_INDIVIDUAL, default=False): cv.boolean(True), + vol.Optional(_CONF_NAME, default=None): cv.string, +}) + +# SCAN_INTERVAL = 30 +# +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(_CONF_TRIGGERS): vol.Any(_ZABBIX_TRIGGER_SCHEMA, None) +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Zabbix sensor platform.""" + sensors = [] + + zapi = hass.data[zabbix.DOMAIN] + if not zapi: + _LOGGER.error("zapi is None. Zabbix component hasn't been loaded?") + return False + + _LOGGER.info("Connected to Zabbix API Version %s", + zapi.api_version()) + + trigger_conf = config.get(_CONF_TRIGGERS) + # The following code seems overly complex. Need to think about this... + if trigger_conf: + hostids = trigger_conf.get(_CONF_HOSTIDS) + individual = trigger_conf.get(_CONF_INDIVIDUAL) + name = trigger_conf.get(_CONF_NAME) + + if individual: + # Individual sensor per host + if not hostids: + # We need hostids + _LOGGER.error("If using 'individual', must specify hostids") + return False + + for hostid in hostids: + _LOGGER.debug("Creating Zabbix Sensor: " + str(hostid)) + sensor = ZabbixSingleHostTriggerCountSensor(zapi, + [hostid], + name) + sensors.append(sensor) + else: + if not hostids: + # Single sensor that provides the total count of triggers. + _LOGGER.debug("Creating Zabbix Sensor") + sensor = ZabbixTriggerCountSensor(zapi, name) + else: + # Single sensor that sums total issues for all hosts + _LOGGER.debug("Creating Zabbix Sensor group: " + str(hostids)) + sensor = ZabbixMultipleHostTriggerCountSensor(zapi, + hostids, + name) + sensors.append(sensor) + else: + # Single sensor that provides the total count of triggers. + _LOGGER.debug("Creating Zabbix Sensor") + sensor = ZabbixTriggerCountSensor(zapi) + sensors.append(sensor) + + add_devices(sensors) + + +class ZabbixTriggerCountSensor(Entity): + """Get the active trigger count for all Zabbix monitored hosts.""" + + def __init__(self, zApi, name="Zabbix"): + """Initiate Zabbix sensor.""" + self._name = name + self._zapi = zApi + self._state = None + self._attributes = {} + + @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 units of measurement.""" + return 'issues' + + def _call_zabbix_api(self): + return self._zapi.trigger.get(output="extend", + only_true=1, + monitored=1, + filter={"value": 1}) + + def update(self): + """Update the sensor.""" + _LOGGER.debug("Updating ZabbixTriggerCountSensor: " + str(self._name)) + triggers = self._call_zabbix_api() + self._state = len(triggers) + + @property + def device_state_attributes(self): + """Return the state attributes of the device.""" + return self._attributes + + +class ZabbixSingleHostTriggerCountSensor(ZabbixTriggerCountSensor): + """Get the active trigger count for a single Zabbix monitored host.""" + + def __init__(self, zApi, hostid, name=None): + """Initiate Zabbix sensor.""" + super().__init__(zApi, name) + self._hostid = hostid + if not name: + self._name = self._zapi.host.get(hostids=self._hostid, + output="extend")[0]["name"] + + self._attributes["Host ID"] = self._hostid + + def _call_zabbix_api(self): + return self._zapi.trigger.get(hostids=self._hostid, + output="extend", + only_true=1, + monitored=1, + filter={"value": 1}) + + +class ZabbixMultipleHostTriggerCountSensor(ZabbixTriggerCountSensor): + """Get the active trigger count for specified Zabbix monitored hosts.""" + + def __init__(self, zApi, hostids, name=None): + """Initiate Zabbix sensor.""" + super().__init__(zApi, name) + self._hostids = hostids + if not name: + host_names = self._zapi.host.get(hostids=self._hostids, + output="extend") + self._name = " ".join(name["name"] for name in host_names) + self._attributes["Host IDs"] = self._hostids + + def _call_zabbix_api(self): + return self._zapi.trigger.get(hostids=self._hostids, + output="extend", + only_true=1, + monitored=1, + filter={"value": 1}) diff --git a/homeassistant/components/zabbix.py b/homeassistant/components/zabbix.py new file mode 100644 index 00000000000..3418bad6c9c --- /dev/null +++ b/homeassistant/components/zabbix.py @@ -0,0 +1,60 @@ +""" +Support for Zabbix. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/zabbix/ +""" +import logging +from urllib.parse import urljoin + +import voluptuous as vol + +from homeassistant.const import ( + CONF_PATH, CONF_HOST, CONF_SSL, CONF_PASSWORD, CONF_USERNAME) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['pyzabbix==0.7.4'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_SSL = False +DEFAULT_PATH = "zabbix" + +DOMAIN = 'zabbix' + + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, + vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, + vol.Optional(CONF_USERNAME): cv.string, + vol.Optional(CONF_PASSWORD): cv.string + }) +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up the Zabbix component.""" + from pyzabbix import ZabbixAPI, ZabbixAPIException + + conf = config[DOMAIN] + if conf[CONF_SSL]: + schema = 'https' + else: + schema = 'http' + + url = urljoin('{}://{}'.format(schema, conf[CONF_HOST]), conf[CONF_PATH]) + username = conf.get(CONF_USERNAME, None) + password = conf.get(CONF_PASSWORD, None) + + zapi = ZabbixAPI(url) + try: + zapi.login(username, password) + _LOGGER.info("Connected to Zabbix API Version %s", zapi.api_version()) + except ZabbixAPIException: + _LOGGER.error("Unable to login to the Zabbix API") + return False + + hass.data[DOMAIN] = zapi + return True diff --git a/requirements_all.txt b/requirements_all.txt index 1ebabd8d9e1..d13833efe18 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -542,6 +542,9 @@ pywemo==0.4.9 # homeassistant.components.light.yeelight pyyeelight==1.0-beta +# homeassistant.components.zabbix +pyzabbix==0.7.4 + # homeassistant.components.climate.radiotherm radiotherm==1.2 From d31f00f672eb8361dcb6f9e0dbcd21177017d626 Mon Sep 17 00:00:00 2001 From: Bill Nelson Date: Tue, 17 Jan 2017 13:53:35 -0800 Subject: [PATCH 037/191] Updated Roku IDLE state --- homeassistant/components/media_player/roku.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/roku.py b/homeassistant/components/media_player/roku.py index 5a4e993aee5..728777e5e9e 100644 --- a/homeassistant/components/media_player/roku.py +++ b/homeassistant/components/media_player/roku.py @@ -114,7 +114,8 @@ class RokuDevice(MediaPlayerDevice): if self.current_app is None: return STATE_UNKNOWN - if self.current_app.name in ["Power Saver", "Default screensaver"]: + idle_list = ["Power Saver", "Screensaver", "screensaver"] + if any(idle_type in self.current_app.name for idle_type in idle_list): return STATE_IDLE elif self.current_app.name == "Roku": return STATE_HOME From cfc936761b967631f82acbeada3f40f08ea14c95 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 17 Jan 2017 23:35:02 +0100 Subject: [PATCH 038/191] Make upc more robust (#5404) * Make upc more robust * update unittest * add test for parse error --- .../components/device_tracker/upc_connect.py | 15 ++-- .../device_tracker/test_upc_connect.py | 83 +++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/device_tracker/upc_connect.py b/homeassistant/components/device_tracker/upc_connect.py index 13336e939a5..2e1a4d7b947 100644 --- a/homeassistant/components/device_tracker/upc_connect.py +++ b/homeassistant/components/device_tracker/upc_connect.py @@ -74,12 +74,15 @@ class UPCDeviceScanner(DeviceScanner): return [] raw = yield from self._async_ws_function(CMD_DEVICES) - if raw is None: - _LOGGER.warning("Can't read device from %s", self.host) - return - xml_root = ET.fromstring(raw) - return [mac.text for mac in xml_root.iter('MACAddr')] + try: + xml_root = ET.fromstring(raw) + return [mac.text for mac in xml_root.iter('MACAddr')] + except (ET.ParseError, TypeError): + _LOGGER.warning("Can't read device from %s", self.host) + self.token = None + + return [] @asyncio.coroutine def async_get_device_name(self, device): @@ -107,7 +110,7 @@ class UPCDeviceScanner(DeviceScanner): }) # successfull? - if data.find("successful") != -1: + if data is not None: return True return False diff --git a/tests/components/device_tracker/test_upc_connect.py b/tests/components/device_tracker/test_upc_connect.py index 728eb104b8b..1bcbc841d3a 100644 --- a/tests/components/device_tracker/test_upc_connect.py +++ b/tests/components/device_tracker/test_upc_connect.py @@ -230,6 +230,50 @@ class TestUPCConnect(object): cookies={'sessionToken': '1235678'} ) + scanner.token = None + mac_list = run_coroutine_threadsafe( + scanner.async_scan_devices(), self.hass.loop).result() + + assert len(aioclient_mock.mock_calls) == 3 + assert aioclient_mock.mock_calls[1][2]['fun'] == 15 + assert mac_list == ['30:D3:2D:0:69:21', '5C:AA:FD:25:32:02', + '70:EE:50:27:A1:38'] + + def test_scan_devices_without_session_wrong_re(self, aioclient_mock): + """Setup a upc platform and scan device with no token and wrong.""" + aioclient_mock.get( + "http://{}/common_page/login.html".format(self.host), + cookies={'sessionToken': '654321'} + ) + aioclient_mock.post( + "http://{}/xml/getter.xml".format(self.host), + content=b'successful', + cookies={'sessionToken': '654321'} + ) + + scanner = run_coroutine_threadsafe(platform.async_get_scanner( + self.hass, {DOMAIN: { + CONF_PLATFORM: 'upc_connect', + CONF_HOST: self.host, + CONF_PASSWORD: '123456' + }} + ), self.hass.loop).result() + + assert aioclient_mock.mock_calls[1][2]['Password'] == '123456' + assert aioclient_mock.mock_calls[1][2]['fun'] == 15 + assert aioclient_mock.mock_calls[1][2]['token'] == '654321' + + aioclient_mock.clear_requests() + aioclient_mock.get( + "http://{}/common_page/login.html".format(self.host), + cookies={'sessionToken': '654321'} + ) + aioclient_mock.post( + "http://{}/xml/getter.xml".format(self.host), + status=400, + cookies={'sessionToken': '1235678'} + ) + scanner.token = None mac_list = run_coroutine_threadsafe( scanner.async_scan_devices(), self.hass.loop).result() @@ -237,3 +281,42 @@ class TestUPCConnect(object): assert len(aioclient_mock.mock_calls) == 2 assert aioclient_mock.mock_calls[1][2]['fun'] == 15 assert mac_list == [] + + def test_scan_devices_parse_error(self, aioclient_mock): + """Setup a upc platform and scan device with parse error.""" + aioclient_mock.get( + "http://{}/common_page/login.html".format(self.host), + cookies={'sessionToken': '654321'} + ) + aioclient_mock.post( + "http://{}/xml/getter.xml".format(self.host), + content=b'successful', + cookies={'sessionToken': '654321'} + ) + + scanner = run_coroutine_threadsafe(platform.async_get_scanner( + self.hass, {DOMAIN: { + CONF_PLATFORM: 'upc_connect', + CONF_HOST: self.host, + CONF_PASSWORD: '123456' + }} + ), self.hass.loop).result() + + assert aioclient_mock.mock_calls[1][2]['Password'] == '123456' + assert aioclient_mock.mock_calls[1][2]['fun'] == 15 + assert aioclient_mock.mock_calls[1][2]['token'] == '654321' + + aioclient_mock.clear_requests() + aioclient_mock.post( + "http://{}/xml/getter.xml".format(self.host), + text="Blablebla blabalble", + cookies={'sessionToken': '1235678'} + ) + + mac_list = run_coroutine_threadsafe( + scanner.async_scan_devices(), self.hass.loop).result() + + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[0][2]['fun'] == 123 + assert scanner.token is None + assert mac_list == [] From bfc0a6a17c471d0124310cc92fac9656b6d581df Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 17 Jan 2017 23:40:34 +0100 Subject: [PATCH 039/191] Use constants (#5390) --- homeassistant/components/light/qwikswitch.py | 5 ++- homeassistant/components/qwikswitch.py | 33 +++++++++++-------- homeassistant/components/switch/qwikswitch.py | 6 ++-- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/light/qwikswitch.py b/homeassistant/components/light/qwikswitch.py index 5612f41c942..b963f14cfb4 100644 --- a/homeassistant/components/light/qwikswitch.py +++ b/homeassistant/components/light/qwikswitch.py @@ -5,8 +5,11 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.qwikswitch/ """ import logging + import homeassistant.components.qwikswitch as qwikswitch +_LOGGER = logging.getLogger(__name__) + DEPENDENCIES = ['qwikswitch'] @@ -14,7 +17,7 @@ DEPENDENCIES = ['qwikswitch'] def setup_platform(hass, config, add_devices, discovery_info=None): """Add lights from the main Qwikswitch component.""" if discovery_info is None: - logging.getLogger(__name__).error('Configure Qwikswitch Component.') + _LOGGER.error("Configure Qwikswitch component") return False add_devices(qwikswitch.QSUSB['light']) diff --git a/homeassistant/components/qwikswitch.py b/homeassistant/components/qwikswitch.py index 2e01d91f50f..3c0e66679bc 100644 --- a/homeassistant/components/qwikswitch.py +++ b/homeassistant/components/qwikswitch.py @@ -5,26 +5,32 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/qwikswitch/ """ import logging + import voluptuous as vol -from homeassistant.const import (EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP) +from homeassistant.const import ( + EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_URL) from homeassistant.helpers.discovery import load_platform -from homeassistant.components.light import (ATTR_BRIGHTNESS, - SUPPORT_BRIGHTNESS, Light) +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) from homeassistant.components.switch import SwitchDevice -DOMAIN = 'qwikswitch' REQUIREMENTS = ['pyqwikswitch==0.4'] _LOGGER = logging.getLogger(__name__) +DOMAIN = 'qwikswitch' + +CONF_DIMMER_ADJUST = 'dimmer_adjust' +CONF_BUTTON_EVENTS = 'button_events' CV_DIM_VALUE = vol.All(vol.Coerce(float), vol.Range(min=1, max=3)) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required('url', default='http://127.0.0.1:2020'): vol.Coerce(str), - vol.Optional('dimmer_adjust', default=1): CV_DIM_VALUE, - vol.Optional('button_events'): vol.Coerce(str) + vol.Required(CONF_URL, default='http://127.0.0.1:2020'): + vol.Coerce(str), + vol.Optional(CONF_DIMMER_ADJUST, default=1): CV_DIM_VALUE, + vol.Optional(CONF_BUTTON_EVENTS): vol.Coerce(str) })}, extra=vol.ALLOW_EXTRA) QSUSB = {} @@ -118,16 +124,17 @@ class QSLight(QSToggleEntity, Light): def setup(hass, config): """Setup the QSUSB component.""" - from pyqwikswitch import (QSUsb, CMD_BUTTONS, QS_NAME, QS_ID, QS_CMD, - PQS_VALUE, PQS_TYPE, QSType) + from pyqwikswitch import ( + QSUsb, CMD_BUTTONS, QS_NAME, QS_ID, QS_CMD, PQS_VALUE, PQS_TYPE, + QSType) # Override which cmd's in /&listen packets will fire events # By default only buttons of type [TOGGLE,SCENE EXE,LEVEL] - cmd_buttons = config[DOMAIN].get('button_events', ','.join(CMD_BUTTONS)) + cmd_buttons = config[DOMAIN].get(CONF_BUTTON_EVENTS, ','.join(CMD_BUTTONS)) cmd_buttons = cmd_buttons.split(',') - url = config[DOMAIN]['url'] - dimmer_adjust = config[DOMAIN]['dimmer_adjust'] + url = config[DOMAIN][CONF_URL] + dimmer_adjust = config[DOMAIN][CONF_DIMMER_ADJUST] qsusb = QSUsb(url, _LOGGER, dimmer_adjust) diff --git a/homeassistant/components/switch/qwikswitch.py b/homeassistant/components/switch/qwikswitch.py index c3adc33deff..7aea1dea1e1 100644 --- a/homeassistant/components/switch/qwikswitch.py +++ b/homeassistant/components/switch/qwikswitch.py @@ -5,8 +5,11 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.qwikswitch/ """ import logging + import homeassistant.components.qwikswitch as qwikswitch +_LOGGER = logging.getLogger(__name__) + DEPENDENCIES = ['qwikswitch'] @@ -14,8 +17,7 @@ DEPENDENCIES = ['qwikswitch'] def setup_platform(hass, config, add_devices, discovery_info=None): """Add switched from the main Qwikswitch component.""" if discovery_info is None: - logging.getLogger(__name__).error( - 'Configure main Qwikswitch component') + _LOGGER.error("Configure Qwikswitch component") return False add_devices(qwikswitch.QSUSB['switch']) From 4c52380519d3e2c683818cf331f862a1c14ba127 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 17 Jan 2017 23:41:09 +0100 Subject: [PATCH 040/191] Sync logger messages with Mi-Flora and link to docs (#5391) --- homeassistant/components/sensor/skybeacon.py | 46 ++++++++------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/sensor/skybeacon.py b/homeassistant/components/sensor/skybeacon.py index 45dcb5ca2ee..dd6a117d447 100644 --- a/homeassistant/components/sensor/skybeacon.py +++ b/homeassistant/components/sensor/skybeacon.py @@ -1,37 +1,27 @@ """ -Support for SKYBEACON temperature/humidity Bluetooth LE sensor. +Support for Skybeacon temperature/humidity Bluetooth LE sensors. -These are inexpensive CR2477-powered ibeacon/eddystone sensors -that come with temperature/sensor module. -More information: http://cnsky9.en.alibaba.com - -example: -sensor: - - platform: skybeacon - mac: 'F7:BE:12:02:47:31' - name: 'living room' +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.skybeacon/ """ - import logging import threading from uuid import UUID import voluptuous as vol + import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_NAME, - CONF_MAC, - TEMP_CELSIUS, - STATE_UNKNOWN, - EVENT_HOMEASSISTANT_STOP) +from homeassistant.const import ( + CONF_NAME, CONF_MAC, TEMP_CELSIUS, STATE_UNKNOWN, EVENT_HOMEASSISTANT_STOP) REQUIREMENTS = ['pygatt==3.0.0'] -CONNECT_LOCK = threading.Lock() - _LOGGER = logging.getLogger(__name__) +CONNECT_LOCK = threading.Lock() + ATTR_DEVICE = 'device' ATTR_MODEL = 'model' @@ -48,17 +38,18 @@ CONNECT_TIMEOUT = 30 # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor.""" + """Set up the sensor.""" name = config.get(CONF_NAME) mac = config.get(CONF_MAC) - _LOGGER.error("setting up..") + _LOGGER.debug("Setting up...") + mon = Monitor(hass, mac, name) add_devices([SkybeaconTemp(name, mon)]) add_devices([SkybeaconHumid(name, mon)]) def monitor_stop(_service_or_event): """Stop the monitor thread.""" - _LOGGER.info("skybeacon: stopping monitor for %s ", name) + _LOGGER.info("Stopping monitor for %s", name) mon.terminate() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, monitor_stop) @@ -147,15 +138,14 @@ class Monitor(threading.Thread): """Thread that keeps connection alive.""" import pygatt from pygatt.backends import Characteristic - from pygatt.exceptions import (BLEError, - NotConnectedError, - NotificationTimeout) + from pygatt.exceptions import ( + BLEError, NotConnectedError, NotificationTimeout) cached_char = Characteristic(BLE_TEMP_UUID, BLE_TEMP_HANDLE) adapter = pygatt.backends.GATTToolBackend() while True: try: - _LOGGER.info("connecting to %s", self.name) + _LOGGER.info("Connecting to %s", self.name) # we need concurrent connect, so lets not reset the device adapter.start(reset_on_start=False) # seems only one connection can be initiated at a time @@ -170,7 +160,7 @@ class Monitor(threading.Thread): # magic: writing this makes device happy device.char_write_handle(0x1b, bytearray([255]), False) device.subscribe(BLE_TEMP_UUID, self._update) - _LOGGER.info("subscribed to %s", self.name) + _LOGGER.info("Subscribed to %s", self.name) while self.keep_going: # protect against stale connections, just read temperature device.char_read(BLE_TEMP_UUID, timeout=CONNECT_TIMEOUT) @@ -183,8 +173,8 @@ class Monitor(threading.Thread): def _update(self, handle, value): """Notification callback from pygatt.""" - _LOGGER.info("%s: %15s temperature = %-2d.%-2d, humidity = %3d", - handle, self.name, value[0], value[2], value[1]) + _LOGGER.debug("%s: %15s temperature = %-2d.%-2d, humidity = %3d", + handle, self.name, value[0], value[2], value[1]) self.data['temp'] = float(("%d.%d" % (value[0], value[2]))) self.data['humid'] = value[1] From 50b326c7fc01c99c7e963f2676271ccc1f263f44 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Jan 2017 21:04:56 -0800 Subject: [PATCH 041/191] Upgrade somecomfort (#5413) --- homeassistant/components/climate/honeywell.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/honeywell.py b/homeassistant/components/climate/honeywell.py index 0d31cdd1387..3387baf76d8 100644 --- a/homeassistant/components/climate/honeywell.py +++ b/homeassistant/components/climate/honeywell.py @@ -16,7 +16,7 @@ from homeassistant.const import ( import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['evohomeclient==0.2.5', - 'somecomfort==0.3.2'] + 'somecomfort==0.4.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index d13833efe18..85cc320ab1c 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -585,7 +585,7 @@ sleepyq==0.6 snapcast==1.2.2 # homeassistant.components.climate.honeywell -somecomfort==0.3.2 +somecomfort==0.4.1 # homeassistant.components.sensor.speedtest speedtest-cli==1.0.1 From 283bcf367b0923f9c55dee6be9d2b3356c168b29 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Jan 2017 21:48:33 -0800 Subject: [PATCH 042/191] Ignore python-eq3bt from auto-building --- requirements_all.txt | 2 +- script/gen_requirements_all.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements_all.txt b/requirements_all.txt index 85cc320ab1c..b8f93caa5b0 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -483,7 +483,7 @@ pysnmp==4.3.2 python-digitalocean==1.10.1 # homeassistant.components.climate.eq3btsmart -python-eq3bt==0.1.4 +# python-eq3bt==0.1.4 # homeassistant.components.sensor.darksky python-forecastio==1.3.5 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 0231e0d5177..81fb17aac17 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -19,6 +19,7 @@ COMMENT_REQUIREMENTS = ( 'pyuserinput', 'evdev', 'pycups', + 'python-eq3bt', ) IGNORE_PACKAGES = ( From 6cd57ac02f33e552eeaadfde5d1f71b125686a8c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Jan 2017 21:53:03 -0800 Subject: [PATCH 043/191] Fix Yamaha doing I/O in event loop (#5387) --- homeassistant/components/media_player/yamaha.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/yamaha.py b/homeassistant/components/media_player/yamaha.py index 2596e7a4ca9..84778cef2d5 100644 --- a/homeassistant/components/media_player/yamaha.py +++ b/homeassistant/components/media_player/yamaha.py @@ -103,6 +103,7 @@ class YamahaDevice(MediaPlayerDevice): self._source_ignore = source_ignore or [] self._source_names = source_names or {} self._reverse_mapping = None + self._playback_support = None self._is_playback_supported = False self._play_status = None self.update() @@ -131,6 +132,7 @@ class YamahaDevice(MediaPlayerDevice): current_source = self._receiver.input self._current_source = self._source_names.get( current_source, current_source) + self._playback_support = self._receiver.get_playback_support() self._is_playback_supported = self._receiver.is_playback_supported( self._current_source) @@ -183,7 +185,7 @@ class YamahaDevice(MediaPlayerDevice): """Flag of media commands that are supported.""" supported_commands = SUPPORT_YAMAHA - supports = self._receiver.get_playback_support() + supports = self._playback_support mapping = {'play': (SUPPORT_PLAY | SUPPORT_PLAY_MEDIA), 'pause': SUPPORT_PAUSE, 'stop': SUPPORT_STOP, From f7ac644c11557905ae7da7a2882ed5d792f8d2a0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Jan 2017 22:00:15 -0800 Subject: [PATCH 044/191] Make SMTP tests fast --- tests/components/notify/test_smtp.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/components/notify/test_smtp.py b/tests/components/notify/test_smtp.py index 6a2f8c7acbf..509310099e3 100644 --- a/tests/components/notify/test_smtp.py +++ b/tests/components/notify/test_smtp.py @@ -1,5 +1,6 @@ """The tests for the notify smtp platform.""" import unittest +from unittest.mock import patch from homeassistant.components.notify import smtp @@ -9,10 +10,6 @@ from tests.common import get_test_home_assistant class MockSMTP(smtp.MailNotificationService): """Test SMTP object that doesn't need a working server.""" - def connection_is_valid(self): - """Pretend connection is always valid for testing.""" - return True - def _send_email(self, msg): """Just return string for testing.""" return msg.as_string() @@ -31,7 +28,8 @@ class TestNotifySmtp(unittest.TestCase): """"Stop down everything that was started.""" self.hass.stop() - def test_text_email(self): + @patch('email.utils.make_msgid', return_value='') + def test_text_email(self, mock_make_msgid): """Test build of default text email behavior.""" msg = self.mailer.send_message('Test msg') expected = ('^Content-Type: text/plain; charset="us-ascii"\n' @@ -47,7 +45,8 @@ class TestNotifySmtp(unittest.TestCase): 'Test msg$') self.assertRegex(msg, expected) - def test_mixed_email(self): + @patch('email.utils.make_msgid', return_value='') + def test_mixed_email(self, mock_make_msgid): """Test build of mixed text email behavior.""" msg = self.mailer.send_message('Test msg', data={'images': ['test.jpg']}) From 2a362fd1ff3b3b614e47bd22d086c6cf4a67f762 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 18 Jan 2017 07:08:03 +0100 Subject: [PATCH 045/191] Asyncio notify component migration (#5377) * Async migrate notify/platform * convert group to async * fix unittest --- homeassistant/components/notify/__init__.py | 116 +++++++++++++------- homeassistant/components/notify/discord.py | 10 +- homeassistant/components/notify/group.py | 25 +++-- tests/components/notify/test_apns.py | 8 +- tests/components/notify/test_demo.py | 34 +++--- tests/components/notify/test_group.py | 31 ++++-- 6 files changed, 136 insertions(+), 88 deletions(-) diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index a5c1e53ef03..d1d35e07054 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -4,13 +4,15 @@ Provides functionality to notify people. For more details about this component, please refer to the documentation at https://home-assistant.io/components/notify/ """ +import asyncio import logging import os from functools import partial import voluptuous as vol -import homeassistant.bootstrap as bootstrap +from homeassistant.bootstrap import async_prepare_setup_platform +from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv from homeassistant.config import load_yaml_config_file from homeassistant.const import CONF_NAME, CONF_PLATFORM @@ -64,91 +66,110 @@ def send_message(hass, message, title=None, data=None): hass.services.call(DOMAIN, SERVICE_NOTIFY, info) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup the notify services.""" - descriptions = load_yaml_config_file( + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, os.path.join(os.path.dirname(__file__), 'services.yaml')) targets = {} - def setup_notify_platform(platform, p_config=None, discovery_info=None): + @asyncio.coroutine + def async_setup_platform(p_type, p_config=None, discovery_info=None): """Set up a notify platform.""" if p_config is None: p_config = {} if discovery_info is None: discovery_info = {} - notify_implementation = bootstrap.prepare_setup_platform( - hass, config, DOMAIN, platform) + platform = yield from async_prepare_setup_platform( + hass, config, DOMAIN, p_type) - if notify_implementation is None: + if platform is None: _LOGGER.error("Unknown notification service specified") - return False + return - notify_service = notify_implementation.get_service( - hass, p_config, discovery_info) + _LOGGER.info("Setting up %s.%s", DOMAIN, p_type) + notify_service = None + try: + if hasattr(platform, 'async_get_service'): + notify_service = yield from \ + platform.async_get_service(hass, p_config, discovery_info) + elif hasattr(platform, 'get_service'): + notify_service = yield from hass.loop.run_in_executor( + None, platform.get_service, hass, p_config, discovery_info) + else: + raise HomeAssistantError("Invalid notify platform.") - if notify_service is None: - _LOGGER.error("Failed to initialize notification service %s", - platform) - return False + if notify_service is None: + _LOGGER.error( + "Failed to initialize notification service %s", p_type) + return - def notify_message(notify_service, call): + except Exception: # pylint: disable=broad-except + _LOGGER.exception('Error setting up platform %s', p_type) + return + + notify_service.hass = hass + + @asyncio.coroutine + def async_notify_message(service): """Handle sending notification message service calls.""" kwargs = {} - message = call.data[ATTR_MESSAGE] - title = call.data.get(ATTR_TITLE) + message = service.data[ATTR_MESSAGE] + title = service.data.get(ATTR_TITLE) if title: title.hass = hass - kwargs[ATTR_TITLE] = title.render() + kwargs[ATTR_TITLE] = title.async_render() - if targets.get(call.service) is not None: - kwargs[ATTR_TARGET] = [targets[call.service]] - elif call.data.get(ATTR_TARGET) is not None: - kwargs[ATTR_TARGET] = call.data.get(ATTR_TARGET) + if targets.get(service.service) is not None: + kwargs[ATTR_TARGET] = [targets[service.service]] + elif service.data.get(ATTR_TARGET) is not None: + kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET) message.hass = hass - kwargs[ATTR_MESSAGE] = message.render() - kwargs[ATTR_DATA] = call.data.get(ATTR_DATA) + kwargs[ATTR_MESSAGE] = message.async_render() + kwargs[ATTR_DATA] = service.data.get(ATTR_DATA) - notify_service.send_message(**kwargs) - - service_call_handler = partial(notify_message, notify_service) + yield from notify_service.async_send_message(**kwargs) if hasattr(notify_service, 'targets'): platform_name = ( p_config.get(CONF_NAME) or discovery_info.get(CONF_NAME) or - platform) + p_type) for name, target in notify_service.targets.items(): target_name = slugify('{}_{}'.format(platform_name, name)) targets[target_name] = target - hass.services.register(DOMAIN, target_name, - service_call_handler, - descriptions.get(SERVICE_NOTIFY), - schema=NOTIFY_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, target_name, async_notify_message, + descriptions.get(SERVICE_NOTIFY), + schema=NOTIFY_SERVICE_SCHEMA) platform_name = ( p_config.get(CONF_NAME) or discovery_info.get(CONF_NAME) or SERVICE_NOTIFY) platform_name_slug = slugify(platform_name) - hass.services.register( - DOMAIN, platform_name_slug, service_call_handler, + hass.services.async_register( + DOMAIN, platform_name_slug, async_notify_message, descriptions.get(SERVICE_NOTIFY), schema=NOTIFY_SERVICE_SCHEMA) return True - for platform, p_config in config_per_platform(config, DOMAIN): - if not setup_notify_platform(platform, p_config): - _LOGGER.error("Failed to set up platform %s", platform) - continue + setup_tasks = [async_setup_platform(p_type, p_config) for p_type, p_config + in config_per_platform(config, DOMAIN)] - def platform_discovered(platform, info): + if setup_tasks: + yield from asyncio.wait(setup_tasks, loop=hass.loop) + + @asyncio.coroutine + def async_platform_discovered(platform, info): """Callback to load a platform.""" - setup_notify_platform(platform, discovery_info=info) + yield from async_setup_platform(platform, discovery_info=info) - discovery.listen_platform(hass, DOMAIN, platform_discovered) + discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered) return True @@ -156,9 +177,20 @@ def setup(hass, config): class BaseNotificationService(object): """An abstract class for notification services.""" + hass = None + def send_message(self, message, **kwargs): """Send a message. kwargs can contain ATTR_TITLE to specify a title. """ - raise NotImplementedError + raise NotImplementedError() + + def async_send_message(self, message, **kwargs): + """Send a message. + + kwargs can contain ATTR_TITLE to specify a title. + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, partial(self.send_message, message, **kwargs)) diff --git a/homeassistant/components/notify/discord.py b/homeassistant/components/notify/discord.py index 3d426b22645..34fb2a1770a 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/notify/discord.py @@ -4,7 +4,7 @@ import asyncio import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) + PLATFORM_SCHEMA, BaseNotificationService, ATTR_TARGET) _LOGGER = logging.getLogger(__name__) @@ -32,20 +32,16 @@ class DiscordNotificationService(BaseNotificationService): self.hass = hass @asyncio.coroutine - def async_send_message(self, message, target): + def async_send_message(self, message, **kwargs): """Login to Discord, send message to channel(s) and log out.""" import discord discord_bot = discord.Client(loop=self.hass.loop) yield from discord_bot.login(self.token) - for channelid in target: + for channelid in kwargs[ATTR_TARGET]: channel = discord.Object(id=channelid) yield from discord_bot.send_message(channel, message) yield from discord_bot.logout() yield from discord_bot.close() - - def send_message(self, message=None, target=None, **kwargs): - """Send a message using Discord.""" - self.hass.async_add_job(self.async_send_message(message, target)) diff --git a/homeassistant/components/notify/group.py b/homeassistant/components/notify/group.py index 3de79f5a7be..07cc7b1146a 100644 --- a/homeassistant/components/notify/group.py +++ b/homeassistant/components/notify/group.py @@ -4,15 +4,15 @@ Group platform for notify component. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.group/ """ +import asyncio import collections from copy import deepcopy import logging import voluptuous as vol from homeassistant.const import ATTR_SERVICE -from homeassistant.components.notify import (DOMAIN, ATTR_MESSAGE, ATTR_DATA, - PLATFORM_SCHEMA, - BaseNotificationService) +from homeassistant.components.notify import ( + DOMAIN, ATTR_MESSAGE, ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) @@ -28,7 +28,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def update(input_dict, update_source): - """Deep update a dictionary.""" + """Deep update a dictionary. + + Async friendly. + """ for key, val in update_source.items(): if isinstance(val, collections.Mapping): recurse = update(input_dict.get(key, {}), val) @@ -38,7 +41,8 @@ def update(input_dict, update_source): return input_dict -def get_service(hass, config, discovery_info=None): +@asyncio.coroutine +def async_get_service(hass, config, discovery_info=None): """Get the Group notification service.""" return GroupNotifyPlatform(hass, config.get(CONF_SERVICES)) @@ -51,14 +55,19 @@ class GroupNotifyPlatform(BaseNotificationService): self.hass = hass self.entities = entities - def send_message(self, message="", **kwargs): + @asyncio.coroutine + def async_send_message(self, message="", **kwargs): """Send message to all entities in the group.""" payload = {ATTR_MESSAGE: message} payload.update({key: val for key, val in kwargs.items() if val}) + tasks = [] for entity in self.entities: sending_payload = deepcopy(payload.copy()) if entity.get(ATTR_DATA) is not None: update(sending_payload, entity.get(ATTR_DATA)) - self.hass.services.call(DOMAIN, entity.get(ATTR_SERVICE), - sending_payload) + tasks.append(self.hass.services.async_call( + DOMAIN, entity.get(ATTR_SERVICE), sending_payload)) + + if tasks: + yield from asyncio.wait(tasks, loop=self.hass.loop) diff --git a/tests/components/notify/test_apns.py b/tests/components/notify/test_apns.py index 6949863280c..7246aea3302 100644 --- a/tests/components/notify/test_apns.py +++ b/tests/components/notify/test_apns.py @@ -41,7 +41,9 @@ class TestApns(unittest.TestCase): assert setup_component(self.hass, notify.DOMAIN, CONFIG) assert handle_config[notify.DOMAIN] - def test_apns_setup_full(self): + @patch('os.path.isfile', return_value=True) + @patch('os.access', return_value=True) + def test_apns_setup_full(self, mock_access, mock_isfile): """Test setup with all data.""" config = { 'notify': { @@ -53,7 +55,9 @@ class TestApns(unittest.TestCase): } } - self.assertTrue(notify.setup(self.hass, config)) + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] def test_apns_setup_missing_name(self): """Test setup with missing name.""" diff --git a/tests/components/notify/test_demo.py b/tests/components/notify/test_demo.py index 1ccb3f5c56d..de13f678ae0 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/notify/test_demo.py @@ -1,4 +1,5 @@ """The tests for the notify demo platform.""" +import asyncio import unittest from unittest.mock import patch @@ -16,6 +17,12 @@ CONFIG = { } +@asyncio.coroutine +def mock_setup_platform(): + """Mock prepare_setup_platform.""" + return None + + class TestNotifyDemo(unittest.TestCase): """Test the demo notify.""" @@ -45,23 +52,16 @@ class TestNotifyDemo(unittest.TestCase): """Test setup.""" self._setup_notify() - @patch('homeassistant.bootstrap.prepare_setup_platform') + @patch('homeassistant.bootstrap.async_prepare_setup_platform', + return_value=mock_setup_platform()) def test_no_prepare_setup_platform(self, mock_prep_setup_platform): """Test missing notify platform.""" - mock_prep_setup_platform.return_value = None - with self.assertLogs('homeassistant.components.notify', - level='ERROR') as log_handle: - self._setup_notify() - self.hass.block_till_done() - assert mock_prep_setup_platform.called - self.assertEqual( - log_handle.output, - ['ERROR:homeassistant.components.notify:' - 'Unknown notification service specified', - 'ERROR:homeassistant.components.notify:' - 'Failed to set up platform demo']) + with assert_setup_component(0): + setup_component(self.hass, notify.DOMAIN, CONFIG) - @patch('homeassistant.components.notify.demo.get_service') + assert mock_prep_setup_platform.called + + @patch('homeassistant.components.notify.demo.get_service', autospec=True) def test_no_notify_service(self, mock_demo_get_service): """Test missing platform notify service instance.""" mock_demo_get_service.return_value = None @@ -73,11 +73,9 @@ class TestNotifyDemo(unittest.TestCase): self.assertEqual( log_handle.output, ['ERROR:homeassistant.components.notify:' - 'Failed to initialize notification service demo', - 'ERROR:homeassistant.components.notify:' - 'Failed to set up platform demo']) + 'Failed to initialize notification service demo']) - @patch('homeassistant.components.notify.demo.get_service') + @patch('homeassistant.components.notify.demo.get_service', autospec=True) def test_discover_notify(self, mock_demo_get_service): """Test discovery of notify demo platform.""" assert notify.DOMAIN not in self.hass.config.components diff --git a/tests/components/notify/test_group.py b/tests/components/notify/test_group.py index 14c8c46b6c3..1aa07fed583 100644 --- a/tests/components/notify/test_group.py +++ b/tests/components/notify/test_group.py @@ -5,6 +5,7 @@ from unittest.mock import MagicMock, patch from homeassistant.bootstrap import setup_component import homeassistant.components.notify as notify from homeassistant.components.notify import group, demo +from homeassistant.util.async import run_coroutine_threadsafe from tests.common import assert_setup_component, get_test_home_assistant @@ -16,8 +17,11 @@ class TestNotifyGroup(unittest.TestCase): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() self.events = [] - self.service1 = MagicMock() - self.service2 = MagicMock() + self.service1 = demo.DemoNotificationService(self.hass) + self.service2 = demo.DemoNotificationService(self.hass) + + self.service1.send_message = MagicMock(autospec=True) + self.service2.send_message = MagicMock(autospec=True) def mock_get_service(hass, config, discovery_info=None): if config['name'] == 'demo1': @@ -37,11 +41,14 @@ class TestNotifyGroup(unittest.TestCase): }] }) - self.service = group.get_service(self.hass, {'services': [ - {'service': 'demo1'}, - {'service': 'demo2', - 'data': {'target': 'unnamed device', - 'data': {'test': 'message'}}}]}) + self.service = run_coroutine_threadsafe( + group.async_get_service(self.hass, {'services': [ + {'service': 'demo1'}, + {'service': 'demo2', + 'data': {'target': 'unnamed device', + 'data': {'test': 'message'}}}]}), + self.hass.loop + ).result() assert self.service is not None @@ -51,17 +58,19 @@ class TestNotifyGroup(unittest.TestCase): def test_send_message_with_data(self): """Test sending a message with to a notify group.""" - self.service.send_message('Hello', title='Test notification', - data={'hello': 'world'}) + run_coroutine_threadsafe( + self.service.async_send_message( + 'Hello', title='Test notification', data={'hello': 'world'}), + self.hass.loop).result() self.hass.block_till_done() + assert self.service1.send_message.mock_calls[0][1][0] == 'Hello' assert self.service1.send_message.mock_calls[0][2] == { - 'message': 'Hello', 'title': 'Test notification', 'data': {'hello': 'world'} } + assert self.service2.send_message.mock_calls[0][1][0] == 'Hello' assert self.service2.send_message.mock_calls[0][2] == { - 'message': 'Hello', 'target': ['unnamed device'], 'title': 'Test notification', 'data': {'hello': 'world', 'test': 'message'} From eb06023aa58c40f9d3879af4ddeda255701bb620 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Wed, 18 Jan 2017 01:15:37 -0500 Subject: [PATCH 046/191] Fix universal mp service call wth no child (#5411) --- .../components/media_player/universal.py | 4 ++++ .../components/media_player/test_universal.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/homeassistant/components/media_player/universal.py b/homeassistant/components/media_player/universal.py index 45c30b979a6..e01717f5693 100644 --- a/homeassistant/components/media_player/universal.py +++ b/homeassistant/components/media_player/universal.py @@ -190,6 +190,10 @@ class UniversalMediaPlayer(MediaPlayerDevice): return active_child = self._child_state + if active_child is None: + # No child to call service on + return + service_data[ATTR_ENTITY_ID] = active_child.entity_id self.hass.services.call(DOMAIN, service_name, service_data, diff --git a/tests/components/media_player/test_universal.py b/tests/components/media_player/test_universal.py index ff70fe36a17..4a06d989ce2 100644 --- a/tests/components/media_player/test_universal.py +++ b/tests/components/media_player/test_universal.py @@ -538,6 +538,25 @@ class TestMediaPlayer(unittest.TestCase): self.assertEqual(check_flags, ump.supported_media_commands) + def test_service_call_no_active_child(self): + """Test a service call to children with no active child.""" + 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.mock_mp_1._state = STATE_OFF + self.mock_mp_1.update_ha_state() + self.mock_mp_2._state = STATE_OFF + self.mock_mp_2.update_ha_state() + ump.update() + + ump.turn_off() + self.assertEqual(0, len(self.mock_mp_1.service_calls['turn_off'])) + self.assertEqual(0, len(self.mock_mp_2.service_calls['turn_off'])) + def test_service_call_to_child(self): """Test service calls that should be routed to a child.""" config = self.config_children_only From 3267aa8c089d3afc98c14428795112c60fdb3ca7 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 18 Jan 2017 11:01:31 +0100 Subject: [PATCH 047/191] WIP fritz install dependencies fix (#5399) * updated fritzconnection dependency to 0.6 from pypi * updated requirements_all for new dependencies of fritz platform --- homeassistant/components/device_tracker/fritz.py | 4 +--- requirements_all.txt | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/device_tracker/fritz.py b/homeassistant/components/device_tracker/fritz.py index 055c3bc85c0..c262a8fdf2a 100644 --- a/homeassistant/components/device_tracker/fritz.py +++ b/homeassistant/components/device_tracker/fritz.py @@ -15,9 +15,7 @@ from homeassistant.components.device_tracker import ( from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.util import Throttle -REQUIREMENTS = ['https://github.com/deisi/fritzconnection/archive/' - 'b5c14515e1c8e2652b06b6316a7f3913df942841.zip' - '#fritzconnection==0.4.6'] +REQUIREMENTS = ['fritzconnection==0.6'] # Return cached results if last scan was less then this time ago. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) diff --git a/requirements_all.txt b/requirements_all.txt index b8f93caa5b0..b8fe4898d70 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -139,6 +139,9 @@ flux_led==0.12 # homeassistant.components.notify.free_mobile freesms==0.1.1 +# homeassistant.components.device_tracker.fritz +# fritzconnection==0.6 + # homeassistant.components.conversation fuzzywuzzy==0.14.0 @@ -210,9 +213,6 @@ https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b6 # homeassistant.components.media_player.onkyo https://github.com/danieljkemp/onkyo-eiscp/archive/python3.zip#onkyo-eiscp==0.9.2 -# homeassistant.components.device_tracker.fritz -# https://github.com/deisi/fritzconnection/archive/b5c14515e1c8e2652b06b6316a7f3913df942841.zip#fritzconnection==0.4.6 - # homeassistant.components.netatmo https://github.com/jabesq/netatmo-api-python/archive/v0.9.0.zip#lnetatmo==0.9.0 From b7bf07eaca92f5c948a5868578c61b707e8ed946 Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Wed, 18 Jan 2017 12:20:39 +0100 Subject: [PATCH 048/191] Update generic_thermostat.py After _control_heating() is executed, current_operation() is correctly called but _is_device_active() still reports the old state if the heater switch, at least in my case. The resulting climate state is incorrect until the next refresh, which apparently occurs only when _sensor_changed() gets called (it can be minutes after). I think the state of the heater switch should be forced to update at the end of _control_heating(), but I don't know how to do that... A simple sleep() fixes it, but obviously is just a temporary workaround, I'm not really expecting this PR to be actually committed, unless there's no other solution. --- homeassistant/components/climate/generic_thermostat.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index a40795c37c5..a61c71a6551 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -16,6 +16,7 @@ from homeassistant.const import ( from homeassistant.helpers import condition from homeassistant.helpers.event import track_state_change import homeassistant.helpers.config_validation as cv +import time _LOGGER = logging.getLogger(__name__) @@ -222,6 +223,7 @@ class GenericThermostat(ClimateDevice): if too_cold: _LOGGER.info('Turning on heater %s', self.heater_entity_id) switch.turn_on(self.hass, self.heater_entity_id) + time.sleep(.1) @property def _is_device_active(self): From 72dca1da091945651788ed6cc5e314570b79361f Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Wed, 18 Jan 2017 12:39:16 +0100 Subject: [PATCH 049/191] Update generic_thermostat.py --- homeassistant/components/climate/generic_thermostat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index a61c71a6551..efebcbbf7e9 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -5,6 +5,7 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/climate.generic_thermostat/ """ import logging +import time import voluptuous as vol @@ -16,7 +17,6 @@ from homeassistant.const import ( from homeassistant.helpers import condition from homeassistant.helpers.event import track_state_change import homeassistant.helpers.config_validation as cv -import time _LOGGER = logging.getLogger(__name__) From 5299c923526f08ed711146aa4838a8d895db6550 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 18 Jan 2017 22:52:11 +0100 Subject: [PATCH 050/191] Bugfix volume up/down (#5426) --- homeassistant/components/media_player/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index f97b169e1bc..576dca25a6a 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -757,6 +757,7 @@ class MediaPlayerDevice(Entity): if hasattr(self, 'volume_up'): # pylint: disable=no-member yield from self.hass.loop.run_in_executor(None, self.volume_up) + return if self.volume_level < 1: yield from self.async_set_volume_level( @@ -771,6 +772,7 @@ class MediaPlayerDevice(Entity): if hasattr(self, 'volume_down'): # pylint: disable=no-member yield from self.hass.loop.run_in_executor(None, self.volume_down) + return if self.volume_level > 0: yield from self.async_set_volume_level( From 216ac14b3d9d1beae46cacc82fcc3ec5532f8dfb Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Wed, 18 Jan 2017 22:15:51 -0500 Subject: [PATCH 051/191] Fix test for async media player volume helpers (#5432) --- tests/components/media_player/test_async_helpers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/components/media_player/test_async_helpers.py b/tests/components/media_player/test_async_helpers.py index 32b527ac4f1..784c54f6d62 100644 --- a/tests/components/media_player/test_async_helpers.py +++ b/tests/components/media_player/test_async_helpers.py @@ -105,7 +105,8 @@ class TestSyncMediaPlayer(unittest.TestCase): self.assertEqual(self.player.volume_level, 0) self.player.set_volume_level(0.5) self.assertEqual(self.player.volume_level, 0.5) - self.player.volume_up() + run_coroutine_threadsafe( + self.player.async_volume_up(), self.hass.loop).result() self.assertEqual(self.player.volume_level, 0.7) def test_volume_down(self): @@ -113,5 +114,6 @@ class TestSyncMediaPlayer(unittest.TestCase): self.assertEqual(self.player.volume_level, 0) self.player.set_volume_level(0.5) self.assertEqual(self.player.volume_level, 0.5) - self.player.volume_down() + run_coroutine_threadsafe( + self.player.async_volume_down(), self.hass.loop).result() self.assertEqual(self.player.volume_level, 0.3) From a87d653077b0a83c6fef29854694d1f225e50804 Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Thu, 19 Jan 2017 10:57:45 +0100 Subject: [PATCH 052/191] Update generic_thermostat.py As suggested, I added a callback on the heater switch state change. Still it doesn't solve the problem. --- homeassistant/components/climate/generic_thermostat.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index efebcbbf7e9..3bf64fab2df 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -5,7 +5,6 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/climate.generic_thermostat/ """ import logging -import time import voluptuous as vol @@ -88,6 +87,7 @@ class GenericThermostat(ClimateDevice): self._unit = hass.config.units.temperature_unit track_state_change(hass, sensor_entity_id, self._sensor_changed) + track_state_change(hass, heater_entity_id, self._switch_changed) sensor_state = hass.states.get(sensor_entity_id) if sensor_state: @@ -166,6 +166,12 @@ class GenericThermostat(ClimateDevice): self._control_heating() self.schedule_update_ha_state() + def _switch_changed(self, entity_id, old_state, new_state): + """Called when heater switch changes state.""" + if new_state is None: + return + self.schedule_update_ha_state() + def _update_temp(self, state): """Update thermostat with latest state from sensor.""" unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) @@ -223,7 +229,6 @@ class GenericThermostat(ClimateDevice): if too_cold: _LOGGER.info('Turning on heater %s', self.heater_entity_id) switch.turn_on(self.hass, self.heater_entity_id) - time.sleep(.1) @property def _is_device_active(self): From 909978b0d1a763861154cfc454890ff1030192c6 Mon Sep 17 00:00:00 2001 From: Touliloup Date: Thu, 19 Jan 2017 15:05:37 +0100 Subject: [PATCH 053/191] [Device Tracker] Xiaomi Mi Router token refresh (#5437) Device token is refreshed if not anymore valid (for example after router reboot). Token refresh will only be tried once per update. --- .../components/device_tracker/xiaomi.py | 104 ++++++++++++------ .../components/device_tracker/test_xiaomi.py | 50 ++++++++- 2 files changed, 118 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/device_tracker/xiaomi.py b/homeassistant/components/device_tracker/xiaomi.py index ff53d1fe99f..7c5c415f054 100644 --- a/homeassistant/components/device_tracker/xiaomi.py +++ b/homeassistant/components/device_tracker/xiaomi.py @@ -31,12 +31,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def get_scanner(hass, config): """Validate the configuration and return a Xiaomi Device Scanner.""" - scanner = XioamiDeviceScanner(config[DOMAIN]) + scanner = XiaomiDeviceScanner(config[DOMAIN]) return scanner if scanner.success_init else None -class XioamiDeviceScanner(DeviceScanner): +class XiaomiDeviceScanner(DeviceScanner): """This class queries a Xiaomi Mi router. Adapted from Luci scanner. @@ -44,15 +44,14 @@ class XioamiDeviceScanner(DeviceScanner): def __init__(self, config): """Initialize the scanner.""" - host = config[CONF_HOST] - username, password = config[CONF_USERNAME], config[CONF_PASSWORD] + self.host = config[CONF_HOST] + self.username = config[CONF_USERNAME] + self.password = config[CONF_PASSWORD] self.lock = threading.Lock() self.last_results = {} - self.token = _get_token(host, username, password) - - self.host = host + self.token = _get_token(self.host, self.username, self.password) self.mac2name = None self.success_init = self.token is not None @@ -66,9 +65,7 @@ class XioamiDeviceScanner(DeviceScanner): """Return the name of the given device or None if we don't know.""" with self.lock: if self.mac2name is None: - url = "http://{}/cgi-bin/luci/;stok={}/api/misystem/devicelist" - url = url.format(self.host, self.token) - result = _get_device_list(url) + result = self._retrieve_list_with_retry() if result: hosts = [x for x in result if 'mac' in x and 'name' in x] @@ -76,7 +73,7 @@ class XioamiDeviceScanner(DeviceScanner): (x['mac'].upper(), x['name']) for x in hosts] self.mac2name = dict(mac2name_list) else: - # Error, handled in the _req_json_rpc + # Error, handled in the _retrieve_list_with_retry return return self.mac2name.get(device.upper(), None) @@ -90,29 +87,72 @@ class XioamiDeviceScanner(DeviceScanner): return False with self.lock: - _LOGGER.info('Refreshing device list') - url = "http://{}/cgi-bin/luci/;stok={}/api/misystem/devicelist" - url = url.format(self.host, self.token) - result = _get_device_list(url) + result = self._retrieve_list_with_retry() if result: - self.last_results = [] - for device_entry in result: - # Check if the device is marked as connected - if int(device_entry['online']) == 1: - self.last_results.append(device_entry['mac']) - + self._store_result(result) return True - return False + def _retrieve_list_with_retry(self): + """Retrieve the device list with a retry if token is invalid. -def _get_device_list(url, **kwargs): + Return the list if successful. + """ + _LOGGER.info('Refreshing device list') + result = _retrieve_list(self.host, self.token) + if result: + return result + else: + _LOGGER.info('Refreshing token and retrying device list refresh') + self.token = _get_token(self.host, self.username, self.password) + return _retrieve_list(self.host, self.token) + + def _store_result(self, result): + """Extract and store the device list in self.last_results.""" + self.last_results = [] + for device_entry in result: + # Check if the device is marked as connected + if int(device_entry['online']) == 1: + self.last_results.append(device_entry['mac']) + + +def _retrieve_list(host, token, **kwargs): + """"Get device list for the given host.""" + url = "http://{}/cgi-bin/luci/;stok={}/api/misystem/devicelist" + url = url.format(host, token) try: res = requests.get(url, timeout=5, **kwargs) except requests.exceptions.Timeout: - _LOGGER.exception('Connection to the router timed out') + _LOGGER.exception('Connection to the router timed out at URL [%s]', + url) + return + if res.status_code != 200: + _LOGGER.exception('Connection failed with http code [%s]', + res.status_code) + return + try: + result = res.json() + except ValueError: + # If json decoder could not parse the response + _LOGGER.exception('Failed to parse response from mi router') + return + try: + xiaomi_code = result['code'] + except KeyError: + _LOGGER.exception('No field code in response from mi router. %s', + result) + return + if xiaomi_code == 0: + try: + return result['list'] + except KeyError: + _LOGGER.exception('No list in response from mi router. %s', result) + return + else: + _LOGGER.info( + 'Receive wrong Xiaomi code [%s], expected [0] in response [%s]', + xiaomi_code, result) return - return _extract_result(res, 'list') def _get_token(host, username, password): @@ -124,10 +164,6 @@ def _get_token(host, username, password): except requests.exceptions.Timeout: _LOGGER.exception('Connection to the router timed out') return - return _extract_result(res, 'token') - - -def _extract_result(res, key_name): if res.status_code == 200: try: result = res.json() @@ -136,10 +172,12 @@ def _extract_result(res, key_name): _LOGGER.exception('Failed to parse response from mi router') return try: - return result[key_name] + return result['token'] except KeyError: - _LOGGER.exception('No %s in response from mi router. %s', - key_name, result) + error_message = "Xiaomi token cannot be refreshed, response from "\ + + "url: [%s] \nwith parameter: [%s] \nwas: [%s]" + _LOGGER.exception(error_message, url, data, result) return else: - _LOGGER.error('Invalid response from mi router: %s', res) + _LOGGER.error('Invalid response: [%s] at url: [%s] with data [%s]', + res, url, data) diff --git a/tests/components/device_tracker/test_xiaomi.py b/tests/components/device_tracker/test_xiaomi.py index 482ed7c0c0d..94a4566a17b 100644 --- a/tests/components/device_tracker/test_xiaomi.py +++ b/tests/components/device_tracker/test_xiaomi.py @@ -15,9 +15,12 @@ from tests.common import get_test_home_assistant _LOGGER = logging.getLogger(__name__) INVALID_USERNAME = 'bob' +TOKEN_TIMEOUT_USERNAME = 'tok' URL_AUTHORIZE = 'http://192.168.0.1/cgi-bin/luci/api/xqsystem/login' URL_LIST_END = 'api/misystem/devicelist' +FIRST_CALL = True + def mocked_requests(*args, **kwargs): """Mock requests.get invocations.""" @@ -44,20 +47,38 @@ def mocked_requests(*args, **kwargs): raise requests.HTTPError(self.status_code) data = kwargs.get('data') + global FIRST_CALL if data and data.get('username', None) == INVALID_USERNAME: + # deliver an invalid token return MockResponse({ "code": "401", "msg": "Invalid token" }, 200) + elif data and data.get('username', None) == TOKEN_TIMEOUT_USERNAME: + # deliver an expired token + return MockResponse({ + "url": "/cgi-bin/luci/;stok=ef5860/web/home", + "token": "timedOut", + "code": "0" + }, 200) elif str(args[0]).startswith(URL_AUTHORIZE): - print("deliver authorized") + # deliver an authorized token return MockResponse({ "url": "/cgi-bin/luci/;stok=ef5860/web/home", "token": "ef5860", "code": "0" }, 200) + elif str(args[0]).endswith("timedOut/" + URL_LIST_END) \ + and FIRST_CALL is True: + FIRST_CALL = False + # deliver an error when called with expired token + return MockResponse({ + "code": "401", + "msg": "Invalid token" + }, 200) elif str(args[0]).endswith(URL_LIST_END): + # deliver the device list return MockResponse({ "mac": "1C:98:EC:0E:D5:A4", "list": [ @@ -144,7 +165,7 @@ class TestXiaomiDeviceScanner(unittest.TestCase): self.hass.stop() @mock.patch( - 'homeassistant.components.device_tracker.xiaomi.XioamiDeviceScanner', + 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', return_value=mock.MagicMock()) def test_config(self, xiaomi_mock): """Testing minimal configuration.""" @@ -165,7 +186,7 @@ class TestXiaomiDeviceScanner(unittest.TestCase): self.assertEqual(call_arg['platform'], 'device_tracker') @mock.patch( - 'homeassistant.components.device_tracker.xiaomi.XioamiDeviceScanner', + 'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner', return_value=mock.MagicMock()) def test_config_full(self, xiaomi_mock): """Testing full configuration.""" @@ -219,3 +240,26 @@ class TestXiaomiDeviceScanner(unittest.TestCase): scanner.get_device_name("23:83:BF:F6:38:A0")) self.assertEqual("Device2", scanner.get_device_name("1D:98:EC:5E:D5:A6")) + + @patch('requests.get', side_effect=mocked_requests) + @patch('requests.post', side_effect=mocked_requests) + def test_token_timed_out(self, mock_get, mock_post): + """"Testing refresh with a timed out token. + + New token is requested and list is downloaded a second time. + """ + config = { + DOMAIN: xiaomi.PLATFORM_SCHEMA({ + CONF_PLATFORM: xiaomi.DOMAIN, + CONF_HOST: '192.168.0.1', + CONF_USERNAME: TOKEN_TIMEOUT_USERNAME, + CONF_PASSWORD: 'passwordTest' + }) + } + scanner = get_scanner(self.hass, config) + self.assertIsNotNone(scanner) + self.assertEqual(2, len(scanner.scan_devices())) + self.assertEqual("Device1", + scanner.get_device_name("23:83:BF:F6:38:A0")) + self.assertEqual("Device2", + scanner.get_device_name("1D:98:EC:5E:D5:A6")) From 97996317974770a54751f1f025be76bc7633ca46 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 19 Jan 2017 18:53:08 +0100 Subject: [PATCH 054/191] [camera/mjpeg] Support still image for thumbmail (#5440) --- homeassistant/components/camera/mjpeg.py | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index dd030099a45..b29cfcf8949 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -26,12 +26,14 @@ from homeassistant.helpers import config_validation as cv _LOGGER = logging.getLogger(__name__) CONF_MJPEG_URL = 'mjpeg_url' +CONF_STILL_IMAGE_URL = 'still_image_url' CONTENT_TYPE_HEADER = 'Content-Type' DEFAULT_NAME = 'Mjpeg Camera' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_MJPEG_URL): cv.url, + vol.Optional(CONF_STILL_IMAGE_URL): cv.url, vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION): vol.In([HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, @@ -70,6 +72,7 @@ class MjpegCamera(Camera): self._username = device_info.get(CONF_USERNAME) self._password = device_info.get(CONF_PASSWORD) self._mjpeg_url = device_info[CONF_MJPEG_URL] + self._still_image_url = device_info[CONF_STILL_IMAGE_URL] self._auth = None if self._username and self._password: @@ -78,6 +81,37 @@ class MjpegCamera(Camera): self._username, password=self._password ) + @asyncio.coroutine + def async_camera_image(self): + """Return a still image response from the camera.""" + # DigestAuth is not supported + if self._authentication == HTTP_DIGEST_AUTHENTICATION or \ + self._still_image_url is None: + image = yield from self.hass.loop.run_in_executor( + None, self.camera_image) + return image + + websession = async_get_clientsession(self.hass) + response = None + try: + with async_timeout.timeout(10, loop=self.hass.loop): + response = websession.get( + self._still_image_url, auth=self._auth) + + image = yield from response.read() + return image + + except asyncio.TimeoutError: + _LOGGER.error('Timeout getting camera image') + + except (aiohttp.errors.ClientError, + aiohttp.errors.ClientDisconnectedError) as err: + _LOGGER.error('Error getting new camera image: %s', err) + + finally: + if response is not None: + yield from response.release() + def camera_image(self): """Return a still image response from the camera.""" if self._username and self._password: From 8da398c0bd937d6bf83f052b5c920771f33b01cf Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 19 Jan 2017 18:55:27 +0100 Subject: [PATCH 055/191] Proxy aiohttp websession / more rebust. (#5419) --- homeassistant/components/camera/__init__.py | 2 +- homeassistant/components/camera/amcrest.py | 43 +++---------------- homeassistant/components/camera/mjpeg.py | 37 ++-------------- homeassistant/components/camera/synology.py | 39 +++-------------- homeassistant/helpers/aiohttp_client.py | 47 +++++++++++++++++++-- 5 files changed, 60 insertions(+), 108 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 89a2f6c5e46..174d0f5a298 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -174,7 +174,7 @@ class Camera(Entity): yield from asyncio.sleep(.5) - except asyncio.CancelledError: + except (asyncio.CancelledError, ConnectionResetError): _LOGGER.debug("Close stream by frontend.") response = None diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py index bec760dbe10..604bf519042 100644 --- a/homeassistant/components/camera/amcrest.py +++ b/homeassistant/components/camera/amcrest.py @@ -8,9 +8,6 @@ import asyncio import logging import aiohttp -from aiohttp import web -from aiohttp.web_exceptions import HTTPGatewayTimeout -import async_timeout import voluptuous as vol import homeassistant.loader as loader @@ -18,7 +15,8 @@ from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT) from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.aiohttp_client import async_create_clientsession +from homeassistant.helpers.aiohttp_client import ( + async_get_clientsession, async_aiohttp_proxy_stream) REQUIREMENTS = ['amcrest==1.1.0'] @@ -108,7 +106,6 @@ class AmcrestCam(Camera): device_info.get(CONF_USERNAME), password=device_info.get(CONF_PASSWORD) ) - self._websession = async_create_clientsession(hass) def camera_image(self): """Return a still image reponse from the camera.""" @@ -125,44 +122,16 @@ class AmcrestCam(Camera): return # Otherwise, stream an MJPEG image stream directly from the camera + websession = async_get_clientsession(self.hass) streaming_url = '%s/mjpg/video.cgi?channel=0&subtype=%d' % ( self._base_url, self._resolution ) - stream = None - response = None - try: - with async_timeout.timeout(TIMEOUT, loop=self.hass.loop): - stream = yield from self._websession.get( - streaming_url, - auth=self._token, - timeout=TIMEOUT - ) - response = web.StreamResponse() - response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) + stream_coro = websession.get( + streaming_url, auth=self._token, timeout=TIMEOUT) - yield from response.prepare(request) - - while True: - data = yield from stream.content.read(16384) - if not data: - break - response.write(data) - - except (asyncio.TimeoutError, aiohttp.errors.ClientError): - _LOGGER.exception("Error on %s", streaming_url) - raise HTTPGatewayTimeout() - - except asyncio.CancelledError: - _LOGGER.debug("Close stream by frontend.") - response = None - - finally: - if stream is not None: - stream.close() - if response is not None: - yield from response.write_eof() + yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro) @property def name(self): diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index b29cfcf8949..125daba16c4 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -9,9 +9,6 @@ import logging from contextlib import closing import aiohttp -from aiohttp import web -from aiohttp.web_exceptions import HTTPGatewayTimeout -import async_timeout import requests from requests.auth import HTTPBasicAuth, HTTPDigestAuth import voluptuous as vol @@ -20,7 +17,8 @@ from homeassistant.const import ( CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_AUTHENTICATION, HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION) from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera) -from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.aiohttp_client import ( + async_get_clientsession, async_aiohttp_proxy_stream) from homeassistant.helpers import config_validation as cv _LOGGER = logging.getLogger(__name__) @@ -137,36 +135,9 @@ class MjpegCamera(Camera): # connect to stream websession = async_get_clientsession(self.hass) - stream = None - response = None - try: - with async_timeout.timeout(10, loop=self.hass.loop): - stream = yield from websession.get(self._mjpeg_url, - auth=self._auth) + stream_coro = websession.get(self._mjpeg_url, auth=self._auth) - response = web.StreamResponse() - response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) - - yield from response.prepare(request) - - while True: - data = yield from stream.content.read(102400) - if not data: - break - response.write(data) - - except asyncio.TimeoutError: - raise HTTPGatewayTimeout() - - except asyncio.CancelledError: - _LOGGER.debug("Close stream by frontend.") - response = None - - finally: - if stream is not None: - stream.close() - if response is not None: - yield from response.write_eof() + yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro) @property def name(self): diff --git a/homeassistant/components/camera/synology.py b/homeassistant/components/camera/synology.py index 424e269c555..39939c73d0d 100644 --- a/homeassistant/components/camera/synology.py +++ b/homeassistant/components/camera/synology.py @@ -10,8 +10,6 @@ import logging import voluptuous as vol import aiohttp -from aiohttp import web -from aiohttp.web_exceptions import HTTPGatewayTimeout import async_timeout from homeassistant.const import ( @@ -20,7 +18,8 @@ from homeassistant.const import ( from homeassistant.components.camera import ( Camera, PLATFORM_SCHEMA) from homeassistant.helpers.aiohttp_client import ( - async_get_clientsession, async_create_clientsession) + async_get_clientsession, async_create_clientsession, + async_aiohttp_proxy_stream) import homeassistant.helpers.config_validation as cv from homeassistant.util.async import run_coroutine_threadsafe @@ -253,38 +252,10 @@ class SynologyCamera(Camera): 'cameraId': self._camera_id, 'format': 'mjpeg' } - stream = None - response = None - try: - with async_timeout.timeout(TIMEOUT, loop=self.hass.loop): - stream = yield from self._websession.get( - streaming_url, - params=streaming_payload - ) - response = web.StreamResponse() - response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) + stream_coro = self._websession.get( + streaming_url, params=streaming_payload) - yield from response.prepare(request) - - while True: - data = yield from stream.content.read(102400) - if not data: - break - response.write(data) - - except (asyncio.TimeoutError, aiohttp.errors.ClientError): - _LOGGER.exception("Error on %s", streaming_url) - raise HTTPGatewayTimeout() - - except asyncio.CancelledError: - _LOGGER.debug("Close stream by frontend.") - response = None - - finally: - if stream is not None: - stream.close() - if response is not None: - yield from response.write_eof() + yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro) @property def name(self): diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 32e0861ff53..6f74493c078 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -1,9 +1,13 @@ """Helper for aiohttp webclient stuff.""" -import sys import asyncio -import aiohttp +import sys + +import aiohttp +from aiohttp.hdrs import USER_AGENT, CONTENT_TYPE +from aiohttp import web +from aiohttp.web_exceptions import HTTPGatewayTimeout +import async_timeout -from aiohttp.hdrs import USER_AGENT from homeassistant.core import callback from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.const import __version__ @@ -65,6 +69,43 @@ def async_create_clientsession(hass, verify_ssl=True, auto_cleanup=True, return clientsession +@asyncio.coroutine +def async_aiohttp_proxy_stream(hass, request, stream_coro, buffer_size=102400, + timeout=10): + """Stream websession request to aiohttp web response.""" + response = None + stream = None + + try: + with async_timeout.timeout(timeout, loop=hass.loop): + stream = yield from stream_coro + + response = web.StreamResponse() + response.content_type = stream.headers.get(CONTENT_TYPE) + + yield from response.prepare(request) + + while True: + data = yield from stream.content.read(buffer_size) + response.write(data) + + except asyncio.TimeoutError: + raise HTTPGatewayTimeout() + + except (aiohttp.errors.ClientError, + aiohttp.errors.ClientDisconnectedError): + pass + + except (asyncio.CancelledError, ConnectionResetError): + response = None + + finally: + if stream is not None: + stream.close() + if response is not None: + yield from response.write_eof() + + @callback # pylint: disable=invalid-name def _async_register_clientsession_shutdown(hass, clientsession): From 11083cf04b491f6db9a12ad9d3bdce3645f47965 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 19 Jan 2017 19:18:32 +0100 Subject: [PATCH 056/191] Fix lint (#5443) --- homeassistant/components/camera/mjpeg.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 125daba16c4..fa008cd6534 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -9,6 +9,7 @@ import logging from contextlib import closing import aiohttp +import async_timeout import requests from requests.auth import HTTPBasicAuth, HTTPDigestAuth import voluptuous as vol From 3da25c227f89301f569fdb9b7986d4c10a6ff32f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 19 Jan 2017 19:33:15 +0100 Subject: [PATCH 057/191] lint v2 (#5444) --- homeassistant/components/camera/mjpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index fa008cd6534..6501439e24f 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -94,7 +94,7 @@ class MjpegCamera(Camera): response = None try: with async_timeout.timeout(10, loop=self.hass.loop): - response = websession.get( + response = yield from websession.get( self._still_image_url, auth=self._auth) image = yield from response.read() From 6ef9714dc115b8ad6e1e5a205a25d3c3348123eb Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Thu, 19 Jan 2017 19:46:58 +0100 Subject: [PATCH 058/191] Update generic_thermostat.py --- homeassistant/components/climate/generic_thermostat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index 3bf64fab2df..4bf704327d2 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -134,8 +134,8 @@ class GenericThermostat(ClimateDevice): if temperature is None: return self._target_temp = temperature + self.schedule_update_ha_state() self._control_heating() - self.update_ha_state() @property def min_temp(self): @@ -163,8 +163,8 @@ class GenericThermostat(ClimateDevice): return self._update_temp(new_state) - self._control_heating() self.schedule_update_ha_state() + self._control_heating() def _switch_changed(self, entity_id, old_state, new_state): """Called when heater switch changes state.""" From 1a82adb054189fd40314df970da1c6be155545d6 Mon Sep 17 00:00:00 2001 From: David McNett Date: Thu, 19 Jan 2017 13:07:01 -0600 Subject: [PATCH 059/191] New platform media_player/anthemav (#5146) * Initial commit of anthemav platform. It loads but has no purpose. * Now presents a card in the UI but the values aren't real * Mute and volume polling/setting work now * Source lists and selection works now. * Reduce debug logging verbosity * Support power on/off and skip polling for details if power is off * Add some static tables to decode numerics from telnet commands * Add stub for unsupported media_play * New style anthemav uses native asyncio structure * Add device callback for asyncio * This is ugly but it works * Simplify async setup and abstract class data retrieval * Implement commands (power on and power off for now) * Add support for scan_interval and set default to 120 seconds * Pass-through to package handlers for volume and input selection * Slight restructuring to satisfy anthemav 0.9 * Load anthemav package from pypi now that it's registered * Proper app_name from a/v info * Mispelled word * media_player/anthemav initial commit of platform requirements * Philio 3-in-1 Gen 4 zwave sensor needs the no-off-event workaround. (#5120) * Add print_config_parameter service to Z-Wave (#5121) * Add print_config_param service to z-wave * Add print_config_parameter service to z-wave * Add print_config_parameter service to z-wave * Fix typos * Fix typos * Fix typo * Conform to Python/project style requirements * Making pylint happy * Bring pip requirements in agreement with the code * Bungled previous update * Remove unnecessady SCAN_INTERVAL logic I was unawre that this is performed as part of the normal platform behavior and it's unnecessary for a platform to independently implement this logic. * Refactor code based on @armills PR requests * Re-add media_play stub to avoid traceback * Align with platform reqirements * Remove references to SCAN_INTERVAL and clean up _lookup logic * Add DEFAULT_PORT assignment * Code style changes and removal of vestigial structures * CONF_NAME handling changes to allow local override to default from device * Address PR feedback from @balloob * Remove media_play function override It's no longer necesary for the platform to implement a stub media_play function override now that the Add SUPPORT_PLAY flag #5181 issue has been resolved and merged into the dev branch. * Rename callback function to async_ for clarity * Use async routines for platform methods * Convert update callback to coroutine for conformity Underlying anthemav library now properly supports coroutine callbacks instead of normal functions. Converted the platform callback to a coroutine for conformance with async operation for the device. Special thanks to @pvizeli and @armills for their invaluable remedial Python instruction! * Further callback refinements Altered the nature of callback handling based on suggestions from @pvizeli * True not needed for local push update_ha_state * Small style fix --- .coveragerc | 1 + .../components/media_player/anthemav.py | 175 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 179 insertions(+) create mode 100644 homeassistant/components/media_player/anthemav.py diff --git a/.coveragerc b/.coveragerc index 8f67671a5a2..a33538b7e63 100644 --- a/.coveragerc +++ b/.coveragerc @@ -201,6 +201,7 @@ omit = homeassistant/components/light/yeelight.py homeassistant/components/light/zengge.py homeassistant/components/lirc.py + homeassistant/components/media_player/anthemav.py homeassistant/components/media_player/aquostv.py homeassistant/components/media_player/braviatv.py homeassistant/components/media_player/cast.py diff --git a/homeassistant/components/media_player/anthemav.py b/homeassistant/components/media_player/anthemav.py new file mode 100644 index 00000000000..2707a62f7bf --- /dev/null +++ b/homeassistant/components/media_player/anthemav.py @@ -0,0 +1,175 @@ +""" +Support for Anthem Network Receivers and Processors. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.anthemav/ +""" +import logging +import asyncio + +import voluptuous as vol + +from homeassistant.components.media_player import ( + PLATFORM_SCHEMA, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_SELECT_SOURCE, + SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, MediaPlayerDevice) +from homeassistant.const import ( + CONF_NAME, CONF_HOST, CONF_PORT, STATE_OFF, STATE_ON, STATE_UNKNOWN, + EVENT_HOMEASSISTANT_STOP) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['anthemav==1.1.7'] + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = 'anthemav' + +DEFAULT_PORT = 14999 + +SUPPORT_ANTHEMAV = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ + SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + }) + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + """Set up our socket to the AVR.""" + import anthemav + + host = config.get(CONF_HOST) + port = config.get(CONF_PORT) + name = config.get(CONF_NAME) + device = None + + _LOGGER.info('Provisioning Anthem AVR device at %s:%d', host, port) + + def async_anthemav_update_callback(message): + """Receive notification from transport that new data exists.""" + _LOGGER.info('Received update calback from AVR: %s', message) + hass.async_add_job(device.async_update_ha_state()) + + avr = yield from anthemav.Connection.create( + host=host, port=port, loop=hass.loop, + update_callback=async_anthemav_update_callback) + + device = AnthemAVR(avr, name) + + _LOGGER.debug('dump_devicedata: '+device.dump_avrdata) + _LOGGER.debug('dump_conndata: '+avr.dump_conndata) + _LOGGER.debug('dump_rawdata: '+avr.protocol.dump_rawdata) + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.avr.close) + yield from async_add_devices([device]) + + +class AnthemAVR(MediaPlayerDevice): + """Entity reading values from Anthem AVR protocol.""" + + def __init__(self, avr, name): + """"Initialize entity with transport.""" + super().__init__() + self.avr = avr + self._name = name + + def _lookup(self, propname, dval=None): + return getattr(self.avr.protocol, propname, dval) + + @property + def supported_media_commands(self): + """Return flag of media commands that are supported.""" + return SUPPORT_ANTHEMAV + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def name(self): + """Return name of device.""" + return self._name or self._lookup('model') + + @property + def state(self): + """Return state of power on/off.""" + pwrstate = self._lookup('power') + + if pwrstate is True: + return STATE_ON + elif pwrstate is False: + return STATE_OFF + else: + return STATE_UNKNOWN + + @property + def is_volume_muted(self): + """Return boolean reflecting mute state on device.""" + return self._lookup('mute', False) + + @property + def volume_level(self): + """Return volume level from 0 to 1.""" + return self._lookup('volume_as_percentage', 0.0) + + @property + def media_title(self): + """Return current input name (closest we have to media title).""" + return self._lookup('input_name', 'No Source') + + @property + def app_name(self): + """Return details about current video and audio stream.""" + return self._lookup('video_input_resolution_text', '') + ' ' \ + + self._lookup('audio_input_name', '') + + @property + def source(self): + """Return currently selected input.""" + return self._lookup('input_name', "Unknown") + + @property + def source_list(self): + """Return all active, configured inputs.""" + return self._lookup('input_list', ["Unknown"]) + + @asyncio.coroutine + def async_select_source(self, source): + """Change AVR to the designated source (by name).""" + self._update_avr('input_name', source) + + @asyncio.coroutine + def async_turn_off(self): + """Turn AVR power off.""" + self._update_avr('power', False) + + @asyncio.coroutine + def async_turn_on(self): + """Turn AVR power on.""" + self._update_avr('power', True) + + @asyncio.coroutine + def async_set_volume_level(self, volume): + """Set AVR volume (0 to 1).""" + self._update_avr('volume_as_percentage', volume) + + @asyncio.coroutine + def async_mute_volume(self, mute): + """Engage AVR mute.""" + self._update_avr('mute', mute) + + def _update_avr(self, propname, value): + """Update a property in the AVR.""" + _LOGGER.info('Sending command to AVR: set '+propname+' to '+str(value)) + setattr(self.avr.protocol, propname, value) + + @property + def dump_avrdata(self): + """Return state of avr object for debugging forensics.""" + attrs = vars(self) + return( + 'dump_avrdata: ' + + ', '.join('%s: %s' % item for item in attrs.items())) diff --git a/requirements_all.txt b/requirements_all.txt index b8fe4898d70..58339edbbaa 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -40,6 +40,9 @@ aiohttp_cors==0.5.0 # homeassistant.components.sensor.amcrest amcrest==1.1.0 +# homeassistant.components.media_player.anthemav +anthemav==1.1.7 + # homeassistant.components.apcupsd apcaccess==0.0.4 From 3b25b5a6da19cc5a926154496dc7d726c0f4d4a8 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 19 Jan 2017 12:07:37 -0800 Subject: [PATCH 060/191] Add support for Avion Bluetooth dimmer switches (#5414) GE sell a range of Bluetooth dimmer switches based on Avi-on technology. Add a module for controlling them. There's also a set of smart switches that speak the same protocol, but I don't have any of those to test support with. --- .coveragerc | 1 + homeassistant/components/light/avion.py | 117 ++++++++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 121 insertions(+) create mode 100644 homeassistant/components/light/avion.py diff --git a/.coveragerc b/.coveragerc index a33538b7e63..0914e2edde1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -189,6 +189,7 @@ omit = homeassistant/components/joaoapps_join.py homeassistant/components/keyboard.py homeassistant/components/keyboard_remote.py + homeassistant/components/light/avion.py homeassistant/components/light/blinksticklight.py homeassistant/components/light/flux_led.py homeassistant/components/light/hue.py diff --git a/homeassistant/components/light/avion.py b/homeassistant/components/light/avion.py new file mode 100644 index 00000000000..7dded545620 --- /dev/null +++ b/homeassistant/components/light/avion.py @@ -0,0 +1,117 @@ +""" +Support for Avion dimmers. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.avion/ +""" +import logging + +import voluptuous as vol + +from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, + PLATFORM_SCHEMA) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['avion==0.5'] + +_LOGGER = logging.getLogger(__name__) + +SUPPORT_AVION_LED = (SUPPORT_BRIGHTNESS) + +DEVICE_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME): cv.string, + vol.Required(CONF_API_KEY): cv.string, +}) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up an Avion switch.""" + lights = [] + for address, device_config in config[CONF_DEVICES].items(): + device = {} + device['name'] = device_config[CONF_NAME] + device['key'] = device_config[CONF_API_KEY] + device['address'] = address + light = AvionLight(device) + if light.is_valid: + lights.append(light) + + add_devices(lights) + + +class AvionLight(Light): + """Representation of an Avion light.""" + + def __init__(self, device): + """Initialize the light.""" + import avion + + self._name = device['name'] + self._address = device['address'] + self._key = device["key"] + self._brightness = 255 + self._state = False + self._switch = avion.avion(self._address, self._key) + self._switch.connect() + self.is_valid = True + + @property + def unique_id(self): + """Return the ID of this light.""" + return "{}.{}".format(self.__class__, self._address) + + @property + def name(self): + """Return the name of the device if any.""" + return self._name + + @property + def is_on(self): + """Return true if device is on.""" + return self._state + + @property + def brightness(self): + """Return the brightness of this light between 0..255.""" + return self._brightness + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_AVION_LED + + @property + def should_poll(self): + """Don't poll.""" + return False + + @property + def assumed_state(self): + """We can't read the actual state, so assume it matches.""" + return True + + def set_state(self, brightness): + """Set the state of this lamp to the provided brightness.""" + self._switch.set_brightness(brightness) + return True + + def turn_on(self, **kwargs): + """Turn the specified or all lights on.""" + brightness = kwargs.get(ATTR_BRIGHTNESS) + + if brightness is not None: + self._brightness = brightness + + self.set_state(self.brightness) + self._state = True + + def turn_off(self, **kwargs): + """Turn the specified or all lights off.""" + self.set_state(0) + self._state = False diff --git a/requirements_all.txt b/requirements_all.txt index 58339edbbaa..1c806033e2f 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -52,6 +52,9 @@ apns2==0.1.1 # homeassistant.components.sun astral==1.3.3 +# homeassistant.components.light.avion +avion==0.5 + # homeassistant.components.sensor.linux_battery batinfo==0.4.2 From 64c9cd805ab4e9b47e5f2cfe67cac1f33e1f3acd Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Thu, 19 Jan 2017 21:26:12 +0100 Subject: [PATCH 061/191] Update generic_thermostat.py --- homeassistant/components/climate/generic_thermostat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index 4bf704327d2..562847567a3 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -134,8 +134,8 @@ class GenericThermostat(ClimateDevice): if temperature is None: return self._target_temp = temperature - self.schedule_update_ha_state() self._control_heating() + self.schedule_update_ha_state() @property def min_temp(self): @@ -163,8 +163,8 @@ class GenericThermostat(ClimateDevice): return self._update_temp(new_state) - self.schedule_update_ha_state() self._control_heating() + self.schedule_update_ha_state() def _switch_changed(self, entity_id, old_state, new_state): """Called when heater switch changes state.""" From 62b785c0408570de78cfda27012fc21840ef9014 Mon Sep 17 00:00:00 2001 From: Daniel Hoyer Iversen Date: Thu, 19 Jan 2017 22:08:55 +0100 Subject: [PATCH 062/191] update rfxtrx lib --- homeassistant/components/rfxtrx.py | 3 ++- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 56026168383..6918a596988 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -14,7 +14,7 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.helpers.entity import Entity from homeassistant.const import (ATTR_ENTITY_ID, TEMP_CELSIUS) -REQUIREMENTS = ['pyRFXtrx==0.14.0'] +REQUIREMENTS = ['pyRFXtrx==0.15.0'] DOMAIN = "rfxtrx" @@ -34,6 +34,7 @@ EVENT_BUTTON_PRESSED = 'button_pressed' DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELSIUS), + ('Temperature2', TEMP_CELSIUS), ('Humidity', '%'), ('Barometer', ''), ('Wind direction', ''), diff --git a/requirements_all.txt b/requirements_all.txt index 1c806033e2f..eb244ac6080 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -387,7 +387,7 @@ py-cpuinfo==0.2.3 pyHS100==0.2.3 # homeassistant.components.rfxtrx -pyRFXtrx==0.14.0 +pyRFXtrx==0.15.0 # homeassistant.components.notify.xmpp pyasn1-modules==0.0.8 From a74258db09e8d70a07127a1be443f2af1b396a3d Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 19 Jan 2017 13:14:48 -0800 Subject: [PATCH 063/191] Block Avion from auto installing because only supported on Linux (cc #5414) --- requirements_all.txt | 2 +- script/gen_requirements_all.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements_all.txt b/requirements_all.txt index 1c806033e2f..b895ff5cdb7 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -53,7 +53,7 @@ apns2==0.1.1 astral==1.3.3 # homeassistant.components.light.avion -avion==0.5 +# avion==0.5 # homeassistant.components.sensor.linux_battery batinfo==0.4.2 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 81fb17aac17..e23c8d09fea 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -20,6 +20,7 @@ COMMENT_REQUIREMENTS = ( 'evdev', 'pycups', 'python-eq3bt', + 'avion' ) IGNORE_PACKAGES = ( From 738292f8176141250249e9775be0b049fe6db13a Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 19 Jan 2017 13:25:35 -0800 Subject: [PATCH 064/191] Ignore import error on avion --- homeassistant/components/light/avion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/avion.py b/homeassistant/components/light/avion.py index 7dded545620..4a53697ccf4 100644 --- a/homeassistant/components/light/avion.py +++ b/homeassistant/components/light/avion.py @@ -50,6 +50,7 @@ class AvionLight(Light): def __init__(self, device): """Initialize the light.""" + # pylint: disable=import-error import avion self._name = device['name'] From dbcad34b47e4adadf02c6f76d076396074f73dde Mon Sep 17 00:00:00 2001 From: HerrHofrat Date: Thu, 19 Jan 2017 23:33:31 +0100 Subject: [PATCH 065/191] Updated valid station id list (#5449) --- homeassistant/components/sensor/zamg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/zamg.py b/homeassistant/components/sensor/zamg.py index 6bb9dd0748d..6f621b683b6 100644 --- a/homeassistant/components/sensor/zamg.py +++ b/homeassistant/components/sensor/zamg.py @@ -35,7 +35,8 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) VALID_STATION_IDS = ( '11010', '11012', '11022', '11035', '11036', '11101', '11121', '11126', - '11130', '11150', '11155', '11157', '11171', '11190', '11204' + '11130', '11150', '11155', '11157', '11171', '11190', '11204', '11240', + '11244', '11265', '11331', '11343', '11389' ) SENSOR_TYPES = { From 887a33c7d1c1fad01f7cfc22149b488ad35f3006 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 19 Jan 2017 21:27:10 -0800 Subject: [PATCH 066/191] Persist emulated hue IDs (#5435) --- .../components/emulated_hue/__init__.py | 40 +++++++++++++++++-- tests/components/emulated_hue/test_hue_api.py | 2 +- tests/components/emulated_hue/test_init.py | 37 +++++++++++------ 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index d412a7af91f..d95224c9469 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/emulated_hue/ """ import asyncio +import json import logging import voluptuous as vol @@ -24,6 +25,8 @@ DOMAIN = 'emulated_hue' _LOGGER = logging.getLogger(__name__) +NUMBERS_FILE = 'emulated_hue_ids.json' + CONF_HOST_IP = 'host_ip' CONF_LISTEN_PORT = 'listen_port' CONF_UPNP_BIND_MULTICAST = 'upnp_bind_multicast' @@ -63,7 +66,7 @@ ATTR_EMULATED_HUE = 'emulated_hue' def setup(hass, yaml_config): """Activate the emulated_hue component.""" - config = Config(yaml_config.get(DOMAIN, {})) + config = Config(hass, yaml_config.get(DOMAIN, {})) server = HomeAssistantWSGI( hass, @@ -112,10 +115,11 @@ def setup(hass, yaml_config): class Config(object): """Holds configuration variables for the emulated hue bridge.""" - def __init__(self, conf): + def __init__(self, hass, conf): """Initialize the instance.""" + self.hass = hass self.type = conf.get(CONF_TYPE) - self.numbers = {} + self.numbers = None self.cached_states = {} # Get the IP address that will be passed to the Echo during discovery @@ -165,6 +169,9 @@ class Config(object): if self.type == TYPE_ALEXA: return entity_id + if self.numbers is None: + self.numbers = self._load_numbers_json() + # Google Home for number, ent_id in self.numbers.items(): if entity_id == ent_id: @@ -172,6 +179,7 @@ class Config(object): number = str(len(self.numbers) + 1) self.numbers[number] = entity_id + self._save_numbers_json() return number def number_to_entity_id(self, number): @@ -179,6 +187,9 @@ class Config(object): if self.type == TYPE_ALEXA: return number + if self.numbers is None: + self.numbers = self._load_numbers_json() + # Google Home assert isinstance(number, str) return self.numbers.get(number) @@ -205,3 +216,26 @@ class Config(object): domain_exposed_by_default and explicit_expose is not False return is_default_exposed or explicit_expose + + def _load_numbers_json(self): + """Helper method to load numbers json.""" + try: + with open(self.hass.config.path(NUMBERS_FILE), + encoding='utf-8') as fil: + return json.loads(fil.read()) + except (OSError, ValueError) as err: + # OSError if file not found or unaccessible/no permissions + # ValueError if could not parse JSON + if not isinstance(err, FileNotFoundError): + _LOGGER.warning('Failed to open %s: %s', NUMBERS_FILE, err) + return {} + + def _save_numbers_json(self): + """Helper method to save numbers json.""" + try: + with open(self.hass.config.path(NUMBERS_FILE), 'w', + encoding='utf-8') as fil: + fil.write(json.dumps(self.numbers)) + except OSError as err: + # OSError if file write permissions + _LOGGER.warning('Failed to write %s: %s', NUMBERS_FILE, err) diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 0b36b835cd5..7c73e933fd3 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -106,7 +106,7 @@ def hass_hue(loop, hass): def hue_client(loop, hass_hue, test_client): """Create web client for emulated hue api.""" web_app = mock_http_component_app(hass_hue) - config = Config({'type': 'alexa'}) + config = Config(None, {'type': 'alexa'}) HueUsernameView().register(web_app.router) HueAllLightsStateView(config).register(web_app.router) diff --git a/tests/components/emulated_hue/test_init.py b/tests/components/emulated_hue/test_init.py index 2ee7c385d8d..8c0a6dc4f60 100755 --- a/tests/components/emulated_hue/test_init.py +++ b/tests/components/emulated_hue/test_init.py @@ -1,31 +1,44 @@ """Test the Emulated Hue component.""" -from unittest.mock import patch +import json + +from unittest.mock import patch, Mock, mock_open 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({ + conf = Config(Mock(), { 'type': 'google_home' }) - number = conf.entity_id_to_number('light.test') - assert number == '1' + mop = mock_open(read_data=json.dumps({'1': 'light.test2'})) + handle = mop() - number = conf.entity_id_to_number('light.test') - assert number == '1' + with patch('homeassistant.components.emulated_hue.open', mop, create=True): + number = conf.entity_id_to_number('light.test') + assert number == '2' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '1': 'light.test2', + '2': 'light.test', + } - number = conf.entity_id_to_number('light.test2') - assert number == '2' + number = conf.entity_id_to_number('light.test') + assert number == '2' + assert handle.write.call_count == 1 - entity_id = conf.number_to_entity_id('1') - assert entity_id == 'light.test' + number = conf.entity_id_to_number('light.test2') + assert number == '1' + assert handle.write.call_count == 1 + + entity_id = conf.number_to_entity_id('1') + assert entity_id == 'light.test2' def test_config_alexa_entity_id_to_number(): """Test config adheres to the type.""" - conf = Config({ + conf = Config(None, { 'type': 'alexa' }) @@ -45,7 +58,7 @@ def test_config_alexa_entity_id_to_number(): 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({ + Config(None, { 'type': 'google_home', 'host_ip': '123.123.123.123', 'listen_port': 8300 From f17efc216876433ed324aaaa7bbe4a28ce5cb5e4 Mon Sep 17 00:00:00 2001 From: happyleavesaoc Date: Fri, 20 Jan 2017 00:31:44 -0500 Subject: [PATCH 067/191] log formats match (#5456) --- homeassistant/bootstrap.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index da7886ad1e8..0c8b0bc688e 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -508,8 +508,10 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False, Async friendly. """ logging.basicConfig(level=logging.INFO) - fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) " - "[%(name)s] %(message)s%(reset)s") + fmt = ("%(asctime)s %(levelname)s (%(threadName)s) " + "[%(name)s] %(message)s") + colorfmt = "%(log_color)s{}%(reset)s".format(fmt) + datefmt = '%y-%m-%d %H:%M:%S' # suppress overly verbose logs from libraries that aren't helpful logging.getLogger("requests").setLevel(logging.WARNING) @@ -519,8 +521,8 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False, try: from colorlog import ColoredFormatter logging.getLogger().handlers[0].setFormatter(ColoredFormatter( - fmt, - datefmt='%y-%m-%d %H:%M:%S', + colorfmt, + datefmt=datefmt, reset=True, log_colors={ 'DEBUG': 'cyan', @@ -554,9 +556,7 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False, err_log_path, mode='w', delay=True) err_handler.setLevel(logging.INFO if verbose else logging.WARNING) - err_handler.setFormatter( - logging.Formatter('%(asctime)s %(name)s: %(message)s', - datefmt='%y-%m-%d %H:%M:%S')) + err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt)) async_handler = AsyncHandler(hass.loop, err_handler) hass.data[core.DATA_ASYNCHANDLER] = async_handler From fe6a8f3367e520da7921911179f87c880bb0c92c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jan 2017 07:21:25 +0100 Subject: [PATCH 068/191] Remove old openalpr component (#5406) * Remove old openalpr component * update region support --- .coveragerc | 1 - .../image_processing/openalpr_cloud.py | 8 +- .../image_processing/openalpr_local.py | 9 +- homeassistant/components/openalpr.py | 472 ------------------ requirements_all.txt | 6 - 5 files changed, 13 insertions(+), 483 deletions(-) delete mode 100644 homeassistant/components/openalpr.py diff --git a/.coveragerc b/.coveragerc index 0914e2edde1..5bd097e8abb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -266,7 +266,6 @@ omit = homeassistant/components/notify/twitter.py homeassistant/components/notify/xmpp.py homeassistant/components/nuimo_controller.py - homeassistant/components/openalpr.py homeassistant/components/remote/harmony.py homeassistant/components/scene/hunterdouglas_powerview.py homeassistant/components/sensor/amcrest.py diff --git a/homeassistant/components/image_processing/openalpr_cloud.py b/homeassistant/components/image_processing/openalpr_cloud.py index 61b3442856a..d17291df07f 100644 --- a/homeassistant/components/image_processing/openalpr_cloud.py +++ b/homeassistant/components/image_processing/openalpr_cloud.py @@ -26,14 +26,18 @@ _LOGGER = logging.getLogger(__name__) OPENALPR_API_URL = "https://api.openalpr.com/v1/recognize" OPENALPR_REGIONS = [ - 'us', - 'eu', 'au', 'auwide', + 'br', + 'eu', + 'fr', 'gb', 'kr', + 'kr2', 'mx', 'sg', + 'us', + 'vn2' ] CONF_REGION = 'region' diff --git a/homeassistant/components/image_processing/openalpr_local.py b/homeassistant/components/image_processing/openalpr_local.py index a1736c00ffc..65c2a683341 100644 --- a/homeassistant/components/image_processing/openalpr_local.py +++ b/homeassistant/components/image_processing/openalpr_local.py @@ -31,16 +31,21 @@ ATTR_PLATES = 'plates' ATTR_VEHICLES = 'vehicles' OPENALPR_REGIONS = [ - 'us', - 'eu', 'au', 'auwide', + 'br', + 'eu', + 'fr', 'gb', 'kr', + 'kr2', 'mx', 'sg', + 'us', + 'vn2' ] + CONF_REGION = 'region' CONF_ALPR_BIN = 'alp_bin' diff --git a/homeassistant/components/openalpr.py b/homeassistant/components/openalpr.py deleted file mode 100644 index eaaba5f8af8..00000000000 --- a/homeassistant/components/openalpr.py +++ /dev/null @@ -1,472 +0,0 @@ -""" -Component that will help set the openalpr for video streams. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/openalpr/ -""" -from base64 import b64encode -import logging -import os -from time import time - -import requests -import voluptuous as vol - -from homeassistant.config import load_yaml_config_file -from homeassistant.const import ( - CONF_API_KEY, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, ATTR_ENTITY_ID, - EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN) -from homeassistant.components.ffmpeg import ( - get_binary, run_test, CONF_INPUT, CONF_EXTRA_ARGUMENTS) -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.entity_component import EntityComponent - -DOMAIN = 'openalpr' -DEPENDENCIES = ['ffmpeg'] -REQUIREMENTS = [ - 'https://github.com/pvizeli/cloudapi/releases/download/1.0.2/' - 'python-1.0.2.zip#openalpr_api==1.0.2', - 'ha-alpr==0.3'] - -_LOGGER = logging.getLogger(__name__) - -SERVICE_SCAN = 'scan' -SERVICE_RESTART = 'restart' - -EVENT_FOUND = 'openalpr.found' - -ATTR_PLATE = 'plate' - - -ENGINE_LOCAL = 'local' -ENGINE_CLOUD = 'cloud' - -RENDER_IMAGE = 'image' -RENDER_FFMPEG = 'ffmpeg' - -OPENALPR_REGIONS = [ - 'us', - 'eu', - 'au', - 'auwide', - 'gb', - 'kr', - 'mx', - 'sg', -] - -CONF_RENDER = 'render' -CONF_ENGINE = 'engine' -CONF_REGION = 'region' -CONF_INTERVAL = 'interval' -CONF_ENTITIES = 'entities' -CONF_CONFIDENCE = 'confidence' -CONF_ALPR_BINARY = 'alpr_binary' - -DEFAULT_NAME = 'OpenAlpr' -DEFAULT_ENGINE = ENGINE_LOCAL -DEFAULT_RENDER = RENDER_FFMPEG -DEFAULT_BINARY = 'alpr' -DEFAULT_INTERVAL = 10 -DEFAULT_CONFIDENCE = 80.0 - -DEVICE_SCHEMA = vol.Schema({ - vol.Required(CONF_INPUT): cv.string, - vol.Optional(CONF_INTERVAL, default=DEFAULT_INTERVAL): cv.positive_int, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_RENDER, default=DEFAULT_RENDER): - vol.In([RENDER_IMAGE, RENDER_FFMPEG]), - vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string, - vol.Optional(CONF_USERNAME): cv.string, - vol.Optional(CONF_PASSWORD): cv.string, -}) - -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_ENGINE): vol.In([ENGINE_LOCAL, ENGINE_CLOUD]), - vol.Required(CONF_REGION): vol.In(OPENALPR_REGIONS), - vol.Optional(CONF_CONFIDENCE, default=DEFAULT_CONFIDENCE): - vol.Coerce(float), - vol.Optional(CONF_API_KEY): cv.string, - vol.Optional(CONF_ALPR_BINARY, default=DEFAULT_BINARY): cv.string, - vol.Required(CONF_ENTITIES): - vol.All(cv.ensure_list, [DEVICE_SCHEMA]), - }) -}, extra=vol.ALLOW_EXTRA) - - -SERVICE_RESTART_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, -}) - -SERVICE_SCAN_SCHEMA = vol.Schema({ - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, -}) - - -def scan(hass, entity_id=None): - """Scan a image immediately.""" - data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_SCAN, data) - - -def restart(hass, entity_id=None): - """Restart a ffmpeg process.""" - data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_RESTART, data) - - -def setup(hass, config): - """Setup the OpenAlpr component.""" - engine = config[DOMAIN].get(CONF_ENGINE) - region = config[DOMAIN].get(CONF_REGION) - confidence = config[DOMAIN].get(CONF_CONFIDENCE) - api_key = config[DOMAIN].get(CONF_API_KEY) - binary = config[DOMAIN].get(CONF_ALPR_BINARY) - use_render_fffmpeg = False - - _LOGGER.warning("This platform is replaced by 'image_processing' and will " - "be removed in a future version!") - - component = EntityComponent(_LOGGER, DOMAIN, hass) - openalpr_device = [] - - for device in config[DOMAIN].get(CONF_ENTITIES): - input_source = device.get(CONF_INPUT) - render = device.get(CONF_RENDER) - - ## - # create api - if engine == ENGINE_LOCAL: - alpr_api = OpenalprApiLocal( - confidence=confidence, - region=region, - binary=binary, - ) - else: - alpr_api = OpenalprApiCloud( - confidence=confidence, - region=region, - api_key=api_key, - ) - - ## - # Create Alpr device / render engine - if render == RENDER_FFMPEG: - use_render_fffmpeg = True - if not run_test(hass, input_source): - _LOGGER.error("'%s' is not valid ffmpeg input", input_source) - continue - - alpr_dev = OpenalprDeviceFFmpeg( - name=device.get(CONF_NAME), - interval=device.get(CONF_INTERVAL), - api=alpr_api, - input_source=input_source, - extra_arguments=device.get(CONF_EXTRA_ARGUMENTS), - ) - else: - alpr_dev = OpenalprDeviceImage( - name=device.get(CONF_NAME), - interval=device.get(CONF_INTERVAL), - api=alpr_api, - input_source=input_source, - username=device.get(CONF_USERNAME), - password=device.get(CONF_PASSWORD), - ) - - # register shutdown event - openalpr_device.append(alpr_dev) - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, alpr_dev.shutdown) - - component.add_entities(openalpr_device) - - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) - - def _handle_service_scan(service): - """Handle service for immediately scan.""" - device_list = component.extract_from_service(service) - - for device in device_list: - device.scan() - - hass.services.register(DOMAIN, SERVICE_SCAN, - _handle_service_scan, - descriptions[DOMAIN][SERVICE_SCAN], - schema=SERVICE_SCAN_SCHEMA) - - # Add restart service only if a device use ffmpeg as render - if not use_render_fffmpeg: - return True - - def _handle_service_restart(service): - """Handle service for restart ffmpeg process.""" - device_list = component.extract_from_service(service) - - for device in device_list: - device.restart() - - hass.services.register(DOMAIN, SERVICE_RESTART, - _handle_service_restart, - descriptions[DOMAIN][SERVICE_RESTART], - schema=SERVICE_RESTART_SCHEMA) - - return True - - -class OpenalprDevice(Entity): - """Represent a openalpr device object for processing stream/images.""" - - def __init__(self, name, interval, api): - """Init image processing.""" - self._name = name - self._interval = interval - self._api = api - self._last = {} - - @property - def state(self): - """Return the state of the entity.""" - confidence = 0 - plate = STATE_UNKNOWN - - # search high plate - for i_pl, i_co in self._last.items(): - if i_co > confidence: - confidence = i_co - plate = i_pl - return plate - - def shutdown(self, event): - """Close stream.""" - if hasattr(self._api, "shutdown"): - self._api.shutdown(event) - - def restart(self): - """Restart stream.""" - raise NotImplementedError() - - def _process_image(self, image): - """Callback for processing image.""" - self._api.process_image(image, self._process_event) - - def _process_event(self, plates): - """Send event with new plates.""" - state_change = False - plates_set = set(plates) - last_set = set(self._last) - new_plates = plates_set - last_set - - # send events - for i_plate in new_plates: - self.hass.bus.fire(EVENT_FOUND, { - ATTR_PLATE: i_plate, - ATTR_ENTITY_ID: self.entity_id - }) - - # update entity store - if last_set <= plates_set: - state_change = True - self._last = plates - - # update HA state - if state_change: - self.update_ha_state() - - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - return {'plates': self._last} - - def scan(self): - """Immediately scan a image.""" - raise NotImplementedError() - - @property - def name(self): - """Return the name of the entity.""" - return self._name - - -class OpenalprDeviceFFmpeg(OpenalprDevice): - """Represent a openalpr device object for processing stream/images.""" - - def __init__(self, name, interval, api, input_source, - extra_arguments=None): - """Init image processing.""" - from haffmpeg import ImageStream, ImageSingle - - super().__init__(name, interval, api) - self._input_source = input_source - self._extra_arguments = extra_arguments - - if self._interval > 0: - self._ffmpeg = ImageStream(get_binary(), self._process_image) - else: - self._ffmpeg = ImageSingle(get_binary()) - - self._start_ffmpeg() - - def shutdown(self, event): - """Close ffmpeg stream.""" - if self._interval > 0: - self._ffmpeg.close() - - def restart(self): - """Restart ffmpeg stream.""" - if self._interval > 0: - self._ffmpeg.close() - self._start_ffmpeg() - - def scan(self): - """Immediately scan a image.""" - from haffmpeg import IMAGE_PNG - - # process single image - if self._interval == 0: - image = self._ffmpeg.get_image( - self._input_source, - output_format=IMAGE_PNG, - extra_cmd=self._extra_arguments - ) - return self._process_image(image) - - # stream - self._ffmpeg.push_image() - - def _start_ffmpeg(self): - """Start a ffmpeg image stream.""" - from haffmpeg import IMAGE_PNG - if self._interval == 0: - return - - self._ffmpeg.open_stream( - input_source=self._input_source, - interval=self._interval, - output_format=IMAGE_PNG, - extra_cmd=self._extra_arguments, - ) - - @property - def should_poll(self): - """Return True if render is be 'image' or False if 'ffmpeg'.""" - return False - - @property - def available(self): - """Return True if entity is available.""" - return self._interval == 0 or self._ffmpeg.is_running - - -class OpenalprDeviceImage(OpenalprDevice): - """Represent a openalpr device object for processing stream/images.""" - - def __init__(self, name, interval, api, input_source, - username=None, password=None): - """Init image processing.""" - super().__init__(name, interval, api) - - self._next = time() - self._username = username - self._password = password - self._url = input_source - - def restart(self): - """Fake restart with scan a picture.""" - self.scan() - - def scan(self): - """Immediately scan a image.""" - # send request - if self._username is not None and self._password is not None: - req = requests.get( - self._url, auth=(self._username, self._password), timeout=15) - else: - req = requests.get(self._url, timeout=15) - - # process image - image = req.content - self._process_image(image) - - @property - def should_poll(self): - """Return True if render is be 'image' or False if 'ffmpeg'.""" - return self._interval > 0 - - def update(self): - """Retrieve latest state.""" - if self._next > time(): - return - self.scan() - self._next = time() + self._interval - - -class OpenalprApi(object): - """OpenAlpr api class.""" - - def __init__(self, region, confidence): - """Init basic api processing.""" - self._region = region - self._confidence = confidence - - def process_image(self, image, event_callback): - """Callback for processing image.""" - raise NotImplementedError() - - -class OpenalprApiCloud(OpenalprApi): - """Use the cloud openalpr api to parse licences plate.""" - - def __init__(self, region, confidence, api_key): - """Init cloud api processing.""" - import openalpr_api - - super().__init__(region=region, confidence=confidence) - self._api = openalpr_api.DefaultApi() - self._api_key = api_key - - def process_image(self, image, event_callback): - """Callback for processing image.""" - result = self._api.recognize_post( - self._api_key, - 'plate', - image="", - image_bytes=str(b64encode(image), 'utf-8'), - country=self._region - ) - - # process result - f_plates = {} - # pylint: disable=no-member - for object_plate in result.plate.results: - plate = object_plate.plate - confidence = object_plate.confidence - if confidence >= self._confidence: - f_plates[plate] = confidence - event_callback(f_plates) - - -class OpenalprApiLocal(OpenalprApi): - """Use local openalpr library to parse licences plate.""" - - def __init__(self, region, confidence, binary): - """Init local api processing.""" - # pylint: disable=import-error - from haalpr import HAAlpr - - super().__init__(region=region, confidence=confidence) - self._api = HAAlpr(binary=binary, country=region) - - def process_image(self, image, event_callback): - """Callback for processing image.""" - result = self._api.recognize_byte(image) - - # process result - f_plates = {} - for found in result: - for plate, confidence in found.items(): - if confidence >= self._confidence: - f_plates[plate] = confidence - event_callback(f_plates) diff --git a/requirements_all.txt b/requirements_all.txt index 4ef334dc632..ec328eca44d 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -169,9 +169,6 @@ googlemaps==2.4.4 # homeassistant.components.sensor.gpsd gps3==0.33.3 -# homeassistant.components.openalpr -ha-alpr==0.3 - # homeassistant.components.ffmpeg ha-ffmpeg==0.15 @@ -244,9 +241,6 @@ https://github.com/nkgilley/python-ecobee-api/archive/4856a704670c53afe1882178a8 # homeassistant.components.notify.joaoapps_join https://github.com/nkgilley/python-join-api/archive/3e1e849f1af0b4080f551b62270c6d244d5fbcbd.zip#python-join-api==0.0.1 -# homeassistant.components.openalpr -https://github.com/pvizeli/cloudapi/releases/download/1.0.2/python-1.0.2.zip#openalpr_api==1.0.2 - # homeassistant.components.switch.edimax https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f3700.zip#pyedimax==0.1 From 5dd45efac3a1774f39d717cb79b268b3395049da Mon Sep 17 00:00:00 2001 From: Ryan Kraus Date: Fri, 20 Jan 2017 01:22:33 -0500 Subject: [PATCH 069/191] Updated ISY component to not overwrite state_attributes. (#5433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated ISY component to not overwrite state_attributes. The ISY component included an ISYDevice base class that is used by all of the isy994 platforms. This still overwrote the state_attributes property instead of the more appropriate device_state_attributes property. This was also repeated in the isy994 light platform. Both of these were addressed. This also fixes issue #5428. * Removed custom state attributes from ISY lights. The brightness attribute need not be manually reported by the isy994 light platform. * Removed ISY Node cleanup. The ISY entities don’t really need to unsubscribe themselves while hass is shutting down. Because these updates are not sent in a thread, there is no negative impact from shutting down without unsubscribing. This greatly speeds up hass shutdown. * Removed unused attribute from isy994 light platform. * Cleaned up ISY994 light entity class. 1) Removed the state property. This property is set in the Entity base class and shouldn’t be overridden here. 2) Set the brightness property. This is the proper way of setting the brightness for the Light base class. 3) Removed properties that are now unused because of these changes. --- homeassistant/components/isy994.py | 6 +----- homeassistant/components/light/isy994.py | 22 ++++++---------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/isy994.py b/homeassistant/components/isy994.py index 7451b3286f7..cbe7c7166e7 100644 --- a/homeassistant/components/isy994.py +++ b/homeassistant/components/isy994.py @@ -228,10 +228,6 @@ class ISYDevice(Entity): self._change_handler = self._node.status.subscribe('changed', self.on_update) - def __del__(self) -> None: - """Cleanup the subscriptions.""" - self._change_handler.unsubscribe() - # pylint: disable=unused-argument def on_update(self, event: object) -> None: """Handle the update event from the ISY994 Node.""" @@ -272,7 +268,7 @@ class ISYDevice(Entity): return self._node.status._val @property - def state_attributes(self) -> Dict: + def device_state_attributes(self) -> Dict: """Get the state attributes for the device.""" attr = {} if hasattr(self._node, 'aux_properties'): diff --git a/homeassistant/components/light/isy994.py b/homeassistant/components/light/isy994.py index 952c52b2809..1cde50de820 100644 --- a/homeassistant/components/light/isy994.py +++ b/homeassistant/components/light/isy994.py @@ -8,18 +8,13 @@ import logging from typing import Callable from homeassistant.components.light import ( - Light, SUPPORT_BRIGHTNESS, ATTR_BRIGHTNESS) + Light, SUPPORT_BRIGHTNESS) import homeassistant.components.isy994 as isy -from homeassistant.const import STATE_ON, STATE_OFF, STATE_UNKNOWN +from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) -VALUE_TO_STATE = { - False: STATE_OFF, - True: STATE_ON, -} - UOM = ['2', '51', '78'] STATES = [STATE_OFF, STATE_ON, 'true', 'false', '%'] @@ -52,12 +47,12 @@ class ISYLightDevice(isy.ISYDevice, Light): @property def is_on(self) -> bool: """Get whether the ISY994 light is on.""" - return self.state == STATE_ON + return self.value > 0 @property - def state(self) -> str: - """Get the state of the ISY994 light.""" - return VALUE_TO_STATE.get(bool(self.value), STATE_UNKNOWN) + def brightness(self) -> float: + """Get the brightness of the ISY994 light.""" + return self.value def turn_off(self, **kwargs) -> None: """Send the turn off command to the ISY994 light device.""" @@ -69,11 +64,6 @@ class ISYLightDevice(isy.ISYDevice, Light): if not self._node.on(val=brightness): _LOGGER.debug('Unable to turn on light.') - @property - def state_attributes(self): - """Flag supported attributes.""" - return {ATTR_BRIGHTNESS: self.value} - @property def supported_features(self): """Flag supported features.""" From 1f6f9a1677ea2abbe7bc9913f4b5ee16a16c750e Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Fri, 20 Jan 2017 02:30:47 -0500 Subject: [PATCH 070/191] Filter new entities from logbook (#5402) --- homeassistant/components/logbook.py | 4 +++ tests/components/test_logbook.py | 44 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/homeassistant/components/logbook.py b/homeassistant/components/logbook.py index 94445935093..b69289db989 100644 --- a/homeassistant/components/logbook.py +++ b/homeassistant/components/logbook.py @@ -307,6 +307,10 @@ def _exclude_events(events, config): if event.event_type == EVENT_STATE_CHANGED: to_state = State.from_dict(event.data.get('new_state')) # Do not report on new entities + if event.data.get('old_state') is None: + continue + + # Do not report on entity removal if not to_state: continue diff --git a/tests/components/test_logbook.py b/tests/components/test_logbook.py index dcb675e00e5..047ca480f6f 100644 --- a/tests/components/test_logbook.py +++ b/tests/components/test_logbook.py @@ -117,6 +117,50 @@ class TestComponentLogbook(unittest.TestCase): self.assertEqual(0, len(entries)) + def test_exclude_new_entities(self): + """Test if events are excluded on first update.""" + entity_id = 'sensor.bla' + entity_id2 = 'sensor.blu' + pointA = dt_util.utcnow() + pointB = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES) + + eventA = self.create_state_changed_event(pointA, entity_id, 10) + eventB = self.create_state_changed_event(pointB, entity_id2, 20) + eventA.data['old_state'] = None + + events = logbook._exclude_events((ha.Event(EVENT_HOMEASSISTANT_STOP), + eventA, eventB), self.EMPTY_CONFIG) + entries = list(logbook.humanify(events)) + + self.assertEqual(2, len(entries)) + self.assert_entry( + entries[0], name='Home Assistant', message='stopped', + domain=ha.DOMAIN) + self.assert_entry( + entries[1], pointB, 'blu', domain='sensor', entity_id=entity_id2) + + def test_exclude_removed_entities(self): + """Test if events are excluded on last update.""" + entity_id = 'sensor.bla' + entity_id2 = 'sensor.blu' + pointA = dt_util.utcnow() + pointB = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES) + + eventA = self.create_state_changed_event(pointA, entity_id, 10) + eventB = self.create_state_changed_event(pointB, entity_id2, 20) + eventA.data['new_state'] = None + + events = logbook._exclude_events((ha.Event(EVENT_HOMEASSISTANT_STOP), + eventA, eventB), self.EMPTY_CONFIG) + entries = list(logbook.humanify(events)) + + self.assertEqual(2, len(entries)) + self.assert_entry( + entries[0], name='Home Assistant', message='stopped', + domain=ha.DOMAIN) + self.assert_entry( + entries[1], pointB, 'blu', domain='sensor', entity_id=entity_id2) + def test_exclude_events_hidden(self): """Test if events are excluded if entity is hidden.""" entity_id = 'sensor.bla' From 2ed0e76e7cb4ef963a18351300f479d82abb37c2 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Jan 2017 08:55:29 +0100 Subject: [PATCH 071/191] Add elevation to as_dict and use unified style for quoting (#5448) --- homeassistant/core.py | 49 +++++++++++++++++++++---------------------- tests/test_core.py | 1 + 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 7fd6006f916..e2831a63d75 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -43,7 +43,7 @@ try: except ImportError: pass -DOMAIN = "homeassistant" +DOMAIN = 'homeassistant' # How often time_changed event should fire TIMER_INTERVAL = 1 # seconds @@ -88,10 +88,10 @@ def is_callback(func: Callable[..., Any]) -> bool: class CoreState(enum.Enum): """Represent the current state of Home Assistant.""" - not_running = "NOT_RUNNING" - starting = "STARTING" - running = "RUNNING" - stopping = "STOPPING" + not_running = 'NOT_RUNNING' + starting = 'STARTING' + running = 'RUNNING' + stopping = 'STOPPING' def __str__(self) -> str: """Return the event.""" @@ -103,7 +103,7 @@ class HomeAssistant(object): def __init__(self, loop=None): """Initialize new Home Assistant object.""" - if sys.platform == "win32": + if sys.platform == 'win32': self.loop = loop or asyncio.ProactorEventLoop() else: self.loop = loop or asyncio.get_event_loop() @@ -164,13 +164,13 @@ class HomeAssistant(object): self.loop.add_signal_handler( signal.SIGTERM, self._async_stop_handler) except ValueError: - _LOGGER.warning('Could not bind to SIGTERM.') + _LOGGER.warning("Could not bind to SIGTERM") try: self.loop.add_signal_handler( signal.SIGHUP, self._async_restart_handler) except ValueError: - _LOGGER.warning('Could not bind to SIGHUP.') + _LOGGER.warning("Could not bind to SIGHUP") # pylint: disable=protected-access self.loop._thread_ident = threading.get_ident() @@ -185,7 +185,7 @@ class HomeAssistant(object): args: parameters for method to call. """ if target is None: - raise ValueError("Don't call add_job with None.") + raise ValueError("Don't call add_job with None") self.loop.call_soon_threadsafe(self.async_add_job, target, *args) @callback @@ -322,8 +322,7 @@ class HomeAssistant(object): kwargs['exc_info'] = (type(exception), exception, exception.__traceback__) - _LOGGER.error('Error doing job: %s', context['message'], - **kwargs) + _LOGGER.error("Error doing job: %s", context['message'], **kwargs) @callback def _async_stop_handler(self, *args): @@ -341,8 +340,8 @@ class HomeAssistant(object): class EventOrigin(enum.Enum): """Represent the origin of an event.""" - local = "LOCAL" - remote = "REMOTE" + local = 'LOCAL' + remote = 'REMOTE' def __str__(self): """Return the event.""" @@ -420,8 +419,8 @@ class EventBus(object): def fire(self, event_type: str, event_data=None, origin=EventOrigin.local): """Fire an event.""" - self._hass.loop.call_soon_threadsafe(self.async_fire, event_type, - event_data, origin) + self._hass.loop.call_soon_threadsafe( + self.async_fire, event_type, event_data, origin) @callback def async_fire(self, event_type: str, event_data=None, @@ -432,7 +431,7 @@ class EventBus(object): """ if event_type != EVENT_HOMEASSISTANT_STOP and \ self._hass.state == CoreState.stopping: - raise ShuttingDown('Home Assistant is shutting down.') + raise ShuttingDown("Home Assistant is shutting down") # Copy the list of the current listeners because some listeners # remove themselves as a listener while being executed which @@ -549,8 +548,7 @@ class EventBus(object): except (KeyError, ValueError): # KeyError is key event_type listener did not exist # ValueError if listener did not exist within event_type - _LOGGER.warning('Unable to remove unknown listener %s', - listener) + _LOGGER.warning("Unable to remove unknown listener %s", listener) class State(object): @@ -995,14 +993,14 @@ class ServiceRegistry(object): if event.data[ATTR_SERVICE_CALL_ID] == call_id: fut.set_result(True) - unsub = self._hass.bus.async_listen(EVENT_SERVICE_EXECUTED, - service_executed) + unsub = self._hass.bus.async_listen( + EVENT_SERVICE_EXECUTED, service_executed) self._hass.bus.async_fire(EVENT_CALL_SERVICE, event_data) if blocking: - done, _ = yield from asyncio.wait([fut], loop=self._hass.loop, - timeout=SERVICE_CALL_LIMIT) + done, _ = yield from asyncio.wait( + [fut], loop=self._hass.loop, timeout=SERVICE_CALL_LIMIT) success = bool(done) unsub() return success @@ -1017,7 +1015,7 @@ class ServiceRegistry(object): if not self.has_service(domain, service): if event.origin == EventOrigin.local: - _LOGGER.warning('Unable to find service %s/%s', + _LOGGER.warning("Unable to find service %s/%s", domain, service) return @@ -1040,7 +1038,7 @@ class ServiceRegistry(object): if service_handler.schema: service_data = service_handler.schema(service_data) except vol.Invalid as ex: - _LOGGER.error('Invalid service data for %s.%s: %s', + _LOGGER.error("Invalid service data for %s.%s: %s", domain, service, humanize_error(service_data, ex)) fire_service_executed() return @@ -1064,7 +1062,7 @@ class ServiceRegistry(object): def _generate_unique_id(self): """Generate a unique service call id.""" self._cur_id += 1 - return "{}-{}".format(id(self), self._cur_id) + return '{}-{}'.format(id(self), self._cur_id) class Config(object): @@ -1118,6 +1116,7 @@ class Config(object): return { 'latitude': self.latitude, 'longitude': self.longitude, + 'elevation': self.elevation, 'unit_system': self.units.as_dict(), 'location_name': self.location_name, 'time_zone': time_zone.zone, diff --git a/tests/test_core.py b/tests/test_core.py index 4049d10d32d..14276584ae2 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -724,6 +724,7 @@ class TestConfig(unittest.TestCase): expected = { 'latitude': None, 'longitude': None, + 'elevation': None, CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(), 'location_name': None, 'time_zone': 'UTC', From f669680b1e393490cbf6c6567e646aac15c73858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Fri, 20 Jan 2017 08:58:15 +0100 Subject: [PATCH 072/191] fix issue ##5398 in yr sensor (#5459) --- homeassistant/components/sensor/yr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/sensor/yr.py b/homeassistant/components/sensor/yr.py index 7da72b6fd38..96b67776000 100644 --- a/homeassistant/components/sensor/yr.py +++ b/homeassistant/components/sensor/yr.py @@ -201,6 +201,7 @@ class YrData(object): for time_entry in self.data['product']['time']: valid_from = dt_util.parse_datetime(time_entry['@from']) valid_to = dt_util.parse_datetime(time_entry['@to']) + new_state = None loc_data = time_entry['location'] From 8496975de887226ab5cf37c35aea8e661dc830da Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jan 2017 09:07:03 +0100 Subject: [PATCH 073/191] Fix if none data is present for a sensor. (#5415) --- homeassistant/components/sensor/netatmo.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/netatmo.py b/homeassistant/components/sensor/netatmo.py index 20c0f94a500..41fc4287f5f 100644 --- a/homeassistant/components/sensor/netatmo.py +++ b/homeassistant/components/sensor/netatmo.py @@ -10,7 +10,7 @@ from datetime import timedelta import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle from homeassistant.loader import get_component @@ -142,7 +142,12 @@ class NetAtmoSensor(Entity): def update(self): """Get the latest data from NetAtmo API and updates the states.""" self.netatmo_data.update() - data = self.netatmo_data.data[self.module_name] + data = self.netatmo_data.data.get(self.module_name) + + if data is None: + _LOGGER.warning("No data found for %s", self.module_name) + self._state = STATE_UNKNOWN + return if self.type == 'temperature': self._state = round(data['Temperature'], 1) From c41cf7c3083bb4e05adee0c11d9f5e9f9a4fd7f9 Mon Sep 17 00:00:00 2001 From: John Arild Berentsen Date: Fri, 20 Jan 2017 13:35:41 +0100 Subject: [PATCH 074/191] Bugfix Zwave Light: Use only supported features for devices (#5370) * Use only supported features for devices * Changes * Holy Macarony! --- homeassistant/components/light/zwave.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index d973f8d8dd2..754c27cbad3 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -42,7 +42,10 @@ TEMP_MID_HASS = (HASS_COLOR_MAX - HASS_COLOR_MIN) / 2 + HASS_COLOR_MIN TEMP_WARM_HASS = (HASS_COLOR_MAX - HASS_COLOR_MIN) / 3 * 2 + HASS_COLOR_MIN TEMP_COLD_HASS = (HASS_COLOR_MAX - HASS_COLOR_MIN) / 3 + HASS_COLOR_MIN -SUPPORT_ZWAVE = SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_RGB_COLOR +SUPPORT_ZWAVE_DIMMER = SUPPORT_BRIGHTNESS +SUPPORT_ZWAVE_COLOR = SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR +SUPPORT_ZWAVE_COLORTEMP = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR + | SUPPORT_COLOR_TEMP) def setup_platform(hass, config, add_devices, discovery_info=None): @@ -161,7 +164,7 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light): @property def supported_features(self): """Flag supported features.""" - return SUPPORT_ZWAVE + return SUPPORT_ZWAVE_DIMMER def turn_on(self, **kwargs): """Turn the device on.""" @@ -351,3 +354,11 @@ class ZwaveColorLight(ZwaveDimmer): self._value_color.node.set_rgbw(self._value_color.value_id, rgbw) super().turn_on(**kwargs) + + @property + def supported_features(self): + """Flag supported features.""" + if self._zw098: + return SUPPORT_ZWAVE_COLORTEMP + else: + return SUPPORT_ZWAVE_COLOR From a7e5c847fb1afb20d0c3698ca6e92beb864b0444 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Fri, 20 Jan 2017 14:52:55 -0500 Subject: [PATCH 075/191] Kodi supports volume stepping (#5467) --- homeassistant/components/media_player/kodi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index 8cfa7a587fb..790bfb3c724 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -14,7 +14,8 @@ import voluptuous as vol from homeassistant.components.media_player import ( SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_PLAY_MEDIA, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_STOP, - SUPPORT_TURN_OFF, SUPPORT_PLAY, MediaPlayerDevice, PLATFORM_SCHEMA) + SUPPORT_TURN_OFF, SUPPORT_PLAY, SUPPORT_VOLUME_STEP, MediaPlayerDevice, + PLATFORM_SCHEMA) from homeassistant.const import ( STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, CONF_HOST, CONF_NAME, CONF_PORT, CONF_USERNAME, CONF_PASSWORD) @@ -35,7 +36,7 @@ TURN_OFF_ACTION = [None, 'quit', 'hibernate', 'suspend', 'reboot', 'shutdown'] SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK | \ - SUPPORT_PLAY_MEDIA | SUPPORT_STOP | SUPPORT_PLAY + SUPPORT_PLAY_MEDIA | SUPPORT_STOP | SUPPORT_PLAY | SUPPORT_VOLUME_STEP PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, From 5ff9dfa44022bdb8e71977d4cffef9d4b65f949b Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Fri, 20 Jan 2017 15:21:27 -0500 Subject: [PATCH 076/191] Use voluptuous for cast ignore-cec (#5468) --- homeassistant/components/media_player/cast.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index faa204e675e..202c877c2b1 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -37,6 +37,7 @@ KNOWN_HOSTS = [] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_IGNORE_CEC): [cv.string], }) @@ -46,11 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): import pychromecast # import CEC IGNORE attributes - ignore_cec = config.get(CONF_IGNORE_CEC, []) - if isinstance(ignore_cec, list): - pychromecast.IGNORE_CEC += ignore_cec - else: - _LOGGER.error('CEC config "%s" must be a list.', CONF_IGNORE_CEC) + pychromecast.IGNORE_CEC += config.get(CONF_IGNORE_CEC, []) hosts = [] From cb47d16282809afab69ff2f16cdcdbec7923d3a8 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Fri, 20 Jan 2017 15:30:09 -0500 Subject: [PATCH 077/191] Don't use Debian's httpredir for backports (#5392) Hopefully this solves https://github.com/home-assistant/home-assistant/pull/5322#issuecomment-273041585 --- script/setup_docker_prereqs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/setup_docker_prereqs b/script/setup_docker_prereqs index f0c6ddf4cc5..a482d9a0ec7 100755 --- a/script/setup_docker_prereqs +++ b/script/setup_docker_prereqs @@ -35,7 +35,7 @@ echo "deb http://download.telldus.com/debian/ stable main" >> /etc/apt/sources.l wget -qO - http://download.telldus.se/debian/telldus-public.key | apt-key add - # Add jessie-backports -echo "deb http://httpredir.debian.org/debian jessie-backports main" >> /etc/apt/sources.list +echo "deb http://deb.debian.org/debian jessie-backports main" >> /etc/apt/sources.list # Install packages apt-get update From 067e11ea5c601021588627c59a3d5ef1cdf0a369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Vran=C3=ADk?= Date: Fri, 20 Jan 2017 21:39:18 +0100 Subject: [PATCH 078/191] HDMI CEC - support for devices and commands (#4781) * cec client object * cec command structure * autodetect source * volume support and native source select * switch device * media player device * detecting of state * friendly names * hdmi cec properties * presence detection * simplified callbacks * stable names * renamed methods * code cleanup * name with vendor * fixed standby call name * fake standby/poweron * domain switch * domain switch * async updating * update separated * cec -> hass event bridge * fixed name generation * code cleanup * code cleanup * icon constants * code cleanup * do not register unavailable devices * discovery of deevices * code cleanup * cec device discovery * moved method implementation into child * service descriptions * service descriptions * service descriptions * changed entity init sequence * logging cleanup * add remove as job * closing cec, no service schemas * correct iterate over dictionary * Volume by commands * threading * logging minimized * get load out of main thread * naming cleanup * get load out of main thread * optimized discovery * async where possible * cleanup logging, constructors first * pydoc * formatting * no async_update from out of loop no hiding entities removed redundant device_state_attributes async updating presence * no async * working async cec * cec in thirdparty lib * cec initialized oudsice * working without SIGSEGV * rollbacked file changed by mistake * sending of commands * working with ha * using hass loop and device driven updates * version up * version up * Command types in pycec, cleanup for HA integration * Removed media player, state moved to switch * service descriptions * requirements: pyCEC * line width to 79 * doc * doc * overindentation solved * HDMI to uppercase * minimal dependency on cec * removed unwanted line * doc wording * margin 79 * line continuation indent * imperative doc * lint: indentation * fixed overindented * fixed overindented * fixed overindented * fixed overindented * order of imports * PEP8 * keep signature of overriding * removed redundant blank line * fixed update call method (#4) * Preparation for merge to upstream (#5) * newer version of pyCEC * updated services.yaml * fixed lint scrpt to operate only on python files * pycec version up * update services * no coverage report * exclude non python files from lint * lint only on python files * Dev (#6) * reordered * sending nonserialized data through hass.data * code formatting * code formatting * import order * Dev (#7) * newer version of pyCEC * updated services.yaml * fixed lint scrpt to operate only on python files * pycec version up * update services * no coverage report * exclude non python files from lint * lint only on python files * reordered * sending nonserialized data through hass.data * import order * fixed object handling * code formatting * Backwards compatibility of hdmi_cec (#10) * services: power_on standby active_source * new version of pyCEC (#12) * newer version of pyCEC * devices config (#13) * getting device name from config * shutdown fix (#14) * correct call on shutdown * remove misplaced annotations (#15) * Preparation for merge to upstream (#5) * newer version of pyCEC * updated services.yaml * reordered * sending nonserialized data through hass.data * services: power_on standby active_source * code formatting * getting device name from config * correct call on shutdown * pyCEC version 0.3.6 (#18) * newer version of pyCEC * updated services.yaml * sending nonserialized data through hass.data * services: ** power_on ** standby ** active_source * getting device name from config * correct call on shutdown * fork new thread on multicore machines * support both config schemas: original and new (#16) * volume press and release support (#17) * support for media_player (#21) * accept hexadecimal format of commands * support for media player * platform customization * type constants * Dev (#23) * accept hexadecimal format of commands * support for media player * platform customization * TCP CEC support (#24) * accept hexadecimal format of commands * support for media player * platform customization * preparing tcp support * volume handling (#25) * Incorporated CR remarks (#26) * cleanup imports * cleanup and enhance services description * removed unwanted file * implemented CR remarks (#27) * pyCEC v0.4.6 * pined dependency version * tighten service schemas * requirements (#28) --- .coveragerc | 2 + homeassistant/components/hdmi_cec.py | 425 +++++++++++++++--- .../components/media_player/hdmi_cec.py | 175 ++++++++ homeassistant/components/services.yaml | 55 +++ homeassistant/components/switch/hdmi_cec.py | 63 +++ requirements_all.txt | 3 + 6 files changed, 658 insertions(+), 65 deletions(-) create mode 100644 homeassistant/components/media_player/hdmi_cec.py create mode 100644 homeassistant/components/switch/hdmi_cec.py diff --git a/.coveragerc b/.coveragerc index 5bd097e8abb..ea0530aa8f7 100644 --- a/.coveragerc +++ b/.coveragerc @@ -214,6 +214,7 @@ omit = homeassistant/components/media_player/emby.py homeassistant/components/media_player/firetv.py homeassistant/components/media_player/gpmdp.py + homeassistant/components/media_player/hdmi_cec.py homeassistant/components/media_player/itunes.py homeassistant/components/media_player/kodi.py homeassistant/components/media_player/lg_netcast.py @@ -356,6 +357,7 @@ omit = homeassistant/components/switch/digitalloggers.py homeassistant/components/switch/dlink.py homeassistant/components/switch/edimax.py + homeassistant/components/switch/hdmi_cec.py homeassistant/components/switch/hikvisioncam.py homeassistant/components/switch/hook.py homeassistant/components/switch/kankun.py diff --git a/homeassistant/components/hdmi_cec.py b/homeassistant/components/hdmi_cec.py index 4fab7f84bd3..44b205993b6 100644 --- a/homeassistant/components/hdmi_cec.py +++ b/homeassistant/components/hdmi_cec.py @@ -1,27 +1,110 @@ """ -CEC component. +HDMI CEC component. For more details about this component, please refer to the documentation at https://home-assistant.io/components/hdmi_cec/ """ import logging +import multiprocessing +import os +from collections import defaultdict +from functools import reduce import voluptuous as vol -from homeassistant.const import (EVENT_HOMEASSISTANT_START, CONF_DEVICES) import homeassistant.helpers.config_validation as cv +from homeassistant import core +from homeassistant.components import discovery +from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER +from homeassistant.components.switch import DOMAIN as SWITCH +from homeassistant.config import load_yaml_config_file +from homeassistant.const import (EVENT_HOMEASSISTANT_START, STATE_UNKNOWN, + EVENT_HOMEASSISTANT_STOP, STATE_ON, + STATE_OFF, CONF_DEVICES, CONF_PLATFORM, + CONF_CUSTOMIZE, STATE_PLAYING, STATE_IDLE, + STATE_PAUSED, CONF_HOST) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import Entity -_CEC = None -_LOGGER = logging.getLogger(__name__) - -ATTR_DEVICE = 'device' +REQUIREMENTS = ['pyCEC==0.4.6'] DOMAIN = 'hdmi_cec' -MAX_DEPTH = 4 +_LOGGER = logging.getLogger(__name__) + +ICON_UNKNOWN = 'mdi:help' +ICON_AUDIO = 'mdi:speaker' +ICON_PLAYER = 'mdi:play' +ICON_TUNER = 'mdi:nest-thermostat' +ICON_RECORDER = 'mdi:microphone' +ICON_TV = 'mdi:television' +ICONS_BY_TYPE = { + 0: ICON_TV, + 1: ICON_RECORDER, + 3: ICON_TUNER, + 4: ICON_PLAYER, + 5: ICON_AUDIO +} + +CEC_DEVICES = defaultdict(list) + +CMD_UP = 'up' +CMD_DOWN = 'down' +CMD_MUTE = 'mute' +CMD_UNMUTE = 'unmute' +CMD_MUTE_TOGGLE = 'toggle mute' +CMD_PRESS = 'press' +CMD_RELEASE = 'release' + +EVENT_CEC_COMMAND_RECEIVED = 'cec_command_received' +EVENT_CEC_KEYPRESS_RECEIVED = 'cec_keypress_received' + +ATTR_PHYSICAL_ADDRESS = 'physical_address' +ATTR_TYPE_ID = 'type_id' +ATTR_VENDOR_NAME = 'vendor_name' +ATTR_VENDOR_ID = 'vendor_id' +ATTR_DEVICE = 'device' +ATTR_COMMAND = 'command' +ATTR_TYPE = 'type' +ATTR_KEY = 'key' +ATTR_DUR = 'dur' +ATTR_SRC = 'src' +ATTR_DST = 'dst' +ATTR_CMD = 'cmd' +ATTR_ATT = 'att' +ATTR_RAW = 'raw' +ATTR_DIR = 'dir' +ATTR_ABT = 'abt' +ATTR_NEW = 'new' + +_VOL_HEX = vol.Any(vol.Coerce(int), lambda x: int(x, 16)) + +SERVICE_SEND_COMMAND = 'send_command' +SERVICE_SEND_COMMAND_SCHEMA = vol.Schema({ + vol.Optional(ATTR_CMD): _VOL_HEX, + vol.Optional(ATTR_SRC): _VOL_HEX, + vol.Optional(ATTR_DST): _VOL_HEX, + vol.Optional(ATTR_ATT): _VOL_HEX, + vol.Optional(ATTR_RAW): vol.Coerce(str) +}, extra=vol.PREVENT_EXTRA) + +SERVICE_VOLUME = 'volume' +SERVICE_VOLUME_SCHEMA = vol.Schema({ + vol.Optional(CMD_UP): vol.Any(CMD_PRESS, CMD_RELEASE, vol.Coerce(int)), + vol.Optional(CMD_DOWN): vol.Any(CMD_PRESS, CMD_RELEASE, vol.Coerce(int)), + vol.Optional(CMD_MUTE): None, + vol.Optional(CMD_UNMUTE): None, + vol.Optional(CMD_MUTE_TOGGLE): None +}, extra=vol.PREVENT_EXTRA) + +SERVICE_UPDATE_DEVICES = 'update' +SERVICE_UPDATE_DEVICES_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({}) +}, extra=vol.PREVENT_EXTRA) + +SERVICE_SELECT_DEVICE = 'select_device' SERVICE_POWER_ON = 'power_on' -SERVICE_SELECT_DEVICE = 'select_device' SERVICE_STANDBY = 'standby' # pylint: disable=unnecessary-lambda @@ -30,92 +113,304 @@ DEVICE_SCHEMA = vol.Schema({ cv.string) }) +CUSTOMIZE_SCHEMA = vol.Schema({ + vol.Optional(CONF_PLATFORM, default=MEDIA_PLAYER): vol.Any(MEDIA_PLAYER, + SWITCH) +}) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_DEVICES): DEVICE_SCHEMA + vol.Optional(CONF_DEVICES): vol.Any(DEVICE_SCHEMA, + vol.Schema({ + vol.All(cv.string): vol.Any( + cv.string) + })), + vol.Optional(CONF_PLATFORM): vol.Any(SWITCH, MEDIA_PLAYER), + vol.Optional(CONF_HOST): cv.string, }) }, extra=vol.ALLOW_EXTRA) +def pad_physical_address(addr): + """Right-pad a physical address.""" + return addr + [0] * (4 - len(addr)) + + def parse_mapping(mapping, parents=None): """Parse configuration device mapping.""" if parents is None: parents = [] for addr, val in mapping.items(): - cur = parents + [str(addr)] - if isinstance(val, dict): - yield from parse_mapping(val, cur) - elif isinstance(val, str): - yield (val, cur) + if isinstance(addr, (str,)) and isinstance(val, (str,)): + from pycec.network import PhysicalAddress + yield (addr, PhysicalAddress(val)) + else: + cur = parents + [addr] + if isinstance(val, dict): + yield from parse_mapping(val, cur) + elif isinstance(val, str): + yield (val, pad_physical_address(cur)) -def pad_physical_address(addr): - """Right-pad a physical address.""" - return addr + ['0'] * (MAX_DEPTH - len(addr)) - - -def setup(hass, config): +def setup(hass: HomeAssistant, base_config): """Setup CEC capability.""" - global _CEC - - try: - import cec - except ImportError: - _LOGGER.error("libcec must be installed") - return False + from pycec.network import HDMINetwork + from pycec.commands import CecCommand, KeyReleaseCommand, KeyPressCommand + from pycec.const import KEY_VOLUME_UP, KEY_VOLUME_DOWN, KEY_MUTE, \ + ADDR_AUDIOSYSTEM, ADDR_BROADCAST, ADDR_UNREGISTERED + from pycec.cec import CecAdapter + from pycec.tcp import TcpAdapter # Parse configuration into a dict of device name to physical address # represented as a list of four elements. - flat = {} - for pair in parse_mapping(config[DOMAIN].get(CONF_DEVICES, {})): - flat[pair[0]] = pad_physical_address(pair[1]) + device_aliases = {} + devices = base_config[DOMAIN].get(CONF_DEVICES, {}) + _LOGGER.debug("Parsing config %s", devices) + device_aliases.update(parse_mapping(devices)) + _LOGGER.debug("Parsed devices: %s", device_aliases) - # Configure libcec. - cfg = cec.libcec_configuration() - cfg.strDeviceName = 'HASS' - cfg.bActivateSource = 0 - cfg.bMonitorOnly = 1 - cfg.clientVersion = cec.LIBCEC_VERSION_CURRENT + platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH) - # Setup CEC adapter. - _CEC = cec.ICECAdapter.Create(cfg) + loop = ( + # Create own thread if more than 1 CPU + hass.loop if multiprocessing.cpu_count() < 2 else None) + host = base_config[DOMAIN].get(CONF_HOST, None) + if host: + adapter = TcpAdapter(host, name="HASS", activate_source=False) + else: + adapter = CecAdapter(name="HASS", activate_source=False) + hdmi_network = HDMINetwork(adapter, loop=loop) - def _power_on(call): - """Power on all devices.""" - _CEC.PowerOnDevices() + def _volume(call): + """Increase/decrease volume and mute/unmute system.""" + for cmd, att in call.data.items(): + if cmd == CMD_UP: + _process_volume(KEY_VOLUME_UP, att) + elif cmd == CMD_DOWN: + _process_volume(KEY_VOLUME_DOWN, att) + elif cmd == CMD_MUTE: + hdmi_network.send_command( + KeyPressCommand(KEY_MUTE, dst=ADDR_AUDIOSYSTEM)) + hdmi_network.send_command( + KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) + _LOGGER.info("Audio muted") + else: + _LOGGER.warning("Unknown command %s", cmd) + def _process_volume(cmd, att): + if isinstance(att, (str,)): + att = att.strip() + if att == CMD_PRESS: + hdmi_network.send_command( + KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM)) + elif att == CMD_RELEASE: + hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) + else: + att = 1 if att == "" else int(att) + for _ in range(1, att): + hdmi_network.send_command( + KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM)) + hdmi_network.send_command( + KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) + + def _tx(call): + """Send CEC command.""" + data = call.data + if ATTR_RAW in data: + command = CecCommand(data[ATTR_RAW]) + else: + if ATTR_SRC in data: + src = data[ATTR_SRC] + else: + src = ADDR_UNREGISTERED + if ATTR_DST in data: + dst = data[ATTR_DST] + else: + dst = ADDR_BROADCAST + if ATTR_CMD in data: + cmd = data[ATTR_CMD] + else: + _LOGGER.error("Attribute 'cmd' is missing") + return False + if ATTR_ATT in data: + if isinstance(data[ATTR_ATT], (list,)): + att = data[ATTR_ATT] + else: + att = reduce(lambda x, y: "%s:%x" % (x, y), data[ATTR_ATT]) + else: + att = "" + command = CecCommand(cmd, dst, src, att) + hdmi_network.send_command(command) + + @callback def _standby(call): - """Standby all devices.""" - _CEC.StandbyDevices() + hdmi_network.standby() + + @callback + def _power_on(call): + hdmi_network.power_on() def _select_device(call): """Select the active device.""" - path = flat.get(call.data[ATTR_DEVICE]) - if not path: + from pycec.network import PhysicalAddress + + addr = call.data[ATTR_DEVICE] + if not addr: _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE]) - cmds = [] - for i in range(1, MAX_DEPTH - 1): - addr = pad_physical_address(path[:i]) - cmds.append('1f:82:{}{}:{}{}'.format(*addr)) - cmds.append('1f:86:{}{}:{}{}'.format(*addr)) - for cmd in cmds: - _CEC.Transmit(_CEC.CommandFromString(cmd)) - _LOGGER.info("Selected %s", call.data[ATTR_DEVICE]) + return + if addr in device_aliases: + addr = device_aliases[addr] + else: + entity = hass.states.get(addr) + _LOGGER.debug("Selecting entity %s", entity) + if entity is not None: + addr = entity.attributes['physical_address'] + _LOGGER.debug("Address acquired: %s", addr) + if addr is None: + _LOGGER.error("Device %s has not physical address.", + call.data[ATTR_DEVICE]) + return + if not isinstance(addr, (PhysicalAddress,)): + addr = PhysicalAddress(addr) + hdmi_network.active_source(addr) + _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr) + + def _update(call): + """ + Callback called when device update is needed. + + - called by service, requests CEC network to update data. + """ + hdmi_network.scan() + + @callback + def _new_device(device): + """Called when new device is detected by HDMI network.""" + key = DOMAIN + '.' + device.name + hass.data[key] = device + discovery.load_platform(hass, base_config.get(core.DOMAIN).get( + CONF_CUSTOMIZE, {}).get(key, {}).get(CONF_PLATFORM, platform), + DOMAIN, discovered={ATTR_NEW: [key]}, + hass_config=base_config) + + def _shutdown(call): + hdmi_network.stop() def _start_cec(event): - """Open CEC adapter.""" - adapters = _CEC.DetectAdapters() - if len(adapters) == 0: - _LOGGER.error("No CEC adapter found") - return + """Register services and start HDMI network to watch for devices.""" + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml'))[DOMAIN] + hass.services.register(DOMAIN, SERVICE_SEND_COMMAND, _tx, + descriptions[SERVICE_SEND_COMMAND], + SERVICE_SEND_COMMAND_SCHEMA) + hass.services.register(DOMAIN, SERVICE_VOLUME, _volume, + descriptions[SERVICE_VOLUME], + SERVICE_VOLUME_SCHEMA) + hass.services.register(DOMAIN, SERVICE_UPDATE_DEVICES, _update, + descriptions[SERVICE_UPDATE_DEVICES], + SERVICE_UPDATE_DEVICES_SCHEMA) + hass.services.register(DOMAIN, SERVICE_POWER_ON, _power_on) + hass.services.register(DOMAIN, SERVICE_STANDBY, _standby) + hass.services.register(DOMAIN, SERVICE_SELECT_DEVICE, _select_device) - if _CEC.Open(adapters[0].strComName): - hass.services.register(DOMAIN, SERVICE_POWER_ON, _power_on) - hass.services.register(DOMAIN, SERVICE_STANDBY, _standby) - hass.services.register(DOMAIN, SERVICE_SELECT_DEVICE, - _select_device) - else: - _LOGGER.error("Failed to open adapter") + hdmi_network.set_new_device_callback(_new_device) + hdmi_network.start() hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _start_cec) + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) return True + + +class CecDevice(Entity): + """Representation of a HDMI CEC device entity.""" + + def __init__(self, hass: HomeAssistant, device, logical): + """Initialize the device.""" + self._device = device + self.hass = hass + self._icon = None + self._state = STATE_UNKNOWN + self._logical_address = logical + self.entity_id = "%s.%d" % (DOMAIN, self._logical_address) + device.set_update_callback(self._update) + + def update(self): + """Update device status.""" + self._update() + + def _update(self, device=None): + """Update device status.""" + if device: + from pycec.const import STATUS_PLAY, STATUS_STOP, STATUS_STILL, \ + POWER_OFF, POWER_ON + if device.power_status == POWER_OFF: + self._state = STATE_OFF + elif device.status == STATUS_PLAY: + self._state = STATE_PLAYING + elif device.status == STATUS_STOP: + self._state = STATE_IDLE + elif device.status == STATUS_STILL: + self._state = STATE_PAUSED + elif device.power_status == POWER_ON: + self._state = STATE_ON + else: + _LOGGER.warning("Unknown state: %d", device.power_status) + self.schedule_update_ha_state() + + @property + def name(self): + """Return the name of the device.""" + return ( + "%s %s" % (self.vendor_name, self._device.osd_name) + if (self._device.osd_name is not None and + self.vendor_name is not None and self.vendor_name != 'Unknown') + else "%s %d" % (self._device.type_name, self._logical_address) + if self._device.osd_name is None + else "%s %d (%s)" % (self._device.type_name, self._logical_address, + self._device.osd_name)) + + @property + def vendor_id(self): + """ID of device's vendor.""" + return self._device.vendor_id + + @property + def vendor_name(self): + """Name of device's vendor.""" + return self._device.vendor + + @property + def physical_address(self): + """Physical address of device in HDMI network.""" + return str(self._device.physical_address) + + @property + def type(self): + """String representation of device's type.""" + return self._device.type_name + + @property + def type_id(self): + """Type ID of device.""" + return self._device.type + + @property + def icon(self): + """Icon for device by its type.""" + return (self._icon if self._icon is not None else + ICONS_BY_TYPE.get(self._device.type) + if self._device.type in ICONS_BY_TYPE else ICON_UNKNOWN) + + @property + def device_state_attributes(self): + """Return the state attributes.""" + state_attr = {} + if self.vendor_id is not None: + state_attr[ATTR_VENDOR_ID] = self.vendor_id + state_attr[ATTR_VENDOR_NAME] = self.vendor_name + if self.type_id is not None: + state_attr[ATTR_TYPE_ID] = self.type_id + state_attr[ATTR_TYPE] = self.type + if self.physical_address is not None: + state_attr[ATTR_PHYSICAL_ADDRESS] = self.physical_address + return state_attr diff --git a/homeassistant/components/media_player/hdmi_cec.py b/homeassistant/components/media_player/hdmi_cec.py new file mode 100644 index 00000000000..4998072018e --- /dev/null +++ b/homeassistant/components/media_player/hdmi_cec.py @@ -0,0 +1,175 @@ +""" +Support for HDMI CEC devices as media players. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/hdmi_cec/ +""" +import logging + +from homeassistant.components.hdmi_cec import ATTR_NEW, CecDevice +from homeassistant.components.media_player import MediaPlayerDevice, DOMAIN, \ + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA, SUPPORT_PAUSE, \ + SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, SUPPORT_STOP, \ + SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_MUTE +from homeassistant.const import STATE_ON, STATE_OFF, STATE_PLAYING, \ + STATE_IDLE, STATE_PAUSED +from homeassistant.core import HomeAssistant + +DEPENDENCIES = ['hdmi_cec'] + +_LOGGER = logging.getLogger(__name__) + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Find and return HDMI devices as +switches.""" + if ATTR_NEW in discovery_info: + _LOGGER.info("Setting up HDMI devices %s", discovery_info[ATTR_NEW]) + add_devices(CecPlayerDevice(hass, hass.data.get(device), + hass.data.get(device).logical_address) for + device in discovery_info[ATTR_NEW]) + + +class CecPlayerDevice(CecDevice, MediaPlayerDevice): + """Representation of a HDMI device as a Media palyer.""" + + def __init__(self, hass: HomeAssistant, device, logical): + """Initialize the HDMI device.""" + CecDevice.__init__(self, hass, device, logical) + self.entity_id = "%s.%s_%s" % ( + DOMAIN, 'hdmi', hex(self._logical_address)[2:]) + self.update() + + def send_keypress(self, key): + """Send keypress to CEC adapter.""" + from pycec.commands import KeyPressCommand, KeyReleaseCommand + _LOGGER.debug("Sending keypress %s to device %s", hex(key), + hex(self._logical_address)) + self._device.send_command( + KeyPressCommand(key, dst=self._logical_address)) + self._device.send_command( + KeyReleaseCommand(dst=self._logical_address)) + + def send_playback(self, key): + """Send playback status to CEC adapter.""" + from pycec.commands import CecCommand + self._device.async_send_command( + CecCommand(key, dst=self._logical_address)) + + def mute_volume(self, mute): + """Mute volume.""" + from pycec.const import KEY_MUTE + self.send_keypress(KEY_MUTE) + + def media_previous_track(self): + """Go to previous track.""" + from pycec.const import KEY_BACKWARD + self.send_keypress(KEY_BACKWARD) + + def turn_on(self): + """Turn device on.""" + self._device.turn_on() + self._state = STATE_ON + + def clear_playlist(self): + """Clear players playlist.""" + raise NotImplementedError() + + def turn_off(self): + """Turn device off.""" + self._device.turn_off() + self._state = STATE_OFF + + def media_stop(self): + """Stop playback.""" + from pycec.const import KEY_STOP + self.send_keypress(KEY_STOP) + self._state = STATE_IDLE + + def play_media(self, media_type, media_id): + """Not supported.""" + raise NotImplementedError() + + def media_next_track(self): + """Skip to next track.""" + from pycec.const import KEY_FORWARD + self.send_keypress(KEY_FORWARD) + + def media_seek(self, position): + """Not supported.""" + raise NotImplementedError() + + def set_volume_level(self, volume): + """Set volume level, range 0..1.""" + raise NotImplementedError() + + def media_pause(self): + """Pause playback.""" + from pycec.const import KEY_PAUSE + self.send_keypress(KEY_PAUSE) + self._state = STATE_PAUSED + + def select_source(self, source): + """Not supported.""" + raise NotImplementedError() + + def media_play(self): + """Start playback.""" + from pycec.const import KEY_PLAY + self.send_keypress(KEY_PLAY) + self._state = STATE_PLAYING + + def volume_up(self): + """Increase volume.""" + from pycec.const import KEY_VOLUME_UP + _LOGGER.debug("%s: volume up", self._logical_address) + self.send_keypress(KEY_VOLUME_UP) + + def volume_down(self): + """Decrease volume.""" + from pycec.const import KEY_VOLUME_DOWN + _LOGGER.debug("%s: volume down", self._logical_address) + self.send_keypress(KEY_VOLUME_DOWN) + + @property + def state(self) -> str: + """Cached state of device.""" + return self._state + + def _update(self, device=None): + """Update device status.""" + if device: + from pycec.const import STATUS_PLAY, STATUS_STOP, STATUS_STILL, \ + POWER_OFF, POWER_ON + if device.power_status == POWER_OFF: + self._state = STATE_OFF + elif not self.support_pause: + if device.power_status == POWER_ON: + self._state = STATE_ON + elif device.status == STATUS_PLAY: + self._state = STATE_PLAYING + elif device.status == STATUS_STOP: + self._state = STATE_IDLE + elif device.status == STATUS_STILL: + self._state = STATE_PAUSED + else: + _LOGGER.warning("Unknown state: %s", device.status) + self.schedule_update_ha_state() + + @property + def supported_media_commands(self): + """Flag media commands that are supported.""" + from pycec.const import TYPE_RECORDER, TYPE_PLAYBACK, TYPE_TUNER, \ + TYPE_AUDIO + if self.type_id == TYPE_RECORDER or self.type == TYPE_PLAYBACK: + return (SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA | + SUPPORT_PAUSE | SUPPORT_STOP | SUPPORT_PREVIOUS_TRACK | + SUPPORT_NEXT_TRACK) + if self.type == TYPE_TUNER: + return (SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA | + SUPPORT_PAUSE | SUPPORT_STOP) + if self.type_id == TYPE_AUDIO: + return (SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_VOLUME_STEP | + SUPPORT_VOLUME_MUTE) + return SUPPORT_TURN_ON | SUPPORT_TURN_OFF diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 54c0e18a3ee..53f82d5c059 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -153,3 +153,58 @@ verisure: device_serial: description: The serial number of the smartcam you want to capture an image from. example: '2DEU AT5Z' + +hdmi_cec: + send_command: + description: Sends CEC command into HDMI CEC capable adapter. + + fields: + raw: + description: 'Raw CEC command in format "00:00:00:00" where first two digits are source and destination, second byte is command and optional other bytes are command parameters. If raw command specified, other params are ignored.' + example: '"10:36"' + + src: + desctiption: 'Source of command. Could be decimal number or string with hexadeximal notation: "0x10".' + example: '12 or "0xc"' + + dst: + description: 'Destination for command. Could be decimal number or string with hexadeximal notation: "0x10".' + example: '5 or "0x5"' + + cmd: + description: 'Command itself. Could be decimal number or string with hexadeximal notation: "0x10".' + example: '144 or "0x90"' + + att: + description: Optional parameters. + example: [0, 2] + + update: + description: Update devices state from network. + + volume: + description: Increase or decrease volume of system. + + fields: + up: + description: Increases volume x levels. + example: 3 + down: + description: Decreases volume x levels. + example: 3 + mute: Mutes audio system. Value is ignored. + unmute: Unmutes audio system. Value is ignored. + toggle mute: Toggles mute of audio system. Value is ignored. + + select_device: + description: Select HDMI device. + fields: + device: + description: Addres of device to select. Can be entity_id, physical address or alias from confuguration. + example: '"switch.hdmi_1" or "1.1.0.0" or "01:10"' + + power_on: + description: Power on all devices which supports it. + + standby: + description: Standby all devices which supports it. diff --git a/homeassistant/components/switch/hdmi_cec.py b/homeassistant/components/switch/hdmi_cec.py new file mode 100644 index 00000000000..bd1f9ea6578 --- /dev/null +++ b/homeassistant/components/switch/hdmi_cec.py @@ -0,0 +1,63 @@ +""" +Support for HDMI CEC devices as switches. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/hdmi_cec/ +""" +import logging + +from homeassistant.components.hdmi_cec import CecDevice, ATTR_NEW +from homeassistant.components.switch import SwitchDevice, DOMAIN +from homeassistant.const import STATE_OFF, STATE_STANDBY, STATE_ON +from homeassistant.core import HomeAssistant + +DEPENDENCIES = ['hdmi_cec'] + +_LOGGER = logging.getLogger(__name__) + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Find and return HDMI devices as switches.""" + if ATTR_NEW in discovery_info: + _LOGGER.info("Setting up HDMI devices %s", discovery_info[ATTR_NEW]) + add_devices(CecSwitchDevice(hass, hass.data.get(device), + hass.data.get(device).logical_address) for + device in discovery_info[ATTR_NEW]) + + +class CecSwitchDevice(CecDevice, SwitchDevice): + """Representation of a HDMI device as a Switch.""" + + def __init__(self, hass: HomeAssistant, device, logical): + """Initialize the HDMI device.""" + CecDevice.__init__(self, hass, device, logical) + self.entity_id = "%s.%s_%s" % ( + DOMAIN, 'hdmi', hex(self._logical_address)[2:]) + self.update() + + def turn_on(self, **kwargs) -> None: + """Turn device on.""" + self._device.turn_on() + self._state = STATE_ON + + def turn_off(self, **kwargs) -> None: + """Turn device off.""" + self._device.turn_off() + self._state = STATE_ON + + @property + def is_on(self) -> bool: + """Return True if entity is on.""" + return self._state == STATE_ON + + @property + def is_standby(self): + """Return true if device is in standby.""" + return self._state == STATE_OFF or self._state == STATE_STANDBY + + @property + def state(self) -> str: + """Cached state of device.""" + return self._state diff --git a/requirements_all.txt b/requirements_all.txt index ec328eca44d..59927a3672b 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -377,6 +377,9 @@ pwaqi==1.3 # homeassistant.components.sensor.cpuspeed py-cpuinfo==0.2.3 +# homeassistant.components.hdmi_cec +pyCEC==0.4.6 + # homeassistant.components.switch.tplink pyHS100==0.2.3 From f4d2d69a5df172bf4f8a5740194e8d12de703d6d Mon Sep 17 00:00:00 2001 From: Martin Vacula Date: Fri, 20 Jan 2017 21:55:28 +0100 Subject: [PATCH 079/191] Beaglebone Black binary sensor (#5422) * Configuration parameters defined * Edge detection added * Example configuration added and tipo corrected * Comments updated, lint update * Added check for input pull_mode * Too long line * trailing white space * Configuration parameters defined * Edge detection added * Example configuration added and tipo corrected * Comments updated, lint update * Added check for input pull_mode * Too long line * trailing white space * pylint disable import error * read_input() changed to return boolean value, according changes in binary sensor * example configuration in docstring changed to mention direct web link, pylint update * read_input() updated according review --- homeassistant/components/bbb_gpio.py | 2 +- .../components/binary_sensor/bbb_gpio.py | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/binary_sensor/bbb_gpio.py diff --git a/homeassistant/components/bbb_gpio.py b/homeassistant/components/bbb_gpio.py index d8acaaa184c..89692a1e1e1 100644 --- a/homeassistant/components/bbb_gpio.py +++ b/homeassistant/components/bbb_gpio.py @@ -63,7 +63,7 @@ def read_input(pin): """Read a value from a GPIO.""" # pylint: disable=import-error,undefined-variable import Adafruit_BBIO.GPIO as GPIO - return GPIO.input(pin) + return GPIO.input(pin) is GPIO.HIGH def edge_detect(pin, event_callback, bounce): diff --git a/homeassistant/components/binary_sensor/bbb_gpio.py b/homeassistant/components/binary_sensor/bbb_gpio.py new file mode 100644 index 00000000000..dd960defaa8 --- /dev/null +++ b/homeassistant/components/binary_sensor/bbb_gpio.py @@ -0,0 +1,89 @@ +""" +Support for binary sensor using Beaglebone Black GPIO. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.bbb_gpio/ +""" +import logging + +import voluptuous as vol + +import homeassistant.components.bbb_gpio as bbb_gpio +from homeassistant.components.binary_sensor import ( + BinarySensorDevice, PLATFORM_SCHEMA) +from homeassistant.const import (DEVICE_DEFAULT_NAME, CONF_NAME) +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['bbb_gpio'] + +CONF_PINS = 'pins' +CONF_BOUNCETIME = 'bouncetime' +CONF_INVERT_LOGIC = 'invert_logic' +CONF_PULL_MODE = 'pull_mode' + +DEFAULT_BOUNCETIME = 50 +DEFAULT_INVERT_LOGIC = False +DEFAULT_PULL_MODE = 'UP' + +PIN_SCHEMA = vol.Schema({ + vol.Required(CONF_NAME): cv.string, + vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int, + vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean, + vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): + vol.In(['UP', 'DOWN']) +}) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_PINS, default={}): + vol.Schema({cv.string: PIN_SCHEMA}), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Beaglebone Black GPIO devices.""" + pins = config.get(CONF_PINS) + + binary_sensors = [] + + for pin, params in pins.items(): + binary_sensors.append(BBBGPIOBinarySensor(pin, params)) + add_devices(binary_sensors) + + +class BBBGPIOBinarySensor(BinarySensorDevice): + """Represent a binary sensor that uses Beaglebone Black GPIO.""" + + def __init__(self, pin, params): + """Initialize the Beaglebone Black binary sensor.""" + self._pin = pin + self._name = params.get(CONF_NAME) or DEVICE_DEFAULT_NAME + self._bouncetime = params.get(CONF_BOUNCETIME) + self._pull_mode = params.get(CONF_PULL_MODE) + self._invert_logic = params.get(CONF_INVERT_LOGIC) + + bbb_gpio.setup_input(self._pin, self._pull_mode) + self._state = bbb_gpio.read_input(self._pin) + + def read_gpio(pin): + """Read state from GPIO.""" + self._state = bbb_gpio.read_input(self._pin) + self.schedule_update_ha_state() + + bbb_gpio.edge_detect(self._pin, read_gpio, self._bouncetime) + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def is_on(self): + """Return the state of the entity.""" + return self._state != self._invert_logic From 41ee798b0f12b876cb7f7aac2f1b463710d668b3 Mon Sep 17 00:00:00 2001 From: John Arild Berentsen Date: Fri, 20 Jan 2017 22:01:36 +0100 Subject: [PATCH 080/191] [WIP][ZWave][Lock] Further improvements to zwave lock platform (#5400) * Further improvements to zwave lock platform * Add missing notification * Some improvements --- homeassistant/components/lock/zwave.py | 103 ++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index 17fc30e93cf..9dbbb8e733f 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -14,7 +14,7 @@ from homeassistant.components import zwave _LOGGER = logging.getLogger(__name__) ATTR_NOTIFICATION = 'notification' - +ATTR_LOCK_STATUS = 'lock_status' LOCK_NOTIFICATION = { 1: 'Manual Lock', 2: 'Manual Unlock', @@ -22,18 +22,64 @@ LOCK_NOTIFICATION = { 4: 'RF Unlock', 5: 'Keypad Lock', 6: 'Keypad Unlock', + 11: 'Lock Jammed', 254: 'Unknown Event' } +LOCK_ALARM_TYPE = { + 9: 'Deadbolt Jammed', + 18: 'Locked with Keypad by user', + 19: 'Unlocked with Keypad by user ', + 21: 'Manually Locked by', + 22: 'Manually Unlocked by Key or Inside thumb turn', + 24: 'Locked by RF', + 25: 'Unlocked by RF', + 27: 'Auto re-lock', + 33: 'User deleted: ', + 112: 'Master code changed or User added: ', + 113: 'Duplicate Pin-code: ', + 130: 'RF module, power restored', + 161: 'Tamper Alarm: ', + 167: 'Low Battery', + 168: 'Critical Battery Level', + 169: 'Battery too low to operate' +} + +MANUAL_LOCK_ALARM_LEVEL = { + 1: 'Key Cylinder or Inside thumb turn', + 2: 'Touch function (lock and leave)' +} + +TAMPER_ALARM_LEVEL = { + 1: 'Too many keypresses', + 2: 'Cover removed' +} + LOCK_STATUS = { 1: True, 2: False, 3: True, 4: False, 5: True, - 6: False + 6: False, + 9: False, + 18: True, + 19: False, + 21: True, + 22: False, + 24: True, + 25: False, + 27: True } +ALARM_TYPE_STD = [ + 18, + 19, + 33, + 112, + 113 +] + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): @@ -68,6 +114,7 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice): self._node = value.node self._state = None self._notification = None + self._lock_status = None dispatcher.connect( self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) self.update_properties() @@ -89,9 +136,55 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice): self._notification = LOCK_NOTIFICATION.get(value.data) if self._notification: self._state = LOCK_STATUS.get(value.data) + _LOGGER.debug('Lock state set from Access Control value and' + ' is %s', value.data) break - if not self._notification: - self._state = self._value.data + + for value in self._node.get_values( + class_id=zwave.const.COMMAND_CLASS_ALARM).values(): + if value.label != "Alarm Type": + continue + alarm_type = LOCK_ALARM_TYPE.get(value.data) + if alarm_type: + self._state = LOCK_STATUS.get(value.data) + _LOGGER.debug('Lock state set from Alarm Type value and' + ' is %s', value.data) + break + + for value in self._node.get_values( + class_id=zwave.const.COMMAND_CLASS_ALARM).values(): + if value.label != "Alarm Level": + continue + alarm_level = value.data + _LOGGER.debug('Lock alarm_level is %s', alarm_level) + if alarm_type is 21: + self._lock_status = '{}{}'.format( + LOCK_ALARM_TYPE.get(alarm_type), + MANUAL_LOCK_ALARM_LEVEL.get(alarm_level)) + if alarm_type in ALARM_TYPE_STD: + self._lock_status = '{}{}'.format( + LOCK_ALARM_TYPE.get(alarm_type), alarm_level) + break + if alarm_type is 161: + self._lock_status = '{}{}'.format( + LOCK_ALARM_TYPE.get(alarm_type), + TAMPER_ALARM_LEVEL.get(alarm_level)) + break + if alarm_type != 0: + self._lock_status = LOCK_ALARM_TYPE.get(alarm_type) + break + + if not self._notification and not self._lock_status: + for value in self._node.get_values( + class_id=zwave.const.COMMAND_CLASS_DOOR_LOCK).values(): + if value.type != zwave.const.TYPE_BOOL: + continue + if value.genre != zwave.const.GENRE_USER: + continue + self._state = value.data + _LOGGER.debug('Lock state set from Bool value and' + ' is %s', value.data) + break @property def is_locked(self): @@ -112,4 +205,6 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice): data = super().device_state_attributes if self._notification: data[ATTR_NOTIFICATION] = self._notification + if self._lock_status: + data[ATTR_LOCK_STATUS] = self._lock_status return data From ec4b148a71c91ff7d926be49ff65fce3a04e1c72 Mon Sep 17 00:00:00 2001 From: Stu Gott Date: Fri, 20 Jan 2017 20:09:03 -0500 Subject: [PATCH 081/191] TTS: Invalidate broken file cache entries If a cached file cannot be read by the TTS component, then it should be removed from the file cache--or it will remain broken. --- homeassistant/components/tts/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 0f731a51485..5ee92747196 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -346,6 +346,7 @@ class SpeechManager(object): try: data = yield from self.hass.loop.run_in_executor(None, load_speech) except OSError: + del self.file_cache[key] raise HomeAssistantError("Can't read {}".format(voice_file)) self._async_store_to_memcache(key, filename, data) From 6b0a6b87defd2b80c40b2be488cc6cc0664a2830 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 20 Jan 2017 20:23:20 -0800 Subject: [PATCH 082/191] Use is_screensaver --- homeassistant/components/media_player/roku.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/media_player/roku.py b/homeassistant/components/media_player/roku.py index 728777e5e9e..ff4ddebbadf 100644 --- a/homeassistant/components/media_player/roku.py +++ b/homeassistant/components/media_player/roku.py @@ -17,8 +17,8 @@ from homeassistant.const import ( import homeassistant.helpers.config_validation as cv REQUIREMENTS = [ - 'https://github.com/bah2830/python-roku/archive/3.1.2.zip' - '#roku==3.1.2'] + 'https://github.com/bah2830/python-roku/archive/3.1.3.zip' + '#roku==3.1.3'] KNOWN_HOSTS = [] DEFAULT_PORT = 8060 @@ -114,8 +114,8 @@ class RokuDevice(MediaPlayerDevice): if self.current_app is None: return STATE_UNKNOWN - idle_list = ["Power Saver", "Screensaver", "screensaver"] - if any(idle_type in self.current_app.name for idle_type in idle_list): + if (self.current_app.name == "Power Saver" or + self.current_app.is_screensaver): return STATE_IDLE elif self.current_app.name == "Roku": return STATE_HOME From 26f6a9ee20d420fd829d82cffe8a260a05233fd2 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 20 Jan 2017 20:24:00 -0800 Subject: [PATCH 083/191] Bump requirements --- requirements_all.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all.txt b/requirements_all.txt index d13833efe18..ad7c5b75b81 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -202,7 +202,7 @@ https://github.com/Xorso/pyalarmdotcom/archive/0.1.1.zip#pyalarmdotcom==0.1.1 https://github.com/aparraga/braviarc/archive/0.3.6.zip#braviarc==0.3.6 # homeassistant.components.media_player.roku -https://github.com/bah2830/python-roku/archive/3.1.2.zip#roku==3.1.2 +https://github.com/bah2830/python-roku/archive/3.1.3.zip#roku==3.1.3 # homeassistant.components.modbus https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0 From 58b698400e8c23c135d585f53f6e2ac9cc516c93 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 20 Jan 2017 21:28:29 -0800 Subject: [PATCH 084/191] Set Roku name to the device name instead of the serial number (#5475) --- homeassistant/components/media_player/roku.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/media_player/roku.py b/homeassistant/components/media_player/roku.py index 5a4e993aee5..35aa07a5a4b 100644 --- a/homeassistant/components/media_player/roku.py +++ b/homeassistant/components/media_player/roku.py @@ -69,10 +69,10 @@ class RokuDevice(MediaPlayerDevice): from roku import Roku self.roku = Roku(host) - self.roku_name = None self.ip_address = host self.channels = [] self.current_app = None + self.device_info = {} self.update() @@ -81,7 +81,7 @@ class RokuDevice(MediaPlayerDevice): import requests.exceptions try: - self.roku_name = "roku_" + self.roku.device_info.sernum + self.device_info = self.roku.device_info self.ip_address = self.roku.host self.channels = self.get_source_list() @@ -106,7 +106,7 @@ class RokuDevice(MediaPlayerDevice): @property def name(self): """Return the name of the device.""" - return self.roku_name + return self.device_info.userdevicename @property def state(self): From b2203f7f4153c7d32b3f6b2201b78bbe9a242e1f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 21 Jan 2017 06:56:22 +0100 Subject: [PATCH 085/191] [ffmpeg] Use new 1.0 version / migrate all asyncio (#5464) * [ffmpeg] Use new 1.0 version / migrate all asyncio * fix lint * fix import * Add new service to binary_sensors * fix lint --- .../components/binary_sensor/ffmpeg.py | 174 ++++++++++++------ .../components/binary_sensor/services.yaml | 18 +- homeassistant/components/camera/ffmpeg.py | 20 +- homeassistant/components/ffmpeg.py | 98 +++++----- requirements_all.txt | 2 +- 5 files changed, 190 insertions(+), 122 deletions(-) diff --git a/homeassistant/components/binary_sensor/ffmpeg.py b/homeassistant/components/binary_sensor/ffmpeg.py index 818a6b5b387..9da14048705 100644 --- a/homeassistant/components/binary_sensor/ffmpeg.py +++ b/homeassistant/components/binary_sensor/ffmpeg.py @@ -4,8 +4,9 @@ Provides a binary sensor which is a collection of ffmpeg tools. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.ffmpeg/ """ +import asyncio import logging -from os import path +import os import voluptuous as vol @@ -13,17 +14,22 @@ import homeassistant.helpers.config_validation as cv from homeassistant.components.binary_sensor import ( BinarySensorDevice, PLATFORM_SCHEMA, DOMAIN) from homeassistant.components.ffmpeg import ( - get_binary, run_test, CONF_INPUT, CONF_OUTPUT, CONF_EXTRA_ARGUMENTS) + DATA_FFMPEG, CONF_INPUT, CONF_OUTPUT, CONF_EXTRA_ARGUMENTS) from homeassistant.config import load_yaml_config_file -from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, CONF_NAME, - ATTR_ENTITY_ID) +from homeassistant.const import ( + EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START, CONF_NAME, + ATTR_ENTITY_ID) DEPENDENCIES = ['ffmpeg'] _LOGGER = logging.getLogger(__name__) +SERVICE_START = 'ffmpeg_start' +SERVICE_STOP = 'ffmpeg_stop' SERVICE_RESTART = 'ffmpeg_restart' +DATA_FFMPEG_DEVICE = 'ffmpeg_binary_sensor' + FFMPEG_SENSOR_NOISE = 'noise' FFMPEG_SENSOR_MOTION = 'motion' @@ -32,6 +38,7 @@ MAP_FFMPEG_BIN = [ FFMPEG_SENSOR_MOTION ] +CONF_INITIAL_STATE = 'initial_state' CONF_TOOL = 'tool' CONF_PEAK = 'peak' CONF_DURATION = 'duration' @@ -41,10 +48,12 @@ CONF_REPEAT = 'repeat' CONF_REPEAT_TIME = 'repeat_time' DEFAULT_NAME = 'FFmpeg' +DEFAULT_INIT_STATE = True PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_TOOL): vol.In(MAP_FFMPEG_BIN), vol.Required(CONF_INPUT): cv.string, + vol.Optional(CONF_INITIAL_STATE, default=DEFAULT_INIT_STATE): cv.boolean, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string, vol.Optional(CONF_OUTPUT): cv.string, @@ -61,7 +70,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.All(vol.Coerce(int), vol.Range(min=0)), }) -SERVICE_RESTART_SCHEMA = vol.Schema({ +SERVICE_FFMPEG_SCHEMA = vol.Schema({ vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, }) @@ -72,86 +81,125 @@ def restart(hass, entity_id=None): hass.services.call(DOMAIN, SERVICE_RESTART, data) -# list of all ffmpeg sensors -DEVICES = [] - - -def setup_platform(hass, config, add_entities, discovery_info=None): +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Create the binary sensor.""" from haffmpeg import SensorNoise, SensorMotion # check source - if not run_test(hass, config.get(CONF_INPUT)): + if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)): return # generate sensor object if config.get(CONF_TOOL) == FFMPEG_SENSOR_NOISE: - entity = FFmpegNoise(SensorNoise, config) + entity = FFmpegNoise(hass, SensorNoise, config) else: - entity = FFmpegMotion(SensorMotion, config) + entity = FFmpegMotion(hass, SensorMotion, config) - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, entity.shutdown_ffmpeg) + @asyncio.coroutine + def async_shutdown(event): + """Stop ffmpeg.""" + yield from entity.async_shutdown_ffmpeg() + + hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, async_shutdown) + + # start on startup + if config.get(CONF_INITIAL_STATE): + @asyncio.coroutine + def async_start(event): + """Start ffmpeg.""" + yield from entity.async_start_ffmpeg() + + hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_START, async_start) # add to system - add_entities([entity]) - DEVICES.append(entity) + yield from async_add_devices([entity]) # exists service? if hass.services.has_service(DOMAIN, SERVICE_RESTART): + hass.data[DATA_FFMPEG_DEVICE].append(entity) return + hass.data[DATA_FFMPEG_DEVICE] = [entity] - descriptions = load_yaml_config_file( - path.join(path.dirname(__file__), 'services.yaml')) + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, + os.path.join(os.path.dirname(__file__), 'services.yaml')) # register service - def _service_handle_restart(service): + @asyncio.coroutine + def async_service_handle(service): """Handle service binary_sensor.ffmpeg_restart.""" entity_ids = service.data.get('entity_id') if entity_ids: - _devices = [device for device in DEVICES + _devices = [device for device in hass.data[DATA_FFMPEG_DEVICE] if device.entity_id in entity_ids] else: - _devices = DEVICES + _devices = hass.data[DATA_FFMPEG_DEVICE] + tasks = [] for device in _devices: - device.restart_ffmpeg() + if service.service == SERVICE_START: + tasks.append(device.async_start_ffmpeg()) + elif service.service == SERVICE_STOP: + tasks.append(device.async_shutdown_ffmpeg()) + else: + tasks.append(device.async_restart_ffmpeg()) - hass.services.register(DOMAIN, SERVICE_RESTART, - _service_handle_restart, - descriptions.get(SERVICE_RESTART), - schema=SERVICE_RESTART_SCHEMA) + if tasks: + yield from asyncio.wait(tasks, loop=hass.loop) + + hass.services.async_register( + DOMAIN, SERVICE_START, async_service_handle, + descriptions.get(SERVICE_START), schema=SERVICE_FFMPEG_SCHEMA) + + hass.services.async_register( + DOMAIN, SERVICE_STOP, async_service_handle, + descriptions.get(SERVICE_STOP), schema=SERVICE_FFMPEG_SCHEMA) + + hass.services.async_register( + DOMAIN, SERVICE_RESTART, async_service_handle, + descriptions.get(SERVICE_RESTART), schema=SERVICE_FFMPEG_SCHEMA) class FFmpegBinarySensor(BinarySensorDevice): """A binary sensor which use ffmpeg for noise detection.""" - def __init__(self, ffobj, config): + def __init__(self, hass, ffobj, config): """Constructor for binary sensor noise detection.""" + self._manager = hass.data[DATA_FFMPEG] self._state = False self._config = config self._name = config.get(CONF_NAME) - self._ffmpeg = ffobj(get_binary(), self._callback) + self._ffmpeg = ffobj( + self._manager.binary, hass.loop, self._async_callback) - self._start_ffmpeg(config) - - def _callback(self, state): + def _async_callback(self, state): """HA-FFmpeg callback for noise detection.""" self._state = state - self.schedule_update_ha_state() + self.hass.async_add_job(self.async_update_ha_state()) - def _start_ffmpeg(self, config): - """Start a FFmpeg instance.""" - raise NotImplementedError + def async_start_ffmpeg(self): + """Start a FFmpeg instance. - def shutdown_ffmpeg(self, event): - """For STOP event to shutdown ffmpeg.""" - self._ffmpeg.close() + This method must be run in the event loop and returns a coroutine. + """ + raise NotImplementedError() - def restart_ffmpeg(self): - """Restart ffmpeg with new config.""" - self._ffmpeg.close() - self._start_ffmpeg(self._config) + def async_shutdown_ffmpeg(self): + """For STOP event to shutdown ffmpeg. + + This method must be run in the event loop and returns a coroutine. + """ + return self._ffmpeg.close() + + @asyncio.coroutine + def async_restart_ffmpeg(self): + """Restart processing.""" + yield from self.async_shutdown_ffmpeg() + yield from self.async_start_ffmpeg() @property def is_on(self): @@ -177,20 +225,23 @@ class FFmpegBinarySensor(BinarySensorDevice): class FFmpegNoise(FFmpegBinarySensor): """A binary sensor which use ffmpeg for noise detection.""" - def _start_ffmpeg(self, config): - """Start a FFmpeg instance.""" + def async_start_ffmpeg(self): + """Start a FFmpeg instance. + + This method must be run in the event loop and returns a coroutine. + """ # init config self._ffmpeg.set_options( - time_duration=config.get(CONF_DURATION), - time_reset=config.get(CONF_RESET), - peak=config.get(CONF_PEAK), + time_duration=self._config.get(CONF_DURATION), + time_reset=self._config.get(CONF_RESET), + peak=self._config.get(CONF_PEAK), ) # run - self._ffmpeg.open_sensor( - input_source=config.get(CONF_INPUT), - output_dest=config.get(CONF_OUTPUT), - extra_cmd=config.get(CONF_EXTRA_ARGUMENTS), + return self._ffmpeg.open_sensor( + input_source=self._config.get(CONF_INPUT), + output_dest=self._config.get(CONF_OUTPUT), + extra_cmd=self._config.get(CONF_EXTRA_ARGUMENTS), ) @property @@ -202,20 +253,23 @@ class FFmpegNoise(FFmpegBinarySensor): class FFmpegMotion(FFmpegBinarySensor): """A binary sensor which use ffmpeg for noise detection.""" - def _start_ffmpeg(self, config): - """Start a FFmpeg instance.""" + def async_start_ffmpeg(self): + """Start a FFmpeg instance. + + This method must be run in the event loop and returns a coroutine. + """ # init config self._ffmpeg.set_options( - time_reset=config.get(CONF_RESET), - time_repeat=config.get(CONF_REPEAT_TIME), - repeat=config.get(CONF_REPEAT), - changes=config.get(CONF_CHANGES), + time_reset=self._config.get(CONF_RESET), + time_repeat=self._config.get(CONF_REPEAT_TIME), + repeat=self._config.get(CONF_REPEAT), + changes=self._config.get(CONF_CHANGES), ) # run - self._ffmpeg.open_sensor( - input_source=config.get(CONF_INPUT), - extra_cmd=config.get(CONF_EXTRA_ARGUMENTS), + return self._ffmpeg.open_sensor( + input_source=self._config.get(CONF_INPUT), + extra_cmd=self._config.get(CONF_EXTRA_ARGUMENTS), ) @property diff --git a/homeassistant/components/binary_sensor/services.yaml b/homeassistant/components/binary_sensor/services.yaml index 9be9915e268..a1ac8cf8b5d 100644 --- a/homeassistant/components/binary_sensor/services.yaml +++ b/homeassistant/components/binary_sensor/services.yaml @@ -1,7 +1,23 @@ # Describes the format for available binary_sensor services +ffmpeg_start: + description: Send a start command to a ffmpeg based sensor. + + fields: + entity_id: + description: Name(s) of entites that will start. Platform dependent. + example: 'binary_sensor.ffmpeg_noise' + +ffmpeg_stop: + description: Send a stop command to a ffmpeg based sensor. + + fields: + entity_id: + description: Name(s) of entites that will stop. Platform dependent. + example: 'binary_sensor.ffmpeg_noise' + ffmpeg_restart: - description: Send a restart command to a ffmpeg based sensor (party mode). + description: Send a restart command to a ffmpeg based sensor. fields: entity_id: diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 0b8d60ab7f5..6b00ae240ed 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -12,10 +12,9 @@ from aiohttp import web from homeassistant.components.camera import Camera, PLATFORM_SCHEMA from homeassistant.components.ffmpeg import ( - async_run_test, get_binary, CONF_INPUT, CONF_EXTRA_ARGUMENTS) + DATA_FFMPEG, CONF_INPUT, CONF_EXTRA_ARGUMENTS) import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_NAME -from homeassistant.util.async import run_coroutine_threadsafe DEPENDENCIES = ['ffmpeg'] @@ -33,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @asyncio.coroutine def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Setup a FFmpeg Camera.""" - if not async_run_test(hass, config.get(CONF_INPUT)): + if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)): return yield from async_add_devices([FFmpegCamera(hass, config)]) @@ -44,20 +43,17 @@ class FFmpegCamera(Camera): def __init__(self, hass, config): """Initialize a FFmpeg camera.""" super().__init__() + + self._manager = hass.data[DATA_FFMPEG] self._name = config.get(CONF_NAME) self._input = config.get(CONF_INPUT) self._extra_arguments = config.get(CONF_EXTRA_ARGUMENTS) - def camera_image(self): - """Return bytes of camera image.""" - return run_coroutine_threadsafe( - self.async_camera_image(), self.hass.loop).result() - @asyncio.coroutine def async_camera_image(self): """Return a still image response from the camera.""" - from haffmpeg import ImageSingleAsync, IMAGE_JPEG - ffmpeg = ImageSingleAsync(get_binary(), loop=self.hass.loop) + from haffmpeg import ImageFrame, IMAGE_JPEG + ffmpeg = ImageFrame(self._manager.binary, loop=self.hass.loop) image = yield from ffmpeg.get_image( self._input, output_format=IMAGE_JPEG, @@ -67,9 +63,9 @@ class FFmpegCamera(Camera): @asyncio.coroutine def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpegAsync + from haffmpeg import CameraMjpeg - stream = CameraMjpegAsync(get_binary(), loop=self.hass.loop) + stream = CameraMjpeg(self._manager.binary, loop=self.hass.loop) yield from stream.open_camera( self._input, extra_cmd=self._extra_arguments) diff --git a/homeassistant/components/ffmpeg.py b/homeassistant/components/ffmpeg.py index f345153e666..56e1cb8c95d 100644 --- a/homeassistant/components/ffmpeg.py +++ b/homeassistant/components/ffmpeg.py @@ -10,13 +10,14 @@ import logging import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.util.async import run_coroutine_threadsafe DOMAIN = 'ffmpeg' -REQUIREMENTS = ["ha-ffmpeg==0.15"] +REQUIREMENTS = ["ha-ffmpeg==1.0"] _LOGGER = logging.getLogger(__name__) +DATA_FFMPEG = 'ffmpeg' + CONF_INPUT = 'input' CONF_FFMPEG_BIN = 'ffmpeg_bin' CONF_EXTRA_ARGUMENTS = 'extra_arguments' @@ -34,53 +35,54 @@ CONFIG_SCHEMA = vol.Schema({ }, extra=vol.ALLOW_EXTRA) -FFMPEG_CONFIG = { - CONF_FFMPEG_BIN: DEFAULT_BINARY, - CONF_RUN_TEST: DEFAULT_RUN_TEST, -} -FFMPEG_TEST_CACHE = {} - - -def setup(hass, config): - """Setup the FFmpeg component.""" - if DOMAIN in config: - FFMPEG_CONFIG.update(config.get(DOMAIN)) - return True - - -def get_binary(): - """Return ffmpeg binary from config. - - Async friendly. - """ - return FFMPEG_CONFIG.get(CONF_FFMPEG_BIN) - - -def run_test(hass, input_source): - """Run test on this input. TRUE is deactivate or run correct.""" - return run_coroutine_threadsafe( - async_run_test(hass, input_source), hass.loop).result() - - @asyncio.coroutine -def async_run_test(hass, input_source): - """Run test on this input. TRUE is deactivate or run correct. +def async_setup(hass, config): + """Setup the FFmpeg component.""" + conf = config.get(DOMAIN, {}) - This method must be run in the event loop. - """ - from haffmpeg import TestAsync + hass.data[DATA_FFMPEG] = FFmpegManager( + hass, + conf.get(CONF_FFMPEG_BIN, DEFAULT_BINARY), + conf.get(CONF_RUN_TEST, DEFAULT_RUN_TEST) + ) - if FFMPEG_CONFIG.get(CONF_RUN_TEST): - # if in cache - if input_source in FFMPEG_TEST_CACHE: - return FFMPEG_TEST_CACHE[input_source] - - # run test - ffmpeg_test = TestAsync(get_binary(), loop=hass.loop) - success = yield from ffmpeg_test.run_test(input_source) - if not success: - _LOGGER.error("FFmpeg '%s' test fails!", input_source) - FFMPEG_TEST_CACHE[input_source] = False - return False - FFMPEG_TEST_CACHE[input_source] = True return True + + +class FFmpegManager(object): + """Helper for ha-ffmpeg.""" + + def __init__(self, hass, ffmpeg_bin, run_test): + """Initialize helper.""" + self.hass = hass + self._cache = {} + self._bin = ffmpeg_bin + self._run_test = run_test + + @property + def binary(self): + """Return ffmpeg binary from config.""" + return self._bin + + @asyncio.coroutine + def async_run_test(self, input_source): + """Run test on this input. TRUE is deactivate or run correct. + + This method must be run in the event loop. + """ + from haffmpeg import Test + + if self._run_test: + # if in cache + if input_source in self._cache: + return self._cache[input_source] + + # run test + ffmpeg_test = Test(self.binary, loop=self.hass.loop) + success = yield from ffmpeg_test.run_test(input_source) + if not success: + _LOGGER.error("FFmpeg '%s' test fails!", input_source) + self._cache[input_source] = False + return False + self._cache[input_source] = True + return True diff --git a/requirements_all.txt b/requirements_all.txt index df8dc7e77b9..26300090067 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -170,7 +170,7 @@ googlemaps==2.4.4 gps3==0.33.3 # homeassistant.components.ffmpeg -ha-ffmpeg==0.15 +ha-ffmpeg==1.0 # homeassistant.components.media_player.philips_js ha-philipsjs==0.0.1 From 14309401d02e9c5f3f4916ad68986887a1c64846 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 Jan 2017 22:10:44 -0800 Subject: [PATCH 086/191] Update frontend --- homeassistant/components/frontend/version.py | 6 +++--- .../components/frontend/www_static/core.js | 8 ++++---- .../components/frontend/www_static/core.js.gz | Bin 32917 -> 33554 bytes .../frontend/www_static/frontend.html | 4 ++-- .../frontend/www_static/frontend.html.gz | Bin 131597 -> 132223 bytes .../www_static/home-assistant-polymer | 2 +- .../panels/ha-panel-dev-service.html | 2 +- .../panels/ha-panel-dev-service.html.gz | Bin 17837 -> 17910 bytes .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 2324 -> 2326 bytes 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 3af14628008..d1a4c4e8f93 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,13 +1,13 @@ """DO NOT MODIFY. Auto-generated by script/fingerprint_frontend.""" FINGERPRINTS = { - "core.js": "22d39af274e1d824ca1302e10971f2d8", - "frontend.html": "61e57194179b27563a05282b58dd4f47", + "core.js": "90c16d2f2c5d52203e2fd5fa2b1ae19c", + "frontend.html": "c8e670c6c9f7c0ea3b971b92ba9013db", "mdi.html": "5bb2f1717206bad0d187c2633062c575", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "panels/ha-panel-dev-event.html": "f19840b9a6a46f57cb064b384e1353f5", "panels/ha-panel-dev-info.html": "3765a371478cc66d677cf6dcc35267c6", - "panels/ha-panel-dev-service.html": "e32bcd3afdf485417a3e20b4fc760776", + "panels/ha-panel-dev-service.html": "1d223225c1c75083738033895ea3e4b5", "panels/ha-panel-dev-state.html": "8257d99a38358a150eafdb23fa6727e0", "panels/ha-panel-dev-template.html": "cbb251acabd5e7431058ed507b70522b", "panels/ha-panel-history.html": "7baeb4bd7d9ce0def4f95eab6f10812e", diff --git a/homeassistant/components/frontend/www_static/core.js b/homeassistant/components/frontend/www_static/core.js index e3134a1ea79..3494529ef9b 100644 --- a/homeassistant/components/frontend/www_static/core.js +++ b/homeassistant/components/frontend/www_static/core.js @@ -1,4 +1,4 @@ -!(function(){"use strict";function t(t){return t&&t.__esModule?t.default:t}function e(t,e){return e={exports:{}},t(e,e.exports),e.exports}function n(t,e){var n=e.authToken,r=e.host;return xe({authToken:n,host:r,isValidating:!0,isInvalid:!1,errorMessage:""})}function r(){return Ve.getInitialState()}function i(t,e){var n=e.errorMessage;return t.withMutations((function(t){return t.set("isValidating",!1).set("isInvalid",!0).set("errorMessage",n)}))}function o(t,e){var n=e.authToken,r=e.host;return Fe({authToken:n,host:r})}function u(){return Ge.getInitialState()}function a(t,e){var n=e.rememberAuth;return n}function s(t){return t.withMutations((function(t){t.set("isStreaming",!0).set("hasError",!1)}))}function c(t){return t.withMutations((function(t){t.set("isStreaming",!1).set("hasError",!0)}))}function f(){return Xe.getInitialState()}function h(t){return{type:"auth",api_password:t}}function l(){return{type:"get_states"}}function p(){return{type:"get_config"}}function _(){return{type:"get_services"}}function d(){return{type:"get_panels"}}function v(t,e,n){var r={type:"call_service",domain:t,service:e};return n&&(r.service_data=n),r}function y(t){var e={type:"subscribe_events"};return t&&(e.event_type=t),e}function m(t){return{type:"unsubscribe_events",subscription:t}}function g(){return{type:"ping"}}function S(t,e){return{type:"result",success:!1,error:{code:t,message:e}}}function b(t){return t.result}function E(t,e){var n=new tn(t,e);return n.connect()}function I(t,e,n,r){void 0===r&&(r=null);var i=t.evaluate(Mo.authInfo),o=i.host+"/api/"+n;return new Promise(function(t,n){var u=new XMLHttpRequest;u.open(e,o,!0),u.setRequestHeader("X-HA-access",i.authToken),u.onload=function(){var e;try{e="application/json"===u.getResponseHeader("content-type")?JSON.parse(u.responseText):u.responseText}catch(t){e=u.responseText}u.status>199&&u.status<300?t(e):n(e)},u.onerror=function(){return n({})},r?(u.setRequestHeader("Content-Type","application/json;charset=UTF-8"),u.send(JSON.stringify(r))):u.send()})}function O(t,e){var n=e.model,r=e.result,i=e.params,o=n.entity;if(!r)return t;var u=i.replace?sn({}):t.get(o),a=Array.isArray(r)?r:[r],s=n.fromJSON||sn;return t.set(o,u.withMutations((function(t){for(var e=0;e6e4}function mt(t,e){var n=e.date;return n.toISOString()}function gt(){return Qr.getInitialState()}function St(t,e){var n=e.date,r=e.stateHistory;return 0===r.length?t.set(n,$r({})):t.withMutations((function(t){r.forEach((function(e){return t.setIn([n,e[0].entity_id],$r(e.map(In.fromJSON)))}))}))}function bt(){return ti.getInitialState()}function Et(t,e){var n=e.stateHistory;return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,ii(e.map(In.fromJSON)))}))}))}function It(){return oi.getInitialState()}function Ot(t,e){var n=e.stateHistory,r=(new Date).getTime();return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,r)})),history.length>1&&t.set(si,r)}))}function wt(){return ci.getInitialState()}function Tt(t,e){t.dispatch(Wr.ENTITY_HISTORY_DATE_SELECTED,{date:e})}function At(t,e){void 0===e&&(e=null),t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_START,{});var n="history/period";return null!==e&&(n+="?filter_entity_id="+e),on(t,"GET",n).then((function(e){return t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_SUCCESS,{stateHistory:e})}),(function(){return t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_ERROR,{})}))}function Dt(t,e){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_START,{date:e}),on(t,"GET","history/period/"+e).then((function(n){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_SUCCESS,{date:e,stateHistory:n})}),(function(){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_ERROR,{})}))}function Ct(t){var e=t.evaluate(li);return Dt(t,e)}function zt(t){t.registerStores({currentEntityHistoryDate:Qr,entityHistory:ti,isLoadingEntityHistory:ni,recentEntityHistory:oi,recentEntityHistoryUpdated:ci})}function Rt(t){t.registerStores({moreInfoEntityId:Yr})}function Mt(t,e){var n=e.model,r=e.result,i=e.params;if(null===t||"entity"!==n.entity||!i.replace)return t;for(var o=0;o0?i=setTimeout(r,e-c):(i=null,n||(s=t.apply(u,o),i||(u=o=null)))}var i,o,u,a,s;null==e&&(e=100);var c=function(){u=this,o=arguments,a=(new Date).getTime();var c=n&&!i;return i||(i=setTimeout(r,e)),c&&(s=t.apply(u,o),u=o=null),s};return c.clear=function(){i&&(clearTimeout(i),i=null)},c}function Yt(t){var e=fo[t.hassId];e&&(e.scheduleHealthCheck.clear(),e.conn.close(),fo[t.hassId]=!1)}function Jt(t,e){void 0===e&&(e={});var n=e.syncOnInitialConnect;void 0===n&&(n=!0),Yt(t);var r=t.evaluate(Mo.authToken),i="https:"===document.location.protocol?"wss://":"ws://";i+=document.location.hostname,document.location.port&&(i+=":"+document.location.port),i+="/api/websocket",E(i,{authToken:r}).then((function(e){var r=Bt((function(){return e.ping()}),so);r(),e.socket.addEventListener("message",r),fo[t.hassId]={conn:e,scheduleHealthCheck:r},co.forEach((function(n){return e.subscribeEvents(ao.bind(null,t),n)})),t.batch((function(){t.dispatch(Ye.STREAM_START),n&&io.fetchAll(t)})),e.addEventListener("disconnected",(function(){t.dispatch(Ye.STREAM_ERROR)})),e.addEventListener("ready",(function(){t.batch((function(){t.dispatch(Ye.STREAM_START),io.fetchAll(t)}))}))}))}function Wt(t){t.registerStores({streamStatus:Xe})}function Xt(t,e,n){void 0===n&&(n={});var r=n.rememberAuth;void 0===r&&(r=!1);var i=n.host;void 0===i&&(i=""),t.dispatch(Ue.VALIDATING_AUTH_TOKEN,{authToken:e,host:i}),io.fetchAll(t).then((function(){t.dispatch(Ue.VALID_AUTH_TOKEN,{authToken:e,host:i,rememberAuth:r}),vo.start(t,{syncOnInitialConnect:!1})}),(function(e){void 0===e&&(e={});var n=e.message;void 0===n&&(n=go),t.dispatch(Ue.INVALID_AUTH_TOKEN,{errorMessage:n})}))}function Qt(t){t.dispatch(Ue.LOG_OUT,{})}function Zt(t){t.registerStores({authAttempt:Ve,authCurrent:Ge,rememberAuth:Be})}function $t(){if(!("localStorage"in window))return{};var t=window.localStorage,e="___test";try{return t.setItem(e,e),t.removeItem(e),t}catch(t){return{}}}function te(){var t=new Uo({debug:!1});return t.hassId=Ho++,t}function ee(t,e,n){Object.keys(n).forEach((function(r){var i=n[r];if("register"in i&&i.register(e),"getters"in i&&Object.defineProperty(t,r+"Getters",{value:i.getters,enumerable:!0}),"actions"in i){var o={};Object.getOwnPropertyNames(i.actions).forEach((function(t){"function"==typeof i.actions[t]&&Object.defineProperty(o,t,{value:i.actions[t].bind(null,e),enumerable:!0})})),Object.defineProperty(t,r+"Actions",{value:o,enumerable:!0})}}))}function ne(t,e){return xo(t.attributes.entity_id.map((function(t){return e.get(t)})).filter((function(t){return!!t})))}function re(t){return on(t,"GET","error_log")}function ie(t,e){var n=e.date;return n.toISOString()}function oe(){return Jo.getInitialState()}function ue(t,e){var n=e.date,r=e.entries;return t.set(n,eu(r.map($o.fromJSON)))}function ae(){return nu.getInitialState()}function se(t,e){var n=e.date;return t.set(n,(new Date).getTime())}function ce(){return ou.getInitialState()}function fe(t,e){t.dispatch(Bo.LOGBOOK_DATE_SELECTED,{date:e})}function he(t,e){t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_START,{date:e}),on(t,"GET","logbook/"+e).then((function(n){return t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_SUCCESS,{date:e,entries:n})}),(function(){return t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_ERROR,{})}))}function le(t){return!t||(new Date).getTime()-t>su}function pe(t){t.registerStores({currentLogbookDate:Jo,isLoadingLogbookEntries:Xo,logbookEntries:nu,logbookEntriesUpdated:ou})}function _e(t){return t.set("active",!0)}function de(t){return t.set("active",!1)}function ve(){return Su.getInitialState()}function ye(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered.");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){var n;return n=navigator.userAgent.toLowerCase().indexOf("firefox")>-1?"firefox":"chrome",on(t,"POST","notify.html5",{subscription:e,browser:n}).then((function(){return t.dispatch(yu.PUSH_NOTIFICATIONS_SUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n;return n=e.message&&e.message.indexOf("gcm_sender_id")!==-1?"Please setup the notify.html5 platform.":"Notification registration failed.",console.error(e),Vn.createNotification(t,n),!1}))}function me(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){return on(t,"DELETE","notify.html5",{subscription:e}).then((function(){return e.unsubscribe()})).then((function(){return t.dispatch(yu.PUSH_NOTIFICATIONS_UNSUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n="Failed unsubscribing for push notifications.";return console.error(e),Vn.createNotification(t,n),!1}))}function ge(t){t.registerStores({pushNotifications:Su})}function Se(t,e){return on(t,"POST","template",{template:e})}function be(t){return t.set("isListening",!0)}function Ee(t,e){var n=e.interimTranscript,r=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!0).set("isTransmitting",!1).set("interimTranscript",n).set("finalTranscript",r)}))}function Ie(t,e){var n=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!1).set("isTransmitting",!0).set("interimTranscript","").set("finalTranscript",n)}))}function Oe(){return ku.getInitialState()}function we(){return ku.getInitialState()}function Te(){return ku.getInitialState()}function Ae(t){return Pu[t.hassId]}function De(t){var e=Ae(t);if(e){var n=e.finalTranscript||e.interimTranscript;t.dispatch(ju.VOICE_TRANSMITTING,{finalTranscript:n}),tr.callService(t,"conversation","process",{text:n}).then((function(){t.dispatch(ju.VOICE_DONE)}),(function(){t.dispatch(ju.VOICE_ERROR)}))}}function Ce(t){var e=Ae(t);e&&(e.recognition.stop(),Pu[t.hassId]=!1)}function ze(t){De(t),Ce(t)}function Re(t){var e=ze.bind(null,t);e();var n=new webkitSpeechRecognition;Pu[t.hassId]={recognition:n,interimTranscript:"",finalTranscript:""},n.interimResults=!0,n.onstart=function(){return t.dispatch(ju.VOICE_START)},n.onerror=function(){return t.dispatch(ju.VOICE_ERROR)},n.onend=e,n.onresult=function(e){var n=Ae(t);if(n){for(var r="",i="",o=e.resultIndex;o=n)}function c(t,e){return h(t,e,0)}function f(t,e){return h(t,e,e)}function h(t,e,n){return void 0===t?n:t<0?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function l(t){return v(t)?t:C(t)}function p(t){return y(t)?t:z(t)}function _(t){return m(t)?t:R(t)}function d(t){return v(t)&&!g(t)?t:M(t)}function v(t){return!(!t||!t[dn])}function y(t){return!(!t||!t[vn])}function m(t){return!(!t||!t[yn])}function g(t){return y(t)||m(t)}function S(t){return!(!t||!t[mn])}function b(t){this.next=t}function E(t,e,n,r){var i=0===t?e:1===t?n:[e,n];return r?r.value=i:r={value:i,done:!1},r}function I(){return{value:void 0,done:!0}}function O(t){return!!A(t)}function w(t){return t&&"function"==typeof t.next}function T(t){var e=A(t);return e&&e.call(t)}function A(t){var e=t&&(En&&t[En]||t[In]);if("function"==typeof e)return e}function D(t){return t&&"number"==typeof t.length}function C(t){return null===t||void 0===t?U():v(t)?t.toSeq():V(t)}function z(t){return null===t||void 0===t?U().toKeyedSeq():v(t)?y(t)?t.toSeq():t.fromEntrySeq():H(t)}function R(t){return null===t||void 0===t?U():v(t)?y(t)?t.entrySeq():t.toIndexedSeq():x(t)}function M(t){return(null===t||void 0===t?U():v(t)?y(t)?t.entrySeq():t:x(t)).toSetSeq()}function j(t){this._array=t,this.size=t.length}function L(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function N(t){this._iterable=t,this.size=t.length||t.size}function k(t){this._iterator=t,this._iteratorCache=[]}function P(t){return!(!t||!t[wn])}function U(){return Tn||(Tn=new j([]))}function H(t){var e=Array.isArray(t)?new j(t).fromEntrySeq():w(t)?new k(t).fromEntrySeq():O(t)?new N(t).fromEntrySeq():"object"==typeof t?new L(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function x(t){var e=q(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function V(t){var e=q(t)||"object"==typeof t&&new L(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function q(t){return D(t)?new j(t):w(t)?new k(t):O(t)?new N(t):void 0}function F(t,e,n,r){var i=t._cache;if(i){for(var o=i.length-1,u=0;u<=o;u++){var a=i[n?o-u:u];if(e(a[1],r?a[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,n)}function G(t,e,n,r){var i=t._cache;if(i){var o=i.length-1,u=0;return new b(function(){var t=i[n?o-u:u];return u++>o?I():E(e,r?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,n)}function K(){throw TypeError("Abstract")}function B(){}function Y(){}function J(){}function W(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return!("function"!=typeof t.equals||"function"!=typeof e.equals||!t.equals(e))}function X(t,e){return e?Q(e,t,"",{"":t}):Z(t)}function Q(t,e,n,r){return Array.isArray(e)?t.call(r,n,R(e).map((function(n,r){return Q(t,n,r,e)}))):$(e)?t.call(r,n,z(e).map((function(n,r){return Q(t,n,r,e)}))):e}function Z(t){return Array.isArray(t)?R(t).map(Z).toList():$(t)?z(t).map(Z).toMap():t}function $(t){return t&&(t.constructor===Object||void 0===t.constructor)}function tt(t){return t>>>1&1073741824|3221225471&t}function et(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){var n=0|t;for(n!==t&&(n^=4294967295*t);t>4294967295;)t/=4294967295,n^=t;return tt(n)}return"string"===e?t.length>Ln?nt(t):rt(t):"function"==typeof t.hashCode?t.hashCode():it(t)}function nt(t){var e=Pn[t];return void 0===e&&(e=rt(t),kn===Nn&&(kn=0,Pn={}),kn++,Pn[t]=e),e}function rt(t){for(var e=0,n=0;n0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ut(t,e){if(!t)throw new Error(e)}function at(t){ut(t!==1/0,"Cannot perform this action with an infinite size.")}function st(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ct(t){this._iter=t,this.size=t.size}function ft(t){this._iter=t,this.size=t.size}function ht(t){this._iter=t,this.size=t.size}function lt(t){var e=jt(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=Lt,e.__iterateUncached=function(e,n){var r=this;return t.__iterate((function(t,n){return e(n,t,r)!==!1}),n)},e.__iteratorUncached=function(e,n){if(e===bn){var r=t.__iterator(e,n);return new b(function(){var t=r.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===Sn?gn:Sn,n)},e}function pt(t,e,n){var r=jt(t);return r.size=t.size,r.has=function(e){return t.has(e)},r.get=function(r,i){var o=t.get(r,ln);return o===ln?i:e.call(n,o,r,t)},r.__iterateUncached=function(r,i){var o=this;return t.__iterate((function(t,i,u){return r(e.call(n,t,i,u),i,o)!==!1}),i)},r.__iteratorUncached=function(r,i){var o=t.__iterator(bn,i);return new b(function(){var i=o.next();if(i.done)return i;var u=i.value,a=u[0];return E(r,a,e.call(n,u[1],a,t),i)})},r}function _t(t,e){var n=jt(t);return n._iter=t,n.size=t.size,n.reverse=function(){return t},t.flip&&(n.flip=function(){var e=lt(t);return e.reverse=function(){return t.flip()},e}),n.get=function(n,r){return t.get(e?n:-1-n,r)},n.has=function(n){return t.has(e?n:-1-n)},n.includes=function(e){return t.includes(e)},n.cacheResult=Lt,n.__iterate=function(e,n){var r=this;return t.__iterate((function(t,n){return e(t,n,r)}),!n)},n.__iterator=function(e,n){return t.__iterator(e,!n)},n}function dt(t,e,n,r){var i=jt(t);return r&&(i.has=function(r){var i=t.get(r,ln);return i!==ln&&!!e.call(n,i,r,t)},i.get=function(r,i){var o=t.get(r,ln);return o!==ln&&e.call(n,o,r,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,a=0;return t.__iterate((function(t,o,s){if(e.call(n,t,o,s))return a++,i(t,r?o:a-1,u)}),o),a},i.__iteratorUncached=function(i,o){var u=t.__iterator(bn,o),a=0;return new b(function(){for(;;){var o=u.next();if(o.done)return o;var s=o.value,c=s[0],f=s[1];if(e.call(n,f,c,t))return E(i,r?c:a++,f,o)}})},i}function vt(t,e,n){var r=Pt().asMutable();return t.__iterate((function(i,o){r.update(e.call(n,i,o,t),0,(function(t){return t+1}))})),r.asImmutable()}function yt(t,e,n){var r=y(t),i=(S(t)?Ie():Pt()).asMutable();t.__iterate((function(o,u){i.update(e.call(n,o,u,t),(function(t){return t=t||[],t.push(r?[u,o]:o),t}))}));var o=Mt(t);return i.map((function(e){return Ct(t,o(e))}))}function mt(t,e,n,r){var i=t.size;if(void 0!==e&&(e|=0),void 0!==n&&(n|=0),s(e,n,i))return t;var o=c(e,i),a=f(n,i);if(o!==o||a!==a)return mt(t.toSeq().cacheResult(),e,n,r);var h,l=a-o;l===l&&(h=l<0?0:l);var p=jt(t);return p.size=0===h?h:t.size&&h||void 0,!r&&P(t)&&h>=0&&(p.get=function(e,n){return e=u(this,e),e>=0&&eh)return I();var t=i.next();return r||e===Sn?t:e===gn?E(e,a-1,void 0,t):E(e,a-1,t.value[1],t)})},p}function gt(t,e,n){var r=jt(t);return r.__iterateUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterate(r,i);var u=0;return t.__iterate((function(t,i,a){return e.call(n,t,i,a)&&++u&&r(t,i,o)})),u},r.__iteratorUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterator(r,i);var u=t.__iterator(bn,i),a=!0;return new b(function(){if(!a)return I();var t=u.next();if(t.done)return t;var i=t.value,s=i[0],c=i[1];return e.call(n,c,s,o)?r===bn?t:E(r,s,c,t):(a=!1,I())})},r}function St(t,e,n,r){var i=jt(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var a=!0,s=0;return t.__iterate((function(t,o,c){if(!a||!(a=e.call(n,t,o,c)))return s++,i(t,r?o:s-1,u)})),s},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var a=t.__iterator(bn,o),s=!0,c=0;return new b(function(){var t,o,f;do{if(t=a.next(),t.done)return r||i===Sn?t:i===gn?E(i,c++,void 0,t):E(i,c++,t.value[1],t);var h=t.value;o=h[0],f=h[1],s&&(s=e.call(n,f,o,u))}while(s);return i===bn?t:E(i,o,f,t)})},i}function bt(t,e){var n=y(t),r=[t].concat(e).map((function(t){return v(t)?n&&(t=p(t)):t=n?H(t):x(Array.isArray(t)?t:[t]),t})).filter((function(t){return 0!==t.size}));if(0===r.length)return t;if(1===r.length){var i=r[0];if(i===t||n&&y(i)||m(t)&&m(i))return i}var o=new j(r);return n?o=o.toKeyedSeq():m(t)||(o=o.toSetSeq()),o=o.flatten(!0),o.size=r.reduce((function(t,e){if(void 0!==t){var n=e.size;if(void 0!==n)return t+n}}),0),o}function Et(t,e,n){var r=jt(t);return r.__iterateUncached=function(r,i){function o(t,s){var c=this;t.__iterate((function(t,i){return(!e||s0}function Dt(t,e,n){var r=jt(t);return r.size=new j(n).map((function(t){return t.size})).min(),r.__iterate=function(t,e){for(var n,r=this,i=this.__iterator(Sn,e),o=0;!(n=i.next()).done&&t(n.value,o++,r)!==!1;);return o},r.__iteratorUncached=function(t,r){var i=n.map((function(t){return t=l(t),T(r?t.reverse():t)})),o=0,u=!1; -return new b(function(){var n;return u||(n=i.map((function(t){return t.next()})),u=n.some((function(t){return t.done}))),u?I():E(t,o++,e.apply(null,n.map((function(t){return t.value}))))})},r}function Ct(t,e){return P(t)?e:t.constructor(e)}function zt(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function Rt(t){return at(t.size),o(t)}function Mt(t){return y(t)?p:m(t)?_:d}function jt(t){return Object.create((y(t)?z:m(t)?R:M).prototype)}function Lt(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):C.prototype.cacheResult.call(this)}function Nt(t,e){return t>e?1:t>>n)&hn,a=(0===n?r:r>>>n)&hn,s=u===a?[Zt(t,e,n+cn,r,i)]:(o=new Ft(e,r,i),u>>=1)u[a]=1&n?e[o++]:void 0;return u[r]=i,new Vt(t,o+1,u)}function ne(t,e,n){for(var r=[],i=0;i>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function ae(t,e,n,r){var o=r?t:i(t);return o[e]=n,o}function se(t,e,n,r){var i=t.length+1;if(r&&e+1===i)return t[e]=n,t;for(var o=new Array(i),u=0,a=0;a0&&ro?0:o-n,c=u-n;return c>fn&&(c=fn),function(){if(i===c)return Yn;var t=e?--c:i++;return r&&r[t]}}function i(t,r,i){var a,s=t&&t.array,c=i>o?0:o-i>>r,f=(u-i>>r)+1;return f>fn&&(f=fn),function(){for(;;){if(a){var t=a();if(t!==Yn)return t;a=null}if(c===f)return Yn;var o=e?--f:c++;a=n(s&&s[o],r-cn,i+(o<=t.size||n<0)return t.withMutations((function(t){n<0?Se(t,n).set(0,r):Se(t,0,n+1).set(n,r)}));n+=t._origin;var i=t._tail,o=t._root,a=e(_n);return n>=Ee(t._capacity)?i=ye(i,t.__ownerID,0,n,r,a):o=ye(o,t.__ownerID,t._level,n,r,a),a.value?t.__ownerID?(t._root=o,t._tail=i,t.__hash=void 0,t.__altered=!0,t):_e(t._origin,t._capacity,t._level,o,i):t}function ye(t,e,r,i,o,u){var a=i>>>r&hn,s=t&&a0){var f=t&&t.array[a],h=ye(f,e,r-cn,i,o,u);return h===f?t:(c=me(t,e),c.array[a]=h,c)}return s&&t.array[a]===o?t:(n(u),c=me(t,e),void 0===o&&a===c.array.length-1?c.array.pop():c.array[a]=o,c)}function me(t,e){return e&&t&&e===t.ownerID?t:new le(t?t.array.slice():[],e)}function ge(t,e){if(e>=Ee(t._capacity))return t._tail;if(e<1<0;)n=n.array[e>>>r&hn],r-=cn;return n}}function Se(t,e,n){void 0!==e&&(e|=0),void 0!==n&&(n|=0);var i=t.__ownerID||new r,o=t._origin,u=t._capacity,a=o+e,s=void 0===n?u:n<0?u+n:o+n;if(a===o&&s===u)return t;if(a>=s)return t.clear();for(var c=t._level,f=t._root,h=0;a+h<0;)f=new le(f&&f.array.length?[void 0,f]:[],i),c+=cn,h+=1<=1<l?new le([],i):_;if(_&&p>l&&acn;y-=cn){var m=l>>>y&hn;v=v.array[m]=me(v.array[m],i)}v.array[l>>>cn&hn]=_}if(s=p)a-=p,s-=p,c=cn,f=null,d=d&&d.removeBefore(i,0,a);else if(a>o||p>>c&hn;if(g!==p>>>c&hn)break;g&&(h+=(1<o&&(f=f.removeBefore(i,c,a-h)),f&&pi&&(i=a.size),v(u)||(a=a.map((function(t){return X(t)}))),r.push(a)}return i>t.size&&(t=t.setSize(i)),ie(t,e,r)}function Ee(t){return t>>cn<=fn&&u.size>=2*o.size?(i=u.filter((function(t,e){return void 0!==t&&a!==e})),r=i.toKeyedSeq().map((function(t){return t[0]})).flip().toMap(),t.__ownerID&&(r.__ownerID=i.__ownerID=t.__ownerID)):(r=o.remove(e),i=a===u.size-1?u.pop():u.set(a,void 0))}else if(s){if(n===u.get(a)[1])return t;r=o,i=u.set(a,[e,n])}else r=o.set(e,u.size),i=u.set(u.size,[e,n]);return t.__ownerID?(t.size=r.size,t._map=r,t._list=i,t.__hash=void 0,t):we(r,i)}function De(t){return null===t||void 0===t?Re():Ce(t)?t:Re().unshiftAll(t)}function Ce(t){return!(!t||!t[Wn])}function ze(t,e,n,r){var i=Object.create(Xn);return i.size=t,i._head=e,i.__ownerID=n,i.__hash=r,i.__altered=!1,i}function Re(){return Qn||(Qn=ze(0))}function Me(t){return null===t||void 0===t?ke():je(t)&&!S(t)?t:ke().withMutations((function(e){var n=d(t);at(n.size),n.forEach((function(t){return e.add(t)}))}))}function je(t){return!(!t||!t[Zn])}function Le(t,e){return t.__ownerID?(t.size=e.size,t._map=e,t):e===t._map?t:0===e.size?t.__empty():t.__make(e)}function Ne(t,e){var n=Object.create($n);return n.size=t?t.size:0,n._map=t,n.__ownerID=e,n}function ke(){return tr||(tr=Ne(Jt()))}function Pe(t){return null===t||void 0===t?xe():Ue(t)?t:xe().withMutations((function(e){var n=d(t);at(n.size),n.forEach((function(t){return e.add(t)}))}))}function Ue(t){return je(t)&&S(t)}function He(t,e){var n=Object.create(er);return n.size=t?t.size:0,n._map=t,n.__ownerID=e,n}function xe(){return nr||(nr=He(Te()))}function Ve(t,e){var n,r=function(o){if(o instanceof r)return o;if(!(this instanceof r))return new r(o);if(!n){n=!0;var u=Object.keys(t);Ge(i,u),i.size=u.length,i._name=e,i._keys=u,i._defaultValues=t}this._map=Pt(o)},i=r.prototype=Object.create(rr);return i.constructor=r,r}function qe(t,e,n){var r=Object.create(Object.getPrototypeOf(t));return r._map=e,r.__ownerID=n,r}function Fe(t){return t._name||t.constructor.name||"Record"}function Ge(t,e){try{e.forEach(Ke.bind(void 0,t))}catch(t){}}function Ke(t,e){Object.defineProperty(t,e,{get:function(){return this.get(e)},set:function(t){ut(this.__ownerID,"Cannot set on an immutable record."),this.set(e,t)}})}function Be(t,e){if(t===e)return!0;if(!v(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||y(t)!==y(e)||m(t)!==m(e)||S(t)!==S(e))return!1;if(0===t.size&&0===e.size)return!0;var n=!g(t);if(S(t)){var r=t.entries();return e.every((function(t,e){var i=r.next().value;return i&&W(i[1],t)&&(n||W(i[0],e))}))&&r.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var o=t;t=e,e=o}var u=!0,a=e.__iterate((function(e,r){if(n?!t.has(e):i?!W(e,t.get(r,ln)):!W(t.get(r,ln),e))return u=!1,!1}));return u&&t.size===a}function Ye(t,e,n){if(!(this instanceof Ye))return new Ye(t,e,n);if(ut(0!==n,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),n=void 0===n?1:Math.abs(n),ee?-1:0}function rn(t){if(t.size===1/0)return 0;var e=S(t),n=y(t),r=e?1:0,i=t.__iterate(n?e?function(t,e){r=31*r+un(et(t),et(e))|0}:function(t,e){r=r+un(et(t),et(e))|0}:e?function(t){r=31*r+et(t)|0}:function(t){r=r+et(t)|0});return on(i,r)}function on(t,e){return e=Dn(e,3432918353),e=Dn(e<<15|e>>>-15,461845907),e=Dn(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=Dn(e^e>>>16,2246822507),e=Dn(e^e>>>13,3266489909),e=tt(e^e>>>16)}function un(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var an=Array.prototype.slice,sn="delete",cn=5,fn=1<r?I():E(t,i,n[e?r-i++:i++])})},t(L,z),L.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},L.prototype.has=function(t){return this._object.hasOwnProperty(t)},L.prototype.__iterate=function(t,e){for(var n=this,r=this._object,i=this._keys,o=i.length-1,u=0;u<=o;u++){var a=i[e?o-u:u];if(t(r[a],a,n)===!1)return u+1}return u},L.prototype.__iterator=function(t,e){var n=this._object,r=this._keys,i=r.length-1,o=0;return new b(function(){var u=r[e?i-o:o];return o++>i?I():E(t,u,n[u])})},L.prototype[mn]=!0,t(N,R),N.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);var r=this._iterable,i=T(r),o=0;if(w(i))for(var u;!(u=i.next()).done&&t(u.value,o++,n)!==!1;);return o},N.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var n=this._iterable,r=T(n);if(!w(r))return new b(I);var i=0;return new b(function(){var e=r.next();return e.done?e:E(t,i++,e.value)})},t(k,R),k.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,i=this._iteratorCache,o=0;o=r.length){var e=n.next();if(e.done)return e;r[i]=e.value}return E(t,i,r[i++])})};var Tn;t(K,l),t(B,K),t(Y,K),t(J,K),K.Keyed=B,K.Indexed=Y,K.Set=J;var An,Dn="function"==typeof Math.imul&&Math.imul(4294967295,2)===-2?Math.imul:function(t,e){t|=0,e|=0;var n=65535&t,r=65535&e;return n*r+((t>>>16)*r+n*(e>>>16)<<16>>>0)|0},Cn=Object.isExtensible,zn=(function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}})(),Rn="function"==typeof WeakMap;Rn&&(An=new WeakMap);var Mn=0,jn="__immutablehash__";"function"==typeof Symbol&&(jn=Symbol(jn));var Ln=16,Nn=255,kn=0,Pn={};t(st,z),st.prototype.get=function(t,e){return this._iter.get(t,e)},st.prototype.has=function(t){return this._iter.has(t)},st.prototype.valueSeq=function(){return this._iter.valueSeq()},st.prototype.reverse=function(){var t=this,e=_t(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},st.prototype.map=function(t,e){var n=this,r=pt(this,t,e);return this._useKeys||(r.valueSeq=function(){return n._iter.toSeq().map(t,e)}),r},st.prototype.__iterate=function(t,e){var n,r=this;return this._iter.__iterate(this._useKeys?function(e,n){return t(e,n,r)}:(n=e?Rt(this):0,function(i){return t(i,e?--n:n++,r)}),e)},st.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var n=this._iter.__iterator(Sn,e),r=e?Rt(this):0;return new b(function(){var i=n.next();return i.done?i:E(t,e?--r:r++,i.value,i)})},st.prototype[mn]=!0,t(ct,R),ct.prototype.includes=function(t){return this._iter.includes(t)},ct.prototype.__iterate=function(t,e){var n=this,r=0;return this._iter.__iterate((function(e){return t(e,r++,n)}),e)},ct.prototype.__iterator=function(t,e){var n=this._iter.__iterator(Sn,e),r=0;return new b(function(){var e=n.next();return e.done?e:E(t,r++,e.value,e)})},t(ft,M),ft.prototype.has=function(t){return this._iter.includes(t)},ft.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){return t(e,e,n)}),e)},ft.prototype.__iterator=function(t,e){var n=this._iter.__iterator(Sn,e);return new b(function(){var e=n.next();return e.done?e:E(t,e.value,e.value,e)})},t(ht,z),ht.prototype.entrySeq=function(){return this._iter.toSeq()},ht.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){if(e){zt(e);var r=v(e);return t(r?e.get(1):e[1],r?e.get(0):e[0],n)}}),e)},ht.prototype.__iterator=function(t,e){var n=this._iter.__iterator(Sn,e);return new b(function(){for(;;){var e=n.next();if(e.done)return e;var r=e.value;if(r){zt(r);var i=v(r);return E(t,i?r.get(0):r[0],i?r.get(1):r[1],e)}}})},ct.prototype.cacheResult=st.prototype.cacheResult=ft.prototype.cacheResult=ht.prototype.cacheResult=Lt,t(Pt,B),Pt.prototype.toString=function(){return this.__toString("Map {","}")},Pt.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},Pt.prototype.set=function(t,e){return Wt(this,t,e)},Pt.prototype.setIn=function(t,e){return this.updateIn(t,ln,(function(){return e}))},Pt.prototype.remove=function(t){return Wt(this,t,ln)},Pt.prototype.deleteIn=function(t){return this.updateIn(t,(function(){return ln}))},Pt.prototype.update=function(t,e,n){return 1===arguments.length?t(this):this.updateIn([t],e,n)},Pt.prototype.updateIn=function(t,e,n){n||(n=e,e=void 0);var r=oe(this,kt(t),e,n);return r===ln?void 0:r},Pt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):Jt()},Pt.prototype.merge=function(){return ne(this,void 0,arguments)},Pt.prototype.mergeWith=function(t){var e=an.call(arguments,1);return ne(this,t,e)},Pt.prototype.mergeIn=function(t){var e=an.call(arguments,1);return this.updateIn(t,Jt(),(function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]}))},Pt.prototype.mergeDeep=function(){return ne(this,re(void 0),arguments)},Pt.prototype.mergeDeepWith=function(t){var e=an.call(arguments,1);return ne(this,re(t),e)},Pt.prototype.mergeDeepIn=function(t){var e=an.call(arguments,1);return this.updateIn(t,Jt(),(function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]}))},Pt.prototype.sort=function(t){return Ie(wt(this,t))},Pt.prototype.sortBy=function(t,e){return Ie(wt(this,e,t))},Pt.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},Pt.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new r)},Pt.prototype.asImmutable=function(){return this.__ensureOwner()},Pt.prototype.wasAltered=function(){return this.__altered},Pt.prototype.__iterator=function(t,e){return new Gt(this,t,e)},Pt.prototype.__iterate=function(t,e){var n=this,r=0;return this._root&&this._root.iterate((function(e){return r++,t(e[1],e[0],n)}),e),r},Pt.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Yt(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Pt.isMap=Ut;var Un="@@__IMMUTABLE_MAP__@@",Hn=Pt.prototype;Hn[Un]=!0,Hn[sn]=Hn.remove,Hn.removeIn=Hn.deleteIn,Ht.prototype.get=function(t,e,n,r){for(var i=this.entries,o=0,u=i.length;o=Vn)return $t(t,f,o,u);var _=t&&t===this.ownerID,d=_?f:i(f);return p?c?h===l-1?d.pop():d[h]=d.pop():d[h]=[o,u]:d.push([o,u]),_?(this.entries=d,this):new Ht(t,d)}},xt.prototype.get=function(t,e,n,r){void 0===e&&(e=et(n));var i=1<<((0===t?e:e>>>t)&hn),o=this.bitmap;return 0===(o&i)?r:this.nodes[ue(o&i-1)].get(t+cn,e,n,r)},xt.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=et(r));var a=(0===e?n:n>>>e)&hn,s=1<=qn)return ee(t,l,c,a,_);if(f&&!_&&2===l.length&&Qt(l[1^h]))return l[1^h];if(f&&_&&1===l.length&&Qt(_))return _;var d=t&&t===this.ownerID,v=f?_?c:c^s:c|s,y=f?_?ae(l,h,_,d):ce(l,h,d):se(l,h,_,d);return d?(this.bitmap=v,this.nodes=y,this):new xt(t,v,y)},Vt.prototype.get=function(t,e,n,r){void 0===e&&(e=et(n));var i=(0===t?e:e>>>t)&hn,o=this.nodes[i];return o?o.get(t+cn,e,n,r):r},Vt.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=et(r));var a=(0===e?n:n>>>e)&hn,s=i===ln,c=this.nodes,f=c[a];if(s&&!f)return this;var h=Xt(f,t,e+cn,n,r,i,o,u);if(h===f)return this;var l=this.count;if(f){if(!h&&(l--,l=0&&t>>e&hn;if(r>=this.array.length)return new le([],t);var i,o=0===r;if(e>0){var u=this.array[r];if(i=u&&u.removeBefore(t,e-cn,n),i===u&&o)return this}if(o&&!i)return this;var a=me(this,t);if(!o)for(var s=0;s>>e&hn;if(r>=this.array.length)return this;var i;if(e>0){var o=this.array[r];if(i=o&&o.removeAfter(t,e-cn,n),i===o&&r===this.array.length-1)return this}var u=me(this,t);return u.array.splice(r+1),i&&(u.array[r]=i),u};var Bn,Yn={};t(Ie,Pt),Ie.of=function(){return this(arguments)},Ie.prototype.toString=function(){return this.__toString("OrderedMap {","}")},Ie.prototype.get=function(t,e){var n=this._map.get(t);return void 0!==n?this._list.get(n)[1]:e},Ie.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):Te()},Ie.prototype.set=function(t,e){return Ae(this,t,e)},Ie.prototype.remove=function(t){return Ae(this,t,ln)},Ie.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Ie.prototype.__iterate=function(t,e){var n=this;return this._list.__iterate((function(e){return e&&t(e[1],e[0],n)}),e)},Ie.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Ie.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),n=this._list.__ensureOwner(t);return t?we(e,n,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=n,this)},Ie.isOrderedMap=Oe,Ie.prototype[mn]=!0,Ie.prototype[sn]=Ie.prototype.remove;var Jn;t(De,Y),De.of=function(){return this(arguments)},De.prototype.toString=function(){return this.__toString("Stack [","]")},De.prototype.get=function(t,e){var n=this._head;for(t=u(this,t);n&&t--;)n=n.next;return n?n.value:e},De.prototype.peek=function(){return this._head&&this._head.value},De.prototype.push=function(){var t=arguments;if(0===arguments.length)return this;for(var e=this.size+arguments.length,n=this._head,r=arguments.length-1;r>=0;r--)n={value:t[r],next:n};return this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):ze(e,n)},De.prototype.pushAll=function(t){if(t=_(t),0===t.size)return this;at(t.size);var e=this.size,n=this._head;return t.reverse().forEach((function(t){e++,n={value:t,next:n}})),this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):ze(e,n)},De.prototype.pop=function(){return this.slice(1)},De.prototype.unshift=function(){return this.push.apply(this,arguments)},De.prototype.unshiftAll=function(t){return this.pushAll(t)},De.prototype.shift=function(){return this.pop.apply(this,arguments)},De.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Re()},De.prototype.slice=function(t,e){if(s(t,e,this.size))return this;var n=c(t,this.size),r=f(e,this.size);if(r!==this.size)return Y.prototype.slice.call(this,t,e);for(var i=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):ze(i,o)},De.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?ze(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},De.prototype.__iterate=function(t,e){var n=this;if(e)return this.reverse().__iterate(t);for(var r=0,i=this._head;i&&t(i.value,r++,n)!==!1;)i=i.next;return r},De.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var n=0,r=this._head;return new b(function(){if(r){var e=r.value;return r=r.next,E(t,n++,e)}return I()})},De.isStack=Ce;var Wn="@@__IMMUTABLE_STACK__@@",Xn=De.prototype;Xn[Wn]=!0,Xn.withMutations=Hn.withMutations,Xn.asMutable=Hn.asMutable,Xn.asImmutable=Hn.asImmutable,Xn.wasAltered=Hn.wasAltered;var Qn;t(Me,J),Me.of=function(){return this(arguments)},Me.fromKeys=function(t){return this(p(t).keySeq())},Me.prototype.toString=function(){return this.__toString("Set {","}")},Me.prototype.has=function(t){return this._map.has(t)},Me.prototype.add=function(t){return Le(this,this._map.set(t,!0))},Me.prototype.remove=function(t){return Le(this,this._map.remove(t))},Me.prototype.clear=function(){return Le(this,this._map.clear())},Me.prototype.union=function(){var t=an.call(arguments,0);return t=t.filter((function(t){return 0!==t.size})),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations((function(e){for(var n=0;n1?" by "+this._step:"")+" ]"},Ye.prototype.get=function(t,e){return this.has(t)?this._start+u(this,t)*this._step:e},Ye.prototype.includes=function(t){var e=(t-this._start)/this._step;return e>=0&&e=0&&nn?I():E(t,o++,u)})},Ye.prototype.equals=function(t){return t instanceof Ye?this._start===t._start&&this._end===t._end&&this._step===t._step:Be(this,t)};var ir;t(Je,R),Je.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Je.prototype.get=function(t,e){return this.has(t)?this._value:e},Je.prototype.includes=function(t){return W(this._value,t)},Je.prototype.slice=function(t,e){var n=this.size;return s(t,e,n)?this:new Je(this._value,f(e,n)-c(t,n))},Je.prototype.reverse=function(){return this},Je.prototype.indexOf=function(t){return W(this._value,t)?0:-1},Je.prototype.lastIndexOf=function(t){return W(this._value,t)?this.size:-1},Je.prototype.__iterate=function(t,e){for(var n=this,r=0;rthis.size?e:this.find((function(e,n){return n===t}),void 0,e)},has:function(t){return t=u(this,t),t>=0&&(void 0!==this.size?this.size===1/0||t-1&&t%1===0&&t<=Number.MAX_VALUE}var i=Function.prototype.bind;e.isString=function(t){return"string"==typeof t||"[object String]"===n(t)},e.isArray=Array.isArray||function(t){return"[object Array]"===n(t)},"function"!=typeof/./&&"object"!=typeof Int8Array?e.isFunction=function(t){return"function"==typeof t||!1}:e.isFunction=function(t){return"[object Function]"===toString.call(t)},e.isObject=function(t){var e=typeof t;return"function"===e||"object"===e&&!!t},e.extend=function(t){var e=arguments,n=arguments.length;if(!t||n<2)return t||{};for(var r=1;r0)){var e=this.reactorState.get("dirtyStores");if(0!==e.size){var n=c.default.Set().withMutations((function(n){n.union(t.observerState.get("any")),e.forEach((function(e){var r=t.observerState.getIn(["stores",e]);r&&n.union(r)}))}));n.forEach((function(e){var n=t.observerState.getIn(["observersMap",e]);if(n){var r=n.get("getter"),i=n.get("handler"),o=p.evaluate(t.prevReactorState,r),u=p.evaluate(t.reactorState,r);t.prevReactorState=o.reactorState,t.reactorState=u.reactorState;var a=o.result,s=u.result;c.default.is(a,s)||i.call(null,s)}}));var r=p.resetDirtyStores(this.reactorState);this.prevReactorState=r,this.reactorState=r}}}},{key:"batchStart",value:function(){this.__batchDepth++}},{key:"batchEnd",value:function(){if(this.__batchDepth--,this.__batchDepth<=0){this.__isDispatching=!0;try{this.__notify()}catch(t){throw this.__isDispatching=!1,t}this.__isDispatching=!1}}}]),t})();e.default=(0,y.toFactory)(g),t.exports=e.default},function(t,e,n){function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n={};return(0,o.each)(e,(function(e,r){n[r]=t.evaluate(e)})),n}Object.defineProperty(e,"__esModule",{value:!0});var o=n(4);e.default=function(t){return{getInitialState:function(){return i(t,this.getDataBindings())},componentDidMount:function(){var e=this;this.__unwatchFns=[],(0,o.each)(this.getDataBindings(),(function(n,i){var o=t.observe(n,(function(t){e.setState(r({},i,t))}));e.__unwatchFns.push(o)}))},componentWillUnmount:function(){for(var t=this;this.__unwatchFns.length;)t.__unwatchFns.shift()()}}},t.exports=e.default},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){return new M({result:t,reactorState:e})}function o(t,e){return t.withMutations((function(t){(0,R.each)(e,(function(e,n){t.getIn(["stores",n])&&console.warn("Store already defined for id = "+n);var r=e.getInitialState();if(void 0===r&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store getInitialState() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,D.isImmutableValue)(r))throw new Error("Store getInitialState() must return an immutable value, did you forget to call toImmutable");t.update("stores",(function(t){return t.set(n,e)})).update("state",(function(t){return t.set(n,r)})).update("dirtyStores",(function(t){return t.add(n)})).update("storeStates",(function(t){return I(t,[n])}))})),E(t)}))}function u(t,e){return t.withMutations((function(t){(0,R.each)(e,(function(e,n){t.update("stores",(function(t){return t.set(n,e)}))}))}))}function a(t,e,n){if(void 0===e&&f(t,"throwOnUndefinedActionType"))throw new Error("`dispatch` cannot be called with an `undefined` action type.");var r=t.get("state"),i=t.get("dirtyStores"),o=r.withMutations((function(r){A.default.dispatchStart(t,e,n),t.get("stores").forEach((function(o,u){var a=r.get(u),s=void 0;try{s=o.handle(a,e,n)}catch(e){throw A.default.dispatchError(t,e.message),e}if(void 0===s&&f(t,"throwOnUndefinedStoreReturnValue")){var c="Store handler must return a value, did you forget a return statement";throw A.default.dispatchError(t,c),new Error(c)}r.set(u,s),a!==s&&(i=i.add(u))})),A.default.dispatchEnd(t,r,i)})),u=t.set("state",o).set("dirtyStores",i).update("storeStates",(function(t){return I(t,i)}));return E(u)}function s(t,e){var n=[],r=(0,D.toImmutable)({}).withMutations((function(r){(0,R.each)(e,(function(e,i){var o=t.getIn(["stores",i]);if(o){var u=o.deserialize(e);void 0!==u&&(r.set(i,u),n.push(i))}}))})),i=w.default.Set(n);return t.update("state",(function(t){return t.merge(r)})).update("dirtyStores",(function(t){return t.union(i)})).update("storeStates",(function(t){return I(t,n)}))}function c(t,e,n){var r=e;(0,z.isKeyPath)(e)&&(e=(0,C.fromKeyPath)(e));var i=t.get("nextId"),o=(0,C.getStoreDeps)(e),u=w.default.Map({id:i,storeDeps:o,getterKey:r,getter:e,handler:n}),a=void 0;return a=0===o.size?t.update("any",(function(t){return t.add(i)})):t.withMutations((function(t){o.forEach((function(e){var n=["stores",e];t.hasIn(n)||t.setIn(n,w.default.Set()),t.updateIn(["stores",e],(function(t){return t.add(i)}))}))})),a=a.set("nextId",i+1).setIn(["observersMap",i],u),{observerState:a,entry:u}}function f(t,e){var n=t.getIn(["options",e]);if(void 0===n)throw new Error("Invalid option: "+e);return n}function h(t,e,n){var r=t.get("observersMap").filter((function(t){var r=t.get("getterKey"),i=!n||t.get("handler")===n;return!!i&&((0,z.isKeyPath)(e)&&(0,z.isKeyPath)(r)?(0,z.isEqual)(e,r):e===r)}));return t.withMutations((function(t){r.forEach((function(e){return l(t,e)}))}))}function l(t,e){return t.withMutations((function(t){var n=e.get("id"),r=e.get("storeDeps");0===r.size?t.update("any",(function(t){return t.remove(n)})):r.forEach((function(e){t.updateIn(["stores",e],(function(t){return t?t.remove(n):t}))})),t.removeIn(["observersMap",n])}))}function p(t){var e=t.get("state");return t.withMutations((function(t){var n=t.get("stores"),r=n.keySeq().toJS();n.forEach((function(n,r){var i=e.get(r),o=n.handleReset(i);if(void 0===o&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store handleReset() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,D.isImmutableValue)(o))throw new Error("Store reset state must be an immutable value, did you forget to call toImmutable");t.setIn(["state",r],o)})),t.update("storeStates",(function(t){return I(t,r)})),v(t)}))}function _(t,e){var n=t.get("state");if((0,z.isKeyPath)(e))return i(n.getIn(e),t);if(!(0,C.isGetter)(e))throw new Error("evaluate must be passed a keyPath or Getter");if(g(t,e))return i(b(t,e),t);var r=(0,C.getDeps)(e).map((function(e){return _(t,e).result})),o=(0,C.getComputeFn)(e).apply(null,r);return i(o,S(t,e,o))}function d(t){var e={};return t.get("stores").forEach((function(n,r){var i=t.getIn(["state",r]),o=n.serialize(i);void 0!==o&&(e[r]=o)})),e}function v(t){return t.set("dirtyStores",w.default.Set())}function y(t){return t}function m(t,e){var n=y(e);return t.getIn(["cache",n])}function g(t,e){var n=m(t,e);if(!n)return!1;var r=n.get("storeStates");return 0!==r.size&&r.every((function(e,n){return t.getIn(["storeStates",n])===e}))}function S(t,e,n){var r=y(e),i=t.get("dispatchId"),o=(0,C.getStoreDeps)(e),u=(0,D.toImmutable)({}).withMutations((function(e){o.forEach((function(n){var r=t.getIn(["storeStates",n]);e.set(n,r)}))}));return t.setIn(["cache",r],w.default.Map({value:n,storeStates:u,dispatchId:i}))}function b(t,e){var n=y(e);return t.getIn(["cache",n,"value"])}function E(t){return t.update("dispatchId",(function(t){return t+1}))}function I(t,e){return t.withMutations((function(t){e.forEach((function(e){var n=t.has(e)?t.get(e)+1:1;t.set(e,n)}))}))}Object.defineProperty(e,"__esModule",{value:!0}),e.registerStores=o,e.replaceStores=u,e.dispatch=a,e.loadState=s,e.addObserver=c,e.getOption=f,e.removeObserver=h,e.removeObserverByEntry=l,e.reset=p,e.evaluate=_,e.serialize=d,e.resetDirtyStores=v;var O=n(3),w=r(O),T=n(9),A=r(T),D=n(5),C=n(10),z=n(11),R=n(4),M=w.default.Record({result:null,reactorState:null})},function(t,e,n){var r=n(8);e.dispatchStart=function(t,e,n){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.groupCollapsed("Dispatch: %s",e),console.group("payload"),console.debug(n),console.groupEnd())},e.dispatchError=function(t,e){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.debug("Dispatch error: "+e),console.groupEnd())},e.dispatchEnd=function(t,e,n){(0,r.getOption)(t,"logDispatches")&&console.group&&((0,r.getOption)(t,"logDirtyStores")&&console.log("Stores updated:",n.toList().toJS()),(0,r.getOption)(t,"logAppState")&&console.debug("Dispatch done, new state: ",e.toJS()),console.groupEnd())}},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,l.isArray)(t)&&(0,l.isFunction)(t[t.length-1])}function o(t){return t[t.length-1]}function u(t){return t.slice(0,t.length-1)}function a(t,e){e||(e=h.default.Set());var n=h.default.Set().withMutations((function(e){if(!i(t))throw new Error("getFlattenedDeps must be passed a Getter");u(t).forEach((function(t){if((0,p.isKeyPath)(t))e.add((0,f.List)(t));else{if(!i(t))throw new Error("Invalid getter, each dependency must be a KeyPath or Getter");e.union(a(t))}}))}));return e.union(n)}function s(t){if(!(0,p.isKeyPath)(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,_]}function c(t){if(t.hasOwnProperty("__storeDeps"))return t.__storeDeps;var e=a(t).map((function(t){return t.first()})).filter((function(t){return!!t}));return Object.defineProperty(t,"__storeDeps",{enumerable:!1,configurable:!1,writable:!1,value:e}),e}Object.defineProperty(e,"__esModule",{value:!0});var f=n(3),h=r(f),l=n(4),p=n(11),_=function(t){return t};e.default={isGetter:i,getComputeFn:o,getFlattenedDeps:a,getStoreDeps:c,getDeps:u,fromKeyPath:s},t.exports=e.default},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,s.isArray)(t)&&!(0,s.isFunction)(t[t.length-1])}function o(t,e){var n=a.default.List(t),r=a.default.List(e);return a.default.is(n,r)}Object.defineProperty(e,"__esModule",{value:!0}),e.isKeyPath=i,e.isEqual=o;var u=n(3),a=r(u),s=n(4)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=(0,r.Map)({logDispatches:!1,logAppState:!1,logDirtyStores:!1,throwOnUndefinedActionType:!1,throwOnUndefinedStoreReturnValue:!1,throwOnNonImmutableStore:!1,throwOnDispatchInDispatch:!1});e.PROD_OPTIONS=i;var o=(0,r.Map)({logDispatches:!0,logAppState:!0,logDirtyStores:!0,throwOnUndefinedActionType:!0,throwOnUndefinedStoreReturnValue:!0,throwOnNonImmutableStore:!0,throwOnDispatchInDispatch:!0});e.DEBUG_OPTIONS=o;var u=(0,r.Record)({dispatchId:0,state:(0,r.Map)(),stores:(0,r.Map)(),cache:(0,r.Map)(),storeStates:(0,r.Map)(),dirtyStores:(0,r.Set)(),debug:!1,options:i});e.ReactorState=u;var a=(0,r.Record)({any:(0,r.Set)(),stores:(0,r.Map)({}),observersMap:(0,r.Map)({}),nextId:1});e.ObserverState=a}])}))})),Ne=t(Le),ke=function(t){var e,n={};if(!(t instanceof Object)||Array.isArray(t))throw new Error("keyMirror(...): Argument must be an object.");for(e in t)t.hasOwnProperty(e)&&(n[e]=e);return n},Pe=ke,Ue=Pe({VALIDATING_AUTH_TOKEN:null,VALID_AUTH_TOKEN:null,INVALID_AUTH_TOKEN:null,LOG_OUT:null}),He=Ne.Store,xe=Ne.toImmutable,Ve=new He({getInitialState:function(){return xe({isValidating:!1,authToken:!1,host:null,isInvalid:!1,errorMessage:""})},initialize:function(){this.on(Ue.VALIDATING_AUTH_TOKEN,n),this.on(Ue.VALID_AUTH_TOKEN,r),this.on(Ue.INVALID_AUTH_TOKEN,i)}}),qe=Ne.Store,Fe=Ne.toImmutable,Ge=new qe({getInitialState:function(){return Fe({authToken:null,host:""})},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,o),this.on(Ue.LOG_OUT,u)}}),Ke=Ne.Store,Be=new Ke({getInitialState:function(){return!0},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,a)}}),Ye=Pe({STREAM_START:null,STREAM_STOP:null,STREAM_ERROR:null}),Je=Ne.Store,We=Ne.toImmutable,Xe=new Je({getInitialState:function(){return We({isStreaming:!1,hasError:!1})},initialize:function(){this.on(Ye.STREAM_START,s),this.on(Ye.STREAM_ERROR,c),this.on(Ye.LOG_OUT,f)}}),Qe=1,Ze=2,$e=3,tn=function(t,e){this.url=t,this.options=e||{},this.commandId=1,this.commands={},this.connectionTries=0,this.eventListeners={},this.closeRequested=!1};tn.prototype.addEventListener=function(t,e){var n=this.eventListeners[t];n||(n=this.eventListeners[t]=[]),n.push(e)},tn.prototype.fireEvent=function(t){var e=this;(this.eventListeners[t]||[]).forEach((function(t){return t(e)}))},tn.prototype.connect=function(){var t=this;return new Promise(function(e,n){var r=t.commands;Object.keys(r).forEach((function(t){var e=r[t];e.reject&&e.reject(S($e,"Connection lost"))}));var i=!1;t.connectionTries+=1,t.socket=new WebSocket(t.url),t.socket.addEventListener("open",(function(){t.connectionTries=0})),t.socket.addEventListener("message",(function(o){var u=JSON.parse(o.data);switch(u.type){case"event":t.commands[u.id].eventCallback(u.event);break;case"result":u.success?t.commands[u.id].resolve(u):t.commands[u.id].reject(u.error),delete t.commands[u.id];break;case"pong":break;case"auth_required":t.sendMessage(h(t.options.authToken));break;case"auth_invalid":n(Ze),i=!0;break;case"auth_ok":e(t),t.fireEvent("ready"),t.commandId=1,t.commands={},Object.keys(r).forEach((function(e){var n=r[e];n.eventType&&t.subscribeEvents(n.eventCallback,n.eventType).then((function(t){n.unsubscribe=t}))}))}})),t.socket.addEventListener("close",(function(){if(!i&&!t.closeRequested){0===t.connectionTries?t.fireEvent("disconnected"):n(Qe);var e=1e3*Math.min(t.connectionTries,5);setTimeout((function(){return t.connect()}),e)}}))})},tn.prototype.close=function(){this.closeRequested=!0,this.socket.close()},tn.prototype.getStates=function(){return this.sendMessagePromise(l()).then(b)},tn.prototype.getServices=function(){return this.sendMessagePromise(_()).then(b)},tn.prototype.getPanels=function(){return this.sendMessagePromise(d()).then(b)},tn.prototype.getConfig=function(){return this.sendMessagePromise(p()).then(b)},tn.prototype.callService=function(t,e,n){return this.sendMessagePromise(v(t,e,n))},tn.prototype.subscribeEvents=function(t,e){var n=this;return this.sendMessagePromise(y(e)).then((function(r){var i={eventCallback:t,eventType:e,unsubscribe:function(){return n.sendMessagePromise(m(r.id)).then((function(){delete n.commands[r.id]}))}};return n.commands[r.id]=i,function(){return i.unsubscribe()}}))},tn.prototype.ping=function(){return this.sendMessagePromise(g())},tn.prototype.sendMessage=function(t){this.socket.send(JSON.stringify(t))},tn.prototype.sendMessagePromise=function(t){var e=this;return new Promise(function(n,r){e.commandId+=1;var i=e.commandId;t.id=i,e.commands[i]={resolve:n,reject:r},e.sendMessage(t)})};var en=Pe({API_FETCH_ALL_START:null,API_FETCH_ALL_SUCCESS:null,API_FETCH_ALL_FAIL:null,SYNC_SCHEDULED:null,SYNC_SCHEDULE_CANCELLED:null}),nn=Ne.Store,rn=new nn({getInitialState:function(){return!0},initialize:function(){this.on(en.API_FETCH_ALL_START,(function(){return!0})),this.on(en.API_FETCH_ALL_SUCCESS,(function(){return!1})),this.on(en.API_FETCH_ALL_FAIL,(function(){return!1})),this.on(en.LOG_OUT,(function(){return!1}))}}),on=I,un=Pe({API_FETCH_SUCCESS:null,API_FETCH_START:null,API_FETCH_FAIL:null,API_SAVE_SUCCESS:null,API_SAVE_START:null,API_SAVE_FAIL:null,API_DELETE_SUCCESS:null,API_DELETE_START:null,API_DELETE_FAIL:null,LOG_OUT:null}),an=Ne.Store,sn=Ne.toImmutable,cn=new an({getInitialState:function(){return sn({})},initialize:function(){var t=this;this.on(un.API_FETCH_SUCCESS,O),this.on(un.API_SAVE_SUCCESS,O),this.on(un.API_DELETE_SUCCESS,w),this.on(un.LOG_OUT,(function(){return t.getInitialState()}))}}),fn=Object.prototype.hasOwnProperty,hn=Object.prototype.propertyIsEnumerable,ln=A()?Object.assign:function(t,e){for(var n,r,i=arguments,o=T(t),u=1;u199&&u.status<300?t(e):n(e)},u.onerror=function(){return n({})},r?(u.setRequestHeader("Content-Type","application/json;charset=UTF-8"),u.send(JSON.stringify(r))):u.send()})}function O(t,e){var n=e.model,r=e.result,i=e.params,o=n.entity;if(!r)return t;var u=i.replace?sn({}):t.get(o),a=Array.isArray(r)?r:[r],s=n.fromJSON||sn;return t.set(o,u.withMutations((function(t){for(var e=0;e6e4}function gt(t,e){var n=e.date;return n.toISOString()}function mt(){return Qr.getInitialState()}function St(t,e){var n=e.date,r=e.stateHistory;return 0===r.length?t.set(n,$r({})):t.withMutations((function(t){r.forEach((function(e){return t.setIn([n,e[0].entity_id],$r(e.map(In.fromJSON)))}))}))}function Et(){return ti.getInitialState()}function bt(t,e){var n=e.stateHistory;return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,ii(e.map(In.fromJSON)))}))}))}function It(){return oi.getInitialState()}function Ot(t,e){var n=e.stateHistory,r=(new Date).getTime();return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,r)})),history.length>1&&t.set(si,r)}))}function wt(){return ci.getInitialState()}function Tt(t,e){t.dispatch(Wr.ENTITY_HISTORY_DATE_SELECTED,{date:e})}function At(t,e){void 0===e&&(e=null),t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_START,{});var n="history/period";return null!==e&&(n+="?filter_entity_id="+e),on(t,"GET",n).then((function(e){return t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_SUCCESS,{stateHistory:e})}),(function(){return t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_ERROR,{})}))}function Ct(t,e){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_START,{date:e}),on(t,"GET","history/period/"+e).then((function(n){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_SUCCESS,{date:e,stateHistory:n})}),(function(){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_ERROR,{})}))}function Dt(t){var e=t.evaluate(li);return Ct(t,e)}function zt(t){t.registerStores({currentEntityHistoryDate:Qr,entityHistory:ti,isLoadingEntityHistory:ni,recentEntityHistory:oi,recentEntityHistoryUpdated:ci})}function Rt(t){t.registerStores({moreInfoEntityId:Yr})}function Mt(t,e){var n=e.model,r=e.result,i=e.params;if(null===t||"entity"!==n.entity||!i.replace)return t;for(var o=0;o0?i=setTimeout(r,e-c):(i=null,n||(s=t.apply(u,o),i||(u=o=null)))}var i,o,u,a,s;null==e&&(e=100);var c=function(){u=this,o=arguments,a=(new Date).getTime();var c=n&&!i;return i||(i=setTimeout(r,e)),c&&(s=t.apply(u,o),u=o=null),s};return c.clear=function(){i&&(clearTimeout(i),i=null)},c}function Yt(t){var e=fo[t.hassId];e&&(e.scheduleHealthCheck.clear(),e.conn.close(),fo[t.hassId]=!1)}function Jt(t,e){void 0===e&&(e={});var n=e.syncOnInitialConnect;void 0===n&&(n=!0),Yt(t);var r=t.evaluate(Mo.authToken),i="https:"===document.location.protocol?"wss://":"ws://";i+=document.location.hostname,document.location.port&&(i+=":"+document.location.port),i+="/api/websocket",b(i,{authToken:r}).then((function(e){var r=Bt((function(){return e.ping()}),so);r(),e.socket.addEventListener("message",r),fo[t.hassId]={conn:e,scheduleHealthCheck:r},co.forEach((function(n){return e.subscribeEvents(ao.bind(null,t),n)})),t.batch((function(){t.dispatch(Ye.STREAM_START),n&&io.fetchAll(t)})),e.addEventListener("disconnected",(function(){t.dispatch(Ye.STREAM_ERROR)})),e.addEventListener("ready",(function(){t.batch((function(){t.dispatch(Ye.STREAM_START),io.fetchAll(t)}))}))}))}function Wt(t){t.registerStores({streamStatus:Xe})}function Xt(t,e,n){void 0===n&&(n={});var r=n.rememberAuth;void 0===r&&(r=!1);var i=n.host;void 0===i&&(i=""),t.dispatch(Ue.VALIDATING_AUTH_TOKEN,{authToken:e,host:i}),io.fetchAll(t).then((function(){t.dispatch(Ue.VALID_AUTH_TOKEN,{authToken:e,host:i,rememberAuth:r}),vo.start(t,{syncOnInitialConnect:!1})}),(function(e){void 0===e&&(e={});var n=e.message;void 0===n&&(n=mo),t.dispatch(Ue.INVALID_AUTH_TOKEN,{errorMessage:n})}))}function Qt(t){t.dispatch(Ue.LOG_OUT,{})}function Zt(t){t.registerStores({authAttempt:Ve,authCurrent:Ge,rememberAuth:Be})}function $t(){if(!("localStorage"in window))return{};var t=window.localStorage,e="___test";try{return t.setItem(e,e),t.removeItem(e),t}catch(t){return{}}}function te(){var t=new Uo({debug:!1});return t.hassId=Ho++,t}function ee(t,e,n){Object.keys(n).forEach((function(r){var i=n[r];if("register"in i&&i.register(e),"getters"in i&&Object.defineProperty(t,r+"Getters",{value:i.getters,enumerable:!0}),"actions"in i){var o={};Object.getOwnPropertyNames(i.actions).forEach((function(t){"function"==typeof i.actions[t]&&Object.defineProperty(o,t,{value:i.actions[t].bind(null,e),enumerable:!0})})),Object.defineProperty(t,r+"Actions",{value:o,enumerable:!0})}}))}function ne(t,e){return xo(t.attributes.entity_id.map((function(t){return e.get(t)})).filter((function(t){return!!t})))}function re(t){return on(t,"GET","error_log")}function ie(t,e){var n=e.date;return n.toISOString()}function oe(){return Jo.getInitialState()}function ue(t,e){var n=e.date,r=e.entries;return t.set(n,eu(r.map($o.fromJSON)))}function ae(){return nu.getInitialState()}function se(t,e){var n=e.date;return t.set(n,(new Date).getTime())}function ce(){return ou.getInitialState()}function fe(t,e){t.dispatch(Bo.LOGBOOK_DATE_SELECTED,{date:e})}function he(t,e){t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_START,{date:e}),on(t,"GET","logbook/"+e).then((function(n){return t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_SUCCESS,{date:e,entries:n})}),(function(){return t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_ERROR,{})}))}function le(t){return!t||(new Date).getTime()-t>su}function pe(t){t.registerStores({currentLogbookDate:Jo,isLoadingLogbookEntries:Xo,logbookEntries:nu,logbookEntriesUpdated:ou})}function _e(t){return t.set("active",!0)}function de(t){return t.set("active",!1)}function ve(){return Su.getInitialState()}function ye(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered.");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){var n;return n=navigator.userAgent.toLowerCase().indexOf("firefox")>-1?"firefox":"chrome",on(t,"POST","notify.html5",{subscription:e,browser:n}).then((function(){return t.dispatch(yu.PUSH_NOTIFICATIONS_SUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n;return n=e.message&&e.message.indexOf("gcm_sender_id")!==-1?"Please setup the notify.html5 platform.":"Notification registration failed.",console.error(e),Vn.createNotification(t,n),!1}))}function ge(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){return on(t,"DELETE","notify.html5",{subscription:e}).then((function(){return e.unsubscribe()})).then((function(){return t.dispatch(yu.PUSH_NOTIFICATIONS_UNSUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n="Failed unsubscribing for push notifications.";return console.error(e),Vn.createNotification(t,n),!1}))}function me(t){t.registerStores({pushNotifications:Su})}function Se(t,e){return on(t,"POST","template",{template:e})}function Ee(t){return t.set("isListening",!0)}function be(t,e){var n=e.interimTranscript,r=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!0).set("isTransmitting",!1).set("interimTranscript",n).set("finalTranscript",r)}))}function Ie(t,e){var n=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!1).set("isTransmitting",!0).set("interimTranscript","").set("finalTranscript",n)}))}function Oe(){return Nu.getInitialState()}function we(){return Nu.getInitialState()}function Te(){return Nu.getInitialState()}function Ae(t){return Pu[t.hassId]}function Ce(t){var e=Ae(t);if(e){var n=e.finalTranscript||e.interimTranscript;t.dispatch(Lu.VOICE_TRANSMITTING,{finalTranscript:n}),tr.callService(t,"conversation","process",{text:n}).then((function(){t.dispatch(Lu.VOICE_DONE)}),(function(){t.dispatch(Lu.VOICE_ERROR)}))}}function De(t){var e=Ae(t);e&&(e.recognition.stop(),Pu[t.hassId]=!1)}function ze(t){Ce(t),De(t)}function Re(t){var e=ze.bind(null,t);e();var n=new webkitSpeechRecognition;Pu[t.hassId]={recognition:n,interimTranscript:"",finalTranscript:""},n.interimResults=!0,n.onstart=function(){return t.dispatch(Lu.VOICE_START)},n.onerror=function(){return t.dispatch(Lu.VOICE_ERROR)},n.onend=e,n.onresult=function(e){var n=Ae(t);if(n){for(var r="",i="",o=e.resultIndex;o>>0;if(""+n!==e||4294967295===n)return NaN;e=n}return e<0?_(t)+e:e}function v(){return!0}function y(t,e,n){return(0===t||void 0!==n&&t<=-n)&&(void 0===e||void 0!==n&&e>=n)}function g(t,e){return S(t,e,0)}function m(t,e){return S(t,e,e)}function S(t,e,n){return void 0===t?n:t<0?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function E(t){this.next=t}function b(t,e,n,r){var i=0===t?e:1===t?n:[e,n];return r?r.value=i:r={value:i,done:!1},r}function I(){return{value:void 0,done:!0}}function O(t){return!!A(t)}function w(t){return t&&"function"==typeof t.next}function T(t){var e=A(t);return e&&e.call(t)}function A(t){var e=t&&(In&&t[In]||t[On]);if("function"==typeof e)return e}function C(t){return t&&"number"==typeof t.length}function D(t){return null===t||void 0===t?U():o(t)?t.toSeq():V(t)}function z(t){return null===t||void 0===t?U().toKeyedSeq():o(t)?u(t)?t.toSeq():t.fromEntrySeq():H(t)}function R(t){return null===t||void 0===t?U():o(t)?u(t)?t.entrySeq():t.toIndexedSeq():x(t)}function M(t){return(null===t||void 0===t?U():o(t)?u(t)?t.entrySeq():t:x(t)).toSetSeq()}function L(t){this._array=t,this.size=t.length}function j(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function k(t){this._iterable=t,this.size=t.length||t.size}function N(t){this._iterator=t,this._iteratorCache=[]}function P(t){return!(!t||!t[Tn])}function U(){return An||(An=new L([]))}function H(t){var e=Array.isArray(t)?new L(t).fromEntrySeq():w(t)?new N(t).fromEntrySeq():O(t)?new k(t).fromEntrySeq():"object"==typeof t?new j(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function x(t){var e=q(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function V(t){var e=q(t)||"object"==typeof t&&new j(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function q(t){return C(t)?new L(t):w(t)?new N(t):O(t)?new k(t):void 0}function F(t,e,n,r){var i=t._cache;if(i){for(var o=i.length-1,u=0;u<=o;u++){var a=i[n?o-u:u];if(e(a[1],r?a[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,n)}function G(t,e,n,r){var i=t._cache;if(i){var o=i.length-1,u=0;return new E(function(){var t=i[n?o-u:u];return u++>o?I():b(e,r?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,n)}function K(t,e){return e?B(e,t,"",{"":t}):Y(t)}function B(t,e,n,r){return Array.isArray(e)?t.call(r,n,R(e).map((function(n,r){return B(t,n,r,e)}))):J(e)?t.call(r,n,z(e).map((function(n,r){return B(t,n,r,e)}))):e}function Y(t){return Array.isArray(t)?R(t).map(Y).toList():J(t)?z(t).map(Y).toMap():t}function J(t){return t&&(t.constructor===Object||void 0===t.constructor)}function W(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return!("function"!=typeof t.equals||"function"!=typeof e.equals||!t.equals(e))}function X(t,e){if(t===e)return!0;if(!o(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||u(t)!==u(e)||a(t)!==a(e)||c(t)!==c(e))return!1;if(0===t.size&&0===e.size)return!0;var n=!s(t);if(c(t)){var r=t.entries();return e.every((function(t,e){var i=r.next().value;return i&&W(i[1],t)&&(n||W(i[0],e))}))&&r.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var f=t;t=e,e=f}var h=!0,l=e.__iterate((function(e,r){if(n?!t.has(e):i?!W(e,t.get(r,yn)):!W(t.get(r,yn),e))return h=!1,!1}));return h&&t.size===l}function Q(t,e){if(!(this instanceof Q))return new Q(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(Cn)return Cn;Cn=this}}function Z(t,e){if(!t)throw new Error(e)}function $(t,e,n){if(!(this instanceof $))return new $(t,e,n);if(Z(0!==n,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),n=void 0===n?1:Math.abs(n),e>>1&1073741824|3221225471&t}function ot(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){if(t!==t||t===1/0)return 0;var n=0|t;for(n!==t&&(n^=4294967295*t);t>4294967295;)t/=4294967295,n^=t;return it(n)}if("string"===e)return t.length>Pn?ut(t):at(t);if("function"==typeof t.hashCode)return t.hashCode();if("object"===e)return st(t);if("function"==typeof t.toString)return at(t.toString());throw new Error("Value type "+e+" cannot be hashed.")}function ut(t){var e=xn[t];return void 0===e&&(e=at(t),Hn===Un&&(Hn=0,xn={}),Hn++,xn[t]=e),e}function at(t){for(var e=0,n=0;n0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ft(t){Z(t!==1/0,"Cannot perform this action with an infinite size.")}function ht(t){return null===t||void 0===t?bt():lt(t)&&!c(t)?t:bt().withMutations((function(e){var r=n(t);ft(r.size),r.forEach((function(t,n){return e.set(n,t)}))}))}function lt(t){return!(!t||!t[Vn])}function pt(t,e){this.ownerID=t,this.entries=e}function _t(t,e,n){this.ownerID=t,this.bitmap=e,this.nodes=n}function dt(t,e,n){this.ownerID=t,this.count=e,this.nodes=n}function vt(t,e,n){this.ownerID=t,this.keyHash=e,this.entries=n}function yt(t,e,n){this.ownerID=t,this.keyHash=e,this.entry=n}function gt(t,e,n){this._type=e,this._reverse=n,this._stack=t._root&&St(t._root)}function mt(t,e){return b(t,e[0],e[1])}function St(t,e){return{node:t,index:0,__prev:e}}function Et(t,e,n,r){var i=Object.create(qn);return i.size=t,i._root=e,i.__ownerID=n,i.__hash=r,i.__altered=!1,i}function bt(){return Fn||(Fn=Et(0))}function It(t,e,n){var r,i;if(t._root){var o=f(gn),u=f(mn);if(r=Ot(t._root,t.__ownerID,0,void 0,e,n,o,u),!u.value)return t;i=t.size+(o.value?n===yn?-1:1:0)}else{if(n===yn)return t;i=1,r=new pt(t.__ownerID,[[e,n]])}return t.__ownerID?(t.size=i,t._root=r,t.__hash=void 0,t.__altered=!0,t):r?Et(i,r):bt()}function Ot(t,e,n,r,i,o,u,a){return t?t.update(e,n,r,i,o,u,a):o===yn?t:(h(a),h(u),new yt(e,r,[i,o]))}function wt(t){return t.constructor===yt||t.constructor===vt}function Tt(t,e,n,r,i){if(t.keyHash===r)return new vt(e,r,[t.entry,i]);var o,u=(0===n?t.keyHash:t.keyHash>>>n)&vn,a=(0===n?r:r>>>n)&vn,s=u===a?[Tt(t,e,n+_n,r,i)]:(o=new yt(e,r,i),u>>=1)u[a]=1&n?e[o++]:void 0;return u[r]=i,new dt(t,o+1,u)}function zt(t,e,r){for(var i=[],u=0;u>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function Nt(t,e,n,r){var i=r?t:p(t);return i[e]=n,i}function Pt(t,e,n,r){var i=t.length+1;if(r&&e+1===i)return t[e]=n,t;for(var o=new Array(i),u=0,a=0;a0&&io?0:o-n,c=u-n;return c>dn&&(c=dn),function(){if(i===c)return Xn;var t=e?--c:i++;return r&&r[t]}}function i(t,r,i){var a,s=t&&t.array,c=i>o?0:o-i>>r,f=(u-i>>r)+1;return f>dn&&(f=dn),function(){for(;;){if(a){var t=a();if(t!==Xn)return t;a=null}if(c===f)return Xn;var o=e?--f:c++;a=n(s&&s[o],r-_n,i+(o<=t.size||e<0)return t.withMutations((function(t){e<0?Wt(t,e).set(0,n):Wt(t,0,e+1).set(e,n)}));e+=t._origin;var r=t._tail,i=t._root,o=f(mn);return e>=Qt(t._capacity)?r=Bt(r,t.__ownerID,0,e,n,o):i=Bt(i,t.__ownerID,t._level,e,n,o),o.value?t.__ownerID?(t._root=i,t._tail=r,t.__hash=void 0,t.__altered=!0,t):Ft(t._origin,t._capacity,t._level,i,r):t}function Bt(t,e,n,r,i,o){var u=r>>>n&vn,a=t&&u0){var c=t&&t.array[u],f=Bt(c,e,n-_n,r,i,o);return f===c?t:(s=Yt(t,e),s.array[u]=f,s)}return a&&t.array[u]===i?t:(h(o),s=Yt(t,e),void 0===i&&u===s.array.length-1?s.array.pop():s.array[u]=i,s)}function Yt(t,e){return e&&t&&e===t.ownerID?t:new Vt(t?t.array.slice():[],e)}function Jt(t,e){if(e>=Qt(t._capacity))return t._tail;if(e<1<0;)n=n.array[e>>>r&vn],r-=_n;return n}}function Wt(t,e,n){void 0!==e&&(e|=0),void 0!==n&&(n|=0);var r=t.__ownerID||new l,i=t._origin,o=t._capacity,u=i+e,a=void 0===n?o:n<0?o+n:i+n;if(u===i&&a===o)return t;if(u>=a)return t.clear();for(var s=t._level,c=t._root,f=0;u+f<0;)c=new Vt(c&&c.array.length?[void 0,c]:[],r),s+=_n,f+=1<=1<h?new Vt([],r):_;if(_&&p>h&&u_n;y-=_n){var g=h>>>y&vn;v=v.array[g]=Yt(v.array[g],r)}v.array[h>>>_n&vn]=_}if(a=p)u-=p,a-=p,s=_n,c=null,d=d&&d.removeBefore(r,0,u);else if(u>i||p>>s&vn;if(m!==p>>>s&vn)break;m&&(f+=(1<i&&(c=c.removeBefore(r,s,u-f)),c&&pu&&(u=c.size),o(s)||(c=c.map((function(t){return K(t)}))),i.push(c)}return u>t.size&&(t=t.setSize(u)),Lt(t,e,i)}function Qt(t){return t>>_n<<_n}function Zt(t){return null===t||void 0===t?ee():$t(t)?t:ee().withMutations((function(e){var r=n(t);ft(r.size),r.forEach((function(t,n){return e.set(n,t)}))}))}function $t(t){return lt(t)&&c(t)}function te(t,e,n,r){var i=Object.create(Zt.prototype);return i.size=t?t.size:0,i._map=t,i._list=e,i.__ownerID=n,i.__hash=r,i}function ee(){return Qn||(Qn=te(bt(),Gt()))}function ne(t,e,n){var r,i,o=t._map,u=t._list,a=o.get(e),s=void 0!==a;if(n===yn){if(!s)return t;u.size>=dn&&u.size>=2*o.size?(i=u.filter((function(t,e){return void 0!==t&&a!==e})),r=i.toKeyedSeq().map((function(t){return t[0]})).flip().toMap(),t.__ownerID&&(r.__ownerID=i.__ownerID=t.__ownerID)):(r=o.remove(e),i=a===u.size-1?u.pop():u.set(a,void 0))}else if(s){if(n===u.get(a)[1])return t;r=o,i=u.set(a,[e,n])}else r=o.set(e,u.size),i=u.set(u.size,[e,n]);return t.__ownerID?(t.size=r.size,t._map=r,t._list=i,t.__hash=void 0,t):te(r,i)}function re(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ie(t){this._iter=t,this.size=t.size}function oe(t){this._iter=t,this.size=t.size}function ue(t){this._iter=t,this.size=t.size}function ae(t){var e=Ce(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=De,e.__iterateUncached=function(e,n){var r=this;return t.__iterate((function(t,n){return e(n,t,r)!==!1}),n)},e.__iteratorUncached=function(e,n){if(e===bn){var r=t.__iterator(e,n);return new E(function(){var t=r.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===En?Sn:En,n)},e}function se(t,e,n){var r=Ce(t);return r.size=t.size,r.has=function(e){return t.has(e)},r.get=function(r,i){var o=t.get(r,yn);return o===yn?i:e.call(n,o,r,t)},r.__iterateUncached=function(r,i){var o=this;return t.__iterate((function(t,i,u){return r(e.call(n,t,i,u),i,o)!==!1}),i)},r.__iteratorUncached=function(r,i){var o=t.__iterator(bn,i);return new E(function(){var i=o.next();if(i.done)return i;var u=i.value,a=u[0];return b(r,a,e.call(n,u[1],a,t),i)})},r}function ce(t,e){var n=Ce(t);return n._iter=t,n.size=t.size,n.reverse=function(){return t},t.flip&&(n.flip=function(){var e=ae(t);return e.reverse=function(){return t.flip()},e}),n.get=function(n,r){return t.get(e?n:-1-n,r)},n.has=function(n){return t.has(e?n:-1-n)},n.includes=function(e){return t.includes(e)},n.cacheResult=De,n.__iterate=function(e,n){var r=this;return t.__iterate((function(t,n){return e(t,n,r)}),!n)},n.__iterator=function(e,n){return t.__iterator(e,!n)},n}function fe(t,e,n,r){var i=Ce(t);return r&&(i.has=function(r){var i=t.get(r,yn);return i!==yn&&!!e.call(n,i,r,t)},i.get=function(r,i){var o=t.get(r,yn);return o!==yn&&e.call(n,o,r,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,a=0;return t.__iterate((function(t,o,s){if(e.call(n,t,o,s))return a++,i(t,r?o:a-1,u)}),o),a},i.__iteratorUncached=function(i,o){var u=t.__iterator(bn,o),a=0;return new E(function(){for(;;){var o=u.next();if(o.done)return o;var s=o.value,c=s[0],f=s[1];if(e.call(n,f,c,t))return b(i,r?c:a++,f,o)}})},i}function he(t,e,n){var r=ht().asMutable();return t.__iterate((function(i,o){r.update(e.call(n,i,o,t),0,(function(t){return t+1}))})),r.asImmutable()}function le(t,e,n){var r=u(t),i=(c(t)?Zt():ht()).asMutable();t.__iterate((function(o,u){i.update(e.call(n,o,u,t),(function(t){return t=t||[],t.push(r?[u,o]:o),t}))}));var o=Ae(t);return i.map((function(e){return Oe(t,o(e))}))}function pe(t,e,n,r){var i=t.size;if(void 0!==e&&(e|=0),void 0!==n&&(n===1/0?n=i:n|=0),y(e,n,i))return t;var o=g(e,i),u=m(n,i);if(o!==o||u!==u)return pe(t.toSeq().cacheResult(),e,n,r);var a,s=u-o;s===s&&(a=s<0?0:s);var c=Ce(t);return c.size=0===a?a:t.size&&a||void 0,!r&&P(t)&&a>=0&&(c.get=function(e,n){return e=d(this,e),e>=0&&ea)return I();var t=i.next();return r||e===En?t:e===Sn?b(e,s-1,void 0,t):b(e,s-1,t.value[1],t)})},c}function _e(t,e,n){var r=Ce(t);return r.__iterateUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterate(r,i);var u=0;return t.__iterate((function(t,i,a){return e.call(n,t,i,a)&&++u&&r(t,i,o)})),u},r.__iteratorUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterator(r,i);var u=t.__iterator(bn,i),a=!0;return new E(function(){if(!a)return I();var t=u.next();if(t.done)return t;var i=t.value,s=i[0],c=i[1];return e.call(n,c,s,o)?r===bn?t:b(r,s,c,t):(a=!1,I())})},r}function de(t,e,n,r){var i=Ce(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var a=!0,s=0;return t.__iterate((function(t,o,c){if(!a||!(a=e.call(n,t,o,c)))return s++,i(t,r?o:s-1,u)})),s},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var a=t.__iterator(bn,o),s=!0,c=0;return new E(function(){var t,o,f;do{if(t=a.next(),t.done)return r||i===En?t:i===Sn?b(i,c++,void 0,t):b(i,c++,t.value[1],t);var h=t.value;o=h[0],f=h[1],s&&(s=e.call(n,f,o,u))}while(s);return i===bn?t:b(i,o,f,t)})},i}function ve(t,e){var r=u(t),i=[t].concat(e).map((function(t){return o(t)?r&&(t=n(t)):t=r?H(t):x(Array.isArray(t)?t:[t]),t})).filter((function(t){return 0!==t.size}));if(0===i.length)return t;if(1===i.length){var s=i[0];if(s===t||r&&u(s)||a(t)&&a(s))return s}var c=new L(i);return r?c=c.toKeyedSeq():a(t)||(c=c.toSetSeq()),c=c.flatten(!0),c.size=i.reduce((function(t,e){if(void 0!==t){var n=e.size;if(void 0!==n)return t+n}}),0),c}function ye(t,e,n){var r=Ce(t);return r.__iterateUncached=function(r,i){function u(t,c){var f=this;t.__iterate((function(t,i){return(!e||c0}function Ie(t,n,r){var i=Ce(t);return i.size=new L(r).map((function(t){return t.size})).min(),i.__iterate=function(t,e){for(var n,r=this,i=this.__iterator(En,e),o=0;!(n=i.next()).done&&t(n.value,o++,r)!==!1;);return o},i.__iteratorUncached=function(t,i){var o=r.map((function(t){return t=e(t),T(i?t.reverse():t)})),u=0,a=!1;return new E(function(){var e;return a||(e=o.map((function(t){return t.next()})),a=e.some((function(t){return t.done}))),a?I():b(t,u++,n.apply(null,e.map((function(t){return t.value}))))})},i}function Oe(t,e){return P(t)?e:t.constructor(e)}function we(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function Te(t){return ft(t.size),_(t)}function Ae(t){return u(t)?n:a(t)?r:i}function Ce(t){return Object.create((u(t)?z:a(t)?R:M).prototype)}function De(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):D.prototype.cacheResult.call(this)}function ze(t,e){return t>e?1:te?-1:0}function on(t){if(t.size===1/0)return 0;var e=c(t),n=u(t),r=e?1:0,i=t.__iterate(n?e?function(t,e){r=31*r+an(ot(t),ot(e))|0}:function(t,e){r=r+an(ot(t),ot(e))|0}:e?function(t){r=31*r+ot(t)|0}:function(t){r=r+ot(t)|0});return un(i,r)}function un(t,e){return e=Rn(e,3432918353),e=Rn(e<<15|e>>>-15,461845907),e=Rn(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=Rn(e^e>>>16,2246822507),e=Rn(e^e>>>13,3266489909),e=it(e^e>>>16)}function an(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var sn=Array.prototype.slice;t(n,e),t(r,e),t(i,e),e.isIterable=o,e.isKeyed=u,e.isIndexed=a,e.isAssociative=s,e.isOrdered=c,e.Keyed=n,e.Indexed=r,e.Set=i;var cn="@@__IMMUTABLE_ITERABLE__@@",fn="@@__IMMUTABLE_KEYED__@@",hn="@@__IMMUTABLE_INDEXED__@@",ln="@@__IMMUTABLE_ORDERED__@@",pn="delete",_n=5,dn=1<<_n,vn=dn-1,yn={},gn={value:!1},mn={value:!1},Sn=0,En=1,bn=2,In="function"==typeof Symbol&&Symbol.iterator,On="@@iterator",wn=In||On;E.prototype.toString=function(){return"[Iterator]"},E.KEYS=Sn,E.VALUES=En,E.ENTRIES=bn,E.prototype.inspect=E.prototype.toSource=function(){return this.toString()},E.prototype[wn]=function(){return this},t(D,e),D.of=function(){return D(arguments)},D.prototype.toSeq=function(){return this},D.prototype.toString=function(){return this.__toString("Seq {","}")},D.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},D.prototype.__iterate=function(t,e){return F(this,t,e,!0)},D.prototype.__iterator=function(t,e){return G(this,t,e,!0)},t(z,D),z.prototype.toKeyedSeq=function(){return this},t(R,D),R.of=function(){return R(arguments)},R.prototype.toIndexedSeq=function(){return this},R.prototype.toString=function(){return this.__toString("Seq [","]")},R.prototype.__iterate=function(t,e){return F(this,t,e,!1)},R.prototype.__iterator=function(t,e){return G(this,t,e,!1)},t(M,D),M.of=function(){return M(arguments)},M.prototype.toSetSeq=function(){return this},D.isSeq=P,D.Keyed=z,D.Set=M,D.Indexed=R;var Tn="@@__IMMUTABLE_SEQ__@@";D.prototype[Tn]=!0,t(L,R),L.prototype.get=function(t,e){return this.has(t)?this._array[d(this,t)]:e},L.prototype.__iterate=function(t,e){for(var n=this,r=this._array,i=r.length-1,o=0;o<=i;o++)if(t(r[e?i-o:o],o,n)===!1)return o+1;return o},L.prototype.__iterator=function(t,e){var n=this._array,r=n.length-1,i=0;return new E(function(){return i>r?I():b(t,i,n[e?r-i++:i++])})},t(j,z),j.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},j.prototype.has=function(t){return this._object.hasOwnProperty(t)},j.prototype.__iterate=function(t,e){for(var n=this,r=this._object,i=this._keys,o=i.length-1,u=0;u<=o;u++){var a=i[e?o-u:u];if(t(r[a],a,n)===!1)return u+1}return u},j.prototype.__iterator=function(t,e){var n=this._object,r=this._keys,i=r.length-1,o=0;return new E(function(){var u=r[e?i-o:o];return o++>i?I():b(t,u,n[u])})},j.prototype[ln]=!0,t(k,R),k.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);var r=this._iterable,i=T(r),o=0;if(w(i))for(var u;!(u=i.next()).done&&t(u.value,o++,n)!==!1;);return o},k.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var n=this._iterable,r=T(n);if(!w(r))return new E(I);var i=0;return new E(function(){var e=r.next();return e.done?e:b(t,i++,e.value)})},t(N,R),N.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,i=this._iteratorCache,o=0;o=r.length){var e=n.next();if(e.done)return e;r[i]=e.value}return b(t,i,r[i++])})};var An;t(Q,R),Q.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Q.prototype.get=function(t,e){return this.has(t)?this._value:e},Q.prototype.includes=function(t){return W(this._value,t)},Q.prototype.slice=function(t,e){var n=this.size;return y(t,e,n)?this:new Q(this._value,m(e,n)-g(t,n))},Q.prototype.reverse=function(){return this},Q.prototype.indexOf=function(t){return W(this._value,t)?0:-1},Q.prototype.lastIndexOf=function(t){return W(this._value,t)?this.size:-1},Q.prototype.__iterate=function(t,e){for(var n=this,r=0;r=0&&e=0&&nn?I():b(t,o++,u)})},$.prototype.equals=function(t){return t instanceof $?this._start===t._start&&this._end===t._end&&this._step===t._step:X(this,t)};var Dn;t(tt,e),t(et,tt),t(nt,tt),t(rt,tt),tt.Keyed=et,tt.Indexed=nt,tt.Set=rt;var zn,Rn="function"==typeof Math.imul&&Math.imul(4294967295,2)===-2?Math.imul:function(t,e){t|=0,e|=0;var n=65535&t,r=65535&e;return n*r+((t>>>16)*r+n*(e>>>16)<<16>>>0)|0},Mn=Object.isExtensible,Ln=(function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}})(),jn="function"==typeof WeakMap;jn&&(zn=new WeakMap);var kn=0,Nn="__immutablehash__";"function"==typeof Symbol&&(Nn=Symbol(Nn));var Pn=16,Un=255,Hn=0,xn={};t(ht,et),ht.of=function(){var t=sn.call(arguments,0);return bt().withMutations((function(e){for(var n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},ht.prototype.toString=function(){return this.__toString("Map {","}")},ht.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},ht.prototype.set=function(t,e){return It(this,t,e)},ht.prototype.setIn=function(t,e){return this.updateIn(t,yn,(function(){return e}))},ht.prototype.remove=function(t){return It(this,t,yn)},ht.prototype.deleteIn=function(t){return this.updateIn(t,(function(){return yn}))},ht.prototype.update=function(t,e,n){return 1===arguments.length?t(this):this.updateIn([t],e,n)},ht.prototype.updateIn=function(t,e,n){n||(n=e,e=void 0);var r=jt(this,Re(t),e,n);return r===yn?void 0:r},ht.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):bt()},ht.prototype.merge=function(){return zt(this,void 0,arguments)},ht.prototype.mergeWith=function(t){var e=sn.call(arguments,1);return zt(this,t,e)},ht.prototype.mergeIn=function(t){var e=sn.call(arguments,1);return this.updateIn(t,bt(),(function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]}))},ht.prototype.mergeDeep=function(){return zt(this,Rt,arguments)},ht.prototype.mergeDeepWith=function(t){var e=sn.call(arguments,1);return zt(this,Mt(t),e)},ht.prototype.mergeDeepIn=function(t){var e=sn.call(arguments,1);return this.updateIn(t,bt(),(function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]}))},ht.prototype.sort=function(t){return Zt(Se(this,t))},ht.prototype.sortBy=function(t,e){return Zt(Se(this,e,t))},ht.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},ht.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new l)},ht.prototype.asImmutable=function(){return this.__ensureOwner()},ht.prototype.wasAltered=function(){return this.__altered},ht.prototype.__iterator=function(t,e){return new gt(this,t,e)},ht.prototype.__iterate=function(t,e){var n=this,r=0;return this._root&&this._root.iterate((function(e){return r++,t(e[1],e[0],n)}),e),r},ht.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Et(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},ht.isMap=lt;var Vn="@@__IMMUTABLE_MAP__@@",qn=ht.prototype;qn[Vn]=!0,qn[pn]=qn.remove,qn.removeIn=qn.deleteIn,pt.prototype.get=function(t,e,n,r){for(var i=this.entries,o=0,u=i.length;o=Gn)return At(t,s,r,i);var _=t&&t===this.ownerID,d=_?s:p(s);return l?a?c===f-1?d.pop():d[c]=d.pop():d[c]=[r,i]:d.push([r,i]),_?(this.entries=d,this):new pt(t,d)}},_t.prototype.get=function(t,e,n,r){void 0===e&&(e=ot(n));var i=1<<((0===t?e:e>>>t)&vn),o=this.bitmap;return 0===(o&i)?r:this.nodes[kt(o&i-1)].get(t+_n,e,n,r)},_t.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=ot(r));var a=(0===e?n:n>>>e)&vn,s=1<=Kn)return Dt(t,l,c,a,_);if(f&&!_&&2===l.length&&wt(l[1^h]))return l[1^h];if(f&&_&&1===l.length&&wt(_))return _;var d=t&&t===this.ownerID,v=f?_?c:c^s:c|s,y=f?_?Nt(l,h,_,d):Ut(l,h,d):Pt(l,h,_,d);return d?(this.bitmap=v,this.nodes=y,this):new _t(t,v,y)},dt.prototype.get=function(t,e,n,r){void 0===e&&(e=ot(n));var i=(0===t?e:e>>>t)&vn,o=this.nodes[i];return o?o.get(t+_n,e,n,r):r},dt.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=ot(r));var a=(0===e?n:n>>>e)&vn,s=i===yn,c=this.nodes,f=c[a];if(s&&!f)return this;var h=Ot(f,t,e+_n,n,r,i,o,u);if(h===f)return this;var l=this.count;if(f){if(!h&&(l--,l=0&&t>>e&vn;if(r>=this.array.length)return new Vt([],t);var i,o=0===r;if(e>0){var u=this.array[r];if(i=u&&u.removeBefore(t,e-_n,n),i===u&&o)return this}if(o&&!i)return this;var a=Yt(this,t);if(!o)for(var s=0;s>>e&vn;if(r>=this.array.length)return this;var i;if(e>0){var o=this.array[r];if(i=o&&o.removeAfter(t,e-_n,n),i===o&&r===this.array.length-1)return this}var u=Yt(this,t);return u.array.splice(r+1),i&&(u.array[r]=i),u};var Wn,Xn={};t(Zt,ht),Zt.of=function(){return this(arguments)},Zt.prototype.toString=function(){return this.__toString("OrderedMap {","}")},Zt.prototype.get=function(t,e){var n=this._map.get(t);return void 0!==n?this._list.get(n)[1]:e},Zt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):ee()},Zt.prototype.set=function(t,e){return ne(this,t,e)},Zt.prototype.remove=function(t){return ne(this,t,yn)},Zt.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Zt.prototype.__iterate=function(t,e){var n=this;return this._list.__iterate((function(e){return e&&t(e[1],e[0],n)}),e)},Zt.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Zt.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),n=this._list.__ensureOwner(t);return t?te(e,n,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=n,this)},Zt.isOrderedMap=$t,Zt.prototype[ln]=!0,Zt.prototype[pn]=Zt.prototype.remove;var Qn;t(re,z),re.prototype.get=function(t,e){return this._iter.get(t,e)},re.prototype.has=function(t){return this._iter.has(t)},re.prototype.valueSeq=function(){return this._iter.valueSeq()},re.prototype.reverse=function(){var t=this,e=ce(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},re.prototype.map=function(t,e){var n=this,r=se(this,t,e);return this._useKeys||(r.valueSeq=function(){return n._iter.toSeq().map(t,e)}),r},re.prototype.__iterate=function(t,e){var n,r=this;return this._iter.__iterate(this._useKeys?function(e,n){return t(e,n,r)}:(n=e?Te(this):0,function(i){return t(i,e?--n:n++,r)}),e)},re.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var n=this._iter.__iterator(En,e),r=e?Te(this):0;return new E(function(){var i=n.next();return i.done?i:b(t,e?--r:r++,i.value,i)})},re.prototype[ln]=!0,t(ie,R),ie.prototype.includes=function(t){return this._iter.includes(t)},ie.prototype.__iterate=function(t,e){var n=this,r=0;return this._iter.__iterate((function(e){return t(e,r++,n)}),e)},ie.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e),r=0;return new E(function(){var e=n.next();return e.done?e:b(t,r++,e.value,e)})},t(oe,M),oe.prototype.has=function(t){return this._iter.includes(t)},oe.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){return t(e,e,n)}),e)},oe.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e);return new E(function(){var e=n.next();return e.done?e:b(t,e.value,e.value,e)})},t(ue,z),ue.prototype.entrySeq=function(){return this._iter.toSeq()},ue.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){if(e){we(e);var r=o(e);return t(r?e.get(1):e[1],r?e.get(0):e[0],n)}}),e)},ue.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e);return new E(function(){for(;;){var e=n.next();if(e.done)return e;var r=e.value;if(r){we(r);var i=o(r);return b(t,i?r.get(0):r[0],i?r.get(1):r[1],e)}}})},ie.prototype.cacheResult=re.prototype.cacheResult=oe.prototype.cacheResult=ue.prototype.cacheResult=De,t(Me,et),Me.prototype.toString=function(){return this.__toString(je(this)+" {","}")},Me.prototype.has=function(t){return this._defaultValues.hasOwnProperty(t)},Me.prototype.get=function(t,e){if(!this.has(t))return e;var n=this._defaultValues[t];return this._map?this._map.get(t,n):n},Me.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var t=this.constructor;return t._empty||(t._empty=Le(this,bt()))},Me.prototype.set=function(t,e){if(!this.has(t))throw new Error('Cannot set unknown key "'+t+'" on '+je(this));if(this._map&&!this._map.has(t)){var n=this._defaultValues[t];if(e===n)return this}var r=this._map&&this._map.set(t,e);return this.__ownerID||r===this._map?this:Le(this,r)},Me.prototype.remove=function(t){if(!this.has(t))return this;var e=this._map&&this._map.remove(t);return this.__ownerID||e===this._map?this:Le(this,e)},Me.prototype.wasAltered=function(){return this._map.wasAltered()},Me.prototype.__iterator=function(t,e){var r=this;return n(this._defaultValues).map((function(t,e){return r.get(e)})).__iterator(t,e)},Me.prototype.__iterate=function(t,e){var r=this;return n(this._defaultValues).map((function(t,e){return r.get(e)})).__iterate(t,e)},Me.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map&&this._map.__ensureOwner(t);return t?Le(this,e,t):(this.__ownerID=t,this._map=e,this)};var Zn=Me.prototype;Zn[pn]=Zn.remove,Zn.deleteIn=Zn.removeIn=qn.removeIn,Zn.merge=qn.merge,Zn.mergeWith=qn.mergeWith,Zn.mergeIn=qn.mergeIn,Zn.mergeDeep=qn.mergeDeep,Zn.mergeDeepWith=qn.mergeDeepWith,Zn.mergeDeepIn=qn.mergeDeepIn,Zn.setIn=qn.setIn,Zn.update=qn.update,Zn.updateIn=qn.updateIn,Zn.withMutations=qn.withMutations,Zn.asMutable=qn.asMutable,Zn.asImmutable=qn.asImmutable,t(Pe,rt),Pe.of=function(){return this(arguments)},Pe.fromKeys=function(t){return this(n(t).keySeq())},Pe.prototype.toString=function(){return this.__toString("Set {","}")},Pe.prototype.has=function(t){return this._map.has(t)},Pe.prototype.add=function(t){ +return He(this,this._map.set(t,!0))},Pe.prototype.remove=function(t){return He(this,this._map.remove(t))},Pe.prototype.clear=function(){return He(this,this._map.clear())},Pe.prototype.union=function(){var t=sn.call(arguments,0);return t=t.filter((function(t){return 0!==t.size})),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations((function(e){for(var n=0;n=0;r--)n={value:t[r],next:n};return this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):Je(e,n)},Be.prototype.pushAll=function(t){if(t=r(t),0===t.size)return this;ft(t.size);var e=this.size,n=this._head;return t.reverse().forEach((function(t){e++,n={value:t,next:n}})),this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):Je(e,n)},Be.prototype.pop=function(){return this.slice(1)},Be.prototype.unshift=function(){return this.push.apply(this,arguments)},Be.prototype.unshiftAll=function(t){return this.pushAll(t)},Be.prototype.shift=function(){return this.pop.apply(this,arguments)},Be.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):We()},Be.prototype.slice=function(t,e){if(y(t,e,this.size))return this;var n=g(t,this.size),r=m(e,this.size);if(r!==this.size)return nt.prototype.slice.call(this,t,e);for(var i=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):Je(i,o)},Be.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Je(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Be.prototype.__iterate=function(t,e){var n=this;if(e)return this.reverse().__iterate(t);for(var r=0,i=this._head;i&&t(i.value,r++,n)!==!1;)i=i.next;return r},Be.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var n=0,r=this._head;return new E(function(){if(r){var e=r.value;return r=r.next,b(t,n++,e)}return I()})},Be.isStack=Ye;var ir="@@__IMMUTABLE_STACK__@@",or=Be.prototype;or[ir]=!0,or.withMutations=qn.withMutations,or.asMutable=qn.asMutable,or.asImmutable=qn.asImmutable,or.wasAltered=qn.wasAltered;var ur;e.Iterator=E,Xe(e,{toArray:function(){ft(this.size);var t=new Array(this.size||0);return this.valueSeq().__iterate((function(e,n){t[n]=e})),t},toIndexedSeq:function(){return new ie(this)},toJS:function(){return this.toSeq().map((function(t){return t&&"function"==typeof t.toJS?t.toJS():t})).__toJS()},toJSON:function(){return this.toSeq().map((function(t){return t&&"function"==typeof t.toJSON?t.toJSON():t})).__toJS()},toKeyedSeq:function(){return new re(this,!0)},toMap:function(){return ht(this.toKeyedSeq())},toObject:function(){ft(this.size);var t={};return this.__iterate((function(e,n){t[n]=e})),t},toOrderedMap:function(){return Zt(this.toKeyedSeq())},toOrderedSet:function(){return qe(u(this)?this.valueSeq():this)},toSet:function(){return Pe(u(this)?this.valueSeq():this)},toSetSeq:function(){return new oe(this)},toSeq:function(){return a(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Be(u(this)?this.valueSeq():this)},toList:function(){return Ht(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(t,e){return 0===this.size?t+e:t+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+e},concat:function(){var t=sn.call(arguments,0);return Oe(this,ve(this,t))},includes:function(t){return this.some((function(e){return W(e,t)}))},entries:function(){return this.__iterator(bn)},every:function(t,e){ft(this.size);var n=!0;return this.__iterate((function(r,i,o){if(!t.call(e,r,i,o))return n=!1,!1})),n},filter:function(t,e){return Oe(this,fe(this,t,e,!0))},find:function(t,e,n){var r=this.findEntry(t,e);return r?r[1]:n},forEach:function(t,e){return ft(this.size),this.__iterate(e?t.bind(e):t)},join:function(t){ft(this.size),t=void 0!==t?""+t:",";var e="",n=!0;return this.__iterate((function(r){n?n=!1:e+=t,e+=null!==r&&void 0!==r?r.toString():""})),e},keys:function(){return this.__iterator(Sn)},map:function(t,e){return Oe(this,se(this,t,e))},reduce:function(t,e,n){ft(this.size);var r,i;return arguments.length<2?i=!0:r=e,this.__iterate((function(e,o,u){i?(i=!1,r=e):r=t.call(n,r,e,o,u)})),r},reduceRight:function(t,e,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Oe(this,ce(this,!0))},slice:function(t,e){return Oe(this,pe(this,t,e,!0))},some:function(t,e){return!this.every($e(t),e)},sort:function(t){return Oe(this,Se(this,t))},values:function(){return this.__iterator(En)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(t,e){return _(t?this.toSeq().filter(t,e):this)},countBy:function(t,e){return he(this,t,e)},equals:function(t){return X(this,t)},entrySeq:function(){var t=this;if(t._cache)return new L(t._cache);var e=t.toSeq().map(Ze).toIndexedSeq();return e.fromEntrySeq=function(){return t.toSeq()},e},filterNot:function(t,e){return this.filter($e(t),e)},findEntry:function(t,e,n){var r=n;return this.__iterate((function(n,i,o){if(t.call(e,n,i,o))return r=[i,n],!1})),r},findKey:function(t,e){var n=this.findEntry(t,e);return n&&n[0]},findLast:function(t,e,n){return this.toKeyedSeq().reverse().find(t,e,n)},findLastEntry:function(t,e,n){return this.toKeyedSeq().reverse().findEntry(t,e,n)},findLastKey:function(t,e){return this.toKeyedSeq().reverse().findKey(t,e)},first:function(){return this.find(v)},flatMap:function(t,e){return Oe(this,ge(this,t,e))},flatten:function(t){return Oe(this,ye(this,t,!0))},fromEntrySeq:function(){return new ue(this)},get:function(t,e){return this.find((function(e,n){return W(n,t)}),void 0,e)},getIn:function(t,e){for(var n,r=this,i=Re(t);!(n=i.next()).done;){var o=n.value;if(r=r&&r.get?r.get(o,yn):yn,r===yn)return e}return r},groupBy:function(t,e){return le(this,t,e)},has:function(t){return this.get(t,yn)!==yn},hasIn:function(t){return this.getIn(t,yn)!==yn},isSubset:function(t){return t="function"==typeof t.includes?t:e(t),this.every((function(e){return t.includes(e)}))},isSuperset:function(t){return t="function"==typeof t.isSubset?t:e(t),t.isSubset(this)},keyOf:function(t){return this.findKey((function(e){return W(e,t)}))},keySeq:function(){return this.toSeq().map(Qe).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(t){return this.toKeyedSeq().reverse().keyOf(t)},max:function(t){return Ee(this,t)},maxBy:function(t,e){return Ee(this,e,t)},min:function(t){return Ee(this,t?tn(t):rn)},minBy:function(t,e){return Ee(this,e?tn(e):rn,t)},rest:function(){return this.slice(1)},skip:function(t){return this.slice(Math.max(0,t))},skipLast:function(t){return Oe(this,this.toSeq().reverse().skip(t).reverse())},skipWhile:function(t,e){return Oe(this,de(this,t,e,!0))},skipUntil:function(t,e){return this.skipWhile($e(t),e)},sortBy:function(t,e){return Oe(this,Se(this,e,t))},take:function(t){return this.slice(0,Math.max(0,t))},takeLast:function(t){return Oe(this,this.toSeq().reverse().take(t).reverse())},takeWhile:function(t,e){return Oe(this,_e(this,t,e))},takeUntil:function(t,e){return this.takeWhile($e(t),e)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=on(this))}});var ar=e.prototype;ar[cn]=!0,ar[wn]=ar.values,ar.__toJS=ar.toArray,ar.__toStringMapper=en,ar.inspect=ar.toSource=function(){return this.toString()},ar.chain=ar.flatMap,ar.contains=ar.includes,Xe(n,{flip:function(){return Oe(this,ae(this))},mapEntries:function(t,e){var n=this,r=0;return Oe(this,this.toSeq().map((function(i,o){return t.call(e,[o,i],r++,n)})).fromEntrySeq())},mapKeys:function(t,e){var n=this;return Oe(this,this.toSeq().flip().map((function(r,i){return t.call(e,r,i,n)})).flip())}});var sr=n.prototype;sr[fn]=!0,sr[wn]=ar.entries,sr.__toJS=ar.toObject,sr.__toStringMapper=function(t,e){return JSON.stringify(e)+": "+en(t)},Xe(r,{toKeyedSeq:function(){return new re(this,!1)},filter:function(t,e){return Oe(this,fe(this,t,e,!1))},findIndex:function(t,e){var n=this.findEntry(t,e);return n?n[0]:-1},indexOf:function(t){var e=this.keyOf(t);return void 0===e?-1:e},lastIndexOf:function(t){var e=this.lastKeyOf(t);return void 0===e?-1:e},reverse:function(){return Oe(this,ce(this,!1))},slice:function(t,e){return Oe(this,pe(this,t,e,!1))},splice:function(t,e){var n=arguments.length;if(e=Math.max(0|e,0),0===n||2===n&&!e)return this;t=g(t,t<0?this.count():this.size);var r=this.slice(0,t);return Oe(this,1===n?r:r.concat(p(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){var n=this.findLastEntry(t,e);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(t){return Oe(this,ye(this,t,!1))},get:function(t,e){return t=d(this,t),t<0||this.size===1/0||void 0!==this.size&&t>this.size?e:this.find((function(e,n){return n===t}),void 0,e)},has:function(t){return t=d(this,t),t>=0&&(void 0!==this.size?this.size===1/0||t-1&&t%1===0&&t<=Number.MAX_VALUE}var i=Function.prototype.bind;e.isString=function(t){return"string"==typeof t||"[object String]"===n(t)},e.isArray=Array.isArray||function(t){return"[object Array]"===n(t)},"function"!=typeof/./&&"object"!=typeof Int8Array?e.isFunction=function(t){return"function"==typeof t||!1}:e.isFunction=function(t){return"[object Function]"===toString.call(t)},e.isObject=function(t){var e=typeof t;return"function"===e||"object"===e&&!!t},e.extend=function(t){var e=arguments,n=arguments.length;if(!t||n<2)return t||{};for(var r=1;r0)){var e=this.reactorState.get("dirtyStores");if(0!==e.size){var n=c.default.Set().withMutations((function(n){n.union(t.observerState.get("any")),e.forEach((function(e){var r=t.observerState.getIn(["stores",e]);r&&n.union(r)}))}));n.forEach((function(e){var n=t.observerState.getIn(["observersMap",e]);if(n){var r=n.get("getter"),i=n.get("handler"),o=p.evaluate(t.prevReactorState,r),u=p.evaluate(t.reactorState,r);t.prevReactorState=o.reactorState,t.reactorState=u.reactorState;var a=o.result,s=u.result;c.default.is(a,s)||i.call(null,s)}}));var r=p.resetDirtyStores(this.reactorState);this.prevReactorState=r,this.reactorState=r}}}},{key:"batchStart",value:function(){this.__batchDepth++}},{key:"batchEnd",value:function(){if(this.__batchDepth--,this.__batchDepth<=0){this.__isDispatching=!0;try{this.__notify()}catch(t){throw this.__isDispatching=!1,t}this.__isDispatching=!1}}}]),t})();e.default=(0,m.toFactory)(E),t.exports=e.default},function(t,e,n){function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n={};return(0,o.each)(e,(function(e,r){n[r]=t.evaluate(e)})),n}Object.defineProperty(e,"__esModule",{value:!0});var o=n(4);e.default=function(t){return{getInitialState:function(){return i(t,this.getDataBindings())},componentDidMount:function(){var e=this;this.__unwatchFns=[],(0,o.each)(this.getDataBindings(),(function(n,i){var o=t.observe(n,(function(t){e.setState(r({},i,t))}));e.__unwatchFns.push(o)}))},componentWillUnmount:function(){for(var t=this;this.__unwatchFns.length;)t.__unwatchFns.shift()()}}},t.exports=e.default},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){return new C({result:t,reactorState:e})}function o(t,e){return t.withMutations((function(t){(0,A.each)(e,(function(e,n){t.getIn(["stores",n])&&console.warn("Store already defined for id = "+n);var r=e.getInitialState();if(void 0===r&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store getInitialState() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,O.isImmutableValue)(r))throw new Error("Store getInitialState() must return an immutable value, did you forget to call toImmutable");t.update("stores",(function(t){return t.set(n,e)})).update("state",(function(t){return t.set(n,r)})).update("dirtyStores",(function(t){return t.add(n)})).update("storeStates",(function(t){return S(t,[n])}))})),m(t)}))}function u(t,e){return t.withMutations((function(t){(0,A.each)(e,(function(e,n){t.update("stores",(function(t){return t.set(n,e)}))}))}))}function a(t,e,n){var r=t.get("logger");if(void 0===e&&f(t,"throwOnUndefinedActionType"))throw new Error("`dispatch` cannot be called with an `undefined` action type.");var i=t.get("state"),o=t.get("dirtyStores"),u=i.withMutations((function(u){r.dispatchStart(t,e,n),t.get("stores").forEach((function(i,a){var s=u.get(a),c=void 0;try{c=i.handle(s,e,n)}catch(e){throw r.dispatchError(t,e.message),e}if(void 0===c&&f(t,"throwOnUndefinedStoreReturnValue")){var h="Store handler must return a value, did you forget a return statement";throw r.dispatchError(t,h),new Error(h)}u.set(a,c),s!==c&&(o=o.add(a))})),r.dispatchEnd(t,u,o,i)})),a=t.set("state",u).set("dirtyStores",o).update("storeStates",(function(t){return S(t,o)}));return m(a)}function s(t,e){var n=[],r=(0,O.toImmutable)({}).withMutations((function(r){(0,A.each)(e,(function(e,i){var o=t.getIn(["stores",i]);if(o){var u=o.deserialize(e);void 0!==u&&(r.set(i,u),n.push(i))}}))})),i=b.default.Set(n);return t.update("state",(function(t){return t.merge(r)})).update("dirtyStores",(function(t){return t.union(i)})).update("storeStates",(function(t){return S(t,n)}))}function c(t,e,n){var r=e;(0,T.isKeyPath)(e)&&(e=(0,w.fromKeyPath)(e));var i=t.get("nextId"),o=(0,w.getStoreDeps)(e),u=b.default.Map({id:i,storeDeps:o,getterKey:r,getter:e,handler:n}),a=void 0;return a=0===o.size?t.update("any",(function(t){return t.add(i)})):t.withMutations((function(t){o.forEach((function(e){var n=["stores",e];t.hasIn(n)||t.setIn(n,b.default.Set()),t.updateIn(["stores",e],(function(t){return t.add(i)}))}))})),a=a.set("nextId",i+1).setIn(["observersMap",i],u),{observerState:a,entry:u}}function f(t,e){var n=t.getIn(["options",e]);if(void 0===n)throw new Error("Invalid option: "+e);return n}function h(t,e,n){var r=t.get("observersMap").filter((function(t){var r=t.get("getterKey"),i=!n||t.get("handler")===n;return!!i&&((0,T.isKeyPath)(e)&&(0,T.isKeyPath)(r)?(0,T.isEqual)(e,r):e===r)}));return t.withMutations((function(t){r.forEach((function(e){return l(t,e)}))}))}function l(t,e){return t.withMutations((function(t){var n=e.get("id"),r=e.get("storeDeps");0===r.size?t.update("any",(function(t){return t.remove(n)})):r.forEach((function(e){t.updateIn(["stores",e],(function(t){return t?t.remove(n):t}))})),t.removeIn(["observersMap",n])}))}function p(t){var e=t.get("state");return t.withMutations((function(t){var n=t.get("stores"),r=n.keySeq().toJS();n.forEach((function(n,r){var i=e.get(r),o=n.handleReset(i);if(void 0===o&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store handleReset() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,O.isImmutableValue)(o))throw new Error("Store reset state must be an immutable value, did you forget to call toImmutable");t.setIn(["state",r],o)})),t.update("storeStates",(function(t){return S(t,r)})),v(t)}))}function _(t,e){var n=t.get("state");if((0,T.isKeyPath)(e))return i(n.getIn(e),t);if(!(0,w.isGetter)(e))throw new Error("evaluate must be passed a keyPath or Getter");var r=t.get("cache"),o=r.lookup(e),u=!o||y(t,o);return u&&(o=g(t,e)),i(o.get("value"),t.update("cache",(function(t){return u?t.miss(e,o):t.hit(e)})))}function d(t){var e={};return t.get("stores").forEach((function(n,r){var i=t.getIn(["state",r]),o=n.serialize(i);void 0!==o&&(e[r]=o)})),e}function v(t){return t.set("dirtyStores",b.default.Set())}function y(t,e){var n=e.get("storeStates");return!n.size||n.some((function(e,n){return t.getIn(["storeStates",n])!==e}))}function g(t,e){var n=(0,w.getDeps)(e).map((function(e){return _(t,e).result})),r=(0,w.getComputeFn)(e).apply(null,n),i=(0,w.getStoreDeps)(e),o=(0,O.toImmutable)({}).withMutations((function(e){i.forEach((function(n){var r=t.getIn(["storeStates",n]);e.set(n,r)}))}));return(0,I.CacheEntry)({value:r,storeStates:o,dispatchId:t.get("dispatchId")})}function m(t){return t.update("dispatchId",(function(t){return t+1}))}function S(t,e){return t.withMutations((function(t){e.forEach((function(e){var n=t.has(e)?t.get(e)+1:1;t.set(e,n)}))}))}Object.defineProperty(e,"__esModule",{value:!0}),e.registerStores=o,e.replaceStores=u,e.dispatch=a,e.loadState=s,e.addObserver=c,e.getOption=f,e.removeObserver=h,e.removeObserverByEntry=l,e.reset=p,e.evaluate=_,e.serialize=d,e.resetDirtyStores=v;var E=n(3),b=r(E),I=n(9),O=n(5),w=n(10),T=n(11),A=n(4),C=b.default.Record({result:null,reactorState:null})},function(t,e,n){function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(){return new s}Object.defineProperty(e,"__esModule",{value:!0});var o=(function(){function t(t,e){for(var n=0;nn.dispatchId)throw new Error("Refusing to cache older value");return n})))}},{key:"evict",value:function(e){return new t(this.cache.remove(e))}}]),t})();e.BasicCache=s;var c=1e3,f=1,h=(function(){function t(){var e=arguments.length<=0||void 0===arguments[0]?c:arguments[0],n=arguments.length<=1||void 0===arguments[1]?f:arguments[1],i=arguments.length<=2||void 0===arguments[2]?new s:arguments[2],o=arguments.length<=3||void 0===arguments[3]?(0,u.OrderedSet)():arguments[3];r(this,t),console.log("using LRU"),this.limit=e,this.evictCount=n,this.cache=i,this.lru=o}return o(t,[{key:"lookup",value:function(t,e){return this.cache.lookup(t,e)}},{key:"has",value:function(t){return this.cache.has(t)}},{key:"asMap",value:function(){return this.cache.asMap()}},{key:"hit",value:function(e){return this.cache.has(e)?new t(this.limit,this.evictCount,this.cache,this.lru.remove(e).add(e)):this}},{key:"miss",value:function(e,n){var r;if(this.lru.size>=this.limit){if(this.has(e))return new t(this.limit,this.evictCount,this.cache.miss(e,n),this.lru.remove(e).add(e));var i=this.lru.take(this.evictCount).reduce((function(t,e){return t.evict(e)}),this.cache).miss(e,n);r=new t(this.limit,this.evictCount,i,this.lru.skip(this.evictCount).add(e))}else r=new t(this.limit,this.evictCount,this.cache.miss(e,n),this.lru.add(e));return r}},{key:"evict",value:function(e){return this.cache.has(e)?new t(this.limit,this.evictCount,this.cache.evict(e),this.lru.remove(e)):this}}]),t})();e.LRUCache=h},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,l.isArray)(t)&&(0,l.isFunction)(t[t.length-1])}function o(t){return t[t.length-1]}function u(t){return t.slice(0,t.length-1)}function a(t,e){e||(e=h.default.Set());var n=h.default.Set().withMutations((function(e){if(!i(t))throw new Error("getFlattenedDeps must be passed a Getter");u(t).forEach((function(t){if((0,p.isKeyPath)(t))e.add((0,f.List)(t));else{if(!i(t))throw new Error("Invalid getter, each dependency must be a KeyPath or Getter");e.union(a(t))}}))}));return e.union(n)}function s(t){if(!(0,p.isKeyPath)(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,_]}function c(t){if(t.hasOwnProperty("__storeDeps"))return t.__storeDeps;var e=a(t).map((function(t){return t.first()})).filter((function(t){return!!t}));return Object.defineProperty(t,"__storeDeps",{enumerable:!1,configurable:!1,writable:!1,value:e}),e}Object.defineProperty(e,"__esModule",{value:!0});var f=n(3),h=r(f),l=n(4),p=n(11),_=function(t){return t};e.default={isGetter:i,getComputeFn:o,getFlattenedDeps:a,getStoreDeps:c,getDeps:u,fromKeyPath:s},t.exports=e.default},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,s.isArray)(t)&&!(0,s.isFunction)(t[t.length-1])}function o(t,e){var n=a.default.List(t),r=a.default.List(e);return a.default.is(n,r)}Object.defineProperty(e,"__esModule",{value:!0}),e.isKeyPath=i,e.isEqual=o;var u=n(3),a=r(u),s=n(4)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(8),i={dispatchStart:function(t,e,n){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.groupCollapsed("Dispatch: %s",e),console.group("payload"),console.debug(n),console.groupEnd())},dispatchError:function(t,e){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.debug("Dispatch error: "+e),console.groupEnd())},dispatchEnd:function(t,e,n,i){(0,r.getOption)(t,"logDispatches")&&console.group&&((0,r.getOption)(t,"logDirtyStores")&&console.log("Stores updated:",n.toList().toJS()),(0,r.getOption)(t,"logAppState")&&console.debug("Dispatch done, new state: ",e.toJS()),console.groupEnd())}};e.ConsoleGroupLogger=i;var o={dispatchStart:function(t,e,n){},dispatchError:function(t,e){},dispatchEnd:function(t,e,n){}};e.NoopLogger=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=n(9),o=n(12),u=(0,r.Map)({logDispatches:!1,logAppState:!1,logDirtyStores:!1,throwOnUndefinedActionType:!1,throwOnUndefinedStoreReturnValue:!1,throwOnNonImmutableStore:!1,throwOnDispatchInDispatch:!1});e.PROD_OPTIONS=u;var a=(0,r.Map)({logDispatches:!0,logAppState:!0,logDirtyStores:!0,throwOnUndefinedActionType:!0,throwOnUndefinedStoreReturnValue:!0,throwOnNonImmutableStore:!0,throwOnDispatchInDispatch:!0});e.DEBUG_OPTIONS=a;var s=(0,r.Record)({dispatchId:0,state:(0,r.Map)(),stores:(0,r.Map)(),cache:(0,i.DefaultCache)(),logger:o.NoopLogger,storeStates:(0,r.Map)(),dirtyStores:(0,r.Set)(),debug:!1,options:u});e.ReactorState=s;var c=(0,r.Record)({any:(0,r.Set)(),stores:(0,r.Map)({}),observersMap:(0,r.Map)({}),nextId:1});e.ObserverState=c}])}))})),ke=t(je),Ne=function(t){var e,n={};if(!(t instanceof Object)||Array.isArray(t))throw new Error("keyMirror(...): Argument must be an object.");for(e in t)t.hasOwnProperty(e)&&(n[e]=e);return n},Pe=Ne,Ue=Pe({VALIDATING_AUTH_TOKEN:null,VALID_AUTH_TOKEN:null,INVALID_AUTH_TOKEN:null,LOG_OUT:null}),He=ke.Store,xe=ke.toImmutable,Ve=new He({getInitialState:function(){return xe({isValidating:!1,authToken:!1,host:null,isInvalid:!1,errorMessage:""})},initialize:function(){this.on(Ue.VALIDATING_AUTH_TOKEN,n),this.on(Ue.VALID_AUTH_TOKEN,r),this.on(Ue.INVALID_AUTH_TOKEN,i)}}),qe=ke.Store,Fe=ke.toImmutable,Ge=new qe({getInitialState:function(){return Fe({authToken:null,host:""})},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,o),this.on(Ue.LOG_OUT,u)}}),Ke=ke.Store,Be=new Ke({getInitialState:function(){return!0},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,a)}}),Ye=Pe({STREAM_START:null,STREAM_STOP:null,STREAM_ERROR:null}),Je=ke.Store,We=ke.toImmutable,Xe=new Je({getInitialState:function(){return We({isStreaming:!1,hasError:!1})},initialize:function(){this.on(Ye.STREAM_START,s),this.on(Ye.STREAM_ERROR,c),this.on(Ye.LOG_OUT,f)}}),Qe=1,Ze=2,$e=3,tn=function(t,e){this.url=t,this.options=e||{},this.commandId=1,this.commands={},this.connectionTries=0,this.eventListeners={},this.closeRequested=!1};tn.prototype.addEventListener=function(t,e){var n=this.eventListeners[t];n||(n=this.eventListeners[t]=[]),n.push(e)},tn.prototype.fireEvent=function(t){var e=this;(this.eventListeners[t]||[]).forEach((function(t){return t(e)}))},tn.prototype.connect=function(){var t=this;return new Promise(function(e,n){var r=t.commands;Object.keys(r).forEach((function(t){var e=r[t];e.reject&&e.reject(S($e,"Connection lost"))}));var i=!1;t.connectionTries+=1,t.socket=new WebSocket(t.url),t.socket.addEventListener("open",(function(){t.connectionTries=0})),t.socket.addEventListener("message",(function(o){var u=JSON.parse(o.data);switch(u.type){case"event":t.commands[u.id].eventCallback(u.event);break;case"result":u.success?t.commands[u.id].resolve(u):t.commands[u.id].reject(u.error), +delete t.commands[u.id];break;case"pong":break;case"auth_required":t.sendMessage(h(t.options.authToken));break;case"auth_invalid":n(Ze),i=!0;break;case"auth_ok":e(t),t.fireEvent("ready"),t.commandId=1,t.commands={},Object.keys(r).forEach((function(e){var n=r[e];n.eventType&&t.subscribeEvents(n.eventCallback,n.eventType).then((function(t){n.unsubscribe=t}))}))}})),t.socket.addEventListener("close",(function(){if(!i&&!t.closeRequested){0===t.connectionTries?t.fireEvent("disconnected"):n(Qe);var e=1e3*Math.min(t.connectionTries,5);setTimeout((function(){return t.connect()}),e)}}))})},tn.prototype.close=function(){this.closeRequested=!0,this.socket.close()},tn.prototype.getStates=function(){return this.sendMessagePromise(l()).then(E)},tn.prototype.getServices=function(){return this.sendMessagePromise(_()).then(E)},tn.prototype.getPanels=function(){return this.sendMessagePromise(d()).then(E)},tn.prototype.getConfig=function(){return this.sendMessagePromise(p()).then(E)},tn.prototype.callService=function(t,e,n){return this.sendMessagePromise(v(t,e,n))},tn.prototype.subscribeEvents=function(t,e){var n=this;return this.sendMessagePromise(y(e)).then((function(r){var i={eventCallback:t,eventType:e,unsubscribe:function(){return n.sendMessagePromise(g(r.id)).then((function(){delete n.commands[r.id]}))}};return n.commands[r.id]=i,function(){return i.unsubscribe()}}))},tn.prototype.ping=function(){return this.sendMessagePromise(m())},tn.prototype.sendMessage=function(t){this.socket.send(JSON.stringify(t))},tn.prototype.sendMessagePromise=function(t){var e=this;return new Promise(function(n,r){e.commandId+=1;var i=e.commandId;t.id=i,e.commands[i]={resolve:n,reject:r},e.sendMessage(t)})};var en=Pe({API_FETCH_ALL_START:null,API_FETCH_ALL_SUCCESS:null,API_FETCH_ALL_FAIL:null,SYNC_SCHEDULED:null,SYNC_SCHEDULE_CANCELLED:null}),nn=ke.Store,rn=new nn({getInitialState:function(){return!0},initialize:function(){this.on(en.API_FETCH_ALL_START,(function(){return!0})),this.on(en.API_FETCH_ALL_SUCCESS,(function(){return!1})),this.on(en.API_FETCH_ALL_FAIL,(function(){return!1})),this.on(en.LOG_OUT,(function(){return!1}))}}),on=I,un=Pe({API_FETCH_SUCCESS:null,API_FETCH_START:null,API_FETCH_FAIL:null,API_SAVE_SUCCESS:null,API_SAVE_START:null,API_SAVE_FAIL:null,API_DELETE_SUCCESS:null,API_DELETE_START:null,API_DELETE_FAIL:null,LOG_OUT:null}),an=ke.Store,sn=ke.toImmutable,cn=new an({getInitialState:function(){return sn({})},initialize:function(){var t=this;this.on(un.API_FETCH_SUCCESS,O),this.on(un.API_SAVE_SUCCESS,O),this.on(un.API_DELETE_SUCCESS,w),this.on(un.LOG_OUT,(function(){return t.getInitialState()}))}}),fn=Object.prototype.hasOwnProperty,hn=Object.prototype.propertyIsEnumerable,ln=A()?Object.assign:function(t,e){for(var n,r,i=arguments,o=T(t),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 literal 32917 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 \ No newline at end of file +n&&e.updateNativeStyleProperties(document.documentElement,this.customStyle)}};return r}(),function(){"use strict";var e=Polymer.Base.serializeValueToAttribute,t=Polymer.StyleProperties,n=Polymer.StyleTransformer,r=Polymer.StyleDefaults,s=Polymer.Settings.useNativeShadow,i=Polymer.Settings.useNativeCSSProperties;Polymer.Base._addFeature({_prepStyleProperties:function(){i||(this._ownStylePropertyNames=this._styles&&this._styles.length?t.decorateStyles(this._styles,this):null)},customStyle:null,getComputedStyleValue:function(e){return i||this._styleProperties||this._computeStyleProperties(),!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 a8fa716d1ea9f89ab4ceec7177870918a4925c6a..edf06e778e90a809b3c905facab70d1238c19d30 100644 GIT binary patch delta 98035 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%0ACgjeq{) zhveZjoAPs~tmZHtm;8R3hxglLx##Em&o#z2cK`Th=C5P8KrDo#)?4YF%H_sgj`}Sr zYA3z~o4LMyG3~;tg%L-loKchv-#knBo94>AFIkTK^&J<_dT~b=-;U2vWnKQ`k<9$% zo1XCe(Ep*X%ORCrw`}!B#9Z(Dyy)SL)_w!)h-{Nof2K0!s?!J zM(S3zZ)ap>z~Pub5$=U~T?gOfYF@Rw+N>!2?&%g;xp^I86H*QKPduCN7@P~V|#s>Zay|!8ETkEdsE#G=(n$3~8sF&gF4^AW;d{v+5=N;zv`LEB{ z&^L#rL@r+6AQqr7>xpIoOXk+N_1169a?BV0`17fV~-ixxWy)xyA^TK8FwO6{furotiKlD;xAZca#3sQ4~Chq&V@3) zp2QN69Mmqd*LiMUUDSbq;8y`B=O3~NnErroMayRs;iE^TDxdE)G&p?t!-s^PJMSwt zUE0N{UHK`k;8WP$$>xiv&a}-k)30qlr^nIPy(+8z)6uUB6chN)tvk9-g7-p(9Ou)t zndiHTXZl62@l5Ag&vTY#=?(?~p$*y+N6#LY>0Ze7p7+m34cjA}_wO+Ub)Po%UX#D& z|NRuH7yaVl8{`fCHG54wCK>j~dW(UX@V#fJZL-&@Y@6p)xqj*zIg8Rumt8k*5x$^Y zSRhpVgIh-7uSNa3X>){MvwYf-pjObrHq&9@^fz8pQo{e>uzA3wCR_xTvZ+1yk zpIFzaS!V0=Fsf+Qok_FStbh2A@rJrI68y?MGH?J*6JUC=?72E9eO!JRsv&B~* zlaZeGFr-*Av&YYff0O<349`;ccN>@geSK%U^qauNh5xtY_x~(g7;8B9@IznoyrBDD z-;W`HQO>{qe3BJs-EF;gYnAM8+)5_%kC zY_6lpQTlaRWc&LmK393oQuZBQ9wx*0dG1qr}Drze;&|4iTVpTXmtL3RC=1y^QrT)!P1&H3g1&zlDVnp_jl)*Vo}oUo3~^NNyY z#e%|{4}wGM9h`V8rY6Ym4SSq%BZ}b{kK2SOrut?cH36%3jSsHp8SZL6{~N3p@4W3; zlTNOHozIsiStn9ipZHZyygsYy$;CB^u03BBq*m!m^j2It`!xINwxWk$SpI&QQ@=1$ zVs|U=`3q&!wixEMu2pQiwKq0xwc7rv{({RU{GMj|Jufoz!?Y)+F|M!H%~~%U`&IN# zLeoDVV+Nc4M47dXUvF9q>s-4PH1AM3SMKZJ)@0CeRXwp$NibxZV5^5*L42OCbs^BSTnzD)SU;J*Y39eje31X`q1=QB3#OA zUq&%F>m60OC7id+c4}(D#gyOehFncjtWS!61pRT@T~*a^z+g&yKe#f|uVv*4o*VDc|kz15+HD`(+@baLuH=i`k{fL5xj1KS;rq(GqS9VsQ>#*z z*@vW~$uC@VPwuU*m~eE}8rvJrvgeW_W=&SwIVVb6oxe<>>)tU>%c8i9*Ueh9o&?=g z(YqRTz(0HD2`|SPnzD$8{OAi70j)*gzMo!0~uF# z@r~to4ZCjnd(Sv@c}@L62@%;&-7WVQq-;=JYJVW$N3;Jikx%|QydIVc-lcnARLGop zuz;EGF`q=pvLhP|M76q39{iAS<(aSaBC|*HR=@s!{q@t-xJ#jW*OMA5&V;<(Qy;(L z$61BsM&qqhEh0I2Z}nerO>dOo6I;;6dNF>*!i9D>>kH;Reg8CrfK`m zUXt2=`HMzxu8kzUR@V zcJ+r!`<7e^pJV9%d19;jk$X$iXKSAiGzpz>Yq3=8g|~rI&s_XhvNmw{-3$&B#tY7N z7dfx2y*^Q-f6d1aXP(~@c)i}CUiXvL+IqD$E6N{xXiqwz683oAAC?uj*Ry$KluP7= zOjKub-QZX^&FtZY{kC&9s881{dgddOcjx}%G_NyWv!uUWH14UNIRO$S$6V4 z-Hg|Q^&7XjJQnHp{+kz_d@AUu$LBMXUK{O9GUPui8O^D)!yq_-O>w{d>9ok3v2CA2 z?;G`OSflWWuiIa;LEA#+;1(Y_nHP1tY(iHl2)fu#zM9++eP^3STEU&NE=%>Zhq_yC zvFH^|{1Fs>>JeM?&C>xv9S%1ej1F=%-RLy&;LV!9#=k@!A zdzS=14F1JrCH{8*jQTfTvrB9G%C3Piu$ zZn|>H^MKG%nU3F|OXu{*Ii~Bam@?&{Nul#Kd0FcZ+O4yS?#8X zYU;mf54h;xvhXn5t}xku@kjY*Y|C<9JeMs%NGkE@Md znapNyy?dGSZ*EX9=n3u$G*ox>{nIGeIQ6LR%hd;KAk=g9i9gFa%td+hor5`qB+_?L8}XZxDDTe1yHiP@$mUMc9%#Pyc9e2S1wnPGpIN>7LWA`Zms!CO=b}(0hY%idu-d zr>V(=Yx89^Ti@DGbvAu#9<1JZY^_{u{;X9dwVUmDT0&YEoH3g5`t~{Dj9SdB`|p{@ z%TGTY#kie&X=+g2R59VbvHQ+V?~P{cV4O5vEQV2^F=o134C8A?q3K4kjP{JO(~Dvm zJ(%LJZr>BjD8d@D_KGmVyT%TIf#XePXP$(JJ^f4{z{?fjbSqFL60 zDv@(t8$!6!)ob%|GZU&rN{;IX7<1?6zE4|`Z22%EDwm(Tq}Sk&iqEEr{4#M|_02Cb zysd9^1?*~DwPEqJof+%)vZVdp6m`bjY|_!3SGIdDY`eF?@Z^o^;Js|W*G``4H<`f~SQiobqWIA~hX%UL!S zlJTb`d3SLc?-YERWGO4Zm2;Px-Knkb%mVZ3??rw*!JzNMy!Ol9)0J-g?g8JmT%1H| zSti9+N&RW;+cZ-pR!}z;ntk6Z$|0=xMxDSQSTF|9sqN(;==KLnJ z;GK&tYkalB&cD5;-569=P*m{$;o;7MzZaiQ+oAj7eC?m$S?bG8?v^XBPd8MH>sh|x zszsOu&#(F=!F}P{J&w}rgpU7fxKX%C!*qkQqs(u{sn<;`YnHyV<37rH?67F@Pm2}$ z=QbJKI6qUhiI*R=GQ0WbH~nqD=chhBzQ)o1g0j`$g4G`m`Y-*n^VrqOsHqi+EeHSp zTo%2o$G2Yl@XV)0$tT!!^0aQv+L4)}|4?n))gaZgIm@}U>gVQdt+ZQx-=oC-e)h|MAD+}Um&V_goFSsH)5Nyo>%Z3@B&B@hTGr>ycfGv+zWkg$ zLG#z&mw)vByJGF$_9FFM_HSA+sxiSo9$IUw+ zsw|$lciY?!=|J`uwkNr&Cr00!)hW8rHPyRnrCYq4m0+Hds=c0ah|=ZODT`n4dc58z zZ6f<&!>XCZ+%Id}_@s6Ov{wifE}Qd=xBlyIXW7e~JH(21ER)<_(X;Kb-nBD}|4QZl zY`vl(@rHj(b@j~~j}I?qIM2H%=*~S)y?_^$T*p6|?ca6Q{{A_={OliJKVE)&{PgYq zh7S+t^GsTww|-sT!|a>)^wJsM$a>c;oc?>xwAnfC)m(4bx4)R$;w2ikvbnOZFgpG7 zZSFtp$Ls3~9M>>i($cB&&`)|D-KliR`@{!t%}Y7bhj#a*toA?E%^D@0q8C5wSxai& zy?ZxUt+>y|Ffxe?_8^OA+X znwpd9PqaPp)!qN|{j$hsb0aVDWZkx&cINTS9WyON^L5X>2oa2%x`FAhAgjlZr|?vBkJqR zue0uUh0=zW6Awd9&2ru?DqQ)G`|U#h3)NrwQX9*<7OmTHTZKvT=bLL$ENkoMa%5&# zzQ1RF$bFUUj0<8Duc=udO4}~u;`~RaZ&`J6jpDniOB#*#I%=o3@8sKc;J)xrg{NI& zuXlt^Phyo3+HbbjU#MT?5?6dm$F&=;=J>p7U=`$FBF*xhZO*D4VUx6W9ee zFKl^HcQS4B&s|R^gmrp9G_z8ee{23XWreKe|GWJ5sckX~n>LYkmzQAwTO}ifi}^3s zg|r>79OuU{4}hR;Ngak(*V3Cy4HXyfaDoh1ySla+U{%{nkCXyMa~r|zA~ z`taQ1kY#;O!MfVaGfQnsBi znDu7$kx4g?9RGCN(ez_~*|DmsMC%AkPXEPE)V%c`8(F&lESs%!?SzPie7Lu%_p+|( zN8WG?{aog|sbb!o#r|?{%WrSU4*jV#TYHlFJ`O$-pta!c&UzO{`|gW zmu+vfH(d4mQ|FcO_}t_<{}{rO&R=5rYFKrAn!HBomN?g5^?m=ho{Cy!UR&T4`XS%0 zwqr}%r)|1z^3RrR=xA4NuUBS}u%BPo+<(jC6Jyp2CEbF+b04m$Z<})C`MdOAKYu+r z5O-?o{*)g-zy10){WkmO7ccq)9v{53#@Kl6(dvhF@8z;=7uJc$y^Yx$@J4g%Ea|xt z*JPgT=rCj{ebTbytaV*e+LPBgEqwc49GA)Xwtz8;apFy%>~5Q$K0e_C!LNGG)PLwH z5qNV@DwuBVK`LLA5`MA-;;rd{L46-rPw(f}n-&F;^LV;JV zAB^>W3;(Hq&in7P@tRZT``0~P^+zZCY3{9?&#zye@89>PyiM;eYxvXK0^Y~wKbW8K zN%Eg>A=^jcf2=!R^~NyPJP^(P9I1Ooc(2yxAG}i*?Cy5|sx_C{Wg0_I_$R?~IsLsS zZY*A)9Jprk;?&^fmt|xVCVuJ+dSEzVxm`$z(AIf;`;WSp`Bv9w)S3sXTy5u9JC^yI z@xpx8i}Sti3A=brbKdeyNi=`g^p|IC_8*83f62Rjs!wzH7x^0u&p%r3Jw=d$QP{Z_34ja8rgLm!AQZkVap?kb#P_hf#-g-C(TCf$=OCe3mA z8y8f1V&}GEy`70gv0D-XWVdHIanGA@>S$WO>$|3+pUbZv*{Zcg!1sgy>I?cTuICCr zXEYr@ZvTko`sCjCZEO1qW#4|`n&I8OEVEkZXX(z5Nw2^D^}3gC!FJr!tIWIJNif4! z(<#}a(44DWCdSmwCy&MZ!Y;)f%WDmHtlXjMKEd_AMCaV3yxsX$k$;|KSzP{Tsi)EX+_Pq?4`|7rh}hpppuZoS+@wcWdYjOJK~=UjGgW1C^K(rvko`(;+| zrD3+AjvAY{2wj+QLjGA%>aN6}2ENbu7ikCayT6_uRK>f|s+32j`Sp`6lz07`a5L+5wqoyM@lUIt za`o|MS|@N+cuY=w_`l7r!Fj1_vp$!~n-%>{hZP#<3x~{@Z1LJ{m%*H{!qQEBr3OVk zr9nM8+%G?^o4Y)7>%4`#b>_VNwMT!)nv-~Z6k%=FN|^KQvP z3p0zmPCxm))&DDxjKhk`vvqgBZ<7ueTV@*nFygbQM2ld?s;WH6${+*z%W>_R_ZM@S zg+DS+(o$adVdF`U+;VO+%@tg}u_~9h3Q1po9e8-zF0ENI?)}SzKjx(hW=f`|oZ{N> zI6a~J=dys#Z(CFArS^T561Gk>+^v0SV~lWHWX6fx#@3tv#<*wa|BKr)eUs0v=^G92 zOnh;PExG(gY(DR7v9qkj>BsgeUy2L+cIn#dc6aql9=V^LOZ{SIJE_@!@!osAVuL{s z`~P|KU+&y{%6@xJci-|etr0o%^dFu(7=P~Tv?LvtTQQ%%F11ZAY>2N<^b3!i)_L#Y z%xKxU5#fK!x?Y?Kyr9ju;z^wO+OI~tS}k1ECKXIj6WqU~=&7g}`=&5~`)4k!KExWM z;~=4CAa>j)_Ka=xVXs%GnRXXRnYty3xCkG*lRoucwUfcCrt7r}Ce*!u=N$0!uJgB( zU-;asWs{$jGS#s7=eXNcMDjd2Qorltqb2Nere7cR-Qx~azgARmsdW0M>ARwIOPl7k zJY-BQi`04Mzf0q@h`!+!bpvka>G@AS`usd`?2@Tpy-x1-YFDKrYO4$bLe}Ry?M|7m z%~Cw2@ZFAYn*v!j)PBysDEFqrc3bzenFn3#m&p8oaIqp=B&^vydcw8l&QEqrH4`oB zZ(MzpoVPtu%;YXl>Fkoby>G0Z$-nuO=x<&1gv&8G_}T?)-^kY6edo8#lZ>c75hu;> zv9w!gdElorSGPa@rDweNFo$VZgqwr=`?r?XB|#JIyZg=yKdopxKP%<&l-VL%7JS+r z^y=;(H=AGnU*5&eW1#f1W^Ol$2BzTKylk1zC}*)iwW3g-jYjLz?4uhC8tbm}L~d-RlH?ax2=JwN=g)rgXa{CP)lzrdTA+DCiqb}{uhP5gQwWvY0V z$AL-PuQ}!3P2gBidqaJN!uBO;Q``?b<@IEotx`L5Rh(hb$=lE8H_l+?yWX5|OV4J_ zX>+4R68@ju)lAZols=tRzkk2K=I7-fXG0UVVS9 zH~i5pUy%OV@~>sx;TD5KZZht<-~Y^>7iXJya8g+K{o>oVzlR)%?2)N&lGf&!A7A7e zD;Kpazff{j*y9s>EkdWZ6bhQxm%Q5*Fg080!<~uqV-(eo+4gLEyo$Sa>6d9&`Sw(4 z=sl59(R})u+5SqZ8QqXNB|L6Rhncwsqz4599aWm}GkM_NE(V-j8Shc)#_`rIpG0s{-=mZ$93l zb-ya!;MU@OuU5X5@wMB0QMy6%?bd>dOShx6+x06O|2-_`w5$oN`~GmZe#rj3AB(nL zd!A4yym)F^g~H+P?WSM*eAa&5J@>ulo%_D4dX`Gvda=Uy1m6lxy$LqIX0d87p6>sz z@Yi;A`+r}i|ITMrXS!d#U8R7rgNbqN_O(TfYD|oL+pm-`x-v3uoi0_zXk5QvEq)Gr zr_SaU$8VxS538?CaOsd+Ia58{D#tK}e{+}I+W!6fD*k;td3$;N-{&uHKmY&U|NQ-# zQVR9D4EonMpMJf)CUcXK2T!>7|URH5)PEEUn zit0fZY1^P*vOoUc=uY9a(U8zmd+WsP?Do;kc=7cu8?XL)DsI_W4L zFUR_eT)h!&8>X9vr!c%TpD}Cu*89nED-Gp+7KRtjxn?H%cgtdS8SdncRo*_!ZzVK2 zJ&)*dNz_`c`84nJ>WFiWC%W!kUy^u>Bf&+Z`WLuTXgjMw3Dkm91FKSK25%RRoCotY7SO{r`l}Bf(F_6fAhIZTwoAH0ikgm8(~SU(N8j#a*UqEcEo^ z#|BXc*}NrO;rq9;pFCXrnyrD`>Vc8%7M9g&@&|&%Hk(b@cW0VI?;mB!<3GO7f6=;f z>c5%G)P*?@ikF7kF>hgiv988^_3ERKQp22DXWesHBJ{BT>KkUCK&$G)`gQ+Xr{4D2 zyYiP)+uy^P`cm%%qtEZY#kI|-+qR|i)3M>1B9RjVhfWlfL#EOM^K?s?r46#?@d zs&!i8ev$`bxlPKLoo1O{|F&csbEWYd`RkQ^j#5TD&UUWo6?d=tw@NiD?&l|gTSlq% z6D7Wue%#(RfmPnVV{3ii@s|%`$QB`Po9(W2>!#DCr@w!TuB@FBCe6)L)#h|C zopsS!;~LEh)*0gEw(L^6rOaEODeQf|??}PoXES&u9Ui~qc&NTSSs=ZlXHxa5oom9n zcYfeG8r}Wqii^m*d6VaF{H?KTW2946%-RG;DanNTE1quO4>hEH$ny~YWB%-Yeb@W? zbw}IY<~`HfT+ue~^Ueh`B5qArd^_p4Lei=anx4h((fU71W@;~wo)_n_Y3ssYp{(Ig z*XKOG`FgtkM#*(wp1$3(L*+}tH|^UFF}rqlmfq=Jc*ZqU_U9Do_iR=9+~w{2w$Id$ zX2=gNk8l2$Ru)yy_J5(@u8CE$Q^Lcievom0_q?>d`pI5ti{NE}9yv3nIXwI5uz=}% z(~Q=@IhQ{P&G26k@m*}2{M)@7-*qrfIW;e*X?0O`Rp7jEs}ry0P3GQwG_mEt;zb|! zp8cm-|3~=YW!d$t(z*_<)+H04O}n&jend~p4YgR0OocWUWvAZ~N15w0yYBYP`~Tz5 z)6e0@m#>eruh!dpFVS%azn0nwb;iefES0uQH?BYLekHu&qROFCp~Ji0zqMKuz*zcx znL!OR`-*FYTD}&X6;ac*FZ7qJymL>lM2dt z=P!NA_?S@>(qHl6_|;DzEe!X`ovgoKf7bHY@r-R639LViGkbU0KYPA5P|`m1?f&E` ze4#V<@!Ws6aqmT|b$wzl%EY#ZUOM&n=x*=2|C5_)S?7pPn4K-VW$uK_#>&}>Ql9N^ zPn_s+{bm35OZ}^9o6fsFHYxN-sykzGXd}bd4Ge6)TTAXwUn}+H-utER>%#wbcGpF8 zb=SvzRmrQF_|bpoda)pTC$@|CinIM4?M})dUr# zhaaz-e{xZ&@VGPS(!MQwK3w0a^X&7Un8Qz2e0Kl*s9xFkR32-arqG!)4EMzXTm*Wa zw6qz${vIQZLepl{eboJJBKZ zDa2^YqsFaO9}nE*wfb+$8e+s<;3YC=1FOzwpQs~?(sG4VSsLy8j_iE(@Uq4niEWix zYgRM6D!Xrxf5#9YGV!eKx7Jrd!S@b*@m)G8VY}GdY(>lJ-%r}7$j{!iO?BF?%UQvH zn$G_+Q8@T?rsttNnX9w!thr)cUo}sJ&Fw^WRm!?cS0>;0Ibjw%>DJ9-fqOzLEQ*d= zU*h>Ma=y^6zN$0MYFG2q7{(cu7Zo@y?2VoYx_RC()?Lrk zh#9YfPbXwPp1dY0s4m^Ebo=Cl=RV zl#EgPB$_?{Cb!4w;wKAOE8m|NbLdcTDLC8kW>QMS7Ka1RMfL_VH@L@$25|CimhGoUiSFy{>nu-BCaJcYI#=su{P|7cDZ?Tv}2f z+{JCBaawzQri!+P9-C!iZTDBPt8b#e&$}JQ$?Z8M=gZQ0n}0vnSz#ACadTGP+RKaI zC6wiga#zn@dhyfBuMO4iJKixIztZTwhg%}Je4pC8_pI^t;eR*0{BmVG+uvSmhqrxu zU+;_zcp}F;r~Z`x?IRkGHvauxK9k|fs-87Yc6JS3KTQrtX>xe2?&P;!Zhj;E(sgTN zS!PNN@(<)`2A-M-)Ji`9lB(%VEg*jERG+!%S$i1FLzB} z8O`-0GwauiM`?#--wD_*m?xbg8+oXOS=aZ2{m=R7pKBtf?7um?zFtxEcenN4pUG-Lu0jNbb&k{%P|C{ft`ozh_ukp`4=O_HjS2 z!Ij^!51d4OHgvSy=hHfy8at(4{-1tpH}h0QhO9rco1fV~Hb|_wx|DSh+u?;zRBnXs zn(BI|w@b8#{qcc$U$-TD81#gCdoDQC{nuE={n&?sGeE3)K)AE;-%kvY*ds{7| zx8`n*b-TCnqQ3fkabC+qT3%f-b#47Sg;j0Ve`amnJHO+=lauAA&kGAawUPAy*Il3g z;^BtNiCdOEJG@e4`y3bZ&7pTgOg(R^Z_Z-0PqcPOjZE=act%tG)`cxv@@sW3emlEN zYmv{EcOJJ&w>~p!?n&Ht!hX|<&;M#9D-#rx&J{2D`NYnFWol4h`Nlc?XFI?4C$Gpp z60olAad15U-83^NM=wLw1#f4(*PJtRa>j+ZYwJb7+8vCwy14P}o&bvwkESgJr6rC*M3v=w_U97m22GTy&KnXg($wTTi#K0kRk0}nzzRaJw36VAJ<)~ z^_p0^%U|=_zJ=eueijzqm|0MKc>lH62L!CS1z0YxR9l2;qBUuH*UZ35#PM@X~c>(ZK8rVz0c@tFWWJFM(bwdBfoprZ=BMu zc<|GajtcD`5h53N3eIZ#b-hSi$HC@cz>N=1|5nDdF5eRq(bl5pabeMw)DvqIroH7# z-mvAtf%ZE_eL~0Ep1SfGimmsa^h-eGlwvMh+oi7)Sj~3)TfRIrv)(|{-_`9v^`s?% zRktP1m+k*I_x!E7wRZW(?XCFvitZ!i97CY;fJZb-TdamDg2odMxm)jNh5tzq(d`>SgI|9iI)}`M9f^ zr^Iq!KVuPg@yolzajc(uR_CSGnVFRQD3Ey`*?jZoiTWwOtY1w!GrR52^7h-+R*~;N z)N||=){tG(|$`y@MekYxz5-* zKj71fdC#1F^H|i^dq-|Dxf+nX@l8{eyROr6olkowdu3-V%4FDSQ4(^!arv}=HFgY= zO;#)Io-dG@EdB2y)5MF?ET6p{Jnsv4Hiu=@Dy3UZWu3L>>DvuID-0dhOxwlX`hxew zfk)yecKzoKs0|R(IQeH!<)0T#8AsO~`^~J)()0-0$qqU3?b?AF;lo$oXhdy@`f>FT>!aVD%3c$+X7gCS zurR7R?7f3atVGrz-PcI$;nalZMNPGO7xYRr=Ib9RfAs2;uU)K|?gpvTIn&frTetPw zIECuP>vW|Cn;bY-_bgj5fB*V=we+jc6}7LcIJq%s3b$`9t*qjnVz=?+vQKR~E+IM# zXY(JJU7UDh{b|>A*A}+8USVzTexno5ljs%s`kDjZU+aVm)}8Gd?+=7}9TPryYq8G? zwS_k-9zSt0zPRhK(+b8eS)~@==vk8|sU;VhGk14!tQ4F#Y4&cfg>#;z-PkP@Dp6lE zt5so}{K5M#F7S30ZHqYjIkxX-vT4qZ=T8|{PBjtuBlFBrNl77nZU$30m+m{2k2eC# zHia5InpiH-(U9fn^4GlEm+SN^_d9p&AKdF(QM+-^l`S?gyE!zMv^Z?~V0L%urpBC2 z+&03Tb)7=q!8~31yzew#e>knU<;-cDoh7L=>gOMuC^fg-KG%L z+2K!_bla*QdQ34=`?TisPwpFYx3JCNzFjhH@&EsOju{p0#WwyE7DZTBEH_d}d%ADMcf9bP z&G|VSyQlqrkXOUId*ThF&C|{beh*3L%bUJTe6C}_8G)UJb7hK8TSdk-PJ0*9xy_7Y z%ewLdB9mK^`P1oe2~Mkki>Ghze|Yg?+}=5nmjtIf zdRD*Cn7_5I&g75U&%U=$IaV(Y@}D2NU+niA&d^XUJ@umwpFZESeHhm4_gk&CzV5LC zhoi`R<0*xDQd=f}ohMWzbvvS9$>Uk|Y+mJj7Gihfaxdw;uAls0=-T*omAaX6{$p^RZ`~Nx}`^@*}Szpex zw0D=3c{k1c^M2ZV_T*yQUAu()qK>%rcFya49d>6?me5Tpj;7PE^k&q(-@AHm+WFa8 z`i_2wJnB6y-mhaiJ;}%A`?6`dRgae!uUcA>ck-OaF0Wm)Uhmkx_RHx?w)Z>yw!Lt; z`0=MuO}FUYA7Sao@A=+x3<`JEytjOfubcAa%8%w7)++0skdmp}{b!F^RWWO5%jwxy zZ>kn3OuS%_RI+H}qC&TS1$A|HhARDiELY!To(eiUIpW0MBlYPioUGrww0XiNP7MqF zy-$$$M(4y*?ox$C>*VzW<}<#&Vp$M&@`TjxT@#Y}tsVF;Yipm2|GyScW@;mpb*x_^8Z>5iU=F)*Rjo?zKY$7DW7Pm<o)U0%<}gpbEBYyPvetW1&7|NY_B!PWfSZGYYGZlC?z zZ0_5yj0sP6cxjwUcipx^uj;60(K$cc>Vx$;7bl9XxVdynOz+-QDeZ|Bi~6_7X}muE zT(DQWi2c&T7{j270}K6@+Wzy5*SHIc3o+nFHa_KCy_^R%itsX~D++V1uV3t)88{ZP5E_dL;!})KDZ4R6Z z+}D0lH7$STYFFF*?28_2 z4#l0hcTnSEu+moTeb#ZTms7*cEj_ye_ia%xlQ`oqC$+G<-pf{St5DloL;l!<+uk%x zac_73=~$5=a&!9ENq@4Zt`bU|Bmc8d@&D}Atm(V{m^iL-@h$uQr{n0MrAOzSTK49X zne7^{4HoyDm&Pc2SNavAb^V!Sk8N7#?28eb_baP~+D=mT5Zs z9_Q-c==q&|<^FepeSOMH>ScHK*q;crnR-h?&s~K(qn5W{w{7{{83o1@3OAS*ShhSl za(MGfIhFHlI?`qyLQF43ZT>k=KFskfZPI>r`K|kY|EOY7h}F>8_iWKBJ+Yqxo7T7U zA4^`zXry#@I@4bzm*#UhHK|s@djh*m7fA#JGEI+H50krP>szz5WZiPzrAO;u&Rw}Y z!A7e7y36GI3pmebL~Jk7=zn`nZ0@HOZL^}2f9FblIoKuA@m7%2nR^yU(#~lQZDk|G+Y~ z(z2%G_-W?G+}u@C|I+xst~6zlZnox+t@pI3dR}lg_`{dd@185~nVmi7WV9{^(w8%F|P3Q_B34zB2kG+kQBAUe9dzyn{U=Vmt4F(DTc2?zdum64j=;HkM#}~8Lce&Tw z^<1+!G@JLuHOV_=94AF@axA+zU(Tvw*M9+quMggpzT;`ge|J2Y!O`ZIUey1edVfC( zm>)hJpZ+&KdfVl9w==fC-CTS7&#JeZPMh6Kt-g~Q{brZ!zWNgBJE=d*W54yW7Pkv> zl*Bz3;$u7G@OR>s82!N4r*(x^c^xs@^kPE1T

{Zx?*Bem^;SX%*k%o8LV$Pu9nL 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 delta 97362 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 Q

\ 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 0215ae8c97cde56efe80be7e63462b79952042fa..5bfed6cbac9226d9519fee89e1129e884e383e35 100644 GIT binary patch delta 9521 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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 e8c6a4989d8..b48eca44937 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=[["/","8fc0e185070f60174a8d0bad69226022"],["/frontend/panels/dev-event-f19840b9a6a46f57cb064b384e1353f5.html","21cf247351b95fdd451c304e308a726c"],["/frontend/panels/dev-info-3765a371478cc66d677cf6dcc35267c6.html","dd614f2ee5e09a9dfd7f98822a55893d"],["/frontend/panels/dev-service-e32bcd3afdf485417a3e20b4fc760776.html","d7b70007dfb97e8ccbaa79bc2b41a51d"],["/frontend/panels/dev-state-8257d99a38358a150eafdb23fa6727e0.html","3cf24bb7e92c759b35a74cf641ed80cb"],["/frontend/panels/dev-template-cbb251acabd5e7431058ed507b70522b.html","edd6ef67f4ab763f9d3dd7d3aa6f4007"],["/frontend/panels/map-3b0ca63286cbe80f27bd36dbc2434e89.html","d22eee1c33886ce901851ccd35cb43ed"],["/static/core-22d39af274e1d824ca1302e10971f2d8.js","c6305fc1dee07b5bf94de95ffaccacc4"],["/static/frontend-61e57194179b27563a05282b58dd4f47.html","0a0bbfc6567f7ba6a4dabd7fef6a0ee7"],["/static/mdi-48fcee544a61b668451faf2b7295df70.html","08069e54df1fd92bbff70299605d8585"],["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","89313f9f2126ddea722150f8154aca03"]],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,n;for(a=0;aUKJ%PV?&fPIttRJO)@x;U?ULG(aPLKW%sI!}oKx3W-!Eg_ zoVALP<^F?RrF##YSy*H){Y^~g$EP3dp~3em%a?BtD=@9#XS@Gk(^}ooT&`G;y!!v2 z{|IS)?wP*y!Kc~W`#$ECTs&|))cXFuy$|p1D;EC3zwc4o?B49*$jNr${#iczIeyFYkAzQH=Dz;_6IRs_V`F)~>)xrc zi7H{2uAD79T(Cp^-1USfcS?`dMD|#JUe~-ca!UQjh;_LIe`d4Js9bw)?)Ln9^#y+{ z3s`-t^Dh2%|NrKm#rapK6s9faf3c?c-G`@}VyvzH-MTjQv3=~q3Cn)ue}8x9r)UN9 z*#nlw<@5BjN^Ad}|DGSmZdSZXGU_7x+ezllsmzzs__8h>&8aGU*du#iv8U4{E#{bs zxTnjO1&J#n{pu|~n4I4F`R4J~T`$iBJyu=6Am=!jo_j*F-6fUtivph5JPcg)IHb#< zljXFMf=-&%y8s88J+I@|>xn zOuA+8@~d0wFT{umxw!Z^I+3zlk5$i`g~5R z&N+)t=Lb=or%e2VW;s=?XD@XAI3d_XLtN)sX3IWzmJl(o!l?enqnoy@S|VVg7E`oE z>{7=>gL4i?6SXcGEDOmAsb^foZsl2e#C4XzTtcQa&`o}5>Ir`>G6%$&io&dGIC$fcHz29EZZa``f+PL5b2=`WNJ=pih?;*il{+;nqdX_I-_;)`#0 ztaQzN^>O#R^e@@5s}4xyPE0kKZX~SZRd3MH;$qXU>{ESC^x&ivd*QE}W}H166{eM> zyU9mb%OOoA$%>~@NO7Z{#=?u|Q}!~2t`W;ll#*t2QQ-~l5xZ)1>_-dBC6Qp3fN$qp z_BP*K_2g`n)O4Tam3^V=JNi85W$1aFab2cx_{g-~m$+_kKRzq|R=eQIm*sz?byiEB z>h!3e*Y1cf0J=R>vg$Rr`2092V4x?sfEYtm>z5cef9(9f(({jwhZC$`x{ z6dA7%sJfse++#ICRlT1A z9xssMd)lWmXNJzo@Oy99YB!6`3_Evz+3n-%=PqnCnB%CL%_@I?Z^bswp6orw>91sd zAF@3^z51Eh+vqP(FUvSbDxF#&_aHdqI-k}CIqyC9Zn!^8k+*o9YdEt$$9?y|Wx9=_jYp zwJNNUnRc81tJ=<6e7)f-a_je4862p0N?&%(Vg6(OpKrpyPyD=FN&9u(g~NYlyj#8@ z`t9wO$Uhx97xu3x>-qllx%K(kC7(9D+qLu0Qr}fvhNWC{k_%-@|HPhF*zu_KXT;1! zCD#167+&u#+TQ!<%fSa9EIuW?G;?a+?z>kbiX&j}?=6`>N*he}6v!`-jQLc=TG3M} z;CQb-l(9`Raf9dYi}Ky5GhxcFnK_?0{L7}Q&Und~yK|cLpWs7PnMGY&u5wO1 zyfFCT8H3H7o8Bu29V)1}@bxbD^YZJppH95({3qr=Y5O+SDcfH!J~@5$uAgkYL4V&i zg|}|o^k$|&_tD?4;|uEDZZt1RKAGD+kw5B8-f4@M6Z78)?r0QS>0PUJ=Fj=(ndgo8 zpPpav_~7B%8NwUR9_yTG{`KmE%F9<~ILbdWcoDM7F+Y%-+4y-{!O`1{HStLcJ_KLn zGx!)?qEqmTXWCp=cdJQrU%k2%mKeOVfBx0gL81Taa@fRjcU)hXZ4>+dl2twP?e$gM z=h*k}soc|Vw(0RNu7uyav^y?~Zj9ObS-Cg)(tY)W4-<5+nTxK~wXNSCFC%&C+1IPD z=Vl9vYi=}Hy7aB{4F30NyLOARZ?iOwv#WvMK~X@+E;#G`SdQa=`)^I0Zf28h(O>s^bIo$`^|zy@@ABW+ zxbEQ2LuP^TmdD?n+_m$>dbJta5ycN%{^uI6@%?dgW_x6;;`8DsHLC??Gp;Hr(GA!A zHqUYE$sbxT?(MwCQl4{W!`5#rgZD<3{W$u!_@Tp}Z-+k~c6jhgkFWgRlX|wY6Y5h% G7#IL$zmzlp delta 2313 zcmbOxG)0JAzMF$1;&#zQcGda=%u7Oj)n=dFl3DEAcalHPtogBHlG8*j&YOj~|KHat zs77vDl2UFp*@cH=-{b1z-x<=c9$H}PtJrINakba1MeEpfXPEzzoL(FtKYLT@!QGh$ z*38OcIB32jFIL~6c*UKw?A6j@HI;vkuF`zJXZPj#VFjiY{A}R`x35iGW!9>vQosFw zWgX|#%44U43Vxnty?`{HKGUgj$)7t_=@$nb0T&qMsnFTa%jD7xh3#nE^!)7I%c7XB!l~vr68lb8K$q^@DeG>QANUTsO1$cb4hQp0MZVZkxZixA-Ts zqiLD#_80#a*O%qbNiR(mIDL`5+(9*dk)40 z=g!Q1-W?S?|NrN|v#&Rvos-oT_Qi4cld}mo8@9}BmMSrv9T#Jfe7xL9*I6M_Xj8y) z#YDsNAsRdCy^6gqPY8OxxnH{U^30{jyyF~i_KC*y9XM9A#B<)!g(oUcJq}U#=`!eb zopRJ+k;)~{cBWSgU9zM$YbYxDZwV@NN)b6G&?=d3GIjY^k*Vw41-*sk423rPoH7a3 zOb%M^bxLEQ=H!=dy+N9trmP+cXDZiiNwPX`iW!uJ9Sig6c#zH>d0RD zSf`WovEYS(BT<`7Qr^S~3+wPWFWYiLazSWQ@icBvSDy-}x+NzIm>2h``8>7k-=uNU z!qdrNYLnZnUYdq9(yPwRY#l6&G?ePOikx`*R9J2)Ub_Q^6 zuuw{z)OcvZVs_4@NsH^b1Z*#8UN#ih=`C5Ov{WMLV6g1s)yG2gmy7=p*QG(_Z)+U)t(P z=GmDsZnK{5^lEuvrC7OYrvj(=%qAY;=yzV)IrrtV>oVotRF>cWn7?67nufYsrChz6 zl5@ueg>wyQid7mpUP6Z^O)g^kbKFgDS)h_g`U=rC(Fz>P?6&op_%FTJydvU* z-d~#>GJc5boX*bJ*KzZfl-yAh6lo~C;H)nsLUc)WZ1iSWsFZyc(ewmm&KX?A7nrDn02 z0(S&Iw$(d-ult#kWiy|BS>64>d9UyO+{BnueRswe{T=r%KP%q<%Jh_TZ*!K#6zfR^ zNt>3$Rc_zDjwdQ5%F=#zp8R|Z=i`S8*ru)#t*QC`=%$*%x{v4PtSo-R``sq~-OT7M z`4^}A7xSccYBX0h_%DpLUU8sG=VMi}eCy=e2R+-5rfiaZTYsyH@wl7SzHe)1^VL}E z>cp2E45_~OCwZb%rmfQDvxlw1j&MiKiSOz5s9gU1{MUIaliLGgr>yvS!%g_XiHkA! z_Z!U)@83}D*v!X-!`|O`=j=$@E?tRyqHNPGCKPT^+e<*KjzHr({}(lcz-=7(R1ksSIb8s84G- z72G&lUi4R4W_r5oQw4*&^P4B1>$B&G{=GJ$ z&dKFh$_Du#>JPR&pW)4O*=}C#67%$BlllDq@!#>6`Sv4l%g_D=ThjN-TKqQVefLw@ z;pC#Ov~PR*j{zbbhYIk(Q^eXWvoQc@fgom z_-&`8%G1ssdw*|1UHM)0{yXzt3Zty_$0b=`fee)A7* z+Wf%7eN%93>@BSvt?wTdo3?{}M<+)#h>_4vZo%kwA9 zc_*FC5pn$cyXut4?GNO1zOSg1jok7u@^Iys*!fSb_g37!w(Cl57jFsgIniyMzfM_} z`CmKhT4#DN=HM)D^*{Nu6LSxpKWB616_@3F<>%3@H+TY;F3#SWwedU8D&J$_j@zGA w3I9n-Zd<$6f9|WZ+YXz*o6pVuXuEu$JR4JadEt)FYX6y^=1)E~RfK^70LHy(XaE2J From 2efd7d4e4a161186d61286b4e62c9f02b2638ebf Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 20 Jan 2017 22:20:07 -0800 Subject: [PATCH 087/191] Hue improvements (#5474) * Allow automatic removal of all Hue entities from emulated_hue * Allow disabling of Hue groups * Only add device state attributes if they need to be there --- homeassistant/components/light/hue.py | 64 ++++++++++++++++++++------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index 259553cc620..3e1e81b05ea 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -25,6 +25,7 @@ from homeassistant.components.light import ( from homeassistant.config import load_yaml_config_file from homeassistant.const import (CONF_FILENAME, CONF_HOST, DEVICE_DEFAULT_NAME) from homeassistant.loader import get_component +from homeassistant.components.emulated_hue import ATTR_EMULATED_HUE import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['phue==0.9'] @@ -50,10 +51,21 @@ SUPPORT_HUE = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_EFFECT | SUPPORT_FLASH | SUPPORT_RGB_COLOR | SUPPORT_TRANSITION | SUPPORT_XY_COLOR) +CONF_ALLOW_IN_EMULATED_HUE = "allow_in_emulated_hue" +DEFAULT_ALLOW_IN_EMULATED_HUE = True + +CONF_ALLOW_HUE_GROUPS = "allow_hue_groups" +DEFAULT_ALLOW_HUE_GROUPS = True + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_HOST): cv.string, - vol.Optional(CONF_ALLOW_UNREACHABLE): cv.boolean, - vol.Optional(CONF_FILENAME): cv.string, + vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_ALLOW_UNREACHABLE, + default=DEFAULT_ALLOW_UNREACHABLE): cv.boolean, + vol.Optional(CONF_FILENAME, default=PHUE_CONFIG_FILE): cv.string, + vol.Optional(CONF_ALLOW_IN_EMULATED_HUE, + default=DEFAULT_ALLOW_IN_EMULATED_HUE): cv.boolean, + vol.Optional(CONF_ALLOW_HUE_GROUPS, + default=DEFAULT_ALLOW_HUE_GROUPS): cv.boolean, }) ATTR_GROUP_NAME = "group_name" @@ -63,6 +75,8 @@ SCENE_SCHEMA = vol.Schema({ vol.Required(ATTR_SCENE_NAME): cv.string, }) +ATTR_IS_HUE_GROUP = "is_hue_group" + def _find_host_from_config(hass, filename=PHUE_CONFIG_FILE): """Attempt to detect host based on existing configuration.""" @@ -84,9 +98,10 @@ def _find_host_from_config(hass, filename=PHUE_CONFIG_FILE): def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Hue lights.""" # Default needed in case of discovery - filename = config.get(CONF_FILENAME, PHUE_CONFIG_FILE) - allow_unreachable = config.get(CONF_ALLOW_UNREACHABLE, - DEFAULT_ALLOW_UNREACHABLE) + filename = config.get(CONF_FILENAME) + allow_unreachable = config.get(CONF_ALLOW_UNREACHABLE) + allow_in_emulated_hue = config.get(CONF_ALLOW_IN_EMULATED_HUE) + allow_hue_groups = config.get(CONF_ALLOW_HUE_GROUPS) if discovery_info is not None: host = urlparse(discovery_info[1]).hostname @@ -109,10 +124,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): socket.gethostbyname(host) in _CONFIGURED_BRIDGES: return - setup_bridge(host, hass, add_devices, filename, allow_unreachable) + setup_bridge(host, hass, add_devices, filename, allow_unreachable, + allow_in_emulated_hue, allow_hue_groups) -def setup_bridge(host, hass, add_devices, filename, allow_unreachable): +def setup_bridge(host, hass, add_devices, filename, allow_unreachable, + allow_in_emulated_hue, allow_hue_groups): """Setup a phue bridge based on host parameter.""" import phue @@ -129,7 +146,8 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): _LOGGER.warning("Connected to Hue at %s but not registered.", host) request_configuration(host, hass, add_devices, filename, - allow_unreachable) + allow_unreachable, allow_in_emulated_hue, + allow_hue_groups) return @@ -143,7 +161,7 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): lights = {} lightgroups = {} - skip_groups = False + skip_groups = not allow_hue_groups @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_lights(): @@ -184,7 +202,8 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): if light_id not in lights: lights[light_id] = HueLight(int(light_id), info, bridge, update_lights, - bridge_type, allow_unreachable) + bridge_type, allow_unreachable, + allow_in_emulated_hue) new_lights.append(lights[light_id]) else: lights[light_id].info = info @@ -200,7 +219,8 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): if lightgroup_id not in lightgroups: lightgroups[lightgroup_id] = HueLight( int(lightgroup_id), info, bridge, update_lights, - bridge_type, allow_unreachable, True) + bridge_type, allow_unreachable, allow_in_emulated_hue, + True) new_lights.append(lightgroups[lightgroup_id]) else: lightgroups[lightgroup_id].info = info @@ -229,7 +249,8 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable): def request_configuration(host, hass, add_devices, filename, - allow_unreachable): + allow_unreachable, allow_in_emulated_hue, + allow_hue_groups): """Request configuration steps from the user.""" configurator = get_component('configurator') @@ -243,7 +264,8 @@ def request_configuration(host, hass, add_devices, filename, # pylint: disable=unused-argument def hue_configuration_callback(data): """The actions to do when our configuration callback is called.""" - setup_bridge(host, hass, add_devices, filename, allow_unreachable) + setup_bridge(host, hass, add_devices, filename, allow_unreachable, + allow_in_emulated_hue, allow_hue_groups) _CONFIGURING[host] = configurator.request_config( hass, "Philips Hue", hue_configuration_callback, @@ -259,7 +281,8 @@ class HueLight(Light): """Representation of a Hue light.""" def __init__(self, light_id, info, bridge, update_lights, - bridge_type, allow_unreachable, is_group=False): + bridge_type, allow_unreachable, allow_in_emulated_hue, + is_group=False): """Initialize the light.""" self.light_id = light_id self.info = info @@ -268,6 +291,7 @@ class HueLight(Light): self.bridge_type = bridge_type self.allow_unreachable = allow_unreachable self.is_group = is_group + self.allow_in_emulated_hue = allow_in_emulated_hue if is_group: self._command_func = self.bridge.set_group @@ -395,3 +419,13 @@ class HueLight(Light): def update(self): """Synchronize state with bridge.""" self.update_lights(no_throttle=True) + + @property + def device_state_attributes(self): + """Return the device state attributes.""" + attributes = {} + if not self.allow_in_emulated_hue: + attributes[ATTR_EMULATED_HUE] = self.allow_in_emulated_hue + if self.is_group: + attributes[ATTR_IS_HUE_GROUP] = self.is_group + return attributes From 074f9315d7f0d20837ed19d1846a295120ed63c9 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 20 Jan 2017 22:21:28 -0800 Subject: [PATCH 088/191] Fan improvements (#5457) * Remove SPEED_MED from fan * Correctly use the oscillation on/off payloads for MQTT fan * Add set_direction service documentation * Correct function name for Wink fans * Check for existence of the correct topic * Enable set fan speed in emulated_hue * features -> functions * Final emulated_hue fan fixes * Fix linting issues * Revert to supported features instead of supported functions * Fix logic * Add a test for emulated_hue fan support --- .../components/emulated_hue/hue_api.py | 39 ++++++++++++++++++- homeassistant/components/fan/__init__.py | 7 +++- homeassistant/components/fan/demo.py | 6 +-- homeassistant/components/fan/isy994.py | 8 ++-- homeassistant/components/fan/mqtt.py | 19 +++++---- homeassistant/components/fan/services.yaml | 13 ++++++- homeassistant/components/fan/wink.py | 2 +- tests/components/emulated_hue/test_hue_api.py | 39 ++++++++++++++++++- 8 files changed, 112 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 9b0a2828394..b56be3484fe 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -17,6 +17,10 @@ from homeassistant.components.media_player import ( ATTR_MEDIA_VOLUME_LEVEL, ATTR_SUPPORTED_MEDIA_COMMANDS, SUPPORT_VOLUME_SET, ) +from homeassistant.components.fan import ( + ATTR_SPEED, SUPPORT_SET_SPEED, SPEED_OFF, SPEED_LOW, + SPEED_MEDIUM, SPEED_HIGH +) from homeassistant.components.http import HomeAssistantView _LOGGER = logging.getLogger(__name__) @@ -174,7 +178,9 @@ class HueOneLightChangeView(HomeAssistantView): # Make sure the entity actually supports brightness entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - if (entity_features & SUPPORT_BRIGHTNESS) == SUPPORT_BRIGHTNESS: + if (entity_features & + SUPPORT_BRIGHTNESS & + (entity.domain == "light")) == SUPPORT_BRIGHTNESS: if brightness is not None: data[ATTR_BRIGHTNESS] = brightness @@ -207,6 +213,23 @@ class HueOneLightChangeView(HomeAssistantView): else: service = SERVICE_CLOSE_COVER + # If the requested entity is a fan, convert to speed + elif entity.domain == "fan": + functions = entity.attributes.get( + ATTR_SUPPORTED_FEATURES, 0) + if (functions & SUPPORT_SET_SPEED) == SUPPORT_SET_SPEED: + if brightness is not None: + domain = entity.domain + # Convert 0-100 to a fan speed + if brightness == 0: + data[ATTR_SPEED] = SPEED_OFF + elif brightness <= 33.3 and brightness > 0: + data[ATTR_SPEED] = SPEED_LOW + elif brightness <= 66.6 and brightness > 33.3: + data[ATTR_SPEED] = SPEED_MEDIUM + elif brightness <= 100 and brightness > 66.6: + data[ATTR_SPEED] = SPEED_HIGH + if entity.domain in config.off_maps_to_on_domains: # Map the off command to on service = SERVICE_TURN_ON @@ -269,7 +292,9 @@ def parse_hue_api_put_light_body(request_json, entity): report_brightness = True result = (brightness > 0) - elif entity.domain == "script" or entity.domain == "media_player": + elif (entity.domain == "script" or + entity.domain == "media_player" or + entity.domain == "fan"): # Convert 0-255 to 0-100 level = brightness / 255 * 100 brightness = round(level) @@ -299,6 +324,16 @@ def get_entity_state(config, entity): ATTR_MEDIA_VOLUME_LEVEL, 1.0 if final_state else 0.0) # Convert 0.0-1.0 to 0-255 final_brightness = round(min(1.0, level) * 255) + elif entity.domain == "fan": + speed = entity.attributes.get(ATTR_SPEED, 0) + # Convert 0.0-1.0 to 0-255 + final_brightness = 0 + if speed == SPEED_LOW: + final_brightness = 85 + elif speed == SPEED_MEDIUM: + final_brightness = 170 + elif speed == SPEED_HIGH: + final_brightness = 255 else: final_state, final_brightness = cached_state # Make sure brightness is valid diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index efb7e0b1496..e6da2ff0fd7 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -41,7 +41,6 @@ SERVICE_SET_DIRECTION = 'set_direction' SPEED_OFF = 'off' SPEED_LOW = 'low' -SPEED_MED = 'med' SPEED_MEDIUM = 'medium' SPEED_HIGH = 'high' @@ -230,6 +229,9 @@ class FanEntity(ToggleEntity): def set_speed(self: ToggleEntity, speed: str) -> None: """Set the speed of the fan.""" + if speed is SPEED_OFF: + self.turn_off() + return raise NotImplementedError() def set_direction(self: ToggleEntity, direction: str) -> None: @@ -238,6 +240,9 @@ class FanEntity(ToggleEntity): def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None: """Turn on the fan.""" + if speed is SPEED_OFF: + self.turn_off() + return raise NotImplementedError() def turn_off(self: ToggleEntity, **kwargs) -> None: diff --git a/homeassistant/components/fan/demo.py b/homeassistant/components/fan/demo.py index 7ba6b4d67fb..6d24f8d3048 100644 --- a/homeassistant/components/fan/demo.py +++ b/homeassistant/components/fan/demo.py @@ -5,7 +5,7 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.fan import (SPEED_LOW, SPEED_MED, SPEED_HIGH, +from homeassistant.components.fan import (SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, FanEntity, SUPPORT_SET_SPEED, SUPPORT_OSCILLATE, SUPPORT_DIRECTION) from homeassistant.const import STATE_OFF @@ -54,9 +54,9 @@ class DemoFan(FanEntity): @property def speed_list(self) -> list: """Get the list of available speeds.""" - return [STATE_OFF, SPEED_LOW, SPEED_MED, SPEED_HIGH] + return [STATE_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] - def turn_on(self, speed: str=SPEED_MED) -> None: + def turn_on(self, speed: str=SPEED_MEDIUM) -> None: """Turn on the entity.""" self.set_speed(speed) diff --git a/homeassistant/components/fan/isy994.py b/homeassistant/components/fan/isy994.py index fd0690f4253..30c1d2ed2a3 100644 --- a/homeassistant/components/fan/isy994.py +++ b/homeassistant/components/fan/isy994.py @@ -8,7 +8,7 @@ import logging from typing import Callable from homeassistant.components.fan import (FanEntity, DOMAIN, SPEED_OFF, - SPEED_LOW, SPEED_MED, + SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH) import homeassistant.components.isy994 as isy from homeassistant.const import STATE_UNKNOWN, STATE_ON, STATE_OFF @@ -20,8 +20,8 @@ VALUE_TO_STATE = { 0: SPEED_OFF, 63: SPEED_LOW, 64: SPEED_LOW, - 190: SPEED_MED, - 191: SPEED_MED, + 190: SPEED_MEDIUM, + 191: SPEED_MEDIUM, 255: SPEED_HIGH, } @@ -29,7 +29,7 @@ STATE_TO_VALUE = {} for key in VALUE_TO_STATE: STATE_TO_VALUE[VALUE_TO_STATE[key]] = key -STATES = [SPEED_OFF, SPEED_LOW, SPEED_MED, SPEED_HIGH] +STATES = [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] # pylint: disable=unused-argument diff --git a/homeassistant/components/fan/mqtt.py b/homeassistant/components/fan/mqtt.py index 08db5ead26b..4540ce01532 100644 --- a/homeassistant/components/fan/mqtt.py +++ b/homeassistant/components/fan/mqtt.py @@ -15,7 +15,7 @@ from homeassistant.const import ( from homeassistant.components.mqtt import ( CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN) import homeassistant.helpers.config_validation as cv -from homeassistant.components.fan import (SPEED_LOW, SPEED_MED, SPEED_MEDIUM, +from homeassistant.components.fan import (SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, FanEntity, SUPPORT_SET_SPEED, SUPPORT_OSCILLATE, SPEED_OFF, ATTR_SPEED) @@ -64,11 +64,11 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_PAYLOAD_OSCILLATION_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, vol.Optional(CONF_PAYLOAD_LOW_SPEED, default=SPEED_LOW): cv.string, - vol.Optional(CONF_PAYLOAD_MEDIUM_SPEED, default=SPEED_MED): cv.string, + vol.Optional(CONF_PAYLOAD_MEDIUM_SPEED, default=SPEED_MEDIUM): cv.string, vol.Optional(CONF_PAYLOAD_HIGH_SPEED, default=SPEED_HIGH): cv.string, vol.Optional(CONF_SPEED_LIST, default=[SPEED_OFF, SPEED_LOW, - SPEED_MED, SPEED_HIGH]): cv.ensure_list, + SPEED_MEDIUM, SPEED_HIGH]): cv.ensure_list, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, }) @@ -162,7 +162,7 @@ class MqttFan(FanEntity): if payload == self._payload[SPEED_LOW]: self._speed = SPEED_LOW elif payload == self._payload[SPEED_MEDIUM]: - self._speed = SPEED_MED + self._speed = SPEED_MEDIUM elif payload == self._payload[SPEED_HIGH]: self._speed = SPEED_HIGH self.update_ha_state() @@ -235,7 +235,7 @@ class MqttFan(FanEntity): """Return the oscillation state.""" return self._oscillation - def turn_on(self, speed: str=SPEED_MED) -> None: + def turn_on(self, speed: str=SPEED_MEDIUM) -> None: """Turn on the entity.""" mqtt.publish(self._hass, self._topic[CONF_COMMAND_TOPIC], self._payload[STATE_ON], self._qos, self._retain) @@ -252,7 +252,7 @@ class MqttFan(FanEntity): mqtt_payload = SPEED_OFF if speed == SPEED_LOW: mqtt_payload = self._payload[SPEED_LOW] - elif speed == SPEED_MED: + elif speed == SPEED_MEDIUM: mqtt_payload = self._payload[SPEED_MEDIUM] elif speed == SPEED_HIGH: mqtt_payload = self._payload[SPEED_HIGH] @@ -265,9 +265,12 @@ class MqttFan(FanEntity): def oscillate(self, oscillating: bool) -> None: """Set oscillation.""" - if self._topic[CONF_SPEED_COMMAND_TOPIC] is not None: + if self._topic[CONF_OSCILLATION_COMMAND_TOPIC] is not None: self._oscillation = oscillating + payload = self._payload[OSCILLATE_ON_PAYLOAD] + if oscillating is False: + payload = self._payload[OSCILLATE_OFF_PAYLOAD] mqtt.publish(self._hass, self._topic[CONF_OSCILLATION_COMMAND_TOPIC], - self._oscillation, self._qos, self._retain) + payload, self._qos, self._retain) self.update_ha_state() diff --git a/homeassistant/components/fan/services.yaml b/homeassistant/components/fan/services.yaml index e729e7f7e89..7862aa9a7c3 100644 --- a/homeassistant/components/fan/services.yaml +++ b/homeassistant/components/fan/services.yaml @@ -50,4 +50,15 @@ toggle: fields: entity_id: description: Name(s) of the entities to toggle - exampl: 'fan.living_room' \ No newline at end of file + exampl: 'fan.living_room' + +set_direction: + description: Set the fan rotation direction + + fields: + entity_id: + description: Name(s) of the entities to toggle + exampl: 'fan.living_room' + direction: + description: The direction to rotate + example: 'left' diff --git a/homeassistant/components/fan/wink.py b/homeassistant/components/fan/wink.py index 066dbfcb561..74fd06e5516 100644 --- a/homeassistant/components/fan/wink.py +++ b/homeassistant/components/fan/wink.py @@ -32,7 +32,7 @@ class WinkFanDevice(WinkDevice, FanEntity): """Initialize the fan.""" WinkDevice.__init__(self, wink, hass) - def set_drection(self: ToggleEntity, direction: str) -> None: + def set_direction(self: ToggleEntity, direction: str) -> None: """Set the direction of the fan.""" self.wink.set_fan_direction(direction) diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 7c73e933fd3..c3888bd9cf7 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -8,7 +8,7 @@ import pytest from homeassistant import bootstrap, const, core import homeassistant.components as core_components from homeassistant.components import ( - emulated_hue, http, light, script, media_player + emulated_hue, http, light, script, media_player, fan ) from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.components.emulated_hue.hue_api import ( @@ -83,6 +83,15 @@ def hass_hue(loop, hass): ] })) + loop.run_until_complete( + bootstrap.async_setup_component(hass, fan.DOMAIN, { + 'fan': [ + { + 'platform': 'demo', + } + ] + })) + # Kitchen light is explicitly excluded from being exposed kitchen_light_entity = hass.states.get('light.kitchen_lights') attrs = dict(kitchen_light_entity.attributes) @@ -137,6 +146,7 @@ def test_discover_lights(hue_client): assert 'media_player.bedroom' in devices assert 'media_player.walkman' in devices assert 'media_player.lounge_room' in devices + assert 'fan.living_room_fan' in devices @asyncio.coroutine @@ -281,6 +291,33 @@ def test_put_light_state_media_player(hass_hue, hue_client): assert walkman.attributes[media_player.ATTR_MEDIA_VOLUME_LEVEL] == level +@asyncio.coroutine +def test_put_light_state_fan(hass_hue, hue_client): + """Test turning on fan and setting speed.""" + # Turn the fan off first + yield from hass_hue.services.async_call( + fan.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'fan.living_room_fan'}, + blocking=True) + + # Emulated hue converts 0-100% to 0-255. + level = 23 + brightness = round(level * 255 / 100) + + fan_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'fan.living_room_fan', True, brightness) + + fan_result_json = yield from fan_result.json() + + assert fan_result.status == 200 + assert len(fan_result_json) == 2 + + living_room_fan = hass_hue.states.get('fan.living_room_fan') + assert living_room_fan.state == 'on' + assert living_room_fan.attributes[fan.ATTR_SPEED] == fan.SPEED_MEDIUM + + # pylint: disable=invalid-name @asyncio.coroutine def test_put_with_form_urlencoded_content_type(hass_hue, hue_client): From 2fff8a5a11549b311f1b945a2d549d1318104073 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 21 Jan 2017 08:35:18 +0100 Subject: [PATCH 089/191] [TTS] options support for service calls (#5436) * [TTS] Support now options like voice and age on service. * Add unittest --- homeassistant/components/tts/__init__.py | 70 ++++++++--- homeassistant/components/tts/demo.py | 7 +- homeassistant/components/tts/google.py | 2 +- homeassistant/components/tts/picotts.py | 2 +- homeassistant/components/tts/voicerss.py | 2 +- homeassistant/components/tts/yandextts.py | 2 +- tests/components/tts/test_init.py | 136 +++++++++++++++++++--- 7 files changed, 181 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 5ee92747196..eda0fa27f53 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -5,6 +5,8 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/tts/ """ import asyncio +import ctypes +import functools as ft import hashlib import logging import mimetypes @@ -49,9 +51,11 @@ SERVICE_CLEAR_CACHE = 'clear_cache' ATTR_MESSAGE = 'message' ATTR_CACHE = 'cache' ATTR_LANGUAGE = 'language' +ATTR_OPTIONS = 'options' -_RE_VOICE_FILE = re.compile(r"([a-f0-9]{40})_([^_]+)_([a-z]+)\.[a-z0-9]{3,4}") -KEY_PATTERN = '{}_{}_{}' +_RE_VOICE_FILE = re.compile( + r"([a-f0-9]{40})_([^_]+)_([^_]+)_([a-z]+)\.[a-z0-9]{3,4}") +KEY_PATTERN = '{0}_{1}_{2}_{3}' PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ vol.Optional(CONF_CACHE, default=DEFAULT_CACHE): cv.boolean, @@ -60,12 +64,12 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ vol.All(vol.Coerce(int), vol.Range(min=60, max=57600)), }) - SCHEMA_SERVICE_SAY = vol.Schema({ vol.Required(ATTR_MESSAGE): cv.string, vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_CACHE): cv.boolean, - vol.Optional(ATTR_LANGUAGE): cv.string + vol.Optional(ATTR_LANGUAGE): cv.string, + vol.Optional(ATTR_OPTIONS): dict, }) SCHEMA_SERVICE_CLEAR_CACHE = vol.Schema({}) @@ -125,10 +129,13 @@ def async_setup(hass, config): message = service.data.get(ATTR_MESSAGE) cache = service.data.get(ATTR_CACHE) language = service.data.get(ATTR_LANGUAGE) + options = service.data.get(ATTR_OPTIONS) try: url = yield from tts.async_get_url( - p_type, message, cache=cache, language=language) + p_type, message, cache=cache, language=language, + options=options + ) except HomeAssistantError as err: _LOGGER.error("Error on init tts: %s", err) return @@ -212,7 +219,9 @@ class SpeechManager(object): record = _RE_VOICE_FILE.match(file_data) if record: key = KEY_PATTERN.format( - record.group(1), record.group(2), record.group(3)) + record.group(1), record.group(2), record.group(3), + record.group(4) + ) cache[key.lower()] = file_data.lower() return cache @@ -249,22 +258,37 @@ class SpeechManager(object): self.providers[engine] = provider @asyncio.coroutine - def async_get_url(self, engine, message, cache=None, language=None): + def async_get_url(self, engine, message, cache=None, language=None, + options=None): """Get URL for play message. This method is a coroutine. """ provider = self.providers[engine] + msg_hash = hashlib.sha1(bytes(message, 'utf-8')).hexdigest() + use_cache = cache if cache is not None else self.use_cache + # languages language = language or provider.default_language if language is None or \ language not in provider.supported_languages: raise HomeAssistantError("Not supported language {0}".format( language)) - msg_hash = hashlib.sha1(bytes(message, 'utf-8')).hexdigest() - key = KEY_PATTERN.format(msg_hash, language, engine).lower() - use_cache = cache if cache is not None else self.use_cache + # options + options = options or provider.default_options + if options is not None: + invalid_opts = [opt_name for opt_name in options.keys() + if opt_name not in provider.supported_options] + if invalid_opts: + raise HomeAssistantError( + "Invalid options found: %s", invalid_opts) + options_key = ctypes.c_size_t(hash(frozenset(options))).value + else: + options_key = '-' + + key = KEY_PATTERN.format( + msg_hash, language, options_key, engine).lower() # is speech allready in memory if key in self.mem_cache: @@ -276,20 +300,21 @@ class SpeechManager(object): # load speech from provider into memory else: filename = yield from self.async_get_tts_audio( - engine, key, message, use_cache, language) + engine, key, message, use_cache, language, options) return "{}/api/tts_proxy/{}".format( self.hass.config.api.base_url, filename) @asyncio.coroutine - def async_get_tts_audio(self, engine, key, message, cache, language): + def async_get_tts_audio(self, engine, key, message, cache, language, + options): """Receive TTS and store for view in cache. This method is a coroutine. """ provider = self.providers[engine] extension, data = yield from provider.async_get_tts_audio( - message, language) + message, language, options) if data is None or extension is None: raise HomeAssistantError( @@ -377,7 +402,7 @@ class SpeechManager(object): raise HomeAssistantError("Wrong tts file format!") key = KEY_PATTERN.format( - record.group(1), record.group(2), record.group(3)) + record.group(1), record.group(2), record.group(3), record.group(4)) if key not in self.mem_cache: if key not in self.file_cache: @@ -403,11 +428,21 @@ class Provider(object): """List of supported languages.""" return None - def get_tts_audio(self, message, language): + @property + def supported_options(self): + """List of supported options like voice, emotionen.""" + return None + + @property + def default_options(self): + """Dict include default options.""" + return None + + def get_tts_audio(self, message, language, options=None): """Load tts audio file from provider.""" raise NotImplementedError() - def async_get_tts_audio(self, message, language): + def async_get_tts_audio(self, message, language, options=None): """Load tts audio file from provider. Return a tuple of file extension and data as bytes. @@ -415,7 +450,8 @@ class Provider(object): This method must be run in the event loop and returns a coroutine. """ return self.hass.loop.run_in_executor( - None, self.get_tts_audio, message, language) + None, ft.partial( + self.get_tts_audio, message, language, options=options)) class TextToSpeechView(HomeAssistantView): diff --git a/homeassistant/components/tts/demo.py b/homeassistant/components/tts/demo.py index 88afa0643f2..95362b49db9 100644 --- a/homeassistant/components/tts/demo.py +++ b/homeassistant/components/tts/demo.py @@ -43,7 +43,12 @@ class DemoProvider(Provider): """List of supported languages.""" return SUPPORT_LANGUAGES - def get_tts_audio(self, message, language): + @property + def supported_options(self): + """List of supported options like voice, emotionen.""" + return ['voice', 'age'] + + def get_tts_audio(self, message, language, options=None): """Load TTS from demo.""" filename = os.path.join(os.path.dirname(__file__), "demo.mp3") try: diff --git a/homeassistant/components/tts/google.py b/homeassistant/components/tts/google.py index 10ce3de6c6b..32c9663eedc 100644 --- a/homeassistant/components/tts/google.py +++ b/homeassistant/components/tts/google.py @@ -70,7 +70,7 @@ class GoogleProvider(Provider): return SUPPORT_LANGUAGES @asyncio.coroutine - def async_get_tts_audio(self, message, language): + def async_get_tts_audio(self, message, language, options=None): """Load TTS from google.""" from gtts_token import gtts_token diff --git a/homeassistant/components/tts/picotts.py b/homeassistant/components/tts/picotts.py index 3cc133864b6..49addd9b177 100644 --- a/homeassistant/components/tts/picotts.py +++ b/homeassistant/components/tts/picotts.py @@ -49,7 +49,7 @@ class PicoProvider(Provider): """List of supported languages.""" return SUPPORT_LANGUAGES - def get_tts_audio(self, message, language): + def get_tts_audio(self, message, language, options=None): """Load TTS using pico2wave.""" with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmpf: fname = tmpf.name diff --git a/homeassistant/components/tts/voicerss.py b/homeassistant/components/tts/voicerss.py index f7a97a354f0..b0c74d1de30 100644 --- a/homeassistant/components/tts/voicerss.py +++ b/homeassistant/components/tts/voicerss.py @@ -114,7 +114,7 @@ class VoiceRSSProvider(Provider): return SUPPORT_LANGUAGES @asyncio.coroutine - def async_get_tts_audio(self, message, language): + def async_get_tts_audio(self, message, language, options=None): """Load TTS from VoiceRSS.""" websession = async_get_clientsession(self.hass) form_data = self._form_data.copy() diff --git a/homeassistant/components/tts/yandextts.py b/homeassistant/components/tts/yandextts.py index d5825ce297f..4dc8618f0d0 100644 --- a/homeassistant/components/tts/yandextts.py +++ b/homeassistant/components/tts/yandextts.py @@ -77,7 +77,7 @@ class YandexSpeechKitProvider(Provider): return SUPPORT_LANGUAGES @asyncio.coroutine - def async_get_tts_audio(self, message, language): + def async_get_tts_audio(self, message, language, options=None): """Load TTS from yandex.""" websession = async_get_clientsession(self.hass) diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index 715b98c4740..023c05edf99 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -1,7 +1,8 @@ """The tests for the TTS component.""" +import ctypes import os import shutil -from unittest.mock import patch +from unittest.mock import patch, PropertyMock import requests @@ -82,11 +83,11 @@ class TestTTS(object): assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find( "/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" - "_en_demo.mp3") \ + "_en_-_demo.mp3") \ != -1 assert os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_and_test_service_with_config_language(self): """Setup the demo platform and call service.""" @@ -111,11 +112,11 @@ class TestTTS(object): assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find( "/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" - "_de_demo.mp3") \ + "_de_-_demo.mp3") \ != -1 assert os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_de_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_de_-_demo.mp3")) def test_setup_component_and_test_service_with_wrong_conf_language(self): """Setup the demo platform and call service with wrong config.""" @@ -152,11 +153,11 @@ class TestTTS(object): assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find( "/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" - "_de_demo.mp3") \ + "_de_-_demo.mp3") \ != -1 assert os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_de_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_de_-_demo.mp3")) def test_setup_component_test_service_with_wrong_service_language(self): """Setup the demo platform and call service.""" @@ -180,7 +181,106 @@ class TestTTS(object): assert len(calls) == 0 assert not os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_lang_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_lang_-_demo.mp3")) + + def test_setup_component_and_test_service_with_service_options(self): + """Setup the demo platform and call service with options.""" + calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + config = { + tts.DOMAIN: { + 'platform': 'demo', + } + } + + with assert_setup_component(1, tts.DOMAIN): + setup_component(self.hass, tts.DOMAIN, config) + + self.hass.services.call(tts.DOMAIN, 'demo_say', { + tts.ATTR_MESSAGE: "I person is on front of your door.", + tts.ATTR_LANGUAGE: "de", + tts.ATTR_OPTIONS: { + 'voice': 'alex' + } + }) + self.hass.block_till_done() + + opt_hash = ctypes.c_size_t(hash(frozenset({'voice': 'alex'}))).value + + assert len(calls) == 1 + assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC + assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find( + "/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" + "_de_{0}_demo.mp3".format(opt_hash)) \ + != -1 + assert os.path.isfile(os.path.join( + self.default_tts_cache, + "265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format( + opt_hash))) + + @patch('homeassistant.components.tts.demo.DemoProvider.default_options', + new_callable=PropertyMock(return_value={'voice': 'alex'})) + def test_setup_component_and_test_with_service_options_def(self, def_mock): + """Setup the demo platform and call service with default options.""" + calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + config = { + tts.DOMAIN: { + 'platform': 'demo', + } + } + + with assert_setup_component(1, tts.DOMAIN): + setup_component(self.hass, tts.DOMAIN, config) + + self.hass.services.call(tts.DOMAIN, 'demo_say', { + tts.ATTR_MESSAGE: "I person is on front of your door.", + tts.ATTR_LANGUAGE: "de", + }) + self.hass.block_till_done() + + opt_hash = ctypes.c_size_t(hash(frozenset({'voice': 'alex'}))).value + + assert len(calls) == 1 + assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC + assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find( + "/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" + "_de_{0}_demo.mp3".format(opt_hash)) \ + != -1 + assert os.path.isfile(os.path.join( + self.default_tts_cache, + "265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format( + opt_hash))) + + def test_setup_component_and_test_service_with_service_options_wrong(self): + """Setup the demo platform and call service with wrong options.""" + calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + config = { + tts.DOMAIN: { + 'platform': 'demo', + } + } + + with assert_setup_component(1, tts.DOMAIN): + setup_component(self.hass, tts.DOMAIN, config) + + self.hass.services.call(tts.DOMAIN, 'demo_say', { + tts.ATTR_MESSAGE: "I person is on front of your door.", + tts.ATTR_LANGUAGE: "de", + tts.ATTR_OPTIONS: { + 'speed': 1 + } + }) + self.hass.block_till_done() + + opt_hash = ctypes.c_size_t(hash(frozenset({'speed': 1}))).value + + assert len(calls) == 0 + assert not os.path.isfile(os.path.join( + self.default_tts_cache, + "265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format( + opt_hash))) def test_setup_component_and_test_service_clear_cache(self): """Setup the demo platform and call service clear cache.""" @@ -203,14 +303,14 @@ class TestTTS(object): assert len(calls) == 1 assert os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) self.hass.services.call(tts.DOMAIN, tts.SERVICE_CLEAR_CACHE, {}) self.hass.block_till_done() assert not os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_and_test_service_with_receive_voice(self): """Setup the demo platform and call service and receive voice.""" @@ -278,7 +378,7 @@ class TestTTS(object): self.hass.start() url = ("{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" - "_en_demo.mp3").format(self.hass.config.api.base_url) + "_en_-_demo.mp3").format(self.hass.config.api.base_url) req = requests.get(url) assert req.status_code == 404 @@ -297,7 +397,7 @@ class TestTTS(object): self.hass.start() url = ("{}/api/tts_proxy/265944dsk32c1b2a621be5930510bb2cd" - "_en_demo.mp3").format(self.hass.config.api.base_url) + "_en_-_demo.mp3").format(self.hass.config.api.base_url) req = requests.get(url) assert req.status_code == 404 @@ -324,7 +424,7 @@ class TestTTS(object): assert len(calls) == 1 assert not os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_test_with_cache_call_service_without_cache(self): """Setup demo platform with cache and call service without cache.""" @@ -349,7 +449,7 @@ class TestTTS(object): assert len(calls) == 1 assert not os.path.isfile(os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")) + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")) def test_setup_component_test_with_cache_dir(self): """Setup demo platform with cache and call service without cache.""" @@ -358,7 +458,7 @@ class TestTTS(object): _, demo_data = self.demo_provider.get_tts_audio("bla", 'en') cache_file = os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3") + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3") os.mkdir(self.default_tts_cache) with open(cache_file, "wb") as voice_file: @@ -384,7 +484,7 @@ class TestTTS(object): assert len(calls) == 1 assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find( "/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" - "_en_demo.mp3") \ + "_en_-_demo.mp3") \ != -1 @patch('homeassistant.components.tts.demo.DemoProvider.get_tts_audio', @@ -414,7 +514,7 @@ class TestTTS(object): _, demo_data = self.demo_provider.get_tts_audio("bla", 'en') cache_file = os.path.join( self.default_tts_cache, - "265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3") + "265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3") os.mkdir(self.default_tts_cache) with open(cache_file, "wb") as voice_file: @@ -433,7 +533,7 @@ class TestTTS(object): self.hass.start() url = ("{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" - "_en_demo.mp3").format(self.hass.config.api.base_url) + "_en_-_demo.mp3").format(self.hass.config.api.base_url) req = requests.get(url) assert req.status_code == 200 From ccd2588cf7f97335b9ea8dc3016af20db14a9ec3 Mon Sep 17 00:00:00 2001 From: Lupin Demid Date: Sat, 21 Jan 2017 11:36:28 +0400 Subject: [PATCH 090/191] added speed and emotion to Yandex tts (#5431) * Added speed and emotion parameters. Refactored test's * Fixed float point arfs in url and added test for float point values --- homeassistant/components/tts/yandextts.py | 21 ++- tests/components/tts/test_yandextts.py | 184 +++++++++++++++++++--- 2 files changed, 186 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/tts/yandextts.py b/homeassistant/components/tts/yandextts.py index 4dc8618f0d0..824ca6ca38f 100644 --- a/homeassistant/components/tts/yandextts.py +++ b/homeassistant/components/tts/yandextts.py @@ -33,19 +33,34 @@ SUPPORT_VOICES = [ 'jane', 'oksana', 'alyss', 'omazh', 'zahar', 'ermil' ] + +SUPPORTED_EMOTION = [ + 'good', 'evil', 'neutral' +] + +MIN_SPEED = 0.1 +MAX_SPEED = 3 + CONF_CODEC = 'codec' CONF_VOICE = 'voice' +CONF_EMOTION = 'emotion' +CONF_SPEED = 'speed' DEFAULT_LANG = 'en-US' DEFAULT_CODEC = 'mp3' DEFAULT_VOICE = 'zahar' - +DEFAULT_EMOTION = 'neutral' +DEFAULT_SPEED = 1 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): cv.string, vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES), vol.Optional(CONF_CODEC, default=DEFAULT_CODEC): vol.In(SUPPORT_CODECS), vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): vol.In(SUPPORT_VOICES), + vol.Optional(CONF_EMOTION, default=DEFAULT_EMOTION): + vol.In(SUPPORTED_EMOTION), + vol.Optional(CONF_SPEED, default=DEFAULT_SPEED): + vol.Range(min=MIN_SPEED, max=MAX_SPEED) }) @@ -65,6 +80,8 @@ class YandexSpeechKitProvider(Provider): self._key = conf.get(CONF_API_KEY) self._speaker = conf.get(CONF_VOICE) self._language = conf.get(CONF_LANG) + self._emotion = conf.get(CONF_EMOTION) + self._speed = str(conf.get(CONF_SPEED)) @property def default_language(self): @@ -92,6 +109,8 @@ class YandexSpeechKitProvider(Provider): 'key': self._key, 'speaker': self._speaker, 'format': self._codec, + 'emotion': self._emotion, + 'speed': self._speed } request = yield from websession.get(YANDEX_API_URL, diff --git a/tests/components/tts/test_yandextts.py b/tests/components/tts/test_yandextts.py index f0c6eb85200..2baa94ae2b8 100644 --- a/tests/components/tts/test_yandextts.py +++ b/tests/components/tts/test_yandextts.py @@ -54,10 +54,17 @@ class TestTTSYandexPlatform(object): """Test service call say.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) - url = "https://tts.voicetech.yandex.net/generate?format=mp3" \ - "&speaker=zahar&key=1234567xx&text=HomeAssistant&lang=en-US" + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 1 + } aioclient_mock.get( - url, status=200, content=b'test') + self._base_url, status=200, content=b'test', params=url_param) config = { tts.DOMAIN: { @@ -81,10 +88,17 @@ class TestTTSYandexPlatform(object): """Test service call say.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) - url = "https://tts.voicetech.yandex.net/generate?format=mp3" \ - "&speaker=zahar&key=1234567xx&text=HomeAssistant&lang=ru-RU" + url_param = { + 'text': 'HomeAssistant', + 'lang': 'ru-RU', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 1 + } aioclient_mock.get( - url, status=200, content=b'test') + self._base_url, status=200, content=b'test', params=url_param) config = { tts.DOMAIN: { @@ -109,10 +123,17 @@ class TestTTSYandexPlatform(object): """Test service call say.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) - url = "https://tts.voicetech.yandex.net/generate?format=mp3" \ - "&speaker=zahar&key=1234567xx&text=HomeAssistant&lang=ru-RU" + url_param = { + 'text': 'HomeAssistant', + 'lang': 'ru-RU', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 1 + } aioclient_mock.get( - url, status=200, content=b'test') + self._base_url, status=200, content=b'test', params=url_param) config = { tts.DOMAIN: { @@ -137,10 +158,18 @@ class TestTTSYandexPlatform(object): """Test service call say.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) - url = "https://tts.voicetech.yandex.net/generate?format=mp3" \ - "&speaker=zahar&key=1234567xx&text=HomeAssistant&lang=en-US" + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 1 + } aioclient_mock.get( - url, status=200, exc=asyncio.TimeoutError()) + self._base_url, status=200, + exc=asyncio.TimeoutError(), params=url_param) config = { tts.DOMAIN: { @@ -164,10 +193,17 @@ class TestTTSYandexPlatform(object): """Test service call say.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) - url = "https://tts.voicetech.yandex.net/generate?format=mp3" \ - "&speaker=zahar&key=1234567xx&text=HomeAssistant&lang=en-US" + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 1 + } aioclient_mock.get( - url, status=403, content=b'test') + self._base_url, status=403, content=b'test', params=url_param) config = { tts.DOMAIN: { @@ -190,10 +226,17 @@ class TestTTSYandexPlatform(object): """Test service call say.""" calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) - url = "https://tts.voicetech.yandex.net/generate?format=mp3" \ - "&speaker=alyss&key=1234567xx&text=HomeAssistant&lang=en-US" + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'alyss', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 1 + } aioclient_mock.get( - url, status=200, content=b'test') + self._base_url, status=200, content=b'test', params=url_param) config = { tts.DOMAIN: { @@ -213,3 +256,108 @@ class TestTTSYandexPlatform(object): assert len(aioclient_mock.mock_calls) == 1 assert len(calls) == 1 + + def test_service_say_specifed_emotion(self, aioclient_mock): + """Test service call say.""" + calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'evil', + 'speed': 1 + } + aioclient_mock.get( + self._base_url, status=200, content=b'test', params=url_param) + + config = { + tts.DOMAIN: { + 'platform': 'yandextts', + 'api_key': '1234567xx', + 'emotion': 'evil' + } + } + + with assert_setup_component(1, tts.DOMAIN): + setup_component(self.hass, tts.DOMAIN, config) + + self.hass.services.call(tts.DOMAIN, 'yandextts_say', { + tts.ATTR_MESSAGE: "HomeAssistant", + }) + self.hass.block_till_done() + + assert len(aioclient_mock.mock_calls) == 1 + assert len(calls) == 1 + + def test_service_say_specified_low_speed(self, aioclient_mock): + """Test service call say.""" + calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': '0.1' + } + aioclient_mock.get( + self._base_url, status=200, content=b'test', params=url_param) + + config = { + tts.DOMAIN: { + 'platform': 'yandextts', + 'api_key': '1234567xx', + 'speed': 0.1 + } + } + + with assert_setup_component(1, tts.DOMAIN): + setup_component(self.hass, tts.DOMAIN, config) + + self.hass.services.call(tts.DOMAIN, 'yandextts_say', { + tts.ATTR_MESSAGE: "HomeAssistant", + }) + self.hass.block_till_done() + + assert len(aioclient_mock.mock_calls) == 1 + assert len(calls) == 1 + + def test_service_say_specified_speed(self, aioclient_mock): + """Test service call say.""" + calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA) + + url_param = { + 'text': 'HomeAssistant', + 'lang': 'en-US', + 'key': '1234567xx', + 'speaker': 'zahar', + 'format': 'mp3', + 'emotion': 'neutral', + 'speed': 2 + } + aioclient_mock.get( + self._base_url, status=200, content=b'test', params=url_param) + + config = { + tts.DOMAIN: { + 'platform': 'yandextts', + 'api_key': '1234567xx', + 'speed': 2 + } + } + + with assert_setup_component(1, tts.DOMAIN): + setup_component(self.hass, tts.DOMAIN, config) + + self.hass.services.call(tts.DOMAIN, 'yandextts_say', { + tts.ATTR_MESSAGE: "HomeAssistant", + }) + self.hass.block_till_done() + + assert len(aioclient_mock.mock_calls) == 1 + assert len(calls) == 1 From a6f341f06ac9612c58e82ff68af57b28c70cfabf Mon Sep 17 00:00:00 2001 From: Dale Higgs Date: Sat, 21 Jan 2017 01:39:50 -0600 Subject: [PATCH 091/191] Add exit code to check_config script (#5471) --- homeassistant/scripts/check_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index 986630e4db0..19dc275e163 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -146,7 +146,7 @@ def run(script_args: List) -> int: print(' -', skey + ':', sval, color('cyan', '[from:', flatsecret .get(skey, 'keyring') + ']')) - return 0 + return len(res['except']) def check(config_path): From 56ed00a1db11ca14f59f4dacc19438d67ca61be4 Mon Sep 17 00:00:00 2001 From: joopert Date: Sat, 21 Jan 2017 13:24:38 +0100 Subject: [PATCH 092/191] update nad library version (#5478) --- homeassistant/components/media_player/nad.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/nad.py b/homeassistant/components/media_player/nad.py index 0b8efda0e44..27122fcfc93 100644 --- a/homeassistant/components/media_player/nad.py +++ b/homeassistant/components/media_player/nad.py @@ -18,7 +18,7 @@ from homeassistant.const import ( import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['https://github.com/joopert/nad_receiver/archive/' - '0.0.2.zip#nad_receiver==0.0.2'] + '0.0.3.zip#nad_receiver==0.0.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 26300090067..cf2f6b78b41 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -226,7 +226,7 @@ https://github.com/jabesq/pybotvac/archive/v0.0.1.zip#pybotvac==0.0.1 https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 # homeassistant.components.media_player.nad -https://github.com/joopert/nad_receiver/archive/0.0.2.zip#nad_receiver==0.0.2 +https://github.com/joopert/nad_receiver/archive/0.0.3.zip#nad_receiver==0.0.3 # homeassistant.components.media_player.russound_rnet https://github.com/laf/russound/archive/0.1.6.zip#russound==0.1.6 From f13774269d96d52d02eb8a116222c93252e4217e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 21 Jan 2017 18:26:17 +0100 Subject: [PATCH 093/191] Upgrade speedtest-cli to 1.0.2 (#5483) --- homeassistant/components/sensor/speedtest.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/speedtest.py b/homeassistant/components/sensor/speedtest.py index 3b661062198..a629b3fdde0 100644 --- a/homeassistant/components/sensor/speedtest.py +++ b/homeassistant/components/sensor/speedtest.py @@ -19,7 +19,7 @@ from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_change -REQUIREMENTS = ['speedtest-cli==1.0.1'] +REQUIREMENTS = ['speedtest-cli==1.0.2'] _LOGGER = logging.getLogger(__name__) _SPEEDTEST_REGEX = re.compile(r'Ping:\s(\d+\.\d+)\sms[\r\n]+' diff --git a/requirements_all.txt b/requirements_all.txt index cf2f6b78b41..e9f04bc0af8 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -591,7 +591,7 @@ snapcast==1.2.2 somecomfort==0.4.1 # homeassistant.components.sensor.speedtest -speedtest-cli==1.0.1 +speedtest-cli==1.0.2 # homeassistant.components.recorder # homeassistant.scripts.db_migrator From 13d801e1c6eb59ff73f9701cfc0656314ac5d4cd Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 21 Jan 2017 21:24:23 +0100 Subject: [PATCH 094/191] Upgrade sqlalchemy to 1.1.5 (#5484) --- homeassistant/components/recorder/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 4f02fe2873d..3d8d1357b0f 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -28,7 +28,7 @@ import homeassistant.util.dt as dt_util DOMAIN = 'recorder' -REQUIREMENTS = ['sqlalchemy==1.1.4'] +REQUIREMENTS = ['sqlalchemy==1.1.5'] DEFAULT_URL = 'sqlite:///{hass_config_path}' DEFAULT_DB_FILE = 'home-assistant_v2.db' diff --git a/requirements_all.txt b/requirements_all.txt index e9f04bc0af8..56f85a8885b 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -595,7 +595,7 @@ speedtest-cli==1.0.2 # homeassistant.components.recorder # homeassistant.scripts.db_migrator -sqlalchemy==1.1.4 +sqlalchemy==1.1.5 # homeassistant.components.statsd statsd==3.2.1 From ed12f9e23794d9ba9269f0db2d1bcd06c6fd47ae Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 21 Jan 2017 21:24:48 +0100 Subject: [PATCH 095/191] Tellstick light fix (#5482) * Fixed crash when lights objects was inited * Fixed initial value for tellstick lights * Fixed problem with lights not working with the turn_on action. --- homeassistant/components/light/tellstick.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/tellstick.py b/homeassistant/components/light/tellstick.py index ea908fda02f..9002731e44e 100644 --- a/homeassistant/components/light/tellstick.py +++ b/homeassistant/components/light/tellstick.py @@ -83,7 +83,9 @@ class TellstickLight(TellstickDevice, Light): def _send_device_command(self, requested_state, requested_data): """Let tellcore update the actual device to the requested state.""" if requested_state: - brightness = requested_data - self._tellcore_device.dim(brightness) + if requested_data is not None: + self._brightness = int(requested_data) + + self._tellcore_device.dim(self._brightness) else: self._tellcore_device.turn_off() From 74989f7941ec15419e1ec76bf08ad06ba1f2b665 Mon Sep 17 00:00:00 2001 From: Greg Dowling Date: Sat, 21 Jan 2017 20:26:57 +0000 Subject: [PATCH 096/191] Bump pyvera version (add RFX device support) (#5487) --- homeassistant/components/vera.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/vera.py b/homeassistant/components/vera.py index 75dd7428010..ff75f6e7314 100644 --- a/homeassistant/components/vera.py +++ b/homeassistant/components/vera.py @@ -20,7 +20,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyvera==0.2.21'] +REQUIREMENTS = ['pyvera==0.2.23'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 56f85a8885b..f97d0095260 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -534,7 +534,7 @@ pyunifi==1.3 # pyuserinput==0.1.11 # homeassistant.components.vera -pyvera==0.2.21 +pyvera==0.2.23 # homeassistant.components.notify.html5 pywebpush==0.6.1 From a89a4f39dce806c8f8ff2b12b919191423c26b66 Mon Sep 17 00:00:00 2001 From: "Craig J. Ward" Date: Sat, 21 Jan 2017 16:00:56 -0600 Subject: [PATCH 097/191] only check for devices when not defined in config (#5490) * only check for devices when not defined in config * lint --- .../components/switch/insteon_local.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/switch/insteon_local.py b/homeassistant/components/switch/insteon_local.py index 54350781344..6935ad21abe 100644 --- a/homeassistant/components/switch/insteon_local.py +++ b/homeassistant/components/switch/insteon_local.py @@ -22,7 +22,7 @@ DOMAIN = 'switch' INSTEON_LOCAL_SWITCH_CONF = 'insteon_local_switch.conf' MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) def setup_platform(hass, config, add_devices, discovery_info=None): @@ -36,15 +36,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None): setup_switch( device_id, conf_switches[device_id], insteonhub, hass, add_devices) + else: + linked = insteonhub.get_linked() - linked = insteonhub.get_linked() - - for device_id in linked: - if linked[device_id]['cat_type'] == 'switch'\ - and device_id not in conf_switches: - request_configuration(device_id, insteonhub, - linked[device_id]['model_name'] + ' ' + - linked[device_id]['sku'], hass, add_devices) + for device_id in linked: + if linked[device_id]['cat_type'] == 'switch'\ + and device_id not in conf_switches: + request_configuration(device_id, insteonhub, + linked[device_id]['model_name'] + ' ' + + linked[device_id]['sku'], + hass, add_devices) def request_configuration(device_id, insteonhub, model, hass, From 06361b1ed15443dc211c3b5bfc313967a678f14b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Vran=C3=ADk?= Date: Sat, 21 Jan 2017 23:13:46 +0100 Subject: [PATCH 098/191] implementing users remarks (#5481) * cec client object * cec command structure * autodetect source * volume support and native source select * switch device * media player device * detecting of state * friendly names * hdmi cec properties * presence detection * simplified callbacks * stable names * renamed methods * code cleanup * name with vendor * fixed standby call name * fake standby/poweron * domain switch * domain switch * async updating * update separated * cec -> hass event bridge * fixed name generation * code cleanup * code cleanup * icon constants * code cleanup * do not register unavailable devices * discovery of deevices * code cleanup * cec device discovery * moved method implementation into child * service descriptions * service descriptions * service descriptions * changed entity init sequence * logging cleanup * add remove as job * closing cec, no service schemas * correct iterate over dictionary * Volume by commands * threading * logging minimized * get load out of main thread * naming cleanup * get load out of main thread * optimized discovery * async where possible * cleanup logging, constructors first * pydoc * formatting * no async_update from out of loop no hiding entities removed redundant device_state_attributes async updating presence * no async * working async cec * cec in thirdparty lib * cec initialized oudsice * working without SIGSEGV * rollbacked file changed by mistake * sending of commands * working with ha * using hass loop and device driven updates * version up * version up * Command types in pycec, cleanup for HA integration * Removed media player, state moved to switch * service descriptions * requirements: pyCEC * line width to 79 * doc * doc * overindentation solved * HDMI to uppercase * minimal dependency on cec * removed unwanted line * doc wording * margin 79 * line continuation indent * imperative doc * lint: indentation * fixed overindented * fixed overindented * fixed overindented * fixed overindented * order of imports * PEP8 * keep signature of overriding * removed redundant blank line * fixed update call method (#4) * Preparation for merge to upstream (#5) * newer version of pyCEC * updated services.yaml * fixed lint scrpt to operate only on python files * pycec version up * update services * no coverage report * exclude non python files from lint * lint only on python files * Dev (#6) * reordered * sending nonserialized data through hass.data * code formatting * code formatting * import order * Dev (#7) * newer version of pyCEC * updated services.yaml * fixed lint scrpt to operate only on python files * pycec version up * update services * no coverage report * exclude non python files from lint * lint only on python files * reordered * sending nonserialized data through hass.data * import order * fixed object handling * code formatting * Backwards compatibility of hdmi_cec (#10) * services: power_on standby active_source * new version of pyCEC (#12) * newer version of pyCEC * devices config (#13) * getting device name from config * shutdown fix (#14) * correct call on shutdown * remove misplaced annotations (#15) * Preparation for merge to upstream (#5) * newer version of pyCEC * updated services.yaml * reordered * sending nonserialized data through hass.data * services: power_on standby active_source * code formatting * getting device name from config * correct call on shutdown * pyCEC version 0.3.6 (#18) * newer version of pyCEC * updated services.yaml * sending nonserialized data through hass.data * services: ** power_on ** standby ** active_source * getting device name from config * correct call on shutdown * fork new thread on multicore machines * support both config schemas: original and new (#16) * volume press and release support (#17) * support for media_player (#21) * accept hexadecimal format of commands * support for media player * platform customization * type constants * Dev (#23) * accept hexadecimal format of commands * support for media player * platform customization * TCP CEC support (#24) * accept hexadecimal format of commands * support for media player * platform customization * preparing tcp support * volume handling (#25) * Incorporated CR remarks (#26) * cleanup imports * cleanup and enhance services description * removed unwanted file * implemented CR remarks (#27) * pyCEC v0.4.6 * pined dependency version * tighten service schemas * requirements (#28) * incorporate remarks from users (#32) * home-assistant-31 make mute schema better (#31) * pycec-30 pyCEC version up (#30) * pycec-30 pyCEC version up (#30) * home-assistant-30 OSD display name from configuration (#30) (#33) * Home assistant 29 (#34) * home-assistant-29 counting from 0 (#29) * Home assistant 31 (#35) * home-assistant-31 add support for mute-on and mute-off (#31) * home-assistant-31 pyCEC version up (#31) * Home assistant 31 (#36) * home-assistant-31 Limit OSD name to 13 chars (#31) * home-assistant-31 Limit OSD name to 13 chars moved to CEC adapter (#31) * home-assistant-31 version up (#31) * home-assistant-31 formatting (#31) * formatting * service description * service description * single attribute for volume * fixed mute on -> mute off * moved config constant from core into component --- homeassistant/components/hdmi_cec.py | 31 +++++++++++++------ .../components/media_player/hdmi_cec.py | 4 +-- homeassistant/components/services.yaml | 6 ++-- requirements_all.txt | 2 +- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/hdmi_cec.py b/homeassistant/components/hdmi_cec.py index 44b205993b6..23c3fc01106 100644 --- a/homeassistant/components/hdmi_cec.py +++ b/homeassistant/components/hdmi_cec.py @@ -26,12 +26,14 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, STATE_UNKNOWN, from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyCEC==0.4.6'] +REQUIREMENTS = ['pyCEC==0.4.9'] DOMAIN = 'hdmi_cec' _LOGGER = logging.getLogger(__name__) +DEFAULT_DISPLAY_NAME = "HomeAssistant" + ICON_UNKNOWN = 'mdi:help' ICON_AUDIO = 'mdi:speaker' ICON_PLAYER = 'mdi:play' @@ -76,6 +78,9 @@ ATTR_RAW = 'raw' ATTR_DIR = 'dir' ATTR_ABT = 'abt' ATTR_NEW = 'new' +ATTR_ON = 'on' +ATTR_OFF = 'off' +ATTR_TOGGLE = 'toggle' _VOL_HEX = vol.Any(vol.Coerce(int), lambda x: int(x, 16)) @@ -92,9 +97,7 @@ SERVICE_VOLUME = 'volume' SERVICE_VOLUME_SCHEMA = vol.Schema({ vol.Optional(CMD_UP): vol.Any(CMD_PRESS, CMD_RELEASE, vol.Coerce(int)), vol.Optional(CMD_DOWN): vol.Any(CMD_PRESS, CMD_RELEASE, vol.Coerce(int)), - vol.Optional(CMD_MUTE): None, - vol.Optional(CMD_UNMUTE): None, - vol.Optional(CMD_MUTE_TOGGLE): None + vol.Optional(CMD_MUTE): vol.Any(ATTR_ON, ATTR_OFF, ATTR_TOGGLE), }, extra=vol.PREVENT_EXTRA) SERVICE_UPDATE_DEVICES = 'update' @@ -118,6 +121,7 @@ CUSTOMIZE_SCHEMA = vol.Schema({ SWITCH) }) +CONF_DISPLAY_NAME = 'osd_name' CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_DEVICES): vol.Any(DEVICE_SCHEMA, @@ -127,6 +131,7 @@ CONFIG_SCHEMA = vol.Schema({ })), vol.Optional(CONF_PLATFORM): vol.Any(SWITCH, MEDIA_PLAYER), vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_DISPLAY_NAME): cv.string, }) }, extra=vol.ALLOW_EXTRA) @@ -156,8 +161,9 @@ def setup(hass: HomeAssistant, base_config): """Setup CEC capability.""" from pycec.network import HDMINetwork from pycec.commands import CecCommand, KeyReleaseCommand, KeyPressCommand - from pycec.const import KEY_VOLUME_UP, KEY_VOLUME_DOWN, KEY_MUTE, \ - ADDR_AUDIOSYSTEM, ADDR_BROADCAST, ADDR_UNREGISTERED + from pycec.const import KEY_VOLUME_UP, KEY_VOLUME_DOWN, KEY_MUTE_ON, \ + KEY_MUTE_OFF, KEY_MUTE_TOGGLE, ADDR_AUDIOSYSTEM, ADDR_BROADCAST, \ + ADDR_UNREGISTERED from pycec.cec import CecAdapter from pycec.tcp import TcpAdapter @@ -175,14 +181,18 @@ def setup(hass: HomeAssistant, base_config): # Create own thread if more than 1 CPU hass.loop if multiprocessing.cpu_count() < 2 else None) host = base_config[DOMAIN].get(CONF_HOST, None) + display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME, + DEFAULT_DISPLAY_NAME) if host: - adapter = TcpAdapter(host, name="HASS", activate_source=False) + adapter = TcpAdapter(host, name=display_name, activate_source=False) else: - adapter = CecAdapter(name="HASS", activate_source=False) + adapter = CecAdapter(name=display_name, activate_source=False) hdmi_network = HDMINetwork(adapter, loop=loop) def _volume(call): """Increase/decrease volume and mute/unmute system.""" + mute_key_mapping = {ATTR_TOGGLE: KEY_MUTE_TOGGLE, ATTR_ON: KEY_MUTE_ON, + ATTR_OFF: KEY_MUTE_OFF} for cmd, att in call.data.items(): if cmd == CMD_UP: _process_volume(KEY_VOLUME_UP, att) @@ -190,7 +200,8 @@ def setup(hass: HomeAssistant, base_config): _process_volume(KEY_VOLUME_DOWN, att) elif cmd == CMD_MUTE: hdmi_network.send_command( - KeyPressCommand(KEY_MUTE, dst=ADDR_AUDIOSYSTEM)) + KeyPressCommand(mute_key_mapping[att], + dst=ADDR_AUDIOSYSTEM)) hdmi_network.send_command( KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) _LOGGER.info("Audio muted") @@ -207,7 +218,7 @@ def setup(hass: HomeAssistant, base_config): hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) else: att = 1 if att == "" else int(att) - for _ in range(1, att): + for _ in range(0, att): hdmi_network.send_command( KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM)) hdmi_network.send_command( diff --git a/homeassistant/components/media_player/hdmi_cec.py b/homeassistant/components/media_player/hdmi_cec.py index 4998072018e..c7e9be562cc 100644 --- a/homeassistant/components/media_player/hdmi_cec.py +++ b/homeassistant/components/media_player/hdmi_cec.py @@ -59,8 +59,8 @@ class CecPlayerDevice(CecDevice, MediaPlayerDevice): def mute_volume(self, mute): """Mute volume.""" - from pycec.const import KEY_MUTE - self.send_keypress(KEY_MUTE) + from pycec.const import KEY_MUTE_TOGGLE + self.send_keypress(KEY_MUTE_TOGGLE) def media_previous_track(self): """Go to previous track.""" diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 53f82d5c059..e42cd56cd8c 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -192,9 +192,9 @@ hdmi_cec: down: description: Decreases volume x levels. example: 3 - mute: Mutes audio system. Value is ignored. - unmute: Unmutes audio system. Value is ignored. - toggle mute: Toggles mute of audio system. Value is ignored. + mute: + description: Mutes audio system. Value should be on, off or toggle. + example: "toggle" select_device: description: Select HDMI device. diff --git a/requirements_all.txt b/requirements_all.txt index f97d0095260..1b92e6b81c4 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -378,7 +378,7 @@ pwaqi==1.3 py-cpuinfo==0.2.3 # homeassistant.components.hdmi_cec -pyCEC==0.4.6 +pyCEC==0.4.9 # homeassistant.components.switch.tplink pyHS100==0.2.3 From 2992cd35be85c482cdadf6f49721c3e3d20478e9 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 21 Jan 2017 14:14:08 -0800 Subject: [PATCH 099/191] Add support for Leviton Decora Bluetooth dimmer switches (#5434) * Add support for Leviton Decora Bluetooth dimmer switches Add support for the Decora Bluetooth smart dimmer switches from Leviton. * Update decora.py --- .coveragerc | 1 + homeassistant/components/light/decora.py | 124 +++++++++++++++++++++++ requirements_all.txt | 3 + script/gen_requirements_all.py | 3 +- 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/light/decora.py diff --git a/.coveragerc b/.coveragerc index ea0530aa8f7..47f602072c0 100644 --- a/.coveragerc +++ b/.coveragerc @@ -191,6 +191,7 @@ omit = homeassistant/components/keyboard_remote.py homeassistant/components/light/avion.py homeassistant/components/light/blinksticklight.py + homeassistant/components/light/decora.py homeassistant/components/light/flux_led.py homeassistant/components/light/hue.py homeassistant/components/light/hyperion.py diff --git a/homeassistant/components/light/decora.py b/homeassistant/components/light/decora.py new file mode 100644 index 00000000000..eaae90f486e --- /dev/null +++ b/homeassistant/components/light/decora.py @@ -0,0 +1,124 @@ +""" +Support for Decora dimmers. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.decora/ +""" +import logging + +import voluptuous as vol + +from homeassistant.const import CONF_API_KEY, CONF_DEVICES, CONF_NAME +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, + PLATFORM_SCHEMA) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['decora==0.3'] + +_LOGGER = logging.getLogger(__name__) + +SUPPORT_DECORA_LED = (SUPPORT_BRIGHTNESS) + +DEVICE_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME): cv.string, + vol.Required(CONF_API_KEY): cv.string, +}) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up an Decora switch.""" + lights = [] + for address, device_config in config[CONF_DEVICES].items(): + device = {} + device['name'] = device_config[CONF_NAME] + device['key'] = device_config[CONF_API_KEY] + device['address'] = address + light = DecoraLight(device) + if light.is_valid: + lights.append(light) + + add_devices(lights) + + +class DecoraLight(Light): + """Representation of an Decora light.""" + + def __init__(self, device): + """Initialize the light.""" + # pylint: disable=import-error + import decora + + self._name = device['name'] + self._address = device['address'] + self._key = device["key"] + self._switch = decora.decora(self._address, self._key) + self._switch.connect() + self._state = self._switch.get_on() + self._brightness = self._switch.get_brightness() + self.is_valid = True + + @property + def unique_id(self): + """Return the ID of this light.""" + return "{}.{}".format(self.__class__, self._address) + + @property + def name(self): + """Return the name of the device if any.""" + return self._name + + @property + def is_on(self): + """Return true if device is on.""" + return self._state + + @property + def brightness(self): + """Return the brightness of this light between 0..255.""" + return self._brightness + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_DECORA_LED + + @property + def should_poll(self): + """We can read the device state, so poll.""" + return True + + @property + def assumed_state(self): + """We can read the actual state.""" + return False + + def set_state(self, brightness): + """Set the state of this lamp to the provided brightness.""" + self._switch.set_brightness(brightness) + self._brightness = brightness + return True + + def turn_on(self, **kwargs): + """Turn the specified or all lights on.""" + brightness = kwargs.get(ATTR_BRIGHTNESS) + + self._switch.on() + if brightness is not None: + self.set_state(brightness) + + self._state = True + + def turn_off(self, **kwargs): + """Turn the specified or all lights off.""" + self._switch.off() + self._state = False + + def update(self): + """Synchronise internal state with the actual light state.""" + self._brightness = self._switch.get_brightness() + self._state = self._switch.get_on() diff --git a/requirements_all.txt b/requirements_all.txt index 1b92e6b81c4..68326dbd1b4 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -87,6 +87,9 @@ colorlog>2.1,<3 # homeassistant.components.binary_sensor.concord232 concord232==0.14 +# homeassistant.components.light.decora +# decora==0.3 + # homeassistant.components.media_player.denonavr denonavr==0.3.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index e23c8d09fea..f5b0a271d84 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -20,7 +20,8 @@ COMMENT_REQUIREMENTS = ( 'evdev', 'pycups', 'python-eq3bt', - 'avion' + 'avion', + 'decora' ) IGNORE_PACKAGES = ( From 7df51dc54528418412d43c64887f2d550841e33a Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sun, 22 Jan 2017 01:31:10 +0200 Subject: [PATCH 100/191] gen_requirement: Raise an error if REQUIREMENT not pinned --- script/gen_requirements_all.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 81fb17aac17..537b93d173a 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -26,6 +26,8 @@ IGNORE_PACKAGES = ( 'homeassistant.components.recorder.models', ) +IGNORE_PIN = ('colorlog>2.1,<3', 'keyring>=9.3,<10.0', 'urllib3') + def explore_module(package, explore_children): """Explore the modules.""" @@ -78,6 +80,9 @@ def gather_modules(): continue for req in module.REQUIREMENTS: + if req.partition('==')[1] == '' and req not in IGNORE_PIN: + errors.append("{}[Please pin requirement {}]" + .format(package, req)) reqs.setdefault(req, []).append(package) for key in reqs: From df361dc1e1db654a09be41e17e54dacbe545e0ce Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Sat, 21 Jan 2017 21:05:15 -0500 Subject: [PATCH 101/191] Fix network tests to use get_test_instance_port --- tests/components/camera/test_init.py | 8 +++++++- tests/components/image_processing/test_init.py | 8 +++++++- tests/components/tts/test_init.py | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 2e58676edcc..13286beae61 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -7,10 +7,12 @@ import pytest from homeassistant.bootstrap import setup_component from homeassistant.const import ATTR_ENTITY_PICTURE import homeassistant.components.camera as camera +import homeassistant.components.http as http from homeassistant.exceptions import HomeAssistantError from homeassistant.util.async import run_coroutine_threadsafe -from tests.common import get_test_home_assistant, assert_setup_component +from tests.common import ( + get_test_home_assistant, get_test_instance_port, assert_setup_component) class TestSetupCamera(object): @@ -43,6 +45,10 @@ class TestGetImage(object): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() + setup_component( + self.hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: get_test_instance_port()}}) + config = { camera.DOMAIN: { 'platform': 'demo' diff --git a/tests/components/image_processing/test_init.py b/tests/components/image_processing/test_init.py index eb52e3262ab..c94064969f1 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -5,9 +5,11 @@ from homeassistant.core import callback from homeassistant.const import ATTR_ENTITY_PICTURE from homeassistant.bootstrap import setup_component from homeassistant.exceptions import HomeAssistantError +import homeassistant.components.http as http import homeassistant.components.image_processing as ip -from tests.common import get_test_home_assistant, assert_setup_component +from tests.common import ( + get_test_home_assistant, get_test_instance_port, assert_setup_component) class TestSetupImageProcessing(object): @@ -53,6 +55,10 @@ class TestImageProcessing(object): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() + setup_component( + self.hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: get_test_instance_port()}}) + config = { ip.DOMAIN: { 'platform': 'demo' diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index 023c05edf99..f7985b8af74 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -6,6 +6,7 @@ from unittest.mock import patch, PropertyMock import requests +import homeassistant.components.http as http import homeassistant.components.tts as tts from homeassistant.components.tts.demo import DemoProvider from homeassistant.components.media_player import ( @@ -14,7 +15,8 @@ from homeassistant.components.media_player import ( from homeassistant.bootstrap import setup_component from tests.common import ( - get_test_home_assistant, assert_setup_component, mock_service) + get_test_home_assistant, get_test_instance_port, assert_setup_component, + mock_service) class TestTTS(object): @@ -26,6 +28,10 @@ class TestTTS(object): self.demo_provider = DemoProvider('en') self.default_tts_cache = self.hass.config.path(tts.DEFAULT_CACHE_DIR) + setup_component( + self.hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: get_test_instance_port()}}) + def teardown_method(self): """Stop everything that was started.""" if os.path.isdir(self.default_tts_cache): From 75adb7ff46e64e510c206d2b1f141253bbc4997a Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 22 Jan 2017 11:36:29 +0100 Subject: [PATCH 102/191] Upgrade Sphinx to 1.5.2 (#5502) --- requirements_docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_docs.txt b/requirements_docs.txt index e5331727ec9..f3d712e8da2 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -1,3 +1,3 @@ -Sphinx==1.5.1 +Sphinx==1.5.2 sphinx-autodoc-typehints==1.1.0 sphinx-autodoc-annotation==1.0.post1 From 59b0491d29dcbb0104681e0836d068203dbc9e18 Mon Sep 17 00:00:00 2001 From: Daniel Perna Date: Sun, 22 Jan 2017 14:20:49 +0100 Subject: [PATCH 103/191] Homematic fixes + device support (#5503) * Using new MaxShutterContact class * Skip unnecessary function calls * Added casting for VALUE * Some renaming to clarify what's happening * Bumped dependency version * Bumped version in requirements --- .../components/binary_sensor/homematic.py | 1 + homeassistant/components/homematic.py | 23 ++++++++++--------- homeassistant/components/sensor/homematic.py | 1 + requirements_all.txt | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/binary_sensor/homematic.py b/homeassistant/components/binary_sensor/homematic.py index 33eda1f2b1a..82b77eb11d4 100644 --- a/homeassistant/components/binary_sensor/homematic.py +++ b/homeassistant/components/binary_sensor/homematic.py @@ -17,6 +17,7 @@ DEPENDENCIES = ['homematic'] SENSOR_TYPES_CLASS = { "Remote": None, "ShutterContact": "opening", + "MaxShutterContact": "opening", "IPShutterContact": "opening", "Smoke": "smoke", "SmokeV2": "smoke", diff --git a/homeassistant/components/homematic.py b/homeassistant/components/homematic.py index 7f2f8b813a4..2b68a268d34 100644 --- a/homeassistant/components/homematic.py +++ b/homeassistant/components/homematic.py @@ -23,7 +23,7 @@ from homeassistant.config import load_yaml_config_file from homeassistant.util import Throttle DOMAIN = 'homematic' -REQUIREMENTS = ["pyhomematic==0.1.19"] +REQUIREMENTS = ["pyhomematic==0.1.20"] MIN_TIME_BETWEEN_UPDATE_HUB = timedelta(seconds=300) SCAN_INTERVAL = timedelta(seconds=30) @@ -432,8 +432,8 @@ def _system_callback_handler(hass, config, src, *args): }, config) -def _get_devices(hass, device_type, keys, proxy): - """Get the Homematic devices.""" +def _get_devices(hass, discovery_type, keys, proxy): + """Get the Homematic devices for given discovery_type.""" device_arr = [] for key in keys: @@ -441,14 +441,14 @@ def _get_devices(hass, device_type, keys, proxy): class_name = device.__class__.__name__ metadata = {} - # is class supported by discovery type - if class_name not in HM_DEVICE_TYPES[device_type]: + # Class supported by discovery type + if class_name not in HM_DEVICE_TYPES[discovery_type]: continue # Load metadata if needed to generate a param list - if device_type == DISCOVER_SENSORS: + if discovery_type == DISCOVER_SENSORS: metadata.update(device.SENSORNODE) - elif device_type == DISCOVER_BINARY_SENSORS: + elif discovery_type == DISCOVER_BINARY_SENSORS: metadata.update(device.BINARYNODE) else: metadata.update({None: device.ELEMENT}) @@ -459,8 +459,9 @@ def _get_devices(hass, device_type, keys, proxy): if param in HM_IGNORE_DISCOVERY_NODE: continue - # add devices - _LOGGER.debug("Handling %s: %s", param, channels) + # Add devices + _LOGGER.debug("%s: Handling %s: %s: %s", + discovery_type, key, param, channels) for channel in channels: name = _create_ha_name( name=device.NAME, channel=channel, param=param, @@ -485,7 +486,7 @@ def _get_devices(hass, device_type, keys, proxy): str(err)) else: _LOGGER.debug("Got no params for %s", key) - _LOGGER.debug("%s autodiscovery: %s", device_type, str(device_arr)) + _LOGGER.debug("%s autodiscovery done: %s", discovery_type, str(device_arr)) return device_arr @@ -873,7 +874,7 @@ class HMDevice(Entity): (self._hmdevice.SENSORNODE, self._hmdevice.getSensorData), (self._hmdevice.BINARYNODE, self._hmdevice.getBinaryData)): for node in metadata: - if node in self._data: + if metadata[node] and node in self._data: self._data[node] = funct(name=node, channel=self._channel) return True diff --git a/homeassistant/components/sensor/homematic.py b/homeassistant/components/sensor/homematic.py index e252e2d30c6..2d7e374c46d 100644 --- a/homeassistant/components/sensor/homematic.py +++ b/homeassistant/components/sensor/homematic.py @@ -41,6 +41,7 @@ HM_UNIT_HA_CAST = { "SUNSHINEDURATION": "#", "AIR_PRESSURE": "hPa", "FREQUENCY": "Hz", + "VALUE": "#" } diff --git a/requirements_all.txt b/requirements_all.txt index 68326dbd1b4..5874dcc7bf7 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -435,7 +435,7 @@ pyharmony==1.0.12 pyhik==0.0.7 # homeassistant.components.homematic -pyhomematic==0.1.19 +pyhomematic==0.1.20 # homeassistant.components.device_tracker.icloud pyicloud==0.9.1 From ab195773222a8577996ce5459918872c7aebb05a Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Sun, 22 Jan 2017 08:22:25 -0500 Subject: [PATCH 104/191] Bump amcrest version and refactored to simplify code (#5499) * Bumped version to Amcrest 1.1.3 and rebase self.base_url() * Refactored amcrest camera and amcrest sensor to simply object reference --- homeassistant/components/camera/amcrest.py | 28 ++++++++------------ homeassistant/components/sensor/amcrest.py | 30 +++++++++++----------- requirements_all.txt | 2 +- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/camera/amcrest.py b/homeassistant/components/camera/amcrest.py index 604bf519042..ecc93dfaaeb 100644 --- a/homeassistant/components/camera/amcrest.py +++ b/homeassistant/components/camera/amcrest.py @@ -18,7 +18,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import ( async_get_clientsession, async_aiohttp_proxy_stream) -REQUIREMENTS = ['amcrest==1.1.0'] +REQUIREMENTS = ['amcrest==1.1.3'] _LOGGER = logging.getLogger(__name__) @@ -62,13 +62,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up an Amcrest IP Camera.""" from amcrest import AmcrestCamera - data = AmcrestCamera( + camera = AmcrestCamera( config.get(CONF_HOST), config.get(CONF_PORT), - config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) + config.get(CONF_USERNAME), config.get(CONF_PASSWORD)).camera persistent_notification = loader.get_component('persistent_notification') try: - data.camera.current_time + camera.current_time # pylint: disable=broad-except except Exception as ex: _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex)) @@ -80,22 +80,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): notification_id=NOTIFICATION_ID) return False - add_devices([AmcrestCam(hass, config, data)]) + add_devices([AmcrestCam(hass, config, camera)]) return True class AmcrestCam(Camera): """An implementation of an Amcrest IP camera.""" - def __init__(self, hass, device_info, data): + def __init__(self, hass, device_info, camera): """Initialize an Amcrest camera.""" super(AmcrestCam, self).__init__() - self._base_url = '%s://%s:%s/cgi-bin' % ( - 'http', - device_info.get(CONF_HOST), - device_info.get(CONF_PORT) - ) - self._data = data + self._camera = camera + self._base_url = self._camera.get_base_url() self._hass = hass self._name = device_info.get(CONF_NAME) self._resolution = RESOLUTION_LIST[device_info.get(CONF_RESOLUTION)] @@ -110,7 +106,7 @@ class AmcrestCam(Camera): 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(channel=self._resolution) + response = self._camera.snapshot(channel=self._resolution) return response.data @asyncio.coroutine @@ -123,10 +119,8 @@ class AmcrestCam(Camera): # Otherwise, stream an MJPEG image stream directly from the camera websession = async_get_clientsession(self.hass) - streaming_url = '%s/mjpg/video.cgi?channel=0&subtype=%d' % ( - self._base_url, - self._resolution - ) + streaming_url = '{0}mjpg/video.cgi?channel=0&subtype={1}'.format( + self._base_url, self._resolution) stream_coro = websession.get( streaming_url, auth=self._token, timeout=TIMEOUT) diff --git a/homeassistant/components/sensor/amcrest.py b/homeassistant/components/sensor/amcrest.py index 7a41bcc6fe4..44fdeca54f1 100644 --- a/homeassistant/components/sensor/amcrest.py +++ b/homeassistant/components/sensor/amcrest.py @@ -20,7 +20,7 @@ import homeassistant.loader as loader from requests.exceptions import HTTPError, ConnectTimeout -REQUIREMENTS = ['amcrest==1.1.0'] +REQUIREMENTS = ['amcrest==1.1.3'] _LOGGER = logging.getLogger(__name__) @@ -55,13 +55,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up a sensor for an Amcrest IP Camera.""" from amcrest import AmcrestCamera - data = AmcrestCamera( + camera = AmcrestCamera( config.get(CONF_HOST), config.get(CONF_PORT), - config.get(CONF_USERNAME), config.get(CONF_PASSWORD)) + config.get(CONF_USERNAME), config.get(CONF_PASSWORD)).camera persistent_notification = loader.get_component('persistent_notification') try: - data.camera.current_time + camera.current_time except (ConnectTimeout, HTTPError) as ex: _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex)) persistent_notification.create( @@ -74,7 +74,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sensors = [] for sensor_type in config.get(CONF_MONITORED_CONDITIONS): - sensors.append(AmcrestSensor(config, data, sensor_type)) + sensors.append(AmcrestSensor(config, camera, sensor_type)) add_devices(sensors, True) @@ -84,11 +84,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class AmcrestSensor(Entity): """A sensor implementation for Amcrest IP camera.""" - def __init__(self, device_info, data, sensor_type): + def __init__(self, device_info, camera, sensor_type): """Initialize a sensor for Amcrest camera.""" super(AmcrestSensor, self).__init__() self._attrs = {} - self._data = data + self._camera = camera self._sensor_type = sensor_type self._name = '{0}_{1}'.format(device_info.get(CONF_NAME), SENSOR_TYPES.get(self._sensor_type)[0]) @@ -122,21 +122,21 @@ class AmcrestSensor(Entity): def update(self): """Get the latest data and updates the state.""" - version, build_date = self._data.camera.software_information + version, build_date = self._camera.software_information self._attrs['Build Date'] = build_date.split('=')[-1] - self._attrs['Serial Number'] = self._data.camera.serial_number + self._attrs['Serial Number'] = self._camera.serial_number self._attrs['Version'] = version.split('=')[-1] if self._sensor_type == 'motion_detector': - self._state = self._data.camera.is_motion_detected - self._attrs['Record Mode'] = self._data.camera.record_mode + self._state = self._camera.is_motion_detected + self._attrs['Record Mode'] = self._camera.record_mode elif self._sensor_type == 'ptz_preset': - self._state = self._data.camera.ptz_presets_count + self._state = self._camera.ptz_presets_count elif self._sensor_type == 'sdcard': - sd_used = self._data.camera.storage_used - sd_total = self._data.camera.storage_total + sd_used = self._camera.storage_used + sd_total = self._camera.storage_total self._attrs['Total'] = '{0} {1}'.format(*sd_total) self._attrs['Used'] = '{0} {1}'.format(*sd_used) - self._state = self._data.camera.percent(sd_used[0], sd_total[0]) + self._state = self._camera.percent(sd_used[0], sd_total[0]) diff --git a/requirements_all.txt b/requirements_all.txt index 5874dcc7bf7..bace30c8fc8 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -38,7 +38,7 @@ aiohttp_cors==0.5.0 # homeassistant.components.camera.amcrest # homeassistant.components.sensor.amcrest -amcrest==1.1.0 +amcrest==1.1.3 # homeassistant.components.media_player.anthemav anthemav==1.1.7 From 699c615d23ab26edf5e5d099866736ef2cd9acf9 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sun, 22 Jan 2017 18:34:00 +0200 Subject: [PATCH 105/191] Add url --- script/gen_requirements_all.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 537b93d173a..33c444cb3a6 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -28,6 +28,9 @@ IGNORE_PACKAGES = ( IGNORE_PIN = ('colorlog>2.1,<3', 'keyring>=9.3,<10.0', 'urllib3') +URL_PIN = ('https://home-assistant.io/developers/code_review_platform/' + '#1-requirements') + def explore_module(package, explore_children): """Explore the modules.""" @@ -81,8 +84,9 @@ def gather_modules(): for req in module.REQUIREMENTS: if req.partition('==')[1] == '' and req not in IGNORE_PIN: - errors.append("{}[Please pin requirement {}]" - .format(package, req)) + errors.append( + "{}[Please pin requirement {}, see {}]".format( + package, req, URL_PIN)) reqs.setdefault(req, []).append(package) for key in reqs: From addc2c43408388eda100d4a4d6969845fc013c0f Mon Sep 17 00:00:00 2001 From: andrey-git Date: Sun, 22 Jan 2017 21:19:50 +0200 Subject: [PATCH 106/191] Allow easier customization of whole domain, entity lists, globs. (#5215) --- homeassistant/config.py | 38 ++++++---- homeassistant/helpers/config_validation.py | 9 +++ homeassistant/helpers/customize.py | 80 ++++++++++++++++++++ homeassistant/helpers/entity.py | 18 +---- tests/helpers/test_config_validation.py | 28 +++++++ tests/helpers/test_customize.py | 87 ++++++++++++++++++++++ tests/helpers/test_entity.py | 6 +- tests/test_config.py | 67 +++++++++++++---- 8 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 homeassistant/helpers/customize.py create mode 100644 tests/helpers/test_customize.py diff --git a/homeassistant/config.py b/homeassistant/config.py index eb29212a67d..bbfee5730a8 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -6,7 +6,7 @@ import os import shutil from types import MappingProxyType # pylint: disable=unused-import -from typing import Any, Tuple # NOQA +from typing import Any, List, Tuple # NOQA import voluptuous as vol @@ -14,15 +14,15 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM, CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS, - __version__) -from homeassistant.core import valid_entity_id, DOMAIN as CONF_CORE + CONF_ENTITY_ID, __version__) +from homeassistant.core import DOMAIN as CONF_CORE from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import get_component from homeassistant.util.yaml import load_yaml import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import set_customize from homeassistant.util import dt as date_util, location as loc_util from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM +from homeassistant.helpers.customize import set_customize _LOGGER = logging.getLogger(__name__) @@ -87,19 +87,24 @@ tts: """ -def _valid_customize(value): - """Config validator for customize.""" - if not isinstance(value, dict): - raise vol.Invalid('Expected dictionary') +CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({ + vol.Required(CONF_ENTITY_ID): vol.All( + cv.ensure_list_csv, vol.Length(min=1), [cv.string], [vol.Lower]) +}, extra=vol.ALLOW_EXTRA) - for key, val in value.items(): - if not valid_entity_id(key): - raise vol.Invalid('Invalid entity ID: {}'.format(key)) - if not isinstance(val, dict): - raise vol.Invalid('Value of {} is not a dictionary'.format(key)) +def _convert_old_config(inp: Any) -> List: + if not isinstance(inp, dict): + return cv.ensure_list(inp) + if CONF_ENTITY_ID in inp: + return [inp] # sigle entry + res = [] - return value + inp = vol.Schema({cv.match_all: dict})(inp) + for key, val in inp.items(): + val[CONF_ENTITY_ID] = key + res.append(val) + return res PACKAGES_CONFIG_SCHEMA = vol.Schema({ @@ -116,7 +121,8 @@ CORE_CONFIG_SCHEMA = vol.Schema({ CONF_UNIT_SYSTEM: cv.unit_system, CONF_TIME_ZONE: cv.time_zone, vol.Required(CONF_CUSTOMIZE, - default=MappingProxyType({})): _valid_customize, + default=MappingProxyType({})): vol.All( + _convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY]), vol.Optional(CONF_PACKAGES, default={}): PACKAGES_CONFIG_SCHEMA, }) @@ -301,7 +307,7 @@ def async_process_ha_core_config(hass, config): if CONF_TIME_ZONE in config: set_time_zone(config.get(CONF_TIME_ZONE)) - set_customize(config.get(CONF_CUSTOMIZE) or {}) + set_customize(hass, config.get(CONF_CUSTOMIZE) or {}) if CONF_UNIT_SYSTEM in config: if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL: diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index b78eedec8c2..0c28dbdd78e 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -376,6 +376,8 @@ def ordered_dict(value_validator, key_validator=match_all): """Validate ordered dict.""" config = OrderedDict() + if not isinstance(value, dict): + raise vol.Invalid('Value {} is not a dictionary'.format(value)) for key, val in value.items(): v_res = item_validator({key: val}) config.update(v_res) @@ -385,6 +387,13 @@ def ordered_dict(value_validator, key_validator=match_all): return validator +def ensure_list_csv(value: Any) -> Sequence: + """Ensure that input is a list or make one from comma-separated string.""" + if isinstance(value, str): + return [member.strip() for member in value.split(',')] + return ensure_list(value) + + # Validator helpers def key_dependency(key, dependency): diff --git a/homeassistant/helpers/customize.py b/homeassistant/helpers/customize.py new file mode 100644 index 00000000000..b03a89ff40f --- /dev/null +++ b/homeassistant/helpers/customize.py @@ -0,0 +1,80 @@ +"""A helper module for customization.""" +import collections +from typing import Dict, List +import fnmatch + +from homeassistant.const import CONF_ENTITY_ID +from homeassistant.core import HomeAssistant, split_entity_id + +_OVERWRITE_KEY = 'overwrite' +_OVERWRITE_CACHE_KEY = 'overwrite_cache' + + +def set_customize(hass: HomeAssistant, customize: List[Dict]) -> None: + """Overwrite all current customize settings. + + Async friendly. + """ + hass.data[_OVERWRITE_KEY] = customize + hass.data[_OVERWRITE_CACHE_KEY] = {} + + +def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict: + """Return a dictionary of overrides related to entity_id. + + Whole-domain overrides are of lowest priorities, + then glob on entity ID, and finally exact entity_id + matches are of highest priority. + + The lookups are cached. + """ + if _OVERWRITE_CACHE_KEY in hass.data and \ + entity_id in hass.data[_OVERWRITE_CACHE_KEY]: + return hass.data[_OVERWRITE_CACHE_KEY][entity_id] + if _OVERWRITE_KEY not in hass.data: + return {} + domain_result = {} # type: Dict[str, Any] + glob_result = {} # type: Dict[str, Any] + exact_result = {} # type: Dict[str, Any] + domain = split_entity_id(entity_id)[0] + + def clean_entry(entry: Dict) -> Dict: + """Clean up entity-matching keys.""" + entry.pop(CONF_ENTITY_ID, None) + return entry + + def deep_update(target: Dict, source: Dict) -> None: + """Deep update a dictionary.""" + for key, value in source.items(): + if isinstance(value, collections.Mapping): + updated_value = target.get(key, {}) + # If the new value is map, but the old value is not - + # overwrite the old value. + if not isinstance(updated_value, collections.Mapping): + updated_value = {} + deep_update(updated_value, value) + target[key] = updated_value + else: + target[key] = source[key] + + for rule in hass.data[_OVERWRITE_KEY]: + if CONF_ENTITY_ID in rule: + entities = rule[CONF_ENTITY_ID] + if domain in entities: + deep_update(domain_result, rule) + if entity_id in entities: + deep_update(exact_result, rule) + for entity_id_glob in entities: + if entity_id_glob == entity_id: + continue + if fnmatch.fnmatchcase(entity_id, entity_id_glob): + deep_update(glob_result, rule) + break + result = {} + deep_update(result, clean_entry(domain_result)) + deep_update(result, clean_entry(glob_result)) + deep_update(result, clean_entry(exact_result)) + if _OVERWRITE_CACHE_KEY not in hass.data: + hass.data[_OVERWRITE_CACHE_KEY] = {} + hass.data[_OVERWRITE_CACHE_KEY][entity_id] = result + return result diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 0d2f56f1807..438de6a66d3 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -4,7 +4,7 @@ import logging import functools as ft from timeit import default_timer as timer -from typing import Any, Optional, List, Dict +from typing import Optional, List from homeassistant.const import ( ATTR_ASSUMED_STATE, ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ICON, @@ -16,9 +16,7 @@ from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.util import ensure_unique_string, slugify from homeassistant.util.async import ( run_coroutine_threadsafe, run_callback_threadsafe) - -# Entity attributes that we will overwrite -_OVERWRITE = {} # type: Dict[str, Any] +from homeassistant.helpers.customize import get_overrides _LOGGER = logging.getLogger(__name__) @@ -57,16 +55,6 @@ def async_generate_entity_id(entity_id_format: str, name: Optional[str], entity_id_format.format(slugify(name)), current_ids) -def set_customize(customize: Dict[str, Any]) -> None: - """Overwrite all current customize settings. - - Async friendly. - """ - global _OVERWRITE - - _OVERWRITE = {key.lower(): val for key, val in customize.items()} - - class Entity(object): """An abstract class for Home Assistant entities.""" @@ -254,7 +242,7 @@ class Entity(object): end - start) # Overwrite properties that have been set in the config file. - attr.update(_OVERWRITE.get(self.entity_id, {})) + attr.update(get_overrides(self.hass, self.entity_id)) # Remove hidden property if false so it won't show up. if not attr.get(ATTR_HIDDEN, True): diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index 252f7f60c95..7255447cd49 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -165,6 +165,25 @@ def test_entity_ids(): ] +def test_ensure_list_csv(): + """Test ensure_list_csv.""" + schema = vol.Schema(cv.ensure_list_csv) + + options = ( + None, + 12, + [], + ['string'], + 'string1,string2' + ) + for value in options: + schema(value) + + assert schema('string1, string2 ') == [ + 'string1', 'string2' + ] + + def test_event_schema(): """Test event_schema validation.""" options = ( @@ -429,6 +448,15 @@ def test_has_at_least_one_key(): schema(value) +def test_ordered_dict_only_dict(): + """Test ordered_dict validator.""" + schema = vol.Schema(cv.ordered_dict(cv.match_all, cv.match_all)) + + for value in (None, [], 100, 'hello'): + with pytest.raises(vol.MultipleInvalid): + schema(value) + + def test_ordered_dict_order(): """Test ordered_dict validator.""" schema = vol.Schema(cv.ordered_dict(int, cv.string)) diff --git a/tests/helpers/test_customize.py b/tests/helpers/test_customize.py new file mode 100644 index 00000000000..e3fd1e325b0 --- /dev/null +++ b/tests/helpers/test_customize.py @@ -0,0 +1,87 @@ +"""Test the customize helper.""" +import homeassistant.helpers.customize as customize + + +class MockHass(object): + """Mock object for HassAssistant.""" + + data = {} + + +class TestHelpersCustomize(object): + """Test homeassistant.helpers.customize module.""" + + def setup_method(self, method): + """Setup things to be run when tests are started.""" + self.entity_id = 'test.test' + self.hass = MockHass() + + def _get_overrides(self, overrides): + customize.set_customize(self.hass, overrides) + return customize.get_overrides(self.hass, self.entity_id) + + def test_override_single_value(self): + """Test entity customization through configuration.""" + result = self._get_overrides([ + {'entity_id': [self.entity_id], 'key': 'value'}]) + + assert result == {'key': 'value'} + + def test_override_multiple_values(self): + """Test entity customization through configuration.""" + result = self._get_overrides([ + {'entity_id': [self.entity_id], 'key1': 'value1'}, + {'entity_id': [self.entity_id], 'key2': 'value2'}]) + + assert result == {'key1': 'value1', 'key2': 'value2'} + + def test_override_same_value(self): + """Test entity customization through configuration.""" + result = self._get_overrides([ + {'entity_id': [self.entity_id], 'key': 'value1'}, + {'entity_id': [self.entity_id], 'key': 'value2'}]) + + assert result == {'key': 'value2'} + + def test_override_by_domain(self): + """Test entity customization through configuration.""" + result = self._get_overrides([ + {'entity_id': ['test'], 'key': 'value'}]) + + assert result == {'key': 'value'} + + def test_override_by_glob(self): + """Test entity customization through configuration.""" + result = self._get_overrides([ + {'entity_id': ['test.?e*'], 'key': 'value'}]) + + assert result == {'key': 'value'} + + def test_override_exact_over_glob_over_domain(self): + """Test entity customization through configuration.""" + result = self._get_overrides([ + {'entity_id': ['test.test'], 'key1': 'valueExact'}, + {'entity_id': ['test.tes?'], + 'key1': 'valueGlob', + 'key2': 'valueGlob'}, + {'entity_id': ['test'], + 'key1': 'valueDomain', + 'key2': 'valueDomain', + 'key3': 'valueDomain'}]) + + assert result == { + 'key1': 'valueExact', + 'key2': 'valueGlob', + 'key3': 'valueDomain'} + + def test_override_deep_dict(self): + """Test we can overwrite hidden property to True.""" + result = self._get_overrides( + [{'entity_id': [self.entity_id], + 'test': {'key1': 'value1', 'key2': 'value2'}}, + {'entity_id': [self.entity_id], + 'test': {'key3': 'value3', 'key2': 'value22'}}]) + assert result['test'] == { + 'key1': 'value1', + 'key2': 'value22', + 'key3': 'value3'} diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index b2db8277085..9ec016ccfcd 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -6,6 +6,7 @@ from unittest.mock import MagicMock import pytest import homeassistant.helpers.entity as entity +from homeassistant.helpers.customize import set_customize from homeassistant.const import ATTR_HIDDEN from tests.common import get_test_home_assistant @@ -78,7 +79,6 @@ class TestHelpersEntity(object): def teardown_method(self, method): """Stop everything that was started.""" - entity.set_customize({}) self.hass.stop() def test_default_hidden_not_in_attributes(self): @@ -88,7 +88,9 @@ class TestHelpersEntity(object): def test_overwriting_hidden_property_to_true(self): """Test we can overwrite hidden property to True.""" - entity.set_customize({self.entity.entity_id: {ATTR_HIDDEN: True}}) + set_customize( + self.hass, + [{'entity_id': [self.entity.entity_id], ATTR_HIDDEN: True}]) self.entity.update_ha_state() state = self.hass.states.get(self.entity.entity_id) diff --git a/tests/test_config.py b/tests/test_config.py index 455ebe33c61..39769486056 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -170,16 +170,17 @@ class TestConfig(unittest.TestCase): os.path.join(CONFIG_DIR, 'non_existing_dir/'), False)) self.assertTrue(mock_print.called) + # pylint: disable=no-self-use def test_core_config_schema(self): """Test core config schema.""" for value in ( - {CONF_UNIT_SYSTEM: 'K'}, - {'time_zone': 'non-exist'}, - {'latitude': '91'}, - {'longitude': -181}, - {'customize': 'bla'}, - {'customize': {'invalid_entity_id': {}}}, - {'customize': {'light.sensor': 100}}, + {CONF_UNIT_SYSTEM: 'K'}, + {'time_zone': 'non-exist'}, + {'latitude': '91'}, + {'longitude': -181}, + {'customize': 'bla'}, + {'customize': {'light.sensor': 100}}, + {'customize': {'entity_id': []}}, ): with pytest.raises(MultipleInvalid): config_util.CORE_CONFIG_SCHEMA(value) @@ -196,13 +197,7 @@ class TestConfig(unittest.TestCase): }, }) - def test_entity_customization(self): - """Test entity customization through configuration.""" - config = {CONF_LATITUDE: 50, - CONF_LONGITUDE: 50, - CONF_NAME: 'Test', - CONF_CUSTOMIZE: {'test.test': {'hidden': True}}} - + def _compute_state(self, config): run_coroutine_threadsafe( config_util.async_process_ha_core_config(self.hass, config), self.hass.loop).result() @@ -214,10 +209,50 @@ class TestConfig(unittest.TestCase): self.hass.block_till_done() - state = self.hass.states.get('test.test') + return self.hass.states.get('test.test') + + def test_entity_customization_false(self): + """Test entity customization through configuration.""" + config = {CONF_LATITUDE: 50, + CONF_LONGITUDE: 50, + CONF_NAME: 'Test', + CONF_CUSTOMIZE: { + 'test.test': {'hidden': False}}} + + state = self._compute_state(config) + + assert 'hidden' not in state.attributes + + def test_entity_customization(self): + """Test entity customization through configuration.""" + config = {CONF_LATITUDE: 50, + CONF_LONGITUDE: 50, + CONF_NAME: 'Test', + CONF_CUSTOMIZE: {'test.test': {'hidden': True}}} + + state = self._compute_state(config) assert state.attributes['hidden'] + def test_entity_customization_comma_separated(self): + """Test entity customization through configuration.""" + config = {CONF_LATITUDE: 50, + CONF_LONGITUDE: 50, + CONF_NAME: 'Test', + CONF_CUSTOMIZE: [ + {'entity_id': 'test.not_test,test,test.not_t*', + 'key1': 'value1'}, + {'entity_id': 'test.test,not_test,test.not_t*', + 'key2': 'value2'}, + {'entity_id': 'test.not_test,not_test,test.t*', + 'key3': 'value3'}]} + + state = self._compute_state(config) + + assert state.attributes['key1'] == 'value1' + assert state.attributes['key2'] == 'value2' + assert state.attributes['key3'] == 'value3' + @mock.patch('homeassistant.config.shutil') @mock.patch('homeassistant.config.os') def test_remove_lib_on_upgrade(self, mock_os, mock_shutil): @@ -229,6 +264,7 @@ class TestConfig(unittest.TestCase): mock_open = mock.mock_open() with mock.patch('homeassistant.config.open', mock_open, create=True): opened_file = mock_open.return_value + # pylint: disable=no-member opened_file.readline.return_value = ha_version self.hass.config.path = mock.Mock() @@ -258,6 +294,7 @@ class TestConfig(unittest.TestCase): mock_open = mock.mock_open() with mock.patch('homeassistant.config.open', mock_open, create=True): opened_file = mock_open.return_value + # pylint: disable=no-member opened_file.readline.return_value = ha_version self.hass.config.path = mock.Mock() From 944213137365f65e89d1ab7fe84b30c892d1a1a3 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 22 Jan 2017 16:21:20 -0800 Subject: [PATCH 107/191] Add organization docs --- CLA.md | 54 +++++++++++++++++++++++++++++++ CODE_OF_CONDUCT.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 20 ------------ LICENSE.md | 25 +++++++++++++++ 4 files changed, 159 insertions(+), 20 deletions(-) create mode 100644 CLA.md create mode 100644 CODE_OF_CONDUCT.md delete mode 100644 LICENSE create mode 100644 LICENSE.md diff --git a/CLA.md b/CLA.md new file mode 100644 index 00000000000..4560e3be643 --- /dev/null +++ b/CLA.md @@ -0,0 +1,54 @@ +# Contributor License Agreement + +The following terms are used throughout this agreement: + +**You** - the person or legal entity including its affiliates asked to accept this agreement. +An affiliate is any entity that controls or is controlled by the legal entity, or is under common control with it. + +**Project** - is an umbrella term that refers to any and all Home Assistant open source projects. + +**Contribution** - any type of work that is submitted to a Project, including any modifications or additions to existing work. + +**Submitted** - conveyed to a Project via a pull request, commit, issue, or any form of electronic, written, or +verbal communication with Home Assistant, contributors or maintainers. + +# 1. Grant of Copyright License. + +Subject to the terms and conditions of this agreement, You grant to the Projects’ maintainers, contributors, +users and to Home Assistant a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, +prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your contributions and such +derivative works. Except for this license, You reserve all rights, title, and interest in your contributions. + +# 2. Grant of Patent License. + +Subject to the terms and conditions of this agreement, You grant to the Projects’ maintainers, contributors, users and to +Home Assistant a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise transfer your contributions, where such license +applies only to those patent claims licensable by you that are necessarily infringed by your contribution or by combination of +your contribution with the project to which this contribution was submitted. + +If any entity institutes patent litigation - including cross-claim or counterclaim in a lawsuit - against You alleging that +your contribution or any project it was submitted to constitutes or is responsible for direct or contributory patent infringement, +then any patent licenses granted to that entity under this agreement shall terminate as of the date such litigation is filed. + +# 3. Source of Contribution. + +Your contribution is either your original creation, based upon previous work that, to the best of your knowledge, +is covered under an appropriate open source license and you have the right under that license to submit that work with modifications, +whether created in whole or in part by you, or you have clearly identified the source of the contribution and any license or other +restriction (like related patents, trademarks, and license agreements) of which you are personally aware. + +## Attribution + +This Contributor License Agreement is adapted from the [GitHub CLA][github-cla]. + +## Signing + +To sign this CLA you must first submit a pull request to a repository under the Home Assistant organization. + +## Adoption + +This Contributor License Agreement (CLA) was first announced on January 21st, 2017 in [this][cla-blog] blog post and adopted January 28th, 2017. + +[github-cla]: https://cla.github.com/agreement +[cla-blog]: https://home-assistant.io/blog/2017/01/21/home-assistant-governance/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..5d2149dce05 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,80 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [safety@home-assistant.io][email]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available [here][version]. + +## Adoption + +This Code of Conduct was first adopted January 21st, 2017 and announced in [this][coc-blog] blog post. + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ +[email]: mailto:safety@home-assistant.io +[coc-blog]: https://home-assistant.io/blog/2017/01/21/home-assistant-governance/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 42a425b4118..00000000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Paulus Schoutsen - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000000..c1a3c4845c2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2013-2017` `Paulus Schoutsen` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. From 8217a4296018637088cd485f1a27fb494b69b41b Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 23 Jan 2017 00:03:55 -0500 Subject: [PATCH 108/191] Don't start test thread as daemon and wait until patching is done (#5494) --- tests/common.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/common.py b/tests/common.py index 514a4973202..b602edbd717 100644 --- a/tests/common.py +++ b/tests/common.py @@ -45,7 +45,6 @@ def get_test_home_assistant(): hass = loop.run_until_complete(async_test_home_assistant(loop)) - # FIXME should not be a daemon. Means hass.stop() not called in teardown stop_event = threading.Event() def run_loop(): @@ -56,8 +55,6 @@ def get_test_home_assistant(): loop.close() stop_event.set() - threading.Thread(name="LoopThread", target=run_loop, daemon=True).start() - orig_start = hass.start orig_stop = hass.stop @@ -76,6 +73,8 @@ def get_test_home_assistant(): hass.start = start_hass hass.stop = stop_hass + threading.Thread(name="LoopThread", target=run_loop, daemon=False).start() + return hass From f54f68903df3d73a14e2ce6bc47d334c66ad7f76 Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 23 Jan 2017 10:17:29 -0500 Subject: [PATCH 109/191] Fixes for rest tests (#5495) * Fixes for rest tests * Linter fixes * Alternate test_setup_missing_config implementation --- tests/components/sensor/test_rest.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/components/sensor/test_rest.py b/tests/components/sensor/test_rest.py index 4abfb2d4551..1c4910927a5 100644 --- a/tests/components/sensor/test_rest.py +++ b/tests/components/sensor/test_rest.py @@ -7,9 +7,11 @@ from requests.exceptions import Timeout, MissingSchema, RequestException import requests_mock from homeassistant.bootstrap import setup_component +import homeassistant.components.sensor as sensor import homeassistant.components.sensor.rest as rest from homeassistant.const import STATE_UNKNOWN from homeassistant.helpers.config_validation import template + from tests.common import get_test_home_assistant, assert_setup_component @@ -26,10 +28,9 @@ class TestRestSwitchSetup(unittest.TestCase): def test_setup_missing_config(self): """Test setup with configuration missing required entries.""" - self.assertFalse(rest.setup_platform(self.hass, { - 'platform': 'rest', - 'resource': 'http://localhost' - }, None)) + with assert_setup_component(0): + assert setup_component(self.hass, sensor.DOMAIN, { + 'sensor': {'platform': 'rest'}}) def test_setup_missing_schema(self): """Test setup with resource missing schema.""" @@ -40,7 +41,8 @@ class TestRestSwitchSetup(unittest.TestCase): 'method': 'GET' }, None) - @patch('requests.get', side_effect=requests.exceptions.ConnectionError()) + @patch('requests.Session.send', + side_effect=requests.exceptions.ConnectionError()) def test_setup_failed_connect(self, mock_req): """Test setup when connection error occurs.""" self.assertFalse(rest.setup_platform(self.hass, { @@ -48,7 +50,7 @@ class TestRestSwitchSetup(unittest.TestCase): 'resource': 'http://localhost', }, None)) - @patch('requests.get', side_effect=Timeout()) + @patch('requests.Session.send', side_effect=Timeout()) def test_setup_timeout(self, mock_req): """Test setup when connection timeout occurs.""" self.assertFalse(rest.setup_platform(self.hass, { From 9bc9e7fbc478f02302042bf5bc144cd5cd993b95 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Mon, 23 Jan 2017 13:20:54 -0800 Subject: [PATCH 110/191] Dont set a speed when fan turns on (#5514) --- homeassistant/components/fan/mqtt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/fan/mqtt.py b/homeassistant/components/fan/mqtt.py index 4540ce01532..4ca1fc8bae4 100644 --- a/homeassistant/components/fan/mqtt.py +++ b/homeassistant/components/fan/mqtt.py @@ -235,11 +235,12 @@ class MqttFan(FanEntity): """Return the oscillation state.""" return self._oscillation - def turn_on(self, speed: str=SPEED_MEDIUM) -> None: + def turn_on(self, speed: str=None) -> None: """Turn on the entity.""" mqtt.publish(self._hass, self._topic[CONF_COMMAND_TOPIC], self._payload[STATE_ON], self._qos, self._retain) - self.set_speed(speed) + if speed: + self.set_speed(speed) def turn_off(self) -> None: """Turn off the entity.""" From d5119a0520f6a0e8f5b79d1b823161aeff400ce0 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 23 Jan 2017 22:21:12 +0100 Subject: [PATCH 111/191] Use device_state_attributes (#5518) --- homeassistant/components/binary_sensor/digital_ocean.py | 4 ++-- homeassistant/components/binary_sensor/threshold.py | 2 +- homeassistant/components/switch/digital_ocean.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/binary_sensor/digital_ocean.py b/homeassistant/components/binary_sensor/digital_ocean.py index 821acb2da95..4c5783cc220 100644 --- a/homeassistant/components/binary_sensor/digital_ocean.py +++ b/homeassistant/components/binary_sensor/digital_ocean.py @@ -29,7 +29,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the Digital Ocean droplet sensor.""" + """Set up the Digital Ocean droplet sensor.""" digital_ocean = get_component('digital_ocean') droplets = config.get(CONF_DROPLETS) @@ -68,7 +68,7 @@ class DigitalOceanBinarySensor(BinarySensorDevice): return DEFAULT_SENSOR_CLASS @property - def state_attributes(self): + def device_state_attributes(self): """Return the state attributes of the Digital Ocean droplet.""" return { ATTR_CREATED_AT: self.data.created_at, diff --git a/homeassistant/components/binary_sensor/threshold.py b/homeassistant/components/binary_sensor/threshold.py index 4dc11a3c5c7..78338de64f7 100644 --- a/homeassistant/components/binary_sensor/threshold.py +++ b/homeassistant/components/binary_sensor/threshold.py @@ -110,7 +110,7 @@ class ThresholdSensor(BinarySensorDevice): return self._sensor_class @property - def state_attributes(self): + def device_state_attributes(self): """Return the state attributes of the sensor.""" return { ATTR_ENTITY_ID: self._entity_id, diff --git a/homeassistant/components/switch/digital_ocean.py b/homeassistant/components/switch/digital_ocean.py index 8df79bebc5d..11414ce96c5 100644 --- a/homeassistant/components/switch/digital_ocean.py +++ b/homeassistant/components/switch/digital_ocean.py @@ -64,7 +64,7 @@ class DigitalOceanSwitch(SwitchDevice): return self.data.status == 'active' @property - def state_attributes(self): + def device_state_attributes(self): """Return the state attributes of the Digital Ocean droplet.""" return { ATTR_CREATED_AT: self.data.created_at, From 900868708e8e6ec3c6222acdbbecc400c4333b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Vran=C3=ADk?= Date: Mon, 23 Jan 2017 22:22:39 +0100 Subject: [PATCH 112/191] check cec message length when asking physical address (#5516) * cec client object * cec command structure * autodetect source * volume support and native source select * switch device * media player device * detecting of state * friendly names * hdmi cec properties * presence detection * simplified callbacks * stable names * renamed methods * code cleanup * name with vendor * fixed standby call name * fake standby/poweron * domain switch * domain switch * async updating * update separated * cec -> hass event bridge * fixed name generation * code cleanup * code cleanup * icon constants * code cleanup * do not register unavailable devices * discovery of deevices * code cleanup * cec device discovery * moved method implementation into child * service descriptions * service descriptions * service descriptions * changed entity init sequence * logging cleanup * add remove as job * closing cec, no service schemas * correct iterate over dictionary * Volume by commands * threading * logging minimized * get load out of main thread * naming cleanup * get load out of main thread * optimized discovery * async where possible * cleanup logging, constructors first * pydoc * formatting * no async_update from out of loop no hiding entities removed redundant device_state_attributes async updating presence * no async * working async cec * cec in thirdparty lib * cec initialized oudsice * working without SIGSEGV * rollbacked file changed by mistake * sending of commands * working with ha * using hass loop and device driven updates * version up * version up * Command types in pycec, cleanup for HA integration * Removed media player, state moved to switch * service descriptions * requirements: pyCEC * line width to 79 * doc * doc * overindentation solved * HDMI to uppercase * minimal dependency on cec * removed unwanted line * doc wording * margin 79 * line continuation indent * imperative doc * lint: indentation * fixed overindented * fixed overindented * fixed overindented * fixed overindented * order of imports * PEP8 * keep signature of overriding * removed redundant blank line * fixed update call method (#4) * Preparation for merge to upstream (#5) * newer version of pyCEC * updated services.yaml * fixed lint scrpt to operate only on python files * pycec version up * update services * no coverage report * exclude non python files from lint * lint only on python files * Dev (#6) * reordered * sending nonserialized data through hass.data * code formatting * code formatting * import order * Dev (#7) * newer version of pyCEC * updated services.yaml * fixed lint scrpt to operate only on python files * pycec version up * update services * no coverage report * exclude non python files from lint * lint only on python files * reordered * sending nonserialized data through hass.data * import order * fixed object handling * code formatting * Backwards compatibility of hdmi_cec (#10) * services: power_on standby active_source * new version of pyCEC (#12) * newer version of pyCEC * devices config (#13) * getting device name from config * shutdown fix (#14) * correct call on shutdown * remove misplaced annotations (#15) * Preparation for merge to upstream (#5) * newer version of pyCEC * updated services.yaml * reordered * sending nonserialized data through hass.data * services: power_on standby active_source * code formatting * getting device name from config * correct call on shutdown * pyCEC version 0.3.6 (#18) * newer version of pyCEC * updated services.yaml * sending nonserialized data through hass.data * services: ** power_on ** standby ** active_source * getting device name from config * correct call on shutdown * fork new thread on multicore machines * support both config schemas: original and new (#16) * volume press and release support (#17) * support for media_player (#21) * accept hexadecimal format of commands * support for media player * platform customization * type constants * Dev (#23) * accept hexadecimal format of commands * support for media player * platform customization * TCP CEC support (#24) * accept hexadecimal format of commands * support for media player * platform customization * preparing tcp support * volume handling (#25) * Incorporated CR remarks (#26) * cleanup imports * cleanup and enhance services description * removed unwanted file * implemented CR remarks (#27) * pyCEC v0.4.6 * pined dependency version * tighten service schemas * requirements (#28) * incorporate remarks from users (#32) * home-assistant-31 make mute schema better (#31) * pycec-30 pyCEC version up (#30) * pycec-30 pyCEC version up (#30) * home-assistant-30 OSD display name from configuration (#30) (#33) * Home assistant 29 (#34) * home-assistant-29 counting from 0 (#29) * Home assistant 31 (#35) * home-assistant-31 add support for mute-on and mute-off (#31) * home-assistant-31 pyCEC version up (#31) * Home assistant 31 (#36) * home-assistant-31 Limit OSD name to 13 chars (#31) * home-assistant-31 Limit OSD name to 13 chars moved to CEC adapter (#31) * home-assistant-31 version up (#31) * home-assistant-31 formatting (#31) * formatting * service description * service description * single attribute for volume * fixed mute on -> mute off * moved config constant from core into component * check cec message length when asking physical address (#38) (#38) * cec turn on/turn off commands instead of power * cec turn on/turn off commands instead of power --- homeassistant/components/hdmi_cec.py | 2 +- homeassistant/components/switch/hdmi_cec.py | 8 ++++++++ requirements_all.txt | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hdmi_cec.py b/homeassistant/components/hdmi_cec.py index 23c3fc01106..1b957e80c65 100644 --- a/homeassistant/components/hdmi_cec.py +++ b/homeassistant/components/hdmi_cec.py @@ -26,7 +26,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, STATE_UNKNOWN, from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyCEC==0.4.9'] +REQUIREMENTS = ['pyCEC==0.4.11'] DOMAIN = 'hdmi_cec' diff --git a/homeassistant/components/switch/hdmi_cec.py b/homeassistant/components/switch/hdmi_cec.py index bd1f9ea6578..0e17aeab8e4 100644 --- a/homeassistant/components/switch/hdmi_cec.py +++ b/homeassistant/components/switch/hdmi_cec.py @@ -47,6 +47,14 @@ class CecSwitchDevice(CecDevice, SwitchDevice): self._device.turn_off() self._state = STATE_ON + def toggle(self): + """Toggle the entity.""" + self._device.toggle() + if self._state == STATE_ON: + self._state = STATE_OFF + else: + self._state = STATE_ON + @property def is_on(self) -> bool: """Return True if entity is on.""" diff --git a/requirements_all.txt b/requirements_all.txt index bace30c8fc8..08ef6b6d9c7 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -381,7 +381,7 @@ pwaqi==1.3 py-cpuinfo==0.2.3 # homeassistant.components.hdmi_cec -pyCEC==0.4.9 +pyCEC==0.4.11 # homeassistant.components.switch.tplink pyHS100==0.2.3 From 318b0f4f36567c5a184a117ad1f353f064da4396 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 23 Jan 2017 22:23:41 +0100 Subject: [PATCH 113/191] Upgrade beautifulsoup4 to 4.5.3 (#5519) --- homeassistant/components/sensor/hydroquebec.py | 2 +- homeassistant/components/sensor/scrape.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/hydroquebec.py b/homeassistant/components/sensor/hydroquebec.py index c7fbac6b56a..c0f4e091c45 100644 --- a/homeassistant/components/sensor/hydroquebec.py +++ b/homeassistant/components/sensor/hydroquebec.py @@ -21,7 +21,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['beautifulsoup4==4.5.1'] +REQUIREMENTS = ['beautifulsoup4==4.5.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sensor/scrape.py b/homeassistant/components/sensor/scrape.py index 082c6a1fcfd..f825628d9ae 100644 --- a/homeassistant/components/sensor/scrape.py +++ b/homeassistant/components/sensor/scrape.py @@ -16,7 +16,7 @@ from homeassistant.const import ( from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['beautifulsoup4==4.5.1'] +REQUIREMENTS = ['beautifulsoup4==4.5.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 08ef6b6d9c7..debca2fb200 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -60,7 +60,7 @@ batinfo==0.4.2 # homeassistant.components.sensor.hydroquebec # homeassistant.components.sensor.scrape -beautifulsoup4==4.5.1 +beautifulsoup4==4.5.3 # homeassistant.components.light.blinksticklight blinkstick==1.1.8 From b7e477fbbac6c7f32562a5096ef68c8532765e3c Mon Sep 17 00:00:00 2001 From: andrey-git Date: Mon, 23 Jan 2017 23:24:13 +0200 Subject: [PATCH 114/191] Copy val in config.py before modifying (#5520) * Copy val before modifying * Bad line location --- homeassistant/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/config.py b/homeassistant/config.py index bbfee5730a8..a2862c95216 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -102,6 +102,7 @@ def _convert_old_config(inp: Any) -> List: inp = vol.Schema({cv.match_all: dict})(inp) for key, val in inp.items(): + val = dict(val) val[CONF_ENTITY_ID] = key res.append(val) return res From a83b61ad58384c561865b03ff24704be6e71c153 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 23 Jan 2017 22:24:45 +0100 Subject: [PATCH 115/191] Allow direct messaging to user (#5521) --- homeassistant/components/notify/twitter.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/notify/twitter.py b/homeassistant/components/notify/twitter.py index 24128edd880..5672429b9c6 100644 --- a/homeassistant/components/notify/twitter.py +++ b/homeassistant/components/notify/twitter.py @@ -11,7 +11,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.notify import ( PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.const import CONF_ACCESS_TOKEN, CONF_USERNAME REQUIREMENTS = ['TwitterAPI==2.4.3'] @@ -22,10 +22,11 @@ CONF_CONSUMER_SECRET = 'consumer_secret' CONF_ACCESS_TOKEN_SECRET = 'access_token_secret' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_CONSUMER_KEY): cv.string, - vol.Required(CONF_CONSUMER_SECRET): cv.string, vol.Required(CONF_ACCESS_TOKEN): cv.string, vol.Required(CONF_ACCESS_TOKEN_SECRET): cv.string, + vol.Required(CONF_CONSUMER_KEY): cv.string, + vol.Required(CONF_CONSUMER_SECRET): cv.string, + vol.Optional(CONF_USERNAME): cv.string, }) @@ -33,7 +34,8 @@ def get_service(hass, config, discovery_info=None): """Get the Twitter notification service.""" return TwitterNotificationService( config[CONF_CONSUMER_KEY], config[CONF_CONSUMER_SECRET], - config[CONF_ACCESS_TOKEN], config[CONF_ACCESS_TOKEN_SECRET] + config[CONF_ACCESS_TOKEN], config[CONF_ACCESS_TOKEN_SECRET], + config.get(CONF_USERNAME) ) @@ -41,15 +43,21 @@ class TwitterNotificationService(BaseNotificationService): """Implementation of a notification service for the Twitter service.""" def __init__(self, consumer_key, consumer_secret, access_token_key, - access_token_secret): + access_token_secret, username): """Initialize the service.""" from TwitterAPI import TwitterAPI + self.user = username self.api = TwitterAPI(consumer_key, consumer_secret, access_token_key, access_token_secret) def send_message(self, message="", **kwargs): - """Tweet some message.""" - resp = self.api.request('statuses/update', {'status': message}) + """Tweet a message.""" + if self.user: + resp = self.api.request( + 'direct_messages/new', {'text': message, 'user': self.user}) + else: + resp = self.api.request('statuses/update', {'status': message}) + if resp.status_code != 200: import json obj = json.loads(resp.text) From 64fc6a08d3bb0ec60af7b8e0de63c582f7e1d8e3 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 23 Jan 2017 22:25:38 +0100 Subject: [PATCH 116/191] Fix typos (#5522) --- homeassistant/helpers/entity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 438de6a66d3..6f09b9592f3 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -266,7 +266,7 @@ class Entity(object): self.entity_id, state, attr, self.force_update) def schedule_update_ha_state(self, force_refresh=False): - """Shedule a update ha state change task. + """Schedule a update ha state change task. That is only needed on executor to not block. """ @@ -285,7 +285,7 @@ class Entity(object): @asyncio.coroutine def async_remove(self) -> None: - """Remove entitiy from async HASS. + """Remove entity from async HASS. This method must be run in the event loop. """ From dd0110e06db7bfc1f28209c9da47ca1e13884d62 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Mon, 23 Jan 2017 23:33:16 -0800 Subject: [PATCH 117/191] Update pyCEC version --- homeassistant/components/hdmi_cec.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hdmi_cec.py b/homeassistant/components/hdmi_cec.py index 1b957e80c65..11a3f0f2d02 100644 --- a/homeassistant/components/hdmi_cec.py +++ b/homeassistant/components/hdmi_cec.py @@ -26,7 +26,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, STATE_UNKNOWN, from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyCEC==0.4.11'] +REQUIREMENTS = ['pyCEC==0.4.12'] DOMAIN = 'hdmi_cec' diff --git a/requirements_all.txt b/requirements_all.txt index debca2fb200..272fbfd8751 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -381,7 +381,7 @@ pwaqi==1.3 py-cpuinfo==0.2.3 # homeassistant.components.hdmi_cec -pyCEC==0.4.11 +pyCEC==0.4.12 # homeassistant.components.switch.tplink pyHS100==0.2.3 From 38d9dc996ba1bf0386b7db0672d1d44f7a6d9f0f Mon Sep 17 00:00:00 2001 From: Aaron Polley Date: Tue, 24 Jan 2017 08:41:33 +0000 Subject: [PATCH 118/191] Piglow support (#5302) * Add support for Piglow * Updated coverage and requirements * Add support for Piglow * Updated coverage and requirements * Fix linting errors * Fix linting errors * Remove trailing whitespace * Shorter lines * Remove trailing whitespace * Update piglow.py * Pinned piglow version * Remove unused method * Remove unused imports * Fix lint errors * Update requirements all * Updated Piglow to allow the component name to be changed * Fix imports * Pass in name * The piglow platform now fails if it cannot detect the piglow device * Tidy subprocess import --- .coveragerc | 1 + homeassistant/components/light/piglow.py | 104 +++++++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 108 insertions(+) create mode 100644 homeassistant/components/light/piglow.py diff --git a/.coveragerc b/.coveragerc index 47f602072c0..ac97522450f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -201,6 +201,7 @@ omit = homeassistant/components/light/tikteck.py homeassistant/components/light/x10.py homeassistant/components/light/yeelight.py + homeassistant/components/light/piglow.py homeassistant/components/light/zengge.py homeassistant/components/lirc.py homeassistant/components/media_player/anthemav.py diff --git a/homeassistant/components/light/piglow.py b/homeassistant/components/light/piglow.py new file mode 100644 index 00000000000..d4e9c9ed106 --- /dev/null +++ b/homeassistant/components/light/piglow.py @@ -0,0 +1,104 @@ +""" +Support for Piglow LED's. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.piglow/ +""" +import logging +import subprocess + +import voluptuous as vol + +# Import the device class from the component that you want to support +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, + ATTR_RGB_COLOR, SUPPORT_RGB_COLOR, + Light, PLATFORM_SCHEMA) +from homeassistant.const import CONF_NAME +import homeassistant.helpers.config_validation as cv + +# Home Assistant depends on 3rd party packages for API specific code. +REQUIREMENTS = ['piglow==1.2.4'] + +_LOGGER = logging.getLogger(__name__) + +SUPPORT_PIGLOW = (SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR) + +DEFAULT_NAME = 'Piglow' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Piglow Light platform.""" + import piglow + + if subprocess.getoutput("i2cdetect -q -y 1 | grep -o 54") != '54': + _LOGGER.error("A Piglow device was not found") + return False + + name = config.get(CONF_NAME) + + # Add devices + add_devices([PiglowLight(piglow, name)]) + + +class PiglowLight(Light): + """Representation of an Piglow Light.""" + + def __init__(self, piglow, name): + """Initialize an PiglowLight.""" + self._piglow = piglow + self._name = name + self._is_on = False + self._brightness = 255 + self._rgb_color = [255, 255, 255] + + @property + def name(self): + """Return the display name of this light.""" + return self._name + + @property + def brightness(self): + """Brightness of the light (an integer in the range 1-255).""" + return self._brightness + + @property + def rgb_color(self): + """Read back the color of the light.""" + return self._rgb_color + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_PIGLOW + + @property + def is_on(self): + """Return true if light is on.""" + return self._is_on + + def turn_on(self, **kwargs): + """Instruct the light to turn on.""" + self._piglow.clear() + self._brightness = kwargs.get(ATTR_BRIGHTNESS, 255) + percent_bright = (self._brightness / 255) + + if ATTR_RGB_COLOR in kwargs: + self._rgb_color = kwargs[ATTR_RGB_COLOR] + self._piglow.red(int(self._rgb_color[0] * percent_bright)) + self._piglow.green(int(self._rgb_color[1] * percent_bright)) + self._piglow.blue(int(self._rgb_color[2] * percent_bright)) + else: + self._piglow.all(self._brightness) + self._piglow.show() + self._is_on = True + + def turn_off(self, **kwargs): + """Instruct the light to turn off.""" + self._piglow.clear() + self._piglow.show() + self._is_on = False diff --git a/requirements_all.txt b/requirements_all.txt index 272fbfd8751..7ba42c209f4 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -348,6 +348,9 @@ pexpect==4.0.1 # homeassistant.components.light.hue phue==0.9 +# homeassistant.components.light.piglow +piglow==1.2.4 + # homeassistant.components.pilight pilight==0.1.1 From cd260d89cb03aabeff0bafce95efdf5dc2a0a589 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 24 Jan 2017 10:02:17 +0100 Subject: [PATCH 119/191] Add location to attributes and option to show position on the map (sensor.iss) (#5465) [sensor.iss] Add location to attributes and option to show position on the map --- .coveragerc | 2 +- .../{sensor => binary_sensor}/iss.py | 92 ++++++++++--------- requirements_all.txt | 2 +- 3 files changed, 50 insertions(+), 46 deletions(-) rename homeassistant/components/{sensor => binary_sensor}/iss.py (56%) diff --git a/.coveragerc b/.coveragerc index ac97522450f..51ca1df2c1f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -133,6 +133,7 @@ omit = homeassistant/components/binary_sensor/concord232.py homeassistant/components/binary_sensor/flic.py homeassistant/components/binary_sensor/hikvision.py + homeassistant/components/binary_sensor/iss.py homeassistant/components/binary_sensor/rest.py homeassistant/components/browser.py homeassistant/components/camera/amcrest.py @@ -303,7 +304,6 @@ omit = homeassistant/components/sensor/hddtemp.py homeassistant/components/sensor/hp_ilo.py homeassistant/components/sensor/hydroquebec.py - homeassistant/components/sensor/iss.py homeassistant/components/sensor/imap.py homeassistant/components/sensor/imap_email_content.py homeassistant/components/sensor/influxdb.py diff --git a/homeassistant/components/sensor/iss.py b/homeassistant/components/binary_sensor/iss.py similarity index 56% rename from homeassistant/components/sensor/iss.py rename to homeassistant/components/binary_sensor/iss.py index 6d9cf4b7106..b4182557878 100644 --- a/homeassistant/components/sensor/iss.py +++ b/homeassistant/components/binary_sensor/iss.py @@ -5,34 +5,39 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.iss/ """ import logging -from datetime import timedelta, datetime +from datetime import timedelta + import requests import voluptuous as vol -from homeassistant.util import Throttle -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_NAME) -from homeassistant.helpers.entity import Entity + import homeassistant.helpers.config_validation as cv +from homeassistant.components.binary_sensor import ( + BinarySensorDevice, PLATFORM_SCHEMA) +from homeassistant.const import (CONF_NAME, ATTR_LONGITUDE, ATTR_LATITUDE) +from homeassistant.util import Throttle REQUIREMENTS = ['pyiss==1.0.1'] _LOGGER = logging.getLogger(__name__) -ATTR_ISS_VISIBLE = 'visible' ATTR_ISS_NEXT_RISE = 'next_rise' ATTR_ISS_NUMBER_PEOPLE_SPACE = 'number_of_people_in_space' +CONF_SHOW_ON_MAP = 'show_on_map' + DEFAULT_NAME = 'ISS' +DEFAULT_SENSOR_CLASS = 'visible' + MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_SHOW_ON_MAP, default=False): cv.boolean, }) def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the ISS sensor.""" - # Validate the configuration + """Set up the ISS sensor.""" if None in (hass.config.latitude, hass.config.longitude): _LOGGER.error("Latitude or longitude not set in Home Assistant config") return False @@ -45,75 +50,74 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False name = config.get(CONF_NAME) + show_on_map = config.get(CONF_SHOW_ON_MAP) - sensors = [] - sensors.append(IssSensor(iss_data, name)) - - add_devices(sensors, True) + add_devices([IssBinarySensor(iss_data, name, show_on_map)], True) -class IssSensor(Entity): - """Implementation of a ISS sensor.""" +class IssBinarySensor(BinarySensorDevice): + """Implementation of the ISS binary sensor.""" - def __init__(self, iss_data, name): + def __init__(self, iss_data, name, show): """Initialize the sensor.""" self.iss_data = iss_data self._state = None - self._attributes = {} - self._client_name = name - self._name = ATTR_ISS_VISIBLE - self._unit_of_measurement = None - self._icon = 'mdi:eye' + self._name = name + self._show_on_map = show + self.update() @property def name(self): """Return the name of the sensor.""" - return '{} {}'.format(self._client_name, self._name) + return self._name @property - def state(self): - """Return the state of the sensor.""" - return self._state + def is_on(self): + """Return true if the binary sensor is on.""" + return self.iss_data.is_above if self.iss_data else False + + @property + def sensor_class(self): + """Return the class of this sensor.""" + return DEFAULT_SENSOR_CLASS @property def device_state_attributes(self): """Return the state attributes.""" - return self._attributes - - @property - def unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement - - @property - def icon(self): - """Icon to use in the frontend, if any.""" - return self._icon + if self.iss_data: + attrs = { + ATTR_ISS_NUMBER_PEOPLE_SPACE: + self.iss_data.number_of_people_in_space, + ATTR_ISS_NEXT_RISE: self.iss_data.next_rise, + } + if self._show_on_map: + attrs[ATTR_LONGITUDE] = self.iss_data.position.get('longitude') + attrs[ATTR_LATITUDE] = self.iss_data.position.get('latitude') + else: + attrs['long'] = self.iss_data.position.get('longitude') + attrs['lat'] = self.iss_data.position.get('latitude') + return attrs def update(self): """Get the latest data from ISS API and updates the states.""" - self._state = self.iss_data.is_above - - self._attributes[ATTR_ISS_NUMBER_PEOPLE_SPACE] = \ - self.iss_data.number_of_people_in_space - delta = self.iss_data.next_rise - datetime.utcnow() - self._attributes[ATTR_ISS_NEXT_RISE] = int(delta.total_seconds() / 60) + self.iss_data.update() class IssData(object): - """Get data from the ISS.""" + """Get data from the ISS API.""" def __init__(self, latitude, longitude): """Initialize the data object.""" self.is_above = None self.next_rise = None self.number_of_people_in_space = None + self.position = None self.latitude = latitude self.longitude = longitude @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): - """Get the latest data from the ISS.""" + """Get the latest data from the ISS API.""" import pyiss try: @@ -121,7 +125,7 @@ class IssData(object): self.is_above = iss.is_ISS_above(self.latitude, self.longitude) self.next_rise = iss.next_rise(self.latitude, self.longitude) self.number_of_people_in_space = iss.number_of_people_in_space() - _LOGGER.error(self.next_rise.tzinfo) + self.position = iss.current_location() except requests.exceptions.HTTPError as error: _LOGGER.error(error) return False diff --git a/requirements_all.txt b/requirements_all.txt index 7ba42c209f4..bd86bd9e38f 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -443,7 +443,7 @@ pyhomematic==0.1.20 # homeassistant.components.device_tracker.icloud pyicloud==0.9.1 -# homeassistant.components.sensor.iss +# homeassistant.components.binary_sensor.iss pyiss==1.0.1 # homeassistant.components.sensor.lastfm From f5062b06a9892805cfe202f913dbe0e1727a910e Mon Sep 17 00:00:00 2001 From: ecksun Date: Tue, 24 Jan 2017 18:20:18 +0100 Subject: [PATCH 120/191] media_player.kodi: Add SSL config option (#5531) This readds support for https for kodi, resolves issue #5527 --- homeassistant/components/media_player/kodi.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index 790bfb3c724..acb6a6f45db 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -18,7 +18,7 @@ from homeassistant.components.media_player import ( PLATFORM_SCHEMA) from homeassistant.const import ( STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, CONF_HOST, CONF_NAME, - CONF_PORT, CONF_USERNAME, CONF_PASSWORD) + CONF_PORT, CONF_SSL, CONF_USERNAME, CONF_PASSWORD) from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv @@ -31,6 +31,7 @@ CONF_TURN_OFF_ACTION = 'turn_off_action' DEFAULT_NAME = 'Kodi' DEFAULT_PORT = 8080 DEFAULT_TIMEOUT = 5 +DEFAULT_SSL = False TURN_OFF_ACTION = [None, 'quit', 'hibernate', 'suspend', 'reboot', 'shutdown'] @@ -42,6 +43,7 @@ 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_SSL, default=DEFAULT_SSL): cv.boolean, vol.Optional(CONF_TURN_OFF_ACTION, default=None): vol.In(TURN_OFF_ACTION), vol.Inclusive(CONF_USERNAME, 'auth'): cv.string, vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string, @@ -54,6 +56,7 @@ def async_setup_platform(hass, config, async_add_entities, """Setup the Kodi platform.""" host = config.get(CONF_HOST) port = config.get(CONF_PORT) + use_encryption = config.get(CONF_SSL) if host.startswith('http://') or host.startswith('https://'): host = host.lstrip('http://').lstrip('https://') @@ -65,7 +68,7 @@ def async_setup_platform(hass, config, async_add_entities, entity = KodiDevice( hass, name=config.get(CONF_NAME), - host=host, port=port, + host=host, port=port, encryption=use_encryption, username=config.get(CONF_USERNAME), password=config.get(CONF_PASSWORD), turn_off_action=config.get(CONF_TURN_OFF_ACTION)) @@ -76,8 +79,8 @@ def async_setup_platform(hass, config, async_add_entities, class KodiDevice(MediaPlayerDevice): """Representation of a XBMC/Kodi device.""" - def __init__(self, hass, name, host, port, username=None, password=None, - turn_off_action=None): + def __init__(self, hass, name, host, port, encryption=False, username=None, + password=None, turn_off_action=None): """Initialize the Kodi device.""" import jsonrpc_async self.hass = hass @@ -94,9 +97,11 @@ class KodiDevice(MediaPlayerDevice): else: image_auth_string = "" - self._http_url = 'http://{}:{}/jsonrpc'.format(host, port) - self._image_url = 'http://{}{}:{}/image'.format( - image_auth_string, host, port) + protocol = 'https' if encryption else 'http' + + self._http_url = '{}://{}:{}/jsonrpc'.format(protocol, host, port) + self._image_url = '{}://{}{}:{}/image'.format( + protocol, image_auth_string, host, port) self._server = jsonrpc_async.Server(self._http_url, **kwargs) From b2ecaa189ab2c9918c61a24700c5871fe9a0901a Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 24 Jan 2017 19:54:14 +0100 Subject: [PATCH 121/191] [binary_sensor.arest] Fix name for sensor and shorten logger messages (#5460) * Fix name for sensor and shorten logger messages * Use variable as name if none is given --- .../components/binary_sensor/arest.py | 19 ++++---- homeassistant/components/sensor/arest.py | 20 +++----- homeassistant/components/switch/arest.py | 48 ++++++++----------- 3 files changed, 36 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/binary_sensor/arest.py b/homeassistant/components/binary_sensor/arest.py index 1c7058cd1b0..834fb490049 100644 --- a/homeassistant/components/binary_sensor/arest.py +++ b/homeassistant/components/binary_sensor/arest.py @@ -30,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the aREST binary sensor.""" + """Set up the aREST binary sensor.""" resource = config.get(CONF_RESOURCE) pin = config.get(CONF_PIN) sensor_class = config.get(CONF_SENSOR_CLASS) @@ -38,13 +38,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: response = requests.get(resource, timeout=10).json() except requests.exceptions.MissingSchema: - _LOGGER.error('Missing resource or schema in configuration. ' - 'Add http:// to your URL.') + _LOGGER.error("Missing resource or schema in configuration. " + "Add http:// to your URL") return False except requests.exceptions.ConnectionError: - _LOGGER.error('No route to device at %s. ' - 'Please check the IP address in the configuration file.', - resource) + _LOGGER.error("No route to device at %s", resource) return False arest = ArestData(resource, pin) @@ -67,10 +65,10 @@ class ArestBinarySensor(BinarySensorDevice): self.update() if self._pin is not None: - request = requests.get('{}/mode/{}/i'.format - (self._resource, self._pin), timeout=10) + request = requests.get( + '{}/mode/{}/i'.format(self._resource, self._pin), timeout=10) if request.status_code is not 200: - _LOGGER.error("Can't set mode. Is device offline?") + _LOGGER.error("Can't set mode of %s", self._resource) @property def name(self): @@ -109,5 +107,4 @@ class ArestData(object): self._resource, self._pin), timeout=10) self.data = {'state': response.json()['return_value']} except requests.exceptions.ConnectionError: - _LOGGER.error("No route to device '%s'. Is device offline?", - self._resource) + _LOGGER.error("No route to device '%s'", self._resource) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 30caa80bc53..d99240cf0d2 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -45,7 +45,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the aREST sensor.""" + """Set up the aREST sensor.""" resource = config.get(CONF_RESOURCE) var_conf = config.get(CONF_MONITORED_VARIABLES) pins = config.get(CONF_PINS) @@ -54,12 +54,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): response = requests.get(resource, timeout=10).json() except requests.exceptions.MissingSchema: _LOGGER.error("Missing resource or schema in configuration. " - "Add http:// to your URL.") + "Add http:// to your URL") return False except requests.exceptions.ConnectionError: - _LOGGER.error("No route to device at %s. " - "Please check the IP address in the configuration file.", - resource) + _LOGGER.error("No route to device at %s", resource) return False arest = ArestData(resource) @@ -75,7 +73,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: return value_template.async_render({'value': value}) except TemplateError: - _LOGGER.exception('Error parsing value') + _LOGGER.exception("Error parsing value") return value return _render @@ -91,7 +89,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): renderer = make_renderer(var_data.get(CONF_VALUE_TEMPLATE)) dev.append(ArestSensor( arest, resource, config.get(CONF_NAME, response[CONF_NAME]), - variable, variable=variable, + var_data.get(CONF_NAME, variable), variable=variable, unit_of_measurement=var_data.get(CONF_UNIT_OF_MEASUREMENT), renderer=renderer)) @@ -127,7 +125,7 @@ class ArestSensor(Entity): request = requests.get( '{}/mode/{}/i'.format(self._resource, self._pin), timeout=10) if request.status_code is not 200: - _LOGGER.error("Can't set mode. Is device offline?") + _LOGGER.error("Can't set mode of %s", self._resource) @property def name(self): @@ -184,15 +182,11 @@ class ArestData(object): response = requests.get('{}/analog/{}'.format( self._resource, self._pin[1:]), timeout=10) self.data = {'value': response.json()['return_value']} - else: - _LOGGER.error("Wrong pin naming. " - "Please check your configuration file.") except TypeError: response = requests.get('{}/digital/{}'.format( self._resource, self._pin), timeout=10) self.data = {'value': response.json()['return_value']} self.available = True except requests.exceptions.ConnectionError: - _LOGGER.error("No route to device %s. Is device offline?", - self._resource) + _LOGGER.error("No route to device %s", self._resource) self.available = False diff --git a/homeassistant/components/switch/arest.py b/homeassistant/components/switch/arest.py index 9ae33698fa4..eba05c64555 100644 --- a/homeassistant/components/switch/arest.py +++ b/homeassistant/components/switch/arest.py @@ -36,19 +36,17 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the aREST switches.""" + """Set up the aREST switches.""" resource = config.get(CONF_RESOURCE) try: response = requests.get(resource, timeout=10) except requests.exceptions.MissingSchema: _LOGGER.error("Missing resource or schema in configuration. " - "Add http:// to your URL.") + "Add http:// to your URL") return False except requests.exceptions.ConnectionError: - _LOGGER.error("No route to device at %s. " - "Please check the IP address in the configuration file.", - resource) + _LOGGER.error("No route to device at %s", resource) return False dev = [] @@ -105,16 +103,15 @@ class ArestSwitchFunction(ArestSwitchBase): '{}/{}'.format(self._resource, self._func), timeout=10) if request.status_code is not 200: - _LOGGER.error("Can't find function. Is device offline?") + _LOGGER.error("Can't find function") return try: request.json()['return_value'] except KeyError: - _LOGGER.error("No return_value received. " - "Is the function name correct.") + _LOGGER.error("No return_value received") except ValueError: - _LOGGER.error("Response invalid. Is the function name correct?") + _LOGGER.error("Response invalid") def turn_on(self, **kwargs): """Turn the device on.""" @@ -125,8 +122,8 @@ class ArestSwitchFunction(ArestSwitchBase): if request.status_code == 200: self._state = True else: - _LOGGER.error("Can't turn on function %s at %s. " - "Is device offline?", self._func, self._resource) + _LOGGER.error( + "Can't turn on function %s at %s", self._func, self._resource) def turn_off(self, **kwargs): """Turn the device off.""" @@ -137,19 +134,18 @@ class ArestSwitchFunction(ArestSwitchBase): if request.status_code == 200: self._state = False else: - _LOGGER.error("Can't turn off function %s at %s. " - "Is device offline?", self._func, self._resource) + _LOGGER.error( + "Can't turn off function %s at %s", self._func, self._resource) def update(self): """Get the latest data from aREST API and update the state.""" try: - request = requests.get('{}/{}'.format(self._resource, - self._func), timeout=10) + request = requests.get( + '{}/{}'.format(self._resource, self._func), timeout=10) self._state = request.json()['return_value'] != 0 self._available = True except requests.exceptions.ConnectionError: - _LOGGER.warning("No route to device %s. Is device offline?", - self._resource) + _LOGGER.warning("No route to device %s", self._resource) self._available = False @@ -164,7 +160,7 @@ class ArestSwitchPin(ArestSwitchBase): request = requests.get( '{}/mode/{}/o'.format(self._resource, self._pin), timeout=10) if request.status_code is not 200: - _LOGGER.error("Can't set mode. Is device offline?") + _LOGGER.error("Can't set mode") self._available = False def turn_on(self, **kwargs): @@ -174,8 +170,8 @@ class ArestSwitchPin(ArestSwitchBase): if request.status_code == 200: self._state = True else: - _LOGGER.error("Can't turn on pin %s at %s. Is device offline?", - self._pin, self._resource) + _LOGGER.error( + "Can't turn on pin %s at %s", self._pin, self._resource) def turn_off(self, **kwargs): """Turn the device off.""" @@ -184,18 +180,16 @@ class ArestSwitchPin(ArestSwitchBase): if request.status_code == 200: self._state = False else: - _LOGGER.error("Can't turn off pin %s at %s. Is device offline?", - self._pin, self._resource) + _LOGGER.error( + "Can't turn off pin %s at %s", self._pin, self._resource) def update(self): """Get the latest data from aREST API and update the state.""" try: - request = requests.get('{}/digital/{}'.format(self._resource, - self._pin), - timeout=10) + request = requests.get( + '{}/digital/{}'.format(self._resource, self._pin), timeout=10) self._state = request.json()['return_value'] != 0 self._available = True except requests.exceptions.ConnectionError: - _LOGGER.warning("No route to device %s. Is device offline?", - self._resource) + _LOGGER.warning("No route to device %s", self._resource) self._available = False From c972e90580241bc5075fe9ac9d19ca9ab11b02d9 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 24 Jan 2017 20:25:51 +0100 Subject: [PATCH 122/191] Bugfix mjpeg camera (#5539) --- homeassistant/components/camera/mjpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 6501439e24f..8d52785557b 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -71,7 +71,7 @@ class MjpegCamera(Camera): self._username = device_info.get(CONF_USERNAME) self._password = device_info.get(CONF_PASSWORD) self._mjpeg_url = device_info[CONF_MJPEG_URL] - self._still_image_url = device_info[CONF_STILL_IMAGE_URL] + self._still_image_url = device_info.get(CONF_STILL_IMAGE_URL) self._auth = None if self._username and self._password: From 92858554e683d6f7aca5a427fb9149979e9efb58 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 24 Jan 2017 20:43:36 +0100 Subject: [PATCH 123/191] Bugfix endless aiohttp streamreader (#5540) --- homeassistant/helpers/aiohttp_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 6f74493c078..2825eb9e49c 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -87,6 +87,8 @@ def async_aiohttp_proxy_stream(hass, request, stream_coro, buffer_size=102400, while True: data = yield from stream.content.read(buffer_size) + if not data: + break response.write(data) except asyncio.TimeoutError: From eb1e8ebc18afac74d882d92d5d35617d541078b7 Mon Sep 17 00:00:00 2001 From: David McNett Date: Tue, 24 Jan 2017 17:20:59 -0600 Subject: [PATCH 124/191] New version of anthemav python library (#5544) --- homeassistant/components/media_player/anthemav.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/anthemav.py b/homeassistant/components/media_player/anthemav.py index 2707a62f7bf..f53f5cf9264 100644 --- a/homeassistant/components/media_player/anthemav.py +++ b/homeassistant/components/media_player/anthemav.py @@ -17,7 +17,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['anthemav==1.1.7'] +REQUIREMENTS = ['anthemav==1.1.8'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index bd86bd9e38f..980fdcebad5 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -41,7 +41,7 @@ aiohttp_cors==0.5.0 amcrest==1.1.3 # homeassistant.components.media_player.anthemav -anthemav==1.1.7 +anthemav==1.1.8 # homeassistant.components.apcupsd apcaccess==0.0.4 From 4462431c7889efe10d073ed1681183cb1473b40e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Jan 2017 00:52:19 +0100 Subject: [PATCH 125/191] Add missing particle value and refactor attributes --- homeassistant/components/sensor/waqi.py | 49 ++++++++++++++++--------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/sensor/waqi.py b/homeassistant/components/sensor/waqi.py index af2d80a0948..73de98c0168 100644 --- a/homeassistant/components/sensor/waqi.py +++ b/homeassistant/components/sensor/waqi.py @@ -24,8 +24,10 @@ ATTR_DOMINENTPOL = 'dominentpol' ATTR_HUMIDITY = 'humidity' ATTR_NITROGEN_DIOXIDE = 'nitrogen_dioxide' ATTR_OZONE = 'ozone' -ATTR_PARTICLE = 'particle' +ATTR_PM10 = 'pm_10' +ATTR_PM2_5 = 'pm_2_5' ATTR_PRESSURE = 'pressure' +ATTR_SULFUR_DIOXIDE = 'sulfur_dioxide' ATTR_TIME = 'time' ATTRIBUTION = 'Data provided by the World Air Quality Index project' @@ -52,7 +54,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): station_filter = config.get(CONF_STATIONS) for location_name in config.get(CONF_LOCATIONS): station_ids = pwaqi.findStationCodesByCity(location_name) - _LOGGER.info('The following stations were returned: %s', station_ids) + _LOGGER.info("The following stations were returned: %s", station_ids) for station in station_ids: waqi_sensor = WaqiSensor(WaqiData(station), station) if (not station_filter) or \ @@ -107,22 +109,35 @@ class WaqiSensor(Entity): return 'AQI' @property - def state_attributes(self): + def device_state_attributes(self): """Return the state attributes of the last update.""" - try: - return { - ATTR_ATTRIBUTION: ATTRIBUTION, - ATTR_TIME: self._details.get('time'), - ATTR_HUMIDITY: self._details['iaqi'][5]['cur'], - ATTR_PRESSURE: self._details['iaqi'][4]['cur'], - ATTR_TEMPERATURE: self._details['iaqi'][3]['cur'], - ATTR_OZONE: self._details['iaqi'][1]['cur'], - ATTR_PARTICLE: self._details['iaqi'][0]['cur'], - ATTR_NITROGEN_DIOXIDE: self._details['iaqi'][2]['cur'], - ATTR_DOMINENTPOL: self._details.get('dominentpol'), - } - except (IndexError, KeyError): - return {ATTR_ATTRIBUTION: ATTRIBUTION} + attrs = {} + + if self.data is not None: + try: + attrs[ATTR_ATTRIBUTION] = ATTRIBUTION + attrs[ATTR_TIME] = self._details.get('time') + attrs[ATTR_DOMINENTPOL] = self._details.get('dominentpol') + for values in self._details['iaqi']: + if values['p'] == 'pm25': + attrs[ATTR_PM2_5] = values['cur'] + elif values['p'] == 'pm10': + attrs[ATTR_PM10] = values['cur'] + elif values['p'] == 'h': + attrs[ATTR_HUMIDITY] = values['cur'] + elif values['p'] == 'p': + attrs[ATTR_PRESSURE] = values['cur'] + elif values['p'] == 't': + attrs[ATTR_TEMPERATURE] = values['cur'] + elif values['p'] == 'o3': + attrs[ATTR_OZONE] = values['cur'] + elif values['p'] == 'no2': + attrs[ATTR_NITROGEN_DIOXIDE] = values['cur'] + elif values['p'] == 'so2': + attrs[ATTR_SULFUR_DIOXIDE] = values['cur'] + return attrs + except (IndexError, KeyError): + return {ATTR_ATTRIBUTION: ATTRIBUTION} def update(self): """Get the latest data and updates the states.""" From e53b2fe12196db639640aea24ce4a85b0f609b88 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Wed, 25 Jan 2017 02:20:18 +0200 Subject: [PATCH 126/191] [script] Only bootstrap frontend if npm installed (#5507) * [scripts] Only bootstrap frontend if npm installed * Message if not frontend dev possible * yarn --- script/bootstrap | 7 ++++++- script/bootstrap_frontend | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 4e77ba60ed5..c6d4a94be94 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,4 +6,9 @@ set -e cd "$(dirname "$0")/.." script/bootstrap_server -script/bootstrap_frontend + +if [ -x "$(command -v yarn >/dev/null)" ]; then + script/bootstrap_frontend +else + echo "Frontend development not possible without Node/yarn" +fi diff --git a/script/bootstrap_frontend b/script/bootstrap_frontend index 296f56c8f88..ed3321b1d93 100755 --- a/script/bootstrap_frontend +++ b/script/bootstrap_frontend @@ -12,10 +12,10 @@ git submodule update cd homeassistant/components/frontend/www_static/home-assistant-polymer # Install node modules -npm install +yarn install # Install bower web components. Allow to download the components as root since the user in docker is root. ./node_modules/.bin/bower install --allow-root -npm run setup_js_dev +yarn run setup_js_dev cd ../../../../.. From 2cf2dcd9ba9bd1b1c1ed754b9f382b3a6e8284a5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 24 Jan 2017 20:25:08 -0800 Subject: [PATCH 127/191] Emulated_hue: default type to Google [Breaking change] (#5549) --- homeassistant/components/emulated_hue/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index d95224c9469..038c50173f5 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -45,7 +45,7 @@ DEFAULT_EXPOSE_BY_DEFAULT = True DEFAULT_EXPOSED_DOMAINS = [ 'switch', 'light', 'group', 'input_boolean', 'media_player', 'fan' ] -DEFAULT_TYPE = TYPE_ALEXA +DEFAULT_TYPE = TYPE_GOOGLE CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -122,6 +122,10 @@ class Config(object): self.numbers = None self.cached_states = {} + if self.type == TYPE_ALEXA: + _LOGGER.warning('Alexa type is deprecated and will be removed in a' + ' future version') + # 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: From 9bd5378fe4cc4451c0f46ddd212a8717a72ea77f Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Tue, 24 Jan 2017 20:57:48 -0800 Subject: [PATCH 128/191] [component/ios] Discover notify.ios when iOS component loads (#5548) --- homeassistant/components/ios.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/ios.py b/homeassistant/components/ios.py index d83bffabc91..22f8b832b3d 100644 --- a/homeassistant/components/ios.py +++ b/homeassistant/components/ios.py @@ -244,9 +244,7 @@ def setup(hass, config): if CONFIG_FILE == {}: CONFIG_FILE[ATTR_DEVICES] = {} - # Notify needs to have discovery - # notify_config = {"notify": {CONF_PLATFORM: "ios"}} - # bootstrap.setup_component(hass, "notify", notify_config) + discovery.load_platform(hass, "notify", DOMAIN, {}, config) discovery.load_platform(hass, "sensor", DOMAIN, {}, config) From 47bbfc309c8d1fd500a40230076a8f6851a6b38a Mon Sep 17 00:00:00 2001 From: William Scanlon Date: Wed, 25 Jan 2017 00:11:18 -0500 Subject: [PATCH 129/191] Support for python-wink 1.0.0 (#5534) --- .../components/alarm_control_panel/wink.py | 68 ++++++++++++++ .../components/binary_sensor/wink.py | 91 ++++++++++++++----- homeassistant/components/light/wink.py | 2 +- homeassistant/components/sensor/wink.py | 45 +++------ homeassistant/components/switch/wink.py | 4 +- homeassistant/components/wink.py | 37 +++++--- requirements_all.txt | 4 +- 7 files changed, 177 insertions(+), 74 deletions(-) create mode 100644 homeassistant/components/alarm_control_panel/wink.py diff --git a/homeassistant/components/alarm_control_panel/wink.py b/homeassistant/components/alarm_control_panel/wink.py new file mode 100644 index 00000000000..2a600fe70a9 --- /dev/null +++ b/homeassistant/components/alarm_control_panel/wink.py @@ -0,0 +1,68 @@ +""" +Interfaces with Wink Cameras. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/alarm_control_panel.wink/ +""" +import logging + +import homeassistant.components.alarm_control_panel as alarm +from homeassistant.const import (STATE_UNKNOWN, + STATE_ALARM_DISARMED, + STATE_ALARM_ARMED_HOME, + STATE_ALARM_ARMED_AWAY) +from homeassistant.components.wink import WinkDevice + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['wink'] +STATE_ALARM_PRIVACY = 'Private' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Wink platform.""" + import pywink + + for camera in pywink.get_cameras(): + add_devices([WinkCameraDevice(camera, hass)]) + + +class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel): + """Representation a Wink camera alarm.""" + + def __init__(self, wink, hass): + """Initialize the Wink alarm.""" + WinkDevice.__init__(self, wink, hass) + + @property + def state(self): + """Return the state of the device.""" + wink_state = self.wink.state() + if wink_state == "away": + state = STATE_ALARM_ARMED_AWAY + elif wink_state == "home": + state = STATE_ALARM_DISARMED + elif wink_state == "night": + state = STATE_ALARM_ARMED_HOME + else: + state = STATE_UNKNOWN + return state + + def alarm_disarm(self, code=None): + """Send disarm command.""" + self.wink.set_mode("home") + + def alarm_arm_home(self, code=None): + """Send arm home command.""" + self.wink.set_mode("night") + + def alarm_arm_away(self, code=None): + """Send arm away command.""" + self.wink.set_mode("away") + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return { + 'private': self.wink.private() + } diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index b129b5f24d4..19ecb853536 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -8,7 +8,6 @@ at https://home-assistant.io/components/binary_sensor.wink/ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.sensor.wink import WinkDevice from homeassistant.helpers.entity import Entity -from homeassistant.loader import get_component DEPENDENCIES = ['wink'] @@ -43,6 +42,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for hub in pywink.get_hubs(): add_devices([WinkHub(hub, hass)]) + for remote in pywink.get_remotes(): + add_devices([WinkRemote(remote, hass)]) + + for button in pywink.get_buttons(): + add_devices([WinkButton(button, hass)]) + + for gang in pywink.get_gangs(): + add_devices([WinkGang(gang, hass)]) + class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): """Representation of a Wink binary sensor.""" @@ -50,33 +58,13 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity): def __init__(self, wink, hass): """Initialize the Wink binary sensor.""" super().__init__(wink, hass) - wink = get_component('wink') - self._unit_of_measurement = self.wink.UNIT + self._unit_of_measurement = self.wink.unit() self.capability = self.wink.capability() @property def is_on(self): """Return true if the binary sensor is on.""" - if self.capability == "loudness": - state = self.wink.loudness_boolean() - elif self.capability == "vibration": - state = self.wink.vibration_boolean() - elif self.capability == "brightness": - state = self.wink.brightness_boolean() - elif self.capability == "liquid_detected": - state = self.wink.liquid_boolean() - elif self.capability == "motion": - state = self.wink.motion_boolean() - elif self.capability == "presence": - state = self.wink.presence_boolean() - elif self.capability == "co_detected": - state = self.wink.co_detected_boolean() - elif self.capability == "smoke_detected": - state = self.wink.smoke_detected_boolean() - else: - state = self.wink.state() - - return state + return self.wink.state() @property def sensor_class(self): @@ -91,6 +79,11 @@ class WinkHub(WinkDevice, BinarySensorDevice, Entity): """Initialize the hub sensor.""" WinkDevice.__init__(self, wink, hass) + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self.wink.state() + @property def device_state_attributes(self): """Return the state attributes.""" @@ -99,7 +92,59 @@ class WinkHub(WinkDevice, BinarySensorDevice, Entity): 'firmware version': self.wink.firmware_version() } + +class WinkRemote(WinkDevice, BinarySensorDevice, Entity): + """Representation of a Wink Lutron Connected bulb remote.""" + + def __init(self, wink, hass): + """Initialize the hub sensor.""" + WinkDevice.__init__(self, wink, hass) + @property def is_on(self): """Return true if the binary sensor is on.""" return self.wink.state() + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return { + 'button_on_pressed': self.wink.button_on_pressed(), + 'button_off_pressed': self.wink.button_off_pressed(), + 'button_up_pressed': self.wink.button_up_pressed(), + 'button_down_pressed': self.wink.button_down_pressed() + } + + +class WinkButton(WinkDevice, BinarySensorDevice, Entity): + """Representation of a Wink Relay button.""" + + def __init(self, wink, hass): + """Initialize the hub sensor.""" + WinkDevice.__init__(self, wink, hass) + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self.wink.state() + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return { + 'pressed': self.wink.pressed(), + 'long_pressed': self.wink.long_pressed() + } + + +class WinkGang(WinkDevice, BinarySensorDevice, Entity): + """Representation of a Wink Relay gang.""" + + def __init(self, wink, hass): + """Initialize the gang sensor.""" + WinkDevice.__init__(self, wink, hass) + + @property + def is_on(self): + """Return true if the gang is connected.""" + return self.wink.state() diff --git a/homeassistant/components/light/wink.py b/homeassistant/components/light/wink.py index 1a4556ee46b..dcff4b31a5c 100644 --- a/homeassistant/components/light/wink.py +++ b/homeassistant/components/light/wink.py @@ -23,7 +23,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Wink lights.""" import pywink - add_devices(WinkLight(light, hass) for light in pywink.get_bulbs()) + add_devices(WinkLight(light, hass) for light in pywink.get_light_bulbs()) class WinkLight(WinkDevice, Light): diff --git a/homeassistant/components/sensor/wink.py b/homeassistant/components/sensor/wink.py index 379d9ac43e5..b43952e6330 100644 --- a/homeassistant/components/sensor/wink.py +++ b/homeassistant/components/sensor/wink.py @@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices([WinkSensorDevice(sensor, hass)]) for eggtray in pywink.get_eggtrays(): - add_devices([WinkEggMinder(eggtray, hass)]) + add_devices([WinkSensorDevice(eggtray, hass)]) for piggy_bank in pywink.get_piggy_banks(): try: @@ -43,53 +43,32 @@ class WinkSensorDevice(WinkDevice, Entity): super().__init__(wink, hass) wink = get_component('wink') self.capability = self.wink.capability() - if self.wink.UNIT == '°': + if self.wink.unit() == '°': self._unit_of_measurement = TEMP_CELSIUS else: - self._unit_of_measurement = self.wink.UNIT + self._unit_of_measurement = self.wink.unit() @property def state(self): """Return the state.""" state = None if self.capability == 'humidity': - if self.wink.humidity_percentage() is not None: - state = round(self.wink.humidity_percentage()) + if self.wink.state() is not None: + state = round(self.wink.state()) elif self.capability == 'temperature': - if self.wink.temperature_float() is not None: - state = round(self.wink.temperature_float(), 1) + if self.wink.state() is not None: + state = round(self.wink.state(), 1) elif self.capability == 'balance': - if self.wink.balance() is not None: - state = round(self.wink.balance() / 100, 2) + if self.wink.state() is not None: + state = round(self.wink.state() / 100, 2) elif self.capability == 'proximity': - if self.wink.proximity_float() is not None: - state = self.wink.proximity_float() + if self.wink.state() is not None: + state = self.wink.state() else: - # A sensor should never get here, anything that does - # will require an update to python-wink - logging.getLogger(__name__).error("Please report this as an issue") - state = None + state = self.wink.state() return state - @property - def available(self): - """True if connection == True.""" - return self.wink.available - @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return self._unit_of_measurement - - -class WinkEggMinder(WinkDevice, Entity): - """Representation of a Wink Egg Minder.""" - - def __init__(self, wink, hass): - """Initialize the sensor.""" - WinkDevice.__init__(self, wink, hass) - - @property - def state(self): - """Return the state.""" - return self.wink.state() diff --git a/homeassistant/components/switch/wink.py b/homeassistant/components/switch/wink.py index 22793d81f3f..5df37d87b53 100644 --- a/homeassistant/components/switch/wink.py +++ b/homeassistant/components/switch/wink.py @@ -17,10 +17,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for switch in pywink.get_switches(): add_devices([WinkToggleDevice(switch, hass)]) - for switch in pywink.get_powerstrip_outlets(): + for switch in pywink.get_powerstrips(): add_devices([WinkToggleDevice(switch, hass)]) for switch in pywink.get_sirens(): add_devices([WinkToggleDevice(switch, hass)]) + for sprinkler in pywink.get_sprinklers(): + add_devices([WinkToggleDevice(sprinkler, hass)]) class WinkToggleDevice(WinkDevice, ToggleEntity): diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index 39c4c21aaa5..2ef80c8058b 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.12.0', 'pubnubsub-handler==0.0.7'] +REQUIREMENTS = ['python-wink==1.0.0', 'pubnubsub-handler==0.0.7'] _LOGGER = logging.getLogger(__name__) @@ -28,14 +28,16 @@ CONF_CLIENT_ID = 'client_id' CONF_CLIENT_SECRET = 'client_secret' CONF_USER_AGENT = 'user_agent' CONF_OATH = 'oath' +CONF_APPSPOT = 'appspot' CONF_DEFINED_BOTH_MSG = 'Remove access token to use oath2.' CONF_MISSING_OATH_MSG = 'Missing oath2 credentials.' +CONF_TOKEN_URL = "https://winkbearertoken.appspot.com/token" CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Inclusive(CONF_EMAIL, CONF_OATH, + vol.Inclusive(CONF_EMAIL, CONF_APPSPOT, msg=CONF_MISSING_OATH_MSG): cv.string, - vol.Inclusive(CONF_PASSWORD, CONF_OATH, + vol.Inclusive(CONF_PASSWORD, CONF_APPSPOT, msg=CONF_MISSING_OATH_MSG): cv.string, vol.Inclusive(CONF_CLIENT_ID, CONF_OATH, msg=CONF_MISSING_OATH_MSG): cv.string, @@ -45,19 +47,22 @@ CONFIG_SCHEMA = vol.Schema({ msg=CONF_DEFINED_BOTH_MSG): cv.string, vol.Exclusive(CONF_ACCESS_TOKEN, CONF_OATH, msg=CONF_DEFINED_BOTH_MSG): cv.string, + vol.Exclusive(CONF_ACCESS_TOKEN, CONF_APPSPOT, + msg=CONF_DEFINED_BOTH_MSG): cv.string, vol.Optional(CONF_USER_AGENT, default=None): cv.string }) }, extra=vol.ALLOW_EXTRA) WINK_COMPONENTS = [ 'binary_sensor', 'sensor', 'light', 'switch', 'lock', 'cover', 'climate', - 'fan' + 'fan', 'alarm_control_panel' ] def setup(hass, config): """Set up the Wink component.""" import pywink + import requests from pubnubsubhandler import PubNubSubscriptionHandler user_agent = config[DOMAIN].get(CONF_USER_AGENT) @@ -66,16 +71,24 @@ def setup(hass, config): pywink.set_user_agent(user_agent) access_token = config[DOMAIN].get(CONF_ACCESS_TOKEN) + client_id = config[DOMAIN].get('client_id') if access_token: pywink.set_bearer_token(access_token) - else: + elif client_id: email = config[DOMAIN][CONF_EMAIL] password = config[DOMAIN][CONF_PASSWORD] client_id = config[DOMAIN]['client_id'] client_secret = config[DOMAIN]['client_secret'] pywink.set_wink_credentials(email, password, client_id, client_secret) + else: + email = config[DOMAIN][CONF_EMAIL] + password = config[DOMAIN][CONF_PASSWORD] + payload = {'username': email, 'password': password} + token_response = requests.post(CONF_TOKEN_URL, data=payload) + token = token_response.text.split(':')[1].split()[0].rstrip(' Date: Wed, 25 Jan 2017 00:29:34 -0500 Subject: [PATCH 130/191] Add missing dependency in emulated_hue component. (#5394) * Add missing dependency in emulated_hue component. On first startup after upgrade to 0.36, the emulated_hue componented failed to start because the http component had installed the modules it depends on, in this particular case 'aiohttp_cors' was missing. * Include dependencies for the emulated_hue web server Emulated_hue uses it's own 'web-server' component to handle hue related discovery and config, so we need to make sure the required http modules are made available before we are initialized. We don't have to depend on the home-assistant http/api component because we do not need to have the frontend to be initialized to handle emulated_hue, so we can just import in the same set of requirements as the http component. * Fix linting error --- homeassistant/components/emulated_hue/__init__.py | 1 + requirements_all.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index 038c50173f5..5d6d6d0e61d 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -14,6 +14,7 @@ from homeassistant import util from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, ) +from homeassistant.components.http import REQUIREMENTS # NOQA from homeassistant.components.http import HomeAssistantWSGI import homeassistant.helpers.config_validation as cv from .hue_api import ( diff --git a/requirements_all.txt b/requirements_all.txt index 34037293a3b..cd47bf80753 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -33,6 +33,7 @@ SoCo==0.12 # homeassistant.components.notify.twitter TwitterAPI==2.4.3 +# homeassistant.components.emulated_hue # homeassistant.components.http aiohttp_cors==0.5.0 From 59cad0f6ef0f9789e7e5d617f7bd8035b9fccb7a Mon Sep 17 00:00:00 2001 From: snagytx Date: Tue, 24 Jan 2017 23:35:12 -0600 Subject: [PATCH 131/191] add a small sleep before reading the rpi-gpio sensor (#5446) * add a small sleep before reading the sensor The read of the sensor might be incorrect if it's read too soon after the setup_input call. It might be isolated to my case where I rely on the the PI internal PULL, but once I added this sleep I never get false initial read. If you think this change is appropriate please merge it. * Update rpi_gpio.py * Update rpi_gpio.py * Update rpi_gpio.py --- homeassistant/components/binary_sensor/rpi_gpio.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/binary_sensor/rpi_gpio.py b/homeassistant/components/binary_sensor/rpi_gpio.py index 03978ac625b..eaf9ee737e5 100644 --- a/homeassistant/components/binary_sensor/rpi_gpio.py +++ b/homeassistant/components/binary_sensor/rpi_gpio.py @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): for port_num, port_name in ports.items(): binary_sensors.append(RPiGPIOBinarySensor( port_name, port_num, pull_mode, bouncetime, invert_logic)) - add_devices(binary_sensors) + add_devices(binary_sensors, True) class RPiGPIOBinarySensor(BinarySensorDevice): @@ -65,9 +65,9 @@ class RPiGPIOBinarySensor(BinarySensorDevice): self._pull_mode = pull_mode self._bouncetime = bouncetime self._invert_logic = invert_logic + self._state = None rpi_gpio.setup_input(self._port, self._pull_mode) - self._state = rpi_gpio.read_input(self._port) def read_gpio(port): """Read state from GPIO.""" @@ -90,3 +90,7 @@ class RPiGPIOBinarySensor(BinarySensorDevice): def is_on(self): """Return the state of the entity.""" return self._state != self._invert_logic + + def update(self): + """Update the GPIO state.""" + self._state = rpi_gpio.read_input(self._port) From 3d081c2564694a866de6efb6a17aa715b68c8129 Mon Sep 17 00:00:00 2001 From: happyleavesaoc Date: Wed, 25 Jan 2017 00:36:18 -0500 Subject: [PATCH 132/191] bump dep version; add name conf (#5451) --- homeassistant/components/sensor/usps.py | 14 +++++++++----- requirements_all.txt | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/usps.py b/homeassistant/components/sensor/usps.py index 0bc7f6cbd5a..680b4e4142d 100644 --- a/homeassistant/components/sensor/usps.py +++ b/homeassistant/components/sensor/usps.py @@ -11,14 +11,15 @@ from datetime import timedelta import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, ATTR_ATTRIBUTION +from homeassistant.const import (CONF_NAME, CONF_USERNAME, CONF_PASSWORD, + ATTR_ATTRIBUTION) from homeassistant.helpers.entity import Entity from homeassistant.util import slugify from homeassistant.util import Throttle from homeassistant.util.dt import now, parse_datetime import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['myusps==1.0.1'] +REQUIREMENTS = ['myusps==1.0.2'] _LOGGER = logging.getLogger(__name__) @@ -30,6 +31,7 @@ STATUS_DELIVERED = 'delivered' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_UPDATE_INTERVAL, default=timedelta(seconds=1800)): ( vol.All(cv.time_period, cv.positive_timedelta)), }) @@ -48,16 +50,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.exception('Could not connect to My USPS') return False - add_devices([USPSSensor(session, config.get(CONF_UPDATE_INTERVAL))]) + add_devices([USPSSensor(session, config.get(CONF_NAME), + config.get(CONF_UPDATE_INTERVAL))]) class USPSSensor(Entity): """USPS Sensor.""" - def __init__(self, session, interval): + def __init__(self, session, name, interval): """Initialize the sensor.""" import myusps self._session = session + self._name = name self._profile = myusps.get_profile(session) self._attributes = None self._state = None @@ -67,7 +71,7 @@ class USPSSensor(Entity): @property def name(self): """Return the name of the sensor.""" - return self._profile.get('address') + return self._name or self._profile.get('address') @property def state(self): diff --git a/requirements_all.txt b/requirements_all.txt index cd47bf80753..e7e54ada716 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -317,7 +317,7 @@ mficlient==0.3.0 miflora==0.1.14 # homeassistant.components.sensor.usps -myusps==1.0.1 +myusps==1.0.2 # homeassistant.components.discovery netdisco==0.8.1 From b57f5728c58f811cd35a5de46a85362162b9d4a1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 25 Jan 2017 06:50:10 +0100 Subject: [PATCH 133/191] [image_processing/microsoft_face_identify] face recognition for automation (#5472) * [image_processing/microsoft_face_verify] face recognition for automation * Add platform for microsoft face identify * add unittest for demo * Add unittest for platform --- .../components/image_processing/__init__.py | 8 + .../components/image_processing/demo.py | 42 +- .../microsoft_face_identify.py | 191 +++++++++ .../image_processing/openalpr_cloud.py | 3 - .../image_processing/openalpr_local.py | 8 - homeassistant/components/microsoft_face.py | 388 ++++++++++++++++++ homeassistant/components/services.yaml | 69 +++- .../components/image_processing/test_init.py | 61 +++ .../test_microsoft_face_identify.py | 163 ++++++++ tests/components/test_microsoft_face.py | 263 ++++++++++++ .../microsoft_face_create_person.json | 3 + tests/fixtures/microsoft_face_detect.json | 27 ++ tests/fixtures/microsoft_face_identify.json | 20 + .../fixtures/microsoft_face_persongroups.json | 12 + tests/fixtures/microsoft_face_persons.json | 21 + 15 files changed, 1259 insertions(+), 20 deletions(-) create mode 100644 homeassistant/components/image_processing/microsoft_face_identify.py create mode 100644 homeassistant/components/microsoft_face.py create mode 100644 tests/components/image_processing/test_microsoft_face_identify.py create mode 100644 tests/components/test_microsoft_face.py create mode 100644 tests/fixtures/microsoft_face_create_person.json create mode 100644 tests/fixtures/microsoft_face_detect.json create mode 100644 tests/fixtures/microsoft_face_identify.json create mode 100644 tests/fixtures/microsoft_face_persongroups.json create mode 100644 tests/fixtures/microsoft_face_persons.json diff --git a/homeassistant/components/image_processing/__init__.py b/homeassistant/components/image_processing/__init__.py index 0d28fe4c605..8e59ba53958 100644 --- a/homeassistant/components/image_processing/__init__.py +++ b/homeassistant/components/image_processing/__init__.py @@ -36,6 +36,7 @@ CONF_SOURCE = 'source' CONF_CONFIDENCE = 'confidence' DEFAULT_TIMEOUT = 10 +DEFAULT_CONFIDENCE = 80 SOURCE_SCHEMA = vol.Schema({ vol.Required(CONF_ENTITY_ID): cv.entity_id, @@ -44,6 +45,8 @@ SOURCE_SCHEMA = vol.Schema({ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ vol.Optional(CONF_SOURCE): vol.All(cv.ensure_list, [SOURCE_SCHEMA]), + vol.Optional(CONF_CONFIDENCE, default=DEFAULT_CONFIDENCE): + vol.All(vol.Coerce(float), vol.Range(min=0, max=100)) }) SERVICE_SCAN_SCHEMA = vol.Schema({ @@ -95,6 +98,11 @@ class ImageProcessingEntity(Entity): """Return camera entity id from process pictures.""" return None + @property + def confidence(self): + """Return minimum confidence for do some things.""" + return None + def process_image(self, image): """Process image.""" raise NotImplementedError() diff --git a/homeassistant/components/image_processing/demo.py b/homeassistant/components/image_processing/demo.py index 8ba835e8df0..62b1f8bee9b 100644 --- a/homeassistant/components/image_processing/demo.py +++ b/homeassistant/components/image_processing/demo.py @@ -8,13 +8,17 @@ https://home-assistant.io/components/demo/ from homeassistant.components.image_processing import ImageProcessingEntity from homeassistant.components.image_processing.openalpr_local import ( ImageProcessingAlprEntity) +from homeassistant.components.image_processing.microsoft_face_identify import ( + ImageProcessingFaceIdentifyEntity) def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the demo image_processing platform.""" add_devices([ DemoImageProcessing('camera.demo_camera', "Demo"), - DemoImageProcessingAlpr('camera.demo_camera', "Demo Alpr") + DemoImageProcessingAlpr('camera.demo_camera', "Demo Alpr"), + DemoImageProcessingFaceIdentify( + 'camera.demo_camera', "Demo Face Identify") ]) @@ -82,3 +86,39 @@ class DemoImageProcessingAlpr(ImageProcessingAlprEntity): } self.process_plates(demo_data, 1) + + +class DemoImageProcessingFaceIdentify(ImageProcessingFaceIdentifyEntity): + """Demo face identify image processing entity.""" + + def __init__(self, camera_entity, name): + """Initialize demo alpr.""" + super().__init__() + + self._name = name + self._camera = camera_entity + + @property + def camera_entity(self): + """Return camera entity id from process pictures.""" + return self._camera + + @property + def confidence(self): + """Return minimum confidence for send events.""" + return 80 + + @property + def name(self): + """Return the name of the entity.""" + return self._name + + def process_image(self, image): + """Process image.""" + demo_data = { + 'Hans': 98.34, + 'Helena': 82.53, + 'Luna': 62.53, + } + + self.process_faces(demo_data, 4) diff --git a/homeassistant/components/image_processing/microsoft_face_identify.py b/homeassistant/components/image_processing/microsoft_face_identify.py new file mode 100644 index 00000000000..c8cb6fc4080 --- /dev/null +++ b/homeassistant/components/image_processing/microsoft_face_identify.py @@ -0,0 +1,191 @@ +""" +Component that will help set the microsoft face for verify processing. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/image_processing.microsoft_face_identify/ +""" +import asyncio +import logging + +import voluptuous as vol + +from homeassistant.core import split_entity_id, callback +from homeassistant.const import STATE_UNKNOWN +from homeassistant.exceptions import HomeAssistantError +from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE +from homeassistant.components.image_processing import ( + PLATFORM_SCHEMA, ImageProcessingEntity, CONF_CONFIDENCE, CONF_SOURCE, + CONF_ENTITY_ID, CONF_NAME, ATTR_ENTITY_ID, ATTR_CONFIDENCE) +import homeassistant.helpers.config_validation as cv +from homeassistant.util.async import run_callback_threadsafe + +DEPENDENCIES = ['microsoft_face'] + +_LOGGER = logging.getLogger(__name__) + +EVENT_IDENTIFY_FACE = 'identify_face' + +ATTR_NAME = 'name' +ATTR_TOTAL_FACES = 'total_faces' +ATTR_KNOWN_FACES = 'known_faces' +CONF_GROUP = 'group' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_GROUP): cv.slugify, +}) + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + """Set up the microsoft face identify platform.""" + api = hass.data[DATA_MICROSOFT_FACE] + face_group = config[CONF_GROUP] + confidence = config[CONF_CONFIDENCE] + + entities = [] + for camera in config[CONF_SOURCE]: + entities.append(MicrosoftFaceIdentifyEntity( + camera[CONF_ENTITY_ID], api, face_group, confidence, + camera.get(CONF_NAME) + )) + + yield from async_add_devices(entities) + + +class ImageProcessingFaceIdentifyEntity(ImageProcessingEntity): + """Base entity class for face identify/verify image processing.""" + + def __init__(self): + """Initialize base face identify/verify entity.""" + self.known_faces = {} # last scan data + self.total_faces = 0 # face count + + @property + def state(self): + """Return the state of the entity.""" + confidence = 0 + face_name = STATE_UNKNOWN + + # search high verify face + for i_name, i_co in self.known_faces.items(): + if i_co > confidence: + confidence = i_co + face_name = i_name + return face_name + + @property + def state_attributes(self): + """Return device specific state attributes.""" + attr = { + ATTR_KNOWN_FACES: self.known_faces, + ATTR_TOTAL_FACES: self.total_faces, + } + + return attr + + def process_faces(self, known, total): + """Send event with detected faces and store data.""" + run_callback_threadsafe( + self.hass.loop, self.async_process_faces, known, total + ).result() + + @callback + def async_process_faces(self, known, total): + """Send event with detected faces and store data. + + known are a dict in follow format: + { 'name': confidence } + + This method must be run in the event loop. + """ + detect = {name: confidence for name, confidence in known.items() + if confidence >= self.confidence} + + # send events + for name, confidence in detect.items(): + self.hass.async_add_job( + self.hass.bus.async_fire, EVENT_IDENTIFY_FACE, { + ATTR_NAME: name, + ATTR_ENTITY_ID: self.entity_id, + ATTR_CONFIDENCE: confidence, + } + ) + + # update entity store + self.known_faces = detect + self.total_faces = total + + +class MicrosoftFaceIdentifyEntity(ImageProcessingFaceIdentifyEntity): + """Microsoft face api entity for identify.""" + + def __init__(self, camera_entity, api, face_group, confidence, name=None): + """Initialize openalpr local api.""" + super().__init__() + + self._api = api + self._camera = camera_entity + self._confidence = confidence + self._face_group = face_group + + if name: + self._name = name + else: + self._name = "MicrosoftFace {0}".format( + split_entity_id(camera_entity)[1]) + + @property + def confidence(self): + """Return minimum confidence for send events.""" + return self._confidence + + @property + def camera_entity(self): + """Return camera entity id from process pictures.""" + return self._camera + + @property + def name(self): + """Return the name of the entity.""" + return self._name + + @asyncio.coroutine + def async_process_image(self, image): + """Process image. + + This method is a coroutine. + """ + detect = None + try: + face_data = yield from self._api.call_api( + 'post', 'detect', image, binary=True) + + face_ids = [data['faceId'] for data in face_data] + + detect = yield from self._api.call_api( + 'post', 'identify', + {'faceIds': face_ids, 'personGroupId': self._face_group}) + + except HomeAssistantError as err: + _LOGGER.error("Can't process image on microsoft face: %s", err) + return + + # parse data + knwon_faces = {} + total = 0 + for face in detect: + total += 1 + if len(face['candidates']) == 0: + continue + + data = face['candidates'][0] + name = '' + for s_name, s_id in self._api.store[self._face_group].items(): + if data['personId'] == s_id: + name = s_name + break + + knwon_faces[name] = data['confidence'] * 100 + + # process data + self.async_process_faces(knwon_faces, total) diff --git a/homeassistant/components/image_processing/openalpr_cloud.py b/homeassistant/components/image_processing/openalpr_cloud.py index d17291df07f..7c7d26ce724 100644 --- a/homeassistant/components/image_processing/openalpr_cloud.py +++ b/homeassistant/components/image_processing/openalpr_cloud.py @@ -41,14 +41,11 @@ OPENALPR_REGIONS = [ ] CONF_REGION = 'region' -DEFAULT_CONFIDENCE = 80 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): cv.string, vol.Required(CONF_REGION): vol.All(vol.Lower, vol.In(OPENALPR_REGIONS)), - vol.Optional(CONF_CONFIDENCE, default=DEFAULT_CONFIDENCE): - vol.All(vol.Coerce(float), vol.Range(min=0, max=100)) }) diff --git a/homeassistant/components/image_processing/openalpr_local.py b/homeassistant/components/image_processing/openalpr_local.py index 65c2a683341..319f14c1f3d 100644 --- a/homeassistant/components/image_processing/openalpr_local.py +++ b/homeassistant/components/image_processing/openalpr_local.py @@ -50,14 +50,11 @@ CONF_REGION = 'region' CONF_ALPR_BIN = 'alp_bin' DEFAULT_BINARY = 'alpr' -DEFAULT_CONFIDENCE = 80 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_REGION): vol.All(vol.Lower, vol.In(OPENALPR_REGIONS)), vol.Optional(CONF_ALPR_BIN, default=DEFAULT_BINARY): cv.string, - vol.Optional(CONF_CONFIDENCE, default=DEFAULT_CONFIDENCE): - vol.All(vol.Coerce(float), vol.Range(min=0, max=100)) }) @@ -84,11 +81,6 @@ class ImageProcessingAlprEntity(ImageProcessingEntity): self.plates = {} # last scan data self.vehicles = 0 # vehicles count - @property - def confidence(self): - """Return minimum confidence for send events.""" - return None - @property def state(self): """Return the state of the entity.""" diff --git a/homeassistant/components/microsoft_face.py b/homeassistant/components/microsoft_face.py new file mode 100644 index 00000000000..3f4483e83c3 --- /dev/null +++ b/homeassistant/components/microsoft_face.py @@ -0,0 +1,388 @@ +""" +Support for microsoft face recognition. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/microsoft_face/ +""" +import asyncio +import json +import logging +import os + +import aiohttp +from aiohttp.hdrs import CONTENT_TYPE +import async_timeout +import voluptuous as vol + +from homeassistant.const import CONF_API_KEY, CONF_TIMEOUT +from homeassistant.config import load_yaml_config_file +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.loader import get_component +from homeassistant.util import slugify + +DOMAIN = 'microsoft_face' +DEPENDENCIES = ['camera'] + +_LOGGER = logging.getLogger(__name__) + +FACE_API_URL = "https://westus.api.cognitive.microsoft.com/face/v1.0/{0}" + +DATA_MICROSOFT_FACE = 'microsoft_face' + +SERVICE_CREATE_GROUP = 'create_group' +SERVICE_DELETE_GROUP = 'delete_group' +SERVICE_TRAIN_GROUP = 'train_group' +SERVICE_CREATE_PERSON = 'create_person' +SERVICE_DELETE_PERSON = 'delete_person' +SERVICE_FACE_PERSON = 'face_person' + +ATTR_GROUP = 'group' +ATTR_PERSON = 'person' +ATTR_CAMERA_ENTITY = 'camera_entity' +ATTR_NAME = 'name' + +DEFAULT_TIMEOUT = 10 + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_API_KEY): cv.string, + vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, + }), +}, extra=vol.ALLOW_EXTRA) + +SCHEMA_GROUP_SERVICE = vol.Schema({ + vol.Required(ATTR_NAME): cv.string, +}) + +SCHEMA_PERSON_SERVICE = SCHEMA_GROUP_SERVICE.extend({ + vol.Required(ATTR_GROUP): cv.slugify, +}) + +SCHEMA_FACE_SERVICE = vol.Schema({ + vol.Required(ATTR_PERSON): cv.string, + vol.Required(ATTR_GROUP): cv.slugify, + vol.Required(ATTR_CAMERA_ENTITY): cv.entity_id, +}) + +SCHEMA_TRAIN_SERVICE = vol.Schema({ + vol.Required(ATTR_GROUP): cv.slugify, +}) + + +def create_group(hass, name): + """Create a new person group.""" + data = {ATTR_NAME: name} + hass.services.call(DOMAIN, SERVICE_CREATE_GROUP, data) + + +def delete_group(hass, name): + """Delete a person group.""" + data = {ATTR_NAME: name} + hass.services.call(DOMAIN, SERVICE_DELETE_GROUP, data) + + +def train_group(hass, group): + """Train a person group.""" + data = {ATTR_GROUP: group} + hass.services.call(DOMAIN, SERVICE_TRAIN_GROUP, data) + + +def create_person(hass, group, name): + """Create a person in a group.""" + data = {ATTR_GROUP: group, ATTR_NAME: name} + hass.services.call(DOMAIN, SERVICE_CREATE_PERSON, data) + + +def delete_person(hass, group, name): + """Delete a person in a group.""" + data = {ATTR_GROUP: group, ATTR_NAME: name} + hass.services.call(DOMAIN, SERVICE_DELETE_PERSON, data) + + +def face_person(hass, group, person, camera_entity): + """Add a new face picture to a person.""" + data = {ATTR_GROUP: group, ATTR_PERSON: person, + ATTR_CAMERA_ENTITY: camera_entity} + hass.services.call(DOMAIN, SERVICE_FACE_PERSON, data) + + +@asyncio.coroutine +def async_setup(hass, config): + """Setup microsoft face.""" + entities = {} + face = MicrosoftFace( + hass, + config[DOMAIN].get(CONF_API_KEY), + config[DOMAIN].get(CONF_TIMEOUT), + entities + ) + + try: + # read exists group/person from cloud and create entities + yield from face.update_store() + except HomeAssistantError as err: + _LOGGER.error("Can't load data from face api: %s", err) + return False + + hass.data[DATA_MICROSOFT_FACE] = face + + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, + os.path.join(os.path.dirname(__file__), 'services.yaml')) + + @asyncio.coroutine + def async_create_group(service): + """Create a new person group.""" + name = service.data[ATTR_NAME] + g_id = slugify(name) + + try: + yield from face.call_api( + 'put', "persongroups/{0}".format(g_id), {'name': name}) + face.store[g_id] = {} + + entities[g_id] = MicrosoftFaceGroupEntity(hass, face, g_id, name) + yield from entities[g_id].async_update_ha_state() + except HomeAssistantError as err: + _LOGGER.error("Can't create group '%s' with error: %s", g_id, err) + + hass.services.async_register( + DOMAIN, SERVICE_CREATE_GROUP, async_create_group, + descriptions[DOMAIN].get(SERVICE_CREATE_GROUP), + schema=SCHEMA_GROUP_SERVICE) + + @asyncio.coroutine + def async_delete_group(service): + """Delete a person group.""" + g_id = slugify(service.data[ATTR_NAME]) + + try: + yield from face.call_api('delete', "persongroups/{0}".format(g_id)) + face.store.pop(g_id) + + entity = entities.pop(g_id) + yield from entity.async_remove() + except HomeAssistantError as err: + _LOGGER.error("Can't delete group '%s' with error: %s", g_id, err) + + hass.services.async_register( + DOMAIN, SERVICE_DELETE_GROUP, async_delete_group, + descriptions[DOMAIN].get(SERVICE_DELETE_GROUP), + schema=SCHEMA_GROUP_SERVICE) + + @asyncio.coroutine + def async_train_group(service): + """Train a person group.""" + g_id = service.data[ATTR_GROUP] + + try: + yield from face.call_api( + 'post', "persongroups/{0}/train".format(g_id)) + except HomeAssistantError as err: + _LOGGER.error("Can't train group '%s' with error: %s", g_id, err) + + hass.services.async_register( + DOMAIN, SERVICE_TRAIN_GROUP, async_train_group, + descriptions[DOMAIN].get(SERVICE_TRAIN_GROUP), + schema=SCHEMA_TRAIN_SERVICE) + + @asyncio.coroutine + def async_create_person(service): + """Create a person in a group.""" + name = service.data[ATTR_NAME] + g_id = service.data[ATTR_GROUP] + + try: + user_data = yield from face.call_api( + 'post', "persongroups/{0}/persons".format(g_id), {'name': name} + ) + + face.store[g_id][name] = user_data['personId'] + yield from entities[g_id].async_update_ha_state() + except HomeAssistantError as err: + _LOGGER.error("Can't create person '%s' with error: %s", name, err) + + hass.services.async_register( + DOMAIN, SERVICE_CREATE_PERSON, async_create_person, + descriptions[DOMAIN].get(SERVICE_CREATE_PERSON), + schema=SCHEMA_PERSON_SERVICE) + + @asyncio.coroutine + def async_delete_person(service): + """Delete a person in a group.""" + name = service.data[ATTR_NAME] + g_id = service.data[ATTR_GROUP] + p_id = face.store[g_id].get(name) + + try: + yield from face.call_api( + 'delete', "persongroups/{0}/persons/{1}".format(g_id, p_id)) + + face.store[g_id].pop(name) + yield from entities[g_id].async_update_ha_state() + except HomeAssistantError as err: + _LOGGER.error("Can't delete person '%s' with error: %s", p_id, err) + + hass.services.async_register( + DOMAIN, SERVICE_DELETE_PERSON, async_delete_person, + descriptions[DOMAIN].get(SERVICE_DELETE_PERSON), + schema=SCHEMA_PERSON_SERVICE) + + @asyncio.coroutine + def async_face_person(service): + """Add a new face picture to a person.""" + g_id = service.data[ATTR_GROUP] + p_id = face.store[g_id].get(service.data[ATTR_PERSON]) + + camera_entity = service.data[ATTR_CAMERA_ENTITY] + camera = get_component('camera') + + try: + image = yield from camera.async_get_image(hass, camera_entity) + + yield from face.call_api( + 'post', + "persongroups/{0}/persons/{1}/persistedFaces".format( + g_id, p_id), + image, + binary=True + ) + except HomeAssistantError as err: + _LOGGER.error("Can't delete person '%s' with error: %s", p_id, err) + + hass.services.async_register( + DOMAIN, SERVICE_FACE_PERSON, async_face_person, + descriptions[DOMAIN].get(SERVICE_FACE_PERSON), + schema=SCHEMA_FACE_SERVICE) + + return True + + +class MicrosoftFaceGroupEntity(Entity): + """Person-Group state/data Entity.""" + + def __init__(self, hass, api, g_id, name): + """Initialize person/group entity.""" + self.hass = hass + self._api = api + self._id = g_id + self._name = name + + @property + def name(self): + """Return the name of the entity.""" + return self._name + + @property + def entity_id(self): + """Return entity id.""" + return "{0}.{1}".format(DOMAIN, self._id) + + @property + def state(self): + """Return the state of the entity.""" + return len(self._api.store[self._id]) + + @property + def should_poll(self): + """Return True if entity has to be polled for state.""" + return False + + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + attr = {} + for name, p_id in self._api.store[self._id].items(): + attr[name] = p_id + + return attr + + +class MicrosoftFace(object): + """Microsoft Face api for HomeAssistant.""" + + def __init__(self, hass, api_key, timeout, entities): + """Initialize Microsoft Face api.""" + self.hass = hass + self.websession = async_get_clientsession(hass) + self.timeout = timeout + self._api_key = api_key + self._store = {} + self._entities = entities + + @property + def store(self): + """Store group/person data and IDs.""" + return self._store + + @asyncio.coroutine + def update_store(self): + """Load all group/person data into local store.""" + groups = yield from self.call_api('get', 'persongroups') + + tasks = [] + for group in groups: + g_id = group['personGroupId'] + self._store[g_id] = {} + self._entities[g_id] = MicrosoftFaceGroupEntity( + self.hass, self, g_id, group['name']) + + persons = yield from self.call_api( + 'get', "persongroups/{0}/persons".format(g_id)) + + for person in persons: + self._store[g_id][person['name']] = person['personId'] + + tasks.append(self._entities[g_id].async_update_ha_state()) + + if tasks: + yield from asyncio.wait(tasks, loop=self.hass.loop) + + @asyncio.coroutine + def call_api(self, method, function, data=None, binary=False, + params=None): + """Make a api call.""" + headers = {"Ocp-Apim-Subscription-Key": self._api_key} + url = FACE_API_URL.format(function) + + payload = None + if binary: + headers[CONTENT_TYPE] = "application/octet-stream" + payload = data + else: + headers[CONTENT_TYPE] = "application/json" + if data is not None: + payload = json.dumps(data).encode() + else: + payload = None + + response = None + try: + with async_timeout.timeout(self.timeout, loop=self.hass.loop): + response = yield from getattr(self.websession, method)( + url, data=payload, headers=headers, params=params) + + answer = yield from response.json() + _LOGGER.debug("Read from microsoft face api: %s", answer) + if response.status == 200: + return answer + + _LOGGER.warning("Error %d microsoft face api %s", + response.status, response.url) + raise HomeAssistantError(answer['error']['message']) + + except (aiohttp.errors.ClientError, + aiohttp.errors.ClientDisconnectedError): + _LOGGER.warning("Can't connect to microsoft face api") + + except asyncio.TimeoutError: + _LOGGER.warning("Timeout from microsoft face api %s", response.url) + + finally: + if response is not None: + yield from response.release() + + raise HomeAssistantError("Network error on microsoft face api.") diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index e42cd56cd8c..dea38cb77f8 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -133,17 +133,70 @@ homematic: reconnect: description: Reconnect to all Homematic Hubs. -openalpr: - scan: - description: Scan immediately a device. +microsoft_face: + create_group: + description: Create a new person group. fields: - entity_id: - description: Name(s) of entities to scan - example: 'openalpr.garage' + name: + description: Name of the group + example: 'family' - restart: - description: Restart ffmpeg process of device. + delete_group: + description: Delete a new person group. + + fields: + name: + description: Name of the group + example: 'family' + + train_group: + description: Train a person group. + + fields: + name: + description: Name of the group + example: 'family' + + create_person: + description: Create a new person in the group. + + fields: + name: + description: Name of the person + example: 'Hans' + + group: + description: Name of the group + example: 'family' + + delete_person: + description: Delete a person in the group. + + fields: + name: + description: Name of the person + example: 'Hans' + + group: + description: Name of the group + example: 'family' + + face_person: + description: Add a new picture to a person. + + fields: + name: + description: Name of the person + example: 'Hans' + + group: + description: Name of the group + example: 'family' + + camera_entity: + description: Camera to take a picture + example: camera.door verisure: capture_smartcam: diff --git a/tests/components/image_processing/test_init.py b/tests/components/image_processing/test_init.py index c94064969f1..77cfd19bf92 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -213,3 +213,64 @@ class TestImageProcessingAlpr(object): assert event_data[0]['plate'] == 'AC3829' assert event_data[0]['confidence'] == 98.3 assert event_data[0]['entity_id'] == 'image_processing.demo_alpr' + + +class TestImageProcessingFaceIdentify(object): + """Test class for image processing.""" + + def setup_method(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + config = { + ip.DOMAIN: { + 'platform': 'demo' + }, + 'camera': { + 'platform': 'demo' + }, + } + + with patch('homeassistant.components.image_processing.demo.' + 'DemoImageProcessingFaceIdentify.should_poll', + new_callable=PropertyMock(return_value=False)): + setup_component(self.hass, ip.DOMAIN, config) + + state = self.hass.states.get('camera.demo_camera') + self.url = "{0}{1}".format( + self.hass.config.api.base_url, + state.attributes.get(ATTR_ENTITY_PICTURE)) + + self.face_events = [] + + @callback + def mock_face_event(event): + """Mock event.""" + self.face_events.append(event) + + self.hass.bus.listen('identify_face', mock_face_event) + + def teardown_method(self): + """Stop everything that was started.""" + self.hass.stop() + + def test_face_event_call(self, aioclient_mock): + """Setup and scan a picture and test faces from event.""" + aioclient_mock.get(self.url, content=b'image') + + ip.scan(self.hass, entity_id='image_processing.demo_face_identify') + self.hass.block_till_done() + + state = self.hass.states.get('image_processing.demo_face_identify') + + assert len(self.face_events) == 2 + assert state.state == 'Hans' + assert state.attributes['total_faces'] == 4 + + event_data = [event.data for event in self.face_events if + event.data.get('name') == 'Hans'] + assert len(event_data) == 1 + assert event_data[0]['name'] == 'Hans' + assert event_data[0]['confidence'] == 98.34 + assert event_data[0]['entity_id'] == \ + 'image_processing.demo_face_identify' diff --git a/tests/components/image_processing/test_microsoft_face_identify.py b/tests/components/image_processing/test_microsoft_face_identify.py new file mode 100644 index 00000000000..8d75f6ff1d3 --- /dev/null +++ b/tests/components/image_processing/test_microsoft_face_identify.py @@ -0,0 +1,163 @@ +"""The tests for the microsoft face identify platform.""" +from unittest.mock import patch, PropertyMock + +from homeassistant.core import callback +from homeassistant.const import ATTR_ENTITY_PICTURE +from homeassistant.bootstrap import setup_component +import homeassistant.components.image_processing as ip +import homeassistant.components.microsoft_face as mf + +from tests.common import ( + get_test_home_assistant, assert_setup_component, load_fixture, mock_coro) + + +class TestMicrosoftFaceIdentifySetup(object): + """Test class for image processing.""" + + def setup_method(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + def teardown_method(self): + """Stop everything that was started.""" + self.hass.stop() + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_setup_platform(self, store_mock): + """Setup platform with one entity.""" + config = { + ip.DOMAIN: { + 'platform': 'microsoft_face_identify', + 'source': { + 'entity_id': 'camera.demo_camera' + }, + 'group': 'Test Group1', + }, + 'camera': { + 'platform': 'demo' + }, + mf.DOMAIN: { + 'api_key': '12345678abcdef6', + } + } + + with assert_setup_component(1, ip.DOMAIN): + setup_component(self.hass, ip.DOMAIN, config) + + assert self.hass.states.get( + 'image_processing.microsoftface_demo_camera') + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_setup_platform_name(self, store_mock): + """Setup platform with one entity and set name.""" + config = { + ip.DOMAIN: { + 'platform': 'microsoft_face_identify', + 'source': { + 'entity_id': 'camera.demo_camera', + 'name': 'test local' + }, + 'group': 'Test Group1', + }, + 'camera': { + 'platform': 'demo' + }, + mf.DOMAIN: { + 'api_key': '12345678abcdef6', + } + } + + with assert_setup_component(1, ip.DOMAIN): + setup_component(self.hass, ip.DOMAIN, config) + + assert self.hass.states.get('image_processing.test_local') + + +class TestMicrosoftFaceIdentify(object): + """Test class for image processing.""" + + def setup_method(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + self.config = { + ip.DOMAIN: { + 'platform': 'microsoft_face_identify', + 'source': { + 'entity_id': 'camera.demo_camera', + 'name': 'test local' + }, + 'group': 'Test Group1', + }, + 'camera': { + 'platform': 'demo' + }, + mf.DOMAIN: { + 'api_key': '12345678abcdef6', + } + } + + def teardown_method(self): + """Stop everything that was started.""" + self.hass.stop() + + @patch('homeassistant.components.image_processing.microsoft_face_identify.' + 'MicrosoftFaceIdentifyEntity.should_poll', + new_callable=PropertyMock(return_value=False)) + def test_openalpr_process_image(self, poll_mock, aioclient_mock): + """Setup and scan a picture and test plates from event.""" + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups"), + text=load_fixture('microsoft_face_persongroups.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group1/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group2/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + + setup_component(self.hass, ip.DOMAIN, self.config) + + state = self.hass.states.get('camera.demo_camera') + url = "{0}{1}".format( + self.hass.config.api.base_url, + state.attributes.get(ATTR_ENTITY_PICTURE)) + + face_events = [] + + @callback + def mock_face_event(event): + """Mock event.""" + face_events.append(event) + + self.hass.bus.listen('identify_face', mock_face_event) + + aioclient_mock.get(url, content=b'image') + + aioclient_mock.post( + mf.FACE_API_URL.format("detect"), + text=load_fixture('microsoft_face_detect.json') + ) + aioclient_mock.post( + mf.FACE_API_URL.format("identify"), + text=load_fixture('microsoft_face_identify.json') + ) + + ip.scan(self.hass, entity_id='image_processing.test_local') + self.hass.block_till_done() + + state = self.hass.states.get('image_processing.test_local') + + assert len(face_events) == 1 + assert state.attributes.get('total_faces') == 2 + assert state.state == 'David' + + assert face_events[0].data['name'] == 'David' + assert face_events[0].data['confidence'] == float(92) + assert face_events[0].data['entity_id'] == \ + 'image_processing.test_local' diff --git a/tests/components/test_microsoft_face.py b/tests/components/test_microsoft_face.py new file mode 100644 index 00000000000..6dee9dc9a55 --- /dev/null +++ b/tests/components/test_microsoft_face.py @@ -0,0 +1,263 @@ +"""The tests for the microsoft face platform.""" +import asyncio +from unittest.mock import patch + +import homeassistant.components.microsoft_face as mf +from homeassistant.bootstrap import setup_component + +from tests.common import ( + get_test_home_assistant, assert_setup_component, mock_coro, load_fixture) + + +class TestMicrosoftFaceSetup(object): + """Test the microsoft face component.""" + + def setup_method(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + self.config = { + mf.DOMAIN: { + 'api_key': '12345678abcdef', + } + } + + def teardown_method(self): + """Stop everything that was started.""" + self.hass.stop() + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_setup_component(self, mock_update): + """Setup component.""" + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_setup_component_wrong_api_key(self, mock_update): + """Setup component without api key.""" + with assert_setup_component(0, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, {mf.DOMAIN: {}}) + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_setup_component_test_service(self, mock_update): + """Setup component.""" + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + assert self.hass.services.has_service(mf.DOMAIN, 'create_group') + assert self.hass.services.has_service(mf.DOMAIN, 'delete_group') + assert self.hass.services.has_service(mf.DOMAIN, 'train_group') + assert self.hass.services.has_service(mf.DOMAIN, 'create_person') + assert self.hass.services.has_service(mf.DOMAIN, 'delete_person') + assert self.hass.services.has_service(mf.DOMAIN, 'face_person') + + def test_setup_component_test_entities(self, aioclient_mock): + """Setup component.""" + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups"), + text=load_fixture('microsoft_face_persongroups.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group1/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group2/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + assert len(aioclient_mock.mock_calls) == 3 + + entity_group1 = self.hass.states.get('microsoft_face.test_group1') + entity_group2 = self.hass.states.get('microsoft_face.test_group2') + + assert entity_group1 is not None + assert entity_group2 is not None + + assert entity_group1.attributes['Ryan'] == \ + '25985303-c537-4467-b41d-bdb45cd95ca1' + assert entity_group1.attributes['David'] == \ + '2ae4935b-9659-44c3-977f-61fac20d0538' + + assert entity_group2.attributes['Ryan'] == \ + '25985303-c537-4467-b41d-bdb45cd95ca1' + assert entity_group2.attributes['David'] == \ + '2ae4935b-9659-44c3-977f-61fac20d0538' + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_service_groups(self, mock_update, aioclient_mock): + """Setup component, test groups services.""" + aioclient_mock.put( + mf.FACE_API_URL.format("persongroups/service_group"), + status=200, text="{}" + ) + aioclient_mock.delete( + mf.FACE_API_URL.format("persongroups/service_group"), + status=200, text="{}" + ) + + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + mf.create_group(self.hass, 'Service Group') + self.hass.block_till_done() + + entity = self.hass.states.get('microsoft_face.service_group') + assert entity is not None + assert len(aioclient_mock.mock_calls) == 1 + + mf.delete_group(self.hass, 'Service Group') + self.hass.block_till_done() + + entity = self.hass.states.get('microsoft_face.service_group') + assert entity is None + assert len(aioclient_mock.mock_calls) == 2 + + def test_service_person(self, aioclient_mock): + """Setup component, test person services.""" + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups"), + text=load_fixture('microsoft_face_persongroups.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group1/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group2/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + assert len(aioclient_mock.mock_calls) == 3 + + aioclient_mock.post( + mf.FACE_API_URL.format("persongroups/test_group1/persons"), + text=load_fixture('microsoft_face_create_person.json') + ) + aioclient_mock.delete( + mf.FACE_API_URL.format( + "persongroups/test_group1/persons/" + "25985303-c537-4467-b41d-bdb45cd95ca1"), + status=200, text="{}" + ) + + mf.create_person(self.hass, 'test group1', 'Hans') + self.hass.block_till_done() + + entity_group1 = self.hass.states.get('microsoft_face.test_group1') + + assert len(aioclient_mock.mock_calls) == 4 + assert entity_group1 is not None + assert entity_group1.attributes['Hans'] == \ + '25985303-c537-4467-b41d-bdb45cd95ca1' + + mf.delete_person(self.hass, 'test group1', 'Hans') + self.hass.block_till_done() + + entity_group1 = self.hass.states.get('microsoft_face.test_group1') + + assert len(aioclient_mock.mock_calls) == 5 + assert entity_group1 is not None + assert 'Hans' not in entity_group1.attributes + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_service_train(self, mock_update, aioclient_mock): + """Setup component, test train groups services.""" + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + aioclient_mock.post( + mf.FACE_API_URL.format("persongroups/service_group/train"), + status=200, text="{}" + ) + + mf.train_group(self.hass, 'Service Group') + self.hass.block_till_done() + + assert len(aioclient_mock.mock_calls) == 1 + + @patch('homeassistant.components.camera.async_get_image', + return_value=mock_coro(return_value=b'Test')()) + def test_service_face(self, camera_mock, aioclient_mock): + """Setup component, test person face services.""" + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups"), + text=load_fixture('microsoft_face_persongroups.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group1/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + aioclient_mock.get( + mf.FACE_API_URL.format("persongroups/test_group2/persons"), + text=load_fixture('microsoft_face_persons.json') + ) + + self.config['camera'] = {'platform': 'demo'} + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + assert len(aioclient_mock.mock_calls) == 3 + + aioclient_mock.post( + mf.FACE_API_URL.format( + "persongroups/test_group2/persons/" + "2ae4935b-9659-44c3-977f-61fac20d0538/persistedFaces"), + status=200, text="{}" + ) + + mf.face_person( + self.hass, 'test_group2', 'David', 'camera.demo_camera') + self.hass.block_till_done() + + assert len(aioclient_mock.mock_calls) == 4 + assert aioclient_mock.mock_calls[3][2] == b'Test' + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_service_status_400(self, mock_update, aioclient_mock): + """Setup component, test groups services with error.""" + aioclient_mock.put( + mf.FACE_API_URL.format("persongroups/service_group"), + status=400, text="{'error': {'message': 'Error'}}" + ) + + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + mf.create_group(self.hass, 'Service Group') + self.hass.block_till_done() + + entity = self.hass.states.get('microsoft_face.service_group') + assert entity is None + assert len(aioclient_mock.mock_calls) == 1 + + @patch('homeassistant.components.microsoft_face.' + 'MicrosoftFace.update_store', return_value=mock_coro()()) + def test_service_status_timeout(self, mock_update, aioclient_mock): + """Setup component, test groups services with timeout.""" + aioclient_mock.put( + mf.FACE_API_URL.format("persongroups/service_group"), + status=400, exc=asyncio.TimeoutError() + ) + + with assert_setup_component(2, mf.DOMAIN): + setup_component(self.hass, mf.DOMAIN, self.config) + + mf.create_group(self.hass, 'Service Group') + self.hass.block_till_done() + + entity = self.hass.states.get('microsoft_face.service_group') + assert entity is None + assert len(aioclient_mock.mock_calls) == 1 diff --git a/tests/fixtures/microsoft_face_create_person.json b/tests/fixtures/microsoft_face_create_person.json new file mode 100644 index 00000000000..60e7a826c13 --- /dev/null +++ b/tests/fixtures/microsoft_face_create_person.json @@ -0,0 +1,3 @@ +{ + "personId":"25985303-c537-4467-b41d-bdb45cd95ca1" +} diff --git a/tests/fixtures/microsoft_face_detect.json b/tests/fixtures/microsoft_face_detect.json new file mode 100644 index 00000000000..f9d819da239 --- /dev/null +++ b/tests/fixtures/microsoft_face_detect.json @@ -0,0 +1,27 @@ +[ + { + "faceId": "c5c24a82-6845-4031-9d5d-978df9175426", + "faceRectangle": { + "width": 78, + "height": 78, + "left": 394, + "top": 54 + }, + "faceAttributes": { + "age": 71.0, + "gender": "male", + "smile": 0.88, + "facialHair": { + "mustache": 0.8, + "beard": 0.1, + "sideburns": 0.02 + }, + "glasses": "sunglasses", + "headPose": { + "roll": 2.1, + "yaw": 3, + "pitch": 0 + } + } + } +] diff --git a/tests/fixtures/microsoft_face_identify.json b/tests/fixtures/microsoft_face_identify.json new file mode 100644 index 00000000000..5b106de5324 --- /dev/null +++ b/tests/fixtures/microsoft_face_identify.json @@ -0,0 +1,20 @@ +[ + { + "faceId":"c5c24a82-6845-4031-9d5d-978df9175426", + "candidates":[ + { + "personId":"2ae4935b-9659-44c3-977f-61fac20d0538", + "confidence":0.92 + } + ] + }, + { + "faceId":"c5c24a82-6825-4031-9d5d-978df0175426", + "candidates":[ + { + "personId":"25985303-c537-4467-b41d-bdb45cd95ca1", + "confidence":0.32 + } + ] + } +] diff --git a/tests/fixtures/microsoft_face_persongroups.json b/tests/fixtures/microsoft_face_persongroups.json new file mode 100644 index 00000000000..0eb0722a550 --- /dev/null +++ b/tests/fixtures/microsoft_face_persongroups.json @@ -0,0 +1,12 @@ +[ + { + "personGroupId":"test_group1", + "name":"test group1", + "userData":"test" + }, + { + "personGroupId":"test_group2", + "name":"test group2", + "userData":"test" + } +] diff --git a/tests/fixtures/microsoft_face_persons.json b/tests/fixtures/microsoft_face_persons.json new file mode 100644 index 00000000000..05da6816023 --- /dev/null +++ b/tests/fixtures/microsoft_face_persons.json @@ -0,0 +1,21 @@ +[ + { + "personId":"25985303-c537-4467-b41d-bdb45cd95ca1", + "name":"Ryan", + "userData":"User-provided data attached to the person", + "persistedFaceIds":[ + "015839fb-fbd9-4f79-ace9-7675fc2f1dd9", + "fce92aed-d578-4d2e-8114-068f8af4492e", + "b64d5e15-8257-4af2-b20a-5a750f8940e7" + ] + }, + { + "personId":"2ae4935b-9659-44c3-977f-61fac20d0538", + "name":"David", + "userData":"User-provided data attached to the person", + "persistedFaceIds":[ + "30ea1073-cc9e-4652-b1e3-d08fb7b95315", + "fbd2a038-dbff-452c-8e79-2ee81b1aa84e" + ] + } +] From a09a772f438737d4b38bfc21fb0b9a0195f980eb Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 25 Jan 2017 07:04:44 +0100 Subject: [PATCH 134/191] Add more validation for mysensors (#5493) * Move isdevice validator under helpers.config_validation. * Check that all persistence files are set and are unique if any is set by user. This is necessary to avoid file name clashes. * Check that a set persistence file has an existing and writable directory. * Check that a device is either a valid device file, "mqtt", or a valid domain name or ip address. --- homeassistant/components/mysensors.py | 60 ++++++++++++++++--- .../components/switch/acer_projector.py | 12 +--- homeassistant/helpers/config_validation.py | 9 +++ 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 79e572defeb..52b851d1983 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -5,12 +5,15 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.mysensors/ """ import logging +import os import socket import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.bootstrap import setup_component +from homeassistant.components.mqtt import (valid_publish_topic, + valid_subscribe_topic) from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_NAME, CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON) @@ -44,22 +47,61 @@ REQUIREMENTS = [ 'https://github.com/theolind/pymysensors/archive/' '0b705119389be58332f17753c53167f551254b6c.zip#pymysensors==0.8'] + +def is_socket_address(value): + """Validate that value is a valid address.""" + try: + socket.getaddrinfo(value, None) + return value + except OSError: + raise vol.Invalid('Device is not a valid domain name or ip address') + + +def has_parent_dir(value): + """Validate that value is in an existing directory which is writetable.""" + parent = os.path.dirname(os.path.realpath(value)) + is_dir_writable = os.path.isdir(parent) and os.access(parent, os.W_OK) + if not is_dir_writable: + raise vol.Invalid( + '{} directory does not exist or is not writetable'.format(parent)) + return value + + +def has_all_unique_files(value): + """Validate that all persistence files are unique and set if any is set.""" + persistence_files = [ + gateway.get(CONF_PERSISTENCE_FILE) for gateway in value] + if None in persistence_files and any( + name is not None for name in persistence_files): + raise vol.Invalid( + 'persistence file name of all devices must be set if any is set') + if not all(name is None for name in persistence_files): + schema = vol.Schema(vol.Unique()) + schema(persistence_files) + return value + + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_GATEWAYS): vol.All(cv.ensure_list, [ - { - vol.Required(CONF_DEVICE): cv.string, - vol.Optional(CONF_PERSISTENCE_FILE): cv.string, + vol.Required(CONF_GATEWAYS): vol.All( + cv.ensure_list, has_all_unique_files, + [{ + vol.Required(CONF_DEVICE): + vol.Any(cv.isdevice, MQTT_COMPONENT, is_socket_address), + vol.Optional(CONF_PERSISTENCE_FILE): + vol.All(cv.string, has_parent_dir), vol.Optional( CONF_BAUD_RATE, default=DEFAULT_BAUD_RATE): cv.positive_int, vol.Optional( CONF_TCP_PORT, default=DEFAULT_TCP_PORT): cv.port, - vol.Optional(CONF_TOPIC_IN_PREFIX, default=''): cv.string, - vol.Optional(CONF_TOPIC_OUT_PREFIX, default=''): cv.string, - }, - ]), + vol.Optional( + CONF_TOPIC_IN_PREFIX, default=''): valid_subscribe_topic, + vol.Optional( + CONF_TOPIC_OUT_PREFIX, default=''): valid_publish_topic, + }] + ), vol.Optional(CONF_DEBUG, default=False): cv.boolean, vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, vol.Optional(CONF_PERSISTENCE, default=True): cv.boolean, @@ -100,7 +142,7 @@ def setup(hass, config): out_prefix=out_prefix, retain=retain) else: try: - socket.inet_aton(device) + socket.getaddrinfo(device, None) # valid ip address gateway = mysensors.TCPGateway( device, event_callback=None, persistence=persistence, diff --git a/homeassistant/components/switch/acer_projector.py b/homeassistant/components/switch/acer_projector.py index 7817a127642..416f6431ba5 100644 --- a/homeassistant/components/switch/acer_projector.py +++ b/homeassistant/components/switch/acer_projector.py @@ -4,7 +4,6 @@ Use serial protocol of Acer projector to obtain state of the projector. For more details about this component, please refer to the documentation at https://home-assistant.io/components/switch.acer_projector/ """ -import os import logging import re @@ -47,17 +46,8 @@ CMD_DICT = {LAMP: '* 0 Lamp ?\r', STATE_OFF: '* 0 IR 002\r'} -def isdevice(dev): - """Check if dev a real device.""" - try: - os.stat(dev) - return str(dev) - except OSError: - raise vol.Invalid("No device found!") - - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_FILENAME): isdevice, + vol.Required(CONF_FILENAME): cv.isdevice, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Optional(CONF_WRITE_TIMEOUT, default=DEFAULT_WRITE_TIMEOUT): diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 0c28dbdd78e..cd26c2779b1 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -70,6 +70,15 @@ def boolean(value: Any) -> bool: return bool(value) +def isdevice(value): + """Validate that value is a real device.""" + try: + os.stat(value) + return str(value) + except OSError: + raise vol.Invalid('No device at {} found'.format(value)) + + def isfile(value: Any) -> str: """Validate that the value is an existing file.""" if value is None: From 43e46154c68db9974837503d4c413dec9e5ac167 Mon Sep 17 00:00:00 2001 From: Nick Touran Date: Tue, 24 Jan 2017 22:08:19 -0800 Subject: [PATCH 135/191] Added new Washington State DOT sensor. (#5496) * Added new Washington State DOT sensor. * Minor changes from review for WSDOT. * Update wsdot.py --- homeassistant/components/sensor/wsdot.py | 143 +++++++++++++++++++++++ tests/components/sensor/test_wsdot.py | 64 ++++++++++ tests/fixtures/wsdot.json | 20 ++++ 3 files changed, 227 insertions(+) create mode 100644 homeassistant/components/sensor/wsdot.py create mode 100644 tests/components/sensor/test_wsdot.py create mode 100644 tests/fixtures/wsdot.json diff --git a/homeassistant/components/sensor/wsdot.py b/homeassistant/components/sensor/wsdot.py new file mode 100644 index 00000000000..a3230a88eb3 --- /dev/null +++ b/homeassistant/components/sensor/wsdot.py @@ -0,0 +1,143 @@ +""" +Support for Washington State Department of Transportation (WSDOT) data. + +Data provided by WSDOT is documented at http://wsdot.com/traffic/api/ +""" +import logging +import re +from datetime import datetime, timezone, timedelta + +import requests +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_API_KEY, CONF_NAME, ATTR_ATTRIBUTION, CONF_ID + ) +from homeassistant.helpers.entity import Entity +import homeassistant.helpers.config_validation as cv + + +_LOGGER = logging.getLogger(__name__) + +CONF_TRAVEL_TIMES = 'travel_time' + +# API codes for travel time details +ATTR_ACCESS_CODE = 'AccessCode' +ATTR_TRAVEL_TIME_ID = 'TravelTimeID' +ATTR_CURRENT_TIME = 'CurrentTime' +ATTR_AVG_TIME = 'AverageTime' +ATTR_NAME = 'Name' +ATTR_TIME_UPDATED = 'TimeUpdated' +ATTR_DESCRIPTION = 'Description' +ATTRIBUTION = "Data provided by WSDOT" + +SCAN_INTERVAL = timedelta(minutes=3) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_API_KEY): cv.string, + vol.Optional(CONF_TRAVEL_TIMES): [{ + vol.Required(CONF_ID): cv.string, + vol.Optional(CONF_NAME): cv.string}] +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Get the WSDOT sensor.""" + sensors = [] + for travel_time in config.get(CONF_TRAVEL_TIMES): + name = (travel_time.get(CONF_NAME) or + travel_time.get(CONF_ID)) + sensors.append( + WashingtonStateTravelTimeSensor( + name, + config.get(CONF_API_KEY), + travel_time.get(CONF_ID))) + add_devices(sensors, True) + + +class WashingtonStateTransportSensor(Entity): + """ + Sensor that reads the WSDOT web API. + + WSDOT provides ferry schedules, toll rates, weather conditions, + mountain pass conditions, and more. Subclasses of this + can read them and make them available. + """ + + ICON = 'mdi:car' + + def __init__(self, name, access_code): + """Initialize the sensor.""" + self._data = {} + self._access_code = access_code + self._name = name + self._state = None + + @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 icon(self): + """Icon to use in the frontend, if any.""" + return self.ICON + + +class WashingtonStateTravelTimeSensor(WashingtonStateTransportSensor): + """Travel time sensor from WSDOT.""" + + RESOURCE = ('http://www.wsdot.wa.gov/Traffic/api/TravelTimes/' + 'TravelTimesREST.svc/GetTravelTimeAsJson') + ICON = 'mdi:car' + + def __init__(self, name, access_code, travel_time_id): + """Construct a travel time sensor.""" + self._travel_time_id = travel_time_id + WashingtonStateTransportSensor.__init__(self, name, access_code) + + def update(self): + """Get the latest data from WSDOT.""" + params = {ATTR_ACCESS_CODE: self._access_code, + ATTR_TRAVEL_TIME_ID: self._travel_time_id} + + response = requests.get(self.RESOURCE, params, timeout=10) + if response.status_code != 200: + _LOGGER.warning('Invalid response from WSDOT API.') + else: + self._data = response.json() + self._state = self._data.get(ATTR_CURRENT_TIME) + + @property + def device_state_attributes(self): + """Return other details about the sensor state.""" + if self._data is not None: + attrs = {ATTR_ATTRIBUTION: ATTRIBUTION} + for key in [ATTR_AVG_TIME, ATTR_NAME, ATTR_DESCRIPTION, + ATTR_TRAVEL_TIME_ID]: + attrs[key] = self._data.get(key) + attrs[ATTR_TIME_UPDATED] = _parse_wsdot_timestamp( + self._data.get(ATTR_TIME_UPDATED)) + return attrs + + @property + def unit_of_measurement(self): + """Return the unit this state is expressed in.""" + return "min" + + +def _parse_wsdot_timestamp(timestamp): + """Convert WSDOT timestamp to datetime.""" + if not timestamp: + return None + # ex: Date(1485040200000-0800) + milliseconds, tzone = re.search( + r'Date\((\d+)([+-]\d\d)\d\d\)', timestamp).groups() + return datetime.fromtimestamp(int(milliseconds) / 1000, + tz=timezone(timedelta(hours=int(tzone)))) diff --git a/tests/components/sensor/test_wsdot.py b/tests/components/sensor/test_wsdot.py new file mode 100644 index 00000000000..4a2dc345f10 --- /dev/null +++ b/tests/components/sensor/test_wsdot.py @@ -0,0 +1,64 @@ +"""The tests for the WSDOT platform.""" +import re +import unittest +from datetime import timedelta, datetime, timezone + +import requests_mock + +from homeassistant.components.sensor import wsdot +from homeassistant.components.sensor.wsdot import ( + WashingtonStateTravelTimeSensor, ATTR_DESCRIPTION, + ATTR_TIME_UPDATED, CONF_API_KEY, CONF_NAME, + CONF_ID, CONF_TRAVEL_TIMES, SCAN_INTERVAL) +from homeassistant.bootstrap import setup_component +from tests.common import load_fixture, get_test_home_assistant + + +class TestWSDOT(unittest.TestCase): + """Test the WSDOT platform.""" + + def add_entities(self, new_entities, update_before_add=False): + """Mock add entities.""" + if update_before_add: + for entity in new_entities: + entity.update() + + for entity in new_entities: + self.entities.append(entity) + + def setUp(self): + """Initialize values for this testcase class.""" + self.hass = get_test_home_assistant() + self.config = { + CONF_API_KEY: 'foo', + SCAN_INTERVAL: timedelta(seconds=120), + CONF_TRAVEL_TIMES: [{ + CONF_ID: 96, + CONF_NAME: 'I90 EB'}], + } + self.entities = [] + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + + def test_setup_with_config(self): + """Test the platform setup with configuration.""" + self.assertTrue( + setup_component(self.hass, 'sensor', {'wsdot': self.config})) + + @requests_mock.Mocker() + def test_setup(self, mock_req): + """Test for operational WSDOT sensor with proper attributes.""" + uri = re.compile(WashingtonStateTravelTimeSensor.RESOURCE + '*') + mock_req.get(uri, text=load_fixture('wsdot.json')) + wsdot.setup_platform(self.hass, self.config, self.add_entities) + self.assertEqual(len(self.entities), 1) + sensor = self.entities[0] + self.assertEqual(sensor.name, 'I90 EB') + self.assertEqual(sensor.state, 11) + self.assertEqual(sensor.device_state_attributes[ATTR_DESCRIPTION], + 'Downtown Seattle to Downtown Bellevue via I-90') + self.assertEqual(sensor.device_state_attributes[ATTR_TIME_UPDATED], + datetime(2017, 1, 21, 15, 10, + tzinfo=timezone(timedelta(hours=-8)))) diff --git a/tests/fixtures/wsdot.json b/tests/fixtures/wsdot.json new file mode 100644 index 00000000000..de5dc80579f --- /dev/null +++ b/tests/fixtures/wsdot.json @@ -0,0 +1,20 @@ +{"Description": "Downtown Seattle to Downtown Bellevue via I-90", + "TimeUpdated": "/Date(1485040200000-0800)/", + "Distance": 10.6, + "EndPoint": {"Direction": "N", + "Description": "I-405 @ NE 8th St in Bellevue", + "Longitude": -122.18797, + "MilePost": 13.6, + "Latitude": 47.61361, + "RoadName": "I-405"}, + "StartPoint": {"Direction": "S", + "Description": "I-5 @ University St in Seattle", + "Longitude": -122.331759, + "MilePost": 165.83, + "Latitude": 47.609294, + "RoadName": "I-5"}, + "CurrentTime": 11, + "TravelTimeID": 96, + "Name": "Seattle-Bellevue via I-90 (EB AM)", + "AverageTime": 11} + From 191d7b0a50f988716878aa3533c8720d7c61d961 Mon Sep 17 00:00:00 2001 From: Stu Gott Date: Wed, 25 Jan 2017 01:10:10 -0500 Subject: [PATCH 136/191] Add support for Wemo CoffeeMaker devices (#5505) * Add support for Wemo CoffeeMaker devices * Add CoffeeMaker to WEMO_MODEL_DISPATCH --- homeassistant/components/switch/wemo.py | 45 +++++++++++++++++++------ homeassistant/components/wemo.py | 5 +-- requirements_all.txt | 2 +- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 2d3d5ea5547..3af93d08fc8 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -18,9 +18,7 @@ _LOGGER = logging.getLogger(__name__) ATTR_SENSOR_STATE = "sensor_state" ATTR_SWITCH_MODE = "switch_mode" ATTR_CURRENT_STATE_DETAIL = 'state_detail' - -MAKER_SWITCH_MOMENTARY = "momentary" -MAKER_SWITCH_TOGGLE = "toggle" +ATTR_COFFEMAKER_MODE = "coffeemaker_mode" MAKER_SWITCH_MOMENTARY = "momentary" MAKER_SWITCH_TOGGLE = "toggle" @@ -52,7 +50,10 @@ class WemoSwitch(SwitchDevice): self.wemo = device self.insight_params = None self.maker_params = None + self.coffeemaker_mode = None self._state = None + # look up model name once as it incurs network traffic + self._model_name = self.wemo.model_name wemo = get_component('wemo') wemo.SUBSCRIPTION_REGISTRY.register(self.wemo) @@ -63,7 +64,11 @@ class WemoSwitch(SwitchDevice): _LOGGER.info( 'Subscription update for %s', _device) - self.update() + if self._model_name == 'CoffeeMaker': + self.wemo.subscription_callback(_params) + self._update(force_update=False) + else: + self.update() if not hasattr(self, 'hass'): return self.schedule_update_ha_state() @@ -102,9 +107,12 @@ class WemoSwitch(SwitchDevice): else: attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_TOGGLE - if self.insight_params: + if self.insight_params or (self.coffeemaker_mode is not None): attr[ATTR_CURRENT_STATE_DETAIL] = self.detail_state + if self.coffeemaker_mode is not None: + attr[ATTR_COFFEMAKER_MODE] = self.coffeemaker_mode + return attr @property @@ -122,6 +130,8 @@ class WemoSwitch(SwitchDevice): @property def detail_state(self): """Return the state of the device.""" + if self.coffeemaker_mode is not None: + return self.wemo.mode_string if self.insight_params: standby_state = int(self.insight_params['state']) if standby_state == WEMO_ON: @@ -141,12 +151,22 @@ class WemoSwitch(SwitchDevice): @property def available(self): """True if switch is available.""" - if self.wemo.model_name == 'Insight' and self.insight_params is None: + if self._model_name == 'Insight' and self.insight_params is None: return False - if self.wemo.model_name == 'Maker' and self.maker_params is None: + if self._model_name == 'Maker' and self.maker_params is None: + return False + if self._model_name == 'CoffeeMaker' and self.coffeemaker_mode is None: return False return True + @property + def icon(self): + """Icon of device based on its type.""" + if self._model_name == 'CoffeeMaker': + return 'mdi:coffee' + else: + return super().icon + def turn_on(self, **kwargs): """Turn the switch on.""" self._state = WEMO_ON @@ -161,13 +181,18 @@ class WemoSwitch(SwitchDevice): def update(self): """Update WeMo state.""" + self._update(force_update=True) + + def _update(self, force_update=True): try: - self._state = self.wemo.get_state(True) - if self.wemo.model_name == 'Insight': + self._state = self.wemo.get_state(force_update) + if self._model_name == 'Insight': self.insight_params = self.wemo.insight_params self.insight_params['standby_state'] = ( self.wemo.get_standby_state) - elif self.wemo.model_name == 'Maker': + elif self._model_name == 'Maker': self.maker_params = self.wemo.maker_params + elif self._model_name == 'CoffeeMaker': + self.coffeemaker_mode = self.wemo.mode except AttributeError: _LOGGER.warning('Could not update status for %s', self.name) diff --git a/homeassistant/components/wemo.py b/homeassistant/components/wemo.py index ba068905087..3fea17ccee5 100644 --- a/homeassistant/components/wemo.py +++ b/homeassistant/components/wemo.py @@ -14,7 +14,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.const import EVENT_HOMEASSISTANT_STOP -REQUIREMENTS = ['pywemo==0.4.9'] +REQUIREMENTS = ['pywemo==0.4.11'] DOMAIN = 'wemo' @@ -25,7 +25,8 @@ WEMO_MODEL_DISPATCH = { 'Maker': 'switch', 'Sensor': 'binary_sensor', 'Socket': 'switch', - 'LightSwitch': 'switch' + 'LightSwitch': 'switch', + 'CoffeeMaker': 'switch' } SUBSCRIPTION_REGISTRY = None diff --git a/requirements_all.txt b/requirements_all.txt index e7e54ada716..6bb84a897de 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -547,7 +547,7 @@ pyvera==0.2.23 pywebpush==0.6.1 # homeassistant.components.wemo -pywemo==0.4.9 +pywemo==0.4.11 # homeassistant.components.light.yeelight pyyeelight==1.0-beta From 264310074faf54f25e77cc83f9d8e6ebcc0d8cf9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 24 Jan 2017 22:13:22 -0800 Subject: [PATCH 137/191] Update Wink requirement --- homeassistant/components/wink.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index 2ef80c8058b..c1de7e340c1 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==1.0.0', 'pubnubsub-handler==0.0.7'] +REQUIREMENTS = ['python-wink==1.0.0', 'pubnubsub-handler==1.0.0'] _LOGGER = logging.getLogger(__name__) From c7ff7af39deb0fba61d1cfcc5089fe6a5d54c0a0 Mon Sep 17 00:00:00 2001 From: Alessandro Mogavero Date: Wed, 25 Jan 2017 08:02:08 +0000 Subject: [PATCH 138/191] Sky hub (#5509) * Added new platform sky_hub * added env to virtual environment gitingore * Removed unuseful imports * BT home hub 5 renamed to sky hub in the comments * Added sky_hub to .coveragerc * Added example configuration in sky_hub docstring * sky_hub made compliant with test style standards * homehub functions renamed to skyhub * Update .gitignore * Update .coveragerc --- .coveragerc | 1 + .../components/device_tracker/sky_hub.py | 130 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 homeassistant/components/device_tracker/sky_hub.py diff --git a/.coveragerc b/.coveragerc index 51ca1df2c1f..c1b47123710 100644 --- a/.coveragerc +++ b/.coveragerc @@ -168,6 +168,7 @@ omit = homeassistant/components/device_tracker/netgear.py homeassistant/components/device_tracker/nmap_tracker.py homeassistant/components/device_tracker/ping.py + homeassistant/components/device_tracker/sky_hub.py homeassistant/components/device_tracker/snmp.py homeassistant/components/device_tracker/swisscom.py homeassistant/components/device_tracker/thomson.py diff --git a/homeassistant/components/device_tracker/sky_hub.py b/homeassistant/components/device_tracker/sky_hub.py new file mode 100644 index 00000000000..9c61b47593e --- /dev/null +++ b/homeassistant/components/device_tracker/sky_hub.py @@ -0,0 +1,130 @@ +""" +Support for Sky Hub. + +# Example configuration.yaml entry +device_tracker: + - platform: sky_hub + host: 192.168.1.254 +""" +import logging +import re +import threading +from datetime import timedelta + +import requests +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.device_tracker import ( + DOMAIN, PLATFORM_SCHEMA, DeviceScanner) +from homeassistant.const import CONF_HOST +from homeassistant.util import Throttle + +# Return cached results if last scan was less then this time ago. +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) + +_LOGGER = logging.getLogger(__name__) + +_MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})') + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string +}) + + +# pylint: disable=unused-argument +def get_scanner(hass, config): + """Return a Sky Hub 5 scanner if successful.""" + scanner = SkyHubDeviceScanner(config[DOMAIN]) + + return scanner if scanner.success_init else None + + +class SkyHubDeviceScanner(DeviceScanner): + """This class queries a Sky Hub router.""" + + def __init__(self, config): + """Initialise the scanner.""" + _LOGGER.info('Initialising Sky Hub') + self.host = config.get(CONF_HOST, '192.168.1.254') + + self.lock = threading.Lock() + + self.last_results = {} + + self.url = 'http://{}/'.format(self.host) + + # Test the router is accessible + data = _get_skyhub_data(self.url) + self.success_init = data is not None + + def scan_devices(self): + """Scan for new devices and return a list with found device IDs.""" + self._update_info() + + return (device for device in self.last_results) + + def get_device_name(self, device): + """Return the name of the given device or None if we don't know.""" + with self.lock: + # If not initialised and not already scanned and not found. + if device not in self.last_results: + self._update_info() + + if not self.last_results: + return None + + return self.last_results.get(device) + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """Ensure the information from the Sky Hub is up to date. + + Return boolean if scanning successful. + """ + if not self.success_init: + return False + + with self.lock: + _LOGGER.info('Scanning') + + data = _get_skyhub_data(self.url) + + if not data: + _LOGGER.warning('Error scanning devices') + return False + + self.last_results = data + + return True + + +def _get_skyhub_data(url): + """Retrieve data from Sky Hub and return parsed result.""" + try: + response = requests.get(url, timeout=5) + except requests.exceptions.Timeout: + _LOGGER.exception("Connection to the router timed out") + return + if response.status_code == 200: + return _parse_skyhub_response(response.text) + else: + _LOGGER.error("Invalid response from Sky Hub: %s", response) + + +def _parse_skyhub_response(data_str): + """Parse the Sky Hub data format.""" + pattmatch = re.search('attach_dev = \'(.*)\'', data_str) + patt = pattmatch.group(1) + + dev = [patt1.split(',') for patt1 in patt.split('')] + + devices = {} + for dvc in dev: + if _MAC_REGEX.match(dvc[1]): + devices[dvc[1]] = dvc[0] + else: + raise RuntimeError('Error: MAC address ' + dvc[1] + + ' not in correct format.') + + return devices From ca1dc202f91c81b1de63c16c6d4eeb5e31c766de Mon Sep 17 00:00:00 2001 From: Oleksii Serdiuk Date: Wed, 25 Jan 2017 09:02:39 +0100 Subject: [PATCH 139/191] ASUSWRT: Add IPv6 support when parsing neighbors (#5536) * ASUSWRT: Add IPv6 support when parsing neighbors The regex for IPv6 should cover most cases, but it doesn't validate whether IP is correct. It also might fail for some edge cases. Also, ignore 'duid xx:xx:xx:xx:xx:xx:xx:xx:xx:xx' line in leases. Closes #2814 - ASUSWRT doesn't support ipv6 * Update asuswrt.py --- homeassistant/components/device_tracker/asuswrt.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index be530abc9e2..512ccba0b74 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -71,10 +71,11 @@ _ARP_REGEX = re.compile( _IP_NEIGH_CMD = 'ip neigh' _IP_NEIGH_REGEX = re.compile( - r'(?P([0-9]{1,3}[\.]){3}[0-9]{1,3})\s' + - r'\w+\s' + - r'\w+\s' + - r'(\w+\s(?P(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))))?\s' + + r'(?P([0-9]{1,3}[\.]){3}[0-9]{1,3}|' + r'([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{1,4}){1,7})\s' + r'\w+\s' + r'\w+\s' + r'(\w+\s(?P(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))))?\s' r'(?P(\w+))') _NVRAM_CMD = 'nvram get client_info_tmp' @@ -323,6 +324,8 @@ class AsusWrtDeviceScanner(DeviceScanner): else: for lease in result.leases: + if lease.startswith(b'duid '): + continue match = _LEASES_REGEX.search(lease.decode('utf-8')) if not match: From 6015274ee2486f797fd6ee8f5f2074a601953e03 Mon Sep 17 00:00:00 2001 From: thecynic Date: Wed, 25 Jan 2017 00:11:37 -0800 Subject: [PATCH 140/191] Add initial support for Lutron RadioRA 2 using pylutron (#5337) Signed-off-by: Dima Zavin --- .coveragerc | 3 + homeassistant/components/light/lutron.py | 98 ++++++++++++++++++++++++ homeassistant/components/lutron.py | 86 +++++++++++++++++++++ requirements_all.txt | 3 + 4 files changed, 190 insertions(+) create mode 100644 homeassistant/components/light/lutron.py create mode 100644 homeassistant/components/lutron.py diff --git a/.coveragerc b/.coveragerc index c1b47123710..b5dda5b6f43 100644 --- a/.coveragerc +++ b/.coveragerc @@ -46,6 +46,9 @@ omit = homeassistant/components/isy994.py homeassistant/components/*/isy994.py + homeassistant/components/lutron.py + homeassistant/components/*/lutron.py + homeassistant/components/modbus.py homeassistant/components/*/modbus.py diff --git a/homeassistant/components/light/lutron.py b/homeassistant/components/light/lutron.py new file mode 100644 index 00000000000..7bc6fb50571 --- /dev/null +++ b/homeassistant/components/light/lutron.py @@ -0,0 +1,98 @@ +"""Support for Lutron lights.""" +import logging + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, DOMAIN, SUPPORT_BRIGHTNESS, Light) +from homeassistant.components.lutron import ( + LutronDevice, LUTRON_DEVICES, LUTRON_GROUPS, LUTRON_CONTROLLER) + +DEPENDENCIES = ['lutron'] + +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup Lutron lights.""" + area_devs = {} + devs = [] + for (area_name, device) in hass.data[LUTRON_DEVICES]['light']: + dev = LutronLight(hass, area_name, device, + hass.data[LUTRON_CONTROLLER]) + area_devs.setdefault(area_name, []).append(dev) + devs.append(dev) + add_devices(devs, True) + + for area in area_devs: + if area not in hass.data[LUTRON_GROUPS]: + continue + grp = hass.data[LUTRON_GROUPS][area] + ids = list(grp.tracking) + [dev.entity_id for dev in area_devs[area]] + grp.update_tracked_entity_ids(ids) + + return True + + +def to_lutron_level(level): + """Convert the given HASS light level (0-255) to Lutron (0.0-100.0).""" + return float((level * 100) / 255) + + +def to_hass_level(level): + """Convert the given Lutron (0.0-100.0) light level to HASS (0-255).""" + return int((level * 255) / 100) + + +class LutronLight(LutronDevice, Light): + """Representation of a Lutron Light, including dimmable.""" + + def __init__(self, hass, area_name, lutron_device, controller): + """Initialize the light.""" + self._prev_brightness = None + LutronDevice.__init__(self, hass, DOMAIN, area_name, lutron_device, + controller) + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_BRIGHTNESS + + @property + def brightness(self): + """Return the brightness of the light.""" + new_brightness = to_hass_level(self._lutron_device.last_level()) + if new_brightness != 0: + self._prev_brightness = new_brightness + return new_brightness + + def turn_on(self, **kwargs): + """Turn the light on.""" + if ATTR_BRIGHTNESS in kwargs and self._lutron_device.is_dimmable: + brightness = kwargs[ATTR_BRIGHTNESS] + elif self._prev_brightness == 0: + brightness = 255 / 2 + else: + brightness = self._prev_brightness + self._prev_brightness = brightness + self._lutron_device.level = to_lutron_level(brightness) + + def turn_off(self, **kwargs): + """Turn the light off.""" + self._lutron_device.level = 0 + + @property + def device_state_attributes(self): + """Return the state attributes.""" + attr = {} + attr['Lutron Integration ID'] = self._lutron_device.id + return attr + + @property + def is_on(self): + """Return true if device is on.""" + return self._lutron_device.last_level() > 0 + + def update(self): + """Called when forcing a refresh of the device.""" + if self._prev_brightness is None: + self._prev_brightness = to_hass_level(self._lutron_device.level) diff --git a/homeassistant/components/lutron.py b/homeassistant/components/lutron.py new file mode 100644 index 00000000000..0c9458ff3f6 --- /dev/null +++ b/homeassistant/components/lutron.py @@ -0,0 +1,86 @@ +""" +Component for interacting with a Lutron RadioRA 2 system. + +Uses pylutron (http://github.com/thecynic/pylutron). +""" + +import logging + +from homeassistant.helpers import discovery +from homeassistant.helpers.entity import (Entity, generate_entity_id) +from homeassistant.loader import get_component + +REQUIREMENTS = ['https://github.com/thecynic/pylutron/archive/v0.1.0.zip#' + 'pylutron==0.1.0'] + +DOMAIN = "lutron" + +_LOGGER = logging.getLogger(__name__) + +LUTRON_CONTROLLER = 'lutron_controller' +LUTRON_DEVICES = 'lutron_devices' +LUTRON_GROUPS = 'lutron_groups' + + +def setup(hass, base_config): + """Setup the Lutron component.""" + from pylutron import Lutron + + hass.data[LUTRON_CONTROLLER] = None + hass.data[LUTRON_DEVICES] = {'light': []} + hass.data[LUTRON_GROUPS] = {} + + config = base_config.get(DOMAIN) + hass.data[LUTRON_CONTROLLER] = Lutron( + config['lutron_host'], + config['lutron_user'], + config['lutron_password'] + ) + hass.data[LUTRON_CONTROLLER].load_xml_db() + hass.data[LUTRON_CONTROLLER].connect() + _LOGGER.info("Connected to Main Repeater @ %s", config['lutron_host']) + + group = get_component('group') + + # Sort our devices into types + for area in hass.data[LUTRON_CONTROLLER].areas: + if area.name not in hass.data[LUTRON_GROUPS]: + grp = group.Group.create_group(hass, area.name, []) + hass.data[LUTRON_GROUPS][area.name] = grp + for output in area.outputs: + hass.data[LUTRON_DEVICES]['light'].append((area.name, output)) + + for component in ('light',): + discovery.load_platform(hass, component, DOMAIN, None, base_config) + return True + + +class LutronDevice(Entity): + """Representation of a Lutron device entity.""" + + def __init__(self, hass, domain, area_name, lutron_device, controller): + """Initialize the device.""" + self._lutron_device = lutron_device + self._controller = controller + self._area_name = area_name + + self.hass = hass + object_id = '{} {}'.format(area_name, lutron_device.name) + self.entity_id = generate_entity_id(domain + '.{}', object_id, + hass=hass) + + self._controller.subscribe(self._lutron_device, self._update_callback) + + def _update_callback(self, _device): + """Callback invoked by pylutron when the device state changes.""" + self.schedule_update_ha_state() + + @property + def name(self): + """Return the name of the device.""" + return self._lutron_device.name + + @property + def should_poll(self): + """No polling needed.""" + return False diff --git a/requirements_all.txt b/requirements_all.txt index 6bb84a897de..b086258b9fd 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -260,6 +260,9 @@ 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 +# homeassistant.components.lutron +https://github.com/thecynic/pylutron/archive/v0.1.0.zip#pylutron==0.1.0 + # homeassistant.components.mysensors https://github.com/theolind/pymysensors/archive/0b705119389be58332f17753c53167f551254b6c.zip#pymysensors==0.8 From 393c7f2cf18880364d6c28c7c393ac3a16e0433d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 25 Jan 2017 18:20:31 +0100 Subject: [PATCH 141/191] [device.upc_connect] Discount on STOP. (#5553) * [device.upc_connect] Discount on STOP. * close session it self * Update upc_connect.py --- .../components/device_tracker/upc_connect.py | 19 ++++++++++++++++++- tests/test_util/aiohttp.py | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/upc_connect.py b/homeassistant/components/device_tracker/upc_connect.py index a042985c08d..5de412f05bc 100644 --- a/homeassistant/components/device_tracker/upc_connect.py +++ b/homeassistant/components/device_tracker/upc_connect.py @@ -12,6 +12,7 @@ import aiohttp import async_timeout import voluptuous as vol +from homeassistant.const import EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv from homeassistant.components.device_tracker import ( DOMAIN, PLATFORM_SCHEMA, DeviceScanner) @@ -29,6 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) CMD_LOGIN = 15 +CMD_LOGOUT = 16 CMD_DEVICES = 123 @@ -62,7 +64,21 @@ class UPCDeviceScanner(DeviceScanner): } self.websession = async_create_clientsession( - hass, cookie_jar=aiohttp.CookieJar(unsafe=True, loop=hass.loop)) + hass, auto_cleanup=False, + cookie_jar=aiohttp.CookieJar(unsafe=True, loop=hass.loop) + ) + + @asyncio.coroutine + def async_logout(event): + """Logout from upc connect box.""" + try: + yield from self._async_ws_function(CMD_LOGOUT) + self.token = None + finally: + self.websession.detach() + + hass.buss.async_listen_once( + EVENT_HOMEASSISTANT_STOP, async_logout) @asyncio.coroutine def async_scan_devices(self): @@ -94,6 +110,7 @@ class UPCDeviceScanner(DeviceScanner): response = None try: # get first token + self.websession.cookie_jar.clear() with async_timeout.timeout(10, loop=self.hass.loop): response = yield from self.websession.get( "http://{}/common_page/login.html".format(self.host) diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index afe2f626de7..7f46f11166d 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -17,6 +17,8 @@ class AiohttpClientMocker: self._cookies = {} self.mock_calls = [] + self.cookie_jar = mock.MagicMock() + def request(self, method, url, *, auth=None, status=200, From d500ddac9a23dedc4b5cbb4288c1feab87578433 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Wed, 25 Jan 2017 19:21:09 +0200 Subject: [PATCH 142/191] [script] Fix dodgy bash syntax for bootstrap (#5552) --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index c6d4a94be94..05e69cc4db7 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,7 +7,7 @@ set -e cd "$(dirname "$0")/.." script/bootstrap_server -if [ -x "$(command -v yarn >/dev/null)" ]; then +if command -v yarn >/dev/null ; then script/bootstrap_frontend else echo "Frontend development not possible without Node/yarn" From 42d33ae26d193101d7c421e05a6d69ae50b1526f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 25 Jan 2017 09:32:39 -0800 Subject: [PATCH 143/191] Disable typing travis build --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4c696a2236..2de101af24b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,15 +8,16 @@ matrix: env: TOXENV=requirements - python: "3.4.2" env: TOXENV=lint - - python: "3.5" - env: TOXENV=typing + # - python: "3.5" + # env: TOXENV=typing - python: "3.5" env: TOXENV=py35 - python: "3.6" env: TOXENV=py36 - allow_failures: - - python: "3.5" - env: TOXENV=typing + # allow_failures: + # - python: "3.5" + # env: TOXENV=typing + cache: directories: - $HOME/.cache/pip From c3a55e7d8283106c1c4eb5acd580393a3a56116d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 25 Jan 2017 18:46:37 +0100 Subject: [PATCH 144/191] Fix upc lint error (#5554) --- homeassistant/components/device_tracker/upc_connect.py | 2 +- tests/test_util/aiohttp.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/device_tracker/upc_connect.py b/homeassistant/components/device_tracker/upc_connect.py index 5de412f05bc..a8d39baed57 100644 --- a/homeassistant/components/device_tracker/upc_connect.py +++ b/homeassistant/components/device_tracker/upc_connect.py @@ -77,7 +77,7 @@ class UPCDeviceScanner(DeviceScanner): finally: self.websession.detach() - hass.buss.async_listen_once( + hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, async_logout) @asyncio.coroutine diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index 7f46f11166d..afe2f626de7 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -17,8 +17,6 @@ class AiohttpClientMocker: self._cookies = {} self.mock_calls = [] - self.cookie_jar = mock.MagicMock() - def request(self, method, url, *, auth=None, status=200, From 9cad9c19f8a29c0173cb7672fd3ad95d91c34ee1 Mon Sep 17 00:00:00 2001 From: Gianluca Barbaro Date: Wed, 25 Jan 2017 19:58:34 +0100 Subject: [PATCH 145/191] Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error --- homeassistant/components/keyboard_remote.py | 136 ++++++++++---------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/homeassistant/components/keyboard_remote.py b/homeassistant/components/keyboard_remote.py index 992f9390124..69151043276 100644 --- a/homeassistant/components/keyboard_remote.py +++ b/homeassistant/components/keyboard_remote.py @@ -1,32 +1,8 @@ """ Receive signals from a keyboard and use it as a remote control. -This component allows to use a keyboard as remote control. It will -fire ´keyboard_remote_command_received´ events witch can then be used -in automation rules. - -The `evdev` package is used to interface with the keyboard and thus this -is Linux only. It also means you can't use your normal keyboard for this, -because `evdev` will block it. - -Example: - keyboard_remote: - device_descriptor: '/dev/input/by-id/foo' - type: 'key_up' # optional alternaive 'key_down' and 'key_hold' - # be carefull, 'key_hold' fires a lot of events - - and an automation rule to bring breath live into it. - - automation: - alias: Keyboard All light on - trigger: - platform: event - event_type: keyboard_remote_command_received - event_data: - key_code: 107 # inspect log to obtain desired keycode - action: - service: light.turn_on - entity_id: light.all +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/keyboard_remote/ """ # pylint: disable=import-error @@ -54,10 +30,14 @@ KEY_CODE = 'key_code' KEY_VALUE = {'key_up': 0, 'key_down': 1, 'key_hold': 2} TYPE = 'type' DEVICE_DESCRIPTOR = 'device_descriptor' +DEVICE_NAME = 'device_name' +DEVICE_ID_GROUP = 'Device descriptor or name' + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(DEVICE_DESCRIPTOR): cv.string, + vol.Exclusive(DEVICE_DESCRIPTOR, DEVICE_ID_GROUP): cv.string, + vol.Exclusive(DEVICE_NAME, DEVICE_ID_GROUP): cv.string, vol.Optional(TYPE, default='key_up'): vol.All(cv.string, vol.Any('key_up', 'key_down', 'key_hold')), }), @@ -67,22 +47,15 @@ CONFIG_SCHEMA = vol.Schema({ def setup(hass, config): """Setup keyboard_remote.""" config = config.get(DOMAIN) - device_descriptor = config.get(DEVICE_DESCRIPTOR) - if not device_descriptor: - id_folder = '/dev/input/' - _LOGGER.error( - 'A device_descriptor must be defined. ' - 'Possible descriptors are %s:\n%s', - id_folder, os.listdir(id_folder) - ) - return - key_value = KEY_VALUE.get(config.get(TYPE, 'key_up')) + if not config.get(DEVICE_DESCRIPTOR) and\ + not config.get(DEVICE_NAME): + _LOGGER.error('No device_descriptor or device_name found.') + return keyboard_remote = KeyboardRemote( hass, - device_descriptor, - key_value + config ) def _start_keyboard_remote(_event): @@ -106,66 +79,93 @@ def setup(hass, config): class KeyboardRemote(threading.Thread): """This interfaces with the inputdevice using evdev.""" - def __init__(self, hass, device_descriptor, key_value): + def __init__(self, hass, config): """Construct a KeyboardRemote interface object.""" - from evdev import InputDevice + from evdev import InputDevice, list_devices - self.device_descriptor = device_descriptor - try: - self.dev = InputDevice(device_descriptor) - except OSError: # Keyboard not present - _LOGGER.debug( - 'KeyboardRemote: keyboard not connected, %s', - self.device_descriptor) - self.keyboard_connected = False + self.device_descriptor = config.get(DEVICE_DESCRIPTOR) + self.device_name = config.get(DEVICE_NAME) + if self.device_descriptor: + self.device_id = self.device_descriptor else: - self.keyboard_connected = True + self.device_id = self.device_name + self.dev = self._get_keyboard_device() + if self.dev is not None: _LOGGER.debug( - 'KeyboardRemote: keyboard connected, %s', - self.dev) + 'Keyboard connected, %s', + self.device_id + ) + else: + id_folder = '/dev/input/by-id/' + device_names = [InputDevice(file_name).name + for file_name in list_devices()] + _LOGGER.debug( + 'Keyboard not connected, %s.\n\ + Check /dev/input/event* permissions.\ + Possible device names are:\n %s.\n \ + Possible device descriptors are %s:\n %s', + self.device_id, + device_names, + id_folder, + os.listdir(id_folder) + ) threading.Thread.__init__(self) self.stopped = threading.Event() self.hass = hass - self.key_value = key_value + self.key_value = KEY_VALUE.get(config.get(TYPE, 'key_up')) + + def _get_keyboard_device(self): + from evdev import InputDevice, list_devices + if self.device_name: + devices = [InputDevice(file_name) for file_name in list_devices()] + for device in devices: + if self.device_name == device.name: + return device + elif self.device_descriptor: + try: + device = InputDevice(self.device_descriptor) + except OSError: + pass + else: + return device + return None def run(self): """Main loop of the KeyboardRemote.""" - from evdev import categorize, ecodes, InputDevice + from evdev import categorize, ecodes - if self.keyboard_connected: + if self.dev is not None: self.dev.grab() _LOGGER.debug( - 'KeyboardRemote interface started for %s', + 'Interface started for %s', self.dev) while not self.stopped.isSet(): # Sleeps to ease load on processor time.sleep(.1) - if not self.keyboard_connected: - try: - self.dev = InputDevice(self.device_descriptor) - except OSError: # still disconnected - continue - else: + if self.dev is None: + self.dev = self._get_keyboard_device() + if self.dev is not None: self.dev.grab() - self.keyboard_connected = True - _LOGGER.debug('KeyboardRemote: keyboard re-connected, %s', - self.device_descriptor) self.hass.bus.fire( KEYBOARD_REMOTE_CONNECTED ) + _LOGGER.debug('Keyboard re-connected, %s', + self.device_id) + else: + continue try: event = self.dev.read_one() except IOError: # Keyboard Disconnected - self.keyboard_connected = False - _LOGGER.debug('KeyboardRemote: keyboard disconnected, %s', - self.device_descriptor) + self.dev = None self.hass.bus.fire( KEYBOARD_REMOTE_DISCONNECTED ) + _LOGGER.debug('Keyboard disconnected, %s', + self.device_id) continue if not event: From 0048267f3e4c358b4dc054e3d6dad96cdae90502 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 22 Jan 2017 12:48:14 +0100 Subject: [PATCH 146/191] homematic: add MAX shutter contact class --- homeassistant/components/homematic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homematic.py b/homeassistant/components/homematic.py index 2b68a268d34..9b38bb0efea 100644 --- a/homeassistant/components/homematic.py +++ b/homeassistant/components/homematic.py @@ -68,7 +68,7 @@ HM_DEVICE_TYPES = { DISCOVER_BINARY_SENSORS: [ 'ShutterContact', 'Smoke', 'SmokeV2', 'Motion', 'MotionV2', 'RemoteMotion', 'WeatherSensor', 'TiltSensor', 'IPShutterContact', - 'HMWIOSwitch'], + 'HMWIOSwitch', 'MaxShutterContact'], DISCOVER_COVER: ['Blind', 'KeyBlind'] } From 7f5d6eb84115f43f2b743bf3b72abe1920ac9664 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 25 Jan 2017 13:03:36 -0800 Subject: [PATCH 147/191] Add a is_coordinator attribute to Sonos (#5556) --- homeassistant/components/media_player/sonos.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 7e9a553fd97..351c0ed4c72 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -58,6 +58,8 @@ CONF_INTERFACE_ADDR = 'interface_addr' # Service call validation schemas ATTR_SLEEP_TIME = 'sleep_time' +ATTR_IS_COORDINATOR = 'is_coordinator' + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_ADVERTISE_ADDR): cv.string, vol.Optional(CONF_INTERFACE_ADDR): cv.string, @@ -973,3 +975,8 @@ class SonosDevice(MediaPlayerDevice): def clear_sleep_timer(self): """Clear the timer on the player.""" self._player.set_sleep_timer(None) + + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + return {ATTR_IS_COORDINATOR: self.is_coordinator} From 58e063a1b6a78fe49633404b1c1b5371f925581e Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 25 Jan 2017 23:15:30 +0200 Subject: [PATCH 148/191] Fix empty image when Sonos is doing nothing. --- homeassistant/components/media_player/sonos.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 7e9a553fd97..748f2cdf8c0 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -653,6 +653,8 @@ class SonosDevice(MediaPlayerDevice): def _format_media_image_url(self, url, fallback_uri): if url in ('', 'NOT_IMPLEMENTED', None): + if fallback_uri in ('', 'NOT_IMPLEMENTED', None): + return None return 'http://{host}:{port}/getaa?s=1&u={uri}'.format( host=self._player.ip_address, port=1400, From 068369c008bb4f0f489af05e6509f1501ca81460 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 25 Jan 2017 15:35:52 -0800 Subject: [PATCH 149/191] Fix grammar in README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1e25b6dcc90..bddbb9fd611 100644 --- a/README.rst +++ b/README.rst @@ -87,7 +87,7 @@ components `__. If you run into issues while using Home Assistant or during development of a component, check the `Home Assistant help -section `__ how to reach us. +section `__ of our website for further help and information. .. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master :target: https://travis-ci.org/home-assistant/home-assistant From 111b482be49a81a51a66693fc337dbf1ccd00ca0 Mon Sep 17 00:00:00 2001 From: John Mihalic Date: Thu, 26 Jan 2017 07:07:50 -0500 Subject: [PATCH 150/191] Update neurio library req. & fix keyerror (#5565) --- homeassistant/components/sensor/neurio_energy.py | 6 +++--- requirements_all.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/neurio_energy.py b/homeassistant/components/sensor/neurio_energy.py index 2315615ca54..1ef328af8f4 100644 --- a/homeassistant/components/sensor/neurio_energy.py +++ b/homeassistant/components/sensor/neurio_energy.py @@ -14,7 +14,7 @@ from homeassistant.const import (CONF_API_KEY, CONF_NAME) from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['neurio==0.2.10'] +REQUIREMENTS = ['neurio==0.3.1'] _LOGGER = logging.getLogger(__name__) @@ -66,7 +66,7 @@ class NeurioEnergy(Entity): @property def name(self): - """Return the name of th sensor.""" + """Return the name of the sensor.""" return self._name @property @@ -94,5 +94,5 @@ class NeurioEnergy(Entity): sample = neurio_client.get_samples_live_last( sensor_id=self.sensor_id) self._state = sample['consumptionPower'] - except (requests.exceptions.RequestException, ValueError): + except (requests.exceptions.RequestException, ValueError, KeyError): _LOGGER.warning('Could not update status for %s', self.name) diff --git a/requirements_all.txt b/requirements_all.txt index b086258b9fd..3f1372ffcce 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -326,7 +326,7 @@ myusps==1.0.2 netdisco==0.8.1 # homeassistant.components.sensor.neurio_energy -neurio==0.2.10 +neurio==0.3.1 # homeassistant.components.google oauth2client==3.0.0 From 4fe54e1cbb18cc02b53c2a6b24b35bee45b867bc Mon Sep 17 00:00:00 2001 From: Hugo Dupras Date: Thu, 26 Jan 2017 15:08:21 +0100 Subject: [PATCH 151/191] Update Netatmo library to fix missing local_url (#5570) --- homeassistant/components/netatmo.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/netatmo.py b/homeassistant/components/netatmo.py index ea691ac94f4..2bd450a2917 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.9.0.zip#lnetatmo==0.9.0'] + 'v0.9.1.zip#lnetatmo==0.9.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 3f1372ffcce..9d707496529 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -221,7 +221,7 @@ https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b6 https://github.com/danieljkemp/onkyo-eiscp/archive/python3.zip#onkyo-eiscp==0.9.2 # homeassistant.components.netatmo -https://github.com/jabesq/netatmo-api-python/archive/v0.9.0.zip#lnetatmo==0.9.0 +https://github.com/jabesq/netatmo-api-python/archive/v0.9.1.zip#lnetatmo==0.9.1 # homeassistant.components.neato https://github.com/jabesq/pybotvac/archive/v0.0.1.zip#pybotvac==0.0.1 From eadf67bd9afa9250d7db83db26230b4fdf175666 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 26 Jan 2017 18:12:30 +0100 Subject: [PATCH 152/191] Update ha-ffmpeg version 1.1 (#5573) --- homeassistant/components/binary_sensor/ffmpeg.py | 1 + homeassistant/components/ffmpeg.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/binary_sensor/ffmpeg.py b/homeassistant/components/binary_sensor/ffmpeg.py index 9da14048705..ea89ff7c743 100644 --- a/homeassistant/components/binary_sensor/ffmpeg.py +++ b/homeassistant/components/binary_sensor/ffmpeg.py @@ -110,6 +110,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): def async_start(event): """Start ffmpeg.""" yield from entity.async_start_ffmpeg() + yield from entity.async_update_ha_state() hass.bus.async_listen_once( EVENT_HOMEASSISTANT_START, async_start) diff --git a/homeassistant/components/ffmpeg.py b/homeassistant/components/ffmpeg.py index 56e1cb8c95d..f9427d4f9d6 100644 --- a/homeassistant/components/ffmpeg.py +++ b/homeassistant/components/ffmpeg.py @@ -12,7 +12,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv DOMAIN = 'ffmpeg' -REQUIREMENTS = ["ha-ffmpeg==1.0"] +REQUIREMENTS = ["ha-ffmpeg==1.1"] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 9d707496529..c52282662d0 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ googlemaps==2.4.4 gps3==0.33.3 # homeassistant.components.ffmpeg -ha-ffmpeg==1.0 +ha-ffmpeg==1.1 # homeassistant.components.media_player.philips_js ha-philipsjs==0.0.1 From 636e7aa31e645a07aa64572dd9443c3e2e869466 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 26 Jan 2017 23:18:15 +0100 Subject: [PATCH 153/191] [microsoft_face] Small error fixes (#5577) * [microsoft_face] Small error fixes * add array to exclusion --- .../components/image_processing/microsoft_face_identify.py | 4 +++- homeassistant/components/microsoft_face.py | 2 +- homeassistant/components/services.yaml | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/image_processing/microsoft_face_identify.py b/homeassistant/components/image_processing/microsoft_face_identify.py index c8cb6fc4080..0402f272eeb 100644 --- a/homeassistant/components/image_processing/microsoft_face_identify.py +++ b/homeassistant/components/image_processing/microsoft_face_identify.py @@ -160,8 +160,10 @@ class MicrosoftFaceIdentifyEntity(ImageProcessingFaceIdentifyEntity): face_data = yield from self._api.call_api( 'post', 'detect', image, binary=True) - face_ids = [data['faceId'] for data in face_data] + if face_data is None or len(face_data) < 1: + return + face_ids = [data['faceId'] for data in face_data] detect = yield from self._api.call_api( 'post', 'identify', {'faceIds': face_ids, 'personGroupId': self._face_group}) diff --git a/homeassistant/components/microsoft_face.py b/homeassistant/components/microsoft_face.py index 3f4483e83c3..dc0e9001b24 100644 --- a/homeassistant/components/microsoft_face.py +++ b/homeassistant/components/microsoft_face.py @@ -367,7 +367,7 @@ class MicrosoftFace(object): answer = yield from response.json() _LOGGER.debug("Read from microsoft face api: %s", answer) - if response.status == 200: + if response.status == 200 or response.status == 202: return answer _LOGGER.warning("Error %d microsoft face api %s", diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index dea38cb77f8..c390f65f5a0 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -154,7 +154,7 @@ microsoft_face: description: Train a person group. fields: - name: + group: description: Name of the group example: 'family' @@ -186,7 +186,7 @@ microsoft_face: description: Add a new picture to a person. fields: - name: + person: description: Name of the person example: 'Hans' From 7136fd0f0a47252ab8b47aa24858e5846793c6d9 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 26 Jan 2017 14:22:47 -0800 Subject: [PATCH 154/191] Add an Amazon Polly TTS platform (#5169) * Remove SPEED_MED from fan * Add an Amazon Polly TTS platform * Update boto library version for notify.aws_* platforms to match the tts.amazon_polly req * Improve log line and add docstring to function * Simplify config logic * Remove duplicate logic * Don't know how this got in here... * initial options work * Remove stale config option and only allow supported languages * Make requested changes * Polly is only supported in some regions * Allow filename to contain underscores (for amazon_polly platform name), remove unnecessary default_lang, other small things * Add options dict to service description --- .coveragerc | 1 + homeassistant/components/notify/aws_lambda.py | 2 +- homeassistant/components/notify/aws_sns.py | 2 +- homeassistant/components/notify/aws_sqs.py | 2 +- homeassistant/components/tts/__init__.py | 2 +- homeassistant/components/tts/amazon_polly.py | 180 ++++++++++++++++++ homeassistant/components/tts/services.yaml | 6 +- requirements_all.txt | 3 +- 8 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/tts/amazon_polly.py diff --git a/.coveragerc b/.coveragerc index b5dda5b6f43..594306bb1db 100644 --- a/.coveragerc +++ b/.coveragerc @@ -378,6 +378,7 @@ omit = homeassistant/components/switch/transmission.py homeassistant/components/switch/wake_on_lan.py homeassistant/components/thingspeak.py + homeassistant/components/tts/amazon_polly.py homeassistant/components/tts/picotts.py homeassistant/components/upnp.py homeassistant/components/weather/bom.py diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/notify/aws_lambda.py index d18da5ae2f0..7bb83db7f82 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -17,7 +17,7 @@ from homeassistant.components.notify import ( import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["boto3==1.3.1"] +REQUIREMENTS = ["boto3==1.4.3"] CONF_REGION = 'region_name' CONF_ACCESS_KEY_ID = 'aws_access_key_id' diff --git a/homeassistant/components/notify/aws_sns.py b/homeassistant/components/notify/aws_sns.py index f02b6b75a84..9b95c486b4d 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/notify/aws_sns.py @@ -17,7 +17,7 @@ from homeassistant.components.notify import ( import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["boto3==1.3.1"] +REQUIREMENTS = ["boto3==1.4.3"] CONF_REGION = 'region_name' CONF_ACCESS_KEY_ID = 'aws_access_key_id' diff --git a/homeassistant/components/notify/aws_sqs.py b/homeassistant/components/notify/aws_sqs.py index ecbadac46ce..76a137734d3 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/notify/aws_sqs.py @@ -16,7 +16,7 @@ from homeassistant.components.notify import ( import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["boto3==1.3.1"] +REQUIREMENTS = ["boto3==1.4.3"] CONF_REGION = 'region_name' CONF_ACCESS_KEY_ID = 'aws_access_key_id' diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index eda0fa27f53..9b4df2749c0 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -54,7 +54,7 @@ ATTR_LANGUAGE = 'language' ATTR_OPTIONS = 'options' _RE_VOICE_FILE = re.compile( - r"([a-f0-9]{40})_([^_]+)_([^_]+)_([a-z]+)\.[a-z0-9]{3,4}") + r"([a-f0-9]{40})_([^_]+)_([^_]+)_([a-z_]+)\.[a-z0-9]{3,4}") KEY_PATTERN = '{0}_{1}_{2}_{3}' PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/tts/amazon_polly.py b/homeassistant/components/tts/amazon_polly.py new file mode 100644 index 00000000000..fe63d72b632 --- /dev/null +++ b/homeassistant/components/tts/amazon_polly.py @@ -0,0 +1,180 @@ +""" +Support for the Amazon Polly text to speech service. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/tts/amazon_polly/ +""" +import logging +import voluptuous as vol + +from homeassistant.components.tts import Provider, PLATFORM_SCHEMA +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) +REQUIREMENTS = ["boto3==1.4.3"] + +CONF_REGION = "region_name" +CONF_ACCESS_KEY_ID = "aws_access_key_id" +CONF_SECRET_ACCESS_KEY = "aws_secret_access_key" +CONF_PROFILE_NAME = "profile_name" +ATTR_CREDENTIALS = "credentials" + +DEFAULT_REGION = "us-east-1" +SUPPORTED_REGIONS = ["us-east-1", "us-east-2", "us-west-2", "eu-west-1"] + +CONF_VOICE = "voice" +CONF_OUTPUT_FORMAT = "output_format" +CONF_SAMPLE_RATE = "sample_rate" +CONF_TEXT_TYPE = "text_type" + +SUPPORTED_VOICES = ["Geraint", "Gwyneth", "Mads", "Naja", "Hans", "Marlene", + "Nicole", "Russell", "Amy", "Brian", "Emma", "Raveena", + "Ivy", "Joanna", "Joey", "Justin", "Kendra", "Kimberly", + "Salli", "Conchita", "Enrique", "Miguel", "Penelope", + "Chantal", "Celine", "Mathieu", "Dora", "Karl", "Carla", + "Giorgio", "Mizuki", "Liv", "Lotte", "Ruben", "Ewa", + "Jacek", "Jan", "Maja", "Ricardo", "Vitoria", "Cristiano", + "Ines", "Carmen", "Maxim", "Tatyana", "Astrid", "Filiz"] + +SUPPORTED_OUTPUT_FORMATS = ["mp3", "ogg_vorbis", "pcm"] + +SUPPORTED_SAMPLE_RATES = ["8000", "16000", "22050"] + +SUPPORTED_SAMPLE_RATES_MAP = { + "mp3": ["8000", "16000", "22050"], + "ogg_vorbis": ["8000", "16000", "22050"], + "pcm": ["8000", "16000"] +} + +SUPPORTED_TEXT_TYPES = ["text", "ssml"] + +CONTENT_TYPE_EXTENSIONS = { + "audio/mpeg": "mp3", + "audio/ogg": "ogg", + "audio/pcm": "pcm" +} + +DEFAULT_VOICE = "Joanna" +DEFAULT_OUTPUT_FORMAT = "mp3" +DEFAULT_TEXT_TYPE = "text" + +DEFAULT_SAMPLE_RATES = { + "mp3": "22050", + "ogg_vorbis": "22050", + "pcm": "16000" +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_REGION, default=DEFAULT_REGION): + vol.In(SUPPORTED_REGIONS), + vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, + vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, + vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, + vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): vol.In(SUPPORTED_VOICES), + vol.Optional(CONF_OUTPUT_FORMAT, default=DEFAULT_OUTPUT_FORMAT): + vol.In(SUPPORTED_OUTPUT_FORMATS), + vol.Optional(CONF_SAMPLE_RATE): vol.All(cv.string, + vol.In(SUPPORTED_SAMPLE_RATES)), + vol.Optional(CONF_TEXT_TYPE, default=DEFAULT_TEXT_TYPE): + vol.In(SUPPORTED_TEXT_TYPES), +}) + + +def get_engine(hass, config): + """Setup Amazon Polly speech component.""" + # pylint: disable=import-error + output_format = config.get(CONF_OUTPUT_FORMAT) + sample_rate = config.get(CONF_SAMPLE_RATE, + DEFAULT_SAMPLE_RATES[output_format]) + if sample_rate not in SUPPORTED_SAMPLE_RATES_MAP.get(output_format): + _LOGGER.error("%s is not a valid sample rate for %s", + sample_rate, output_format) + return None + + config[CONF_SAMPLE_RATE] = sample_rate + + import boto3 + + profile = config.get(CONF_PROFILE_NAME) + + if profile is not None: + boto3.setup_default_session(profile_name=profile) + + aws_config = { + CONF_REGION: config.get(CONF_REGION), + CONF_ACCESS_KEY_ID: config.get(CONF_ACCESS_KEY_ID), + CONF_SECRET_ACCESS_KEY: config.get(CONF_SECRET_ACCESS_KEY), + } + + del config[CONF_REGION] + del config[CONF_ACCESS_KEY_ID] + del config[CONF_SECRET_ACCESS_KEY] + + polly_client = boto3.client("polly", **aws_config) + + supported_languages = [] + + all_voices = {} + + all_voices_req = polly_client.describe_voices() + + for voice in all_voices_req.get("Voices"): + all_voices[voice.get("Id")] = voice + if voice.get("LanguageCode") not in supported_languages: + supported_languages.append(voice.get("LanguageCode")) + + return AmazonPollyProvider(polly_client, config, supported_languages, + all_voices) + + +class AmazonPollyProvider(Provider): + """Amazon Polly speech api provider.""" + + def __init__(self, polly_client, config, supported_languages, + all_voices): + """Initialize Amazon Polly provider for TTS.""" + self.client = polly_client + self.config = config + self.supported_langs = supported_languages + self.all_voices = all_voices + self.default_voice = self.config.get(CONF_VOICE) + + @property + def supported_languages(self): + """List of supported languages.""" + return self.supported_langs + + @property + def default_language(self): + """Default language.""" + return self.all_voices.get(self.default_voice).get("LanguageCode") + + @property + def default_options(self): + """Dict include default options.""" + return {CONF_VOICE: self.default_voice} + + @property + def supported_options(self): + """List of supported options.""" + return [CONF_VOICE] + + def get_tts_audio(self, message, language=None, options=None): + """Request TTS file from Polly.""" + voice_id = options.get(CONF_VOICE, self.default_voice) + voice_in_dict = self.all_voices.get(voice_id) + if language is not voice_in_dict.get("LanguageCode"): + _LOGGER.error("%s does not support the %s language", + voice_id, language) + return (None, None) + + resp = self.client.synthesize_speech( + OutputFormat=self.config[CONF_OUTPUT_FORMAT], + SampleRate=self.config[CONF_SAMPLE_RATE], + Text=message, + TextType=self.config[CONF_TEXT_TYPE], + VoiceId=voice_id + ) + + return (CONTENT_TYPE_EXTENSIONS[resp.get("ContentType")], + resp.get("AudioStream").read()) diff --git a/homeassistant/components/tts/services.yaml b/homeassistant/components/tts/services.yaml index b44ef6ac66c..7e69d4939f0 100644 --- a/homeassistant/components/tts/services.yaml +++ b/homeassistant/components/tts/services.yaml @@ -16,7 +16,11 @@ say: language: description: Language to use for speech generation. - example: 'ru' + example: 'ru' + + options: + description: A dictionary containing platform-specific options. Optional depending on the platform. + example: platform specific clear_cache: description: Remove cache files and RAM cache. diff --git a/requirements_all.txt b/requirements_all.txt index c52282662d0..9db19cba17f 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -72,7 +72,8 @@ blockchain==1.3.3 # homeassistant.components.notify.aws_lambda # homeassistant.components.notify.aws_sns # homeassistant.components.notify.aws_sqs -boto3==1.3.1 +# homeassistant.components.tts.amazon_polly +boto3==1.4.3 # homeassistant.components.sensor.broadlink # homeassistant.components.switch.broadlink From 36e47473c50db42a54ae6292e4634efcf972034a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 27 Jan 2017 00:22:31 +0100 Subject: [PATCH 155/191] pump ffmpeg version 1.2 to fix close bug (#5579) --- homeassistant/components/ffmpeg.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ffmpeg.py b/homeassistant/components/ffmpeg.py index f9427d4f9d6..2a498198e3c 100644 --- a/homeassistant/components/ffmpeg.py +++ b/homeassistant/components/ffmpeg.py @@ -12,7 +12,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv DOMAIN = 'ffmpeg' -REQUIREMENTS = ["ha-ffmpeg==1.1"] +REQUIREMENTS = ["ha-ffmpeg==1.2"] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 9db19cba17f..de4a2cbeab3 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -175,7 +175,7 @@ googlemaps==2.4.4 gps3==0.33.3 # homeassistant.components.ffmpeg -ha-ffmpeg==1.1 +ha-ffmpeg==1.2 # homeassistant.components.media_player.philips_js ha-philipsjs==0.0.1 From fb49c588e57056123f8f829a8f8d18fd139deb74 Mon Sep 17 00:00:00 2001 From: John Mihalic Date: Thu, 26 Jan 2017 18:30:42 -0500 Subject: [PATCH 156/191] Handle Squeezebox issues (#5566) * Handle Squeezebox issues * Fix double logging --- homeassistant/components/media_player/squeezebox.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index c338c6dffd8..852ce522559 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -182,7 +182,7 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def state(self): """Return the state of the device.""" - if 'power' in self._status and self._status['power'] == '0': + if 'power' in self._status and self._status['power'] == 0: return STATE_OFF if 'mode' in self._status: if self._status['mode'] == 'pause': @@ -213,8 +213,16 @@ class SqueezeBoxDevice(MediaPlayerDevice): "status", "-", "1", "tags:{tags}" .format(tags=tags)) + if response is False: + return + + self._status = response.copy() + + try: + self._status.update(response["playlist_loop"][0]) + except KeyError: + pass try: - self._status = response.copy() self._status.update(response["remoteMeta"]) except KeyError: pass From 61ad11fcd74311d854bea2021cf6d7a8288600eb Mon Sep 17 00:00:00 2001 From: fakezeta Date: Fri, 27 Jan 2017 00:32:01 +0100 Subject: [PATCH 157/191] Added notify.twilio_call component for Voice calling with Twilio --- .../components/notify/twilio_call.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 homeassistant/components/notify/twilio_call.py diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py new file mode 100644 index 00000000000..8bc9185e789 --- /dev/null +++ b/homeassistant/components/notify/twilio_call.py @@ -0,0 +1,65 @@ +""" +Twilio Call platform for notify component. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/notify.twilio_call/ +""" +import logging +import urllib + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.notify import ( + ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) + +_LOGGER = logging.getLogger(__name__) +REQUIREMENTS = ["twilio==5.4.0"] + + +CONF_ACCOUNT_SID = "account_sid" +CONF_AUTH_TOKEN = "auth_token" +CONF_FROM_NUMBER = "from_number" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_ACCOUNT_SID): cv.string, + vol.Required(CONF_AUTH_TOKEN): cv.string, + vol.Required(CONF_FROM_NUMBER): + vol.All(cv.string, vol.Match(r"^\+?[1-9]\d{1,14}$")), +}) + + +def get_service(hass, config, discovery_info=None): + """Get the Twilio Call notification service.""" + # pylint: disable=import-error + from twilio.rest import TwilioRestClient + + twilio_client = TwilioRestClient(config[CONF_ACCOUNT_SID], + config[CONF_AUTH_TOKEN]) + + return TwilioCallNotificationService(twilio_client, + config[CONF_FROM_NUMBER]) + + +class TwilioCallNotificationService(BaseNotificationService): + """Implement the notification service for the Twilio Call service.""" + + def __init__(self, twilio_client, from_number): + """Initialize the service.""" + self.client = twilio_client + self.from_number = from_number + + def send_message(self, message="", **kwargs): + """Call to specified target users.""" + targets = kwargs.get(ATTR_TARGET) + + if not targets: + _LOGGER.info("At least 1 target is required") + return + + for target in targets: + twimlet_url = 'http://twimlets.com/message?Me=' + twimlet_url += urllib.parse.quote(message, safe='') + self.client.calls.create(to=target, + url=twimlet_url, + from_=self.from_number) From 3f13bdb1f73973ef12102c1e48cf6025ceea229d Mon Sep 17 00:00:00 2001 From: fakezeta Date: Fri, 27 Jan 2017 00:48:58 +0100 Subject: [PATCH 158/191] Modified .coveragerc --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index b5dda5b6f43..d23a4cc2ce4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -271,6 +271,7 @@ omit = homeassistant/components/notify/telegram.py homeassistant/components/notify/telstra.py homeassistant/components/notify/twilio_sms.py + homeassistant/components/notify/twilio_call.py homeassistant/components/notify/twitter.py homeassistant/components/notify/xmpp.py homeassistant/components/nuimo_controller.py From e4f0b0a57f8b2e01493a53668d84ba76bc3e9346 Mon Sep 17 00:00:00 2001 From: fakezeta Date: Fri, 27 Jan 2017 01:12:28 +0100 Subject: [PATCH 159/191] Updated requirements_all.txt --- requirements_all.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_all.txt b/requirements_all.txt index de4a2cbeab3..1a6d008ab35 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -634,6 +634,7 @@ tikteck==0.4 # homeassistant.components.switch.transmission transmissionrpc==0.11 +# homeassistant.components.notify.twilio_call # homeassistant.components.notify.twilio_sms twilio==5.4.0 From a465a45588316c261e5a778a4e045236ce36b92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Fri, 27 Jan 2017 06:41:30 +0100 Subject: [PATCH 160/191] Fix for assumed state in command_line (#5578) * Bug fix for assumed state in command_line * command line * command line * command line * command line test --- homeassistant/components/switch/command_line.py | 2 +- tests/components/switch/test_command_line.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switch/command_line.py b/homeassistant/components/switch/command_line.py index eca8f8b6023..e80348b0bee 100644 --- a/homeassistant/components/switch/command_line.py +++ b/homeassistant/components/switch/command_line.py @@ -124,7 +124,7 @@ class CommandSwitch(SwitchDevice): @property def assumed_state(self): """Return true if we do optimistic updates.""" - return self._command_state is False + return self._command_state is None def _query_state(self): """Query for state.""" diff --git a/tests/components/switch/test_command_line.py b/tests/components/switch/test_command_line.py index 008727df7f8..87eb12d8508 100644 --- a/tests/components/switch/test_command_line.py +++ b/tests/components/switch/test_command_line.py @@ -159,7 +159,7 @@ class TestCommandSwitch(unittest.TestCase): state = self.hass.states.get('switch.test') self.assertEqual(STATE_ON, state.state) - def test_assumed_state_should_be_true_if_command_state_is_false(self): + def test_assumed_state_should_be_true_if_command_state_is_none(self): """Test with state value.""" # args: hass, device_name, friendly_name, command_on, command_off, # command_state, value_template @@ -169,7 +169,7 @@ class TestCommandSwitch(unittest.TestCase): "Test friendly name!", "echo 'on command'", "echo 'off command'", - False, + None, None, ] From 3f2fdb97a0f425c1ced82af757aa460dbcecb47c Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Fri, 27 Jan 2017 07:42:14 +0200 Subject: [PATCH 161/191] check_config: Add support for packages (#5574) --- homeassistant/scripts/check_config.py | 38 +++++++++++++++++---------- tests/scripts/test_check_config.py | 21 +++++++++++++++ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index 19dc275e163..cb825ad44c8 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -27,7 +27,9 @@ MOCKS = { 'get': ("homeassistant.loader.get_component", loader.get_component), 'secrets': ("homeassistant.util.yaml._secret_yaml", yaml._secret_yaml), 'except': ("homeassistant.bootstrap.async_log_exception", - bootstrap.async_log_exception) + bootstrap.async_log_exception), + 'package_error': ("homeassistant.config._log_pkg_error", + config_util._log_pkg_error), } SILENCE = ( 'homeassistant.bootstrap.clear_secret_cache', @@ -213,6 +215,15 @@ def check(config_path): MOCKS['except'][1](ex, domain, config, hass) res['except'][domain] = config.get(domain, config) + def mock_package_error( # pylint: disable=unused-variable + package, component, config, message): + """Mock config_util._log_pkg_error.""" + MOCKS['package_error'][1](package, component, config, message) + + pkg_key = 'homeassistant.packages.{}'.format(package) + res['except'][pkg_key] = config.get('homeassistant', {}) \ + .get('packages', {}).get(package) + # Patches to skip functions for sil in SILENCE: PATCHES[sil] = patch(sil) @@ -247,25 +258,24 @@ def check(config_path): return res +def line_info(obj, **kwargs): + """Display line config source.""" + if hasattr(obj, '__config_file__'): + return color('cyan', "[source {}:{}]" + .format(obj.__config_file__, obj.__line__ or '?'), + **kwargs) + return '?' + + def dump_dict(layer, indent_count=3, listi=False, **kwargs): """Display a dict. A friendly version of print yaml.yaml.dump(config). """ - def line_src(this): - """Display line config source.""" - if hasattr(this, '__config_file__'): - return color('cyan', "[source {}:{}]" - .format(this.__config_file__, this.__line__ or '?'), - **kwargs) - return '' - def sort_dict_key(val): """Return the dict key for sorting.""" - skey = str.lower(val[0]) - if str(skey) == 'platform': - skey = '0' - return skey + key = str.lower(val[0]) + return '0' if key == 'platform' else key indent_str = indent_count * ' ' if listi or isinstance(layer, list): @@ -273,7 +283,7 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs): if isinstance(layer, Dict): for key, value in sorted(layer.items(), key=sort_dict_key): if isinstance(value, dict) or isinstance(value, list): - print(indent_str, key + ':', line_src(value)) + print(indent_str, key + ':', line_info(value, **kwargs)) dump_dict(value, indent_count + 2) else: print(indent_str, key + ':', value) diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index b4994c5f136..1d0bbbd8dfd 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -180,3 +180,24 @@ class TestCheckConfig(unittest.TestCase): 'secrets': {'http_pw': 'abc123'}, 'yaml_files': ['.../secret.yaml', '.../secrets.yaml'] }, res) + + def test_package_invalid(self): \ + # pylint: disable=no-self-use,invalid-name + """Test a valid platform setup.""" + files = { + 'bad.yaml': BASE_CONFIG + (' packages:\n' + ' p1:\n' + ' group: ["a"]'), + } + with patch_yaml_files(files): + res = check_config.check(get_test_config_dir('bad.yaml')) + change_yaml_files(res) + + err = res['except'].pop('homeassistant.packages.p1') + assert res['except'] == {} + assert err == {'group': ['a']} + assert res['yaml_files'] == ['.../bad.yaml'] + + assert res['components'] == {} + assert res['secret_cache'] == {} + assert res['secrets'] == {} From 295a23237465d378a96dccb7c88839ab87eedca5 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 26 Jan 2017 21:43:45 -0800 Subject: [PATCH 162/191] Allow a protocol in the http.base_url parameter (#5557) --- homeassistant/remote.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/remote.py b/homeassistant/remote.py index 69e8b88305f..c124d509f9c 100644 --- a/homeassistant/remote.py +++ b/homeassistant/remote.py @@ -61,7 +61,9 @@ class API(object): self.port = port self.api_password = api_password - if use_ssl: + if host.startswith(("http://", "https://")): + self.base_url = host + elif use_ssl: self.base_url = "https://{}".format(host) else: self.base_url = "http://{}".format(host) From d0538fe3aad44f9a12f7e2ebf8f1117d38a494be Mon Sep 17 00:00:00 2001 From: John Arild Berentsen Date: Fri, 27 Jan 2017 06:45:04 +0100 Subject: [PATCH 163/191] [lock.zwave] Add set, get and clear usercodes for zwave locks (#5489) * Add set, get and clear usercodes for zwave locks * Fix CRLF --- homeassistant/components/lock/services.yaml | 78 ++++++++++++----- homeassistant/components/lock/zwave.py | 97 ++++++++++++++++++++- 2 files changed, 153 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/lock/services.yaml b/homeassistant/components/lock/services.yaml index 40a7c3ffe38..6b12d49302d 100644 --- a/homeassistant/components/lock/services.yaml +++ b/homeassistant/components/lock/services.yaml @@ -1,21 +1,57 @@ -lock: - description: Lock all or specified locks - - fields: - entity_id: - description: Name of lock to lock - example: 'lock.front_door' - code: - description: An optional code to lock the lock with - example: 1234 - -unlock: - description: Unlock all or specified locks - - fields: - entity_id: - description: Name of lock to unlock - example: 'lock.front_door' - code: - description: An optional code to unlock the lock with - example: 1234 +clear_usercode: + description: Clear a usercode from lock + + fields: + node_id: + description: Node id of the lock + example: 18 + code_slot: + description: Code slot to clear code from + example: 1 + +get_usercode: + description: Retrieve a usercode from lock + + fields: + node_id: + description: Node id of the lock + example: 18 + code_slot: + description: Code slot to retrive a code from + example: 1 + +lock: + description: Lock all or specified locks + + fields: + entity_id: + description: Name of lock to lock + example: 'lock.front_door' + code: + description: An optional code to lock the lock with + example: 1234 + +set_usercode: + description: Set a usercode to lock + + fields: + node_id: + description: Node id of the lock + example: 18 + code_slot: + description: Code slot to set the code + example: 1 + usercode: + description: Code to set + example: 1234 + +unlock: + description: Unlock all or specified locks + + fields: + entity_id: + description: Name of lock to unlock + example: 'lock.front_door' + code: + description: An optional code to unlock the lock with + example: 1234 diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index 9dbbb8e733f..16f2f82d81e 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -7,14 +7,25 @@ https://home-assistant.io/components/lock.zwave/ # Because we do not compile openzwave on CI # pylint: disable=import-error import logging +from os import path + +import voluptuous as vol from homeassistant.components.lock import DOMAIN, LockDevice from homeassistant.components import zwave +from homeassistant.config import load_yaml_config_file _LOGGER = logging.getLogger(__name__) ATTR_NOTIFICATION = 'notification' ATTR_LOCK_STATUS = 'lock_status' +ATTR_CODE_SLOT = 'code_slot' +ATTR_USERCODE = 'usercode' + +SERVICE_SET_USERCODE = 'set_usercode' +SERVICE_GET_USERCODE = 'get_usercode' +SERVICE_CLEAR_USERCODE = 'clear_usercode' + LOCK_NOTIFICATION = { 1: 'Manual Lock', 2: 'Manual Unlock', @@ -80,6 +91,22 @@ ALARM_TYPE_STD = [ 113 ] +SET_USERCODE_SCHEMA = vol.Schema({ + vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int), + vol.Required(ATTR_CODE_SLOT): vol.Coerce(int), + vol.Required(ATTR_USERCODE): vol.Coerce(int), +}) + +GET_USERCODE_SCHEMA = vol.Schema({ + vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int), + vol.Required(ATTR_CODE_SLOT): vol.Coerce(int), +}) + +CLEAR_USERCODE_SCHEMA = vol.Schema({ + vol.Required(zwave.const.ATTR_NODE_ID): vol.Coerce(int), + vol.Required(ATTR_CODE_SLOT): vol.Coerce(int), +}) + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): @@ -90,13 +117,81 @@ def setup_platform(hass, config, add_devices, discovery_info=None): node = zwave.NETWORK.nodes[discovery_info[zwave.const.ATTR_NODE_ID]] value = node.values[discovery_info[zwave.const.ATTR_VALUE_ID]] + descriptions = load_yaml_config_file( + path.join(path.dirname(__file__), 'services.yaml')) + + def set_usercode(service): + """Set the usercode to index X on the lock.""" + node_id = service.data.get(zwave.const.ATTR_NODE_ID) + lock_node = zwave.NETWORK.nodes[node_id] + code_slot = service.data.get(ATTR_CODE_SLOT) + usercode = service.data.get(ATTR_USERCODE) + + for value in lock_node.get_values( + class_id=zwave.const.COMMAND_CLASS_USER_CODE).values(): + if value.index != code_slot: + continue + if len(str(usercode)) > 4: + _LOGGER.error('Invalid code provided: (%s)' + ' usercode must %s or less digits', + usercode, len(value.data)) + value.data = str(usercode) + break + + def get_usercode(service): + """Get a usercode at index X on the lock.""" + node_id = service.data.get(zwave.const.ATTR_NODE_ID) + lock_node = zwave.NETWORK.nodes[node_id] + code_slot = service.data.get(ATTR_CODE_SLOT) + + for value in lock_node.get_values( + class_id=zwave.const.COMMAND_CLASS_USER_CODE).values(): + if value.index != code_slot: + continue + _LOGGER.info('Usercode at slot %s is: %s', value.index, value.data) + break + + def clear_usercode(service): + """Set usercode to slot X on the lock.""" + node_id = service.data.get(zwave.const.ATTR_NODE_ID) + lock_node = zwave.NETWORK.nodes[node_id] + code_slot = service.data.get(ATTR_CODE_SLOT) + data = '' + + for value in lock_node.get_values( + class_id=zwave.const.COMMAND_CLASS_USER_CODE).values(): + if value.index != code_slot: + continue + for i in range(len(value.data)): + data += '\0' + i += 1 + _LOGGER.debug('Data to clear lock: %s', data) + value.data = data + _LOGGER.info('Usercode at slot %s is cleared', value.index) + break + if value.command_class != zwave.const.COMMAND_CLASS_DOOR_LOCK: return if value.type != zwave.const.TYPE_BOOL: return if value.genre != zwave.const.GENRE_USER: return - + if node.has_command_class(zwave.const.COMMAND_CLASS_USER_CODE): + hass.services.register(DOMAIN, + SERVICE_SET_USERCODE, + set_usercode, + descriptions.get(SERVICE_SET_USERCODE), + schema=SET_USERCODE_SCHEMA) + hass.services.register(DOMAIN, + SERVICE_GET_USERCODE, + get_usercode, + descriptions.get(SERVICE_GET_USERCODE), + schema=GET_USERCODE_SCHEMA) + hass.services.register(DOMAIN, + SERVICE_CLEAR_USERCODE, + clear_usercode, + descriptions.get(SERVICE_CLEAR_USERCODE), + schema=CLEAR_USERCODE_SCHEMA) value.set_change_verified(False) add_devices([ZwaveLock(value)]) From 923431110a0cb6dfeefc17225058e1f5e85b287c Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Fri, 27 Jan 2017 01:21:33 -0500 Subject: [PATCH 164/191] [*.zwave] Refactor of zwave value_changed (#5512) * Refactor of zwave value_changed * Rename update_properties to update * Revert "Rename update_properties to update" This reverts commit 723578e7d4e2156507c70f649a124a6193edc0db. --- .../components/binary_sensor/zwave.py | 27 +++-------- homeassistant/components/climate/zwave.py | 15 +----- homeassistant/components/cover/zwave.py | 28 +---------- homeassistant/components/light/zwave.py | 48 ++++++++----------- homeassistant/components/lock/zwave.py | 15 +----- homeassistant/components/sensor/zwave.py | 20 ++------ homeassistant/components/switch/zwave.py | 16 +------ homeassistant/components/zwave/__init__.py | 25 +++++++++- 8 files changed, 58 insertions(+), 136 deletions(-) diff --git a/homeassistant/components/binary_sensor/zwave.py b/homeassistant/components/binary_sensor/zwave.py index 32de0f653a7..b0054d7b00f 100644 --- a/homeassistant/components/binary_sensor/zwave.py +++ b/homeassistant/components/binary_sensor/zwave.py @@ -8,7 +8,6 @@ import logging import datetime import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_point_in_time -from homeassistant.helpers.entity import Entity from homeassistant.components import zwave from homeassistant.components.binary_sensor import ( DOMAIN, @@ -65,21 +64,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices([ZWaveBinarySensor(value, None)]) -class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity, Entity): +class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity): """Representation of a binary sensor within Z-Wave.""" def __init__(self, value, sensor_class): """Initialize the sensor.""" self._sensor_type = sensor_class - # pylint: disable=import-error - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher - zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN) - dispatcher.connect( - self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) - @property def is_on(self): """Return True if the binary sensor is on.""" @@ -95,32 +87,25 @@ class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity, Entity): """No polling needed.""" return False - def value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id or \ - self._value.node == value.node: - _LOGGER.debug('Value changed for label %s', self._value.label) - self.schedule_update_ha_state() - -class ZWaveTriggerSensor(ZWaveBinarySensor, Entity): +class ZWaveTriggerSensor(ZWaveBinarySensor): """Representation of a stateless sensor within Z-Wave.""" - def __init__(self, sensor_value, sensor_class, hass, re_arm_sec=60): + def __init__(self, value, sensor_class, hass, re_arm_sec=60): """Initialize the sensor.""" - super(ZWaveTriggerSensor, self).__init__(sensor_value, sensor_class) + super(ZWaveTriggerSensor, self).__init__(value, sensor_class) self._hass = hass self.re_arm_sec = re_arm_sec self.invalidate_after = dt_util.utcnow() + datetime.timedelta( seconds=self.re_arm_sec) # If it's active make sure that we set the timeout tracker - if sensor_value.data: + if value.data: track_point_in_time( self._hass, self.async_update_ha_state, self.invalidate_after) def value_changed(self, value): - """Called when a value has changed on the network.""" + """Called when a value for this entity's node has changed.""" if self._value.value_id == value.value_id: self.schedule_update_ha_state() if value.data: diff --git a/homeassistant/components/climate/zwave.py b/homeassistant/components/climate/zwave.py index f7bbe341cf7..fc2e8736ee9 100755 --- a/homeassistant/components/climate/zwave.py +++ b/homeassistant/components/climate/zwave.py @@ -52,8 +52,6 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): def __init__(self, value, temp_unit): """Initialize the Z-Wave climate device.""" - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher ZWaveDeviceEntity.__init__(self, value, DOMAIN) self._index = value.index self._node = value.node @@ -71,9 +69,6 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): _LOGGER.debug("temp_unit is %s", self._unit) self._zxt_120 = None self.update_properties() - # register listener - dispatcher.connect( - self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) # Make sure that we have values for the key before converting to int if (value.node.manufacturer_id.strip() and value.node.product_id.strip()): @@ -85,16 +80,8 @@ class ZWaveClimate(ZWaveDeviceEntity, ClimateDevice): " workaround") self._zxt_120 = 1 - def value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id or \ - self._value.node == value.node: - _LOGGER.debug('Value changed for label %s', self._value.label) - self.update_properties() - self.schedule_update_ha_state() - def update_properties(self): - """Callback on data change for the registered node/value pair.""" + """Callback on data changes for node values.""" # Operation Mode for value in self._node.get_values( class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_MODE).values(): diff --git a/homeassistant/components/cover/zwave.py b/homeassistant/components/cover/zwave.py index 89947e3e4fc..d9d33942e15 100644 --- a/homeassistant/components/cover/zwave.py +++ b/homeassistant/components/cover/zwave.py @@ -53,8 +53,6 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): def __init__(self, value): """Initialize the zwave rollershutter.""" import libopenzwave - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher ZWaveDeviceEntity.__init__(self, value, DOMAIN) # pylint: disable=no-member self._lozwmgr = libopenzwave.PyManager() @@ -62,8 +60,6 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): self._node = value.node self._current_position = None self._workaround = None - dispatcher.connect( - self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) if (value.node.manufacturer_id.strip() and value.node.product_id.strip()): specific_sensor_key = (int(value.node.manufacturer_id, 16), @@ -74,16 +70,8 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): _LOGGER.debug("Controller without positioning feedback") self._workaround = 1 - def value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id or \ - self._value.node == value.node: - _LOGGER.debug('Value changed for label %s', self._value.label) - self.update_properties() - self.schedule_update_ha_state() - def update_properties(self): - """Callback on data change for the registered node/value pair.""" + """Callback on data changes for node values.""" # Position value for value in self._node.get_values( class_id=zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL).values(): @@ -160,24 +148,12 @@ class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice): def __init__(self, value): """Initialize the zwave garage door.""" - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher ZWaveDeviceEntity.__init__(self, value, DOMAIN) - self._state = value.data - dispatcher.connect( - self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) - - def value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id: - _LOGGER.debug('Value changed for label %s', self._value.label) - self._state = value.data - self.schedule_update_ha_state() @property def is_closed(self): """Return the current position of Zwave garage door.""" - return not self._state + return not self._value.data def close_cover(self): """Close the garage door.""" diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index 754c27cbad3..ab6cb3cdecd 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -90,9 +90,6 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light): def __init__(self, value, refresh, delay): """Initialize the light.""" - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher - zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN) self._brightness = None self._state = None @@ -118,38 +115,33 @@ class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light): self._timer = None _LOGGER.debug('self._refreshing=%s self.delay=%s', self._refresh_value, self._delay) - dispatcher.connect( - self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) def update_properties(self): """Update internal properties based on zwave values.""" # Brightness self._brightness, self._state = brightness_state(self._value) - def _value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id or \ - self._value.node == value.node: - _LOGGER.debug('Value changed for label %s', self._value.label) - if self._refresh_value: - if self._refreshing: - self._refreshing = False - self.update_properties() - else: - def _refresh_value(): - """Used timer callback for delayed value refresh.""" - self._refreshing = True - self._value.refresh() - - if self._timer is not None and self._timer.isAlive(): - self._timer.cancel() - - self._timer = Timer(self._delay, _refresh_value) - self._timer.start() - self.schedule_update_ha_state() - else: + def value_changed(self, value): + """Called when a value for this entity's node has changed.""" + if self._refresh_value: + if self._refreshing: + self._refreshing = False self.update_properties() - self.schedule_update_ha_state() + else: + def _refresh_value(): + """Used timer callback for delayed value refresh.""" + self._refreshing = True + self._value.refresh() + + if self._timer is not None and self._timer.isAlive(): + self._timer.cancel() + + self._timer = Timer(self._delay, _refresh_value) + self._timer.start() + self.schedule_update_ha_state() + else: + self.update_properties() + self.schedule_update_ha_state() @property def brightness(self): diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index 16f2f82d81e..6ff628f158f 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -201,29 +201,16 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice): def __init__(self, value): """Initialize the Z-Wave switch device.""" - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher - zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN) self._node = value.node self._state = None self._notification = None self._lock_status = None - dispatcher.connect( - self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) self.update_properties() - def _value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id or \ - self._value.node == value.node: - _LOGGER.debug('Value changed for label %s', self._value.label) - self.update_properties() - self.schedule_update_ha_state() - def update_properties(self): - """Callback on data change for the registered node/value pair.""" + """Callback on data changes for node values.""" for value in self._node.get_values( class_id=zwave.const.COMMAND_CLASS_ALARM).values(): if value.label != "Access Control": diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 67e2801974f..c66816541fb 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -10,7 +10,6 @@ import logging from homeassistant.components.sensor import DOMAIN from homeassistant.components import zwave from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT -from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) @@ -48,18 +47,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices([ZWaveAlarmSensor(value)]) -class ZWaveSensor(zwave.ZWaveDeviceEntity, Entity): +class ZWaveSensor(zwave.ZWaveDeviceEntity): """Representation of a Z-Wave sensor.""" - def __init__(self, sensor_value): + def __init__(self, value): """Initialize the sensor.""" - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher - - zwave.ZWaveDeviceEntity.__init__(self, sensor_value, DOMAIN) - - dispatcher.connect( - self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) + zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN) @property def state(self): @@ -71,13 +64,6 @@ class ZWaveSensor(zwave.ZWaveDeviceEntity, Entity): """Return the unit of measurement the value is expressed in.""" return self._value.units - def value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id or \ - self._value.node == value.node: - _LOGGER.debug('Value changed for label %s', self._value.label) - self.schedule_update_ha_state() - class ZWaveMultilevelSensor(ZWaveSensor): """Representation of a multi level sensor Z-Wave sensor.""" diff --git a/homeassistant/components/switch/zwave.py b/homeassistant/components/switch/zwave.py index 2f409f94ef3..fa50156ba4e 100644 --- a/homeassistant/components/switch/zwave.py +++ b/homeassistant/components/switch/zwave.py @@ -37,26 +37,12 @@ class ZwaveSwitch(zwave.ZWaveDeviceEntity, SwitchDevice): def __init__(self, value): """Initialize the Z-Wave switch device.""" - from openzwave.network import ZWaveNetwork - from pydispatch import dispatcher - zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN) - self._state = value.data - dispatcher.connect( - self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) - - def _value_changed(self, value): - """Called when a value has changed on the network.""" - if self._value.value_id == value.value_id: - _LOGGER.debug('Value changed for label %s', self._value.label) - self._state = value.data - self.schedule_update_ha_state() - @property def is_on(self): """Return true if device is on.""" - return self._state + return self._value.data def turn_on(self, **kwargs): """Turn the device on.""" diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 82c116aaa5c..c4b51ca9451 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -15,6 +15,7 @@ from homeassistant.helpers import discovery from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_LOCATION, ATTR_ENTITY_ID, CONF_CUSTOMIZE, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) +from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_change from homeassistant.util import convert, slugify import homeassistant.config as conf_util @@ -600,14 +601,36 @@ def setup(hass, config): return True -class ZWaveDeviceEntity: +class ZWaveDeviceEntity(Entity): """Representation of a Z-Wave node entity.""" def __init__(self, value, domain): """Initialize the z-Wave device.""" + # pylint: disable=import-error + from openzwave.network import ZWaveNetwork + from pydispatch import dispatcher self._value = value self.entity_id = "{}.{}".format(domain, self._object_id()) + dispatcher.connect( + self.network_value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) + + def network_value_changed(self, value): + """Called when a value has changed on the network.""" + if self._value.value_id == value.value_id or \ + self._value.node == value.node: + _LOGGER.debug('Value changed for label %s', self._value.label) + self.value_changed(value) + + def value_changed(self, value): + """Called when a value for this entity's node has changed.""" + self.update_properties() + self.schedule_update_ha_state() + + def update_properties(self): + """Callback on data changes for node values.""" + pass + @property def should_poll(self): """No polling needed.""" From f2870c310331f70dc2df3916e083550d91ef7eff Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Fri, 27 Jan 2017 08:26:49 +0200 Subject: [PATCH 165/191] [core.config] Support customize in packages (#5543) * Support customize in packages * GMT * Update test_config.py --- homeassistant/config.py | 27 ++++++++++++++++++++++----- tests/test_config.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/homeassistant/config.py b/homeassistant/config.py index a2862c95216..2714f066035 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -4,7 +4,6 @@ from collections import OrderedDict import logging import os import shutil -from types import MappingProxyType # pylint: disable=unused-import from typing import Any, List, Tuple # NOQA @@ -121,9 +120,8 @@ CORE_CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit, CONF_UNIT_SYSTEM: cv.unit_system, CONF_TIME_ZONE: cv.time_zone, - vol.Required(CONF_CUSTOMIZE, - default=MappingProxyType({})): vol.All( - _convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY]), + vol.Optional(CONF_CUSTOMIZE, default=[]): vol.All( + _convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY]), vol.Optional(CONF_PACKAGES, default={}): PACKAGES_CONFIG_SCHEMA, }) @@ -308,7 +306,9 @@ def async_process_ha_core_config(hass, config): if CONF_TIME_ZONE in config: set_time_zone(config.get(CONF_TIME_ZONE)) - set_customize(hass, config.get(CONF_CUSTOMIZE) or {}) + customize = merge_packages_customize( + config[CONF_CUSTOMIZE], config[CONF_PACKAGES]) + set_customize(hass, customize) if CONF_UNIT_SYSTEM in config: if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL: @@ -407,6 +407,8 @@ def merge_packages_config(config, packages): PACKAGES_CONFIG_SCHEMA(packages) for pack_name, pack_conf in packages.items(): for comp_name, comp_conf in pack_conf.items(): + if comp_name == CONF_CORE: + continue component = get_component(comp_name) if component is None: @@ -459,3 +461,18 @@ def merge_packages_config(config, packages): config[comp_name] = comp_conf return config + + +def merge_packages_customize(customize, packages): + """Merge customize from packages.""" + schema = vol.Schema({ + vol.Optional(CONF_CORE): vol.Schema({ + CONF_CUSTOMIZE: vol.All( + _convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY])}) + }, extra=vol.ALLOW_EXTRA) + + cust = list(customize) + for pkg in packages.values(): + conf = schema(pkg) + cust.extend(conf.get(CONF_CORE, {}).get(CONF_CUSTOMIZE, [])) + return cust diff --git a/tests/test_config.py b/tests/test_config.py index 39769486056..1d647cb2c92 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -473,7 +473,6 @@ def test_merge_type_mismatch(merge_log_err): def test_merge_once_only(merge_log_err): """Test if we have a merge for a comp that may occur only once.""" packages = { - 'pack_1': {'homeassistant': {}}, 'pack_2': { 'mqtt': {}, 'api': {}, # No config schema @@ -484,7 +483,7 @@ def test_merge_once_only(merge_log_err): 'mqtt': {}, 'api': {} } config_util.merge_packages_config(config, packages) - assert merge_log_err.call_count == 3 + assert merge_log_err.call_count == 2 assert len(config) == 3 @@ -519,3 +518,29 @@ def test_merge_duplicate_keys(merge_log_err): assert merge_log_err.call_count == 1 assert len(config) == 2 assert len(config['input_select']) == 1 + + +@pytest.mark.asyncio +def test_merge_customize(hass): + """Test loading core config onto hass object.""" + core_config = { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'GMT', + 'customize': {'a.a': {'friendly_name': 'A'}}, + 'packages': {'pkg1': {'homeassistant': {'customize': { + 'b.b': {'friendly_name': 'BB'}}}}}, + } + yield from config_util.async_process_ha_core_config(hass, core_config) + + entity = Entity() + entity.entity_id = 'b.b' + entity.hass = hass + yield from entity.async_update_ha_state() + + state = hass.states.get('b.b') + assert state is not None + assert state.attributes['friendly_name'] == 'BB' From 837994196ed5f8d272557cde07ace070066a1c6d Mon Sep 17 00:00:00 2001 From: nordlead2005 Date: Fri, 27 Jan 2017 01:32:45 -0500 Subject: [PATCH 166/191] Added forecast support to DarkSky (#5264) * Added forecast support to DarkSky modified: homeassistant/components/sensor/darksky.py modified: tests/components/sensor/test_darksky.py * Fix async_volume_up / async_volume_down (#5249) async_volume_up / async_volume_down should be async versions of volume_up / volume_down, not a async version of the default variants of volume_up / volume_down. The previous code always called into the mediaplayers set_volume_level, and never into volume_up / volume_down. Signed-off-by: Anton Lundin * adding a default icon "blind" to a PowerView blinds scene. (#5210) * adding a default icon "blind" to a PowerView blinds scene. * Adding icon property to define blind icon. Removed it from the state attributes dict. * fixing lint error * Added forecast support to DarkSky modified: homeassistant/components/sensor/darksky.py modified: tests/components/sensor/test_darksky.py * Use SHA hash to make token harder to guess (#5258) * Use SHA hash to make token harder to guess Use hashlib SHA256 to encode object id instead of using it directly. * Cache access token Instead of generating a token on the fly cache it in the constructor. * Fix lint * Bugfix async device_tracker see callback (#5259) * Add support for NAD receivers (#5191) * Add support for NAD receivers * remove self.update() in various methods * remove setting attributes in various methods * Change import to hass style * Updated Config Validation, extended daily forecast to all supported types * Fix style errors from previous commit, fix test since adding daily for all supported types * Removed temperature from daily as it isn't supported * Added forecast support to DarkSky modified: homeassistant/components/sensor/darksky.py modified: tests/components/sensor/test_darksky.py * Updated Config Validation, extended daily forecast to all supported types * Fix style errors from previous commit, fix test since adding daily for all supported types * Removed temperature from daily as it isn't supported * Revert "Bugfix camera streams (#5306)" This reverts commit 4b43537801a5c088329f6b12c99c95fdb2eb0e9c. Revert "Version bump for kodi dependency (#5307)" This reverts commit 6abad6b76e610b1bfb13f3f9342a2a0a53971fcf. Revert "Add HMWIOSwitch to sensor, binary (#5304)" This reverts commit 2c3f55acc4cc8890e54bf6a94f5a960eee28c486. Revert "Remove GTFS default name & string change" This reverts commit 6000c59bb559b8e37553b3f0def79c2bd84f2af2. Revert "Update pyhomematic 1.19 & small cleanups (#5299)" This reverts commit a30711f1a0e2d4a286799d714fe59ff147883fab. Revert "[sensor] Add Dublin bus RTPI sensor (#5257)" This reverts commit 1219ca3c3bc083c8f919c4db7eb3670686e52861. Revert "Bugfix group reload (#5292)" This reverts commit baa8e53e66167a1fb0f9d090f28325454ad3d4ef. Revert "Support for TrackR device trackers (#5010)" This reverts commit f7a1d63d52dc7687a07cd2c52ef4e8e6894e45d9. Revert "Bump pywemo version." This reverts commit dc937cc8cffbb9ec2b4342d801f8d7332a8dd9cf. Revert "Upgrade to voluptuous to 0.9.3 (#5288)" This reverts commit d12decc4714cb61af58ab08581712b8be5367960. Revert "Upgrade distro to 1.0.2 (#5291)" This reverts commit 64800fd48c02520b1f44be960dc8c539f82d1692. Revert "Don't build Adafruit_BBIO - doesn't work on all platforms. (#5281)" This reverts commit 9a3c0c8cd3a06d118cfcf58d1078912e41f12f31. Revert "Convert flic to synchronous platform. (#5276)" This reverts commit eb9b95c2922181b097258856af9bd2bc4d7a814e. Revert "Upgrade to aiohttp 1.2 (#4964)" This reverts commit e68e29e03ebd43175761d1ae2b4e598d382d2cf4. Revert "Fix TCP sensor to correctly use value_template (#5211)" This reverts commit 1cf9ae5a01d663bb9e3d3e38741b2ae818b36f93. Revert "Cleanup language support on TTS (#5255)" This reverts commit 3f3a3bcc8ac7eec2e5e9eba9981c74db3842f22d. Revert "Add last triggered to script (#5261)" This reverts commit 467cb18625da9323f743ed62a342e446a79fb05b. Revert "Bump flux_led version and make use of PyPi package (#5267)" This reverts commit 34a9fb01ac1fb9568f18677be5faf3d23ab7dc2a. Revert "Add support for NAD receivers (#5191)" This reverts commit 3b59e169f1bc11b3887bc98b2f8425f6c70a0df2. Revert "Bugfix async device_tracker see callback (#5259)" This reverts commit 71fddd26eb9c9ffe6cbd809298f07e17aad152a4. Revert "Use SHA hash to make token harder to guess (#5258)" This reverts commit 922308bc1f7a2a0a769a8c29d663c90a97a0583b. * Revert "Revert "Bugfix camera streams (#5306)"" This reverts commit 2ee8c44021cf9c3a91d20f9ee26752aa8369d2e6. * Update darksky.py --- homeassistant/components/sensor/darksky.py | 102 ++++++++++++++------- tests/components/sensor/test_darksky.py | 5 +- 2 files changed, 70 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/sensor/darksky.py b/homeassistant/components/sensor/darksky.py index ab79cff2aad..bf3ff0587a3 100644 --- a/homeassistant/components/sensor/darksky.py +++ b/homeassistant/components/sensor/darksky.py @@ -25,58 +25,76 @@ _LOGGER = logging.getLogger(__name__) CONF_ATTRIBUTION = "Powered by Dark Sky" CONF_UNITS = 'units' CONF_UPDATE_INTERVAL = 'update_interval' +CONF_FORECAST = 'forecast' DEFAULT_NAME = 'Dark Sky' # Sensor types are defined like so: # Name, si unit, us unit, ca unit, uk unit, uk2 unit SENSOR_TYPES = { - 'summary': ['Summary', None, None, None, None, None, None], + 'summary': ['Summary', None, None, None, None, None, None, []], 'minutely_summary': ['Minutely Summary', - None, None, None, None, None, None], - 'hourly_summary': ['Hourly Summary', None, None, None, None, None, None], - 'daily_summary': ['Daily Summary', None, None, None, None, None, None], - 'icon': ['Icon', None, None, None, None, None, None], + None, None, None, None, None, None, []], + 'hourly_summary': ['Hourly Summary', None, None, None, None, None, None, + []], + 'daily_summary': ['Daily Summary', None, None, None, None, None, None, []], + 'icon': ['Icon', None, None, None, None, None, None, + ['currently', 'hourly', 'daily']], 'nearest_storm_distance': ['Nearest Storm Distance', - 'km', 'mi', 'km', 'km', 'mi', - 'mdi:weather-lightning'], + 'km', 'm', 'km', 'km', 'm', + 'mdi:weather-lightning', ['currently']], 'nearest_storm_bearing': ['Nearest Storm Bearing', '°', '°', '°', '°', '°', - 'mdi:weather-lightning'], + 'mdi:weather-lightning', ['currently']], 'precip_type': ['Precip', None, None, None, None, None, - 'mdi:weather-pouring'], + 'mdi:weather-pouring', + ['currently', 'minutely', 'hourly', 'daily']], 'precip_intensity': ['Precip Intensity', - 'mm', 'in', 'mm', 'mm', 'mm', 'mdi:weather-rainy'], + 'mm', 'in', 'mm', 'mm', 'mm', 'mdi:weather-rainy', + ['currently', 'minutely', 'hourly', 'daily']], 'precip_probability': ['Precip Probability', - '%', '%', '%', '%', '%', 'mdi:water-percent'], + '%', '%', '%', '%', '%', 'mdi:water-percent', + ['currently', 'minutely', 'hourly', 'daily']], 'temperature': ['Temperature', - '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer'], + '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer', + ['currently', 'hourly']], 'apparent_temperature': ['Apparent Temperature', - '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer'], + '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer', + ['currently', 'hourly']], 'dew_point': ['Dew point', '°C', '°F', '°C', '°C', '°C', - 'mdi:thermometer'], + 'mdi:thermometer', ['currently', 'hourly', 'daily']], 'wind_speed': ['Wind Speed', 'm/s', 'mph', 'km/h', 'mph', 'mph', - 'mdi:weather-windy'], - 'wind_bearing': ['Wind Bearing', '°', '°', '°', '°', '°', 'mdi:compass'], + 'mdi:weather-windy', ['currently', 'hourly', 'daily']], + 'wind_bearing': ['Wind Bearing', '°', '°', '°', '°', '°', 'mdi:compass', + ['currently', 'hourly', 'daily']], 'cloud_cover': ['Cloud Coverage', '%', '%', '%', '%', '%', - 'mdi:weather-partlycloudy'], - 'humidity': ['Humidity', '%', '%', '%', '%', '%', 'mdi:water-percent'], + 'mdi:weather-partlycloudy', + ['currently', 'hourly', 'daily']], + 'humidity': ['Humidity', '%', '%', '%', '%', '%', 'mdi:water-percent', + ['currently', 'hourly', 'daily']], 'pressure': ['Pressure', 'mbar', 'mbar', 'mbar', 'mbar', 'mbar', - 'mdi:gauge'], - 'visibility': ['Visibility', 'km', 'mi', 'km', 'km', 'mi', 'mdi:eye'], - 'ozone': ['Ozone', 'DU', 'DU', 'DU', 'DU', 'DU', 'mdi:eye'], + 'mdi:gauge', ['currently', 'hourly', 'daily']], + 'visibility': ['Visibility', 'km', 'm', 'km', 'km', 'm', 'mdi:eye', + ['currently', 'hourly', 'daily']], + 'ozone': ['Ozone', 'DU', 'DU', 'DU', 'DU', 'DU', 'mdi:eye', + ['currently', 'hourly', 'daily']], 'apparent_temperature_max': ['Daily High Apparent Temperature', '°C', '°F', '°C', '°C', '°C', - 'mdi:thermometer'], + 'mdi:thermometer', + ['currently', 'hourly', 'daily']], 'apparent_temperature_min': ['Daily Low Apparent Temperature', '°C', '°F', '°C', '°C', '°C', - 'mdi:thermometer'], + 'mdi:thermometer', + ['currently', 'hourly', 'daily']], 'temperature_max': ['Daily High Temperature', - '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer'], + '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer', + ['currently', 'hourly', 'daily']], 'temperature_min': ['Daily Low Temperature', - '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer'], + '°C', '°F', '°C', '°C', '°C', 'mdi:thermometer', + ['currently', 'hourly', 'daily']], 'precip_intensity_max': ['Daily Max Precip Intensity', - 'mm', 'in', 'mm', 'mm', 'mm', 'mdi:thermometer'], + 'mm', 'in', 'mm', 'mm', 'mm', 'mdi:thermometer', + ['currently', 'hourly', 'daily']], } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -87,6 +105,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_UNITS): vol.In(['auto', 'si', 'us', 'ca', 'uk', 'uk2']), vol.Optional(CONF_UPDATE_INTERVAL, default=timedelta(seconds=120)): ( vol.All(cv.time_period, cv.positive_timedelta)), + vol.Optional(CONF_FORECAST): + vol.All(cv.ensure_list, [vol.Range(min=1, max=7)]), }) @@ -119,9 +139,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = config.get(CONF_NAME) + forecast = config.get(CONF_FORECAST) sensors = [] for variable in config[CONF_MONITORED_CONDITIONS]: sensors.append(DarkSkySensor(forecast_data, variable, name)) + if forecast is not None and 'daily' in SENSOR_TYPES[variable][7]: + for forecast_day in forecast: + sensors.append(DarkSkySensor(forecast_data, + variable, name, forecast_day)) add_devices(sensors, True) @@ -129,19 +154,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class DarkSkySensor(Entity): """Implementation of a Dark Sky sensor.""" - def __init__(self, forecast_data, sensor_type, name): + def __init__(self, forecast_data, sensor_type, name, forecast_day=0): """Initialize the sensor.""" self.client_name = name self._name = SENSOR_TYPES[sensor_type][0] self.forecast_data = forecast_data self.type = sensor_type + self.forecast_day = forecast_day self._state = None self._unit_of_measurement = None @property def name(self): """Return the name of the sensor.""" - return '{} {}'.format(self.client_name, self._name) + if self.forecast_day == 0: + return '{} {}'.format(self.client_name, self._name) + else: + return '{} {} {}'.format(self.client_name, self._name, + self.forecast_day) @property def state(self): @@ -198,19 +228,21 @@ class DarkSkySensor(Entity): self.forecast_data.update_hourly() hourly = self.forecast_data.data_hourly self._state = getattr(hourly, 'summary', '') - elif self.type in ['daily_summary', - 'temperature_min', - 'temperature_max', - 'apparent_temperature_min', - 'apparent_temperature_max', - 'precip_intensity_max']: + elif self.forecast_day > 0 or ( + self.type in ['daily_summary', + 'temperature_min', + 'temperature_max', + 'apparent_temperature_min', + 'apparent_temperature_max', + 'precip_intensity_max']): self.forecast_data.update_daily() daily = self.forecast_data.data_daily if self.type == 'daily_summary': self._state = getattr(daily, 'summary', '') else: if hasattr(daily, 'data'): - self._state = self.get_state(daily.data[0]) + self._state = self.get_state( + daily.data[self.forecast_day]) else: self._state = 0 else: diff --git a/tests/components/sensor/test_darksky.py b/tests/components/sensor/test_darksky.py index e3c83bad2a6..effa7b3dbd8 100644 --- a/tests/components/sensor/test_darksky.py +++ b/tests/components/sensor/test_darksky.py @@ -32,7 +32,8 @@ class TestDarkSkySetup(unittest.TestCase): self.key = 'foo' self.config = { 'api_key': 'foo', - 'monitored_conditions': ['summary', 'icon'], + 'forecast': [1, 2], + 'monitored_conditions': ['summary', 'icon', 'temperature_max'], 'update_interval': timedelta(seconds=120), } self.lat = 37.8267 @@ -80,4 +81,4 @@ class TestDarkSkySetup(unittest.TestCase): darksky.setup_platform(self.hass, self.config, self.add_entities) self.assertTrue(mock_get_forecast.called) self.assertEqual(mock_get_forecast.call_count, 1) - self.assertEqual(len(self.entities), 2) + self.assertEqual(len(self.entities), 7) From 41218e5a370f44d0617b30bf1828bc04c7a0fca6 Mon Sep 17 00:00:00 2001 From: Jan Losinski Date: Fri, 27 Jan 2017 07:40:14 +0100 Subject: [PATCH 167/191] [switch.pilight] Implement echo config option (#5056) * Implement echo config option for pilight Pilight switches can get a receive code configured. If so they act on received codes. In the current implementation "act on" means not only to set the internal state, but also to send the code again. If the receiver is directly parred with the switch, to act even if HA is not running, this causes it to receive the signal twice because the HA sends it again. In my case this causes a dimmer to start dimming until I hit the switch again. This implements a "echo" argument for the receive codes that let the user choose if a received signal should result in any sending or not. If not, only the status of pilight will be updated. The echo option defaults to True, as this was the default since now. Simply set it to halse to disable this behaviour. Signed-off-by: Jan Losinski * Add documentation to set_state in switch/pilight. Signed-off-by: Jan Losinski --- homeassistant/components/switch/pilight.py | 70 +++++++++++++++++----- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/switch/pilight.py b/homeassistant/components/switch/pilight.py index 84cbbd9fb0e..40c459dc189 100644 --- a/homeassistant/components/switch/pilight.py +++ b/homeassistant/components/switch/pilight.py @@ -23,6 +23,7 @@ CONF_ON_CODE_RECIEVE = 'on_code_receive' CONF_SYSTEMCODE = 'systemcode' CONF_UNIT = 'unit' CONF_UNITCODE = 'unitcode' +CONF_ECHO = 'echo' DEPENDENCIES = ['pilight'] @@ -37,6 +38,10 @@ COMMAND_SCHEMA = vol.Schema({ vol.Optional(CONF_SYSTEMCODE): cv.positive_int, }, extra=vol.ALLOW_EXTRA) +RECEIVE_SCHEMA = COMMAND_SCHEMA.extend({ + vol.Optional(CONF_ECHO): cv.boolean +}) + SWITCHES_SCHEMA = vol.Schema({ vol.Required(CONF_ON_CODE): COMMAND_SCHEMA, vol.Required(CONF_OFF_CODE): COMMAND_SCHEMA, @@ -73,6 +78,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices(devices) +class _ReceiveHandle(object): + def __init__(self, config, echo): + """Initialize the handle.""" + self.config_items = config.items() + self.echo = echo + + def match(self, code): + """Test if the received code matches the configured values. + + The received values have to be a subset of the configured options. + """ + return self.config_items <= code.items() + + def run(self, switch, turn_on): + """Change the state of the switch.""" + switch.set_state(turn_on=turn_on, send_code=self.echo) + + class PilightSwitch(SwitchDevice): """Representation of a Pilight switch.""" @@ -84,8 +107,15 @@ class PilightSwitch(SwitchDevice): self._state = False self._code_on = code_on self._code_off = code_off - self._code_on_receive = code_on_receive - self._code_off_receive = code_off_receive + + self._code_on_receive = [] + self._code_off_receive = [] + + for code_list, conf in ((self._code_on_receive, code_on_receive), + (self._code_off_receive, code_off_receive)): + for code in conf: + echo = code.pop(CONF_ECHO, True) + code_list.append(_ReceiveHandle(code, echo)) if any(self._code_on_receive) or any(self._code_off_receive): hass.bus.listen(pilight.EVENT, self._handle_code) @@ -116,26 +146,38 @@ class PilightSwitch(SwitchDevice): # - Call turn on/off only once, even if more than one code is received if any(self._code_on_receive): for on_code in self._code_on_receive: - if on_code.items() <= call.data.items(): - self.turn_on() + if on_code.match(call.data): + on_code.run(switch=self, turn_on=True) break if any(self._code_off_receive): for off_code in self._code_off_receive: - if off_code.items() <= call.data.items(): - self.turn_off() + if off_code.match(call.data): + off_code.run(switch=self, turn_on=False) break + def set_state(self, turn_on, send_code=True): + """Set the state of the switch. + + This sets the state of the switch. If send_code is set to True, then + it will call the pilight.send service to actually send the codes + to the pilight daemon. + """ + if send_code: + if turn_on: + self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, + self._code_on, blocking=True) + else: + self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, + self._code_off, blocking=True) + + self._state = turn_on + self.schedule_update_ha_state() + def turn_on(self): """Turn the switch on by calling pilight.send service with on code.""" - self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, - self._code_on, blocking=True) - self._state = True - self.schedule_update_ha_state() + self.set_state(turn_on=True) def turn_off(self): """Turn the switch on by calling pilight.send service with off code.""" - self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, - self._code_off, blocking=True) - self._state = False - self.schedule_update_ha_state() + self.set_state(turn_on=False) From 4831f578347977ee6c7e72b00b68ef1dceda6e18 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 27 Jan 2017 07:50:36 +0100 Subject: [PATCH 168/191] Bugfix sonos / refactor of sonos function for TTS (#5571) * Bugfix sonos / refactor of sonos function for TTS * fix unittest * update service yaml * restore group of a coordinator * use group function to evaluate * fix state flooting * fix comments --- .../components/media_player/services.yaml | 16 +- .../components/media_player/sonos.py | 375 ++++++++++-------- pylintrc | 1 + tests/components/media_player/test_sonos.py | 64 +-- 4 files changed, 255 insertions(+), 201 deletions(-) diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index ca3aaba7a9e..fa26e1613dc 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -154,10 +154,14 @@ clear_playlist: description: Name(s) of entites to change source on example: 'media_player.living_room_chromecast' -sonos_group_players: - description: Send Sonos media player the command for grouping all players into one (party mode). +sonos_join: + description: Group player together. fields: + master: + description: Entity ID of the player that should become the coordinator of the group. + example: 'media_player.living_room_sonos' + entity_id: description: Name(s) of entites that will coordinate the grouping. Platform dependent. example: 'media_player.living_room_sonos' @@ -178,6 +182,10 @@ sonos_snapshot: description: Name(s) of entites that will be snapshot. Platform dependent. example: 'media_player.living_room_sonos' + with_group: + description: True (default) or False. Snapshot with all group attributes. + example: 'true' + sonos_restore: description: Restore a snapshot of the media player. @@ -186,6 +194,10 @@ sonos_restore: description: Name(s) of entites that will be restored. Platform dependent. example: 'media_player.living_room_sonos' + with_group: + description: True (default) or False. Restore with all group attributes. + example: 'true' + sonos_set_sleep_timer: description: Set a Sonos timer diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 82bfc9c7b5a..c675210a075 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -42,13 +42,15 @@ SUPPORT_SONOS = SUPPORT_STOP | SUPPORT_PAUSE | SUPPORT_VOLUME_SET |\ SUPPORT_PLAY_MEDIA | SUPPORT_SEEK | SUPPORT_CLEAR_PLAYLIST |\ SUPPORT_SELECT_SOURCE | SUPPORT_PLAY -SERVICE_GROUP_PLAYERS = 'sonos_group_players' +SERVICE_JOIN = 'sonos_join' SERVICE_UNJOIN = 'sonos_unjoin' SERVICE_SNAPSHOT = 'sonos_snapshot' SERVICE_RESTORE = 'sonos_restore' SERVICE_SET_TIMER = 'sonos_set_sleep_timer' SERVICE_CLEAR_TIMER = 'sonos_clear_sleep_timer' +DATA_SONOS = 'sonos' + SUPPORT_SOURCE_LINEIN = 'Line-in' SUPPORT_SOURCE_TV = 'TV' @@ -57,6 +59,8 @@ CONF_INTERFACE_ADDR = 'interface_addr' # Service call validation schemas ATTR_SLEEP_TIME = 'sleep_time' +ATTR_MASTER = 'master' +ATTR_WITH_GROUP = 'with_group' ATTR_IS_COORDINATOR = 'is_coordinator' @@ -70,19 +74,26 @@ SONOS_SCHEMA = vol.Schema({ ATTR_ENTITY_ID: cv.entity_ids, }) -SONOS_SET_TIMER_SCHEMA = SONOS_SCHEMA.extend({ - vol.Required(ATTR_SLEEP_TIME): vol.All(vol.Coerce(int), - vol.Range(min=0, max=86399)) +SONOS_JOIN_SCHEMA = SONOS_SCHEMA.extend({ + vol.Required(ATTR_MASTER): cv.entity_id, }) -# List of devices that have been registered -DEVICES = [] +SONOS_STATES_SCHEMA = SONOS_SCHEMA.extend({ + vol.Optional(ATTR_WITH_GROUP, default=True): cv.boolean, +}) + +SONOS_SET_TIMER_SCHEMA = SONOS_SCHEMA.extend({ + vol.Required(ATTR_SLEEP_TIME): + vol.All(vol.Coerce(int), vol.Range(min=0, max=86399)) +}) def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Sonos platform.""" import soco - global DEVICES + + if DATA_SONOS not in hass.data: + hass.data[DATA_SONOS] = [] advertise_addr = config.get(CONF_ADVERTISE_ADDR, None) if advertise_addr: @@ -92,154 +103,92 @@ def setup_platform(hass, config, add_devices, discovery_info=None): player = soco.SoCo(discovery_info) # if device allready exists by config - if player.uid in [x.unique_id for x in DEVICES]: - return True + if player.uid in [x.unique_id for x in hass.data[DATA_SONOS]]: + return if player.is_visible: device = SonosDevice(hass, player) add_devices([device], True) - if not DEVICES: - register_services(hass) - DEVICES.append(device) - return True - return False + hass.data[DATA_SONOS].append(device) + if len(hass.data[DATA_SONOS]) > 1: + return + else: + players = None + hosts = config.get(CONF_HOSTS, None) + if hosts: + # Support retro compatibility with comma separated list of hosts + # from config + hosts = hosts[0] if len(hosts) == 1 else hosts + hosts = hosts.split(',') if isinstance(hosts, str) else hosts + players = [] + for host in hosts: + players.append(soco.SoCo(socket.gethostbyname(host))) - players = None - hosts = config.get(CONF_HOSTS, None) - if hosts: - # Support retro compatibility with comma separated list of hosts - # from config - hosts = hosts[0] if len(hosts) == 1 else hosts - hosts = hosts.split(',') if isinstance(hosts, str) else hosts - players = [] - for host in hosts: - players.append(soco.SoCo(socket.gethostbyname(host))) + if not players: + players = soco.discover( + interface_addr=config.get(CONF_INTERFACE_ADDR)) - if not players: - players = soco.discover(interface_addr=config.get(CONF_INTERFACE_ADDR)) + if not players: + _LOGGER.warning('No Sonos speakers found.') + return - if not players: - _LOGGER.warning('No Sonos speakers found.') - return False + hass.data[DATA_SONOS] = [SonosDevice(hass, p) for p in players] + add_devices(hass.data[DATA_SONOS], True) + _LOGGER.info('Added %s Sonos speakers', len(players)) - DEVICES = [SonosDevice(hass, p) for p in players] - add_devices(DEVICES, True) - register_services(hass) - _LOGGER.info('Added %s Sonos speakers', len(players)) - return True - - -def register_services(hass): - """Register all services for sonos devices.""" descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) - hass.services.register(DOMAIN, SERVICE_GROUP_PLAYERS, - _group_players_service, - descriptions.get(SERVICE_GROUP_PLAYERS), - schema=SONOS_SCHEMA) + def service_handle(service): + """Internal func for applying a service.""" + entity_ids = service.data.get('entity_id') - hass.services.register(DOMAIN, SERVICE_UNJOIN, - _unjoin_service, - descriptions.get(SERVICE_UNJOIN), - schema=SONOS_SCHEMA) - - hass.services.register(DOMAIN, SERVICE_SNAPSHOT, - _snapshot_service, - descriptions.get(SERVICE_SNAPSHOT), - schema=SONOS_SCHEMA) - - hass.services.register(DOMAIN, SERVICE_RESTORE, - _restore_service, - descriptions.get(SERVICE_RESTORE), - schema=SONOS_SCHEMA) - - hass.services.register(DOMAIN, SERVICE_SET_TIMER, - _set_sleep_timer_service, - descriptions.get(SERVICE_SET_TIMER), - schema=SONOS_SET_TIMER_SCHEMA) - - hass.services.register(DOMAIN, SERVICE_CLEAR_TIMER, - _clear_sleep_timer_service, - descriptions.get(SERVICE_CLEAR_TIMER), - schema=SONOS_SCHEMA) - - -def _apply_service(service, service_func, *service_func_args): - """Internal func for applying a service.""" - entity_ids = service.data.get('entity_id') - - if entity_ids: - _devices = [device for device in DEVICES - if device.entity_id in entity_ids] - else: - _devices = DEVICES - - for device in _devices: - service_func(device, *service_func_args) - device.update_ha_state(True) - - -def _group_players_service(service): - """Group media players, use player as coordinator.""" - _apply_service(service, SonosDevice.group_players) - - -def _unjoin_service(service): - """Unjoin the player from a group.""" - _apply_service(service, SonosDevice.unjoin) - - -def _snapshot_service(service): - """Take a snapshot.""" - _apply_service(service, SonosDevice.snapshot) - - -def _restore_service(service): - """Restore a snapshot.""" - _apply_service(service, SonosDevice.restore) - - -def _set_sleep_timer_service(service): - """Set a timer.""" - _apply_service(service, - SonosDevice.set_sleep_timer, - service.data[ATTR_SLEEP_TIME]) - - -def _clear_sleep_timer_service(service): - """Set a timer.""" - _apply_service(service, - SonosDevice.clear_sleep_timer) - - -def only_if_coordinator(func): - """Decorator for coordinator. - - If used as decorator, avoid calling the decorated method if player is not - a coordinator. If not, a grouped speaker (not in coordinator role) will - throw soco.exceptions.SoCoSlaveException. - - Also, partially catch exceptions like: - - soco.exceptions.SoCoUPnPException: UPnP Error 701 received: - Transition not available from - """ - def wrapper(*args, **kwargs): - """Decorator wrapper.""" - if args[0].is_coordinator: - from soco.exceptions import SoCoUPnPException - try: - func(*args, **kwargs) - except SoCoUPnPException: - _LOGGER.error('command "%s" for Sonos device "%s" ' - 'not available in this mode', - func.__name__, args[0].name) + if entity_ids: + devices = [device for device in hass.data[DATA_SONOS] + if device.entity_id in entity_ids] else: - _LOGGER.debug('Ignore command "%s" for Sonos device "%s" (%s)', - func.__name__, args[0].name, 'not coordinator') + devices = hass.data[DATA_SONOS] - return wrapper + for device in devices: + if service.service == SERVICE_JOIN: + if device.entity_id != service.data[ATTR_MASTER]: + device.join(service.data[ATTR_MASTER]) + elif service.service == SERVICE_UNJOIN: + device.unjoin() + elif service.service == SERVICE_SNAPSHOT: + device.snapshot(service.data[ATTR_WITH_GROUP]) + elif service.service == SERVICE_RESTORE: + device.restore(service.data[ATTR_WITH_GROUP]) + elif service.service == SERVICE_SET_TIMER: + device.set_timer(service.data[ATTR_SLEEP_TIME]) + elif service.service == SERVICE_CLEAR_TIMER: + device.clear_timer() + + device.schedule_update_ha_state(True) + + hass.services.register( + DOMAIN, SERVICE_JOIN, service_handle, + descriptions.get(SERVICE_JOIN), schema=SONOS_JOIN_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_UNJOIN, service_handle, + descriptions.get(SERVICE_UNJOIN), schema=SONOS_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_SNAPSHOT, service_handle, + descriptions.get(SERVICE_SNAPSHOT), schema=SONOS_STATES_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_RESTORE, service_handle, + descriptions.get(SERVICE_RESTORE), schema=SONOS_STATES_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_SET_TIMER, service_handle, + descriptions.get(SERVICE_SET_TIMER), schema=SONOS_SET_TIMER_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_CLEAR_TIMER, service_handle, + descriptions.get(SERVICE_CLEAR_TIMER), schema=SONOS_SCHEMA) def _parse_timespan(timespan): @@ -264,6 +213,14 @@ class _ProcessSonosEventQueue(): self._sonos_device.process_sonos_event(item) +def _get_entity_from_soco(hass, soco): + """Return SonosDevice from SoCo.""" + for device in hass.data[DATA_SONOS]: + if soco == device.soco_device: + return device + raise ValueError("No entity for SoCo device!") + + class SonosDevice(MediaPlayerDevice): """Representation of a Sonos device.""" @@ -304,6 +261,7 @@ class SonosDevice(MediaPlayerDevice): self._favorite_sources = None self._source_name = None self.soco_snapshot = Snapshot(self._player) + self._snapshot_group = None @property def should_poll(self): @@ -338,6 +296,16 @@ class SonosDevice(MediaPlayerDevice): """Return true if player is a coordinator.""" return self._coordinator is None + @property + def soco_device(self): + """Return soco device.""" + return self._player + + @property + def coordinator(self): + """Return coordinator of this player.""" + return self._coordinator + def _is_available(self): try: sock = socket.create_connection( @@ -374,6 +342,19 @@ class SonosDevice(MediaPlayerDevice): if is_available: + if self._player.group.coordinator != self._player: + try: + self._coordinator = _get_entity_from_soco( + self.hass, self._player.group.coordinator) + except ValueError: + self._coordinator = None + else: + self._coordinator = None + + if self._coordinator == self: + _LOGGER.warning("Coordinator loop on: %s", self.unique_id) + self._coordinator = None + track_info = None if self._last_avtransport_event: variables = self._last_avtransport_event.variables @@ -404,16 +385,6 @@ class SonosDevice(MediaPlayerDevice): if not track_info: track_info = self._player.get_current_track_info() - if track_info['uri'].startswith('x-rincon:'): - # this speaker is a slave, find the coordinator - # the uri of the track is 'x-rincon:{coordinator-id}' - coordinator_id = track_info['uri'][9:] - coordinators = [device for device in DEVICES - if device.unique_id == coordinator_id] - self._coordinator = coordinators[0] if coordinators else None - else: - self._coordinator = None - if not self._coordinator: is_playing_tv = self._player.is_playing_tv @@ -550,6 +521,10 @@ class SonosDevice(MediaPlayerDevice): update_media_position |= rel_time is not None and \ self._media_position is None + # used only if a media is playing + if self.state != STATE_PLAYING: + update_media_position = None + # position changed? if rel_time is not None and \ self._media_position is not None: @@ -616,10 +591,10 @@ class SonosDevice(MediaPlayerDevice): self._source_name = source_name # update state of the whole group - # pylint: disable=protected-access - for device in [x for x in DEVICES if x._coordinator == self]: + for device in [x for x in self.hass.data[DATA_SONOS] + if x.coordinator == self]: if device.entity_id is not self.entity_id: - self.hass.add_job(device.async_update_ha_state) + self.schedule_update_ha_state() if self._queue is None and self.entity_id is not None: self._subscribe_to_player_events() @@ -707,7 +682,7 @@ class SonosDevice(MediaPlayerDevice): self._player_volume_muted = \ event.variables['mute'].get('Master') == '1' - self.update_ha_state(True) + self.schedule_update_ha_state(True) if next_track_image_url: self.preload_media_image_url(next_track_image_url) @@ -946,37 +921,93 @@ class SonosDevice(MediaPlayerDevice): else: self._player.play_uri(media_id) - def group_players(self): - """Group all players under this coordinator.""" - if self._coordinator: - self._coordinator.group_players() - else: - self._player.partymode() + def join(self, master): + """Join the player to a group.""" + coord = [device.soco_device for device in self.hass.data[DATA_SONOS] + if device.entity_id == master] + + if coord and master != self.entity_id: + self._player.join(coord[0]) + else: + _LOGGER.error("Master not found %s", master) - @only_if_coordinator def unjoin(self): """Unjoin the player from a group.""" self._player.unjoin() - @only_if_coordinator - def snapshot(self): + def snapshot(self, with_group=True): """Snapshot the player.""" self.soco_snapshot.snapshot() - @only_if_coordinator - def restore(self): - """Restore snapshot for the player.""" - self.soco_snapshot.restore(True) + if with_group: + self._snapshot_group = self._player.group + if self._coordinator: + self._coordinator.snapshot(False) + else: + self._snapshot_group = None + + def restore(self, with_group=True): + """Restore snapshot for the player.""" + from soco.exceptions import SoCoException + try: + # need catch exception if a coordinator is going to slave. + # this state will recover with group part. + self.soco_snapshot.restore(True) + except (TypeError, SoCoException): + _LOGGER.debug("Error on restore %s", self.entity_id) + + # restore groups + if with_group and self._snapshot_group: + old = self._snapshot_group + actual = self._player.group + + ## + # Master have not change, update group + if old.coordinator == actual.coordinator: + if self._player is not old.coordinator: + # restore state of the groups + self._coordinator.restore(False) + remove = actual.members - old.members + add = old.members - actual.members + + # remove new members + for soco_dev in list(remove): + soco_dev.unjoin() + + # add old members + for soco_dev in list(add): + soco_dev.join(old.coordinator) + return + + ## + # old is allready master, rejoin + if old.coordinator.group.coordinator == old.coordinator: + self._player.join(old.coordinator) + return + + ## + # restore old master, update group + old.coordinator.unjoin() + coordinator = _get_entity_from_soco(self.hass, old.coordinator) + coordinator.restore(False) + + for s_dev in list(old.members): + if s_dev != old.coordinator: + s_dev.join(old.coordinator) - @only_if_coordinator def set_sleep_timer(self, sleep_time): """Set the timer on the player.""" - self._player.set_sleep_timer(sleep_time) + if self._coordinator: + self._coordinator.set_sleep_timer(sleep_time) + else: + self._player.set_sleep_timer(sleep_time) - @only_if_coordinator def clear_sleep_timer(self): """Clear the timer on the player.""" - self._player.set_sleep_timer(None) + if self._coordinator: + self._coordinator.set_sleep_timer(None) + else: + self._player.set_sleep_timer(None) @property def device_state_attributes(self): diff --git a/pylintrc b/pylintrc index 9a46acc6a56..4c0b1523078 100644 --- a/pylintrc +++ b/pylintrc @@ -30,6 +30,7 @@ disable= too-many-public-methods, too-many-return-statements, too-many-statements, + too-many-lines, too-few-public-methods, abstract-method diff --git a/tests/components/media_player/test_sonos.py b/tests/components/media_player/test_sonos.py index 9835b8d7635..e494d3242fa 100644 --- a/tests/components/media_player/test_sonos.py +++ b/tests/components/media_player/test_sonos.py @@ -84,8 +84,8 @@ class SoCoMock(): """Return true if coordinator.""" return True - def partymode(self): - """Cause the speaker to join all other speakers in the network.""" + def join(self, master): + """Join speaker to a group.""" return def set_sleep_timer(self, sleep_time_seconds): @@ -100,6 +100,10 @@ class SoCoMock(): """Return a player uid.""" return "RINCON_XXXXXXXXXXXXXXXXX" + def group(self): + """Return all group data of this player.""" + return + def fake_add_device(devices, update_befor_add=False): """Fake add device / update.""" @@ -129,7 +133,6 @@ class TestSonosMediaPlayer(unittest.TestCase): """Stop everything that was started.""" # Monkey patches sonos.SonosDevice.available = self.real_available - sonos.DEVICES = [] self.hass.stop() @mock.patch('soco.SoCo', new=SoCoMock) @@ -138,8 +141,8 @@ class TestSonosMediaPlayer(unittest.TestCase): """Test a single device using the autodiscovery provided by HASS.""" sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') - self.assertEqual(len(sonos.DEVICES), 1) - self.assertEqual(sonos.DEVICES[0].name, 'Kitchen') + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 1) + self.assertEqual(self.hass.data[sonos.DATA_SONOS][0].name, 'Kitchen') @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch('socket.create_connection', side_effect=socket.error()) @@ -157,7 +160,7 @@ class TestSonosMediaPlayer(unittest.TestCase): assert setup_component(self.hass, DOMAIN, config) - self.assertEqual(len(sonos.DEVICES), 1) + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 1) self.assertEqual(discover_mock.call_count, 1) @mock.patch('soco.SoCo', new=SoCoMock) @@ -177,7 +180,7 @@ class TestSonosMediaPlayer(unittest.TestCase): assert setup_component(self.hass, DOMAIN, config) - self.assertEqual(len(sonos.DEVICES), 1) + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 1) self.assertEqual(discover_mock.call_count, 1) self.assertEqual(soco.config.EVENT_ADVERTISE_IP, '192.0.1.1') @@ -194,8 +197,8 @@ class TestSonosMediaPlayer(unittest.TestCase): assert setup_component(self.hass, DOMAIN, config) - self.assertEqual(len(sonos.DEVICES), 1) - self.assertEqual(sonos.DEVICES[0].name, 'Kitchen') + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 1) + self.assertEqual(self.hass.data[sonos.DATA_SONOS][0].name, 'Kitchen') @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch('socket.create_connection', side_effect=socket.error()) @@ -210,8 +213,8 @@ class TestSonosMediaPlayer(unittest.TestCase): assert setup_component(self.hass, DOMAIN, config) - self.assertEqual(len(sonos.DEVICES), 2) - self.assertEqual(sonos.DEVICES[0].name, 'Kitchen') + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 2) + self.assertEqual(self.hass.data[sonos.DATA_SONOS][0].name, 'Kitchen') @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch('socket.create_connection', side_effect=socket.error()) @@ -226,8 +229,8 @@ class TestSonosMediaPlayer(unittest.TestCase): assert setup_component(self.hass, DOMAIN, config) - self.assertEqual(len(sonos.DEVICES), 2) - self.assertEqual(sonos.DEVICES[0].name, 'Kitchen') + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 2) + self.assertEqual(self.hass.data[sonos.DATA_SONOS][0].name, 'Kitchen') @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch.object(soco, 'discover', new=socoDiscoverMock.discover) @@ -235,20 +238,25 @@ class TestSonosMediaPlayer(unittest.TestCase): def test_ensure_setup_sonos_discovery(self, *args): """Test a single device using the autodiscovery provided by Sonos.""" sonos.setup_platform(self.hass, {}, fake_add_device) - self.assertEqual(len(sonos.DEVICES), 1) - self.assertEqual(sonos.DEVICES[0].name, 'Kitchen') + self.assertEqual(len(self.hass.data[sonos.DATA_SONOS]), 1) + self.assertEqual(self.hass.data[sonos.DATA_SONOS][0].name, 'Kitchen') @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch('socket.create_connection', side_effect=socket.error()) - @mock.patch.object(SoCoMock, 'partymode') - def test_sonos_group_players(self, partymodeMock, *args): + @mock.patch.object(SoCoMock, 'join') + def test_sonos_group_players(self, join_mock, *args): """Ensuring soco methods called for sonos_group_players service.""" sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') - device = sonos.DEVICES[-1] - partymodeMock.return_value = True - device.group_players() - self.assertEqual(partymodeMock.call_count, 1) - self.assertEqual(partymodeMock.call_args, mock.call()) + device = self.hass.data[sonos.DATA_SONOS][-1] + + device_master = mock.MagicMock() + device_master.entity_id = "media_player.test" + device_master.soco_device = device + self.hass.data[sonos.DATA_SONOS].append(device_master) + + join_mock.return_value = True + device.join("media_player.test") + self.assertEqual(join_mock.call_count, 1) @mock.patch('soco.SoCo', new=SoCoMock) @mock.patch('socket.create_connection', side_effect=socket.error()) @@ -256,7 +264,7 @@ class TestSonosMediaPlayer(unittest.TestCase): def test_sonos_unjoin(self, unjoinMock, *args): """Ensuring soco methods called for sonos_unjoin service.""" sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') - device = sonos.DEVICES[-1] + device = self.hass.data[sonos.DATA_SONOS][-1] unjoinMock.return_value = True device.unjoin() self.assertEqual(unjoinMock.call_count, 1) @@ -268,7 +276,7 @@ class TestSonosMediaPlayer(unittest.TestCase): def test_sonos_set_sleep_timer(self, set_sleep_timerMock, *args): """Ensuring soco methods called for sonos_set_sleep_timer service.""" sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') - device = sonos.DEVICES[-1] + device = self.hass.data[sonos.DATA_SONOS][-1] device.set_sleep_timer(30) set_sleep_timerMock.assert_called_once_with(30) @@ -278,7 +286,7 @@ class TestSonosMediaPlayer(unittest.TestCase): def test_sonos_clear_sleep_timer(self, set_sleep_timerMock, *args): """Ensuring soco methods called for sonos_clear_sleep_timer service.""" sonos.setup_platform(self.hass, {}, mock.MagicMock(), '192.0.2.1') - device = sonos.DEVICES[-1] + device = self.hass.data[sonos.DATA_SONOS][-1] device.set_sleep_timer(None) set_sleep_timerMock.assert_called_once_with(None) @@ -288,7 +296,7 @@ class TestSonosMediaPlayer(unittest.TestCase): def test_sonos_snapshot(self, snapshotMock, *args): """Ensuring soco methods called for sonos_snapshot service.""" sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') - device = sonos.DEVICES[-1] + device = self.hass.data[sonos.DATA_SONOS][-1] snapshotMock.return_value = True device.snapshot() self.assertEqual(snapshotMock.call_count, 1) @@ -300,8 +308,10 @@ class TestSonosMediaPlayer(unittest.TestCase): def test_sonos_restore(self, restoreMock, *args): """Ensuring soco methods called for sonos_restor service.""" sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') - device = sonos.DEVICES[-1] + device = self.hass.data[sonos.DATA_SONOS][-1] restoreMock.return_value = True + device._snapshot_coordinator = mock.MagicMock() + device._snapshot_coordinator.soco_device = SoCoMock('192.0.2.17') device.restore() self.assertEqual(restoreMock.call_count, 1) self.assertEqual(restoreMock.call_args, mock.call(True)) From 5948b5e33a7757c6363870b758b0cc673d683666 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 Jan 2017 22:59:13 -0800 Subject: [PATCH 169/191] Update frontend --- homeassistant/components/frontend/version.py | 10 +++++----- .../components/frontend/www_static/core.js | 8 ++++---- .../components/frontend/www_static/core.js.gz | Bin 33554 -> 33501 bytes .../frontend/www_static/frontend.html | 4 ++-- .../frontend/www_static/frontend.html.gz | Bin 132223 -> 132201 bytes .../www_static/home-assistant-polymer | 2 +- .../www_static/panels/ha-panel-history.html | 4 ++-- .../panels/ha-panel-history.html.gz | Bin 6791 -> 7373 bytes .../www_static/panels/ha-panel-logbook.html | 4 ++-- .../panels/ha-panel-logbook.html.gz | Bin 7322 -> 7904 bytes .../www_static/panels/ha-panel-map.html | 11 +++++++++-- .../www_static/panels/ha-panel-map.html.gz | Bin 43807 -> 43843 bytes .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 2326 -> 2329 bytes 14 files changed, 26 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index d1a4c4e8f93..1730078da87 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,8 +1,8 @@ """DO NOT MODIFY. Auto-generated by script/fingerprint_frontend.""" FINGERPRINTS = { - "core.js": "90c16d2f2c5d52203e2fd5fa2b1ae19c", - "frontend.html": "c8e670c6c9f7c0ea3b971b92ba9013db", + "core.js": "769f3fdd4e04b34bd66c7415743cf7b5", + "frontend.html": "d48d9a13f7d677e59b1d22c6db051207", "mdi.html": "5bb2f1717206bad0d187c2633062c575", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "panels/ha-panel-dev-event.html": "f19840b9a6a46f57cb064b384e1353f5", @@ -10,9 +10,9 @@ FINGERPRINTS = { "panels/ha-panel-dev-service.html": "1d223225c1c75083738033895ea3e4b5", "panels/ha-panel-dev-state.html": "8257d99a38358a150eafdb23fa6727e0", "panels/ha-panel-dev-template.html": "cbb251acabd5e7431058ed507b70522b", - "panels/ha-panel-history.html": "7baeb4bd7d9ce0def4f95eab6f10812e", + "panels/ha-panel-history.html": "9f2c72574fb6135beb1b381a4b8b7703", "panels/ha-panel-iframe.html": "d920f0aa3c903680f2f8795e2255daab", - "panels/ha-panel-logbook.html": "93de4cee3a2352a6813b5c218421d534", - "panels/ha-panel-map.html": "3b0ca63286cbe80f27bd36dbc2434e89", + "panels/ha-panel-logbook.html": "313f2ac57aaa5ad55933c9bbf8d8a1e5", + "panels/ha-panel-map.html": "13f120066c0b5faa2ce1db2c3f3cc486", "websocket_test.html": "575de64b431fe11c3785bf96d7813450" } diff --git a/homeassistant/components/frontend/www_static/core.js b/homeassistant/components/frontend/www_static/core.js index 3494529ef9b..f3679c981d7 100644 --- a/homeassistant/components/frontend/www_static/core.js +++ b/homeassistant/components/frontend/www_static/core.js @@ -1,4 +1,4 @@ -!(function(){"use strict";function t(t){return t&&t.__esModule?t.default:t}function e(t,e){return e={exports:{}},t(e,e.exports),e.exports}function n(t,e){var n=e.authToken,r=e.host;return xe({authToken:n,host:r,isValidating:!0,isInvalid:!1,errorMessage:""})}function r(){return Ve.getInitialState()}function i(t,e){var n=e.errorMessage;return t.withMutations((function(t){return t.set("isValidating",!1).set("isInvalid",!0).set("errorMessage",n)}))}function o(t,e){var n=e.authToken,r=e.host;return Fe({authToken:n,host:r})}function u(){return Ge.getInitialState()}function a(t,e){var n=e.rememberAuth;return n}function s(t){return t.withMutations((function(t){t.set("isStreaming",!0).set("hasError",!1)}))}function c(t){return t.withMutations((function(t){t.set("isStreaming",!1).set("hasError",!0)}))}function f(){return Xe.getInitialState()}function h(t){return{type:"auth",api_password:t}}function l(){return{type:"get_states"}}function p(){return{type:"get_config"}}function _(){return{type:"get_services"}}function d(){return{type:"get_panels"}}function v(t,e,n){var r={type:"call_service",domain:t,service:e};return n&&(r.service_data=n),r}function y(t){var e={type:"subscribe_events"};return t&&(e.event_type=t),e}function g(t){return{type:"unsubscribe_events",subscription:t}}function m(){return{type:"ping"}}function S(t,e){return{type:"result",success:!1,error:{code:t,message:e}}}function E(t){return t.result}function b(t,e){var n=new tn(t,e);return n.connect()}function I(t,e,n,r){void 0===r&&(r=null);var i=t.evaluate(Mo.authInfo),o=i.host+"/api/"+n;return new Promise(function(t,n){var u=new XMLHttpRequest;u.open(e,o,!0),u.setRequestHeader("X-HA-access",i.authToken),u.onload=function(){var e;try{e="application/json"===u.getResponseHeader("content-type")?JSON.parse(u.responseText):u.responseText}catch(t){e=u.responseText}u.status>199&&u.status<300?t(e):n(e)},u.onerror=function(){return n({})},r?(u.setRequestHeader("Content-Type","application/json;charset=UTF-8"),u.send(JSON.stringify(r))):u.send()})}function O(t,e){var n=e.model,r=e.result,i=e.params,o=n.entity;if(!r)return t;var u=i.replace?sn({}):t.get(o),a=Array.isArray(r)?r:[r],s=n.fromJSON||sn;return t.set(o,u.withMutations((function(t){for(var e=0;e6e4}function gt(t,e){var n=e.date;return n.toISOString()}function mt(){return Qr.getInitialState()}function St(t,e){var n=e.date,r=e.stateHistory;return 0===r.length?t.set(n,$r({})):t.withMutations((function(t){r.forEach((function(e){return t.setIn([n,e[0].entity_id],$r(e.map(In.fromJSON)))}))}))}function Et(){return ti.getInitialState()}function bt(t,e){var n=e.stateHistory;return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,ii(e.map(In.fromJSON)))}))}))}function It(){return oi.getInitialState()}function Ot(t,e){var n=e.stateHistory,r=(new Date).getTime();return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,r)})),history.length>1&&t.set(si,r)}))}function wt(){return ci.getInitialState()}function Tt(t,e){t.dispatch(Wr.ENTITY_HISTORY_DATE_SELECTED,{date:e})}function At(t,e){void 0===e&&(e=null),t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_START,{});var n="history/period";return null!==e&&(n+="?filter_entity_id="+e),on(t,"GET",n).then((function(e){return t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_SUCCESS,{stateHistory:e})}),(function(){return t.dispatch(Wr.RECENT_ENTITY_HISTORY_FETCH_ERROR,{})}))}function Ct(t,e){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_START,{date:e}),on(t,"GET","history/period/"+e).then((function(n){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_SUCCESS,{date:e,stateHistory:n})}),(function(){return t.dispatch(Wr.ENTITY_HISTORY_FETCH_ERROR,{})}))}function Dt(t){var e=t.evaluate(li);return Ct(t,e)}function zt(t){t.registerStores({currentEntityHistoryDate:Qr,entityHistory:ti,isLoadingEntityHistory:ni,recentEntityHistory:oi,recentEntityHistoryUpdated:ci})}function Rt(t){t.registerStores({moreInfoEntityId:Yr})}function Mt(t,e){var n=e.model,r=e.result,i=e.params;if(null===t||"entity"!==n.entity||!i.replace)return t;for(var o=0;o0?i=setTimeout(r,e-c):(i=null,n||(s=t.apply(u,o),i||(u=o=null)))}var i,o,u,a,s;null==e&&(e=100);var c=function(){u=this,o=arguments,a=(new Date).getTime();var c=n&&!i;return i||(i=setTimeout(r,e)),c&&(s=t.apply(u,o),u=o=null),s};return c.clear=function(){i&&(clearTimeout(i),i=null)},c}function Yt(t){var e=fo[t.hassId];e&&(e.scheduleHealthCheck.clear(),e.conn.close(),fo[t.hassId]=!1)}function Jt(t,e){void 0===e&&(e={});var n=e.syncOnInitialConnect;void 0===n&&(n=!0),Yt(t);var r=t.evaluate(Mo.authToken),i="https:"===document.location.protocol?"wss://":"ws://";i+=document.location.hostname,document.location.port&&(i+=":"+document.location.port),i+="/api/websocket",b(i,{authToken:r}).then((function(e){var r=Bt((function(){return e.ping()}),so);r(),e.socket.addEventListener("message",r),fo[t.hassId]={conn:e,scheduleHealthCheck:r},co.forEach((function(n){return e.subscribeEvents(ao.bind(null,t),n)})),t.batch((function(){t.dispatch(Ye.STREAM_START),n&&io.fetchAll(t)})),e.addEventListener("disconnected",(function(){t.dispatch(Ye.STREAM_ERROR)})),e.addEventListener("ready",(function(){t.batch((function(){t.dispatch(Ye.STREAM_START),io.fetchAll(t)}))}))}))}function Wt(t){t.registerStores({streamStatus:Xe})}function Xt(t,e,n){void 0===n&&(n={});var r=n.rememberAuth;void 0===r&&(r=!1);var i=n.host;void 0===i&&(i=""),t.dispatch(Ue.VALIDATING_AUTH_TOKEN,{authToken:e,host:i}),io.fetchAll(t).then((function(){t.dispatch(Ue.VALID_AUTH_TOKEN,{authToken:e,host:i,rememberAuth:r}),vo.start(t,{syncOnInitialConnect:!1})}),(function(e){void 0===e&&(e={});var n=e.message;void 0===n&&(n=mo),t.dispatch(Ue.INVALID_AUTH_TOKEN,{errorMessage:n})}))}function Qt(t){t.dispatch(Ue.LOG_OUT,{})}function Zt(t){t.registerStores({authAttempt:Ve,authCurrent:Ge,rememberAuth:Be})}function $t(){if(!("localStorage"in window))return{};var t=window.localStorage,e="___test";try{return t.setItem(e,e),t.removeItem(e),t}catch(t){return{}}}function te(){var t=new Uo({debug:!1});return t.hassId=Ho++,t}function ee(t,e,n){Object.keys(n).forEach((function(r){var i=n[r];if("register"in i&&i.register(e),"getters"in i&&Object.defineProperty(t,r+"Getters",{value:i.getters,enumerable:!0}),"actions"in i){var o={};Object.getOwnPropertyNames(i.actions).forEach((function(t){"function"==typeof i.actions[t]&&Object.defineProperty(o,t,{value:i.actions[t].bind(null,e),enumerable:!0})})),Object.defineProperty(t,r+"Actions",{value:o,enumerable:!0})}}))}function ne(t,e){return xo(t.attributes.entity_id.map((function(t){return e.get(t)})).filter((function(t){return!!t})))}function re(t){return on(t,"GET","error_log")}function ie(t,e){var n=e.date;return n.toISOString()}function oe(){return Jo.getInitialState()}function ue(t,e){var n=e.date,r=e.entries;return t.set(n,eu(r.map($o.fromJSON)))}function ae(){return nu.getInitialState()}function se(t,e){var n=e.date;return t.set(n,(new Date).getTime())}function ce(){return ou.getInitialState()}function fe(t,e){t.dispatch(Bo.LOGBOOK_DATE_SELECTED,{date:e})}function he(t,e){t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_START,{date:e}),on(t,"GET","logbook/"+e).then((function(n){return t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_SUCCESS,{date:e,entries:n})}),(function(){return t.dispatch(Bo.LOGBOOK_ENTRIES_FETCH_ERROR,{})}))}function le(t){return!t||(new Date).getTime()-t>su}function pe(t){t.registerStores({currentLogbookDate:Jo,isLoadingLogbookEntries:Xo,logbookEntries:nu,logbookEntriesUpdated:ou})}function _e(t){return t.set("active",!0)}function de(t){return t.set("active",!1)}function ve(){return Su.getInitialState()}function ye(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered.");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){var n;return n=navigator.userAgent.toLowerCase().indexOf("firefox")>-1?"firefox":"chrome",on(t,"POST","notify.html5",{subscription:e,browser:n}).then((function(){return t.dispatch(yu.PUSH_NOTIFICATIONS_SUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n;return n=e.message&&e.message.indexOf("gcm_sender_id")!==-1?"Please setup the notify.html5 platform.":"Notification registration failed.",console.error(e),Vn.createNotification(t,n),!1}))}function ge(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){return on(t,"DELETE","notify.html5",{subscription:e}).then((function(){return e.unsubscribe()})).then((function(){return t.dispatch(yu.PUSH_NOTIFICATIONS_UNSUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n="Failed unsubscribing for push notifications.";return console.error(e),Vn.createNotification(t,n),!1}))}function me(t){t.registerStores({pushNotifications:Su})}function Se(t,e){return on(t,"POST","template",{template:e})}function Ee(t){return t.set("isListening",!0)}function be(t,e){var n=e.interimTranscript,r=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!0).set("isTransmitting",!1).set("interimTranscript",n).set("finalTranscript",r)}))}function Ie(t,e){var n=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!1).set("isTransmitting",!0).set("interimTranscript","").set("finalTranscript",n)}))}function Oe(){return Nu.getInitialState()}function we(){return Nu.getInitialState()}function Te(){return Nu.getInitialState()}function Ae(t){return Pu[t.hassId]}function Ce(t){var e=Ae(t);if(e){var n=e.finalTranscript||e.interimTranscript;t.dispatch(Lu.VOICE_TRANSMITTING,{finalTranscript:n}),tr.callService(t,"conversation","process",{text:n}).then((function(){t.dispatch(Lu.VOICE_DONE)}),(function(){t.dispatch(Lu.VOICE_ERROR)}))}}function De(t){var e=Ae(t);e&&(e.recognition.stop(),Pu[t.hassId]=!1)}function ze(t){Ce(t),De(t)}function Re(t){var e=ze.bind(null,t);e();var n=new webkitSpeechRecognition;Pu[t.hassId]={recognition:n,interimTranscript:"",finalTranscript:""},n.interimResults=!0,n.onstart=function(){return t.dispatch(Lu.VOICE_START)},n.onerror=function(){return t.dispatch(Lu.VOICE_ERROR)},n.onend=e,n.onresult=function(e){var n=Ae(t);if(n){for(var r="",i="",o=e.resultIndex;o>>0;if(""+n!==e||4294967295===n)return NaN;e=n}return e<0?_(t)+e:e}function v(){return!0}function y(t,e,n){return(0===t||void 0!==n&&t<=-n)&&(void 0===e||void 0!==n&&e>=n)}function g(t,e){return S(t,e,0)}function m(t,e){return S(t,e,e)}function S(t,e,n){return void 0===t?n:t<0?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function E(t){this.next=t}function b(t,e,n,r){var i=0===t?e:1===t?n:[e,n];return r?r.value=i:r={value:i,done:!1},r}function I(){return{value:void 0,done:!0}}function O(t){return!!A(t)}function w(t){return t&&"function"==typeof t.next}function T(t){var e=A(t);return e&&e.call(t)}function A(t){var e=t&&(In&&t[In]||t[On]);if("function"==typeof e)return e}function C(t){return t&&"number"==typeof t.length}function D(t){return null===t||void 0===t?U():o(t)?t.toSeq():V(t)}function z(t){return null===t||void 0===t?U().toKeyedSeq():o(t)?u(t)?t.toSeq():t.fromEntrySeq():H(t)}function R(t){return null===t||void 0===t?U():o(t)?u(t)?t.entrySeq():t.toIndexedSeq():x(t)}function M(t){return(null===t||void 0===t?U():o(t)?u(t)?t.entrySeq():t:x(t)).toSetSeq()}function L(t){this._array=t,this.size=t.length}function j(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function k(t){this._iterable=t,this.size=t.length||t.size}function N(t){this._iterator=t,this._iteratorCache=[]}function P(t){return!(!t||!t[Tn])}function U(){return An||(An=new L([]))}function H(t){var e=Array.isArray(t)?new L(t).fromEntrySeq():w(t)?new N(t).fromEntrySeq():O(t)?new k(t).fromEntrySeq():"object"==typeof t?new j(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function x(t){var e=q(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function V(t){var e=q(t)||"object"==typeof t&&new j(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function q(t){return C(t)?new L(t):w(t)?new N(t):O(t)?new k(t):void 0}function F(t,e,n,r){var i=t._cache;if(i){for(var o=i.length-1,u=0;u<=o;u++){var a=i[n?o-u:u];if(e(a[1],r?a[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,n)}function G(t,e,n,r){var i=t._cache;if(i){var o=i.length-1,u=0;return new E(function(){var t=i[n?o-u:u];return u++>o?I():b(e,r?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,n)}function K(t,e){return e?B(e,t,"",{"":t}):Y(t)}function B(t,e,n,r){return Array.isArray(e)?t.call(r,n,R(e).map((function(n,r){return B(t,n,r,e)}))):J(e)?t.call(r,n,z(e).map((function(n,r){return B(t,n,r,e)}))):e}function Y(t){return Array.isArray(t)?R(t).map(Y).toList():J(t)?z(t).map(Y).toMap():t}function J(t){return t&&(t.constructor===Object||void 0===t.constructor)}function W(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return!("function"!=typeof t.equals||"function"!=typeof e.equals||!t.equals(e))}function X(t,e){if(t===e)return!0;if(!o(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||u(t)!==u(e)||a(t)!==a(e)||c(t)!==c(e))return!1;if(0===t.size&&0===e.size)return!0;var n=!s(t);if(c(t)){var r=t.entries();return e.every((function(t,e){var i=r.next().value;return i&&W(i[1],t)&&(n||W(i[0],e))}))&&r.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var f=t;t=e,e=f}var h=!0,l=e.__iterate((function(e,r){if(n?!t.has(e):i?!W(e,t.get(r,yn)):!W(t.get(r,yn),e))return h=!1,!1}));return h&&t.size===l}function Q(t,e){if(!(this instanceof Q))return new Q(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(Cn)return Cn;Cn=this}}function Z(t,e){if(!t)throw new Error(e)}function $(t,e,n){if(!(this instanceof $))return new $(t,e,n);if(Z(0!==n,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),n=void 0===n?1:Math.abs(n),e>>1&1073741824|3221225471&t}function ot(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){if(t!==t||t===1/0)return 0;var n=0|t;for(n!==t&&(n^=4294967295*t);t>4294967295;)t/=4294967295,n^=t;return it(n)}if("string"===e)return t.length>Pn?ut(t):at(t);if("function"==typeof t.hashCode)return t.hashCode();if("object"===e)return st(t);if("function"==typeof t.toString)return at(t.toString());throw new Error("Value type "+e+" cannot be hashed.")}function ut(t){var e=xn[t];return void 0===e&&(e=at(t),Hn===Un&&(Hn=0,xn={}),Hn++,xn[t]=e),e}function at(t){for(var e=0,n=0;n0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ft(t){Z(t!==1/0,"Cannot perform this action with an infinite size.")}function ht(t){return null===t||void 0===t?bt():lt(t)&&!c(t)?t:bt().withMutations((function(e){var r=n(t);ft(r.size),r.forEach((function(t,n){return e.set(n,t)}))}))}function lt(t){return!(!t||!t[Vn])}function pt(t,e){this.ownerID=t,this.entries=e}function _t(t,e,n){this.ownerID=t,this.bitmap=e,this.nodes=n}function dt(t,e,n){this.ownerID=t,this.count=e,this.nodes=n}function vt(t,e,n){this.ownerID=t,this.keyHash=e,this.entries=n}function yt(t,e,n){this.ownerID=t,this.keyHash=e,this.entry=n}function gt(t,e,n){this._type=e,this._reverse=n,this._stack=t._root&&St(t._root)}function mt(t,e){return b(t,e[0],e[1])}function St(t,e){return{node:t,index:0,__prev:e}}function Et(t,e,n,r){var i=Object.create(qn);return i.size=t,i._root=e,i.__ownerID=n,i.__hash=r,i.__altered=!1,i}function bt(){return Fn||(Fn=Et(0))}function It(t,e,n){var r,i;if(t._root){var o=f(gn),u=f(mn);if(r=Ot(t._root,t.__ownerID,0,void 0,e,n,o,u),!u.value)return t;i=t.size+(o.value?n===yn?-1:1:0)}else{if(n===yn)return t;i=1,r=new pt(t.__ownerID,[[e,n]])}return t.__ownerID?(t.size=i,t._root=r,t.__hash=void 0,t.__altered=!0,t):r?Et(i,r):bt()}function Ot(t,e,n,r,i,o,u,a){return t?t.update(e,n,r,i,o,u,a):o===yn?t:(h(a),h(u),new yt(e,r,[i,o]))}function wt(t){return t.constructor===yt||t.constructor===vt}function Tt(t,e,n,r,i){if(t.keyHash===r)return new vt(e,r,[t.entry,i]);var o,u=(0===n?t.keyHash:t.keyHash>>>n)&vn,a=(0===n?r:r>>>n)&vn,s=u===a?[Tt(t,e,n+_n,r,i)]:(o=new yt(e,r,i),u>>=1)u[a]=1&n?e[o++]:void 0;return u[r]=i,new dt(t,o+1,u)}function zt(t,e,r){for(var i=[],u=0;u>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function Nt(t,e,n,r){var i=r?t:p(t);return i[e]=n,i}function Pt(t,e,n,r){var i=t.length+1;if(r&&e+1===i)return t[e]=n,t;for(var o=new Array(i),u=0,a=0;a0&&io?0:o-n,c=u-n;return c>dn&&(c=dn),function(){if(i===c)return Xn;var t=e?--c:i++;return r&&r[t]}}function i(t,r,i){var a,s=t&&t.array,c=i>o?0:o-i>>r,f=(u-i>>r)+1;return f>dn&&(f=dn),function(){for(;;){if(a){var t=a();if(t!==Xn)return t;a=null}if(c===f)return Xn;var o=e?--f:c++;a=n(s&&s[o],r-_n,i+(o<=t.size||e<0)return t.withMutations((function(t){e<0?Wt(t,e).set(0,n):Wt(t,0,e+1).set(e,n)}));e+=t._origin;var r=t._tail,i=t._root,o=f(mn);return e>=Qt(t._capacity)?r=Bt(r,t.__ownerID,0,e,n,o):i=Bt(i,t.__ownerID,t._level,e,n,o),o.value?t.__ownerID?(t._root=i,t._tail=r,t.__hash=void 0,t.__altered=!0,t):Ft(t._origin,t._capacity,t._level,i,r):t}function Bt(t,e,n,r,i,o){var u=r>>>n&vn,a=t&&u0){var c=t&&t.array[u],f=Bt(c,e,n-_n,r,i,o);return f===c?t:(s=Yt(t,e),s.array[u]=f,s)}return a&&t.array[u]===i?t:(h(o),s=Yt(t,e),void 0===i&&u===s.array.length-1?s.array.pop():s.array[u]=i,s)}function Yt(t,e){return e&&t&&e===t.ownerID?t:new Vt(t?t.array.slice():[],e)}function Jt(t,e){if(e>=Qt(t._capacity))return t._tail;if(e<1<0;)n=n.array[e>>>r&vn],r-=_n;return n}}function Wt(t,e,n){void 0!==e&&(e|=0),void 0!==n&&(n|=0);var r=t.__ownerID||new l,i=t._origin,o=t._capacity,u=i+e,a=void 0===n?o:n<0?o+n:i+n;if(u===i&&a===o)return t;if(u>=a)return t.clear();for(var s=t._level,c=t._root,f=0;u+f<0;)c=new Vt(c&&c.array.length?[void 0,c]:[],r),s+=_n,f+=1<=1<h?new Vt([],r):_;if(_&&p>h&&u_n;y-=_n){var g=h>>>y&vn;v=v.array[g]=Yt(v.array[g],r)}v.array[h>>>_n&vn]=_}if(a=p)u-=p,a-=p,s=_n,c=null,d=d&&d.removeBefore(r,0,u);else if(u>i||p>>s&vn;if(m!==p>>>s&vn)break;m&&(f+=(1<i&&(c=c.removeBefore(r,s,u-f)),c&&pu&&(u=c.size),o(s)||(c=c.map((function(t){return K(t)}))),i.push(c)}return u>t.size&&(t=t.setSize(u)),Lt(t,e,i)}function Qt(t){return t>>_n<<_n}function Zt(t){return null===t||void 0===t?ee():$t(t)?t:ee().withMutations((function(e){var r=n(t);ft(r.size),r.forEach((function(t,n){return e.set(n,t)}))}))}function $t(t){return lt(t)&&c(t)}function te(t,e,n,r){var i=Object.create(Zt.prototype);return i.size=t?t.size:0,i._map=t,i._list=e,i.__ownerID=n,i.__hash=r,i}function ee(){return Qn||(Qn=te(bt(),Gt()))}function ne(t,e,n){var r,i,o=t._map,u=t._list,a=o.get(e),s=void 0!==a;if(n===yn){if(!s)return t;u.size>=dn&&u.size>=2*o.size?(i=u.filter((function(t,e){return void 0!==t&&a!==e})),r=i.toKeyedSeq().map((function(t){return t[0]})).flip().toMap(),t.__ownerID&&(r.__ownerID=i.__ownerID=t.__ownerID)):(r=o.remove(e),i=a===u.size-1?u.pop():u.set(a,void 0))}else if(s){if(n===u.get(a)[1])return t;r=o,i=u.set(a,[e,n])}else r=o.set(e,u.size),i=u.set(u.size,[e,n]);return t.__ownerID?(t.size=r.size,t._map=r,t._list=i,t.__hash=void 0,t):te(r,i)}function re(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ie(t){this._iter=t,this.size=t.size}function oe(t){this._iter=t,this.size=t.size}function ue(t){this._iter=t,this.size=t.size}function ae(t){var e=Ce(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=De,e.__iterateUncached=function(e,n){var r=this;return t.__iterate((function(t,n){return e(n,t,r)!==!1}),n)},e.__iteratorUncached=function(e,n){if(e===bn){var r=t.__iterator(e,n);return new E(function(){var t=r.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===En?Sn:En,n)},e}function se(t,e,n){var r=Ce(t);return r.size=t.size,r.has=function(e){return t.has(e)},r.get=function(r,i){var o=t.get(r,yn);return o===yn?i:e.call(n,o,r,t)},r.__iterateUncached=function(r,i){var o=this;return t.__iterate((function(t,i,u){return r(e.call(n,t,i,u),i,o)!==!1}),i)},r.__iteratorUncached=function(r,i){var o=t.__iterator(bn,i);return new E(function(){var i=o.next();if(i.done)return i;var u=i.value,a=u[0];return b(r,a,e.call(n,u[1],a,t),i)})},r}function ce(t,e){var n=Ce(t);return n._iter=t,n.size=t.size,n.reverse=function(){return t},t.flip&&(n.flip=function(){var e=ae(t);return e.reverse=function(){return t.flip()},e}),n.get=function(n,r){return t.get(e?n:-1-n,r)},n.has=function(n){return t.has(e?n:-1-n)},n.includes=function(e){return t.includes(e)},n.cacheResult=De,n.__iterate=function(e,n){var r=this;return t.__iterate((function(t,n){return e(t,n,r)}),!n)},n.__iterator=function(e,n){return t.__iterator(e,!n)},n}function fe(t,e,n,r){var i=Ce(t);return r&&(i.has=function(r){var i=t.get(r,yn);return i!==yn&&!!e.call(n,i,r,t)},i.get=function(r,i){var o=t.get(r,yn);return o!==yn&&e.call(n,o,r,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,a=0;return t.__iterate((function(t,o,s){if(e.call(n,t,o,s))return a++,i(t,r?o:a-1,u)}),o),a},i.__iteratorUncached=function(i,o){var u=t.__iterator(bn,o),a=0;return new E(function(){for(;;){var o=u.next();if(o.done)return o;var s=o.value,c=s[0],f=s[1];if(e.call(n,f,c,t))return b(i,r?c:a++,f,o)}})},i}function he(t,e,n){var r=ht().asMutable();return t.__iterate((function(i,o){r.update(e.call(n,i,o,t),0,(function(t){return t+1}))})),r.asImmutable()}function le(t,e,n){var r=u(t),i=(c(t)?Zt():ht()).asMutable();t.__iterate((function(o,u){i.update(e.call(n,o,u,t),(function(t){return t=t||[],t.push(r?[u,o]:o),t}))}));var o=Ae(t);return i.map((function(e){return Oe(t,o(e))}))}function pe(t,e,n,r){var i=t.size;if(void 0!==e&&(e|=0),void 0!==n&&(n===1/0?n=i:n|=0),y(e,n,i))return t;var o=g(e,i),u=m(n,i);if(o!==o||u!==u)return pe(t.toSeq().cacheResult(),e,n,r);var a,s=u-o;s===s&&(a=s<0?0:s);var c=Ce(t);return c.size=0===a?a:t.size&&a||void 0,!r&&P(t)&&a>=0&&(c.get=function(e,n){return e=d(this,e),e>=0&&ea)return I();var t=i.next();return r||e===En?t:e===Sn?b(e,s-1,void 0,t):b(e,s-1,t.value[1],t)})},c}function _e(t,e,n){var r=Ce(t);return r.__iterateUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterate(r,i);var u=0;return t.__iterate((function(t,i,a){return e.call(n,t,i,a)&&++u&&r(t,i,o)})),u},r.__iteratorUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterator(r,i);var u=t.__iterator(bn,i),a=!0;return new E(function(){if(!a)return I();var t=u.next();if(t.done)return t;var i=t.value,s=i[0],c=i[1];return e.call(n,c,s,o)?r===bn?t:b(r,s,c,t):(a=!1,I())})},r}function de(t,e,n,r){var i=Ce(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var a=!0,s=0;return t.__iterate((function(t,o,c){if(!a||!(a=e.call(n,t,o,c)))return s++,i(t,r?o:s-1,u)})),s},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var a=t.__iterator(bn,o),s=!0,c=0;return new E(function(){var t,o,f;do{if(t=a.next(),t.done)return r||i===En?t:i===Sn?b(i,c++,void 0,t):b(i,c++,t.value[1],t);var h=t.value;o=h[0],f=h[1],s&&(s=e.call(n,f,o,u))}while(s);return i===bn?t:b(i,o,f,t)})},i}function ve(t,e){var r=u(t),i=[t].concat(e).map((function(t){return o(t)?r&&(t=n(t)):t=r?H(t):x(Array.isArray(t)?t:[t]),t})).filter((function(t){return 0!==t.size}));if(0===i.length)return t;if(1===i.length){var s=i[0];if(s===t||r&&u(s)||a(t)&&a(s))return s}var c=new L(i);return r?c=c.toKeyedSeq():a(t)||(c=c.toSetSeq()),c=c.flatten(!0),c.size=i.reduce((function(t,e){if(void 0!==t){var n=e.size;if(void 0!==n)return t+n}}),0),c}function ye(t,e,n){var r=Ce(t);return r.__iterateUncached=function(r,i){function u(t,c){var f=this;t.__iterate((function(t,i){return(!e||c0}function Ie(t,n,r){var i=Ce(t);return i.size=new L(r).map((function(t){return t.size})).min(),i.__iterate=function(t,e){for(var n,r=this,i=this.__iterator(En,e),o=0;!(n=i.next()).done&&t(n.value,o++,r)!==!1;);return o},i.__iteratorUncached=function(t,i){var o=r.map((function(t){return t=e(t),T(i?t.reverse():t)})),u=0,a=!1;return new E(function(){var e;return a||(e=o.map((function(t){return t.next()})),a=e.some((function(t){return t.done}))),a?I():b(t,u++,n.apply(null,e.map((function(t){return t.value}))))})},i}function Oe(t,e){return P(t)?e:t.constructor(e)}function we(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function Te(t){return ft(t.size),_(t)}function Ae(t){return u(t)?n:a(t)?r:i}function Ce(t){return Object.create((u(t)?z:a(t)?R:M).prototype)}function De(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):D.prototype.cacheResult.call(this)}function ze(t,e){return t>e?1:te?-1:0}function on(t){if(t.size===1/0)return 0;var e=c(t),n=u(t),r=e?1:0,i=t.__iterate(n?e?function(t,e){r=31*r+an(ot(t),ot(e))|0}:function(t,e){r=r+an(ot(t),ot(e))|0}:e?function(t){r=31*r+ot(t)|0}:function(t){r=r+ot(t)|0});return un(i,r)}function un(t,e){return e=Rn(e,3432918353),e=Rn(e<<15|e>>>-15,461845907),e=Rn(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=Rn(e^e>>>16,2246822507),e=Rn(e^e>>>13,3266489909),e=it(e^e>>>16)}function an(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var sn=Array.prototype.slice;t(n,e),t(r,e),t(i,e),e.isIterable=o,e.isKeyed=u,e.isIndexed=a,e.isAssociative=s,e.isOrdered=c,e.Keyed=n,e.Indexed=r,e.Set=i;var cn="@@__IMMUTABLE_ITERABLE__@@",fn="@@__IMMUTABLE_KEYED__@@",hn="@@__IMMUTABLE_INDEXED__@@",ln="@@__IMMUTABLE_ORDERED__@@",pn="delete",_n=5,dn=1<<_n,vn=dn-1,yn={},gn={value:!1},mn={value:!1},Sn=0,En=1,bn=2,In="function"==typeof Symbol&&Symbol.iterator,On="@@iterator",wn=In||On;E.prototype.toString=function(){return"[Iterator]"},E.KEYS=Sn,E.VALUES=En,E.ENTRIES=bn,E.prototype.inspect=E.prototype.toSource=function(){return this.toString()},E.prototype[wn]=function(){return this},t(D,e),D.of=function(){return D(arguments)},D.prototype.toSeq=function(){return this},D.prototype.toString=function(){return this.__toString("Seq {","}")},D.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},D.prototype.__iterate=function(t,e){return F(this,t,e,!0)},D.prototype.__iterator=function(t,e){return G(this,t,e,!0)},t(z,D),z.prototype.toKeyedSeq=function(){return this},t(R,D),R.of=function(){return R(arguments)},R.prototype.toIndexedSeq=function(){return this},R.prototype.toString=function(){return this.__toString("Seq [","]")},R.prototype.__iterate=function(t,e){return F(this,t,e,!1)},R.prototype.__iterator=function(t,e){return G(this,t,e,!1)},t(M,D),M.of=function(){return M(arguments)},M.prototype.toSetSeq=function(){return this},D.isSeq=P,D.Keyed=z,D.Set=M,D.Indexed=R;var Tn="@@__IMMUTABLE_SEQ__@@";D.prototype[Tn]=!0,t(L,R),L.prototype.get=function(t,e){return this.has(t)?this._array[d(this,t)]:e},L.prototype.__iterate=function(t,e){for(var n=this,r=this._array,i=r.length-1,o=0;o<=i;o++)if(t(r[e?i-o:o],o,n)===!1)return o+1;return o},L.prototype.__iterator=function(t,e){var n=this._array,r=n.length-1,i=0;return new E(function(){return i>r?I():b(t,i,n[e?r-i++:i++])})},t(j,z),j.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},j.prototype.has=function(t){return this._object.hasOwnProperty(t)},j.prototype.__iterate=function(t,e){for(var n=this,r=this._object,i=this._keys,o=i.length-1,u=0;u<=o;u++){var a=i[e?o-u:u];if(t(r[a],a,n)===!1)return u+1}return u},j.prototype.__iterator=function(t,e){var n=this._object,r=this._keys,i=r.length-1,o=0;return new E(function(){var u=r[e?i-o:o];return o++>i?I():b(t,u,n[u])})},j.prototype[ln]=!0,t(k,R),k.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);var r=this._iterable,i=T(r),o=0;if(w(i))for(var u;!(u=i.next()).done&&t(u.value,o++,n)!==!1;);return o},k.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var n=this._iterable,r=T(n);if(!w(r))return new E(I);var i=0;return new E(function(){var e=r.next();return e.done?e:b(t,i++,e.value)})},t(N,R),N.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,i=this._iteratorCache,o=0;o=r.length){var e=n.next();if(e.done)return e;r[i]=e.value}return b(t,i,r[i++])})};var An;t(Q,R),Q.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Q.prototype.get=function(t,e){return this.has(t)?this._value:e},Q.prototype.includes=function(t){return W(this._value,t)},Q.prototype.slice=function(t,e){var n=this.size;return y(t,e,n)?this:new Q(this._value,m(e,n)-g(t,n))},Q.prototype.reverse=function(){return this},Q.prototype.indexOf=function(t){return W(this._value,t)?0:-1},Q.prototype.lastIndexOf=function(t){return W(this._value,t)?this.size:-1},Q.prototype.__iterate=function(t,e){for(var n=this,r=0;r=0&&e=0&&nn?I():b(t,o++,u)})},$.prototype.equals=function(t){return t instanceof $?this._start===t._start&&this._end===t._end&&this._step===t._step:X(this,t)};var Dn;t(tt,e),t(et,tt),t(nt,tt),t(rt,tt),tt.Keyed=et,tt.Indexed=nt,tt.Set=rt;var zn,Rn="function"==typeof Math.imul&&Math.imul(4294967295,2)===-2?Math.imul:function(t,e){t|=0,e|=0;var n=65535&t,r=65535&e;return n*r+((t>>>16)*r+n*(e>>>16)<<16>>>0)|0},Mn=Object.isExtensible,Ln=(function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}})(),jn="function"==typeof WeakMap;jn&&(zn=new WeakMap);var kn=0,Nn="__immutablehash__";"function"==typeof Symbol&&(Nn=Symbol(Nn));var Pn=16,Un=255,Hn=0,xn={};t(ht,et),ht.of=function(){var t=sn.call(arguments,0);return bt().withMutations((function(e){for(var n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},ht.prototype.toString=function(){return this.__toString("Map {","}")},ht.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},ht.prototype.set=function(t,e){return It(this,t,e)},ht.prototype.setIn=function(t,e){return this.updateIn(t,yn,(function(){return e}))},ht.prototype.remove=function(t){return It(this,t,yn)},ht.prototype.deleteIn=function(t){return this.updateIn(t,(function(){return yn}))},ht.prototype.update=function(t,e,n){return 1===arguments.length?t(this):this.updateIn([t],e,n)},ht.prototype.updateIn=function(t,e,n){n||(n=e,e=void 0);var r=jt(this,Re(t),e,n);return r===yn?void 0:r},ht.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):bt()},ht.prototype.merge=function(){return zt(this,void 0,arguments)},ht.prototype.mergeWith=function(t){var e=sn.call(arguments,1);return zt(this,t,e)},ht.prototype.mergeIn=function(t){var e=sn.call(arguments,1);return this.updateIn(t,bt(),(function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]}))},ht.prototype.mergeDeep=function(){return zt(this,Rt,arguments)},ht.prototype.mergeDeepWith=function(t){var e=sn.call(arguments,1);return zt(this,Mt(t),e)},ht.prototype.mergeDeepIn=function(t){var e=sn.call(arguments,1);return this.updateIn(t,bt(),(function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]}))},ht.prototype.sort=function(t){return Zt(Se(this,t))},ht.prototype.sortBy=function(t,e){return Zt(Se(this,e,t))},ht.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},ht.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new l)},ht.prototype.asImmutable=function(){return this.__ensureOwner()},ht.prototype.wasAltered=function(){return this.__altered},ht.prototype.__iterator=function(t,e){return new gt(this,t,e)},ht.prototype.__iterate=function(t,e){var n=this,r=0;return this._root&&this._root.iterate((function(e){return r++,t(e[1],e[0],n)}),e),r},ht.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Et(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},ht.isMap=lt;var Vn="@@__IMMUTABLE_MAP__@@",qn=ht.prototype;qn[Vn]=!0,qn[pn]=qn.remove,qn.removeIn=qn.deleteIn,pt.prototype.get=function(t,e,n,r){for(var i=this.entries,o=0,u=i.length;o=Gn)return At(t,s,r,i);var _=t&&t===this.ownerID,d=_?s:p(s);return l?a?c===f-1?d.pop():d[c]=d.pop():d[c]=[r,i]:d.push([r,i]),_?(this.entries=d,this):new pt(t,d)}},_t.prototype.get=function(t,e,n,r){void 0===e&&(e=ot(n));var i=1<<((0===t?e:e>>>t)&vn),o=this.bitmap;return 0===(o&i)?r:this.nodes[kt(o&i-1)].get(t+_n,e,n,r)},_t.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=ot(r));var a=(0===e?n:n>>>e)&vn,s=1<=Kn)return Dt(t,l,c,a,_);if(f&&!_&&2===l.length&&wt(l[1^h]))return l[1^h];if(f&&_&&1===l.length&&wt(_))return _;var d=t&&t===this.ownerID,v=f?_?c:c^s:c|s,y=f?_?Nt(l,h,_,d):Ut(l,h,d):Pt(l,h,_,d);return d?(this.bitmap=v,this.nodes=y,this):new _t(t,v,y)},dt.prototype.get=function(t,e,n,r){void 0===e&&(e=ot(n));var i=(0===t?e:e>>>t)&vn,o=this.nodes[i];return o?o.get(t+_n,e,n,r):r},dt.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=ot(r));var a=(0===e?n:n>>>e)&vn,s=i===yn,c=this.nodes,f=c[a];if(s&&!f)return this;var h=Ot(f,t,e+_n,n,r,i,o,u);if(h===f)return this;var l=this.count;if(f){if(!h&&(l--,l=0&&t>>e&vn;if(r>=this.array.length)return new Vt([],t);var i,o=0===r;if(e>0){var u=this.array[r];if(i=u&&u.removeBefore(t,e-_n,n),i===u&&o)return this}if(o&&!i)return this;var a=Yt(this,t);if(!o)for(var s=0;s>>e&vn;if(r>=this.array.length)return this;var i;if(e>0){var o=this.array[r];if(i=o&&o.removeAfter(t,e-_n,n),i===o&&r===this.array.length-1)return this}var u=Yt(this,t);return u.array.splice(r+1),i&&(u.array[r]=i),u};var Wn,Xn={};t(Zt,ht),Zt.of=function(){return this(arguments)},Zt.prototype.toString=function(){return this.__toString("OrderedMap {","}")},Zt.prototype.get=function(t,e){var n=this._map.get(t);return void 0!==n?this._list.get(n)[1]:e},Zt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):ee()},Zt.prototype.set=function(t,e){return ne(this,t,e)},Zt.prototype.remove=function(t){return ne(this,t,yn)},Zt.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Zt.prototype.__iterate=function(t,e){var n=this;return this._list.__iterate((function(e){return e&&t(e[1],e[0],n)}),e)},Zt.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Zt.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),n=this._list.__ensureOwner(t);return t?te(e,n,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=n,this)},Zt.isOrderedMap=$t,Zt.prototype[ln]=!0,Zt.prototype[pn]=Zt.prototype.remove;var Qn;t(re,z),re.prototype.get=function(t,e){return this._iter.get(t,e)},re.prototype.has=function(t){return this._iter.has(t)},re.prototype.valueSeq=function(){return this._iter.valueSeq()},re.prototype.reverse=function(){var t=this,e=ce(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},re.prototype.map=function(t,e){var n=this,r=se(this,t,e);return this._useKeys||(r.valueSeq=function(){return n._iter.toSeq().map(t,e)}),r},re.prototype.__iterate=function(t,e){var n,r=this;return this._iter.__iterate(this._useKeys?function(e,n){return t(e,n,r)}:(n=e?Te(this):0,function(i){return t(i,e?--n:n++,r)}),e)},re.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var n=this._iter.__iterator(En,e),r=e?Te(this):0;return new E(function(){var i=n.next();return i.done?i:b(t,e?--r:r++,i.value,i)})},re.prototype[ln]=!0,t(ie,R),ie.prototype.includes=function(t){return this._iter.includes(t)},ie.prototype.__iterate=function(t,e){var n=this,r=0;return this._iter.__iterate((function(e){return t(e,r++,n)}),e)},ie.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e),r=0;return new E(function(){var e=n.next();return e.done?e:b(t,r++,e.value,e)})},t(oe,M),oe.prototype.has=function(t){return this._iter.includes(t)},oe.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){return t(e,e,n)}),e)},oe.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e);return new E(function(){var e=n.next();return e.done?e:b(t,e.value,e.value,e)})},t(ue,z),ue.prototype.entrySeq=function(){return this._iter.toSeq()},ue.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){if(e){we(e);var r=o(e);return t(r?e.get(1):e[1],r?e.get(0):e[0],n)}}),e)},ue.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e);return new E(function(){for(;;){var e=n.next();if(e.done)return e;var r=e.value;if(r){we(r);var i=o(r);return b(t,i?r.get(0):r[0],i?r.get(1):r[1],e)}}})},ie.prototype.cacheResult=re.prototype.cacheResult=oe.prototype.cacheResult=ue.prototype.cacheResult=De,t(Me,et),Me.prototype.toString=function(){return this.__toString(je(this)+" {","}")},Me.prototype.has=function(t){return this._defaultValues.hasOwnProperty(t)},Me.prototype.get=function(t,e){if(!this.has(t))return e;var n=this._defaultValues[t];return this._map?this._map.get(t,n):n},Me.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var t=this.constructor;return t._empty||(t._empty=Le(this,bt()))},Me.prototype.set=function(t,e){if(!this.has(t))throw new Error('Cannot set unknown key "'+t+'" on '+je(this));if(this._map&&!this._map.has(t)){var n=this._defaultValues[t];if(e===n)return this}var r=this._map&&this._map.set(t,e);return this.__ownerID||r===this._map?this:Le(this,r)},Me.prototype.remove=function(t){if(!this.has(t))return this;var e=this._map&&this._map.remove(t);return this.__ownerID||e===this._map?this:Le(this,e)},Me.prototype.wasAltered=function(){return this._map.wasAltered()},Me.prototype.__iterator=function(t,e){var r=this;return n(this._defaultValues).map((function(t,e){return r.get(e)})).__iterator(t,e)},Me.prototype.__iterate=function(t,e){var r=this;return n(this._defaultValues).map((function(t,e){return r.get(e)})).__iterate(t,e)},Me.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map&&this._map.__ensureOwner(t);return t?Le(this,e,t):(this.__ownerID=t,this._map=e,this)};var Zn=Me.prototype;Zn[pn]=Zn.remove,Zn.deleteIn=Zn.removeIn=qn.removeIn,Zn.merge=qn.merge,Zn.mergeWith=qn.mergeWith,Zn.mergeIn=qn.mergeIn,Zn.mergeDeep=qn.mergeDeep,Zn.mergeDeepWith=qn.mergeDeepWith,Zn.mergeDeepIn=qn.mergeDeepIn,Zn.setIn=qn.setIn,Zn.update=qn.update,Zn.updateIn=qn.updateIn,Zn.withMutations=qn.withMutations,Zn.asMutable=qn.asMutable,Zn.asImmutable=qn.asImmutable,t(Pe,rt),Pe.of=function(){return this(arguments)},Pe.fromKeys=function(t){return this(n(t).keySeq())},Pe.prototype.toString=function(){return this.__toString("Set {","}")},Pe.prototype.has=function(t){return this._map.has(t)},Pe.prototype.add=function(t){ -return He(this,this._map.set(t,!0))},Pe.prototype.remove=function(t){return He(this,this._map.remove(t))},Pe.prototype.clear=function(){return He(this,this._map.clear())},Pe.prototype.union=function(){var t=sn.call(arguments,0);return t=t.filter((function(t){return 0!==t.size})),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations((function(e){for(var n=0;n=0;r--)n={value:t[r],next:n};return this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):Je(e,n)},Be.prototype.pushAll=function(t){if(t=r(t),0===t.size)return this;ft(t.size);var e=this.size,n=this._head;return t.reverse().forEach((function(t){e++,n={value:t,next:n}})),this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):Je(e,n)},Be.prototype.pop=function(){return this.slice(1)},Be.prototype.unshift=function(){return this.push.apply(this,arguments)},Be.prototype.unshiftAll=function(t){return this.pushAll(t)},Be.prototype.shift=function(){return this.pop.apply(this,arguments)},Be.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):We()},Be.prototype.slice=function(t,e){if(y(t,e,this.size))return this;var n=g(t,this.size),r=m(e,this.size);if(r!==this.size)return nt.prototype.slice.call(this,t,e);for(var i=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):Je(i,o)},Be.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Je(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Be.prototype.__iterate=function(t,e){var n=this;if(e)return this.reverse().__iterate(t);for(var r=0,i=this._head;i&&t(i.value,r++,n)!==!1;)i=i.next;return r},Be.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var n=0,r=this._head;return new E(function(){if(r){var e=r.value;return r=r.next,b(t,n++,e)}return I()})},Be.isStack=Ye;var ir="@@__IMMUTABLE_STACK__@@",or=Be.prototype;or[ir]=!0,or.withMutations=qn.withMutations,or.asMutable=qn.asMutable,or.asImmutable=qn.asImmutable,or.wasAltered=qn.wasAltered;var ur;e.Iterator=E,Xe(e,{toArray:function(){ft(this.size);var t=new Array(this.size||0);return this.valueSeq().__iterate((function(e,n){t[n]=e})),t},toIndexedSeq:function(){return new ie(this)},toJS:function(){return this.toSeq().map((function(t){return t&&"function"==typeof t.toJS?t.toJS():t})).__toJS()},toJSON:function(){return this.toSeq().map((function(t){return t&&"function"==typeof t.toJSON?t.toJSON():t})).__toJS()},toKeyedSeq:function(){return new re(this,!0)},toMap:function(){return ht(this.toKeyedSeq())},toObject:function(){ft(this.size);var t={};return this.__iterate((function(e,n){t[n]=e})),t},toOrderedMap:function(){return Zt(this.toKeyedSeq())},toOrderedSet:function(){return qe(u(this)?this.valueSeq():this)},toSet:function(){return Pe(u(this)?this.valueSeq():this)},toSetSeq:function(){return new oe(this)},toSeq:function(){return a(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Be(u(this)?this.valueSeq():this)},toList:function(){return Ht(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(t,e){return 0===this.size?t+e:t+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+e},concat:function(){var t=sn.call(arguments,0);return Oe(this,ve(this,t))},includes:function(t){return this.some((function(e){return W(e,t)}))},entries:function(){return this.__iterator(bn)},every:function(t,e){ft(this.size);var n=!0;return this.__iterate((function(r,i,o){if(!t.call(e,r,i,o))return n=!1,!1})),n},filter:function(t,e){return Oe(this,fe(this,t,e,!0))},find:function(t,e,n){var r=this.findEntry(t,e);return r?r[1]:n},forEach:function(t,e){return ft(this.size),this.__iterate(e?t.bind(e):t)},join:function(t){ft(this.size),t=void 0!==t?""+t:",";var e="",n=!0;return this.__iterate((function(r){n?n=!1:e+=t,e+=null!==r&&void 0!==r?r.toString():""})),e},keys:function(){return this.__iterator(Sn)},map:function(t,e){return Oe(this,se(this,t,e))},reduce:function(t,e,n){ft(this.size);var r,i;return arguments.length<2?i=!0:r=e,this.__iterate((function(e,o,u){i?(i=!1,r=e):r=t.call(n,r,e,o,u)})),r},reduceRight:function(t,e,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Oe(this,ce(this,!0))},slice:function(t,e){return Oe(this,pe(this,t,e,!0))},some:function(t,e){return!this.every($e(t),e)},sort:function(t){return Oe(this,Se(this,t))},values:function(){return this.__iterator(En)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(t,e){return _(t?this.toSeq().filter(t,e):this)},countBy:function(t,e){return he(this,t,e)},equals:function(t){return X(this,t)},entrySeq:function(){var t=this;if(t._cache)return new L(t._cache);var e=t.toSeq().map(Ze).toIndexedSeq();return e.fromEntrySeq=function(){return t.toSeq()},e},filterNot:function(t,e){return this.filter($e(t),e)},findEntry:function(t,e,n){var r=n;return this.__iterate((function(n,i,o){if(t.call(e,n,i,o))return r=[i,n],!1})),r},findKey:function(t,e){var n=this.findEntry(t,e);return n&&n[0]},findLast:function(t,e,n){return this.toKeyedSeq().reverse().find(t,e,n)},findLastEntry:function(t,e,n){return this.toKeyedSeq().reverse().findEntry(t,e,n)},findLastKey:function(t,e){return this.toKeyedSeq().reverse().findKey(t,e)},first:function(){return this.find(v)},flatMap:function(t,e){return Oe(this,ge(this,t,e))},flatten:function(t){return Oe(this,ye(this,t,!0))},fromEntrySeq:function(){return new ue(this)},get:function(t,e){return this.find((function(e,n){return W(n,t)}),void 0,e)},getIn:function(t,e){for(var n,r=this,i=Re(t);!(n=i.next()).done;){var o=n.value;if(r=r&&r.get?r.get(o,yn):yn,r===yn)return e}return r},groupBy:function(t,e){return le(this,t,e)},has:function(t){return this.get(t,yn)!==yn},hasIn:function(t){return this.getIn(t,yn)!==yn},isSubset:function(t){return t="function"==typeof t.includes?t:e(t),this.every((function(e){return t.includes(e)}))},isSuperset:function(t){return t="function"==typeof t.isSubset?t:e(t),t.isSubset(this)},keyOf:function(t){return this.findKey((function(e){return W(e,t)}))},keySeq:function(){return this.toSeq().map(Qe).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(t){return this.toKeyedSeq().reverse().keyOf(t)},max:function(t){return Ee(this,t)},maxBy:function(t,e){return Ee(this,e,t)},min:function(t){return Ee(this,t?tn(t):rn)},minBy:function(t,e){return Ee(this,e?tn(e):rn,t)},rest:function(){return this.slice(1)},skip:function(t){return this.slice(Math.max(0,t))},skipLast:function(t){return Oe(this,this.toSeq().reverse().skip(t).reverse())},skipWhile:function(t,e){return Oe(this,de(this,t,e,!0))},skipUntil:function(t,e){return this.skipWhile($e(t),e)},sortBy:function(t,e){return Oe(this,Se(this,e,t))},take:function(t){return this.slice(0,Math.max(0,t))},takeLast:function(t){return Oe(this,this.toSeq().reverse().take(t).reverse())},takeWhile:function(t,e){return Oe(this,_e(this,t,e))},takeUntil:function(t,e){return this.takeWhile($e(t),e)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=on(this))}});var ar=e.prototype;ar[cn]=!0,ar[wn]=ar.values,ar.__toJS=ar.toArray,ar.__toStringMapper=en,ar.inspect=ar.toSource=function(){return this.toString()},ar.chain=ar.flatMap,ar.contains=ar.includes,Xe(n,{flip:function(){return Oe(this,ae(this))},mapEntries:function(t,e){var n=this,r=0;return Oe(this,this.toSeq().map((function(i,o){return t.call(e,[o,i],r++,n)})).fromEntrySeq())},mapKeys:function(t,e){var n=this;return Oe(this,this.toSeq().flip().map((function(r,i){return t.call(e,r,i,n)})).flip())}});var sr=n.prototype;sr[fn]=!0,sr[wn]=ar.entries,sr.__toJS=ar.toObject,sr.__toStringMapper=function(t,e){return JSON.stringify(e)+": "+en(t)},Xe(r,{toKeyedSeq:function(){return new re(this,!1)},filter:function(t,e){return Oe(this,fe(this,t,e,!1))},findIndex:function(t,e){var n=this.findEntry(t,e);return n?n[0]:-1},indexOf:function(t){var e=this.keyOf(t);return void 0===e?-1:e},lastIndexOf:function(t){var e=this.lastKeyOf(t);return void 0===e?-1:e},reverse:function(){return Oe(this,ce(this,!1))},slice:function(t,e){return Oe(this,pe(this,t,e,!1))},splice:function(t,e){var n=arguments.length;if(e=Math.max(0|e,0),0===n||2===n&&!e)return this;t=g(t,t<0?this.count():this.size);var r=this.slice(0,t);return Oe(this,1===n?r:r.concat(p(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){var n=this.findLastEntry(t,e);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(t){return Oe(this,ye(this,t,!1))},get:function(t,e){return t=d(this,t),t<0||this.size===1/0||void 0!==this.size&&t>this.size?e:this.find((function(e,n){return n===t}),void 0,e)},has:function(t){return t=d(this,t),t>=0&&(void 0!==this.size?this.size===1/0||t-1&&t%1===0&&t<=Number.MAX_VALUE}var i=Function.prototype.bind;e.isString=function(t){return"string"==typeof t||"[object String]"===n(t)},e.isArray=Array.isArray||function(t){return"[object Array]"===n(t)},"function"!=typeof/./&&"object"!=typeof Int8Array?e.isFunction=function(t){return"function"==typeof t||!1}:e.isFunction=function(t){return"[object Function]"===toString.call(t)},e.isObject=function(t){var e=typeof t;return"function"===e||"object"===e&&!!t},e.extend=function(t){var e=arguments,n=arguments.length;if(!t||n<2)return t||{};for(var r=1;r0)){var e=this.reactorState.get("dirtyStores");if(0!==e.size){var n=c.default.Set().withMutations((function(n){n.union(t.observerState.get("any")),e.forEach((function(e){var r=t.observerState.getIn(["stores",e]);r&&n.union(r)}))}));n.forEach((function(e){var n=t.observerState.getIn(["observersMap",e]);if(n){var r=n.get("getter"),i=n.get("handler"),o=p.evaluate(t.prevReactorState,r),u=p.evaluate(t.reactorState,r);t.prevReactorState=o.reactorState,t.reactorState=u.reactorState;var a=o.result,s=u.result;c.default.is(a,s)||i.call(null,s)}}));var r=p.resetDirtyStores(this.reactorState);this.prevReactorState=r,this.reactorState=r}}}},{key:"batchStart",value:function(){this.__batchDepth++}},{key:"batchEnd",value:function(){if(this.__batchDepth--,this.__batchDepth<=0){this.__isDispatching=!0;try{this.__notify()}catch(t){throw this.__isDispatching=!1,t}this.__isDispatching=!1}}}]),t})();e.default=(0,m.toFactory)(E),t.exports=e.default},function(t,e,n){function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n={};return(0,o.each)(e,(function(e,r){n[r]=t.evaluate(e)})),n}Object.defineProperty(e,"__esModule",{value:!0});var o=n(4);e.default=function(t){return{getInitialState:function(){return i(t,this.getDataBindings())},componentDidMount:function(){var e=this;this.__unwatchFns=[],(0,o.each)(this.getDataBindings(),(function(n,i){var o=t.observe(n,(function(t){e.setState(r({},i,t))}));e.__unwatchFns.push(o)}))},componentWillUnmount:function(){for(var t=this;this.__unwatchFns.length;)t.__unwatchFns.shift()()}}},t.exports=e.default},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){return new C({result:t,reactorState:e})}function o(t,e){return t.withMutations((function(t){(0,A.each)(e,(function(e,n){t.getIn(["stores",n])&&console.warn("Store already defined for id = "+n);var r=e.getInitialState();if(void 0===r&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store getInitialState() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,O.isImmutableValue)(r))throw new Error("Store getInitialState() must return an immutable value, did you forget to call toImmutable");t.update("stores",(function(t){return t.set(n,e)})).update("state",(function(t){return t.set(n,r)})).update("dirtyStores",(function(t){return t.add(n)})).update("storeStates",(function(t){return S(t,[n])}))})),m(t)}))}function u(t,e){return t.withMutations((function(t){(0,A.each)(e,(function(e,n){t.update("stores",(function(t){return t.set(n,e)}))}))}))}function a(t,e,n){var r=t.get("logger");if(void 0===e&&f(t,"throwOnUndefinedActionType"))throw new Error("`dispatch` cannot be called with an `undefined` action type.");var i=t.get("state"),o=t.get("dirtyStores"),u=i.withMutations((function(u){r.dispatchStart(t,e,n),t.get("stores").forEach((function(i,a){var s=u.get(a),c=void 0;try{c=i.handle(s,e,n)}catch(e){throw r.dispatchError(t,e.message),e}if(void 0===c&&f(t,"throwOnUndefinedStoreReturnValue")){var h="Store handler must return a value, did you forget a return statement";throw r.dispatchError(t,h),new Error(h)}u.set(a,c),s!==c&&(o=o.add(a))})),r.dispatchEnd(t,u,o,i)})),a=t.set("state",u).set("dirtyStores",o).update("storeStates",(function(t){return S(t,o)}));return m(a)}function s(t,e){var n=[],r=(0,O.toImmutable)({}).withMutations((function(r){(0,A.each)(e,(function(e,i){var o=t.getIn(["stores",i]);if(o){var u=o.deserialize(e);void 0!==u&&(r.set(i,u),n.push(i))}}))})),i=b.default.Set(n);return t.update("state",(function(t){return t.merge(r)})).update("dirtyStores",(function(t){return t.union(i)})).update("storeStates",(function(t){return S(t,n)}))}function c(t,e,n){var r=e;(0,T.isKeyPath)(e)&&(e=(0,w.fromKeyPath)(e));var i=t.get("nextId"),o=(0,w.getStoreDeps)(e),u=b.default.Map({id:i,storeDeps:o,getterKey:r,getter:e,handler:n}),a=void 0;return a=0===o.size?t.update("any",(function(t){return t.add(i)})):t.withMutations((function(t){o.forEach((function(e){var n=["stores",e];t.hasIn(n)||t.setIn(n,b.default.Set()),t.updateIn(["stores",e],(function(t){return t.add(i)}))}))})),a=a.set("nextId",i+1).setIn(["observersMap",i],u),{observerState:a,entry:u}}function f(t,e){var n=t.getIn(["options",e]);if(void 0===n)throw new Error("Invalid option: "+e);return n}function h(t,e,n){var r=t.get("observersMap").filter((function(t){var r=t.get("getterKey"),i=!n||t.get("handler")===n;return!!i&&((0,T.isKeyPath)(e)&&(0,T.isKeyPath)(r)?(0,T.isEqual)(e,r):e===r)}));return t.withMutations((function(t){r.forEach((function(e){return l(t,e)}))}))}function l(t,e){return t.withMutations((function(t){var n=e.get("id"),r=e.get("storeDeps");0===r.size?t.update("any",(function(t){return t.remove(n)})):r.forEach((function(e){t.updateIn(["stores",e],(function(t){return t?t.remove(n):t}))})),t.removeIn(["observersMap",n])}))}function p(t){var e=t.get("state");return t.withMutations((function(t){var n=t.get("stores"),r=n.keySeq().toJS();n.forEach((function(n,r){var i=e.get(r),o=n.handleReset(i);if(void 0===o&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store handleReset() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,O.isImmutableValue)(o))throw new Error("Store reset state must be an immutable value, did you forget to call toImmutable");t.setIn(["state",r],o)})),t.update("storeStates",(function(t){return S(t,r)})),v(t)}))}function _(t,e){var n=t.get("state");if((0,T.isKeyPath)(e))return i(n.getIn(e),t);if(!(0,w.isGetter)(e))throw new Error("evaluate must be passed a keyPath or Getter");var r=t.get("cache"),o=r.lookup(e),u=!o||y(t,o);return u&&(o=g(t,e)),i(o.get("value"),t.update("cache",(function(t){return u?t.miss(e,o):t.hit(e)})))}function d(t){var e={};return t.get("stores").forEach((function(n,r){var i=t.getIn(["state",r]),o=n.serialize(i);void 0!==o&&(e[r]=o)})),e}function v(t){return t.set("dirtyStores",b.default.Set())}function y(t,e){var n=e.get("storeStates");return!n.size||n.some((function(e,n){return t.getIn(["storeStates",n])!==e}))}function g(t,e){var n=(0,w.getDeps)(e).map((function(e){return _(t,e).result})),r=(0,w.getComputeFn)(e).apply(null,n),i=(0,w.getStoreDeps)(e),o=(0,O.toImmutable)({}).withMutations((function(e){i.forEach((function(n){var r=t.getIn(["storeStates",n]);e.set(n,r)}))}));return(0,I.CacheEntry)({value:r,storeStates:o,dispatchId:t.get("dispatchId")})}function m(t){return t.update("dispatchId",(function(t){return t+1}))}function S(t,e){return t.withMutations((function(t){e.forEach((function(e){var n=t.has(e)?t.get(e)+1:1;t.set(e,n)}))}))}Object.defineProperty(e,"__esModule",{value:!0}),e.registerStores=o,e.replaceStores=u,e.dispatch=a,e.loadState=s,e.addObserver=c,e.getOption=f,e.removeObserver=h,e.removeObserverByEntry=l,e.reset=p,e.evaluate=_,e.serialize=d,e.resetDirtyStores=v;var E=n(3),b=r(E),I=n(9),O=n(5),w=n(10),T=n(11),A=n(4),C=b.default.Record({result:null,reactorState:null})},function(t,e,n){function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(){return new s}Object.defineProperty(e,"__esModule",{value:!0});var o=(function(){function t(t,e){for(var n=0;nn.dispatchId)throw new Error("Refusing to cache older value");return n})))}},{key:"evict",value:function(e){return new t(this.cache.remove(e))}}]),t})();e.BasicCache=s;var c=1e3,f=1,h=(function(){function t(){var e=arguments.length<=0||void 0===arguments[0]?c:arguments[0],n=arguments.length<=1||void 0===arguments[1]?f:arguments[1],i=arguments.length<=2||void 0===arguments[2]?new s:arguments[2],o=arguments.length<=3||void 0===arguments[3]?(0,u.OrderedSet)():arguments[3];r(this,t),console.log("using LRU"),this.limit=e,this.evictCount=n,this.cache=i,this.lru=o}return o(t,[{key:"lookup",value:function(t,e){return this.cache.lookup(t,e)}},{key:"has",value:function(t){return this.cache.has(t)}},{key:"asMap",value:function(){return this.cache.asMap()}},{key:"hit",value:function(e){return this.cache.has(e)?new t(this.limit,this.evictCount,this.cache,this.lru.remove(e).add(e)):this}},{key:"miss",value:function(e,n){var r;if(this.lru.size>=this.limit){if(this.has(e))return new t(this.limit,this.evictCount,this.cache.miss(e,n),this.lru.remove(e).add(e));var i=this.lru.take(this.evictCount).reduce((function(t,e){return t.evict(e)}),this.cache).miss(e,n);r=new t(this.limit,this.evictCount,i,this.lru.skip(this.evictCount).add(e))}else r=new t(this.limit,this.evictCount,this.cache.miss(e,n),this.lru.add(e));return r}},{key:"evict",value:function(e){return this.cache.has(e)?new t(this.limit,this.evictCount,this.cache.evict(e),this.lru.remove(e)):this}}]),t})();e.LRUCache=h},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,l.isArray)(t)&&(0,l.isFunction)(t[t.length-1])}function o(t){return t[t.length-1]}function u(t){return t.slice(0,t.length-1)}function a(t,e){e||(e=h.default.Set());var n=h.default.Set().withMutations((function(e){if(!i(t))throw new Error("getFlattenedDeps must be passed a Getter");u(t).forEach((function(t){if((0,p.isKeyPath)(t))e.add((0,f.List)(t));else{if(!i(t))throw new Error("Invalid getter, each dependency must be a KeyPath or Getter");e.union(a(t))}}))}));return e.union(n)}function s(t){if(!(0,p.isKeyPath)(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,_]}function c(t){if(t.hasOwnProperty("__storeDeps"))return t.__storeDeps;var e=a(t).map((function(t){return t.first()})).filter((function(t){return!!t}));return Object.defineProperty(t,"__storeDeps",{enumerable:!1,configurable:!1,writable:!1,value:e}),e}Object.defineProperty(e,"__esModule",{value:!0});var f=n(3),h=r(f),l=n(4),p=n(11),_=function(t){return t};e.default={isGetter:i,getComputeFn:o,getFlattenedDeps:a,getStoreDeps:c,getDeps:u,fromKeyPath:s},t.exports=e.default},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,s.isArray)(t)&&!(0,s.isFunction)(t[t.length-1])}function o(t,e){var n=a.default.List(t),r=a.default.List(e);return a.default.is(n,r)}Object.defineProperty(e,"__esModule",{value:!0}),e.isKeyPath=i,e.isEqual=o;var u=n(3),a=r(u),s=n(4)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(8),i={dispatchStart:function(t,e,n){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.groupCollapsed("Dispatch: %s",e),console.group("payload"),console.debug(n),console.groupEnd())},dispatchError:function(t,e){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.debug("Dispatch error: "+e),console.groupEnd())},dispatchEnd:function(t,e,n,i){(0,r.getOption)(t,"logDispatches")&&console.group&&((0,r.getOption)(t,"logDirtyStores")&&console.log("Stores updated:",n.toList().toJS()),(0,r.getOption)(t,"logAppState")&&console.debug("Dispatch done, new state: ",e.toJS()),console.groupEnd())}};e.ConsoleGroupLogger=i;var o={dispatchStart:function(t,e,n){},dispatchError:function(t,e){},dispatchEnd:function(t,e,n){}};e.NoopLogger=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=n(9),o=n(12),u=(0,r.Map)({logDispatches:!1,logAppState:!1,logDirtyStores:!1,throwOnUndefinedActionType:!1,throwOnUndefinedStoreReturnValue:!1,throwOnNonImmutableStore:!1,throwOnDispatchInDispatch:!1});e.PROD_OPTIONS=u;var a=(0,r.Map)({logDispatches:!0,logAppState:!0,logDirtyStores:!0,throwOnUndefinedActionType:!0,throwOnUndefinedStoreReturnValue:!0,throwOnNonImmutableStore:!0,throwOnDispatchInDispatch:!0});e.DEBUG_OPTIONS=a;var s=(0,r.Record)({dispatchId:0,state:(0,r.Map)(),stores:(0,r.Map)(),cache:(0,i.DefaultCache)(),logger:o.NoopLogger,storeStates:(0,r.Map)(),dirtyStores:(0,r.Set)(),debug:!1,options:u});e.ReactorState=s;var c=(0,r.Record)({any:(0,r.Set)(),stores:(0,r.Map)({}),observersMap:(0,r.Map)({}),nextId:1});e.ObserverState=c}])}))})),ke=t(je),Ne=function(t){var e,n={};if(!(t instanceof Object)||Array.isArray(t))throw new Error("keyMirror(...): Argument must be an object.");for(e in t)t.hasOwnProperty(e)&&(n[e]=e);return n},Pe=Ne,Ue=Pe({VALIDATING_AUTH_TOKEN:null,VALID_AUTH_TOKEN:null,INVALID_AUTH_TOKEN:null,LOG_OUT:null}),He=ke.Store,xe=ke.toImmutable,Ve=new He({getInitialState:function(){return xe({isValidating:!1,authToken:!1,host:null,isInvalid:!1,errorMessage:""})},initialize:function(){this.on(Ue.VALIDATING_AUTH_TOKEN,n),this.on(Ue.VALID_AUTH_TOKEN,r),this.on(Ue.INVALID_AUTH_TOKEN,i)}}),qe=ke.Store,Fe=ke.toImmutable,Ge=new qe({getInitialState:function(){return Fe({authToken:null,host:""})},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,o),this.on(Ue.LOG_OUT,u)}}),Ke=ke.Store,Be=new Ke({getInitialState:function(){return!0},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,a)}}),Ye=Pe({STREAM_START:null,STREAM_STOP:null,STREAM_ERROR:null}),Je=ke.Store,We=ke.toImmutable,Xe=new Je({getInitialState:function(){return We({isStreaming:!1,hasError:!1})},initialize:function(){this.on(Ye.STREAM_START,s),this.on(Ye.STREAM_ERROR,c),this.on(Ye.LOG_OUT,f)}}),Qe=1,Ze=2,$e=3,tn=function(t,e){this.url=t,this.options=e||{},this.commandId=1,this.commands={},this.connectionTries=0,this.eventListeners={},this.closeRequested=!1};tn.prototype.addEventListener=function(t,e){var n=this.eventListeners[t];n||(n=this.eventListeners[t]=[]),n.push(e)},tn.prototype.fireEvent=function(t){var e=this;(this.eventListeners[t]||[]).forEach((function(t){return t(e)}))},tn.prototype.connect=function(){var t=this;return new Promise(function(e,n){var r=t.commands;Object.keys(r).forEach((function(t){var e=r[t];e.reject&&e.reject(S($e,"Connection lost"))}));var i=!1;t.connectionTries+=1,t.socket=new WebSocket(t.url),t.socket.addEventListener("open",(function(){t.connectionTries=0})),t.socket.addEventListener("message",(function(o){var u=JSON.parse(o.data);switch(u.type){case"event":t.commands[u.id].eventCallback(u.event);break;case"result":u.success?t.commands[u.id].resolve(u):t.commands[u.id].reject(u.error), -delete t.commands[u.id];break;case"pong":break;case"auth_required":t.sendMessage(h(t.options.authToken));break;case"auth_invalid":n(Ze),i=!0;break;case"auth_ok":e(t),t.fireEvent("ready"),t.commandId=1,t.commands={},Object.keys(r).forEach((function(e){var n=r[e];n.eventType&&t.subscribeEvents(n.eventCallback,n.eventType).then((function(t){n.unsubscribe=t}))}))}})),t.socket.addEventListener("close",(function(){if(!i&&!t.closeRequested){0===t.connectionTries?t.fireEvent("disconnected"):n(Qe);var e=1e3*Math.min(t.connectionTries,5);setTimeout((function(){return t.connect()}),e)}}))})},tn.prototype.close=function(){this.closeRequested=!0,this.socket.close()},tn.prototype.getStates=function(){return this.sendMessagePromise(l()).then(E)},tn.prototype.getServices=function(){return this.sendMessagePromise(_()).then(E)},tn.prototype.getPanels=function(){return this.sendMessagePromise(d()).then(E)},tn.prototype.getConfig=function(){return this.sendMessagePromise(p()).then(E)},tn.prototype.callService=function(t,e,n){return this.sendMessagePromise(v(t,e,n))},tn.prototype.subscribeEvents=function(t,e){var n=this;return this.sendMessagePromise(y(e)).then((function(r){var i={eventCallback:t,eventType:e,unsubscribe:function(){return n.sendMessagePromise(g(r.id)).then((function(){delete n.commands[r.id]}))}};return n.commands[r.id]=i,function(){return i.unsubscribe()}}))},tn.prototype.ping=function(){return this.sendMessagePromise(m())},tn.prototype.sendMessage=function(t){this.socket.send(JSON.stringify(t))},tn.prototype.sendMessagePromise=function(t){var e=this;return new Promise(function(n,r){e.commandId+=1;var i=e.commandId;t.id=i,e.commands[i]={resolve:n,reject:r},e.sendMessage(t)})};var en=Pe({API_FETCH_ALL_START:null,API_FETCH_ALL_SUCCESS:null,API_FETCH_ALL_FAIL:null,SYNC_SCHEDULED:null,SYNC_SCHEDULE_CANCELLED:null}),nn=ke.Store,rn=new nn({getInitialState:function(){return!0},initialize:function(){this.on(en.API_FETCH_ALL_START,(function(){return!0})),this.on(en.API_FETCH_ALL_SUCCESS,(function(){return!1})),this.on(en.API_FETCH_ALL_FAIL,(function(){return!1})),this.on(en.LOG_OUT,(function(){return!1}))}}),on=I,un=Pe({API_FETCH_SUCCESS:null,API_FETCH_START:null,API_FETCH_FAIL:null,API_SAVE_SUCCESS:null,API_SAVE_START:null,API_SAVE_FAIL:null,API_DELETE_SUCCESS:null,API_DELETE_START:null,API_DELETE_FAIL:null,LOG_OUT:null}),an=ke.Store,sn=ke.toImmutable,cn=new an({getInitialState:function(){return sn({})},initialize:function(){var t=this;this.on(un.API_FETCH_SUCCESS,O),this.on(un.API_SAVE_SUCCESS,O),this.on(un.API_DELETE_SUCCESS,w),this.on(un.LOG_OUT,(function(){return t.getInitialState()}))}}),fn=Object.prototype.hasOwnProperty,hn=Object.prototype.propertyIsEnumerable,ln=A()?Object.assign:function(t,e){for(var n,r,i=arguments,o=T(t),u=1;u199&&u.status<300?t(e):n(e)},u.onerror=function(){return n({})},r?(u.setRequestHeader("Content-Type","application/json;charset=UTF-8"),u.send(JSON.stringify(r))):u.send()})}function O(t,e){var n=e.model,r=e.result,i=e.params,o=n.entity;if(!r)return t;var u=i.replace?sn({}):t.get(o),a=Array.isArray(r)?r:[r],s=n.fromJSON||sn;return t.set(o,u.withMutations((function(t){for(var e=0;e6e4}function gt(t,e){var n=e.date;return n.toISOString()}function mt(){return Zr.getInitialState()}function St(t,e){var n=e.date,r=e.stateHistory;return 0===r.length?t.set(n,ti({})):t.withMutations((function(t){r.forEach((function(e){return t.setIn([n,e[0].entity_id],ti(e.map(On.fromJSON)))}))}))}function Et(){return ei.getInitialState()}function bt(t,e){var n=e.stateHistory;return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,oi(e.map(On.fromJSON)))}))}))}function It(){return ui.getInitialState()}function Ot(t,e){var n=e.stateHistory,r=(new Date).getTime();return t.withMutations((function(t){n.forEach((function(e){return t.set(e[0].entity_id,r)})),history.length>1&&t.set(ci,r)}))}function wt(){return fi.getInitialState()}function Tt(t,e){t.dispatch(Xr.ENTITY_HISTORY_DATE_SELECTED,{date:e})}function At(t,e){void 0===e&&(e=null),t.dispatch(Xr.RECENT_ENTITY_HISTORY_FETCH_START,{});var n="history/period";return null!==e&&(n+="?filter_entity_id="+e),on(t,"GET",n).then((function(e){return t.dispatch(Xr.RECENT_ENTITY_HISTORY_FETCH_SUCCESS,{stateHistory:e})}),(function(){return t.dispatch(Xr.RECENT_ENTITY_HISTORY_FETCH_ERROR,{})}))}function Ct(t,e){return t.dispatch(Xr.ENTITY_HISTORY_FETCH_START,{date:e}),on(t,"GET","history/period/"+e).then((function(n){return t.dispatch(Xr.ENTITY_HISTORY_FETCH_SUCCESS,{date:e,stateHistory:n})}),(function(){return t.dispatch(Xr.ENTITY_HISTORY_FETCH_ERROR,{})}))}function Dt(t){var e=t.evaluate(pi);return Ct(t,e)}function zt(t){t.registerStores({currentEntityHistoryDate:Zr,entityHistory:ei,isLoadingEntityHistory:ri,recentEntityHistory:ui,recentEntityHistoryUpdated:fi})}function Rt(t){t.registerStores({moreInfoEntityId:Jr})}function Mt(t,e){var n=e.model,r=e.result,i=e.params;if(null===t||"entity"!==n.entity||!i.replace)return t;for(var o=0;o0?i=setTimeout(r,e-c):(i=null,n||(s=t.apply(u,o),i||(u=o=null)))}var i,o,u,a,s;null==e&&(e=100);var c=function(){u=this,o=arguments,a=(new Date).getTime();var c=n&&!i;return i||(i=setTimeout(r,e)),c&&(s=t.apply(u,o),u=o=null),s};return c.clear=function(){i&&(clearTimeout(i),i=null)},c}function Yt(t){var e=ho[t.hassId];e&&(e.scheduleHealthCheck.clear(),e.conn.close(),ho[t.hassId]=!1)}function Jt(t,e){void 0===e&&(e={});var n=e.syncOnInitialConnect;void 0===n&&(n=!0),Yt(t);var r=t.evaluate(Lo.authToken),i="https:"===document.location.protocol?"wss://":"ws://";i+=document.location.hostname,document.location.port&&(i+=":"+document.location.port),i+="/api/websocket",b(i,{authToken:r}).then((function(e){var r=Bt((function(){return e.ping()}),co);r(),e.socket.addEventListener("message",r),ho[t.hassId]={conn:e,scheduleHealthCheck:r},fo.forEach((function(n){return e.subscribeEvents(so.bind(null,t),n)})),t.batch((function(){t.dispatch(Ye.STREAM_START),n&&oo.fetchAll(t)})),e.addEventListener("disconnected",(function(){t.dispatch(Ye.STREAM_ERROR)})),e.addEventListener("ready",(function(){t.batch((function(){t.dispatch(Ye.STREAM_START),oo.fetchAll(t)}))}))}))}function Wt(t){t.registerStores({streamStatus:Xe})}function Xt(t,e,n){void 0===n&&(n={});var r=n.rememberAuth;void 0===r&&(r=!1);var i=n.host;void 0===i&&(i=""),t.dispatch(Ue.VALIDATING_AUTH_TOKEN,{authToken:e,host:i}),oo.fetchAll(t).then((function(){t.dispatch(Ue.VALID_AUTH_TOKEN,{authToken:e,host:i,rememberAuth:r}),yo.start(t,{syncOnInitialConnect:!1})}),(function(e){void 0===e&&(e={});var n=e.message;void 0===n&&(n=So),t.dispatch(Ue.INVALID_AUTH_TOKEN,{errorMessage:n})}))}function Qt(t){t.dispatch(Ue.LOG_OUT,{})}function Zt(t){t.registerStores({authAttempt:Ve,authCurrent:Ge,rememberAuth:Be})}function $t(){if(!("localStorage"in window))return{};var t=window.localStorage,e="___test";try{return t.setItem(e,e),t.removeItem(e),t}catch(t){return{}}}function te(){var t=new Ho({debug:!1});return t.hassId=xo++,t}function ee(t,e,n){Object.keys(n).forEach((function(r){var i=n[r];if("register"in i&&i.register(e),"getters"in i&&Object.defineProperty(t,r+"Getters",{value:i.getters,enumerable:!0}),"actions"in i){var o={};Object.getOwnPropertyNames(i.actions).forEach((function(t){"function"==typeof i.actions[t]&&Object.defineProperty(o,t,{value:i.actions[t].bind(null,e),enumerable:!0})})),Object.defineProperty(t,r+"Actions",{value:o,enumerable:!0})}}))}function ne(t,e){return Vo(t.attributes.entity_id.map((function(t){return e.get(t)})).filter((function(t){return!!t})))}function re(t){return on(t,"GET","error_log")}function ie(t,e){var n=e.date;return n.toISOString()}function oe(){return Wo.getInitialState()}function ue(t,e){var n=e.date,r=e.entries;return t.set(n,nu(r.map(tu.fromJSON)))}function ae(){return ru.getInitialState()}function se(t,e){var n=e.date;return t.set(n,(new Date).getTime())}function ce(){return uu.getInitialState()}function fe(t,e){t.dispatch(Yo.LOGBOOK_DATE_SELECTED,{date:e})}function he(t,e){t.dispatch(Yo.LOGBOOK_ENTRIES_FETCH_START,{date:e}),on(t,"GET","logbook/"+e).then((function(n){return t.dispatch(Yo.LOGBOOK_ENTRIES_FETCH_SUCCESS,{date:e,entries:n})}),(function(){return t.dispatch(Yo.LOGBOOK_ENTRIES_FETCH_ERROR,{})}))}function le(t){return!t||(new Date).getTime()-t>cu}function pe(t){t.registerStores({currentLogbookDate:Wo,isLoadingLogbookEntries:Qo,logbookEntries:ru,logbookEntriesUpdated:uu})}function _e(t){return t.set("active",!0)}function de(t){return t.set("active",!1)}function ve(){return Eu.getInitialState()}function ye(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered.");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){var n;return n=navigator.userAgent.toLowerCase().indexOf("firefox")>-1?"firefox":"chrome",on(t,"POST","notify.html5",{subscription:e,browser:n}).then((function(){return t.dispatch(gu.PUSH_NOTIFICATIONS_SUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n;return n=e.message&&e.message.indexOf("gcm_sender_id")!==-1?"Please setup the notify.html5 platform.":"Notification registration failed.",console.error(e),qn.createNotification(t,n),!1}))}function ge(t){return navigator.serviceWorker.getRegistration().then((function(t){if(!t)throw new Error("No service worker registered");return t.pushManager.subscribe({userVisibleOnly:!0})})).then((function(e){return on(t,"DELETE","notify.html5",{subscription:e}).then((function(){return e.unsubscribe()})).then((function(){return t.dispatch(gu.PUSH_NOTIFICATIONS_UNSUBSCRIBE,{})})).then((function(){return!0}))})).catch((function(e){var n="Failed unsubscribing for push notifications.";return console.error(e),qn.createNotification(t,n),!1}))}function me(t){t.registerStores({pushNotifications:Eu})}function Se(t,e){return on(t,"POST","template",{template:e})}function Ee(t){return t.set("isListening",!0)}function be(t,e){var n=e.interimTranscript,r=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!0).set("isTransmitting",!1).set("interimTranscript",n).set("finalTranscript",r)}))}function Ie(t,e){var n=e.finalTranscript;return t.withMutations((function(t){return t.set("isListening",!1).set("isTransmitting",!0).set("interimTranscript","").set("finalTranscript",n)}))}function Oe(){return Pu.getInitialState()}function we(){return Pu.getInitialState()}function Te(){return Pu.getInitialState()}function Ae(t){return Uu[t.hassId]}function Ce(t){var e=Ae(t);if(e){var n=e.finalTranscript||e.interimTranscript;t.dispatch(ju.VOICE_TRANSMITTING,{finalTranscript:n}),er.callService(t,"conversation","process",{text:n}).then((function(){t.dispatch(ju.VOICE_DONE)}),(function(){t.dispatch(ju.VOICE_ERROR)}))}}function De(t){var e=Ae(t);e&&(e.recognition.stop(),Uu[t.hassId]=!1)}function ze(t){Ce(t),De(t)}function Re(t){var e=ze.bind(null,t);e();var n=new webkitSpeechRecognition;Uu[t.hassId]={recognition:n,interimTranscript:"",finalTranscript:""},n.interimResults=!0,n.onstart=function(){return t.dispatch(ju.VOICE_START)},n.onerror=function(){return t.dispatch(ju.VOICE_ERROR)},n.onend=e,n.onresult=function(e){var n=Ae(t);if(n){for(var r="",i="",o=e.resultIndex;o>>0;if(""+n!==e||4294967295===n)return NaN;e=n}return e<0?_(t)+e:e}function v(){return!0}function y(t,e,n){return(0===t||void 0!==n&&t<=-n)&&(void 0===e||void 0!==n&&e>=n)}function g(t,e){return S(t,e,0)}function m(t,e){return S(t,e,e)}function S(t,e,n){return void 0===t?n:t<0?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function E(t){this.next=t}function b(t,e,n,r){var i=0===t?e:1===t?n:[e,n];return r?r.value=i:r={value:i,done:!1},r}function I(){return{value:void 0,done:!0}}function O(t){return!!A(t)}function w(t){return t&&"function"==typeof t.next}function T(t){var e=A(t);return e&&e.call(t)}function A(t){var e=t&&(In&&t[In]||t[On]);if("function"==typeof e)return e}function C(t){return t&&"number"==typeof t.length}function D(t){return null===t||void 0===t?U():o(t)?t.toSeq():V(t)}function z(t){return null===t||void 0===t?U().toKeyedSeq():o(t)?u(t)?t.toSeq():t.fromEntrySeq():H(t)}function R(t){return null===t||void 0===t?U():o(t)?u(t)?t.entrySeq():t.toIndexedSeq():x(t)}function M(t){return(null===t||void 0===t?U():o(t)?u(t)?t.entrySeq():t:x(t)).toSetSeq()}function L(t){this._array=t,this.size=t.length}function j(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function k(t){this._iterable=t,this.size=t.length||t.size}function N(t){this._iterator=t,this._iteratorCache=[]}function P(t){return!(!t||!t[Tn])}function U(){return An||(An=new L([]))}function H(t){var e=Array.isArray(t)?new L(t).fromEntrySeq():w(t)?new N(t).fromEntrySeq():O(t)?new k(t).fromEntrySeq():"object"==typeof t?new j(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function x(t){var e=q(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function V(t){var e=q(t)||"object"==typeof t&&new j(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function q(t){return C(t)?new L(t):w(t)?new N(t):O(t)?new k(t):void 0}function F(t,e,n,r){var i=t._cache;if(i){for(var o=i.length-1,u=0;u<=o;u++){var a=i[n?o-u:u];if(e(a[1],r?a[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,n)}function G(t,e,n,r){var i=t._cache;if(i){var o=i.length-1,u=0;return new E(function(){var t=i[n?o-u:u];return u++>o?I():b(e,r?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,n)}function K(t,e){return e?B(e,t,"",{"":t}):Y(t)}function B(t,e,n,r){return Array.isArray(e)?t.call(r,n,R(e).map((function(n,r){return B(t,n,r,e)}))):J(e)?t.call(r,n,z(e).map((function(n,r){return B(t,n,r,e)}))):e}function Y(t){return Array.isArray(t)?R(t).map(Y).toList():J(t)?z(t).map(Y).toMap():t}function J(t){return t&&(t.constructor===Object||void 0===t.constructor)}function W(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return!("function"!=typeof t.equals||"function"!=typeof e.equals||!t.equals(e))}function X(t,e){if(t===e)return!0;if(!o(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||u(t)!==u(e)||a(t)!==a(e)||c(t)!==c(e))return!1;if(0===t.size&&0===e.size)return!0;var n=!s(t);if(c(t)){var r=t.entries();return e.every((function(t,e){var i=r.next().value;return i&&W(i[1],t)&&(n||W(i[0],e))}))&&r.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var f=t;t=e,e=f}var h=!0,l=e.__iterate((function(e,r){if(n?!t.has(e):i?!W(e,t.get(r,yn)):!W(t.get(r,yn),e))return h=!1,!1}));return h&&t.size===l}function Q(t,e){if(!(this instanceof Q))return new Q(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(Cn)return Cn;Cn=this}}function Z(t,e){if(!t)throw new Error(e)}function $(t,e,n){if(!(this instanceof $))return new $(t,e,n);if(Z(0!==n,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),n=void 0===n?1:Math.abs(n),e>>1&1073741824|3221225471&t}function ot(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){if(t!==t||t===1/0)return 0;var n=0|t;for(n!==t&&(n^=4294967295*t);t>4294967295;)t/=4294967295,n^=t;return it(n)}if("string"===e)return t.length>Pn?ut(t):at(t);if("function"==typeof t.hashCode)return t.hashCode();if("object"===e)return st(t);if("function"==typeof t.toString)return at(t.toString());throw new Error("Value type "+e+" cannot be hashed.")}function ut(t){var e=xn[t];return void 0===e&&(e=at(t),Hn===Un&&(Hn=0,xn={}),Hn++,xn[t]=e),e}function at(t){for(var e=0,n=0;n0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ft(t){Z(t!==1/0,"Cannot perform this action with an infinite size.")}function ht(t){return null===t||void 0===t?bt():lt(t)&&!c(t)?t:bt().withMutations((function(e){var r=n(t);ft(r.size),r.forEach((function(t,n){return e.set(n,t)}))}))}function lt(t){return!(!t||!t[Vn])}function pt(t,e){this.ownerID=t,this.entries=e}function _t(t,e,n){this.ownerID=t,this.bitmap=e,this.nodes=n}function dt(t,e,n){this.ownerID=t,this.count=e,this.nodes=n}function vt(t,e,n){this.ownerID=t,this.keyHash=e,this.entries=n}function yt(t,e,n){this.ownerID=t,this.keyHash=e,this.entry=n}function gt(t,e,n){this._type=e,this._reverse=n,this._stack=t._root&&St(t._root)}function mt(t,e){return b(t,e[0],e[1])}function St(t,e){return{node:t,index:0,__prev:e}}function Et(t,e,n,r){var i=Object.create(qn);return i.size=t,i._root=e,i.__ownerID=n,i.__hash=r,i.__altered=!1,i}function bt(){return Fn||(Fn=Et(0))}function It(t,e,n){var r,i;if(t._root){var o=f(gn),u=f(mn);if(r=Ot(t._root,t.__ownerID,0,void 0,e,n,o,u),!u.value)return t;i=t.size+(o.value?n===yn?-1:1:0)}else{if(n===yn)return t;i=1,r=new pt(t.__ownerID,[[e,n]])}return t.__ownerID?(t.size=i,t._root=r,t.__hash=void 0,t.__altered=!0,t):r?Et(i,r):bt()}function Ot(t,e,n,r,i,o,u,a){return t?t.update(e,n,r,i,o,u,a):o===yn?t:(h(a),h(u),new yt(e,r,[i,o]))}function wt(t){return t.constructor===yt||t.constructor===vt}function Tt(t,e,n,r,i){if(t.keyHash===r)return new vt(e,r,[t.entry,i]);var o,u=(0===n?t.keyHash:t.keyHash>>>n)&vn,a=(0===n?r:r>>>n)&vn,s=u===a?[Tt(t,e,n+_n,r,i)]:(o=new yt(e,r,i),u>>=1)u[a]=1&n?e[o++]:void 0;return u[r]=i,new dt(t,o+1,u)}function zt(t,e,r){for(var i=[],u=0;u>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function Nt(t,e,n,r){var i=r?t:p(t);return i[e]=n,i}function Pt(t,e,n,r){var i=t.length+1;if(r&&e+1===i)return t[e]=n,t;for(var o=new Array(i),u=0,a=0;a0&&io?0:o-n,c=u-n;return c>dn&&(c=dn),function(){if(i===c)return Xn;var t=e?--c:i++;return r&&r[t]}}function i(t,r,i){var a,s=t&&t.array,c=i>o?0:o-i>>r,f=(u-i>>r)+1;return f>dn&&(f=dn),function(){for(;;){if(a){var t=a();if(t!==Xn)return t;a=null}if(c===f)return Xn;var o=e?--f:c++;a=n(s&&s[o],r-_n,i+(o<=t.size||e<0)return t.withMutations((function(t){e<0?Wt(t,e).set(0,n):Wt(t,0,e+1).set(e,n)}));e+=t._origin;var r=t._tail,i=t._root,o=f(mn);return e>=Qt(t._capacity)?r=Bt(r,t.__ownerID,0,e,n,o):i=Bt(i,t.__ownerID,t._level,e,n,o),o.value?t.__ownerID?(t._root=i,t._tail=r,t.__hash=void 0,t.__altered=!0,t):Ft(t._origin,t._capacity,t._level,i,r):t}function Bt(t,e,n,r,i,o){var u=r>>>n&vn,a=t&&u0){var c=t&&t.array[u],f=Bt(c,e,n-_n,r,i,o);return f===c?t:(s=Yt(t,e),s.array[u]=f,s)}return a&&t.array[u]===i?t:(h(o),s=Yt(t,e),void 0===i&&u===s.array.length-1?s.array.pop():s.array[u]=i,s)}function Yt(t,e){return e&&t&&e===t.ownerID?t:new Vt(t?t.array.slice():[],e)}function Jt(t,e){if(e>=Qt(t._capacity))return t._tail;if(e<1<0;)n=n.array[e>>>r&vn],r-=_n;return n}}function Wt(t,e,n){void 0!==e&&(e|=0),void 0!==n&&(n|=0);var r=t.__ownerID||new l,i=t._origin,o=t._capacity,u=i+e,a=void 0===n?o:n<0?o+n:i+n;if(u===i&&a===o)return t;if(u>=a)return t.clear();for(var s=t._level,c=t._root,f=0;u+f<0;)c=new Vt(c&&c.array.length?[void 0,c]:[],r),s+=_n,f+=1<=1<h?new Vt([],r):_;if(_&&p>h&&u_n;y-=_n){var g=h>>>y&vn;v=v.array[g]=Yt(v.array[g],r)}v.array[h>>>_n&vn]=_}if(a=p)u-=p,a-=p,s=_n,c=null,d=d&&d.removeBefore(r,0,u);else if(u>i||p>>s&vn;if(m!==p>>>s&vn)break;m&&(f+=(1<i&&(c=c.removeBefore(r,s,u-f)),c&&pu&&(u=c.size),o(s)||(c=c.map((function(t){return K(t)}))),i.push(c)}return u>t.size&&(t=t.setSize(u)),Lt(t,e,i)}function Qt(t){return t>>_n<<_n}function Zt(t){return null===t||void 0===t?ee():$t(t)?t:ee().withMutations((function(e){var r=n(t);ft(r.size),r.forEach((function(t,n){return e.set(n,t)}))}))}function $t(t){return lt(t)&&c(t)}function te(t,e,n,r){var i=Object.create(Zt.prototype);return i.size=t?t.size:0,i._map=t,i._list=e,i.__ownerID=n,i.__hash=r,i}function ee(){return Qn||(Qn=te(bt(),Gt()))}function ne(t,e,n){var r,i,o=t._map,u=t._list,a=o.get(e),s=void 0!==a;if(n===yn){if(!s)return t;u.size>=dn&&u.size>=2*o.size?(i=u.filter((function(t,e){return void 0!==t&&a!==e})),r=i.toKeyedSeq().map((function(t){return t[0]})).flip().toMap(),t.__ownerID&&(r.__ownerID=i.__ownerID=t.__ownerID)):(r=o.remove(e),i=a===u.size-1?u.pop():u.set(a,void 0))}else if(s){if(n===u.get(a)[1])return t;r=o,i=u.set(a,[e,n])}else r=o.set(e,u.size),i=u.set(u.size,[e,n]);return t.__ownerID?(t.size=r.size,t._map=r,t._list=i,t.__hash=void 0,t):te(r,i)}function re(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ie(t){this._iter=t,this.size=t.size}function oe(t){this._iter=t,this.size=t.size}function ue(t){this._iter=t,this.size=t.size}function ae(t){var e=Ce(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=De,e.__iterateUncached=function(e,n){var r=this;return t.__iterate((function(t,n){return e(n,t,r)!==!1}),n)},e.__iteratorUncached=function(e,n){if(e===bn){var r=t.__iterator(e,n);return new E(function(){var t=r.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===En?Sn:En,n)},e}function se(t,e,n){var r=Ce(t);return r.size=t.size,r.has=function(e){return t.has(e)},r.get=function(r,i){var o=t.get(r,yn);return o===yn?i:e.call(n,o,r,t)},r.__iterateUncached=function(r,i){var o=this;return t.__iterate((function(t,i,u){return r(e.call(n,t,i,u),i,o)!==!1}),i)},r.__iteratorUncached=function(r,i){var o=t.__iterator(bn,i);return new E(function(){var i=o.next();if(i.done)return i;var u=i.value,a=u[0];return b(r,a,e.call(n,u[1],a,t),i)})},r}function ce(t,e){var n=Ce(t);return n._iter=t,n.size=t.size,n.reverse=function(){return t},t.flip&&(n.flip=function(){var e=ae(t);return e.reverse=function(){return t.flip()},e}),n.get=function(n,r){return t.get(e?n:-1-n,r)},n.has=function(n){return t.has(e?n:-1-n)},n.includes=function(e){return t.includes(e)},n.cacheResult=De,n.__iterate=function(e,n){var r=this;return t.__iterate((function(t,n){return e(t,n,r)}),!n)},n.__iterator=function(e,n){return t.__iterator(e,!n)},n}function fe(t,e,n,r){var i=Ce(t);return r&&(i.has=function(r){var i=t.get(r,yn);return i!==yn&&!!e.call(n,i,r,t)},i.get=function(r,i){var o=t.get(r,yn);return o!==yn&&e.call(n,o,r,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,a=0;return t.__iterate((function(t,o,s){if(e.call(n,t,o,s))return a++,i(t,r?o:a-1,u)}),o),a},i.__iteratorUncached=function(i,o){var u=t.__iterator(bn,o),a=0;return new E(function(){for(;;){var o=u.next();if(o.done)return o;var s=o.value,c=s[0],f=s[1];if(e.call(n,f,c,t))return b(i,r?c:a++,f,o)}})},i}function he(t,e,n){var r=ht().asMutable();return t.__iterate((function(i,o){r.update(e.call(n,i,o,t),0,(function(t){return t+1}))})),r.asImmutable()}function le(t,e,n){var r=u(t),i=(c(t)?Zt():ht()).asMutable();t.__iterate((function(o,u){i.update(e.call(n,o,u,t),(function(t){return t=t||[],t.push(r?[u,o]:o),t}))}));var o=Ae(t);return i.map((function(e){return Oe(t,o(e))}))}function pe(t,e,n,r){var i=t.size;if(void 0!==e&&(e|=0),void 0!==n&&(n===1/0?n=i:n|=0),y(e,n,i))return t;var o=g(e,i),u=m(n,i);if(o!==o||u!==u)return pe(t.toSeq().cacheResult(),e,n,r);var a,s=u-o;s===s&&(a=s<0?0:s);var c=Ce(t);return c.size=0===a?a:t.size&&a||void 0,!r&&P(t)&&a>=0&&(c.get=function(e,n){return e=d(this,e),e>=0&&ea)return I();var t=i.next();return r||e===En?t:e===Sn?b(e,s-1,void 0,t):b(e,s-1,t.value[1],t)})},c}function _e(t,e,n){var r=Ce(t);return r.__iterateUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterate(r,i);var u=0;return t.__iterate((function(t,i,a){return e.call(n,t,i,a)&&++u&&r(t,i,o)})),u},r.__iteratorUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterator(r,i);var u=t.__iterator(bn,i),a=!0;return new E(function(){if(!a)return I();var t=u.next();if(t.done)return t;var i=t.value,s=i[0],c=i[1];return e.call(n,c,s,o)?r===bn?t:b(r,s,c,t):(a=!1,I())})},r}function de(t,e,n,r){var i=Ce(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var a=!0,s=0;return t.__iterate((function(t,o,c){if(!a||!(a=e.call(n,t,o,c)))return s++,i(t,r?o:s-1,u)})),s},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var a=t.__iterator(bn,o),s=!0,c=0;return new E(function(){var t,o,f;do{if(t=a.next(),t.done)return r||i===En?t:i===Sn?b(i,c++,void 0,t):b(i,c++,t.value[1],t);var h=t.value;o=h[0],f=h[1],s&&(s=e.call(n,f,o,u))}while(s);return i===bn?t:b(i,o,f,t)})},i}function ve(t,e){var r=u(t),i=[t].concat(e).map((function(t){return o(t)?r&&(t=n(t)):t=r?H(t):x(Array.isArray(t)?t:[t]),t})).filter((function(t){return 0!==t.size}));if(0===i.length)return t;if(1===i.length){var s=i[0];if(s===t||r&&u(s)||a(t)&&a(s))return s}var c=new L(i);return r?c=c.toKeyedSeq():a(t)||(c=c.toSetSeq()),c=c.flatten(!0),c.size=i.reduce((function(t,e){if(void 0!==t){var n=e.size;if(void 0!==n)return t+n}}),0),c}function ye(t,e,n){var r=Ce(t);return r.__iterateUncached=function(r,i){function u(t,c){var f=this;t.__iterate((function(t,i){return(!e||c0}function Ie(t,n,r){var i=Ce(t);return i.size=new L(r).map((function(t){return t.size})).min(),i.__iterate=function(t,e){for(var n,r=this,i=this.__iterator(En,e),o=0;!(n=i.next()).done&&t(n.value,o++,r)!==!1;);return o},i.__iteratorUncached=function(t,i){var o=r.map((function(t){return t=e(t),T(i?t.reverse():t)})),u=0,a=!1;return new E(function(){var e;return a||(e=o.map((function(t){return t.next()})),a=e.some((function(t){return t.done}))),a?I():b(t,u++,n.apply(null,e.map((function(t){return t.value}))))})},i}function Oe(t,e){return P(t)?e:t.constructor(e)}function we(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function Te(t){return ft(t.size),_(t)}function Ae(t){return u(t)?n:a(t)?r:i}function Ce(t){return Object.create((u(t)?z:a(t)?R:M).prototype)}function De(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):D.prototype.cacheResult.call(this)}function ze(t,e){return t>e?1:te?-1:0}function on(t){if(t.size===1/0)return 0;var e=c(t),n=u(t),r=e?1:0,i=t.__iterate(n?e?function(t,e){r=31*r+an(ot(t),ot(e))|0}:function(t,e){r=r+an(ot(t),ot(e))|0}:e?function(t){r=31*r+ot(t)|0}:function(t){r=r+ot(t)|0});return un(i,r)}function un(t,e){return e=Rn(e,3432918353),e=Rn(e<<15|e>>>-15,461845907),e=Rn(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=Rn(e^e>>>16,2246822507),e=Rn(e^e>>>13,3266489909),e=it(e^e>>>16)}function an(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var sn=Array.prototype.slice;t(n,e),t(r,e),t(i,e),e.isIterable=o,e.isKeyed=u,e.isIndexed=a,e.isAssociative=s,e.isOrdered=c,e.Keyed=n,e.Indexed=r,e.Set=i;var cn="@@__IMMUTABLE_ITERABLE__@@",fn="@@__IMMUTABLE_KEYED__@@",hn="@@__IMMUTABLE_INDEXED__@@",ln="@@__IMMUTABLE_ORDERED__@@",pn="delete",_n=5,dn=1<<_n,vn=dn-1,yn={},gn={value:!1},mn={value:!1},Sn=0,En=1,bn=2,In="function"==typeof Symbol&&Symbol.iterator,On="@@iterator",wn=In||On;E.prototype.toString=function(){return"[Iterator]"},E.KEYS=Sn,E.VALUES=En,E.ENTRIES=bn,E.prototype.inspect=E.prototype.toSource=function(){return this.toString()},E.prototype[wn]=function(){return this},t(D,e),D.of=function(){return D(arguments)},D.prototype.toSeq=function(){return this},D.prototype.toString=function(){return this.__toString("Seq {","}")},D.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},D.prototype.__iterate=function(t,e){return F(this,t,e,!0)},D.prototype.__iterator=function(t,e){return G(this,t,e,!0)},t(z,D),z.prototype.toKeyedSeq=function(){return this},t(R,D),R.of=function(){return R(arguments)},R.prototype.toIndexedSeq=function(){return this},R.prototype.toString=function(){return this.__toString("Seq [","]")},R.prototype.__iterate=function(t,e){return F(this,t,e,!1)},R.prototype.__iterator=function(t,e){return G(this,t,e,!1)},t(M,D),M.of=function(){return M(arguments)},M.prototype.toSetSeq=function(){return this},D.isSeq=P,D.Keyed=z,D.Set=M,D.Indexed=R;var Tn="@@__IMMUTABLE_SEQ__@@";D.prototype[Tn]=!0,t(L,R),L.prototype.get=function(t,e){return this.has(t)?this._array[d(this,t)]:e},L.prototype.__iterate=function(t,e){for(var n=this,r=this._array,i=r.length-1,o=0;o<=i;o++)if(t(r[e?i-o:o],o,n)===!1)return o+1;return o},L.prototype.__iterator=function(t,e){var n=this._array,r=n.length-1,i=0;return new E(function(){return i>r?I():b(t,i,n[e?r-i++:i++])})},t(j,z),j.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},j.prototype.has=function(t){return this._object.hasOwnProperty(t)},j.prototype.__iterate=function(t,e){for(var n=this,r=this._object,i=this._keys,o=i.length-1,u=0;u<=o;u++){var a=i[e?o-u:u];if(t(r[a],a,n)===!1)return u+1}return u},j.prototype.__iterator=function(t,e){var n=this._object,r=this._keys,i=r.length-1,o=0;return new E(function(){var u=r[e?i-o:o];return o++>i?I():b(t,u,n[u])})},j.prototype[ln]=!0,t(k,R),k.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);var r=this._iterable,i=T(r),o=0;if(w(i))for(var u;!(u=i.next()).done&&t(u.value,o++,n)!==!1;);return o},k.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var n=this._iterable,r=T(n);if(!w(r))return new E(I);var i=0;return new E(function(){var e=r.next();return e.done?e:b(t,i++,e.value)})},t(N,R),N.prototype.__iterateUncached=function(t,e){var n=this;if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,i=this._iteratorCache,o=0;o=r.length){var e=n.next();if(e.done)return e;r[i]=e.value}return b(t,i,r[i++])})};var An;t(Q,R),Q.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Q.prototype.get=function(t,e){return this.has(t)?this._value:e},Q.prototype.includes=function(t){return W(this._value,t)},Q.prototype.slice=function(t,e){var n=this.size;return y(t,e,n)?this:new Q(this._value,m(e,n)-g(t,n))},Q.prototype.reverse=function(){return this},Q.prototype.indexOf=function(t){return W(this._value,t)?0:-1},Q.prototype.lastIndexOf=function(t){return W(this._value,t)?this.size:-1},Q.prototype.__iterate=function(t,e){for(var n=this,r=0;r=0&&e=0&&nn?I():b(t,o++,u)})},$.prototype.equals=function(t){return t instanceof $?this._start===t._start&&this._end===t._end&&this._step===t._step:X(this,t)};var Dn;t(tt,e),t(et,tt),t(nt,tt),t(rt,tt),tt.Keyed=et,tt.Indexed=nt,tt.Set=rt;var zn,Rn="function"==typeof Math.imul&&Math.imul(4294967295,2)===-2?Math.imul:function(t,e){t|=0,e|=0;var n=65535&t,r=65535&e;return n*r+((t>>>16)*r+n*(e>>>16)<<16>>>0)|0},Mn=Object.isExtensible,Ln=(function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}})(),jn="function"==typeof WeakMap;jn&&(zn=new WeakMap);var kn=0,Nn="__immutablehash__";"function"==typeof Symbol&&(Nn=Symbol(Nn));var Pn=16,Un=255,Hn=0,xn={};t(ht,et),ht.of=function(){var t=sn.call(arguments,0);return bt().withMutations((function(e){for(var n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},ht.prototype.toString=function(){return this.__toString("Map {","}")},ht.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},ht.prototype.set=function(t,e){return It(this,t,e)},ht.prototype.setIn=function(t,e){return this.updateIn(t,yn,(function(){return e}))},ht.prototype.remove=function(t){return It(this,t,yn)},ht.prototype.deleteIn=function(t){return this.updateIn(t,(function(){return yn}))},ht.prototype.update=function(t,e,n){return 1===arguments.length?t(this):this.updateIn([t],e,n)},ht.prototype.updateIn=function(t,e,n){n||(n=e,e=void 0);var r=jt(this,Re(t),e,n);return r===yn?void 0:r},ht.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):bt()},ht.prototype.merge=function(){return zt(this,void 0,arguments)},ht.prototype.mergeWith=function(t){var e=sn.call(arguments,1);return zt(this,t,e)},ht.prototype.mergeIn=function(t){var e=sn.call(arguments,1);return this.updateIn(t,bt(),(function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]}))},ht.prototype.mergeDeep=function(){return zt(this,Rt,arguments)},ht.prototype.mergeDeepWith=function(t){var e=sn.call(arguments,1);return zt(this,Mt(t),e)},ht.prototype.mergeDeepIn=function(t){var e=sn.call(arguments,1);return this.updateIn(t,bt(),(function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]}))},ht.prototype.sort=function(t){return Zt(Se(this,t))},ht.prototype.sortBy=function(t,e){return Zt(Se(this,e,t))},ht.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},ht.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new l)},ht.prototype.asImmutable=function(){return this.__ensureOwner()},ht.prototype.wasAltered=function(){return this.__altered},ht.prototype.__iterator=function(t,e){return new gt(this,t,e)},ht.prototype.__iterate=function(t,e){var n=this,r=0;return this._root&&this._root.iterate((function(e){return r++,t(e[1],e[0],n)}),e),r},ht.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Et(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},ht.isMap=lt;var Vn="@@__IMMUTABLE_MAP__@@",qn=ht.prototype;qn[Vn]=!0,qn[pn]=qn.remove,qn.removeIn=qn.deleteIn,pt.prototype.get=function(t,e,n,r){for(var i=this.entries,o=0,u=i.length;o=Gn)return At(t,s,r,i);var _=t&&t===this.ownerID,d=_?s:p(s);return l?a?c===f-1?d.pop():d[c]=d.pop():d[c]=[r,i]:d.push([r,i]),_?(this.entries=d,this):new pt(t,d)}},_t.prototype.get=function(t,e,n,r){void 0===e&&(e=ot(n));var i=1<<((0===t?e:e>>>t)&vn),o=this.bitmap;return 0===(o&i)?r:this.nodes[kt(o&i-1)].get(t+_n,e,n,r)},_t.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=ot(r));var a=(0===e?n:n>>>e)&vn,s=1<=Kn)return Dt(t,l,c,a,_);if(f&&!_&&2===l.length&&wt(l[1^h]))return l[1^h];if(f&&_&&1===l.length&&wt(_))return _;var d=t&&t===this.ownerID,v=f?_?c:c^s:c|s,y=f?_?Nt(l,h,_,d):Ut(l,h,d):Pt(l,h,_,d);return d?(this.bitmap=v,this.nodes=y,this):new _t(t,v,y)},dt.prototype.get=function(t,e,n,r){void 0===e&&(e=ot(n));var i=(0===t?e:e>>>t)&vn,o=this.nodes[i];return o?o.get(t+_n,e,n,r):r},dt.prototype.update=function(t,e,n,r,i,o,u){void 0===n&&(n=ot(r));var a=(0===e?n:n>>>e)&vn,s=i===yn,c=this.nodes,f=c[a];if(s&&!f)return this;var h=Ot(f,t,e+_n,n,r,i,o,u);if(h===f)return this;var l=this.count;if(f){if(!h&&(l--,l=0&&t>>e&vn;if(r>=this.array.length)return new Vt([],t);var i,o=0===r;if(e>0){var u=this.array[r];if(i=u&&u.removeBefore(t,e-_n,n),i===u&&o)return this}if(o&&!i)return this;var a=Yt(this,t);if(!o)for(var s=0;s>>e&vn;if(r>=this.array.length)return this;var i;if(e>0){var o=this.array[r];if(i=o&&o.removeAfter(t,e-_n,n),i===o&&r===this.array.length-1)return this}var u=Yt(this,t);return u.array.splice(r+1),i&&(u.array[r]=i),u};var Wn,Xn={};t(Zt,ht),Zt.of=function(){return this(arguments)},Zt.prototype.toString=function(){return this.__toString("OrderedMap {","}")},Zt.prototype.get=function(t,e){var n=this._map.get(t);return void 0!==n?this._list.get(n)[1]:e},Zt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):ee()},Zt.prototype.set=function(t,e){return ne(this,t,e)},Zt.prototype.remove=function(t){return ne(this,t,yn)},Zt.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Zt.prototype.__iterate=function(t,e){var n=this;return this._list.__iterate((function(e){return e&&t(e[1],e[0],n)}),e)},Zt.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Zt.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),n=this._list.__ensureOwner(t);return t?te(e,n,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=n,this)},Zt.isOrderedMap=$t,Zt.prototype[ln]=!0,Zt.prototype[pn]=Zt.prototype.remove;var Qn;t(re,z),re.prototype.get=function(t,e){return this._iter.get(t,e)},re.prototype.has=function(t){return this._iter.has(t)},re.prototype.valueSeq=function(){return this._iter.valueSeq()},re.prototype.reverse=function(){var t=this,e=ce(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},re.prototype.map=function(t,e){var n=this,r=se(this,t,e);return this._useKeys||(r.valueSeq=function(){return n._iter.toSeq().map(t,e)}),r},re.prototype.__iterate=function(t,e){var n,r=this;return this._iter.__iterate(this._useKeys?function(e,n){return t(e,n,r)}:(n=e?Te(this):0,function(i){return t(i,e?--n:n++,r)}),e)},re.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var n=this._iter.__iterator(En,e),r=e?Te(this):0;return new E(function(){var i=n.next();return i.done?i:b(t,e?--r:r++,i.value,i)})},re.prototype[ln]=!0,t(ie,R),ie.prototype.includes=function(t){return this._iter.includes(t)},ie.prototype.__iterate=function(t,e){var n=this,r=0;return this._iter.__iterate((function(e){return t(e,r++,n)}),e)},ie.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e),r=0;return new E(function(){var e=n.next();return e.done?e:b(t,r++,e.value,e)})},t(oe,M),oe.prototype.has=function(t){return this._iter.includes(t)},oe.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){return t(e,e,n)}),e)},oe.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e);return new E(function(){var e=n.next();return e.done?e:b(t,e.value,e.value,e)})},t(ue,z),ue.prototype.entrySeq=function(){return this._iter.toSeq()},ue.prototype.__iterate=function(t,e){var n=this;return this._iter.__iterate((function(e){if(e){we(e);var r=o(e);return t(r?e.get(1):e[1],r?e.get(0):e[0],n)}}),e)},ue.prototype.__iterator=function(t,e){var n=this._iter.__iterator(En,e);return new E(function(){for(;;){var e=n.next();if(e.done)return e;var r=e.value;if(r){we(r);var i=o(r);return b(t,i?r.get(0):r[0],i?r.get(1):r[1],e)}}})},ie.prototype.cacheResult=re.prototype.cacheResult=oe.prototype.cacheResult=ue.prototype.cacheResult=De,t(Me,et),Me.prototype.toString=function(){return this.__toString(je(this)+" {","}")},Me.prototype.has=function(t){return this._defaultValues.hasOwnProperty(t)},Me.prototype.get=function(t,e){if(!this.has(t))return e;var n=this._defaultValues[t];return this._map?this._map.get(t,n):n},Me.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var t=this.constructor;return t._empty||(t._empty=Le(this,bt()))},Me.prototype.set=function(t,e){if(!this.has(t))throw new Error('Cannot set unknown key "'+t+'" on '+je(this));if(this._map&&!this._map.has(t)){var n=this._defaultValues[t];if(e===n)return this}var r=this._map&&this._map.set(t,e);return this.__ownerID||r===this._map?this:Le(this,r)},Me.prototype.remove=function(t){if(!this.has(t))return this;var e=this._map&&this._map.remove(t);return this.__ownerID||e===this._map?this:Le(this,e)},Me.prototype.wasAltered=function(){return this._map.wasAltered()},Me.prototype.__iterator=function(t,e){var r=this;return n(this._defaultValues).map((function(t,e){return r.get(e)})).__iterator(t,e)},Me.prototype.__iterate=function(t,e){var r=this;return n(this._defaultValues).map((function(t,e){return r.get(e)})).__iterate(t,e)},Me.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map&&this._map.__ensureOwner(t);return t?Le(this,e,t):(this.__ownerID=t,this._map=e,this)};var Zn=Me.prototype;Zn[pn]=Zn.remove,Zn.deleteIn=Zn.removeIn=qn.removeIn,Zn.merge=qn.merge,Zn.mergeWith=qn.mergeWith,Zn.mergeIn=qn.mergeIn,Zn.mergeDeep=qn.mergeDeep,Zn.mergeDeepWith=qn.mergeDeepWith,Zn.mergeDeepIn=qn.mergeDeepIn,Zn.setIn=qn.setIn,Zn.update=qn.update,Zn.updateIn=qn.updateIn,Zn.withMutations=qn.withMutations,Zn.asMutable=qn.asMutable,Zn.asImmutable=qn.asImmutable,t(Pe,rt),Pe.of=function(){return this(arguments)},Pe.fromKeys=function(t){return this(n(t).keySeq())},Pe.prototype.toString=function(){return this.__toString("Set {","}")},Pe.prototype.has=function(t){return this._map.has(t)},Pe.prototype.add=function(t){return He(this,this._map.set(t,!0))},Pe.prototype.remove=function(t){return He(this,this._map.remove(t)); +},Pe.prototype.clear=function(){return He(this,this._map.clear())},Pe.prototype.union=function(){var t=sn.call(arguments,0);return t=t.filter((function(t){return 0!==t.size})),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations((function(e){for(var n=0;n=0;r--)n={value:t[r],next:n};return this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):Je(e,n)},Be.prototype.pushAll=function(t){if(t=r(t),0===t.size)return this;ft(t.size);var e=this.size,n=this._head;return t.reverse().forEach((function(t){e++,n={value:t,next:n}})),this.__ownerID?(this.size=e,this._head=n,this.__hash=void 0,this.__altered=!0,this):Je(e,n)},Be.prototype.pop=function(){return this.slice(1)},Be.prototype.unshift=function(){return this.push.apply(this,arguments)},Be.prototype.unshiftAll=function(t){return this.pushAll(t)},Be.prototype.shift=function(){return this.pop.apply(this,arguments)},Be.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):We()},Be.prototype.slice=function(t,e){if(y(t,e,this.size))return this;var n=g(t,this.size),r=m(e,this.size);if(r!==this.size)return nt.prototype.slice.call(this,t,e);for(var i=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):Je(i,o)},Be.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Je(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Be.prototype.__iterate=function(t,e){var n=this;if(e)return this.reverse().__iterate(t);for(var r=0,i=this._head;i&&t(i.value,r++,n)!==!1;)i=i.next;return r},Be.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var n=0,r=this._head;return new E(function(){if(r){var e=r.value;return r=r.next,b(t,n++,e)}return I()})},Be.isStack=Ye;var ir="@@__IMMUTABLE_STACK__@@",or=Be.prototype;or[ir]=!0,or.withMutations=qn.withMutations,or.asMutable=qn.asMutable,or.asImmutable=qn.asImmutable,or.wasAltered=qn.wasAltered;var ur;e.Iterator=E,Xe(e,{toArray:function(){ft(this.size);var t=new Array(this.size||0);return this.valueSeq().__iterate((function(e,n){t[n]=e})),t},toIndexedSeq:function(){return new ie(this)},toJS:function(){return this.toSeq().map((function(t){return t&&"function"==typeof t.toJS?t.toJS():t})).__toJS()},toJSON:function(){return this.toSeq().map((function(t){return t&&"function"==typeof t.toJSON?t.toJSON():t})).__toJS()},toKeyedSeq:function(){return new re(this,!0)},toMap:function(){return ht(this.toKeyedSeq())},toObject:function(){ft(this.size);var t={};return this.__iterate((function(e,n){t[n]=e})),t},toOrderedMap:function(){return Zt(this.toKeyedSeq())},toOrderedSet:function(){return qe(u(this)?this.valueSeq():this)},toSet:function(){return Pe(u(this)?this.valueSeq():this)},toSetSeq:function(){return new oe(this)},toSeq:function(){return a(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Be(u(this)?this.valueSeq():this)},toList:function(){return Ht(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(t,e){return 0===this.size?t+e:t+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+e},concat:function(){var t=sn.call(arguments,0);return Oe(this,ve(this,t))},includes:function(t){return this.some((function(e){return W(e,t)}))},entries:function(){return this.__iterator(bn)},every:function(t,e){ft(this.size);var n=!0;return this.__iterate((function(r,i,o){if(!t.call(e,r,i,o))return n=!1,!1})),n},filter:function(t,e){return Oe(this,fe(this,t,e,!0))},find:function(t,e,n){var r=this.findEntry(t,e);return r?r[1]:n},forEach:function(t,e){return ft(this.size),this.__iterate(e?t.bind(e):t)},join:function(t){ft(this.size),t=void 0!==t?""+t:",";var e="",n=!0;return this.__iterate((function(r){n?n=!1:e+=t,e+=null!==r&&void 0!==r?r.toString():""})),e},keys:function(){return this.__iterator(Sn)},map:function(t,e){return Oe(this,se(this,t,e))},reduce:function(t,e,n){ft(this.size);var r,i;return arguments.length<2?i=!0:r=e,this.__iterate((function(e,o,u){i?(i=!1,r=e):r=t.call(n,r,e,o,u)})),r},reduceRight:function(t,e,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Oe(this,ce(this,!0))},slice:function(t,e){return Oe(this,pe(this,t,e,!0))},some:function(t,e){return!this.every($e(t),e)},sort:function(t){return Oe(this,Se(this,t))},values:function(){return this.__iterator(En)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(t,e){return _(t?this.toSeq().filter(t,e):this)},countBy:function(t,e){return he(this,t,e)},equals:function(t){return X(this,t)},entrySeq:function(){var t=this;if(t._cache)return new L(t._cache);var e=t.toSeq().map(Ze).toIndexedSeq();return e.fromEntrySeq=function(){return t.toSeq()},e},filterNot:function(t,e){return this.filter($e(t),e)},findEntry:function(t,e,n){var r=n;return this.__iterate((function(n,i,o){if(t.call(e,n,i,o))return r=[i,n],!1})),r},findKey:function(t,e){var n=this.findEntry(t,e);return n&&n[0]},findLast:function(t,e,n){return this.toKeyedSeq().reverse().find(t,e,n)},findLastEntry:function(t,e,n){return this.toKeyedSeq().reverse().findEntry(t,e,n)},findLastKey:function(t,e){return this.toKeyedSeq().reverse().findKey(t,e)},first:function(){return this.find(v)},flatMap:function(t,e){return Oe(this,ge(this,t,e))},flatten:function(t){return Oe(this,ye(this,t,!0))},fromEntrySeq:function(){return new ue(this)},get:function(t,e){return this.find((function(e,n){return W(n,t)}),void 0,e)},getIn:function(t,e){for(var n,r=this,i=Re(t);!(n=i.next()).done;){var o=n.value;if(r=r&&r.get?r.get(o,yn):yn,r===yn)return e}return r},groupBy:function(t,e){return le(this,t,e)},has:function(t){return this.get(t,yn)!==yn},hasIn:function(t){return this.getIn(t,yn)!==yn},isSubset:function(t){return t="function"==typeof t.includes?t:e(t),this.every((function(e){return t.includes(e)}))},isSuperset:function(t){return t="function"==typeof t.isSubset?t:e(t),t.isSubset(this)},keyOf:function(t){return this.findKey((function(e){return W(e,t)}))},keySeq:function(){return this.toSeq().map(Qe).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(t){return this.toKeyedSeq().reverse().keyOf(t)},max:function(t){return Ee(this,t)},maxBy:function(t,e){return Ee(this,e,t)},min:function(t){return Ee(this,t?tn(t):rn)},minBy:function(t,e){return Ee(this,e?tn(e):rn,t)},rest:function(){return this.slice(1)},skip:function(t){return this.slice(Math.max(0,t))},skipLast:function(t){return Oe(this,this.toSeq().reverse().skip(t).reverse())},skipWhile:function(t,e){return Oe(this,de(this,t,e,!0))},skipUntil:function(t,e){return this.skipWhile($e(t),e)},sortBy:function(t,e){return Oe(this,Se(this,e,t))},take:function(t){return this.slice(0,Math.max(0,t))},takeLast:function(t){return Oe(this,this.toSeq().reverse().take(t).reverse())},takeWhile:function(t,e){return Oe(this,_e(this,t,e))},takeUntil:function(t,e){return this.takeWhile($e(t),e)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=on(this))}});var ar=e.prototype;ar[cn]=!0,ar[wn]=ar.values,ar.__toJS=ar.toArray,ar.__toStringMapper=en,ar.inspect=ar.toSource=function(){return this.toString()},ar.chain=ar.flatMap,ar.contains=ar.includes,Xe(n,{flip:function(){return Oe(this,ae(this))},mapEntries:function(t,e){var n=this,r=0;return Oe(this,this.toSeq().map((function(i,o){return t.call(e,[o,i],r++,n)})).fromEntrySeq())},mapKeys:function(t,e){var n=this;return Oe(this,this.toSeq().flip().map((function(r,i){return t.call(e,r,i,n)})).flip())}});var sr=n.prototype;sr[fn]=!0,sr[wn]=ar.entries,sr.__toJS=ar.toObject,sr.__toStringMapper=function(t,e){return JSON.stringify(e)+": "+en(t)},Xe(r,{toKeyedSeq:function(){return new re(this,!1)},filter:function(t,e){return Oe(this,fe(this,t,e,!1))},findIndex:function(t,e){var n=this.findEntry(t,e);return n?n[0]:-1},indexOf:function(t){var e=this.keyOf(t);return void 0===e?-1:e},lastIndexOf:function(t){var e=this.lastKeyOf(t);return void 0===e?-1:e},reverse:function(){return Oe(this,ce(this,!1))},slice:function(t,e){return Oe(this,pe(this,t,e,!1))},splice:function(t,e){var n=arguments.length;if(e=Math.max(0|e,0),0===n||2===n&&!e)return this;t=g(t,t<0?this.count():this.size);var r=this.slice(0,t);return Oe(this,1===n?r:r.concat(p(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){var n=this.findLastEntry(t,e);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(t){return Oe(this,ye(this,t,!1))},get:function(t,e){return t=d(this,t),t<0||this.size===1/0||void 0!==this.size&&t>this.size?e:this.find((function(e,n){return n===t}),void 0,e)},has:function(t){return t=d(this,t),t>=0&&(void 0!==this.size?this.size===1/0||t-1&&t%1===0&&t<=Number.MAX_VALUE}var i=Function.prototype.bind;e.isString=function(t){return"string"==typeof t||"[object String]"===n(t)},e.isArray=Array.isArray||function(t){return"[object Array]"===n(t)},"function"!=typeof/./&&"object"!=typeof Int8Array?e.isFunction=function(t){return"function"==typeof t||!1}:e.isFunction=function(t){return"[object Function]"===toString.call(t)},e.isObject=function(t){var e=typeof t;return"function"===e||"object"===e&&!!t},e.extend=function(t){var e=arguments,n=arguments.length;if(!t||n<2)return t||{};for(var r=1;r0)){var e=this.reactorState.get("dirtyStores");if(0!==e.size){var n=c.default.Set().withMutations((function(n){n.union(t.observerState.get("any")),e.forEach((function(e){var r=t.observerState.getIn(["stores",e]);r&&n.union(r)}))}));n.forEach((function(e){var n=t.observerState.getIn(["observersMap",e]);if(n){var r=n.get("getter"),i=n.get("handler"),o=p.evaluate(t.prevReactorState,r),u=p.evaluate(t.reactorState,r);t.prevReactorState=o.reactorState,t.reactorState=u.reactorState;var a=o.result,s=u.result;c.default.is(a,s)||i.call(null,s)}}));var r=p.resetDirtyStores(this.reactorState);this.prevReactorState=r,this.reactorState=r}}}},{key:"batchStart",value:function(){this.__batchDepth++}},{key:"batchEnd",value:function(){if(this.__batchDepth--,this.__batchDepth<=0){this.__isDispatching=!0;try{this.__notify()}catch(t){throw this.__isDispatching=!1,t}this.__isDispatching=!1}}}]),t})();e.default=(0,m.toFactory)(E),t.exports=e.default}),(function(t,e,n){function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n={};return(0,o.each)(e,(function(e,r){n[r]=t.evaluate(e)})),n}Object.defineProperty(e,"__esModule",{value:!0});var o=n(4);e.default=function(t){return{getInitialState:function(){return i(t,this.getDataBindings())},componentDidMount:function(){var e=this;this.__unwatchFns=[],(0,o.each)(this.getDataBindings(),(function(n,i){var o=t.observe(n,(function(t){e.setState(r({},i,t))}));e.__unwatchFns.push(o)}))},componentWillUnmount:function(){for(var t=this;this.__unwatchFns.length;)t.__unwatchFns.shift()()}}},t.exports=e.default}),(function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t,e){return new C({result:t,reactorState:e})}function o(t,e){return t.withMutations((function(t){(0,A.each)(e,(function(e,n){t.getIn(["stores",n])&&console.warn("Store already defined for id = "+n);var r=e.getInitialState();if(void 0===r&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store getInitialState() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,O.isImmutableValue)(r))throw new Error("Store getInitialState() must return an immutable value, did you forget to call toImmutable");t.update("stores",(function(t){return t.set(n,e)})).update("state",(function(t){return t.set(n,r)})).update("dirtyStores",(function(t){return t.add(n)})).update("storeStates",(function(t){return S(t,[n])}))})),m(t)}))}function u(t,e){return t.withMutations((function(t){(0,A.each)(e,(function(e,n){t.update("stores",(function(t){return t.set(n,e)}))}))}))}function a(t,e,n){var r=t.get("logger");if(void 0===e&&f(t,"throwOnUndefinedActionType"))throw new Error("`dispatch` cannot be called with an `undefined` action type.");var i=t.get("state"),o=t.get("dirtyStores"),u=i.withMutations((function(u){r.dispatchStart(t,e,n),t.get("stores").forEach((function(i,a){var s=u.get(a),c=void 0;try{c=i.handle(s,e,n)}catch(e){throw r.dispatchError(t,e.message),e}if(void 0===c&&f(t,"throwOnUndefinedStoreReturnValue")){var h="Store handler must return a value, did you forget a return statement";throw r.dispatchError(t,h),new Error(h)}u.set(a,c),s!==c&&(o=o.add(a))})),r.dispatchEnd(t,u,o,i)})),a=t.set("state",u).set("dirtyStores",o).update("storeStates",(function(t){return S(t,o)}));return m(a)}function s(t,e){var n=[],r=(0,O.toImmutable)({}).withMutations((function(r){(0,A.each)(e,(function(e,i){var o=t.getIn(["stores",i]);if(o){var u=o.deserialize(e);void 0!==u&&(r.set(i,u),n.push(i))}}))})),i=b.default.Set(n);return t.update("state",(function(t){return t.merge(r)})).update("dirtyStores",(function(t){return t.union(i)})).update("storeStates",(function(t){return S(t,n)}))}function c(t,e,n){var r=e;(0,T.isKeyPath)(e)&&(e=(0,w.fromKeyPath)(e));var i=t.get("nextId"),o=(0,w.getStoreDeps)(e),u=b.default.Map({id:i,storeDeps:o,getterKey:r,getter:e,handler:n}),a=void 0;return a=0===o.size?t.update("any",(function(t){return t.add(i)})):t.withMutations((function(t){o.forEach((function(e){var n=["stores",e];t.hasIn(n)||t.setIn(n,b.default.Set()),t.updateIn(["stores",e],(function(t){return t.add(i)}))}))})),a=a.set("nextId",i+1).setIn(["observersMap",i],u),{observerState:a,entry:u}}function f(t,e){var n=t.getIn(["options",e]);if(void 0===n)throw new Error("Invalid option: "+e);return n}function h(t,e,n){var r=t.get("observersMap").filter((function(t){var r=t.get("getterKey"),i=!n||t.get("handler")===n;return!!i&&((0,T.isKeyPath)(e)&&(0,T.isKeyPath)(r)?(0,T.isEqual)(e,r):e===r)}));return t.withMutations((function(t){r.forEach((function(e){return l(t,e)}))}))}function l(t,e){return t.withMutations((function(t){var n=e.get("id"),r=e.get("storeDeps");0===r.size?t.update("any",(function(t){return t.remove(n)})):r.forEach((function(e){t.updateIn(["stores",e],(function(t){return t?t.remove(n):t}))})),t.removeIn(["observersMap",n])}))}function p(t){var e=t.get("state");return t.withMutations((function(t){var n=t.get("stores"),r=n.keySeq().toJS();n.forEach((function(n,r){var i=e.get(r),o=n.handleReset(i);if(void 0===o&&f(t,"throwOnUndefinedStoreReturnValue"))throw new Error("Store handleReset() must return a value, did you forget a return statement");if(f(t,"throwOnNonImmutableStore")&&!(0,O.isImmutableValue)(o))throw new Error("Store reset state must be an immutable value, did you forget to call toImmutable");t.setIn(["state",r],o)})),t.update("storeStates",(function(t){return S(t,r)})),v(t)}))}function _(t,e){var n=t.get("state");if((0,T.isKeyPath)(e))return i(n.getIn(e),t);if(!(0,w.isGetter)(e))throw new Error("evaluate must be passed a keyPath or Getter");var r=t.get("cache"),o=r.lookup(e),u=!o||y(t,o);return u&&(o=g(t,e)),i(o.get("value"),t.update("cache",(function(t){return u?t.miss(e,o):t.hit(e)})))}function d(t){var e={};return t.get("stores").forEach((function(n,r){var i=t.getIn(["state",r]),o=n.serialize(i);void 0!==o&&(e[r]=o)})),e}function v(t){return t.set("dirtyStores",b.default.Set())}function y(t,e){var n=e.get("storeStates");return!n.size||n.some((function(e,n){return t.getIn(["storeStates",n])!==e}))}function g(t,e){var n=(0,w.getDeps)(e).map((function(e){return _(t,e).result})),r=(0,w.getComputeFn)(e).apply(null,n),i=(0,w.getStoreDeps)(e),o=(0,O.toImmutable)({}).withMutations((function(e){i.forEach((function(n){var r=t.getIn(["storeStates",n]);e.set(n,r)}))}));return(0,I.CacheEntry)({value:r,storeStates:o,dispatchId:t.get("dispatchId")})}function m(t){return t.update("dispatchId",(function(t){return t+1}))}function S(t,e){return t.withMutations((function(t){e.forEach((function(e){var n=t.has(e)?t.get(e)+1:1;t.set(e,n)}))}))}Object.defineProperty(e,"__esModule",{value:!0}),e.registerStores=o,e.replaceStores=u,e.dispatch=a,e.loadState=s,e.addObserver=c,e.getOption=f,e.removeObserver=h,e.removeObserverByEntry=l,e.reset=p,e.evaluate=_,e.serialize=d,e.resetDirtyStores=v;var E=n(3),b=r(E),I=n(9),O=n(5),w=n(10),T=n(11),A=n(4),C=b.default.Record({result:null,reactorState:null})}),(function(t,e,n){function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(){return new s}Object.defineProperty(e,"__esModule",{value:!0});var o=(function(){function t(t,e){for(var n=0;nn.dispatchId)throw new Error("Refusing to cache older value");return n})))}},{key:"evict",value:function(e){return new t(this.cache.remove(e))}}]),t})();e.BasicCache=s;var c=1e3,f=1,h=(function(){function t(){var e=arguments.length<=0||void 0===arguments[0]?c:arguments[0],n=arguments.length<=1||void 0===arguments[1]?f:arguments[1],i=arguments.length<=2||void 0===arguments[2]?new s:arguments[2],o=arguments.length<=3||void 0===arguments[3]?(0,u.OrderedSet)():arguments[3];r(this,t),console.log("using LRU"),this.limit=e,this.evictCount=n,this.cache=i,this.lru=o}return o(t,[{key:"lookup",value:function(t,e){return this.cache.lookup(t,e)}},{key:"has",value:function(t){return this.cache.has(t)}},{key:"asMap",value:function(){return this.cache.asMap()}},{key:"hit",value:function(e){return this.cache.has(e)?new t(this.limit,this.evictCount,this.cache,this.lru.remove(e).add(e)):this}},{key:"miss",value:function(e,n){var r;if(this.lru.size>=this.limit){if(this.has(e))return new t(this.limit,this.evictCount,this.cache.miss(e,n),this.lru.remove(e).add(e));var i=this.lru.take(this.evictCount).reduce((function(t,e){return t.evict(e)}),this.cache).miss(e,n);r=new t(this.limit,this.evictCount,i,this.lru.skip(this.evictCount).add(e))}else r=new t(this.limit,this.evictCount,this.cache.miss(e,n),this.lru.add(e));return r}},{key:"evict",value:function(e){return this.cache.has(e)?new t(this.limit,this.evictCount,this.cache.evict(e),this.lru.remove(e)):this}}]),t})();e.LRUCache=h}),(function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,l.isArray)(t)&&(0,l.isFunction)(t[t.length-1])}function o(t){return t[t.length-1]}function u(t){return t.slice(0,t.length-1)}function a(t,e){e||(e=h.default.Set());var n=h.default.Set().withMutations((function(e){if(!i(t))throw new Error("getFlattenedDeps must be passed a Getter");u(t).forEach((function(t){if((0,p.isKeyPath)(t))e.add((0,f.List)(t));else{if(!i(t))throw new Error("Invalid getter, each dependency must be a KeyPath or Getter");e.union(a(t))}}))}));return e.union(n)}function s(t){if(!(0,p.isKeyPath)(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,_]}function c(t){if(t.hasOwnProperty("__storeDeps"))return t.__storeDeps;var e=a(t).map((function(t){return t.first()})).filter((function(t){return!!t}));return Object.defineProperty(t,"__storeDeps",{enumerable:!1,configurable:!1,writable:!1,value:e}),e}Object.defineProperty(e,"__esModule",{value:!0});var f=n(3),h=r(f),l=n(4),p=n(11),_=function(t){return t};e.default={isGetter:i,getComputeFn:o,getFlattenedDeps:a,getStoreDeps:c,getDeps:u,fromKeyPath:s},t.exports=e.default}),(function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return(0,s.isArray)(t)&&!(0,s.isFunction)(t[t.length-1])}function o(t,e){var n=a.default.List(t),r=a.default.List(e);return a.default.is(n,r)}Object.defineProperty(e,"__esModule",{value:!0}),e.isKeyPath=i,e.isEqual=o;var u=n(3),a=r(u),s=n(4)}),(function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(8),i={dispatchStart:function(t,e,n){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.groupCollapsed("Dispatch: %s",e),console.group("payload"),console.debug(n),console.groupEnd())},dispatchError:function(t,e){(0,r.getOption)(t,"logDispatches")&&console.group&&(console.debug("Dispatch error: "+e),console.groupEnd())},dispatchEnd:function(t,e,n,i){(0,r.getOption)(t,"logDispatches")&&console.group&&((0,r.getOption)(t,"logDirtyStores")&&console.log("Stores updated:",n.toList().toJS()),(0,r.getOption)(t,"logAppState")&&console.debug("Dispatch done, new state: ",e.toJS()),console.groupEnd())}};e.ConsoleGroupLogger=i;var o={dispatchStart:function(t,e,n){},dispatchError:function(t,e){},dispatchEnd:function(t,e,n){}};e.NoopLogger=o}),(function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=n(9),o=n(12),u=(0,r.Map)({logDispatches:!1,logAppState:!1,logDirtyStores:!1,throwOnUndefinedActionType:!1,throwOnUndefinedStoreReturnValue:!1,throwOnNonImmutableStore:!1,throwOnDispatchInDispatch:!1});e.PROD_OPTIONS=u;var a=(0,r.Map)({logDispatches:!0,logAppState:!0,logDirtyStores:!0,throwOnUndefinedActionType:!0,throwOnUndefinedStoreReturnValue:!0,throwOnNonImmutableStore:!0,throwOnDispatchInDispatch:!0});e.DEBUG_OPTIONS=a;var s=(0,r.Record)({dispatchId:0,state:(0,r.Map)(),stores:(0,r.Map)(),cache:(0,i.DefaultCache)(),logger:o.NoopLogger,storeStates:(0,r.Map)(),dirtyStores:(0,r.Set)(),debug:!1,options:u});e.ReactorState=s;var c=(0,r.Record)({any:(0,r.Set)(),stores:(0,r.Map)({}),observersMap:(0,r.Map)({}),nextId:1});e.ObserverState=c})])}))})),ke=t(je),Ne=function(t){var e,n={};if(!(t instanceof Object)||Array.isArray(t))throw new Error("keyMirror(...): Argument must be an object.");for(e in t)t.hasOwnProperty(e)&&(n[e]=e);return n},Pe=Ne,Ue=Pe({VALIDATING_AUTH_TOKEN:null,VALID_AUTH_TOKEN:null,INVALID_AUTH_TOKEN:null,LOG_OUT:null}),He=ke.Store,xe=ke.toImmutable,Ve=new He({getInitialState:function(){return xe({isValidating:!1,authToken:!1,host:null,isInvalid:!1,errorMessage:""})},initialize:function(){this.on(Ue.VALIDATING_AUTH_TOKEN,n),this.on(Ue.VALID_AUTH_TOKEN,r),this.on(Ue.INVALID_AUTH_TOKEN,i)}}),qe=ke.Store,Fe=ke.toImmutable,Ge=new qe({getInitialState:function(){return Fe({authToken:null,host:""})},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,o),this.on(Ue.LOG_OUT,u)}}),Ke=ke.Store,Be=new Ke({getInitialState:function(){return!0},initialize:function(){this.on(Ue.VALID_AUTH_TOKEN,a)}}),Ye=Pe({STREAM_START:null,STREAM_STOP:null,STREAM_ERROR:null}),Je=ke.Store,We=ke.toImmutable,Xe=new Je({getInitialState:function(){return We({isStreaming:!1,hasError:!1})},initialize:function(){this.on(Ye.STREAM_START,s),this.on(Ye.STREAM_ERROR,c),this.on(Ye.LOG_OUT,f)}}),Qe=1,Ze=2,$e=3,tn=function(t,e){this.url=t,this.options=e||{},this.commandId=1,this.commands={},this.connectionTries=0,this.eventListeners={},this.closeRequested=!1};tn.prototype.addEventListener=function(t,e){var n=this.eventListeners[t];n||(n=this.eventListeners[t]=[]),n.push(e)},tn.prototype.fireEvent=function(t){var e=this;(this.eventListeners[t]||[]).forEach((function(t){return t(e)}))},tn.prototype.connect=function(){var t=this;return new Promise(function(e,n){var r=t.commands;Object.keys(r).forEach((function(t){var e=r[t];e.reject&&e.reject(S($e,"Connection lost"))}));var i=!1;t.connectionTries+=1,t.socket=new WebSocket(t.url),t.socket.addEventListener("open",(function(){t.connectionTries=0})),t.socket.addEventListener("message",(function(o){var u=JSON.parse(o.data);switch(u.type){case"event":t.commands[u.id].eventCallback(u.event);break;case"result":u.success?t.commands[u.id].resolve(u):t.commands[u.id].reject(u.error),delete t.commands[u.id];break;case"pong":break; +case"auth_required":t.sendMessage(h(t.options.authToken));break;case"auth_invalid":n(Ze),i=!0;break;case"auth_ok":e(t),t.fireEvent("ready"),t.commandId=1,t.commands={},Object.keys(r).forEach((function(e){var n=r[e];n.eventType&&t.subscribeEvents(n.eventCallback,n.eventType).then((function(t){n.unsubscribe=t}))}))}})),t.socket.addEventListener("close",(function(){if(!i&&!t.closeRequested){0===t.connectionTries?t.fireEvent("disconnected"):n(Qe);var e=1e3*Math.min(t.connectionTries,5);setTimeout((function(){return t.connect()}),e)}}))})},tn.prototype.close=function(){this.closeRequested=!0,this.socket.close()},tn.prototype.getStates=function(){return this.sendMessagePromise(l()).then(E)},tn.prototype.getServices=function(){return this.sendMessagePromise(_()).then(E)},tn.prototype.getPanels=function(){return this.sendMessagePromise(d()).then(E)},tn.prototype.getConfig=function(){return this.sendMessagePromise(p()).then(E)},tn.prototype.callService=function(t,e,n){return this.sendMessagePromise(v(t,e,n))},tn.prototype.subscribeEvents=function(t,e){var n=this;return this.sendMessagePromise(y(e)).then((function(r){var i={eventCallback:t,eventType:e,unsubscribe:function(){return n.sendMessagePromise(g(r.id)).then((function(){delete n.commands[r.id]}))}};return n.commands[r.id]=i,function(){return i.unsubscribe()}}))},tn.prototype.ping=function(){return this.sendMessagePromise(m())},tn.prototype.sendMessage=function(t){this.socket.send(JSON.stringify(t))},tn.prototype.sendMessagePromise=function(t){var e=this;return new Promise(function(n,r){e.commandId+=1;var i=e.commandId;t.id=i,e.commands[i]={resolve:n,reject:r},e.sendMessage(t)})};var en=Pe({API_FETCH_ALL_START:null,API_FETCH_ALL_SUCCESS:null,API_FETCH_ALL_FAIL:null,SYNC_SCHEDULED:null,SYNC_SCHEDULE_CANCELLED:null}),nn=ke.Store,rn=new nn({getInitialState:function(){return!0},initialize:function(){this.on(en.API_FETCH_ALL_START,(function(){return!0})),this.on(en.API_FETCH_ALL_SUCCESS,(function(){return!1})),this.on(en.API_FETCH_ALL_FAIL,(function(){return!1})),this.on(en.LOG_OUT,(function(){return!1}))}}),on=I,un=Pe({API_FETCH_SUCCESS:null,API_FETCH_START:null,API_FETCH_FAIL:null,API_SAVE_SUCCESS:null,API_SAVE_START:null,API_SAVE_FAIL:null,API_DELETE_SUCCESS:null,API_DELETE_START:null,API_DELETE_FAIL:null,LOG_OUT:null}),an=ke.Store,sn=ke.toImmutable,cn=new an({getInitialState:function(){return sn({})},initialize:function(){var t=this;this.on(un.API_FETCH_SUCCESS,O),this.on(un.API_SAVE_SUCCESS,O),this.on(un.API_DELETE_SUCCESS,w),this.on(un.LOG_OUT,(function(){return t.getInitialState()}))}}),fn=Object.getOwnPropertySymbols,hn=Object.prototype.hasOwnProperty,ln=Object.prototype.propertyIsEnumerable,pn=A()?Object.assign:function(t,e){for(var n,r,i=arguments,o=T(t),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 literal 33554 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 diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 04ece1856c2..b0b33404525 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 +n&&e.updateNativeStyleProperties(document.documentElement,this.customStyle)}};return r}(),function(){"use strict";var e=Polymer.Base.serializeValueToAttribute,t=Polymer.StyleProperties,n=Polymer.StyleTransformer,r=Polymer.StyleDefaults,s=Polymer.Settings.useNativeShadow,i=Polymer.Settings.useNativeCSSProperties;Polymer.Base._addFeature({_prepStyleProperties:function(){i||(this._ownStylePropertyNames=this._styles&&this._styles.length?t.decorateStyles(this._styles,this):null)},customStyle:null,getComputedStyleValue:function(e){return i||this._styleProperties||this._computeStyleProperties(),!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 edf06e778e90a809b3c905facab70d1238c19d30..6c318038174a7c47cb63d232d556075fa9a132bd 100644 GIT binary patch delta 86150 zcmez0!SS+#gI&IxgQM(y*GBf(_4P^C2?aI>9vl(rdVkOMZF0cT=@ywUf@>zZM)}>? z`s&5StgSjv>{3F4#aySd&pndJGDq<5j5|~AUU9$n(Um8~r(B@#qrqa4e~e-*{dEnh z#!WfL-E_|SoaKFdO=5kesPa#jMUs|9s;BQYm%g#&(hyXf&BiEOoJo08Ie4 zXBJtnGa0_@)Y5#DBg%XH>g0$!;$eGVG^nik;@M*)CNI_AtobV_B#GnVsl)f&9P%!8 znlo);*jlwXbB0iFg1qf|7s~?sLt^ITIUkEB)qGiD`6o1QwzJu$7hetvsLhM0)jx1Q zr*!Xw2OMTfI`wRE$4QG7yrL!+kd?IhO@<$kJb<7TtBjU zh17z!hP=mepI*x(OU6IBwjuY+y+c1UzaKhwVA8V2nR|kROAWmw>RB(hTeuz6+SAk+ zy*jMU&+>LZf7P5hnHo1FW(3So*I-y?d3p40$_m!6By5b`YfkDqJzdlkeh?0DP{% z-mCV+>j^8gZZawQbgZFK_M+RFu+9D}7tTB6s&ws)jpMxcJh3))K0Y5FN$$*e!Wx)# zq4?wMrv{o%=N}s~-zb^(+}o+q7&LjG zK+u(t?ncgEK>@O1cPDJiVR2JpvA=A&;OV>^-d7KIZ`ElQbT(h;^TapJa+a%*QDtL zbTJBA%-8FZw4T|eYQAal7pVqo<)&njiyiD1>ZXzbazL+*jMwX!_Z$p^(T4GiBE4?C|9 zema-?#@zb3B1M6pZzpaM@2e5v+okaJtIhq-(OZ>gTd}5J`6%nV()IXJ*3?6M+O7tb zhav`4iTsS zZM4t`x#YaDz39f{)k+()>yNQs`L#4Z(p`Pas$))b_>Ou7)bFSmS zr-XaMnROhDQbT8nzS#I{{^}h9rIYnF1uJe;F`0fmb!T10UaiZK@@D0Wcs*4=t(t%7 zvr-vv?=RQJ1s#pD{69O}T3xT6-tooBP3nW#g#TkK&yp798(ms2DGH)thIF zS;D;Ly=&~w@IN}Yq}X%Uc0PkD<68kG^&$)(4DKOb>tQdiCq>eR5*? ztD6@vMSOU)%>U0uxr<$XLMcZYRaX1duUR#Rd3W{6xMGvgE!_#FYK-}BgGDEs2C_9Z z*WIg+3!HZ8)xrybTdrLAw9%=_;%UcwQ~yb+_WB>{mdV@8@O{0xVtU(EKmFpa@1|n1 zdAhbqOA{Z+Zx?^2&@f?Q%GAX-geq3m)huA%uyoD6zta}ES8aaxl&^lrg8x^$7L8&iRkCkmGNoDX@hpx;(GOP961v~{NFDM{Ni zI%2_zX%9;bva?j{75zn1bT=(}6TvfMPDbm2rA4cxZ2L?WU(aea44>-iIeU6-ScA%1 zD+WKl!~LgDtTael)^KP~!(Pwbm8^&63N97BX%fHZSb}(xow&D_UfqP-md9ot%J``- z)#R=i?XPWdCct+W=MCEnd&E|J<+=V?W!)nqFQ%zZ${NZ&RjZ#}l&p7}{%HR`U%iiS zEpQE+%@%}z2KZbYk2fZLITa?yL0~83+UZ+Uy#6Ra6W&5 z!;65{S+e(cr`J8!jR}~qe|*j?=K84%d&*hPueo=t#(bli_t|d$4YOp;7W8IiFTdvW zInK0$Q}$Bzce|%A_ZX+_TBH{jaii}v5mGO7|+V3x0#-SCv^TyY|3U{wa{jCr8 ztS`Lm;Qjmmku(3(L_|Bz{_)+W@aVdq#D2xEzxRpwUummdk-K#xRIn z+|=Z?d}@8es+>Q*+w)YGJ-%1JKv!)F+cTGrEiKnOjvk!WbTR7q3I8<=22xf9G&$1XIwQ$ z#bzBl$F9jUcFNW37B?ildzqT>B5K>>qqYkx16RMee=o{_m&GH1fAO8$-0vlB^Sv&A ze~+(~ZOc;C`G+rNZ_d2CQvdU#S<*jum@BltG5RjI{6W@p$(^pB&WCns?8>`Ubw+`s z+U)yXwSvsO88+eJaSAu_oioR_Kf9hMbAX+D!gCyq)vakboWa(ugrO8)OI>spL9z+ zt`wj2m-VmEeUAfD{b4sk&jyPt7Q8!~DI4viuy60($0_Y!&u-V8ub#xmtB}pm`Nz#j zo$HOu^OQst&SyzitI`^mD#_kEP=Alfc-3puZtnyAUeCfly*hL&-rGsgwbOOSnIATJ z)<*8jqccuybGEyYd)Oyi+V{H3j=O>?K2Zw2B~LD%W!V4PWATxyRZmL;KF_~*uTH63 zHerLBq}$}K^aBcUFETwXKOYjD^3>`k=ZAmQ+JQ{Z47-Eu&n%j`W{Os@k^sY}^9`Ey z8D&+xIVHPd?o^~imfYvQ+*=~MU}wk#^Iij{!lO;=cTHG$Ry}kIv$9y+#&bgTmwc~$ zKI~HZXCeDuzWo(-22~CBxPKg8*sZjmZI0%==fyL0Z|gcm-2B|~-d`nWCik}Z7pcNe z7A%le^TtV8lQ;uJKogF`aRj0hu#)~f+PSyAF@u}Zyz5ZIQ)??9{#qLvz|4i7< z5b55p@%$rmSbD{pO>*`h4qJXYXSG7JBf52ofM(G?;hGDLMmZ1LUo!OflrUaW_^;}{ z!oTXskym?WEk1bJb(YA62a?{0A0C+g{N+An6BeI~>8wV)>pxqD>OOty>^9x+$cpms zXN(;7f74oaufAQ`w(FZhd=2A0$9rqE#EiP@H_WM6_-ey_9a+nYnFsA_SW8!4yd>y& zOt6+$=S-!Xn#w~}$4{JhwH7{TImofsmt%sic;?lk&v$G+t(dE(6{yEj!Xhaq?=1Qu z-9%J)l2Xxq2EFxCB{xHuYHtgwkXEGEs@-oJqVijeUs!3{@_PvCIrukZWE zHQ&Sa#`A*$c3&Uy$4?5q$>jIGa!XyxmhUsnO{`kfj2?OL$$h%U$8UJ!PHL0C5MTd| z{8?QOK1g^U<}Cf8pmJOBM}4V+wQUQV21i^0$K1f&7i~v(bN96|aGMr=X>b(nY5VZn z&Y@g*ZKlye)xL>w4IC0xGQ4{gj-}7#`ToIC^+dRwyG4zmjt z{C~LpUY$YrxBreQMfN{f>^V=otBm02OoA2{?(m~e)oDpF^g{17xKTh(?{loS4*mR%9YbUPHdo0Qr@>}!VGwvsC zC3jt&`%oq_tpx#`p=fZ3>H&c&sC<* z+SlgDGHce&oBEGG^sGB=_2OLL%S(^7cmG|H{5X3LgP)g6ZT1bPk5gR>w>ueG%PjX_ zBE2k5|7)YH)-K6cokE5D`^{xeA5S|l{~5OkpH$oOn+hjpO!J*GiDPR<%HlaTnyZu7 z^$Df^;izAm^RVD~RD`v3=Wg5buRH(0nDz13ie2y2b*8)_r$=eD(|ZR89N{X8}6U-H^OT~-I_DV2ei{E0P9IWj97l%~#IW~}itMIvqT zKMk3QMJE{U9kR9YpBo&-K2iSe>!ak9&J(&F$T|>DbfnfxOT72JB1pXro4vuQKAj^acbm={Kg*d}$(#)C8u8w*7L^57`2N_cu*CP-zD4H` z7k+d3aO3fs>ywpUWttsTobpDz;JC}i;0D*2qr7QT4<`Zf8%F z>E|eIVTFP%DW{}Nij_Uxdu>h~JLAoqHFf^viGoYZe?H^6=2OFUIic;@;V<=0tfoFZ zKPNa$e9d*}#3BAJsVm>#r8cYnwn&W3c)aE9wTG6~-XHnizVQkBpfrE&skG49pZ~4& z^j0hJxh;L!ByG{8fLW%db<^~py?^{Uyu#rV-;5018>gpROxmv()*9gy_ChuOMA)&i zg0jS`*LbdE<){Q3o&DvzkX@{nv?~00hY2iZahosmDTbMJk33cGdQd5p6ecm@0yJ2jpp15XFrL!1~3VC zTsh)=eSLt)>cU@36b+THi~i@z+Pv5NQE#`>0ftR1F$GuLX3p~2o9oE0y;y%Q%jNCY zKI}LraAr%4UG}2~vkE{fi});H^apzzH^ zujjER7H_t4@0|Rnc-!F>0d2EXFUa3Mq~HA~N@mU3t&DC5E~c|Ae|4uc+;PX5=SO#J zEihSdH&I8Wc$3>%_DeZG4tI8lubDPsQ`D*VTE|#^gfEG>BJAg&mUm%C#)77*UvUfv z1QeL&-w3}@*bp)K)J(Q|4G}4QnTX2CXXoh$6*(VFipbhIg`r{2i^G9OKM2m!`r;^k zUUKHS%Xam%TYmpvaQ?4@bV;s!;hWh7deYCD-J0Ex&nw$~P~ZDUD7)v}zMn<61NP(| zlD*g5)~(EeDsG44pU}n&Q`MzX4eHmJ2uHJ- zD1DGl-+k@+iL2tKj~tpVd|l=Fa#s}dFv;$7dKuD-UZ@^O~-zGks!&bytr9%40M zE;|vWw~amdGWYI7EG-!;bKE#3uJ8Rm|2eDF;>X2i3Li4drix^S@^5|fp>Ms-jbg6* z>ZgM0Hzkx@ss3(q?n9;JoXNrQRo}x8n!FT0bVruWd4lU}$@bziO=r&XpL${c<6-e1 z&WLx6{}TTPRZO&CElYgckakC8YeWBwVypG*48#uhN5wSB_v)>jUfOrjY4z-a9Ty!V zqAzbPWKQ$@YFKhWE%U%U&R?>7?XA0a-M-gycRBA&uFv%|>y)Mh$5u(?EBsxuX@&R9 zUBwYh^=t3W;`Ta{ThTIWzSCqLm=!|HDVYIrB{09!`mllZz~!7PVWs8;3dAGZ<&g!`fJf+K*{2QS=nvo}wmOMyOKCp*`|RBZOIKVA?2Y4PwtVjq`13&h3Jt}&A6 zYgysC;)_X;u296s>D#9yu3Pfrh3s>9&?fsRHulX~vZ@wX$^!um_%X#MQ zJ$w4h(d^@EJ{&FTX)aZfD0=Zi)&0ae$tY&VXI)8V6KcQbIZk`~@8G^$a_mVv_x)+w za=GE(>F%D>jqw>QJzVvc4Lk2oefm49vajV$^Vg!__fOrv6>SdIH{Uc}dhc}UwbQrM z=)TAhu6_Fad|d9#0NK|GtFjiYInt9MVKK`rsPeMX+wPC0$%xc%`0l%j^`d~7M^XH zee`1A4VSvzM<-gEPOvOiTzigp^{gt}t@W%|*gpKd@M48ievi-ApGz4Fc^aoq;dy&` zsq;G1`pw>-SDeh4pQE{|&@Sf1)y(C~Z~6USHd!pf%)Y;=_fzVX+}YiA z#&17-=P&5qniCXreA%;1#s2}XXL%jDd1%_JUAva-{HGS7D{DPV{@$Y^bekai{zWn}GWks6 z!moAybljbODJ*M?ZQ9S?n>h}%GGP`p6OcjO3M25 z{0UNT*`;noi5q9a`!hc zKYMx)?Ma%8s-zFf24l*>WSqR%h;d% zj`dh{Y>JKSFSg6#=YKxfmC=z=ay0+HimAZ$ZCb4N1zz5k<`t86G-+m}Y#K(8v%US?ko=yD`c| zb4%8}kJ2aIQv~^Y+*y?hwpXUL-kHr;@-$+?uC1ou-@RSxm)e`Cd%!&NlmXF`U zW__G_SRh>Eho6+y#2f!Mb*yjQFY-6xxs<^1eG9ZpUT;||FDaGyT1sVNI`^vsI!0$E zS9;fHyZyX+DezabTUze9_r5_}cnj*PRtBZLb)+ ziM{-G%xquI=d4F@&t>OKW3gWM%WSdD!&|lPn{5JP%+DyF*Ens*TBH!`^rSK`d9R7{ z_usW?n~yWu8$5NMRrr*U-lcy_;B%lOP=wh zzRfCa<^!oTUcS$TtM{iyN_ueJjx+s#rg7Vpf6v$t%-nigN%;6}S-t%wzvjN%CMhuE zx%0!hX{YbMm-%t#$>;bB=krc$NZ%Lr4{4nn#?-x8)!?pEcHfe}8#iRh+GLnLRc&@$ zam9A-a@UZM?v?i!wx(P=@PAe7;R#VK4$Es=E|;FJx5=5!YRA|mwB~j7T<6ABxp}qR zn!9Eim@njG>~*xTiKr7ayK1d_;o4C{-@1*J(;GJ@MxS*{$Uk)MMMgy|n^l2M<{#6k zSLSqn-g`L6;7g~;*S=OOg(;PPpPUwQalGcG?zsMq!*WBXBk2$3pZ<7vbxb**WxqXtvq(l)*V@&u{Ji_|&rdGhf3VAJnmnuW?my4;-^RYb^;9qR zs#@J%>B_vr7Th=gMQBU&3cgC2ck$?9_Dfg(ExhBTW59OXLy6O$^-#e--w+3Xg=6rH;)U?K}JEy*i%2^Y6SNzQii{>M5PIcboYsi+a z0f$?n_M*LQE6;mqyNayxI+{Tja3A1j=d?%(9;+y62#>hDBbVT_>+_y}YJYv_P+VvY73mEn+fDPCT>;78c*I{ezQbvzvHmjbC`o4+n?vKlfi2 z?#{*(;L2K0dg&x^VsRUdEFTc}weyUYTlX?8y|#HVe_wQVZ3(DLa*W z^5I>l*WD;$kp8Z;F!%L>Kqmi5^R6`C$ck3lemb+r=;NLM7Sns{T*~h$xcr&4aohCr z3$tcPCHY?8elufx#5(o{x7$AZZ@DIsceL%Qd^^$~Q*3}=ox7%KxTb0#a?d%_8c7A5@LaRStYptbbpIpTWrik;i zuQhM$JJmPa*|}G5MW4sUbzA25+|oO_&D!(^XG~m|&~FMZ$Ry7K+a zm+uUDX7a1W{@rkGE0@Qel`iQ8A2?i|T&=WvJzstQ@`4Y1eNBl576(@U-I&8+d8A`KwiSMg892Y^2OeY7aOb`&=`9o&1$ubr&w| zd9^$D#_{qOzXR94cVCphoAsLY+82WE?N$GeFZzG@?l1YPg}E)d{csgGAvQ#^e9% ztvHmOgrYjy`6NsfkCqy?FW3BVfSYAG!@NnSrdh{b?mSigZN8$2oYR8IpM@)x^IxrB z8t`sW_`b#09IMwVm#;niEsFon*1lWWwnf*i^46O!I`<^XbZ2+LI>qNx%A$?KDvo!D zPH;%?zG`!`Nw;R>(-#RxKZjmTkJ9lydg$`ZK$iOdkNeB_ngndGKEAk($i~4t)FU0cSo%=QN?Y_{+n1ID{?j;ZEQ~AXkvU%TT|5EjxE&6Z5kstEm z{r?2)5B)!K=JDBCpR-Qh-TlQptLoQ6yDt^F&y zw`|hezF{Wc?j>fAXUu-QBy4B>sT=!lW$#;;QPpyrUO4|AFhLrTsn*bMK0(9KSC6H1>G>uUEy!0dEhht>0S5+VY_M zP4&eizQ6T^(oJr09`!9xU*ON!YnY+4+hF}FUH*ncGZXI$FF2_8|Kir$mBOqn2dDjA zBA%sk(qa)GPtlz@mk)Uudk1f~P2Xv?-|NxOu%7yq=}&j()Fr>QxWx2vv9O5Ooz$n% z*~T#$g)Tq8=ly^9vA?c;=6xPHIqSVaR$hfiEN%W2E{~jeU~9~o{~IHJik;o`^3pdK z04uuUn;w|{fvFl zrg-bGVXFrI1V%qZ(6+YCd{gM{Y*qOG^@EkDpe#e`DkPwO_tS%gXY)GzlFOS-_^CU$6Ude|`DNy<7^Z z>2c>@_PJi!cV}1W>vd~VRQ2`Tzjt*`&Oh|(i){X{?-|S1ZC`Z6>uhLX*Yt`_*}j`v zOtbI3F*1sje*LjHaBF=HtN-^$pHH5>yZpRq?XA`oYBANT=Rd3ZI`wbC*?V=rroVSH zmQU8Q%|7cAxc zs(tQ>r2o{XZ=QKf%K5AL`_hGcFW)7Pnj>yMnKJFs^fiLH8~CQiSU=nARR74w<;=&0 z_5CNFY?R#mw#KI*^;BJy-qiz3nP%Qr-)vtw-9PD{&|!i2M+`?Nu0QzH*I^2;2=DsW z??25w()~&&`c2-(zbCr)Y`d&6{PfJyCL`W^w7WC&wco|!l2%G(ft>Z?0O}F8*Sd_1Z8>L zlXw$8ahX$1X-MeC6F%u)(M<0Po*Z4Z#l|9ckMZ@l2Ay2{me;O%^+3vC?cz>}exI%- z?-{)}6feGXUtG+0{<*~-Bfq1L zCQ%yO`9h-S9Mj*_vQDR!MMCOdX_=|py32uIb#|Y*E~0xcJLX4ps>GDG?{USq*st2J z`~0iBB{XqH-ogA8j_DKJbORqmxZT_Oyj00{>x|UnTgqnlohW;xk(FNW{AH%x{E}nq zJ}ckvG%i`*P+sF_aV9o>Now&!erDs(zaARNZI~fDJ3+_Uyz+3W%*2T2ntqEi^uM|% z_PuK|xuL7wuyI*^5k|1|2f6+ZAsHJ-})UY56|9f(o(u|ciPOTbemWDG4a7kXLm4im)4b9 zyBVIZ4!+46^ZC=aGA3)5`bCqr)CN}XS?bXJf@S8`sVnV2aplR0*3Ms6?ezWX`5CjS z?y_X@2ACf+NHa>QUfIF=uzR+h*|{roLe_UwH)$#MZpx@Khf)(|A*4J41CRl&1 zKOz&hD?E>BffmD|y;)3gwywsEXOz|FS+oR+*NLVdi+%VZXx7p-fh!w!d-f@o2XAhF z{3m6eyv&iMFIt}8{3>TuYwVcqIWc7q=k?4K+ZyF95gCtp3J!mDl;V|n7J7uyb>*sp zefjq*{%mSYV%T$Y;pMiJ*>0D%hyRdVl2rO>wpD%aEU{I(iQMdb0lyje8y3Kcm*K`Kz>PTBz1ScduPbdgMxkHyJGfxc?luMmEZP1&HCKEFu~>Oyy|&>zgkXNY{hX+&_Gt`>xvkG;3xf| z+c%swij{l&N4oy$$=WR5#4_>Fe&&Ze-vlH_J#DVC@AUm=^f%`D?4NzJoXV^CKFgK7 z;`{mF@h&;B^_Sk(Rq1ZuX?J7BL!J7kVeLCzc5L(NXAU|eGT(21&!$Z*uL?Id^Z740 zC)?M?fhukT1H2~#Vl^~mARk%sQ)2yf4{o=+-uco zH#Tx;=mfFGGVCh2>uf*uv&Y)Ke7b#Gn=Ph1`21%T(~HB~WqA!FSf=j1F=y4~_9><& zH~h-#`Aj~1bCS`u4y?O!Zq?K27mORyG_s`6Gvcr9(+c}w1uC;dA zJAHO~E}Pt`7BsKzmQL&J>K@&CeVKd7sg7wU5_#78w0ucv->PW8>h9Ae@u&MQnf@IkJ9=t-HEM@9pBWw#nPNB;yP>cGoZ3 zmG|%2w+C|J8mFetU)OnA;Y`i$9Tj4iJ6gjh)+}><(J-gylg8WoN8MX&dWzSCwsX97 z4YOm+v-^Uhsh6!=FRvlP_NIf6@T3_`cL1JFA-;rz0Rn%iJ7f9 z|M98X_UUOI>n8sUuP<(p7yr?DeWS{f=r3JTF<1Dcb1W5auM6Ir@b|B2NR@C!>*Yl| z?|r`%^NCZk>HgE_`gZXr`<}l#nl*Q#h5GV~i*i;l?CoJYyPQe=Y}@<-kw?-dq7&Z# zh@5xNdS1`JdMm$A7f;&m)R}w7s+GUOlqLPVvv8cPmd0Jt)t5f0ehJ*Z;?s=B50+Ur zdn7*Of4e18W$$U@!1YT7yEJFtF%a9JEySQir-1$GWki#H^U(R}>6H9od-HNd0)-&}vY^hiIJXY(+_dSi0 zvKBdT;^Q)5i%ILO8jTie7Tf$@RLRY}^tQIZnm@};&R*nuEIFZi`}X>+`ciD3cr3n+5G$W?^mxs9$q=qe6HWwGcW(P8z~&z zu+(@$e__5sasB>;RX%YZ>)h*~>aEyxvrI}*qEId8in9iLf?%BHbcg+p^%hTDqt_oj zpL6$+{?m;+lV7VHQZ$ag`ZzQ;)=s!s@>M3M`15CKhif|4bNE;ADD>MfS+0NnQ)_qJ zcIUTm>ZK=WUyuv%&|}}{StnxSEo@1stN5=Y7TNE%N-6 zZk`EgI*>BC=F5)eYZ)@%7QejXR3Y@^xaos~Pyf{|F`NI(`pmcy1!;t|C)0@7?bPyyCo9m*2>EV|I5DT z_T+j+>HHg+S+f@Iy?Tu)Df()H%AQ4^kDEM7yv|@(VZ8Uy*1margy;9mhM(EQ$(Fg- zIwwrrw((VS{ORM+dTb{rIdwTRz*XF z$pfy}@3QRKq>ShPQToE3Q2(W`B5Ovn`!tU;cemfa=5gU~+1`fsV%aa{Ek;|q>ra2Q zQi-(W*JrQMAFNH+N>!e!4cPrC>eJy>nd_P&Jq!)r-PoMR7|FJ$_x-13 zyVi+Uma|yinBn>_&N(Fg;Ihp<6L#pNPyEq(c5ixv^scw+%*9IzQ#PBsBy5>k?<91{ z(tpZ~T&9p3ZCo$aC>`Xd zCiq)5x{_=8;`GR+3Re%kKJ_}W-{bs)oVaJFm82(1Tz~dF_)>Pt+7dZi?G=j_esmAr zy`z81%F4dguS>W8Qr7;s=b!zhvsZlA-;&y@bJy$UuY&3SCzNWwi|sF&^z>kphL7@< z?_xPM#SiKa@7qzg+-Q&3Yr9`Rr~jW2Wjg)8gWcT47gPMDqCYE~8f%N0N7buoSR>SXh6cKv&bl@>Ph<}gS#T(({<%-G&E z=SV_ugUzaKN{2o^v&rB6;H!{;cVR|(t75D8&Yu3IlM+)IGpny>>{uticYSaE1JRy6 zEV8QaKDtzeJ3DH|e6^`*cx$E@a;%+w!?rUVcORVNNcmj(M}^hp!In1`oh{2PogT(@ zZLCw-A5_lXYp__g%u3O>e(RkJu7ZIpoDRgbx&=>#<0qn9?AC}%sC<6 zy`A@1-lo(07xH{_Q;bNgdv&3Ld!3i6*W0tYlNWzedB?R?wuxzdO@eK;l1(Ou*-ZXz z=3WaIt^8)JAd|G`5{tJ)c#BR$@-~kr>#SSvv1t7Hv104d+?66hA=9RuUbC=Xe~#PP zz-!Z!6J=*FXtTUGWmCiw_xrC4r-dh}9&lG|f47KrO{?q+iydNnANKoyK7T|na^I>4 zC;vUqGsE~Lqm`4qR~qrCblw^=rBNx8H5gva8%=tK8iRp*@g>bcfm z#yh#US-j=))f*c7p1eO6X`>`Pwl_YRq4haK5cf|wERoUoXaL=XSK_R?oT@I zy!x+&T)i*l_W;W_7&FJKVMA?oF>3VTCVdeOSpkw?3ulf3{9Vd5_caV^1nC zcFcC)6_)Q4aQHUwicjwY6U~>dcA2~+*=@Ft&xA(~()*?K`X%{#3az#uIQQG?PTi@9 zF2xtYn@xH(uO+{DIj1P+kk?$hFEKe!+2w`0+)VnX*jzRc`0l}-*Hiyg9FZaR@`y^fUom&Oe+_b_u;yW##(H!&4tK=M1;l3rXgNV->+kUpDB^()I|B zZ@xD7*rfBkp1R-X%=ZYK-;wg>o9*M?son}kOqV|Y`Jrc)k(IHkUNTJbm|^{C!K@OM z(+RFF5n{E%PN8CdwI5vj67^2pE9&W*+sdtL+2%Z~Z0&9Pl9{l^s>jwct;8UQUs+H5 ztkQyrAL(=MJa9W3vGz!&dPeG(dk1t5KS)%0xZm+=D_@6j`36*{OKjQmLoJ$`J&1u}I!PsjrdRS`Ok0Wi1&C}f1?3napnc?Xch8rRQ-ARE( zi|;M4+`A_2t=djQ@zhP7$Gy|u?@LWv&vjO-pnl)my?>z@u>{V?g-LfG zu{_}0#2JvV@bH@Y^B$Z0ZXe%p*v2Hu#Jw!B=cbZ?sPV+Tf5dj4>g0`%KKXjj|Crg< zbF}a6(XD&2qvwr>MqS;e$E{_XnhiDzwk?=DO;Gmj{2akcdz_zp-_3a$tR^0!Is0+u zhL*WSZBLow`&}#l+2o5=?mV+AcG3+wgN%DlC+m;+EpodOu~EVNQg8jY(vN-Kf!`fe zXQi6mUE_agYE#zFpx)hqOq$c)2S&&5h-Le5$-~ff;q5g>+s~^hHZPnlw03Vt;3AK6 zo{s{;y{_Jf`f~gB!U?Ru#JKJxUokbmxX98{>B{cP`F{7?4_+y}92VXfTd7%7am3iW z_tENOZ@y_hS^q3#&&An(5^Z-6JgD#ftU8SS!Z54KM~X{|J^Yo~PHt~_w5hnv?Ys_K>b-Av`GMVR&#pXq zUw`uHqABtgv(sw6%U8yAo;z%NH$(bpo{iXzvo$?d``iCKuP8E_y#92hqRYO&yEV4& zDcCcOh5K@AY{y%%*|Bf--Sjl%W7?^D@nfHF<143obw^W_#dqc9%w7KXZ@#16t7k^* zblzB&ibx|y5Ccm#xFps{qZPD%B7YhsvOpiBky6@y%ZuRW{v!)aL%0~|0C=k+Z zyYt#|cG4kn)z(&d`w9Ox-VJ$c!eD>+-PZlfmY$Wk?#mOhw(x#W@26{9ENASzERf69 znm+N|so%5AW*gitt7mv9G--}^zUA+Y4d8=%XdWq(Mkul zOuc$rdH4K#TfJBPbJ;0TThw!3-TYYNjgDMH?!vBMp28DP-+Alw&3iw^`IyGc84TJ& zb3gu1aa=b0xqPbem0Qg_%BN?VZP=L_wm3gE>`J)8uayz&S&xU!U)p!5KH2;2kscLw zX7gPKKP>B?v$*hjoBLi}c4_Y1^#zY@HlC~AVdeg68=J^)zm3mr=e_uEC!;9C`uF_l zJ+Wb`7DBy2?<|(hQ|uI+>CIe!?)<|UUwc(IX2m@H*}hhEjpzGo?V+jdeolO8cfO{I zXzgwM{=T(w_mp7i_Nz(Tgo~%{@66f#$K9kM#Y=*dH%c?! zYhr=f)Ew3CDGo<{x9?qY-`n+O*&}l)1GmX}4Y#tp`hV6dY*a|Px?5Q=d2+Gb0w3Yt z{Q4_x%K{nqvi)(;{udU?ZGU0Wo^Odd>2Sb`FH2#m$D1<@XP1}U zKYskv_M!$KZ}tL)qXmU)9rw1CaLrrCmi+Zpb*fJL^Q|-Ag`U4?Qs%kAQ~Fw3c;=Rf zaNl!p-4cs+-szZltvmMkjm)IHL*Liy7}THGzsc9X{_auJi9YgAX4O~MEUNf;bV0)! z3wQ1{=JQW&KV+|U)8P5>qbL8>X~(yC)!F}hze=P1qC%JX9l3oK&l%T;UAw)sd+y7R zs=2@Q6=UB7Pfy)(Q{|BL(e9^}|NaFRUVXbSQshpMN5c=ELR+wM8QO__@ zwMA(6)^DDnA7*JEy|Dea((PS)m=51543=3Ud1r=v{f|zwV-C7^-3qv*4hLVVIu=`X zibu_TTa){ZuhZ-0T{qcD+I{``^`CcH&$OfJZ(i&>d#*!tikote^qd9ttKaXd`DWx2 zX(bf7V@21xUCxm&Q){+g%8}k3eKRREeR`#ZziNrkw}=OK#7{Rp(b9}M*LM0JYiml{ zho5ha_;$p4v=z2+eG4f6*l7PTe9DH6k2U{IteSMvG;GJdp!>TH?}<5eO6jTV%P8A< zQWnm)q9aRMn!`PJ&so^r;I;AmmS6QhJ*PY8*cv^lm&`F+t@>0j>*dZSaW={PkMlS< z@}+kDpUTD0t?-~tQ+55AtoTxusV7}MGb_(ku3W?GiOGMH zXCm(X-hrIb(W3Vx9_~-@sqwz0T(nR_;Z3Q?7LT7FJqqTBA2ti+dBJh=3rB9ru}f#` zt+YJfy?1X3`YUrVul&Z+V_T>E%H{pP;OH!??$ov3O5gpnW@h<@MjvQ8)%;s^@XeW=bxVKtbEEdCDpfHoLj|a>6Klya6_EQ6~T>c7ahCPChy{0 zwCNs0T+7qntvmP*F1xU8{Z#csA={?^TF0}FLug|`;56^_i7!4l|I%!}Z~AVcOK&&- zdqdWzx#ztXeXH0f56(IWX>R?D2W`OfU| zjOCjBB>a-xyT6OpU3>l9HTULklO0R^Zx%}^JbA(W#&KKR?Y=WEA9772K63O;l6K^m zQWl)F@xWyZ1IzM%Rq}RS4Z@AL^;2fPHaVPp-ITTN*X19dAlkd}& zp1LZZFFMK{Iuoty;4i$ZOitZQ{4zyx(fA=F2PJr=j|z;N_W4-q6L{dsgTMWqnd4;y?O!L zF&%-lmv+V;8r(%kw7hkS9 zR6F7H)2$cVKmAE z^+w)mNfu2#)};M1B$)B(2R`>3D!Y~!>cp#jlYDpS?q@Xv^N-VZ3O0PwJ65opuW^^n zmBZF2(kpEqpINlzOwE$b={uvo?Pst3cha(&VauO2AvUVUg4%)pHzS+PzS~xx-8`ec z;Mg<08T!kr!{T;ZpIKm#{vpMPpP}C=`dqPQ{nhVl+b;_JPjEO?SeRj`l|HfVwTt>A z%a+!jCo@AIr)a33m}2Jh*JG{o_IasZSEiL5TyE*oK2dR|V%Nu4#XPaAuCRO0oWrB` zpN=Q!)n&XWnech0SIT(C;NS$xr~wTW^ku4x!rPT;uk zCpK#LJC8Z_$zll)Wvn9X1h};WwD;|unzQtR@V%(m$EnIDKg&A9Q@6HP-7sD#EgrD_ z=0dZd260x6eKI_vbx~_mUd%tVS0GSRZRO=e#@$m&*4EFN(st0dmrvC#m}iR-Plmke zDSIK=X^RAVA6iD0*(p9@TAXx^?ZhoV`nPM__4J$X=e0DZwQUI<~2v3I8PHz8w1e zC*T)Pw(%E+?oj1VBD1y&RyFZPPW#LC(>Y7x?^RD`}ovMG`I=wem z`*;QSl((~3ADGzGfBf|F{rWz?`r2>Lwtv^}JCd;Y$eNd~Y1%<0Y_hI~nF8CL71*X+ zPg&8n$Hw7E_|>VG`bE4Go_VagDcSf|ltta|yr=0|hc%YVb?#`KU4JM3`fb_v;_B1q zB{HIROGM20wtrRa-3JDCQ*LdY{%dY!Wqsa^`pKPBPa896uH`%PbLBay`L#d4|2chO^}C!~3uBvluZMi^ zv@Q9k5>Z-uDze-BqPvikG-IukVn8o%#}f5}yRQeQ1zPLHF8bzJxw8HK-HR%E=am=t zeP}seufL0JQ@He8j%-*I zps@9$@LrZ37iO)R7aO`n#s2^EjumbpbB{23y;Ui!x1Cdeput7zZ&+6D&8dAt>R%LI z>K?w4@Vv-zubS_Y%8%#1*d6it!@E!W6#qhwZ@W~^d}tQmJ?~CNcXX^)Rd4+S#YQu)}>!$k-K|$;ZwVsh0DWcocp$N4nrl&=9it*mh3$qctz;`TmGtz0!At? zPy27yQRcg;rj&f`_wS!^QQGdzvaBbsKgdg5bz_5({`%c_`8y80eUo#3#;p2@>tf8R zH{MxN(ulV)urvCExaSRKKtG>K`EuLRzZ&mxZ&g`swNsdX3 z&GmO?>>eJQq^{2EZA-E3VVSpZi9@jBRi~7GuM&#;TNV{s$Ah!V&J1$S-*Kfi}=~5eT$zhirC6-aBrpr$M?^2llwOr zf8x0NJYn5AMzg+*)C-Jd)KJGN=RkB6MVtVx=vPu_wD69 zS9T`Osh9t>=9bm+h$dqOv(2*V8@?{iToSvg?cl9#+}3Xn@lP*XtC48_Q!F|=T2{>X z(6T@77vntQubk=lu%J8fMP=PF#wCb}lcSIs!e+NeG6jGSsrZHb2@FL)d@~ocM)}Q`gJS1cC zKYRMp`=*i!9SpyM<0?e&owlAZPfJq$_MaOTb_vbqeRtftV~URVm?vG&t#>GQm=s*I z_MDNh&Z5fio((1&-*am2Ql7@hSv`e)rk~THh0eR4NIZY&cqH$g_rXUv!vV? z^Bn3@RDBb_Y2E^Xx#sJgQ*-2>lpM~BX8HSKWx(B)JNb^Xrxb8}bzWM-ah=VrC;s5+ z8c9c?30JOWUFb{Hd*U8wBhvLu)HA4l>&;4O?YGSgAI0J}+t1VC%Fn8kI5TBlG1tWv zEP<_Zj24jzsv2(@)@I)|UUFE8?*RMco{u8YH>{b1U&pTf#PRNm(&XQb6Edr=J>LAa z)HY-z%k+1Ri$8^hJB4y{nZH=|=vP+5RJILYCqB|{UuiC)#lEC5+U9@m(Y~@+Wu92~ z^7`4<^83zlz0`ZxyywT&9VLnX-Ofe${kr?x@#W?<+wNbzs~@TH%1%16U8L-v=${4c z3ol5uE-C-PefigeyDUps1t!S<)0psoS=-6eaToVkS1m35_o}RSPu;__OkOs^hEs2> z>WMwCyVOXH*H6dmflbiu_Io$y)LQdRSiR4C`L6Dp+v^v0=39M#!s#IK{Cx`()AREU zNzYv**_CcG{oY-{Fj=VQ+2ftMlXZ;U|7C2tVQ{&iy7OEX)3cQC@_9lHo369E*}m@l zTzdS49m~1>T%7)wRYQ_ByL;GtCJC?kY~^gIu=zxRxpc6L@OP`y!=CakE0(U4VD|d) zxL z!j)>yn^v5@DXVm~QOcz%?(VFFJuGRFDklx~41U>KF-^GT9X#jykE8Xg#n;FGDJ%JP zd-ijEji78hw>SEcX=%cmH@m(pK5*wi{X(+~GyCm0)D(6Z>clZWleGF&zNaFMYlDz% zY4v4q$Jy^%a$hRn{qTmnD_vy{NA!XB#&?fI3UD!>ad~qgws^{sc}$Ulvf`&FYsehZ zu+KX_VfHrZzJ%2dwmESR<)eS;)?Zv+-4^;LMK{SV3=15x+j|QJP3>mbN%+^dxLsS^{@C4~_rd)APKTRc`oETUv$xQ_|G4q~V$VCp z>h~0D+f{D_zN+u*{_3vTR;tzW_j_rx->V0)@1`E8xTHMg!ZBN=>WEptHMVZIZQGgj zZNsOT%12{ERZsQk>j!eoGTr&yn?uZnv#Ik?cm9-Z4I;6JueVRW@nr?mRyU{37r(3( z`DFij{$bBM&v&(bV&~h+M9d_Dci#Pw7&ni&+xc#Kc!sOR_tLpo__Jp>tPq+Wm zIx#qeTO@$BcYZ|o!YBXiziu(#rMLBeM)q6wgU6N?-#X^M=YqYTni89ZLi_4cxd8P8 zZVBgwe*RT;pWPeCRxJ`eJN?DZG;`Z*VUcIjh7)_$7H{~rTAF2NbV7goPH$GRr%E3f z>iW%Eu6=G?eCO6nm9_Qm*&<@~SCwPRtKB&)v>nR%4?ef(ySXV*Tqs&>&tEq6Z_KCW zZjG&;uW@a~61n|B%6*rZ?%1j2Z@D*Ti>_0)&JT7E-xVuW8n5(SH=OGE{o9x0Z`b}X z`u&}EeN^tNM(?bTe>)ykZH#()WcqSxfhZ}r$Rk18k}ji$BolN85YgWs?9yN=jyiU zh9+7PobuV`w{#aP%xKw?^!R1-U5?%(^Lct*{^)YbE;_VixoJt@j-P(6;y*08Ewe?} zCfHuDU-C)&W&N+J61{h6OMWpFtlBzlVfp0qyXV;m|KBUDW!q$Xe5K&w{)sngs?*w+ zUte(f+5^Q6%fxScTd75r9my^~%x8Jj>C(H~t)I__NABEXSQ{2|T}5O@@A7^h`%R0t zF;v*fpVZD4Tc$Jjn_$@MoQq{&K3slhJHh|-?<>uPelgn0tH1wHtM@3WbkH$T`^t2F z&GWbe`Le$?6S_L@*3C%@{PukU&zFb3Y!cS@bnZ>qZI$u+SJ$7+Tr=yU)&)mx?O4K3@EN%jPAFSEKjrmsIAr%z1oWSgFEsU)i@6T{h=Ge&Xk- z&sBS(r@Wu*sjuPZMN#_J+0OE8dT(UfE0uO}$8r|RB}i*K`P|nQnm-}X;1RdwGx7Jw zH-AsLzo2A=_0^u;U8`oaIo)qJoPH*>kuUUL@ytgjX5Y*dREdq>cw$0*@zLckE~3;jEy>vKyW{Y=2TXd+i}WXMxwJiR|GfpqYYcy1 zi=C+Go_!(X#Acf}Jm0fDJ-$fxKHn)cWlPl4wjE5fe3ROgw{5oQnjXE+fBVjqbDk+I zA@A0{HN3Wy@$lsO>E(MP!$e=q_P)Jbyj^g=*`mm53CVn(hWx{s)4Y8x%A~ZWWG}gB zQT&U+iZlz0O?!>dLjx`&@ZXU0wYu$U0ZxY|Q%NyIYtuYa@2uZCemq&#>3S zN^#D`*2S@j8%#Jk`f~mqzpJZr)}Hli_?%%C;b;@CN{xvqNpX-mT^h!8bD|vl&^JmfXJJO4acx=N}H{SgC#_P&y z^P|n2z%f3&#m>D&^K zdx~K8+~Za{9y70o{ms%9myt92DZl0Eq#tJ_7TT}*z@_y5fkN){Hid<|7esLSZeK3( zDXu<4Z|0J!S=t($`CBdtmE2#txljB3ibI!PJ(|2lr!B>`Ptu01t*)?t%awc~=k$W_ ziUA+3dm2yW%eMS2Gp|;UmR+8EakVwa!_1R&_S*Avx42(=8ebErTD^< z9j>pxT-;Ot=0knNmCsB5>0kQ#*maW+@74R3$Bysz_^$Wp$)gR~Vc+B)*6oxk- z5-WMN)2%V6e8+yxb=4C(muaonEPu{FWpYlb_RH?M=T~s`@A;*Be!9ayuNhfqCcl}+ z6|wcOtNE?A&)vr-A6_f&VJIzD(3`WjYx1L$F88YUzCG%5XysB)`-An%CZGKN)^?9? zk8eTdi!H6*HYYF3{Quy@l|SuR+No%t-#w{&w{R)2b3aSzSkW%a~^j z!3)H<1S*`*zMR2fFZ1l*%X2?PB#wVSr!L<6wj_U-YRJ5=7Z0D~V*2nwGWLn&E;hlu zL)%|+Ch+Q2onKw^e8WVqU2A2QR@D1FFHzqY=J!#uwlO4p<0|{9T~XqZ#z_^ffgy9} z2d9*JPq-3oWW(ftX6Mq0?_H#$HoQO2xM7pXz5ab?bY8Ca3imx9^@T~hI`-gP<{Rxc zD|hgE&)w0mD170S9cRSKBy#*`zy4Fa=VSDX+nLv06&O|YxA5gJ5x1AlQP7na?h z#`A}B!Eyc?(IWr7dVxDV=Ie(nzOC1*|4y#@)0sEDw^q?+pY=#)I=`P=`(`e= z?Mi}YPwZ_woV8E0UQuV!DYk3c;$grVEr^M5M_vUVAZP%%nL?>QdYZmC#va|YMTx(g#j9TNf zM|mc#D=c5bGI?UX=&Z-~?{Dx(COXdd<==Al^0Jy05yy+gxRlqwZsE0i|6t{dy&Kt1 zsh#~4ma=Y=>WbH0_f0q4kV|8iKHs=%;>s9rsmyuYC)*zF`#-7n%vAqJdp(7+j;m-~ z7n*!v_idT}O*hiwkIz}E5@EEdJ6fAh-8fu5GWm}bC+CyAj~6N=W*n$5<64>)=qwa} z_}ZS;7CBG!3+&}*$v?K2-L7uT|2Qh)<*&NFeav1FmPI-LrX7sFI;XvC@p-%HmgmK| ze@R~ITW%;^tJk$VZPG0MyoQX6w?DCX#Q#m>nxlJg9go8T_9a)H#MX)VrM-VBw)M&8 zx2HDy^XTRzWC!zdw;u}Py({zoe!Y3g$}@YolCLVC`qXP^u>38*?2-H1c^-)#-{L1# zt*Y|AC|Z2eu?z2wZt^-D=AOK5qp4cdOl$sCzJK2SxaIheqasAkxw`gy;oAMV5}x-j ztxcBRpgI3Ur@g?ou1!-4b^mUZs9|S6w3Fd-Wc`hlBB7&QJ(UwnPNh!p1{J9a0L{!7PqPudm9zRWE8{Kee<^|KU3SSFuyn<_qkci5d1 z95r)42fnG~Uz@*^r)Ed%Ju6u^o4vb|q%^+<<>lvZ@!aP%HC$Xe+2rLmCfT(eA17{m z?QQrnHX^W?N?va)okFaH|?Cm9MLk%dYhE;1&3Y9`L8Sb zS5Mxtcg1RdW|R23MzckSHJ0x=Snta2@b|yCKy-7C?=1`Fo04kKGD8uitXrVq879ShzjRGw;zE-hICoJ=?VE7Wa}Lt2U+xEm*(X zYFcaJw_m;C%XTyRPg;8Z;aX$8)&36e;%_#ada@>aoZK4!=DnZc9FI?`xo+VnwPJhL zO+NkQV9KODLDi-!jwDy)@-Y~k)tLRfU!TAIsDSS3^^-%3B-MX>)~f$GX@U5bf`p@5 zed$LF8sFqK>&@BU#Llhfb!2zy`uh(H-gfXd{MyEvGj~C!(oZYDU!Mc{G*Z4V`%wOt zJL=Y2UryDjoKcf6+gz>rz2QprXPMT$O)d8_I={L-F1}-tSK_L*Co=r#Pe1?d(lrY` z?MwLDwPfZ;XqPY*os^!EQ}$wR{i;8UcUjoGpLtZWz*%;-U;geNiPx-y&V|@09!)pe z5O$#2XI2c$rrs_01zbO~O}9JmUB9s4Q0o+re{&X34BgNpb949WPpKzA3Zx3v3)V6p zD65?6J?l`7&kiZkS!do|t;~7YvLGa;LG+VYOE|L^2glS?Z^b=M=W%;Kma3e-K0&#@ zan-}$F07}2#{LSZzB^}ewu#d>?av=i^zxlnIeyyV=aPtpZI9o-U`SdjuCKDft)5S| z*jI^b=H~tV_q?Md&5|xWJl3RUKRvYV@LVyc3Db%iWUkv;SnJ3vzOi8Ch1&YC>33JF z#9qo`uiYQtIqk*X)7NjUU;W(mZrw{IE8bs{t0d|d7n;p=$$liIcX{=iyie0#Yh`QJ z1%_=e2zgM}QoG^vry@_w;3-SiyETXfy}l5#BsOSL^Uab{ju)SA6>)4*vc2;9QbBau zfgc9)Q$N~9G=}aGR48;lZICT#z;`shw)sFzT4~eXfZgp=3wJuSweX#oYR~`GfAyQT zulY}BRWCo4QvW~d_WB>H&l3InQ$J{Ms?5FDC3uhV&$nfMyRY;$=+2ql!hh-hhK2Xs z8MAVB^Ehp5llwnO_8AlBiM4ya?KU@2I4jm<{P`GD`mE?1UpAF{xE}4`+PUScbZm~* z_oW;yZ`&tLUY@5WQoMR|`IEch&XV3ip$cO9UZ(fAE%#d`rrc}v%BAEZX2QMBL9C& z&5nz|eOgr@k!z-Y`+meZ;Z%v*rl)JeC61f#+$hlSq%0=IVLI1e!P2u@9~)~oTgklj z%lvx1bLH-*m-o%NG%ezLgn9j<<_TxEaLAs#@p$jDg}bJlN?rISXWH6Z6V^oDKD%k* z#TOT^re_9ayUY_%$~z;{)DtPfdbi6ar|F>ama7Iw`4_&Oy^w!XnSrFk=4SZ^`!6#8 zSZ-&3`ox0bNj>sOA$mM-jvidco`N~oWSb135^jopqW!rZ}Un^`srs8LKAwGP;skd6HX=Pt{ zf2leiouv28p@{vgaOa{~a<^}szZV_ze3AL2oV&5ncUa3GiN0A@w8*W`?fLwlXZLLu zVm-*Jg%clxtSBK{qfvNlOgANS62RG7wC6v+I_ zqWj*BBc6?g)^g7e75?4xV1n$=!oo!{Mavkc&+%z7*A#Pcb9`L?L-onhV-MJJZ*@pW z=^k6^IXhz4x9s1hjk`kX-fVB&#A`I~(1$5ogDYKs%kyoh5>^d+7}{QDpeiV3C6s>t ztWll$CrABlUdL6Z&0bzI%cc6oQv2yjrT-rqd6qEzJpJZ$_7neOW^0y|O>ck3;9fRQ z`SGlqZ++$*oZMh~W!q&L=gRtq$qVmXWS2`35vaN`^K94tXO2cmeGI_4ViL4+M2J zCx?2^s!($(+caNd=%_m*t zM8zWytEEQGJ$mSt*R2fGY3nk7ZQ$C_kadyys(UVv?EQPw{FhDsq1N24eZ?y0Qq{KO z^5xNv(&xEfPrv--(Y#rkOBZ%qP zay4SVLt^~{C6T8xdv;ajEX$v6&GRM9>FMDQcfYwjsx(i2qP$F9Q_IdG`(^i+sV|Q< ztKJDvUbuJVhs}HBj~sb(>D|o$_7gkGSY~NX(J$S3?c)h;@7nVdI1|&4mWQ2O^7ZA1 zCW8QFEfaC+xq->9GftMy{a>?Aj+asG?aXtb?Wa6m^YHQHbJwRDBu$Zjob0my;*@#E zEcab2%i-y^db?tKoXCqy`~qikUx>Y2x-QFwvWs>2Yms2Hoo%LWl>-v@R^Az_3Z?t+Nj0Gld zx8DDHW6guF#n%pX_BStnky!Qd!LHigMtSx6 zGOvs744PgupNNznD*K$Bcjn41u{%=(elthx75eZz{bAV8di5E>0!r349Q9YfU7Qvn z(9}HDT=T*EgZ_-)pKjT5>*xp5fEA`ecGmA2x86OM@~5}l?`49|^d);TKExGW`@a6e zN$(Zsnk9ZWH@yDZr#S0(_#u_eU)$#D#HAI?+v~j5^4T*#o#R(xg~SC-56+BCNsY4T zz0dvQvPN>8QA^F|G>;GUXX3I$--r~t%E~{E-;-0ZBJFE@mn!?mLrNzbqCDA5!t;FG zlNjG{7Tmgc?EZ#F7gpER_;{+X$Yh+l^v#L6YfU%VdM!G)^4!Ob9vg0dh*ElSKySKb zyhO&7x}>T59?NFEYbkC2lq>Ne`_vVybz$XtTW`(H?YS3NwKVnZs*D{AmKN2YVqUvz z1M8BOM^h&*vgCH1XJgZ|;ws|8<~8;wJAM$(&xzZ|hx~`%lJZX#8N$c=T@S zMrM)RHy_K_M%UJv?WzCoX!PS_d&&of$Cu|kynAo!smbj1wx{KDk3Bz;`bO{TpO;S< zr~kSXdg9CO{-M!Tr^Lw?`tU#&m`y$J^Z8@J`j0wmxx_IEG-m5+J-tnjZByT+7f3mdh9dEo-d#M6uYP_q`8}ofa99`N4vRG`G9IYYd~edrm4HO#2w7xJ)XQp;}z?^hSrBdO^@E^ZWJqi#IgF3 z^2Ba~|EJE1c0Vt3?L8H-PKx2ktkiS|p7~sPb1XY$-DJ*ZEKC2wq4jy0q;0{$C>C=c zA06=}uE%D6>eoB>;AQB>1Ah~?7q8-UYAJ74pD110FlG0uMM05U?Ko^)chpZbu=}so zab%00#h$%yn{2(_u~_)D`iK}_pMFm+qPsd)>}}>&tGzAZ;%oG{bdPYYRyE6;_#y5i zo9*-E{SMz^&n<0z$9Qkk*#hbG_6xPfQ7K<9E%Q=1xbWhW^^T0|8FE&hTmNZI>uk}# z-yhEPIGpu@iRW(O3#N`!599X)tz@yCQ$Imd>%H&#d9~H|-YS_s-+ozCOL+b@(={dc zUm2zrcdl4);+l1v)8wPfF83B_h`)ZF`fgj#tg{St%34<*C~OJ)S#k9ohQ$&OPxExAZgNqZy;0(~*7@n?`z{$78tv8FAhvMg?0FVT&)AzR zvF*D5tnJdIMLZY#Cp1j>9Go0grWd+xQ*7C+l@IeR)wizli%#g!Uv?g~925YXMbJ9Lk-slYA0C(BDhKM3Y;WLmPDtLOX431>9e zEz%-_4k~Wq{}yhLaVB`mpMT$eUS2G|eE+|)FOTltpSN$8@8@=F#jqbQs^)R@XFZEk z)IRcu?_);AtwhdKDU(YU)GIa?lod{P&sU!FRqz#u&hMtKZI{GiW=?HipPD`Cm@&t- zBb&0GKcBk!nylDv#`=pUGBv&{4^}Jx`?h>`TWHPRna#)BV-og>AA74Uw9#O^8>|P*+pL!ZXNere$g?lq1|HbKGkJy^FWenn(A?!DN-u>eck<^B*!bZyxoEV z?^R`vM@W_%o=%^6Rep+vOG>6my?m=&$L`4P1A?Utb~?9mdiOmNwR?ZC-bP$}rPuX& zR`v-#8`n*EB3S>fi{CbDlIbm(_nh&q4cqF2=IC7K<$O@B^kG)1kAcjBPyeoV9O+hx zRk@aN;^aD2rQcFdC*@AOTr0e0!aTQhov$0`B*#Ap;44|(srlRC@0EpD7d7?SznQvb zK})wI$PM z{#LLkQoXfz^0tJF+mlS#U&MP0i%6eN^nbtmv*{}1g5QOeQ|t9U2#h1U5l0A+tbgsW$2dP6t(~1sL!Lj_=1hnBKG^w z7gf4HJ{lao$*Ndnk>10-yXw!WIv!osaJM+kP3qoTjgxJAyY^?b@0clDwoJo*QT5^6 z_IV5&tE&0qE@><}u(92H;!8O(C&doEMLo?M&Tv=k z2v%^on|I*;vW|I5Njp3&Omb7+-}UbaJ6fN|wc7Nb$D3C^{FaHeDwQHD1#d13duCKC zRKu&Ud%c#a#w2UTO|RD@h^1Z#E374Djc&HJveaTM@LTTJyD1JC;Pqz_`bXqrE%fJoVT;r7N0tM z^0VbOt9nT-|D2pw=Z~p(H*78tdl7ge;Kj`)rH&39g_Av!qja|IY@TFQ+|KgsXH@I# zhWBFk#SZRb`cr*z$+K3rXN!;Jx82;Y+*LkvenfAZsgF71l~)-YXUi9GJWyRB@qzzG zp?c;tK_T52{2A(DJxQEWi~J+LW{a!~k}J&o_2qXs1B-aQ@&Wa>U3LtYq8+AaIKe(J9kKEh&kR;7S{j*p^I{5hqSj~G(hx;o`%O3j*Z zdGXhlh0_C{cK+LyxT${c3<+7sV~qdxK7Egp>sMXN%MlmzW_rZEE(zZh|D8tGB6r?> z{bhS*al{knFPR&rYhO>xUt(;tn&;D;2~ zFF(C%RrPIy(}lqq2bq0!uZafi`N+e)P>$PYtwa3BwHj@~>_x&;BVULsgz%JQmQKm| z=zq9)>LvN&-ojnxP9K7F(i0;usL5@w7q84bHSLh!q_wZ!eA)ckXXd4)zkf1oC(iEU zPAZA_rF1>i`Uh1R7n#pCK`p)ni zbeNU#@bBSvd%GX6yBVHG8qH7(@=Ljy*{VC&hnI29j*ZuT98*6gQ!KIi$48^=&4Tg= zuUz796t@9q2j5&c_3=dc!^`1%I&wjuS~MI^!KH9)4a#AT?^kI zVY%cmt8nGF7rQtXh;ymeDU|F!`=tHP!*^ZUWw$rRE%Mu~TCIEOPk`10h3nlu+nAcA zj$M27{!T@BbXk`?&!u8t!}G2;7A(HL{*puZJhL4w)|WasLl~?!ov~kZ|Ipg{?KOex zR;}Or#M$Z3=B%HoQj@I~3#_$QTDa-FjbXt1vs`}C*I7P^;aQVFQR;HZZHrHEC_K(kht)D*gws%I#qEDHttwUpZ z<+|n0t}ifVw4S-a@Yi)E#@8%SClqQ{Y*%9bD)n8=_Q03!MU^jH4+uDBm+x9BU2{&U@lNlkhS-A}Fh zC!XlzHj&ZMzx_J@`HAlJrKZ&jOr7NZtXQI*qi!hOEB|*!{J-qLX_lUsB^EU|39K*B z3=8S{{`614XQTF!`ZKTVH485| zGVTzsJ0U-D!Qc1)Gq`yEPmeiR`^ll`is`O>my`~!swtj2xgcY~9((CO_n+0P?$O$x zAUK`3#@$neYv!4_`-jcHRehgU!RNh&&-_+(TT!F#0-@{VGZ6 z-@=cMD@u3QK5fg8IC;cwj^7E+y!e@tr{hgtpO$4>|JIx*sa8Fw!$ z-|IZWckzhd(c^rdb;V9P^DncYX6^~bRV4FQ?|G2V*A{dUkZMgLO1BODeJwOmpQ@h{p&rM zG3OHtlw(r9Zhj>td28;~(_Pz9USV?t;==C^hsR3#oRbC)oRzxJWmyDf6Msk z-;r&a}mNamgMQN2^SCh$iN|AeqW!zE3+lVkkW zmG_=H7(H$GNxNrqF=ijwa_^iI@BVX&^^Lbi!~(t*#c6(lm9y^l&v|-!{{E7+OjDJ2 zM1L3AT(V{n&&D$zUG)={f;Ow2QC2o_+%5k}-(LL5Ce~~d@uRD3Gd6FQTq11hJ@sDl zr@*qdkMaxpR`ca$RV4?gFd4s;yT`Zg!*avaqMgA^{w@Iz52ab}xM4J}Rw2qr$E~B?+TG1G_Zq9$r^Y#bkhcecO|M%?wFHUIbQTemkrbf;O?0gi(6H>L}%jOpE*<+kK$jDw`;!4p>1N>*(a3-fPsIWx_x zKCt87gthW#f@K*PvmV|FlAg|PD3tAiP+N*r=4BuEgV|;qJMwcyy(xg!}=cYnwwc} zdx$Qvur<0@ESg)aJ?EqKjF};gF8psT6CXcWwsPHT*MCee?@Q|3;bptHSUjt!td?#0 zYQ`@bb*kNKVxBBH%G&v>S*I^%(izUqwGC5FmCQ}tsjnr&z4}b|9hPZAdI?wTVxL^i zYWT%#r0tzpW?6qnca_#Yx49}mokM1&8ySVG>)p($iZgs!C_ZnqQ-a-Gzx{uz{~hh` zm#hw|8&KbT;gnTW{97rM$$keerCo z!(2k!gx9`GH~c(9%OxEa+KTvELu>uQFW* zJ&|=2+$1vZu1k!c;l|rJ?afQ2PxoFGv{?N}W}KlA?lfCL&Ce%a&YDT7)I3;DUBaNp zJ@$ZII79!I%HXh_iMlNB80xP*ng5^p>_7jl*1K~4u}^cFwdS{8nEX@6Crd0!;W^6|opyOo}Nm>*hd|2g9!yIMz?##^uL3Nx#w=-I1$@DQt?(Ih)lfm`5Q(9SPMFDP5YI5p_)Gw?Rt zso`B4IL+nq9M%0Ux{+~Hrlj6z%&JojdGcA?$3aY7^#T8^J$90N7=FzbZ=4yOB>1Cw z&q8ru@6I31yR`mkZu$7IJxcB9<4fF2Cw++$sy-dz^{cE$Otg^4T4P@9p6*A^+uCvr zu8P!ss5j{lR7-Q*ztX1g&<>NoOH@VX7{s}M6tsJM_UkF@$F^L2YT`Fc%2Z1{WqaPU4Q z>suQw=GjF&HE-&?ep1GMX=`-ox;68Du4nz-E0F&;O!LIjN4!4S^OkAqe_S~8)T_@S zwP{P8PunlfkxslXd-J!-;vPixxA>pviwWe z-7D8m#dL0AdSi7d+Ey>FWsUN`txHYJcN#`+@H=UD&$d^$_rXq?lePz3>($oWI+?pW zV&~E?l861xrF)k&nw-?)%nMv}!}{3H)~~6aA-ie~zA#w7+4zK2Vv}y^V|I$@*w#-2ouB`X|%9H~!>#ssV}ifT=LjFRpY6wa`3W5MlsDpCcSnI4~z@uTAE#K&~dO0 zc$3>Z|IpE$EK}`Q+wEU&H1}yJr`!S=z0*uj?{55O)Gcs8DJbXAg@=M!J7#vi@p@#}Qp>b{@$MaTgw`4&rO`fX(doBd~MmS$Qx5(AG(@0-m zGL%tGMl5;eG#~YkZ{M<d8`n;*T^f};=gQn` zujannRXF*uw&=SLvEM~1)|xG;o3LF!cS#<{vL_n(`T1s_Lqquu*EgttS|GcdL+{-I z>*Gf~Urlcd*?#a)ws&~F{+n55TIpXJ&&*j@d1J9seqPD8u5&pIcJEx*P1tiL>{q(` z>L3pBJX2khSG=70GFC1Zn6F7YT~d6Oy6RX;tZODq|NRf#EPK)PZ zuerc~WToGczn8cc{cHH}w9%#S!v~{Z(^+O5kayf=`f0yX|-2i z?!o<@Yh&*{wb?dNCb?LFOYz(eLKE=>b6F) z>uG_!lh1anXK zGcJ?rr+6Nj?SFZCZhg|FitWqJrh3iz5yKN!L1#vZ(|uxUcFU{8h2xZ(*fTcK>JL18m|< zigMT6f3C4T1?ocA33BQ-nn_SVRxvh%OFoZs85 z{d~sqW|9Aur#7B1&UNzlh?FMs|FjE=7G<8AboGd*b?8hbiLPwFyw0L?Ua6Zmr$rpQ z-+hL8YFOf|l&GjFKKxBq`!-fy?U@?;ZkxmM9pN>9FV{_Ns^1wc%p_V}pZj4-@yVB^ z(v!bCzn^fV>-6J`_Iu8WtraXY$?X4@qL*2r>ei9}M0&ZWy33!95dtsPeBEPd@u1ax zw!l}3Qy&aV3h&H0^0lyR(j)yJ*83&mW6i}6Tisirr#ts;_hZe!77SWGJ+B}A*mtJd z!QL(H-eF<8gW|{SKj^PH88=P8Pkv?n8Tp^rJ<{^JrdXO61av#swL^z-eo_W<9M<@e)}H&O}FuH@}kY*7A-%X*~Feb|6oepjRg1b zm-jY&kaB1fRG8x+ePT-2)qgV&@7lZf(6RIR`QazC_FQ;z;J{P1NfPTGc!btS>jbYp z^x{tapF`)`P9L9qJ9>*i%)9sT3*7|HDES#2)assTV}5dz-TfRZ(>jlv3l^FFF>e(>n|s7y2UkJuwUNRA)NpFw`-TTTx(5!*m6&5({9zW6F>geW^Pt| zl6fg}ql9`i;|%S0$7YDlvDx^^`LI`VcSgFLuashQz~kz=v-gU>eOIrZpm=UUglMwQ zA6grB2gYc{SvhFlztpugz5ZAIRG0ZHR!%Wr zd->%y<%FGyZq_#wkFXx`j&ItjxyL#B`PmKe7n|~L>#yHzWPLt6%kt|nr2vn~a`QVf z87D@?#Kr$hx0>tYI_Zdh)0XFfr`P@CO{9%px);?*+mtxjuH|(gJ`8?d; zSGaMZTF&k@9C`V=S4_^Id~w)q-7_7b6~KAJED9Eo^PJC)Hl zyx;BQJ%jp5*Ear5Ib-+v8fUw}qHFbDe*3Hv4F#{wVcY$2zQLX2r_O)V6qH((H+xD$ zvi$YWH)gLe|JwZ0bJF62v#qy^=-v{xP|1H)UXt2T|LpB)M+KQ{s}CMZy0$Ywa>dzI zBD3AZZ|yQXwaYMl)gMo*&bP@IcD~$T^W=G%0`nRjyFWS&Q5&XMeY4zfS~)_ge0K%E zQU3G?j`>eM@7T`Z!N;IvxMpv^&t2IIZcN)S?yjYq_!_A5HDjrPR+m@7mFF1FiLilG`NqTMlW`&It4+Xz3 zEXaTU_F!Z8vs;rtswYS7a5`fCvFqqDUM2UP+$=5P`o|=M7q0oRN3CJe;b5j38Esh$ z=7_bCNAD{5D!;XvDD>o+%gk=;eo>e9om=N_-yVC#a*y&3jx`Ek> z&68m|%lg2fW6z~TEswHZQc3W-#a!RDOZC~$?4M%iCf15BW!QB*YJNkan)yK%^_~NT zRVSZJt2lpR>Jb~WcP1Ja)}?;oTV!-;dz;0Uju|t~Jh-SeKSX`*I*XFozI*Sjte*a* zR7xb}P3$b0Z#TpG8sqF%Z<=-T#r2H9Ipz@z+q6&3b`_DI`9XM)a;S7Nu{@VtF` zMyX4t(t$yVb-Ko_51jim^B{Jns7|5% zhtb!2QWm3se2UUc(+qBvU29!W>|3_#_Wb=0mQEZF^_v%lcPx)b4i;I=j+^ym#^qIe`ST~((-Sn<6^(il^RxT-6{L-5F zX<=#8@dXZ?F3S0*nWlxUb-U2c6+1gATgl8WW!nw+H6d%dzn%WKFQ`3tX`lJsQ+LBF zirSZP9FJOVY7wweild?b{p#w_H(C?-b?=Gbzg_)3dFxR=p@y56v=b&B2#(C`Efra{ zRUt&cys~!Q{_Wi9o{fk3Q!MKbXn)_vmSQ%2a;1}jTK#GDuddhjI6dUt@`decO2dX3 z2d*8m|Cp$|;;i9M3C3LoYC%O)7YQi7I;p>D+AsCz%4M6+G4z%mQD^y-TB&r>CA;I{ z?L%iK)Gl;bndTT=Hp6yh`q#-k^L{WMdtkP*sq$xO^~}jit$v1%o34h-d`_L~mZG#M zi|wrJ{RPSE^*oQN8FHqtHZ)eY&@*9oPB+kJ_PgYBJb(HbOGX8wccHKMGbf*lx|+{nQQY za(y!6k24HkBB#ITF1i|V*5&P{o(P#kj9nbJ)1Fx{-mOYquEb!&Ji&33w4a93!Hn&) z^JcONl?5x_&p&O%O6nJRHTwmlkOv-k8KTbL)QjX=+~8akbC0C+p2LvWt$tet5_0w8`g=71+MI z*ZQDwR=4SusWI&z7J400wEvku&4y84L3(q()XWcA@)kGQT%;~sFpOCHFEMI|p}P0# zbu7;oJ@T4vZ^Nj`)NDUJ%ZAae{=+SWCA`JAJq?aK*{gW^mwpv{mG*pQ=8pWcp7%1Y zeN&izc$Utd%HPXQ`9Ip{c}R8pVUeTX4Cj6m;@lIirh8o@TJT_cqn~C@mAa?JYbDN$ zI?p#~7hk&>7sROL@=W~JBNM|K-z}@6cfM7c8?LEoe*fOIM~iNUypSudRMr*)APD_d8?Hxp&t(OJd$${4z_&AYb0-#0!>&4f>r2 z+%(m;?BBg@!kxNb=XcACpZWK?D)Gtjok6Y(7GHM#9PO>9aY^8=S;_qwZLfH>SNwdS zFez)bq@@3@_ZxSu)%ARF;aST8)dl&Q+gtpCyl)70+mDP=BSLrKuCP zd~T|-^-}d!e~K@bT`?6&ICiwKdeXEtQyI1t&+OK{ zK6Q!SL*I)whnY^qZDds{t3Pa{`DjUs=)~pUZ0^Mrmf$kyllJDT}L|HU3D zjx~%Wzn1P=$A0Tn>Id=KdHjasIbt8 zd#SqrYpdT)>r4JvuX%JkI^*#UtIo+oTw!iTHIZpS- z+qds2bgAK4u{B9^srysIy18dw*B?%_6-!`VEOaucYUM)Sr)yVDE3(UR4xRg?VDbsI zPrP5XSvFUmiF-81u)RUzoVeG$DceGyn;iWzMQ2)n_X~m6HBnj_vph(ZKFNjszPqdmgWpLTG|7_BK>nbLCX*tRVjD_T4mJ!jl5%Se4La&}ILoAwRK?23)` z99fS3qC7GamR^nE?QhxnYQx);ho^PU7Co`Xr@YJ2Yl6AuvCAfB9^87Cz;J&`IP|L8nd-{XJ zuUri_^DUa0C+D&MAXo4HZ;NhBnQC19W@frtvO@g@rk^v`B!yc=l?vKFfB5Z|^9s9* z?$g9tVoouqIeX1p`?hDE_}^N;XRYtnHN5jWz@41!yQP)u>BnCg9`CZ8`vdpPPvWXf zx$~vU#WV5ZhJTh#j@)ikt;$C+)M%}sL+i+1sW7h7O4K2Kz zcCb5VeX{V`qGp|RbMoB_pT(kntk~hQ?~YzyLjJTv4Lpyn)E(^W6OLvS)`sc0rT?_= z-XrICjDPY$mVfs9=JPBooU*3t>#Q|L^lvqS`{ImG%zSX>+gyt%1vORiZQS_tjqUWNb`&U@2 z4qEr=t*)25*(vh!)GCcjjdAs^S!@gb2~90{__MmwXur!B?LX}dki;g@${XC56(4?bEcDlFbC{9}Z-RI5|wW&+^c6? zA@-3^PdCzQk4o*r=ONNsq6L=r=hrVt+;TMgOA6kYsQ`} zyB;qpO3Jt~=StfgVK<=&0A6Yy3Pu-x!&?$r<*-&)AlJeU7(@0`)2go z3wOG&a+yliXC6Nu#=Ye|zx(BvFaG&|_-Gwa?ytMbYuc6DYtuSkyV&NNeLQpHgw0A5 zi}v7PGsI=j^l6jSpVRhVqDcL$Ib7vSAA2N>$Nu| zbZv!=ZO)}cPKCn!hy(jKYc@LkuJGc!*Jk;2%ZL0qKK!%Zx10XC9m7Bv;gK=y-qE|5~OiUwvwr%LPq)ldl=F_Vb=Cb{9JSXx6ldtiL<#_V|TwcHMQ$ zqEdSA6K=OhiyEpoKaz13(-xTi^2`f2tL_+>g%pUhKK)dks7P{qnhPn@&Gc zwkdouscUliyQ!-M(nP+doI1T^GmGr;4gqy)y`FwtHMC?Zeft}Zr%VG{rWNKaY)% z%zx(lCb=>{O|&%DTf}|1e+ttoZ;w6Kqh8b>wOBgcO|);HULfP@15DF`zia>U3%_}P zw&kw9OKo=kxWdlFT;_A`+uj)thRas}XV`0UGCMx^bn??Ho+8YD3V-BGv-iB)!)$Wj zw#Ke{rS<+v8tnN;o&_t;d(E9vXFHp7Pe(UH{>#+n3oPdRvArLJ)7(V_r`B0cKAsd( ze5yE7zJ7sb#9q@?`=?*^O!wmda79KT{^r(|<#+!5zq7Zp{M&8#J+l)V2e*Bb^KFvM*OS6ZElMkZ}Lq_z(A9wS0_bzq}>MMSJ$GqG1%e||2 zuZCXTDznh=K=Pg)-yh2PJh=a9fvMr2oq3MC7M%*Z^25SLqF(-Kyu8}m>!-Se^WX7& z(D_q&Xtk!;R<-2#=nQ6`pKz@OpZxKQT~T5 zeLYvx6f>%tZimQ-u3E~^y=Klw&m%fnOXs(Ts^1L{-q!Um$lrOn1KVt+xTW$N%Afkm zPZGVS9phvchhK6 zuk&m*ec$u1xcbA!y(dHKi})EspXSL4_UvK)mma&MY2IY2@AI2+Y1s?S%YKEAy`9XKyQ9{JF|d>)EQ!;!ot{4<|*td&YcPsM63VRBO=pYh~wwb#^;q zOm#mcOA1Il-EbyQ`iZk$f|j-1fymPjtLG(LNLTwiSK02R&oR9P%b)VxnYm!Dc3ALR z^X|9xhc0jYT$3uDwd|UW;WS~BS3XyaHIJTnDXYT%JWpw&-ZF)YQ|p7Zw)vW`*4pv* z6wgLS&bu$eWbNt%l{!}+l9jHWuIaY7c&BXn{h(EwAI>^|Kc`px&+UWjq(8h_ysVtx z^tEYh#QaN#e*{|XlHbp??&zzEU0vlw zw)giKubbLbeb3wX|J_}V|8>R9^Ob!%4a_^@CxtvIt^4=ove6cn8Sn449{kr``{2mk z`ulskADpY2FPHHA?CyGj-3!HL=O_K~eZ24C#Cr#B7(9+G5!lc8!j>(-&hz~-`(@wN z-gd-s^lG!|t0V~A>)x`Zt2!d9XVtsCO9I+gzTfAme`Zg5(bV{>S1pgd_gjDFO}O|5 zyO(#~Jk#{vckY6-$+7DdujWiGwP5Zuem7lG>W#YXx%skl8Fw>@e?03uFQPulL3f3~ zuW2rKJeM(-HqYZc=`-Qw@*J-B7j+kgI&bXIoat<|F3nW^O>ofN7gw9)1Qx%ZDz@#} zw&aUmTz(&qTH1N1o|OC$|G_^>z~tH=pYUX6p2n@3^MgMwUY^IpUXyy8`{VZzMd_Et zR(vmH<(^GpKYmJ7=w!&-<)4J+pE^BdUVZ+Njz6uVE8jaI!HZgs8U{KFsOX@Lpy4bs&Y{IdS8iuKE$sax5xcgv;qdOeVVy-M{)aq zu1!mr31NG-DFlD2$Tf5>L7fw$vg6@K2+7fjo< z&Zsk2-8(t8Ik#rNBa_>kz=qwER0Jk?YdzO5+5F3~LzwWR5KWP{F7(5W` zeLYnw(~VJEXo16~s)~x!ZhoJ?CC_7CZhPX?&j$q}9ytxg7nVEtDW7=ux#7n= zOFP3>9$`NbQoc?oV!BF$)MTqA2J){;K3`@!wj`+C>z~N6#$7Ds4ytpO&#c$byLQ6* zeQ%?rTw6PP@{_vblc!$ejW{{kym|8WSM&Bru08X{adYKM>5?+Xf|!ud$@=Tqco~Ze zs#MF4W;D2c_RugtT@yCvWXY{0U#sMb)C@I^qpS@R8e$I?;1pSWa-EB3nW@(%?IIbS z-D=BkhFYFo*X`K#X4Z_P>xV_wsV+aMyYl2Osdw@XyoSGCvCn4L*!S{P=7};xSJTz$ z8!GFkl?kukT)8pp%*I4XKhxQ+t7eMzj6>m@BS7<7)meRPZwvw)I&p)cEt$ zaJP=wip)0oM?9DP4Qu}VH8&|(YTO-Pf8Nzc{K!0|jQTShd+xU%^m0+K)JSA;4UV@z zQDJP=Clw+twkII|&!pdeHK!hS_**_#y%V$|(1`bl{=4v+6MaXQzly9WzMk#y^`H4l z@%%#%7UfPqdGO%zetCEK_r0rr{dw>~zy9axgUh?W6*1~wTpViS9jFnYc2Kd+b=R51 z-LYy1Z3UGxzu9rtzY9Jx&G^8^cR^~KZ$x@cUG%-dYtnBPp(jXVCQ2s@~ zkEWZBg?2Ks-wShbPIAb3)~3q3aNRwV*B|CiTDj|3+nMqoSGaB@?%OQGdcphv^Etr) zo{F>fja&}qC*~(ET=FF4{vR2IGQmk{3jce)y=OcrnZ_?Q#Y*^LRdKR*<3C$_SqZNV z8f$Ly&q$Fvb5)@;N`;l*L{GYAP5rCy9xE8v3%yx!w2$NY58)hFhkG!Kas-QV!+92?wbk zuD>9j(qk|3J2$g?wd?8SqJ?RXZ5!Gj<>%+|eYCz@9~Q0jxZB8NeZt4C9K(wH3y<>7 zOS@ZpWBt~1dlYL{dM-4$G;1BJ!IL1LMYa#qZUslOiTiG=msqT@BGAFES^ud2k?s<) z_$BGWHw+|MzYE{ts9D@SCA4eaP2+^GDg5P%Uzy}}A0IZEJJIPRSJxCL=Q(RnG~D(R zGyGc@ttxK#Ho0DK<2@N~z9l<+Vl8hwc)b@|@1wsYZOM}~%}}M+GY*yLtY%%!y0BC= z-PZW>VXI}ow(L%NHM=+DTGvHw^RB`kPTu0r*}ZRk_1<%YtxWp3JX;`e={{@bT5INM z?uw3+uPv-&Fn!PNye+`xEL+@~3XZueRCgA$^KXinx|O|p#omcW!Nf>*N@%*+-T}asSBF6^_*8C>G=1nR0Pm2J6|a>D&+2p0u|5Wjp(5 zarm>_uBBOyts#mw5^alLJh1t;&blr7cPeLa#T-+K=0(aITfa$9;W1nCg-` zPX9J8Wz=L(S-DPJw!c|YrlkHr;vL53Q&S|wmN~f^T1v?E+}e}re8uffap~W>T~TpQ zRAZkO>9Tbk_qlp*cX?~qeI6q*i{R+a$x|;Z_{$MnBG&ORfiWvW;BTvx)T_yV*{{FM znk6e;ns9l+J`VNlm$!A+7FzrEPi|X!{V`|4%+2h^oHL9$B^+WeZ%%f2m2s$~exaSk zrg=e(v3GVZu8S`?>aAB@A*1(*TdVe@)velsEI6U8>Pyh zsyp1RvvqLVeYjw8myMuC6Yrl&k^OFHT46WzscBKkEIGx84`l#`%Kg&;> z8Nmna4wz`md2jG$`+hj4zhkSa)bfa!SLL^*f~RS@%}SpZGySA!cV))88(s+qTW2;I zas2%2S#Ngw)H|bh>wiuA*p|vFxFDkD6hq9xhaAgNKRs~xe);LjAl6QWS2NehG2eN# z&DF5^@wVVImYHI4Dp%ABCUzg0zAH$~{Kl!cwn<5xr`V=&x(OWd^Ki-v_?_b0#Q#R# zOhC}kRro;CS!?f8d^IkDE<$lq?K{usmM^Hf{o*?7sle3wvr7bh{Lk9zZ%diP^khoV z51F}JLTBq5fh)|$u0TNbl+9nPIFk5S{dQue(AAr{-# zt@kZaxfB#A*tmMZeX;r_VSY^0G@d!!nZNdN%@-!CV#lRRc$RfaZ~FJ*Mq|9@l_{U! zO}YBwMq%7LK90rHzWMrACfc`j)hY4FnqAoX>cfAnx&vE6rKR4QG+eUq^A?e{xUg*P zo`rr4XF@H?pXxs<-1vA#Y|%%*-)Gub8x!XQ^>kTkW*ZfgyguV8neyPmPJ*?GItQ=RATku!wWmn>H}y7FPHUd=9G8{t*_US53Wx4e}-HNA2c zt`jb}^;lnvnySrN%Tbc`dW+E{o~6fw7>*v2XfZq+Zs|HxZT%{?1-hk)-wIf!9B5x> zty=tu|5*9Eo~7X~i~ASHwsp1H@19aD-EVc^n%!Sl=b-wB*IV{}nw6y=l$YS1+qUNM z*9qZ@3O)DiuPJ-Xd7Y&H)**6snzXdfuT?2eyV~ZYz56QiHfK|NGGplLP2N|Uq_dZW zOz#!5p4GJ?vHbFOYZrr5^XXR&&xQs5Holvmymmt8!cU*tM03_|E-pUYWpYmO^1Sk^ z8-+hld7bpdJcobRg!)di+)sIOtKVm7M93dw6lafPym6Cd!?zjgN8J6nQW~B|RmZgL zTT&VjTpaG0YLHU8Q!gerU{?2y*7)-shkYBi-tO4mRB@V!IO&ArucZ>&BsFK*YqHd9xTT@y?`PW~2js5oGIQ?1?NAN-cN z1{E@C1!vP=Tm(7G2A`w%?#Ja3u@)U9DiC0D+ZdEE|d*mJ*5_M%%UjDwE6y~bxYPy>6jUH zr2WY|QCptC|80wn4mZ!=ShuV@q?mPSPDf1MhKxn^n->(WRyMYLEcmfUNAGn;R;`@+ z<;NLJPuR^u*G63qPk$I1Iic_p%VOEf);Tuc9) zB`=v?b%XJ;)C{w{>|BzetL#<^9AdJ!i{5zNmV4 z-1JAD{Q0F5igSMTWJdD&9*olqmR%Qc(m-viuhJ3mO(CtHPkfM{b&<=o{L1Zi@iRTb z9&?#lUwv9;$7L+(>nEdf>e}wf^>eJk_ZdBt<5TtuvS`>+*06A%2epC+td?0h35qh;Ex9r{}{luX~%hHJHJ&2H;h*&n1G zc(Jp$K(%kdQTOU-0?PA!{Uu}3VH9uxj%ZsAB=6zQbo4-waB5=osL-^-`m%C-S zI!#zrW?z^PF-t}8%o>4pMTy!2-8L8|FR^TYkCF z$vejP%Y#e8=cgvcs9P@1v@9=rJo$EBjlo`vcHU`=r=1UM{53Jxl~F75pyt6P6;TT& zFSwd{`P#w||E|94>wf0UdluFGD*pYfe7;8t0SC%|%?mpH!tO-y^BbGCr=ERarh6k^ ztyrwBIN`_^;rge6Y~e!N9o4K?J({JPR$0DSNTbUAkMDgphw4PN}lY@S&;Jx^nFHNLj%9Mw( zDnS>Max7d#&pbLOyFy4@L%r)n?AjfZF3jE#F=^w^D{52gvmY|+r_a1r_0das(^P9g zhLTzS8;*O1*y!|kt-oxfx!Z8S{PoM4`!)WP# zvw7yq&u#Ns&+nU-nmFx)sG8XbS1RVF6$+DPx z@XYzfk-5~b+Vz%)_;kNmMma`?>AA6t5{zf2x5YB5unNzKzqWq*`dG$X31(%nt#gIN z{hUlXi=>xaKkd)y#=LY|_x%#Kt2Vej=9glQ+rHI9w{+DW*YsChw!zb7;u+N5hTw_NRip<}a0e0pd+qf-6Hz#n=4&h8E@X?fQ(D}SHRlzi=4 z2L6`E-Da;Q%ulMnB+;k4D#+vVN8g~Ix~x&+_dnl=?44h>WSPyZX(p1FTKP|Z5SZN= z7*zG<$KS6nrYnWXn?799NB4=g=F0r(;a6$1!ZK3)FokEqyY{?5o_o?CAU( zGg98{T^ZN>_uA=hp4b{zgDIX(JMd)Q)IXWr>3+Bm1L{}xNEu>8~dsPfqw=I_^3q%?>x zYYh8!@2Sn=cI6e-l1hqQc1$PrY{An6{k-h;R0qww)^(80F$*+z$Y57zunzy0_8rcch-7RbL4oLjde z^v8qcLH}YNzp`29wa4JlgLgHTb%T#B+duW;nWCMBDa>N#qPf0zOg2p~5WbzYa#DTT zwqVw&e%o)^%nyB3I_L7sEwiVTze>;O;ocgsha=cM^QBxJmrCM;lwWPKg|p^$O-|+B z`zV1&;(^GCXHSsd#*Kcf_ zl%05Q_lE~E$=V+$9+)5|yxHg3?oDYTa(yMM>*_hb-QGH_^6cdY;x|%mu2>m%;LZcS z=H#4`m-kNH_}T3c%(N{1p!mUmHhkv{mmLybeB$8lv|aW#b=MQ_PA_9qWA)JU<&WF* zTdk&vZ8H1A?%C=WzsxRYKdyh}`RVfZAHQQg;@>a0Q&_{-{{GLwnJOn2ZGQQ4qrg;G z_l26SN9I`k;Hqcd?7URN^8S7Gr{C0D7K#dXb6GaeOp*P}e^bfk>p77H6^d!E+FM%= znaQ=M$tDL&};=cb6Zt$P$40VWVb>)9qnNzDB=u*WfS*PhbE7!R~ zd}7Ymz{-6N+0AF3dS9Ni;mrEm($?wEWOb6 zF*NLo3Uku?N7Ltp?`-AqJCM|MMQmCmL8SeCqW& z9_B`glibd^Y9C)bdT^*@o=Hf2LBJdSn-lh|d}{vxuF2^=#@@3WbE}U}sUaEDr7ItJgTL;8KCkGHf`yCj%;CBm zc(>C>VDE|Zx4vdYIm zT0YxFL3w`t-Kg#bTay)Yb>H9l8TP|tE3@z|rAxO`+;6;@)|}BX|LDb%{2u)yvHnu3 zAKH(){4~4nDE^~bNq$Mp#U$D5uY%SzI6u0+>}%npiB3@uyLm23MbDk=DZ4@8QDaH- zgL@6ZqSw8GT&JJqzp6edBp~;P!~3vWof^+gOeQt=6W#Nw?CP&fobW3{oaa+v*%bfo zzkic^7`Mj#{#zZ9?fJxBXqMPr{hK*v@!MrCX%%YTabtfH{QGeoi_6rN?dkFXv`^#LKs99NTjw;-E)^*(=^4o3gvM)A$x7 z6|r1*5RM4a+F}!NGdhyD{vVIKMZ4g+t@^z?r?l%mo?K!&SGm-#+5W}@BU{T|byG@K zdOhkdVqCYF`{n%h6M{jLYbQn+&Hl0WPg#KPpUrbO&YO3eFRbtIWR0EYCUx)dITx_+ zu3ERL5$mbrSA)(@TKP!T@OP8TzP!mg^VHK{Om_c$*X&Ky)Y?_we@Hl8c`u__pB3IW ztJSDVAZnYo`L@tblcREWJn_{=my(nMHGYM$U)rf>XKU!K=#VM=Q|jp4H<>2Wy_oj) zX4P-I<-KK#{Nd-z7r!jw|E8F*HF^Jhp<6pXt1P!~n0jNgiDPZgy^T-Vy=2#h2R`n6 zzdtCon8KY;|?=`0v3kH&oAeH43ze?bKnhK!m4*l z^4_l-(}aHC`RijJD=UA)d*`R=Joa~E?*1t&&X<#|Wi@?w_N5o&u-av z;_r(W28{JKOV6F)Iq$)KFt2Cf&Kok$#S8Cm3cTC9ZeI75<<1F#PBI}U{TwgzEVw8z z#mVcm{P$-|68Z(jL-X>UD`^Qyg?u>Q+3{GHar^YF1(T=O3SXSJyk?$yz<-%CxvhnL z{u^$b3n`OyzI8YG$N5`Du~&CHeOwu{nE%NY&bpYBM}M8r{9OM-SK z$Kn5&Kk&T9@(9_|Eg$%MriXu>Gf#T@C+7Q7{w-~bt;?=8Cp1RpQHhCRK`9$pk$H+A(VtG91HuYS4yxcfJ2 z=jrd7R(~q(P<`D0!QbR3%l~B-%|8VHGsS%MTE}4bp=;~UC1TGe#!Zdh8O3XUihoN=TuTXtr%52vCI5!_m`@9^B=E2{bkl|uVn|h zfAQxuZ2r-6ukC4kUFwAUKc4maHCD2$n)lDY;b&=U8~=pg8WZH_uUW7C`nCCMZ?kSq zwrNdA<}0=a=!!9HTf>pLrQ)5}MCV{Z%l0XU>+SoM10Q&aT8M=HT(0$DcHjY@>4z6_ zn!W$rZ}CE>BXZVJPa99aMRocste(W&v=qN%uv0I}V8y*{Sx(&ZCY(B&cE9Tm>(fvE zt2?zrGZbe%m>;(AK1bJ_$ImXZ+~40a^Z z_3y(!*&Q~lx9AJr6fM-{@Y+*Q>Tu`oz#WetY%)wLQjTc4#(U8AZTCUnqq>$&I=@bN zyb&qBU3>N9BlYmZGyBdL26`Gj4%-nP$$WcD`tA2xT(!ktLf=}MOFgt&ux+owu7j)d z*{vsZ=$7nyRVn*>f@yqC?NeWV&A#7D^q+9IE#E#_f5!jk|EJU&ODO1VuXLI9=AF^W zf=4kMeB~vj5z90B{+HAimW5K4Ya*sDIaxn_W|?oySA`tm@N^wpwRL`)myb;KdvQv(s%K)8(|q}_ zc6)jr_PqGzoVI%H`WBPExD)!*wJpq+ylqf?py=Dy`d|7N1K-qXJo_{zY*=i^#n;SI z=NFLZ_h?P_%R>cGN3VXgx_0n{;WbagO}a}?-!7ZC>}plpyG;e#UTPTrw>aVhVycN=!!lQPqt#r^DDPqq7!y=&eqU3;Cs z-*3spThH2G&Dmkr>Qnb(de!=m35gQ?|7`8A7FSQK&$=ll>pzow;|-hmLvx$=&v~tT zG=gKx&S$T^>#L7FV%UFZ&bm!H!g~)JZ@04AxbD|mk&81IF9??jIJq}3>UC-fcVUN* z%0mTTr8>{2r?hqWQr0T$o3}8$wRlHFgMm+iPG9A&8P(g`re2xOTK3c=tLKPDhg!?d z^IChqwJ^JE=V9hkZx%(IkuME=#u=Bi!aqT*O)viBR+tkGz3BR9(FS5NMSe+w&*0{O* zpNHkYgNr}j(pbZmzfEB+yYSQcTCdOm2dgxT@dcCo@;)AO9MgR@E+;C9<)nq3_=9 z;<6V@l9oL{o4e_h?Tqh#@cjOX&U0xVJ29;;O>xoi0#`OmaV8y-A-_HnOZ$J>{F zn-x>OMFuaNbvtbVx9c@og&$5Y&Da7`%67(F`1;W7W5v}iSC?`&NqoP!@L|cNval60 zWz*gJoaY+}T7C^|Z&;f;ua^Hu4A(8y1}DMI3*XjDyZz;eP_aFBnlbA0&;64he5?-G zVzBAcPUkws8#{j;ss3BSD$%O+y78pe`KuEeRP)!i-r99gz~T1>{{Y84&$Al+ZLN1C zF3$Su)3REh!DaGxe|ttlZkcuL2e-slL`=_5aWS0tgx@FQ%n_F-v;Ft&m;d=``Ol56 z^*`R{YCV({JncH36xMnzOBS+7&Syu(j_;nskvXZwy$YrETb|C7?I zn+BJ3JzmaP%lB&AUC(Kscp|RswY8lp^SeCZr|NX+d`2h6XVc^I8O^d6%g61j%RbUm zUBdN;)0)%uEl08cF|obREzaEa(fY=q=;dU6W%09xDhli;9*C9N{W$-|`CD3;`021` zydq)F{mYEDu&%QWoN!z0o|^Ih1)W(AhS<4@How|fwHH6{|5x~FySx4WPa$s~6i=@xU{q$ZubMu$fYF^L zIc!V*^cMw;YK*I=^A|D}GR>Br-c-nF&A5H~zCuQO#uL;36*8(YYn;0R6 z*X+kCckg|<@mw$A{Lstv%c)q*UHXm zM?*ACydNClfwhkABG1_+(`_wh{rLOvhqbKl9X-QI{WezH zcFdVP>r_ml@Q!Jd|M~Q#EX;fNeV5qs8x^(A3!;UWSuyZd&1jl`SN6dQqlsZ1?!70~ z-CoSDKOLCw_y3Qo+%zVhw$&yNU%ia!HSgIKC6@nb8>_&IZEP72j=k2&3zHU*J=;Ao zXzv7Hjc?~2ROcSQ>AO7i!`6}>tHSddq8$Y`>gS42r!zh}QnWMTi(VvFBWV^lSaprB3tAQvQ3!9b39>ddJ2Ii?hFdnxVJ+>@6m~_$BwI z+O|7i+%WI%*1xWCD`(dG_p7h-IV{yu6D2MA@I};D5v~HM2m6lGZ`EI@JJlziTm6<# zhwE*Ts?S0P|7+%6y+%7rlX{%5e9^AD7I^%(=KM1`lQ!C^v`+Z2%j3SP4WHHXQ?(9R zTUT7Intk_+f%aST)S9EVQ|3vX(YDu(USP2#!`muVmh(o(-RTEw6kIj-<~N2kiTO6U zbJxpP^&Rh6X#Q~a7Uc~chEe@nTOaAvfA#E6QEcX$A(C14PzC!d{}>@ zYvs$uC!}BgKOeYS=iv%zZJXDXiHd7gG@TTdy)Jg;aQHUe@Xz6*W}`*hI+~k!GHO^k zZw5B9#crIFGUdVcKV6^K39*N;aja1{_RiLsXZf2~SVr_tR=vEZ_)-Du?$ZU!zN|jE z*|`2?UUf-W>El>y*0T;?ui}=y>2d$!Xck)dEw=FDK0Su?x17^n?l^F*@x`S}hs8Io z@hEtf!MONK%j+0{ePM!K7q1`o6tgaxbTn^{%1*;K>1u}6+QyoH$^D`y%lohLNs z%ay|iU9;C49N#tTkNEOQwUSEn=B~Ort*hjsO;a2rZ~fKI_-)N!pBkQ3KXiA(r{uln z^ArwE&*}ECx$uhieFTxyW1YtuftaoZx- ze`2+&XSW5%naueI<`t`4^4l=$k4D9usV90D-q|yU-$CqIGf*S zs~q~iLa|f8Hptk&eqG6pq#bf;qP9`;_O{i(eqDTh-2U(9CtolB|2}JYU}dcec`}_z&#hwtX%K7Ls7?uRcD5|*z<} zPpawCJGqsjr!6@mFaDOA+T*!%@dgw73&#s1CjVc3??t=VRoOy`Yv&g=zt)hGOJcE5 zYuU+RMIuJOVe))t(aJ^@>t^kKjFOwTQAvP{!*c& z_+!nwQ|b-h817EZPb=LTVRPPVMZjh0ju53kJ7?`}*Yrx)KlgS2hN9Uk%|qi)Y^b+* z8+q6^>2+mz^uhYDw2kcshOw74oaQ*Id*qj1a(SziEq8e4`R8}~=de6t+kQm<=^;(# z9odb?@*F%iKf5*WN|{1_+vn}2^O7gD@#GaNWj#JRao+x{&ROC6Y+APY+>BRotBw7$ zRx#z^asH&Y^B*T#vGrJoy_s2Ws8Zgr^UlT1LKEMf3A$9T&C6Q ztM3b7tet(~hKh4a&~LN)`o;HpUz@$QR&blVal)=7jlN<#w<85#1Tok#ZTsC>8-k&q-W#& z@!BE_Q^D5V&!4EjvFpmqnKohH+OQ1@y*vi5R2Ek!Iv7`&FX8?t{rS87(eL)xlMa{F zn-@=yv~fNEGbX@C=k4hUWhZL|3`2iRS+aBSTJf4aC#GK3_19Oqc`M-Wrl!@O%C~)d z^7Qlb8$98^{**<<2vr&Uo|~(nclXW_t9M5Oo-JC{S97X&JM-S{t+$=;-#+tPw_*Fr z+v^?cjjeT?{|Bhw_1MdIYV~TbiazDYlLJv*oo)Tj~AVr4b96UY+wz{K3W- zu=a7ttOB7ATlB&g$iEEQxieBC@RG-=33JUJGI6+ky?9D{^XrE@)bfvg^x3gv-qNQR zA07O$Lhj>(`(dZsA0PQqrBMHWN^wvB#hkzcTp!XeNtM>0J0G>!sIGq1t@`5{va5`0 zCHLJ;u3lUkBddGitxn$RB~yQQmrwumPo4EQmxaE9`E{!dYsKa2Zr7YlCh^^#I8n0u zOZ}~v|F7t#&hI*w`DntCKQjtjk{MqmGV;vIdbv(N+VsWV``-8euKOh{{&$0j_}*8Z zcYY{6o?jHNdqRKyGZRKf9{trnx$3|EdX^c$u-Re`cb=g{itps@9X6K|Osp)9Uf}kT zpX%{gE@CNvQ#=1H8Hsyq&Nen5P5xl8bH%K0QC7kJ=PsE3|K;B_;c*DUIsdbx?6aoa<>CoenK6@bpYDPV1&NcKys6g? z*R!*4+Rn~?LE}%hPYL(xyiS{(55Jzso>;4Vm9u*O-u*Kq`}z3S|I^-|wM6E~sT$6N zh?MZTHr`F2szMK?E_DK%ayjq7loTs5Q4%p;=lFF@~l&SEg_sG8rRb{Ue!6z?t zv0r<2lsVn}Mg5=YA^|6L4rm4zoDth_#yC^3d)8LZDFQ6}q&kbQ9$p@3VVLvzNJKb$ zms?+g{T;>y8cMUP-*8`Dx@=GD%h_J42lI4qO?N8#_UmH5roCBeuD4F<@~g{!@Xr65 z>CklAcyh~Ki&f@3BUhGxvC-t|nehF~iI}A;)%VYwkh@D|>*n6Y^%bi>6g=rFUn2RB zbKc{ce?OS^mXxrc-pOR}d6A<~L0!rj<(^3!(xc+!dz=)si)Z`rdF7T&JZdZ7Ds8bg zR^jlLJn_IgJ2%UwZQJ3$;<)O={rNi=`I?_F-toxoThX@+^GQLBc8PnJ9D26Mc!$pu z?Q3?M#V1UAe!`La)4ln+4FZm?9S>$P-c&uwklEO9UbAX3%Z(6*hPzhg#TRP3R2E%G z-cxOtpH`AvyKrS-^!6Fj{JYEZ?!8`IoMz4wRq_5!U-TTa%Lh+pPoG%ND8V$>div^m zMh}+DS#?XNf3IhZ)w+Ho{nB-7V_DDq{n@N+?fc#>ShJ{7CBv=Ie@bZdoY=kR4Bu!g zG99{PuQa``fl;SE^>aeyMEyvi_Y(VzlyQ4&&u7J z^`CvUMUVMvi#u7nPV;0mE57Nu`eDyHyRHp;>aRRuEEhRp`mce-u;-7Vr%?Rk8qG;3 zI(F~p?b+cLBzNa7|Fro6enzeP^BGrGD5q$+ecaD$aOHRI11C|R4IT9@`s`DmEz&#n z{r~hsM;lKGFqr;(wBYCb!WjnpUR`1eY?kS~rZb!L}H5VqFuexohB5`ch zQk8&*NB>FoDSxi$ntL<2tfG9+ACX$MMa54ZA3HQ>-K}l6^pxLUc`<$R`R+EEL{U{& zJ^Q1_?{o^yyZ*E3&^>>bgilX?KV4qm*-qRLg??usg&=y=Q^{)v3V zlgjdaJvJKz44+$GswomzX!2UQVt2&3_Gd?a9XGsUnxqka_~Xj;ZF$Dh3l)~m5V}xy z=6k)!xif_(FV0=-s+#{mZ|;j5-%frI5dI+k??vXU*HzKtXXT{Mb}f0h@Um#x-haW| z?q6-zKfMC?HX}9!*)!pSH+4lo}|N7b4dBbGK zzGv~TOA|WgvT`)NyfQIVUY?zq-7nz8r2OQGKSO86iq#v(I;pH+atjyj-0^l_#GBmG zW!*O~73qY89q#IQqxx*RXz(5HGlyt~i2?>=+E_+04bh=*L;qZ0-o4r$1 zOkrMvMoxv|f0uQKlH>GroSno~G6G*2rGyEbE^9T6h$?vCoG0ntnSA(@QoBU=bXCt< z4z8yY%$S{DW}T>Kl8*U5dGacg8B>lcDL=6Fyri)=w21ZHzNx1?+I_%$T`*+t*J! zIpdf3R2s%y`u{B4^|R0I&j#(smWMpq+_*ZUW=;Ca_D#u!rN{2}l~;AQFfL~3F3ZkW zvv*zsA?{E2=Zid4JZQ|Gxcu$)0-;1*_Bptb!O#4U!+->-=iyC7t$d zrox-2)>g@_SEt#sd=UTcd0@k?qs6Ums)4*B)XjIItjjhP31s z2IWatUCs8Kn3cMJPPxKWoBTm)90fu36|_ zpf-I`3!`{_-O@a#(B9U0cgqz*14`WVO{-qTGP4PHdQMy#9QtsbUn=`f&Xdam@BhCv zXYt*AMH%nki0XEjIrVQU*t+6#z#IlwsT0dx%y_3CyLNBUqs%^LPNACJ%H8XJzq>mz z`m}ywZ?XHn*9}MfH7Y~=ix<|i&Dp zt5vTL1bf%py>H}koEtK~IH2z&@BfU(6E8%Yep)VABHwYuQG1KsMB}-qn0)VjD!Wl* zA+HeTeYe%=3tLLUN9mNif5leVX>d+Ssk>%VQ{cBHDeUpzM$xt-9X2%$pJg1*eYWT4 zJKp`9`N#VY|MEQyllcDBTOMB+Ak2Q{PNVfSxxgkyt0Px;$ZOwveK7!<5d$x9WG<>}B8~G)FhA{&UUr@Cvn>6{*reBig-}`rLSI&yp(KprQzpm%RrKvbU zXOD0B${dB+Z{vXbQT zSD)d}dH(0@RtNdbtJ@4klXcm+mt7KC-IufG*z1+$Ow$6ciZALbY~}wT|Nf)ro!6BY z)|@-n<Um`ZoTGaOpkazB0}0xG!DPe*aTC zZ`qooe;-|EdH+T_`pu%Jb~~iw59O(BJF_OW_=v6OjK#N?zI06&5HWVii%~y*XOrKw z@C8e)Ll>>(Wm+3;u=QEpd*MZ2PprMeymeu}Y>MY@j)xsN9A#6t=T2D@dB-H6<%r_` zvm3+${@aUuIo8or|1HaMf$*FS=hEhwZ+3ZM@onSBg7b6298P=s$<5!eWIk&K`^i;x zYWIV>&A+dXxOc(FxwUNmtnHHC#~&%Hi%1^Iahmz_*;{F4%Pron^({vZoc>vFdP(GP zSlWG!UD~_f&(zpFb8*x2moX=-wb)e)1~^e(pN-Wx9yqpxw?2;y}&l6N|m19 zyxV3@)!W_uzA!+5WwP1X{!fC63ua4yo1*nSMIxX`AdxNZmea9@Kc7_Hn)pJ#eEODO zQL-gmKYd!sk<@@oa#p9(*Up#c-S4|Ez1s57s})x+r(a!v)UMhobZXOSPs0g+_7uDC zxVqr-KF@{sEjXM6jy~r}ozu=cqsw#A zpBv{*b6=`1JihbwI*EkMag0}Q6)N3voivy6@Y+bH1cN}9y09%1KPJuRczo};#kbCo zAmMtO#IXJCnspizri({jbonyr&-(wSfFJ= zb?w*o-ivV*efTM@w2D#2L&c;=Vs6Hoj5!nS=gyt$%X4zEvZHDBtW6=GEyRlKKT1YA z9o+j!s5vxcY3S8``SmW$Wl1S_on$*!yl&?{viyKmDc_t>qfN)w=`Fdj*zcO<%PSZB z!l#sTL`}=$X}q}p#XS2(x9`MW+2rtlUfbl$e|RqJxx3F}&)u`Bcf5ATBw62yd3K@Q zX32Tc@}%JX?^o>F-p4NbBVSg$W0tOIS>uV>PdO%^d$)mqIlC8IM9p=_=k-b-6zZEM z@c%3qc>1x*U23aii}jq;uXBzDss|}#u3?^@88*E_JFotP;+h@Lyq4emW3yk)QriBu z#HrdPwl5}{TfPXrvfz2k@9zt`Ha4y5X8ga-uU_?|Qr#Y#zf)Pt+1(5^uWb1h{Hlq! zhpV)8bMZ9|iHMesQ#9kWHLrN@;tUL`U;U(Y({n)`Et!StHo{Wv`}S9h9u8?=VtD+| zv?s@$ZkD*LJ*g@oazkK^=B|))0ao0Wy{1!qVjn8++bx}J6Y=L^NmqSezr4ip({_8R zKD;{Il=wgJ``c&xeABCI86+&%33Yw)oT$6%b6nx1Ih%ve#~DPIEMd(m(^L&l&YyYA zOQn9^3+1)!r+$5`ba~u$r?JE$yhlW?U`23dzkSf@8xjZa2%0I&lp89WZ@D4erd)B@ zGcln#)aR|sq`d2Ylrxj>EmJ>gv(ftTm-Nr3e7EexI^Qx~JGoT+t6j0oqAziemxvZt z|I^+1cs$>XXtGgA6~FXK-}%O2u6lSG?NE zZcy<<{kI7JhTmELjG7(z6L432hf!CKQ9&fwe_k|xUnR(UMZ&K0}{kNU6Y@a8$yS-Qx~%3G{y)n)I8)EwcY;>nHGllc1Gwq^Zej!bvG$Nohk{Y%Z-P79u7imH!4oZS5|Q(f&2 zPl`+qZw#NJ<;M>>tJqI&W<1@-JE_H?inag$l_v&GJ7+$5f6#sHd%Jydj2_}#UF$8^ zulnq2*Yald!@VCPvKo3kJ}-6H&v9{qv2^^*b1dtpCiQBtg-ki{G`w?adzrs*yyo23 znckU&Rq3xZCHjxuH^2CV+vK5SJ@4yjd`~yty|pM2+G_18tseJ1PpL}(-CftPbve$N-&Mlo513qIe*fo9(&p8YXFL)o?)Eit zaOA(WaoP*_WoOmvEBNPgx>auVI2j=DWxgLLAFp4>{V9>)ox=LT-pS@}y!-3lyRSFYfBy9R_jZlDvF+!sCP&xY{c9&z zTW&6Q_jj?)uhWTJjtR!)J8VDqV4JhYx)W{&z5iT&2A@MfBRwQ?FVM3 z_rKm$t~0~B$6>+edaLOL1_^Ahw1bNGI;_>-?y*8k@brmA&UKYnVxzVB&RvFhc_NN&;7N7hzp_gZd_ zJU&USpw%hs=hREnPlxi!GzxZg{W-s7lkNcpgOd9_yR7_TUKn~U4{WK=IA?cm>Dk`R zi2*E2zU_`OKm2TE8Yj11@8?90Z?Bmx)P6gP_@wMmyy~PCEYZ9#^rgh)h7~%&rc6L{iAD1dFs1Y z2EiXgelNRnSMK)f9SIWxqpfq7?yNua;)U}?Ii1!0jBeJqBVGvEt}(gImHI+4b#`r3 zyznNzHv#Uaxud@%xz1OTI(F;icI${tu^&#m77O%p&sH+Iv@?$HWJO0#yN7AbVegdh zKR;d%y)2gQYWKc=rb%nzp$!@5zU6gEM44LHg_SNzkDc)bUh`bb8{#@Q zpIvmF_?5xuD*yZUaeosE!nX#z7QC~q^IS@6Q<8nXiQCUT3Z@&fKcD=aclWAzeUj^b^HT59+_@$zil4q#uDMzB!erIM^5_ql59LokaBIJB&Me|k z`A^)xxf zd?-0J!SMXa(j)cn_AQG!`A3iApw+x+wS97(`8Q(TH_m!<(`nE5{iY1BtiM&f%6yn_ z%v9~q$A9th)azGD7Y7A~|35Y}GJL_F6ZSh>TLQGp8Xiq7{knSA1)d|mT>JA_xo-cw z{#SPUY>A~e6zioH=Y*VlP#Zb7>RQNwIUAkX{rNI)^XC^{s@ylfadEx9^zQY&>C4)$ z<;1S}{P#ytlI-FLcK+Ejxl~IF1A@~|KYMuXkgQ{$TwAA@?agZrZLDtQg653XcE%39 z9GufS_b^6H*kV+6)9K1|<&5iBCS8zdeQVS3H~0+u{F_ZXH+JTIkuRx#<#)cd(0%Ug z=o^1}c6jT4RqL33V;Q?;+~Ie+^{EG5E==#NR;hpZ@I~G8)Gnc?se6~+of|&?(9)ml z*3J!|A1&a$tmM(#+ov|~7G61V*XaPpX?u6)^iH_PCUA9jpFh8tas67&Pg~+u%QfG%uCHBLnXpl?P;=GV z;(DKwY4Xe822B>rUB0&Fb#2nSjPq_s>VD36cjt4TcLvwd!-wtl?TuSDCNcwbJF{$suM;QPC!W;+tO74E&Rn%*EXo7K0dtW3-|;_}WPy3f1U@IU@4 z-!MbF_+ymZ*{_{z~YD^4e9tv->> ze{fIO&iE&f0v@rv{#GtIC01|3-KbZQ+pCLfG&(>2Z=d^glhM~d|KIPi)$O;O?|!ya zzJCs{$c9;#`}q=O0{&Dr>gO$FiL;4#WX*K((8mQk_MCa%dc{#BpQlFQaidD#^0VdW`ajoAeW=&RrF~Fdr+#MbqV;ct z>lU6;U%l_S_v!l`wx(LESkj9E-b_CtD9Zg@Z^iR@Z0ZKu5C3nMS+_u)=h>v~|5;BT zv1`x1k@n>FQ{8`DQ&#PH6Gu?#xs6N_A*>Q)Vl1f!n-AZ^-ty1=V*S|_sNp; z+q*|QZ}sk-{#iWW(EHmBo=Z4)DeFI9)Vp2lZKmaSKj--c8?VJzir=ohbY~k&QSj_A zp(iZuk0v}?@9^WW=F5{CcfU7#8(7V@Xy?7p8xq+UKTfJrXDs;0e`-{-Pb1FKX#3QS9Tuv%eISxMjW|t|w{>3N08!I>L zQ!CS|%jEqY7@Hn`?a8m2>vHS=P803OwYer*b9v)LSD85hj6ZLkb1_r9Jz@IKGA+?3 z`&d>u*Yke$oB#LymrpF3#jiHL?rvKW|D;8ZS?zLv_p3>F_MiJ4>C4W+C)v|pDjn{v z#Zk{NJ!$146R(9vEkE8wC`v5jt@G}v&CPEtw71_qcj3*xjY)01o^y`7_UPZbemj1~ z=h(Oz-5Krdxlh?$H$`pfx0)uN@*^p49v{aIVQv3u8;$Gtx>uKnUFTnCk>TzZyz1G7 zJd;AjymCjm)ISz0q!qUb;!uF3W}MZ4$Q@lhQ2nooNL>xZMt*Z-W8y~r(Oe#EE7tF&^V!>2%(o{4X?4cs3~{J;G&YG%gF z;@75{n{$rs%k=%M>9{sBbaub1^==MZw}y>8O8P$X>(e7AvzyFS2-MQr{5{`5)#V@8 zgulNzr!L}IeZ_u`P|Nk6ng#q9-x-+Q`jXSqzmiop!NBc}+Nbv?p44|QeaHT-bEky* z{G|U{ok2?C4&OCpe;IA^-*m{`f8W;1IUZ|@7SDM8u+u~Md3WPA*2XnjGTN5j2@;Pr zogJiBBy)LezQk?wU&K>YO`7fZ&lYFPw-xtxbTTteJ#nF@=v@B6b1u!HdH^VSD!5w?6%_Wo_Xz7PgWn8dC6jf_|xNS7y4Z<6M4Dh zv;MxYp530Ga_22xywN~5_j+g9nr4Sd{Kf~>@BiZQ<2fFiHgO+Af~(Z3+#i`IILszV zYvlDTUi1A@wCnUoi68HNn-nmkJNm?~mLze7kL4GSrbC$VXb?W8xYH7wVV!9K!?f8o?|EZAety{~m zl$H6`i)jXHP6u}gR6pZ5l)dzKcI6snzDb7@nd|qS&r$cEA;CMvS8bl~FNILEt4)6w z_3DcfPLtmyxR^iU`Iyds&%{+Hdakm=c8B9z zer8Yl`z_C|UW`{eGVeP}{=_K-Gh#Mtf8sy5aDSq}q2&Ja%&yuKoa4+^M}(gyw%W&6GKJSs%shvJL zfs4)=#9UIa7xxxvfUOn$te;`)zkx5cw;nZBxQ*SefV(Z4ncYU|YA3}AQE zsX6w-?tj5_v%eA@>wX9y$^UQ0bHt5B#KYC*yL{if^^zQMA|6kRoXKxp8_rI@l zIpHL8l3(78tN#wh6@ZNqH zvAoqhEpoHy(@LMO4@+&^BF?o%9{((%@aW;ftaA|s-TRIAl-~|#b`;;BUL#lI*?p5DsvbIM?uaox+Vk?4-*2afFIf_wzP4``&Ap|PVQ(US^?0llXK}pXj;w_A z4<$+Yb=N}bB?8vYo~qxyY}Rv!U(bGL8dM0s`F`y_|GcVVS7AX1>y4WjP3I;LmV zpX!-Zb(3U0rNZ4_R61X%|MBAE?ezNQ$r_rmqEnxK57@P5Q+@q}{v)M1zKPPVBKazJ z?;VP)k8G>^rG2YXIl1%y{P$0~b50aUZhHImzrnh~$%`f(-}^#G+y4h^|EwbQ>-KZH zd%mv!px=LJZT8$*yoYbyPK>!H+>{y+5x7Ct@t z%Ut%>|A*}_x#ipMZ)QAd^tV2+{A6DB)8slXncKhbzt5^_5xMmKNnY)p7iMlHkBsI_ z{>Qt$d;P1o(w-I@&#{S@)_t4W+V+y&OewCmY4T;)whN-SWfG74+RE~!{qMe2@-iv& z_ilVLt4{LDGq%^0Htw1;!MpZ`AIJOeSC}RE-kzJ=e?T@=-ty5TcRf+>6S<#K?$poR zQS$I_`^5BRebSysmPARFF}>N9VJ?@!CUhfZ@q)io+W9NFL~b0>*WLHD_=Sm}-YfAc z&Xk9-G6LC_HGf`wot(Jgeb^E|d1i%shZZ=Oo_8=`yzN&ir$FVL#0BeSBwl)zX(pxe zBWWvRsfP^50@WGO9n8YB&hL%2GUq(MeB!sq^{2w-GnH&G@SQbnv7u?_;W+~PlzCV# ze2wt7VBaiI?h?zh$yZBlx9jYHSLFxzSaxhbxNb#2*nu0lk%3)Rf0ees4PTuhAhzZi z_xoFn9ZZH>T34sW9Ez9x_wK$@{5idj3rWd8r|jx&Ic7XD%SOn7@${XG-0DG%26vrz zJBqD7V7amW_8mw0|BHV82w(Gg+Oym~PnoT1pM-axQ_V}RT|86v#ocv3dadr+@;F@Q zYiMb&6?pq->$#jWWk)i%sGVSB{c<+d>{@==f^Elg*UjgPyV2Gxsj_Uz@V_~rip^mXkAS^ap>`tQ z3&E)ye{Qw0bIE07=ePLHP=7F@>*)>d?@=>y>WYOoJPmRWv3wj69>BoRe5JGJtw2b6 zx*OBJjngyvq%-z4{e675D)+e-n_Kt;tyZY`P8;Q08d=$ialu7$a~FC3KkVl?gXR#`vqBUhU0eL0_} zKeSj|KJVlwZ)2;o-(&r4#T@kmt~T_?O8v2oKd|x~yZu4?YO_sO3m=%pm~7E~{xh{T z;&ZO4fb+6%Z2hy?_#(o#eOwSX^&AfS=RDw*zrP*hmFJ@nT^j&b}1`UFmIb?`;oCgjCW35n%{=A=X1YHI=2__G_HE) zd+*P^We@kP@e{X9=;FG(aP{`GQw$r*b;H|#U5}L#zxVtr-|RPQEX42W@>%6PIDA}5 z{&-^ZxB6|L&Hmge-Cg-bQCn=qG1Ci2_ZcL#-(L2Lb$95?ZOXe&h z9i)zsPVF?&ZCO?nc<;!YO-rKITohk&YFGG)uv2Te1?(5w#MS5T+;>L&;q8KMzZ*eM z>KiR(b3|NQ zds14p!O!CS#mSZF9j`A z48JSBJFd7l#am+LXZMDZw3pgH{(hIb!SBw~)5~YKWxxGX_79(@RTOE@xcqO)!9o&)g=cgS~Ii+B-wfavsP zuQ&-wVhepmDbVe`;O+&fq9+S;?=<-HxN4`q7Q zGhfQzT;jnj9m;-cl5$y4`JueY?^+Wp%8kWSZs)z1-E8<%_IxJ0!o{knDe_&*9JcdZ z|FSqjLF8waNm5&B{12apkN8XWEeT}5FQT<%+0l))4x)ZbRDUdYXH!4-;@EVN#TIi@ zbkV+_d?qz&==XHRc74wgA5Fkx%8+ilquQ5Lsa8YOmI;}Vs2W=X3z zdwt-{%nRqf=9%m6-Z?k@xsJ1^*T(4S4_HNh+kDusU>foM0pGTFQ!1~T9p*nFes)vq z?N6suSqeDiLL z?yPOIzc0JE#AwQPU9(TF{}f*@S^Z(dzY|+t&p5kbmzhvb=Fd|vjqgrK?^w!v;unu= zi|X4G<+2>yovDV|;iYyb`;UCKh%G%7;C=tf`3Z-;m{WE&{gqiRaV~-z2%>U%BuX(^!92ZwTx7Te!$KO_ zyE1uEZua*>x9ZpKJYT!w_2p0({)ro>*X?-wB||w`nkCNG-81*@w!KR|AHRIY^Xcn0 z-EF&8*?;>rkMTN#nxkPxTCRcN+?9XRgQuRJ-+fu?1V??&H`erjF{z*baYUp|DLcG# z`ikFM)~)_!DH9^{DAL^2+=1ij{g%g%y-j|2@0}18VAZ;4c{x|chWcxl#9c4$X_H&m zBDrXt;#_5&gjGx`DLZl<9u#him=krc;MI->;Tn-c3-h#i;-_9smFRfIoRd{BP4tC> zLPz$EFcy}>{D;oR3bgKunbIQdt@LD9@5iW2b8((q%QHCipLuM1lP7nkW!dWG`7>Ac zzxuO3eAPU**)Q%ft9~|&t*Crd&r@=MA@-qDeCb}d&aa-|Tdb$$YHv{!7uk@zIrXDW zwA%F>M!Z*J)V{{e|LEI&%gO%QRaKMKiwX;wFTaVnq^_mtYA@>o)<|%odw>!r!z25cbhK_5KnN%0$y3Vs%zcz33fjJQZ zpA!W4uM}HzG)3S2bJn&)*B1y*{(We3ssu;se-5jhSCL$=%~pA}ZQAkd1DisEwDPz?BCdgS6oxHq=%=(sDpNTc%Z#gyF=J2!t-`IT~}GmB$~NMq|c zIbE?&K`vSQVsr%~yLB(j6wzPmr0HO~PAu2UDxu1H!R+3d1v4gIea><(t=;da|K?x? z)`uQERpxK3!g$M$t?K*2A@(8DY&VB4Pm)Y{vC*`5lPohDW$Gm~wREn=va(Gq_~d+6 z{_D4?7R<5hW^rd^?2T)W4q5*4{wn-qp#B?m$&~3{KYPBUnf&|{yALu0UseE$Nk=>+==E7DnFOf)N)?CGTw5382CmRW%hl%JKlo{d!1ZbKKA6}~J7ldEz{PmM%4$`s^KOY*{w;oqmZw)6Ka_a?!v6lA z8pEs%)`|7vE0dGfy?07vb$nd%Q$6XI;)T?Q+cxBVf7kj^@Y{={xmVW7Dmoy7j^GM_2lfSi ze#j5vE*9SUnPpu7`+kn;YYY2&Hceps;c`Q0j&RD7?bEuJSpVW&(qbumC^#i`-U83W z{Mwj~J!(_xc#c$A&D+Ai>7tVU`PMt=8co359avs@!v8NS3! zD4S_xsFuog;&!an!ewWFiqr>q%=8ZInCP-*R%rg0g&AQPhR1fTuy$}ZWh{07TPsURFj$W?cLuQB67(K>x370g$fv_O8sH2ujgjn zy8C;gZrFyi7p;sqpV%~gZK<)cUnjQO+u>PJ=)-l@-Srn*uAUIP=%x^?zg_3^u3s8X zduH#pn<8K;*1NzvoNtbR^xc^Z+YWYR==112IAp=sEFtjijcrzfTJV9~0--7M&4g?!{bM;>oc_uvipLsDL+I(~fS zo!BZ}@}4hv@qxl*L;YE&7iC`X`}J^gdj357>d(s_AAMB(b*DMgKjW*+J689u=j!_q zf56Y-c!K|#Wgi3|gq5?;t*9~C9J1q@%J0`)CnWy#0aTi0h(g}{(y z+gEBY*LkfZ+c zWXi?TGV9;J=F9Om|IINGcKfpJ?w>k`Y2O{^rDAwum{Y_{E- zfA%jb%LS~?x?Q)CuHIAsc;B|;+40WxKe^4W$o?o-+4SkuoD&h{iw_Fuu49NYe#q}PYejG4}Pf+5->Tb)-$xe~pw19|p~PcbELH@)fq zSX)NnmCjEyv#tD-s|)!bPV7lO*r;~@(ALRu+1@)=`L&fF-z(bQ{{7}7`MTc6JpV*r zMVW57x#rXpgL%(iD0C;+B|JE95uNh&X?b{1Brj?z3dhd=GTu=Xcwe!cn zJll_Vt$tt&cw&k>&rp6o>eY4ZLJf((LcK+cNB1dFdtruGd zUVc#@T(~P@rlx|Y`Nf(QuTRv4TwXHO)<;6&*VVxGB^x@<#$`T^yP@=hj!lWPulGqT`PzK= zWjJj6`Ber*$&e_3+QGObxZ`cE7U zng1>MiO}MTRD}kM>&^~er)xyEKE1Zlt)l6Q)bgs}FZP97US%X+)eV}iY88J@Ozv6J z$t`EMUEz~Yl<;Eys=R~mpaYNoY(;^3)vpu2+GyU`$t8a8?2a{cua~S#oZEg#lCOVV zcG`c!;>gm$H_m)gK3;1Iip zjisnQ)iba7_@+PICEw4TKYUn|(RGts0%Nn5*OdT^^!aJ>IrCGDl|Bhou5@Z#vS;)3 zZ-ui@obpadOG!&fIU=sGa1P@I_8WoSul+ckDrDw9z5ZaCl6UB%2VxNlfbd z%j37yzKdVc)6sW0nYCYd`O)m*y?W!Si4`fezt&hWF6B>U zyjO7K)F}^b5f9dr^_EdGy3-7P)GSDxQ~J~FyTqGqcjNE9`ObCt^qWw1(-xDv<{R0{ z#2X|JxfPl`m|kYR`s^={=efO%G2zp%sMO^sG+6O<9gz(bqOH@l+`D zuk?TRG*8HR;k+-A#^R@pE}1IDACh{eZhqN7-s)0$yD8U#iO;xK>RQ$>limF^TVSV$ ztfA7!DX|-3D=b8Ii9UVdaC}L@>sirx$JhQ7Xo>t<|J(85o%9>0A6Zw<%UOPWdw)^h z>0?u-CVt*}pmeI=q$-2p%0*Xxa!cIRdfV~eLC9mKlzD7Kr1WxL^TZwMtDTRmWO!G+ zKXxH=#DO1AEM9Xb7R~yryZS-t-ud;@>)YqIpW;(zylfL-HiKvVlv$y(?jEk@jhXOr z>zeF$OCt~Q_#Wvksoiue+@t;VwngSyj{T~4oOi_3HQX?>k2X7|9rZYPd6eJn5bk8X zt`%NC7x7&D@HJcJ@r8%a5^gHzUghi5Js77XoV#mx%Dhe4XI+Yy7%b?$X>-Ih`J-E0 zVf}U9zNP3s&KgXLLKl%Lm9scosLAim&%lKv9t3Q8XS~jo!QdZSR zv68a6JBl@3T!Kp7U+C}J^X^#eova!o13+7f(6wVT8j5wj5^P+EyZi-c+#P@-XQFGbKhbcpH1_&Xf2ctxq0pM zUm-#D-U&~GjXg>(BwDeE%v-P1>X7Pb_|Qd-^_PaHlf3VzCE)$00>HTfIQYt20ok++abeX`7zQk~+9Gu1PLx^8dVly&`QJzM^ib5lMG z?s>ggVa7e*B#|!;M~WTxS_vubEWGnr>CcIH`I%htU7tE<2qjO-juqGyzkkx_*;gE9 zJ@u16#*h@}p_ch!T8r)BZ&O;VthNO^7`?bA85X%_jVR0Sjl2>P{qD0CL@$3r%9Ze^Fj+3tIzte2~R+@dPW{Zr~ue=Sv|NI2)`t<7l@$bje`1(4d zcUPC47xXys^Vj9a=Kl3{s{0AEmAE@z^3L-*B~181c5g&*jzU&6gY zwQ%B(5GTpZ=~{e??_EBa@a68C!;{tf>rLeCI4-vCv$EP zs`}TRnqQO7J~WlxKiBr>hn5J5mBLH+b?__Mw$@kdTJe^1O2xM~huhPiaR+^9(eRi* zZ)RlNng<gtc(+Yd)xyMCl4^HAQc#-_sH zZR%n#?#>DoyQy`k{kWTLMbgXQaz?MWNfIjJn>VkRRa$;is@LT1%ET4V=FeHN`u56Y zy=uQ^TxnLnHH&r2DW0S?{m=dithI{1tMMblrFP1zw3%uty_xKGE_+|hlwedZ(Fk_( z`jjr?e|pQu+^hAw_RYKUtszNG@{(BdzsqZW+;+TdwxhDET0Yo#n)epb?hJd^sb7^} zdq>O8kG8MsS#(y$Mq44=WwGDfUKdRx4>KXj+*X6xJO)KVmU+t)3N1e8%@SLtx$t=2 z<-=dNGKDH7%!x_b7*%^|ZpeKfc2mJv_7z#-@yJF##mYcl3sLNgA^ z{g+)6bb1IOHB3ynO>R%WrQ5L&xUm6ve{s zWm#Jl<#)EeOtAWOH1ZSU?(kVZx4xTRbmrmOkNaiH9(=s;a!c->SNBZcY~1kq9nY@2 z6B~sm+!c&Yj$GKjW6zNlCFjfU)N5b38sViFuV1Pd*0-ae^y$|UU)PMMvmSc*R%Ed3 zZ2S~>`iqC}o9fERAD0SM9%OHy@BH`QV*Z5JC8u}{BkxTMJjWxYx9{84Mb)#q_FB7t zy8Afx$MMdxV_qk6(w%=Sw`L3Nb8>d&_F8-EX6-p1D?=kEmi9kC4nLC0%bc)6F)FqG zm8;gY0~rEuyY~1CPMg+P|E+v}DYL+hOS|OmC(o5$(4(i4U-heZ#=m9MoJ-cXi@tJycKclg|tlyUG z&4-_q1pe{4$;-8HVt?GCqeUmm#aa?3hFVU(p1wp=G*UDy`$gd6!fzFGL*ilzA1{yA ziA&sYx%_Cowc`J-)4ZA6vl_m*ew!YC_0jXAUskPQy}jM(88xx}26 zhl_#tM7bWb*@9(H)DOP?8_eYKOMK?*cm|WSX}Skz^UVua+Tin7JmGZCH~km)W=THa z_gWUFuuglz|Ezr)kPp5c=Jl0=Pk9)^W~Jdadf`R`rc!t0V^d}Y9C735aETVm;{V+x zR*@D|Hv0&7OG0JUH1W#Hk8vAPw^g;vTXFrFSbu2d+=aytHK%@OIrNU#XHS<*=8uCj zx(_i`cg~7X?>_W!Q{{=IyMOsg%G|#i1v^iz;f3 zsAE<58pBTZRl0@AnkNMO?rc3;;r`x{z0LFJ&;LL2|J(L;$e-X@F5(`nrf224;$iN) zD+f33UHz6xdHvaFYxB7}8y8e&Ul^VdA*ZFpm=c-;Rt_iiZ2GyHuu&r3jkrrtq@ zpKry_)ho_(l{stiOQhI9$F62_dTQ6%Px%kolix~pWY?#wk+Pl1O#3Bils zKXMUBd1vx7;9Gq=tIx%%pPB|c8?{+?&%3{d<$QkPBkF73G7_5Ip~IqE7}rA4hP|H@DJwPayz@80KO z>+L_xIwTxwhD6I-%8IG0@uInMsP`0<7a(GP+yo*`C-oynm&$KP+>5+fq9 zIlnaXtiAB6(tVxtzkGdt*6QQrlLg$XSpK+IH%9cX_}#HK^7QW738|BgrWnNSIiXW> z@m&SuI*&^q9##}GozN7OIa!tL@S1ntg^AHF{=b@o6K*E(d0H%JIgxtXB%yw8?^cyR zd6S;lUD)(x5tH@mwzv70mSlREy_u~pUD7$}#V4iK^tdUkr>CuQjST7Z$er2~V;h0Ep@D_xqZT=K>x1TDT^&lUAiyL_t<*l@l|JYATI>d09A7qfMND*0O)Df7h!mD%-Skug4=>n{1|+UpJ<2 z$^K_vsBBXAaC@brSxD~I6;VfedGunKXI@zI_s)~8vKQ*L9JLoP@aEW7MgP)`7P*!1 z?yqS5p8d0K3r}A5NXc2Z<;+dxRIeVPX`#o@e@T4YI)97$DiQXr=ADL4@!NL%*}nRX z)%qnu3Jx2?U&dbXlCFJe(VoG-`0cmS;B{}$y;~=@b=ud}&go4YpAz4y&@w(O>@uA99hOyeV%J~ zOV>^-$kTIRxNqmfTJ#~|^@RF~QuAl)T1WkPf3x9w+`~;w4w0ch*-{%8XPdb1470is zdi93r=>Tqz8M&+UgzNXVye+VfTHminDYx>^=DaaGXMqEcgnU8%4IR1JKRo02@6!Jt%jP@(2kVt!0sq&p zi#PvVz3+X_gx88&n)b}QZF=$Yjw^cA%EA)q@tbSep3ktUbCO-H*>|TewZ3-tYuk;s z0xk)i(-K1WUGaKx_n?AEtnrp~fq<0@7DhEVuz&r^aIRpU_`72l88(?dW{CFWE4PR` zDa|`2jn5>!S8Vd5sV5H?3O#-)Vg5#cIR>2*hYmF5xG~yJvQaqcy}jYBkK{kqv-_(&d_UdPP>blVxO;cX zM}dgVYVsO_CF?@B8j9~LD4(3PNHJ1;p0|Me0=4DyWrX|L)r(9fHpWd0kUMnb#Hw9) zRm|(ZimjOLU~_)WWbYJzhGyw5{%S30r|&PhCf!UCIAk8^n9p+m%*()P$7>>dr@XBk zm2|~l3f%Bs7Vq)=Lqlh_eBGP!F3)G*4Od!aD&6wo`tW<=&&C!#x7t|=rn3K3xp_mv zuTSotCsfemV&W^DTK@aX%fsrs?av5p{oa%3uu*qf{l)bO-9dd~QfX;(WA^MhcaVeS z<4*UF`ioC{Z!9lSY&t47am$;~wQf~QKSW*fH1&&5c^o3JN9o1Xkm@6AWdd}Ab}1ft zbpEp|S3-=@YrnXE)BdTs{X5var=`04LF<~`{)L;bNpF9&JLfP%UB0dO!`Cx^ytk?D zwTRrBAG`hD0pYg#$k(T){a4&xX8Cf5Vf;#82{t{=n{98lRKIsPZS;nF&8tc4U+q3D zUtd+p>tif1xzR7VyY%d*j}4O^r!zBm{}E}LmU7*@aJo=&-Q-EzZ@sV(Yp$x>^5eg_ zLcy^Ledo1bE7q3=S4h;KOs(QAl@(cR zXQOhm@VO25<4emEFY^BvzIk&?|J~NL6CU^#oc8&>E4%i^%_k=hcrE755VB`~lm5?< z=j)S|9w)ght{FVgxTVc};CSDSv*Br%!;iO`EIm->ef#e;TOM})DW(dUn?Gl&*1arV zvf=#1ruoXO*6yD=?`yHE9v1e?`CHE~=cD>XM4*_7pC@=@_F9dOp!6H1S)M`3*|l@# zy$Rn^%Xl)lmvh^!5RDgWGIS4eS%o#Y?tR#4;pEkN#B}zQbD5^BJ)63}1-0Gk)IYS` zPO<3l$^Hd*9v$gqEc#HyI9o?TtCO|{5s_-%bizGirhIQHYs*1xOSYDO%U2Bbz(tWr`!(J`6}EDmv5zc zMBIP3Ir&XC^HG=lpv;r6xdcuwE56{A^1uJ}Kl%FWe=B3cooz2j3g5}}x!#bhuyYmb zG385~CU-6*#0!VHH123B?dv{}c*)eJQ;~n!BX)%szHJSB3jQ5`W9GOlzB9+cE~(kF zWmX@@gc}+c`0ng@yjlCg4q17Ap~4HDvriZ}9+xpXS6A+)9FlZ_^`kCtz3?OD3E|>1 z9(qlyl?EG`SPUd;f>`cks_BDa!#Cm zN`6Yd-t?>DU1=Y-Z9huP2la_>iCK4qGBe;cb00#>l(bDS?wEAuA2I^$d|um|GH2zz54KO z&w|unAFrpJw`~qNX{y7~Sijh>bjP$`*5x;zUgEx7w!o-t&9ccJ+b_0$m#jaPv$iq( z{rx{e%UC;?Et_F;Z-EzwK>gX`@;RqW3_9oPW`zEJa8Tf`&ViLa)44Cmam`ia+dq4& zMS6c}kC|fb+mzqtD+-0g1=iMD*+uqSMcT&XOzPLZv`R>IMKJ4*7fE6}KCwO)cvfa0 z><~Qf&6@?ztdkj;R9kmUYqmd;R9`s9JLExE@&l(&PiCmj?cuVIp8jPa&zi{k6L!LK z75Nv}?mTpFLDovAyWJUUtpwfUotkINHq^QHU2g4(Jw98wW0p*BdHiA5H>I^FoE1(K z{eD%_busvKZ2Mv>%R2SsnIBiIc=)36;S09#tKJtM$HmRjtzrzRzMbj#bWiCAfwb?J zTJ#q1zuY6Rx938>0L#poD@3-N6_hv_stDI_dZEKy&+fSBhwehz^qc2bv^51YG*=z> zzIf?5D@*I9#r;f+wq_h=(0Ai{cKF-}wymbd?{+sFdHYi_(~bgkLryH>}~9{6*;o3YU|`_!A>V&@dj>c*UN8sc8b zZc%eeZLb%{RR3ChSnznMj(?t?UNYfqfNOu(!S6SJ&VIeLGP?7L_LLi0 z!G)1}f)nft)h?EXPpPW9yJxL$+JPkjmTSwGkBj7r3Luh_JsJu-ojix&L+Bf~aDRr@k$zw-9wDW}bx=h$+Vx~G4y zu$~bcxAX9a$}_i%rXKt#d3#2sKRff=dyC88H|Rd9snaN95b#O971kl3Sbd;p(oHt| zqa{m=(({5<|8y_jzcHwO=gVztMV7By7!;O`ZXj?PLe z)+h;uGdHI@mS0}4x9QMqC$}{_Zr$cs^fqOow&IBiJ8nd=+}gazF{&e1k>}7lX;mR# zHF^E@Q&}H9mY;t8%=_Z^;U7+4$xdZ?;D4$}`2F39Rf`^6Z2W1)Vzn(ew((ef!zL}p zkIO!Ouv_%0Y3j5Eb1kdtKWyK>xAOhmgWIiZ&#t(y*R~|7toww)v~=a|hE`YJsfaZ{ zdm1=n`gPrz-AU282P2j&O>TUAK%QB^XW_hg4_1Eeajch^cT)d+X@&7L^S)D8r)01B zaGSB_SF-7Q1u?&rrp#wcxlXAx{`U-Q5B029mDM~iaouvquaCKwPJCB&mAkS|y*{WD zq3@Q<*Q8>RR>SS1`yhnFXS2M0(|?;^b7}?tzWo0>DCXxScK##0A13bbvRh;~uS+kk z-cq&vk-NaZz>QZASL~0fIdkN$o60ALH%H}T563yz2e>Ii!!IEvu zCQf%1k=V55ZiJJQpsC5Oe|=tig?;u_&8@eReeu0>4}+<1eg7Vbj_e9iyWpQwd71Tm zWR`r~m~iMt;*r%;y;7>{HhOH@z2{xo@i3NvuiiU_4eHl73YV8`co$syU**{I+_*iy z>+@^uStHkmGA>ixyH}ARHzhtbyDoC8W!Cmp&wd^1I3m4m&?@qWSa^ZJ(#9s6A`Hp37x#o@kt2clDzjn{#-^G^f@+}g- zoEWdY`owcny<&|(=C7}_-*42|;e6toXN}h9y<+=)6!fQdbwy6BIC|;d0u`RXhU}pG zj7`_SEdG_!@E{^}0#Dc{xryu_`p#8#uMgU^Z5_Ef2jA!6eRwhc)I(+iKnbRF^VgGbWFUHXWugA z>9@sC)?P|1n96r}^_A+{mESeD=FKeanEPHaSmH#t*CUfQd10X=r+zVoJzV{UCuxC< zu89B#Q(V&iyyyrYH;yUA7Z#;d$@T=AWdzoT`F_%!9d`Q7wYSXfY~0&(ewWsjmAQY~ z*i|4N6*BRh{qlIPjn@$>C&AUuv$YE6$|+6Xexfh^!)vxJ zH|{Q`Lg#?pCcgLQh)?CZ4@_ctgpq$2oCn=BFwre|>$_p!$sR zq+ZwB2h%p}SoNctV@F|<>+2MY7wZ<9bx7#EyD8bYoq5Ke`l~f<0qeHxKDXcHLD013 zlRjdh;t3_@%_&{W1Im@sp47y+{jIYP`*@CLFTeac)4KLIbKkeB8fjnY3%)r2{>f8Y zoQ-D$+aFHWD^hy2$n9D8XYZCbt2fDQlAZCuQRie!^yw!z&fI@GO-o{ta?rU{FU`p+ zTlTNvwO8fnYI;>1Qz~&a#*AGJLCK}!f)&!EIS%- z`os#Y&f3_>7ORaky_`ZAptU5nCEy|Wpy&gK}#WF^F?^AWTOlug5_1D`|G_+`uz7Viu$ON|L~t``hAT*uidpT2NL%I}{i&Q0?>^*1XeRc>|M zGgtTW@Gy-_AxqAGk(%@I@0?5x^= zEXVjGTf{Z4$e#UPV-|3t%ZB$%0IOl$$A?I!5q9RS8u=8u+mPy=cj?f(54y`r#CF8pn)|0!YR~%k&$f4io1z_Z zO7#r58o%cTG;?B4bu>E@dzm1!FMa~?(;*#^nzC`EU zi_CddOT>40R^O=q)qH!#rCT>m)BVownYK3T*t!j~j(>f7xwB>6?LP~+uATUlB$Di~ zb@S8(V%Mk5|D08Ik&~gb>#(0&qpI`a8G_c7t3<*KiAguSxByEJ;1bAZr-6d#d@9x$DQw9zG=F@ZtkJU z4-=>A@|y2&KlrmYu&n8K(4pBCD)kSZlwFl&-q*SJh{^o8>a*$FGvuDxJ@lDyoRjrY0!$QA#+RyTv=s`Z}R55)tcHqBB$c*TT& zR_Xor_WO^&MPD&r_3{&!g6AK({QFDnn-dqmo^^~rsgm3!{a2;8ccUY$F$ zcvgOB$c4rSx{J8iKijGBqSlO6HNf&^#=HIRE`GdS8?y4R-@zW=C>@uIu)$+tL^7w?_8<(a$n=IJ?l7ZRLd48L#=mub()$CcLy)?Ha?6 zWjsO zr5|+9z1LY1d0cDX(cMCF*4A^lC$vVj?=srKI(s(Pz2`6OZU59?`@68pqn=?4V?}Y* zhM36G6W><#ZLeO*tGhbxbo->fZ+4CGr-~1U9S)qgbVkoy$Gd;aRxZn0$s(8kW%lL^ zTN&q7F41TH6B#^tx^7?j6aUW{EAO^l$o(XsCwaVZuVHe{qpJtJT=`yK3~0Yz z`g`p*>GDhc?CO&}3+8_>;(I&i<<`1Ys~;|rdiy->yzaeL34xRXt=PLWk}iqwtWT0M zSao7|l>f4l(CC6)N|CD@Y*vO%e{wu`?!K-Ud@GN*@K(3wEx7t3J>R#yYVFlf-b)r5 zn{Qp<-9EYD1b>?D&dBIf5(n&0tkqr|t+YaI%e%=Tk51IR`F85Yq@G|8yNc^#i{ASw z)_hr~%M_o=Si0xu%&o_Z*WHW$#@SWS(&zcHzG86&@8rg1)>X#Sulb5h&I`)5Q`)w^ zX{O3H-A7rEqn@TqS*qKAlr^7~v8Sl3b&*nfqV%bnO|P5Uh4@m=I?5}wEL*v;VV(85 z$;P)PT(GJ0ZGDw_-09lY&1EqhA*Hh$Z`w?{&3EQnv(UWRyve65Zz`u<o&e&&B>X z!!ddLqUAcVwUe#gx!*HxpLqPB+m)4DV$++JE;wlAZ@hnRZgqWlxWR>0JVwS=%?;1* zN*|2oFkJGt`%4zLMXw?M(%)a?4|`o${`%oHyVz;ZZ^~+Y+U3uD#lY@w&a`j|w)UO7 zdCN;h44!^u^L%Z$GquwBbqn9B?(>h6xW7FN%sRjFO3%}`ZI^x+_bgbo!oDWXEix+Y zO3eIqT@9Bj>f|!!iMG!@T(9L)*fXv2;#&1zKVz5q-g#GQqLk9Wm>y;>asSxYC39a0 zZ9C2?w&tDt>8iPZ)|#>IycEUf!=k*nMyI+-*gkFbrndVTJ8gD~Zq9KR71><5t9|1# zvFo;7oW_&loRm^!K6qtt^*k@R#;e?ynO*g7d+AjnNS5KT_#IY%}f63Rx*Jn>z z=&LcF?3^L^!$_!S>iI>#SM+^0^P2u}s*GQ~>EfG{=jq1(x#+*=#;OqK4PU~Rrk#pr zIA{KE;VH$ITK~KA-AleyvP;Zi8}(ZJ5$b_*PkX1{>rm-VfibiyH1AD71yDj!N6^}8prPEg|Jy*u)=@BWp%eQ$p2 zx9=W*ttIMz^K6J}xEZ|s`;(VC*6-a+zBcEzSUz~9QP;Emw46k`)%mRB3kw%tZko5f z=FIXBayOQ#oV%8$w_kd7LE875LYfO&@AS1z+idaVveBQy%DG=2{5SVYkKJk3lk2U) z(0kcYwV*tjt$vy0pO5PfKd}2!xqGHVf&Q$P_vg-?Q@JI+fTR9mWW;vsYwOz#es7=e zFSYho?9Elz=kIBYiCW;|5z^`MRmAG$ovT|M`Xz7jGSv6i%=o!gTHn;u>l;%p?abT9k<0%-zRdDZwkkNQ*5vS~`YVAtQ>X4s zSo-Ff9Lq_oCv1!7rY15z2&|VbTu|?=zj@PQHjB;!Clr)kH=Ov`!&LrWWl`u{7cUoo zrQ&3P_@$>>A8{FcFY{ixp!UX9*T=tt?FkG8Q%$WG?&f=aOY`o# zsJ-R;{r<+P-MaUAQMmEx{J7bhBVTZGGtWr)9$mT2oF(DF&I0$#`$UiDGD)A^eK4rNC-IpYq%^<M2b_fzr;*TF}= z4m`TG_oJ&W?Up{MW?#>|wf5E2)CIREJ%5#)8u3{^Po!q$ebLYQef@h9_G}Nzz2>8R z%-W|r>V18f{jq6BYWIhx6`O21RhK!j`=a!kbAc-#PyQOkuixS|!Aw3w`D>?aXaeVo zb4C%1>hD@~o>Xx5YY;i3cvCL@>)nrATYIidley6+nY{jNUd^Nf-hE}$MgK;1-s?}= z8h7MOut4_3(v3y+r!L-;V7j!pi#M!jiw~>p2X-bc?zCB{!Ck$I)248_NXF_KF8;y) z>)L@&={pa+3H%%#c|KH9HYZCgFZaEJ7Hx+O<%8l$F6yb@{;{*Tb$ocn5myWPbwws zw4&T87E5iB{i4&a+Ob#iR^Kdlf7fx_cLB41#c|FV`>Z6hmt8o`Zfm@(lTrG?nyTjc z2d4=riY*ZlIPgnv+0FVtrQaMm-Ufe|XnytB8Lq&qZG2uetJXLuTm5b{Rhhe>nd?uB zRESFCjFfAO=2T1e~E_zfns|R%wo=LUG(}c{xts z9QWckQ)fQ7xwWu(%drQE#|#3a)|jtys^3*t`z$X0+^@5p*NQHsS^i7t-!yam#_cU$ zpEmKXS2kVKcU%6u%crnEJ2H(pJ(4ch@P)2=9vHg&Y4^&DQd2yys`s9r=oiSAS+Zus zX3x9Vn7of~^L^iZSIa-k9 z={PQ_`fbt6_X>%meG=KkxA2JUgeNx{Y!0feF?=TXW>9vDMV-Zhc;=Y4XR4+|clzdwYZ)G4)q2Ufh>nul`z()vb4N#4k~)Qt|jR zt2o{k^)#L9Wpmxkv*6>t6@Lp~Ogb5pbwSd0gHwD4yJP9iMKctZ_+Fl_m3{u%b@k1~ z6PNkM%dC|8$^Ec)#~XPIffW0Fb#EUm?D}})Nt2NC?)P~n4O|+D8&bB;($@XfI_>A( zB|B3^=PciuX?khbynqY&6vX?_?@RPZ*^xQ_AIDD(arsy2igVg8uGp_F zyd<_Fs%iPX3<2IJvoHAM7%BLCJuyV~!hZT9ki`!(@vsJ(J-Lsk3)uIrm} zau`qaulM^>(jW6VeOv9mQ=6R3>|%AEZO^`QrRa8@_qyD>i}b2@v(-LttDn6#=~Ro` z?pTY40|8sRwI6@~QSTcgxi93(>7$Q6^Ky&pUyHl{<_>T5E_v;1T$}Ueg@k@lf3Iq? zNz-xLsNwk_b6yttv1WJ&a|Kc531`sYlQ zvx}5|8m@1_wl79$+V4B%=NE??^X+X5TfrZ3c;yY*FTa!4FZ7IKyS81^T6o?seDT|<_4A#z;$llzvwrA4Bm1Z>G9-LQrc>xKvB{@4=WW+n zGwDPG53{M=xA?Iw3=hAhw#n!SG3N3vkqOT*E%!w z4NGYAmR-#US@nvQW%Vq05TsIlFe7A@@IMJx+M_6(x z$I(3Ns2k5>kDnI!r`|tf-#trjiMI-oCOz1U*N6y@r$=oS0A;!wJ|W} z`b$@{zaEWO7k*U!^P@{>u5NdIxtZ4Lb7I*ZcT3(cpB;YvqW+>y)AsJ!mG@@J&pZ42 z%Q?-~hv-)l$n&Py+5VSK7E&`j-P*z*}218v;Ojm?%LIsdSTI= zAl}r4wXsr$CoE^NeExFr%hS7&$A5D@@sMVZZJjZ3%|DH0-*W!VGI_$kJ-0r7r3$Ox zQa#zU^l2{^&swi$`twlkzc|xYej**?0EgtRue| zypq<5dYoCS+$p=zs&ng;=|_CFSiW@HGP{0R^y;gERcFtw**>vwt8%=6dCc0UubH}o z|9P9mHwpS{K6n29r0uU%=xc$A|K93*yqsd7*DrBUBtzj3XG6QX@7=;m?cCYY5x)C+ zwn;PYUK%)c$JX8;V}pacZ^$n9D-8a;M{?fo!1_x)l|tbSGL=zRCMEdIxiUfTsiKNq zu3F#P`dQL{&3k8SYu|0NDqVMXlEvPh{44WaqC9v$o|I-*cKOQ%8 zZAEhbnU(kNNKf|va^%m1y&t$X@+p|4#OHJ-@y>r{+%Do(Z$IPZ_XLA4N4nyYD!Ui> zty#|&kh_1^2`+!BLX}&6Q!_Z#zHKh}>-}Mo#p&}0m1{Qsx_17c_g4*fex}oV|9twx z_D1=qaLoL?R>tiIR9p2Xzdw0@Z`HQnHQ#r7=&rYmc{VlPp|ZqIQg8j$%lCv1E3e$X zSN+&q;s4tRrx{Z*>+|3JdeA z9`fSW4*sHaH{!L8zTLdIwa-?pk55f6y(KWu|CaU9kpIUYBc`pRwW(eXa1zyBayfo4)0*oOZf?^UpOU zcjxyUWp++qtRJ$VyqNjwx6hKRb_8Uri3Zo2zL@gE=~wX7BGD+n&`m6V0{ZHWG|Q(d z-B`C=pm4=j5rrzJ48vD)UbSbm&U){7Db=r2o!cQBKB*yb<;qFnpJkpf2~U_VmhbX@ z#X+tq&lf#ldLL~iUUTNsj9YWH*p4l33$EX}DuitfC)0a!-5>3sIx36CL<2KucDQ#f_*VwrJ>8?IfrTu%2 z5|d(#^V6Eu#jmDs-+NPITD@$<&a=W{XS7$9=R~Pri@Evto0RpF%C*}jT;*S~R4{l; zptbqyt@Zm>++ty8(+n%_npSQpdwSOnSEq`Oollx}`Y)gI_x1H32CJ108-EB;&ivk@ zC3{+NI@7m^SB4?({n`;9g)0|cWcFNrmS47X@orU?Ie}}}94!`1y_9#^WRIHHnwN9m z@Z9opzt)x5Joky|p-Gn~gnTx*Hg&pp)-P|v(6{qauYP&-w7%xv#!%bYul7&<@m2Hp zv!dy{wq?(LEf+BO2q>12JR9ma*A`@Q|{5dAoll`YquYq5C8mA zJoLiMW_LN+vr8J!>MwsD@Tcr$qsgN5-@jJAZqe%8p}%Ki_-EIdJJ%*YI`%Gk-OVuO z3zN<-I2ZkCR{WN!tp{^iC2s6|_g{2%-p0uH9!Ak;BW^}Y&ug6Ww<>n_qn;<~^>cP# zp1H}irR8+A_pU(xyc*+MFZQf{a^wDwNwelYSt2d->Xu?wp^dWr0z20KX(o3vSWm8S zb6m7=c8+>e>GrZ^?tooag)OG}sjUVn%#`9 z`4PUZbHU@2rz&pmjSsx`DDKv})#;fZ-?k>?_8OfIzZr7s;IWF#nW@Gh+U$GGOFA`T zc&of#a4mY4b&~m!9q(LU<=c{RjvH^^h;iI$eLE&|*8h~*t6eS6K5uv)H|P4z>-&Nu z#as`6xPPv`%Jb^oOCH&Y9+^hTTq(O3pPcqAZ?~}Dr+e97cOSKFS)gER7!&?cyKX{} z#a|Au2k#fk8J?K%wC_8==cID|OD21l>rc}7xq713nafRUej8~t@BRGmXu;-V=28WT zVQFn)hc~`myLpOAc=%-9TN}R433({%c8l+&o^RHt%-f3pcN^7985VmxFMA1JA+-L7-NU5L;vOnFILH=Z*CJ0zk8~%bW%!h|KF$lr+ico9bf3NU$k7SqueX+D zx6{%JQv4=d;r z1NAR`zIC_pJ&K-t(nL7g_VrfVt*M)MABorOz3fvO^R;mcv*XOF{gZz+nD3Y*lzMQ{ zzDw)3zchLnq1xvDU03hw=`Hm$^{gcwPG9hxUb>q>tzuKz2bPu0r&?UU8ZGQ}jx{@hHQKkNQsy`U!l)*rETDpq0*dK)?gkG04yb+eLt%@A@v!P;5y z-pzdi%yk=mmM1iM{yVq1qJTMvpJ@HRP?A-Q=&eYpRjuM+c}V}tMawv-$W zd|5vKiiE7<*92)}(_@@F!ZS}jS?hLZ-trW)FOMev6KhiWc5zW|{Vj!4>*`kjP?1-C z6(_dshS}en+s%$;K4`6<>#%;#5^r9U)pDE+(Z~IMK3Tj+YQ{BQovyO@4@-!fVO#q&}_oh|Y@XS0Z z{&jxP8pDe_w<^D}ELWDi#H(z2ciF<&%K~=Qv+lWu`CsG;_-?kTitm2Jd*y142inqo zEAn#Y9ggKJ=3ZyDbU}9B{m3#N$vXb?>w2!Ojf=jYuru0bdr)(EZGL@f%70D%b#3BF zCjJ}NS4`Y<)%VeSAG>dQ>0aOGow8nWqvFQj%P9ptPi`z;F}r}GLoXky}alz4- zXL!spUcJSA!Ev$QYB|r7m}h;k?f&QY#coPW{X%;uHS_X{*Gq-p{)?}^DYp6J&gq|K zZ8|trqNiiCpN`!6lgS%HEY7!Wf6UH5McQbk)AyI##8s};ZA#qi$K(;3e>QVR6U$To zt@V+X*F#LJjcfxqSI)Tdl*c|OnC++6+0`$3Q@6H$)CGM``yHZf?03P zh3Zu@YRk*6abCVITf2GQ>~p8?SucONw)*c`4YN7+PQ^*jRsQz78${1_*|=stN13U~ zf~gJ%zDEm9oY`DfGUt2vqqbVTe}4;{&i{|{2S6C|CYPz#lGCQMdsPb za|^6r6{JpLx-5RSe)WOyJag-+p2=!D3{IDQnR30}E_POWF;PY<)MYuR%bXMXn~#P* z&3&*ZYv=Ym+$ZHqo!`e~yk=yden~e+Y=-uxeK(#Rwb54yTAd!g`=oBot{JJz864+? z#f5lP<~Hm2JaAp?U%@(e;SOyNvHjT(z25#>s{AdeM64xC-($&BqqX%pf}GBe`%8{(SN?qM6xIALQ0#i@&V}dVnN2@&)$@idU-~t=<4TUfyR$;FJD*FRIGi@ES?=n` zsK(hZIysNTCf~jp>Nw?|k6?I+7>sbMA{5)pPZ7$Y{SclkJfy=9+zcZ}ok*U;0eSjT^M~T@v7Y zkytK$;DYVyhsg{Z4=w(9glqLgzk^b-Nn6(co-g)!_31O)UrOB%+}v@>Y}KEVd9|mW zP0aKP|HE=6=hmkM@p|jRF8jQ@!6o?5op~G6HQ~d{r4}9y(!SZcbM6rVDND6O{yOcw z^>hCSvHaVz;+^Alb=_aRz4a#+?Z0@ShVvp*Lig0qYUPoN9K{E?qyD7K`L$b1@RQGh ze)~ja*)^)2Pk5KR{M=PgH-B!fk;8qFt&s=CV|48P>$NdI+V3sAASh5_$0OYjn|tM-w0xt2$##KV_bbM1d!^7_lPy+QGYMUSr4tNq_)?HA6stZ#X{_vE>E)z;=t zoyET9$Az`FOB2s!dWxP7h915UEtT6J{U!>wG0@^m7d zByF@3pYPwbhjn3ny-vRKGx^hQO8+@{RSaG&vpxK*zpQ3svj3mNuw3isYc?qeD{h#$ z_fley2LCz_%M9JQUtb^FeXaBVj~Q$&D{sj7>;8-7oO7;nlc2QIyZsZ(ZCw9rEt&jB z$6dCvz;ROMF~xbS-&H-ipV81$xNwtf3cJzsIX{{Dp7{&>nt4PvtxBT4N{Krx{PEI` z%Dl(RI#}PoS>Cbr59j+d(}i|@eAc0=DPK#bojntBFtIsZZ=O!+B`!B@{(D*dM=lD4 zcCQdo{&4Y`eereCxBJeXGP2#yW4rcr*4aqbw^AnhAH7Q!_e$Sc`>tV{!Tzad_sZQc zONco3&vtTxI~&jIshb#kaLuSc@4vskTl!d$>PwdsL9f_vMe)WHYc8`^}tYnK;2LZR?Zsy*szEY%^5% zeE)WnsX_C0`z>#F+t`~wd*<$qOz*u%cmD-AHJ}N zs~0+a+;b0$-;7TCbzS|e95L#-Tji5P)D1g&+}bokyb*zHGf>t4T>$5o-mJ>H)lv0pS~Y)vc@t*(($#yb&Aq*$x?2{g zD(*g?AK(5UcumUpmv471-p;h@*~^0Oe4ACibKYEg@ZH+b@<8>Z;gv9SR(mC5;q!q){u)lO;L)$XFWn1i6 zzWwE$T-C1L|3+fbjb595QzRLyN<(7KJ`bBeQ|-Kx?4yIP3-7FSvtAl{#;U5}{7 zY`*HJB8AWM!ZxhmY?afO5js_)$ZPWY)s4c7eIoy}Ay z=kOg}RB0&ifBSWOymG=Ka)IZnP>KHz(!uD>R8!#m;XPa7Dc-ah`FT+!sb zf>TYblTF4lbIW1ZRa-Bwc*}gOUM76sd+pde5nN5nF4&8$?thS>I-BwTd!3t-7NNKQ zWgk4Hc>36@^cRuh84aH*qrH}gtWkbD{nu$%J}c{twX6NC?HbOWZ(zK%VCme2^?jvp zwG(PT2$&@Ol)PduYyE<2m(?5IZ>!y%eB^kKZJivO#&F=6|E@sKXBsP-KCsChOG~Ps zClMZLwN`9(9ot&2vUe5h`{%XSh|82dsh$6Cisi%a$KRYz{FX0Uu+Po@uU|ROzL!o> za^d=3PhH|{Yi^u9Z}qgWbkX-aKemW0XN#3QvNe@S{vI&Qn9^|o`-YJIPxtH18rnm@7W`lG$^mp66A71`*mTcvLO zVU_^jq9>Y(C$g`9+P(VO6NUTIehHPKe|8#gi%i#fx}aQ}$1GR#Fr?X~?$z3VMyX+PgZ-3Fz9;OfLL4)D zl0|Q2C&+8Gd2wGZni2SJ@!_QEpHuEN%{LBRH*Zhwj~}Zt4j<{9Ss%UoHLFTRzvNmE zmY#Ss5xXUG_H3DUeC4AFPm?Fs_7&XH{hcA=+4)9qw@CtX^fe{x`h-;nO%1u6PrOO} zecVv)`o;`fJ4xBc^@1!~c9H5m2M>36t4jTNfBecrj~GFftARdC3l(N}OIwVEzeY4l&Yol_} zsvY*?p<8v_`{iPFj;v#-k1$ugar(>Wql=4Q)$z~o+I&-4#Z$%RlQW~BpKnRPl&i10 z{L;Ao6}Oizb6aq>aiL|L&ettx>Y_suU*vQLhEMsqrQuHe{(D&}Yfj#gc*=TT_5EF^ zz%O!pYimES&-t@&OUJAu86wi=)@K&Pn62A(K=qbb{fbW?+H)=_8Z(4WnqFToo-O3G z<_!-+&x$VxIu2}{(a2YNWBooo-U#p9#%Z5R_E~J7CaY>1p%C0Q+t|>|_QL%@2E(Ft zwSRhUZcf~nZ1bOkBg@FwHT{yXWQ)$;Rbtsj3q@mvxA*HOXes1vHwoka}^QyGZ#c{D;9CkA6zmw2QoGF?j=@7~AOD1Q>%{w*uk&FRSTpr@Pbr(Kc z@^W*1vza=Jzo*j8I@X2H7xc0@@A;JU@u&DBfxmZpwzSSr-y~#Oy2&OeAhDI z8K!(RUv?sg;q<|UpWdjw`4OKZR%POzV9_j`RM)j?&)$t+CAz-tIdp8*q1>B4i?Wv# zCoRiwda|8ytNyKqTif%Fy-&To*LU^o4NR$1JL8Memsiz)c(B81&wiC7UbB^(ex_P& z=E?iE`Fhn+wTbI)FXAW){;*-!`rICyA1~7PFeSehTs2R}_RiIvxy{mRy`C_!Jl#06 znkVe&F3!pI8+Xh)UzaAoP4(zgMn|6pZoZ>)b#MEee$LChSnfBo`CNyGehEud%a-i% zocVU|Iz{J=C$q2h9WSo`dWVEf_A^)fPli+bN zeRS^E3cakgh527!F6fbaT+|Pc zoAKPnfnMRMLO?!7*p6)KmQ~bv&PyDv-_-f;VZ5yAvU5ejBTKrl!<+Z$l32QydKwPqebjNSCf@Jji!I{n}*7 zb+;#McrX7_`L9v7CrdB8qHza%?Z#4uZ@>HP8n&L%JS<*s*2i{`UH|Kz)JRZt=@9+G}PmBI7z5M#(ZJ+adPLv#fXa4C+;>#;yvMa;o^;#U>)E+5U(hn@% zwVFTI>Tk z#gt`Bgn734&AArr`|W{&?AE9%U~-uxA+G68G)s;8&EU)2&kS0VGD zbA^u9?+=FSndJ4v|G!(hj`cLh#S}yFkoyHu^EjezUHG5K_&z~OxTsQgYJK<49a%Ng zc^}N*S}0WXQJFovojGsMQRAQkk1owgxBXU8y+pp_5DUA*tG8ZLg0G(VWI5fDp|t+w z&l;XdN-bO(X|^|WzRCQ%sLOvnd3$tiiulztzPbET(s?WF4;{BivE017!0nA$@R8HA z)9xrGosO3OjgkAv@Cx_N!9Z(?L>W+_%aT7U3LV8NmkSDR@Jwn0}*SLvIl zT&%EY@0zkejiA{Wp6sXUyyU~LK)Yorg=OI#oqtjwByX8>95wVb-lCW zrRz7&Wz9^-4K?>2_;Bt{?CeE%_KIe&Ja_kuQr}LIL&^&+j)vJjcIx0~cG5ck?9Rpy zKO+9t>q@7sQFP_TR;+08S^rGv^o1=-i|Z*YV!RsZuZ2=m_v+ z1zt{$WRW~~`}Sm6*@kl5nhu89^HPsQH>$e<4SQHo5sP4 zm`QxM)|!ZZdj9tU1H&_J!v!2|kAf!FI_2jtVR{@Rp>^f_?}bc)1?%;n+i`YHRS|Xn zv|O=e=^cLE4+@-WZb_nFS?U>|&GZ*bnWQ57VE!bjUl-?1xP9x)M(?AsUwta-r^Gev z-6LyhVwk?BEGx{g!9%Hld&xHd&w{stRcHMic zilf%5qxF_=N^d9L%S`7CG1 zu~4p7?qOHR{jxrStGeCbU#y9^oz=1lC+k}c=5;v#I-K;eaLeoy?N=@)A2gi&SeR8# zd-49-GyA3_Y*Lwadv;0H#fPgD3aS)OGA)|xsBb;tO02VrNg;o-M2~3329-OJ@%LBx zb04&LBqf`AGAXF#lBns=X7=vCZ*J&TUpn1)w@7_)&3llyl+IJU><w_9^CR)zbKgHv^ za>wd#ygGTM8uqUx_N(vQp0H{X^TB;vw3lDB(bnqNU#wVv&hd@h+Sw-3O+f*trmXS5 zZuUXWXI0(KInP7Y>qN@G9=?5T^OD&6TXItEt*s{JN-kT!d&yR#udkTru*h%l^1jA= zetP$s7ZnF9_q9erkL54MQvMZ zyMrg1%UnoYHaGgCB*&%a_i2zR2O`tT~@PIbUe| zUp95^K9?W=C(p30b~|6+Qht7h9p@x%4mP$RTiLrRr*?}<3M>pc(Y)o#%ZWn5k34b= zo8(qC#YWb*J-KhN)meTrN6Ei-l|>2_=d`qs`b9N8Z|+esxZn`I_fzq{GxaY_13hK( zFRO|KTW-2p>1-wwrYYd+q7%c;yU?vwP$oSxgzs&TPk!eukrgTnHl8@D`&DP|dB^=K zlMVMV^GQ_9(tkETVaE=$=94B4ZNE|uM23F!Pl@Obmr&fgwAds;=j1W}Q~z|W+JqaT z*KEp~vU#CgV&vUrn!DLQy*={o%HOT^x!2eH5Okma&_XioPLK=V8}{<4Q=N;}3vsc( zZaMpL+WUpGQxm?uI&Np^?P7c?nse>hkPT7S%S28|yb*rEA^-6H3SOTd7ws0w|8TsZ z|G8qfE$^#+MmG;}zIT3i{YQGceDFWPa~f|JOkbC0oS?6!eOzbNHXU}+EE|nauW~EbX{b&2Etvfy+0>pTSXp_|X5O9O zR+h8ht1{m28}~CQ^VPQdy92Z@moP*h&uLzk)8{sQOZ#tavn{c|cdqGOxNxoPH3`R; z2Se}Q-uq(8VOyVDdArkP?N(l1&gXHI%bf4sH=)-)Re$Pdrsc`Lzax2_CGN+T5WO%Z zGp&jG@3!neIk!o43B!S<#lOvM^NM3*GnPevIjD3lCNTbW)wN9v6V|ew$&&l^Ml!_1 z_};F{#A$+?ESYM(om z7pxWL+wGe3bnlOanmYds-o=MZ|7;Pd68H7tBdh<5qUC#ozaNVIB`>yFe*=%JZ1sQP M3n`01RkLKmY&$ delta 86137 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 ZPSzALpANXP@pnDL#=n35-L&Ii008!Tu9W}) diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index c8af07ab6b3..5159326a7b3 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit c8af07ab6b354dae6a971db6d69232392ca32289 +Subproject commit 5159326a7b3d1ba29ae17a7861fa2eaa8c2c95f6 diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-history.html b/homeassistant/components/frontend/www_static/panels/ha-panel-history.html index 840f82cfd48..ac1979c3cb1 100644 --- a/homeassistant/components/frontend/www_static/panels/ha-panel-history.html +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-history.html @@ -1,4 +1,4 @@ -

\ No newline at end of file + */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.pika-single:after,.pika-single:before{content:" ";display:table}.pika-single:after{clear:both}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;filter:alpha(opacity=0);opacity:0}.pika-next,.pika-prev{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5}.pika-next:hover,.pika-prev:hover{opacity:1}.is-rtl .pika-next,.pika-prev{float:left;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==)}.is-rtl .pika-prev,.pika-next{float:right;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=)}.pika-next.is-disabled,.pika-prev.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table td,.pika-table th{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.is-inrange .pika-button{background:#D5E9F7}.is-startrange .pika-button{color:#fff;background:#6CB31D;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button,.is-outside-current-month .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.pika-button:hover{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:none;cursor:help}} \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-history.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-history.html.gz index 8d03ffbcfe493df7f49adf55fa1ea8b45f653743..589cf52692ce4accbe313b45d80b180badab17ac 100644 GIT binary patch literal 7373 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 diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html index 8a13ca837f7..c3fd949967a 100644 --- a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html @@ -1,4 +1,4 @@ - \ No newline at end of file + */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.pika-single:after,.pika-single:before{content:" ";display:table}.pika-single:after{clear:both}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;filter:alpha(opacity=0);opacity:0}.pika-next,.pika-prev{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5}.pika-next:hover,.pika-prev:hover{opacity:1}.is-rtl .pika-next,.pika-prev{float:left;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==)}.is-rtl .pika-prev,.pika-next{float:right;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=)}.pika-next.is-disabled,.pika-prev.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table td,.pika-table th{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.is-inrange .pika-button{background:#D5E9F7}.is-startrange .pika-button{color:#fff;background:#6CB31D;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button,.is-outside-current-month .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.pika-button:hover{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:none;cursor:help}} \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html.gz index ebe5f5edee4ad205871b147f08e1adb0e417b40b..2844f1ce8e084fbfb3ab615bd4fe2e20f1210d2e 100644 GIT binary patch literal 7904 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 literal 7322 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 Xe{8P3{fGVEKlW3T%uhe}XJ7yTzP<`T 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 38dd2b6e961..42097a123a2 100644 --- a/homeassistant/components/frontend/www_static/panels/ha-panel-map.html +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-map.html @@ -75,8 +75,15 @@ Polymer({ }, fitMap: function () { - var bounds = new window.L.latLngBounds( - this._mapItems.map(function (item) { return item.getLatLng(); })); + var bounds; + + if (this._mapItems.length === 0) { + bounds = new window.L.latLngBounds( + [window.L.latLng(this.locationGPS.latitude, this.locationGPS.longitude)]); + } else { + bounds = new window.L.latLngBounds( + this._mapItems.map(function (item) { return item.getLatLng(); })); + } this._map.fitBounds(bounds.pad(0.5)); }, 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 56a98ed3431887e2fdb5c9f724ac501f8244834b..f542d52730d373d1ac0f41b2ec48a07c830b33a1 100644 GIT binary patch delta 734 zcmbP#jp^_;CU*I54vw<-T^rfUR@4`H{H#n63P|{1Ao^2dhL7683p{>Tm{MFMgOo4N zX*zrQ&HgLYS`Hi7vwk0U{2d$_b7A|75MPr8EQjk;RnA?p@E874RKYGBV!|n3 znRc~sL(dhC8^2C!6!pIG>Pg$+k-qog1BIm32R>j3s$}9;1FUJG}Fpz=l9)f zVLA2i#deR~#^2W{h>9tIt?GWtExqbNZRanK>>;@)jGuxSkat@t(Ina>Lt?v*a`c*OmU;`0Cq1$z4j{ z$|T;f{8M}Ayd#(Mo}xAH+}=lDw4%32)P-F?&|sCcoNvQg72{83`mt!> zv3}=w4Zd%3b3d)O(o&FN?RlZ6{9a1nm#Kl!0{62^H>*iCYSu_wD+{JtcKF}gVDjbo zXJ0{u><4Dr&h>uRzezvdmLvOTmQdg{&-Y3nn%XWuJl-pBb$JgHpP`4RBFmF(*%X(b z|8q~QX>7g_&3gO(|1X#M{o4=c{$3ptQ~66Q{vEs4*4?r^>>U4GcyyNizOsb##}o$E z_hwfWb=wrIA`+~Bd^>CGkmz(#NwZ69Z_V$QSC`kU1SITFs3+Y_{WX-c`lIjVtU|H~rc0u8 zmxOUIHJG?+;=4x6X4NMhBN!A`We&-sq zJrvSrIcPRnxALpj>)gdI&ZidVnljJex;%OA)XmX%W^UNn|4%+eR(XS}w-2#gxbS_2aTUd15cwuQXS+peUd*Xzv3R5!AE6s_$tN(JH zRmG0mTNko?`Sf|g_Ev9Ab%pk>^=~t0e=R&v7#F6WsBAoeqmrRfX>$9jHBB5mJ2PtP zL$2`2&#Edc(rRDanZMh3`msq_9G5xj)#D$8sHC#mG5?sFV8r=UK_pN+BU+o`l6n?Mtp{dRJLCP)uxtVc{ z>7q-PcrYH?TF+;+=+A$%lxqhZUsN}}eP93UW&85O54Zgd)vB?n?biR!JoVOHzBXo- zdZlChms7GXu{^JCV9K8TYQh)&4nDCPeEVuY^90P0+v3r+$V>lzUDel@^7q#+FnYax z?v0X%3s>))xcFsr$Fa=zATEtX55ugv9>2HQ{Y`K~Y(L)(q3Z8C^|v=lzrD^|{(3)y za_pz$g=vdpR|uT$51ae@eT-T6BJT-)%dcJg*!>ijANd!2v#Z|pz3ce#Ij zkJ-KbHS46`CjF~Cc|&U@uhUD{B^FD52cJvewA8rf)W@{;$%k!6zCSp~9IIr(x6RX3 ziSMBkTgDAOp>K7!dmML6;$mE-es+$R-1bvdw>3B?TuO~T%dqUw7w`0^Z^X*ga^5Wd O&n;CmRq0R*69WLv(pMY+ diff --git a/homeassistant/components/frontend/www_static/service_worker.js b/homeassistant/components/frontend/www_static/service_worker.js index b48eca44937..3c0d0ab8b0d 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=[["/","133b753ac529b641fb200f391aea789f"],["/frontend/panels/dev-event-f19840b9a6a46f57cb064b384e1353f5.html","21cf247351b95fdd451c304e308a726c"],["/frontend/panels/dev-info-3765a371478cc66d677cf6dcc35267c6.html","dd614f2ee5e09a9dfd7f98822a55893d"],["/frontend/panels/dev-service-1d223225c1c75083738033895ea3e4b5.html","3c6d75bd5b2e38bb73391f71ea338496"],["/frontend/panels/dev-state-8257d99a38358a150eafdb23fa6727e0.html","3cf24bb7e92c759b35a74cf641ed80cb"],["/frontend/panels/dev-template-cbb251acabd5e7431058ed507b70522b.html","edd6ef67f4ab763f9d3dd7d3aa6f4007"],["/frontend/panels/map-3b0ca63286cbe80f27bd36dbc2434e89.html","d22eee1c33886ce901851ccd35cb43ed"],["/static/core-90c16d2f2c5d52203e2fd5fa2b1ae19c.js","fe8237fbcd8a865c436369aa35125476"],["/static/frontend-c8e670c6c9f7c0ea3b971b92ba9013db.html","a29570556980c45b291bfb709be249b6"],["/static/mdi-5bb2f1717206bad0d187c2633062c575.html","fd915b78a66e34026eb1eebfe58157d8"],["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","89313f9f2126ddea722150f8154aca03"]],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,n;for(a=0;a~ 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 Date: Thu, 26 Jan 2017 22:59:32 -0800 Subject: [PATCH 170/191] Update MDI --- homeassistant/components/frontend/version.py | 2 +- .../components/frontend/www_static/mdi.html | 2 +- .../frontend/www_static/mdi.html.gz | Bin 184175 -> 188451 bytes 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 1730078da87..12566881be8 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -3,7 +3,7 @@ FINGERPRINTS = { "core.js": "769f3fdd4e04b34bd66c7415743cf7b5", "frontend.html": "d48d9a13f7d677e59b1d22c6db051207", - "mdi.html": "5bb2f1717206bad0d187c2633062c575", + "mdi.html": "7a0f14bbf3822449f9060b9c53bd7376", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "panels/ha-panel-dev-event.html": "f19840b9a6a46f57cb064b384e1353f5", "panels/ha-panel-dev-info.html": "3765a371478cc66d677cf6dcc35267c6", diff --git a/homeassistant/components/frontend/www_static/mdi.html b/homeassistant/components/frontend/www_static/mdi.html index ce1d5d24574..44dfedbfb39 100644 --- a/homeassistant/components/frontend/www_static/mdi.html +++ b/homeassistant/components/frontend/www_static/mdi.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/mdi.html.gz b/homeassistant/components/frontend/www_static/mdi.html.gz index 774ce87fa36a21fb769b21c4d52f87df9872f890..b540bf33f7d30d002347b3299f2b44d6d34f035c 100644 GIT binary patch literal 188451 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
95I-5=e*z20_x80Te)z>l*QbIQ&> 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ǨYrp)T@nzl3Uy~ZYZN49~`%GQ??8AwxHRa~d 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 AkpKVy literal 184175 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

+6_}T2fuW3xU`zj{!m4n@AW4+ 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 Xmw(q+*>!Z?e{RJk(No-Ba4-M>#qjb& From 86dfa7ad258e7ba02c973ffffb939d122be75ea6 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Fri, 27 Jan 2017 08:08:08 +0100 Subject: [PATCH 171/191] [switch.flux] Allow disabling setting the brightness (#5407) * flux: allow disabling setting the brightness * Use separate boolean for disabling brightness adjustment * Update flux.py --- homeassistant/components/switch/flux.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/flux.py b/homeassistant/components/switch/flux.py index 8f3ff769a06..354e3b409db 100644 --- a/homeassistant/components/switch/flux.py +++ b/homeassistant/components/switch/flux.py @@ -32,6 +32,7 @@ CONF_START_CT = 'start_colortemp' CONF_SUNSET_CT = 'sunset_colortemp' CONF_STOP_CT = 'stop_colortemp' CONF_BRIGHTNESS = 'brightness' +CONF_DISABLE_BRIGTNESS_ADJUST = 'disable_brightness_adjust' CONF_MODE = 'mode' MODE_XY = 'xy' @@ -52,6 +53,7 @@ PLATFORM_SCHEMA = vol.Schema({ vol.All(vol.Coerce(int), vol.Range(min=1000, max=40000)), vol.Optional(CONF_BRIGHTNESS): vol.All(vol.Coerce(int), vol.Range(min=0, max=255)), + vol.Optional(CONF_DISABLE_BRIGTNESS_ADJUST): cv.boolean, vol.Optional(CONF_MODE, default=DEFAULT_MODE): vol.Any(MODE_XY, MODE_MIRED) }) @@ -88,10 +90,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): sunset_colortemp = config.get(CONF_SUNSET_CT) stop_colortemp = config.get(CONF_STOP_CT) brightness = config.get(CONF_BRIGHTNESS) + disable_brightness_adjust = config.get(CONF_DISABLE_BRIGTNESS_ADJUST) mode = config.get(CONF_MODE) flux = FluxSwitch(name, hass, False, lights, start_time, stop_time, start_colortemp, sunset_colortemp, stop_colortemp, - brightness, mode) + brightness, disable_brightness_adjust, mode) add_devices([flux]) def update(call=None): @@ -106,7 +109,7 @@ class FluxSwitch(SwitchDevice): def __init__(self, name, hass, state, lights, start_time, stop_time, start_colortemp, sunset_colortemp, stop_colortemp, - brightness, mode): + brightness, disable_brightness_adjust, mode): """Initialize the Flux switch.""" self._name = name self.hass = hass @@ -118,6 +121,7 @@ class FluxSwitch(SwitchDevice): self._sunset_colortemp = sunset_colortemp self._stop_colortemp = stop_colortemp self._brightness = brightness + self._disable_brightness_adjust = disable_brightness_adjust self._mode = mode self.unsub_tracker = None @@ -192,6 +196,8 @@ class FluxSwitch(SwitchDevice): temp = self._sunset_colortemp + temp_offset x_val, y_val, b_val = color_RGB_to_xy(*color_temperature_to_rgb(temp)) brightness = self._brightness if self._brightness else b_val + if self._disable_brightness_adjust: + brightness = None if self._mode == MODE_XY: set_lights_xy(self.hass, self._lights, x_val, y_val, brightness) From 5a7a84fad1ca93988ec97b7461590e307ce4472c Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 27 Jan 2017 08:27:29 +0100 Subject: [PATCH 172/191] Device Tracker for Linksys Access Points (#4933) * Implementation for Linksys Access Points * update .coveragerc * update requirements * add default timeout of 10sec * address lint issues --- .coveragerc | 1 + .../components/device_tracker/linksys_ap.py | 108 ++++++++++++++++++ requirements_all.txt | 1 + 3 files changed, 110 insertions(+) create mode 100644 homeassistant/components/device_tracker/linksys_ap.py diff --git a/.coveragerc b/.coveragerc index 594306bb1db..d5536795e9c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -167,6 +167,7 @@ omit = homeassistant/components/device_tracker/fritz.py homeassistant/components/device_tracker/gpslogger.py homeassistant/components/device_tracker/icloud.py + homeassistant/components/device_tracker/linksys_ap.py homeassistant/components/device_tracker/luci.py homeassistant/components/device_tracker/netgear.py homeassistant/components/device_tracker/nmap_tracker.py diff --git a/homeassistant/components/device_tracker/linksys_ap.py b/homeassistant/components/device_tracker/linksys_ap.py new file mode 100644 index 00000000000..29246f79fc5 --- /dev/null +++ b/homeassistant/components/device_tracker/linksys_ap.py @@ -0,0 +1,108 @@ +""" +Support for Linksys Access Points. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.linksys_ap/ +""" +import base64 +import logging +import threading +from datetime import timedelta + +import requests +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA +from homeassistant.const import (CONF_HOST, CONF_PASSWORD, CONF_USERNAME, + CONF_VERIFY_SSL) +from homeassistant.util import Throttle + +# Return cached results if last scan was less then this time ago. +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) +INTERFACES = 2 +DEFAULT_TIMEOUT = 10 + +REQUIREMENTS = ['beautifulsoup4==4.5.3'] + +_LOGGER = logging.getLogger(__name__) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, +}) + + +def get_scanner(hass, config): + """Validate the configuration and return a Linksys AP scanner.""" + try: + return LinksysAPDeviceScanner(config[DOMAIN]) + except ConnectionError: + return None + + +class LinksysAPDeviceScanner(object): + """This class queries a Linksys Access Point.""" + + def __init__(self, config): + """Initialize the scanner.""" + self.host = config[CONF_HOST] + self.username = config[CONF_USERNAME] + self.password = config[CONF_PASSWORD] + self.verify_ssl = config[CONF_VERIFY_SSL] + + self.lock = threading.Lock() + self.last_results = [] + + # Check if the access point is accessible + response = self._make_request() + if not response.status_code == 200: + raise ConnectionError('Cannot connect to Linksys Access Point') + + def scan_devices(self): + """Scan for new devices and return a list with found device IDs.""" + self._update_info() + + return self.last_results + + # pylint: disable=no-self-use + def get_device_name(self, mac): + """ + Return the name (if known) of the device. + + Linksys does not provide an API to get a name for a device, + so we just return None + """ + return None + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """Check for connected devices.""" + from bs4 import BeautifulSoup as BS + + with self.lock: + _LOGGER.info('Checking Linksys AP') + + self.last_results = [] + for interface in range(INTERFACES): + request = self._make_request(interface) + self.last_results.extend( + [x.find_all('td')[1].text + for x in BS(request.content, "html.parser") + .find_all(class_='section-row')] + ) + + return True + + def _make_request(self, unit=0): + # No, the '&&' is not a typo - this is expected by the web interface. + login = base64.b64encode(bytes(self.username, 'utf8')).decode('ascii') + pwd = base64.b64encode(bytes(self.password, 'utf8')).decode('ascii') + return requests.get( + 'https://%s/StatusClients.htm&&unit=%s&vap=0' % (self.host, unit), + timeout=DEFAULT_TIMEOUT, + verify=self.verify_ssl, + cookies={'LoginName': login, + 'LoginPWD': pwd}) diff --git a/requirements_all.txt b/requirements_all.txt index de4a2cbeab3..247a12a455c 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -59,6 +59,7 @@ astral==1.3.3 # homeassistant.components.sensor.linux_battery batinfo==0.4.2 +# homeassistant.components.device_tracker.linksys_ap # homeassistant.components.sensor.hydroquebec # homeassistant.components.sensor.scrape beautifulsoup4==4.5.3 From 1d4e9671067aae0611b1d0feec6b5e522a8fe38d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 27 Jan 2017 15:37:16 +0100 Subject: [PATCH 173/191] sonos set coordinator after join/unjoin (#5584) * sonos set coordinator after join/unjoin * fix unittest --- homeassistant/components/media_player/sonos.py | 11 ++++++++--- tests/components/media_player/test_sonos.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index c675210a075..38408914448 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -71,7 +71,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) SONOS_SCHEMA = vol.Schema({ - ATTR_ENTITY_ID: cv.entity_ids, + vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, }) SONOS_JOIN_SCHEMA = SONOS_SCHEMA.extend({ @@ -923,17 +923,22 @@ class SonosDevice(MediaPlayerDevice): def join(self, master): """Join the player to a group.""" - coord = [device.soco_device for device in self.hass.data[DATA_SONOS] + coord = [device for device in self.hass.data[DATA_SONOS] if device.entity_id == master] if coord and master != self.entity_id: - self._player.join(coord[0]) + coord = coord[0] + if coord.soco_device.group.coordinator != coord.soco_device: + coord.soco_device.unjoin() + self._player.join(coord.soco_device) + self._coordinator = coord else: _LOGGER.error("Master not found %s", master) def unjoin(self): """Unjoin the player from a group.""" self._player.unjoin() + self._coordinator = None def snapshot(self, with_group=True): """Snapshot the player.""" diff --git a/tests/components/media_player/test_sonos.py b/tests/components/media_player/test_sonos.py index e494d3242fa..3d80536fb82 100644 --- a/tests/components/media_player/test_sonos.py +++ b/tests/components/media_player/test_sonos.py @@ -251,7 +251,7 @@ class TestSonosMediaPlayer(unittest.TestCase): device_master = mock.MagicMock() device_master.entity_id = "media_player.test" - device_master.soco_device = device + device_master.soco_device = mock.MagicMock() self.hass.data[sonos.DATA_SONOS].append(device_master) join_mock.return_value = True From b732174def5894708e90c7d8708e971410a43afd Mon Sep 17 00:00:00 2001 From: Duoxilian Date: Fri, 27 Jan 2017 10:57:18 -0600 Subject: [PATCH 174/191] Hold mode (#5586) * Initial commit of hold_mode feature. * Added deprecation warning for climate.away_mode * Add tests to demo environment. --- homeassistant/components/climate/__init__.py | 60 +++++++ homeassistant/components/climate/demo.py | 21 ++- homeassistant/components/climate/ecobee.py | 154 ++++++++++++------ .../components/climate/services.yaml | 12 ++ homeassistant/helpers/state.py | 11 +- tests/components/climate/test_demo.py | 21 +++ 6 files changed, 223 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 3058258c75a..0cd9bbe17d3 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -32,6 +32,7 @@ SERVICE_SET_AWAY_MODE = "set_away_mode" SERVICE_SET_AUX_HEAT = "set_aux_heat" SERVICE_SET_TEMPERATURE = "set_temperature" SERVICE_SET_FAN_MODE = "set_fan_mode" +SERVICE_SET_HOLD_MODE = "set_hold_mode" SERVICE_SET_OPERATION_MODE = "set_operation_mode" SERVICE_SET_SWING_MODE = "set_swing_mode" SERVICE_SET_HUMIDITY = "set_humidity" @@ -56,6 +57,7 @@ ATTR_CURRENT_HUMIDITY = "current_humidity" ATTR_HUMIDITY = "humidity" ATTR_MAX_HUMIDITY = "max_humidity" ATTR_MIN_HUMIDITY = "min_humidity" +ATTR_HOLD_MODE = "hold_mode" ATTR_OPERATION_MODE = "operation_mode" ATTR_OPERATION_LIST = "operation_list" ATTR_SWING_MODE = "swing_mode" @@ -93,6 +95,10 @@ SET_FAN_MODE_SCHEMA = vol.Schema({ vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_FAN_MODE): cv.string, }) +SET_HOLD_MODE_SCHEMA = vol.Schema({ + vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_HOLD_MODE): cv.string, +}) SET_OPERATION_MODE_SCHEMA = vol.Schema({ vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_OPERATION_MODE): cv.string, @@ -116,9 +122,23 @@ def set_away_mode(hass, away_mode, entity_id=None): if entity_id: data[ATTR_ENTITY_ID] = entity_id + _LOGGER.warning( + 'This service has been deprecated; use climate.set_hold_mode') hass.services.call(DOMAIN, SERVICE_SET_AWAY_MODE, data) +def set_hold_mode(hass, hold_mode, entity_id=None): + """Set new hold mode.""" + data = { + ATTR_HOLD_MODE: hold_mode + } + + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.services.call(DOMAIN, SERVICE_SET_HOLD_MODE, data) + + def set_aux_heat(hass, aux_heat, entity_id=None): """Turn all or specified climate devices auxillary heater on.""" data = { @@ -229,6 +249,8 @@ def async_setup(hass, config): SERVICE_SET_AWAY_MODE, ATTR_AWAY_MODE) return + _LOGGER.warning( + 'This service has been deprecated; use climate.set_hold_mode') for climate in target_climate: if away_mode: yield from climate.async_turn_away_mode_on() @@ -242,6 +264,23 @@ def async_setup(hass, config): descriptions.get(SERVICE_SET_AWAY_MODE), schema=SET_AWAY_MODE_SCHEMA) + @asyncio.coroutine + def async_hold_mode_set_service(service): + """Set hold mode on target climate devices.""" + target_climate = component.async_extract_from_service(service) + + hold_mode = service.data.get(ATTR_HOLD_MODE) + + for climate in target_climate: + yield from climate.async_set_hold_mode(hold_mode) + + yield from _async_update_climate(target_climate) + + hass.services.async_register( + DOMAIN, SERVICE_SET_HOLD_MODE, async_hold_mode_set_service, + descriptions.get(SERVICE_SET_HOLD_MODE), + schema=SET_HOLD_MODE_SCHEMA) + @asyncio.coroutine def async_aux_heat_set_service(service): """Set auxillary heater on target climate devices.""" @@ -446,6 +485,10 @@ class ClimateDevice(Entity): if self.operation_list: data[ATTR_OPERATION_LIST] = self.operation_list + is_hold = self.current_hold_mode + if is_hold is not None: + data[ATTR_HOLD_MODE] = is_hold + swing_mode = self.current_swing_mode if swing_mode is not None: data[ATTR_SWING_MODE] = swing_mode @@ -517,6 +560,11 @@ class ClimateDevice(Entity): """Return true if away mode is on.""" return None + @property + def current_hold_mode(self): + """Return the current hold mode, e.g., home, away, temp.""" + return None + @property def is_aux_heat_on(self): """Return true if aux heater.""" @@ -626,6 +674,18 @@ class ClimateDevice(Entity): return self.hass.loop.run_in_executor( None, self.turn_away_mode_off) + def set_hold_mode(self, hold_mode): + """Set new target hold mode.""" + raise NotImplementedError() + + def async_set_hold_mode(self, hold_mode): + """Set new target hold mode. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, self.set_hold_mode, hold_mode) + def turn_aux_heat_on(self): """Turn auxillary heater on.""" raise NotImplementedError() diff --git a/homeassistant/components/climate/demo.py b/homeassistant/components/climate/demo.py index 04053febf90..a66873cbc63 100644 --- a/homeassistant/components/climate/demo.py +++ b/homeassistant/components/climate/demo.py @@ -12,11 +12,11 @@ from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Demo climate devices.""" add_devices([ - DemoClimate("HeatPump", 68, TEMP_FAHRENHEIT, None, 77, "Auto Low", - None, None, "Auto", "heat", None, None, None), - DemoClimate("Hvac", 21, TEMP_CELSIUS, True, 22, "On High", + DemoClimate("HeatPump", 68, TEMP_FAHRENHEIT, None, None, 77, + "Auto Low", None, None, "Auto", "heat", None, None, None), + DemoClimate("Hvac", 21, TEMP_CELSIUS, True, None, 22, "On High", 67, 54, "Off", "cool", False, None, None), - DemoClimate("Ecobee", None, TEMP_CELSIUS, None, 23, "Auto Low", + DemoClimate("Ecobee", None, TEMP_CELSIUS, None, None, 23, "Auto Low", None, None, "Auto", "auto", None, 24, 21) ]) @@ -25,7 +25,7 @@ class DemoClimate(ClimateDevice): """Representation of a demo climate device.""" def __init__(self, name, target_temperature, unit_of_measurement, - away, current_temperature, current_fan_mode, + away, hold, current_temperature, current_fan_mode, target_humidity, current_humidity, current_swing_mode, current_operation, aux, target_temp_high, target_temp_low): """Initialize the climate device.""" @@ -34,6 +34,7 @@ class DemoClimate(ClimateDevice): self._target_humidity = target_humidity self._unit_of_measurement = unit_of_measurement self._away = away + self._hold = hold self._current_temperature = current_temperature self._current_humidity = current_humidity self._current_fan_mode = current_fan_mode @@ -106,6 +107,11 @@ class DemoClimate(ClimateDevice): """Return if away mode is on.""" return self._away + @property + def current_hold_mode(self): + """Return hold mode setting.""" + return self._hold + @property def is_aux_heat_on(self): """Return true if away mode is on.""" @@ -171,6 +177,11 @@ class DemoClimate(ClimateDevice): self._away = False self.update_ha_state() + def set_hold_mode(self, hold): + """Update hold mode on.""" + self._hold = hold + self.update_ha_state() + def turn_aux_heat_on(self): """Turn away auxillary heater on.""" self._aux = True diff --git a/homeassistant/components/climate/ecobee.py b/homeassistant/components/climate/ecobee.py index f820d69754d..dcee6d9ce31 100644 --- a/homeassistant/components/climate/ecobee.py +++ b/homeassistant/components/climate/ecobee.py @@ -183,6 +183,19 @@ class Thermostat(ClimateDevice): else: return STATE_OFF + @property + def current_hold_mode(self): + """Return current hold mode.""" + if self.is_away_mode_on: + hold = 'away' + elif self.is_home_mode_on: + hold = 'home' + elif self.is_temp_hold_on(): + hold = 'temp' + else: + hold = None + return hold + @property def current_operation(self): """Return current operation.""" @@ -236,30 +249,94 @@ class Thermostat(ClimateDevice): "fan_min_on_time": self.fan_min_on_time } + def is_vacation_on(self): + """Return true if vacation mode is on.""" + events = self.thermostat['events'] + return any(event['type'] == 'vacation' and event['running'] + for event in events) + + def is_temp_hold_on(self): + """Return true if temperature hold is on.""" + events = self.thermostat['events'] + return any(event['type'] == 'hold' and event['running'] + for event in events) + @property def is_away_mode_on(self): """Return true if away mode is on.""" - mode = self.mode events = self.thermostat['events'] - for event in events: - if event['holdClimateRef'] == 'away' or \ - event['type'] == 'autoAway': - mode = "away" - break - return 'away' in mode + return any(event['holdClimateRef'] == 'away' or + event['type'] == 'autoAway' + for event in events) def turn_away_mode_on(self): """Turn away on.""" - if self.hold_temp: - self.data.ecobee.set_climate_hold(self.thermostat_index, - "away", "indefinite") - else: - self.data.ecobee.set_climate_hold(self.thermostat_index, "away") + self.data.ecobee.set_climate_hold(self.thermostat_index, + "away", self.hold_preference()) self.update_without_throttle = True def turn_away_mode_off(self): """Turn away off.""" - self.data.ecobee.resume_program(self.thermostat_index) + self.set_hold_mode(None) + + @property + def is_home_mode_on(self): + """Return true if home mode is on.""" + events = self.thermostat['events'] + return any(event['holdClimateRef'] == 'home' or + event['type'] == 'autoHome' + for event in events) + + def turn_home_mode_on(self): + """Turn home on.""" + self.data.ecobee.set_climate_hold(self.thermostat_index, + "home", self.hold_preference()) + self.update_without_throttle = True + + def set_hold_mode(self, hold_mode): + """Set hold mode (away, home, temp).""" + hold = self.current_hold_mode + + if hold == hold_mode: + return + elif hold_mode == 'away': + self.turn_away_mode_on() + elif hold_mode == 'home': + self.turn_home_mode_on() + elif hold_mode == 'temp': + self.set_temp_hold(int(self.current_temperature)) + else: + self.data.ecobee.resume_program(self.thermostat_index) + self.update_without_throttle = True + + def set_auto_temp_hold(self, heat_temp, cool_temp): + """Set temperature hold in auto mode.""" + self.data.ecobee.set_hold_temp(self.thermostat_index, cool_temp, + heat_temp, self.hold_preference()) + _LOGGER.debug("Setting ecobee hold_temp to: heat=%s, is=%s, " + "cool=%s, is=%s", heat_temp, isinstance( + heat_temp, (int, float)), cool_temp, + isinstance(cool_temp, (int, float))) + + self.update_without_throttle = True + + def set_temp_hold(self, temp): + """Set temperature hold in modes other than auto.""" + # Set arbitrary range when not in auto mode + if self.current_operation == STATE_HEAT: + heat_temp = temp + cool_temp = temp + 20 + elif self.current_operation == STATE_COOL: + heat_temp = temp - 20 + cool_temp = temp + + self.data.ecobee.set_hold_temp(self.thermostat_index, cool_temp, + heat_temp, self.hold_preference()) + _LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, " + "cool=%s, is=%s", heat_temp, isinstance( + heat_temp, (int, float)), cool_temp, + isinstance(cool_temp, (int, float))) + self.update_without_throttle = True def set_temperature(self, **kwargs): @@ -268,33 +345,14 @@ class Thermostat(ClimateDevice): high_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) temp = kwargs.get(ATTR_TEMPERATURE) - if self.current_operation == STATE_HEAT and temp is not None: - low_temp = temp - high_temp = temp + 20 - elif self.current_operation == STATE_COOL and temp is not None: - low_temp = temp - 20 - high_temp = temp - if low_temp is None and high_temp is None: + if self.current_operation == STATE_AUTO and low_temp is not None \ + and high_temp is not None: + self.set_auto_temp_hold(int(low_temp), int(high_temp)) + elif temp is not None: + self.set_temp_hold(int(temp)) + else: _LOGGER.error( 'Missing valid arguments for set_temperature in %s', kwargs) - return - - low_temp = int(low_temp) - high_temp = int(high_temp) - - if self.hold_temp: - self.data.ecobee.set_hold_temp( - self.thermostat_index, high_temp, low_temp, "indefinite") - else: - self.data.ecobee.set_hold_temp( - self.thermostat_index, high_temp, low_temp) - - _LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, " - "high=%s, is=%s", low_temp, isinstance( - low_temp, (int, float)), high_temp, - isinstance(high_temp, (int, float))) - - self.update_without_throttle = True def set_operation_mode(self, operation_mode): """Set HVAC mode (auto, auxHeatOnly, cool, heat, off).""" @@ -313,15 +371,19 @@ class Thermostat(ClimateDevice): str(resume_all).lower()) self.update_without_throttle = True - # Home and Sleep mode aren't used in UI yet: + def hold_preference(self): + """Return user preference setting for hold time.""" + # Values returned from thermostat are 'useEndTime4hour', + # 'useEndTime2hour', 'nextTransition', 'indefinite', 'askMe' + default = self.thermostat['settings']['holdAction'] + if default == 'nextTransition': + return default + elif default == 'indefinite': + return default + else: + return 'nextTransition' - # def turn_home_mode_on(self): - # """ Turns home mode on. """ - # self.data.ecobee.set_climate_hold(self.thermostat_index, "home") - - # def turn_home_mode_off(self): - # """ Turns home mode off. """ - # self.data.ecobee.resume_program(self.thermostat_index) + # Sleep mode isn't used in UI yet: # def turn_sleep_mode_on(self): # """ Turns sleep mode on. """ diff --git a/homeassistant/components/climate/services.yaml b/homeassistant/components/climate/services.yaml index 801052b31ff..899a3dcfe33 100644 --- a/homeassistant/components/climate/services.yaml +++ b/homeassistant/components/climate/services.yaml @@ -22,6 +22,18 @@ set_away_mode: description: New value of away mode example: true +set_hold_mode: + description: Turn hold mode for climate device + + fields: + entity_id: + description: Name(s) of entities to change + example: 'climate.kitchen' + + hold_mode: + description: New value of hold mode + example: 'away' + set_temperature: description: Set target temperature of climate device diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 3be344e7d9d..ea33d27a814 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -16,11 +16,11 @@ from homeassistant.components.sun import ( from homeassistant.components.switch.mysensors import ( ATTR_IR_CODE, SERVICE_SEND_IR_CODE) from homeassistant.components.climate import ( - ATTR_AUX_HEAT, ATTR_AWAY_MODE, ATTR_FAN_MODE, ATTR_HUMIDITY, - ATTR_OPERATION_MODE, ATTR_SWING_MODE, - SERVICE_SET_AUX_HEAT, SERVICE_SET_AWAY_MODE, SERVICE_SET_FAN_MODE, - SERVICE_SET_HUMIDITY, SERVICE_SET_OPERATION_MODE, SERVICE_SET_SWING_MODE, - SERVICE_SET_TEMPERATURE) + ATTR_AUX_HEAT, ATTR_AWAY_MODE, ATTR_FAN_MODE, ATTR_HOLD_MODE, + ATTR_HUMIDITY, ATTR_OPERATION_MODE, ATTR_SWING_MODE, + SERVICE_SET_AUX_HEAT, SERVICE_SET_AWAY_MODE, SERVICE_SET_HOLD_MODE, + SERVICE_SET_FAN_MODE, SERVICE_SET_HUMIDITY, SERVICE_SET_OPERATION_MODE, + SERVICE_SET_SWING_MODE, SERVICE_SET_TEMPERATURE) from homeassistant.components.climate.ecobee import ( ATTR_FAN_MIN_ON_TIME, SERVICE_SET_FAN_MIN_ON_TIME, ATTR_RESUME_ALL, SERVICE_RESUME_PROGRAM) @@ -57,6 +57,7 @@ SERVICE_ATTRIBUTES = { SERVICE_SET_TEMPERATURE: [ATTR_TEMPERATURE], SERVICE_SET_HUMIDITY: [ATTR_HUMIDITY], SERVICE_SET_SWING_MODE: [ATTR_SWING_MODE], + SERVICE_SET_HOLD_MODE: [ATTR_HOLD_MODE], SERVICE_SET_OPERATION_MODE: [ATTR_OPERATION_MODE], SERVICE_SET_AUX_HEAT: [ATTR_AUX_HEAT], SERVICE_SELECT_SOURCE: [ATTR_INPUT_SOURCE], diff --git a/tests/components/climate/test_demo.py b/tests/components/climate/test_demo.py index 518e4ca2c81..898f6ba2df6 100644 --- a/tests/components/climate/test_demo.py +++ b/tests/components/climate/test_demo.py @@ -208,6 +208,27 @@ class TestDemoClimate(unittest.TestCase): state = self.hass.states.get(ENTITY_CLIMATE) self.assertEqual('off', state.attributes.get('away_mode')) + def test_set_hold_mode_home(self): + """Test setting the hold mode home.""" + climate.set_hold_mode(self.hass, 'home', ENTITY_ECOBEE) + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_ECOBEE) + self.assertEqual('home', state.attributes.get('hold_mode')) + + def test_set_hold_mode_away(self): + """Test setting the hold mode away.""" + climate.set_hold_mode(self.hass, 'away', ENTITY_ECOBEE) + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_ECOBEE) + self.assertEqual('away', state.attributes.get('hold_mode')) + + def test_set_hold_mode_none(self): + """Test setting the hold mode off/false.""" + climate.set_hold_mode(self.hass, None, ENTITY_ECOBEE) + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_ECOBEE) + self.assertEqual(None, state.attributes.get('hold_mode')) + def test_set_aux_heat_bad_attr(self): """Test setting the auxillary heater without required attribute.""" state = self.hass.states.get(ENTITY_CLIMATE) From db85e2bc2a573579208e3ded4a62d64115b15664 Mon Sep 17 00:00:00 2001 From: fakezeta Date: Sat, 28 Jan 2017 00:51:30 +0100 Subject: [PATCH 175/191] Checking message if valid url or a string for TTS --- .../components/notify/twilio_call.py | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index 8bc9185e789..37068d6e220 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -7,6 +7,7 @@ https://home-assistant.io/components/notify.twilio_call/ import logging import urllib + import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -41,6 +42,57 @@ def get_service(hass, config, discovery_info=None): config[CONF_FROM_NUMBER]) +def is_validurl(url): + """Check if the passed url is valid using dperini regex.""" + import re + + ip_middle_oct = u"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))" + ip_last_oct = u"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))" + + regex = re.compile( + u"^" + # protocol identifier + u"(?:(?:https?|ftp)://)" + # user:pass authentication + u"(?:\S+(?::\S*)?@)?" + u"(?:" + u"(?P" + # IP address exclusion + # private & local networks + u"(?:(?:10|127)" + ip_middle_oct + u"{2}" + ip_last_oct + u")|" + u"(?:(?:169\.254|192\.168)" + ip_middle_oct + ip_last_oct + u")|" + u"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_oct + ip_last_oct + u"))" + u"|" + # IP address dotted notation octets + # excludes loopback network 0.0.0.0 + # excludes reserved space >= 224.0.0.0 + # excludes network & broadcast addresses + # (first & last IP address of each class) + u"(?P" + u"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])" + u"" + ip_middle_oct + u"{2}" + u"" + ip_last_oct + u")" + u"|" + # host name + u"(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)" + # domain name + u"(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*" + # TLD identifier + u"(?:\.(?:[a-z\u00a1-\uffff]{2,}))" + u")" + # port number + u"(?::\d{2,5})?" + # resource path + u"(?:/\S*)?" + # query string + u"(?:\?\S*)?" + u"$", + re.UNICODE | re.IGNORECASE + ) + + return regex.match(url) + + class TwilioCallNotificationService(BaseNotificationService): """Implement the notification service for the Twilio Call service.""" @@ -57,9 +109,13 @@ class TwilioCallNotificationService(BaseNotificationService): _LOGGER.info("At least 1 target is required") return - for target in targets: - twimlet_url = 'http://twimlets.com/message?Me=' + if is_validurl(message): + twimlet_url = message + else: + twimlet_url = 'http://twimlets.com/message?Message=' twimlet_url += urllib.parse.quote(message, safe='') + + for target in targets: self.client.calls.create(to=target, url=twimlet_url, from_=self.from_number) From 549c3b2c84d60fe92bd0b57e5062cf0e6600acef Mon Sep 17 00:00:00 2001 From: fakezeta Date: Sat, 28 Jan 2017 01:06:01 +0100 Subject: [PATCH 176/191] Minor changes to pass lint check --- .../components/notify/twilio_call.py | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index 37068d6e220..59da15aeebc 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -46,47 +46,47 @@ def is_validurl(url): """Check if the passed url is valid using dperini regex.""" import re - ip_middle_oct = u"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))" - ip_last_oct = u"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))" + ip_middle_oct = r"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))" + ip_last_oct = r"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))" regex = re.compile( - u"^" + r"^" # protocol identifier - u"(?:(?:https?|ftp)://)" + r"(?:(?:https?|ftp)://)" # user:pass authentication - u"(?:\S+(?::\S*)?@)?" - u"(?:" - u"(?P" + r"(?:\S+(?::\S*)?@)?" + r"(?:" + r"(?P" # IP address exclusion # private & local networks - u"(?:(?:10|127)" + ip_middle_oct + u"{2}" + ip_last_oct + u")|" - u"(?:(?:169\.254|192\.168)" + ip_middle_oct + ip_last_oct + u")|" - u"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_oct + ip_last_oct + u"))" - u"|" + r"(?:(?:10|127)" + ip_middle_oct + u"{2}" + ip_last_oct + u")|" + r"(?:(?:169\.254|192\.168)" + ip_middle_oct + ip_last_oct + u")|" + r"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_oct + ip_last_oct + u"))" + r"|" # IP address dotted notation octets # excludes loopback network 0.0.0.0 # excludes reserved space >= 224.0.0.0 # excludes network & broadcast addresses # (first & last IP address of each class) - u"(?P" - u"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])" - u"" + ip_middle_oct + u"{2}" - u"" + ip_last_oct + u")" - u"|" + r"(?P" + r"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])" + r"" + ip_middle_oct + u"{2}" + r"" + ip_last_oct + u")" + r"|" # host name - u"(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)" + r"(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)" # domain name - u"(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*" + r"(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*" # TLD identifier - u"(?:\.(?:[a-z\u00a1-\uffff]{2,}))" - u")" + r"(?:\.(?:[a-z\u00a1-\uffff]{2,}))" + r")" # port number - u"(?::\d{2,5})?" + r"(?::\d{2,5})?" # resource path - u"(?:/\S*)?" + r"(?:/\S*)?" # query string - u"(?:\?\S*)?" - u"$", + r"(?:\?\S*)?" + r"$", re.UNICODE | re.IGNORECASE ) From 564aad0ab8318929a06f29bf98dc88800275308d Mon Sep 17 00:00:00 2001 From: fakezeta Date: Sat, 28 Jan 2017 01:35:54 +0100 Subject: [PATCH 177/191] Removed Regexp and added error logging --- .../components/notify/twilio_call.py | 64 +++---------------- 1 file changed, 9 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index 59da15aeebc..14e6fe27aa7 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -42,57 +42,6 @@ def get_service(hass, config, discovery_info=None): config[CONF_FROM_NUMBER]) -def is_validurl(url): - """Check if the passed url is valid using dperini regex.""" - import re - - ip_middle_oct = r"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))" - ip_last_oct = r"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))" - - regex = re.compile( - r"^" - # protocol identifier - r"(?:(?:https?|ftp)://)" - # user:pass authentication - r"(?:\S+(?::\S*)?@)?" - r"(?:" - r"(?P" - # IP address exclusion - # private & local networks - r"(?:(?:10|127)" + ip_middle_oct + u"{2}" + ip_last_oct + u")|" - r"(?:(?:169\.254|192\.168)" + ip_middle_oct + ip_last_oct + u")|" - r"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_oct + ip_last_oct + u"))" - r"|" - # IP address dotted notation octets - # excludes loopback network 0.0.0.0 - # excludes reserved space >= 224.0.0.0 - # excludes network & broadcast addresses - # (first & last IP address of each class) - r"(?P" - r"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])" - r"" + ip_middle_oct + u"{2}" - r"" + ip_last_oct + u")" - r"|" - # host name - r"(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)" - # domain name - r"(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*" - # TLD identifier - r"(?:\.(?:[a-z\u00a1-\uffff]{2,}))" - r")" - # port number - r"(?::\d{2,5})?" - # resource path - r"(?:/\S*)?" - # query string - r"(?:\?\S*)?" - r"$", - re.UNICODE | re.IGNORECASE - ) - - return regex.match(url) - - class TwilioCallNotificationService(BaseNotificationService): """Implement the notification service for the Twilio Call service.""" @@ -103,19 +52,24 @@ class TwilioCallNotificationService(BaseNotificationService): def send_message(self, message="", **kwargs): """Call to specified target users.""" + from twilio import TwilioRestException + targets = kwargs.get(ATTR_TARGET) if not targets: _LOGGER.info("At least 1 target is required") return - if is_validurl(message): + if message.startswith("http://"): twimlet_url = message else: twimlet_url = 'http://twimlets.com/message?Message=' twimlet_url += urllib.parse.quote(message, safe='') for target in targets: - self.client.calls.create(to=target, - url=twimlet_url, - from_=self.from_number) + try: + self.client.calls.create(to=target, + url=twimlet_url, + from_=self.from_number) + except TwilioRestException as exc: + _LOGGER.error(exc) From b4c3de321577ce7276331e20812b4f12f4555690 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 27 Jan 2017 22:45:57 -0800 Subject: [PATCH 178/191] Fix switch.tplink doing I/O in event bus (#5589) * Fix switch.tplink doing I/O in event bus * Update tplink.py --- homeassistant/components/switch/tplink.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/tplink.py b/homeassistant/components/switch/tplink.py index 2457e49f955..961ee72496e 100644 --- a/homeassistant/components/switch/tplink.py +++ b/homeassistant/components/switch/tplink.py @@ -66,7 +66,7 @@ class SmartPlugSwitch(SwitchDevice): @property def is_on(self): """Return true if switch is on.""" - return self.smartplug.is_on + return self._state def turn_on(self, **kwargs): """Turn the switch on.""" @@ -84,7 +84,8 @@ class SmartPlugSwitch(SwitchDevice): def update(self): """Update the TP-Link switch's state.""" try: - self._state = self.smartplug.state + self._state = self.smartplug.state == \ + self.smartplug.SWITCH_STATE_ON if self.smartplug.has_emeter: emeter_readings = self.smartplug.get_emeter_realtime() From 6ede1c08ca6c3964ee907871b768d85be61bfe64 Mon Sep 17 00:00:00 2001 From: "Craig J. Ward" Date: Sat, 28 Jan 2017 01:31:36 -0600 Subject: [PATCH 179/191] Insteon config (#5595) * only check for devices when not defined in config * lint * performance fix --- .../components/light/insteon_local.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/light/insteon_local.py b/homeassistant/components/light/insteon_local.py index c6a52be2842..c51c7d9d839 100644 --- a/homeassistant/components/light/insteon_local.py +++ b/homeassistant/components/light/insteon_local.py @@ -38,15 +38,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None): setup_light(device_id, conf_lights[device_id], insteonhub, hass, add_devices) - linked = insteonhub.get_linked() + else: + linked = insteonhub.get_linked() - for device_id in linked: - if (linked[device_id]['cat_type'] == 'dimmer' and - device_id not in conf_lights): - request_configuration(device_id, - insteonhub, - linked[device_id]['model_name'] + ' ' + - linked[device_id]['sku'], hass, add_devices) + for device_id in linked: + if (linked[device_id]['cat_type'] == 'dimmer' and + device_id not in conf_lights): + request_configuration(device_id, + insteonhub, + linked[device_id]['model_name'] + ' ' + + linked[device_id]['sku'], + hass, add_devices) def request_configuration(device_id, insteonhub, model, hass, From b7aba525cab9a84cbb7b390c652c0db9e72a27f3 Mon Sep 17 00:00:00 2001 From: jeremydk Date: Fri, 27 Jan 2017 23:42:37 -0800 Subject: [PATCH 180/191] Emulated Hue "host-ip" fails to bind when running in docker without --net=host (#5550) * UPNP changes to allow a separate advertised IP and Port. * Fixing lint. * UPNP changes to allow a separate advertised IP and Port. * Fixing lint. * Update __init__.py * Moved logic for advertised ip and port into config class. * Commenting change for clarity. * Spacing changes for PEP8 * Spacing Changes for PEP8 * Style Changes --- homeassistant/components/emulated_hue/__init__.py | 15 ++++++++++++++- homeassistant/components/emulated_hue/upnp.py | 11 ++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index 5d6d6d0e61d..2412b283abe 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -30,6 +30,8 @@ NUMBERS_FILE = 'emulated_hue_ids.json' CONF_HOST_IP = 'host_ip' CONF_LISTEN_PORT = 'listen_port' +CONF_ADVERTISE_IP = 'advertise_ip' +CONF_ADVERTISE_PORT = 'advertise_port' CONF_UPNP_BIND_MULTICAST = 'upnp_bind_multicast' CONF_OFF_MAPS_TO_ON_DOMAINS = 'off_maps_to_on_domains' CONF_EXPOSE_BY_DEFAULT = 'expose_by_default' @@ -53,6 +55,9 @@ CONFIG_SCHEMA = 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_ADVERTISE_IP): cv.string, + vol.Optional(CONF_ADVERTISE_PORT): + vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)), vol.Optional(CONF_UPNP_BIND_MULTICAST): cv.boolean, vol.Optional(CONF_OFF_MAPS_TO_ON_DOMAINS): cv.ensure_list, vol.Optional(CONF_EXPOSE_BY_DEFAULT): cv.boolean, @@ -92,7 +97,8 @@ def setup(hass, yaml_config): upnp_listener = UPNPResponderThread( config.host_ip_addr, config.listen_port, - config.upnp_bind_multicast) + config.upnp_bind_multicast, config.advertise_ip, + config.advertise_port) @asyncio.coroutine def stop_emulated_hue_bridge(event): @@ -169,6 +175,13 @@ class Config(object): self.exposed_domains = conf.get( CONF_EXPOSED_DOMAINS, DEFAULT_EXPOSED_DOMAINS) + # Calculated effective advertised IP and port for network isolation + self.advertise_ip = conf.get( + CONF_ADVERTISE_IP) or self.host_ip_addr + + self.advertise_port = conf.get( + CONF_ADVERTISE_PORT) or self.listen_port + def entity_id_to_number(self, entity_id): """Get a unique number for the entity id.""" if self.type == TYPE_ALEXA: diff --git a/homeassistant/components/emulated_hue/upnp.py b/homeassistant/components/emulated_hue/upnp.py index de3be34e2de..31d8ab60e30 100644 --- a/homeassistant/components/emulated_hue/upnp.py +++ b/homeassistant/components/emulated_hue/upnp.py @@ -50,7 +50,7 @@ class DescriptionXmlView(HomeAssistantView): """ resp_text = xml_template.format( - self.config.host_ip_addr, self.config.listen_port) + self.config.advertise_ip, self.config.advertise_port) return web.Response(text=resp_text, content_type='text/xml') @@ -60,7 +60,8 @@ class UPNPResponderThread(threading.Thread): _interrupted = False - def __init__(self, host_ip_addr, listen_port, upnp_bind_multicast): + def __init__(self, host_ip_addr, listen_port, upnp_bind_multicast, + advertise_ip, advertise_port): """Initialize the class.""" threading.Thread.__init__(self) @@ -81,9 +82,9 @@ 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') + self.upnp_response = resp_template.format( + advertise_ip, advertise_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 From 72bc8fc5bff939ad1589f51cbce4d48622f49fa0 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Sat, 28 Jan 2017 08:52:46 +0100 Subject: [PATCH 181/191] eq3btsmart: add reporting for availability (#5594) * eq3btsmart: add reporting for availability * Update eq3btsmart.py --- homeassistant/components/climate/eq3btsmart.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/climate/eq3btsmart.py b/homeassistant/components/climate/eq3btsmart.py index a8ab9bd30b2..6587ad86300 100644 --- a/homeassistant/components/climate/eq3btsmart.py +++ b/homeassistant/components/climate/eq3btsmart.py @@ -76,6 +76,11 @@ class EQ3BTSmartThermostat(ClimateDevice): self._name = _name self._thermostat = eq3.Thermostat(_mac) + @property + def available(self) -> bool: + """Return if thermostat is available.""" + return self.current_operation != STATE_UNKNOWN + @property def name(self): """Return the name of the device.""" From e1412a223c156df34e488e056c0b574453097f8c Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 28 Jan 2017 16:02:19 +0100 Subject: [PATCH 182/191] Update docstring (quotes, links, content) (#5602) --- .../components/device_tracker/linksys_ap.py | 9 ++++----- .../components/device_tracker/sky_hub.py | 16 ++++++---------- homeassistant/components/device_tracker/tado.py | 13 ++++++------- homeassistant/components/light/avion.py | 2 +- homeassistant/components/lutron.py | 8 ++++---- homeassistant/components/notify/discord.py | 7 ++++++- homeassistant/components/sensor/wsdot.py | 6 +++--- homeassistant/components/tts/amazon_polly.py | 2 +- homeassistant/components/zabbix.py | 2 +- 9 files changed, 32 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/device_tracker/linksys_ap.py b/homeassistant/components/device_tracker/linksys_ap.py index 29246f79fc5..fc8f9f96a37 100644 --- a/homeassistant/components/device_tracker/linksys_ap.py +++ b/homeassistant/components/device_tracker/linksys_ap.py @@ -14,11 +14,10 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA -from homeassistant.const import (CONF_HOST, CONF_PASSWORD, CONF_USERNAME, - CONF_VERIFY_SSL) +from homeassistant.const import ( + CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_VERIFY_SSL) from homeassistant.util import Throttle -# Return cached results if last scan was less then this time ago. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) INTERFACES = 2 DEFAULT_TIMEOUT = 10 @@ -59,7 +58,7 @@ class LinksysAPDeviceScanner(object): # Check if the access point is accessible response = self._make_request() if not response.status_code == 200: - raise ConnectionError('Cannot connect to Linksys Access Point') + raise ConnectionError("Cannot connect to Linksys Access Point") def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" @@ -83,7 +82,7 @@ class LinksysAPDeviceScanner(object): from bs4 import BeautifulSoup as BS with self.lock: - _LOGGER.info('Checking Linksys AP') + _LOGGER.info("Checking Linksys AP") self.last_results = [] for interface in range(INTERFACES): diff --git a/homeassistant/components/device_tracker/sky_hub.py b/homeassistant/components/device_tracker/sky_hub.py index 9c61b47593e..647731d8485 100644 --- a/homeassistant/components/device_tracker/sky_hub.py +++ b/homeassistant/components/device_tracker/sky_hub.py @@ -1,10 +1,8 @@ """ Support for Sky Hub. -# Example configuration.yaml entry -device_tracker: - - platform: sky_hub - host: 192.168.1.254 +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.sky_hub/ """ import logging import re @@ -20,13 +18,11 @@ from homeassistant.components.device_tracker import ( from homeassistant.const import CONF_HOST from homeassistant.util import Throttle -# Return cached results if last scan was less then this time ago. -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) - _LOGGER = logging.getLogger(__name__) - _MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})') +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string }) @@ -45,7 +41,7 @@ class SkyHubDeviceScanner(DeviceScanner): def __init__(self, config): """Initialise the scanner.""" - _LOGGER.info('Initialising Sky Hub') + _LOGGER.info("Initialising Sky Hub") self.host = config.get(CONF_HOST, '192.168.1.254') self.lock = threading.Lock() @@ -86,7 +82,7 @@ class SkyHubDeviceScanner(DeviceScanner): return False with self.lock: - _LOGGER.info('Scanning') + _LOGGER.info("Scanning") data = _get_skyhub_data(self.url) diff --git a/homeassistant/components/device_tracker/tado.py b/homeassistant/components/device_tracker/tado.py index 5cb1f8fcbd2..9127ef4fad2 100644 --- a/homeassistant/components/device_tracker/tado.py +++ b/homeassistant/components/device_tracker/tado.py @@ -1,8 +1,8 @@ """ Support for Tado Smart Thermostat. -Device tracker platform that supports presence detection. -The detection is based on geofencing enabled devices used with Tado. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.tado/ """ import logging from datetime import timedelta @@ -17,15 +17,14 @@ import voluptuous as vol from homeassistant.const import CONF_USERNAME, CONF_PASSWORD import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -from homeassistant.components.device_tracker import \ - DOMAIN, PLATFORM_SCHEMA, DeviceScanner +from homeassistant.components.device_tracker import ( + DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.helpers.aiohttp_client import async_create_clientsession -# Return cached results if last scan was less then this time ago -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) - _LOGGER = logging.getLogger(__name__) +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_USERNAME): cv.string diff --git a/homeassistant/components/light/avion.py b/homeassistant/components/light/avion.py index 4a53697ccf4..929b2bc33ac 100644 --- a/homeassistant/components/light/avion.py +++ b/homeassistant/components/light/avion.py @@ -55,7 +55,7 @@ class AvionLight(Light): self._name = device['name'] self._address = device['address'] - self._key = device["key"] + self._key = device['key'] self._brightness = 255 self._state = False self._switch = avion.avion(self._address, self._key) diff --git a/homeassistant/components/lutron.py b/homeassistant/components/lutron.py index 0c9458ff3f6..d5512e9e5b6 100644 --- a/homeassistant/components/lutron.py +++ b/homeassistant/components/lutron.py @@ -1,9 +1,9 @@ """ Component for interacting with a Lutron RadioRA 2 system. -Uses pylutron (http://github.com/thecynic/pylutron). +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/lutron/ """ - import logging from homeassistant.helpers import discovery @@ -13,7 +13,7 @@ from homeassistant.loader import get_component REQUIREMENTS = ['https://github.com/thecynic/pylutron/archive/v0.1.0.zip#' 'pylutron==0.1.0'] -DOMAIN = "lutron" +DOMAIN = 'lutron' _LOGGER = logging.getLogger(__name__) @@ -38,7 +38,7 @@ def setup(hass, base_config): ) hass.data[LUTRON_CONTROLLER].load_xml_db() hass.data[LUTRON_CONTROLLER].connect() - _LOGGER.info("Connected to Main Repeater @ %s", config['lutron_host']) + _LOGGER.info("Connected to Main Repeater at %s", config['lutron_host']) group = get_component('group') diff --git a/homeassistant/components/notify/discord.py b/homeassistant/components/notify/discord.py index 34fb2a1770a..e6c4b3bad96 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/notify/discord.py @@ -1,4 +1,9 @@ -"""Discord platform for notify component.""" +""" +Discord platform for notify component. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/notify.discord/ +""" import logging import asyncio import voluptuous as vol diff --git a/homeassistant/components/sensor/wsdot.py b/homeassistant/components/sensor/wsdot.py index a3230a88eb3..fecff260716 100644 --- a/homeassistant/components/sensor/wsdot.py +++ b/homeassistant/components/sensor/wsdot.py @@ -1,7 +1,8 @@ """ Support for Washington State Department of Transportation (WSDOT) data. -Data provided by WSDOT is documented at http://wsdot.com/traffic/api/ +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.wsdot/ """ import logging import re @@ -17,7 +18,6 @@ from homeassistant.const import ( from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv - _LOGGER = logging.getLogger(__name__) CONF_TRAVEL_TIMES = 'travel_time' @@ -109,7 +109,7 @@ class WashingtonStateTravelTimeSensor(WashingtonStateTransportSensor): response = requests.get(self.RESOURCE, params, timeout=10) if response.status_code != 200: - _LOGGER.warning('Invalid response from WSDOT API.') + _LOGGER.warning("Invalid response from WSDOT API") else: self._data = response.json() self._state = self._data.get(ATTR_CURRENT_TIME) diff --git a/homeassistant/components/tts/amazon_polly.py b/homeassistant/components/tts/amazon_polly.py index fe63d72b632..e40c10f5e14 100644 --- a/homeassistant/components/tts/amazon_polly.py +++ b/homeassistant/components/tts/amazon_polly.py @@ -2,7 +2,7 @@ Support for the Amazon Polly text to speech service. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts/amazon_polly/ +https://home-assistant.io/components/tts.amazon_polly/ """ import logging import voluptuous as vol diff --git a/homeassistant/components/zabbix.py b/homeassistant/components/zabbix.py index 3418bad6c9c..adbf34a474c 100644 --- a/homeassistant/components/zabbix.py +++ b/homeassistant/components/zabbix.py @@ -18,7 +18,7 @@ REQUIREMENTS = ['pyzabbix==0.7.4'] _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = False -DEFAULT_PATH = "zabbix" +DEFAULT_PATH = 'zabbix' DOMAIN = 'zabbix' From b0d07a414bc5297deb4f43c76473d9740b5dc8f7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Jan 2017 11:51:35 -0800 Subject: [PATCH 183/191] Token tweaks (#5599) * Base status code on auth when entity not found * Also allow previous camera token * Fix tests * Address comments --- homeassistant/components/camera/__init__.py | 41 ++++++++++++++----- .../components/media_player/__init__.py | 12 +++++- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 174d0f5a298..b531a931a7a 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -6,14 +6,17 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/camera/ """ import asyncio +import collections from datetime import timedelta import logging import hashlib +from random import SystemRandom import aiohttp from aiohttp import web import async_timeout +from homeassistant.core import callback from homeassistant.const import ATTR_ENTITY_PICTURE from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -21,6 +24,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED +from homeassistant.helpers.event import async_track_time_interval _LOGGER = logging.getLogger(__name__) @@ -35,6 +39,9 @@ STATE_IDLE = 'idle' ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?token={1}' +TOKEN_CHANGE_INTERVAL = timedelta(minutes=5) +_RND = SystemRandom() + @asyncio.coroutine def async_get_image(hass, entity_id, timeout=10): @@ -80,6 +87,15 @@ def async_setup(hass, config): hass.http.register_view(CameraMjpegStream(component.entities)) yield from component.async_setup(config) + + @callback + def update_tokens(time): + """Update tokens of the entities.""" + for entity in component.entities.values(): + entity.async_update_token() + hass.async_add_job(entity.async_update_ha_state()) + + async_track_time_interval(hass, update_tokens, TOKEN_CHANGE_INTERVAL) return True @@ -89,13 +105,8 @@ class Camera(Entity): def __init__(self): """Initialize a camera.""" self.is_streaming = False - self._access_token = hashlib.sha256( - str.encode(str(id(self)))).hexdigest() - - @property - def access_token(self): - """Access token for this camera.""" - return self._access_token + self.access_tokens = collections.deque([], 2) + self.async_update_token() @property def should_poll(self): @@ -105,7 +116,7 @@ class Camera(Entity): @property def entity_picture(self): """Return a link to the camera feed as entity picture.""" - return ENTITY_IMAGE_URL.format(self.entity_id, self.access_token) + return ENTITY_IMAGE_URL.format(self.entity_id, self.access_tokens[-1]) @property def is_recording(self): @@ -196,7 +207,7 @@ class Camera(Entity): def state_attributes(self): """Camera state attributes.""" attr = { - 'access_token': self.access_token, + 'access_token': self.access_tokens[-1], } if self.model: @@ -207,6 +218,13 @@ class Camera(Entity): return attr + @callback + def async_update_token(self): + """Update the used token.""" + self.access_tokens.append( + hashlib.sha256( + _RND.getrandbits(256).to_bytes(32, 'little')).hexdigest()) + class CameraView(HomeAssistantView): """Base CameraView.""" @@ -223,10 +241,11 @@ class CameraView(HomeAssistantView): camera = self.entities.get(entity_id) if camera is None: - return web.Response(status=404) + status = 404 if request[KEY_AUTHENTICATED] else 401 + return web.Response(status=status) authenticated = (request[KEY_AUTHENTICATED] or - request.GET.get('token') == camera.access_token) + request.GET.get('token') in camera.access_tokens) if not authenticated: return web.Response(status=401) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 576dca25a6a..71901b6256a 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -10,6 +10,7 @@ import functools as ft import hashlib import logging import os +from random import SystemRandom from aiohttp import web import async_timeout @@ -32,6 +33,7 @@ from homeassistant.const import ( SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK) _LOGGER = logging.getLogger(__name__) +_RND = SystemRandom() DOMAIN = 'media_player' DEPENDENCIES = ['http'] @@ -389,6 +391,8 @@ def async_setup(hass, config): class MediaPlayerDevice(Entity): """ABC for media player devices.""" + _access_token = None + # pylint: disable=no-self-use # Implement these for your media player @property @@ -399,7 +403,10 @@ class MediaPlayerDevice(Entity): @property def access_token(self): """Access token for this media player.""" - return str(id(self)) + if self._access_token is None: + self._access_token = hashlib.sha256( + _RND.getrandbits(256).to_bytes(32, 'little')).hexdigest() + return self._access_token @property def volume_level(self): @@ -895,7 +902,8 @@ class MediaPlayerImageView(HomeAssistantView): """Start a get request.""" player = self.entities.get(entity_id) if player is None: - return web.Response(status=404) + status = 404 if request[KEY_AUTHENTICATED] else 401 + return web.Response(status=status) authenticated = (request[KEY_AUTHENTICATED] or request.GET.get('token') == player.access_token) From 1aa1074054b24acf4daa9265b53d568f0c252ce1 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sat, 28 Jan 2017 12:12:34 -0800 Subject: [PATCH 184/191] Update LICENSE.md and CLA.md to reflect the new Apache 2.0 license --- CLA.md | 55 +++++--------- LICENSE.md | 211 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 210 insertions(+), 56 deletions(-) diff --git a/CLA.md b/CLA.md index 4560e3be643..f8570cef551 100644 --- a/CLA.md +++ b/CLA.md @@ -1,46 +1,32 @@ # Contributor License Agreement -The following terms are used throughout this agreement: +``` +By making a contribution to this project, I certify that: -**You** - the person or legal entity including its affiliates asked to accept this agreement. -An affiliate is any entity that controls or is controlled by the legal entity, or is under common control with it. +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the Apache 2.0 license; or -**Project** - is an umbrella term that refers to any and all Home Assistant open source projects. +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the Apache 2.0 license; or -**Contribution** - any type of work that is submitted to a Project, including any modifications or additions to existing work. +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. -**Submitted** - conveyed to a Project via a pull request, commit, issue, or any form of electronic, written, or -verbal communication with Home Assistant, contributors or maintainers. - -# 1. Grant of Copyright License. - -Subject to the terms and conditions of this agreement, You grant to the Projects’ maintainers, contributors, -users and to Home Assistant a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, -prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your contributions and such -derivative works. Except for this license, You reserve all rights, title, and interest in your contributions. - -# 2. Grant of Patent License. - -Subject to the terms and conditions of this agreement, You grant to the Projects’ maintainers, contributors, users and to -Home Assistant a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise transfer your contributions, where such license -applies only to those patent claims licensable by you that are necessarily infringed by your contribution or by combination of -your contribution with the project to which this contribution was submitted. - -If any entity institutes patent litigation - including cross-claim or counterclaim in a lawsuit - against You alleging that -your contribution or any project it was submitted to constitutes or is responsible for direct or contributory patent infringement, -then any patent licenses granted to that entity under this agreement shall terminate as of the date such litigation is filed. - -# 3. Source of Contribution. - -Your contribution is either your original creation, based upon previous work that, to the best of your knowledge, -is covered under an appropriate open source license and you have the right under that license to submit that work with modifications, -whether created in whole or in part by you, or you have clearly identified the source of the contribution and any license or other -restriction (like related patents, trademarks, and license agreements) of which you are personally aware. +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it) is maintained indefinitely + and may be redistributed consistent with this project or the open + source license(s) involved. +``` ## Attribution -This Contributor License Agreement is adapted from the [GitHub CLA][github-cla]. +The text of this license is available under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). It is based on the Linux [Developer Certificate Of Origin](http://elinux.org/Developer_Certificate_Of_Origin), but is modified to explicitly use the Apache 2.0 license +and not mention sign-off. ## Signing @@ -50,5 +36,4 @@ To sign this CLA you must first submit a pull request to a repository under the This Contributor License Agreement (CLA) was first announced on January 21st, 2017 in [this][cla-blog] blog post and adopted January 28th, 2017. -[github-cla]: https://cla.github.com/agreement [cla-blog]: https://home-assistant.io/blog/2017/01/21/home-assistant-governance/ diff --git a/LICENSE.md b/LICENSE.md index c1a3c4845c2..b62a9b5ff78 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,25 +1,194 @@ -The MIT License (MIT) -===================== +Apache License +============== -Copyright © `2013-2017` `Paulus Schoutsen` +_Version 2.0, January 2004_ +_<>_ -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the “Software”), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: +### Terms and Conditions for use, reproduction, and distribution -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +#### 1. Definitions -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ + +### APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets `[]` replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 405b2fdfa01ccc5ed36cbcdb909964a5ad9f7617 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sat, 28 Jan 2017 12:29:09 -0800 Subject: [PATCH 185/191] Correct project year, update license --- homeassistant/const.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2bdefe6a9fd..a56140f8076 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -10,9 +10,9 @@ REQUIRED_PYTHON_VER_WIN = (3, 5, 2) PROJECT_NAME = 'Home Assistant' PROJECT_PACKAGE_NAME = 'homeassistant' -PROJECT_LICENSE = 'MIT License' +PROJECT_LICENSE = 'Apache License 2.0' PROJECT_AUTHOR = 'The Home Assistant Authors' -PROJECT_COPYRIGHT = ' 2016, {}'.format(PROJECT_AUTHOR) +PROJECT_COPYRIGHT = ' 2013, {}'.format(PROJECT_AUTHOR) PROJECT_URL = 'https://home-assistant.io/' PROJECT_EMAIL = 'hello@home-assistant.io' PROJECT_DESCRIPTION = ('Open-source home automation platform ' @@ -25,7 +25,7 @@ PROJECT_LONG_DESCRIPTION = ('Home Assistant is an open-source ' PROJECT_CLASSIFIERS = [ 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', + 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3.4', 'Topic :: Home Automation' From 1fb372ffdb2d17e0a357dfc3eaabc28d359ddf11 Mon Sep 17 00:00:00 2001 From: andrey-git Date: Sat, 28 Jan 2017 22:29:51 +0200 Subject: [PATCH 186/191] Apply new customize format to Zwave (#5603) --- homeassistant/components/light/zwave.py | 8 +-- homeassistant/components/zwave/__init__.py | 20 +++---- homeassistant/config.py | 40 +++---------- homeassistant/helpers/customize.py | 57 +++++++++++++----- homeassistant/helpers/entity.py | 4 +- tests/components/test_zwave.py | 68 ++++++++++++++++++++++ tests/helpers/test_customize.py | 38 +++++++++++- tests/helpers/test_entity.py | 1 + tests/test_config.py | 19 ------ 9 files changed, 171 insertions(+), 84 deletions(-) create mode 100644 tests/components/test_zwave.py diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index ab6cb3cdecd..5bab9ace4c6 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -17,6 +17,7 @@ from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.util.color import HASS_COLOR_MAX, HASS_COLOR_MIN, \ color_temperature_mired_to_kelvin, color_temperature_to_rgb, \ color_rgb_to_rgbw, color_rgbw_to_rgb +from homeassistant.helpers import customize _LOGGER = logging.getLogger(__name__) @@ -54,13 +55,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return node = zwave.NETWORK.nodes[discovery_info[zwave.const.ATTR_NODE_ID]] value = node.values[discovery_info[zwave.const.ATTR_VALUE_ID]] - customize = hass.data['zwave_customize'] name = '{}.{}'.format(DOMAIN, zwave.object_id(value)) - node_config = customize.get(name, {}) + node_config = customize.get_overrides(hass, zwave.DOMAIN, name) refresh = node_config.get(zwave.CONF_REFRESH_VALUE) delay = node_config.get(zwave.CONF_REFRESH_DELAY) - _LOGGER.debug('customize=%s name=%s node_config=%s CONF_REFRESH_VALUE=%s' - ' CONF_REFRESH_DELAY=%s', customize, name, node_config, + _LOGGER.debug('name=%s node_config=%s CONF_REFRESH_VALUE=%s' + ' CONF_REFRESH_DELAY=%s', name, node_config, refresh, delay) if value.command_class != zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL: return diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index c4b51ca9451..5afd74c1503 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -11,10 +11,10 @@ from pprint import pprint import voluptuous as vol -from homeassistant.helpers import discovery +from homeassistant.helpers import discovery, customize from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_LOCATION, ATTR_ENTITY_ID, CONF_CUSTOMIZE, - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) + EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_ENTITY_ID) from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_change from homeassistant.util import convert, slugify @@ -150,9 +150,9 @@ CHANGE_ASSOCIATION_SCHEMA = vol.Schema({ vol.Optional(const.ATTR_INSTANCE, default=0x00): vol.Coerce(int) }) -CUSTOMIZE_SCHEMA = vol.Schema({ - vol.Optional(CONF_POLLING_INTENSITY): - vol.All(cv.positive_int), +_ZWAVE_CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({ + vol.Required(CONF_ENTITY_ID): cv.match_all, + vol.Optional(CONF_POLLING_INTENSITY): cv.positive_int, vol.Optional(CONF_IGNORED, default=DEFAULT_CONF_IGNORED): cv.boolean, vol.Optional(CONF_REFRESH_VALUE, default=DEFAULT_CONF_REFRESH_VALUE): cv.boolean, @@ -164,8 +164,9 @@ CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_AUTOHEAL, default=DEFAULT_CONF_AUTOHEAL): cv.boolean, vol.Optional(CONF_CONFIG_PATH): cv.string, - vol.Optional(CONF_CUSTOMIZE, default={}): - vol.Schema({cv.string: CUSTOMIZE_SCHEMA}), + vol.Optional(CONF_CUSTOMIZE, default=[]): + vol.All(customize.CUSTOMIZE_SCHEMA, + [_ZWAVE_CUSTOMIZE_SCHEMA_ENTRY]), vol.Optional(CONF_DEBUG, default=DEFAULT_DEBUG): cv.boolean, vol.Optional(CONF_POLLING_INTERVAL, default=DEFAULT_POLLING_INTERVAL): cv.positive_int, @@ -268,8 +269,7 @@ def setup(hass, config): # Load configuration use_debug = config[DOMAIN].get(CONF_DEBUG) - hass.data['zwave_customize'] = config[DOMAIN].get(CONF_CUSTOMIZE) - customize = hass.data['zwave_customize'] + customize.set_customize(hass, DOMAIN, config[DOMAIN].get(CONF_CUSTOMIZE)) autoheal = config[DOMAIN].get(CONF_AUTOHEAL) # Setup options @@ -349,7 +349,7 @@ def setup(hass, config): value.genre) name = "{}.{}".format(component, object_id(value)) - node_config = customize.get(name, {}) + node_config = customize.get_overrides(hass, DOMAIN, name) if node_config.get(CONF_IGNORED): _LOGGER.info("Ignoring device %s", name) diff --git a/homeassistant/config.py b/homeassistant/config.py index 2714f066035..d8e30987e16 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -13,7 +13,7 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM, CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS, - CONF_ENTITY_ID, __version__) + __version__) from homeassistant.core import DOMAIN as CONF_CORE from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import get_component @@ -21,7 +21,7 @@ from homeassistant.util.yaml import load_yaml import homeassistant.helpers.config_validation as cv from homeassistant.util import dt as date_util, location as loc_util from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM -from homeassistant.helpers.customize import set_customize +from homeassistant.helpers import customize _LOGGER = logging.getLogger(__name__) @@ -86,27 +86,6 @@ tts: """ -CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({ - vol.Required(CONF_ENTITY_ID): vol.All( - cv.ensure_list_csv, vol.Length(min=1), [cv.string], [vol.Lower]) -}, extra=vol.ALLOW_EXTRA) - - -def _convert_old_config(inp: Any) -> List: - if not isinstance(inp, dict): - return cv.ensure_list(inp) - if CONF_ENTITY_ID in inp: - return [inp] # sigle entry - res = [] - - inp = vol.Schema({cv.match_all: dict})(inp) - for key, val in inp.items(): - val = dict(val) - val[CONF_ENTITY_ID] = key - res.append(val) - return res - - PACKAGES_CONFIG_SCHEMA = vol.Schema({ cv.slug: vol.Schema( # Package names are slugs {cv.slug: vol.Any(dict, list)}) # Only slugs for component names @@ -120,8 +99,7 @@ CORE_CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit, CONF_UNIT_SYSTEM: cv.unit_system, CONF_TIME_ZONE: cv.time_zone, - vol.Optional(CONF_CUSTOMIZE, default=[]): vol.All( - _convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY]), + vol.Optional(CONF_CUSTOMIZE, default=[]): customize.CUSTOMIZE_SCHEMA, vol.Optional(CONF_PACKAGES, default={}): PACKAGES_CONFIG_SCHEMA, }) @@ -280,6 +258,7 @@ def async_process_ha_core_config(hass, config): This method is a coroutine. """ + print(CORE_CONFIG_SCHEMA) config = CORE_CONFIG_SCHEMA(config) hac = hass.config @@ -306,9 +285,9 @@ def async_process_ha_core_config(hass, config): if CONF_TIME_ZONE in config: set_time_zone(config.get(CONF_TIME_ZONE)) - customize = merge_packages_customize( + merged_customize = merge_packages_customize( config[CONF_CUSTOMIZE], config[CONF_PACKAGES]) - set_customize(hass, customize) + customize.set_customize(hass, CONF_CORE, merged_customize) if CONF_UNIT_SYSTEM in config: if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL: @@ -463,15 +442,14 @@ def merge_packages_config(config, packages): return config -def merge_packages_customize(customize, packages): +def merge_packages_customize(core_customize, packages): """Merge customize from packages.""" schema = vol.Schema({ vol.Optional(CONF_CORE): vol.Schema({ - CONF_CUSTOMIZE: vol.All( - _convert_old_config, [CUSTOMIZE_SCHEMA_ENTRY])}) + CONF_CUSTOMIZE: customize.CUSTOMIZE_SCHEMA}), }, extra=vol.ALLOW_EXTRA) - cust = list(customize) + cust = list(core_customize) for pkg in packages.values(): conf = schema(pkg) cust.extend(conf.get(CONF_CORE, {}).get(CONF_CUSTOMIZE, [])) diff --git a/homeassistant/helpers/customize.py b/homeassistant/helpers/customize.py index b03a89ff40f..e9cd7c0269a 100644 --- a/homeassistant/helpers/customize.py +++ b/homeassistant/helpers/customize.py @@ -1,25 +1,51 @@ """A helper module for customization.""" import collections -from typing import Dict, List +from typing import Any, Dict, List import fnmatch +import voluptuous as vol from homeassistant.const import CONF_ENTITY_ID from homeassistant.core import HomeAssistant, split_entity_id +import homeassistant.helpers.config_validation as cv -_OVERWRITE_KEY = 'overwrite' -_OVERWRITE_CACHE_KEY = 'overwrite_cache' +_OVERWRITE_KEY_FORMAT = '{}.overwrite' +_OVERWRITE_CACHE_KEY_FORMAT = '{}.overwrite_cache' + +_CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({ + vol.Required(CONF_ENTITY_ID): vol.All( + cv.ensure_list_csv, vol.Length(min=1), [vol.Schema(str)], [vol.Lower]) +}, extra=vol.ALLOW_EXTRA) -def set_customize(hass: HomeAssistant, customize: List[Dict]) -> None: +def _convert_old_config(inp: Any) -> List: + if not isinstance(inp, dict): + return cv.ensure_list(inp) + if CONF_ENTITY_ID in inp: + return [inp] # sigle entry + res = [] + + inp = vol.Schema({cv.match_all: dict})(inp) + for key, val in inp.items(): + val = dict(val) + val[CONF_ENTITY_ID] = key + res.append(val) + return res + + +CUSTOMIZE_SCHEMA = vol.All(_convert_old_config, [_CUSTOMIZE_SCHEMA_ENTRY]) + + +def set_customize( + hass: HomeAssistant, domain: str, customize: List[Dict]) -> None: """Overwrite all current customize settings. Async friendly. """ - hass.data[_OVERWRITE_KEY] = customize - hass.data[_OVERWRITE_CACHE_KEY] = {} + hass.data[_OVERWRITE_KEY_FORMAT.format(domain)] = customize + hass.data[_OVERWRITE_CACHE_KEY_FORMAT.format(domain)] = {} -def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict: +def get_overrides(hass: HomeAssistant, domain: str, entity_id: str) -> Dict: """Return a dictionary of overrides related to entity_id. Whole-domain overrides are of lowest priorities, @@ -28,10 +54,11 @@ def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict: The lookups are cached. """ - if _OVERWRITE_CACHE_KEY in hass.data and \ - entity_id in hass.data[_OVERWRITE_CACHE_KEY]: - return hass.data[_OVERWRITE_CACHE_KEY][entity_id] - if _OVERWRITE_KEY not in hass.data: + cache_key = _OVERWRITE_CACHE_KEY_FORMAT.format(domain) + if cache_key in hass.data and entity_id in hass.data[cache_key]: + return hass.data[cache_key][entity_id] + overwrite_key = _OVERWRITE_KEY_FORMAT.format(domain) + if overwrite_key not in hass.data: return {} domain_result = {} # type: Dict[str, Any] glob_result = {} # type: Dict[str, Any] @@ -57,7 +84,7 @@ def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict: else: target[key] = source[key] - for rule in hass.data[_OVERWRITE_KEY]: + for rule in hass.data[overwrite_key]: if CONF_ENTITY_ID in rule: entities = rule[CONF_ENTITY_ID] if domain in entities: @@ -74,7 +101,7 @@ def get_overrides(hass: HomeAssistant, entity_id: str) -> Dict: deep_update(result, clean_entry(domain_result)) deep_update(result, clean_entry(glob_result)) deep_update(result, clean_entry(exact_result)) - if _OVERWRITE_CACHE_KEY not in hass.data: - hass.data[_OVERWRITE_CACHE_KEY] = {} - hass.data[_OVERWRITE_CACHE_KEY][entity_id] = result + if cache_key not in hass.data: + hass.data[cache_key] = {} + hass.data[cache_key][entity_id] = result return result diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 6f09b9592f3..ac124b3abf3 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -11,7 +11,7 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_ENTITY_PICTURE) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, DOMAIN as CORE_DOMAIN from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.util import ensure_unique_string, slugify from homeassistant.util.async import ( @@ -242,7 +242,7 @@ class Entity(object): end - start) # Overwrite properties that have been set in the config file. - attr.update(get_overrides(self.hass, self.entity_id)) + attr.update(get_overrides(self.hass, CORE_DOMAIN, self.entity_id)) # Remove hidden property if false so it won't show up. if not attr.get(ATTR_HIDDEN, True): diff --git a/tests/components/test_zwave.py b/tests/components/test_zwave.py new file mode 100644 index 00000000000..5c9be9ba22a --- /dev/null +++ b/tests/components/test_zwave.py @@ -0,0 +1,68 @@ +"""The tests for the zwave component.""" +import unittest +from unittest.mock import MagicMock, patch + +from homeassistant.bootstrap import setup_component +from homeassistant.components import zwave +from tests.common import get_test_home_assistant + + +class TestComponentZwave(unittest.TestCase): + """Test the Zwave component.""" + + def setUp(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + def _validate_config(self, validator, config): + libopenzwave = MagicMock() + libopenzwave.__file__ = 'test' + with patch.dict('sys.modules', { + 'libopenzwave': libopenzwave, + 'openzwave.option': MagicMock(), + 'openzwave.network': MagicMock(), + 'openzwave.group': MagicMock(), + }): + validator(setup_component(self.hass, zwave.DOMAIN, { + zwave.DOMAIN: config, + })) + + def test_empty_config(self): + """Test empty config.""" + self._validate_config(self.assertTrue, {}) + + def test_empty_customize(self): + """Test empty customize.""" + self._validate_config(self.assertTrue, {'customize': {}}) + self._validate_config(self.assertTrue, {'customize': []}) + + def test_empty_customize_content(self): + """Test empty customize.""" + self._validate_config( + self.assertTrue, {'customize': {'test.test': {}}}) + + def test_full_customize_dict(self): + """Test full customize as dict.""" + self._validate_config(self.assertTrue, {'customize': {'test.test': { + zwave.CONF_POLLING_INTENSITY: 10, + zwave.CONF_IGNORED: 1, + zwave.CONF_REFRESH_VALUE: 1, + zwave.CONF_REFRESH_DELAY: 10}}}) + + def test_full_customize_list(self): + """Test full customize as list.""" + self._validate_config(self.assertTrue, {'customize': [{ + 'entity_id': 'test.test', + zwave.CONF_POLLING_INTENSITY: 10, + zwave.CONF_IGNORED: 1, + zwave.CONF_REFRESH_VALUE: 1, + zwave.CONF_REFRESH_DELAY: 10}]}) + + def test_bad_customize(self): + """Test customize with extra keys.""" + self._validate_config( + self.assertFalse, {'customize': {'test.test': {'extra_key': 10}}}) diff --git a/tests/helpers/test_customize.py b/tests/helpers/test_customize.py index e3fd1e325b0..0fb1b6ab14c 100644 --- a/tests/helpers/test_customize.py +++ b/tests/helpers/test_customize.py @@ -1,5 +1,7 @@ """Test the customize helper.""" import homeassistant.helpers.customize as customize +from voluptuous import MultipleInvalid +import pytest class MockHass(object): @@ -17,8 +19,9 @@ class TestHelpersCustomize(object): self.hass = MockHass() def _get_overrides(self, overrides): - customize.set_customize(self.hass, overrides) - return customize.get_overrides(self.hass, self.entity_id) + test_domain = 'test.domain' + customize.set_customize(self.hass, test_domain, overrides) + return customize.get_overrides(self.hass, test_domain, self.entity_id) def test_override_single_value(self): """Test entity customization through configuration.""" @@ -75,7 +78,7 @@ class TestHelpersCustomize(object): 'key3': 'valueDomain'} def test_override_deep_dict(self): - """Test we can overwrite hidden property to True.""" + """Test we can deep-overwrite a dict.""" result = self._get_overrides( [{'entity_id': [self.entity_id], 'test': {'key1': 'value1', 'key2': 'value2'}}, @@ -85,3 +88,32 @@ class TestHelpersCustomize(object): 'key1': 'value1', 'key2': 'value22', 'key3': 'value3'} + + def test_schema_bad_schema(self): + """Test bad customize schemas.""" + for value in ( + {'test.test': 10}, + {'test.test': ['hello']}, + {'entity_id': {'a': 'b'}}, + {'entity_id': 10}, + [{'test.test': 'value'}], + ): + with pytest.raises( + MultipleInvalid, + message="{} should have raised MultipleInvalid".format( + value)): + customize.CUSTOMIZE_SCHEMA(value) + + def test_get_customize_schema_allow_extra(self): + """Test schema with ALLOW_EXTRA.""" + for value in ( + {'test.test': {'hidden': True}}, + {'test.test': {'key': ['value1', 'value2']}}, + [{'entity_id': 'id1', 'key': 'value'}], + ): + customize.CUSTOMIZE_SCHEMA(value) + + def test_get_customize_schema_csv(self): + """Test schema with comma separated entity IDs.""" + assert [{'entity_id': ['id1', 'id2', 'id3']}] == \ + customize.CUSTOMIZE_SCHEMA([{'entity_id': 'id1,ID2 , id3'}]) diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index 9ec016ccfcd..061c206c116 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -90,6 +90,7 @@ class TestHelpersEntity(object): """Test we can overwrite hidden property to True.""" set_customize( self.hass, + entity.CORE_DOMAIN, [{'entity_id': [self.entity.entity_id], ATTR_HIDDEN: True}]) self.entity.update_ha_state() diff --git a/tests/test_config.py b/tests/test_config.py index 1d647cb2c92..1197a0130d8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -234,25 +234,6 @@ class TestConfig(unittest.TestCase): assert state.attributes['hidden'] - def test_entity_customization_comma_separated(self): - """Test entity customization through configuration.""" - config = {CONF_LATITUDE: 50, - CONF_LONGITUDE: 50, - CONF_NAME: 'Test', - CONF_CUSTOMIZE: [ - {'entity_id': 'test.not_test,test,test.not_t*', - 'key1': 'value1'}, - {'entity_id': 'test.test,not_test,test.not_t*', - 'key2': 'value2'}, - {'entity_id': 'test.not_test,not_test,test.t*', - 'key3': 'value3'}]} - - state = self._compute_state(config) - - assert state.attributes['key1'] == 'value1' - assert state.attributes['key2'] == 'value2' - assert state.attributes['key3'] == 'value3' - @mock.patch('homeassistant.config.shutil') @mock.patch('homeassistant.config.os') def test_remove_lib_on_upgrade(self, mock_os, mock_shutil): From 8f418831a12dfef34a2392c3b5c63d71423925ca Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sat, 28 Jan 2017 12:45:32 -0800 Subject: [PATCH 187/191] Update Twilio SDK version --- homeassistant/components/notify/twilio_call.py | 2 +- homeassistant/components/notify/twilio_sms.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index 14e6fe27aa7..b1cc8e5eeed 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -15,7 +15,7 @@ from homeassistant.components.notify import ( ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["twilio==5.4.0"] +REQUIREMENTS = ["twilio==5.7.0"] CONF_ACCOUNT_SID = "account_sid" diff --git a/homeassistant/components/notify/twilio_sms.py b/homeassistant/components/notify/twilio_sms.py index 950e0eed221..ab3ac89e6b2 100644 --- a/homeassistant/components/notify/twilio_sms.py +++ b/homeassistant/components/notify/twilio_sms.py @@ -13,7 +13,7 @@ from homeassistant.components.notify import ( ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["twilio==5.4.0"] +REQUIREMENTS = ["twilio==5.7.0"] CONF_ACCOUNT_SID = "account_sid" diff --git a/requirements_all.txt b/requirements_all.txt index 1a6d008ab35..ea4ebc95ecb 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -636,7 +636,7 @@ transmissionrpc==0.11 # homeassistant.components.notify.twilio_call # homeassistant.components.notify.twilio_sms -twilio==5.4.0 +twilio==5.7.0 # homeassistant.components.sensor.uber uber_rides==0.2.7 From 379ae11405a2bcd775784e1e61de98682d9778c9 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sat, 28 Jan 2017 12:45:52 -0800 Subject: [PATCH 188/191] Minor style fixes --- homeassistant/components/notify/twilio_call.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index b1cc8e5eeed..2ae00081240 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -7,7 +7,6 @@ https://home-assistant.io/components/notify.twilio_call/ import logging import urllib - import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -63,8 +62,8 @@ class TwilioCallNotificationService(BaseNotificationService): if message.startswith("http://"): twimlet_url = message else: - twimlet_url = 'http://twimlets.com/message?Message=' - twimlet_url += urllib.parse.quote(message, safe='') + twimlet_url = "http://twimlets.com/message?Message=" + twimlet_url += urllib.parse.quote(message, safe="") for target in targets: try: From b7bb31cb95070f0b485cd4348426ec8ef10318ab Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sat, 28 Jan 2017 12:46:34 -0800 Subject: [PATCH 189/191] Allow both http and https URLs --- homeassistant/components/notify/twilio_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index 2ae00081240..374e77b9507 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -59,7 +59,7 @@ class TwilioCallNotificationService(BaseNotificationService): _LOGGER.info("At least 1 target is required") return - if message.startswith("http://"): + if message.startswith(("http://", "https://")): twimlet_url = message else: twimlet_url = "http://twimlets.com/message?Message=" From b48a7e40070687cc469887be004d70ed9049952f Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sun, 29 Jan 2017 00:52:30 +0200 Subject: [PATCH 190/191] Bugfix customize (#5613) --- homeassistant/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/config.py b/homeassistant/config.py index d8e30987e16..23d52f6bc66 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -258,7 +258,6 @@ def async_process_ha_core_config(hass, config): This method is a coroutine. """ - print(CORE_CONFIG_SCHEMA) config = CORE_CONFIG_SCHEMA(config) hac = hass.config From 8bcb26b90c34490cfddae3b3c933bb1cbb1ac5c3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Jan 2017 15:13:17 -0800 Subject: [PATCH 191/191] Version bump to 0.37 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index a56140f8076..2bde15106d2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 37 -PATCH_VERSION = '0.dev0' +PATCH_VERSION = '0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4, 2)