From 4ac9e9fc4c61cf94515a32c091126acbb52c0e62 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Thu, 8 Oct 2015 17:48:03 -0500 Subject: [PATCH 001/744] initial commit --- .../components/thermostat/radiotherm.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 homeassistant/components/thermostat/radiotherm.py diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py new file mode 100644 index 00000000000..28899fac56e --- /dev/null +++ b/homeassistant/components/thermostat/radiotherm.py @@ -0,0 +1,109 @@ +""" +homeassistant.components.thermostat.radiotherm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Adds support for Radio Thermostat wifi-enabled home thermostats +""" +import logging + +from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, + STATE_IDLE, STATE_HEAT) +from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) +from urllib.error import URLError + +#TODO: investigate why this fails +# REQUIREMENTS = ['radiotherm-1.2'] + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Radio Thermostat. """ + logger = logging.getLogger(__name__) + + try: + import radiotherm + except ImportError: + logger.exception( + "Error while importing dependency radiotherm. " + "Did you maybe not install the radiotherm dependency?") + return + + host = config.get(CONF_HOST) + if host is None: + logger.error("host not defined in config.") + return + + try: + tstat = radiotherm.get_thermostat(host) + except URLError as err: + logger.Exception( + "Unable to connect to Radio Thermostat") + return + + add_devices([RadioThermostat(tstat)]) + + +class RadioThermostat(ThermostatDevice): + """ Represent a Radio Thermostat. """ + + def __init__(self, device, name=None): + self.device = device + self._name = name + + @property + def name(self): + """ Returns the name of the Radio Thermostat. """ + return 'radiothermostat' + #return self.device.name + + @property + def unit_of_measurement(self): + """ Unit of measurement this thermostat expresses itself in. """ + return TEMP_FAHRENHEIT + + @property + def device_state_attributes(self): + """ Returns device specific state attributes. """ + # Move these to Thermostat Device and make them global + return { + "humidity": None, + "target_humidity": None, + "fan": self.device.fmode['human'], + "mode": self.device.tmode['human'] + } + + + @property + def current_temperature(self): + """ Returns the current temperature. """ + return self.device.temp['raw'] + + @property + def operation(self): + """ Returns current operation. head, cool idle """ + if self.device.tmode['human'] == 'Cool': + return STATE_COOL + elif self.device.tmode['human'] == 'Heat': + return STATE_HEAT + else: + return STATE_IDLE + + @property + def target_temperature(self): + """ Returns the temperature we try to reach. """ + + if self.operation == STATE_COOL: + temp = self.device.t_cool['raw'] + elif self.operation == STATE_HEAT: + temp = self.device.t_heat['raw'] + + return round(temp, 1) + + + def set_temperate(self, temperature): + """ Set new target temperature """ + if self.operation == STATE_COOL: + self.device.t_cool = temperature + elif self.operation == STATE_HEAT: + self.device.t_heat + + + From e5d68d8a1e32e09d25c534a5eac8b5159a6301cb Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 10:43:14 -0500 Subject: [PATCH 002/744] set name of device through hass config --- homeassistant/components/thermostat/radiotherm.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 28899fac56e..229fed220cd 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -27,6 +27,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return host = config.get(CONF_HOST) + name = config.get(CONF_NAME) if host is None: logger.error("host not defined in config.") return @@ -38,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Unable to connect to Radio Thermostat") return - add_devices([RadioThermostat(tstat)]) + add_devices([RadioThermostat(tstat, name)]) class RadioThermostat(ThermostatDevice): @@ -46,13 +47,13 @@ class RadioThermostat(ThermostatDevice): def __init__(self, device, name=None): self.device = device - self._name = name + if name: + self.set_name(name) @property def name(self): """ Returns the name of the Radio Thermostat. """ - return 'radiothermostat' - #return self.device.name + return self.device.name['raw'] @property def unit_of_measurement(self): @@ -98,12 +99,16 @@ class RadioThermostat(ThermostatDevice): return round(temp, 1) - def set_temperate(self, temperature): + def set_temperature(self, temperature): """ Set new target temperature """ if self.operation == STATE_COOL: self.device.t_cool = temperature elif self.operation == STATE_HEAT: self.device.t_heat + def set_name(self, name): + """ Set thermostat name """ + self.device.name = name + From fc1cf49fd3392624202d9a03696e43126c4ad55e Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 10:49:54 -0500 Subject: [PATCH 003/744] added REQUIREMENTS for radiotherm python module --- homeassistant/components/thermostat/radiotherm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 229fed220cd..8936ab15292 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -10,8 +10,7 @@ from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) from urllib.error import URLError -#TODO: investigate why this fails -# REQUIREMENTS = ['radiotherm-1.2'] +REQUIREMENTS = ['radiotherm'] def setup_platform(hass, config, add_devices, discovery_info=None): From 0cf909cce9ba47f34297e87ae800f49b7ea4e18a Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 11:34:14 -0500 Subject: [PATCH 004/744] Correct ci failed tests --- homeassistant/components/thermostat/radiotherm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 8936ab15292..dcdb70840d4 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -33,8 +33,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: tstat = radiotherm.get_thermostat(host) - except URLError as err: - logger.Exception( + except URLError: + logger.exception( "Unable to connect to Radio Thermostat") return @@ -103,7 +103,7 @@ class RadioThermostat(ThermostatDevice): if self.operation == STATE_COOL: self.device.t_cool = temperature elif self.operation == STATE_HEAT: - self.device.t_heat + self.device.t_heat = temperature def set_name(self, name): """ Set thermostat name """ From a3d295d88588f1dfd08d1dd23d7feb16fd4762f3 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 11:38:39 -0500 Subject: [PATCH 005/744] Correct formatting --- homeassistant/components/thermostat/radiotherm.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index dcdb70840d4..6aee1aa061d 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -70,7 +70,6 @@ class RadioThermostat(ThermostatDevice): "mode": self.device.tmode['human'] } - @property def current_temperature(self): """ Returns the current temperature. """ @@ -97,7 +96,6 @@ class RadioThermostat(ThermostatDevice): return round(temp, 1) - def set_temperature(self, temperature): """ Set new target temperature """ if self.operation == STATE_COOL: @@ -108,6 +106,3 @@ class RadioThermostat(ThermostatDevice): def set_name(self, name): """ Set thermostat name """ self.device.name = name - - - From 37278aab20e8a85357eac2ed3a2e38747704c309 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Sat, 10 Oct 2015 11:36:34 -0500 Subject: [PATCH 006/744] add set_time and begin discovery --- .../components/thermostat/radiotherm.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 6aee1aa061d..b0ae38461b3 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -4,13 +4,14 @@ homeassistant.components.thermostat.radiotherm Adds support for Radio Thermostat wifi-enabled home thermostats """ import logging +import datetime +from urllib.error import URLError from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) -from urllib.error import URLError -REQUIREMENTS = ['radiotherm'] +REQUIREMENTS = ['radiotherm==1.2'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -25,6 +26,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Did you maybe not install the radiotherm dependency?") return + hosts = [] + logger.info(discovery_info) + + host = config.get(CONF_HOST) name = config.get(CONF_NAME) if host is None: @@ -48,6 +53,7 @@ class RadioThermostat(ThermostatDevice): self.device = device if name: self.set_name(name) + self.set_time() @property def name(self): @@ -106,3 +112,9 @@ class RadioThermostat(ThermostatDevice): def set_name(self, name): """ Set thermostat name """ self.device.name = name + + def set_time(self): + """ Set device time """ + now = datetime.datetime.now() + self.device.time = {'day': now.weekday(), + 'hour': now.hour, 'minute': now.minute} From 84c72ebf634e0f89c381b46b44e9d050a053b9d0 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Sun, 11 Oct 2015 09:28:25 -0500 Subject: [PATCH 007/744] Add support for multiple thermostats (via hass-config) and auto-discovery via ratiotherm module --- .../components/thermostat/radiotherm.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index b0ae38461b3..e11f845b0b4 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -26,24 +26,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Did you maybe not install the radiotherm dependency?") return + # Detect hosts with hass discovery, config or radiotherm discovery hosts = [] - logger.info(discovery_info) + if discovery_info: + logger.info('hass radiotherm discovery', discovery_info) + elif CONF_HOST in config: + hosts = [config[CONF_HOST]] + else: + hosts.append(radiotherm.discover.discover_address()) - - host = config.get(CONF_HOST) name = config.get(CONF_NAME) - if host is None: - logger.error("host not defined in config.") + if hosts is None: + logger.error("no radiotherm thermostats detected") return - try: - tstat = radiotherm.get_thermostat(host) - except URLError: - logger.exception( - "Unable to connect to Radio Thermostat") - return + tstats = [] - add_devices([RadioThermostat(tstat, name)]) + for host in hosts: + try: + tstat = radiotherm.get_thermostat(host) + tstats.append(RadioThermostat(tstat)) + except (URLError, OSError): + logger.exception( + "Unable to connect to Radio Thermostat @{}".format(host)) + + add_devices(tstats) class RadioThermostat(ThermostatDevice): From 6c1c2430002db665a82266230a39a31e7b509c1a Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Sun, 11 Oct 2015 11:42:24 -0500 Subject: [PATCH 008/744] start away mode --- .../components/thermostat/radiotherm.py | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index e11f845b0b4..850bdcc2b83 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -9,7 +9,7 @@ from urllib.error import URLError from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) -from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) +from homeassistant.const import (CONF_HOST, TEMP_FAHRENHEIT) REQUIREMENTS = ['radiotherm==1.2'] @@ -29,13 +29,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Detect hosts with hass discovery, config or radiotherm discovery hosts = [] if discovery_info: - logger.info('hass radiotherm discovery', discovery_info) + logger.info('hass radiotherm discovery: %s', discovery_info) elif CONF_HOST in config: hosts = [config[CONF_HOST]] else: hosts.append(radiotherm.discover.discover_address()) - name = config.get(CONF_NAME) if hosts is None: logger.error("no radiotherm thermostats detected") return @@ -48,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): tstats.append(RadioThermostat(tstat)) except (URLError, OSError): logger.exception( - "Unable to connect to Radio Thermostat @{}".format(host)) + "Unable to connect to Radio Thermostat: %s", host) add_devices(tstats) @@ -61,12 +60,19 @@ class RadioThermostat(ThermostatDevice): if name: self.set_name(name) self.set_time() + self._away = False + self._away_cool = 82 + self._away_heat = 70 @property def name(self): """ Returns the name of the Radio Thermostat. """ return self.device.name['raw'] + def set_name(self, name): + """ Set thermostat name """ + self.device.name = name + @property def unit_of_measurement(self): """ Unit of measurement this thermostat expresses itself in. """ @@ -116,9 +122,18 @@ class RadioThermostat(ThermostatDevice): elif self.operation == STATE_HEAT: self.device.t_heat = temperature - def set_name(self, name): - """ Set thermostat name """ - self.device.name = name + @property + def is_away_mode_on(self): + """ Returns away mode """ + return self._away + + def turn_away_mode_on(self): + """ Turns away mode on. """ + self._away = True + + def turn_away_mode_off(self): + """ Turns away mode off. """ + self._away = False def set_time(self): """ Set device time """ From b2e39884f90ccfbe965ddece44d04c4457916633 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Wed, 14 Oct 2015 11:02:07 -0500 Subject: [PATCH 009/744] Removed name and netdisco functions, implemented update method to caches values, radiotherm lib to coveragerc and requirements_all.txt --- .coveragerc | 1 + .../components/thermostat/radiotherm.py | 69 ++++++++----------- requirements_all.txt | 3 + 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/.coveragerc b/.coveragerc index 1ff145e2de3..e44a905a0ab 100644 --- a/.coveragerc +++ b/.coveragerc @@ -88,6 +88,7 @@ omit = homeassistant/components/switch/transmission.py homeassistant/components/switch/wemo.py homeassistant/components/thermostat/nest.py + homeassistant/components/thermostat/radiotherm.py [report] diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index e11f845b0b4..3acd3ef8986 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -9,33 +9,23 @@ from urllib.error import URLError from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) -from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) +from homeassistant.const import (CONF_HOST, TEMP_FAHRENHEIT) REQUIREMENTS = ['radiotherm==1.2'] def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Radio Thermostat. """ + import radiotherm + logger = logging.getLogger(__name__) - try: - import radiotherm - except ImportError: - logger.exception( - "Error while importing dependency radiotherm. " - "Did you maybe not install the radiotherm dependency?") - return - - # Detect hosts with hass discovery, config or radiotherm discovery hosts = [] - if discovery_info: - logger.info('hass radiotherm discovery', discovery_info) - elif CONF_HOST in config: + if CONF_HOST in config: hosts = [config[CONF_HOST]] else: hosts.append(radiotherm.discover.discover_address()) - name = config.get(CONF_NAME) if hosts is None: logger.error("no radiotherm thermostats detected") return @@ -48,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): tstats.append(RadioThermostat(tstat)) except (URLError, OSError): logger.exception( - "Unable to connect to Radio Thermostat @{}".format(host)) + "Unable to connect to Radio Thermostat: %s", host) add_devices(tstats) @@ -56,16 +46,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class RadioThermostat(ThermostatDevice): """ Represent a Radio Thermostat. """ - def __init__(self, device, name=None): + def __init__(self, device): self.device = device - if name: - self.set_name(name) self.set_time() + self._target_temperature = None + self._current_temperature = None + self._operation = STATE_IDLE + self._name = None + self.update() @property def name(self): """ Returns the name of the Radio Thermostat. """ - return self.device.name['raw'] + return self._name @property def unit_of_measurement(self): @@ -75,10 +68,7 @@ class RadioThermostat(ThermostatDevice): @property def device_state_attributes(self): """ Returns device specific state attributes. """ - # Move these to Thermostat Device and make them global return { - "humidity": None, - "target_humidity": None, "fan": self.device.fmode['human'], "mode": self.device.tmode['human'] } @@ -86,39 +76,38 @@ class RadioThermostat(ThermostatDevice): @property def current_temperature(self): """ Returns the current temperature. """ - return self.device.temp['raw'] + return round(self._current_temperature, 1) @property def operation(self): """ Returns current operation. head, cool idle """ - if self.device.tmode['human'] == 'Cool': - return STATE_COOL - elif self.device.tmode['human'] == 'Heat': - return STATE_HEAT - else: - return STATE_IDLE + return self._operation @property def target_temperature(self): """ Returns the temperature we try to reach. """ - if self.operation == STATE_COOL: - temp = self.device.t_cool['raw'] - elif self.operation == STATE_HEAT: - temp = self.device.t_heat['raw'] + return round(self._target_temperature, 1) - return round(temp, 1) + def update(self): + self._current_temperature = self.device.temp['raw'] + self._name = self.device.name['raw'] + if self.device.tmode['human'] == 'Cool': + self._target_temperature = self.device.t_cool['raw'] + self._operation = STATE_COOL + elif self.device.tmode['human'] == 'Heat': + self._target_temperature = self.device.t_heat['raw'] + self._operation = STATE_HEAT + else: + self._operation = STATE_IDLE def set_temperature(self, temperature): """ Set new target temperature """ - if self.operation == STATE_COOL: + if self._operation == STATE_COOL: self.device.t_cool = temperature - elif self.operation == STATE_HEAT: + elif self._operation == STATE_HEAT: self.device.t_heat = temperature - - def set_name(self, name): - """ Set thermostat name """ - self.device.name = name + self.device.hold = 1 def set_time(self): """ Set device time """ diff --git a/requirements_all.txt b/requirements_all.txt index 2b7074d91cd..668cedf65b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -137,3 +137,6 @@ SoCo==0.11.1 # PlexAPI (media_player.plex) https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 + +# Radio Thermostat (thermostat.radiotherm) +radiotherm=1.2 From 347597ebdc20707451d3469b20ffc0307032c482 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 14 Oct 2015 23:09:52 -0700 Subject: [PATCH 010/744] Base Script on entity --- homeassistant/components/script.py | 240 +++++++++++++++++------------ tests/components/test_script.py | 217 ++++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 98 deletions(-) create mode 100644 tests/components/test_script.py diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index c4f70b6d6d3..c5b1d4872de 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -8,154 +8,198 @@ by the user or automatically based upon automation events, etc. import logging from datetime import timedelta import homeassistant.util.dt as date_util +from itertools import islice import threading -from homeassistant.helpers.event import track_point_in_time +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import ToggleEntity +from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.util import split_entity_id from homeassistant.const import ( - STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, EVENT_TIME_CHANGED) + ATTR_ENTITY_ID, EVENT_TIME_CHANGED, STATE_ON, SERVICE_TURN_ON, + SERVICE_TURN_OFF) DOMAIN = "script" +ENTITY_ID_FORMAT = DOMAIN + '.{}' DEPENDENCIES = ["group"] +STATE_NOT_RUNNING = 'Not Running' + CONF_ALIAS = "alias" -CONF_SERVICE = "execute_service" +CONF_SERVICE = "service" +CONF_SERVICE_OLD = "execute_service" CONF_SERVICE_DATA = "service_data" CONF_SEQUENCE = "sequence" CONF_EVENT = "event" CONF_EVENT_DATA = "event_data" CONF_DELAY = "delay" -ATTR_ENTITY_ID = "entity_id" + +ATTR_LAST_ACTION = 'last_action' _LOGGER = logging.getLogger(__name__) +def is_on(hass, entity_id): + """ Returns if the switch is on based on the statemachine. """ + return hass.states.is_state(entity_id, STATE_ON) + + +def turn_on(hass, entity_id): + """ Turn script on. """ + _, object_id = split_entity_id(entity_id) + + hass.services.call(DOMAIN, object_id) + + +def turn_off(hass, entity_id): + """ Turn script on. """ + hass.services.call(DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}) + + def setup(hass, config): """ Load the scripts from the configuration. """ - scripts = [] + component = EntityComponent(_LOGGER, DOMAIN, hass) + + def service_handler(service): + """ Execute a service call to script. - - - - - \ No newline at end of file + } \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 055403f9d38..ac34b5fc269 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 055403f9d3892118243c8a64a341bce5b74ce8aa +Subproject commit ac34b5fc26928238343f34f334a6f6f1a19d5442 From f1aa685cf2b1c33ea79aa5289c7f7b30c9f92494 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 21:00:22 -0700 Subject: [PATCH 148/744] Add version to config API --- homeassistant/core.py | 3 ++- tests/test_core.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index b834efce406..fc1ff2e8cbe 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -17,7 +17,7 @@ import functools as ft from collections import namedtuple from homeassistant.const import ( - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, + __version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL, EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED, @@ -741,6 +741,7 @@ class Config(object): 'location_name': self.location_name, 'time_zone': time_zone.zone, 'components': self.components, + 'version': __version__ } diff --git a/tests/test_core.py b/tests/test_core.py index 01ede9e138e..bb59aac03fa 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -21,7 +21,7 @@ from homeassistant.exceptions import ( import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_state_change from homeassistant.const import ( - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, + __version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, ATTR_FRIENDLY_NAME, TEMP_CELCIUS, TEMP_FAHRENHEIT) @@ -555,6 +555,7 @@ class TestConfig(unittest.TestCase): 'location_name': None, 'time_zone': 'UTC', 'components': [], + 'version': __version__, } self.assertEqual(expected, self.config.as_dict()) From 06c8c1b168d7c7a494de6ff3054e5d0fd65da87f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 23:11:50 -0700 Subject: [PATCH 149/744] Update to latest version frontend --- homeassistant/components/frontend/version.py | 2 +- .../components/frontend/www_static/frontend.html | 16 ++++++++-------- .../frontend/www_static/home-assistant-polymer | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 758c92dc3e9..1c753d1638e 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "231652c54795bcc542269ff03ec2e9b2" +VERSION = "beb922c55bb26ea576581b453f6d7c04" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index f17de5a2781..7343bd3afd0 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -5993,12 +5993,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN font-weight: 300; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - } \ No newline at end of file +console.group&&(console.groupCollapsed("Dispatch: %s",t),console.group("payload"),console.debug(e),console.groupEnd())},e.dispatchError=function(t){console.group&&(console.debug("Dispatch error: "+t),console.groupEnd())},e.storeHandled=function(t,e,n){console.group&&e!==n&&console.debug("Store "+t+" handled action")},e.dispatchEnd=function(t){console.group&&(console.debug("Dispatch done, new state: ",t.toJS()),console.groupEnd())}},function(t,e,n){function r(t,e){this.__prevState=t,this.__evaluator=e,this.__prevValues=i.Map(),this.__observers=[]}var i=n(2),o=n(7),u=n(8);Object.defineProperty(r.prototype,"notifyObservers",{writable:!0,configurable:!0,value:function(t){if(this.__observers.length>0){var e=i.Map();this.__observers.forEach(function(n){var r,i=n.getter,a=o(i),s=this.__prevState;this.__prevValues.has(a)?r=this.__prevValues.get(a):(r=this.__evaluator.evaluate(s,i),this.__prevValues=this.__prevValues.set(a,r));var c=this.__evaluator.evaluate(t,i);u(r,c)||(n.handler.call(null,c),e=e.set(a,c))}.bind(this)),this.__prevValues=e}this.__prevState=t}}),Object.defineProperty(r.prototype,"onChange",{writable:!0,configurable:!0,value:function(t,e){var n={getter:t,handler:e};return this.__observers.push(n),function(){var t=this.__observers.indexOf(n);t>-1&&this.__observers.splice(t,1)}.bind(this)}}),Object.defineProperty(r.prototype,"reset",{writable:!0,configurable:!0,value:function(t){this.__prevState=t,this.__prevValues=i.Map(),this.__observers=[]}}),t.exports=r},function(t,e,n){var r=n(2);t.exports=function(t,e){if(t.hasOwnProperty("__hashCode"))return t.__hashCode;var n=r.fromJS(t).hashCode();return e||(Object.defineProperty(t,"__hashCode",{enumerable:!1,configurable:!1,writable:!1,value:n}),Object.freeze(t)),n}},function(t,e,n){var r=n(2);t.exports=function(t,e){return r.is(t,e)}},function(t,e,n){function r(t){return s(t)&&a(t[t.length-1])}function i(t){return t[t.length-1]}function o(t){return t.slice(0,t.length-1)}function u(t){if(!c(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,l]}var a=n(3).isFunction,s=n(3).isArray,c=n(10).isKeyPath,l=function(t){return t};t.exports={isGetter:r,getComputeFn:i,getDeps:o,fromKeyPath:u}},function(t,e,n){var r=n(3).isArray,i=n(3).isFunction;e.isKeyPath=function(t){return r(t)&&!i(t[t.length-1])}},function(t,e,n){function r(){this.__cachedGetters=i.Map({})}var i=n(2),o=n(1).toImmutable,u=n(7),a=n(8),s=n(9).getComputeFn,c=n(9).getDeps,l=n(10).isKeyPath,f=n(9).isGetter,d=!1;Object.defineProperty(r.prototype,"evaluate",{writable:!0,configurable:!0,value:function(t,e){if(l(e))return t.getIn(e);if(!f(e))throw new Error("evaluate must be passed a keyPath or Getter");var n=u(e);if(this.__isCached(t,e))return this.__cachedGetters.getIn([n,"value"]);var r=c(e).map(function(e){return this.evaluate(t,e)}.bind(this));if(this.__hasStaleValue(t,e)){var i=this.__cachedGetters.getIn([n,"args"]);if(a(i,o(r))){var p=this.__cachedGetters.getIn([n,"value"]);return this.__cacheValue(t,e,i,p),p}}if(d===!0)throw d=!1,new Error("Evaluate may not be called within a Getters computeFn");var h;d=!0;try{h=s(e).apply(null,r),d=!1}catch(_){throw d=!1,_}return this.__cacheValue(t,e,r,h),h}}),Object.defineProperty(r.prototype,"__hasStaleValue",{writable:!0,configurable:!0,value:function(t,e){var n=u(e),r=this.__cachedGetters;return r.has(n)&&r.getIn([n,"stateHashCode"])!==t.hashCode()}}),Object.defineProperty(r.prototype,"__cacheValue",{writable:!0,configurable:!0,value:function(t,e,n,r){var a=u(e);this.__cachedGetters=this.__cachedGetters.set(a,i.Map({value:r,args:o(n),stateHashCode:t.hashCode()}))}}),Object.defineProperty(r.prototype,"__isCached",{writable:!0,configurable:!0,value:function(t,e){var n=u(e);return this.__cachedGetters.hasIn([n,"value"])&&this.__cachedGetters.getIn([n,"stateHashCode"])===t.hashCode()}}),Object.defineProperty(r.prototype,"untrack",{writable:!0,configurable:!0,value:function(t){}}),Object.defineProperty(r.prototype,"reset",{writable:!0,configurable:!0,value:function(){this.__cachedGetters=i.Map({})}}),t.exports=r},function(t,e,n){function r(t,e){var n={};return i(e,function(e,r){n[r]=t.evaluate(e)}),n}var i=n(3).each;t.exports=function(t){return{getInitialState:function(){return r(t,this.getDataBindings())},componentDidMount:function(){var e=this;e.__unwatchFns=[],i(this.getDataBindings(),function(n,r){var i=t.observe(n,function(t){var n={};n[r]=t,e.setState(n)});e.__unwatchFns.push(i)})},componentWillUnmount:function(){for(;this.__unwatchFns.length;)this.__unwatchFns.shift()()}}}},function(t,e,n){function r(t){return this instanceof r?(this.__handlers=o({}),t&&u(this,t),void this.initialize()):new r(t)}function i(t){return t instanceof r}var o=n(2).Map,u=n(3).extend,a=n(1).toJS,s=n(1).toImmutable;Object.defineProperty(r.prototype,"initialize",{writable:!0,configurable:!0,value:function(){}}),Object.defineProperty(r.prototype,"getInitialState",{writable:!0,configurable:!0,value:function(){return o()}}),Object.defineProperty(r.prototype,"handle",{writable:!0,configurable:!0,value:function(t,e,n){var r=this.__handlers.get(e);return"function"==typeof r?r.call(this,t,n,e):t}}),Object.defineProperty(r.prototype,"handleReset",{writable:!0,configurable:!0,value:function(t){return this.getInitialState()}}),Object.defineProperty(r.prototype,"on",{writable:!0,configurable:!0,value:function(t,e){this.__handlers=this.__handlers.set(t,e)}}),Object.defineProperty(r.prototype,"serialize",{writable:!0,configurable:!0,value:function(t){return a(t)}}),Object.defineProperty(r.prototype,"deserialize",{writable:!0,configurable:!0,value:function(t){return s(t)}}),t.exports=r,t.exports.isStore=i}])})},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(121),u=r(o);e["default"]=u["default"](i.reactor),t.exports=e["default"]},function(t,e){"use strict";var n=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};t.exports=n},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(130),o=r(i);e.callApi=o["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(146),u=i(o),a=n(147),s=r(a),c=u["default"];e.actions=c;var l=s;e.getters=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){t.registerStores({restApiCache:l["default"]})}function o(t){return[["restApiCache",t.entity],function(t){return!!t}]}function u(t){return[["restApiCache",t.entity],function(t){return t||s.toImmutable({})}]}function a(t){return function(e){return["restApiCache",t.entity,e]}}Object.defineProperty(e,"__esModule",{value:!0}),e.register=i,e.createHasDataGetter=o,e.createEntityMapGetter=u,e.createByIdGetter=a;var s=n(3),c=n(170),l=r(c),f=n(169),d=r(f);e.createApiActions=d["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(80),n(37),e["default"]=new o["default"]({is:"state-info",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"partial-base",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1}},computeMenuButtonClass:function(t,e){return!t&&e?"invisible":""},toggleMenu:function(){this.fire("open-menu")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({ENTITY_HISTORY_DATE_SELECTED:null,ENTITY_HISTORY_FETCH_START:null,ENTITY_HISTORY_FETCH_ERROR:null,ENTITY_HISTORY_FETCH_SUCCESS:null,RECENT_ENTITY_HISTORY_FETCH_START:null,RECENT_ENTITY_HISTORY_FETCH_ERROR:null,RECENT_ENTITY_HISTORY_FETCH_SUCCESS:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({LOGBOOK_DATE_SELECTED:null,LOGBOOK_ENTRIES_FETCH_START:null,LOGBOOK_ENTRIES_FETCH_ERROR:null,LOGBOOK_ENTRIES_FETCH_SUCCESS:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(171),u=i(o),a=n(57),s=r(a),c=u["default"];e.actions=c;var l=s;e.getters=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({VALIDATING_AUTH_TOKEN:null,VALID_AUTH_TOKEN:null,INVALID_AUTH_TOKEN:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({authAttempt:a["default"],authCurrent:c["default"],rememberAuth:f["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(133),a=i(u),s=n(134),c=i(s),l=n(135),f=i(l),d=n(131),p=r(d),h=n(132),_=r(h),v=p;e.actions=v;var y=_;e.getters=y},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var u=function(){function t(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var u,a=t[Symbol.iterator]();!(r=(u=a.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(s){i=!0,o=s}finally{try{!r&&a["return"]&&a["return"]()}finally{if(i)throw o}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),a=function(){function t(t,e){for(var n=0;n5?"value big":"value"}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"loading-box"}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(122),a=r(u);n(39),n(120),n(119),n(116),n(118),n(117),e["default"]=new o["default"]({is:"state-card-content",properties:{stateObj:{type:Object,observer:"stateObjChanged"}},stateObjChanged:function(t,e){var n=o["default"].dom(this);if(!t)return void(n.lastChild&&n.removeChild(n.lastChild));var r=a["default"](t);if(e&&a["default"](e)===r)n.lastChild.stateObj=t;else{n.lastChild&&n.removeChild(n.lastChild);var i=document.createElement("state-card-"+r);i.stateObj=t,n.appendChild(i)}}}),t.exports=e["default"]},function(t,e){"use strict";function n(t,e){return t?e.map(function(e){return e in t.attributes?"has-"+e:""}).join(" "):""}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return i.reactor.evaluate(i.serviceGetters.canToggleEntity(t))}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=r;var i=n(2);t.exports=e["default"]},function(t,e,n){"use strict";function r(t,e,n){function r(){y&&clearTimeout(y),p&&clearTimeout(p),g=0,p=y=m=void 0}function s(e,n){n&&clearTimeout(n),p=y=m=void 0,e&&(g=o(),h=t.apply(v,d),y||p||(d=v=void 0))}function c(){var t=e-(o()-_);0>=t||t>e?s(m,p):y=setTimeout(c,t)}function l(){s(O,y)}function f(){if(d=arguments,_=o(),v=this,m=O&&(y||!w),b===!1)var n=w&&!y;else{p||w||(g=_);var r=b-(_-g),i=0>=r||r>b;i?(p&&(p=clearTimeout(p)),g=_,h=t.apply(v,d)):p||(p=setTimeout(l,r))}return i&&y?y=clearTimeout(y):y||e===b||(y=setTimeout(c,e)),n&&(i=!0,h=t.apply(v,d)),!i||y||p||(d=v=void 0),h}var d,p,h,_,v,y,m,g=0,b=!1,O=!0;if("function"!=typeof t)throw new TypeError(u);if(e=0>e?0:+e||0,n===!0){var w=!0;O=!1}else i(n)&&(w=!!n.leading,b="maxWait"in n&&a(+n.maxWait||0,e),O="trailing"in n?!!n.trailing:O);return f.cancel=r,f}var i=n(46),o=n(126),u="Expected a function",a=Math.max;t.exports=r},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({SERVER_CONFIG_LOADED:null,COMPONENT_LOADED:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({serverComponent:a["default"],serverConfig:c["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(138),a=i(u),s=n(139),c=i(s),l=n(136),f=r(l),d=n(137),p=r(d),h=f;e.actions=h;var _=p;e.getters=_},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(148),u=i(o),a=n(149),s=r(a),c=u["default"];e.actions=c;var l=s;e.getters=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({NAVIGATE:null,SHOW_SIDEBAR:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({notifications:a["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(166),a=i(u),s=n(164),c=r(s),l=n(165),f=r(l),d=c;e.actions=d;var p=f;e.getters=p},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({streamStatus:a["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(178),a=i(u),s=n(174),c=r(s),l=n(175),f=r(l),d=c;e.actions=d;var p=f;e.getters=p},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({API_FETCH_ALL_START:null,API_FETCH_ALL_SUCCESS:null,API_FETCH_ALL_FAIL:null,SYNC_SCHEDULED:null,SYNC_SCHEDULE_CANCELLED:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({isFetchingData:a["default"],isSyncScheduled:c["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(180),a=i(u),s=n(181),c=i(s),l=n(179),f=r(l),d=n(60),p=r(d),h=f;e.actions=h;var _=p;e.getters=_},function(t,e){"use strict";function n(t){return t.getFullYear()+"-"+(t.getMonth()+1)+"-"+t.getDate()}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e){"use strict";function n(t){var e=t.split(" "),n=r(e,2),i=n[0],o=n[1],u=i.split(":"),a=r(u,3),s=a[0],c=a[1],l=a[2],f=o.split("-"),d=r(f,3),p=d[0],h=d[1],_=d[2];return new Date(Date.UTC(_,parseInt(h,10)-1,p,s,c,l))}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var u,a=t[Symbol.iterator]();!(r=(u=a.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(s){i=!0,o=s}finally{try{!r&&a["return"]&&a["return"]()}finally{if(i)throw o}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();e["default"]=n,t.exports=e["default"]},function(t,e,n){(function(t){"use strict";!function(e,n){t.exports=n()}(void 0,function(){function e(){return Ln.apply(null,arguments)}function n(t){Ln=t}function r(t){return"[object Array]"===Object.prototype.toString.call(t)}function i(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function o(t,e){var n,r=[];for(n=0;n0)for(n in zn)r=zn[n],i=e[r],"undefined"!=typeof i&&(t[r]=i);return t}function h(t){p(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),Rn===!1&&(Rn=!0,e.updateOffset(this),Rn=!1)}function _(t){return t instanceof h||null!=t&&null!=t._isAMomentObject}function v(t){return 0>t?Math.ceil(t):Math.floor(t)}function y(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=v(e)),n}function m(t,e,n){var r,i=Math.min(t.length,e.length),o=Math.abs(t.length-e.length),u=0;for(r=0;i>r;r++)(n&&t[r]!==e[r]||!n&&y(t[r])!==y(e[r]))&&u++;return u+o}function g(){}function b(t){return t?t.toLowerCase().replace("_","-"):t}function O(t){for(var e,n,r,i,o=0;o0;){if(r=w(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&m(i,n,!0)>=e-1)break;e--}o++}return null}function w(e){var n=null;if(!Hn[e]&&"undefined"!=typeof t&&t&&t.exports)try{n=Nn._abbr,!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),S(n)}catch(r){}return Hn[e]}function S(t,e){var n;return t&&(n="undefined"==typeof e?T(t):M(t,e),n&&(Nn=n)),Nn._abbr}function M(t,e){return null!==e?(e.abbr=t,Hn[t]=Hn[t]||new g,Hn[t].set(e),S(t),Hn[t]):(delete Hn[t],null)}function T(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Nn;if(!r(t)){if(e=w(t))return e;t=[t]}return O(t)}function E(t,e){var n=t.toLowerCase();Yn[n]=Yn[n+"s"]=Yn[e]=t}function j(t){return"string"==typeof t?Yn[t]||Yn[t.toLowerCase()]:void 0}function I(t){var e,n,r={};for(n in t)u(t,n)&&(e=j(n),e&&(r[e]=t[n]));return r}function P(t,n){return function(r){return null!=r?(C(this,t,r),e.updateOffset(this,n),this):D(this,t)}}function D(t,e){return t._d["get"+(t._isUTC?"UTC":"")+e]()}function C(t,e,n){return t._d["set"+(t._isUTC?"UTC":"")+e](n)}function A(t,e){var n;if("object"==typeof t)for(n in t)this.set(n,t[n]);else if(t=j(t),"function"==typeof this[t])return this[t](e);return this}function k(t,e,n){var r=""+Math.abs(t),i=e-r.length,o=t>=0;return(o?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}function x(t,e,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),t&&(Bn[t]=i),e&&(Bn[e[0]]=function(){return k(i.apply(this,arguments),e[1],e[2])}),n&&(Bn[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),t)})}function L(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function N(t){var e,n,r=t.match(Gn);for(e=0,n=r.length;n>e;e++)Bn[r[e]]?r[e]=Bn[r[e]]:r[e]=L(r[e]);return function(i){var o="";for(e=0;n>e;e++)o+=r[e]instanceof Function?r[e].call(i,t):r[e];return o}}function z(t,e){return t.isValid()?(e=R(e,t.localeData()),Un[e]=Un[e]||N(e),Un[e](t)):t.localeData().invalidDate()}function R(t,e){function n(t){return e.longDateFormat(t)||t}var r=5;for(Fn.lastIndex=0;r>=0&&Fn.test(t);)t=t.replace(Fn,n),Fn.lastIndex=0,r-=1;return t}function H(t){return"function"==typeof t&&"[object Function]"===Object.prototype.toString.call(t)}function Y(t,e,n){or[t]=H(e)?e:function(t){return t&&n?n:e}}function G(t,e){return u(or,t)?or[t](e._strict,e._locale):new RegExp(F(t))}function F(t){return t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,n,r,i){return e||n||r||i}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function U(t,e){var n,r=e;for("string"==typeof t&&(t=[t]),"number"==typeof e&&(r=function(t,n){n[e]=y(t)}),n=0;nr;r++){if(i=s([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(o="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(o.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}}function $(t,e){var n;return"string"==typeof e&&(e=t.localeData().monthsParse(e),"number"!=typeof e)?t:(n=Math.min(t.date(),q(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t)}function Z(t){return null!=t?($(this,t),e.updateOffset(this,!0),this):D(this,"Month")}function X(){return q(this.year(),this.month())}function Q(t){var e,n=t._a;return n&&-2===l(t).overflow&&(e=n[sr]<0||n[sr]>11?sr:n[cr]<1||n[cr]>q(n[ar],n[sr])?cr:n[lr]<0||n[lr]>24||24===n[lr]&&(0!==n[fr]||0!==n[dr]||0!==n[pr])?lr:n[fr]<0||n[fr]>59?fr:n[dr]<0||n[dr]>59?dr:n[pr]<0||n[pr]>999?pr:-1,l(t)._overflowDayOfYear&&(ar>e||e>cr)&&(e=cr),l(t).overflow=e),t}function tt(t){e.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function et(t,e){var n=!0;return a(function(){return n&&(tt(t+"\n"+(new Error).stack),n=!1),e.apply(this,arguments)},e)}function nt(t,e){vr[t]||(tt(e),vr[t]=!0)}function rt(t){var e,n,r=t._i,i=yr.exec(r);if(i){for(l(t).iso=!0,e=0,n=mr.length;n>e;e++)if(mr[e][1].exec(r)){t._f=mr[e][0];break}for(e=0,n=gr.length;n>e;e++)if(gr[e][1].exec(r)){t._f+=(i[6]||" ")+gr[e][0];break}r.match(nr)&&(t._f+="Z"),wt(t)}else t._isValid=!1}function it(t){var n=br.exec(t._i);return null!==n?void(t._d=new Date(+n[1])):(rt(t),void(t._isValid===!1&&(delete t._isValid,e.createFromInputFallback(t))))}function ot(t,e,n,r,i,o,u){var a=new Date(t,e,n,r,i,o,u);return 1970>t&&a.setFullYear(t),a}function ut(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function at(t){return st(t)?366:365}function st(t){return t%4===0&&t%100!==0||t%400===0}function ct(){return st(this.year())}function lt(t,e,n){var r,i=n-e,o=n-t.day();return o>i&&(o-=7),i-7>o&&(o+=7),r=Dt(t).add(o,"d"),{week:Math.ceil(r.dayOfYear()/7),year:r.year()}}function ft(t){return lt(t,this._week.dow,this._week.doy).week}function dt(){return this._week.dow}function pt(){return this._week.doy}function ht(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")}function _t(t){var e=lt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")}function vt(t,e,n,r,i){var o,u=6+i-r,a=ut(t,0,1+u),s=a.getUTCDay();return i>s&&(s+=7),n=null!=n?1*n:i,o=1+u+7*(e-1)-s+n,{year:o>0?t:t-1,dayOfYear:o>0?o:at(t-1)+o}}function yt(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")}function mt(t,e,n){return null!=t?t:null!=e?e:n}function gt(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function bt(t){var e,n,r,i,o=[];if(!t._d){for(r=gt(t),t._w&&null==t._a[cr]&&null==t._a[sr]&&Ot(t),t._dayOfYear&&(i=mt(t._a[ar],r[ar]),t._dayOfYear>at(i)&&(l(t)._overflowDayOfYear=!0),n=ut(i,0,t._dayOfYear),t._a[sr]=n.getUTCMonth(),t._a[cr]=n.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=o[e]=r[e];for(;7>e;e++)t._a[e]=o[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[lr]&&0===t._a[fr]&&0===t._a[dr]&&0===t._a[pr]&&(t._nextDay=!0,t._a[lr]=0),t._d=(t._useUTC?ut:ot).apply(null,o),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[lr]=24)}}function Ot(t){var e,n,r,i,o,u,a;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(o=1,u=4,n=mt(e.GG,t._a[ar],lt(Dt(),1,4).year),r=mt(e.W,1),i=mt(e.E,1)):(o=t._locale._week.dow,u=t._locale._week.doy,n=mt(e.gg,t._a[ar],lt(Dt(),o,u).year),r=mt(e.w,1),null!=e.d?(i=e.d,o>i&&++r):i=null!=e.e?e.e+o:o),a=vt(n,r,i,u,o),t._a[ar]=a.year,t._dayOfYear=a.dayOfYear}function wt(t){if(t._f===e.ISO_8601)return void rt(t);t._a=[],l(t).empty=!0;var n,r,i,o,u,a=""+t._i,s=a.length,c=0;for(i=R(t._f,t._locale).match(Gn)||[],n=0;n0&&l(t).unusedInput.push(u),a=a.slice(a.indexOf(r)+r.length),c+=r.length),Bn[o]?(r?l(t).empty=!1:l(t).unusedTokens.push(o),V(o,r,t)):t._strict&&!r&&l(t).unusedTokens.push(o);l(t).charsLeftOver=s-c,a.length>0&&l(t).unusedInput.push(a),l(t).bigHour===!0&&t._a[lr]<=12&&t._a[lr]>0&&(l(t).bigHour=void 0),t._a[lr]=St(t._locale,t._a[lr],t._meridiem),bt(t),Q(t)}function St(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?(r=t.isPM(n),r&&12>e&&(e+=12),r||12!==e||(e=0),e):e}function Mt(t){var e,n,r,i,o;if(0===t._f.length)return l(t).invalidFormat=!0,void(t._d=new Date(NaN));for(i=0;io)&&(r=o,n=e));a(t,n||e)}function Tt(t){if(!t._d){var e=I(t._i);t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],bt(t)}}function Et(t){var e=new h(Q(jt(t)));return e._nextDay&&(e.add(1,"d"),e._nextDay=void 0),e}function jt(t){var e=t._i,n=t._f;return t._locale=t._locale||T(t._l),null===e||void 0===n&&""===e?d({nullInput:!0}):("string"==typeof e&&(t._i=e=t._locale.preparse(e)),_(e)?new h(Q(e)):(r(n)?Mt(t):n?wt(t):i(e)?t._d=e:It(t),t))}function It(t){var n=t._i;void 0===n?t._d=new Date:i(n)?t._d=new Date(+n):"string"==typeof n?it(t):r(n)?(t._a=o(n.slice(0),function(t){return parseInt(t,10)}),bt(t)):"object"==typeof n?Tt(t):"number"==typeof n?t._d=new Date(n):e.createFromInputFallback(t)}function Pt(t,e,n,r,i){var o={};return"boolean"==typeof n&&(r=n,n=void 0),o._isAMomentObject=!0,o._useUTC=o._isUTC=i,o._l=n,o._i=t,o._f=e,o._strict=r,Et(o)}function Dt(t,e,n,r){return Pt(t,e,n,r,!1)}function Ct(t,e){var n,i;if(1===e.length&&r(e[0])&&(e=e[0]),!e.length)return Dt();for(n=e[0],i=1;it&&(t=-t,n="-"),n+k(~~(t/60),2)+e+k(~~t%60,2)})}function zt(t){var e=(t||"").match(nr)||[],n=e[e.length-1]||[],r=(n+"").match(Tr)||["-",0,0],i=+(60*r[1])+y(r[2]);return"+"===r[0]?i:-i}function Rt(t,n){var r,o;return n._isUTC?(r=n.clone(),o=(_(t)||i(t)?+t:+Dt(t))-+r,r._d.setTime(+r._d+o),e.updateOffset(r,!1),r):Dt(t).local()}function Ht(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function Yt(t,n){var r,i=this._offset||0;return null!=t?("string"==typeof t&&(t=zt(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&n&&(r=Ht(this)),this._offset=t,this._isUTC=!0,null!=r&&this.add(r,"m"),i!==t&&(!n||this._changeInProgress?ne(this,Zt(t-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,e.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?i:Ht(this)}function Gt(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}function Ft(t){return this.utcOffset(0,t)}function Ut(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ht(this),"m")),this}function Bt(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(zt(this._i)),this}function Vt(t){return t=t?Dt(t).utcOffset():0,(this.utcOffset()-t)%60===0}function qt(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Wt(){if("undefined"!=typeof this._isDSTShifted)return this._isDSTShifted;var t={};if(p(t,this),t=jt(t),t._a){var e=t._isUTC?s(t._a):Dt(t._a);this._isDSTShifted=this.isValid()&&m(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Kt(){return!this._isUTC}function Jt(){return this._isUTC}function $t(){return this._isUTC&&0===this._offset}function Zt(t,e){var n,r,i,o=t,a=null;return Lt(t)?o={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(o={},e?o[e]=t:o.milliseconds=t):(a=Er.exec(t))?(n="-"===a[1]?-1:1,o={y:0,d:y(a[cr])*n,h:y(a[lr])*n,m:y(a[fr])*n,s:y(a[dr])*n,ms:y(a[pr])*n}):(a=jr.exec(t))?(n="-"===a[1]?-1:1,o={y:Xt(a[2],n),M:Xt(a[3],n),d:Xt(a[4],n),h:Xt(a[5],n),m:Xt(a[6],n),s:Xt(a[7],n),w:Xt(a[8],n)}):null==o?o={}:"object"==typeof o&&("from"in o||"to"in o)&&(i=te(Dt(o.from),Dt(o.to)),o={},o.ms=i.milliseconds,o.M=i.months),r=new xt(o),Lt(t)&&u(t,"_locale")&&(r._locale=t._locale),r}function Xt(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Qt(t,e){var n={milliseconds:0,months:0};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function te(t,e){var n;return e=Rt(e,t),t.isBefore(e)?n=Qt(t,e):(n=Qt(e,t),n.milliseconds=-n.milliseconds,n.months=-n.months),n}function ee(t,e){return function(n,r){var i,o;return null===r||isNaN(+r)||(nt(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period)."),o=n,n=r,r=o),n="string"==typeof n?+n:n,i=Zt(n,r),ne(this,i,t),this}}function ne(t,n,r,i){var o=n._milliseconds,u=n._days,a=n._months;i=null==i?!0:i,o&&t._d.setTime(+t._d+o*r),u&&C(t,"Date",D(t,"Date")+u*r),a&&$(t,D(t,"Month")+a*r),i&&e.updateOffset(t,u||a)}function re(t,e){var n=t||Dt(),r=Rt(n,this).startOf("day"),i=this.diff(r,"days",!0),o=-6>i?"sameElse":-1>i?"lastWeek":0>i?"lastDay":1>i?"sameDay":2>i?"nextDay":7>i?"nextWeek":"sameElse";return this.format(e&&e[o]||this.localeData().calendar(o,this,Dt(n)))}function ie(){return new h(this)}function oe(t,e){var n;return e=j("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=_(t)?t:Dt(t),+this>+t):(n=_(t)?+t:+Dt(t),n<+this.clone().startOf(e))}function ue(t,e){var n;return e=j("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=_(t)?t:Dt(t),+t>+this):(n=_(t)?+t:+Dt(t),+this.clone().endOf(e)e-o?(n=t.clone().add(i-1,"months"),r=(e-o)/(o-n)):(n=t.clone().add(i+1,"months"),r=(e-o)/(n-o)),-(i+r)}function fe(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function de(){var t=this.clone().utc();return 0e;e++)if(this._weekdaysParse[e]||(n=Dt([2e3,1]).day(e),r="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(r.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e}function Fe(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ze(t,this.localeData()),this.add(t-e,"d")):e}function Ue(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")}function Be(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)}function Ve(t,e){x(t,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)})}function qe(t,e){return e._meridiemParse}function We(t){return"p"===(t+"").toLowerCase().charAt(0)}function Ke(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"}function Je(t,e){e[pr]=y(1e3*("0."+t))}function $e(){return this._isUTC?"UTC":""}function Ze(){return this._isUTC?"Coordinated Universal Time":""}function Xe(t){return Dt(1e3*t)}function Qe(){return Dt.apply(null,arguments).parseZone()}function tn(t,e,n){var r=this._calendar[t];return"function"==typeof r?r.call(e,n):r}function en(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t])}function nn(){return this._invalidDate}function rn(t){return this._ordinal.replace("%d",t)}function on(t){return t}function un(t,e,n,r){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,r):i.replace(/%d/i,t)}function an(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)}function sn(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function cn(t,e,n,r){var i=T(),o=s().set(r,e);return i[n](o,t)}function ln(t,e,n,r,i){if("number"==typeof t&&(e=t,t=void 0),t=t||"",null!=e)return cn(t,e,n,i);var o,u=[];for(o=0;r>o;o++)u[o]=cn(t,o,n,i);return u}function fn(t,e){return ln(t,e,"months",12,"month")}function dn(t,e){return ln(t,e,"monthsShort",12,"month")}function pn(t,e){return ln(t,e,"weekdays",7,"day")}function hn(t,e){return ln(t,e,"weekdaysShort",7,"day")}function _n(t,e){return ln(t,e,"weekdaysMin",7,"day")}function vn(){var t=this._data;return this._milliseconds=$r(this._milliseconds),this._days=$r(this._days),this._months=$r(this._months),t.milliseconds=$r(t.milliseconds),t.seconds=$r(t.seconds),t.minutes=$r(t.minutes),t.hours=$r(t.hours),t.months=$r(t.months),t.years=$r(t.years),this}function yn(t,e,n,r){var i=Zt(e,n);return t._milliseconds+=r*i._milliseconds,t._days+=r*i._days,t._months+=r*i._months,t._bubble()}function mn(t,e){return yn(this,t,e,1)}function gn(t,e){return yn(this,t,e,-1)}function bn(t){return 0>t?Math.floor(t):Math.ceil(t)}function On(){var t,e,n,r,i,o=this._milliseconds,u=this._days,a=this._months,s=this._data;return o>=0&&u>=0&&a>=0||0>=o&&0>=u&&0>=a||(o+=864e5*bn(Sn(a)+u),u=0,a=0),s.milliseconds=o%1e3,t=v(o/1e3),s.seconds=t%60,e=v(t/60),s.minutes=e%60,n=v(e/60),s.hours=n%24,u+=v(n/24),i=v(wn(u)),a+=i,u-=bn(Sn(i)),r=v(a/12),a%=12,s.days=u,s.months=a,s.years=r,this}function wn(t){return 4800*t/146097}function Sn(t){return 146097*t/4800}function Mn(t){var e,n,r=this._milliseconds;if(t=j(t),"month"===t||"year"===t)return e=this._days+r/864e5,n=this._months+wn(e),"month"===t?n:n/12;switch(e=this._days+Math.round(Sn(this._months)),t){case"week":return e/7+r/6048e5;case"day":return e+r/864e5;case"hour":return 24*e+r/36e5;case"minute":return 1440*e+r/6e4;case"second":return 86400*e+r/1e3;case"millisecond":return Math.floor(864e5*e)+r;default:throw new Error("Unknown unit "+t)}}function Tn(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*y(this._months/12)}function En(t){return function(){return this.as(t)}}function jn(t){return t=j(t),this[t+"s"]()}function In(t){return function(){return this._data[t]}}function Pn(){return v(this.days()/7)}function Dn(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}function Cn(t,e,n){var r=Zt(t).abs(),i=di(r.as("s")),o=di(r.as("m")),u=di(r.as("h")),a=di(r.as("d")),s=di(r.as("M")),c=di(r.as("y")),l=i0,l[4]=n,Dn.apply(null,l)}function An(t,e){return void 0===pi[t]?!1:void 0===e?pi[t]:(pi[t]=e,!0)}function kn(t){var e=this.localeData(),n=Cn(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)}function xn(){var t,e,n,r=hi(this._milliseconds)/1e3,i=hi(this._days),o=hi(this._months);t=v(r/60),e=v(t/60),r%=60,t%=60,n=v(o/12),o%=12;var u=n,a=o,s=i,c=e,l=t,f=r,d=this.asSeconds();return d?(0>d?"-":"")+"P"+(u?u+"Y":"")+(a?a+"M":"")+(s?s+"D":"")+(c||l||f?"T":"")+(c?c+"H":"")+(l?l+"M":"")+(f?f+"S":""):"P0D"}var Ln,Nn,zn=e.momentProperties=[],Rn=!1,Hn={},Yn={},Gn=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Fn=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Un={},Bn={},Vn=/\d/,qn=/\d\d/,Wn=/\d{3}/,Kn=/\d{4}/,Jn=/[+-]?\d{6}/,$n=/\d\d?/,Zn=/\d{1,3}/,Xn=/\d{1,4}/,Qn=/[+-]?\d{1,6}/,tr=/\d+/,er=/[+-]?\d+/,nr=/Z|[+-]\d\d:?\d\d/gi,rr=/[+-]?\d+(\.\d{1,3})?/,ir=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,or={},ur={},ar=0,sr=1,cr=2,lr=3,fr=4,dr=5,pr=6;x("M",["MM",2],"Mo",function(){return this.month()+1}),x("MMM",0,0,function(t){return this.localeData().monthsShort(this,t)}),x("MMMM",0,0,function(t){return this.localeData().months(this,t)}),E("month","M"),Y("M",$n),Y("MM",$n,qn),Y("MMM",ir),Y("MMMM",ir),U(["M","MM"],function(t,e){e[sr]=y(t)-1}),U(["MMM","MMMM"],function(t,e,n,r){var i=n._locale.monthsParse(t,r,n._strict);null!=i?e[sr]=i:l(n).invalidMonth=t});var hr="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),_r="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),vr={};e.suppressDeprecationWarnings=!1;var yr=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mr=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],gr=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],br=/^\/?Date\((\-?\d+)/i;e.createFromInputFallback=et("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),x(0,["YY",2],0,function(){return this.year()%100}),x(0,["YYYY",4],0,"year"),x(0,["YYYYY",5],0,"year"),x(0,["YYYYYY",6,!0],0,"year"),E("year","y"),Y("Y",er),Y("YY",$n,qn),Y("YYYY",Xn,Kn),Y("YYYYY",Qn,Jn),Y("YYYYYY",Qn,Jn),U(["YYYYY","YYYYYY"],ar),U("YYYY",function(t,n){n[ar]=2===t.length?e.parseTwoDigitYear(t):y(t)}),U("YY",function(t,n){n[ar]=e.parseTwoDigitYear(t)}),e.parseTwoDigitYear=function(t){return y(t)+(y(t)>68?1900:2e3)};var Or=P("FullYear",!1);x("w",["ww",2],"wo","week"),x("W",["WW",2],"Wo","isoWeek"),E("week","w"),E("isoWeek","W"),Y("w",$n),Y("ww",$n,qn),Y("W",$n),Y("WW",$n,qn),B(["w","ww","W","WW"],function(t,e,n,r){e[r.substr(0,1)]=y(t)});var wr={dow:0,doy:6};x("DDD",["DDDD",3],"DDDo","dayOfYear"),E("dayOfYear","DDD"),Y("DDD",Zn),Y("DDDD",Wn),U(["DDD","DDDD"],function(t,e,n){n._dayOfYear=y(t)}),e.ISO_8601=function(){};var Sr=et("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var t=Dt.apply(null,arguments);return this>t?this:t}),Mr=et("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var t=Dt.apply(null,arguments);return t>this?this:t});Nt("Z",":"),Nt("ZZ",""),Y("Z",nr),Y("ZZ",nr),U(["Z","ZZ"],function(t,e,n){n._useUTC=!0,n._tzm=zt(t)});var Tr=/([\+\-]|\d\d)/gi;e.updateOffset=function(){};var Er=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,jr=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Zt.fn=xt.prototype;var Ir=ee(1,"add"),Pr=ee(-1,"subtract");e.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var Dr=et("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return void 0===t?this.localeData():this.locale(t)});x(0,["gg",2],0,function(){return this.weekYear()%100}),x(0,["GG",2],0,function(){return this.isoWeekYear()%100}),De("gggg","weekYear"),De("ggggg","weekYear"),De("GGGG","isoWeekYear"),De("GGGGG","isoWeekYear"),E("weekYear","gg"),E("isoWeekYear","GG"),Y("G",er),Y("g",er),Y("GG",$n,qn),Y("gg",$n,qn),Y("GGGG",Xn,Kn),Y("gggg",Xn,Kn),Y("GGGGG",Qn,Jn),Y("ggggg",Qn,Jn),B(["gggg","ggggg","GGGG","GGGGG"],function(t,e,n,r){e[r.substr(0,2)]=y(t)}),B(["gg","GG"],function(t,n,r,i){n[i]=e.parseTwoDigitYear(t)}),x("Q",0,0,"quarter"),E("quarter","Q"),Y("Q",Vn),U("Q",function(t,e){e[sr]=3*(y(t)-1)}),x("D",["DD",2],"Do","date"),E("date","D"),Y("D",$n),Y("DD",$n,qn),Y("Do",function(t,e){return t?e._ordinalParse:e._ordinalParseLenient}),U(["D","DD"],cr),U("Do",function(t,e){e[cr]=y(t.match($n)[0],10)});var Cr=P("Date",!0);x("d",0,"do","day"),x("dd",0,0,function(t){return this.localeData().weekdaysMin(this,t)}),x("ddd",0,0,function(t){return this.localeData().weekdaysShort(this,t)}),x("dddd",0,0,function(t){return this.localeData().weekdays(this,t)}),x("e",0,0,"weekday"),x("E",0,0,"isoWeekday"),E("day","d"),E("weekday","e"),E("isoWeekday","E"),Y("d",$n),Y("e",$n),Y("E",$n),Y("dd",ir),Y("ddd",ir),Y("dddd",ir),B(["dd","ddd","dddd"],function(t,e,n){var r=n._locale.weekdaysParse(t);null!=r?e.d=r:l(n).invalidWeekday=t}),B(["d","e","E"],function(t,e,n,r){e[r]=y(t)});var Ar="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),kr="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),xr="Su_Mo_Tu_We_Th_Fr_Sa".split("_");x("H",["HH",2],0,"hour"),x("h",["hh",2],0,function(){return this.hours()%12||12}),Ve("a",!0),Ve("A",!1),E("hour","h"),Y("a",qe),Y("A",qe),Y("H",$n),Y("h",$n),Y("HH",$n,qn),Y("hh",$n,qn),U(["H","HH"],lr),U(["a","A"],function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t}),U(["h","hh"],function(t,e,n){e[lr]=y(t),l(n).bigHour=!0});var Lr=/[ap]\.?m?\.?/i,Nr=P("Hours",!0);x("m",["mm",2],0,"minute"),E("minute","m"),Y("m",$n),Y("mm",$n,qn),U(["m","mm"],fr);var zr=P("Minutes",!1);x("s",["ss",2],0,"second"),E("second","s"),Y("s",$n),Y("ss",$n,qn),U(["s","ss"],dr);var Rr=P("Seconds",!1);x("S",0,0,function(){return~~(this.millisecond()/100)}),x(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),x(0,["SSS",3],0,"millisecond"),x(0,["SSSS",4],0,function(){return 10*this.millisecond()}),x(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),x(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),x(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),x(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),x(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),E("millisecond","ms"),Y("S",Zn,Vn),Y("SS",Zn,qn),Y("SSS",Zn,Wn);var Hr;for(Hr="SSSS";Hr.length<=9;Hr+="S")Y(Hr,tr);for(Hr="S";Hr.length<=9;Hr+="S")U(Hr,Je);var Yr=P("Milliseconds",!1);x("z",0,0,"zoneAbbr"),x("zz",0,0,"zoneName");var Gr=h.prototype;Gr.add=Ir,Gr.calendar=re,Gr.clone=ie,Gr.diff=ce,Gr.endOf=Oe,Gr.format=pe,Gr.from=he,Gr.fromNow=_e,Gr.to=ve,Gr.toNow=ye,Gr.get=A,Gr.invalidAt=Pe,Gr.isAfter=oe,Gr.isBefore=ue,Gr.isBetween=ae,Gr.isSame=se,Gr.isValid=je,Gr.lang=Dr,Gr.locale=me,Gr.localeData=ge,Gr.max=Mr,Gr.min=Sr,Gr.parsingFlags=Ie,Gr.set=A,Gr.startOf=be,Gr.subtract=Pr,Gr.toArray=Te,Gr.toObject=Ee,Gr.toDate=Me,Gr.toISOString=de,Gr.toJSON=de,Gr.toString=fe,Gr.unix=Se,Gr.valueOf=we,Gr.year=Or,Gr.isLeapYear=ct,Gr.weekYear=Ae,Gr.isoWeekYear=ke,Gr.quarter=Gr.quarters=Ne,Gr.month=Z,Gr.daysInMonth=X,Gr.week=Gr.weeks=ht,Gr.isoWeek=Gr.isoWeeks=_t,Gr.weeksInYear=Le,Gr.isoWeeksInYear=xe,Gr.date=Cr,Gr.day=Gr.days=Fe,Gr.weekday=Ue,Gr.isoWeekday=Be,Gr.dayOfYear=yt,Gr.hour=Gr.hours=Nr,Gr.minute=Gr.minutes=zr,Gr.second=Gr.seconds=Rr,Gr.millisecond=Gr.milliseconds=Yr,Gr.utcOffset=Yt,Gr.utc=Ft,Gr.local=Ut,Gr.parseZone=Bt,Gr.hasAlignedHourOffset=Vt,Gr.isDST=qt,Gr.isDSTShifted=Wt,Gr.isLocal=Kt,Gr.isUtcOffset=Jt,Gr.isUtc=$t,Gr.isUTC=$t,Gr.zoneAbbr=$e,Gr.zoneName=Ze,Gr.dates=et("dates accessor is deprecated. Use date instead.",Cr),Gr.months=et("months accessor is deprecated. Use month instead",Z),Gr.years=et("years accessor is deprecated. Use year instead",Or),Gr.zone=et("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Gt);var Fr=Gr,Ur={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Br={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Vr="Invalid date",qr="%d",Wr=/\d{1,2}/,Kr={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Jr=g.prototype;Jr._calendar=Ur,Jr.calendar=tn,Jr._longDateFormat=Br,Jr.longDateFormat=en,Jr._invalidDate=Vr,Jr.invalidDate=nn,Jr._ordinal=qr,Jr.ordinal=rn,Jr._ordinalParse=Wr,Jr.preparse=on,Jr.postformat=on,Jr._relativeTime=Kr,Jr.relativeTime=un,Jr.pastFuture=an,Jr.set=sn,Jr.months=W,Jr._months=hr,Jr.monthsShort=K,Jr._monthsShort=_r,Jr.monthsParse=J,Jr.week=ft,Jr._week=wr,Jr.firstDayOfYear=pt,Jr.firstDayOfWeek=dt,Jr.weekdays=Re,Jr._weekdays=Ar,Jr.weekdaysMin=Ye,Jr._weekdaysMin=xr,Jr.weekdaysShort=He,Jr._weekdaysShort=kr,Jr.weekdaysParse=Ge,Jr.isPM=We,Jr._meridiemParse=Lr,Jr.meridiem=Ke,S("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,n=1===y(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+n}}),e.lang=et("moment.lang is deprecated. Use moment.locale instead.",S),e.langData=et("moment.langData is deprecated. Use moment.localeData instead.",T);var $r=Math.abs,Zr=En("ms"),Xr=En("s"),Qr=En("m"),ti=En("h"),ei=En("d"),ni=En("w"),ri=En("M"),ii=En("y"),oi=In("milliseconds"),ui=In("seconds"),ai=In("minutes"),si=In("hours"),ci=In("days"),li=In("months"),fi=In("years"),di=Math.round,pi={s:45,m:45,h:22,d:26,M:11},hi=Math.abs,_i=xt.prototype;_i.abs=vn,_i.add=mn,_i.subtract=gn,_i.as=Mn,_i.asMilliseconds=Zr,_i.asSeconds=Xr,_i.asMinutes=Qr,_i.asHours=ti,_i.asDays=ei,_i.asWeeks=ni,_i.asMonths=ri,_i.asYears=ii,_i.valueOf=Tn,_i._bubble=On,_i.get=jn,_i.milliseconds=oi,_i.seconds=ui,_i.minutes=ai,_i.hours=si,_i.days=ci,_i.weeks=Pn,_i.months=li,_i.years=fi,_i.humanize=kn,_i.toISOString=xn,_i.toString=xn,_i.toJSON=xn,_i.locale=me,_i.localeData=ge,_i.toIsoString=et("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",xn),_i.lang=Dr,x("X",0,0,"unix"),x("x",0,0,"valueOf"),Y("x",er),Y("X",rr),U("X",function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))}),U("x",function(t,e,n){n._d=new Date(y(t))}),e.version="2.10.6",n(Dt),e.fn=Fr,e.min=At,e.max=kt,e.utc=s,e.unix=Xe,e.months=fn,e.isDate=i,e.locale=S,e.invalid=d,e.duration=Zt,e.isMoment=_,e.weekdays=pn,e.parseZone=Qe,e.localeData=T,e.isDuration=Lt,e.monthsShort=dn,e.weekdaysMin=_n,e.defineLocale=M,e.weekdaysShort=hn,e.normalizeUnits=j,e.relativeTimeThreshold=An;var vi=e;return vi})}).call(e,n(72)(t))},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);e["default"]=new u["default"]({is:"ha-entity-toggle",properties:{stateObj:{type:Object,observer:"stateObjChanged"},toggleChecked:{type:Boolean,value:!1}},ready:function(){this.forceStateChange()},toggleChanged:function(t){var e=t.target.checked;e&&"off"===this.stateObj.state?this.turn_on():e||"off"===this.stateObj.state||this.turn_off()},stateObjChanged:function(t){t&&this.updateToggle(t)},updateToggle:function(t){this.toggleChecked=t&&"off"!==t.state},forceStateChange:function(){this.updateToggle(this.stateObj)},turn_on:function(){var t=this;i.serviceActions.callTurnOn(this.stateObj.entityId).then(function(){return t.forceStateChange()})},turn_off:function(){var t=this;i.serviceActions.callTurnOff(this.stateObj.entityId).then(function(){return t.forceStateChange()})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"ha-card",properties:{title:{type:String},header:{type:String}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(34),o=r(i),u=n(2),a=n(1),s=r(a),c=6e4,l=u.util.parseDateTime;e["default"]=new s["default"]({is:"relative-ha-datetime",properties:{datetime:{type:String,observer:"datetimeChanged"},datetimeObj:{type:Object,observer:"datetimeObjChanged"},parsedDateTime:{type:Object},relativeTime:{type:String,value:"not set"}},created:function(){this.updateRelative=this.updateRelative.bind(this)},attached:function(){this._interval=setInterval(this.updateRelative,c)},detached:function(){clearInterval(this._interval)},datetimeChanged:function(t){this.parsedDateTime=t?l(t):null,this.updateRelative()},datetimeObjChanged:function(t){this.parsedDateTime=t,this.updateRelative()},updateRelative:function(){this.relativeTime=this.parsedDateTime?o["default"](this.parsedDateTime).fromNow():""}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(19),n(91),n(90),e["default"]=new o["default"]({is:"state-history-charts",properties:{stateHistory:{type:Object},isLoadingData:{type:Boolean,value:!1},apiLoaded:{type:Boolean,value:!1},isLoading:{type:Boolean,computed:"computeIsLoading(isLoadingData, apiLoaded)"},groupedStateHistory:{type:Object,computed:"computeGroupedStateHistory(isLoading, stateHistory)"},isSingleDevice:{type:Boolean,computed:"computeIsSingleDevice(stateHistory)"}},computeIsSingleDevice:function(t){return t&&1===t.size},computeGroupedStateHistory:function(t,e){if(t||!e)return{line:[],timeline:[]};var n={},r=[];e.forEach(function(t){if(t&&0!==t.size){var e=t.find(function(t){return"unit_of_measurement"in t.attributes}),i=e?e.attributes.unit_of_measurement:!1;i?i in n?n[i].push(t.toArray()):n[i]=[t.toArray()]:r.push(t.toArray())}}),r=r.length>0&&r;var i=Object.keys(n).map(function(t){return[t,n[t]]});return{line:i,timeline:r}},googleApiLoaded:function(){var t=this;window.google.load("visualization","1",{packages:["timeline","corechart"],callback:function(){return t.apiLoaded=!0}})},computeContentClasses:function(t){return t?"loading":""},computeIsLoading:function(t,e){return t||!e},computeIsEmpty:function(t){return t&&0===t.size},extractUnit:function(t){return t[0]},extractData:function(t){return t[1]}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),e["default"]=new o["default"]({is:"state-card-display",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e){"use strict";function n(t,e){switch(t){case"homeassistant":return"home";case"group":return"homeassistant-24:group";case"device_tracker":return"social:person";case"switch":return"image:flash-on";case"alarm_control_panel":return e&&"disarmed"===e?"icons:lock-open":"icons:lock";case"media_player":var n="hardware:cast";return e&&"off"!==e&&"idle"!==e&&(n+="-connected"),n;case"sun":return"image:wb-sunny";case"light":return"image:wb-incandescent";case"simple_alarm":return"social:notifications";case"notify":return"announcement";case"thermostat":return"homeassistant-100:thermostat";case"sensor":return"visibility";case"configurator":return"settings";case"conversation":return"av:hearing";case"script":return"description";case"scene":return"social:pages";case"updater":return"update_available"===e?"icons:cloud-download":"icons:cloud-done";default:return"bookmark"}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){return u["default"](t).format("LT")}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(34),u=r(o);t.exports=e["default"]},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2);e["default"]=function(t,e){r.authActions.validate(t,{rememberAuth:e,useStreaming:r.localStoragePreferences.useStreaming})},t.exports=e["default"]},function(t,e,n){"use strict";function r(t,e){var n=null==t?void 0:t[e];return i(n)?n:void 0}var i=n(129);t.exports=r},function(t,e){"use strict";function n(t){return!!t&&"object"==typeof t}t.exports=n},function(t,e,n){"use strict";function r(t){return i(t)&&a.call(t)==o}var i=n(46),o="[object Function]",u=Object.prototype,a=u.toString;t.exports=r},function(t,e){"use strict";function n(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=["isLoadingEntityHistory"];e.isLoadingEntityHistory=i;var o=["currentEntityHistoryDate"];e.currentDate=o;var u=["entityHistory"];e.entityHistoryMap=u;var a=[o,u,function(t,e){return e.get(t)||r.toImmutable({})}];e.entityHistoryForCurrentDate=a;var s=[o,u,function(t,e){return!!e.get(t)}];e.hasDataForCurrentDate=s;var c=["recentEntityHistory"];e.recentEntityHistoryMap=c;var l=["recentEntityHistory"];e.recentEntityHistoryUpdatedMap=l},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({currentEntityHistoryDate:a["default"],entityHistory:c["default"],isLoadingEntityHistory:f["default"],recentEntityHistory:p["default"],recentEntityHistoryUpdated:_["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(141),a=i(u),s=n(142),c=i(s),l=n(143),f=i(l),d=n(144),p=i(d),h=n(145),_=i(h),v=n(140),y=r(v),m=n(47),g=r(m),b=y;e.actions=b;var O=g;e.getters=O},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function t(t,e){for(var n=0;n6e4}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var u=n(167),a=n(187),s=i(a),c=n(189),l=i(c),f=n(191),d=i(f),p=n(15),h=r(p),_=n(25),v=r(_),y=n(7),m=r(y),g=n(48),b=r(g),O=n(26),w=r(O),S=n(152),M=r(S),T=n(51),E=r(T),j=n(54),I=r(j),P=n(28),D=r(P),C=n(13),A=r(C),k=n(29),x=r(k),L=n(31),N=r(L),z=n(184),R=r(z),H=n(8),Y=r(H),G=function F(){o(this,F);var t=s["default"]();Object.defineProperties(this,{demo:{value:!1,enumerable:!0},localStoragePreferences:{value:u.localStoragePreferences,enumerable:!0},reactor:{value:t,enumerable:!0},util:{value:d["default"],enumerable:!0},startLocalStoragePreferencesSync:{value:u.localStoragePreferences.startSync.bind(u.localStoragePreferences,t)},startUrlSync:{value:I.urlSync.startSync.bind(null,t)},stopUrlSync:{value:I.urlSync.stopSync.bind(null,t)}}),l["default"](this,t,{auth:h,config:v,entity:m,entityHistory:b,event:w,logbook:M,moreInfo:E,navigation:I,notification:D,service:A,stream:x,sync:N,voice:R,restApi:Y})};e["default"]=G,t.exports=e["default"]},function(t,e){"use strict";function n(t){return function(e){return null==e?void 0:e[t]}}t.exports=n},function(t,e,n){"use strict";var r=n(64),i=r("length");t.exports=i},function(t,e,n){"use strict";function r(t){return null!=t&&o(i(t))}var i=n(65),o=n(69);t.exports=r},function(t,e){"use strict";function n(t,e){return t="number"==typeof t||r.test(t)?+t:-1,e=null==e?i:e,t>-1&&t%1==0&&e>t}var r=/^\d+$/,i=9007199254740991;t.exports=n},function(t,e,n){"use strict";function r(t,e,n){if(!u(n))return!1;var r=typeof e;if("number"==r?i(n)&&o(e,n.length):"string"==r&&e in n){var a=n[e];return t===t?t===a:a!==a}return!1}var i=n(66),o=n(67),u=n(70);t.exports=r},function(t,e){"use strict";function n(t){return"number"==typeof t&&t>-1&&t%1==0&&r>=t}var r=9007199254740991;t.exports=n},function(t,e){"use strict";function n(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){"use strict";function r(t,e,n){n&&i(t,e,n)&&(e=n=void 0),t=+t||0,n=null==n?1:+n||0,null==e?(e=t,t=0):e=+e||0;for(var r=-1,a=u(o((e-t)/(n||1)),0),s=Array(a);++r1}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(36),e["default"]=new o["default"]({is:"ha-introduction-card",properties:{showInstallInstruction:{type:Boolean,value:!1},showHideInstruction:{type:Boolean,value:!0}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(41),a=r(u);e["default"]=new o["default"]({is:"display-time",properties:{dateObj:{type:Object}},computeTime:function(t){return t?a["default"](t):""}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"entity-list",behaviors:[s["default"]],properties:{entities:{type:Array,bindNuclear:[i.entityGetters.entityMap,function(t){return t.valueSeq().sortBy(function(t){return t.entityId}).toArray()}]}},entitySelected:function(t){t.preventDefault(),this.fire("entity-selected",{entityId:t.model.entity.entityId})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2);n(18),e["default"]=new o["default"]({is:"ha-entity-marker",properties:{entityId:{type:String,value:""},state:{type:Object,computed:"computeState(entityId)"},icon:{type:Object,computed:"computeIcon(state)"},image:{type:Object,computed:"computeImage(state)"},value:{type:String,computed:"computeValue(state)"}},listeners:{click:"badgeTap"},badgeTap:function(t){var e=this;t.stopPropagation(),this.entityId&&this.async(function(){return u.moreInfoActions.selectEntity(e.entityId)},1)},computeState:function(t){return t&&u.reactor.evaluate(u.entityGetters.byId(t))},computeIcon:function(t){return!t&&"home"},computeImage:function(t){return t&&t.attributes.entity_picture},computeValue:function(t){return t&&t.entityDisplay.split(" ").map(function(t){return t.substr(0,1)}).join("")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(40),s=r(a),c=n(22),l=r(c);n(18),e["default"]=new o["default"]({is:"ha-state-label-badge",properties:{state:{type:Object,observer:"stateChanged"}},listeners:{click:"badgeTap"},badgeTap:function(t){var e=this;return t.stopPropagation(),l["default"](this.state.entityId)?void("scene"===this.state.domain?u.serviceActions.callTurnOn(this.state.entityId):"off"===this.state.state?u.serviceActions.callTurnOn(this.state.entityId):u.serviceActions.callTurnOff(this.state.entityId)):void this.async(function(){return u.moreInfoActions.selectEntity(e.state.entityId)},1)},computeClasses:function(t){switch(t.domain){case"scene":return"green";case"script":return"on"===t.state?"blue":"grey";default:return""}},computeGlow:function(t){switch(t.domain){case"scene":case"script":return"on"===t.state;default:return!1}},computeValue:function(t){switch(t.domain){case"device_tracker":case"sun":case"scene":case"script":case"alarm_control_panel":return void 0;case"sensor":return t.attributes.unit_of_measurement&&t.state;default:return t.state}},computeIcon:function(t){switch(t.domain){case"device_tracker":case"alarm_control_panel":case"scene":case"script":return s["default"](t.domain,t.state);case"sensor":return!t.attributes.unit_of_measurement&&s["default"](t.domain);case"sun":return"above_horizon"===t.state?"image:wb-sunny":"image:brightness-3";default:return void 0}},computeImage:function(t){return t.attributes.entity_picture},computeLabel:function(t){switch(t.domain){case"scene":case"script":return t.domain;case"sensor":return t.attributes.unit_of_measurement||t.state;case"device_tracker":return"not_home"===t.state?"Away":t.state;case"alarm_control_panel":return t.state;default:return t.attributes.unit_of_measurement}},computeDescription:function(t){return t.entityDisplay},stateChanged:function(){this.updateStyles()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(124),a=r(u);n(17),e["default"]=new o["default"]({is:"state-badge",properties:{stateObj:{type:Object,observer:"updateIconColor"}},updateIconColor:function(t){if("light"===t.domain&&"on"===t.state&&t.attributes.brightness&&t.attributes.xy_color){var e=a["default"](t.attributes.xy_color[0],t.attributes.xy_color[1],t.attributes.brightness);this.$.icon.style.color="rgb("+e.map(Math.floor).join(",")+")"}else this.$.icon.style.color=null}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"events-list",behaviors:[s["default"]],properties:{events:{type:Array,bindNuclear:[i.eventGetters.entityMap,function(t){return t.valueSeq().sortBy(function(t){return t.event}).toArray()}]}},eventSelected:function(t){t.preventDefault(),this.fire("event-selected",{eventType:t.model.event.event})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){var e=t.toString(16);return 1===e.length?"0"+e:e}function o(t){return"#"+i(t.r)+i(t.g)+i(t.b)}Object.defineProperty(e,"__esModule",{value:!0});var u=n(1),a=r(u);e["default"]=new a["default"]({is:"ha-color-picker",properties:{width:{type:Number,value:300},height:{type:Number,value:300},color:{type:Object}},listeners:{mousedown:"onMouseDown",mouseup:"onMouseUp",touchstart:"onTouchStart",touchend:"onTouchEnd",tap:"onTap"},onMouseDown:function(t){this.onMouseMove(t),this.addEventListener("mousemove",this.onMouseMove)},onMouseUp:function(){this.removeEventListener("mousemove",this.onMouseMove)},onTouchStart:function(t){this.onTouchMove(t),this.addEventListener("touchmove",this.onTouchMove)},onTouchEnd:function(){this.removeEventListener("touchmove",this.onTouchMove)},onTap:function(t){t.stopPropagation()},onTouchMove:function(t){var e=t.touches[0];this.onColorSelect(t,{x:e.clientX,y:e.clientY})},onMouseMove:function(t){var e=this;t.preventDefault(),this.mouseMoveIsThrottled&&(this.mouseMoveIsThrottled=!1,this.onColorSelect(t),this.async(function(){return e.mouseMoveIsThrottled=!0},100))},onColorSelect:function(t,e){if(this.context){var n=e||this.relativeMouseCoordinates(t),r=this.context.getImageData(n.x,n.y,1,1).data;this.setColor({r:r[0],g:r[1],b:r[2]})}},setColor:function(t){this.color={hex:o(t),rgb:t},this.fire("colorselected",{rgb:this.color.rgb,hex:this.color.hex})},relativeMouseCoordinates:function(t){var e=0,n=0;if(this.canvas){var r=this.canvas.getBoundingClientRect();e=t.clientX-r.left,n=t.clientY-r.top}return{x:e,y:n}},ready:function(){this.setColor=this.setColor.bind(this),this.mouseMoveIsThrottled=!0,this.canvas=this.children[0],this.context=this.canvas.getContext("2d");var t=this.context.createLinearGradient(0,0,this.width,0);t.addColorStop(0,"rgb(255,0,0)"),t.addColorStop(.16,"rgb(255,0,255)"),t.addColorStop(.32,"rgb(0,0,255)"),t.addColorStop(.48,"rgb(0,255,255)"),t.addColorStop(.64,"rgb(0,255,0)"),t.addColorStop(.8,"rgb(255,255,0)"),t.addColorStop(1,"rgb(255,0,0)"),this.context.fillStyle=t,this.context.fillRect(0,0,this.width,this.height);var e=this.context.createLinearGradient(0,0,0,this.height);e.addColorStop(0,"rgba(255,255,255,1)"),e.addColorStop(.5,"rgba(255,255,255,0)"),e.addColorStop(.5,"rgba(0,0,0,0)"),e.addColorStop(1,"rgba(0,0,0,1)"),this.context.fillStyle=e,this.context.fillRect(0,0,this.width,this.height)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(18),e["default"]=new o["default"]({is:"ha-demo-badge"}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(88),e["default"]=new o["default"]({is:"ha-logbook",properties:{entries:{type:Object,value:[]}},noEntries:function(t){return!t.length}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(92),e["default"]=new u["default"]({is:"ha-sidebar",behaviors:[s["default"]],properties:{menuShown:{type:Boolean},menuSelected:{type:String},selected:{type:String,bindNuclear:i.navigationGetters.activePane,observer:"selectedChanged"},hasHistoryComponent:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("history")},hasLogbookComponent:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("logbook")}},selectedChanged:function(t){for(var e=this.querySelectorAll(".menu [data-panel]"),n=0;nd;d++)f._columns[d]=[];var p=0;return n&&u(),c.keySeq().sortBy(function(t){return i(t)}).forEach(function(t){if("a"===t)return void(f._demo=!0);var n=i(t);n>=0&&10>n?f._badges.push.apply(f._badges,r(c.get(t)).sortBy(o).toArray()):"group"===t?c.get(t).filter(function(t){return!t.attributes.auto}).sortBy(o).forEach(function(t){var n=s.util.expandGroup(t,e);n.forEach(function(t){return l[t.entityId]=!0}),a(t.entityDisplay,n.toArray(),t)}):a(t,r(c.get(t)).sortBy(o).toArray())}),f},computeShouldRenderColumn:function(t,e){return 0===t||e.length},computeShowIntroduction:function(t,e,n){return 0===t&&(e||n._demo)},computeShowHideInstruction:function(t,e){return t.size>0&&!0&&!e._demo},computeGroupEntityOfCard:function(t,e){return t[e].groupEntity},computeStatesOfCard:function(t,e){return t[e].entities}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(17),n(76),n(37),e["default"]=new u["default"]({is:"logbook-entry",entityClicked:function(t){t.preventDefault(),i.moreInfoActions.selectEntity(this.entryObj.entityId)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(17),e["default"]=new u["default"]({is:"services-list",behaviors:[s["default"]],properties:{serviceDomains:{type:Array,bindNuclear:i.serviceGetters.entityMap}},computeDomains:function(t){return t.valueSeq().map(function(t){return t.domain}).sort().toJS()},computeServices:function(t,e){return t.get(e).get("services").keySeq().toArray()},serviceClicked:function(t){t.preventDefault(),this.fire("service-selected",{domain:t.model.domain,service:t.model.service})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){var e=parseFloat(t);return!isNaN(e)&&isFinite(e)?e:null}Object.defineProperty(e,"__esModule",{value:!0});var o=n(71),u=r(o),a=n(1),s=r(a);e["default"]=new s["default"]({is:"state-history-chart-line",properties:{data:{type:Object,observer:"dataChanged"},unit:{type:String},isSingleDevice:{type:Boolean,value:!1},isAttached:{type:Boolean,value:!1,observer:"dataChanged"},chartEngine:{type:Object}},created:function(){this.style.display="block"},attached:function(){this.isAttached=!0},dataChanged:function(){this.drawChart()},drawChart:function(){if(this.isAttached){this.chartEngine||(this.chartEngine=new window.google.visualization.LineChart(this));var t=this.unit,e=this.data;if(0!==e.length){var n={legend:{position:"top"},interpolateNulls:!0,titlePosition:"none",vAxes:{0:{title:t}},hAxis:{format:"H:mm"},chartArea:{left:"60",width:"95%"},explorer:{actions:["dragToZoom","rightClickToReset","dragToPan"],keepInBounds:!0,axis:"horizontal",maxZoomIn:.1}};this.isSingleDevice&&(n.legend.position="none",n.vAxes[0].title=null,n.chartArea.left=40,n.chartArea.height="80%",n.chartArea.top=5,n.enableInteractivity=!1);var r=new Date(Math.min.apply(null,e.map(function(t){return t[0].lastChangedAsDate}))),o=new Date(r);o.setDate(o.getDate()+1),o>new Date&&(o=new Date);var a=e.map(function(t){function e(t,e){c&&e&&s.push([t[0]].concat(c.slice(1).map(function(t,n){return e[n]?t:null}))),s.push(t),c=t}var n=t[t.length-1],r=n.domain,u=n.entityDisplay,a=new window.google.visualization.DataTable;a.addColumn({type:"datetime",id:"Time"});var s=[],c=void 0;if("thermostat"===r){var l=t.reduce(function(t,e){return t||e.attributes.target_temp_high!==e.attributes.target_temp_low},!1);a.addColumn("number",u+" current temperature");var f=void 0;l?!function(){a.addColumn("number",u+" target temperature high"),a.addColumn("number",u+" target temperature low");var t=[!1,!0,!0];f=function(n){var r=i(n.attributes.current_temperature),o=i(n.attributes.target_temp_high),u=i(n.attributes.target_temp_low);e([n.lastChangedAsDate,r,o,u],t)}}():!function(){a.addColumn("number",u+" target temperature");var t=[!1,!0];f=function(n){var r=i(n.attributes.current_temperature),o=i(n.attributes.temperature);e([n.lastChangedAsDate,r,o],t)}}(),t.forEach(f)}else!function(){a.addColumn("number",u);var n="sensor"!==r&&[!0];t.forEach(function(t){var r=i(t.state);e([t.lastChangedAsDate,r],n)})}();return e([o].concat(c.slice(1)),!1),a.addRows(s),a}),s=void 0;s=1===a.length?a[0]:a.slice(1).reduce(function(t,e){return window.google.visualization.data.join(t,e,"full",[[0,0]],u["default"](1,t.getNumberOfColumns()),u["default"](1,e.getNumberOfColumns()))},a[0]),this.chartEngine.draw(s,n)}}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"state-history-chart-timeline",properties:{data:{type:Object,observer:"dataChanged"},isAttached:{type:Boolean,value:!1,observer:"dataChanged"}},attached:function(){this.isAttached=!0},dataChanged:function(){this.drawChart()},drawChart:function(){function t(t,e,n,r){var o=e.replace(/_/g," ");i.addRow([t,o,n,r])}if(this.isAttached){for(var e=o["default"].dom(this),n=this.data;e.node.lastChild;)e.node.removeChild(e.node.lastChild);if(n&&0!==n.length){var r=new window.google.visualization.Timeline(this),i=new window.google.visualization.DataTable;i.addColumn({type:"string",id:"Entity"}),i.addColumn({type:"string",id:"State"}),i.addColumn({type:"date",id:"Start"}),i.addColumn({type:"date",id:"End"});var u=new Date(n.reduce(function(t,e){return Math.min(t,e[0].lastChangedAsDate)},new Date)),a=new Date(u);a.setDate(a.getDate()+1),a>new Date&&(a=new Date);var s=0;n.forEach(function(e){if(0!==e.length){var n=e[0].entityDisplay,r=void 0,i=null,o=null;e.forEach(function(e){null!==i&&e.state!==i?(r=e.lastChangedAsDate,t(n,i,o,r),i=e.state,o=r):null===i&&(i=e.state,o=e.lastChangedAsDate)}),t(n,i,o,a),s++}}),r.draw(i,{height:55+42*s,timeline:{showRowLabels:n.length>1},hAxis:{format:"H:mm"}})}}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"stream-status",behaviors:[s["default"]],properties:{isStreaming:{type:Boolean,bindNuclear:i.streamGetters.isStreamingEvents},hasError:{type:Boolean,bindNuclear:i.streamGetters.hasStreamingEventsError}},toggleChanged:function(){this.isStreaming?i.streamActions.stop():i.streamActions.start()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(20),n(38),n(107);var c=["camera","configurator","scene"];e["default"]=new u["default"]({is:"more-info-dialog",behaviors:[s["default"]],properties:{stateObj:{type:Object,bindNuclear:i.moreInfoGetters.currentEntity,observer:"stateObjChanged"},stateHistory:{type:Object,bindNuclear:[i.moreInfoGetters.currentEntityHistory,function(t){return t?[t]:!1}]},isLoadingHistoryData:{type:Boolean,computed:"computeIsLoadingHistoryData(_delayedDialogOpen, _isLoadingHistoryData)"},_isLoadingHistoryData:{type:Boolean,bindNuclear:i.entityHistoryGetters.isLoadingEntityHistory},hasHistoryComponent:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("history"),observer:"fetchHistoryData"},shouldFetchHistory:{type:Boolean,bindNuclear:i.moreInfoGetters.isCurrentEntityHistoryStale,observer:"fetchHistoryData"},showHistoryComponent:{type:Boolean,value:!1},dialogOpen:{type:Boolean,value:!1,observer:"dialogOpenChanged"},_delayedDialogOpen:{type:Boolean,value:!1},_boundOnBackdropTap:{type:Function,value:function(){return this._onBackdropTap.bind(this)}}},computeIsLoadingHistoryData:function(t,e){return!t||e},fetchHistoryData:function(){this.stateObj&&this.hasHistoryComponent&&this.shouldFetchHistory&&i.entityHistoryActions.fetchRecent(this.stateObj.entityId)},stateObjChanged:function(t){var e=this;return t?(this.showHistoryComponent=this.hasHistoryComponent&&-1===c.indexOf(this.stateObj.domain),void this.async(function(){e.fetchHistoryData(),e.dialogOpen=!0},10)):void(this.dialogOpen=!1)},dialogOpenChanged:function(t){var e=this;t?(this.$.dialog.backdropElement.addEventListener("click",this._boundOnBackdropTap),this.async(function(){return e._delayedDialogOpen=!0},10)):!t&&this.stateObj&&(i.moreInfoActions.deselectEntity(),this._delayedDialogOpen=!1)},_onBackdropTap:function(){this.$.dialog.backdropElement.removeEventListener("click",this._boundOnBackdropTap),this.dialogOpen=!1}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(4),s=r(a);n(85),n(102),n(100),n(99),n(101),n(96),n(97),n(98),n(103),n(93),e["default"]=new o["default"]({is:"home-assistant-main",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},activePane:{type:String,bindNuclear:u.navigationGetters.activePane,observer:"activePaneChanged"},isSelectedStates:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("states")},isSelectedHistory:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("history")},isSelectedMap:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("map")},isSelectedLogbook:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("logbook")},isSelectedDevEvent:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devEvent")},isSelectedDevState:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devState")},isSelectedDevService:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devService")},showSidebar:{type:Boolean,bindNuclear:u.navigationGetters.showSidebar}},listeners:{"open-menu":"openMenu","close-menu":"closeMenu"},openMenu:function(){this.narrow?this.$.drawer.openDrawer():u.navigationActions.showSidebar(!0)},closeMenu:function(){this.$.drawer.closeDrawer(),this.showSidebar&&u.navigationActions.showSidebar(!1)},activePaneChanged:function(){this.narrow&&this.$.drawer.closeDrawer()},attached:function(){u.startUrlSync()},computeForceNarrow:function(t,e){return t||!e},detached:function(){u.stopUrlSync()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(4),s=r(a),c=n(42),l=r(c);e["default"]=new o["default"]({is:"login-form",behaviors:[s["default"]],properties:{isValidating:{type:Boolean,observer:"isValidatingChanged",bindNuclear:u.authGetters.isValidating},isInvalid:{type:Boolean,bindNuclear:u.authGetters.isInvalidAttempt},errorMessage:{type:String,bindNuclear:u.authGetters.attemptErrorMessage}},listeners:{keydown:"passwordKeyDown","loginButton.click":"validatePassword"},observers:["validatingChanged(isValidating, isInvalid)"],validatingChanged:function(t,e){t||e||(this.$.passwordInput.value="")},isValidatingChanged:function(t){var e=this;t||this.async(function(){return e.$.passwordInput.focus()},10)},passwordKeyDown:function(t){13===t.keyCode?(this.validatePassword(),t.preventDefault()):this.isInvalid&&(this.isInvalid=!1)},validatePassword:function(){this.$.hideKeyboardOnFocus.focus(),l["default"](this.$.passwordInput.value,this.$.rememberLogin.checked)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(10),n(89),e["default"]=new u["default"]({is:"partial-dev-call-service",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},domain:{type:String,value:""},service:{type:String,value:""},serviceData:{type:String,value:""},description:{type:String,computed:"computeDescription(domain, service)"}},computeDescription:function(t,e){return i.reactor.evaluate([i.serviceGetters.entityMap,function(n){return n.has(t)&&n.get(t).get("services").has(e)?JSON.stringify(n.get(t).get("services").get(e).toJS(),null,2):"No description available"; +}])},serviceSelected:function(t){this.domain=t.detail.domain,this.service=t.detail.service},callService:function(){var t=void 0;try{t=this.serviceData?JSON.parse(this.serviceData):{}}catch(e){return void alert("Error parsing JSON: "+e)}i.serviceActions.callService(this.domain,this.service,t)},computeFormClasses:function(t){return"layout "+(t?"vertical":"horizontal")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(10),n(81),e["default"]=new u["default"]({is:"partial-dev-fire-event",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},eventType:{type:String,value:""},eventData:{type:String,value:""}},eventSelected:function(t){this.eventType=t.detail.eventType},fireEvent:function(){var t=void 0;try{t=this.eventData?JSON.parse(this.eventData):{}}catch(e){return void alert("Error parsing JSON: "+e)}i.eventActions.fireEvent(this.eventType,t)},computeFormClasses:function(t){return"layout "+(t?"vertical":"horizontal")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(10),n(77),e["default"]=new u["default"]({is:"partial-dev-set-state",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},entityId:{type:String,value:""},state:{type:String,value:""},stateAttributes:{type:String,value:""}},setStateData:function(t){var e=t?JSON.stringify(t,null," "):"";this.$.inputData.value=e,this.$.inputDataWrapper.update(this.$.inputData)},entitySelected:function(t){var e=i.reactor.evaluate(i.entityGetters.byId(t.detail.entityId));this.entityId=e.entityId,this.state=e.state,this.stateAttributes=JSON.stringify(e.attributes,null," ")},handleSetState:function(){var t=void 0;try{t=this.stateAttributes?JSON.parse(this.stateAttributes):{}}catch(e){return void alert("Error parsing JSON: "+e)}i.entityActions.save({entityId:this.entityId,state:this.state,attributes:t})},computeFormClasses:function(t){return"layout "+(t?"vertical":"horizontal")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(10),n(38),e["default"]=new u["default"]({is:"partial-history",behaviors:[s["default"]],properties:{narrow:{type:Boolean},showMenu:{type:Boolean,value:!1},isDataLoaded:{type:Boolean,bindNuclear:i.entityHistoryGetters.hasDataForCurrentDate,observer:"isDataLoadedChanged"},stateHistory:{type:Object,bindNuclear:i.entityHistoryGetters.entityHistoryForCurrentDate},isLoadingData:{type:Boolean,bindNuclear:i.entityHistoryGetters.isLoadingEntityHistory},selectedDate:{type:String,value:null,bindNuclear:i.entityHistoryGetters.currentDate}},isDataLoadedChanged:function(t){t||this.async(function(){return i.entityHistoryActions.fetchSelectedDate()},1)},handleRefreshClick:function(){i.entityHistoryActions.fetchSelectedDate()},datepickerFocus:function(){this.datePicker.adjustPosition()},attached:function(){this.datePicker=new window.Pikaday({field:this.$.datePicker.inputElement,onSelect:i.entityHistoryActions.changeCurrentDate})},detached:function(){this.datePicker.destroy()},computeContentClasses:function(t){return"flex content "+(t?"narrow":"wide")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(10),n(84),n(19),e["default"]=new u["default"]({is:"partial-logbook",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},selectedDate:{type:String,bindNuclear:i.logbookGetters.currentDate},isLoading:{type:Boolean,bindNuclear:i.logbookGetters.isLoadingEntries},isStale:{type:Boolean,bindNuclear:i.logbookGetters.isCurrentStale,observer:"isStaleChanged"},entries:{type:Array,bindNuclear:[i.logbookGetters.currentEntries,function(t){return t.reverse().toArray()}]},datePicker:{type:Object}},isStaleChanged:function(t){var e=this;t&&this.async(function(){return i.logbookActions.fetchDate(e.selectedDate)},1)},handleRefresh:function(){i.logbookActions.fetchDate(this.selectedDate)},datepickerFocus:function(){this.datePicker.adjustPosition()},attached:function(){this.datePicker=new window.Pikaday({field:this.$.datePicker.inputElement,onSelect:i.logbookActions.changeCurrentDate})},detached:function(){this.datePicker.destroy()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(78),window.L.Icon.Default.imagePath="/static/images/leaflet",e["default"]=new u["default"]({is:"partial-map",behaviors:[s["default"]],properties:{locationGPS:{type:Number,bindNuclear:i.configGetters.locationGPS},locationName:{type:String,bindNuclear:i.configGetters.locationName},locationEntities:{type:Array,bindNuclear:[i.entityGetters.visibleEntityMap,function(t){return t.valueSeq().filter(function(t){return t.attributes.latitude&&"home"!==t.state}).toArray()}]},zoneEntities:{type:Array,bindNuclear:[i.entityGetters.entityMap,function(t){return t.valueSeq().filter(function(t){return"zone"===t.domain}).toArray()}]},narrow:{type:Boolean},showMenu:{type:Boolean,value:!1}},attached:function(){var t=this;window.L.Browser.mobileWebkit&&this.async(function(){var e=t.$.map,n=e.style.display;e.style.display="none",t.async(function(){e.style.display=n},1)},1)},computeMenuButtonClass:function(t,e){return!t&&e?"invisible":""},toggleMenu:function(){this.fire("open-menu")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(10),n(86),n(87),e["default"]=new u["default"]({is:"partial-zone",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},isFetching:{type:Boolean,bindNuclear:i.syncGetters.isFetching},isStreaming:{type:Boolean,bindNuclear:i.streamGetters.isStreamingEvents},canListen:{type:Boolean,bindNuclear:[i.voiceGetters.isVoiceSupported,i.configGetters.isComponentLoaded("conversation"),function(t,e){return t&&e}]},isListening:{type:Boolean,bindNuclear:i.voiceGetters.isListening},showListenInterface:{type:Boolean,bindNuclear:[i.voiceGetters.isListening,i.voiceGetters.isTransmitting,function(t,e){return t||e}]},introductionLoaded:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("introduction")},locationName:{type:String,bindNuclear:i.configGetters.locationName},showMenu:{type:Boolean,value:!1,observer:"windowChange"},states:{type:Object,bindNuclear:i.entityGetters.visibleEntityMap},columns:{type:Number}},created:function(){var t=this;this.windowChange=this.windowChange.bind(this);for(var e=[],n=0;5>n;n++)e.push(278+278*n);this.mqls=e.map(function(e){var n=window.matchMedia("(min-width: "+e+"px)");return n.addListener(t.windowChange),n})},detached:function(){var t=this;this.mqls.forEach(function(e){return e.removeListener(t.windowChange)})},windowChange:function(){var t=this.mqls.reduce(function(t,e){return t+e.matches},0);this.columns=Math.max(1,t-this.showMenu)},handleRefresh:function(){i.syncActions.fetchAll()},handleListenClick:function(){this.isListening?i.voiceActions.stop():i.voiceActions.listen()},computeDomains:function(t){return t.keySeq().toArray()},computeMenuButtonClass:function(t,e){return!t&&e?"invisible":""},computeStatesOfDomain:function(t,e){return t.get(e).toArray()},computeListenButtonIcon:function(t){return t?"av:mic-off":"av:mic"},computeRefreshButtonClass:function(t){return t?"ha-spin":void 0},computeShowIntroduction:function(t,e){return t||0===e.size},toggleMenu:function(){this.fire("open-menu")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"notification-manager",behaviors:[s["default"]],properties:{text:{type:String,bindNuclear:i.notificationGetters.lastNotificationMessage,observer:"showNotification"}},showNotification:function(t){t&&this.$.toast.show()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);e["default"]=new u["default"]({is:"more-info-alarm_control_panel",handleDisarmTap:function(){this.callService("alarm_disarm",{code:this.enteredCode})},handleHomeTap:function(){this.callService("alarm_arm_home",{code:this.enteredCode})},handleAwayTap:function(){this.callService("alarm_arm_away",{code:this.enteredCode})},properties:{stateObj:{type:Object,observer:"stateObjChanged"},enteredCode:{type:String,value:""},disarmButtonVisible:{type:Boolean,value:!1},armHomeButtonVisible:{type:Boolean,value:!1},armAwayButtonVisible:{type:Boolean,value:!1},codeInputVisible:{type:Boolean,value:!1},codeInputEnabled:{type:Boolean,value:!1},codeFormat:{type:String,value:""},codeValid:{type:Boolean,computed:"validateCode(enteredCode, codeFormat)"}},validateCode:function(t,e){var n=new RegExp(e);return null===e?!0:n.test(t)},stateObjChanged:function(t){var e=this;t&&(this.codeFormat=t.attributes.code_format,this.codeInputVisible=null!==this.codeFormat,this.codeInputEnabled="armed_home"===t.state||"armed_away"===t.state||"disarmed"===t.state||"pending"===t.state||"triggered"===t.state,this.disarmButtonVisible="armed_home"===t.state||"armed_away"===t.state||"pending"===t.state||"triggered"===t.state,this.armHomeButtonVisible="disarmed"===t.state,this.armAwayButtonVisible="disarmed"===t.state),this.async(function(){return e.fire("iron-resize")},500)},callService:function(t,e){var n=e||{};n.entity_id=this.stateObj.entityId,i.serviceActions.callService("alarm_control_panel",t,n)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"more-info-camera",properties:{stateObj:{type:Object},dialogOpen:{type:Boolean}},imageLoaded:function(){this.fire("iron-resize")},computeCameraImageUrl:function(t){return t?"/api/camera_proxy_stream/"+this.stateObj.entityId:"data:image/gif;base64,R0lGODlhAQABAAAAACw="}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(19),e["default"]=new u["default"]({is:"more-info-configurator",behaviors:[s["default"]],properties:{stateObj:{type:Object},action:{type:String,value:"display"},isStreaming:{type:Boolean,bindNuclear:i.streamGetters.isStreamingEvents},isConfigurable:{type:Boolean,computed:"computeIsConfigurable(stateObj)"},isConfiguring:{type:Boolean,value:!1},submitCaption:{type:String,computed:"computeSubmitCaption(stateObj)"},fieldInput:{type:Object,value:{}}},computeIsConfigurable:function(t){return"configure"===t.state},computeSubmitCaption:function(t){return t.attributes.submit_caption||"Set configuration"},fieldChanged:function(t){var e=t.target;this.fieldInput[e.id]=e.value},submitClicked:function(){var t=this;this.isConfiguring=!0;var e={configure_id:this.stateObj.attributes.configure_id,fields:this.fieldInput};i.serviceActions.callService("configurator","configure",e).then(function(){t.isConfiguring=!1,t.isStreaming||i.syncActions.fetchAll()},function(){t.isConfiguring=!1})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(123),a=r(u);n(108),n(109),n(113),n(106),n(114),n(112),n(110),n(111),n(105),n(115),n(104),e["default"]=new o["default"]({is:"more-info-content",properties:{stateObj:{type:Object,observer:"stateObjChanged"},dialogOpen:{type:Boolean,value:!1,observer:"dialogOpenChanged"}},dialogOpenChanged:function(t){var e=o["default"].dom(this);e.lastChild&&(e.lastChild.dialogOpen=t)},stateObjChanged:function(t,e){var n=o["default"].dom(this);if(!t)return void(n.lastChild&&n.removeChild(n.lastChild));var r=a["default"](t);if(e&&a["default"](e)===r)n.lastChild.dialogOpen=this.dialogOpen,n.lastChild.stateObj=t;else{n.lastChild&&n.removeChild(n.lastChild);var i=document.createElement("more-info-"+r);i.stateObj=t,i.dialogOpen=this.dialogOpen,n.appendChild(i)}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=["entity_picture","friendly_name","unit_of_measurement"];e["default"]=new o["default"]({is:"more-info-default",properties:{stateObj:{type:Object}},computeDisplayAttributes:function(t){return t?Object.keys(t.attributes).filter(function(t){return-1===u.indexOf(t)}):[]},getAttributeValue:function(t,e){return t.attributes[e]}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(20),e["default"]=new u["default"]({is:"more-info-group",behaviors:[s["default"]],properties:{stateObj:{type:Object},states:{type:Array,bindNuclear:[i.moreInfoGetters.currentEntity,i.entityGetters.entityMap,function(t,e){return t?t.attributes.entity_id.map(e.get.bind(e)):[]}]}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(21),s=r(a);n(82);var c=["brightness","xy_color"];e["default"]=new u["default"]({is:"more-info-light",properties:{stateObj:{type:Object,observer:"stateObjChanged"},brightnessSliderValue:{type:Number,value:0}},stateObjChanged:function(t){var e=this;t&&"on"===t.state&&(this.brightnessSliderValue=t.attributes.brightness),this.async(function(){return e.fire("iron-resize")},500)},computeClassNames:function(t){return s["default"](t,c)},brightnessSliderChanged:function(t){var e=parseInt(t.target.value,10);isNaN(e)||(0===e?i.serviceActions.callTurnOff(this.stateObj.entityId):i.serviceActions.callService("light","turn_on",{entity_id:this.stateObj.entityId,brightness:e}))},colorPicked:function(t){var e=t.detail.rgb;i.serviceActions.callService("light","turn_on",{entity_id:this.stateObj.entityId,rgb_color:[e.r,e.g,e.b]})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(21),s=r(a),c=["volume_level"];e["default"]=new u["default"]({is:"more-info-media_player",properties:{stateObj:{type:Object,observer:"stateObjChanged"},isOff:{type:Boolean,value:!1},isPlaying:{type:Boolean,value:!1},isMuted:{type:Boolean,value:!1},volumeSliderValue:{type:Number,value:0},supportsPause:{type:Boolean,value:!1},supportsVolumeSet:{type:Boolean,value:!1},supportsVolumeMute:{type:Boolean,value:!1},supportsPreviousTrack:{type:Boolean,value:!1},supportsNextTrack:{type:Boolean,value:!1},supportsTurnOn:{type:Boolean,value:!1},supportsTurnOff:{type:Boolean,value:!1}},stateObjChanged:function(t){var e=this;t&&(this.isOff="off"===t.state,this.isPlaying="playing"===t.state,this.volumeSliderValue=100*t.attributes.volume_level,this.isMuted=t.attributes.is_volume_muted,this.supportsPause=0!==(1&t.attributes.supported_media_commands),this.supportsVolumeSet=0!==(4&t.attributes.supported_media_commands),this.supportsVolumeMute=0!==(8&t.attributes.supported_media_commands),this.supportsPreviousTrack=0!==(16&t.attributes.supported_media_commands),this.supportsNextTrack=0!==(32&t.attributes.supported_media_commands),this.supportsTurnOn=0!==(128&t.attributes.supported_media_commands),this.supportsTurnOff=0!==(256&t.attributes.supported_media_commands)),this.async(function(){return e.fire("iron-resize")},500)},computeClassNames:function(t){return s["default"](t,c)},computeIsOff:function(t){return"off"===t.state},computeMuteVolumeIcon:function(t){return t?"av:volume-off":"av:volume-up"},computePlaybackControlIcon:function(){return this.isPlaying?this.supportsPause?"av:pause":"av:stop":"av:play-arrow"},computeHidePowerButton:function(t,e,n){return t?!e:!n},handleTogglePower:function(){this.callService(this.isOff?"turn_on":"turn_off")},handlePrevious:function(){this.callService("media_previous_track")},handlePlaybackControl:function(){this.callService("media_play_pause")},handleNext:function(){this.callService("media_next_track")},handleVolumeTap:function(){this.supportsVolumeMute&&this.callService("volume_mute",{is_volume_muted:!this.isMuted})},volumeSliderChanged:function(t){var e=parseFloat(t.target.value),n=e>0?e/100:0;this.callService("volume_set",{volume_level:n})},callService:function(t,e){var n=e||{};n.entity_id=this.stateObj.entityId,i.serviceActions.callService("media_player",t,n)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"more-info-script",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(41),s=r(a),c=u.util.parseDateTime;e["default"]=new o["default"]({is:"more-info-sun",properties:{stateObj:{type:Object},risingDate:{type:Object,computed:"computeRising(stateObj)"},settingDate:{type:Object,computed:"computeSetting(stateObj)"}},computeRising:function(t){return c(t.attributes.next_rising)},computeSetting:function(t){return c(t.attributes.next_setting)},computeOrder:function(t,e){return t>e?["set","ris"]:["ris","set"]},itemCaption:function(t){return"ris"===t?"Rising ":"Setting "},itemDate:function(t){return"ris"===t?this.risingDate:this.settingDate},itemValue:function(t){return s["default"](this.itemDate(t))}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(21),s=r(a),c=["away_mode"];e["default"]=new u["default"]({is:"more-info-thermostat",properties:{stateObj:{type:Object,observer:"stateObjChanged"},tempMin:{type:Number},tempMax:{type:Number},targetTemperatureSliderValue:{type:Number},awayToggleChecked:{type:Boolean}},stateObjChanged:function(t){this.targetTemperatureSliderValue=t.attributes.temperature,this.awayToggleChecked="on"===t.attributes.away_mode,this.tempMin=t.attributes.min_temp,this.tempMax=t.attributes.max_temp},computeClassNames:function(t){return s["default"](t,c)},targetTemperatureSliderChanged:function(t){var e=parseInt(t.target.value,10);isNaN(e)||i.serviceActions.callService("thermostat","set_temperature",{entity_id:this.stateObj.entityId,temperature:e})},toggleChanged:function(t){var e=t.target.checked;e&&"off"===this.stateObj.attributes.away_mode?this.service_set_away(!0):e||"on"!==this.stateObj.attributes.away_mode||this.service_set_away(!1)},service_set_away:function(t){var e=this;i.serviceActions.callService("thermostat","set_away_mode",{away_mode:t,entity_id:this.stateObj.entityId}).then(function(){return e.stateObjChanged(e.stateObj)})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2);e["default"]=new o["default"]({is:"more-info-updater",properties:{stateObj:{type:Object}},updateTapped:function(){u.serviceActions.callService("updater","update",{})},linkTapped:function(){window.open(this.stateObj.attributes.link,"_blank")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),n(39),e["default"]=new o["default"]({is:"state-card-configurator",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9);var u=["playing","paused"];e["default"]=new o["default"]({is:"state-card-media_player",properties:{stateObj:{type:Object},isPlaying:{type:Boolean,computed:"computeIsPlaying(stateObj)"}},computeIsPlaying:function(t){return-1!==u.indexOf(t.state)},computePrimaryText:function(t,e){return e?t.attributes.media_title:t.stateDisplay},computeSecondaryText:function(t){var e=void 0;return"music"===t.attributes.media_content_type?t.attributes.media_artist:"tvshow"===t.attributes.media_content_type?(e=t.attributes.media_series_title,t.attributes.media_season&&t.attributes.media_episode&&(e+=" S"+t.attributes.media_season+"E"+t.attributes.media_episode),e):t.attributes.app_name?t.attributes.app_name:""}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2);n(9),e["default"]=new o["default"]({is:"state-card-scene",properties:{stateObj:{type:Object}},activateScene:function(){u.serviceActions.callTurnOn(this.stateObj.entityId)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),e["default"]=new o["default"]({is:"state-card-thermostat",properties:{stateObj:{type:Object}},computeTargetTemperature:function(t){return t.attributes.temperature+" "+t.attributes.unit_of_measurement}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),n(35),e["default"]=new o["default"]({is:"state-card-toggle"}),t.exports=e["default"]},function(t,e){"use strict";function n(t){return{attached:function(){var e=this;this.__unwatchFns=Object.keys(this.properties).reduce(function(n,r){if(!("bindNuclear"in e.properties[r]))return n;var i=e.properties[r].bindNuclear;if(!i)throw new Error("Undefined getter specified for key "+r);return e[r]=t.evaluate(i),n.concat(t.observe(i,function(t){e[r]=t}))},[])},detached:function(){for(;this.__unwatchFns.length;)this.__unwatchFns.shift()()}}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){return-1!==a.indexOf(t.domain)?t.domain:u["default"](t.entityId)?"toggle":"display"}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(22),u=r(o),a=["thermostat","configurator","scene","media_player"];t.exports=e["default"]},function(t,e){"use strict";function n(t){return-1!==r.indexOf(t.domain)?t.domain:"default"}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n;var r=["light","group","sun","configurator","thermostat","script","media_player","camera","updater","alarm_control_panel"];t.exports=e["default"]},function(t,e){"use strict";function n(t,e,n){var r=1-t-e,i=n/255,o=i/e*t,u=i/e*r,a=1.612*o-.203*i-.302*u,s=.509*-o+1.412*i+.066*u,c=.026*o-.072*i+.962*u;a=.0031308>=a?12.92*a:1.055*Math.pow(a,1/2.4)-.055,s=.0031308>=s?12.92*s:1.055*Math.pow(s,1/2.4)-.055,c=.0031308>=c?12.92*c:1.055*Math.pow(c,1/2.4)-.055;var l=Math.max(a,s,c);return a/=l,s/=l,c/=l,a=255*a,0>a&&(a=255),s=255*s,0>s&&(s=255),c=255*c,0>c&&(c=255),[a,s,c]}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){var r;(function(t,i,o){"use strict";(function(){function u(t){return"function"==typeof t||"object"==typeof t&&null!==t}function a(t){return"function"==typeof t}function s(t){return"object"==typeof t&&null!==t}function c(t){W=t}function l(t){Z=t}function f(){return function(){t.nextTick(v)}}function d(){return function(){q(v)}}function p(){var t=0,e=new tt(v),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function h(){var t=new MessageChannel;return t.port1.onmessage=v,function(){t.port2.postMessage(0)}}function _(){return function(){setTimeout(v,1)}}function v(){for(var t=0;$>t;t+=2){var e=rt[t],n=rt[t+1];e(n),rt[t]=void 0,rt[t+1]=void 0}$=0}function y(){try{var t=n(196);return q=t.runOnLoop||t.runOnContext,d()}catch(e){return _()}}function m(){}function g(){return new TypeError("You cannot resolve a promise with itself")}function b(){return new TypeError("A promises callback cannot return that same promise.")}function O(t){try{return t.then}catch(e){return at.error=e,at}}function w(t,e,n,r){try{t.call(e,n,r)}catch(i){return i}}function S(t,e,n){Z(function(t){var r=!1,i=w(n,e,function(n){r||(r=!0,e!==n?E(t,n):I(t,n))},function(e){r||(r=!0,P(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&i&&(r=!0,P(t,i))},t)}function M(t,e){e._state===ot?I(t,e._result):e._state===ut?P(t,e._result):D(e,void 0,function(e){E(t,e)},function(e){P(t,e)})}function T(t,e){if(e.constructor===t.constructor)M(t,e);else{var n=O(e);n===at?P(t,at.error):void 0===n?I(t,e):a(n)?S(t,e,n):I(t,e)}}function E(t,e){t===e?P(t,g()):u(e)?T(t,e):I(t,e)}function j(t){t._onerror&&t._onerror(t._result),C(t)}function I(t,e){t._state===it&&(t._result=e,t._state=ot,0!==t._subscribers.length&&Z(C,t))}function P(t,e){t._state===it&&(t._state=ut,t._result=e,Z(j,t))}function D(t,e,n,r){var i=t._subscribers,o=i.length;t._onerror=null,i[o]=e,i[o+ot]=n,i[o+ut]=r,0===o&&t._state&&Z(C,t)}function C(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,i,o=t._result,u=0;uu;u++)D(r.resolve(t[u]),void 0,e,n);return i}function H(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(m);return E(n,t),n}function Y(t){var e=this,n=new e(m);return P(n,t),n}function G(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function U(t){this._id=ht++,this._state=void 0,this._result=void 0,this._subscribers=[],m!==t&&(a(t)||G(),this instanceof U||F(),L(this,t))}function B(){var t;if("undefined"!=typeof i)t=i;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=_t)}var V;V=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var q,W,K,J=V,$=0,Z=({}.toString,function(t,e){rt[$]=t,rt[$+1]=e,$+=2,2===$&&(W?W(v):K())}),X="undefined"!=typeof window?window:void 0,Q=X||{},tt=Q.MutationObserver||Q.WebKitMutationObserver,et="undefined"!=typeof t&&"[object process]"==={}.toString.call(t),nt="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,rt=new Array(1e3);K=et?f():tt?p():nt?h():void 0===X?y():_();var it=void 0,ot=1,ut=2,at=new A,st=new A;N.prototype._validateInput=function(t){return J(t)},N.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},N.prototype._init=function(){this._result=new Array(this.length)};var ct=N;N.prototype._enumerate=function(){for(var t=this,e=t.length,n=t.promise,r=t._input,i=0;n._state===it&&e>i;i++)t._eachEntry(r[i],i)},N.prototype._eachEntry=function(t,e){var n=this,r=n._instanceConstructor;s(t)?t.constructor===r&&t._state!==it?(t._onerror=null,n._settledAt(t._state,e,t._result)):n._willSettleAt(r.resolve(t),e):(n._remaining--,n._result[e]=t)},N.prototype._settledAt=function(t,e,n){var r=this,i=r.promise;i._state===it&&(r._remaining--,t===ut?P(i,n):r._result[e]=n),0===r._remaining&&I(i,r._result)},N.prototype._willSettleAt=function(t,e){var n=this;D(t,void 0,function(t){n._settledAt(ot,e,t)},function(t){n._settledAt(ut,e,t)})};var lt=z,ft=R,dt=H,pt=Y,ht=0,_t=U;U.all=lt,U.race=ft,U.resolve=dt,U.reject=pt,U._setScheduler=c,U._setAsap=l,U._asap=Z,U.prototype={constructor:U,then:function(t,e){var n=this,r=n._state;if(r===ot&&!t||r===ut&&!e)return this;var i=new this.constructor(m),o=n._result;if(r){var u=arguments[r-1];Z(function(){x(r,i,u,o)})}else D(n,i,t,e);return i},"catch":function(t){return this.then(null,t)}};var vt=B,yt={Promise:_t,polyfill:vt};n(195).amd?(r=function(){return yt}.call(e,n,e,o),!(void 0!==r&&(o.exports=r))):"undefined"!=typeof o&&o.exports?o.exports=yt:"undefined"!=typeof this&&(this.ES6Promise=yt),vt()}).call(void 0)}).call(e,n(193),function(){return this}(),n(192)(t))},function(t,e,n){"use strict";var r=n(43),i=r(Date,"now"),o=i||function(){return(new Date).getTime()};t.exports=o},function(t,e){"use strict";function n(t){return"number"==typeof t&&t>-1&&t%1==0&&r>=t}var r=9007199254740991;t.exports=n},function(t,e,n){"use strict";var r=n(43),i=n(127),o=n(44),u="[object Array]",a=Object.prototype,s=a.toString,c=r(Array,"isArray"),l=c||function(t){return o(t)&&i(t.length)&&s.call(t)==u};t.exports=l},function(t,e,n){"use strict";function r(t){return null==t?!1:i(t)?l.test(s.call(t)):o(t)&&u.test(t)}var i=n(45),o=n(44),u=/^\[object .+?Constructor\]$/,a=Object.prototype,s=Function.prototype.toString,c=a.hasOwnProperty,l=RegExp("^"+s.call(c).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(125),i=n(15),o=function(t,e,n){var o=arguments.length<=3||void 0===arguments[3]?null:arguments[3],u=t.evaluate(i.getters.authInfo),a=u.host+"/api/"+n;return new r.Promise(function(t,n){var r=new XMLHttpRequest;r.open(e,a,!0),r.setRequestHeader("X-HA-access",u.authToken),r.onload=function(){if(r.status>199&&r.status<300)t(JSON.parse(r.responseText));else try{n(JSON.parse(r.responseText))}catch(e){n({})}},r.onerror=function(){return n({})},o?r.send(JSON.stringify(o)):r.send()})};e["default"]=o,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2],r=n.useStreaming,i=void 0===r?t.evaluate(s.getters.isSupported):r,o=n.rememberAuth,u=void 0===o?!1:o,f=n.host,d=void 0===f?"":f;t.dispatch(a["default"].VALIDATING_AUTH_TOKEN,{authToken:e,host:d}),c.actions.fetchAll(t).then(function(){t.dispatch(a["default"].VALID_AUTH_TOKEN,{authToken:e,host:d,rememberAuth:u}),i?s.actions.start(t,{syncOnInitialConnect:!1}):c.actions.start(t,{skipInitialSync:!0})},function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=e.message,r=void 0===n?l:n;t.dispatch(a["default"].INVALID_AUTH_TOKEN,{errorMessage:r +})})}function o(t){t.dispatch(a["default"].LOG_OUT,{})}Object.defineProperty(e,"__esModule",{value:!0}),e.validate=i,e.logOut=o;var u=n(14),a=r(u),s=n(29),c=n(31),l="Unexpected result from API"},function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=[["authAttempt","isValidating"],function(t){return!!t}];e.isValidating=n;var r=[["authAttempt","isInvalid"],function(t){return!!t}];e.isInvalidAttempt=r;var i=["authAttempt","errorMessage"];e.attemptErrorMessage=i;var o=["rememberAuth"];e.rememberAuth=o;var u=[["authAttempt","authToken"],["authAttempt","host"],function(t,e){return{authToken:t,host:e}}];e.attemptAuthInfo=u;var a=["authCurrent","authToken"];e.currentAuthToken=a;var s=[a,["authCurrent","host"],function(t,e){return{authToken:t,host:e}}];e.currentAuthInfo=s;var c=[n,["authAttempt","authToken"],["authCurrent","authToken"],function(t,e,n){return t?e:n}];e.authToken=c;var l=[n,u,s,function(t,e,n){return t?e:n}];e.authInfo=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){if(null==t)throw new TypeError("Cannot destructure undefined")}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function u(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t,e){var n=e.authToken,r=e.host;return d.toImmutable({authToken:n,host:r,isValidating:"true",isInvalid:!1,errorMessage:""})}function s(t,e){return i(e),v.getInitialState()}function c(t,e){var n=e.errorMessage;return t.withMutations(function(t){return t.set("isValidating",!1).set("isInvalid","true").set("errorMessage",n)})}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n1&&t.set(p,r)})}function a(){return _.getInitialState()}Object.defineProperty(e,"__esModule",{value:!0});var s=function(){function t(t,e){for(var n=0;no}Object.defineProperty(e,"__esModule",{value:!0});var i=n(3),o=6e4,u=["currentLogbookDate"];e.currentDate=u;var a=[u,["logbookEntriesUpdated"],function(t,e){return r(e.get(t))}];e.isCurrentStale=a;var s=[u,["logbookEntries"],function(t,e){return e.get(t)||i.toImmutable([])}];e.currentEntries=s;var c=["isLoadingLogbookEntries"];e.isLoadingEntries=c},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({currentLogbookDate:a["default"],isLoadingLogbookEntries:c["default"],logbookEntries:f["default"],logbookEntriesUpdated:p["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(154),a=i(u),s=n(155),c=i(s),l=n(156),f=i(l),d=n(157),p=i(d),h=n(150),_=r(h),v=n(151),y=r(v),m=_;e.actions=m;var g=y;e.getters=g},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var u=function(){function t(t,e){for(var n=0;n1)for(var n=1;n \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index ac34b5fc269..24623ff26ab 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit ac34b5fc26928238343f34f334a6f6f1a19d5442 +Subproject commit 24623ff26ab8cbf7b39f0a25c26d9d991063b61a From 18747f8ae1ef3958c0c25b0b7b19b7d17e31d798 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 23:12:00 -0700 Subject: [PATCH 150/744] Update some docs --- homeassistant/components/light/hue.py | 2 ++ homeassistant/components/light/hyperion.py | 12 +----------- homeassistant/components/light/limitlessled.py | 17 +---------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index b438d7b92b1..eff9aef4f36 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -2,6 +2,8 @@ homeassistant.components.light.hue ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for Hue lights. + +https://home-assistant.io/components/light.hue.html """ import logging import socket diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index fe48d58f945..d5fa3f9f2ce 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -3,17 +3,7 @@ homeassistant.components.light.hyperion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for Hyperion remotes. -Configuration: - -To connect to [a Hyperion server](https://github.com/tvdzwan/hyperion) you -will need to add something like the following to your configuration.yaml file: - -light: - platform: hyperion - host: 192.168.1.98 - port: 19444 - -The JSON server port is 19444 by default. +https://home-assistant.io/components/light.hyperion.html """ import logging import socket diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index ba8b8235260..b35ed379047 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -12,22 +12,7 @@ Support for LimitlessLED bulbs, also known as... - dekolight - iLight -Configuration: - -To use limitlessled you will need to add the following to your -configuration.yaml file. - -light: - platform: limitlessled - bridges: - - host: 192.168.1.10 - group_1_name: Living Room - group_2_name: Bedroom - group_3_name: Office - group_3_type: white - group_4_name: Kitchen - - host: 192.168.1.11 - group_2_name: Basement +https://home-assistant.io/components/light.limitlessled.html """ import logging From 0826ae2742f08faac8fb879b01ea7369dc782228 Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 08:37:13 +0000 Subject: [PATCH 151/744] Revise pywemo version, update discovery.device_from_description parameters --- homeassistant/components/switch/wemo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index b598af04946..5cc3be0dbe9 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -11,7 +11,7 @@ import logging from homeassistant.components.switch import SwitchDevice from homeassistant.const import STATE_ON, STATE_OFF, STATE_STANDBY -REQUIREMENTS = ['pywemo==0.3.1'] +REQUIREMENTS = ['pywemo==0.3.2'] _LOGGER = logging.getLogger(__name__) @@ -22,7 +22,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): import pywemo.discovery as discovery if discovery_info is not None: - device = discovery.device_from_description(discovery_info[2]) + device = discovery.device_from_description(discovery_info[2], discovery_info[3]) if device: add_devices_callback([WemoSwitch(device)]) From ef6c209c6f28a753b660de80a4a136d16e933d6a Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 09:03:57 +0000 Subject: [PATCH 152/744] Revise for clarity, disable pylink check --- homeassistant/components/switch/wemo.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 5cc3be0dbe9..820c0736c87 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -22,7 +22,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): import pywemo.discovery as discovery if discovery_info is not None: - device = discovery.device_from_description(discovery_info[2], discovery_info[3]) + location = discovery_info[2] + mac = discovery_info[3] + # pylint: disable=too-many-function-args + device = discovery.device_from_description(location, mac) if device: add_devices_callback([WemoSwitch(device)]) From c9f1dce6a2e6b0669f9775fd6973d1b886f2b6b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Mon, 26 Oct 2015 11:32:00 +0100 Subject: [PATCH 153/744] Coding style fixes --- homeassistant/components/device_tracker/ubus.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py index a231af02be7..3135993e91b 100644 --- a/homeassistant/components/device_tracker/ubus.py +++ b/homeassistant/components/device_tracker/ubus.py @@ -124,8 +124,7 @@ class UbusDeviceScanner(object): if not self.hostapd: hostapd = _req_json_rpc(self.url, self.session_id, 'list', 'hostapd.*', '') - for key in hostapd.keys(): - self.hostapd.append(key) + self.hostapd.extend(hostapd.keys()) self.last_results = [] results = 0 @@ -135,14 +134,9 @@ class UbusDeviceScanner(object): if result: results = results + 1 - for key in result["clients"].keys(): - self.last_results.append(key) - - if results: - return True - else: - return False + self.last_results.extend(result['clients'].keys()) + return bool(results) def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): """ Perform one JSON RPC operation. """ From fbb73dd5da7ade81683fc404c8e66ae78211b4c3 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Mon, 26 Oct 2015 11:50:09 +0100 Subject: [PATCH 154/744] fixed pylint warnings --- homeassistant/components/device_tracker/ubus.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py index 3135993e91b..195ed33e77b 100644 --- a/homeassistant/components/device_tracker/ubus.py +++ b/homeassistant/components/device_tracker/ubus.py @@ -89,8 +89,8 @@ class UbusDeviceScanner(object): 'call', 'uci', 'get', config="dhcp", type="dnsmasq") if result: - self.leasefile = next(iter(result["values"]. - values()))["leasefile"] + values = result["values"].values() + self.leasefile = next(iter(values))["leasefile"] else: return @@ -101,8 +101,8 @@ class UbusDeviceScanner(object): if result: self.mac2name = dict() for line in result["data"].splitlines(): - [time, mac, ip, name, lid] = line.split(" ") - self.mac2name[mac.upper()] = name + hosts = line.split(" ") + self.mac2name[hosts[1].upper()] = hosts[3] else: # Error, handled in the _req_json_rpc return @@ -138,6 +138,7 @@ class UbusDeviceScanner(object): return bool(results) + def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): """ Perform one JSON RPC operation. """ @@ -147,8 +148,7 @@ def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): "params": [session_id, subsystem, method, - params] - }) + params]}) try: res = requests.post(url, data=data, timeout=5) @@ -159,7 +159,7 @@ def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): if res.status_code == 200: response = res.json() - if (rpcmethod == "call"): + if rpcmethod == "call": return response["result"][1] else: return response["result"] From 49f4d92c62a28a3ebddf7f36b0c7983b603fccbb Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 10:51:23 +0000 Subject: [PATCH 155/744] Add dimmer as switch --- homeassistant/components/light/vera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index dc447f2e4b5..2c9cac640f0 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): controller = veraApi.VeraController(base_url) devices = [] try: - devices = controller.get_devices(['Switch', 'On/Off Switch']) + devices = controller.get_devices(['Switch', 'On/Off Switch', 'Dimmable Switch']) except RequestException: # There was a network related error connecting to the vera controller _LOGGER.exception("Error communicating with Vera API") From 649124d16217a744e53d6e7dfaf82933169fa3e0 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Mon, 26 Oct 2015 11:55:20 +0100 Subject: [PATCH 156/744] Added ubus.py to .coveragerc --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 7e474d287c2..b1ec9681fef 100644 --- a/.coveragerc +++ b/.coveragerc @@ -37,6 +37,7 @@ omit = homeassistant/components/device_tracker/asuswrt.py homeassistant/components/device_tracker/ddwrt.py homeassistant/components/device_tracker/luci.py + homeassistant/components/device_tracker/ubus.py homeassistant/components/device_tracker/netgear.py homeassistant/components/device_tracker/nmap_tracker.py homeassistant/components/device_tracker/thomson.py From b66e4f1e155801bb4d3b93b1eed2723c4fdaf7e2 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 26 Oct 2015 15:05:01 +0100 Subject: [PATCH 157/744] two different demo lights on without RGB and one with RGB support. and code cleanup more pylint aligned --- homeassistant/components/light/mqtt.py | 236 ++++++++++++++++--------- 1 file changed, 151 insertions(+), 85 deletions(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 078a9a4075f..ff78ecb3ca8 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -2,6 +2,33 @@ homeassistant.components.light.mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a MQTT light. + +config for RGB Version with brightness: + +light: + platform: mqtt + name: "Office Light RGB" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + rgb_state_topic: "office/rgb1/rgb/status" + rgb_command_topic: "office/rgb1/rgb/set" + qos: 0 + payload_on: "on" + payload_off: "off" + +config without RGB: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + qos: 0 + payload_on: "on" + payload_off: "off" + """ import logging import homeassistant.components.mqtt as mqtt @@ -17,15 +44,7 @@ DEFAULT_PAYLOAD_OFF = "off" DEFAULT_RGB = [255, 255, 255] DEFAULT_RGB_PATTERN = "%d,%d,%d" DEFAULT_BRIGHTNESS = 120 - -DEFAULT_STATE_TOPIC = "homeassistant/light/state" -DEFAULT_COMMAND_TOPIC = "homeassistant/light/switch" - -DEFAULT_STATE_BRIGHTNESS = "homeassistant/light/brightness/state" -DEFAULT_COMMAND_BRIGHTNESS = "homeassistant/light/brightness/set" - -DEFAULT_STATE_RGB = "homeassistant/light/rgb/state" -DEFAULT_COMMAND_RGB = "homeassistant/light/rgb/set" +DEFAULT_OPTIMISTIC = False DEPENDENCIES = ['mqtt'] @@ -39,80 +58,70 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): _LOGGER.error("Missing required variable: command_topic") return False - add_devices_callback([MqttLight( - hass, - config.get('name', DEFAULT_NAME), - config.get('state_topic', DEFAULT_STATE_TOPIC), - config.get('command_topic', DEFAULT_COMMAND_TOPIC), - config.get('brightness_state_topic', DEFAULT_STATE_BRIGHTNESS), - config.get('brightness_command_topic', DEFAULT_COMMAND_BRIGHTNESS), - config.get('rgb_state_topic', DEFAULT_STATE_RGB), - config.get('rgb_command_topic', DEFAULT_COMMAND_RGB), - config.get('rgb', DEFAULT_RGB), - config.get('qos', DEFAULT_QOS), - config.get('payload_on', DEFAULT_PAYLOAD_ON), - config.get('payload_off', DEFAULT_PAYLOAD_OFF), - config.get('brightness', DEFAULT_BRIGHTNESS))]) + if config.get('rgb_command_topic') is not None: + add_devices_callback([MqttLightRGB( + hass, + config.get('name', DEFAULT_NAME), + {"state_topic": config.get('state_topic'), + "command_topic": config.get('command_topic'), + "brightness_state_topic": config.get('brightness_state_topic'), + "brightness_command_topic": + config.get('brightness_command_topic'), + "rgb_state_topic": config.get('rgb_state_topic'), + "rgb_command_topic": config.get('rgb_command_topic')}, + config.get('rgb', DEFAULT_RGB), + config.get('qos', DEFAULT_QOS), + {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), + "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, + config.get('brightness', DEFAULT_BRIGHTNESS), + config.get('optimistic', DEFAULT_OPTIMISTIC))]) + + else: + add_devices_callback([MqttLight( + hass, + config.get('name', DEFAULT_NAME), + {"state_topic": config.get('state_topic'), + "command_topic": config.get('command_topic')}, + config.get('qos', DEFAULT_QOS), + {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), + "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, + config.get('optimistic', DEFAULT_OPTIMISTIC))]) class MqttLight(Light): - """ Provides a demo switch. """ - - # pylint: disable=too-many-instance-attributes - # pylint: disable=too-many-arguments,too-many-locals,bad-builtin - # Eight is reasonable in this case. + """ Provides a demo light. """ + # pylint: disable=too-many-arguments def __init__(self, hass, name, - state_topic, command_topic, - brightness_state_topic, brightness_command_topic, - rgb_state_topic, rgb_command_topic, - rgb, qos, - payload_on, payload_off, - brightness): + topic, + qos, + payload, + optimistic): self._hass = hass self._name = name - self._state_topic = state_topic - self._command_topic = command_topic - self._brightness_state_topic = brightness_state_topic - self._brightness_command_topic = brightness_command_topic - self._rgb_state_topic = rgb_state_topic - self._rgb_command_topic = rgb_command_topic - self._rgb = rgb + self._topic = topic self._qos = qos - self._payload_on = payload_on - self._payload_off = payload_off - self._brightness = brightness - self._xy = [[0.5, 0.5]] + self._payload = payload + self._optimistic = optimistic self._state = False def message_received(topic, payload, qos): """ A new MQTT message has been received. """ - if payload == self._payload_on: + if payload == self._payload["on"]: self._state = True - self.update_ha_state() - elif payload == self._payload_off: + elif payload == self._payload["off"]: self._state = False - self.update_ha_state() - def brightness_received(topic, payload, qos): - """ A new MQTT message has been received. """ - self._brightness = int(payload) self.update_ha_state() - def rgb_received(topic, payload, qos): - """ A new MQTT message has been received. """ - rgb = payload.split(",") - self._rgb = list(map(int, rgb)) - self.update_ha_state() - - # subscribe the state_topic - mqtt.subscribe(self._hass, self._state_topic, - message_received, self._qos) - mqtt.subscribe(self._hass, self._brightness_state_topic, - brightness_received, self._qos) - mqtt.subscribe(self._hass, self._rgb_state_topic, - rgb_received, self._qos) + if self._topic["state_topic"] is None: + # force optimistic mode + self._optimistic = True + else: + # subscribe the state_topic + mqtt.subscribe(self._hass, self._topic["state_topic"], + message_received, self._qos) @property def should_poll(self): @@ -124,6 +133,68 @@ class MqttLight(Light): """ Returns the name of the device if any. """ return self._name + @property + def is_on(self): + """ True if device is on. """ + return self._state + + def turn_on(self, **kwargs): + """ Turn the device on. """ + + mqtt.publish(self._hass, self._topic["command_topic"], + self._payload["on"], self._qos) + + if self._optimistic: + # optimistically assume that switch has changed state + self._state = True + self.update_ha_state() + + def turn_off(self, **kwargs): + """ Turn the device off. """ + mqtt.publish(self._hass, self._topic["command_topic"], + self._payload["off"], self._qos) + + if self._optimistic: + # optimistically assume that switch has changed state + self._state = False + self.update_ha_state() + + +class MqttLightRGB(MqttLight): + """ Provides a demo RGB light. """ + + # pylint: disable=too-many-arguments + def __init__(self, hass, name, + topic, + rgb, qos, + payload, + brightness, optimistic): + + super().__init__(hass, name, topic, qos, + payload, optimistic) + + self._rgb = rgb + self._brightness = brightness + self._xy = [[0.5, 0.5]] + + def brightness_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._brightness = int(payload) + self.update_ha_state() + + def rgb_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._rgb = [int(val) for val in payload.split(',')] + self.update_ha_state() + + if self._topic["brightness_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["brightness_state_topic"], + brightness_received, self._qos) + + if self._topic["rgb_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["rgb_state_topic"], + rgb_received, self._qos) + @property def brightness(self): """ Brightness of this light between 0..255. """ @@ -139,33 +210,28 @@ class MqttLight(Light): """ RGB color value. """ return self._xy - @property - def is_on(self): - """ True if device is on. """ - return self._state - def turn_on(self, **kwargs): """ Turn the device on. """ - if ATTR_RGB_COLOR in kwargs: + if ATTR_RGB_COLOR in kwargs and \ + self._topic["rgb_command_topic"] is not None: + self._rgb = kwargs[ATTR_RGB_COLOR] rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) - mqtt.publish(self._hass, self._rgb_command_topic, rgb, self._qos) + mqtt.publish(self._hass, self._topic["rgb_command_topic"], + rgb, self._qos) + + if ATTR_BRIGHTNESS in kwargs and \ + self._topic["brightness_command_topic"] is not None: - if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] - mqtt.publish(self._hass, self._brightness_command_topic, + mqtt.publish(self._hass, self._topic["brightness_command_topic"], self._brightness, self._qos) - if not self._state: - self._state = True - mqtt.publish(self._hass, self._command_topic, - self._payload_on, self._qos) - self.update_ha_state() + mqtt.publish(self._hass, self._topic["command_topic"], + self._payload["on"], self._qos) - def turn_off(self, **kwargs): - """ Turn the device off. """ - self._state = False - mqtt.publish(self._hass, self._command_topic, - self._payload_off, self._qos) - self.update_ha_state() + if self._optimistic: + # optimistically assume that switch has changed state + self._state = True + self.update_ha_state() From dbc05450a0a2cbc57ea4eb90c89340114ec9f3c9 Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 16:32:12 +0000 Subject: [PATCH 158/744] Bump requirements_all.txt version, remove pylint disable --- homeassistant/components/switch/wemo.py | 1 - requirements_all.txt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 820c0736c87..c710a914d80 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -24,7 +24,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if discovery_info is not None: location = discovery_info[2] mac = discovery_info[3] - # pylint: disable=too-many-function-args device = discovery.device_from_description(location, mac) if device: diff --git a/requirements_all.txt b/requirements_all.txt index 11d91043d12..2ec0fb339bb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -90,7 +90,7 @@ pynetgear==0.3 netdisco==0.5 # Wemo (switch.wemo) -pywemo==0.3.1 +pywemo==0.3.2 # Wink (*.wink) https://github.com/balloob/python-wink/archive/c2b700e8ca866159566ecf5e644d9c297f69f257.zip#python-wink==0.1 From 9ab9d0e383b61c301e2ae59da8dab4e6570ac387 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2015 20:49:20 -0700 Subject: [PATCH 159/744] Update netdisco requirement --- homeassistant/components/discovery.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index b75abc32e4b..f61d792bc60 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -19,7 +19,7 @@ from homeassistant.const import ( DOMAIN = "discovery" DEPENDENCIES = [] -REQUIREMENTS = ['netdisco==0.5'] +REQUIREMENTS = ['netdisco==0.5.1'] SCAN_INTERVAL = 300 # seconds diff --git a/requirements_all.txt b/requirements_all.txt index 2ec0fb339bb..0a26e15ecc6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -87,7 +87,7 @@ https://github.com/theolind/pymysensors/archive/d4b809c2167650691058d1e29bfd2c4b pynetgear==0.3 # Netdisco (discovery) -netdisco==0.5 +netdisco==0.5.1 # Wemo (switch.wemo) pywemo==0.3.2 From 55c0ee6b32fd8e797b3c64518d175e10ee37118d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2015 21:27:42 -0700 Subject: [PATCH 160/744] Version bump to 0.7.6 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4d5f68e24c5..07eee5c793b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.6.dev0" +__version__ = "0.7.6" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From ab48a94d2a8ba9b8abfbb49a67af38d242363118 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2015 21:32:04 -0700 Subject: [PATCH 161/744] Version bump to 0.7.7.dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 07eee5c793b..9d09aa52e97 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.6" +__version__ = "0.7.7.dev0" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From 52b1080ccd6626e7cad5c11d7a260bfa737d37cb Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 26 Oct 2015 21:11:24 +0100 Subject: [PATCH 162/744] Catch invalid chat ids --- homeassistant/components/notify/telegram.py | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/notify/telegram.py b/homeassistant/components/notify/telegram.py index 23b915baf1e..46f1acb1e75 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/notify/telegram.py @@ -15,6 +15,14 @@ from homeassistant.components.notify import ( from homeassistant.const import CONF_API_KEY _LOGGER = logging.getLogger(__name__) + +try: + import telegram +except ImportError: + _LOGGER.exception( + "Unable to import python-telegram-bot. " + "Did you maybe not install the 'python-telegram-bot' package?") + REQUIREMENTS = ['python-telegram-bot==2.8.7'] @@ -26,18 +34,10 @@ def get_service(hass, config): _LOGGER): return None - try: - import telegram - except ImportError: - _LOGGER.exception( - "Unable to import python-telegram-bot. " - "Did you maybe not install the 'python-telegram-bot' package?") - return None - try: bot = telegram.Bot(token=config[DOMAIN][CONF_API_KEY]) username = bot.getMe()['username'] - _LOGGER.info("Telegram bot is' %s'", username) + _LOGGER.info("Telegram bot is '%s'.", username) except urllib.error.HTTPError: _LOGGER.error("Please check your access token.") return None @@ -52,7 +52,6 @@ class TelegramNotificationService(BaseNotificationService): """ Implements notification service for Telegram. """ def __init__(self, api_key, chat_id): - import telegram self._api_key = api_key self._chat_id = chat_id self.bot = telegram.Bot(token=self._api_key) @@ -62,5 +61,8 @@ class TelegramNotificationService(BaseNotificationService): title = kwargs.get(ATTR_TITLE) - self.bot.sendMessage(chat_id=self._chat_id, - text=title + " " + message) + try: + self.bot.sendMessage(chat_id=self._chat_id, + text=title + " " + message) + except telegram.error.TelegramError: + _LOGGER.error("Your chat id '%s' is not valid.", self._chat_id) From 4fcd27e9050051371c9efa7f7850dbd486267c99 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 27 Oct 2015 16:52:43 +0100 Subject: [PATCH 163/744] light/mqtt to .coveragerc --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 28b4b926eab..bef52d57248 100644 --- a/.coveragerc +++ b/.coveragerc @@ -48,6 +48,7 @@ omit = homeassistant/components/downloader.py homeassistant/components/keyboard.py homeassistant/components/light/hue.py + homeassistant/components/light/mqtt.py homeassistant/components/light/limitlessled.py homeassistant/components/light/blinksticklight.py homeassistant/components/light/hyperion.py From e25503bc4a4fbc3f8f894480d197bb4f12c1dcd3 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 27 Oct 2015 22:34:49 +0000 Subject: [PATCH 164/744] Hue device capabilities. Color temperature support for light component and hue platform --- homeassistant/components/light/__init__.py | 25 +++++++++++-- homeassistant/components/light/hue.py | 37 +++++++++++++++++--- homeassistant/components/light/services.yaml | 4 +++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index c1b1579b4b5..051218bde24 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -43,6 +43,9 @@ Supports following parameters: A list containing three integers representing the xy color you want the light to be. + - ct_color + An INT in mireds represending the color temperature you want the light to be + - brightness Integer between 0 and 255 representing how bright you want the light to be. @@ -77,6 +80,7 @@ ATTR_TRANSITION = "transition" # lists holding color values ATTR_RGB_COLOR = "rgb_color" ATTR_XY_COLOR = "xy_color" +ATTR_CT_COLOR = "ct_color" # int with value 0 .. 255 representing brightness of the light ATTR_BRIGHTNESS = "brightness" @@ -105,6 +109,7 @@ DISCOVERY_PLATFORMS = { PROP_TO_ATTR = { 'brightness': ATTR_BRIGHTNESS, 'color_xy': ATTR_XY_COLOR, + 'color_ct': ATTR_CT_COLOR, } _LOGGER = logging.getLogger(__name__) @@ -119,8 +124,8 @@ def is_on(hass, entity_id=None): # pylint: disable=too-many-arguments def turn_on(hass, entity_id=None, transition=None, brightness=None, - rgb_color=None, xy_color=None, profile=None, flash=None, - effect=None): + rgb_color=None, xy_color=None, ct_color=None, profile=None, + flash=None, effect=None): """ Turns all or specified light on. """ data = { key: value for key, value in [ @@ -130,6 +135,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None, (ATTR_BRIGHTNESS, brightness), (ATTR_RGB_COLOR, rgb_color), (ATTR_XY_COLOR, xy_color), + (ATTR_CT_COLOR, ct_color), (ATTR_FLASH, flash), (ATTR_EFFECT, effect), ] if value is not None @@ -240,6 +246,16 @@ def setup(hass, config): # ValueError if value could not be converted to float pass + if ATTR_CT_COLOR in dat: + # ct_color should be an int of mirads value + ctcolor = dat.get(ATTR_CT_COLOR) + + # Without this check, a ctcolor with value '99' would work + # These values are based on Philips Hue, may need ajustment later + if isinstance(ctcolor, int): + if 154 <= ctcolor <= 500: + params[ATTR_CT_COLOR] = ctcolor + if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints @@ -301,6 +317,11 @@ class Light(ToggleEntity): """ XY color value [float, float]. """ return None + @property + def color_ct(self): + """ CT color value in mirads. """ + return None + @property def device_state_attributes(self): """ Returns device specific state attributes. """ diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index eff9aef4f36..88db7b990ee 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -14,9 +14,9 @@ from homeassistant.loader import get_component import homeassistant.util as util from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION, - ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT, - EFFECT_COLORLOOP) + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR, + ATTR_TRANSITION, ATTR_FLASH, FLASH_LONG, FLASH_SHORT, + ATTR_EFFECT, EFFECT_COLORLOOP) REQUIREMENTS = ['phue==0.8'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) @@ -147,6 +147,16 @@ class HueLight(Light): self.bridge = bridge self.update_lights = update_lights + # Hue can control multiple type of lights + capability_map = { + 'Dimmable light': ['bri'], + 'Dimmable plug-in unit': ['bri'], + 'Color light': ['bri', 'xy'], + 'Extended color light': ['bri', 'xy', 'ct'], + 'unknown': []} + self.capabilities = capability_map.get( + self.info['type'], 'unknown') + @property def unique_id(self): """ Returns the id of this Hue light """ @@ -161,12 +171,26 @@ class HueLight(Light): @property def brightness(self): """ Brightness of this light between 0..255. """ - return self.info['state']['bri'] + if 'bri' in self.capabilities: + return self.info['state']['bri'] + else: + return None @property def color_xy(self): """ XY color value. """ - return self.info['state'].get('xy') + if 'xy' in self.capabilities: + return self.info['state'].get('xy') + else: + return None + + @property + def color_ct(self): + """ CT color value. """ + if 'ct' in self.capabilities: + return self.info['state'].get('ct') + else: + return None @property def is_on(self): @@ -190,6 +214,9 @@ class HueLight(Light): if ATTR_XY_COLOR in kwargs: command['xy'] = kwargs[ATTR_XY_COLOR] + if ATTR_CT_COLOR in kwargs: + command['ct'] = kwargs[ATTR_CT_COLOR] + flash = kwargs.get(ATTR_FLASH) if flash == FLASH_LONG: diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index ed8b4b663ea..17e06a038af 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -20,6 +20,10 @@ turn_on: description: Color for the light in XY-format example: '[0.52, 0.43]' + ct_color: + description: Color temperature for the light in mireds (154-500) + example: '250' + brightness: description: Number between 0..255 indicating brightness example: 120 From e4d33bc6d4f5e9a0e16cdba5ac0526178275b977 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 27 Oct 2015 22:45:35 +0000 Subject: [PATCH 165/744] Included ct_color in code coverage --- homeassistant/components/light/demo.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 40a8cc023c5..0d72ef0dafd 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -8,7 +8,7 @@ Demo platform that implements lights. import random from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR) + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR) LIGHT_COLORS = [ @@ -16,22 +16,25 @@ LIGHT_COLORS = [ [0.460, 0.470], ] +LIGHT_TEMPS = [160, 300, 500] + def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return demo lights. """ add_devices_callback([ DemoLight("Bed Light", False), - DemoLight("Ceiling Lights", True, LIGHT_COLORS[0]), - DemoLight("Kitchen Lights", True, LIGHT_COLORS[1]) + DemoLight("Ceiling Lights", True, LIGHT_TEMPS[1], LIGHT_COLORS[0]), + DemoLight("Kitchen Lights", True, LIGHT_TEMPS[0], LIGHT_COLORS[1]) ]) class DemoLight(Light): """ Provides a demo switch. """ - def __init__(self, name, state, xy=None, brightness=180): + def __init__(self, name, state, xy=None, ct=None, brightness=180): self._name = name self._state = state self._xy = xy or random.choice(LIGHT_COLORS) + self._ct = ct or random.choice(LIGHT_TEMPS) self._brightness = brightness @property @@ -54,6 +57,11 @@ class DemoLight(Light): """ XY color value. """ return self._xy + @property + def color_ct(self): + """ CT color temperature. """ + return self._ct + @property def is_on(self): """ True if device is on. """ @@ -66,6 +74,9 @@ class DemoLight(Light): if ATTR_XY_COLOR in kwargs: self._xy = kwargs[ATTR_XY_COLOR] + if ATTR_CT_COLOR in kwargs: + self._ct = kwargs[ATTR_CT_COLOR] + if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] From 805aecd6f90b4ff0fbc1f9394e4bfe9e2ed30446 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 27 Oct 2015 22:49:45 +0000 Subject: [PATCH 166/744] pylint & flake cleanup --- homeassistant/components/light/demo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 0d72ef0dafd..c3c70da5418 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -30,6 +30,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class DemoLight(Light): """ Provides a demo switch. """ + # pylint: disable=too-many-arguments def __init__(self, name, state, xy=None, ct=None, brightness=180): self._name = name self._state = state From bef0b2b01e4c8259957b79db47529de120fef3d1 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 27 Oct 2015 23:51:16 +0100 Subject: [PATCH 167/744] Make pins optional --- homeassistant/components/sensor/arest.py | 54 +++++++++++++----------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 22f84e1ecd9..bd1f5edcaea 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -28,10 +28,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): resource = config.get(CONF_RESOURCE) var_conf = config.get(CONF_MONITORED_VARIABLES) + pins = config.get('pins', None) - if None in (resource, var_conf): + + if resource is None: _LOGGER.error('Not all required config keys present: %s', - ', '.join((CONF_RESOURCE, CONF_MONITORED_VARIABLES))) + CONF_RESOURCE) return False try: @@ -49,31 +51,35 @@ def setup_platform(hass, config, add_devices, discovery_info=None): arest = ArestData(resource) dev = [] - pins = config.get('pins', None) - for variable in config['monitored_variables']: - if variable['name'] not in response['variables']: - _LOGGER.error('Variable: "%s" does not exist', variable['name']) - continue + if var_conf is not None: + for variable in config['monitored_variables']: + if variable['name'] not in response['variables']: + _LOGGER.error('Variable: "%s" does not exist', + variable['name']) + continue - dev.append(ArestSensor(arest, - resource, - config.get('name', response['name']), - variable['name'], - variable=variable['name'], - unit_of_measurement=variable.get( - 'unit_of_measurement'))) + dev.append(ArestSensor(arest, + resource, + config.get('name', response['name']), + variable['name'], + variable=variable['name'], + unit_of_measurement=variable.get( + 'unit_of_measurement'))) - for pinnum, pin in pins.items(): - dev.append(ArestSensor(ArestData(resource, pinnum), - resource, - config.get('name', response['name']), - pin.get('name'), - pin=pinnum, - unit_of_measurement=pin.get( - 'unit_of_measurement'), - corr_factor=pin.get('correction_factor', None), - decimal_places=pin.get('decimal_places', None))) + if pins is not None: + for pinnum, pin in pins.items(): + dev.append(ArestSensor(ArestData(resource, pinnum), + resource, + config.get('name', response['name']), + pin.get('name'), + pin=pinnum, + unit_of_measurement=pin.get( + 'unit_of_measurement'), + corr_factor=pin.get('correction_factor', + None), + decimal_places=pin.get('decimal_places', + None))) add_devices(dev) From b3b2f2e326b7211d707a74ae907b01a3428f7950 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 28 Oct 2015 00:18:03 +0100 Subject: [PATCH 168/744] Fix pylint and flake issues --- homeassistant/components/sensor/arest.py | 1 - homeassistant/components/switch/wemo.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index bd1f5edcaea..d1533e14798 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -30,7 +30,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): var_conf = config.get(CONF_MONITORED_VARIABLES) pins = config.get('pins', None) - if resource is None: _LOGGER.error('Not all required config keys present: %s', CONF_RESOURCE) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index c710a914d80..8781b5adf0f 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -15,7 +15,7 @@ REQUIREMENTS = ['pywemo==0.3.2'] _LOGGER = logging.getLogger(__name__) -# pylint: disable=unused-argument +# pylint: disable=unused-argument, too-many-function-args def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return WeMo switches. """ import pywemo From 6ef0d089eae6dc7bc5ab754aefc36d47a84c0706 Mon Sep 17 00:00:00 2001 From: pavoni Date: Tue, 27 Oct 2015 23:18:46 +0000 Subject: [PATCH 169/744] Add VeraLight class based on VeraSwitch - add dimmer support --- homeassistant/components/light/vera.py | 29 +++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index 2c9cac640f0..21df4485527 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -7,9 +7,13 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.vera.html """ import logging +import time + from requests.exceptions import RequestException from homeassistant.components.switch.vera import VeraSwitch +from homeassistant.components.light import Light, ATTR_BRIGHTNESS + REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/' 'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip' '#python-vera==0.1'] @@ -47,6 +51,29 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): exclude = extra_data.get('exclude', False) if exclude is not True: - lights.append(VeraSwitch(device, extra_data)) + lights.append(VeraLight(device, extra_data)) add_devices_callback(lights) + +class VeraLight(VeraSwitch): + """ Represents a Vera Light, including dimmable. """ + + @property + def state_attributes(self): + attr = super().state_attributes or {} + + if self.vera_device.is_dimmable: + attr[ATTR_BRIGHTNESS] = self.vera_device.get_brightness() + + return attr + + + def turn_on(self, **kwargs): + if ATTR_BRIGHTNESS in kwargs and self.vera_device.is_dimmable: + self.vera_device.set_brightness(kwargs[ATTR_BRIGHTNESS]) + else: + self.vera_device.switch_on() + + self.last_command_send = time.time() + self.is_on_status = True + From 0a36c96a557cb58b185d20c763ff7f52814c0c2e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Oct 2015 19:51:50 -0700 Subject: [PATCH 170/744] Fill in service info for thermostat --- .../components/thermostat/services.yaml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/homeassistant/components/thermostat/services.yaml b/homeassistant/components/thermostat/services.yaml index e69de29bb2d..0d4f4726204 100644 --- a/homeassistant/components/thermostat/services.yaml +++ b/homeassistant/components/thermostat/services.yaml @@ -0,0 +1,24 @@ + +set_away_mode: + description: Turn away mode on/off for a thermostat + + fields: + entity_id: + description: Name(s) of entities to change + example: 'light.kitchen' + + away_mode: + description: New value of away mode + example: true + +set_temperature: + description: Set temperature of thermostat + + fields: + entity_id: + description: Name(s) of entities to change + example: 'light.kitchen' + + temperature: + description: New target temperature for thermostat + example: 25 From 16a3511c0a614d85294a4a0575a9a1112712bb48 Mon Sep 17 00:00:00 2001 From: pavoni Date: Wed, 28 Oct 2015 10:57:10 +0000 Subject: [PATCH 171/744] Catch request exceptions and log a warning --- homeassistant/components/sensor/efergy.py | 37 ++++++++++++----------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/sensor/efergy.py b/homeassistant/components/sensor/efergy.py index 8bc8a668cca..7aa33b181dd 100644 --- a/homeassistant/components/sensor/efergy.py +++ b/homeassistant/components/sensor/efergy.py @@ -8,7 +8,7 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.efergy.html """ import logging -from requests import get +from requests import get, RequestException from homeassistant.helpers.entity import Entity @@ -80,19 +80,22 @@ class EfergySensor(Entity): def update(self): """ Gets the Efergy monitor data from the web service. """ - if self.type == 'instant_readings': - url_string = _RESOURCE + 'getInstant?token=' + self.app_token - response = get(url_string) - self._state = response.json()['reading'] / 1000 - elif self.type == 'budget': - url_string = _RESOURCE + 'getBudget?token=' + self.app_token - response = get(url_string) - self._state = response.json()['status'] - elif self.type == 'cost': - url_string = _RESOURCE + 'getCost?token=' + self.app_token \ - + '&offset=' + self.utc_offset + '&period=' \ - + self.period - response = get(url_string) - self._state = response.json()['sum'] - else: - self._state = 'Unknown' + try: + if self.type == 'instant_readings': + url_string = _RESOURCE + 'getInstant?token=' + self.app_token + response = get(url_string) + self._state = response.json()['reading'] / 1000 + elif self.type == 'budget': + url_string = _RESOURCE + 'getBudget?token=' + self.app_token + response = get(url_string) + self._state = response.json()['status'] + elif self.type == 'cost': + url_string = _RESOURCE + 'getCost?token=' + self.app_token \ + + '&offset=' + self.utc_offset + '&period=' \ + + self.period + response = get(url_string) + self._state = response.json()['sum'] + else: + self._state = 'Unknown' + except RequestException: + _LOGGER.warning('Could not update status for %s', self.name) From de027609d8696a15ff57ee3f13b3868ec63f058e Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 12:27:58 +0100 Subject: [PATCH 172/744] Fixed entity_id for the script component. Alias now does not override the entity_id Fixed issue: #561 --- homeassistant/components/script.py | 5 +++-- homeassistant/helpers/entity_component.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 3f892fdfa80..e5bfe61bfa5 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -75,7 +75,7 @@ def setup(hass, config): _LOGGER.warn("Missing key 'sequence' for script %s", name) continue alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, cfg[CONF_SEQUENCE]) + script = Script(hass, alias, name, cfg[CONF_SEQUENCE]) component.add_entities((script,)) _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) @@ -100,9 +100,10 @@ def setup(hass, config): class Script(ToggleEntity): """ Represents a script. """ - def __init__(self, hass, name, sequence): + def __init__(self, hass, name, entity_id, sequence): self.hass = hass self._name = name + self.entity_id = entity_id self.sequence = sequence self._lock = threading.Lock() self._cur = -1 diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 708bf6e93a9..5223b7f0ca3 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -65,8 +65,10 @@ class EntityComponent(object): if entity is not None and entity not in self.entities.values(): entity.hass = self.hass - entity.entity_id = generate_entity_id( - self.entity_id_format, entity.name, self.entities.keys()) + if entity.entity_id is None: + entity.entity_id = generate_entity_id( + self.entity_id_format, entity.name, + self.entities.keys()) self.entities[entity.entity_id] = entity From 48bfc98acb45b0114ad7c004fc1aaa69ad823ab4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 19:52:09 +0100 Subject: [PATCH 173/744] Fixed entity name --- homeassistant/components/script.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index e5bfe61bfa5..9849a243a41 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -75,7 +75,8 @@ def setup(hass, config): _LOGGER.warn("Missing key 'sequence' for script %s", name) continue alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, name, cfg[CONF_SEQUENCE]) + entity_id = "{}.{}".format(DOMAIN, name) + script = Script(hass, alias, entity_id, cfg[CONF_SEQUENCE]) component.add_entities((script,)) _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) From 10c95b435250ccc7b754aa726153413238026ad7 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 20:17:17 +0100 Subject: [PATCH 174/744] Added pylint hint --- homeassistant/components/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 9849a243a41..084f5502426 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -99,6 +99,7 @@ def setup(hass, config): return True +# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ def __init__(self, hass, name, entity_id, sequence): From 12495c717e9c901e106fd3f46c79049e73d5e7eb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Oct 2015 12:24:33 -0700 Subject: [PATCH 175/744] Fix script regression --- homeassistant/components/script.py | 21 +++++++++++++-------- homeassistant/helpers/entity_component.py | 6 ++++-- tests/components/test_script.py | 14 +++++++++++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 3f892fdfa80..6d5f0ea37a1 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -16,7 +16,7 @@ import threading from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.event import track_point_in_utc_time -from homeassistant.util import split_entity_id +from homeassistant.util import slugify, split_entity_id from homeassistant.const import ( ATTR_ENTITY_ID, EVENT_TIME_CHANGED, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF) @@ -70,14 +70,17 @@ def setup(hass, config): if script: script.turn_on() - for name, cfg in config[DOMAIN].items(): - if not cfg.get(CONF_SEQUENCE): - _LOGGER.warn("Missing key 'sequence' for script %s", name) + for object_id, cfg in config[DOMAIN].items(): + if object_id != slugify(object_id): + _LOGGER.warn("Found invalid key for script: %s. Use %s instead.", + object_id, slugify(object_id)) continue - alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, cfg[CONF_SEQUENCE]) + if not cfg.get(CONF_SEQUENCE): + _LOGGER.warn("Missing key 'sequence' for script %s", object_id) + continue + alias = cfg.get(CONF_ALIAS, object_id) + script = Script(hass, object_id, alias, cfg[CONF_SEQUENCE]) component.add_entities((script,)) - _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) def turn_on_service(service): @@ -100,8 +103,10 @@ def setup(hass, config): class Script(ToggleEntity): """ Represents a script. """ - def __init__(self, hass, name, sequence): + # pylint: disable=too-many-instance-attributes + def __init__(self, hass, object_id, name, sequence): self.hass = hass + self.entity_id = ENTITY_ID_FORMAT.format(object_id) self._name = name self.sequence = sequence self._lock = threading.Lock() diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 708bf6e93a9..d3c0514dcad 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -65,8 +65,10 @@ class EntityComponent(object): if entity is not None and entity not in self.entities.values(): entity.hass = self.hass - entity.entity_id = generate_entity_id( - self.entity_id_format, entity.name, self.entities.keys()) + if getattr(entity, 'entity_id', None) is None: + entity.entity_id = generate_entity_id( + self.entity_id_format, entity.name, + self.entities.keys()) self.entities[entity.entity_id] = entity diff --git a/tests/components/test_script.py b/tests/components/test_script.py index d3974661bc6..e4abed18ec9 100644 --- a/tests/components/test_script.py +++ b/tests/components/test_script.py @@ -47,7 +47,18 @@ class TestScript(unittest.TestCase): } })) - self.assertIsNone(self.hass.states.get(ENTITY_ID)) + self.assertEqual(0, len(self.hass.states.entity_ids('script'))) + + def test_setup_with_invalid_object_id(self): + self.assertTrue(script.setup(self.hass, { + 'script': { + 'test hello world': { + 'sequence': [] + } + } + })) + + self.assertEqual(0, len(self.hass.states.entity_ids('script'))) def test_firing_event(self): event = 'test_event' @@ -61,6 +72,7 @@ class TestScript(unittest.TestCase): self.assertTrue(script.setup(self.hass, { 'script': { 'test': { + 'alias': 'Test Script', 'sequence': [{ 'event': event, 'event_data': { From 5f40115605c26b3c04197af7521b29c624df86aa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Oct 2015 12:43:04 -0700 Subject: [PATCH 176/744] Version bump to 0.7.7 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 9d09aa52e97..00f8ee408a3 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.7.dev0" +__version__ = "0.7.7" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From 2eb65c8eb8e3aca6021a04abf0a8a84e9762fa24 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Oct 2015 12:45:57 -0700 Subject: [PATCH 177/744] Version bump to 0.8.0.dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 00f8ee408a3..49825cee094 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.7" +__version__ = "0.8.0.dev0" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From f3500542dd85df362710aa921a5c2e2b777cb4cb Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 20:17:17 +0100 Subject: [PATCH 178/744] Added pylint hint --- homeassistant/components/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 6d5f0ea37a1..a14919cab67 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -101,6 +101,7 @@ def setup(hass, config): return True +# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ # pylint: disable=too-many-instance-attributes From 30de5af445772775401f894d76c0c8a095aed2c8 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 22:08:04 +0100 Subject: [PATCH 179/744] Fix for Philio Zwave devices which don't send an off event. --- homeassistant/components/sensor/zwave.py | 63 +++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 0cd136421a0..10d8734bcac 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -7,15 +7,27 @@ For more details about the zwave component, please refer to the documentation at https://home-assistant.io/components/zwave.html """ # pylint: disable=import-error +from homeassistant.helpers.event import track_point_in_time from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - +import datetime +import homeassistant.util.dt as dt_util import homeassistant.components.zwave as zwave from homeassistant.helpers.entity import Entity from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF, TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) +PHILIO = '013c' +PHILIO_SLIM_SENSOR = '0002' +PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) + +WORKAROUND_NO_TRIGGER_OFF_EVENT = 'trigger_no_off_event' + +SPECIFIC_DEVICE_MAPPINGS = [ + (WORKAROUND_NO_TRIGGER_OFF_EVENT, PHILIO_SLIM_SENSOR_MOTION), +] + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ @@ -24,10 +36,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value.set_change_verified(False) - # if 1 in groups and (zwave.NETWORK.controller.node_id not in - # groups[1].associations): - # node.groups[1].add_association(zwave.NETWORK.controller.node_id) + # Check workaround mappings for specific devices + for workaround_definition in SPECIFIC_DEVICE_MAPPINGS: + workaround, sensor_specification = workaround_definition + if sensor_specification == ( + value.command_class, value.node.manufacturer_id, + value.node.manufacturer_id, value.node.manufacturer_id): + if workaround == WORKAROUND_NO_TRIGGER_OFF_EVENT: + add_devices([ZWaveTriggerSensor(value, hass)]) + return + # generic Device mappings if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) @@ -37,12 +56,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ZWaveSensor(Entity): """ Represents a Z-Wave sensor. """ + def __init__(self, sensor_value): self._value = sensor_value self._node = sensor_value.node dispatcher.connect( - self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) + self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) @property def should_poll(self): @@ -90,7 +110,7 @@ class ZWaveSensor(Entity): def unit_of_measurement(self): return self._value.units - def _value_changed(self, value): + def value_changed(self, value): """ Called when a value has changed on the network. """ if self._value.value_id == value.value_id: self.update_ha_state() @@ -106,6 +126,37 @@ class ZWaveBinarySensor(ZWaveSensor): return STATE_ON if self._value.data else STATE_OFF +class ZWaveTriggerSensor(ZWaveSensor): + """ Represents a stateless sensor which triggers events within Z-Wave. """ + + def __init__(self, sensor_value, hass): + super(ZWaveTriggerSensor, self).__init__(sensor_value) + self._hass = hass + self.invalidate_after = None + + def value_changed(self, value): + """ Called when a value has changed on the network. """ + if self._value.value_id == value.value_id: + self.update_ha_state() + if value.data: + # only allow this value to be true for 60 secs + self.invalidate_after = dt_util.utcnow() + datetime.timedelta( + seconds=60) + track_point_in_time( + self._hass, self.update_ha_state, + self.invalidate_after) + + @property + def state(self): + """ Returns the state of the sensor. """ + if not self._value.data or \ + (self.invalidate_after is not None and + self.invalidate_after <= dt_util.utcnow()): + return STATE_OFF + + return STATE_ON + + class ZWaveMultilevelSensor(ZWaveSensor): """ Represents a multi level sensor Z-Wave sensor. """ From d578bf3494c0416c5c361f02242e0a6d027ebff4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 22:17:17 +0100 Subject: [PATCH 180/744] Removed extra pylint hint from a previous merge --- homeassistant/components/script.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index a14919cab67..6d5f0ea37a1 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -101,7 +101,6 @@ def setup(hass, config): return True -# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ # pylint: disable=too-many-instance-attributes From 3b37a7b73785940793e01eb183509a7b05c2a678 Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 28 Oct 2015 17:20:15 -0400 Subject: [PATCH 181/744] bugfix for actiontec device tracker --- .../components/device_tracker/actiontec.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index a2f94f34c3a..1c13fedec48 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -30,7 +30,9 @@ _LOGGER = logging.getLogger(__name__) _LEASES_REGEX = re.compile( r'(?P([0-9]{1,3}[\.]){3}[0-9]{1,3})' + - r'\smac:\s(?P([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))') + r'\smac:\s(?P([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))' + + r'\svalid\sfor:\s(?P(-?\d+))' + + r'\ssec') # pylint: disable=unused-argument @@ -40,9 +42,7 @@ def get_scanner(hass, config): {DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]}, _LOGGER): return None - scanner = ActiontecDeviceScanner(config[DOMAIN]) - return scanner if scanner.success_init else None Device = namedtuple("Device", ["mac", "ip", "last_update"]) @@ -60,11 +60,8 @@ class ActiontecDeviceScanner(object): self.password = config[CONF_PASSWORD] minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0) self.home_interval = timedelta(minutes=minutes) - self.lock = threading.Lock() - self.last_results = [] - # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None @@ -109,7 +106,6 @@ class ActiontecDeviceScanner(object): exclude_targets.add(host) if len(exclude_targets) > 0: exclude_target_list = [t.ip for t in exclude_targets] - actiontec_data = self.get_actiontec_data() if not actiontec_data: return False @@ -118,8 +114,9 @@ class ActiontecDeviceScanner(object): if client in actiontec_data: actiontec_data.pop(client) for name, data in actiontec_data.items(): - device = Device(data['mac'], name, now) - self.last_results.append(device) + if data['timevalid'] > 0: + device = Device(data['mac'], name, now) + self.last_results.append(device) self.last_results.extend(exclude_targets) _LOGGER.info("actiontec scan successful") return True @@ -153,6 +150,7 @@ class ActiontecDeviceScanner(object): if match is not None: devices[match.group('ip')] = { 'ip': match.group('ip'), - 'mac': match.group('mac').upper() + 'mac': match.group('mac').upper(), + 'timevalid': int(match.group('timevalid')) } return devices From 8eeca945171b1975a3097f5ebc959d51f33a4dac Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 28 Oct 2015 17:43:41 -0400 Subject: [PATCH 182/744] removed home_interval option since it was added to the main device_tracker component --- .../components/device_tracker/actiontec.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index 1c13fedec48..38f5f78907f 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -23,9 +23,6 @@ from homeassistant.components.device_tracker import DOMAIN # Return cached results if last scan was less then this time ago MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) -# Interval in minutes to exclude devices from a scan while they are home -CONF_HOME_INTERVAL = "home_interval" - _LOGGER = logging.getLogger(__name__) _LEASES_REGEX = re.compile( @@ -58,16 +55,12 @@ class ActiontecDeviceScanner(object): self.host = config[CONF_HOST] self.username = config[CONF_USERNAME] self.password = config[CONF_PASSWORD] - minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0) - self.home_interval = timedelta(minutes=minutes) self.lock = threading.Lock() self.last_results = [] # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None _LOGGER.info("actiontec scanner initialized") - if self.home_interval: - _LOGGER.info("home_interval set to: %s", self.home_interval) def scan_devices(self): """ @@ -100,12 +93,6 @@ class ActiontecDeviceScanner(object): exclude_targets = set() exclude_target_list = [] now = dt_util.now() - if self.home_interval: - for host in self.last_results: - if host.last_update + self.home_interval > now: - exclude_targets.add(host) - if len(exclude_targets) > 0: - exclude_target_list = [t.ip for t in exclude_targets] actiontec_data = self.get_actiontec_data() if not actiontec_data: return False From bcb2451752e6a2603e98a36bed5475ea58c05519 Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 28 Oct 2015 17:47:13 -0400 Subject: [PATCH 183/744] fix pylint warning --- homeassistant/components/device_tracker/actiontec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index 38f5f78907f..c627c6fe118 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -17,7 +17,7 @@ import telnetlib import homeassistant.util.dt as dt_util from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers import validate_config -from homeassistant.util import Throttle, convert +from homeassistant.util import Throttle from homeassistant.components.device_tracker import DOMAIN # Return cached results if last scan was less then this time ago From 6bad702db4a3dd80787057543d4dbee5db0298f4 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 28 Oct 2015 23:12:16 +0000 Subject: [PATCH 184/744] Renamed to color_temp, removed capabilities (not needed afterall) --- homeassistant/components/light/__init__.py | 24 +++++++------- homeassistant/components/light/demo.py | 8 ++--- homeassistant/components/light/hue.py | 35 +++++--------------- homeassistant/components/light/services.yaml | 2 +- 4 files changed, 25 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 051218bde24..d9ec71c460b 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -43,7 +43,7 @@ Supports following parameters: A list containing three integers representing the xy color you want the light to be. - - ct_color + - color_temp An INT in mireds represending the color temperature you want the light to be - brightness @@ -80,7 +80,7 @@ ATTR_TRANSITION = "transition" # lists holding color values ATTR_RGB_COLOR = "rgb_color" ATTR_XY_COLOR = "xy_color" -ATTR_CT_COLOR = "ct_color" +ATTR_COLOR_TEMP = "color_temp" # int with value 0 .. 255 representing brightness of the light ATTR_BRIGHTNESS = "brightness" @@ -109,7 +109,7 @@ DISCOVERY_PLATFORMS = { PROP_TO_ATTR = { 'brightness': ATTR_BRIGHTNESS, 'color_xy': ATTR_XY_COLOR, - 'color_ct': ATTR_CT_COLOR, + 'color_temp': ATTR_COLOR_TEMP, } _LOGGER = logging.getLogger(__name__) @@ -124,7 +124,7 @@ def is_on(hass, entity_id=None): # pylint: disable=too-many-arguments def turn_on(hass, entity_id=None, transition=None, brightness=None, - rgb_color=None, xy_color=None, ct_color=None, profile=None, + rgb_color=None, xy_color=None, color_temp=None, profile=None, flash=None, effect=None): """ Turns all or specified light on. """ data = { @@ -135,7 +135,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None, (ATTR_BRIGHTNESS, brightness), (ATTR_RGB_COLOR, rgb_color), (ATTR_XY_COLOR, xy_color), - (ATTR_CT_COLOR, ct_color), + (ATTR_COLOR_TEMP, color_temp), (ATTR_FLASH, flash), (ATTR_EFFECT, effect), ] if value is not None @@ -246,15 +246,15 @@ def setup(hass, config): # ValueError if value could not be converted to float pass - if ATTR_CT_COLOR in dat: - # ct_color should be an int of mirads value - ctcolor = dat.get(ATTR_CT_COLOR) + if ATTR_COLOR_TEMP in dat: + # color_temp should be an int of mirads value + colortemp = dat.get(ATTR_COLOR_TEMP) # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later - if isinstance(ctcolor, int): - if 154 <= ctcolor <= 500: - params[ATTR_CT_COLOR] = ctcolor + if isinstance(colortemp, int): + if 154 <= colortemp <= 500: + params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: try: @@ -318,7 +318,7 @@ class Light(ToggleEntity): return None @property - def color_ct(self): + def color_temp(self): """ CT color value in mirads. """ return None diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index c3c70da5418..76ac07a0073 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -8,7 +8,7 @@ Demo platform that implements lights. import random from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR) + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_COLOR_TEMP) LIGHT_COLORS = [ @@ -59,7 +59,7 @@ class DemoLight(Light): return self._xy @property - def color_ct(self): + def color_temp(self): """ CT color temperature. """ return self._ct @@ -75,8 +75,8 @@ class DemoLight(Light): if ATTR_XY_COLOR in kwargs: self._xy = kwargs[ATTR_XY_COLOR] - if ATTR_CT_COLOR in kwargs: - self._ct = kwargs[ATTR_CT_COLOR] + if ATTR_COLOR_TEMP in kwargs: + self._ct = kwargs[ATTR_COLOR_TEMP] if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index 88db7b990ee..dcd05b9f4c9 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -14,7 +14,7 @@ from homeassistant.loader import get_component import homeassistant.util as util from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR, + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_COLOR_TEMP, ATTR_TRANSITION, ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT, EFFECT_COLORLOOP) @@ -124,7 +124,7 @@ def request_configuration(host, hass, add_devices_callback): _CONFIGURING[host], "Failed to register, please try again.") return - + # pylint: disable=unused-argument def hue_configuration_callback(data): """ Actions to do when our configuration callback is called. """ setup_bridge(host, hass, add_devices_callback) @@ -147,16 +147,6 @@ class HueLight(Light): self.bridge = bridge self.update_lights = update_lights - # Hue can control multiple type of lights - capability_map = { - 'Dimmable light': ['bri'], - 'Dimmable plug-in unit': ['bri'], - 'Color light': ['bri', 'xy'], - 'Extended color light': ['bri', 'xy', 'ct'], - 'unknown': []} - self.capabilities = capability_map.get( - self.info['type'], 'unknown') - @property def unique_id(self): """ Returns the id of this Hue light """ @@ -171,26 +161,17 @@ class HueLight(Light): @property def brightness(self): """ Brightness of this light between 0..255. """ - if 'bri' in self.capabilities: - return self.info['state']['bri'] - else: - return None + return self.info['state']['bri'] @property def color_xy(self): """ XY color value. """ - if 'xy' in self.capabilities: - return self.info['state'].get('xy') - else: - return None + return self.info['state'].get('xy') @property - def color_ct(self): + def color_temp(self): """ CT color value. """ - if 'ct' in self.capabilities: - return self.info['state'].get('ct') - else: - return None + return self.info['state'].get('ct') @property def is_on(self): @@ -214,8 +195,8 @@ class HueLight(Light): if ATTR_XY_COLOR in kwargs: command['xy'] = kwargs[ATTR_XY_COLOR] - if ATTR_CT_COLOR in kwargs: - command['ct'] = kwargs[ATTR_CT_COLOR] + if ATTR_COLOR_TEMP in kwargs: + command['ct'] = kwargs[ATTR_COLOR_TEMP] flash = kwargs.get(ATTR_FLASH) diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index 17e06a038af..8a0c5b8fded 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -20,7 +20,7 @@ turn_on: description: Color for the light in XY-format example: '[0.52, 0.43]' - ct_color: + color_temp: description: Color temperature for the light in mireds (154-500) example: '250' From f456d2ff23dde27bad930939deb7a6960763e5ce Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 28 Oct 2015 23:16:25 +0000 Subject: [PATCH 185/744] styling fix --- homeassistant/components/light/hue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index dcd05b9f4c9..cd9ad21984f 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -124,6 +124,7 @@ def request_configuration(host, hass, add_devices_callback): _CONFIGURING[host], "Failed to register, please try again.") return + # pylint: disable=unused-argument def hue_configuration_callback(data): """ Actions to do when our configuration callback is called. """ From c7a0b5800cafbc986e9b839bb1c5cbf9cd3846ad Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 29 Oct 2015 00:23:05 -0700 Subject: [PATCH 186/744] Update links in introduction component --- homeassistant/components/introduction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/introduction.py b/homeassistant/components/introduction.py index 4c367703903..5e5dcbd77c2 100644 --- a/homeassistant/components/introduction.py +++ b/homeassistant/components/introduction.py @@ -26,13 +26,13 @@ def setup(hass, config=None): Here are some resources to get started: - Configuring Home Assistant: - https://home-assistant.io/getting-started/configuration.html + https://home-assistant.io/getting-started/configuration/ - Available components: https://home-assistant.io/components/ - Troubleshooting your configuration: - https://home-assistant.io/getting-started/troubleshooting-configuration.html + https://home-assistant.io/getting-started/troubleshooting-configuration/ - Getting help: https://home-assistant.io/help/ From 031d5ce2553120b23f38019d66f919469cc9c4d3 Mon Sep 17 00:00:00 2001 From: pavoni Date: Tue, 27 Oct 2015 23:43:06 +0000 Subject: [PATCH 187/744] Fix style issues, update pyvera version. --- homeassistant/components/light/vera.py | 16 +++++++++------- homeassistant/components/switch/vera.py | 6 +++--- requirements_all.txt | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index 21df4485527..2e9e02ebbe5 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -12,11 +12,11 @@ import time from requests.exceptions import RequestException from homeassistant.components.switch.vera import VeraSwitch -from homeassistant.components.light import Light, ATTR_BRIGHTNESS +from homeassistant.components.light import ATTR_BRIGHTNESS -REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/' - 'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip' - '#python-vera==0.1'] +REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' + '90e203b58c5897930a9567252943b7c96c39652b.zip' + '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) @@ -39,7 +39,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): controller = veraApi.VeraController(base_url) devices = [] try: - devices = controller.get_devices(['Switch', 'On/Off Switch', 'Dimmable Switch']) + devices = controller.get_devices([ + 'Switch', + 'On/Off Switch', + 'Dimmable Switch']) except RequestException: # There was a network related error connecting to the vera controller _LOGGER.exception("Error communicating with Vera API") @@ -55,6 +58,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): add_devices_callback(lights) + class VeraLight(VeraSwitch): """ Represents a Vera Light, including dimmable. """ @@ -67,7 +71,6 @@ class VeraLight(VeraSwitch): return attr - def turn_on(self, **kwargs): if ATTR_BRIGHTNESS in kwargs and self.vera_device.is_dimmable: self.vera_device.set_brightness(kwargs[ATTR_BRIGHTNESS]) @@ -76,4 +79,3 @@ class VeraLight(VeraSwitch): self.last_command_send = time.time() self.is_on_status = True - diff --git a/homeassistant/components/switch/vera.py b/homeassistant/components/switch/vera.py index ecf922e8cfa..55e519d8616 100644 --- a/homeassistant/components/switch/vera.py +++ b/homeassistant/components/switch/vera.py @@ -15,9 +15,9 @@ from homeassistant.helpers.entity import ToggleEntity from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME) -REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/' - 'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip' - '#python-vera==0.1'] +REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' + '90e203b58c5897930a9567252943b7c96c39652b.zip' + '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 11d91043d12..d04ea496cea 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -128,7 +128,7 @@ pyfttt==0.3 https://github.com/balloob/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 # Vera (*.vera) -https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip#python-vera==0.1 +https://github.com/pavoni/home-assistant-vera-api/archive/90e203b58c5897930a9567252943b7c96c39652b.zip#python-vera==0.1.1 # Sonos (media_player.sonos) SoCo==0.11.1 From 0269be581355f811a98326e73f188ac5e4f0f7fc Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 30 Oct 2015 09:30:22 +0000 Subject: [PATCH 188/744] Update pyvera version --- homeassistant/components/light/vera.py | 2 +- homeassistant/components/switch/vera.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index 2e9e02ebbe5..f4c46976626 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -15,7 +15,7 @@ from homeassistant.components.switch.vera import VeraSwitch from homeassistant.components.light import ATTR_BRIGHTNESS REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' - '90e203b58c5897930a9567252943b7c96c39652b.zip' + 'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip' '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/switch/vera.py b/homeassistant/components/switch/vera.py index 55e519d8616..51d682bcdc1 100644 --- a/homeassistant/components/switch/vera.py +++ b/homeassistant/components/switch/vera.py @@ -16,7 +16,7 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME) REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' - '90e203b58c5897930a9567252943b7c96c39652b.zip' + 'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip' '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index d04ea496cea..6b7f57d79bc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -128,7 +128,7 @@ pyfttt==0.3 https://github.com/balloob/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 # Vera (*.vera) -https://github.com/pavoni/home-assistant-vera-api/archive/90e203b58c5897930a9567252943b7c96c39652b.zip#python-vera==0.1.1 +https://github.com/pavoni/home-assistant-vera-api/archive/efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip#python-vera==0.1.1 # Sonos (media_player.sonos) SoCo==0.11.1 From e961dd5f95aba15994be339bfa24d95b844a3f24 Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Fri, 30 Oct 2015 07:00:35 -0400 Subject: [PATCH 189/744] increase valid for time to 60 since I was having some issues. removed deprecated lines. --- .../components/device_tracker/actiontec.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index c627c6fe118..6296d766456 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -57,7 +57,6 @@ class ActiontecDeviceScanner(object): self.password = config[CONF_PASSWORD] self.lock = threading.Lock() self.last_results = [] - # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None _LOGGER.info("actiontec scanner initialized") @@ -90,21 +89,13 @@ class ActiontecDeviceScanner(object): return False with self.lock: - exclude_targets = set() - exclude_target_list = [] now = dt_util.now() actiontec_data = self.get_actiontec_data() if not actiontec_data: return False - self.last_results = [] - for client in exclude_target_list: - if client in actiontec_data: - actiontec_data.pop(client) - for name, data in actiontec_data.items(): - if data['timevalid'] > 0: - device = Device(data['mac'], name, now) - self.last_results.append(device) - self.last_results.extend(exclude_targets) + self.last_results = [Device(data['mac'], name, now) + for name, data in actiontec_data.items() + if data['timevalid'] > -60] _LOGGER.info("actiontec scan successful") return True From a56173676e43d0ba4b082aa393818b21af756367 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 30 Oct 2015 15:28:06 +0100 Subject: [PATCH 190/744] Fixed the workaround match logic --- homeassistant/components/sensor/zwave.py | 33 +++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 10d8734bcac..d6b55f93641 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -22,12 +22,11 @@ PHILIO = '013c' PHILIO_SLIM_SENSOR = '0002' PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) -WORKAROUND_NO_TRIGGER_OFF_EVENT = 'trigger_no_off_event' - -SPECIFIC_DEVICE_MAPPINGS = [ - (WORKAROUND_NO_TRIGGER_OFF_EVENT, PHILIO_SLIM_SENSOR_MOTION), -] +WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event' +DEVICE_MAPPINGS = { + PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, +} def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ @@ -36,17 +35,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value.set_change_verified(False) - # Check workaround mappings for specific devices - for workaround_definition in SPECIFIC_DEVICE_MAPPINGS: - workaround, sensor_specification = workaround_definition - if sensor_specification == ( - value.command_class, value.node.manufacturer_id, - value.node.manufacturer_id, value.node.manufacturer_id): - if workaround == WORKAROUND_NO_TRIGGER_OFF_EVENT: - add_devices([ZWaveTriggerSensor(value, hass)]) - return + # if 1 in groups and (zwave.NETWORK.controller.node_id not in + # groups[1].associations): + # node.groups[1].add_association(zwave.NETWORK.controller.node_id) - # generic Device mappings + specific_sensor_key = (value.node.manufacturer_id, + value.node.product_id, + value.index) + + # Check workaround mappings for specific devices + if specific_sensor_key in DEVICE_MAPPINGS: + if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: + add_devices([ZWaveTriggerSensor(value, hass)]) + return + + # generic Device mappings if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) From 2ad647bb097b72cf06e155d328e5a20ee20dc0b3 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 30 Oct 2015 15:30:08 +0100 Subject: [PATCH 191/744] Style fixes --- homeassistant/components/sensor/zwave.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index d6b55f93641..da5c8839d58 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -25,9 +25,10 @@ PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event' DEVICE_MAPPINGS = { - PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, + PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, } + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] From 194c6343ac89b754dbf5e4105ab850c3480cd92d Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Fri, 30 Oct 2015 19:01:42 +0000 Subject: [PATCH 192/744] Minor corrections to light and light/demo --- homeassistant/components/light/__init__.py | 3 +-- homeassistant/components/light/demo.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index d9ec71c460b..7c8a01407ac 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -252,8 +252,7 @@ def setup(hass, config): # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later - if isinstance(colortemp, int): - if 154 <= colortemp <= 500: + if isinstance(colortemp, int) and 154 <= colortemp <= 500: params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 76ac07a0073..d12a288ccc3 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -16,7 +16,7 @@ LIGHT_COLORS = [ [0.460, 0.470], ] -LIGHT_TEMPS = [160, 300, 500] +LIGHT_TEMPS = [160, 500] def setup_platform(hass, config, add_devices_callback, discovery_info=None): From b76471c4b3267e2ed805cf82027d36559bc2fe22 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Fri, 30 Oct 2015 19:15:38 +0000 Subject: [PATCH 193/744] :( .. pyliny --- homeassistant/components/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 7c8a01407ac..3bb2b1ab239 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -253,7 +253,7 @@ def setup(hass, config): # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later if isinstance(colortemp, int) and 154 <= colortemp <= 500: - params[ATTR_COLOR_TEMP] = colortemp + params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: try: From 31826ab263212fa6821b933899dad9c9f96aba44 Mon Sep 17 00:00:00 2001 From: hexxter Date: Sat, 31 Oct 2015 19:26:03 +0100 Subject: [PATCH 194/744] redesigned mqtt light an first steps with the unittest system --- homeassistant/components/light/mqtt.py | 187 +++++++++++-------------- tests/components/light/test_mqtt.py | 130 +++++++++++++++++ 2 files changed, 209 insertions(+), 108 deletions(-) create mode 100644 tests/components/light/test_mqtt.py diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index ff78ecb3ca8..4e9b58e272e 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -31,6 +31,8 @@ light: """ import logging + +import homeassistant.util.color as color_util import homeassistant.components.mqtt as mqtt from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR) @@ -41,9 +43,7 @@ DEFAULT_NAME = "MQTT Light" DEFAULT_QOS = 0 DEFAULT_PAYLOAD_ON = "on" DEFAULT_PAYLOAD_OFF = "off" -DEFAULT_RGB = [255, 255, 255] DEFAULT_RGB_PATTERN = "%d,%d,%d" -DEFAULT_BRIGHTNESS = 120 DEFAULT_OPTIMISTIC = False DEPENDENCIES = ['mqtt'] @@ -58,53 +58,46 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): _LOGGER.error("Missing required variable: command_topic") return False - if config.get('rgb_command_topic') is not None: - add_devices_callback([MqttLightRGB( - hass, - config.get('name', DEFAULT_NAME), - {"state_topic": config.get('state_topic'), - "command_topic": config.get('command_topic'), - "brightness_state_topic": config.get('brightness_state_topic'), - "brightness_command_topic": + add_devices_callback([MqttLight( + hass, + config.get('name', DEFAULT_NAME), + {"state_topic": config.get('state_topic'), + "command_topic": config.get('command_topic'), + "brightness_state_topic": config.get('brightness_state_topic'), + "brightness_command_topic": config.get('brightness_command_topic'), - "rgb_state_topic": config.get('rgb_state_topic'), - "rgb_command_topic": config.get('rgb_command_topic')}, - config.get('rgb', DEFAULT_RGB), - config.get('qos', DEFAULT_QOS), - {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), - "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, - config.get('brightness', DEFAULT_BRIGHTNESS), - config.get('optimistic', DEFAULT_OPTIMISTIC))]) + "rgb_state_topic": config.get('rgb_state_topic'), + "rgb_command_topic": config.get('rgb_command_topic')}, + config.get('rgb', [255, 255, 255]), + config.get('qos', DEFAULT_QOS), + {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), + "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, + config.get('brightness'), + config.get('optimistic', DEFAULT_OPTIMISTIC))]) - else: - add_devices_callback([MqttLight( - hass, - config.get('name', DEFAULT_NAME), - {"state_topic": config.get('state_topic'), - "command_topic": config.get('command_topic')}, - config.get('qos', DEFAULT_QOS), - {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), - "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, - config.get('optimistic', DEFAULT_OPTIMISTIC))]) +# pylint: disable=too-many-instance-attributes class MqttLight(Light): - """ Provides a demo light. """ + """ Provides a demo Mqtt light. """ # pylint: disable=too-many-arguments def __init__(self, hass, name, topic, - qos, + rgb, qos, payload, - optimistic): + brightness, optimistic): self._hass = hass self._name = name self._topic = topic + self._rgb = rgb self._qos = qos self._payload = payload + self._brightness = brightness self._optimistic = optimistic self._state = False + self._xy = None def message_received(topic, payload, qos): """ A new MQTT message has been received. """ @@ -123,6 +116,48 @@ class MqttLight(Light): mqtt.subscribe(self._hass, self._topic["state_topic"], message_received, self._qos) + def brightness_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._brightness = int(payload) + self.update_ha_state() + + def rgb_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._rgb = [int(val) for val in payload.split(',')] + self._xy = color_util.color_RGB_to_xy(int(self._rgb[0]), + int(self._rgb[1]), + int(self._rgb[2])) + self.update_ha_state() + + if self._topic["brightness_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["brightness_state_topic"], + brightness_received, self._qos) + self._brightness = 255 + else: + self._brightness = None + + if self._topic["rgb_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["rgb_state_topic"], + rgb_received, self._qos) + self._xy = [0, 0] + else: + self._xy = None + + @property + def brightness(self): + """ Brightness of this light between 0..255. """ + return self._brightness + + @property + def rgb_color(self): + """ RGB color value. """ + return self._rgb + + @property + def color_xy(self): + """ RGB color value. """ + return self._xy + @property def should_poll(self): """ No polling needed for a demo light. """ @@ -141,6 +176,19 @@ class MqttLight(Light): def turn_on(self, **kwargs): """ Turn the device on. """ + if ATTR_RGB_COLOR in kwargs and \ + self._topic["rgb_command_topic"] is not None: + self._rgb = kwargs[ATTR_RGB_COLOR] + rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) + mqtt.publish(self._hass, self._topic["rgb_command_topic"], + rgb, self._qos) + + if ATTR_BRIGHTNESS in kwargs and \ + self._topic["brightness_command_topic"] is not None: + self._brightness = kwargs[ATTR_BRIGHTNESS] + mqtt.publish(self._hass, self._topic["brightness_command_topic"], + self._brightness, self._qos) + mqtt.publish(self._hass, self._topic["command_topic"], self._payload["on"], self._qos) @@ -158,80 +206,3 @@ class MqttLight(Light): # optimistically assume that switch has changed state self._state = False self.update_ha_state() - - -class MqttLightRGB(MqttLight): - """ Provides a demo RGB light. """ - - # pylint: disable=too-many-arguments - def __init__(self, hass, name, - topic, - rgb, qos, - payload, - brightness, optimistic): - - super().__init__(hass, name, topic, qos, - payload, optimistic) - - self._rgb = rgb - self._brightness = brightness - self._xy = [[0.5, 0.5]] - - def brightness_received(topic, payload, qos): - """ A new MQTT message has been received. """ - self._brightness = int(payload) - self.update_ha_state() - - def rgb_received(topic, payload, qos): - """ A new MQTT message has been received. """ - self._rgb = [int(val) for val in payload.split(',')] - self.update_ha_state() - - if self._topic["brightness_state_topic"] is not None: - mqtt.subscribe(self._hass, self._topic["brightness_state_topic"], - brightness_received, self._qos) - - if self._topic["rgb_state_topic"] is not None: - mqtt.subscribe(self._hass, self._topic["rgb_state_topic"], - rgb_received, self._qos) - - @property - def brightness(self): - """ Brightness of this light between 0..255. """ - return self._brightness - - @property - def rgb_color(self): - """ RGB color value. """ - return self._rgb - - @property - def color_xy(self): - """ RGB color value. """ - return self._xy - - def turn_on(self, **kwargs): - """ Turn the device on. """ - - if ATTR_RGB_COLOR in kwargs and \ - self._topic["rgb_command_topic"] is not None: - - self._rgb = kwargs[ATTR_RGB_COLOR] - rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) - mqtt.publish(self._hass, self._topic["rgb_command_topic"], - rgb, self._qos) - - if ATTR_BRIGHTNESS in kwargs and \ - self._topic["brightness_command_topic"] is not None: - - self._brightness = kwargs[ATTR_BRIGHTNESS] - mqtt.publish(self._hass, self._topic["brightness_command_topic"], - self._brightness, self._qos) - - mqtt.publish(self._hass, self._topic["command_topic"], - self._payload["on"], self._qos) - - if self._optimistic: - # optimistically assume that switch has changed state - self._state = True - self.update_ha_state() diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py new file mode 100644 index 00000000000..28c0e75e256 --- /dev/null +++ b/tests/components/light/test_mqtt.py @@ -0,0 +1,130 @@ +""" +tests.components.light.test_mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests mqtt light. + +config for RGB Version with brightness: + +light: + platform: mqtt + name: "Office Light RGB" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + rgb_state_topic: "office/rgb1/rgb/status" + rgb_command_topic: "office/rgb1/rgb/set" + qos: 0 + payload_on: "on" + payload_off: "off" + +config without RGB: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + qos: 0 + payload_on: "on" + payload_off: "off" + +config without RGB and brightness: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + qos: 0 + payload_on: "on" + payload_off: "off" +""" +import unittest + +from homeassistant.const import STATE_ON, STATE_OFF +import homeassistant.core as ha +import homeassistant.components.light as light +from tests.common import mock_mqtt_component, fire_mqtt_message + + +class TestLightMQTT(unittest.TestCase): + """ Test the MQTT light. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + self.mock_publish = mock_mqtt_component(self.hass) + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass.stop() + + def test_controlling_state_via_topic(self): + self.assertTrue(light.setup(self.hass, { + 'switch': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'test_light_rgb/status', + 'command_topic': 'test_light_rgb/set', + 'brightness_state_topic': 'test_light_rgb/brightness/status', + 'brightness_command_topic': 'test_light_rgb/brightness/set', + 'rgb_state_topic': 'test_light_rgb/rgb/status', + 'rgb_command_topic': 'test_light_rgb/rgb/set', + 'qos': 0, + 'payload_on': 'on', + 'payload_off': 'off' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test', 'on') + self.hass.pool.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + fire_mqtt_message(self.hass, 'test', 'off') + self.hass.pool.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + def test_sending_mqtt_commands_and_optimistic(self): + self.assertTrue(light.setup(self.hass, { + 'switch': { + 'platform': 'mqtt', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness_state_topic': 'test_light_rgb/brightness/status', + 'brightness_command_topic': 'test_light_rgb/brightness/set', + 'rgb_state_topic': 'test_light_rgb/rgb/status', + 'rgb_command_topic': 'test_light_rgb/rgb/set', + 'qos': 2, + 'payload_on': 'on', + 'payload_off': 'off' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + switch.turn_on(self.hass, 'light.test') + self.hass.pool.block_till_done() + + self.assertEqual(('test_light_rgb/set', 'on', 2), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + switch.turn_off(self.hass, 'light.test') + self.hass.pool.block_till_done() + + self.assertEqual(('test_light_rgb/set', 'off', 2), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) From cae8932b180caef8039125747ed5ad07e95880a4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 21:23:33 +0100 Subject: [PATCH 195/744] Z-Wave workaround - Connected to the timeout to the configured node value "9. Turn Off Light Time" --- homeassistant/components/sensor/zwave.py | 24 +++++++++++++++++------- homeassistant/components/zwave.py | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index da5c8839d58..1880efccd57 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -47,11 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Check workaround mappings for specific devices if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: - add_devices([ZWaveTriggerSensor(value, hass)]) - return + re_arm_time = (zwave.get_config_value(node, 9) * 8) + add_devices([ZWaveTriggerSensor(value, hass, re_arm_time)]) - # generic Device mappings - if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: + # generic Device mappings + elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: @@ -131,12 +131,22 @@ class ZWaveBinarySensor(ZWaveSensor): class ZWaveTriggerSensor(ZWaveSensor): - """ Represents a stateless sensor which triggers events within Z-Wave. """ + """ + Represents a stateless sensor which + triggers events just 'On' within Z-Wave. + """ - def __init__(self, sensor_value, hass): + def __init__(self, sensor_value, hass, re_arm_sec=60): + """ + :param sensor_value: The z-wave node + :param hass: + :param re_arm_sec: Set state to Off re_arm_sec after the last On event + :return: + """ super(ZWaveTriggerSensor, self).__init__(sensor_value) self._hass = hass self.invalidate_after = None + self.re_arm_sec = re_arm_sec def value_changed(self, value): """ Called when a value has changed on the network. """ @@ -145,7 +155,7 @@ class ZWaveTriggerSensor(ZWaveSensor): if value.data: # only allow this value to be true for 60 secs self.invalidate_after = dt_util.utcnow() + datetime.timedelta( - seconds=60) + seconds=self.re_arm_sec) track_point_in_time( self._hass, self.update_ha_state, self.invalidate_after) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 86d65d1c42e..755908ad2d4 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -58,6 +58,15 @@ def nice_print_node(node): print("\n\n\n") +def get_config_value(node, value_index): + """ Returns the current config value for a specific index """ + + for _, value in node.values.items(): + # 112 == config command class + if value.command_class == 112 and value.index == value_index: + return value.data + + def setup(hass, config): """ Setup Z-wave. From c4261ae2e00d2f70de6a9019993db2e357985b68 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 23:03:40 +0100 Subject: [PATCH 196/744] Z-Wave workaround - Added a default value if we did not get any config value. --- homeassistant/components/sensor/zwave.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 1880efccd57..0cfc0682454 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -47,8 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Check workaround mappings for specific devices if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: - re_arm_time = (zwave.get_config_value(node, 9) * 8) - add_devices([ZWaveTriggerSensor(value, hass, re_arm_time)]) + # Default the multiplier to 4 + re_arm_multiplier = (zwave.get_config_value(value.node, 9) or 4) + add_devices([ + ZWaveTriggerSensor(value, hass, re_arm_multiplier * 8) + ]) # generic Device mappings elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: @@ -145,7 +148,7 @@ class ZWaveTriggerSensor(ZWaveSensor): """ super(ZWaveTriggerSensor, self).__init__(sensor_value) self._hass = hass - self.invalidate_after = None + self.invalidate_after = dt_util.utcnow() self.re_arm_sec = re_arm_sec def value_changed(self, value): From ec732becfce6eb68b666997cb68ab7de0131d9b6 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 23:34:19 +0100 Subject: [PATCH 197/744] Fixed the get_config_value method when the zwave node was changed while reading it. --- homeassistant/components/zwave.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 755908ad2d4..9f7df64312d 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -61,10 +61,15 @@ def nice_print_node(node): def get_config_value(node, value_index): """ Returns the current config value for a specific index """ - for _, value in node.values.items(): - # 112 == config command class - if value.command_class == 112 and value.index == value_index: - return value.data + try: + for value in node.values.values(): + # 112 == config command class + if value.command_class == 112 and value.index == value_index: + return value.data + except RuntimeError: + # If we get an runtime error the dict has changed while + # we was looking for a value, just do it again + return get_config_value(node, value_index) def setup(hass, config): From 82aec895a0cec4245122bd704c5babf438d1b52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 12:40:41 +0100 Subject: [PATCH 198/744] Minor bug in rfxtrx --- homeassistant/components/sensor/rfxtrx.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 07912b719d2..0ecaaf839c9 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -11,12 +11,14 @@ from collections import OrderedDict from homeassistant.const import (TEMP_CELCIUS) from homeassistant.helpers.entity import Entity -import homeassistant.components.rfxtrx as rfxtrx from RFXtrx import SensorEvent from homeassistant.util import slugify DEPENDENCIES = ['rfxtrx'] +import homeassistant.components.rfxtrx as rfxtrx + + DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), ('Humidity', '%'), From 92b05389f210f9db343f6c7dd5840ca6d067f2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 12:41:21 +0100 Subject: [PATCH 199/744] Update rfxtrx.py --- homeassistant/components/sensor/rfxtrx.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 0ecaaf839c9..217e2066dda 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -18,7 +18,6 @@ DEPENDENCIES = ['rfxtrx'] import homeassistant.components.rfxtrx as rfxtrx - DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), ('Humidity', '%'), From 77539a5b892ebbb3cf56408dfc7e8ffd59b893c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 12:51:09 +0100 Subject: [PATCH 200/744] revert prev commit --- homeassistant/components/sensor/rfxtrx.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 217e2066dda..40f79c97c42 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -12,12 +12,11 @@ from collections import OrderedDict from homeassistant.const import (TEMP_CELCIUS) from homeassistant.helpers.entity import Entity from RFXtrx import SensorEvent +import homeassistant.components.rfxtrx as rfxtrx from homeassistant.util import slugify DEPENDENCIES = ['rfxtrx'] -import homeassistant.components.rfxtrx as rfxtrx - DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), ('Humidity', '%'), From 0ff6a460c2d6871c95c30f9a84b48b8835d208cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 14:20:11 +0100 Subject: [PATCH 201/744] Update rfxtrx.py --- homeassistant/components/sensor/rfxtrx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 40f79c97c42..220d4ddbca7 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): def sensor_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ - if isinstance(event.device, SensorEvent): + if isinstance(event, SensorEvent): entity_id = slugify(event.device.id_string.lower()) # Add entity if not exist and the automatic_add is True From 728cd8bb5e65896deb8c291dc83c658574097d3b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 Nov 2015 00:02:17 -0800 Subject: [PATCH 202/744] Upgrade Vincenty to latest version --- requirements.txt | 1 + requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1b7d2396971..1bcfe79c2d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ requests>=2,<3 pyyaml>=3.11,<4 pytz>=2015.4 pip>=7.0.0 +vincenty==0.1.3 diff --git a/requirements_all.txt b/requirements_all.txt index 6b5ba8837ce..74fb53357c0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -3,7 +3,7 @@ requests>=2,<3 pyyaml>=3.11,<4 pytz>=2015.4 pip>=7.0.0 -vincenty==0.1.2 +vincenty==0.1.3 # Optional, needed for specific components From c6b5a043124c48a5106a35ae5b950cfc19bc8bcd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 Nov 2015 00:03:44 -0800 Subject: [PATCH 203/744] Allow more static files to be fingerprinted --- homeassistant/components/frontend/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index b327e510cd8..a15244ac52f 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -25,6 +25,8 @@ FRONTEND_URLS = [ '/devEvent'] STATES_URL = re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)') +_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE) + def setup(hass, config): """ Setup serving the frontend. """ @@ -80,9 +82,10 @@ def _handle_get_static(handler, path_match, data): """ Returns a static file for the frontend. """ req_file = util.sanitize_path(path_match.group('file')) - # Strip md5 hash out of frontend filename - if re.match(r'^frontend-[A-Za-z0-9]{32}\.html$', req_file): - req_file = "frontend.html" + # Strip md5 hash out + fingerprinted = _FINGERPRINT.match(req_file) + if fingerprinted: + req_file = "{}.{}".format(*fingerprinted.groups()) path = os.path.join(os.path.dirname(__file__), 'www_static', req_file) From 168eb8e5a2b3ae12dc90774374684684463ac951 Mon Sep 17 00:00:00 2001 From: hexxter Date: Mon, 2 Nov 2015 17:02:34 +0100 Subject: [PATCH 204/744] mqtt light test is working more test should be written --- tests/components/light/__init__.py | 0 tests/components/light/test_mqtt.py | 14 +++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 tests/components/light/__init__.py diff --git a/tests/components/light/__init__.py b/tests/components/light/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 28c0e75e256..98c3615aeba 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -64,7 +64,7 @@ class TestLightMQTT(unittest.TestCase): def test_controlling_state_via_topic(self): self.assertTrue(light.setup(self.hass, { - 'switch': { + 'light': { 'platform': 'mqtt', 'name': 'test', 'state_topic': 'test_light_rgb/status', @@ -82,21 +82,21 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - fire_mqtt_message(self.hass, 'test', 'on') + fire_mqtt_message(self.hass, 'test_light_rgb/status', 'on') self.hass.pool.block_till_done() state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) - fire_mqtt_message(self.hass, 'test', 'off') + fire_mqtt_message(self.hass, 'test_light_rgb/status', 'off') self.hass.pool.block_till_done() state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - + def test_sending_mqtt_commands_and_optimistic(self): self.assertTrue(light.setup(self.hass, { - 'switch': { + 'light': { 'platform': 'mqtt', 'name': 'test', 'command_topic': 'test_light_rgb/set', @@ -113,7 +113,7 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - switch.turn_on(self.hass, 'light.test') + light.turn_on(self.hass, 'light.test') self.hass.pool.block_till_done() self.assertEqual(('test_light_rgb/set', 'on', 2), @@ -121,7 +121,7 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) - switch.turn_off(self.hass, 'light.test') + light.turn_off(self.hass, 'light.test') self.hass.pool.block_till_done() self.assertEqual(('test_light_rgb/set', 'off', 2), From 186f68cce30a1f0200b632aa21ee2d75269ad7a1 Mon Sep 17 00:00:00 2001 From: hexxter Date: Mon, 2 Nov 2015 20:16:36 +0100 Subject: [PATCH 205/744] not working mqtt light unittest --- tests/components/light/test_mqtt.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 98c3615aeba..0d97b61441c 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -93,6 +93,13 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test_light_rgb/brightness/status', '100') + self.hass.pool.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual(100, + light_state.attributes) def test_sending_mqtt_commands_and_optimistic(self): self.assertTrue(light.setup(self.hass, { From 218a05356a44c0d24ed32351f6d921ac908db6cf Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 30 Oct 2015 10:54:04 +0100 Subject: [PATCH 206/744] Add docstrings --- homeassistant/components/sensor/cpuspeed.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/sensor/cpuspeed.py b/homeassistant/components/sensor/cpuspeed.py index 7f03e21c7a8..ed5d57597d9 100644 --- a/homeassistant/components/sensor/cpuspeed.py +++ b/homeassistant/components/sensor/cpuspeed.py @@ -47,6 +47,7 @@ class CpuSpeedSensor(Entity): @property def name(self): + """ The name of the sensor. """ return self._name @property @@ -56,6 +57,7 @@ class CpuSpeedSensor(Entity): @property def unit_of_measurement(self): + """ Unit the value is expressed in. """ return self._unit_of_measurement @property From 3cd89f84745bd20ea170198268b0677beab1d4ac Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sun, 1 Nov 2015 10:04:23 -0500 Subject: [PATCH 207/744] add disco, white effects --- homeassistant/components/light/__init__.py | 3 +++ .../components/light/limitlessled.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 3bb2b1ab239..a7865e1f514 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -96,6 +96,7 @@ FLASH_LONG = "long" # Apply an effect to the light, can be EFFECT_COLORLOOP ATTR_EFFECT = "effect" EFFECT_COLORLOOP = "colorloop" +EFFECT_WHITE = "white" LIGHT_PROFILES_FILE = "light_profiles.csv" @@ -282,6 +283,8 @@ def setup(hass, config): if ATTR_EFFECT in dat: if dat[ATTR_EFFECT] == EFFECT_COLORLOOP: params[ATTR_EFFECT] = EFFECT_COLORLOOP + if dat[ATTR_EFFECT] == EFFECT_WHITE: + params[ATTR_EFFECT] = EFFECT_WHITE for light in target_lights: light.turn_on(**params) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index b35ed379047..be9a59ede89 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -18,7 +18,8 @@ import logging from homeassistant.const import DEVICE_DEFAULT_NAME from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, - ATTR_XY_COLOR) + ATTR_XY_COLOR, ATTR_EFFECT, + EFFECT_COLORLOOP, EFFECT_WHITE) from homeassistant.util.color import color_RGB_to_xy _LOGGER = logging.getLogger(__name__) @@ -159,10 +160,22 @@ class RGBWLimitlessLED(LimitlessLED): if ATTR_XY_COLOR in kwargs: self._xy_color = kwargs[ATTR_XY_COLOR] - self.pool.execute(self.controller_id, "set_color", - self._xy_to_led_color(self._xy_color), self.group) + effect = kwargs.get(ATTR_EFFECT) + + if effect: + if effect == EFFECT_COLORLOOP: + self.pool.execute(self.controller_id, "disco", self.group) + if effect == EFFECT_WHITE: + self.pool.execute(self.controller_id, "white", self.group) + else: + self.pool.execute(self.controller_id, "set_color", + self._xy_to_led_color(self._xy_color), + self.group) + + # Brightness can be set independently of color self.pool.execute(self.controller_id, "set_brightness", self._brightness / 255.0, self.group) + self.update_ha_state() From 566712023dac96ebb88d95b8a123822a3f82a09d Mon Sep 17 00:00:00 2001 From: happyleaves Date: Mon, 2 Nov 2015 17:24:24 -0500 Subject: [PATCH 208/744] consolidate conditionals --- homeassistant/components/light/limitlessled.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index be9a59ede89..fef6cd0b8b1 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -162,11 +162,10 @@ class RGBWLimitlessLED(LimitlessLED): effect = kwargs.get(ATTR_EFFECT) - if effect: - if effect == EFFECT_COLORLOOP: - self.pool.execute(self.controller_id, "disco", self.group) - if effect == EFFECT_WHITE: - self.pool.execute(self.controller_id, "white", self.group) + if effect == EFFECT_COLORLOOP: + self.pool.execute(self.controller_id, "disco", self.group) + if effect == EFFECT_WHITE: + self.pool.execute(self.controller_id, "white", self.group) else: self.pool.execute(self.controller_id, "set_color", self._xy_to_led_color(self._xy_color), From 4d958c6d18e6016979b44cfe3ef72a6996c60100 Mon Sep 17 00:00:00 2001 From: happyleaves Date: Mon, 2 Nov 2015 17:51:17 -0500 Subject: [PATCH 209/744] style fix --- homeassistant/components/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index a7865e1f514..ab39474c093 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -157,7 +157,7 @@ def turn_off(hass, entity_id=None, transition=None): hass.services.call(DOMAIN, SERVICE_TURN_OFF, data) -# pylint: disable=too-many-branches, too-many-locals +# pylint: disable=too-many-branches, too-many-locals, too-many-statements def setup(hass, config): """ Exposes light control via statemachine and services. """ From 7b968f6a6b6b289a2d7f8b3823565d74341f7d09 Mon Sep 17 00:00:00 2001 From: happyleaves Date: Mon, 2 Nov 2015 18:11:58 -0500 Subject: [PATCH 210/744] re-fix conditionals --- homeassistant/components/light/limitlessled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index fef6cd0b8b1..0e2ce2230ac 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -164,7 +164,7 @@ class RGBWLimitlessLED(LimitlessLED): if effect == EFFECT_COLORLOOP: self.pool.execute(self.controller_id, "disco", self.group) - if effect == EFFECT_WHITE: + elif effect == EFFECT_WHITE: self.pool.execute(self.controller_id, "white", self.group) else: self.pool.execute(self.controller_id, "set_color", From 5fce381b893754e7a99c84dfe6647fbc8a52e419 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 3 Nov 2015 08:50:27 +0100 Subject: [PATCH 211/744] Remove empty point --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fed012402c..e1528e393bd 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat Examples of devices it can interface it: * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT - * - * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors + * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv)) * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) * Interaction with [IFTTT](https://ifttt.com/) From 72b4212b19b32b3afa399ba087497ca9096b7061 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:19:28 -0800 Subject: [PATCH 212/744] Demo uses device tracker demo platform --- homeassistant/components/demo.py | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 388a869ae0c..7c873e834bd 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -10,14 +10,15 @@ import homeassistant.core as ha import homeassistant.bootstrap as bootstrap import homeassistant.loader as loader from homeassistant.const import ( - CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME) + CONF_PLATFORM, ATTR_ENTITY_ID) DOMAIN = "demo" -DEPENDENCIES = ['introduction', 'conversation'] +DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ - 'switch', 'light', 'sensor', 'thermostat', 'media_player', 'notify'] + 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', + 'thermostat'] def setup(hass, config): @@ -110,25 +111,6 @@ def setup(hass, config): }}, ]}) - # Setup fake device tracker - hass.states.set("device_tracker.paulus", "home", - {ATTR_ENTITY_PICTURE: - "http://graph.facebook.com/297400035/picture", - ATTR_FRIENDLY_NAME: 'Paulus'}) - hass.states.set("device_tracker.anne_therese", "not_home", - {ATTR_FRIENDLY_NAME: 'Anne Therese', - 'latitude': hass.config.latitude + 0.002, - 'longitude': hass.config.longitude + 0.002}) - - hass.states.set("group.all_devices", "home", - { - "auto": True, - ATTR_ENTITY_ID: [ - "device_tracker.paulus", - "device_tracker.anne_therese" - ] - }) - # Setup configurator configurator_ids = [] From 77f4fc8c2228e5acdb579161307dee181eacbe2f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:20:20 -0800 Subject: [PATCH 213/744] Frontend: Add materialdesignicons --- homeassistant/components/frontend/__init__.py | 3 +- .../components/frontend/index.html.template | 2 +- .../components/frontend/mdi_version.py | 2 + .../components/frontend/www_static/mdi.html | 1 + script/update_mdi.py | 92 +++++++++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/frontend/mdi_version.py create mode 100644 homeassistant/components/frontend/www_static/mdi.html create mode 100755 script/update_mdi.py diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index a15244ac52f..d51a7623767 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -8,7 +8,7 @@ import re import os import logging -from . import version +from . import version, mdi_version import homeassistant.util as util from homeassistant.const import URL_ROOT, HTTP_OK from homeassistant.config import get_default_config_dir @@ -74,6 +74,7 @@ def _handle_get_root(handler, path_match, data): template_html = template_html.replace('{{ app_url }}', app_url) template_html = template_html.replace('{{ auth }}', auth) + template_html = template_html.replace('{{ icons }}', mdi_version.VERSION) handler.wfile.write(template_html.encode("UTF-8")) diff --git a/homeassistant/components/frontend/index.html.template b/homeassistant/components/frontend/index.html.template index 8906e8902a0..409ea6752db 100644 --- a/homeassistant/components/frontend/index.html.template +++ b/homeassistant/components/frontend/index.html.template @@ -46,6 +46,6 @@ - + diff --git a/homeassistant/components/frontend/mdi_version.py b/homeassistant/components/frontend/mdi_version.py new file mode 100644 index 00000000000..c9d06a4b300 --- /dev/null +++ b/homeassistant/components/frontend/mdi_version.py @@ -0,0 +1,2 @@ +""" DO NOT MODIFY. Auto-generated by update_mdi script """ +VERSION = "38EF63D0474411E4B3CF842B2B6CFE1B" diff --git a/homeassistant/components/frontend/www_static/mdi.html b/homeassistant/components/frontend/www_static/mdi.html new file mode 100644 index 00000000000..42212a3a301 --- /dev/null +++ b/homeassistant/components/frontend/www_static/mdi.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/script/update_mdi.py b/script/update_mdi.py new file mode 100755 index 00000000000..f7899be3964 --- /dev/null +++ b/script/update_mdi.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Downloads the latest Polymer v1 iconset version for materialdesignicons.com +""" + +import os +import re +import requests +import sys + +GETTING_STARTED_URL = ('https://raw.githubusercontent.com/Templarian/' + 'MaterialDesign/master/site/getting-started.savvy') +DOWNLOAD_LINK = re.compile(r'(/api/download/polymer/v1/([A-Z0-9-]{36}))') +START_ICONSET = ' Date: Tue, 3 Nov 2015 00:20:48 -0800 Subject: [PATCH 214/744] Add icon support to entity --- homeassistant/components/switch/demo.py | 12 +++++++++--- homeassistant/components/zone.py | 16 ++++++++-------- homeassistant/const.py | 3 +++ homeassistant/helpers/entity.py | 10 +++++++++- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/switch/demo.py b/homeassistant/components/switch/demo.py index 4ee1dc82413..16676cb239e 100644 --- a/homeassistant/components/switch/demo.py +++ b/homeassistant/components/switch/demo.py @@ -12,16 +12,17 @@ from homeassistant.const import DEVICE_DEFAULT_NAME def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return demo switches. """ add_devices_callback([ - DemoSwitch('Decorative Lights', True), - DemoSwitch('AC', False) + DemoSwitch('Decorative Lights', True, None), + DemoSwitch('AC', False, 'mdi:air-conditioner') ]) class DemoSwitch(SwitchDevice): """ Provides a demo switch. """ - def __init__(self, name, state): + def __init__(self, name, state, icon): self._name = name or DEVICE_DEFAULT_NAME self._state = state + self._icon = icon @property def should_poll(self): @@ -33,6 +34,11 @@ class DemoSwitch(SwitchDevice): """ Returns the name of the device if any. """ return self._name + @property + def icon(self): + """ Returns the icon to use for device if any. """ + return self._icon + @property def current_power_mwh(self): """ Current power usage in mwh. """ diff --git a/homeassistant/components/zone.py b/homeassistant/components/zone.py index 9ec7829316d..e978d963c5a 100644 --- a/homeassistant/components/zone.py +++ b/homeassistant/components/zone.py @@ -9,7 +9,7 @@ https://home-assistant.io/components/zone.html import logging from homeassistant.const import ( - ATTR_HIDDEN, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_NAME) + ATTR_HIDDEN, ATTR_ICON, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_NAME) from homeassistant.helpers import extract_domain_configs, generate_entity_id from homeassistant.helpers.entity import Entity from homeassistant.util.location import distance @@ -25,8 +25,7 @@ DEFAULT_NAME = 'Unnamed zone' ATTR_RADIUS = 'radius' DEFAULT_RADIUS = 100 -ATTR_ICON = 'icon' -ICON_HOME = 'home' +ICON_HOME = 'mdi:home' def active_zone(hass, latitude, longitude, radius=0): @@ -110,7 +109,7 @@ class Zone(Entity): self.latitude = latitude self.longitude = longitude self.radius = radius - self.icon = icon + self._icon = icon def should_poll(self): return False @@ -124,14 +123,15 @@ class Zone(Entity): """ The state property really does nothing for a zone. """ return STATE + @property + def icon(self): + return self._icon + @property def state_attributes(self): - attr = { + return { ATTR_HIDDEN: True, ATTR_LATITUDE: self.latitude, ATTR_LONGITUDE: self.longitude, ATTR_RADIUS: self.radius, } - if self.icon: - attr[ATTR_ICON] = self.icon - return attr diff --git a/homeassistant/const.py b/homeassistant/const.py index 49825cee094..7762f4acc6a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -74,6 +74,9 @@ ATTR_FRIENDLY_NAME = "friendly_name" # A picture to represent entity ATTR_ENTITY_PICTURE = "entity_picture" +# Icon to use in the frontend +ATTR_ICON = "icon" + # The unit of measurement if applicable ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement" diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 82dafac5576..fd2611889c9 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -10,7 +10,7 @@ from collections import defaultdict from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.const import ( - ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_UNIT_OF_MEASUREMENT, + ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_UNIT_OF_MEASUREMENT, ATTR_ICON, DEVICE_DEFAULT_NAME, STATE_ON, STATE_OFF, STATE_UNKNOWN, TEMP_CELCIUS, TEMP_FAHRENHEIT) @@ -61,6 +61,11 @@ class Entity(object): """ Unit of measurement of this entity, if any. """ return None + @property + def icon(self): + """ Icon to use in the frontend, if any. """ + return None + @property def hidden(self): """ Suggestion if the entity should be hidden from UIs. """ @@ -102,6 +107,9 @@ class Entity(object): if ATTR_UNIT_OF_MEASUREMENT not in attr and self.unit_of_measurement: attr[ATTR_UNIT_OF_MEASUREMENT] = self.unit_of_measurement + if ATTR_ICON not in attr and self.icon: + attr[ATTR_ICON] = self.icon + if self.hidden: attr[ATTR_HIDDEN] = self.hidden From be6dd2023617603495f654299820c9d2cfd7af81 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:20:59 -0800 Subject: [PATCH 215/744] Update frontend with new icons --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 1546 +++++++++-------- .../www_static/home-assistant-polymer | 2 +- 3 files changed, 784 insertions(+), 766 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 1c753d1638e..1012508d53f 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "beb922c55bb26ea576581b453f6d7c04" +VERSION = "5e61b80689feebb3a7043de07fc01971" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 7343bd3afd0..5d68c878b66 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,6 @@ -