From 377d2c6e5a3e8e6a153ea91f27fb88f450afe3d1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Nov 2015 00:18:42 -0800 Subject: [PATCH 001/125] Allow generating requirements_all.txt --- MANIFEST.in | 2 +- .../components/light/blinksticklight.py | 6 +- homeassistant/components/light/rfxtrx.py | 3 +- homeassistant/components/light/tellstick.py | 28 +- homeassistant/components/light/zwave.py | 6 +- homeassistant/components/sensor/rfxtrx.py | 2 +- homeassistant/components/sensor/tellstick.py | 6 +- .../components/sensor/transmission.py | 9 +- homeassistant/components/sensor/zwave.py | 5 +- .../components/switch/hikvisioncam.py | 15 +- homeassistant/components/switch/orvibo.py | 13 +- homeassistant/components/switch/rfxtrx.py | 2 +- homeassistant/components/switch/tellstick.py | 19 +- .../components/switch/transmission.py | 6 +- homeassistant/components/switch/zwave.py | 6 +- requirements_all.txt | 285 +++++++++--------- script/gen_requirements_all.py | 76 +++++ 17 files changed, 283 insertions(+), 206 deletions(-) create mode 100755 script/gen_requirements_all.py diff --git a/MANIFEST.in b/MANIFEST.in index 8233015e646..d04d86bae58 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include README.md +include README.rst include LICENSE graft homeassistant prune homeassistant/components/frontend/www_static/home-assistant-polymer diff --git a/homeassistant/components/light/blinksticklight.py b/homeassistant/components/light/blinksticklight.py index 086a004eba2..5cc14a9034b 100644 --- a/homeassistant/components/light/blinksticklight.py +++ b/homeassistant/components/light/blinksticklight.py @@ -8,9 +8,7 @@ https://home-assistant.io/components/light.blinksticklight/ """ import logging -from blinkstick import blinkstick - -from homeassistant.components.light import (Light, ATTR_RGB_COLOR) +from homeassistant.components.light import Light, ATTR_RGB_COLOR _LOGGER = logging.getLogger(__name__) @@ -22,6 +20,8 @@ DEPENDENCIES = [] # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Add device specified by serial number. """ + from blinkstick import blinkstick + stick = blinkstick.find_by_serial(config['serial']) add_devices_callback([BlinkStickLight(stick, config['name'])]) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index ff52001353b..8d6a2b86217 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -8,7 +8,6 @@ https://home-assistant.io/components/light.rfxtrx/ """ import logging import homeassistant.components.rfxtrx as rfxtrx -import RFXtrx as rfxtrxmod from homeassistant.components.light import Light from homeassistant.util import slugify @@ -20,6 +19,8 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + import RFXtrx as rfxtrxmod + lights = [] devices = config.get('devices', None) if devices: diff --git a/homeassistant/components/light/tellstick.py b/homeassistant/components/light/tellstick.py index 9a22a4dcdc0..24d86c47c51 100644 --- a/homeassistant/components/light/tellstick.py +++ b/homeassistant/components/light/tellstick.py @@ -6,13 +6,9 @@ Support for Tellstick lights. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.tellstick/ """ -import logging -# pylint: disable=no-name-in-module, import-error from homeassistant.components.light import Light, ATTR_BRIGHTNESS from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, ATTR_FRIENDLY_NAME) -import tellcore.constants as tellcore_constants -from tellcore.library import DirectCallbackDispatcher REQUIREMENTS = ['tellcore-py==1.1.2'] @@ -20,12 +16,9 @@ REQUIREMENTS = ['tellcore-py==1.1.2'] def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return Tellstick lights. """ - try: - import tellcore.telldus as telldus - except ImportError: - logging.getLogger(__name__).exception( - "Failed to import tellcore") - return [] + import tellcore.telldus as telldus + from tellcore.library import DirectCallbackDispatcher + import tellcore.constants as tellcore_constants core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher()) @@ -58,17 +51,20 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class TellstickLight(Light): """ Represents a Tellstick light. """ - last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | - tellcore_constants.TELLSTICK_TURNOFF | - tellcore_constants.TELLSTICK_DIM | - tellcore_constants.TELLSTICK_UP | - tellcore_constants.TELLSTICK_DOWN) def __init__(self, tellstick_device): + import tellcore.constants as tellcore_constants + self.tellstick_device = tellstick_device self.state_attr = {ATTR_FRIENDLY_NAME: tellstick_device.name} self._brightness = 0 + self.last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | + tellcore_constants.TELLSTICK_TURNOFF | + tellcore_constants.TELLSTICK_DIM | + tellcore_constants.TELLSTICK_UP | + tellcore_constants.TELLSTICK_DOWN) + @property def name(self): """ Returns the name of the switch if any. """ @@ -104,6 +100,8 @@ class TellstickLight(Light): def update(self): """ Update state of the light. """ + import tellcore.constants as tellcore_constants + last_command = self.tellstick_device.last_sent_command( self.last_sent_command_mask) diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index f1cd6f57fc0..31cd64d2530 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -7,9 +7,6 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.zwave/ """ # pylint: disable=import-error -from openzwave.network import ZWaveNetwork -from pydispatch import dispatcher - import homeassistant.components.zwave as zwave from homeassistant.const import STATE_ON, STATE_OFF @@ -51,6 +48,9 @@ class ZwaveDimmer(Light): """ Provides a Z-Wave dimmer. """ # pylint: disable=too-many-arguments def __init__(self, value): + from openzwave.network import ZWaveNetwork + from pydispatch import dispatcher + self._value = value self._node = value.node diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 0118c30ceb6..c67810c86eb 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -11,7 +11,6 @@ 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 @@ -28,6 +27,7 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + from RFXtrx import SensorEvent def sensor_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ diff --git a/homeassistant/components/sensor/tellstick.py b/homeassistant/components/sensor/tellstick.py index b6eb42f6dbb..c6993de462d 100644 --- a/homeassistant/components/sensor/tellstick.py +++ b/homeassistant/components/sensor/tellstick.py @@ -9,9 +9,6 @@ https://home-assistant.io/components/sensor.tellstick/ import logging from collections import namedtuple -import tellcore.telldus as telldus -import tellcore.constants as tellcore_constants - from homeassistant.const import TEMP_CELCIUS from homeassistant.helpers.entity import Entity import homeassistant.util as util @@ -24,6 +21,9 @@ REQUIREMENTS = ['tellcore-py==1.1.2'] # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Tellstick sensors. """ + import tellcore.telldus as telldus + import tellcore.constants as tellcore_constants + sensor_value_descriptions = { tellcore_constants.TELLSTICK_TEMPERATURE: DatatypeDescription( diff --git a/homeassistant/components/sensor/transmission.py b/homeassistant/components/sensor/transmission.py index c4a40e64470..484b045f295 100644 --- a/homeassistant/components/sensor/transmission.py +++ b/homeassistant/components/sensor/transmission.py @@ -11,10 +11,6 @@ from datetime import timedelta from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers.entity import Entity -# pylint: disable=no-name-in-module, import-error -import transmissionrpc - -from transmissionrpc.error import TransmissionError import logging @@ -33,6 +29,9 @@ _THROTTLED_REFRESH = None # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Transmission sensors. """ + import transmissionrpc + from transmissionrpc.error import TransmissionError + host = config.get(CONF_HOST) username = config.get(CONF_USERNAME, None) password = config.get(CONF_PASSWORD, None) @@ -97,6 +96,8 @@ class TransmissionSensor(Entity): def refresh_transmission_data(self): """ Calls the throttled Transmission refresh method. """ + from transmissionrpc.error import TransmissionError + if _THROTTLED_REFRESH is not None: try: _THROTTLED_REFRESH() diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 0a9b3e8290f..23d2f8948f8 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -8,8 +8,6 @@ at https://home-assistant.io/components/zwave/ """ # 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 @@ -79,6 +77,9 @@ class ZWaveSensor(Entity): """ Represents a Z-Wave sensor. """ def __init__(self, sensor_value): + from openzwave.network import ZWaveNetwork + from pydispatch import dispatcher + self._value = sensor_value self._node = sensor_value.node diff --git a/homeassistant/components/switch/hikvisioncam.py b/homeassistant/components/switch/hikvisioncam.py index ec74a83dbc2..2d91acdf361 100644 --- a/homeassistant/components/switch/hikvisioncam.py +++ b/homeassistant/components/switch/hikvisioncam.py @@ -11,12 +11,6 @@ from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD import logging -try: - import hikvision.api - from hikvision.error import HikvisionError, MissingParamError -except ImportError: - hikvision.api = None - _LOGGING = logging.getLogger(__name__) REQUIREMENTS = ['hikvision==0.4'] # pylint: disable=too-many-arguments @@ -25,6 +19,8 @@ REQUIREMENTS = ['hikvision==0.4'] def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup Hikvision camera. """ + import hikvision.api + from hikvision.error import HikvisionError, MissingParamError host = config.get(CONF_HOST, None) port = config.get('port', "80") @@ -32,13 +28,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): username = config.get(CONF_USERNAME, "admin") password = config.get(CONF_PASSWORD, "12345") - if hikvision.api is None: - _LOGGING.error(( - "Failed to import hikvision. Did you maybe not install the " - "'hikvision' dependency?")) - - return False - try: hikvision_cam = hikvision.api.CreateDevice( host, port=port, username=username, diff --git a/homeassistant/components/switch/orvibo.py b/homeassistant/components/switch/orvibo.py index 04864e13fdd..b9469d15df0 100644 --- a/homeassistant/components/switch/orvibo.py +++ b/homeassistant/components/switch/orvibo.py @@ -10,8 +10,6 @@ import logging from homeassistant.components.switch import SwitchDevice -from orvibo.s20 import S20, S20Exception - DEFAULT_NAME = "Orvibo S20 Switch" REQUIREMENTS = ['orvibo==1.0.0'] _LOGGER = logging.getLogger(__name__) @@ -20,6 +18,8 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return S20 switches. """ + from orvibo.s20 import S20, S20Exception + if config.get('host') is None: _LOGGER.error("Missing required variable: host") return @@ -34,9 +34,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class S20Switch(SwitchDevice): """ Represents an S20 switch. """ def __init__(self, name, s20): + from orvibo.s20 import S20Exception + self._name = name self._s20 = s20 self._state = False + self._exc = S20Exception @property def should_poll(self): @@ -57,19 +60,19 @@ class S20Switch(SwitchDevice): """ Update device state. """ try: self._state = self._s20.on - except S20Exception: + except self._exc: _LOGGER.exception("Error while fetching S20 state") def turn_on(self, **kwargs): """ Turn the device on. """ try: self._s20.on = True - except S20Exception: + except self._exc: _LOGGER.exception("Error while turning on S20") def turn_off(self, **kwargs): """ Turn the device off. """ try: self._s20.on = False - except S20Exception: + except self._exc: _LOGGER.exception("Error while turning off S20") diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 83100598245..86bcf580f41 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -8,7 +8,6 @@ https://home-assistant.io/components/switch.rfxtrx/ """ import logging import homeassistant.components.rfxtrx as rfxtrx -from RFXtrx import LightingDevice from homeassistant.components.switch import SwitchDevice from homeassistant.util import slugify @@ -20,6 +19,7 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + from RFXtrx import LightingDevice # Add switch from config file switchs = [] diff --git a/homeassistant/components/switch/tellstick.py b/homeassistant/components/switch/tellstick.py index 2966673520f..61edbed0af4 100644 --- a/homeassistant/components/switch/tellstick.py +++ b/homeassistant/components/switch/tellstick.py @@ -11,8 +11,6 @@ import logging from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, ATTR_FRIENDLY_NAME) from homeassistant.helpers.entity import ToggleEntity -import tellcore.constants as tellcore_constants -from tellcore.library import DirectCallbackDispatcher SIGNAL_REPETITIONS = 1 REQUIREMENTS = ['tellcore-py==1.1.2'] @@ -22,11 +20,9 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return Tellstick switches. """ - try: - import tellcore.telldus as telldus - except ImportError: - _LOGGER.exception("Failed to import tellcore") - return + import tellcore.telldus as telldus + import tellcore.constants as tellcore_constants + from tellcore.library import DirectCallbackDispatcher core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher()) @@ -62,14 +58,17 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class TellstickSwitchDevice(ToggleEntity): """ Represents a Tellstick switch. """ - last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | - tellcore_constants.TELLSTICK_TURNOFF) def __init__(self, tellstick_device, signal_repetitions): + import tellcore.constants as tellcore_constants + self.tellstick_device = tellstick_device self.state_attr = {ATTR_FRIENDLY_NAME: tellstick_device.name} self.signal_repetitions = signal_repetitions + self.last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | + tellcore_constants.TELLSTICK_TURNOFF) + @property def should_poll(self): """ Tells Home Assistant not to poll this entity. """ @@ -88,6 +87,8 @@ class TellstickSwitchDevice(ToggleEntity): @property def is_on(self): """ True if switch is on. """ + import tellcore.constants as tellcore_constants + last_command = self.tellstick_device.last_sent_command( self.last_sent_command_mask) diff --git a/homeassistant/components/switch/transmission.py b/homeassistant/components/switch/transmission.py index bb4f6616975..f3f6a9a8765 100644 --- a/homeassistant/components/switch/transmission.py +++ b/homeassistant/components/switch/transmission.py @@ -10,9 +10,6 @@ from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.helpers.entity import ToggleEntity -# pylint: disable=no-name-in-module, import-error -import transmissionrpc -from transmissionrpc.error import TransmissionError import logging _LOGGING = logging.getLogger(__name__) @@ -22,6 +19,9 @@ REQUIREMENTS = ['transmissionrpc==0.11'] # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Sets up the transmission sensor. """ + import transmissionrpc + from transmissionrpc.error import TransmissionError + host = config.get(CONF_HOST) username = config.get(CONF_USERNAME, None) password = config.get(CONF_PASSWORD, None) diff --git a/homeassistant/components/switch/zwave.py b/homeassistant/components/switch/zwave.py index 7d86605c646..493e2234bcf 100644 --- a/homeassistant/components/switch/zwave.py +++ b/homeassistant/components/switch/zwave.py @@ -5,8 +5,6 @@ homeassistant.components.switch.zwave Zwave platform that handles simple binary switches. """ # pylint: disable=import-error -from openzwave.network import ZWaveNetwork -from pydispatch import dispatcher import homeassistant.components.zwave as zwave @@ -36,11 +34,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ZwaveSwitch(SwitchDevice): """ Provides a zwave switch. """ def __init__(self, value): + from openzwave.network import ZWaveNetwork + from pydispatch import dispatcher + self._value = value self._node = value.node self._state = value.data - dispatcher.connect( self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) diff --git a/requirements_all.txt b/requirements_all.txt index ce6cbfabc96..081c8235b12 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1,161 +1,168 @@ -# Required for Home Assistant core +# Home Assistant core requests>=2,<3 pyyaml>=3.11,<4 pytz>=2015.4 pip>=7.0.0 vincenty==0.1.3 -# Optional, needed for specific components - -# Sun (sun) -astral==0.8.1 - -# Philips Hue (lights.hue) -phue==0.8 - -# Limitlessled/Easybulb/Milight (lights.limitlessled) -ledcontroller==1.1.0 - -# Chromecast (media_player.cast) -pychromecast==0.6.12 - -# Keyboard (keyboard) -pyuserinput==0.1.9 - -# Tellstick (*.tellstick) -tellcore-py==1.1.2 - -# Nmap (device_tracker.nmap) -python-nmap==0.4.3 - -# PushBullet (notify.pushbullet) -pushbullet.py==0.9.0 - -# Nest Thermostat (thermostat.nest) -python-nest==2.6.0 - -# Z-Wave (*.zwave) -pydispatcher==2.0.5 - -# ISY994 (isy994) -PyISY==1.0.5 - -# PSutil (sensor.systemmonitor) -psutil==3.2.2 - -# Pushover (notify.pushover) -python-pushover==0.2 - -# Transmission Torrent Client (*.transmission) -transmissionrpc==0.11 - -# OpenWeatherMap (sensor.openweathermap) -pyowm==2.2.1 - -# XMPP (notify.xmpp) -sleekxmpp==1.3.1 -dnspython3==1.12.0 - -# Blockchain (sensor.bitcoin) -blockchain==1.1.2 - -# Music Player Daemon (media_player.mpd) -python-mpd2==0.5.4 - -# Hikvision (switch.hikvisioncam) -hikvision==0.4 - -# Console log coloring -colorlog==2.6.0 - -# JSON-RPC interface (media_player.kodi) -jsonrpc-requests==0.1 - -# Forecast.io (sensor.forecast) -python-forecastio==1.3.3 - -# Firmata (*.arduino) +# homeassistant.components.arduino PyMata==2.07a -# Rfxtrx (rfxtrx) -https://github.com/Danielhiversen/pyRFXtrx/archive/ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15 - -# Mysensors (sensor.mysensors) -https://github.com/theolind/pymysensors/archive/d4b809c2167650691058d1e29bfd2c4b1792b4b0.zip#pymysensors==0.3 - -# Netgear (device_tracker.netgear) +# homeassistant.components.device_tracker.netgear pynetgear==0.3 -# Netdisco (discovery) -netdisco==0.5.1 +# homeassistant.components.device_tracker.nmap_tracker +python-nmap==0.4.3 -# Wemo (switch.wemo) -pywemo==0.3.2 - -# Wink (*.wink) -https://github.com/balloob/python-wink/archive/9eb39eaba0717922815e673ad1114c685839d890.zip#python-wink==0.1.1 - -# Slack notifier (notify.slack) -slacker==0.6.8 - -# Temper sensors (sensor.temper) -https://github.com/rkabadi/temper-python/archive/3dbdaf2d87b8db9a3cd6e5585fc704537dd2d09b.zip#temperusb==1.2.3 - -# PyEdimax (switch.edimax) -https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f3700.zip#pyedimax==0.1 - -# RPI-GPIO platform (*.rpi_gpio) -# Uncomment for Raspberry Pi -# RPi.GPIO==0.5.11 - -# Adafruit temperature/humidity sensor (sensor.dht) -# Uncomment on a Raspberry Pi / Beaglebone -# http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0 - -# PAHO MQTT (mqtt) -paho-mqtt==1.1 - -# PyModbus (modbus) -https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0 - -# Verisure (verisure) -https://github.com/persandstrom/python-verisure/archive/9873c4527f01b1ba1f72ae60f7f35854390d59be.zip#python-verisure==0.2.6 - -# IFTTT Maker Channel (ifttt) -pyfttt==0.3 - -# SABnzbd (sensor.sabnzbd) -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/efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip#python-vera==0.1.1 - -# Sonos (media_player.sonos) -SoCo==0.11.1 - -# PlexAPI (media_player.plex) -plexapi==1.1.0 - -# SNMP (device_tracker.snmp) +# homeassistant.components.device_tracker.snmp pysnmp==4.2.5 -# Blinkstick (light.blinksticklight) +# homeassistant.components.discovery +netdisco==0.5.1 + +# homeassistant.components.ifttt +pyfttt==0.3 + +# homeassistant.components.isy994 +PyISY==1.0.5 + +# homeassistant.components.keyboard +pyuserinput==0.1.9 + +# homeassistant.components.light.blinksticklight blinkstick==1.1.7 -# Telegram (notify.telegram) -python-telegram-bot==2.8.7 +# homeassistant.components.light.hue +phue==0.8 -# CPUinfo (sensor.cpuinfo) -py-cpuinfo==0.1.6 +# homeassistant.components.light.limitlessled +ledcontroller==1.1.0 -# Radio Thermostat (thermostat.radiotherm) -radiotherm==1.2 +# homeassistant.components.light.tellstick +# homeassistant.components.sensor.tellstick +# homeassistant.components.switch.tellstick +tellcore-py==1.1.2 -# Honeywell Evo Home Client (thermostat.honeywell) -evohomeclient==0.2.3 +# homeassistant.components.light.vera +# homeassistant.components.switch.vera +https://github.com/pavoni/home-assistant-vera-api/archive/efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip#python-vera==0.1.1 -# Pushetta (notify.pushetta) +# homeassistant.components.wink +# homeassistant.components.light.wink +# homeassistant.components.sensor.wink +# homeassistant.components.switch.wink +https://github.com/balloob/python-wink/archive/9eb39eaba0717922815e673ad1114c685839d890.zip#python-wink==0.1.1 + +# homeassistant.components.media_player.cast +pychromecast==0.6.12 + +# homeassistant.components.media_player.kodi +jsonrpc-requests==0.1 + +# homeassistant.components.media_player.mpd +python-mpd2==0.5.4 + +# homeassistant.components.media_player.plex +plexapi==1.1.0 + +# homeassistant.components.media_player.sonos +SoCo==0.11.1 + +# homeassistant.components.modbus +https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0 + +# homeassistant.components.mqtt +paho-mqtt==1.1 + +# homeassistant.components.notify.pushbullet +pushbullet.py==0.9.0 + +# homeassistant.components.notify.pushetta pushetta==1.0.15 -# Orvibo S10 +# homeassistant.components.notify.pushover +python-pushover==0.2 + +# homeassistant.components.notify.slack +slacker==0.6.8 + +# homeassistant.components.notify.telegram +python-telegram-bot==2.8.7 + +# homeassistant.components.notify.xmpp +sleekxmpp==1.3.1 + +# homeassistant.components.notify.xmpp +dnspython3==1.12.0 + +# homeassistant.components.rfxtrx +https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip#RFXtrx==0.2 + +# homeassistant.components.sensor.bitcoin +blockchain==1.1.2 + +# homeassistant.components.sensor.cpuspeed +py-cpuinfo==0.1.6 + +# homeassistant.components.sensor.dht +http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0 + +# homeassistant.components.sensor.forecast +python-forecastio==1.3.3 + +# homeassistant.components.sensor.mysensors +https://github.com/theolind/pymysensors/archive/d4b809c2167650691058d1e29bfd2c4b1792b4b0.zip#pymysensors==0.3 + +# homeassistant.components.sensor.openweathermap +pyowm==2.2.1 + +# homeassistant.components.sensor.rpi_gpio +# homeassistant.components.switch.rpi_gpio +RPi.GPIO==0.5.11 + +# homeassistant.components.sensor.sabnzbd +https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 + +# homeassistant.components.sensor.systemmonitor +psutil==3.2.2 + +# homeassistant.components.sensor.temper +https://github.com/rkabadi/temper-python/archive/3dbdaf2d87b8db9a3cd6e5585fc704537dd2d09b.zip#temperusb==1.2.3 + +# homeassistant.components.sensor.transmission +# homeassistant.components.switch.transmission +transmissionrpc==0.11 + +# homeassistant.components.sensor.vera +https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip#python-vera==0.1 + +# homeassistant.components.sun +astral==0.8.1 + +# homeassistant.components.switch.edimax +https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f3700.zip#pyedimax==0.1 + +# homeassistant.components.switch.hikvisioncam +hikvision==0.4 + +# homeassistant.components.switch.orvibo orvibo==1.0.0 + +# homeassistant.components.switch.wemo +pywemo==0.3.2 + +# homeassistant.components.thermostat.honeywell +evohomeclient==0.2.3 + +# homeassistant.components.thermostat.nest +python-nest==2.6.0 + +# homeassistant.components.thermostat.radiotherm +radiotherm==1.2 + +# homeassistant.components.verisure +https://github.com/persandstrom/python-verisure/archive/9873c4527f01b1ba1f72ae60f7f35854390d59be.zip#python-verisure==0.2.6 + +# homeassistant.components.zwave +pydispatcher==2.0.5 + diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py new file mode 100755 index 00000000000..15b11063efa --- /dev/null +++ b/script/gen_requirements_all.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +""" +Generate an updated requirements_all.txt +""" + +from collections import OrderedDict +import importlib +import os +import pkgutil +import re + + +def explore_module(package, explore_children): + module = importlib.import_module(package) + + found = [] + + if not hasattr(module, '__path__'): + return found + + for _, name, ispkg in pkgutil.iter_modules(module.__path__, package + '.'): + found.append(name) + + if explore_children: + found.extend(explore_module(name, False)) + + return found + + +def core_requirements(): + with open('setup.py') as inp: + reqs_raw = re.search( + r'REQUIRES = \[(.*?)\]', inp.read(), re.S).group(1) + + return re.findall(r"'(.*?)'", reqs_raw) + + +def main(): + if not os.path.isfile('requirements_all.txt'): + print('Run this from HA root dir') + return + + reqs = OrderedDict() + + errors = [] + for package in sorted(explore_module('homeassistant.components', True)): + try: + module = importlib.import_module(package) + except ImportError: + errors.append(package) + continue + + if not getattr(module, 'REQUIREMENTS', None): + continue + + for req in module.REQUIREMENTS: + reqs.setdefault(req, []).append(package) + + if errors: + print("Found errors") + print('\n'.join(errors)) + return + + print('# Home Assistant core') + print('\n'.join(core_requirements())) + print() + + for pkg, requirements in reqs.items(): + for req in sorted(requirements, + key=lambda name: (len(name.split('.')), name)): + print('#', req) + print(pkg) + print() + +if __name__ == '__main__': + main() From e92fe149febaee281e612dde0b34293ca8395baa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Nov 2015 00:28:22 -0800 Subject: [PATCH 002/125] Comment out requirements that break on certain platforms --- requirements_all.txt | 4 ++-- script/gen_requirements_all.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/requirements_all.txt b/requirements_all.txt index 081c8235b12..7df9584e7fa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -105,7 +105,7 @@ blockchain==1.1.2 py-cpuinfo==0.1.6 # homeassistant.components.sensor.dht -http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0 +# http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0 # homeassistant.components.sensor.forecast python-forecastio==1.3.3 @@ -118,7 +118,7 @@ pyowm==2.2.1 # homeassistant.components.sensor.rpi_gpio # homeassistant.components.switch.rpi_gpio -RPi.GPIO==0.5.11 +# RPi.GPIO==0.5.11 # homeassistant.components.sensor.sabnzbd https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 15b11063efa..b10c0f38ed8 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -9,6 +9,11 @@ import os import pkgutil import re +COMMENT_REQUIREMENTS = [ + 'RPi.GPIO', + 'Adafruit_Python_DHT' +] + def explore_module(package, explore_children): module = importlib.import_module(package) @@ -35,6 +40,11 @@ def core_requirements(): return re.findall(r"'(.*?)'", reqs_raw) +def comment_requirement(req): + """ Some requirements don't install on all systems. """ + return any(ign in req for ign in COMMENT_REQUIREMENTS) + + def main(): if not os.path.isfile('requirements_all.txt'): print('Run this from HA root dir') @@ -69,7 +79,11 @@ def main(): for req in sorted(requirements, key=lambda name: (len(name.split('.')), name)): print('#', req) - print(pkg) + + if comment_requirement(pkg): + print('#', pkg) + else: + print(pkg) print() if __name__ == '__main__': From bca65b620afeb6be9829af203ca90903d93901e1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Nov 2015 00:34:14 -0800 Subject: [PATCH 003/125] Update Vera sensor dependency --- homeassistant/components/sensor/vera.py | 6 +++--- requirements_all.txt | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/vera.py b/homeassistant/components/sensor/vera.py index 00ad9336705..1848c680517 100644 --- a/homeassistant/components/sensor/vera.py +++ b/homeassistant/components/sensor/vera.py @@ -15,9 +15,9 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME, TEMP_CELCIUS, TEMP_FAHRENHEIT) -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/' + 'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip' + '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 7df9584e7fa..c6b4ae81721 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -44,6 +44,7 @@ ledcontroller==1.1.0 tellcore-py==1.1.2 # homeassistant.components.light.vera +# homeassistant.components.sensor.vera # homeassistant.components.switch.vera https://github.com/pavoni/home-assistant-vera-api/archive/efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip#python-vera==0.1.1 @@ -133,9 +134,6 @@ https://github.com/rkabadi/temper-python/archive/3dbdaf2d87b8db9a3cd6e5585fc7045 # homeassistant.components.switch.transmission transmissionrpc==0.11 -# homeassistant.components.sensor.vera -https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip#python-vera==0.1 - # homeassistant.components.sun astral==0.8.1 From c78899c4f305101cce19c23058e35ccc181fb7c0 Mon Sep 17 00:00:00 2001 From: miniconfig Date: Tue, 17 Nov 2015 10:17:57 -0500 Subject: [PATCH 004/125] Added support for Locks, including those connected through a wink hub. --- homeassistant/components/lock/__init__.py | 124 ++++++++++++++++++++ homeassistant/components/lock/demo.py | 56 +++++++++ homeassistant/components/lock/services.yaml | 0 homeassistant/components/lock/wink.py | 73 ++++++++++++ homeassistant/components/wink.py | 4 +- homeassistant/const.py | 8 ++ 6 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/lock/__init__.py create mode 100644 homeassistant/components/lock/demo.py create mode 100644 homeassistant/components/lock/services.yaml create mode 100644 homeassistant/components/lock/wink.py diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py new file mode 100644 index 00000000000..88768208c1e --- /dev/null +++ b/homeassistant/components/lock/__init__.py @@ -0,0 +1,124 @@ +""" +homeassistant.components.lock +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Component to interface with various locks that can be controlled remotely. + +For more details about this component, please refer to the documentation +at https://home-assistant.io/components/lock/ +""" +from datetime import timedelta +import logging +import os + +from homeassistant.config import load_yaml_config_file +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import ToggleEntity + +from homeassistant.const import ( + STATE_LOCKED, SERVICE_LOCK, SERVICE_UNLOCK, ATTR_ENTITY_ID) +from homeassistant.components import ( + group, wink) + +DOMAIN = 'lock' +DEPENDENCIES = [] +SCAN_INTERVAL = 30 + +GROUP_NAME_ALL_LOCKS = 'all locks' +ENTITY_ID_ALL_LOCKS = group.ENTITY_ID_FORMAT.format('all_locks') + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + +ATTR_LOCKED = "locked" + +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) + +# Maps discovered services to their platforms +DISCOVERY_PLATFORMS = { + wink.DISCOVER_LOCKS: 'wink' +} + +PROP_TO_ATTR = { + 'locked': ATTR_LOCKED +} + +_LOGGER = logging.getLogger(__name__) + + +def is_locked(hass, entity_id=None): + """ Returns if the lock is locked based on the statemachine. """ + entity_id = entity_id or ENTITY_ID_ALL_LOCKS + return hass.states.is_state(entity_id, STATE_LOCKED) + + +def do_lock(hass, entity_id=None): + """ Locks all or specified locks. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_LOCK, data) + + +def do_unlock(hass, entity_id=None): + """ Unlocks all or specified locks. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_UNLOCK, data) + + +def setup(hass, config): + """ Track states and offer events for locks. """ + component = EntityComponent( + _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, + GROUP_NAME_ALL_LOCKS) + component.setup(config) + + def handle_lock_service(service): + """ Handles calls to the lock services. """ + target_locks = component.extract_from_service(service) + + for lock in target_locks: + if service.service == SERVICE_LOCK: + lock.do_lock() + else: + lock.do_unlock() + + if lock.should_poll: + lock.update_ha_state(True) + + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service, + descriptions.get(SERVICE_UNLOCK)) + hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service, + descriptions.get(SERVICE_LOCK)) + + return True + + +class LockDevice(ToggleEntity): + """ Represents a lock within Home Assistant. """ + # pylint: disable=no-self-use + + @property + def locked(self): + """ Is the lock locked or unlocked. """ + return None + + @property + def device_state_attributes(self): + """ Returns device specific state attributes. """ + return None + + @property + def state_attributes(self): + """ Returns optional state attributes. """ + data = {} + + for prop, attr in PROP_TO_ATTR.items(): + value = getattr(self, prop) + if value: + data[attr] = value + + device_attr = self.device_state_attributes + + if device_attr is not None: + data.update(device_attr) + + return data diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py new file mode 100644 index 00000000000..76eea0104fb --- /dev/null +++ b/homeassistant/components/lock/demo.py @@ -0,0 +1,56 @@ +""" +homeassistant.components.lock.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Demo platform that has two fake locks. +""" +from homeassistant.components.lock import LockDevice +from homeassistant.const import ( + DEVICE_DEFAULT_NAME, STATE_LOCKED, STATE_UNLOCKED) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Find and return demo locks. """ + add_devices_callback([ + DemoLock('Left Door', STATE_LOCKED, None), + DemoLock('Right Door', STATE_UNLOCKED, None) + ]) + + +class DemoLock(LockDevice): + """ Provides a demo lock. """ + def __init__(self, name, state, icon): + self._name = name or DEVICE_DEFAULT_NAME + self._state = state + self._icon = icon + + @property + def should_poll(self): + """ No polling needed for a demo lock. """ + return False + + @property + def name(self): + """ 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 is_locked(self): + """ True if device is locked. """ + return self._state + + def do_lock(self, **kwargs): + """ Lock the device. """ + self._state = STATE_LOCKED + self.update_ha_state() + + def do_unlock(self, **kwargs): + """ Unlock the device. """ + self._state = STATE_UNLOCKED + self.update_ha_state() diff --git a/homeassistant/components/lock/services.yaml b/homeassistant/components/lock/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py new file mode 100644 index 00000000000..059c3a56611 --- /dev/null +++ b/homeassistant/components/lock/wink.py @@ -0,0 +1,73 @@ +""" +homeassistant.components.lock.wink +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for Wink locks. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/lock.wink/ +""" +import logging + +from homeassistant.helpers.entity import Entity +from homeassistant.const import CONF_ACCESS_TOKEN, STATE_LOCKED, STATE_UNLOCKED + +REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' + '9eb39eaba0717922815e673ad1114c685839d890.zip' + '#python-wink==0.1.1'] + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Wink platform. """ + import pywink + + if discovery_info is None: + token = config.get(CONF_ACCESS_TOKEN) + + if token is None: + logging.getLogger(__name__).error( + "Missing wink access_token. " + "Get one at https://winkbearertoken.appspot.com/") + return + + pywink.set_bearer_token(token) + + add_devices(WinkLockDevice(lock) for lock in pywink.get_locks()) + + +class WinkLockDevice(Entity): + """ Represents a Wink lock. """ + + def __init__(self, wink): + self.wink = wink + + @property + def state(self): + """ Returns the state. """ + return STATE_LOCKED if self.is_locked else STATE_UNLOCKED + + @property + def unique_id(self): + """ Returns the id of this wink lock """ + return "{}.{}".format(self.__class__, self.wink.deviceId()) + + @property + def name(self): + """ Returns the name of the lock if any. """ + return self.wink.name() + + def update(self): + """ Update the state of the lock. """ + self.wink.updateState() + + @property + def is_locked(self): + """ True if device is locked. """ + return self.wink.state() + + def do_lock(self): + """ Lock the device. """ + self.wink.setState(True) + + def do_unlock(self): + """ Unlock the device. """ + self.wink.setState(False) diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index 03601f1d958..66ea29ff4dd 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -25,6 +25,7 @@ REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' DISCOVER_LIGHTS = "wink.lights" DISCOVER_SWITCHES = "wink.switches" DISCOVER_SENSORS = "wink.sensors" +DISCOVER_LOCKS = "wink.locks" def setup(hass, config): @@ -41,7 +42,8 @@ def setup(hass, config): for component_name, func_exists, discovery_type in ( ('light', pywink.get_bulbs, DISCOVER_LIGHTS), ('switch', pywink.get_switches, DISCOVER_SWITCHES), - ('sensor', pywink.get_sensors, DISCOVER_SENSORS)): + ('sensor', pywink.get_sensors, DISCOVER_SENSORS), + ('lock', pywink.get_locks, DISCOVER_LOCKS)): if func_exists(): component = get_component(component_name) diff --git a/homeassistant/const.py b/homeassistant/const.py index da1e424718f..5b0b5a5e214 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -53,6 +53,8 @@ STATE_ALARM_ARMED_HOME = 'armed_home' STATE_ALARM_ARMED_AWAY = 'armed_away' STATE_ALARM_PENDING = 'pending' STATE_ALARM_TRIGGERED = 'triggered' +STATE_LOCKED = 'locked' +STATE_UNLOCKED = 'unlocked' # #### STATE AND EVENT ATTRIBUTES #### # Contains current time for a TIME_CHANGED event @@ -96,6 +98,9 @@ ATTR_BATTERY_LEVEL = "battery_level" # For devices which support an armed state ATTR_ARMED = "device_armed" +# For devices which support a locked state +ATTR_LOCKED = "locked" + # For sensors that support 'tripping', eg. motion and door sensors ATTR_TRIPPED = "device_tripped" @@ -135,6 +140,9 @@ SERVICE_ALARM_ARM_HOME = "alarm_arm_home" SERVICE_ALARM_ARM_AWAY = "alarm_arm_away" SERVICE_ALARM_TRIGGER = "alarm_trigger" +SERVICE_LOCK = "lock" +SERVICE_UNLOCK = "unlock" + # #### API / REMOTE #### SERVER_PORT = 8123 From ab9e173179fc7a9e890f7df81088a095ffa3ec21 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 18 Nov 2015 08:42:49 +0100 Subject: [PATCH 005/125] Update docstrings --- homeassistant/components/camera/__init__.py | 15 ++++++++------- homeassistant/components/camera/generic.py | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index ff5198b7ab1..ae5fe28beac 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -58,7 +58,7 @@ MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n' # pylint: disable=too-many-branches def setup(hass, config): - """ Track states and offer events for sensors. """ + """ Track states and offer events for cameras. """ component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, @@ -101,7 +101,8 @@ def setup(hass, config): # pylint: disable=unused-argument def _proxy_camera_mjpeg_stream(handler, path_match, data): - """ Proxies the camera image as an mjpeg stream via the HA server. + """ + Proxies the camera image as an mjpeg stream via the HA server. This function takes still images from the IP camera and turns them into an MJPEG stream. This means that HA can return a live video stream even with only a still image URL available. @@ -163,7 +164,7 @@ def setup(hass, config): class Camera(Entity): - """ The base class for camera components """ + """ The base class for camera components. """ def __init__(self): self.is_streaming = False @@ -171,23 +172,23 @@ class Camera(Entity): @property # pylint: disable=no-self-use def is_recording(self): - """ Returns true if the device is recording """ + """ Returns true if the device is recording. """ return False @property # pylint: disable=no-self-use def brand(self): - """ Should return a string of the camera brand """ + """ Should return a string of the camera brand. """ return None @property # pylint: disable=no-self-use def model(self): - """ Returns string of camera model """ + """ Returns string of camera model. """ return None def camera_image(self): - """ Return bytes of camera image """ + """ Return bytes of camera image. """ raise NotImplementedError() @property diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index b8be51292bf..7e9542908d5 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -40,7 +40,7 @@ class GenericCamera(Camera): self._still_image_url = device_info['still_image_url'] def camera_image(self): - """ Return a still image reponse from the camera. """ + """ Return a still image response from the camera. """ if self._username and self._password: try: response = requests.get( From f1fed789927e416b1b82d955af80bd563701e1bd Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 18 Nov 2015 19:19:27 +0100 Subject: [PATCH 006/125] Fix issue with older glances releases #637 (thanks @jdotbdot) --- homeassistant/components/sensor/glances.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/glances.py b/homeassistant/components/sensor/glances.py index 176081336df..1c7bfa8dbd9 100644 --- a/homeassistant/components/sensor/glances.py +++ b/homeassistant/components/sensor/glances.py @@ -116,7 +116,11 @@ class GlancesSensor(Entity): elif self.type == 'disk_use': return round(value['fs'][0]['used'] / 1024**3, 1) elif self.type == 'disk_free': - return round(value['fs'][0]['free'] / 1024**3, 1) + try: + return round(value['fs'][0]['free'] / 1024**3, 1) + except KeyError: + return round((value['fs'][0]['size'] - + value['fs'][0]['used']) /1024**3, 1) elif self.type == 'memory_use_percent': return value['mem']['percent'] elif self.type == 'memory_use': From 4eacea32da47d605edf09dd1720a244a2ae41e88 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 19 Nov 2015 18:07:54 +0100 Subject: [PATCH 007/125] Fix pylint issue --- homeassistant/components/sensor/glances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/glances.py b/homeassistant/components/sensor/glances.py index 1c7bfa8dbd9..092e4c75733 100644 --- a/homeassistant/components/sensor/glances.py +++ b/homeassistant/components/sensor/glances.py @@ -120,7 +120,7 @@ class GlancesSensor(Entity): return round(value['fs'][0]['free'] / 1024**3, 1) except KeyError: return round((value['fs'][0]['size'] - - value['fs'][0]['used']) /1024**3, 1) + value['fs'][0]['used']) / 1024**3, 1) elif self.type == 'memory_use_percent': return value['mem']['percent'] elif self.type == 'memory_use': From 5f89db59577b78e0f3117cf10d5cdcf2480b1641 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 19 Nov 2015 19:00:22 +0100 Subject: [PATCH 008/125] Binary sensor component --- .../components/binary_sensor/__init__.py | 44 +++++++++++++++++ .../components/binary_sensor/demo.py | 49 +++++++++++++++++++ homeassistant/components/demo.py | 2 +- 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/binary_sensor/__init__.py create mode 100644 homeassistant/components/binary_sensor/demo.py diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..b02d38a9f81 --- /dev/null +++ b/homeassistant/components/binary_sensor/__init__.py @@ -0,0 +1,44 @@ +""" +homeassistant.components.binary_sensor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Component to interface with binary sensors (sensors which only know two states) +that can be monitored. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/binary_sensor/ +""" +import logging + +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import Entity + +DOMAIN = 'binary_sensor' +DEPENDENCIES = [] +SCAN_INTERVAL = 30 + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + + +def setup(hass, config): + """ Track states and offer events for binary sensors. """ + component = EntityComponent( + logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) + + component.setup(config) + + return True + + +# pylint: disable=no-self-use +class BinarySensorDevice(Entity): + """ Represents a binary sensor.. """ + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return False + + @property + def friendly_state(self): + """ Returns the friendly state of the binary sensor. """ + return None diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py new file mode 100644 index 00000000000..266b46b7e17 --- /dev/null +++ b/homeassistant/components/binary_sensor/demo.py @@ -0,0 +1,49 @@ +""" +homeassistant.components.binary_sensor.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has two fake binary sensors. +""" +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.const import (STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo binary sensors. """ + add_devices([ + DemoBinarySensor('Window Bathroom', True, None), + DemoBinarySensor('Floor Basement', False, None), + ]) + + +class DemoBinarySensor(BinarySensorDevice): + """ A Demo binary sensor. """ + + def __init__(self, name, state, icon=None): + self._name = name or DEVICE_DEFAULT_NAME + self._state = state + self._icon = icon + + @property + def should_poll(self): + """ No polling needed for a demo binary sensor. """ + return False + + @property + def name(self): + """ Returns the name of the binary sensor. """ + return self._name + + @property + def icon(self): + """ Returns the icon to use for device if any. """ + return self._icon + + @property + def is_on(self): + """ True if the sensor is on. """ + return self._state + + @property + def state(self): + """ Returns the state of the binary sensor. """ + return STATE_ON if self.is_on else STATE_OFF diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 7c873e834bd..3022a0e9393 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -18,7 +18,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', - 'thermostat'] + 'thermostat', 'binary_sensor'] def setup(hass, config): From ca32c81612d2c31f0cda34319fad2011fdbac515 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 19 Nov 2015 19:27:10 +0100 Subject: [PATCH 009/125] Camera demo --- homeassistant/components/camera/demo.py | 37 +++++++++++++++++++++ homeassistant/components/camera/demo_1.png | Bin 0 -> 9772 bytes homeassistant/components/camera/demo_2.png | Bin 0 -> 9667 bytes homeassistant/components/camera/demo_3.png | Bin 0 -> 9595 bytes homeassistant/components/camera/demo_4.png | Bin 0 -> 9906 bytes homeassistant/components/camera/demo_5.png | Bin 0 -> 9699 bytes 6 files changed, 37 insertions(+) create mode 100644 homeassistant/components/camera/demo.py create mode 100644 homeassistant/components/camera/demo_1.png create mode 100644 homeassistant/components/camera/demo_2.png create mode 100644 homeassistant/components/camera/demo_3.png create mode 100644 homeassistant/components/camera/demo_4.png create mode 100644 homeassistant/components/camera/demo_5.png diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py new file mode 100644 index 00000000000..fc3ec263143 --- /dev/null +++ b/homeassistant/components/camera/demo.py @@ -0,0 +1,37 @@ +""" +homeassistant.components.camera.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has a fake camera. +""" +import os +from random import randint +from homeassistant.components.camera import Camera + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo camera. """ + add_devices([ + DemoCamera('Demo camera') + ]) + + +class DemoCamera(Camera): + """ A Demo camera. """ + + def __init__(self, name): + super().__init__() + self._name = name + + def camera_image(self): + """ Return a faked still image response. """ + + image_path = os.path.join(os.path.dirname(__file__), + 'demo_{}.png'.format(randint(1, 5))) + with open(image_path, 'rb') as file: + output = file.read() + return output + + @property + def name(self): + """ Return the name of this device. """ + return self._name diff --git a/homeassistant/components/camera/demo_1.png b/homeassistant/components/camera/demo_1.png new file mode 100644 index 0000000000000000000000000000000000000000..fc681fccecd69fb91801a6ecad8cd28454cae278 GIT binary patch literal 9772 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 W=7Q<<3J~7eg`g;>DqAIO9{fKAR1o+8 literal 0 HcmV?d00001 diff --git a/homeassistant/components/camera/demo_2.png b/homeassistant/components/camera/demo_2.png new file mode 100644 index 0000000000000000000000000000000000000000..255cd5c45d4510cdc5853f22080017af1fad0e68 GIT binary patch literal 9667 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 IF~h+B0sj)foB#j- literal 0 HcmV?d00001 diff --git a/homeassistant/components/camera/demo_3.png b/homeassistant/components/camera/demo_3.png new file mode 100644 index 0000000000000000000000000000000000000000..b54c1ffb57c97644426efae5950dcf47e8c70031 GIT binary patch literal 9595 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 literal 0 HcmV?d00001 diff --git a/homeassistant/components/camera/demo_5.png b/homeassistant/components/camera/demo_5.png new file mode 100644 index 0000000000000000000000000000000000000000..874b95ef6a50b1638d27374aca0c42689506d76f GIT binary patch literal 9699 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 C*Y+I% literal 0 HcmV?d00001 From 685964785d6a2da7a9f47d69cd921034749dcb10 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 19 Nov 2015 19:35:51 +0100 Subject: [PATCH 010/125] Add camera to demos --- homeassistant/components/demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 7c873e834bd..c8888a71c1b 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -18,7 +18,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', - 'thermostat'] + 'thermostat', 'camera'] def setup(hass, config): From fa7391cdf684bb643bba77fbeb79ef0691fa37c0 Mon Sep 17 00:00:00 2001 From: miniconfig Date: Thu, 19 Nov 2015 16:54:55 -0500 Subject: [PATCH 011/125] Changed do_lock and do_unlock methods to lock and unlock. Implemented state method. Fixed locked method for demo interface. Changed LockDevice to extend Entity instead of ToggleEntity --- homeassistant/components/lock/__init__.py | 51 ++++++++--------------- homeassistant/components/lock/demo.py | 11 +++-- homeassistant/components/lock/wink.py | 17 +++----- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 88768208c1e..613410dfd07 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -12,12 +12,12 @@ import os from homeassistant.config import load_yaml_config_file from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.entity import ToggleEntity +from homeassistant.helpers.entity import Entity from homeassistant.const import ( - STATE_LOCKED, SERVICE_LOCK, SERVICE_UNLOCK, ATTR_ENTITY_ID) -from homeassistant.components import ( - group, wink) + STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK, + ATTR_ENTITY_ID) +from homeassistant.components import (group, wink) DOMAIN = 'lock' DEPENDENCIES = [] @@ -44,19 +44,19 @@ PROP_TO_ATTR = { _LOGGER = logging.getLogger(__name__) -def is_locked(hass, entity_id=None): +def locked(hass, entity_id=None): """ Returns if the lock is locked based on the statemachine. """ entity_id = entity_id or ENTITY_ID_ALL_LOCKS return hass.states.is_state(entity_id, STATE_LOCKED) -def do_lock(hass, entity_id=None): +def lock(hass, entity_id=None): """ Locks all or specified locks. """ data = {ATTR_ENTITY_ID: entity_id} if entity_id else None hass.services.call(DOMAIN, SERVICE_LOCK, data) -def do_unlock(hass, entity_id=None): +def unlock(hass, entity_id=None): """ Unlocks all or specified locks. """ data = {ATTR_ENTITY_ID: entity_id} if entity_id else None hass.services.call(DOMAIN, SERVICE_UNLOCK, data) @@ -73,14 +73,14 @@ def setup(hass, config): """ Handles calls to the lock services. """ target_locks = component.extract_from_service(service) - for lock in target_locks: + for item in target_locks: if service.service == SERVICE_LOCK: - lock.do_lock() + item.lock() else: - lock.do_unlock() + item.unlock() - if lock.should_poll: - lock.update_ha_state(True) + if item.should_poll: + item.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) @@ -92,7 +92,7 @@ def setup(hass, config): return True -class LockDevice(ToggleEntity): +class LockDevice(Entity): """ Represents a lock within Home Assistant. """ # pylint: disable=no-self-use @@ -102,23 +102,8 @@ class LockDevice(ToggleEntity): return None @property - def device_state_attributes(self): - """ Returns device specific state attributes. """ - return None - - @property - def state_attributes(self): - """ Returns optional state attributes. """ - data = {} - - for prop, attr in PROP_TO_ATTR.items(): - value = getattr(self, prop) - if value: - data[attr] = value - - device_attr = self.device_state_attributes - - if device_attr is not None: - data.update(device_attr) - - return data + def state(self): + is_locked = self.locked + if is_locked is None: + return STATE_UNKNOWN + return STATE_LOCKED if is_locked else STATE_UNLOCKED diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index 76eea0104fb..67e7032849f 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -41,16 +41,19 @@ class DemoLock(LockDevice): return self._icon @property - def is_locked(self): + def locked(self): """ True if device is locked. """ - return self._state + if self._state == STATE_LOCKED: + return True + else: + return False - def do_lock(self, **kwargs): + def lock(self, **kwargs): """ Lock the device. """ self._state = STATE_LOCKED self.update_ha_state() - def do_unlock(self, **kwargs): + def unlock(self, **kwargs): """ Unlock the device. """ self._state = STATE_UNLOCKED self.update_ha_state() diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py index 059c3a56611..5e463f49a8d 100644 --- a/homeassistant/components/lock/wink.py +++ b/homeassistant/components/lock/wink.py @@ -8,8 +8,8 @@ https://home-assistant.io/components/lock.wink/ """ import logging -from homeassistant.helpers.entity import Entity -from homeassistant.const import CONF_ACCESS_TOKEN, STATE_LOCKED, STATE_UNLOCKED +from homeassistant.components.lock import LockDevice +from homeassistant.const import CONF_ACCESS_TOKEN REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' '9eb39eaba0717922815e673ad1114c685839d890.zip' @@ -34,17 +34,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices(WinkLockDevice(lock) for lock in pywink.get_locks()) -class WinkLockDevice(Entity): +class WinkLockDevice(LockDevice): """ Represents a Wink lock. """ def __init__(self, wink): self.wink = wink - @property - def state(self): - """ Returns the state. """ - return STATE_LOCKED if self.is_locked else STATE_UNLOCKED - @property def unique_id(self): """ Returns the id of this wink lock """ @@ -60,14 +55,14 @@ class WinkLockDevice(Entity): self.wink.updateState() @property - def is_locked(self): + def locked(self): """ True if device is locked. """ return self.wink.state() - def do_lock(self): + def lock(self): """ Lock the device. """ self.wink.setState(True) - def do_unlock(self): + def unlock(self): """ Unlock the device. """ self.wink.setState(False) From 77a1a1529c4ec978fc248666973f3ee2a93802cb Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 19 Nov 2015 22:14:41 +0000 Subject: [PATCH 012/125] Added support for email, removed 'device/' hack to send to all own devices, as own email address does the same --- homeassistant/components/notify/pushbullet.py | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/notify/pushbullet.py b/homeassistant/components/notify/pushbullet.py index 9e9b941394e..16c4e4192eb 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/notify/pushbullet.py @@ -48,20 +48,22 @@ class PushBulletNotificationService(BaseNotificationService): def refresh(self): ''' - Refresh devices, contacts, channels, etc + Refresh devices, contacts, etc pbtargets stores all targets available from this pushbullet instance into a dict. These are PB objects!. It sacrifices a bit of memory for faster processing at send_message + + As of sept 2015, contacts were replaced by chats. This is not + implemented in the module yet ''' self.pushbullet.refresh() self.pbtargets = { - 'device': - {tgt.nickname: tgt for tgt in self.pushbullet.devices}, - 'contact': - {tgt.email: tgt for tgt in self.pushbullet.contacts}, - 'channel': - {tgt.channel_tag: tgt for tgt in self.pushbullet.channels}, + 'device': { + tgt.nickname.lower(): tgt for tgt in self.pushbullet.devices}, + 'channel': { + tgt.channel_tag.lower(): tgt for + tgt in self.pushbullet.channels}, } def send_message(self, message=None, **kwargs): @@ -69,6 +71,8 @@ class PushBulletNotificationService(BaseNotificationService): Send a message to a specified target. If no target specified, a 'normal' push will be sent to all devices linked to the PB account. + Email is special, these are assumed to always exist. We use a special + call which doesn't require a push object """ targets = kwargs.get(ATTR_TARGET) title = kwargs.get(ATTR_TITLE) @@ -86,36 +90,36 @@ class PushBulletNotificationService(BaseNotificationService): # Main loop, Process all targets specified for target in targets: - - # Allow for untargeted push, combined with other types - if target in ['device', 'device/']: - self.pushbullet.push_note(title, message) - _LOGGER.info('Sent notification to self') - continue - try: ttype, tname = target.split('/', 1) except ValueError: _LOGGER.error('Invalid target syntax: %s', target) continue + # Target is email, send directly, don't use a target object + # This also seems works to send to all devices in own account + if ttype == 'email': + self.pushbullet.push_note(title, message, email=tname) + _LOGGER.info('Sent notification to self') + continue + # Refresh if name not found. While awaiting periodic refresh # solution in component, poor mans refresh ;) if ttype not in self.pbtargets: _LOGGER.error('Invalid target syntax: %s', target) continue - if tname not in self.pbtargets[ttype] and not refreshed: + if tname.lower() not in self.pbtargets[ttype] and not refreshed: self.refresh() refreshed = True # Attempt push_note on a dict value. Keys are types & target # name. Dict pbtargets has all *actual* targets. try: - self.pbtargets[ttype][tname].push_note(title, message) + self.pbtargets[ttype][tname.lower()].push_note(title, message) except KeyError: - _LOGGER.error('No such target: %s.%s', ttype, tname) + _LOGGER.error('No such target: %s/%s', ttype, tname) continue except self.pushbullet.errors.PushError: - _LOGGER.error('Notify failed to: %s.%s', ttype, tname) + _LOGGER.error('Notify failed to: %s/%s', ttype, tname) continue - _LOGGER.info('Sent notification to %s.%s', ttype, tname) + _LOGGER.info('Sent notification to %s/%s', ttype, tname) From b9730e6914f4dfece1656bb587492545640e3ead Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 19 Nov 2015 19:00:22 +0100 Subject: [PATCH 013/125] Binary sensor component --- .../components/binary_sensor/__init__.py | 44 +++++++++++++++++ .../components/binary_sensor/demo.py | 49 +++++++++++++++++++ homeassistant/components/demo.py | 2 +- 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/binary_sensor/__init__.py create mode 100644 homeassistant/components/binary_sensor/demo.py diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..b02d38a9f81 --- /dev/null +++ b/homeassistant/components/binary_sensor/__init__.py @@ -0,0 +1,44 @@ +""" +homeassistant.components.binary_sensor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Component to interface with binary sensors (sensors which only know two states) +that can be monitored. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/binary_sensor/ +""" +import logging + +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import Entity + +DOMAIN = 'binary_sensor' +DEPENDENCIES = [] +SCAN_INTERVAL = 30 + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + + +def setup(hass, config): + """ Track states and offer events for binary sensors. """ + component = EntityComponent( + logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) + + component.setup(config) + + return True + + +# pylint: disable=no-self-use +class BinarySensorDevice(Entity): + """ Represents a binary sensor.. """ + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return False + + @property + def friendly_state(self): + """ Returns the friendly state of the binary sensor. """ + return None diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py new file mode 100644 index 00000000000..266b46b7e17 --- /dev/null +++ b/homeassistant/components/binary_sensor/demo.py @@ -0,0 +1,49 @@ +""" +homeassistant.components.binary_sensor.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has two fake binary sensors. +""" +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.const import (STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo binary sensors. """ + add_devices([ + DemoBinarySensor('Window Bathroom', True, None), + DemoBinarySensor('Floor Basement', False, None), + ]) + + +class DemoBinarySensor(BinarySensorDevice): + """ A Demo binary sensor. """ + + def __init__(self, name, state, icon=None): + self._name = name or DEVICE_DEFAULT_NAME + self._state = state + self._icon = icon + + @property + def should_poll(self): + """ No polling needed for a demo binary sensor. """ + return False + + @property + def name(self): + """ Returns the name of the binary sensor. """ + return self._name + + @property + def icon(self): + """ Returns the icon to use for device if any. """ + return self._icon + + @property + def is_on(self): + """ True if the sensor is on. """ + return self._state + + @property + def state(self): + """ Returns the state of the binary sensor. """ + return STATE_ON if self.is_on else STATE_OFF diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index c8888a71c1b..547a1d54a03 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -18,7 +18,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', - 'thermostat', 'camera'] + 'thermostat', 'camera', 'binary_sensor'] def setup(hass, config): From a6006b1835d53292c48050f32ec61118369ece86 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Nov 2015 14:58:49 +0100 Subject: [PATCH 014/125] Move state --- homeassistant/components/binary_sensor/__init__.py | 10 ++++++++-- homeassistant/components/binary_sensor/demo.py | 10 ++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py index b02d38a9f81..2ef9e83cc30 100644 --- a/homeassistant/components/binary_sensor/__init__.py +++ b/homeassistant/components/binary_sensor/__init__.py @@ -11,6 +11,7 @@ import logging from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity import Entity +from homeassistant.const import (STATE_ON, STATE_OFF) DOMAIN = 'binary_sensor' DEPENDENCIES = [] @@ -31,12 +32,17 @@ def setup(hass, config): # pylint: disable=no-self-use class BinarySensorDevice(Entity): - """ Represents a binary sensor.. """ + """ Represents a binary sensor. """ @property def is_on(self): """ True if the binary sensor is on. """ - return False + return None + + @property + def state(self): + """ Returns the state of the binary sensor. """ + return STATE_ON if self.is_on else STATE_OFF @property def friendly_state(self): diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py index 266b46b7e17..a24b893c610 100644 --- a/homeassistant/components/binary_sensor/demo.py +++ b/homeassistant/components/binary_sensor/demo.py @@ -4,7 +4,6 @@ homeassistant.components.binary_sensor.demo Demo platform that has two fake binary sensors. """ from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.const import (STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME) def setup_platform(hass, config, add_devices, discovery_info=None): @@ -19,7 +18,7 @@ class DemoBinarySensor(BinarySensorDevice): """ A Demo binary sensor. """ def __init__(self, name, state, icon=None): - self._name = name or DEVICE_DEFAULT_NAME + self._name = name self._state = state self._icon = icon @@ -40,10 +39,5 @@ class DemoBinarySensor(BinarySensorDevice): @property def is_on(self): - """ True if the sensor is on. """ + """ True if the binary sensor is on. """ return self._state - - @property - def state(self): - """ Returns the state of the binary sensor. """ - return STATE_ON if self.is_on else STATE_OFF From d254e7e9e50271442f5c680cc5ff1b957ac96639 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Nov 2015 15:29:36 +0100 Subject: [PATCH 015/125] Fix pylint issue --- homeassistant/components/binary_sensor/demo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py index 22eeaa7b8a5..a24b893c610 100644 --- a/homeassistant/components/binary_sensor/demo.py +++ b/homeassistant/components/binary_sensor/demo.py @@ -41,4 +41,3 @@ class DemoBinarySensor(BinarySensorDevice): def is_on(self): """ True if the binary sensor is on. """ return self._state - From 3df6c584c0c8458ce7c92634f2f8c2399cbb59d2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 Nov 2015 08:38:56 -0800 Subject: [PATCH 016/125] Update frontend --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 62 +++++++++---------- .../www_static/home-assistant-polymer | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 1435fe16c5c..0199b05156e 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 = "b75e3c9ebd3de2dae0912a89499127a9" +VERSION = "dff74f773ea8b0356b0bd8130ed6f0cf" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 5157f4dd5a6..b2a2eb97b3e 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -839,7 +839,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --google-grey-300: #e0e0e0; --google-grey-500: #9e9e9e; --google-grey-700: #616161; - + /* Material Design color palette from online spec document */ --paper-red-50: #ffebee; @@ -856,7 +856,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-red-a200: #ff5252; --paper-red-a400: #ff1744; --paper-red-a700: #d50000; - + --paper-pink-50: #fce4ec; --paper-pink-100: #f8bbd0; --paper-pink-200: #f48fb1; @@ -871,7 +871,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-pink-a200: #ff4081; --paper-pink-a400: #f50057; --paper-pink-a700: #c51162; - + --paper-purple-50: #f3e5f5; --paper-purple-100: #e1bee7; --paper-purple-200: #ce93d8; @@ -886,7 +886,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-purple-a200: #e040fb; --paper-purple-a400: #d500f9; --paper-purple-a700: #aa00ff; - + --paper-deep-purple-50: #ede7f6; --paper-deep-purple-100: #d1c4e9; --paper-deep-purple-200: #b39ddb; @@ -901,7 +901,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-deep-purple-a200: #7c4dff; --paper-deep-purple-a400: #651fff; --paper-deep-purple-a700: #6200ea; - + --paper-indigo-50: #e8eaf6; --paper-indigo-100: #c5cae9; --paper-indigo-200: #9fa8da; @@ -916,7 +916,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-indigo-a200: #536dfe; --paper-indigo-a400: #3d5afe; --paper-indigo-a700: #304ffe; - + --paper-blue-50: #e3f2fd; --paper-blue-100: #bbdefb; --paper-blue-200: #90caf9; @@ -931,7 +931,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-blue-a200: #448aff; --paper-blue-a400: #2979ff; --paper-blue-a700: #2962ff; - + --paper-light-blue-50: #e1f5fe; --paper-light-blue-100: #b3e5fc; --paper-light-blue-200: #81d4fa; @@ -946,7 +946,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-light-blue-a200: #40c4ff; --paper-light-blue-a400: #00b0ff; --paper-light-blue-a700: #0091ea; - + --paper-cyan-50: #e0f7fa; --paper-cyan-100: #b2ebf2; --paper-cyan-200: #80deea; @@ -961,7 +961,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-cyan-a200: #18ffff; --paper-cyan-a400: #00e5ff; --paper-cyan-a700: #00b8d4; - + --paper-teal-50: #e0f2f1; --paper-teal-100: #b2dfdb; --paper-teal-200: #80cbc4; @@ -976,7 +976,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-teal-a200: #64ffda; --paper-teal-a400: #1de9b6; --paper-teal-a700: #00bfa5; - + --paper-green-50: #e8f5e9; --paper-green-100: #c8e6c9; --paper-green-200: #a5d6a7; @@ -991,7 +991,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-green-a200: #69f0ae; --paper-green-a400: #00e676; --paper-green-a700: #00c853; - + --paper-light-green-50: #f1f8e9; --paper-light-green-100: #dcedc8; --paper-light-green-200: #c5e1a5; @@ -1006,7 +1006,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-light-green-a200: #b2ff59; --paper-light-green-a400: #76ff03; --paper-light-green-a700: #64dd17; - + --paper-lime-50: #f9fbe7; --paper-lime-100: #f0f4c3; --paper-lime-200: #e6ee9c; @@ -1021,7 +1021,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-lime-a200: #eeff41; --paper-lime-a400: #c6ff00; --paper-lime-a700: #aeea00; - + --paper-yellow-50: #fffde7; --paper-yellow-100: #fff9c4; --paper-yellow-200: #fff59d; @@ -1036,7 +1036,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-yellow-a200: #ffff00; --paper-yellow-a400: #ffea00; --paper-yellow-a700: #ffd600; - + --paper-amber-50: #fff8e1; --paper-amber-100: #ffecb3; --paper-amber-200: #ffe082; @@ -1051,7 +1051,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-amber-a200: #ffd740; --paper-amber-a400: #ffc400; --paper-amber-a700: #ffab00; - + --paper-orange-50: #fff3e0; --paper-orange-100: #ffe0b2; --paper-orange-200: #ffcc80; @@ -1066,7 +1066,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-orange-a200: #ffab40; --paper-orange-a400: #ff9100; --paper-orange-a700: #ff6500; - + --paper-deep-orange-50: #fbe9e7; --paper-deep-orange-100: #ffccbc; --paper-deep-orange-200: #ffab91; @@ -1081,7 +1081,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-deep-orange-a200: #ff6e40; --paper-deep-orange-a400: #ff3d00; --paper-deep-orange-a700: #dd2c00; - + --paper-brown-50: #efebe9; --paper-brown-100: #d7ccc8; --paper-brown-200: #bcaaa4; @@ -1092,7 +1092,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-brown-700: #5d4037; --paper-brown-800: #4e342e; --paper-brown-900: #3e2723; - + --paper-grey-50: #fafafa; --paper-grey-100: #f5f5f5; --paper-grey-200: #eeeeee; @@ -1103,7 +1103,7 @@ var a=i[o];r&&"#"==a[0]?s=Polymer.Collection.get(r).getItem(a):(s=s[a],n&&r&&par --paper-grey-700: #616161; --paper-grey-800: #424242; --paper-grey-900: #212121; - + --paper-blue-grey-50: #eceff1; --paper-blue-grey-100: #cfd8dc; --paper-blue-grey-200: #b0bec5; @@ -2176,7 +2176,7 @@ case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return } + } \ 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 99af263595d..39e09d85b74 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 99af263595dbbf057d26bb266101fa1e386442c6 +Subproject commit 39e09d85b74afb332ad2872b5aa556c9c9d113c3 From b4cf0e874afeca102780b0936faefd290b72585d Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Fri, 20 Nov 2015 22:03:17 +0100 Subject: [PATCH 017/125] Support parsing mqtt messages via jsonpath --- homeassistant/components/mqtt/__init__.py | 11 +++++++++++ homeassistant/components/sensor/mqtt.py | 15 ++++++++++++--- homeassistant/components/switch/mqtt.py | 13 +++++++++++-- requirements_all.txt | 1 + 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index cd5b5370175..24a19494d41 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -9,6 +9,8 @@ https://home-assistant.io/components/mqtt/ import logging import os import socket +import json +import jsonpath_rw from homeassistant.exceptions import HomeAssistantError import homeassistant.util as util @@ -127,6 +129,15 @@ def setup(hass, config): return True +class JsonFmtParser(object): + """ Implements a json parser on xpath""" + def __init__(self, jsonpath): + self._expr = jsonpath_rw.parse(jsonpath) + def __call__(self, payload): + match = self._expr.find(json.loads(payload)) + return match[0].value if len(match) > 0 else None + + # This is based on one of the paho-mqtt examples: # http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.python.git/tree/examples/sub-class.py # pylint: disable=too-many-arguments diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 2623d2fdcce..f2608bd1657 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -7,6 +7,8 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.mqtt/ """ import logging +import json +import jsonpath_rw from homeassistant.helpers.entity import Entity import homeassistant.components.mqtt as mqtt @@ -31,23 +33,30 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('name', DEFAULT_NAME), config.get('state_topic'), config.get('qos', DEFAULT_QOS), - config.get('unit_of_measurement'))]) + config.get('unit_of_measurement'), + config.get('state_format'))]) # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSensor(Entity): """ Represents a sensor that can be updated using MQTT. """ - def __init__(self, hass, name, state_topic, qos, unit_of_measurement): + def __init__(self, hass, name, state_topic, qos, unit_of_measurement,state_format): self._state = "-" self._hass = hass self._name = name self._state_topic = state_topic self._qos = qos self._unit_of_measurement = unit_of_measurement + self._state_format = state_format + + if self._state_format.startswith('json:'): + self._parser = mqtt.JsonFmtParser(self._state_format[5:]) + else: + self._parser = lambda x: x def message_received(topic, payload, qos): """ A new MQTT message has been received. """ - self._state = payload + self._state = self._parser(payload) self.update_ha_state() mqtt.subscribe(hass, self._state_topic, message_received, self._qos) diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index 12d3f486323..a94ebe6b6b1 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -37,14 +37,15 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('qos', DEFAULT_QOS), config.get('payload_on', DEFAULT_PAYLOAD_ON), config.get('payload_off', DEFAULT_PAYLOAD_OFF), - config.get('optimistic', DEFAULT_OPTIMISTIC))]) + config.get('optimistic', DEFAULT_OPTIMISTIC), + config.get('state_format'))]) # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSwitch(SwitchDevice): """ Represents a switch that can be togggled using MQTT. """ def __init__(self, hass, name, state_topic, command_topic, qos, - payload_on, payload_off, optimistic): + payload_on, payload_off, optimistic, state_format): self._state = False self._hass = hass self._name = name @@ -54,9 +55,17 @@ class MqttSwitch(SwitchDevice): self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic + + self._state_format = state_format + + if self._state_format.startswith('json:'): + self._parser = mqtt.JsonFmtParser(self._state_format[5:]) + else: + self._parser = lambda x: x def message_received(topic, payload, qos): """ A new MQTT message has been received. """ + payload = self._parser(payload) if payload == self._payload_on: self._state = True self.update_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index c6b4ae81721..7e15e594313 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -74,6 +74,7 @@ https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b6 # homeassistant.components.mqtt paho-mqtt==1.1 +jsonpath-rw==1.4.0 # homeassistant.components.notify.pushbullet pushbullet.py==0.9.0 From 105dc2847e9c57429afd8b79304f9a7c046b0570 Mon Sep 17 00:00:00 2001 From: miniconfig Date: Fri, 20 Nov 2015 16:34:27 -0500 Subject: [PATCH 018/125] Changed locked method of lock support to "is_locked". Added lock and unlock methods Updated wink components to use the new version of the wink library. --- homeassistant/components/light/wink.py | 4 ++-- homeassistant/components/lock/__init__.py | 18 +++++++++++++----- homeassistant/components/lock/demo.py | 2 +- homeassistant/components/lock/wink.py | 6 +++--- homeassistant/components/sensor/wink.py | 4 ++-- homeassistant/components/switch/wink.py | 4 ++-- homeassistant/components/wink.py | 4 ++-- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/light/wink.py b/homeassistant/components/light/wink.py index 4fbf87aea2d..eaa703799f7 100644 --- a/homeassistant/components/light/wink.py +++ b/homeassistant/components/light/wink.py @@ -13,8 +13,8 @@ from homeassistant.components.wink import WinkToggleDevice from homeassistant.const import CONF_ACCESS_TOKEN REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' - '9eb39eaba0717922815e673ad1114c685839d890.zip' - '#python-wink==0.1.1'] + '42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip' + '#python-wink==0.2'] def setup_platform(hass, config, add_devices_callback, discovery_info=None): diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 613410dfd07..2cbd3a40872 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -44,7 +44,7 @@ PROP_TO_ATTR = { _LOGGER = logging.getLogger(__name__) -def locked(hass, entity_id=None): +def is_locked(hass, entity_id=None): """ Returns if the lock is locked based on the statemachine. """ entity_id = entity_id or ENTITY_ID_ALL_LOCKS return hass.states.is_state(entity_id, STATE_LOCKED) @@ -97,13 +97,21 @@ class LockDevice(Entity): # pylint: disable=no-self-use @property - def locked(self): + def is_locked(self): """ Is the lock locked or unlocked. """ return None + def lock(self): + """ Locks the lock. """ + raise NotImplementedError() + + def unlock(self): + """ Unlocks the lock. """ + raise NotImplementedError() + @property def state(self): - is_locked = self.locked - if is_locked is None: + locked = self.is_locked + if locked is None: return STATE_UNKNOWN - return STATE_LOCKED if is_locked else STATE_UNLOCKED + return STATE_LOCKED if locked else STATE_UNLOCKED diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index 67e7032849f..ac7bbed3dc2 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -41,7 +41,7 @@ class DemoLock(LockDevice): return self._icon @property - def locked(self): + def is_locked(self): """ True if device is locked. """ if self._state == STATE_LOCKED: return True diff --git a/homeassistant/components/lock/wink.py b/homeassistant/components/lock/wink.py index 5e463f49a8d..27f602d65fa 100644 --- a/homeassistant/components/lock/wink.py +++ b/homeassistant/components/lock/wink.py @@ -12,8 +12,8 @@ from homeassistant.components.lock import LockDevice from homeassistant.const import CONF_ACCESS_TOKEN REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' - '9eb39eaba0717922815e673ad1114c685839d890.zip' - '#python-wink==0.1.1'] + '42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip' + '#python-wink==0.2'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -55,7 +55,7 @@ class WinkLockDevice(LockDevice): self.wink.updateState() @property - def locked(self): + def is_locked(self): """ True if device is locked. """ return self.wink.state() diff --git a/homeassistant/components/sensor/wink.py b/homeassistant/components/sensor/wink.py index 8bfdb9205fa..26fe6538e05 100644 --- a/homeassistant/components/sensor/wink.py +++ b/homeassistant/components/sensor/wink.py @@ -12,8 +12,8 @@ from homeassistant.helpers.entity import Entity from homeassistant.const import CONF_ACCESS_TOKEN, STATE_OPEN, STATE_CLOSED REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' - '9eb39eaba0717922815e673ad1114c685839d890.zip' - '#python-wink==0.1.1'] + '42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip' + '#python-wink==0.2'] def setup_platform(hass, config, add_devices, discovery_info=None): diff --git a/homeassistant/components/switch/wink.py b/homeassistant/components/switch/wink.py index b022d8cbf72..f0dc18003c6 100644 --- a/homeassistant/components/switch/wink.py +++ b/homeassistant/components/switch/wink.py @@ -12,8 +12,8 @@ from homeassistant.components.wink import WinkToggleDevice from homeassistant.const import CONF_ACCESS_TOKEN REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' - '9eb39eaba0717922815e673ad1114c685839d890.zip' - '#python-wink==0.1.1'] + '42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip' + '#python-wink==0.2'] def setup_platform(hass, config, add_devices, discovery_info=None): diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index 66ea29ff4dd..bd79210bf75 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -19,8 +19,8 @@ from homeassistant.const import ( DOMAIN = "wink" DEPENDENCIES = [] REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' - '9eb39eaba0717922815e673ad1114c685839d890.zip' - '#python-wink==0.1.1'] + '42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip' + '#python-wink==0.2'] DISCOVER_LIGHTS = "wink.lights" DISCOVER_SWITCHES = "wink.switches" From 799043dc0a2031318b235d4a78f0c6d1178e7dca Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Fri, 20 Nov 2015 22:45:09 +0100 Subject: [PATCH 019/125] refactor format mqtt format parser --- homeassistant/components/mqtt/__init__.py | 23 +++++++++++++++++------ homeassistant/components/sensor/mqtt.py | 7 +------ homeassistant/components/switch/mqtt.py | 8 +------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 24a19494d41..b2884a0ff34 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -129,13 +129,24 @@ def setup(hass, config): return True -class JsonFmtParser(object): - """ Implements a json parser on xpath""" - def __init__(self, jsonpath): - self._expr = jsonpath_rw.parse(jsonpath) +class FmtParser(object): + """ wrapper for all supported formats """ + + class _JsonFmtParser(object): + """ Implements a json parser on xpath""" + def __init__(self, jsonpath): + self._expr = jsonpath_rw.parse(jsonpath) + def __call__(self, payload): + match = self._expr.find(json.loads(payload)) + return match[0].value if len(match) > 0 else None + + def __init__(self, fmt): + if fmt.startswith('json:'): + self._parser = FmtParser._JsonFmtParser(fmt[5:]) + else: + self._parser = lambda x: x def __call__(self, payload): - match = self._expr.find(json.loads(payload)) - return match[0].value if len(match) > 0 else None + return self._parser(payload) # This is based on one of the paho-mqtt examples: diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index f2608bd1657..64a8852d06e 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -47,12 +47,7 @@ class MqttSensor(Entity): self._state_topic = state_topic self._qos = qos self._unit_of_measurement = unit_of_measurement - self._state_format = state_format - - if self._state_format.startswith('json:'): - self._parser = mqtt.JsonFmtParser(self._state_format[5:]) - else: - self._parser = lambda x: x + self._parser = mqtt.FmtParser(state_format) def message_received(topic, payload, qos): """ A new MQTT message has been received. """ diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index a94ebe6b6b1..eeb4551b921 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -55,13 +55,7 @@ class MqttSwitch(SwitchDevice): self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic - - self._state_format = state_format - - if self._state_format.startswith('json:'): - self._parser = mqtt.JsonFmtParser(self._state_format[5:]) - else: - self._parser = lambda x: x + self._parser = mqtt.FmtParser(state_format) def message_received(topic, payload, qos): """ A new MQTT message has been received. """ From 030686a978225255d386fe3b9b6925744514f2f6 Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Fri, 20 Nov 2015 22:55:52 +0100 Subject: [PATCH 020/125] fix flak8 warnings --- homeassistant/components/mqtt/__init__.py | 32 ++++++++++++----------- homeassistant/components/sensor/mqtt.py | 5 ++-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index b2884a0ff34..b5b8ca9cca7 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -129,24 +129,26 @@ def setup(hass, config): return True -class FmtParser(object): - """ wrapper for all supported formats """ - - class _JsonFmtParser(object): +class _JsonFmtParser(object): """ Implements a json parser on xpath""" def __init__(self, jsonpath): - self._expr = jsonpath_rw.parse(jsonpath) + self._expr = jsonpath_rw.parse(jsonpath) + def __call__(self, payload): - match = self._expr.find(json.loads(payload)) - return match[0].value if len(match) > 0 else None - - def __init__(self, fmt): - if fmt.startswith('json:'): - self._parser = FmtParser._JsonFmtParser(fmt[5:]) - else: - self._parser = lambda x: x - def __call__(self, payload): - return self._parser(payload) + match = self._expr.find(json.loads(payload)) + return match[0].value if len(match) > 0 else payload + + +class FmtParser(object): + """ wrapper for all supported formats """ + def __init__(self, fmt): + if fmt.startswith('json:'): + self._parser = _JsonFmtParser(fmt[5:]) + else: + self._parser = lambda x: x + + def __call__(self, payload): + return self._parser(payload) # This is based on one of the paho-mqtt examples: diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 64a8852d06e..384d0f8a48a 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -7,8 +7,6 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.mqtt/ """ import logging -import json -import jsonpath_rw from homeassistant.helpers.entity import Entity import homeassistant.components.mqtt as mqtt @@ -40,7 +38,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSensor(Entity): """ Represents a sensor that can be updated using MQTT. """ - def __init__(self, hass, name, state_topic, qos, unit_of_measurement,state_format): + def __init__(self, hass, name, state_topic, qos, unit_of_measurement, + state_format): self._state = "-" self._hass = hass self._name = name From 065f4b7c20d7cf1da468c4227337be94b8527e40 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Nov 2015 23:39:39 +0100 Subject: [PATCH 021/125] Add binary sensor for aREST --- .../components/binary_sensor/arest.py | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 homeassistant/components/binary_sensor/arest.py diff --git a/homeassistant/components/binary_sensor/arest.py b/homeassistant/components/binary_sensor/arest.py new file mode 100644 index 00000000000..3077fe40bdb --- /dev/null +++ b/homeassistant/components/binary_sensor/arest.py @@ -0,0 +1,106 @@ +""" +homeassistant.components.binary_sensor.arest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The arest sensor will consume an exposed aREST API of a device. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.arest/ +""" +import logging +import requests +from datetime import timedelta + +from homeassistant.util import Throttle +from homeassistant.components.binary_sensor import BinarySensorDevice + +_LOGGER = logging.getLogger(__name__) + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) + +CONF_RESOURCE = 'resource' +CONF_PIN = 'pin' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Get the aREST binary sensor. """ + + resource = config.get(CONF_RESOURCE) + pin = config.get(CONF_PIN) + + if None in (resource, pin): + _LOGGER.error('Not all required config keys present: %s', + ', '.join((CONF_RESOURCE, CONF_PIN))) + return False + + try: + response = requests.get(resource, timeout=10).json() + except requests.exceptions.MissingSchema: + _LOGGER.error('Missing resource or schema in configuration. ' + 'Add http:// to your URL.') + return False + except requests.exceptions.ConnectionError: + _LOGGER.error('No route to device at %s. ' + 'Please check the IP address in the configuration file.', + resource) + return False + + arest = ArestData(resource, pin) + + add_devices([ArestBinarySensor(arest, + resource, + config.get('name', response['name']), + pin)]) + + +# pylint: disable=too-many-instance-attributes, too-many-arguments +class ArestBinarySensor(BinarySensorDevice): + """ Implements an aREST binary sensor for a pin. """ + + def __init__(self, arest, resource, name, pin): + self.arest = arest + self._resource = resource + self._name = name + self._pin = pin + self.update() + + if self._pin is not None: + request = requests.get('{}/mode/{}/i'.format + (self._resource, self._pin), timeout=10) + if request.status_code is not 200: + _LOGGER.error("Can't set mode. Is device offline?") + + @property + def name(self): + """ The name of the binary sensor. """ + return self._name + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return bool(self.arest.data.get('state')) + + def update(self): + """ Gets the latest data from aREST API. """ + self.arest.update() + + +# pylint: disable=too-few-public-methods +class ArestData(object): + """ Class for handling the data retrieval for pins. """ + + def __init__(self, resource, pin): + self._resource = resource + self._pin = pin + self.data = {} + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ Gets the latest data from aREST device. """ + try: + response = requests.get('{}/digital/{}'.format( + self._resource, self._pin), timeout=10) + self.data = {'state': response.json()['return_value']} + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to device '%s'. Is device offline?", + self._resource) From 44714614ada3b5974c706235811c53f7709ff162 Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Fri, 20 Nov 2015 23:42:22 +0100 Subject: [PATCH 022/125] Fix unit tests for mqtt --- homeassistant/components/mqtt/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index b5b8ca9cca7..5b92da90d0a 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -142,10 +142,10 @@ class _JsonFmtParser(object): class FmtParser(object): """ wrapper for all supported formats """ def __init__(self, fmt): - if fmt.startswith('json:'): - self._parser = _JsonFmtParser(fmt[5:]) - else: - self._parser = lambda x: x + self._parser = lambda x: x + if fmt: + if fmt.startswith('json:'): + self._parser = _JsonFmtParser(fmt[5:]) def __call__(self, payload): return self._parser(payload) From 08d29d3630f498932a00fde2187a1cfe40c3a6d0 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Nov 2015 23:43:59 +0100 Subject: [PATCH 023/125] Add MQTT binary sensor --- .../components/binary_sensor/mqtt.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 homeassistant/components/binary_sensor/mqtt.py diff --git a/homeassistant/components/binary_sensor/mqtt.py b/homeassistant/components/binary_sensor/mqtt.py new file mode 100644 index 00000000000..cac991d4eb2 --- /dev/null +++ b/homeassistant/components/binary_sensor/mqtt.py @@ -0,0 +1,76 @@ +""" +homeassistant.components.binary_sensor.mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a MQTT binary sensor. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.mqtt/ +""" +import logging +from homeassistant.components.binary_sensor import BinarySensorDevice +import homeassistant.components.mqtt as mqtt + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'MQTT Binary sensor' +DEFAULT_QOS = 0 +DEFAULT_PAYLOAD_ON = 'ON' +DEFAULT_PAYLOAD_OFF = 'OFF' + +DEPENDENCIES = ['mqtt'] + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Add MQTT binary sensor. """ + + if config.get('state_topic') is None: + _LOGGER.error('Missing required variable: state_topic') + return False + + add_devices([MqttBinarySensor( + hass, + config.get('name', DEFAULT_NAME), + config.get('state_topic', None), + config.get('qos', DEFAULT_QOS), + config.get('payload_on', DEFAULT_PAYLOAD_ON), + config.get('payload_off', DEFAULT_PAYLOAD_OFF))]) + + +# pylint: disable=too-many-arguments, too-many-instance-attributes +class MqttBinarySensor(BinarySensorDevice): + """ Represents a binary sensor that is updated by MQTT. """ + def __init__(self, hass, name, state_topic, qos, payload_on, payload_off): + self._hass = hass + self._name = name + self._state = False + self._state_topic = state_topic + self._payload_on = payload_on + self._payload_off = payload_off + self._qos = qos + + def message_received(topic, payload, qos): + """ A new MQTT message has been received. """ + if payload == self._payload_on: + self._state = True + self.update_ha_state() + elif payload == self._payload_off: + self._state = False + self.update_ha_state() + + mqtt.subscribe(hass, self._state_topic, message_received, self._qos) + + @property + def should_poll(self): + """ No polling needed. """ + return False + + @property + def name(self): + """ The name of the binary sensor. """ + return self._name + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return self._state From 820b2a31b3745e2dbd162565915fba2ce23592b3 Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Fri, 20 Nov 2015 23:44:46 +0100 Subject: [PATCH 024/125] Add additional unit tests for mqtt state format parsing --- tests/components/sensor/test_mqtt.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/components/sensor/test_mqtt.py b/tests/components/sensor/test_mqtt.py index b59ea867c58..8d1aef4d57f 100644 --- a/tests/components/sensor/test_mqtt.py +++ b/tests/components/sensor/test_mqtt.py @@ -39,3 +39,22 @@ class TestSensorMQTT(unittest.TestCase): self.assertEqual('100', state.state) self.assertEqual('fav unit', state.attributes.get('unit_of_measurement')) + + def test_setting_sensor_value_via_mqtt_json_message(self): + self.assertTrue(sensor.setup(self.hass, { + 'sensor': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'test-topic', + 'unit_of_measurement': 'fav unit', + 'state_format': 'json:val' + } + })) + + fire_mqtt_message(self.hass, 'test-topic', '{ "val": "100" }') + self.hass.pool.block_till_done() + state = self.hass.states.get('sensor.test') + + self.assertEqual('100', state.state) + self.assertEqual('fav unit', + state.attributes.get('unit_of_measurement')) From b4ee3f73b46aed0f36bc660db80487869c739863 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Nov 2015 23:47:49 +0100 Subject: [PATCH 025/125] Add aREST binary sensor and fix ordering --- .coveragerc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.coveragerc b/.coveragerc index f19e37d00a1..07d4a8dd4bc 100644 --- a/.coveragerc +++ b/.coveragerc @@ -29,7 +29,7 @@ omit = homeassistant/components/rfxtrx.py homeassistant/components/*/rfxtrx.py - homeassistant/components/ifttt.py + homeassistant/components/binary_sensor/arest.py homeassistant/components/browser.py homeassistant/components/camera/* homeassistant/components/device_tracker/actiontec.py @@ -48,6 +48,7 @@ omit = homeassistant/components/device_tracker/snmp.py homeassistant/components/discovery.py homeassistant/components/downloader.py + homeassistant/components/ifttt.py homeassistant/components/keyboard.py homeassistant/components/light/hue.py homeassistant/components/light/mqtt.py @@ -84,7 +85,6 @@ omit = homeassistant/components/sensor/glances.py homeassistant/components/sensor/mysensors.py homeassistant/components/sensor/openweathermap.py - homeassistant/components/switch/orvibo.py homeassistant/components/sensor/rest.py homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/sabnzbd.py @@ -98,6 +98,7 @@ omit = homeassistant/components/switch/command_switch.py homeassistant/components/switch/edimax.py homeassistant/components/switch/hikvisioncam.py + homeassistant/components/switch/orvibo.py homeassistant/components/switch/rest.py homeassistant/components/switch/rpi_gpio.py homeassistant/components/switch/transmission.py From 422a93e73599f661b9c6be2f2b6a7bdc6b28bdeb Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 20 Nov 2015 23:48:59 +0100 Subject: [PATCH 026/125] Add tests for MQTT binary sensor --- tests/components/binary_sensor/__init__.py | 0 tests/components/binary_sensor/test_mqtt.py | 48 +++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/components/binary_sensor/__init__.py create mode 100644 tests/components/binary_sensor/test_mqtt.py diff --git a/tests/components/binary_sensor/__init__.py b/tests/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/components/binary_sensor/test_mqtt.py b/tests/components/binary_sensor/test_mqtt.py new file mode 100644 index 00000000000..83fa532d051 --- /dev/null +++ b/tests/components/binary_sensor/test_mqtt.py @@ -0,0 +1,48 @@ +""" +tests.components.binary_sensor.test_mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests MQTT binary sensor. +""" +import unittest + +import homeassistant.core as ha +import homeassistant.components.binary_sensor as binary_sensor +from tests.common import mock_mqtt_component, fire_mqtt_message +from homeassistant.const import (STATE_OFF, STATE_ON) + + +class TestSensorMQTT(unittest.TestCase): + """ Test the MQTT sensor. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + mock_mqtt_component(self.hass) + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass.stop() + + def test_setting_sensor_value_via_mqtt_message(self): + self.assertTrue(binary_sensor.setup(self.hass, { + 'binary_sensor': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'test-topic', + 'payload_on': 'ON', + 'payload_off': 'OFF', + } + })) + + state = self.hass.states.get('binary_sensor.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test-topic', 'ON') + self.hass.pool.block_till_done() + state = self.hass.states.get('binary_sensor.test') + self.assertEqual(STATE_ON, state.state) + + fire_mqtt_message(self.hass, 'test-topic', 'OFF') + self.hass.pool.block_till_done() + state = self.hass.states.get('binary_sensor.test') + self.assertEqual(STATE_OFF, state.state) From 427944cc4491225bc043b43175d81fce6fd44e27 Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Fri, 20 Nov 2015 23:50:46 +0100 Subject: [PATCH 027/125] add test for mqtt+json switch --- tests/components/sensor/test_mqtt.py | 3 +-- tests/components/switch/test_mqtt.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/components/sensor/test_mqtt.py b/tests/components/sensor/test_mqtt.py index 8d1aef4d57f..0c17b95e212 100644 --- a/tests/components/sensor/test_mqtt.py +++ b/tests/components/sensor/test_mqtt.py @@ -56,5 +56,4 @@ class TestSensorMQTT(unittest.TestCase): state = self.hass.states.get('sensor.test') self.assertEqual('100', state.state) - self.assertEqual('fav unit', - state.attributes.get('unit_of_measurement')) + diff --git a/tests/components/switch/test_mqtt.py b/tests/components/switch/test_mqtt.py index a09fcf86c58..4754e64e575 100644 --- a/tests/components/switch/test_mqtt.py +++ b/tests/components/switch/test_mqtt.py @@ -80,3 +80,31 @@ class TestSensorMQTT(unittest.TestCase): self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('switch.test') self.assertEqual(STATE_OFF, state.state) + + def test_controlling_state_via_topic_and_json_message(self): + self.assertTrue(switch.setup(self.hass, { + 'switch': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'payload_on': 'beer on', + 'payload_off': 'beer off', + 'state_format': 'json:val' + } + })) + + state = self.hass.states.get('switch.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '{"val":"beer on"}') + self.hass.pool.block_till_done() + + state = self.hass.states.get('switch.test') + self.assertEqual(STATE_ON, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '{"val":"beer off"}') + self.hass.pool.block_till_done() + + state = self.hass.states.get('switch.test') + self.assertEqual(STATE_OFF, state.state) \ No newline at end of file From 06212604356216b45905d64c434bcaf83a6a5b15 Mon Sep 17 00:00:00 2001 From: Brad Buran Date: Thu, 19 Nov 2015 20:35:18 -0800 Subject: [PATCH 028/125] Fix issue with finding location of OpenZWave conf Under some install scenarios, it may be possible that OpenZWave can't automatically discover the location of the vendor-specific XML config files. In this event, we need to specify the location in the Home Assistant configuration.yaml file. --- homeassistant/components/zwave.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 06f471cd692..11515e4031d 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -6,6 +6,9 @@ Connects Home Assistant to a Z-Wave network. For more details about this component, please refer to the documentation at https://home-assistant.io/components/zwave/ """ +import sys +import os.path + from pprint import pprint from homeassistant import bootstrap @@ -21,6 +24,8 @@ CONF_USB_STICK_PATH = "usb_path" DEFAULT_CONF_USB_STICK_PATH = "/zwaveusbstick" CONF_DEBUG = "debug" CONF_POLLING_INTERVAL = "polling_interval" +DEFAULT_ZWAVE_CONFIG_PATH = os.path.join(sys.prefix, 'share', + 'python-openzwave', 'config') DISCOVER_SENSORS = "zwave.sensors" DISCOVER_SWITCHES = "zwave.switch" @@ -120,7 +125,9 @@ def setup(hass, config): # Setup options options = ZWaveOption( config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH), - user_path=hass.config.config_dir) + user_path=hass.config.config_dir, + config_path=config[DOMAIN].get('config_path', + DEFAULT_ZWAVE_CONFIG_PATH),) options.set_console_output(use_debug) options.lock() From f37d0d1c20d4bfa204a16d99f1f00d92bca769d9 Mon Sep 17 00:00:00 2001 From: miniconfig Date: Sat, 21 Nov 2015 09:52:43 -0500 Subject: [PATCH 029/125] Updated wink library version in requirements_all.txt --- requirements_all.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all.txt b/requirements_all.txt index ce6cbfabc96..97d08d97c58 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -93,7 +93,7 @@ netdisco==0.5.1 pywemo==0.3.2 # Wink (*.wink) -https://github.com/balloob/python-wink/archive/9eb39eaba0717922815e673ad1114c685839d890.zip#python-wink==0.1.1 +https://github.com/balloob/python-wink/archive/42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip#python-wink==0.2 # Slack notifier (notify.slack) slacker==0.6.8 From 715abf241e732d69c43798493b134ca51cae9194 Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Sat, 21 Nov 2015 17:57:15 +0100 Subject: [PATCH 030/125] Disable pylint warning for callable classes --- homeassistant/components/mqtt/__init__.py | 8 +++++--- homeassistant/components/sensor/mqtt.py | 4 ++-- homeassistant/components/switch/mqtt.py | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 5b92da90d0a..c27abbebe58 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -129,6 +129,7 @@ def setup(hass, config): return True +# pylint: disable=too-few-public-methods class _JsonFmtParser(object): """ Implements a json parser on xpath""" def __init__(self, jsonpath): @@ -139,16 +140,17 @@ class _JsonFmtParser(object): return match[0].value if len(match) > 0 else payload +# pylint: disable=too-few-public-methods class FmtParser(object): """ wrapper for all supported formats """ def __init__(self, fmt): - self._parser = lambda x: x + self._parse = lambda x: x if fmt: if fmt.startswith('json:'): - self._parser = _JsonFmtParser(fmt[5:]) + self._parse = _JsonFmtParser(fmt[5:]) def __call__(self, payload): - return self._parser(payload) + return self._parse(payload) # This is based on one of the paho-mqtt examples: diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 384d0f8a48a..2bbed97e40c 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -46,11 +46,11 @@ class MqttSensor(Entity): self._state_topic = state_topic self._qos = qos self._unit_of_measurement = unit_of_measurement - self._parser = mqtt.FmtParser(state_format) + self._parse = mqtt.FmtParser(state_format) def message_received(topic, payload, qos): """ A new MQTT message has been received. """ - self._state = self._parser(payload) + self._state = self._parse(payload) self.update_ha_state() mqtt.subscribe(hass, self._state_topic, message_received, self._qos) diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index eeb4551b921..ed99a87868a 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -55,11 +55,11 @@ class MqttSwitch(SwitchDevice): self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic - self._parser = mqtt.FmtParser(state_format) + self._parse = mqtt.FmtParser(state_format) def message_received(topic, payload, qos): """ A new MQTT message has been received. """ - payload = self._parser(payload) + payload = self._parse(payload) if payload == self._payload_on: self._state = True self.update_ha_state() From dbcd055cfecb4c0757f3d4d42826b2638f3fc52c Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Sun, 22 Nov 2015 16:18:05 +0100 Subject: [PATCH 031/125] move import of jsonpath-rw to c'tor of _JsonFmtParser --- homeassistant/components/mqtt/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index c27abbebe58..f729d02af8a 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -10,7 +10,6 @@ import logging import os import socket import json -import jsonpath_rw from homeassistant.exceptions import HomeAssistantError import homeassistant.util as util @@ -32,7 +31,8 @@ SERVICE_PUBLISH = 'publish' EVENT_MQTT_MESSAGE_RECEIVED = 'MQTT_MESSAGE_RECEIVED' DEPENDENCIES = [] -REQUIREMENTS = ['paho-mqtt==1.1'] +REQUIREMENTS = ['paho-mqtt==1.1' + 'jsonpath-rw==1.4.0'] CONF_BROKER = 'broker' CONF_PORT = 'port' @@ -133,6 +133,7 @@ def setup(hass, config): class _JsonFmtParser(object): """ Implements a json parser on xpath""" def __init__(self, jsonpath): + import jsonpath_rw self._expr = jsonpath_rw.parse(jsonpath) def __call__(self, payload): From 90681c2dc97fdddf9dee418ddb0b6e2981e26c62 Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Sun, 22 Nov 2015 16:19:08 +0100 Subject: [PATCH 032/125] fix incorrect requirements --- homeassistant/components/mqtt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index f729d02af8a..24c30c5eca6 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -31,7 +31,7 @@ SERVICE_PUBLISH = 'publish' EVENT_MQTT_MESSAGE_RECEIVED = 'MQTT_MESSAGE_RECEIVED' DEPENDENCIES = [] -REQUIREMENTS = ['paho-mqtt==1.1' +REQUIREMENTS = ['paho-mqtt==1.1', 'jsonpath-rw==1.4.0'] CONF_BROKER = 'broker' From 100400f14940bfade04ed29c51434ec55f3ac83d Mon Sep 17 00:00:00 2001 From: Oliver van Porten Date: Sun, 22 Nov 2015 16:28:21 +0100 Subject: [PATCH 033/125] move requirements to single line to not to affect coverage --- homeassistant/components/mqtt/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 24c30c5eca6..9ec5169c729 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -31,8 +31,7 @@ SERVICE_PUBLISH = 'publish' EVENT_MQTT_MESSAGE_RECEIVED = 'MQTT_MESSAGE_RECEIVED' DEPENDENCIES = [] -REQUIREMENTS = ['paho-mqtt==1.1', - 'jsonpath-rw==1.4.0'] +REQUIREMENTS = ['paho-mqtt==1.1', 'jsonpath-rw==1.4.0'] CONF_BROKER = 'broker' CONF_PORT = 'port' From d4f0f0ffd331461efe1c86c873b880257e2b229a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 22 Nov 2015 15:10:24 -0800 Subject: [PATCH 034/125] Update automation url in warning --- homeassistant/components/automation/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index d3ef80d7192..23d83f554ca 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -123,7 +123,7 @@ def _migrate_old_config(config): _LOGGER.warning( 'You are using an old configuration format. Please upgrade: ' - 'https://home-assistant.io/components/automation.html') + 'https://home-assistant.io/components/automation/') new_conf = { CONF_TRIGGER: dict(config), From f17079918233350fe23e1ff43202f376259302cb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 22 Nov 2015 15:09:56 -0800 Subject: [PATCH 035/125] Reconnect when disconnected from MQTT --- homeassistant/components/mqtt/__init__.py | 146 ++++++++++++++-------- 1 file changed, 93 insertions(+), 53 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 9ec5169c729..30f7dc71b41 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -6,10 +6,12 @@ MQTT component, using paho-mqtt. For more details about this component, please refer to the documentation at https://home-assistant.io/components/mqtt/ """ +import json import logging import os import socket -import json +import time + from homeassistant.exceptions import HomeAssistantError import homeassistant.util as util @@ -45,6 +47,8 @@ ATTR_TOPIC = 'topic' ATTR_PAYLOAD = 'payload' ATTR_QOS = 'qos' +MAX_RECONNECT_WAIT = 300 # seconds + def publish(hass, topic, payload, qos=None): """ Send an MQTT message. """ @@ -66,9 +70,7 @@ def subscribe(hass, topic, callback, qos=DEFAULT_QOS): event.data[ATTR_QOS]) hass.bus.listen(EVENT_MQTT_MESSAGE_RECEIVED, mqtt_topic_subscriber) - - if topic not in MQTT_CLIENT.topics: - MQTT_CLIENT.subscribe(topic, qos) + MQTT_CLIENT.subscribe(topic, qos) def setup(hass, config): @@ -162,24 +164,30 @@ class MQTT(object): # pragma: no cover password, certificate): import paho.mqtt.client as mqtt - self.hass = hass - self._progress = {} - self.topics = {} + self.userdata = { + 'hass': hass, + 'topics': {}, + 'progress': {}, + } if client_id is None: self._mqttc = mqtt.Client() else: self._mqttc = mqtt.Client(client_id) + self._mqttc.user_data_set(self.userdata) + if username is not None: self._mqttc.username_pw_set(username, password) if certificate is not None: self._mqttc.tls_set(certificate) - self._mqttc.on_subscribe = self._mqtt_on_subscribe - self._mqttc.on_unsubscribe = self._mqtt_on_unsubscribe - self._mqttc.on_connect = self._mqtt_on_connect - self._mqttc.on_message = self._mqtt_on_message + self._mqttc.on_subscribe = _mqtt_on_subscribe + self._mqttc.on_unsubscribe = _mqtt_on_unsubscribe + self._mqttc.on_connect = _mqtt_on_connect + self._mqttc.on_disconnect = _mqtt_on_disconnect + self._mqttc.on_message = _mqtt_on_message + self._mqttc.connect(broker, port, keepalive) def publish(self, topic, payload, qos): @@ -190,7 +198,7 @@ class MQTT(object): # pragma: no cover """ Unsubscribe from topic. """ result, mid = self._mqttc.unsubscribe(topic) _raise_on_error(result) - self._progress[mid] = topic + self.userdata['progress'][mid] = topic def start(self): """ Run the MQTT client. """ @@ -202,55 +210,87 @@ class MQTT(object): # pragma: no cover def subscribe(self, topic, qos): """ Subscribe to a topic. """ - if topic in self.topics: + if topic in self.userdata['topics']: return result, mid = self._mqttc.subscribe(topic, qos) _raise_on_error(result) - self._progress[mid] = topic - self.topics[topic] = None + self.userdata['progress'][mid] = topic + self.userdata['topics'][topic] = None - def _mqtt_on_connect(self, mqttc, obj, flags, result_code): - """ On connect, resubscribe to all topics we were subscribed to. """ - if result_code != 0: - _LOGGER.error('Unable to connect to the MQTT broker: %s', { - 1: 'Incorrect protocol version', - 2: 'Invalid client identifier', - 3: 'Server unavailable', - 4: 'Bad username or password', - 5: 'Not authorised' - }.get(result_code)) - self._mqttc.disconnect() - return - old_topics = self.topics - self._progress = {} - self.topics = {} - for topic, qos in old_topics.items(): - # qos is None if we were in process of subscribing - if qos is not None: - self._mqttc.subscribe(topic, qos) +def _mqtt_on_message(mqttc, userdata, msg): + """ Message callback """ + userdata['hass'].bus.fire(EVENT_MQTT_MESSAGE_RECEIVED, { + ATTR_TOPIC: msg.topic, + ATTR_QOS: msg.qos, + ATTR_PAYLOAD: msg.payload.decode('utf-8'), + }) - def _mqtt_on_subscribe(self, mqttc, obj, mid, granted_qos): - """ Called when subscribe succesfull. """ - topic = self._progress.pop(mid, None) - if topic is None: - return - self.topics[topic] = granted_qos - def _mqtt_on_unsubscribe(self, mqttc, obj, mid, granted_qos): - """ Called when subscribe succesfull. """ - topic = self._progress.pop(mid, None) - if topic is None: - return - self.topics.pop(topic, None) +def _mqtt_on_connect(mqttc, userdata, flags, result_code): + """ On connect, resubscribe to all topics we were subscribed to. """ + if result_code != 0: + _LOGGER.error('Unable to connect to the MQTT broker: %s', { + 1: 'Incorrect protocol version', + 2: 'Invalid client identifier', + 3: 'Server unavailable', + 4: 'Bad username or password', + 5: 'Not authorised' + }.get(result_code)) + mqttc.disconnect() + return - def _mqtt_on_message(self, mqttc, obj, msg): - """ Message callback """ - self.hass.bus.fire(EVENT_MQTT_MESSAGE_RECEIVED, { - ATTR_TOPIC: msg.topic, - ATTR_QOS: msg.qos, - ATTR_PAYLOAD: msg.payload.decode('utf-8'), - }) + old_topics = userdata['topics'] + + userdata['topics'] = {} + userdata['progress'] = {} + + for topic, qos in old_topics.items(): + # qos is None if we were in process of subscribing + if qos is not None: + mqttc.subscribe(topic, qos) + + +def _mqtt_on_subscribe(mqttc, userdata, mid, granted_qos): + """ Called when subscribe successfull. """ + topic = userdata['progress'].pop(mid, None) + if topic is None: + return + userdata['topics'][topic] = granted_qos + + +def _mqtt_on_unsubscribe(mqttc, userdata, mid, granted_qos): + """ Called when subscribe successfull. """ + topic = userdata['progress'].pop(mid, None) + if topic is None: + return + userdata['topics'].pop(topic, None) + + +def _mqtt_on_disconnect(mqttc, userdata, result_code): + """ Called when being disconnected. """ + # When disconnected because of calling disconnect() + if result_code == 0: + return + + tries = 0 + wait_time = 0 + + while True: + try: + if mqttc.reconnect() == 0: + _LOGGER.info('Successfully reconnected to the MQTT server') + break + except socket.error: + pass + + wait_time = min(2**tries, MAX_RECONNECT_WAIT) + _LOGGER.warning( + 'Disconnected from MQTT (%s). Trying to reconnect in %ss', + result_code, wait_time) + # It is ok to sleep here as we are in the MQTT thread. + time.sleep(wait_time) + tries += 1 def _raise_on_error(result): # pragma: no cover From 1bda0bd73bc34f0bb90b71c08f9f688bf852475b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 22 Nov 2015 16:04:16 -0800 Subject: [PATCH 036/125] Add some MQTT tests --- homeassistant/components/mqtt/__init__.py | 18 +++--- tests/components/test_mqtt.py | 74 ++++++++++++++++++++++- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 30f7dc71b41..7f4ff030d36 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -158,7 +158,7 @@ class FmtParser(object): # This is based on one of the paho-mqtt examples: # http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.python.git/tree/examples/sub-class.py # pylint: disable=too-many-arguments -class MQTT(object): # pragma: no cover +class MQTT(object): """ Implements messaging service for MQTT. """ def __init__(self, hass, broker, port, client_id, keepalive, username, password, certificate): @@ -194,12 +194,6 @@ class MQTT(object): # pragma: no cover """ Publish a MQTT message. """ self._mqttc.publish(topic, payload, qos) - def unsubscribe(self, topic): - """ Unsubscribe from topic. """ - result, mid = self._mqttc.unsubscribe(topic) - _raise_on_error(result) - self.userdata['progress'][mid] = topic - def start(self): """ Run the MQTT client. """ self._mqttc.loop_start() @@ -217,6 +211,12 @@ class MQTT(object): # pragma: no cover self.userdata['progress'][mid] = topic self.userdata['topics'][topic] = None + def unsubscribe(self, topic): + """ Unsubscribe from topic. """ + result, mid = self._mqttc.unsubscribe(topic) + _raise_on_error(result) + self.userdata['progress'][mid] = topic + def _mqtt_on_message(mqttc, userdata, msg): """ Message callback """ @@ -236,7 +236,7 @@ def _mqtt_on_connect(mqttc, userdata, flags, result_code): 3: 'Server unavailable', 4: 'Bad username or password', 5: 'Not authorised' - }.get(result_code)) + }.get(result_code, 'Unknown reason')) mqttc.disconnect() return @@ -293,7 +293,7 @@ def _mqtt_on_disconnect(mqttc, userdata, result_code): tries += 1 -def _raise_on_error(result): # pragma: no cover +def _raise_on_error(result): """ Raise error if error result. """ if result != 0: raise HomeAssistantError('Error talking to MQTT: {}'.format(result)) diff --git a/tests/components/test_mqtt.py b/tests/components/test_mqtt.py index 4c3dbb1d20a..47a5ac7b4e1 100644 --- a/tests/components/test_mqtt.py +++ b/tests/components/test_mqtt.py @@ -4,6 +4,7 @@ tests.test_component_mqtt Tests MQTT component. """ +from collections import namedtuple import unittest from unittest import mock import socket @@ -17,8 +18,8 @@ from tests.common import ( get_test_home_assistant, mock_mqtt_component, fire_mqtt_message) -class TestDemo(unittest.TestCase): - """ Test the demo module. """ +class TestMQTT(unittest.TestCase): + """ Test the MQTT module. """ def setUp(self): # pylint: disable=invalid-name self.hass = get_test_home_assistant(1) @@ -136,3 +137,72 @@ class TestDemo(unittest.TestCase): self.hass.pool.block_till_done() self.assertEqual(0, len(self.calls)) + + +class TestMQTTCallbacks(unittest.TestCase): + """ Test the MQTT callbacks. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = get_test_home_assistant(1) + mock_mqtt_component(self.hass) + self.calls = [] + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass.stop() + + def test_receiving_mqtt_message_fires_hass_event(self): + calls = [] + + def record(event): + calls.append(event) + + self.hass.bus.listen_once(mqtt.EVENT_MQTT_MESSAGE_RECEIVED, record) + + MQTTMessage = namedtuple('MQTTMessage', ['topic', 'qos', 'payload']) + message = MQTTMessage('test_topic', 1, 'Hello World!'.encode('utf-8')) + + mqtt._mqtt_on_message(None, {'hass': self.hass}, message) + self.hass.pool.block_till_done() + + self.assertEqual(1, len(calls)) + last_event = calls[0] + self.assertEqual('Hello World!', last_event.data['payload']) + self.assertEqual(message.topic, last_event.data['topic']) + self.assertEqual(message.qos, last_event.data['qos']) + + def test_mqtt_failed_connection_results_in_disconnect(self): + for result_code in range(1, 6): + mqttc = mock.MagicMock() + mqtt._mqtt_on_connect(mqttc, {'topics': {}}, 0, result_code) + self.assertTrue(mqttc.disconnect.called) + + def test_mqtt_subscribes_topics_on_connect(self): + prev_topics = { + 'topic/test': 1, + 'home/sensor': 2, + 'still/pending': None + } + mqttc = mock.MagicMock() + mqtt._mqtt_on_connect(mqttc, {'topics': prev_topics}, 0, 0) + self.assertFalse(mqttc.disconnect.called) + + expected = [(topic, qos) for topic, qos in prev_topics.items() + if qos is not None] + self.assertEqual(expected, [call[1] for call + in mqttc.subscribe.mock_calls]) + + def test_mqtt_disconnect_tries_no_reconnect_on_stop(self): + mqttc = mock.MagicMock() + mqtt._mqtt_on_disconnect(mqttc, {}, 0) + self.assertFalse(mqttc.reconnect.called) + + @mock.patch('homeassistant.components.mqtt.time.sleep') + def test_mqtt_disconnect_tries_reconnect(self, mock_sleep): + mqttc = mock.MagicMock() + mqttc.reconnect.side_effect = [1, 1, 1, 0] + mqtt._mqtt_on_disconnect(mqttc, {}, 1) + self.assertTrue(mqttc.reconnect.called) + self.assertEqual(4, len(mqttc.reconnect.mock_calls)) + self.assertEqual([1, 2, 4], + [call[1][0] for call in mock_sleep.mock_calls]) From 8269e843f2a38ea9bf6da904e7a60d559ee29ba6 Mon Sep 17 00:00:00 2001 From: sfam Date: Mon, 23 Nov 2015 00:22:43 +0000 Subject: [PATCH 037/125] Initial commit for rollershutter component --- .../components/rollershutter/__init__.py | 116 +++++++++++++++++ .../components/rollershutter/mqtt.py | 119 ++++++++++++++++++ .../components/rollershutter/services.yaml | 0 tests/components/rollershutter/test_mqtt.py | 116 +++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 homeassistant/components/rollershutter/__init__.py create mode 100644 homeassistant/components/rollershutter/mqtt.py create mode 100644 homeassistant/components/rollershutter/services.yaml create mode 100644 tests/components/rollershutter/test_mqtt.py diff --git a/homeassistant/components/rollershutter/__init__.py b/homeassistant/components/rollershutter/__init__.py new file mode 100644 index 00000000000..461a34200b2 --- /dev/null +++ b/homeassistant/components/rollershutter/__init__.py @@ -0,0 +1,116 @@ +""" +homeassistant.components.rollershutter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Rollershutter component. + +""" +import os +import logging + +from homeassistant.config import load_yaml_config_file +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import Entity +from homeassistant.components import group +from homeassistant.const import ( + SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_MOVE_STOP, + STATE_OPEN, ATTR_ENTITY_ID) + + +DOMAIN = 'rollershutter' +DEPENDENCIES = [] +SCAN_INTERVAL = 15 + +GROUP_NAME_ALL_ROLLERSHUTTERS = 'all rollershutters' +ENTITY_ID_ALL_ROLLERSHUTTERS = group.ENTITY_ID_FORMAT.format( + 'all_rollershutters') + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + +# Maps discovered services to their platforms +DISCOVERY_PLATFORMS = {} + +_LOGGER = logging.getLogger(__name__) + + +def is_open(hass, entity_id=None): + """ Returns if the rollershutter is open based on the statemachine. """ + entity_id = entity_id or ENTITY_ID_ALL_ROLLERSHUTTERS + return hass.states.is_state(entity_id, STATE_OPEN) + + +def move_up(hass, entity_id=None): + """ Moves all or specified rollershutter up. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_MOVE_UP, data) + + +def move_down(hass, entity_id=None): + """ Moves all or specified rollershutter down. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_MOVE_DOWN, data) + + +def move_stop(hass, entity_id=None): + """ Stops all or specified rollershutter. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_MOVE_STOP, data) + + +def setup(hass, config): + """ Track states and offer events for rollershutters. """ + component = EntityComponent( + _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, + GROUP_NAME_ALL_ROLLERSHUTTERS) + component.setup(config) + + def handle_rollershutter_service(service): + """ Handles calls to the rollershutter services. """ + target_rollershutters = component.extract_from_service(service) + + for rollershutter in target_rollershutters: + if service.service == SERVICE_MOVE_UP: + rollershutter.move_up() + elif service.service == SERVICE_MOVE_DOWN: + rollershutter.move_down() + elif service.service == SERVICE_MOVE_STOP: + rollershutter.move_stop() + + if rollershutter.should_poll: + rollershutter.update_ha_state(True) + + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + + hass.services.register(DOMAIN, SERVICE_MOVE_UP, + handle_rollershutter_service, + descriptions.get(SERVICE_MOVE_UP)) + hass.services.register(DOMAIN, SERVICE_MOVE_DOWN, + handle_rollershutter_service, + descriptions.get(SERVICE_MOVE_DOWN)) + hass.services.register(DOMAIN, SERVICE_MOVE_STOP, + handle_rollershutter_service, + descriptions.get(SERVICE_MOVE_STOP)) + + return True + + +class RollershutterDevice(Entity): + """ Represents a rollershutter within Home Assistant. """ + # pylint: disable=no-self-use + + @property + def state_attributes(self): + """ Returns optional state attributes. """ + return None + + def move_up(self, **kwargs): + """ Moves the device UP. """ + raise NotImplementedError() + + def move_down(self, **kwargs): + """ Moves the device DOWN. """ + raise NotImplementedError() + + def move_stop(self, **kwargs): + """ Moves the device to STOP. """ + raise NotImplementedError() diff --git a/homeassistant/components/rollershutter/mqtt.py b/homeassistant/components/rollershutter/mqtt.py new file mode 100644 index 00000000000..ad5a4346bc9 --- /dev/null +++ b/homeassistant/components/rollershutter/mqtt.py @@ -0,0 +1,119 @@ +""" +homeassistant.components.rollershutter.mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a MQTT rollershutter. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/rollershutter.mqtt/ +""" +import logging +import homeassistant.components.mqtt as mqtt +from homeassistant.components.rollershutter import RollershutterDevice +from homeassistant.const import (STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN) +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['mqtt'] + +DEFAULT_NAME = "MQTT Shutter" +DEFAULT_QOS = 0 +DEFAULT_PAYLOAD_UP = "UP" +DEFAULT_PAYLOAD_DOWN = "DOWN" +DEFAULT_PAYLOAD_STOP = "STOP" + +ATTR_CURRENT_POSITION = 'current_position' + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Add MQTT Roller Shutter """ + + if config.get('command_topic') is None: + _LOGGER.error("Missing required variable: command_topic") + return False + + add_devices_callback([MqttRollershutter( + hass, + config.get('name', DEFAULT_NAME), + config.get('state_topic'), + config.get('command_topic'), + config.get('qos', DEFAULT_QOS), + config.get('payload_up', DEFAULT_PAYLOAD_UP), + config.get('payload_down', DEFAULT_PAYLOAD_DOWN), + config.get('payload_stop', DEFAULT_PAYLOAD_STOP), + config.get('state_format'))]) + + +# pylint: disable=too-many-arguments, too-many-instance-attributes +class MqttRollershutter(RollershutterDevice): + """ Represents a rollershutter that can be togggled using MQTT """ + def __init__(self, hass, name, state_topic, command_topic, qos, + payload_up, payload_down, payload_stop, state_format): + self._state = -1 + self._hass = hass + self._name = name + self._state_topic = state_topic + self._command_topic = command_topic + self._qos = qos + self._payload_up = payload_up + self._payload_down = payload_down + self._payload_stop = payload_stop + self._parse = mqtt.FmtParser(state_format) + + if self._state_topic: + def message_received(topic, payload, qos): + """ A new MQTT message has been received. """ + value = self._parse(payload) + if value.isnumeric(): + if 0 <= int(value) <= 100: + self._state = int(value) + self.update_ha_state() + + mqtt.subscribe(hass, self._state_topic, message_received, + self._qos) + + @property + def should_poll(self): + """ No polling needed """ + return False + + @property + def name(self): + """ The name of the rollershutter """ + return self._name + + @property + def state(self): + """ Returns the state of the device. """ + if self._state == -1: + return STATE_UNKNOWN + elif self._state == 0: + return STATE_CLOSED + else: + return STATE_OPEN + + @property + def is_open(self): + """ True if device is open. """ + return self.state == STATE_OPEN + + def move_up(self, **kwargs): + """ Moves the device UP. """ + mqtt.publish(self.hass, self._command_topic, self._payload_up, + self._qos) + + def move_down(self, **kwargs): + """ Moves the device DOWN. """ + mqtt.publish(self.hass, self._command_topic, self._payload_down, + self._qos) + + def move_stop(self, **kwargs): + """ Moves the device to STOP. """ + mqtt.publish(self.hass, self._command_topic, self._payload_stop, + self._qos) + + @property + def state_attributes(self): + """ Return the state attributes. """ + state_attr = { + ATTR_CURRENT_POSITION: self._state, + } + return state_attr diff --git a/homeassistant/components/rollershutter/services.yaml b/homeassistant/components/rollershutter/services.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/components/rollershutter/test_mqtt.py b/tests/components/rollershutter/test_mqtt.py new file mode 100644 index 00000000000..099d8fad1ea --- /dev/null +++ b/tests/components/rollershutter/test_mqtt.py @@ -0,0 +1,116 @@ +""" +tests.components.rollershutter.test_mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests mqtt rollershutter. +""" +import unittest + +from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN +import homeassistant.core as ha +import homeassistant.components.rollershutter as rollershutter +from tests.common import mock_mqtt_component, fire_mqtt_message + + +class TestRollershutterMQTT(unittest.TestCase): + """ Test the MQTT rollershutter. """ + + 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(rollershutter.setup(self.hass, { + 'rollershutter': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'qos': 0, + 'payload_up': 'UP', + 'payload_down': 'DOWN', + 'payload_stop': 'STOP' + } + })) + + state = self.hass.states.get('rollershutter.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '0') + self.hass.pool.block_till_done() + + state = self.hass.states.get('rollershutter.test') + self.assertEqual(STATE_CLOSED, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '50') + self.hass.pool.block_till_done() + + state = self.hass.states.get('rollershutter.test') + self.assertEqual(STATE_OPEN, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '100') + self.hass.pool.block_till_done() + + state = self.hass.states.get('rollershutter.test') + self.assertEqual(STATE_OPEN, state.state) + + def test_sending_mqtt_commands(self): + self.assertTrue(rollershutter.setup(self.hass, { + 'rollershutter': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'qos': 2 + } + })) + + state = self.hass.states.get('rollershutter.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + rollershutter.move_up(self.hass, 'rollershutter.test') + self.hass.pool.block_till_done() + + self.assertEqual(('command-topic', 'UP', 2), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('rollershutter.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + def test_state_attributes_current_position(self): + self.assertTrue(rollershutter.setup(self.hass, { + 'rollershutter': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'payload_up': 'UP', + 'payload_down': 'DOWN', + 'payload_stop': 'STOP' + } + })) + + current_position = self.hass.states.get( + 'rollershutter.test').attributes['current_position'] + self.assertEqual(-1, current_position) + + fire_mqtt_message(self.hass, 'state-topic', '0') + self.hass.pool.block_till_done() + current_position = self.hass.states.get( + 'rollershutter.test').attributes['current_position'] + self.assertEqual(0, current_position) + + fire_mqtt_message(self.hass, 'state-topic', '50') + self.hass.pool.block_till_done() + current_position = self.hass.states.get( + 'rollershutter.test').attributes['current_position'] + self.assertEqual(50, current_position) + + fire_mqtt_message(self.hass, 'state-topic', '101') + self.hass.pool.block_till_done() + current_position = self.hass.states.get( + 'rollershutter.test').attributes['current_position'] + self.assertEqual(50, current_position) From d4b3af327d0dfab571df7c411e26f95a4612fa2f Mon Sep 17 00:00:00 2001 From: sfam Date: Mon, 23 Nov 2015 00:25:10 +0000 Subject: [PATCH 038/125] Initial commit for rollershutter component --- homeassistant/const.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 5b0b5a5e214..047f66ef924 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -53,8 +53,6 @@ STATE_ALARM_ARMED_HOME = 'armed_home' STATE_ALARM_ARMED_AWAY = 'armed_away' STATE_ALARM_PENDING = 'pending' STATE_ALARM_TRIGGERED = 'triggered' -STATE_LOCKED = 'locked' -STATE_UNLOCKED = 'unlocked' # #### STATE AND EVENT ATTRIBUTES #### # Contains current time for a TIME_CHANGED event @@ -98,9 +96,6 @@ ATTR_BATTERY_LEVEL = "battery_level" # For devices which support an armed state ATTR_ARMED = "device_armed" -# For devices which support a locked state -ATTR_LOCKED = "locked" - # For sensors that support 'tripping', eg. motion and door sensors ATTR_TRIPPED = "device_tripped" @@ -140,8 +135,9 @@ SERVICE_ALARM_ARM_HOME = "alarm_arm_home" SERVICE_ALARM_ARM_AWAY = "alarm_arm_away" SERVICE_ALARM_TRIGGER = "alarm_trigger" -SERVICE_LOCK = "lock" -SERVICE_UNLOCK = "unlock" +SERVICE_MOVE_UP = 'move_up' +SERVICE_MOVE_DOWN = 'move_down' +SERVICE_MOVE_STOP = 'move_stop' # #### API / REMOTE #### SERVER_PORT = 8123 From 68ff9dd74f61fce99adfeaba3dc4519bbc0dc997 Mon Sep 17 00:00:00 2001 From: sfam Date: Mon, 23 Nov 2015 00:35:22 +0000 Subject: [PATCH 039/125] rollershutter component - fix const --- homeassistant/const.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/homeassistant/const.py b/homeassistant/const.py index 047f66ef924..add1848b72c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -53,6 +53,8 @@ STATE_ALARM_ARMED_HOME = 'armed_home' STATE_ALARM_ARMED_AWAY = 'armed_away' STATE_ALARM_PENDING = 'pending' STATE_ALARM_TRIGGERED = 'triggered' +STATE_LOCKED = 'locked' +STATE_UNLOCKED = 'unlocked' # #### STATE AND EVENT ATTRIBUTES #### # Contains current time for a TIME_CHANGED event @@ -96,6 +98,9 @@ ATTR_BATTERY_LEVEL = "battery_level" # For devices which support an armed state ATTR_ARMED = "device_armed" +# For devices which support a locked state +ATTR_LOCKED = "locked" + # For sensors that support 'tripping', eg. motion and door sensors ATTR_TRIPPED = "device_tripped" @@ -135,6 +140,9 @@ SERVICE_ALARM_ARM_HOME = "alarm_arm_home" SERVICE_ALARM_ARM_AWAY = "alarm_arm_away" SERVICE_ALARM_TRIGGER = "alarm_trigger" +SERVICE_LOCK = "lock" +SERVICE_UNLOCK = "unlock" + SERVICE_MOVE_UP = 'move_up' SERVICE_MOVE_DOWN = 'move_down' SERVICE_MOVE_STOP = 'move_stop' From 09a82dedf058a7a44105ea577a7211bbf3c5242e Mon Sep 17 00:00:00 2001 From: Allan Glen Date: Mon, 23 Nov 2015 11:57:46 -0700 Subject: [PATCH 040/125] Re-order Dockerfile for faster rebuild on code changes --- Dockerfile | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9344ec65245..82e334504d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,27 @@ -FROM python:3-onbuild +FROM python:3 MAINTAINER Paulus Schoutsen VOLUME /config -RUN pip3 install --no-cache-dir -r requirements_all.txt +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app # For the nmap tracker RUN apt-get update && \ apt-get install -y --no-install-recommends nmap net-tools && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +COPY script/build_python_openzwave script/build_python_openzwave RUN apt-get update && \ apt-get install -y cython3 libudev-dev && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ pip3 install "cython<0.23" && \ script/build_python_openzwave +COPY requirements_all.txt requirements_all.txt +RUN pip3 install --no-cache-dir -r requirements_all.txt + +# Copy source +COPY . . + CMD [ "python", "-m", "homeassistant", "--config", "/config" ] From 0d3a099926f37b36ac34147a4f8cd921fd08c9a8 Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Mon, 23 Nov 2015 21:46:47 +0100 Subject: [PATCH 041/125] arest: fix order argument order of log messages --- homeassistant/components/switch/arest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/arest.py b/homeassistant/components/switch/arest.py index c7bf1b34e4a..92bd70cb734 100644 --- a/homeassistant/components/switch/arest.py +++ b/homeassistant/components/switch/arest.py @@ -76,7 +76,7 @@ class ArestSwitch(SwitchDevice): self._state = True else: _LOGGER.error("Can't turn on pin %s at %s. Is device offline?", - self._resource, self._pin) + self._pin, self._resource) def turn_off(self, **kwargs): """ Turn the device off. """ @@ -86,7 +86,7 @@ class ArestSwitch(SwitchDevice): self._state = False else: _LOGGER.error("Can't turn off pin %s at %s. Is device offline?", - self._resource, self._pin) + self._pin, self._resource) def update(self): """ Gets the latest data from aREST API and updates the state. """ From d141306493e9a206dc7d8ce57cdb572706e5a991 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 23 Nov 2015 20:42:32 -0800 Subject: [PATCH 042/125] Update Dockerfile to depend on Python 3.4 instead of latest --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 82e334504d5..a1f9d459295 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3 +FROM python:3.4 MAINTAINER Paulus Schoutsen VOLUME /config From e001ea913a6132571a1b79cad50e07ac6d288190 Mon Sep 17 00:00:00 2001 From: sfam Date: Tue, 24 Nov 2015 08:03:02 +0000 Subject: [PATCH 043/125] add __init__.py to test folder --- tests/components/alarm_control_panel/__init__.py | 0 tests/components/rollershutter/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/components/alarm_control_panel/__init__.py create mode 100644 tests/components/rollershutter/__init__.py diff --git a/tests/components/alarm_control_panel/__init__.py b/tests/components/alarm_control_panel/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/components/rollershutter/__init__.py b/tests/components/rollershutter/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 351430c1b38415ec790701b7c303d2a8ad1d3a04 Mon Sep 17 00:00:00 2001 From: sfam Date: Tue, 24 Nov 2015 10:41:39 +0000 Subject: [PATCH 044/125] move current_position to RollershutterDevice class --- .../components/rollershutter/__init__.py | 17 ++++++- .../components/rollershutter/mqtt.py | 46 +++++++++---------- tests/components/rollershutter/test_mqtt.py | 12 +++-- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/rollershutter/__init__.py b/homeassistant/components/rollershutter/__init__.py index 461a34200b2..5ebcf8a3d21 100644 --- a/homeassistant/components/rollershutter/__init__.py +++ b/homeassistant/components/rollershutter/__init__.py @@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.components import group from homeassistant.const import ( SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_MOVE_STOP, - STATE_OPEN, ATTR_ENTITY_ID) + STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN, ATTR_ENTITY_ID) DOMAIN = 'rollershutter' @@ -98,6 +98,21 @@ class RollershutterDevice(Entity): """ Represents a rollershutter within Home Assistant. """ # pylint: disable=no-self-use + @property + def current_position(self): + """ Return current position of rollershutter. + None is unknown, 0 is closed, 100 is fully open. """ + raise NotImplementedError() + + @property + def state(self): + current = self.current_position + + if current is None: + return STATE_UNKNOWN + + return STATE_CLOSED if current == 0 else STATE_OPEN + @property def state_attributes(self): """ Returns optional state attributes. """ diff --git a/homeassistant/components/rollershutter/mqtt.py b/homeassistant/components/rollershutter/mqtt.py index ad5a4346bc9..6128a4d9819 100644 --- a/homeassistant/components/rollershutter/mqtt.py +++ b/homeassistant/components/rollershutter/mqtt.py @@ -8,7 +8,6 @@ https://home-assistant.io/components/rollershutter.mqtt/ import logging import homeassistant.components.mqtt as mqtt from homeassistant.components.rollershutter import RollershutterDevice -from homeassistant.const import (STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN) _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] @@ -47,7 +46,7 @@ class MqttRollershutter(RollershutterDevice): """ Represents a rollershutter that can be togggled using MQTT """ def __init__(self, hass, name, state_topic, command_topic, qos, payload_up, payload_down, payload_stop, state_format): - self._state = -1 + self._state = None self._hass = hass self._name = name self._state_topic = state_topic @@ -58,17 +57,20 @@ class MqttRollershutter(RollershutterDevice): self._payload_stop = payload_stop self._parse = mqtt.FmtParser(state_format) - if self._state_topic: - def message_received(topic, payload, qos): - """ A new MQTT message has been received. """ - value = self._parse(payload) - if value.isnumeric(): - if 0 <= int(value) <= 100: - self._state = int(value) - self.update_ha_state() + if self._state_topic is None: + return - mqtt.subscribe(hass, self._state_topic, message_received, - self._qos) + def message_received(topic, payload, qos): + """ A new MQTT message has been received. """ + value = self._parse(payload) + if value.isnumeric() and 0 <= int(value) <= 100: + self._state = int(value) + self.update_ha_state() + else: + _LOGGER.warning( + "Payload is expected to be an integer between 0 and 100") + + mqtt.subscribe(hass, self._state_topic, message_received, self._qos) @property def should_poll(self): @@ -81,19 +83,15 @@ class MqttRollershutter(RollershutterDevice): return self._name @property - def state(self): - """ Returns the state of the device. """ - if self._state == -1: - return STATE_UNKNOWN - elif self._state == 0: - return STATE_CLOSED - else: - return STATE_OPEN + def current_position(self): + """ Return current position of rollershutter. + None is unknown, 0 is closed, 100 is fully open. """ + return self._state @property def is_open(self): """ True if device is open. """ - return self.state == STATE_OPEN + return self._state > 0 def move_up(self, **kwargs): """ Moves the device UP. """ @@ -113,7 +111,7 @@ class MqttRollershutter(RollershutterDevice): @property def state_attributes(self): """ Return the state attributes. """ - state_attr = { - ATTR_CURRENT_POSITION: self._state, - } + state_attr = {} + if self._state is not None: + state_attr[ATTR_CURRENT_POSITION] = self._state return state_attr diff --git a/tests/components/rollershutter/test_mqtt.py b/tests/components/rollershutter/test_mqtt.py index 099d8fad1ea..2a71163c50f 100644 --- a/tests/components/rollershutter/test_mqtt.py +++ b/tests/components/rollershutter/test_mqtt.py @@ -93,9 +93,9 @@ class TestRollershutterMQTT(unittest.TestCase): } })) - current_position = self.hass.states.get( - 'rollershutter.test').attributes['current_position'] - self.assertEqual(-1, current_position) + state_attributes_dict = self.hass.states.get( + 'rollershutter.test').attributes + self.assertFalse('current_position' in state_attributes_dict) fire_mqtt_message(self.hass, 'state-topic', '0') self.hass.pool.block_till_done() @@ -114,3 +114,9 @@ class TestRollershutterMQTT(unittest.TestCase): current_position = self.hass.states.get( 'rollershutter.test').attributes['current_position'] self.assertEqual(50, current_position) + + fire_mqtt_message(self.hass, 'state-topic', 'non-numeric') + self.hass.pool.block_till_done() + current_position = self.hass.states.get( + 'rollershutter.test').attributes['current_position'] + self.assertEqual(50, current_position) From b0fa80ad4c5b84f77432c6fb06253383a7694571 Mon Sep 17 00:00:00 2001 From: Goir Date: Mon, 23 Nov 2015 19:52:08 +0100 Subject: [PATCH 045/125] Added support for Homematic thermostat --- .coveragerc | 1 + .../components/thermostat/homematic.py | 141 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 homeassistant/components/thermostat/homematic.py diff --git a/.coveragerc b/.coveragerc index 07d4a8dd4bc..8efc162b3eb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -103,6 +103,7 @@ omit = homeassistant/components/switch/rpi_gpio.py homeassistant/components/switch/transmission.py homeassistant/components/switch/wemo.py + homeassistant/components/thermostat/homematic.py homeassistant/components/thermostat/honeywell.py homeassistant/components/thermostat/nest.py homeassistant/components/thermostat/radiotherm.py diff --git a/homeassistant/components/thermostat/homematic.py b/homeassistant/components/thermostat/homematic.py new file mode 100644 index 00000000000..aea3e34f8c8 --- /dev/null +++ b/homeassistant/components/thermostat/homematic.py @@ -0,0 +1,141 @@ +""" +homeassistant.components.thermostat.homematic +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Adds support for Homematic (HM-TC-IT-WM-W-EU, HM-CC-RT-DN) +thermostats using Homegear or Homematic central (CCU1/CCU2). + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/thermostat.homematic/ + +Configuration example: + +thermostat: + platform: homematic + address: HOMEGEAR/CCU ADDRESS (e.g. http://localhost:2001) + devices: + Livingroom 1: + id: DEVICE_SERIAL_NO (e.g. MEQ0791521) + Livingroom 2: + id: DEVICE_SERIAL_NO + +""" +import logging +import socket +from xmlrpc.client import ServerProxy + +from homeassistant.components.thermostat import ThermostatDevice +from homeassistant.const import TEMP_CELCIUS + +REQUIREMENTS = [] + +CONF_ADDRESS = 'address' +CONF_DEVICES = 'devices' +CONF_ID = 'id' +PROPERTY_SET_TEMPERATURE = 'SET_TEMPERATURE' +PROPERTY_VALVE_STATE = 'VALVE_STATE' +PROPERTY_ACTUAL_TEMPERATURE = 'ACTUAL_TEMPERATURE' +PROPERTY_BATTERY_STATE = 'BATTERY_STATE' +PROPERTY_CONTROL_MODE = 'CONTROL_MODE' + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Homematic thermostat. """ + + devices = [] + try: + homegear = ServerProxy(config[CONF_ADDRESS]) + for name, device_cfg in config[CONF_DEVICES].items(): + # get device description to detect the type + device_type = homegear.getDeviceDescription( + device_cfg[CONF_ID] + ':-1')['TYPE'] + + if device_type in ['HM-CC-RT-DN', 'HM-CC-RT-DN-BoM']: + devices.append(HomematicThermostat(homegear, + device_cfg[CONF_ID], + name, 4)) + elif device_type == 'HM-TC-IT-WM-W-EU': + devices.append(HomematicThermostat(homegear, + device_cfg[CONF_ID], + name, 2)) + else: + raise ValueError( + "Device Type '{}' currently not supported".format( + device_type)) + except socket.error: + _LOGGER.exception("Connection error to homematic web service") + return False + + add_devices(devices) + + return True + + +# pylint: disable=too-many-instance-attributes +class HomematicThermostat(ThermostatDevice): + """ Represents a Homematic thermostat. """ + + def __init__(self, device, _id, name, channel): + self.device = device + self._id = _id + self._channel = channel + self._name = name + self._full_device_name = '{}:{}'.format(self._id, self._channel) + + self._current_temperature = None + self._target_temperature = None + self._valve = None + self._battery = None + self._mode = None + self.update() + + @property + def name(self): + """ Returns the name of the Homematic device. """ + return self._name + + @property + def unit_of_measurement(self): + """ Unit of measurement this thermostat expresses itself in. """ + return TEMP_CELCIUS + + @property + def current_temperature(self): + """ Returns the current temperature. """ + return self._current_temperature + + @property + def target_temperature(self): + """ Returns the temperature we try to reach. """ + return self._target_temperature + + def set_temperature(self, temperature): + """ Set new target temperature """ + self.device.setValue(self._full_device_name, + PROPERTY_SET_TEMPERATURE, + temperature) + + @property + def device_state_attributes(self): + return {"valve": self._valve, + "battery": self._battery, + "mode": self._mode} + + def update(self): + try: + self._current_temperature = self.device.getValue( + self._full_device_name, + PROPERTY_ACTUAL_TEMPERATURE) + self._target_temperature = self.device.getValue( + self._full_device_name, + PROPERTY_SET_TEMPERATURE) + self._valve = self.device.getValue(self._full_device_name, + PROPERTY_VALVE_STATE) + self._battery = self.device.getValue(self._full_device_name, + PROPERTY_BATTERY_STATE) + self._mode = self.device.getValue(self._full_device_name, + PROPERTY_CONTROL_MODE) + except socket.error: + _LOGGER.exception("Did not receive any temperature data from the " + "homematic API.") From bc106bcb108476eb1e575854b3b2ea6374c427ca Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 08:48:27 +0100 Subject: [PATCH 046/125] Move configuration details to docs --- .../components/thermostat/homematic.py | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/thermostat/homematic.py b/homeassistant/components/thermostat/homematic.py index aea3e34f8c8..ab81e368589 100644 --- a/homeassistant/components/thermostat/homematic.py +++ b/homeassistant/components/thermostat/homematic.py @@ -1,23 +1,11 @@ """ homeassistant.components.thermostat.homematic ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Adds support for Homematic (HM-TC-IT-WM-W-EU, HM-CC-RT-DN) -thermostats using Homegear or Homematic central (CCU1/CCU2). +Adds support for Homematic (HM-TC-IT-WM-W-EU, HM-CC-RT-DN) thermostats using +Homegear or Homematic central (CCU1/CCU2). For more details about this platform, please refer to the documentation at https://home-assistant.io/components/thermostat.homematic/ - -Configuration example: - -thermostat: - platform: homematic - address: HOMEGEAR/CCU ADDRESS (e.g. http://localhost:2001) - devices: - Livingroom 1: - id: DEVICE_SERIAL_NO (e.g. MEQ0791521) - Livingroom 2: - id: DEVICE_SERIAL_NO - """ import logging import socket @@ -111,18 +99,20 @@ class HomematicThermostat(ThermostatDevice): return self._target_temperature def set_temperature(self, temperature): - """ Set new target temperature """ + """ Set new target temperature. """ self.device.setValue(self._full_device_name, PROPERTY_SET_TEMPERATURE, temperature) @property def device_state_attributes(self): + """ Returns device specific state attributes. """ return {"valve": self._valve, "battery": self._battery, "mode": self._mode} def update(self): + """ Update the data from the thermostat. """ try: self._current_temperature = self.device.getValue( self._full_device_name, From 3e60c4801c74f02fe8257c9cd9815b191c9017f6 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 08:56:50 +0100 Subject: [PATCH 047/125] Update docstrings --- homeassistant/components/ifttt.py | 4 +-- homeassistant/components/mqtt/__init__.py | 8 +++--- homeassistant/components/notify/pushbullet.py | 10 ++++---- homeassistant/components/recorder.py | 25 +++++++++++-------- homeassistant/components/switch/mqtt.py | 2 +- .../components/thermostat/honeywell.py | 1 + .../components/thermostat/radiotherm.py | 1 + homeassistant/components/zwave.py | 4 +-- 8 files changed, 30 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/ifttt.py b/homeassistant/components/ifttt.py index 246265a5268..da1b377b078 100644 --- a/homeassistant/components/ifttt.py +++ b/homeassistant/components/ifttt.py @@ -28,7 +28,7 @@ REQUIREMENTS = ['pyfttt==0.3'] def trigger(hass, event, value1=None, value2=None, value3=None): - """ Trigger a Maker IFTTT recipe """ + """ Trigger a Maker IFTTT recipe. """ data = { ATTR_EVENT: event, ATTR_VALUE1: value1, @@ -39,7 +39,7 @@ def trigger(hass, event, value1=None, value2=None, value3=None): def setup(hass, config): - """ Setup the ifttt service component """ + """ Setup the ifttt service component. """ if not validate_config(config, {DOMAIN: ['key']}, _LOGGER): return False diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 7f4ff030d36..b8dc8909887 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -132,7 +132,7 @@ def setup(hass, config): # pylint: disable=too-few-public-methods class _JsonFmtParser(object): - """ Implements a json parser on xpath""" + """ Implements a json parser on xpath. """ def __init__(self, jsonpath): import jsonpath_rw self._expr = jsonpath_rw.parse(jsonpath) @@ -144,7 +144,7 @@ class _JsonFmtParser(object): # pylint: disable=too-few-public-methods class FmtParser(object): - """ wrapper for all supported formats """ + """ Wrapper for all supported formats. """ def __init__(self, fmt): self._parse = lambda x: x if fmt: @@ -252,7 +252,7 @@ def _mqtt_on_connect(mqttc, userdata, flags, result_code): def _mqtt_on_subscribe(mqttc, userdata, mid, granted_qos): - """ Called when subscribe successfull. """ + """ Called when subscribe successful. """ topic = userdata['progress'].pop(mid, None) if topic is None: return @@ -260,7 +260,7 @@ def _mqtt_on_subscribe(mqttc, userdata, mid, granted_qos): def _mqtt_on_unsubscribe(mqttc, userdata, mid, granted_qos): - """ Called when subscribe successfull. """ + """ Called when subscribe successful. """ topic = userdata['progress'].pop(mid, None) if topic is None: return diff --git a/homeassistant/components/notify/pushbullet.py b/homeassistant/components/notify/pushbullet.py index 16c4e4192eb..5dc97a399b5 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/notify/pushbullet.py @@ -47,16 +47,16 @@ class PushBulletNotificationService(BaseNotificationService): self.refresh() def refresh(self): - ''' + """ Refresh devices, contacts, etc pbtargets stores all targets available from this pushbullet instance into a dict. These are PB objects!. It sacrifices a bit of memory - for faster processing at send_message + for faster processing at send_message. As of sept 2015, contacts were replaced by chats. This is not - implemented in the module yet - ''' + implemented in the module yet. + """ self.pushbullet.refresh() self.pbtargets = { 'device': { @@ -72,7 +72,7 @@ class PushBulletNotificationService(BaseNotificationService): If no target specified, a 'normal' push will be sent to all devices linked to the PB account. Email is special, these are assumed to always exist. We use a special - call which doesn't require a push object + call which doesn't require a push object. """ targets = kwargs.get(ATTR_TARGET) title = kwargs.get(ATTR_TITLE) diff --git a/homeassistant/components/recorder.py b/homeassistant/components/recorder.py index b09e10f7d92..f654f035857 100644 --- a/homeassistant/components/recorder.py +++ b/homeassistant/components/recorder.py @@ -59,7 +59,7 @@ def query_events(event_query, arguments=None): def row_to_state(row): - """ Convert a databsae row to a state. """ + """ Convert a database row to a state. """ try: return State( row[1], row[2], json.loads(row[3]), @@ -83,8 +83,9 @@ def row_to_event(row): def run_information(point_in_time=None): - """ Returns information about current run or the run that - covers point_in_time. """ + """ + Returns information about current run or the run that covers point_in_time. + """ _verify_instance() if point_in_time is None or point_in_time > _INSTANCE.recording_start: @@ -142,8 +143,10 @@ class RecorderRun(object): @property def where_after_start_run(self): - """ Returns SQL WHERE clause to select rows - created after the start of the run. """ + """ + Returns SQL WHERE clause to select rows created after the start of the + run. + """ return "created >= {} ".format(_adapt_datetime(self.start)) @property @@ -158,9 +161,7 @@ class RecorderRun(object): class Recorder(threading.Thread): - """ - Threaded recorder - """ + """ Threaded recorder class """ def __init__(self, hass): threading.Thread.__init__(self) @@ -208,8 +209,10 @@ class Recorder(threading.Thread): self.queue.task_done() def event_listener(self, event): - """ Listens for new events on the EventBus and puts them - in the process queue. """ + """ + Listens for new events on the EventBus and puts them in the process + queue. + """ self.queue.put(event) def shutdown(self, event): @@ -433,6 +436,6 @@ def _adapt_datetime(datetimestamp): def _verify_instance(): - """ throws error if recorder not initialized. """ + """ Throws error if recorder not initialized. """ if _INSTANCE is None: raise RuntimeError("Recorder not initialized.") diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index ed99a87868a..43bceab69a5 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -43,7 +43,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSwitch(SwitchDevice): - """ Represents a switch that can be togggled using MQTT. """ + """ Represents a switch that can be toggled using MQTT. """ def __init__(self, hass, name, state_topic, command_topic, qos, payload_on, payload_off, optimistic, state_format): self._state = False diff --git a/homeassistant/components/thermostat/honeywell.py b/homeassistant/components/thermostat/honeywell.py index 56880c4eded..2d724502d85 100644 --- a/homeassistant/components/thermostat/honeywell.py +++ b/homeassistant/components/thermostat/honeywell.py @@ -74,6 +74,7 @@ class RoundThermostat(ThermostatDevice): self.device.set_temperature(self._name, temperature) def update(self): + """ Update the data from the thermostat. """ try: # Only take first thermostat data from API for now data = next(self.device.temperatures(force_refresh=True)) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 748d0421acd..051a2a6413e 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -101,6 +101,7 @@ class RadioThermostat(ThermostatDevice): return round(self._target_temperature, 1) def update(self): + """ Update the data from the thermostat. """ self._current_temperature = self.device.temp['raw'] self._name = self.device.name['raw'] if self.device.tmode['human'] == 'Cool': diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 11515e4031d..e21c339fb23 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -83,7 +83,7 @@ def _obj_to_dict(obj): def nice_print_node(node): - """ Prints a nice formatted node to the output (debug method) """ + """ Prints a nice formatted node to the output (debug method). """ node_dict = _obj_to_dict(node) node_dict['values'] = {value_id: _obj_to_dict(value) for value_id, value in node.values.items()} @@ -95,7 +95,7 @@ def nice_print_node(node): def get_config_value(node, value_index): - """ Returns the current config value for a specific index """ + """ Returns the current config value for a specific index. """ try: for value in node.values.values(): From a8ddae9343683ca69067eecbece5ecff6d4e5d1d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 17:21:11 +0100 Subject: [PATCH 048/125] Add myStrom switch platform --- homeassistant/components/switch/mystrom.py | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 homeassistant/components/switch/mystrom.py diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/switch/mystrom.py new file mode 100644 index 00000000000..86cd6686f1e --- /dev/null +++ b/homeassistant/components/switch/mystrom.py @@ -0,0 +1,103 @@ +""" +homeassistant.components.switch.mystrom +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for myStrom switches. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/switch.mystrom/ +""" +import logging +import requests + +from homeassistant.components.switch import SwitchDevice +from homeassistant.const import STATE_UNKNOWN + +DEFAULT_NAME = 'myStrom Switch' + +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=unused-argument, too-many-function-args +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Find and return myStrom switches. """ + resource = config.get('resource') + + if resource is None: + _LOGGER.error('Missing required variable: resource') + return False + + try: + requests.get(resource, timeout=10) + except requests.exceptions.MissingSchema: + _LOGGER.error("Missing resource or schema in configuration. " + "Add http:// to your URL.") + return False + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to device. " + "Please check the IP address in the configuration file.") + return False + + add_devices([MyStromSwitch( + config.get('name', DEFAULT_NAME), + config.get('resource'))]) + + +class MyStromSwitch(SwitchDevice): + """ Represents a myStrom switch. """ + def __init__(self, name, resource): + self._state = STATE_UNKNOWN + self._name = name + self._resource = resource + self.consumption = 0 + + @property + def name(self): + """ The name of the switch. """ + return self._name + + @property + def is_on(self): + """ True if switch is on. """ + return self._state + + @property + def current_power_mwh(self): + """ Current power consumption in mwh. """ + return self.consumption + + def turn_on(self, **kwargs): + """ Turn the switch on. """ + request = requests.get('{}/relay'.format(self._resource), + params={'state': '1'}, + timeout=10) + if request.status_code == 200: + self._state = True + else: + _LOGGER.error("Can't turn on %s. Is device offline?", + self._resource) + + def turn_off(self, **kwargs): + """ Turn the switch off. """ + request = requests.get('{}/relay'.format(self._resource), + params={'state': '0'}, + timeout=10) + if request.status_code == 200: + self._state = False + else: + _LOGGER.error("Can't turn off %s. Is device offline?", + self._resource) + + def update(self): + """ Gets the latest data from REST API and updates the state. """ + try: + request = requests.get('{}/report'.format(self._resource), + timeout=10) + if request.json()['relay'] is True: + self._state = True + else: + self._state = False + + self.consumption = request.json()['power'] + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to device '%s'. Is device offline?", + self._resource) From 9dfa4ea2339c0ff4303ddbd8540d56984f918b02 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 17:21:31 +0100 Subject: [PATCH 049/125] Add mystrom switch --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 8efc162b3eb..22e8116693c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -98,6 +98,7 @@ omit = homeassistant/components/switch/command_switch.py homeassistant/components/switch/edimax.py homeassistant/components/switch/hikvisioncam.py + homeassistant/components/switch/mystrom.py homeassistant/components/switch/orvibo.py homeassistant/components/switch/rest.py homeassistant/components/switch/rpi_gpio.py From e45ef0013b3c7f551e84200c5f12e3b5b5d2229e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 18:07:29 +0100 Subject: [PATCH 050/125] Catch connection timeout --- homeassistant/components/switch/mystrom.py | 37 +++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/switch/mystrom.py index 86cd6686f1e..48f47b41ed6 100644 --- a/homeassistant/components/switch/mystrom.py +++ b/homeassistant/components/switch/mystrom.py @@ -10,7 +10,6 @@ import logging import requests from homeassistant.components.switch import SwitchDevice -from homeassistant.const import STATE_UNKNOWN DEFAULT_NAME = 'myStrom Switch' @@ -45,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class MyStromSwitch(SwitchDevice): """ Represents a myStrom switch. """ def __init__(self, name, resource): - self._state = STATE_UNKNOWN + self._state = False self._name = name self._resource = resource self.consumption = 0 @@ -67,24 +66,32 @@ class MyStromSwitch(SwitchDevice): def turn_on(self, **kwargs): """ Turn the switch on. """ - request = requests.get('{}/relay'.format(self._resource), - params={'state': '1'}, - timeout=10) - if request.status_code == 200: - self._state = True - else: + try: + request = requests.get('{}/relay'.format(self._resource), + params={'state': '1'}, + timeout=10) + if request.status_code == 200: + self._state = True + else: + raise requests.exceptions.ConnectionError + + except requests.exceptions.ConnectionError: _LOGGER.error("Can't turn on %s. Is device offline?", self._resource) def turn_off(self, **kwargs): """ Turn the switch off. """ - request = requests.get('{}/relay'.format(self._resource), - params={'state': '0'}, - timeout=10) - if request.status_code == 200: - self._state = False - else: - _LOGGER.error("Can't turn off %s. Is device offline?", + try: + request = requests.get('{}/relay'.format(self._resource), + params={'state': '0'}, + timeout=10) + if request.status_code == 200: + self._state = False + else: + raise requests.exceptions.ConnectionError + + except requests.exceptions.ConnectionError: + _LOGGER.error("Can't turn on %s. Is device offline?", self._resource) def update(self): From 1bebb17e9ff5dc9b67a173b80bc76237a8273841 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 18:14:19 +0100 Subject: [PATCH 051/125] Not need to raise exception --- homeassistant/components/switch/mystrom.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/switch/mystrom.py index 48f47b41ed6..3acbfb24ce6 100644 --- a/homeassistant/components/switch/mystrom.py +++ b/homeassistant/components/switch/mystrom.py @@ -72,9 +72,6 @@ class MyStromSwitch(SwitchDevice): timeout=10) if request.status_code == 200: self._state = True - else: - raise requests.exceptions.ConnectionError - except requests.exceptions.ConnectionError: _LOGGER.error("Can't turn on %s. Is device offline?", self._resource) @@ -87,9 +84,6 @@ class MyStromSwitch(SwitchDevice): timeout=10) if request.status_code == 200: self._state = False - else: - raise requests.exceptions.ConnectionError - except requests.exceptions.ConnectionError: _LOGGER.error("Can't turn on %s. Is device offline?", self._resource) From 08ba71a35931f4f37ae3a094dcf3623c65c5b9da Mon Sep 17 00:00:00 2001 From: sfam Date: Wed, 25 Nov 2015 18:13:39 +0000 Subject: [PATCH 052/125] rename component to motor and services to open/close/stop --- homeassistant/components/motor/__init__.py | 130 +++++++++++++++++ .../{rollershutter => motor}/mqtt.py | 52 +++---- .../{rollershutter => motor}/services.yaml | 0 .../components/rollershutter/__init__.py | 131 ------------------ .../{rollershutter => motor}/__init__.py | 0 .../{rollershutter => motor}/test_mqtt.py | 56 ++++---- 6 files changed, 184 insertions(+), 185 deletions(-) create mode 100644 homeassistant/components/motor/__init__.py rename homeassistant/components/{rollershutter => motor}/mqtt.py (71%) rename homeassistant/components/{rollershutter => motor}/services.yaml (100%) delete mode 100644 homeassistant/components/rollershutter/__init__.py rename tests/components/{rollershutter => motor}/__init__.py (100%) rename tests/components/{rollershutter => motor}/test_mqtt.py (67%) diff --git a/homeassistant/components/motor/__init__.py b/homeassistant/components/motor/__init__.py new file mode 100644 index 00000000000..feea836a0dd --- /dev/null +++ b/homeassistant/components/motor/__init__.py @@ -0,0 +1,130 @@ +""" +homeassistant.components.motor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Motor component. + +""" +import os +import logging + +from homeassistant.config import load_yaml_config_file +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import Entity +from homeassistant.components import group +from homeassistant.const import ( + SERVICE_OPEN, SERVICE_CLOSE, SERVICE_STOP, + STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN, ATTR_ENTITY_ID) + + +DOMAIN = 'motor' +DEPENDENCIES = [] +SCAN_INTERVAL = 15 + +GROUP_NAME_ALL_MOTORS = 'all motors' +ENTITY_ID_ALL_MOTORS = group.ENTITY_ID_FORMAT.format('all_motors') + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + +# Maps discovered services to their platforms +DISCOVERY_PLATFORMS = {} + +_LOGGER = logging.getLogger(__name__) + + +def is_open(hass, entity_id=None): + """ Returns if the motor is open based on the statemachine. """ + entity_id = entity_id or ENTITY_ID_ALL_MOTORS + return hass.states.is_state(entity_id, STATE_OPEN) + + +def call_open(hass, entity_id=None): + """ Open all or specified motor. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_OPEN, data) + + +def call_close(hass, entity_id=None): + """ Close all or specified motor. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_CLOSE, data) + + +def call_stop(hass, entity_id=None): + """ Stops all or specified motor. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_STOP, data) + + +def setup(hass, config): + """ Track states and offer events for motors. """ + component = EntityComponent( + _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, + GROUP_NAME_ALL_MOTORS) + component.setup(config) + + def handle_motor_service(service): + """ Handles calls to the motor services. """ + target_motors = component.extract_from_service(service) + + for motor in target_motors: + if service.service == SERVICE_OPEN: + motor.open() + elif service.service == SERVICE_CLOSE: + motor.close() + elif service.service == SERVICE_STOP: + motor.stop() + + if motor.should_poll: + motor.update_ha_state(True) + + descriptions = load_yaml_config_file( + os.path.join(os.path.dirname(__file__), 'services.yaml')) + + hass.services.register(DOMAIN, SERVICE_OPEN, + handle_motor_service, + descriptions.get(SERVICE_OPEN)) + hass.services.register(DOMAIN, SERVICE_CLOSE, + handle_motor_service, + descriptions.get(SERVICE_CLOSE)) + hass.services.register(DOMAIN, SERVICE_STOP, + handle_motor_service, + descriptions.get(SERVICE_STOP)) + + return True + + +class MotorDevice(Entity): + """ Represents a motor within Home Assistant. """ + # pylint: disable=no-self-use + + @property + def current_position(self): + """ Return current position of motor. + None is unknown, 0 is closed, 100 is fully open. """ + raise NotImplementedError() + + @property + def state(self): + current = self.current_position + + if current is None: + return STATE_UNKNOWN + + return STATE_CLOSED if current == 0 else STATE_OPEN + + @property + def state_attributes(self): + """ Returns optional state attributes. """ + return None + + def open(self, **kwargs): + """ Open the device. """ + raise NotImplementedError() + + def close(self, **kwargs): + """ Close the device. """ + raise NotImplementedError() + + def stop(self, **kwargs): + """ Stop the device. """ + raise NotImplementedError() diff --git a/homeassistant/components/rollershutter/mqtt.py b/homeassistant/components/motor/mqtt.py similarity index 71% rename from homeassistant/components/rollershutter/mqtt.py rename to homeassistant/components/motor/mqtt.py index 6128a4d9819..48a20d5a3b6 100644 --- a/homeassistant/components/rollershutter/mqtt.py +++ b/homeassistant/components/motor/mqtt.py @@ -1,21 +1,21 @@ """ -homeassistant.components.rollershutter.mqtt +homeassistant.components.motor.mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Allows to configure a MQTT rollershutter. +Allows to configure a MQTT motor. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/rollershutter.mqtt/ +https://home-assistant.io/components/motor.mqtt/ """ import logging import homeassistant.components.mqtt as mqtt -from homeassistant.components.rollershutter import RollershutterDevice +from homeassistant.components.motor import MotorDevice _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] -DEFAULT_NAME = "MQTT Shutter" +DEFAULT_NAME = "MQTT Motor" DEFAULT_QOS = 0 -DEFAULT_PAYLOAD_UP = "UP" -DEFAULT_PAYLOAD_DOWN = "DOWN" +DEFAULT_PAYLOAD_OPEN = "OPEN" +DEFAULT_PAYLOAD_CLOSE = "CLOSE" DEFAULT_PAYLOAD_STOP = "STOP" ATTR_CURRENT_POSITION = 'current_position' @@ -23,37 +23,37 @@ ATTR_CURRENT_POSITION = 'current_position' # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): - """ Add MQTT Roller Shutter """ + """ Add MQTT Motor """ if config.get('command_topic') is None: _LOGGER.error("Missing required variable: command_topic") return False - add_devices_callback([MqttRollershutter( + add_devices_callback([MqttMotor( hass, config.get('name', DEFAULT_NAME), config.get('state_topic'), config.get('command_topic'), config.get('qos', DEFAULT_QOS), - config.get('payload_up', DEFAULT_PAYLOAD_UP), - config.get('payload_down', DEFAULT_PAYLOAD_DOWN), + config.get('payload_open', DEFAULT_PAYLOAD_OPEN), + config.get('payload_close', DEFAULT_PAYLOAD_CLOSE), config.get('payload_stop', DEFAULT_PAYLOAD_STOP), config.get('state_format'))]) # pylint: disable=too-many-arguments, too-many-instance-attributes -class MqttRollershutter(RollershutterDevice): - """ Represents a rollershutter that can be togggled using MQTT """ +class MqttMotor(MotorDevice): + """ Represents a motor that can be controlled using MQTT """ def __init__(self, hass, name, state_topic, command_topic, qos, - payload_up, payload_down, payload_stop, state_format): + payload_open, payload_close, payload_stop, state_format): self._state = None self._hass = hass self._name = name self._state_topic = state_topic self._command_topic = command_topic self._qos = qos - self._payload_up = payload_up - self._payload_down = payload_down + self._payload_open = payload_open + self._payload_close = payload_close self._payload_stop = payload_stop self._parse = mqtt.FmtParser(state_format) @@ -79,12 +79,12 @@ class MqttRollershutter(RollershutterDevice): @property def name(self): - """ The name of the rollershutter """ + """ The name of the motor """ return self._name @property def current_position(self): - """ Return current position of rollershutter. + """ Return current position of motor. None is unknown, 0 is closed, 100 is fully open. """ return self._state @@ -93,18 +93,18 @@ class MqttRollershutter(RollershutterDevice): """ True if device is open. """ return self._state > 0 - def move_up(self, **kwargs): - """ Moves the device UP. """ - mqtt.publish(self.hass, self._command_topic, self._payload_up, + def open(self, **kwargs): + """ Close the device. """ + mqtt.publish(self.hass, self._command_topic, self._payload_open, self._qos) - def move_down(self, **kwargs): - """ Moves the device DOWN. """ - mqtt.publish(self.hass, self._command_topic, self._payload_down, + def close(self, **kwargs): + """ Open the device. """ + mqtt.publish(self.hass, self._command_topic, self._payload_close, self._qos) - def move_stop(self, **kwargs): - """ Moves the device to STOP. """ + def stop(self, **kwargs): + """ Stop the device. """ mqtt.publish(self.hass, self._command_topic, self._payload_stop, self._qos) diff --git a/homeassistant/components/rollershutter/services.yaml b/homeassistant/components/motor/services.yaml similarity index 100% rename from homeassistant/components/rollershutter/services.yaml rename to homeassistant/components/motor/services.yaml diff --git a/homeassistant/components/rollershutter/__init__.py b/homeassistant/components/rollershutter/__init__.py deleted file mode 100644 index 5ebcf8a3d21..00000000000 --- a/homeassistant/components/rollershutter/__init__.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -homeassistant.components.rollershutter -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Rollershutter component. - -""" -import os -import logging - -from homeassistant.config import load_yaml_config_file -from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.entity import Entity -from homeassistant.components import group -from homeassistant.const import ( - SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_MOVE_STOP, - STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN, ATTR_ENTITY_ID) - - -DOMAIN = 'rollershutter' -DEPENDENCIES = [] -SCAN_INTERVAL = 15 - -GROUP_NAME_ALL_ROLLERSHUTTERS = 'all rollershutters' -ENTITY_ID_ALL_ROLLERSHUTTERS = group.ENTITY_ID_FORMAT.format( - 'all_rollershutters') - -ENTITY_ID_FORMAT = DOMAIN + '.{}' - -# Maps discovered services to their platforms -DISCOVERY_PLATFORMS = {} - -_LOGGER = logging.getLogger(__name__) - - -def is_open(hass, entity_id=None): - """ Returns if the rollershutter is open based on the statemachine. """ - entity_id = entity_id or ENTITY_ID_ALL_ROLLERSHUTTERS - return hass.states.is_state(entity_id, STATE_OPEN) - - -def move_up(hass, entity_id=None): - """ Moves all or specified rollershutter up. """ - data = {ATTR_ENTITY_ID: entity_id} if entity_id else None - hass.services.call(DOMAIN, SERVICE_MOVE_UP, data) - - -def move_down(hass, entity_id=None): - """ Moves all or specified rollershutter down. """ - data = {ATTR_ENTITY_ID: entity_id} if entity_id else None - hass.services.call(DOMAIN, SERVICE_MOVE_DOWN, data) - - -def move_stop(hass, entity_id=None): - """ Stops all or specified rollershutter. """ - data = {ATTR_ENTITY_ID: entity_id} if entity_id else None - hass.services.call(DOMAIN, SERVICE_MOVE_STOP, data) - - -def setup(hass, config): - """ Track states and offer events for rollershutters. """ - component = EntityComponent( - _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, - GROUP_NAME_ALL_ROLLERSHUTTERS) - component.setup(config) - - def handle_rollershutter_service(service): - """ Handles calls to the rollershutter services. """ - target_rollershutters = component.extract_from_service(service) - - for rollershutter in target_rollershutters: - if service.service == SERVICE_MOVE_UP: - rollershutter.move_up() - elif service.service == SERVICE_MOVE_DOWN: - rollershutter.move_down() - elif service.service == SERVICE_MOVE_STOP: - rollershutter.move_stop() - - if rollershutter.should_poll: - rollershutter.update_ha_state(True) - - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) - - hass.services.register(DOMAIN, SERVICE_MOVE_UP, - handle_rollershutter_service, - descriptions.get(SERVICE_MOVE_UP)) - hass.services.register(DOMAIN, SERVICE_MOVE_DOWN, - handle_rollershutter_service, - descriptions.get(SERVICE_MOVE_DOWN)) - hass.services.register(DOMAIN, SERVICE_MOVE_STOP, - handle_rollershutter_service, - descriptions.get(SERVICE_MOVE_STOP)) - - return True - - -class RollershutterDevice(Entity): - """ Represents a rollershutter within Home Assistant. """ - # pylint: disable=no-self-use - - @property - def current_position(self): - """ Return current position of rollershutter. - None is unknown, 0 is closed, 100 is fully open. """ - raise NotImplementedError() - - @property - def state(self): - current = self.current_position - - if current is None: - return STATE_UNKNOWN - - return STATE_CLOSED if current == 0 else STATE_OPEN - - @property - def state_attributes(self): - """ Returns optional state attributes. """ - return None - - def move_up(self, **kwargs): - """ Moves the device UP. """ - raise NotImplementedError() - - def move_down(self, **kwargs): - """ Moves the device DOWN. """ - raise NotImplementedError() - - def move_stop(self, **kwargs): - """ Moves the device to STOP. """ - raise NotImplementedError() diff --git a/tests/components/rollershutter/__init__.py b/tests/components/motor/__init__.py similarity index 100% rename from tests/components/rollershutter/__init__.py rename to tests/components/motor/__init__.py diff --git a/tests/components/rollershutter/test_mqtt.py b/tests/components/motor/test_mqtt.py similarity index 67% rename from tests/components/rollershutter/test_mqtt.py rename to tests/components/motor/test_mqtt.py index 2a71163c50f..4d7af04ae6c 100644 --- a/tests/components/rollershutter/test_mqtt.py +++ b/tests/components/motor/test_mqtt.py @@ -1,19 +1,19 @@ """ -tests.components.rollershutter.test_mqtt +tests.components.motor.test_mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Tests mqtt rollershutter. +Tests mqtt motor. """ import unittest from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN import homeassistant.core as ha -import homeassistant.components.rollershutter as rollershutter +import homeassistant.components.motor as motor from tests.common import mock_mqtt_component, fire_mqtt_message -class TestRollershutterMQTT(unittest.TestCase): - """ Test the MQTT rollershutter. """ +class TestMotorMQTT(unittest.TestCase): + """ Test the MQTT motor. """ def setUp(self): # pylint: disable=invalid-name self.hass = ha.HomeAssistant() @@ -24,43 +24,43 @@ class TestRollershutterMQTT(unittest.TestCase): self.hass.stop() def test_controlling_state_via_topic(self): - self.assertTrue(rollershutter.setup(self.hass, { - 'rollershutter': { + self.assertTrue(motor.setup(self.hass, { + 'motor': { 'platform': 'mqtt', 'name': 'test', 'state_topic': 'state-topic', 'command_topic': 'command-topic', 'qos': 0, - 'payload_up': 'UP', - 'payload_down': 'DOWN', + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', 'payload_stop': 'STOP' } })) - state = self.hass.states.get('rollershutter.test') + state = self.hass.states.get('motor.test') self.assertEqual(STATE_UNKNOWN, state.state) fire_mqtt_message(self.hass, 'state-topic', '0') self.hass.pool.block_till_done() - state = self.hass.states.get('rollershutter.test') + state = self.hass.states.get('motor.test') self.assertEqual(STATE_CLOSED, state.state) fire_mqtt_message(self.hass, 'state-topic', '50') self.hass.pool.block_till_done() - state = self.hass.states.get('rollershutter.test') + state = self.hass.states.get('motor.test') self.assertEqual(STATE_OPEN, state.state) fire_mqtt_message(self.hass, 'state-topic', '100') self.hass.pool.block_till_done() - state = self.hass.states.get('rollershutter.test') + state = self.hass.states.get('motor.test') self.assertEqual(STATE_OPEN, state.state) def test_sending_mqtt_commands(self): - self.assertTrue(rollershutter.setup(self.hass, { - 'rollershutter': { + self.assertTrue(motor.setup(self.hass, { + 'motor': { 'platform': 'mqtt', 'name': 'test', 'state_topic': 'state-topic', @@ -69,54 +69,54 @@ class TestRollershutterMQTT(unittest.TestCase): } })) - state = self.hass.states.get('rollershutter.test') + state = self.hass.states.get('motor.test') self.assertEqual(STATE_UNKNOWN, state.state) - rollershutter.move_up(self.hass, 'rollershutter.test') + motor.call_open(self.hass, 'motor.test') self.hass.pool.block_till_done() - self.assertEqual(('command-topic', 'UP', 2), + self.assertEqual(('command-topic', 'OPEN', 2), self.mock_publish.mock_calls[-1][1]) - state = self.hass.states.get('rollershutter.test') + state = self.hass.states.get('motor.test') self.assertEqual(STATE_UNKNOWN, state.state) def test_state_attributes_current_position(self): - self.assertTrue(rollershutter.setup(self.hass, { - 'rollershutter': { + self.assertTrue(motor.setup(self.hass, { + 'motor': { 'platform': 'mqtt', 'name': 'test', 'state_topic': 'state-topic', 'command_topic': 'command-topic', - 'payload_up': 'UP', - 'payload_down': 'DOWN', + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', 'payload_stop': 'STOP' } })) state_attributes_dict = self.hass.states.get( - 'rollershutter.test').attributes + 'motor.test').attributes self.assertFalse('current_position' in state_attributes_dict) fire_mqtt_message(self.hass, 'state-topic', '0') self.hass.pool.block_till_done() current_position = self.hass.states.get( - 'rollershutter.test').attributes['current_position'] + 'motor.test').attributes['current_position'] self.assertEqual(0, current_position) fire_mqtt_message(self.hass, 'state-topic', '50') self.hass.pool.block_till_done() current_position = self.hass.states.get( - 'rollershutter.test').attributes['current_position'] + 'motor.test').attributes['current_position'] self.assertEqual(50, current_position) fire_mqtt_message(self.hass, 'state-topic', '101') self.hass.pool.block_till_done() current_position = self.hass.states.get( - 'rollershutter.test').attributes['current_position'] + 'motor.test').attributes['current_position'] self.assertEqual(50, current_position) fire_mqtt_message(self.hass, 'state-topic', 'non-numeric') self.hass.pool.block_till_done() current_position = self.hass.states.get( - 'rollershutter.test').attributes['current_position'] + 'motor.test').attributes['current_position'] self.assertEqual(50, current_position) From 0dbf4b3c10e92ec207c3e5288646186b12196b90 Mon Sep 17 00:00:00 2001 From: sfam Date: Wed, 25 Nov 2015 18:20:16 +0000 Subject: [PATCH 053/125] add const.py changes to commit --- homeassistant/const.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index add1848b72c..1513c188cc2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -143,9 +143,9 @@ SERVICE_ALARM_TRIGGER = "alarm_trigger" SERVICE_LOCK = "lock" SERVICE_UNLOCK = "unlock" -SERVICE_MOVE_UP = 'move_up' -SERVICE_MOVE_DOWN = 'move_down' -SERVICE_MOVE_STOP = 'move_stop' +SERVICE_OPEN = 'open' +SERVICE_CLOSE = 'close' +SERVICE_STOP = 'stop' # #### API / REMOTE #### SERVER_PORT = 8123 From e60ab8f4c2a6aa327b3d57bb0b220f2063341e2e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 23:31:04 +0100 Subject: [PATCH 054/125] Add possibility to write data to file --- script/gen_requirements_all.py | 68 +++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index b10c0f38ed8..a5f3c347fa4 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -8,6 +8,7 @@ import importlib import os import pkgutil import re +import argparse COMMENT_REQUIREMENTS = [ 'RPi.GPIO', @@ -16,6 +17,7 @@ COMMENT_REQUIREMENTS = [ def explore_module(package, explore_children): + """ Explore the modules. """ module = importlib.import_module(package) found = [] @@ -33,10 +35,10 @@ def explore_module(package, explore_children): def core_requirements(): + """ Gather core requirements out of setup.py. """ with open('setup.py') as inp: reqs_raw = re.search( r'REQUIRES = \[(.*?)\]', inp.read(), re.S).group(1) - return re.findall(r"'(.*?)'", reqs_raw) @@ -45,20 +47,23 @@ def comment_requirement(req): return any(ign in req for ign in COMMENT_REQUIREMENTS) -def main(): - if not os.path.isfile('requirements_all.txt'): - print('Run this from HA root dir') - return - +def gather_modules(): + """ Collect the information and construct the output. """ reqs = OrderedDict() errors = [] + output = [] + for package in sorted(explore_module('homeassistant.components', True)): try: module = importlib.import_module(package) except ImportError: errors.append(package) continue + # For catching the error by RPi.GPIO + # RuntimeError: This module can only be run on a Raspberry Pi! + except RuntimeError: + continue if not getattr(module, 'REQUIREMENTS', None): continue @@ -71,20 +76,55 @@ def main(): print('\n'.join(errors)) return - print('# Home Assistant core') - print('\n'.join(core_requirements())) - print() - + output.append('# Home Assistant core') + output.append('\n') + output.append('\n'.join(core_requirements())) + output.append('\n') for pkg, requirements in reqs.items(): for req in sorted(requirements, key=lambda name: (len(name.split('.')), name)): - print('#', req) + output.append('\n# {}'.format(req)) if comment_requirement(pkg): - print('#', pkg) + output.append('\n# {}\n'.format(pkg)) else: - print(pkg) - print() + output.append('\n{}\n'.format(pkg)) + + return ''.join(output) + + +def write_file(data): + """ Writes the modules to the requirements_all.txt. """ + with open('requirements_all.txt', 'w+') as req_file: + req_file.write(data) + + +def display(data): + """ Prints the output to command line. """ + print(data) + + +def argparsing(): + """ Parsing the command line arguments. """ + parser = argparse.ArgumentParser( + description='Generate a requirements_all.txt') + parser.add_argument('file', nargs='?', + help='create new requirements_all.txt file') + return parser.parse_args() + + +def main(): + """ Main """ + if not os.path.isfile('requirements_all.txt'): + print('Run this from HA root dir') + return + args = argparsing() + data = gather_modules() + + if args.file: + write_file(data) + else: + display(data) if __name__ == '__main__': main() From 4bd0db30c988da8c1110df504ab29d3e20f69adb Mon Sep 17 00:00:00 2001 From: badele Date: Thu, 26 Nov 2015 07:52:37 +0100 Subject: [PATCH 055/125] Add should_fire_event in rfxtrx component --- homeassistant/components/light/rfxtrx.py | 60 ++++++++++++++++------ homeassistant/components/rfxtrx.py | 22 +++++--- homeassistant/components/switch/rfxtrx.py | 61 +++++++++++++++++------ 3 files changed, 107 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 8d6a2b86217..e5ff38241d9 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -8,10 +8,16 @@ https://home-assistant.io/components/light.rfxtrx/ """ import logging import homeassistant.components.rfxtrx as rfxtrx +import RFXtrx as rfxtrxmod from homeassistant.components.light import Light from homeassistant.util import slugify +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.components.rfxtrx import ATTR_STATE, ATTR_FIREEVENT, ATTR_PACKETID, \ + ATTR_NAME, EVENT_BUTTON_PRESSED + + DEPENDENCIES = ['rfxtrx'] _LOGGER = logging.getLogger(__name__) @@ -19,16 +25,22 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - import RFXtrx as rfxtrxmod - lights = [] devices = config.get('devices', None) + if devices: for entity_id, entity_info in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: - _LOGGER.info("Add %s rfxtrx.light", entity_info['name']) - rfxobject = rfxtrx.get_rfx_object(entity_info['packetid']) - new_light = RfxtrxLight(entity_info['name'], rfxobject, False) + _LOGGER.info("Add %s rfxtrx.light", entity_info[ATTR_NAME]) + + # Check if i must fire event + fire_event = entity_info.get(ATTR_FIREEVENT, False) + datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event} + + rfxobject = rfxtrx.get_rfx_object(entity_info[ATTR_PACKETID]) + new_light = RfxtrxLight( + entity_info[ATTR_NAME], rfxobject, datas + ) rfxtrx.RFX_DEVICES[entity_id] = new_light lights.append(new_light) @@ -54,12 +66,14 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): ) pkt_id = "".join("{0:02x}".format(x) for x in event.data) entity_name = "%s : %s" % (entity_id, pkt_id) - new_light = RfxtrxLight(entity_name, event, False) + datas = {ATTR_STATE: False, ATTR_FIREEVENT: False} + new_light = RfxtrxLight(entity_name, event, datas) rfxtrx.RFX_DEVICES[entity_id] = new_light add_devices_callback([new_light]) # Check if entity exists or previously added automatically - if entity_id in rfxtrx.RFX_DEVICES: + if entity_id in rfxtrx.RFX_DEVICES \ + and isinstance(rfxtrx.RFX_DEVICES[entity_id], RfxtrxLight): _LOGGER.debug( "EntityID: %s light_update. Command: %s", entity_id, @@ -67,10 +81,22 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): ) if event.values['Command'] == 'On'\ or event.values['Command'] == 'Off': - if event.values['Command'] == 'On': - rfxtrx.RFX_DEVICES[entity_id].turn_on() - else: - rfxtrx.RFX_DEVICES[entity_id].turn_off() + + # Update the rfxtrx device state + is_on = event.values['Command'] == 'On' + # pylint: disable=protected-access + rfxtrx.RFX_DEVICES[entity_id]._state = is_on + rfxtrx.RFX_DEVICES[entity_id].update_ha_state() + + # Fire event + if rfxtrx.RFX_DEVICES[entity_id].should_fire_event: + rfxtrx.RFX_DEVICES[entity_id].hass.bus.fire( + EVENT_BUTTON_PRESSED, { + ATTR_ENTITY_ID: + rfxtrx.RFX_DEVICES[entity_id].entity_id, + ATTR_STATE: event.values['Command'].lower() + } + ) # Subscribe to main rfxtrx events if light_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: @@ -79,10 +105,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class RfxtrxLight(Light): """ Provides a RFXtrx light. """ - def __init__(self, name, event, state): + def __init__(self, name, event, datas): self._name = name self._event = event - self._state = state + self._state = datas[ATTR_STATE] + self._should_fire_event = datas[ATTR_FIREEVENT] @property def should_poll(self): @@ -94,6 +121,11 @@ class RfxtrxLight(Light): """ Returns the name of the light if any. """ return self._name + @property + def should_fire_event(self): + """ Returns is the device must fire event""" + return self._should_fire_event + @property def is_on(self): """ True if light is on. """ @@ -115,4 +147,4 @@ class RfxtrxLight(Light): self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False - self.update_ha_state() + self.update_ha_state() \ No newline at end of file diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 3c4675d806d..e02a56d3c7a 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -4,7 +4,7 @@ homeassistant.components.rfxtrx Provides support for RFXtrx components. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/rfxtrx/ +https://home-assistant.io/components/rfxtrx.html """ import logging from homeassistant.util import slugify @@ -14,8 +14,16 @@ REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip' + '#RFXtrx==0.2'] DOMAIN = "rfxtrx" -CONF_DEVICE = 'device' -CONF_DEBUG = 'debug' + +ATTR_DEVICE = 'device' +ATTR_DEBUG = 'debug' +ATTR_STATE = 'state' +ATTR_NAME = 'name' +ATTR_PACKETID = 'packetid' +ATTR_FIREEVENT = 'fire_event' + +EVENT_BUTTON_PRESSED = 'button_pressed' + RECEIVED_EVT_SUBSCRIBERS = [] RFX_DEVICES = {} _LOGGER = logging.getLogger(__name__) @@ -50,15 +58,15 @@ def setup(hass, config): # Init the rfxtrx module global RFXOBJECT - if CONF_DEVICE not in config[DOMAIN]: + if ATTR_DEVICE not in config[DOMAIN]: _LOGGER.exception( "can found device parameter in %s YAML configuration section", DOMAIN ) return False - device = config[DOMAIN][CONF_DEVICE] - debug = config[DOMAIN].get(CONF_DEBUG, False) + device = config[DOMAIN][ATTR_DEVICE] + debug = config[DOMAIN].get(ATTR_DEBUG, False) RFXOBJECT = rfxtrxmod.Core(device, handle_receive, debug=debug) @@ -86,4 +94,4 @@ def get_rfx_object(packetid): return obj - return None + return None \ No newline at end of file diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 86bcf580f41..e4400bdf86f 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -8,10 +8,16 @@ https://home-assistant.io/components/switch.rfxtrx/ """ import logging import homeassistant.components.rfxtrx as rfxtrx +import RFXtrx as rfxtrxmod from homeassistant.components.switch import SwitchDevice from homeassistant.util import slugify +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.components.rfxtrx import ATTR_STATE, ATTR_FIREEVENT, ATTR_PACKETID, \ + ATTR_NAME, EVENT_BUTTON_PRESSED + + DEPENDENCIES = ['rfxtrx'] _LOGGER = logging.getLogger(__name__) @@ -19,7 +25,6 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - from RFXtrx import LightingDevice # Add switch from config file switchs = [] @@ -27,9 +32,15 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if devices: for entity_id, entity_info in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: - _LOGGER.info("Add %s rfxtrx.switch", entity_info['name']) - rfxobject = rfxtrx.get_rfx_object(entity_info['packetid']) - newswitch = RfxtrxSwitch(entity_info['name'], rfxobject, False) + _LOGGER.info("Add %s rfxtrx.switch", entity_info[ATTR_NAME]) + + # Check if i must fire event + fire_event = entity_info.get(ATTR_FIREEVENT, False) + datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event} + + rfxobject = rfxtrx.get_rfx_object(entity_info[ATTR_PACKETID]) + newswitch = RfxtrxSwitch( + entity_info[ATTR_NAME], rfxobject, datas) rfxtrx.RFX_DEVICES[entity_id] = newswitch switchs.append(newswitch) @@ -37,7 +48,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): def switch_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ - if isinstance(event.device, LightingDevice): + if not isinstance(event.device, rfxtrxmod.LightingDevice): return # Add entity if not exist and the automatic_add is True @@ -55,12 +66,14 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): ) pkt_id = "".join("{0:02x}".format(x) for x in event.data) entity_name = "%s : %s" % (entity_id, pkt_id) - new_switch = RfxtrxSwitch(entity_name, event, False) + datas = {ATTR_STATE: False, ATTR_FIREEVENT: False} + new_switch = RfxtrxSwitch(entity_name, event, datas) rfxtrx.RFX_DEVICES[entity_id] = new_switch add_devices_callback([new_switch]) # Check if entity exists or previously added automatically - if entity_id in rfxtrx.RFX_DEVICES: + if entity_id in rfxtrx.RFX_DEVICES \ + and isinstance(rfxtrx.RFX_DEVICES[entity_id], RfxtrxSwitch): _LOGGER.debug( "EntityID: %s switch_update. Command: %s", entity_id, @@ -68,10 +81,22 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): ) if event.values['Command'] == 'On'\ or event.values['Command'] == 'Off': - if event.values['Command'] == 'On': - rfxtrx.RFX_DEVICES[entity_id].turn_on() - else: - rfxtrx.RFX_DEVICES[entity_id].turn_off() + + # Update the rfxtrx device state + is_on = event.values['Command'] == 'On' + # pylint: disable=protected-access + rfxtrx.RFX_DEVICES[entity_id]._state = is_on + rfxtrx.RFX_DEVICES[entity_id].update_ha_state() + + # Fire event + if rfxtrx.RFX_DEVICES[entity_id].should_fire_event: + rfxtrx.RFX_DEVICES[entity_id].hass.bus.fire( + EVENT_BUTTON_PRESSED, { + ATTR_ENTITY_ID: + rfxtrx.RFX_DEVICES[entity_id].entity_id, + ATTR_STATE: event.values['Command'].lower() + } + ) # Subscribe to main rfxtrx events if switch_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: @@ -80,10 +105,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class RfxtrxSwitch(SwitchDevice): """ Provides a RFXtrx switch. """ - def __init__(self, name, event, state): + def __init__(self, name, event, datas): self._name = name self._event = event - self._state = state + self._state = datas[ATTR_STATE] + self._should_fire_event = datas[ATTR_FIREEVENT] @property def should_poll(self): @@ -95,9 +121,14 @@ class RfxtrxSwitch(SwitchDevice): """ Returns the name of the device if any. """ return self._name + @property + def should_fire_event(self): + """ Returns is the device must fire event""" + return self._should_fire_event + @property def is_on(self): - """ True if device is on. """ + """ True if light is on. """ return self._state def turn_on(self, **kwargs): @@ -114,4 +145,4 @@ class RfxtrxSwitch(SwitchDevice): self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False - self.update_ha_state() + self.update_ha_state() \ No newline at end of file From a33220db7f49962a97c58181582f0437723752f1 Mon Sep 17 00:00:00 2001 From: badele Date: Thu, 26 Nov 2015 08:12:04 +0100 Subject: [PATCH 056/125] Fix pylint style --- homeassistant/components/light/rfxtrx.py | 2 +- homeassistant/components/rfxtrx.py | 2 +- homeassistant/components/switch/rfxtrx.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index e5ff38241d9..9da69300623 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -147,4 +147,4 @@ class RfxtrxLight(Light): self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False - self.update_ha_state() \ No newline at end of file + self.update_ha_state() diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index e02a56d3c7a..bcdbec5327b 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -94,4 +94,4 @@ def get_rfx_object(packetid): return obj - return None \ No newline at end of file + return None diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index e4400bdf86f..24bd2f2fd6d 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -145,4 +145,4 @@ class RfxtrxSwitch(SwitchDevice): self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False - self.update_ha_state() \ No newline at end of file + self.update_ha_state() From 128e3bb762b2cb6900ee09f4650405e2cb9609b4 Mon Sep 17 00:00:00 2001 From: badele Date: Thu, 26 Nov 2015 08:27:31 +0100 Subject: [PATCH 057/125] Move import module --- homeassistant/components/light/rfxtrx.py | 3 ++- homeassistant/components/rfxtrx.py | 2 +- homeassistant/components/switch/rfxtrx.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 9da69300623..6132c10a99c 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -8,7 +8,6 @@ https://home-assistant.io/components/light.rfxtrx/ """ import logging import homeassistant.components.rfxtrx as rfxtrx -import RFXtrx as rfxtrxmod from homeassistant.components.light import Light from homeassistant.util import slugify @@ -25,6 +24,8 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + import RFXtrx as rfxtrxmod + lights = [] devices = config.get('devices', None) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index bcdbec5327b..38aa785e7ad 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -4,7 +4,7 @@ homeassistant.components.rfxtrx Provides support for RFXtrx components. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/rfxtrx.html +https://home-assistant.io/components/rfxtrx/ """ import logging from homeassistant.util import slugify diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 24bd2f2fd6d..69e08e7d129 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -8,7 +8,6 @@ https://home-assistant.io/components/switch.rfxtrx/ """ import logging import homeassistant.components.rfxtrx as rfxtrx -import RFXtrx as rfxtrxmod from homeassistant.components.switch import SwitchDevice from homeassistant.util import slugify @@ -25,6 +24,7 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + import RFXtrx as rfxtrxmod # Add switch from config file switchs = [] From 4f75286f64dde2cbfa429eb19167169f88c2baa1 Mon Sep 17 00:00:00 2001 From: sfam Date: Thu, 26 Nov 2015 09:38:25 +0000 Subject: [PATCH 058/125] fix some comments --- homeassistant/components/motor/mqtt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/motor/mqtt.py b/homeassistant/components/motor/mqtt.py index 48a20d5a3b6..2aac5e37c16 100644 --- a/homeassistant/components/motor/mqtt.py +++ b/homeassistant/components/motor/mqtt.py @@ -90,16 +90,16 @@ class MqttMotor(MotorDevice): @property def is_open(self): - """ True if device is open. """ + """ True if device is current position is not zero. """ return self._state > 0 def open(self, **kwargs): - """ Close the device. """ + """ Open the device. """ mqtt.publish(self.hass, self._command_topic, self._payload_open, self._qos) def close(self, **kwargs): - """ Open the device. """ + """ Close the device. """ mqtt.publish(self.hass, self._command_topic, self._payload_close, self._qos) From f2f598bd264bb9536065058a65f49c49153f2920 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 26 Nov 2015 16:37:00 +0100 Subject: [PATCH 059/125] Use host as config var instead of resource --- homeassistant/components/switch/mystrom.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/switch/mystrom.py index 3acbfb24ce6..36a62fe33e4 100644 --- a/homeassistant/components/switch/mystrom.py +++ b/homeassistant/components/switch/mystrom.py @@ -16,29 +16,27 @@ DEFAULT_NAME = 'myStrom Switch' _LOGGER = logging.getLogger(__name__) -# pylint: disable=unused-argument, too-many-function-args def setup_platform(hass, config, add_devices, discovery_info=None): """ Find and return myStrom switches. """ - resource = config.get('resource') + host = config.get('host') - if resource is None: - _LOGGER.error('Missing required variable: resource') + if host is None: + _LOGGER.error('Missing required variable: host') return False + resource = 'http://{}'.format(host) + try: requests.get(resource, timeout=10) - except requests.exceptions.MissingSchema: - _LOGGER.error("Missing resource or schema in configuration. " - "Add http:// to your URL.") - return False except requests.exceptions.ConnectionError: - _LOGGER.error("No route to device. " - "Please check the IP address in the configuration file.") + _LOGGER.error("No route to device %s. " + "Please check the IP address in the configuration file", + host) return False add_devices([MyStromSwitch( config.get('name', DEFAULT_NAME), - config.get('resource'))]) + resource)]) class MyStromSwitch(SwitchDevice): From 067011af1563ee67441292c496ed7feee39e136b Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Thu, 26 Nov 2015 20:45:07 +0100 Subject: [PATCH 060/125] function support for aREST backend The implementation sends /?params=1 for turn_on and /?params=0 for turn_off It uses the return value of the function to determine the current state. 0=Off, 1=On --- homeassistant/components/switch/arest.py | 95 ++++++++++++++++++++---- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/switch/arest.py b/homeassistant/components/switch/arest.py index 92bd70cb734..c42295660d4 100644 --- a/homeassistant/components/switch/arest.py +++ b/homeassistant/components/switch/arest.py @@ -34,30 +34,33 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False dev = [] - pins = config.get('pins') + pins = config.get('pins', {}) for pinnum, pin in pins.items(): - dev.append(ArestSwitch(resource, - config.get('name', response.json()['name']), - pin.get('name'), - pinnum)) + dev.append(ArestSwitchPin(resource, + config.get('name', response.json()['name']), + pin.get('name'), + pinnum)) + + functions = config.get('functions', {}) + for funcname, func in functions.items(): + dev.append(ArestSwitchFunction(resource, + config.get('name', + response.json()['name']), + func.get('name'), + funcname)) + add_devices(dev) -class ArestSwitch(SwitchDevice): +class ArestSwitchBase(SwitchDevice): """ Implements an aREST switch. """ - def __init__(self, resource, location, name, pin): + def __init__(self, resource, location, name): self._resource = resource self._name = '{} {}'.format(location.title(), name.title()) \ or DEVICE_DEFAULT_NAME - self._pin = pin self._state = None - request = requests.get('{}/mode/{}/o'.format(self._resource, - self._pin), timeout=10) - if request.status_code is not 200: - _LOGGER.error("Can't set mode. Is device offline?") - @property def name(self): """ The name of the switch. """ @@ -68,6 +71,72 @@ class ArestSwitch(SwitchDevice): """ True if device is on. """ return self._state + +class ArestSwitchFunction(ArestSwitchBase): + """ Implements an aREST switch. Based on functions. """ + + def __init__(self, resource, location, name, func): + super().__init__(resource, location, name) + self._func = func + + request = requests.get('{}/{}'.format(self._resource, self._func), + timeout=10) + + if request.status_code is not 200: + _LOGGER.error("Can't find function. Is device offline?") + return + + try: + request.json()['return_value'] + except KeyError: + _LOGGER.error("No return_value received. " + "Is the function name correct.") + except ValueError: + _LOGGER.error("Response invalid. Is the function name correct.") + + def turn_on(self, **kwargs): + """ Turn the device on. """ + request = requests.get('{}/{}'.format(self._resource, self._func), + timeout=10, params={"params": "1"}) + + if request.status_code == 200: + self._state = True + else: + _LOGGER.error("Can't turn on function %s at %s. " + "Is device offline?", + self._func, self._resource) + + def turn_off(self, **kwargs): + """ Turn the device off. """ + request = requests.get('{}/{}'.format(self._resource, self._func), + timeout=10, params={"params": "0"}) + + if request.status_code == 200: + self._state = False + else: + _LOGGER.error("Can't turn off function %s at %s. " + "Is device offline?", + self._func, self._resource) + + def update(self): + """ Gets the latest data from aREST API and updates the state. """ + request = requests.get('{}/{}'.format(self._resource, + self._func), timeout=10) + self._state = request.json()['return_value'] != 0 + + +class ArestSwitchPin(ArestSwitchBase): + """ Implements an aREST switch. Based on digital I/O """ + + def __init__(self, resource, location, name, pin): + super().__init__(resource, location, name) + self._pin = pin + + request = requests.get('{}/mode/{}/o'.format(self._resource, + self._pin), timeout=10) + if request.status_code is not 200: + _LOGGER.error("Can't set mode. Is device offline?") + def turn_on(self, **kwargs): """ Turn the device on. """ request = requests.get('{}/digital/{}/1'.format(self._resource, From 69e9d39690e4ba6c35f8d905040ec0194b0fb22c Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Tue, 24 Nov 2015 21:30:06 +0100 Subject: [PATCH 061/125] Allow setting the retain flag for mqtt switch. Some devices can read the initial value on startup. If the retain flag is set they always receive the value as last set by home assistant. --- homeassistant/components/mqtt/__init__.py | 15 +++++++++++---- homeassistant/components/switch/mqtt.py | 9 ++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index b8dc8909887..65db49f8636 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -28,6 +28,7 @@ MQTT_CLIENT = None DEFAULT_PORT = 1883 DEFAULT_KEEPALIVE = 60 DEFAULT_QOS = 0 +DEFAULT_RETAIN = False SERVICE_PUBLISH = 'publish' EVENT_MQTT_MESSAGE_RECEIVED = 'MQTT_MESSAGE_RECEIVED' @@ -46,11 +47,12 @@ CONF_CERTIFICATE = 'certificate' ATTR_TOPIC = 'topic' ATTR_PAYLOAD = 'payload' ATTR_QOS = 'qos' +ATTR_RETAIN = 'retain' MAX_RECONNECT_WAIT = 300 # seconds -def publish(hass, topic, payload, qos=None): +def publish(hass, topic, payload, qos=None, retain=None): """ Send an MQTT message. """ data = { ATTR_TOPIC: topic, @@ -58,6 +60,10 @@ def publish(hass, topic, payload, qos=None): } if qos is not None: data[ATTR_QOS] = qos + + if retain is not None: + data[ATTR_RETAIN] = retain + hass.services.call(DOMAIN, SERVICE_PUBLISH, data) @@ -119,9 +125,10 @@ def setup(hass, config): msg_topic = call.data.get(ATTR_TOPIC) payload = call.data.get(ATTR_PAYLOAD) qos = call.data.get(ATTR_QOS, DEFAULT_QOS) + retain = call.data.get(ATTR_RETAIN, DEFAULT_RETAIN) if msg_topic is None or payload is None: return - MQTT_CLIENT.publish(msg_topic, payload, qos) + MQTT_CLIENT.publish(msg_topic, payload, qos, retain) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_mqtt) @@ -190,9 +197,9 @@ class MQTT(object): self._mqttc.connect(broker, port, keepalive) - def publish(self, topic, payload, qos): + def publish(self, topic, payload, qos, retain): """ Publish a MQTT message. """ - self._mqttc.publish(topic, payload, qos) + self._mqttc.publish(topic, payload, qos, retain) def start(self): """ Run the MQTT client. """ diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index 43bceab69a5..7b973799eed 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -17,6 +17,7 @@ DEFAULT_QOS = 0 DEFAULT_PAYLOAD_ON = "ON" DEFAULT_PAYLOAD_OFF = "OFF" DEFAULT_OPTIMISTIC = False +DEFAULT_RETAIN = False DEPENDENCIES = ['mqtt'] @@ -35,6 +36,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('state_topic'), config.get('command_topic'), config.get('qos', DEFAULT_QOS), + config.get('retain', DEFAULT_RETAIN), config.get('payload_on', DEFAULT_PAYLOAD_ON), config.get('payload_off', DEFAULT_PAYLOAD_OFF), config.get('optimistic', DEFAULT_OPTIMISTIC), @@ -44,7 +46,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSwitch(SwitchDevice): """ Represents a switch that can be toggled using MQTT. """ - def __init__(self, hass, name, state_topic, command_topic, qos, + def __init__(self, hass, name, state_topic, command_topic, qos, retain, payload_on, payload_off, optimistic, state_format): self._state = False self._hass = hass @@ -52,6 +54,7 @@ class MqttSwitch(SwitchDevice): self._state_topic = state_topic self._command_topic = command_topic self._qos = qos + self._retain = retain self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic @@ -93,7 +96,7 @@ class MqttSwitch(SwitchDevice): def turn_on(self, **kwargs): """ Turn the device on. """ mqtt.publish(self.hass, self._command_topic, self._payload_on, - self._qos) + self._qos, self._retain) if self._optimistic: # optimistically assume that switch has changed state self._state = True @@ -102,7 +105,7 @@ class MqttSwitch(SwitchDevice): def turn_off(self, **kwargs): """ Turn the device off. """ mqtt.publish(self.hass, self._command_topic, self._payload_off, - self._qos) + self._qos, self._retain) if self._optimistic: # optimistically assume that switch has changed state self._state = False From 341c3a8fcda4c514edd3ae5b3de0b0d17270109e Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Thu, 26 Nov 2015 20:55:26 +0100 Subject: [PATCH 062/125] Fix tests for mqtt publish with retain. --- tests/components/alarm_control_panel/test_mqtt.py | 6 +++--- tests/components/light/test_mqtt.py | 4 ++-- tests/components/switch/test_mqtt.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/components/alarm_control_panel/test_mqtt.py b/tests/components/alarm_control_panel/test_mqtt.py index 6cba26c15a6..58c55350cd2 100644 --- a/tests/components/alarm_control_panel/test_mqtt.py +++ b/tests/components/alarm_control_panel/test_mqtt.py @@ -101,7 +101,7 @@ class TestAlarmControlPanelMQTT(unittest.TestCase): alarm_control_panel.alarm_arm_home(self.hass) self.hass.pool.block_till_done() - self.assertEqual(('alarm/command', 'ARM_HOME', 0), + self.assertEqual(('alarm/command', 'ARM_HOME', 0, False), self.mock_publish.mock_calls[-1][1]) def test_arm_home_not_publishes_mqtt_with_invalid_code(self): @@ -130,7 +130,7 @@ class TestAlarmControlPanelMQTT(unittest.TestCase): alarm_control_panel.alarm_arm_away(self.hass) self.hass.pool.block_till_done() - self.assertEqual(('alarm/command', 'ARM_AWAY', 0), + self.assertEqual(('alarm/command', 'ARM_AWAY', 0, False), self.mock_publish.mock_calls[-1][1]) def test_arm_away_not_publishes_mqtt_with_invalid_code(self): @@ -159,7 +159,7 @@ class TestAlarmControlPanelMQTT(unittest.TestCase): alarm_control_panel.alarm_disarm(self.hass) self.hass.pool.block_till_done() - self.assertEqual(('alarm/command', 'DISARM', 0), + self.assertEqual(('alarm/command', 'DISARM', 0, False), self.mock_publish.mock_calls[-1][1]) def test_disarm_not_publishes_mqtt_with_invalid_code(self): diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 39c81ee0a04..fdebb0c1ef5 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -139,7 +139,7 @@ class TestLightMQTT(unittest.TestCase): light.turn_on(self.hass, 'light.test') self.hass.pool.block_till_done() - self.assertEqual(('test_light_rgb/set', 'on', 2), + self.assertEqual(('test_light_rgb/set', 'on', 2, False), self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) @@ -147,7 +147,7 @@ class TestLightMQTT(unittest.TestCase): light.turn_off(self.hass, 'light.test') self.hass.pool.block_till_done() - self.assertEqual(('test_light_rgb/set', 'off', 2), + self.assertEqual(('test_light_rgb/set', 'off', 2, False), self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) diff --git a/tests/components/switch/test_mqtt.py b/tests/components/switch/test_mqtt.py index 4754e64e575..2cfe29c2910 100644 --- a/tests/components/switch/test_mqtt.py +++ b/tests/components/switch/test_mqtt.py @@ -68,7 +68,7 @@ class TestSensorMQTT(unittest.TestCase): switch.turn_on(self.hass, 'switch.test') self.hass.pool.block_till_done() - self.assertEqual(('command-topic', 'beer on', 2), + self.assertEqual(('command-topic', 'beer on', 2, False), self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('switch.test') self.assertEqual(STATE_ON, state.state) @@ -76,7 +76,7 @@ class TestSensorMQTT(unittest.TestCase): switch.turn_off(self.hass, 'switch.test') self.hass.pool.block_till_done() - self.assertEqual(('command-topic', 'beer off', 2), + self.assertEqual(('command-topic', 'beer off', 2, False), self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('switch.test') self.assertEqual(STATE_OFF, state.state) From 2861bbb02c1c06b80473176edb7f6e85a5473806 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 Nov 2015 13:08:13 -0800 Subject: [PATCH 063/125] Warn if config invalid shape for script --- homeassistant/components/script.py | 5 +++-- tests/components/test_script.py | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 2b18a5143fd..f8240bbf7f5 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -76,8 +76,9 @@ def setup(hass, config): _LOGGER.warn("Found invalid key for script: %s. Use %s instead.", object_id, slugify(object_id)) continue - if not cfg.get(CONF_SEQUENCE): - _LOGGER.warn("Missing key 'sequence' for script %s", object_id) + if not isinstance(cfg.get(CONF_SEQUENCE), list): + _LOGGER.warn("Key 'sequence' for script %s should be a list", + object_id) continue alias = cfg.get(CONF_ALIAS, object_id) script = Script(hass, object_id, alias, cfg[CONF_SEQUENCE]) diff --git a/tests/components/test_script.py b/tests/components/test_script.py index 50cfba55ec5..30b7e4e3c8f 100644 --- a/tests/components/test_script.py +++ b/tests/components/test_script.py @@ -27,23 +27,10 @@ class TestScript(unittest.TestCase): """ Stop down stuff we started. """ self.hass.stop() - def test_setup_with_empty_sequence(self): - self.assertTrue(script.setup(self.hass, { - 'script': { - 'test': { - 'sequence': [] - } - } - })) - - self.assertIsNone(self.hass.states.get(ENTITY_ID)) - def test_setup_with_missing_sequence(self): self.assertTrue(script.setup(self.hass, { 'script': { - 'test': { - 'sequence': [] - } + 'test': {} } })) @@ -60,6 +47,19 @@ class TestScript(unittest.TestCase): self.assertEqual(0, len(self.hass.states.entity_ids('script'))) + def test_setup_with_dict_as_sequence(self): + self.assertTrue(script.setup(self.hass, { + 'script': { + 'test': { + 'sequence': { + 'event': 'test_event' + } + } + } + })) + + self.assertEqual(0, len(self.hass.states.entity_ids('script'))) + def test_firing_event(self): event = 'test_event' calls = [] From fabd0ced3fe96839b93a9f790fb498a69c996222 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 Nov 2015 13:11:59 -0800 Subject: [PATCH 064/125] Make DEPENDENCIES optional for components --- homeassistant/bootstrap.py | 19 +++++++++---------- .../alarm_control_panel/__init__.py | 1 - .../components/alarm_control_panel/manual.py | 2 -- homeassistant/components/arduino.py | 1 - .../components/binary_sensor/__init__.py | 1 - homeassistant/components/browser.py | 1 - homeassistant/components/configurator.py | 1 - homeassistant/components/conversation.py | 1 - homeassistant/components/discovery.py | 1 - homeassistant/components/downloader.py | 1 - homeassistant/components/group.py | 1 - homeassistant/components/http.py | 1 - homeassistant/components/ifttt.py | 2 -- homeassistant/components/introduction.py | 1 - homeassistant/components/isy994.py | 1 - homeassistant/components/keyboard.py | 1 - homeassistant/components/light/__init__.py | 1 - .../components/light/blinksticklight.py | 1 - homeassistant/components/lock/__init__.py | 1 - homeassistant/components/logger.py | 1 - .../components/media_player/__init__.py | 1 - homeassistant/components/modbus.py | 1 - homeassistant/components/mqtt/__init__.py | 1 - homeassistant/components/notify/__init__.py | 1 - homeassistant/components/recorder.py | 1 - homeassistant/components/rfxtrx.py | 1 - homeassistant/components/sensor/__init__.py | 1 - homeassistant/components/shell_command.py | 1 - homeassistant/components/sun.py | 1 - homeassistant/components/switch/__init__.py | 1 - .../components/thermostat/__init__.py | 1 - homeassistant/components/updater.py | 1 - homeassistant/components/wink.py | 1 - homeassistant/components/zone.py | 1 - homeassistant/components/zwave.py | 1 - homeassistant/loader.py | 2 +- 36 files changed, 10 insertions(+), 47 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 4d68227d4d0..41377aadebf 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -82,7 +82,7 @@ def _setup_component(hass, domain, config): return True component = loader.get_component(domain) - missing_deps = [dep for dep in component.DEPENDENCIES + missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components] if missing_deps: @@ -106,7 +106,7 @@ def _setup_component(hass, domain, config): # Assumption: if a component does not depend on groups # it communicates with devices - if group.DOMAIN not in component.DEPENDENCIES: + if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []): hass.pool.add_worker() hass.bus.fire( @@ -133,14 +133,13 @@ def prepare_setup_platform(hass, config, domain, platform_name): return platform # Load dependencies - if hasattr(platform, 'DEPENDENCIES'): - for component in platform.DEPENDENCIES: - if not setup_component(hass, component, config): - _LOGGER.error( - 'Unable to prepare setup for platform %s because ' - 'dependency %s could not be initialized', platform_path, - component) - return None + for component in getattr(platform, 'DEPENDENCIES', []): + if not setup_component(hass, component, config): + _LOGGER.error( + 'Unable to prepare setup for platform %s because ' + 'dependency %s could not be initialized', platform_path, + component) + return None if not _handle_requirements(hass, platform, platform_path): return None diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index d3289e08e62..3f5e6362fb6 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -15,7 +15,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent DOMAIN = 'alarm_control_panel' -DEPENDENCIES = [] SCAN_INTERVAL = 30 ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/alarm_control_panel/manual.py b/homeassistant/components/alarm_control_panel/manual.py index ca1816db9e6..63bc989f3df 100644 --- a/homeassistant/components/alarm_control_panel/manual.py +++ b/homeassistant/components/alarm_control_panel/manual.py @@ -18,8 +18,6 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = [] - DEFAULT_ALARM_NAME = 'HA Alarm' DEFAULT_PENDING_TIME = 60 DEFAULT_TRIGGER_TIME = 120 diff --git a/homeassistant/components/arduino.py b/homeassistant/components/arduino.py index 0c278ceee63..88967ec1f74 100644 --- a/homeassistant/components/arduino.py +++ b/homeassistant/components/arduino.py @@ -19,7 +19,6 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) DOMAIN = "arduino" -DEPENDENCIES = [] REQUIREMENTS = ['PyMata==2.07a'] BOARD = None _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py index 2ef9e83cc30..ccfd57aff8c 100644 --- a/homeassistant/components/binary_sensor/__init__.py +++ b/homeassistant/components/binary_sensor/__init__.py @@ -14,7 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.const import (STATE_ON, STATE_OFF) DOMAIN = 'binary_sensor' -DEPENDENCIES = [] SCAN_INTERVAL = 30 ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/browser.py b/homeassistant/components/browser.py index db0f3710158..88548e2a1b3 100644 --- a/homeassistant/components/browser.py +++ b/homeassistant/components/browser.py @@ -8,7 +8,6 @@ https://home-assistant.io/components/browser/ """ DOMAIN = "browser" -DEPENDENCIES = [] SERVICE_BROWSE_URL = "browse_url" diff --git a/homeassistant/components/configurator.py b/homeassistant/components/configurator.py index 8bec580abf9..515daffc71c 100644 --- a/homeassistant/components/configurator.py +++ b/homeassistant/components/configurator.py @@ -15,7 +15,6 @@ from homeassistant.helpers import generate_entity_id from homeassistant.const import EVENT_TIME_CHANGED DOMAIN = "configurator" -DEPENDENCIES = [] ENTITY_ID_FORMAT = DOMAIN + ".{}" SERVICE_CONFIGURE = "configure" diff --git a/homeassistant/components/conversation.py b/homeassistant/components/conversation.py index d9cba832df7..7cd1193448c 100644 --- a/homeassistant/components/conversation.py +++ b/homeassistant/components/conversation.py @@ -14,7 +14,6 @@ from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF) DOMAIN = "conversation" -DEPENDENCIES = [] SERVICE_PROCESS = "process" diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 3a43c86f58a..807f1fe3944 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -17,7 +17,6 @@ from homeassistant.const import ( ATTR_SERVICE, ATTR_DISCOVERED) DOMAIN = "discovery" -DEPENDENCIES = [] REQUIREMENTS = ['netdisco==0.5.1'] SCAN_INTERVAL = 300 # seconds diff --git a/homeassistant/components/downloader.py b/homeassistant/components/downloader.py index a69a6ca1517..655bf7d4eb6 100644 --- a/homeassistant/components/downloader.py +++ b/homeassistant/components/downloader.py @@ -15,7 +15,6 @@ from homeassistant.helpers import validate_config from homeassistant.util import sanitize_filename DOMAIN = "downloader" -DEPENDENCIES = [] SERVICE_DOWNLOAD_FILE = "download_file" diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index 9ae83cb734a..52ffe824e42 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -17,7 +17,6 @@ from homeassistant.const import ( STATE_UNKNOWN) DOMAIN = "group" -DEPENDENCIES = [] ENTITY_ID_FORMAT = DOMAIN + ".{}" diff --git a/homeassistant/components/http.py b/homeassistant/components/http.py index a7ae0c5af6e..88392ed3fe4 100644 --- a/homeassistant/components/http.py +++ b/homeassistant/components/http.py @@ -34,7 +34,6 @@ import homeassistant.util.dt as date_util import homeassistant.bootstrap as bootstrap DOMAIN = "http" -DEPENDENCIES = [] CONF_API_PASSWORD = "api_password" CONF_SERVER_HOST = "server_host" diff --git a/homeassistant/components/ifttt.py b/homeassistant/components/ifttt.py index 246265a5268..3721f5b1d33 100644 --- a/homeassistant/components/ifttt.py +++ b/homeassistant/components/ifttt.py @@ -22,8 +22,6 @@ ATTR_VALUE1 = 'value1' ATTR_VALUE2 = 'value2' ATTR_VALUE3 = 'value3' -DEPENDENCIES = [] - REQUIREMENTS = ['pyfttt==0.3'] diff --git a/homeassistant/components/introduction.py b/homeassistant/components/introduction.py index 08a71b27292..540d928f7f5 100644 --- a/homeassistant/components/introduction.py +++ b/homeassistant/components/introduction.py @@ -9,7 +9,6 @@ https://home-assistant.io/components/introduction/ import logging DOMAIN = 'introduction' -DEPENDENCIES = [] def setup(hass, config=None): diff --git a/homeassistant/components/isy994.py b/homeassistant/components/isy994.py index 427ef4f048e..2a36f2060fc 100644 --- a/homeassistant/components/isy994.py +++ b/homeassistant/components/isy994.py @@ -20,7 +20,6 @@ from homeassistant.const import ( ATTR_FRIENDLY_NAME) DOMAIN = "isy994" -DEPENDENCIES = [] REQUIREMENTS = ['PyISY==1.0.5'] DISCOVER_LIGHTS = "isy994.lights" DISCOVER_SWITCHES = "isy994.switches" diff --git a/homeassistant/components/keyboard.py b/homeassistant/components/keyboard.py index ea650d8b421..c772d1c6e74 100644 --- a/homeassistant/components/keyboard.py +++ b/homeassistant/components/keyboard.py @@ -15,7 +15,6 @@ from homeassistant.const import ( DOMAIN = "keyboard" -DEPENDENCIES = [] REQUIREMENTS = ['pyuserinput==0.1.9'] diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index d7f8746de5a..1b80035fb0d 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -21,7 +21,6 @@ import homeassistant.util.color as color_util DOMAIN = "light" -DEPENDENCIES = [] SCAN_INTERVAL = 30 GROUP_NAME_ALL_LIGHTS = 'all lights' diff --git a/homeassistant/components/light/blinksticklight.py b/homeassistant/components/light/blinksticklight.py index 5cc14a9034b..fae9890c93d 100644 --- a/homeassistant/components/light/blinksticklight.py +++ b/homeassistant/components/light/blinksticklight.py @@ -14,7 +14,6 @@ _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ["blinkstick==1.1.7"] -DEPENDENCIES = [] # pylint: disable=unused-argument diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 2cbd3a40872..fdc2da3e8d4 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -20,7 +20,6 @@ from homeassistant.const import ( from homeassistant.components import (group, wink) DOMAIN = 'lock' -DEPENDENCIES = [] SCAN_INTERVAL = 30 GROUP_NAME_ALL_LOCKS = 'all locks' diff --git a/homeassistant/components/logger.py b/homeassistant/components/logger.py index a6dafa56005..9a5d1c59d1a 100644 --- a/homeassistant/components/logger.py +++ b/homeassistant/components/logger.py @@ -10,7 +10,6 @@ import logging from collections import OrderedDict DOMAIN = 'logger' -DEPENDENCIES = [] LOGSEVERITY = { 'CRITICAL': 50, diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 8140bbb2af9..8204052b4a9 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -22,7 +22,6 @@ from homeassistant.const import ( SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK) DOMAIN = 'media_player' -DEPENDENCIES = [] SCAN_INTERVAL = 10 ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/modbus.py b/homeassistant/components/modbus.py index 099801eb7cf..6f53c89835a 100644 --- a/homeassistant/components/modbus.py +++ b/homeassistant/components/modbus.py @@ -13,7 +13,6 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, DOMAIN = "modbus" -DEPENDENCIES = [] REQUIREMENTS = ['https://github.com/bashwork/pymodbus/archive/' 'd7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0'] diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 9ec5169c729..7ce9340b27e 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -30,7 +30,6 @@ DEFAULT_QOS = 0 SERVICE_PUBLISH = 'publish' EVENT_MQTT_MESSAGE_RECEIVED = 'MQTT_MESSAGE_RECEIVED' -DEPENDENCIES = [] REQUIREMENTS = ['paho-mqtt==1.1', 'jsonpath-rw==1.4.0'] CONF_BROKER = 'broker' diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index 6cd7a2196cf..9182f1dbf3a 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -17,7 +17,6 @@ from homeassistant.helpers import config_per_platform from homeassistant.const import CONF_NAME DOMAIN = "notify" -DEPENDENCIES = [] # Title of notification ATTR_TITLE = "title" diff --git a/homeassistant/components/recorder.py b/homeassistant/components/recorder.py index b09e10f7d92..6b2557dd1d6 100644 --- a/homeassistant/components/recorder.py +++ b/homeassistant/components/recorder.py @@ -23,7 +23,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) DOMAIN = "recorder" -DEPENDENCIES = [] DB_FILE = 'home-assistant.db' diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 3c4675d806d..894634c8fc2 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -9,7 +9,6 @@ https://home-assistant.io/components/rfxtrx/ import logging from homeassistant.util import slugify -DEPENDENCIES = [] REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip' + '#RFXtrx==0.2'] diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 32ee59a6fa9..95cb331b91f 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -12,7 +12,6 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.components import wink, zwave, isy994, verisure DOMAIN = 'sensor' -DEPENDENCIES = [] SCAN_INTERVAL = 30 ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/shell_command.py b/homeassistant/components/shell_command.py index 61c9add3f23..5e12c8bfd6e 100644 --- a/homeassistant/components/shell_command.py +++ b/homeassistant/components/shell_command.py @@ -12,7 +12,6 @@ import subprocess from homeassistant.util import slugify DOMAIN = 'shell_command' -DEPENDENCIES = [] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sun.py b/homeassistant/components/sun.py index a5b83e76929..efe0a7dec2b 100644 --- a/homeassistant/components/sun.py +++ b/homeassistant/components/sun.py @@ -15,7 +15,6 @@ import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.helpers.entity import Entity -DEPENDENCIES = [] REQUIREMENTS = ['astral==0.8.1'] DOMAIN = "sun" ENTITY_ID = "sun.sun" diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index 9a0abb4ce7a..e7b3c629f39 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -20,7 +20,6 @@ from homeassistant.components import ( group, discovery, wink, isy994, verisure, zwave) DOMAIN = 'switch' -DEPENDENCIES = [] SCAN_INTERVAL = 30 GROUP_NAME_ALL_SWITCHES = 'all switches' diff --git a/homeassistant/components/thermostat/__init__.py b/homeassistant/components/thermostat/__init__.py index 480e3e4805e..b475fce39d8 100644 --- a/homeassistant/components/thermostat/__init__.py +++ b/homeassistant/components/thermostat/__init__.py @@ -20,7 +20,6 @@ from homeassistant.const import ( TEMP_CELCIUS) DOMAIN = "thermostat" -DEPENDENCIES = [] ENTITY_ID_FORMAT = DOMAIN + ".{}" SCAN_INTERVAL = 60 diff --git a/homeassistant/components/updater.py b/homeassistant/components/updater.py index 803cfa609ca..a020a6c0abb 100644 --- a/homeassistant/components/updater.py +++ b/homeassistant/components/updater.py @@ -16,7 +16,6 @@ from homeassistant.helpers import event _LOGGER = logging.getLogger(__name__) PYPI_URL = 'https://pypi.python.org/pypi/homeassistant/json' -DEPENDENCIES = [] DOMAIN = 'updater' ENTITY_ID = 'updater.updater' diff --git a/homeassistant/components/wink.py b/homeassistant/components/wink.py index bd79210bf75..1ab82236596 100644 --- a/homeassistant/components/wink.py +++ b/homeassistant/components/wink.py @@ -17,7 +17,6 @@ from homeassistant.const import ( ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME) DOMAIN = "wink" -DEPENDENCIES = [] REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/' '42fdcfa721b1bc583688e3592d8427f4c13ba6d9.zip' '#python-wink==0.2'] diff --git a/homeassistant/components/zone.py b/homeassistant/components/zone.py index a32a297caeb..da0341129f7 100644 --- a/homeassistant/components/zone.py +++ b/homeassistant/components/zone.py @@ -15,7 +15,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util.location import distance DOMAIN = "zone" -DEPENDENCIES = [] ENTITY_ID_FORMAT = 'zone.{}' ENTITY_ID_HOME = ENTITY_ID_FORMAT.format('home') STATE = 'zoning' diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 11515e4031d..5d526293083 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -17,7 +17,6 @@ from homeassistant.const import ( EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED) DOMAIN = "zwave" -DEPENDENCIES = [] REQUIREMENTS = ['pydispatcher==2.0.5'] CONF_USB_STICK_PATH = "usb_path" diff --git a/homeassistant/loader.py b/homeassistant/loader.py index b05083b4abd..8b38f5e0966 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -193,7 +193,7 @@ def _load_order_component(comp_name, load_order, loading): loading.add(comp_name) - for dependency in component.DEPENDENCIES: + for dependency in getattr(component, 'DEPENDENCIES', []): # Check not already loaded if dependency in load_order: continue From 67bcb00c9e28c546a17bc91862d74c41705b4672 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 26 Nov 2015 19:09:32 +0100 Subject: [PATCH 065/125] Add demo platform for alarm control panel --- .../components/alarm_control_panel/demo.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 homeassistant/components/alarm_control_panel/demo.py diff --git a/homeassistant/components/alarm_control_panel/demo.py b/homeassistant/components/alarm_control_panel/demo.py new file mode 100644 index 00000000000..c20a0462540 --- /dev/null +++ b/homeassistant/components/alarm_control_panel/demo.py @@ -0,0 +1,74 @@ +""" +homeassistant.components.alarm_control_panel.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has two fake alarm control panels. +""" +import homeassistant.components.alarm_control_panel as Alarm +from homeassistant.const import (STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, + STATE_ALARM_ARMED_AWAY) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo alarm control panels. """ + add_devices([ + DemoAlarmControlPanel('Front door', '1234', STATE_ALARM_ARMED_HOME), + DemoAlarmControlPanel('Safe', '1234', STATE_ALARM_ARMED_AWAY), + ]) + + +class DemoAlarmControlPanel(Alarm.AlarmControlPanel): + """ A Demo alarm control panel. """ + + def __init__(self, name, code, state): + self._state = state + self._name = name + self._code = str(code) if code else None + + @property + def should_poll(self): + """ No polling needed. """ + return False + + @property + def name(self): + """ Returns the name of the device. """ + return self._name + + @property + def state(self): + """ Returns the state of the device. """ + return self._state + + @property + def code_format(self): + """ One or more characters. """ + return None if self._code is None else '.+' + + def alarm_disarm(self, code=None): + """ Send disarm command. """ + if not self._validate_code(code, STATE_ALARM_DISARMED): + return + self._state = STATE_ALARM_DISARMED + self.update_ha_state() + + def alarm_arm_home(self, code=None): + """ Send arm home command. """ + if not self._validate_code(code, STATE_ALARM_ARMED_HOME): + return + self._state = STATE_ALARM_ARMED_HOME + self.update_ha_state() + + def alarm_arm_away(self, code=None): + """ Send arm away command. """ + if not self._validate_code(code, STATE_ALARM_ARMED_AWAY): + return + self._state = STATE_ALARM_ARMED_AWAY + self.update_ha_state() + + def alarm_trigger(self, code=None): + """ Send alarm trigger command. No code needed. """ + pass + + def _validate_code(self, code, state): + """ Validate given code. """ + return self._code is None or code == self._code From 60460e82174c16625dd11966df70a8157b24777d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 26 Nov 2015 19:10:06 +0100 Subject: [PATCH 066/125] Add alarm control panel as platform and remove camera/acp entries --- homeassistant/components/demo.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 547a1d54a03..261c0e1b6a9 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -18,7 +18,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', - 'thermostat', 'camera', 'binary_sensor'] + 'thermostat', 'camera', 'binary_sensor', 'alarm_control_panel'] def setup(hass, config): @@ -55,23 +55,6 @@ def setup(hass, config): group.setup_group(hass, 'bedroom', [lights[0], switches[1], media_players[0]]) - # Setup IP Camera - bootstrap.setup_component( - hass, 'camera', - {'camera': { - 'platform': 'generic', - 'name': 'IP Camera', - 'still_image_url': 'http://home-assistant.io/demo/webcam.jpg', - }}) - - # Setup alarm_control_panel - bootstrap.setup_component( - hass, 'alarm_control_panel', - {'alarm_control_panel': { - 'platform': 'manual', - 'name': 'Test Alarm', - }}) - # Setup scripts bootstrap.setup_component( hass, 'script', From 7224775aa87e2abf60a0424aacf9a563821e4e7e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 27 Nov 2015 00:40:51 +0100 Subject: [PATCH 067/125] Use manual alarm control panel as base for demo --- .../components/alarm_control_panel/demo.py | 60 ++++--------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/demo.py b/homeassistant/components/alarm_control_panel/demo.py index c20a0462540..2d2e36d03bc 100644 --- a/homeassistant/components/alarm_control_panel/demo.py +++ b/homeassistant/components/alarm_control_panel/demo.py @@ -3,72 +3,36 @@ homeassistant.components.alarm_control_panel.demo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Demo platform that has two fake alarm control panels. """ -import homeassistant.components.alarm_control_panel as Alarm -from homeassistant.const import (STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, +import homeassistant.components.alarm_control_panel.manual as Alarm +from homeassistant.const import (STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY) def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Demo alarm control panels. """ add_devices([ - DemoAlarmControlPanel('Front door', '1234', STATE_ALARM_ARMED_HOME), - DemoAlarmControlPanel('Safe', '1234', STATE_ALARM_ARMED_AWAY), + + DemoAlarmControlPanel(hass, 'Front door', '1234', 2, 4, + STATE_ALARM_DISARMED), + DemoAlarmControlPanel(hass, 'Safe', '1234', 2, 4, + STATE_ALARM_ARMED_AWAY), ]) -class DemoAlarmControlPanel(Alarm.AlarmControlPanel): +# pylint: disable=too-many-arguments +class DemoAlarmControlPanel(Alarm.ManualAlarm): """ A Demo alarm control panel. """ - def __init__(self, name, code, state): + def __init__(self, hass, name, code, pending_time, trigger_time, state): + super().__init__(hass, name, code, pending_time, trigger_time) self._state = state - self._name = name - self._code = str(code) if code else None @property def should_poll(self): - """ No polling needed. """ + """ No polling needed for a demo panel. """ return False - @property - def name(self): - """ Returns the name of the device. """ - return self._name - @property def state(self): """ Returns the state of the device. """ return self._state - - @property - def code_format(self): - """ One or more characters. """ - return None if self._code is None else '.+' - - def alarm_disarm(self, code=None): - """ Send disarm command. """ - if not self._validate_code(code, STATE_ALARM_DISARMED): - return - self._state = STATE_ALARM_DISARMED - self.update_ha_state() - - def alarm_arm_home(self, code=None): - """ Send arm home command. """ - if not self._validate_code(code, STATE_ALARM_ARMED_HOME): - return - self._state = STATE_ALARM_ARMED_HOME - self.update_ha_state() - - def alarm_arm_away(self, code=None): - """ Send arm away command. """ - if not self._validate_code(code, STATE_ALARM_ARMED_AWAY): - return - self._state = STATE_ALARM_ARMED_AWAY - self.update_ha_state() - - def alarm_trigger(self, code=None): - """ Send alarm trigger command. No code needed. """ - pass - - def _validate_code(self, code, state): - """ Validate given code. """ - return self._code is None or code == self._code From 7204bc018fc127a9f2b677a1f184a25b9454b26a Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 27 Nov 2015 11:43:43 +0000 Subject: [PATCH 068/125] Add exception handler for vera timeout --- homeassistant/components/switch/vera.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/vera.py b/homeassistant/components/switch/vera.py index 80724f92757..14983919c64 100644 --- a/homeassistant/components/switch/vera.py +++ b/homeassistant/components/switch/vera.py @@ -126,5 +126,8 @@ class VeraSwitch(ToggleEntity): def update(self): # We need to debounce the status call after turning switch on or off # because the vera has some lag in updating the device status - if (self.last_command_send + 5) < time.time(): - self.is_on_status = self.vera_device.is_switched_on() + try: + if (self.last_command_send + 5) < time.time(): + self.is_on_status = self.vera_device.is_switched_on() + except RequestException: + _LOGGER.warning('Could not update status for %s', self.name) From 00f0dfb97104d465c90942fb0e2190bc99742c90 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 27 Nov 2015 18:21:26 +0000 Subject: [PATCH 069/125] Trap and trace error rather than throwing exception when efergy server --- homeassistant/components/sensor/efergy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/efergy.py b/homeassistant/components/sensor/efergy.py index 99140b5b2a9..447903a714e 100644 --- a/homeassistant/components/sensor/efergy.py +++ b/homeassistant/components/sensor/efergy.py @@ -97,5 +97,5 @@ class EfergySensor(Entity): self._state = response.json()['sum'] else: self._state = 'Unknown' - except RequestException: + except (RequestException, ValueError): _LOGGER.warning('Could not update status for %s', self.name) From bbf73e0bae587583bc16c0e87942e29cb4787566 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 27 Nov 2015 19:05:15 +0000 Subject: [PATCH 070/125] Bump pywemo version - fixed scan device = None bug --- homeassistant/components/switch/wemo.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 1d701bf88cc..bad471ce437 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.2'] +REQUIREMENTS = ['pywemo==0.3.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index a9f24cdb65f..982cf125572 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -149,7 +149,7 @@ hikvision==0.4 orvibo==1.0.0 # homeassistant.components.switch.wemo -pywemo==0.3.2 +pywemo==0.3.3 # homeassistant.components.thermostat.honeywell evohomeclient==0.2.3 From d91fe792c56d872e9054fd608978fd51a99142dd Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sat, 14 Nov 2015 12:56:18 -0500 Subject: [PATCH 071/125] limitlessled improvements --- .../components/light/limitlessled.py | 348 +++++++++++------- requirements_all.txt | 2 +- 2 files changed, 226 insertions(+), 124 deletions(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index ad5f8487a2a..8700dd85bf2 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -8,171 +8,273 @@ https://home-assistant.io/components/light.limitlessled/ """ import logging -from homeassistant.const import DEVICE_DEFAULT_NAME from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_EFFECT, + ATTR_COLOR_TEMP, ATTR_TRANSITION, + ATTR_FLASH, FLASH_LONG, EFFECT_COLORLOOP, EFFECT_WHITE) +from limitlessled import Color +from limitlessled.bridge import Bridge +from limitlessled.group.rgbw import RgbwGroup +from limitlessled.group.white import WhiteGroup +from limitlessled.pipeline import Pipeline +from limitlessled.presets import COLORLOOP + + _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['ledcontroller==1.1.0'] - -COLOR_TABLE = { - 'white': [0xFF, 0xFF, 0xFF], - 'violet': [0xEE, 0x82, 0xEE], - 'royal_blue': [0x41, 0x69, 0xE1], - 'baby_blue': [0x87, 0xCE, 0xFA], - 'aqua': [0x00, 0xFF, 0xFF], - 'royal_mint': [0x7F, 0xFF, 0xD4], - 'seafoam_green': [0x2E, 0x8B, 0x57], - 'green': [0x00, 0x80, 0x00], - 'lime_green': [0x32, 0xCD, 0x32], - 'yellow': [0xFF, 0xFF, 0x00], - 'yellow_orange': [0xDA, 0xA5, 0x20], - 'orange': [0xFF, 0xA5, 0x00], - 'red': [0xFF, 0x00, 0x00], - 'pink': [0xFF, 0xC0, 0xCB], - 'fusia': [0xFF, 0x00, 0xFF], - 'lilac': [0xDA, 0x70, 0xD6], - 'lavendar': [0xE6, 0xE6, 0xFA], -} +REQUIREMENTS = ['limitlessled==1.0.0'] +RGB_BOUNDARY = 40 +DEFAULT_TRANSITION = 0 +DEFAULT_PORT = 8899 +DEFAULT_VERSION = 5 +DEFAULT_LED_TYPE = 'rgbw' +WHITE = [255, 255, 255] -def _distance_squared(rgb1, rgb2): - """ Return sum of squared distances of each color part. """ - return sum((val1-val2)**2 for val1, val2 in zip(rgb1, rgb2)) - - -def _rgb_to_led_color(rgb_color): - """ Convert an RGB color to the closest color string and color. """ - return sorted((_distance_squared(rgb_color, color), name) - for name, color in COLOR_TABLE.items())[0][1] +def legacy_setup(config, add_devices_callback): + """ Perform setup using legacy format. """ + bridges = config.get('bridges', [config]) + lights = [] + for bridge_conf in bridges: + bridge = Bridge(bridge_conf.get('host')) + for i in range(1, 5): + name_key = 'group_%d_name' % i + if name_key in bridge_conf: + group_type = bridge_conf.get('group_%d_type' % i, + DEFAULT_LED_TYPE) + group = bridge.add_group(i, + bridge_conf.get(name_key), + group_type) + lights.append(LimitlessLEDGroup.factory(group)) + add_devices_callback(lights) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Gets the LimitlessLED lights. """ - import ledcontroller - # Handle old configuration format: - bridges = config.get('bridges', [config]) - - for bridge_id, bridge in enumerate(bridges): - bridge['id'] = bridge_id - - pool = ledcontroller.LedControllerPool([x['host'] for x in bridges]) + # Two legacy configuration formats are supported to + # maintain backwards compatibility. + legacy_setup(config, add_devices_callback) + # Use the expanded configuration format. + if 'bridges' not in config: + return lights = [] - for bridge in bridges: - for i in range(1, 5): - name_key = 'group_%d_name' % i - if name_key in bridge: - group_type = bridge.get('group_%d_type' % i, 'rgbw') - lights.append(LimitlessLED.factory(pool, bridge['id'], i, - bridge[name_key], - group_type)) - + for bridge_conf in config.get('bridges'): + if 'groups' not in bridge_conf: + continue + bridge = Bridge(bridge_conf.get('host'), + port=bridge_conf.get('port', DEFAULT_PORT), + version=bridge_conf.get('version', DEFAULT_VERSION)) + for group_conf in bridge_conf.get('groups'): + group = bridge.add_group(group_conf.get('number'), + group_conf.get('name'), + group_conf.get('type', DEFAULT_LED_TYPE)) + lights.append(LimitlessLEDGroup.factory(group)) add_devices_callback(lights) -class LimitlessLED(Light): - """ Represents a LimitlessLED light """ +def state(new_state): + """ State decorator. + + Specify True (turn on) or False (turn off). + """ + def decorator(function): + """ Decorator function. """ + # pylint: disable=no-member + def wrapper(self, **kwargs): + """ Wrap a group state change. """ + pipeline = Pipeline() + transition_time = DEFAULT_TRANSITION + # Stop any repeating pipeline. + if self.repeating: + self.repeating = False + self.group.stop() + # Not on? Turn on. + if not self.is_on: + pipeline.on() + # Set transition time. + if ATTR_TRANSITION in kwargs: + transition_time = kwargs[ATTR_TRANSITION] + # Do group type-specific work. + function(self, transition_time, pipeline, **kwargs) + # Update state. + self.on_state = new_state + self.group.enqueue(pipeline) + self.update_ha_state() + return wrapper + return decorator + + +class LimitlessLEDGroup(Light): + """ LimitessLED group. """ + def __init__(self, group): + """ Initialize a group. """ + self.group = group + self.repeating = False + self.on_state = False + self._brightness = None @staticmethod - def factory(pool, controller_id, group, name, group_type): - ''' Construct a Limitless LED of the appropriate type ''' - if group_type == 'white': - return WhiteLimitlessLED(pool, controller_id, group, name) - elif group_type == 'rgbw': - return RGBWLimitlessLED(pool, controller_id, group, name) - - # pylint: disable=too-many-arguments - def __init__(self, pool, controller_id, group, name, group_type): - self.pool = pool - self.controller_id = controller_id - self.group = group - - self.pool.execute(self.controller_id, "set_group_type", self.group, - group_type) - - # LimitlessLEDs don't report state, we have track it ourselves. - self.pool.execute(self.controller_id, "off", self.group) - - self._name = name or DEVICE_DEFAULT_NAME - self._state = False + def factory(group): + """ Produce LimitlessLEDGroup objects. """ + if isinstance(group, WhiteGroup): + return LimitlessLEDWhiteGroup(group) + elif isinstance(group, RgbwGroup): + return LimitlessLEDRGBWGroup(group) @property def should_poll(self): - """ No polling needed. """ + """ No polling needed. + + LimitlessLED state cannot be fetched. + """ return False @property def name(self): - """ Returns the name of the device if any. """ - return self._name + """ Returns the name of the group. """ + return self.group.name @property def is_on(self): """ True if device is on. """ - return self._state - - def turn_off(self, **kwargs): - """ Turn the device off. """ - self._state = False - self.pool.execute(self.controller_id, "off", self.group) - self.update_ha_state() - - -class RGBWLimitlessLED(LimitlessLED): - """ Represents a RGBW LimitlessLED light """ - - def __init__(self, pool, controller_id, group, name): - super().__init__(pool, controller_id, group, name, 'rgbw') - - self._brightness = 100 - self._led_color = 'white' + return self.on_state @property def brightness(self): + """ Brightness property. """ return self._brightness + @state(False) + def turn_off(self, transition_time, pipeline, **kwargs): + """ Turn off a group. """ + pipeline.transition(transition_time, brightness=0.0).off() + + +class LimitlessLEDWhiteGroup(LimitlessLEDGroup): + """ LimitlessLED White group. """ + def __init__(self, group): + """ Initialize White group. """ + super().__init__(group) + # Initialize group with known values. + self.group.on = True + self.group.temperature = 1.0 + self.group.brightness = 0.0 + self._brightness = _to_hass_brightness(1.0) + self._temperature = _to_hass_temperature(self.group.temperature) + self.group.on = False + + @property + def color_temp(self): + """ Temperature property. """ + return self._temperature + + @state(True) + def turn_on(self, transition_time, pipeline, **kwargs): + """ Turn on (or adjust property of) a group. """ + # Check arguments. + if ATTR_BRIGHTNESS in kwargs: + self._brightness = kwargs[ATTR_BRIGHTNESS] + if ATTR_COLOR_TEMP in kwargs: + self._temperature = kwargs[ATTR_COLOR_TEMP] + # Set up transition. + pipeline.transition(transition_time, + brightness=_from_hass_brightness( + self._brightness), + temperature=_from_hass_temperature( + self._temperature)) + + +class LimitlessLEDRGBWGroup(LimitlessLEDGroup): + """ LimitlessLED RGBW group. """ + def __init__(self, group): + """ Initialize RGBW group. """ + super().__init__(group) + # Initialize group with known values. + self.group.on = True + self.group.white() + self._color = WHITE + self.group.brightness = 0.0 + self._brightness = _to_hass_brightness(1.0) + self.group.on = False + @property def rgb_color(self): - return COLOR_TABLE[self._led_color] - - def turn_on(self, **kwargs): - """ Turn the device on. """ - self._state = True + """ Color property. """ + return self._color + @state(True) + def turn_on(self, transition_time, pipeline, **kwargs): + """ Turn on (or adjust property of) a group. """ + # Check arguments. if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] - if ATTR_RGB_COLOR in kwargs: - self._led_color = _rgb_to_led_color(kwargs[ATTR_RGB_COLOR]) - - effect = kwargs.get(ATTR_EFFECT) - - if effect == EFFECT_COLORLOOP: - self.pool.execute(self.controller_id, "disco", self.group) - elif effect == EFFECT_WHITE: - self.pool.execute(self.controller_id, "white", self.group) - else: - self.pool.execute(self.controller_id, "set_color", - self._led_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() + self._color = kwargs[ATTR_RGB_COLOR] + # White is a special case. + if min(self._color) > 256 - RGB_BOUNDARY: + pipeline.white() + self._color = WHITE + # Set up transition. + pipeline.transition(transition_time, + brightness=_from_hass_brightness( + self._brightness), + color=_from_hass_color(self._color)) + # Flash. + if ATTR_FLASH in kwargs: + duration = 0 + if kwargs[ATTR_FLASH] == FLASH_LONG: + duration = 1 + pipeline.flash(duration=duration) + # Add effects. + if ATTR_EFFECT in kwargs: + if kwargs[ATTR_EFFECT] == EFFECT_COLORLOOP: + self.repeating = True + pipeline.append(COLORLOOP) + if kwargs[ATTR_EFFECT] == EFFECT_WHITE: + pipeline.white() + self._color = WHITE -class WhiteLimitlessLED(LimitlessLED): - """ Represents a White LimitlessLED light """ +def _from_hass_temperature(temperature): + """ Convert Home Assistant color temperature + units to percentage. + """ + return (temperature - 154) / 346 - def __init__(self, pool, controller_id, group, name): - super().__init__(pool, controller_id, group, name, 'white') - def turn_on(self, **kwargs): - """ Turn the device on. """ - self._state = True - self.pool.execute(self.controller_id, "on", self.group) - self.update_ha_state() +def _to_hass_temperature(temperature): + """ Convert percentage to Home Assistant + color temperature units. + """ + return int(temperature * 346) + 154 + + +def _from_hass_brightness(brightness): + """ Convert Home Assistant brightness units + to percentage. + """ + return brightness / 255 + + +def _to_hass_brightness(brightness): + """ Convert percentage to Home Assistant + brightness units. + """ + return int(brightness * 255) + + +def _from_hass_color(color): + """ Convert Home Assistant RGB list + to Color tuple. + """ + return Color(*tuple(color)) + + +def _to_hass_color(color): + """ Convert from Color tuple to + Home Assistant RGB list. + """ + return list([int(c) for c in color]) diff --git a/requirements_all.txt b/requirements_all.txt index a9f24cdb65f..e563895d94b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -36,7 +36,7 @@ blinkstick==1.1.7 phue==0.8 # homeassistant.components.light.limitlessled -ledcontroller==1.1.0 +limitlessled==1.0.0 # homeassistant.components.light.tellstick # homeassistant.components.sensor.tellstick From 6809a881faf0cf2a4e2036805a1327ff39b48d29 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 01:02:35 -0800 Subject: [PATCH 072/125] Tweak MQTT Motor component --- homeassistant/components/motor/__init__.py | 20 ++++++--- homeassistant/components/motor/mqtt.py | 16 -------- tests/components/motor/test_mqtt.py | 48 +++++++++++++++++++++- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/motor/__init__.py b/homeassistant/components/motor/__init__.py index feea836a0dd..806dd9e0eb1 100644 --- a/homeassistant/components/motor/__init__.py +++ b/homeassistant/components/motor/__init__.py @@ -17,7 +17,6 @@ from homeassistant.const import ( DOMAIN = 'motor' -DEPENDENCIES = [] SCAN_INTERVAL = 15 GROUP_NAME_ALL_MOTORS = 'all motors' @@ -30,6 +29,8 @@ DISCOVERY_PLATFORMS = {} _LOGGER = logging.getLogger(__name__) +ATTR_CURRENT_POSITION = 'current_position' + def is_open(hass, entity_id=None): """ Returns if the motor is open based on the statemachine. """ @@ -114,17 +115,24 @@ class MotorDevice(Entity): @property def state_attributes(self): - """ Returns optional state attributes. """ - return None + """ Return the state attributes. """ + current = self.current_position + + if current is None: + return None + + return { + ATTR_CURRENT_POSITION: current + } def open(self, **kwargs): - """ Open the device. """ + """ Open the motor. """ raise NotImplementedError() def close(self, **kwargs): - """ Close the device. """ + """ Close the motor. """ raise NotImplementedError() def stop(self, **kwargs): - """ Stop the device. """ + """ Stop the motor. """ raise NotImplementedError() diff --git a/homeassistant/components/motor/mqtt.py b/homeassistant/components/motor/mqtt.py index 2aac5e37c16..1075030fc37 100644 --- a/homeassistant/components/motor/mqtt.py +++ b/homeassistant/components/motor/mqtt.py @@ -18,10 +18,7 @@ DEFAULT_PAYLOAD_OPEN = "OPEN" DEFAULT_PAYLOAD_CLOSE = "CLOSE" DEFAULT_PAYLOAD_STOP = "STOP" -ATTR_CURRENT_POSITION = 'current_position' - -# pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Add MQTT Motor """ @@ -88,11 +85,6 @@ class MqttMotor(MotorDevice): None is unknown, 0 is closed, 100 is fully open. """ return self._state - @property - def is_open(self): - """ True if device is current position is not zero. """ - return self._state > 0 - def open(self, **kwargs): """ Open the device. """ mqtt.publish(self.hass, self._command_topic, self._payload_open, @@ -107,11 +99,3 @@ class MqttMotor(MotorDevice): """ Stop the device. """ mqtt.publish(self.hass, self._command_topic, self._payload_stop, self._qos) - - @property - def state_attributes(self): - """ Return the state attributes. """ - state_attr = {} - if self._state is not None: - state_attr[ATTR_CURRENT_POSITION] = self._state - return state_attr diff --git a/tests/components/motor/test_mqtt.py b/tests/components/motor/test_mqtt.py index 4d7af04ae6c..1638e830f2d 100644 --- a/tests/components/motor/test_mqtt.py +++ b/tests/components/motor/test_mqtt.py @@ -58,7 +58,7 @@ class TestMotorMQTT(unittest.TestCase): state = self.hass.states.get('motor.test') self.assertEqual(STATE_OPEN, state.state) - def test_sending_mqtt_commands(self): + def test_send_open_command(self): self.assertTrue(motor.setup(self.hass, { 'motor': { 'platform': 'mqtt', @@ -75,7 +75,51 @@ class TestMotorMQTT(unittest.TestCase): motor.call_open(self.hass, 'motor.test') self.hass.pool.block_till_done() - self.assertEqual(('command-topic', 'OPEN', 2), + self.assertEqual(('command-topic', 'OPEN', 2, False), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('motor.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + def test_send_close_command(self): + self.assertTrue(motor.setup(self.hass, { + 'motor': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'qos': 2 + } + })) + + state = self.hass.states.get('motor.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + motor.call_close(self.hass, 'motor.test') + self.hass.pool.block_till_done() + + self.assertEqual(('command-topic', 'CLOSE', 2, False), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('motor.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + def test_send_stop_command(self): + self.assertTrue(motor.setup(self.hass, { + 'motor': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'qos': 2 + } + })) + + state = self.hass.states.get('motor.test') + self.assertEqual(STATE_UNKNOWN, state.state) + + motor.call_stop(self.hass, 'motor.test') + self.hass.pool.block_till_done() + + self.assertEqual(('command-topic', 'STOP', 2, False), self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('motor.test') self.assertEqual(STATE_UNKNOWN, state.state) From 1146d6e58d4187da0300ddd08d9b3cc5459bb006 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 01:18:02 -0800 Subject: [PATCH 073/125] Allow thermostat temperatures with decimal numbers --- homeassistant/components/frontend/version.py | 2 +- .../components/frontend/www_static/frontend.html | 6 +++--- .../frontend/www_static/home-assistant-polymer | 2 +- homeassistant/components/thermostat/__init__.py | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 0199b05156e..ed954909e66 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 = "dff74f773ea8b0356b0bd8130ed6f0cf" +VERSION = "c90d40a0240cc1feec791ee820d928b3" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index b2a2eb97b3e..73b2ac1b7dd 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -5838,7 +5838,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN .has-away_mode .away-mode-toggle { display: block; - }
- -
Initializing
+ +
diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index ed954909e66..7e957562bba 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 = "c90d40a0240cc1feec791ee820d928b3" +VERSION = "c1df3a08faa4a9978b25639ca0fd63cd" diff --git a/homeassistant/components/frontend/www_static/favicon-384x384.png b/homeassistant/components/frontend/www_static/favicon-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..51f67770790077d5e271231ebeadf09a08feb52c GIT binary patch literal 19825 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 CBmXY| literal 0 HcmV?d00001 diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 73b2ac1b7dd..cf19d314f24 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,6 @@ -
\ 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 62e494bd045..f29b1062b30 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 62e494bd04509e8d9b73354b0e17d3381955e0c8 +Subproject commit f29b1062b30c76ceeea87fd426a042d98358394a diff --git a/homeassistant/components/frontend/www_static/manifest.json b/homeassistant/components/frontend/www_static/manifest.json index 69143ce5179..3d0eb5fa443 100644 --- a/homeassistant/components/frontend/www_static/manifest.json +++ b/homeassistant/components/frontend/www_static/manifest.json @@ -5,10 +5,14 @@ "display": "standalone", "icons": [ { - "src": "\/static\/favicon-192x192.png", + "src": "/static/favicon-192x192.png", "sizes": "192x192", - "type": "image\/png", - "density": "4.0" + "type": "image/png", + }, + { + "src": "/static/favicon-384x384.png", + "sizes": "384x384", + "type": "image/png", } ] } diff --git a/homeassistant/components/frontend/www_static/webcomponents-lite.min.js b/homeassistant/components/frontend/www_static/webcomponents-lite.min.js index 3a3fd4e8564..4f8af01fd15 100644 --- a/homeassistant/components/frontend/www_static/webcomponents-lite.min.js +++ b/homeassistant/components/frontend/www_static/webcomponents-lite.min.js @@ -7,6 +7,6 @@ * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -// @version 0.7.17 -!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents-lite.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,r=e.split("=");r[0]&&(t=r[0].match(/wc-(.+)/))&&(n[t[1]]=r[1]||!0)}),t)for(var r,o=0;r=t.attributes[o];o++)"src"!==r.name&&(n[r.name]=r.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),function(e){"use strict";function t(e){return void 0!==h[e]}function n(){s.call(this),this._isInvalid=!0}function r(e){return""==e&&n.call(this),e.toLowerCase()}function o(e){var t=e.charCodeAt(0);return t>32&&127>t&&-1==[34,35,60,62,63,96].indexOf(t)?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&127>t&&-1==[34,35,60,62,96].indexOf(t)?e:encodeURIComponent(e)}function a(e,a,s){function c(e){g.push(e)}var d=a||"scheme start",u=0,l="",_=!1,w=!1,g=[];e:for(;(e[u-1]!=p||0==u)&&!this._isInvalid;){var b=e[u];switch(d){case"scheme start":if(!b||!m.test(b)){if(a){c("Invalid scheme.");break e}l="",d="no scheme";continue}l+=b.toLowerCase(),d="scheme";break;case"scheme":if(b&&v.test(b))l+=b.toLowerCase();else{if(":"!=b){if(a){if(p==b)break e;c("Code point not allowed in scheme: "+b);break e}l="",u=0,d="no scheme";continue}if(this._scheme=l,l="",a)break e;t(this._scheme)&&(this._isRelative=!0),d="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==b?(this._query="?",d="query"):"#"==b?(this._fragment="#",d="fragment"):p!=b&&" "!=b&&"\n"!=b&&"\r"!=b&&(this._schemeData+=o(b));break;case"no scheme":if(s&&t(s._scheme)){d="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=b||"/"!=e[u+1]){c("Expected /, got: "+b),d="relative";continue}d="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),p==b){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==b||"\\"==b)"\\"==b&&c("\\ is an invalid code point."),d="relative slash";else if("?"==b)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,d="query";else{if("#"!=b){var y=e[u+1],E=e[u+2];("file"!=this._scheme||!m.test(b)||":"!=y&&"|"!=y||p!=E&&"/"!=E&&"\\"!=E&&"?"!=E&&"#"!=E)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),d="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,d="fragment"}break;case"relative slash":if("/"!=b&&"\\"!=b){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),d="relative path";continue}"\\"==b&&c("\\ is an invalid code point."),d="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=b){c("Expected '/', got: "+b),d="authority ignore slashes";continue}d="authority second slash";break;case"authority second slash":if(d="authority ignore slashes","/"!=b){c("Expected '/', got: "+b);continue}break;case"authority ignore slashes":if("/"!=b&&"\\"!=b){d="authority";continue}c("Expected authority, got: "+b);break;case"authority":if("@"==b){_&&(c("@ already seen."),l+="%40"),_=!0;for(var L=0;L>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return t&&t[0]===e?(t[0]=t[1]=void 0,!0):!1},has:function(e){var t=e[this.name];return t?t[0]===e:!1}},window.WeakMap=n}(),function(e){function t(e){b.push(e),g||(g=!0,m(r))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function r(){g=!1;var e=b;b=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();o(e),n.length&&(e.callback_(n,e),t=!0)}),t&&r()}function o(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var r=v.get(n);if(r)for(var o=0;o0){var o=n[r-1],i=f(o,e);if(i)return void(n[r-1]=i)}else t(this.observer);n[r]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n":return">";case" ":return" "}}function t(t){return t.replace(a,e)}var n="template",r=document.implementation.createHTMLDocument("template"),o=!0;HTMLTemplateElement=function(){},HTMLTemplateElement.prototype=Object.create(HTMLElement.prototype),HTMLTemplateElement.decorate=function(e){e.content||(e.content=r.createDocumentFragment());for(var n;n=e.firstChild;)e.content.appendChild(n);if(o)try{Object.defineProperty(e,"innerHTML",{get:function(){for(var e="",n=this.content.firstChild;n;n=n.nextSibling)e+=n.outerHTML||t(n.data);return e},set:function(e){for(r.body.innerHTML=e,HTMLTemplateElement.bootstrap(r);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;r.body.firstChild;)this.content.appendChild(r.body.firstChild)},configurable:!0})}catch(i){o=!1}},HTMLTemplateElement.bootstrap=function(e){for(var t,r=e.querySelectorAll(n),o=0,i=r.length;i>o&&(t=r[o]);o++)HTMLTemplateElement.decorate(t)},document.addEventListener("DOMContentLoaded",function(){HTMLTemplateElement.bootstrap(document)});var i=document.createElement;document.createElement=function(){"use strict";var e=i.apply(document,arguments);return"template"==e.localName&&HTMLTemplateElement.decorate(e),e};var a=/[&\u00A0<>]/g}(),function(e){"use strict";if(!window.performance){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var r=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(r.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}))}}var o=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||o&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||o&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||p,r(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===_}function r(e,t){if(n(t))e&&e();else{var o=function(){("complete"===t.readyState||t.readyState===_)&&(t.removeEventListener(w,o),r(e,t))};t.addEventListener(w,o)}}function o(e){e.target.__loaded=!0}function i(e,t){function n(){c==d&&e&&e({allImports:s,loadedImports:u,errorImports:l})}function r(e){o(e),u.push(this),c++,n()}function i(e){l.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,d=s.length,u=[],l=[];if(d)for(var h,f=0;d>f&&(h=s[f]);f++)a(h)?(c++,n()):(h.addEventListener("load",r),h.addEventListener("error",i));else n()}function a(e){return l?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)c(t)&&d(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function d(e){var t=e["import"];t?o({target:e}):(e.addEventListener("load",o),e.addEventListener("error",o))}var u="import",l=Boolean(u in document.createElement("link")),h=Boolean(window.ShadowDOMPolyfill),f=function(e){return h?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},p=f(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return f(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(p,"_currentScript",m);var v=/Trident/.test(navigator.userAgent),_=v?"complete":"interactive",w="readystatechange";l&&(new MutationObserver(function(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,r=t.length;r>n&&(e=t[n]);n++)d(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=p.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),p.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=u,e.useNative=l,e.rootDocument=p,e.whenReady=t,e.isIE=v}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},r=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=r}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,r={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,r=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,r),e},resolveUrlsInCssText:function(e,r,o){var i=this.replaceUrls(e,o,r,t);return i=this.replaceUrls(i,o,r,n)},replaceUrls:function(e,t,n,r){return e.replace(r,function(e,r,o,i){var a=o.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,r+"'"+a+"'"+i})}};e.path=r}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,r,o){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}r.call(o,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,r=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};r.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,r){if(n.load&&console.log("fetch",e,r),e)if(e.match(/^data:/)){var o=e.split(","),i=o[0],a=o[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,r,null,a)}.bind(this),0)}else{var s=function(t,n,o){this.receive(e,r,t,n,o)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,r,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,r,o){this.cache[e]=r;for(var i,a=this.pending[e],s=0,c=a.length;c>s&&(i=a[s]);s++)this.onload(e,i,r,n,o),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=r}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===u}function n(e){var t=r(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function r(e){return e.textContent+o(e)}function o(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,r=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+r+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,d=e.isIE,u=e.IMPORT_LINK_TYPE,l="link[rel="+u+"]",h={documentSelectors:l,importsSelectors:[l,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,r=function(o){e.removeEventListener("load",r),e.removeEventListener("error",r),t&&t(o),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",r),e.addEventListener("error",r),d&&"style"===e.localName){var o=!1;if(-1==e.textContent.indexOf("@import"))o=!0;else if(e.sheet){o=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;s>c&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(o=o&&Boolean(i.styleSheet))}o&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var r=document.createElement("script");r.__importElement=t,r.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(r,function(t){r.parentNode&&r.parentNode.removeChild(r),e.currentScript=null}),this.addElementToDocument(r)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var r,o=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=o.length;a>i&&(r=o[i]);i++)if(!this.isParsed(r))return this.hasResource(r)?t(r)?this.nextToParseInDoc(r.__doc,r):r:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return t(e)&&void 0===e.__doc?!1:!0}};e.parser=h,e.IMPORT_SELECTOR=l}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function r(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function o(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var o=n.createElement("base");o.setAttribute("href",t),n.baseURI||r(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(o),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,d=e.Loader,u=e.Observer,l=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){f.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);f.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,r,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=r,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:o(r,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}l.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),l.parseNext()},loadedAll:function(){l.parseNext()}},f=new d(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new u,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(c,"baseURI",p)}e.importer=h,e.importLoader=f}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,r={added:function(e){for(var r,o,i,a,s=0,c=e.length;c>s&&(a=e[s]);s++)r||(r=a.ownerDocument,o=t.isParsed(r)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&o&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&o.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&o.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=r.added.bind(r);var o=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(r)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var r=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],r=function(e){n.push(e)},o=function(){n.forEach(function(t){t(e)})};e.addModule=r,e.initializeModules=o,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return t(e)?!0:void r(e,t)}),r(e,t)}function n(e,t,r){var o=e.firstElementChild;if(!o)for(o=e.firstChild;o&&o.nodeType!==Node.ELEMENT_NODE;)o=o.nextSibling;for(;o;)t(o,r)!==!0&&n(o,t,r),o=o.nextElementSibling;return null}function r(e,n){for(var r=e.shadowRoot;r;)t(r,n),r=r.olderShadowRoot}function o(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var r,o=e.querySelectorAll("link[rel="+a+"]"),s=0,c=o.length;c>s&&(r=o[s]);s++)r["import"]&&i(r["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=o,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||r(e,t)}function n(t,n){return e.upgrade(t,n)?!0:void(n&&a(t))}function r(e,t){b(e,function(e){return n(e,t)?!0:void 0})}function o(e){T.push(e),L||(L=!0,setTimeout(i))}function i(){L=!1;for(var e,t=T,n=0,r=t.length;r>n&&(e=t[n]);n++)e();T=[]}function a(e){E?o(function(){s(e)}):s(e)}function s(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){d(e),b(e,function(e){d(e)})}function d(e){E?o(function(){u(e)}):u(e)}function u(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function l(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function h(e){if(e.shadowRoot&&!e.shadowRoot.__watched){g.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function f(e,n){if(g.dom){var r=n[0];if(r&&"childList"===r.type&&r.addedNodes&&r.addedNodes){for(var o=r.addedNodes[0];o&&o!==document&&!o.host;)o=o.parentNode;var i=o&&(o.URL||o._URL||o.host&&o.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=l(e);n.forEach(function(e){"childList"===e.type&&(M(e.addedNodes,function(e){e.localName&&t(e,a)}),M(e.removedNodes,function(e){e.localName&&c(e)}))}),g.dom&&console.groupEnd()}function p(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(f(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(f.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),g.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document); -t(e,n),m(e),g.dom&&console.groupEnd()}function _(e){y(e,v)}function w(e){HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(e),t(e)}var g=e.flags,b=e.forSubtree,y=e.forDocumentTree,E=window.MutationObserver._isPolyfilled&&g["throttle-attached"];e.hasPolyfillMutations=E,e.hasThrottledAttached=E;var L=!1,T=[],M=Array.prototype.forEach.call.bind(Array.prototype.forEach),N=Element.prototype.createShadowRoot;N&&(Element.prototype.createShadowRoot=function(){var e=N.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=h,e.upgradeDocumentTree=_,e.upgradeDocument=v,e.upgradeSubtree=r,e.upgradeAll=w,e.attached=a,e.takeRecords=p}),window.CustomElements.addModule(function(e){function t(t,r){if(!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var o=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(o);if(i&&(o&&i.tag==t.localName||!o&&!i["extends"]))return n(t,i,r)}}function n(t,n,o){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),r(t,n),t.__upgraded__=!0,i(t),o&&e.attached(t),e.upgradeSubtree(t,o),a.upgrade&&console.groupEnd(),t}function r(e,t){Object.__proto__?e.__proto__=t.prototype:(o(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function o(e,t,n){for(var r={},o=t;o!==n&&o!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(o),s=0;i=a[s];s++)r[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(o,i)),r[i]=1);o=Object.getPrototypeOf(o)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=r}),window.CustomElements.addModule(function(e){function t(t,r){var c=r||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(o(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(d(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),u(c.__name,c),c.ctor=l(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&_(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){r.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){r.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function r(e,t,n){e=e.toLowerCase();var r=this.getAttribute(e);n.apply(this,arguments);var o=this.getAttribute(e);this.attributeChangedCallback&&o!==r&&this.attributeChangedCallback(e,r,o)}function o(e){for(var t=0;t=0&&b(r,HTMLElement),r)}function p(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return w(e),e}}var m,v=e.isIE,_=e.upgradeDocumentTree,w=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,y=e.useNative,E=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],L={},T="http://www.w3.org/1999/xhtml",M=document.createElement.bind(document),N=document.createElementNS.bind(document);m=Object.__proto__||y?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},p(Node.prototype,"cloneNode"),p(document,"importNode"),v&&!function(){var e=document.importNode;document.importNode=function(){var t=e.apply(document,arguments);if(t.nodeType==t.DOCUMENT_FRAGMENT_NODE){var n=document.createDocumentFragment();return n.appendChild(t),n}return t}}(),document.registerElement=t,document.createElement=f,document.createElementNS=h,e.registry=L,e["instanceof"]=m,e.reservedTagList=E,e.getRegisteredDefinition=d,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,r=e.initializeModules;e.isIE;if(n){var o=function(){};e.watchShadow=o,e.upgrade=o,e.upgradeAll=o,e.upgradeDocumentTree=o,e.upgradeSubtree=o,e.takeRecords=o,e["instanceof"]=function(e,t){return e instanceof t}}else r();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents); \ No newline at end of file +// @version 0.7.18 +!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents-lite.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,r=e.split("=");r[0]&&(t=r[0].match(/wc-(.+)/))&&(n[t[1]]=r[1]||!0)}),t)for(var r,o=0;r=t.attributes[o];o++)"src"!==r.name&&(n[r.name]=r.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),function(e){"use strict";function t(e){return void 0!==h[e]}function n(){s.call(this),this._isInvalid=!0}function r(e){return""==e&&n.call(this),e.toLowerCase()}function o(e){var t=e.charCodeAt(0);return t>32&&127>t&&-1==[34,35,60,62,63,96].indexOf(t)?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&127>t&&-1==[34,35,60,62,96].indexOf(t)?e:encodeURIComponent(e)}function a(e,a,s){function c(e){g.push(e)}var d=a||"scheme start",u=0,l="",w=!1,_=!1,g=[];e:for(;(e[u-1]!=p||0==u)&&!this._isInvalid;){var b=e[u];switch(d){case"scheme start":if(!b||!m.test(b)){if(a){c("Invalid scheme.");break e}l="",d="no scheme";continue}l+=b.toLowerCase(),d="scheme";break;case"scheme":if(b&&v.test(b))l+=b.toLowerCase();else{if(":"!=b){if(a){if(p==b)break e;c("Code point not allowed in scheme: "+b);break e}l="",u=0,d="no scheme";continue}if(this._scheme=l,l="",a)break e;t(this._scheme)&&(this._isRelative=!0),d="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==b?(this._query="?",d="query"):"#"==b?(this._fragment="#",d="fragment"):p!=b&&" "!=b&&"\n"!=b&&"\r"!=b&&(this._schemeData+=o(b));break;case"no scheme":if(s&&t(s._scheme)){d="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=b||"/"!=e[u+1]){c("Expected /, got: "+b),d="relative";continue}d="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),p==b){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==b||"\\"==b)"\\"==b&&c("\\ is an invalid code point."),d="relative slash";else if("?"==b)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,d="query";else{if("#"!=b){var y=e[u+1],E=e[u+2];("file"!=this._scheme||!m.test(b)||":"!=y&&"|"!=y||p!=E&&"/"!=E&&"\\"!=E&&"?"!=E&&"#"!=E)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),d="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,d="fragment"}break;case"relative slash":if("/"!=b&&"\\"!=b){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),d="relative path";continue}"\\"==b&&c("\\ is an invalid code point."),d="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=b){c("Expected '/', got: "+b),d="authority ignore slashes";continue}d="authority second slash";break;case"authority second slash":if(d="authority ignore slashes","/"!=b){c("Expected '/', got: "+b);continue}break;case"authority ignore slashes":if("/"!=b&&"\\"!=b){d="authority";continue}c("Expected authority, got: "+b);break;case"authority":if("@"==b){w&&(c("@ already seen."),l+="%40"),w=!0;for(var L=0;L>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return t&&t[0]===e?(t[0]=t[1]=void 0,!0):!1},has:function(e){var t=e[this.name];return t?t[0]===e:!1}},window.WeakMap=n}(),function(e){function t(e){b.push(e),g||(g=!0,m(r))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function r(){g=!1;var e=b;b=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();o(e),n.length&&(e.callback_(n,e),t=!0)}),t&&r()}function o(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var r=v.get(n);if(r)for(var o=0;o0){var o=n[r-1],i=f(o,e);if(i)return void(n[r-1]=i)}else t(this.observer);n[r]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n":return">";case" ":return" "}}function t(t){return t.replace(a,e)}var n="template",r=document.implementation.createHTMLDocument("template"),o=!0;HTMLTemplateElement=function(){},HTMLTemplateElement.prototype=Object.create(HTMLElement.prototype),HTMLTemplateElement.decorate=function(e){if(!e.content){e.content=r.createDocumentFragment();for(var n;n=e.firstChild;)e.content.appendChild(n);if(o)try{Object.defineProperty(e,"innerHTML",{get:function(){for(var e="",n=this.content.firstChild;n;n=n.nextSibling)e+=n.outerHTML||t(n.data);return e},set:function(e){for(r.body.innerHTML=e,HTMLTemplateElement.bootstrap(r);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;r.body.firstChild;)this.content.appendChild(r.body.firstChild)},configurable:!0})}catch(i){o=!1}HTMLTemplateElement.bootstrap(e.content)}},HTMLTemplateElement.bootstrap=function(e){for(var t,r=e.querySelectorAll(n),o=0,i=r.length;i>o&&(t=r[o]);o++)HTMLTemplateElement.decorate(t)},document.addEventListener("DOMContentLoaded",function(){HTMLTemplateElement.bootstrap(document)});var i=document.createElement;document.createElement=function(){"use strict";var e=i.apply(document,arguments);return"template"==e.localName&&HTMLTemplateElement.decorate(e),e};var a=/[&\u00A0<>]/g}(),function(e){"use strict";if(!window.performance){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var r=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(r.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var o=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||o&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||o&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||p,r(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===w}function r(e,t){if(n(t))e&&e();else{var o=function(){("complete"===t.readyState||t.readyState===w)&&(t.removeEventListener(_,o),r(e,t))};t.addEventListener(_,o)}}function o(e){e.target.__loaded=!0}function i(e,t){function n(){c==d&&e&&e({allImports:s,loadedImports:u,errorImports:l})}function r(e){o(e),u.push(this),c++,n()}function i(e){l.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,d=s.length,u=[],l=[];if(d)for(var h,f=0;d>f&&(h=s[f]);f++)a(h)?(c++,n()):(h.addEventListener("load",r),h.addEventListener("error",i));else n()}function a(e){return l?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)c(t)&&d(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function d(e){var t=e["import"];t?o({target:e}):(e.addEventListener("load",o),e.addEventListener("error",o))}var u="import",l=Boolean(u in document.createElement("link")),h=Boolean(window.ShadowDOMPolyfill),f=function(e){return h?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},p=f(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return f(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(p,"_currentScript",m);var v=/Trident/.test(navigator.userAgent),w=v?"complete":"interactive",_="readystatechange";l&&(new MutationObserver(function(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,r=t.length;r>n&&(e=t[n]);n++)d(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=p.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),p.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=u,e.useNative=l,e.rootDocument=p,e.whenReady=t,e.isIE=v}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},r=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=r}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,r={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,r=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,r),e},resolveUrlsInCssText:function(e,r,o){var i=this.replaceUrls(e,o,r,t);return i=this.replaceUrls(i,o,r,n)},replaceUrls:function(e,t,n,r){return e.replace(r,function(e,r,o,i){var a=o.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,r+"'"+a+"'"+i})}};e.path=r}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,r,o){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}r.call(o,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,r=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};r.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,r){if(n.load&&console.log("fetch",e,r),e)if(e.match(/^data:/)){var o=e.split(","),i=o[0],a=o[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,r,null,a)}.bind(this),0)}else{var s=function(t,n,o){this.receive(e,r,t,n,o)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,r,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,r,o){this.cache[e]=r;for(var i,a=this.pending[e],s=0,c=a.length;c>s&&(i=a[s]);s++)this.onload(e,i,r,n,o),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=r}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===u}function n(e){var t=r(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function r(e){return e.textContent+o(e)}function o(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,r=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+r+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,d=e.isIE,u=e.IMPORT_LINK_TYPE,l="link[rel="+u+"]",h={documentSelectors:l,importsSelectors:[l,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,r=function(o){e.removeEventListener("load",r),e.removeEventListener("error",r),t&&t(o),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",r),e.addEventListener("error",r),d&&"style"===e.localName){var o=!1;if(-1==e.textContent.indexOf("@import"))o=!0;else if(e.sheet){o=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;s>c&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(o=o&&Boolean(i.styleSheet))}o&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var r=document.createElement("script");r.__importElement=t,r.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(r,function(t){r.parentNode&&r.parentNode.removeChild(r),e.currentScript=null}),this.addElementToDocument(r)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var r,o=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=o.length;a>i&&(r=o[i]);i++)if(!this.isParsed(r))return this.hasResource(r)?t(r)?this.nextToParseInDoc(r.__doc,r):r:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return t(e)&&void 0===e.__doc?!1:!0}};e.parser=h,e.IMPORT_SELECTOR=l}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function r(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function o(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var o=n.createElement("base");o.setAttribute("href",t),n.baseURI||r(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(o),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,d=e.Loader,u=e.Observer,l=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){f.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);f.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,r,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=r,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:o(r,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}l.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),l.parseNext()},loadedAll:function(){l.parseNext()}},f=new d(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new u,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(c,"baseURI",p)}e.importer=h,e.importLoader=f}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,r={added:function(e){for(var r,o,i,a,s=0,c=e.length;c>s&&(a=e[s]);s++)r||(r=a.ownerDocument,o=t.isParsed(r)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&o&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&o.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&o.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=r.added.bind(r);var o=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(r)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var r=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],r=function(e){n.push(e)},o=function(){n.forEach(function(t){t(e)})};e.addModule=r,e.initializeModules=o,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return t(e)?!0:void r(e,t)}),r(e,t)}function n(e,t,r){var o=e.firstElementChild;if(!o)for(o=e.firstChild;o&&o.nodeType!==Node.ELEMENT_NODE;)o=o.nextSibling;for(;o;)t(o,r)!==!0&&n(o,t,r),o=o.nextElementSibling;return null}function r(e,n){for(var r=e.shadowRoot;r;)t(r,n),r=r.olderShadowRoot}function o(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var r,o=e.querySelectorAll("link[rel="+a+"]"),s=0,c=o.length;c>s&&(r=o[s]);s++)r["import"]&&i(r["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=o,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||r(e,t)}function n(t,n){return e.upgrade(t,n)?!0:void(n&&a(t))}function r(e,t){g(e,function(e){return n(e,t)?!0:void 0})}function o(e){L.push(e),E||(E=!0,setTimeout(i))}function i(){E=!1;for(var e,t=L,n=0,r=t.length;r>n&&(e=t[n]);n++)e();L=[]}function a(e){y?o(function(){s(e)}):s(e)}function s(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){d(e),g(e,function(e){d(e)})}function d(e){y?o(function(){u(e)}):u(e)}function u(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function l(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function h(e){if(e.shadowRoot&&!e.shadowRoot.__watched){_.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function f(e,n){if(_.dom){var r=n[0];if(r&&"childList"===r.type&&r.addedNodes&&r.addedNodes){for(var o=r.addedNodes[0];o&&o!==document&&!o.host;)o=o.parentNode;var i=o&&(o.URL||o._URL||o.host&&o.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=l(e);n.forEach(function(e){"childList"===e.type&&(T(e.addedNodes,function(e){e.localName&&t(e,a)}),T(e.removedNodes,function(e){e.localName&&c(e)}))}),_.dom&&console.groupEnd()}function p(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(f(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(f.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),_.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop()); +var n=e===window.wrap(document);t(e,n),m(e),_.dom&&console.groupEnd()}function w(e){b(e,v)}var _=e.flags,g=e.forSubtree,b=e.forDocumentTree,y=window.MutationObserver._isPolyfilled&&_["throttle-attached"];e.hasPolyfillMutations=y,e.hasThrottledAttached=y;var E=!1,L=[],T=Array.prototype.forEach.call.bind(Array.prototype.forEach),M=Element.prototype.createShadowRoot;M&&(Element.prototype.createShadowRoot=function(){var e=M.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=h,e.upgradeDocumentTree=w,e.upgradeDocument=v,e.upgradeSubtree=r,e.upgradeAll=t,e.attached=a,e.takeRecords=p}),window.CustomElements.addModule(function(e){function t(t,r){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var o=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(o);if(i&&(o&&i.tag==t.localName||!o&&!i["extends"]))return n(t,i,r)}}function n(t,n,o){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),r(t,n),t.__upgraded__=!0,i(t),o&&e.attached(t),e.upgradeSubtree(t,o),a.upgrade&&console.groupEnd(),t}function r(e,t){Object.__proto__?e.__proto__=t.prototype:(o(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function o(e,t,n){for(var r={},o=t;o!==n&&o!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(o),s=0;i=a[s];s++)r[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(o,i)),r[i]=1);o=Object.getPrototypeOf(o)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=r}),window.CustomElements.addModule(function(e){function t(t,r){var c=r||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(o(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(d(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),u(c.__name,c),c.ctor=l(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&w(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){r.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){r.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function r(e,t,n){e=e.toLowerCase();var r=this.getAttribute(e);n.apply(this,arguments);var o=this.getAttribute(e);this.attributeChangedCallback&&o!==r&&this.attributeChangedCallback(e,r,o)}function o(e){for(var t=0;t=0&&b(r,HTMLElement),r)}function p(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return _(e),e}}var m,v=e.isIE,w=e.upgradeDocumentTree,_=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,y=e.useNative,E=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],L={},T="http://www.w3.org/1999/xhtml",M=document.createElement.bind(document),N=document.createElementNS.bind(document);m=Object.__proto__||y?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},p(Node.prototype,"cloneNode"),p(document,"importNode"),v&&!function(){var e=document.importNode;document.importNode=function(){var t=e.apply(document,arguments);if(t.nodeType==t.DOCUMENT_FRAGMENT_NODE){var n=document.createDocumentFragment();return n.appendChild(t),n}return t}}(),document.registerElement=t,document.createElement=f,document.createElementNS=h,e.registry=L,e["instanceof"]=m,e.reservedTagList=E,e.getRegisteredDefinition=d,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,r=e.initializeModules;e.isIE;if(n){var o=function(){};e.watchShadow=o,e.upgrade=o,e.upgradeAll=o,e.upgradeDocumentTree=o,e.upgradeSubtree=o,e.takeRecords=o,e["instanceof"]=function(e,t){return e instanceof t}}else r();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents); \ No newline at end of file From 0df39b4df57be3681e041a5fd4e7f905fa567902 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 18:32:15 -0800 Subject: [PATCH 086/125] Remove no password set boolean --- homeassistant/components/frontend/__init__.py | 2 +- homeassistant/components/http.py | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 9bc46f86023..dac2041fa56 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -65,7 +65,7 @@ def _handle_get_root(handler, path_match, data): app_url = "frontend-{}.html".format(version.VERSION) # auto login if no password was set, else check api_password param - auth = ('no_password_set' if handler.server.no_password_set + auth = ('no_password_set' if handler.server.api_password is None else data.get('api_password', '')) with open(INDEX_PATH) as template_file: diff --git a/homeassistant/components/http.py b/homeassistant/components/http.py index 33290a159fa..5e8332e283a 100644 --- a/homeassistant/components/http.py +++ b/homeassistant/components/http.py @@ -53,10 +53,6 @@ def setup(hass, config): conf = config[DOMAIN] api_password = util.convert(conf.get(CONF_API_PASSWORD), str) - no_password_set = api_password is None - - if no_password_set: - api_password = util.get_random_string() # If no server host is given, accept all incoming requests server_host = conf.get(CONF_SERVER_HOST, '0.0.0.0') @@ -66,7 +62,7 @@ def setup(hass, config): try: server = HomeAssistantHTTPServer( (server_host, server_port), RequestHandler, hass, api_password, - development, no_password_set) + development) except OSError: # If address already in use _LOGGER.exception("Error setting up HTTP server") @@ -93,14 +89,13 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer): # pylint: disable=too-many-arguments def __init__(self, server_address, request_handler_class, - hass, api_password, development, no_password_set): + hass, api_password, development): super().__init__(server_address, request_handler_class) self.server_address = server_address self.hass = hass self.api_password = api_password self.development = development - self.no_password_set = no_password_set self.paths = [] self.sessions = SessionStore() @@ -157,7 +152,7 @@ class RequestHandler(SimpleHTTPRequestHandler): def log_message(self, fmt, *arguments): """ Redirect built-in log to HA logging """ - if self.server.no_password_set: + if self.server.api_password is None: _LOGGER.info(fmt, *arguments) else: _LOGGER.info( @@ -192,8 +187,7 @@ class RequestHandler(SimpleHTTPRequestHandler): "Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY) return - if self.server.no_password_set: - _LOGGER.warning('NO PASSWORD SET') + if self.server.api_password is None: self.authenticated = True elif HTTP_HEADER_HA_AUTH in self.headers: api_password = self.headers.get(HTTP_HEADER_HA_AUTH) From 546377e80a7104805cb94400612f6439e8a2cb83 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 18:59:59 -0800 Subject: [PATCH 087/125] Throttle camera stream to 2fps --- homeassistant/components/camera/__init__.py | 37 ++++++++++----------- homeassistant/components/camera/demo.py | 4 +-- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index ae5fe28beac..e63665230ca 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -19,6 +19,7 @@ from homeassistant.const import ( ) from homeassistant.helpers.entity_component import EntityComponent +import homeassistant.util.dt as dt_util DOMAIN = 'camera' @@ -80,19 +81,21 @@ def setup(hass, config): def _proxy_camera_image(handler, path_match, data): """ Proxies the camera image via the HA server. """ entity_id = path_match.group(ATTR_ENTITY_ID) + camera = component.entities.get(entity_id) - camera = None - if entity_id in component.entities.keys(): - camera = component.entities[entity_id] - - if camera: - response = camera.camera_image() - if response is not None: - handler.wfile.write(response) - else: - handler.send_response(HTTP_NOT_FOUND) - else: + if camera is None: handler.send_response(HTTP_NOT_FOUND) + handler.end_headers() + return + + response = camera.camera_image() + + if response is None: + handler.send_response(HTTP_NOT_FOUND) + handler.end_headers() + return + + handler.wfile.write(response) hass.http.register_path( 'GET', @@ -108,12 +111,9 @@ def setup(hass, config): stream even with only a still image URL available. """ entity_id = path_match.group(ATTR_ENTITY_ID) + camera = component.entities.get(entity_id) - camera = None - if entity_id in component.entities.keys(): - camera = component.entities[entity_id] - - if not camera: + if camera is None: handler.send_response(HTTP_NOT_FOUND) handler.end_headers() return @@ -131,7 +131,6 @@ def setup(hass, config): # MJPEG_START_HEADER.format() while True: - img_bytes = camera.camera_image() if img_bytes is None: continue @@ -148,12 +147,12 @@ def setup(hass, config): handler.request.sendall( bytes('--jpgboundary\r\n', 'utf-8')) + time.sleep(0.5) + except (requests.RequestException, IOError): camera.is_streaming = False camera.update_ha_state() - camera.is_streaming = False - hass.http.register_path( 'GET', re.compile( diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py index fc3ec263143..fd79bc3ce82 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/camera/demo.py @@ -24,12 +24,10 @@ class DemoCamera(Camera): def camera_image(self): """ Return a faked still image response. """ - image_path = os.path.join(os.path.dirname(__file__), 'demo_{}.png'.format(randint(1, 5))) with open(image_path, 'rb') as file: - output = file.read() - return output + return file.read() @property def name(self): From e67732b4f85820d71d9bbb14b1b6de3014bfda7a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 19:12:42 -0800 Subject: [PATCH 088/125] Remove no longer needed image --- .../components/frontend/www_static/splash.png | Bin 51901 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 homeassistant/components/frontend/www_static/splash.png diff --git a/homeassistant/components/frontend/www_static/splash.png b/homeassistant/components/frontend/www_static/splash.png deleted file mode 100644 index 582140a2bc35f598fc227782ff06039759b39fe5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51901 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 Date: Sat, 28 Nov 2015 22:14:40 -0800 Subject: [PATCH 089/125] Streaming API will keep session alive --- homeassistant/components/api.py | 3 +++ homeassistant/components/http.py | 43 +++++++++++++++++++------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/api.py b/homeassistant/components/api.py index a81c0f3227f..1e6e66baee0 100644 --- a/homeassistant/components/api.py +++ b/homeassistant/components/api.py @@ -106,6 +106,7 @@ def _handle_get_api_stream(handler, path_match, data): wfile = handler.wfile write_lock = threading.Lock() block = threading.Event() + session_id = None restrict = data.get('restrict') if restrict: @@ -119,6 +120,7 @@ def _handle_get_api_stream(handler, path_match, data): try: wfile.write(msg.encode("UTF-8")) wfile.flush() + handler.server.sessions.extend_validation(session_id) except IOError: block.set() @@ -138,6 +140,7 @@ def _handle_get_api_stream(handler, path_match, data): handler.send_response(HTTP_OK) handler.send_header('Content-type', 'text/event-stream') + session_id = handler.set_session_cookie_header() handler.end_headers() hass.bus.listen(MATCH_ALL, forward_events) diff --git a/homeassistant/components/http.py b/homeassistant/components/http.py index 5e8332e283a..81965179afe 100644 --- a/homeassistant/components/http.py +++ b/homeassistant/components/http.py @@ -337,26 +337,28 @@ class RequestHandler(SimpleHTTPRequestHandler): def set_cache_header(self): """ Add cache headers if not in development """ - if not self.server.development: - # 1 year in seconds - cache_time = 365 * 86400 + if self.server.development: + return - self.send_header( - HTTP_HEADER_CACHE_CONTROL, - "public, max-age={}".format(cache_time)) - self.send_header( - HTTP_HEADER_EXPIRES, - self.date_time_string(time.time()+cache_time)) + # 1 year in seconds + cache_time = 365 * 86400 + + self.send_header( + HTTP_HEADER_CACHE_CONTROL, + "public, max-age={}".format(cache_time)) + self.send_header( + HTTP_HEADER_EXPIRES, + self.date_time_string(time.time()+cache_time)) def set_session_cookie_header(self): - """ Add the header for the session cookie. """ + """ Add the header for the session cookie and return session id. """ if not self.authenticated: return - current = self.get_cookie_session_id() + session_id = self.get_cookie_session_id() - if current is not None: - self.server.sessions.extend_validation(current) + if session_id is not None: + self.server.sessions.extend_validation(session_id) return self.send_header( @@ -364,6 +366,8 @@ class RequestHandler(SimpleHTTPRequestHandler): '{}={}'.format(SESSION_KEY, self.server.sessions.create()) ) + return session_id + def verify_session(self): """ Verify that we are in a valid session. """ return self.get_cookie_session_id() is not None @@ -387,19 +391,22 @@ class RequestHandler(SimpleHTTPRequestHandler): if morsel is None: return None - current = cookie[SESSION_KEY].value + session_id = cookie[SESSION_KEY].value - return current if self.server.sessions.is_valid(current) else None + if self.server.sessions.is_valid(session_id): + return session_id + + return None def destroy_session(self): """ Destroys session. """ - current = self.get_cookie_session_id() + session_id = self.get_cookie_session_id() - if current is None: + if session_id is None: return self.send_header('Set-Cookie', '') - self.server.sessions.destroy(current) + self.server.sessions.destroy(session_id) def session_valid_time(): From f76edf0ed94469afcda4657c6aeaf772c93b6e6c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 22:15:11 -0800 Subject: [PATCH 090/125] Tweak manifest and frontend index --- .../components/frontend/index.html.template | 19 +++++-------------- .../frontend/www_static/manifest.json | 1 + 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/frontend/index.html.template b/homeassistant/components/frontend/index.html.template index 533fc43ac4e..87c5f6638a7 100644 --- a/homeassistant/components/frontend/index.html.template +++ b/homeassistant/components/frontend/index.html.template @@ -4,18 +4,13 @@ Home Assistant - - - - + + + href='/static/favicon-apple-180x180.png'> - + -
- -
-
+
diff --git a/homeassistant/components/frontend/www_static/manifest.json b/homeassistant/components/frontend/www_static/manifest.json index 3d0eb5fa443..3767a4b1c5b 100644 --- a/homeassistant/components/frontend/www_static/manifest.json +++ b/homeassistant/components/frontend/www_static/manifest.json @@ -3,6 +3,7 @@ "short_name": "Assistant", "start_url": "/", "display": "standalone", + "theme_color": "#03A9F4", "icons": [ { "src": "/static/favicon-192x192.png", From 286299c4c9716d41c059145b8391de714c37c68d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 22:19:34 -0800 Subject: [PATCH 091/125] update frontend --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 18 +++++++++--------- .../frontend/www_static/home-assistant-polymer | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 7e957562bba..04ccb720f6b 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 = "c1df3a08faa4a9978b25639ca0fd63cd" +VERSION = "36df87bb6c219a2ee59adf416e3abdfa" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index cf19d314f24..e600c4c5583 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -6052,12 +6052,12 @@ case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return font-weight: 300; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - } \ 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 f29b1062b30..33124030f6d 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit f29b1062b30c76ceeea87fd426a042d98358394a +Subproject commit 33124030f6d119ad3a58cb520062f2aa58022c6d From 70698f7ab09d487ea994b76bac575aa7f80aa8bb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 Nov 2015 23:16:20 -0800 Subject: [PATCH 092/125] Update demo camera images --- homeassistant/components/camera/__init__.py | 1 - homeassistant/components/camera/demo.py | 6 ++++-- homeassistant/components/camera/demo_0.jpg | Bin 0 -> 43574 bytes homeassistant/components/camera/demo_1.jpg | Bin 0 -> 44897 bytes homeassistant/components/camera/demo_1.png | Bin 9772 -> 0 bytes homeassistant/components/camera/demo_2.jpg | Bin 0 -> 44535 bytes homeassistant/components/camera/demo_2.png | Bin 9667 -> 0 bytes homeassistant/components/camera/demo_3.jpg | Bin 0 -> 44897 bytes homeassistant/components/camera/demo_3.png | Bin 9595 -> 0 bytes homeassistant/components/camera/demo_4.png | Bin 9906 -> 0 bytes homeassistant/components/camera/demo_5.png | Bin 9699 -> 0 bytes 11 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/camera/demo_0.jpg create mode 100644 homeassistant/components/camera/demo_1.jpg delete mode 100644 homeassistant/components/camera/demo_1.png create mode 100644 homeassistant/components/camera/demo_2.jpg delete mode 100644 homeassistant/components/camera/demo_2.png create mode 100644 homeassistant/components/camera/demo_3.jpg delete mode 100644 homeassistant/components/camera/demo_3.png delete mode 100644 homeassistant/components/camera/demo_4.png delete mode 100644 homeassistant/components/camera/demo_5.png diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index e63665230ca..169c97595af 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -19,7 +19,6 @@ from homeassistant.const import ( ) from homeassistant.helpers.entity_component import EntityComponent -import homeassistant.util.dt as dt_util DOMAIN = 'camera' diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py index fd79bc3ce82..0ad992db86d 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/camera/demo.py @@ -4,8 +4,8 @@ homeassistant.components.camera.demo Demo platform that has a fake camera. """ import os -from random import randint from homeassistant.components.camera import Camera +import homeassistant.util.dt as dt_util def setup_platform(hass, config, add_devices, discovery_info=None): @@ -24,8 +24,10 @@ class DemoCamera(Camera): def camera_image(self): """ Return a faked still image response. """ + now = dt_util.utcnow() + image_path = os.path.join(os.path.dirname(__file__), - 'demo_{}.png'.format(randint(1, 5))) + 'demo_{}.jpg'.format(now.second % 4)) with open(image_path, 'rb') as file: return file.read() diff --git a/homeassistant/components/camera/demo_0.jpg b/homeassistant/components/camera/demo_0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff87d5179f8364d86acf3a875e374741c5285bcb GIT binary patch literal 43574 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
UY%bB%f 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 HsonqC*w#h! literal 0 HcmV?d00001 diff --git a/homeassistant/components/camera/demo_1.png b/homeassistant/components/camera/demo_1.png deleted file mode 100644 index fc681fccecd69fb91801a6ecad8cd28454cae278..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9772 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 W=7Q<<3J~7eg`g;>DqAIO9{fKAR1o+8 diff --git a/homeassistant/components/camera/demo_2.jpg b/homeassistant/components/camera/demo_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71356479ab08c00a17ec0e09c9198714154f85d2 GIT binary patch literal 44535 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 IF~h+B0sj)foB#j- diff --git a/homeassistant/components/camera/demo_3.jpg b/homeassistant/components/camera/demo_3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..06166fffa859d9fdccf1fc89a563cd6574319d42 GIT binary patch literal 44897 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
UY%bB%f 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 HsonqC*w#h! literal 0 HcmV?d00001 diff --git a/homeassistant/components/camera/demo_3.png b/homeassistant/components/camera/demo_3.png deleted file mode 100644 index b54c1ffb57c97644426efae5950dcf47e8c70031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9595 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 diff --git a/homeassistant/components/camera/demo_5.png b/homeassistant/components/camera/demo_5.png deleted file mode 100644 index 874b95ef6a50b1638d27374aca0c42689506d76f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9699 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 C*Y+I% From 41a0f2c198722c683045bc26d9436b254ce7f8a6 Mon Sep 17 00:00:00 2001 From: pavoni Date: Sun, 29 Nov 2015 10:47:20 +0000 Subject: [PATCH 093/125] Add elevation attribute --- homeassistant/components/sun.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/sun.py b/homeassistant/components/sun.py index efe0a7dec2b..687c0856e5f 100644 --- a/homeassistant/components/sun.py +++ b/homeassistant/components/sun.py @@ -12,12 +12,14 @@ import urllib import homeassistant.util as util import homeassistant.util.dt as dt_util -from homeassistant.helpers.event import track_point_in_utc_time +from homeassistant.helpers.event import ( + track_point_in_utc_time, track_utc_time_change) from homeassistant.helpers.entity import Entity REQUIREMENTS = ['astral==0.8.1'] DOMAIN = "sun" ENTITY_ID = "sun.sun" +ENTITY_ID_ELEVATION = "sun.elevation" CONF_ELEVATION = 'elevation' @@ -26,6 +28,7 @@ STATE_BELOW_HORIZON = "below_horizon" STATE_ATTR_NEXT_RISING = "next_rising" STATE_ATTR_NEXT_SETTING = "next_setting" +STATE_ATTR_ELEVATION = "elevation" _LOGGER = logging.getLogger(__name__) @@ -139,11 +142,13 @@ class Sun(Entity): self.hass = hass self.location = location self._state = self.next_rising = self.next_setting = None + track_utc_time_change(hass, self.timer_update, second=30) @property def should_poll(self): - """ We trigger updates ourselves after sunset/sunrise """ - return False + """ We trigger updates ourselves after sunset/sunrise, + but sun angle requires poll """ + return True @property def name(self): @@ -159,8 +164,11 @@ class Sun(Entity): @property def state_attributes(self): return { - STATE_ATTR_NEXT_RISING: dt_util.datetime_to_str(self.next_rising), - STATE_ATTR_NEXT_SETTING: dt_util.datetime_to_str(self.next_setting) + STATE_ATTR_NEXT_RISING: + dt_util.datetime_to_str(self.next_rising), + STATE_ATTR_NEXT_SETTING: + dt_util.datetime_to_str(self.next_setting), + STATE_ATTR_ELEVATION: round(self.solar_elevation, 2) } @property @@ -168,6 +176,15 @@ class Sun(Entity): """ Returns the datetime when the next change to the state is. """ return min(self.next_rising, self.next_setting) + @property + def solar_elevation(self): + """ Returns the angle the sun is above the horizon""" + from astral import Astral + return Astral().solar_elevation( + dt_util.utcnow(), + self.location.latitude, + self.location.longitude) + def update_as_of(self, utc_point_in_time): """ Calculate sun state at a point in UTC time. """ mod = -1 @@ -198,3 +215,7 @@ class Sun(Entity): track_point_in_utc_time( self.hass, self.point_in_time_listener, self.next_change + timedelta(seconds=1)) + + def timer_update(self, time): + """ Needed to update solar elevation. """ + self.update_ha_state() From f4c3fbe8fde415c23bedbb714402f694b1bfbcfa Mon Sep 17 00:00:00 2001 From: pavoni Date: Sun, 29 Nov 2015 11:56:47 +0000 Subject: [PATCH 094/125] Add attribuet config to numeric state platform to allow trigger based in attributes rather than states. --- .../components/automation/numeric_state.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/automation/numeric_state.py b/homeassistant/components/automation/numeric_state.py index ab3529235d6..fe0a4f98bd9 100644 --- a/homeassistant/components/automation/numeric_state.py +++ b/homeassistant/components/automation/numeric_state.py @@ -14,6 +14,7 @@ from homeassistant.helpers.event import track_state_change CONF_ENTITY_ID = "entity_id" CONF_BELOW = "below" CONF_ABOVE = "above" +CONF_ATTRIBUTE = "attribute" _LOGGER = logging.getLogger(__name__) @@ -28,6 +29,7 @@ def trigger(hass, config, action): below = config.get(CONF_BELOW) above = config.get(CONF_ABOVE) + attribute = config.get(CONF_ATTRIBUTE) if below is None and above is None: _LOGGER.error("Missing configuration key." @@ -40,8 +42,8 @@ def trigger(hass, config, action): """ Listens for state changes and calls action. """ # Fire action if we go from outside range into range - if _in_range(to_s.state, above, below) and \ - (from_s is None or not _in_range(from_s.state, above, below)): + if _in_range(to_s, above, below, attribute) and \ + (from_s is None or not _in_range(from_s, above, below, attribute)): action() track_state_change( @@ -61,6 +63,7 @@ def if_action(hass, config): below = config.get(CONF_BELOW) above = config.get(CONF_ABOVE) + attribute = config.get(CONF_ABOVE) if below is None and above is None: _LOGGER.error("Missing configuration key." @@ -71,14 +74,15 @@ def if_action(hass, config): def if_numeric_state(): """ Test numeric state condition. """ state = hass.states.get(entity_id) - return state is not None and _in_range(state.state, above, below) + return state is not None and _in_range(state, above, below, attribute) return if_numeric_state -def _in_range(value, range_start, range_end): +def _in_range(state, range_start, range_end, attribute): """ Checks if value is inside the range """ - + value = (state.state if attribute is None + else state.attributes.get(attribute)) try: value = float(value) except ValueError: From 73e3ce504479172eaee245793a9ca0a7c878e6b1 Mon Sep 17 00:00:00 2001 From: pavoni Date: Sun, 29 Nov 2015 12:18:54 +0000 Subject: [PATCH 095/125] Fix bug --- homeassistant/components/automation/numeric_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/automation/numeric_state.py b/homeassistant/components/automation/numeric_state.py index fe0a4f98bd9..9c9215cacb5 100644 --- a/homeassistant/components/automation/numeric_state.py +++ b/homeassistant/components/automation/numeric_state.py @@ -63,7 +63,7 @@ def if_action(hass, config): below = config.get(CONF_BELOW) above = config.get(CONF_ABOVE) - attribute = config.get(CONF_ABOVE) + attribute = config.get(CONF_ATTRIBUTE) if below is None and above is None: _LOGGER.error("Missing configuration key." From 7d503e3f8b668748c6db64377b9e0395a600d32e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 29 Nov 2015 15:52:44 +0100 Subject: [PATCH 096/125] Update docstrings --- homeassistant/components/motor/__init__.py | 11 ++++++++--- homeassistant/components/motor/mqtt.py | 13 ++++++++----- homeassistant/components/mqtt/__init__.py | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/motor/__init__.py b/homeassistant/components/motor/__init__.py index 806dd9e0eb1..dda7c52c05d 100644 --- a/homeassistant/components/motor/__init__.py +++ b/homeassistant/components/motor/__init__.py @@ -1,8 +1,10 @@ """ homeassistant.components.motor -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Motor component. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/motor/ """ import os import logging @@ -100,12 +102,15 @@ class MotorDevice(Entity): @property def current_position(self): - """ Return current position of motor. - None is unknown, 0 is closed, 100 is fully open. """ + """ + Return current position of motor. + None is unknown, 0 is closed, 100 is fully open. + """ raise NotImplementedError() @property def state(self): + """ Returns the state of the motor. """ current = self.current_position if current is None: diff --git a/homeassistant/components/motor/mqtt.py b/homeassistant/components/motor/mqtt.py index 1075030fc37..7c25e382f84 100644 --- a/homeassistant/components/motor/mqtt.py +++ b/homeassistant/components/motor/mqtt.py @@ -1,7 +1,8 @@ """ homeassistant.components.motor.mqtt -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a MQTT motor. + For more details about this platform, please refer to the documentation at https://home-assistant.io/components/motor.mqtt/ """ @@ -40,7 +41,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttMotor(MotorDevice): - """ Represents a motor that can be controlled using MQTT """ + """ Represents a motor that can be controlled using MQTT. """ def __init__(self, hass, name, state_topic, command_topic, qos, payload_open, payload_close, payload_stop, state_format): self._state = None @@ -76,13 +77,15 @@ class MqttMotor(MotorDevice): @property def name(self): - """ The name of the motor """ + """ The name of the motor. """ return self._name @property def current_position(self): - """ Return current position of motor. - None is unknown, 0 is closed, 100 is fully open. """ + """ + Return current position of motor. + None is unknown, 0 is closed, 100 is fully open. + """ return self._state def open(self, **kwargs): diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 0e8506523d2..2c5dbf82923 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -138,7 +138,7 @@ def setup(hass, config): # pylint: disable=too-few-public-methods class _JsonFmtParser(object): - """ Implements a json parser on xpath. """ + """ Implements a JSON parser on xpath. """ def __init__(self, jsonpath): import jsonpath_rw self._expr = jsonpath_rw.parse(jsonpath) From 8841eef2b7f039d36dd8b795273d861ad28c67a2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 11:44:27 -0800 Subject: [PATCH 097/125] Add tests for lock component --- homeassistant/components/lock/__init__.py | 4 -- homeassistant/components/lock/demo.py | 22 +++------- tests/components/lock/__init__.py | 0 tests/components/lock/test_demo.py | 51 +++++++++++++++++++++++ 4 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 tests/components/lock/__init__.py create mode 100644 tests/components/lock/test_demo.py diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index fdc2da3e8d4..0d67679e82c 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -36,10 +36,6 @@ DISCOVERY_PLATFORMS = { wink.DISCOVER_LOCKS: 'wink' } -PROP_TO_ATTR = { - 'locked': ATTR_LOCKED -} - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index ac7bbed3dc2..472b17f46bf 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -5,25 +5,23 @@ homeassistant.components.lock.demo Demo platform that has two fake locks. """ from homeassistant.components.lock import LockDevice -from homeassistant.const import ( - DEVICE_DEFAULT_NAME, STATE_LOCKED, STATE_UNLOCKED) +from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return demo locks. """ add_devices_callback([ - DemoLock('Left Door', STATE_LOCKED, None), - DemoLock('Right Door', STATE_UNLOCKED, None) + DemoLock('Front Door', STATE_LOCKED), + DemoLock('Kitchen Door', STATE_UNLOCKED) ]) class DemoLock(LockDevice): """ Provides a demo lock. """ - def __init__(self, name, state, icon): - self._name = name or DEVICE_DEFAULT_NAME + def __init__(self, name, state): + self._name = name self._state = state - self._icon = icon @property def should_poll(self): @@ -35,18 +33,10 @@ class DemoLock(LockDevice): """ 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 is_locked(self): """ True if device is locked. """ - if self._state == STATE_LOCKED: - return True - else: - return False + return self._state == STATE_LOCKED def lock(self, **kwargs): """ Lock the device. """ diff --git a/tests/components/lock/__init__.py b/tests/components/lock/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/components/lock/test_demo.py b/tests/components/lock/test_demo.py new file mode 100644 index 00000000000..7320b1aa69a --- /dev/null +++ b/tests/components/lock/test_demo.py @@ -0,0 +1,51 @@ +""" +tests.components.lock.test_demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests demo lock component. +""" +import unittest + +import homeassistant.core as ha +from homeassistant.components import lock + + +FRONT = 'lock.front_door' +KITCHEN = 'lock.kitchen_door' + + +class TestLockDemo(unittest.TestCase): + """ Test the demo lock. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + self.assertTrue(lock.setup(self.hass, { + 'lock': { + 'platform': 'demo' + } + })) + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass.stop() + + def test_is_locked(self): + self.assertTrue(lock.is_locked(self.hass, FRONT)) + self.hass.states.is_state(FRONT, 'locked') + + self.assertFalse(lock.is_locked(self.hass, KITCHEN)) + self.hass.states.is_state(KITCHEN, 'unlocked') + + def test_locking(self): + lock.lock(self.hass, KITCHEN) + + self.hass.pool.block_till_done() + + self.assertTrue(lock.is_locked(self.hass, KITCHEN)) + + def test_unlocking(self): + lock.unlock(self.hass, FRONT) + + self.hass.pool.block_till_done() + + self.assertFalse(lock.is_locked(self.hass, FRONT)) From 01203c7c4c17c788f8544bda6e9452631c88ecdd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 12:13:06 -0800 Subject: [PATCH 098/125] Add updater tests --- tests/components/test_updater.py | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/components/test_updater.py diff --git a/tests/components/test_updater.py b/tests/components/test_updater.py new file mode 100644 index 00000000000..3e5cf55e0b0 --- /dev/null +++ b/tests/components/test_updater.py @@ -0,0 +1,74 @@ +""" +tests.test_updater +~~~~~~~~~~~~~~~~~~ + +Tests updater component. +""" +import unittest +from unittest.mock import patch + +import requests + +import homeassistant.core as ha +from homeassistant.const import __version__ as CURRENT_VERSION +from homeassistant.components import updater +import homeassistant.util.dt as dt_util +from tests.common import fire_time_changed + +NEW_VERSION = '10000.0' + + +class TestUpdater(unittest.TestCase): + """ Test the demo lock. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass.stop() + + @patch('homeassistant.components.updater.get_newest_version') + def test_new_version_shows_entity_on_start(self, mock_get_newest_version): + mock_get_newest_version.return_value = NEW_VERSION + + self.assertTrue(updater.setup(self.hass, { + 'updater': None + })) + + self.assertTrue(self.hass.states.is_state(updater.ENTITY_ID, + NEW_VERSION)) + + @patch('homeassistant.components.updater.get_newest_version') + def test_no_entity_on_same_version(self, mock_get_newest_version): + mock_get_newest_version.return_value = CURRENT_VERSION + + self.assertTrue(updater.setup(self.hass, { + 'updater': None + })) + + self.assertIsNone(self.hass.states.get(updater.ENTITY_ID)) + + mock_get_newest_version.return_value = NEW_VERSION + + fire_time_changed(self.hass, + dt_util.utcnow().replace(hour=0, minute=0, second=0)) + + self.hass.pool.block_till_done() + + self.assertTrue(self.hass.states.is_state(updater.ENTITY_ID, + NEW_VERSION)) + + @patch('homeassistant.components.updater.requests.get') + def test_errors_while_fetching_new_version(self, mock_get): + mock_get.side_effect = requests.RequestException + + self.assertIsNone(updater.get_newest_version()) + + mock_get.side_effect = ValueError + + self.assertIsNone(updater.get_newest_version()) + + mock_get.side_effect = KeyError + + self.assertIsNone(updater.get_newest_version()) From aff1c273725be75426bfa790a3b33f4198ed8773 Mon Sep 17 00:00:00 2001 From: pavoni Date: Sun, 29 Nov 2015 20:45:03 +0000 Subject: [PATCH 099/125] Remove unused and potentially confusing property --- homeassistant/components/sun.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/homeassistant/components/sun.py b/homeassistant/components/sun.py index 687c0856e5f..2e1c0c9b377 100644 --- a/homeassistant/components/sun.py +++ b/homeassistant/components/sun.py @@ -144,12 +144,6 @@ class Sun(Entity): self._state = self.next_rising = self.next_setting = None track_utc_time_change(hass, self.timer_update, second=30) - @property - def should_poll(self): - """ We trigger updates ourselves after sunset/sunrise, - but sun angle requires poll """ - return True - @property def name(self): return "Sun" From 18ca7b4f9e3d3186384a5b95a1ba0be7ea1d87a9 Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sun, 29 Nov 2015 15:52:16 -0500 Subject: [PATCH 100/125] bump requirement version; support multiple switches per platform --- homeassistant/components/switch/orvibo.py | 28 +++++++++++++++-------- requirements_all.txt | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/switch/orvibo.py b/homeassistant/components/switch/orvibo.py index b9469d15df0..c636a7f3f55 100644 --- a/homeassistant/components/switch/orvibo.py +++ b/homeassistant/components/switch/orvibo.py @@ -11,7 +11,7 @@ import logging from homeassistant.components.switch import SwitchDevice DEFAULT_NAME = "Orvibo S20 Switch" -REQUIREMENTS = ['orvibo==1.0.0'] +REQUIREMENTS = ['orvibo==1.0.1'] _LOGGER = logging.getLogger(__name__) @@ -20,15 +20,23 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return S20 switches. """ from orvibo.s20 import S20, S20Exception - if config.get('host') is None: - _LOGGER.error("Missing required variable: host") - return - try: - s20 = S20(config.get('host')) - add_devices_callback([S20Switch(config.get('name', DEFAULT_NAME), - s20)]) - except S20Exception: - _LOGGER.exception("S20 couldn't be initialized") + switches = [] + switch_conf = config.get('switches', [config]) + + for switch in switch_conf: + if switch.get('host') is None: + _LOGGER.error("Missing required variable: host") + continue + host = switch.get('host') + try: + switches.append(S20Switch(switch.get('name', DEFAULT_NAME), + S20(host))) + _LOGGER.info("Initialized S20 at %s", host) + except S20Exception: + _LOGGER.exception("S20 at %s couldn't be initialized", + host) + + add_devices_callback(switches) class S20Switch(SwitchDevice): diff --git a/requirements_all.txt b/requirements_all.txt index a9f24cdb65f..7dd5dc8996a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -146,7 +146,7 @@ https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f hikvision==0.4 # homeassistant.components.switch.orvibo -orvibo==1.0.0 +orvibo==1.0.1 # homeassistant.components.switch.wemo pywemo==0.3.2 From cb0eb2df7dc788ee7a9cf37b9e2ec080f936186b Mon Sep 17 00:00:00 2001 From: pavoni Date: Sun, 29 Nov 2015 21:37:08 +0000 Subject: [PATCH 101/125] Add tests --- .../automation/test_numeric_state.py | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/tests/components/automation/test_numeric_state.py b/tests/components/automation/test_numeric_state.py index a04b8d01f4e..8280f396f93 100644 --- a/tests/components/automation/test_numeric_state.py +++ b/tests/components/automation/test_numeric_state.py @@ -253,6 +253,156 @@ class TestAutomationNumericState(unittest.TestCase): self.hass.pool.block_till_done() self.assertEqual(0, len(self.calls)) + def test_if_fires_on_entity_change_below_with_attribute(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 9 is below 10 + self.hass.states.set('test.entity', 9, { 'test_attribute': 11 }) + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + + def test_if_not_fires_on_entity_change_not_below_with_attribute(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 11 is not below 10 + self.hass.states.set('test.entity', 11, { 'test_attribute': 9 }) + self.hass.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_if_fires_on_attribute_change_with_attribute_below(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'attribute': 'test_attribute', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 9 is below 10 + self.hass.states.set('test.entity', 'entity', { 'test_attribute': 9 }) + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + + def test_if_not_fires_on_attribute_change_with_attribute_not_below(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'attribute': 'test_attribute', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 11 is not below 10 + self.hass.states.set('test.entity', 'entity', { 'test_attribute': 11 }) + self.hass.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_if_not_fires_on_entity_change_with_attribute_below(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'attribute': 'test_attribute', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 11 is not below 10, entity state value should not be tested + self.hass.states.set('test.entity', '9', { 'test_attribute': 11 }) + self.hass.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_if_not_fires_on_entity_change_with_not_attribute_below(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'attribute': 'test_attribute', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 11 is not below 10, entity state value should not be tested + self.hass.states.set('test.entity', 'entity') + self.hass.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_if_fires_on_attribute_change_with_attribute_below_multiple_attributes(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'attribute': 'test_attribute', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 9 is not below 10 + self.hass.states.set('test.entity', 'entity', { 'test_attribute': 9, 'not_test_attribute': 11 }) + self.hass.pool.block_till_done() + self.assertEqual(1, len(self.calls)) + + def test_if_not_fires_on_attribute_change_with_attribute_not_below_multiple_attributes(self): + self.assertTrue(automation.setup(self.hass, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'numeric_state', + 'entity_id': 'test.entity', + 'attribute': 'test_attribute', + 'below': 10, + }, + 'action': { + 'service': 'test.automation' + } + } + })) + # 11 is not below 10 + self.hass.states.set('test.entity', 'entity', { 'test_attribute': 11, 'not_test_attribute': 9 }) + self.hass.pool.block_till_done() + self.assertEqual(0, len(self.calls)) + def test_if_action(self): entity_id = 'domain.test_entity' test_state = 10 From a301d869d7824c796ce6606af462bcf696927498 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 13:49:05 -0800 Subject: [PATCH 102/125] PyLint 1.5 fixes --- .../components/automation/numeric_state.py | 2 +- homeassistant/components/automation/zone.py | 1 + homeassistant/components/binary_sensor/arest.py | 5 +++-- homeassistant/components/camera/__init__.py | 9 +++++---- homeassistant/components/camera/generic.py | 9 +++++---- homeassistant/components/camera/mjpeg.py | 13 +++++++------ homeassistant/components/ecobee.py | 7 ++++--- homeassistant/components/light/zwave.py | 5 +++-- homeassistant/components/media_player/firetv.py | 2 +- homeassistant/components/notify/xmpp.py | 4 ++-- homeassistant/components/recorder.py | 2 +- homeassistant/components/script.py | 10 +++++----- homeassistant/components/sensor/arest.py | 9 +++++---- homeassistant/components/sensor/ecobee.py | 3 ++- homeassistant/components/sensor/forecast.py | 16 ++++------------ homeassistant/components/sensor/glances.py | 5 +++-- homeassistant/components/sensor/rest.py | 7 ++++--- homeassistant/components/sensor/rpi_gpio.py | 11 +++-------- homeassistant/components/sensor/sabnzbd.py | 5 ++--- homeassistant/components/sensor/transmission.py | 9 ++++----- homeassistant/components/sensor/vera.py | 2 +- homeassistant/components/sensor/zwave.py | 4 +++- homeassistant/components/switch/hikvisioncam.py | 7 ++++--- homeassistant/components/switch/mystrom.py | 9 +++------ homeassistant/components/switch/transmission.py | 8 ++++---- homeassistant/components/switch/zwave.py | 2 +- homeassistant/components/thermostat/ecobee.py | 5 +++-- homeassistant/components/updater.py | 6 +++--- homeassistant/config.py | 2 +- homeassistant/helpers/event.py | 1 + homeassistant/util/package.py | 6 ++++-- pylintrc | 4 +++- 32 files changed, 96 insertions(+), 94 deletions(-) diff --git a/homeassistant/components/automation/numeric_state.py b/homeassistant/components/automation/numeric_state.py index ab3529235d6..115f351a4f9 100644 --- a/homeassistant/components/automation/numeric_state.py +++ b/homeassistant/components/automation/numeric_state.py @@ -82,7 +82,7 @@ def _in_range(value, range_start, range_end): try: value = float(value) except ValueError: - _LOGGER.warn("Missing value in numeric check") + _LOGGER.warning("Missing value in numeric check") return False if range_start is not None and range_end is not None: diff --git a/homeassistant/components/automation/zone.py b/homeassistant/components/automation/zone.py index 4bf7eccf41e..f0f800bd313 100644 --- a/homeassistant/components/automation/zone.py +++ b/homeassistant/components/automation/zone.py @@ -46,6 +46,7 @@ def trigger(hass, config, action): from_match = _in_zone(hass, zone_entity_id, from_s) if from_s else None to_match = _in_zone(hass, zone_entity_id, to_s) + # pylint: disable=too-many-boolean-expressions if event == EVENT_ENTER and not from_match and to_match or \ event == EVENT_LEAVE and from_match and not to_match: action() diff --git a/homeassistant/components/binary_sensor/arest.py b/homeassistant/components/binary_sensor/arest.py index 3077fe40bdb..7eafca9f2ae 100644 --- a/homeassistant/components/binary_sensor/arest.py +++ b/homeassistant/components/binary_sensor/arest.py @@ -6,9 +6,10 @@ The arest sensor will consume an exposed aREST API of a device. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.arest/ """ -import logging -import requests from datetime import timedelta +import logging + +import requests from homeassistant.util import Throttle from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 169c97595af..fc5c739c888 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -7,19 +7,20 @@ Component to interface with various cameras. For more details about this component, please refer to the documentation at https://home-assistant.io/components/camera/ """ -import requests import logging -import time import re +import time + +import requests + from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity_component import EntityComponent from homeassistant.const import ( ATTR_ENTITY_PICTURE, HTTP_NOT_FOUND, ATTR_ENTITY_ID, ) -from homeassistant.helpers.entity_component import EntityComponent - DOMAIN = 'camera' DEPENDENCIES = ['http'] diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index 7e9542908d5..c81febccc86 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -7,11 +7,12 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/camera.generic/ """ import logging -from requests.auth import HTTPBasicAuth -from homeassistant.helpers import validate_config -from homeassistant.components.camera import DOMAIN -from homeassistant.components.camera import Camera + import requests +from requests.auth import HTTPBasicAuth + +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN, Camera _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 1e643304add..0d59c8d60c7 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -6,13 +6,14 @@ Support for IP Cameras. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/camera.mjpeg/ """ -import logging -from requests.auth import HTTPBasicAuth -from homeassistant.helpers import validate_config -from homeassistant.components.camera import DOMAIN -from homeassistant.components.camera import Camera -import requests from contextlib import closing +import logging + +import requests +from requests.auth import HTTPBasicAuth + +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN, Camera _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ecobee.py b/homeassistant/components/ecobee.py index a41bf7f25cc..03a678f5c49 100644 --- a/homeassistant/components/ecobee.py +++ b/homeassistant/components/ecobee.py @@ -26,14 +26,15 @@ ecobee: """ +from datetime import timedelta +import logging +import os + from homeassistant.loader import get_component from homeassistant import bootstrap from homeassistant.util import Throttle from homeassistant.const import ( EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED, CONF_API_KEY) -from datetime import timedelta -import logging -import os DOMAIN = "ecobee" DISCOVER_THERMOSTAT = "ecobee.thermostat" diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index 31cd64d2530..02664ed896c 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -6,12 +6,13 @@ Support for Z-Wave lights. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.zwave/ """ +# Because we do not compile openzwave on CI # pylint: disable=import-error -import homeassistant.components.zwave as zwave +from threading import Timer from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.components.light import (Light, ATTR_BRIGHTNESS) -from threading import Timer +import homeassistant.components.zwave as zwave def setup_platform(hass, config, add_devices, discovery_info=None): diff --git a/homeassistant/components/media_player/firetv.py b/homeassistant/components/media_player/firetv.py index 1b2c921e3d4..e5f9885f86e 100644 --- a/homeassistant/components/media_player/firetv.py +++ b/homeassistant/components/media_player/firetv.py @@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _LOGGER.info( 'Device %s accessible and ready for control', device_id) else: - _LOGGER.warn( + _LOGGER.warning( 'Device %s is not registered with firetv-server', device_id) except requests.exceptions.RequestException: _LOGGER.error('Could not connect to firetv-server at %s', host) diff --git a/homeassistant/components/notify/xmpp.py b/homeassistant/components/notify/xmpp.py index 016e0a949fd..4b688fb7a79 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/notify/xmpp.py @@ -8,14 +8,14 @@ https://home-assistant.io/components/notify.xmpp/ """ import logging -_LOGGER = logging.getLogger(__name__) - from homeassistant.helpers import validate_config from homeassistant.components.notify import ( DOMAIN, ATTR_TITLE, BaseNotificationService) REQUIREMENTS = ['sleekxmpp==1.3.1', 'dnspython3==1.12.0'] +_LOGGER = logging.getLogger(__name__) + def get_service(hass, config): """ Get the Jabber (XMPP) notification service. """ diff --git a/homeassistant/components/recorder.py b/homeassistant/components/recorder.py index acda166e12a..126d8c9f40e 100644 --- a/homeassistant/components/recorder.py +++ b/homeassistant/components/recorder.py @@ -73,7 +73,7 @@ def row_to_state(row): def row_to_event(row): """ Convert a databse row to an event. """ try: - return Event(row[1], json.loads(row[2]), EventOrigin[row[3].lower()], + return Event(row[1], json.loads(row[2]), EventOrigin(row[3]), date_util.utc_from_timestamp(row[5])) except ValueError: # When json.loads fails diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index f8240bbf7f5..bf7c51fe3fb 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -9,7 +9,6 @@ https://home-assistant.io/components/script/ """ import logging from datetime import timedelta -import homeassistant.util.dt as date_util from itertools import islice import threading @@ -17,6 +16,7 @@ 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 slugify, split_entity_id +import homeassistant.util.dt as date_util from homeassistant.const import ( ATTR_ENTITY_ID, EVENT_TIME_CHANGED, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF) @@ -73,12 +73,12 @@ def setup(hass, config): 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)) + _LOGGER.warning("Found invalid key for script: %s. Use %s instead", + object_id, slugify(object_id)) continue if not isinstance(cfg.get(CONF_SEQUENCE), list): - _LOGGER.warn("Key 'sequence' for script %s should be a list", - object_id) + _LOGGER.warning("Key 'sequence' for script %s should be a list", + object_id) continue alias = cfg.get(CONF_ALIAS, object_id) script = Script(hass, object_id, alias, cfg[CONF_SEQUENCE]) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 332725102dd..f1faa7cc932 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -6,13 +6,14 @@ The arest sensor will consume an exposed aREST API of a device. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.arest/ """ -import logging -import requests from datetime import timedelta +import logging + +import requests -from homeassistant.util import Throttle -from homeassistant.helpers.entity import Entity from homeassistant.const import DEVICE_DEFAULT_NAME +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sensor/ecobee.py b/homeassistant/components/sensor/ecobee.py index a6499949015..5950b5deccb 100644 --- a/homeassistant/components/sensor/ecobee.py +++ b/homeassistant/components/sensor/ecobee.py @@ -25,10 +25,11 @@ ecobee: hold_temp: True """ +import logging + from homeassistant.helpers.entity import Entity from homeassistant.components import ecobee from homeassistant.const import TEMP_FAHRENHEIT -import logging DEPENDENCIES = ['ecobee'] diff --git a/homeassistant/components/sensor/forecast.py b/homeassistant/components/sensor/forecast.py index aa3dd3d3d04..c024c19b5f7 100644 --- a/homeassistant/components/sensor/forecast.py +++ b/homeassistant/components/sensor/forecast.py @@ -9,17 +9,11 @@ https://home-assistant.io/components/sensor.forecast/ import logging from datetime import timedelta -REQUIREMENTS = ['python-forecastio==1.3.3'] - -try: - import forecastio -except ImportError: - forecastio = None - from homeassistant.util import Throttle from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS) from homeassistant.helpers.entity import Entity +REQUIREMENTS = ['python-forecastio==1.3.3'] _LOGGER = logging.getLogger(__name__) # Sensor types are defined like so: @@ -53,11 +47,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120) def setup_platform(hass, config, add_devices, discovery_info=None): """ Get the Forecast.io sensor. """ - - global forecastio # pylint: disable=invalid-name - if forecastio is None: - import forecastio as forecastio_ - forecastio = forecastio_ + import forecastio if None in (hass.config.latitude, hass.config.longitude): _LOGGER.error("Latitude or longitude not set in Home Assistant config") @@ -141,6 +131,7 @@ class ForeCastSensor(Entity): # pylint: disable=too-many-branches def update(self): """ Gets the latest data from Forecast.io and updates the states. """ + import forecastio self.forecast_client.update() data = self.forecast_client.data @@ -209,6 +200,7 @@ class ForeCastData(object): @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """ Gets the latest data from Forecast.io. """ + import forecastio forecast = forecastio.load_forecast(self._api_key, self.latitude, diff --git a/homeassistant/components/sensor/glances.py b/homeassistant/components/sensor/glances.py index 092e4c75733..7938ae7e659 100644 --- a/homeassistant/components/sensor/glances.py +++ b/homeassistant/components/sensor/glances.py @@ -6,9 +6,10 @@ Gathers system information of hosts which running glances. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.glances/ """ -import logging -import requests from datetime import timedelta +import logging + +import requests from homeassistant.util import Throttle from homeassistant.helpers.entity import Entity diff --git a/homeassistant/components/sensor/rest.py b/homeassistant/components/sensor/rest.py index 7fe3a583b08..53609dbb237 100644 --- a/homeassistant/components/sensor/rest.py +++ b/homeassistant/components/sensor/rest.py @@ -6,10 +6,11 @@ The rest sensor will consume JSON responses sent by an exposed REST API. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.rest/ """ -import logging -import requests -from json import loads from datetime import timedelta +from json import loads +import logging + +import requests from homeassistant.util import Throttle from homeassistant.helpers.entity import Entity diff --git a/homeassistant/components/sensor/rpi_gpio.py b/homeassistant/components/sensor/rpi_gpio.py index 2e2746fe9d4..ef7ea8c33c1 100644 --- a/homeassistant/components/sensor/rpi_gpio.py +++ b/homeassistant/components/sensor/rpi_gpio.py @@ -6,13 +6,10 @@ Allows to configure a binary state sensor using RPi GPIO. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.rpi_gpio/ """ +# pylint: disable=import-error import logging from homeassistant.helpers.entity import Entity -try: - import RPi.GPIO as GPIO -except ImportError: - GPIO = None from homeassistant.const import (DEVICE_DEFAULT_NAME, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) @@ -29,10 +26,7 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Raspberry PI GPIO ports. """ - if GPIO is None: - _LOGGER.error('RPi.GPIO not available. rpi_gpio ports ignored.') - return - # pylint: disable=no-member + import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) sensors = [] @@ -65,6 +59,7 @@ class RPiGPIOSensor(Entity): def __init__(self, port_name, port_num, pull_mode, value_high, value_low, bouncetime): # pylint: disable=no-member + import RPi.GPIO as GPIO self._name = port_name or DEVICE_DEFAULT_NAME self._port = port_num self._pull = GPIO.PUD_DOWN if pull_mode == "DOWN" else GPIO.PUD_UP diff --git a/homeassistant/components/sensor/sabnzbd.py b/homeassistant/components/sensor/sabnzbd.py index e478daac2f9..98d76a302dd 100644 --- a/homeassistant/components/sensor/sabnzbd.py +++ b/homeassistant/components/sensor/sabnzbd.py @@ -6,12 +6,11 @@ Monitors SABnzbd NZB client API. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.sabnzbd/ """ -from homeassistant.util import Throttle from datetime import timedelta +import logging from homeassistant.helpers.entity import Entity - -import logging +from homeassistant.util import Throttle REQUIREMENTS = ['https://github.com/jamespcole/home-assistant-nzb-clients/' 'archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip' diff --git a/homeassistant/components/sensor/transmission.py b/homeassistant/components/sensor/transmission.py index 484b045f295..62afdd39bf4 100644 --- a/homeassistant/components/sensor/transmission.py +++ b/homeassistant/components/sensor/transmission.py @@ -6,14 +6,13 @@ Monitors Transmission BitTorrent client API. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.transmission/ """ -from homeassistant.util import Throttle from datetime import timedelta -from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD - -from homeassistant.helpers.entity import Entity - import logging +from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD +from homeassistant.util import Throttle +from homeassistant.helpers.entity import Entity + REQUIREMENTS = ['transmissionrpc==0.11'] SENSOR_TYPES = { 'current_status': ['Status', ''], diff --git a/homeassistant/components/sensor/vera.py b/homeassistant/components/sensor/vera.py index 1848c680517..7fb72fd91b7 100644 --- a/homeassistant/components/sensor/vera.py +++ b/homeassistant/components/sensor/vera.py @@ -95,7 +95,7 @@ class VeraSensor(Entity): @property def state_attributes(self): - attr = super().state_attributes + attr = {} if self.vera_device.has_battery: attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level + '%' diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 23d2f8948f8..1ed831b286d 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -6,9 +6,11 @@ Interfaces with Z-Wave sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/zwave/ """ +# Because we do not compile openzwave on CI # pylint: disable=import-error -from homeassistant.helpers.event import track_point_in_time import datetime + +from homeassistant.helpers.event import track_point_in_time import homeassistant.util.dt as dt_util import homeassistant.components.zwave as zwave from homeassistant.helpers.entity import Entity diff --git a/homeassistant/components/switch/hikvisioncam.py b/homeassistant/components/switch/hikvisioncam.py index 2d91acdf361..c85aae4a7f0 100644 --- a/homeassistant/components/switch/hikvisioncam.py +++ b/homeassistant/components/switch/hikvisioncam.py @@ -6,11 +6,12 @@ Support turning on/off motion detection on Hikvision cameras. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.hikvision/ """ -from homeassistant.helpers.entity import ToggleEntity -from homeassistant.const import STATE_ON, STATE_OFF -from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD import logging +from homeassistant.helpers.entity import ToggleEntity +from homeassistant.const import (STATE_ON, STATE_OFF, + CONF_HOST, CONF_USERNAME, CONF_PASSWORD) + _LOGGING = logging.getLogger(__name__) REQUIREMENTS = ['hikvision==0.4'] # pylint: disable=too-many-arguments diff --git a/homeassistant/components/switch/mystrom.py b/homeassistant/components/switch/mystrom.py index 36a62fe33e4..919ff28e4ef 100644 --- a/homeassistant/components/switch/mystrom.py +++ b/homeassistant/components/switch/mystrom.py @@ -91,12 +91,9 @@ class MyStromSwitch(SwitchDevice): try: request = requests.get('{}/report'.format(self._resource), timeout=10) - if request.json()['relay'] is True: - self._state = True - else: - self._state = False - - self.consumption = request.json()['power'] + data = request.json() + self._state = bool(data['relay']) + self.consumption = data['power'] except requests.exceptions.ConnectionError: _LOGGER.error("No route to device '%s'. Is device offline?", self._resource) diff --git a/homeassistant/components/switch/transmission.py b/homeassistant/components/switch/transmission.py index f3f6a9a8765..1f0da4a00e0 100644 --- a/homeassistant/components/switch/transmission.py +++ b/homeassistant/components/switch/transmission.py @@ -6,12 +6,12 @@ Enable or disable Transmission BitTorrent client Turtle Mode. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.transmission/ """ -from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD -from homeassistant.const import STATE_ON, STATE_OFF - -from homeassistant.helpers.entity import ToggleEntity import logging +from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, + STATE_ON, STATE_OFF) +from homeassistant.helpers.entity import ToggleEntity + _LOGGING = logging.getLogger(__name__) REQUIREMENTS = ['transmissionrpc==0.11'] diff --git a/homeassistant/components/switch/zwave.py b/homeassistant/components/switch/zwave.py index 493e2234bcf..f4777340445 100644 --- a/homeassistant/components/switch/zwave.py +++ b/homeassistant/components/switch/zwave.py @@ -4,8 +4,8 @@ homeassistant.components.switch.zwave Zwave platform that handles simple binary switches. """ +# Because we do not compile openzwave on CI # pylint: disable=import-error - import homeassistant.components.zwave as zwave from homeassistant.components.switch import SwitchDevice diff --git a/homeassistant/components/thermostat/ecobee.py b/homeassistant/components/thermostat/ecobee.py index 78f4d555c9c..66117954a51 100644 --- a/homeassistant/components/thermostat/ecobee.py +++ b/homeassistant/components/thermostat/ecobee.py @@ -25,11 +25,12 @@ ecobee: hold_temp: True """ +import logging + +from homeassistant.components import ecobee from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) from homeassistant.const import (TEMP_FAHRENHEIT, STATE_ON, STATE_OFF) -from homeassistant.components import ecobee -import logging DEPENDENCIES = ['ecobee'] diff --git a/homeassistant/components/updater.py b/homeassistant/components/updater.py index a020a6c0abb..d4eb97f5ec5 100644 --- a/homeassistant/components/updater.py +++ b/homeassistant/components/updater.py @@ -47,10 +47,10 @@ def get_newest_version(): return req.json()['info']['version'] except requests.RequestException: _LOGGER.exception('Could not contact PyPI to check for updates') - return + return None except ValueError: _LOGGER.exception('Received invalid response from PyPI') - return + return None except KeyError: _LOGGER.exception('Response from PyPI did not include version') - return + return None diff --git a/homeassistant/config.py b/homeassistant/config.py index 2c2152df7a0..3d17fce5e17 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -67,7 +67,7 @@ def create_default_config(config_dir, detect_location=True): Returns path to new config file if success, None if failed. """ config_path = os.path.join(config_dir, YAML_CONFIG_FILE) - info = {attr: default for attr, default, *_ in DEFAULT_CONFIG} + info = {attr: default for attr, default, _, _ in DEFAULT_CONFIG} location_info = detect_location and loc_util.detect_location_info() diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 60377fd1f5d..3934a6c52ef 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -124,6 +124,7 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None, mat = _matcher + # pylint: disable=too-many-boolean-expressions if mat(now.year, year) and \ mat(now.month, month) and \ mat(now.day, day) and \ diff --git a/homeassistant/util/package.py b/homeassistant/util/package.py index fdfbc133944..fd320090736 100644 --- a/homeassistant/util/package.py +++ b/homeassistant/util/package.py @@ -1,12 +1,13 @@ """Helpers to install PyPi packages.""" import logging import os -import pkg_resources import subprocess import sys import threading from urllib.parse import urlparse +import pkg_resources + _LOGGER = logging.getLogger(__name__) INSTALL_LOCK = threading.Lock() @@ -27,7 +28,7 @@ def install_package(package, upgrade=True, target=None): args += ['--target', os.path.abspath(target)] try: - return 0 == subprocess.call(args) + return subprocess.call(args) == 0 except subprocess.SubprocessError: _LOGGER.exception('Unable to install pacakge %s', package) return False @@ -50,4 +51,5 @@ def check_package_exists(package, lib_dir): return True # Check packages from global + virtual environment + # pylint: disable=not-an-iterable return any(dist in req for dist in pkg_resources.working_set) diff --git a/pylintrc b/pylintrc index e8455cf4245..768cd3d46ff 100644 --- a/pylintrc +++ b/pylintrc @@ -9,6 +9,7 @@ reports=no # abstract-class-not-used - is flaky, should not show up but does # unused-argument - generic callbacks and setup methods create a lot of warnings # global-statement - used for the on-demand requirement installation +# redefined-variable-type - this is Python, we're duck typing! disable= locally-disabled, duplicate-code, @@ -16,7 +17,8 @@ disable= abstract-class-little-used, abstract-class-not-used, unused-argument, - global-statement + global-statement, + redefined-variable-type [EXCEPTIONS] overgeneral-exceptions=Exception,HomeAssistantError From 5023772b3eba14112c8e6f30cff30eeddca44234 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 13:55:46 -0800 Subject: [PATCH 103/125] Fix gen_requirements_all.py and add updated requirements_all.txt --- requirements_all.txt | 1 - script/gen_requirements_all.py | 31 ++++++------------------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/requirements_all.txt b/requirements_all.txt index bf07dd4dfcf..01af666b720 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -170,4 +170,3 @@ https://github.com/persandstrom/python-verisure/archive/9873c4527f01b1ba1f72ae60 # homeassistant.components.zwave pydispatcher==2.0.5 - diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index a5f3c347fa4..730886075ec 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -8,7 +8,6 @@ import importlib import os import pkgutil import re -import argparse COMMENT_REQUIREMENTS = [ 'RPi.GPIO', @@ -60,10 +59,6 @@ def gather_modules(): except ImportError: errors.append(package) continue - # For catching the error by RPi.GPIO - # RuntimeError: This module can only be run on a Raspberry Pi! - except RuntimeError: - continue if not getattr(module, 'REQUIREMENTS', None): continue @@ -74,7 +69,7 @@ def gather_modules(): if errors: print("Found errors") print('\n'.join(errors)) - return + return None output.append('# Home Assistant core') output.append('\n') @@ -99,32 +94,18 @@ def write_file(data): req_file.write(data) -def display(data): - """ Prints the output to command line. """ - print(data) - - -def argparsing(): - """ Parsing the command line arguments. """ - parser = argparse.ArgumentParser( - description='Generate a requirements_all.txt') - parser.add_argument('file', nargs='?', - help='create new requirements_all.txt file') - return parser.parse_args() - - def main(): """ Main """ if not os.path.isfile('requirements_all.txt'): print('Run this from HA root dir') return - args = argparsing() + data = gather_modules() - if args.file: - write_file(data) - else: - display(data) + if data is None: + return + + write_file(data) if __name__ == '__main__': main() From 7ba9fb90f1782c6c77561e2d73456146e1e2a291 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 14:04:44 -0800 Subject: [PATCH 104/125] More PyLint fixes --- homeassistant/components/camera/foscam.py | 7 ++++--- homeassistant/components/media_player/itunes.py | 4 ++-- homeassistant/components/media_player/kodi.py | 12 ++---------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/camera/foscam.py b/homeassistant/components/camera/foscam.py index d4d707c790f..b210e1a2f1b 100644 --- a/homeassistant/components/camera/foscam.py +++ b/homeassistant/components/camera/foscam.py @@ -7,11 +7,12 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/camera.foscam/ """ import logging -from homeassistant.helpers import validate_config -from homeassistant.components.camera import DOMAIN -from homeassistant.components.camera import Camera + import requests +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN, Camera + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index 275e7d96dee..5d08a7e95d4 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -8,6 +8,8 @@ https://home-assistant.io/components/media_player.itunes/ """ import logging +import requests + from homeassistant.components.media_player import ( MediaPlayerDevice, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, SUPPORT_PAUSE, SUPPORT_SEEK, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, @@ -17,8 +19,6 @@ from homeassistant.components.media_player import ( from homeassistant.const import ( STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_ON) -import requests - _LOGGER = logging.getLogger(__name__) SUPPORT_ITUNES = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index eda143b6cce..6fe6be554c6 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -15,11 +15,6 @@ from homeassistant.components.media_player import ( from homeassistant.const import ( STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF) -try: - import jsonrpc_requests -except ImportError: - jsonrpc_requests = None - _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['jsonrpc-requests==0.1'] @@ -31,11 +26,6 @@ SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the kodi platform. """ - global jsonrpc_requests # pylint: disable=invalid-name - if jsonrpc_requests is None: - import jsonrpc_requests as jsonrpc_requests_ - jsonrpc_requests = jsonrpc_requests_ - add_devices([ KodiDevice( config.get('name', 'Kodi'), @@ -60,6 +50,7 @@ class KodiDevice(MediaPlayerDevice): # pylint: disable=too-many-public-methods def __init__(self, name, url, auth=None): + import jsonrpc_requests self._name = name self._url = url self._server = jsonrpc_requests.Server(url, auth=auth) @@ -77,6 +68,7 @@ class KodiDevice(MediaPlayerDevice): def _get_players(self): """ Returns the active player objects or None """ + import jsonrpc_requests try: return self._server.Player.GetActivePlayers() except jsonrpc_requests.jsonrpc.TransportError: From 5fba67f6c37a7c36935e4f9cb227b21f1b7a1c7b Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sat, 14 Nov 2015 12:56:18 -0500 Subject: [PATCH 105/125] limitlessled improvements --- .../components/light/limitlessled.py | 348 +++++++++++------- requirements_all.txt | 2 +- 2 files changed, 226 insertions(+), 124 deletions(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index ad5f8487a2a..8700dd85bf2 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -8,171 +8,273 @@ https://home-assistant.io/components/light.limitlessled/ """ import logging -from homeassistant.const import DEVICE_DEFAULT_NAME from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_EFFECT, + ATTR_COLOR_TEMP, ATTR_TRANSITION, + ATTR_FLASH, FLASH_LONG, EFFECT_COLORLOOP, EFFECT_WHITE) +from limitlessled import Color +from limitlessled.bridge import Bridge +from limitlessled.group.rgbw import RgbwGroup +from limitlessled.group.white import WhiteGroup +from limitlessled.pipeline import Pipeline +from limitlessled.presets import COLORLOOP + + _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['ledcontroller==1.1.0'] - -COLOR_TABLE = { - 'white': [0xFF, 0xFF, 0xFF], - 'violet': [0xEE, 0x82, 0xEE], - 'royal_blue': [0x41, 0x69, 0xE1], - 'baby_blue': [0x87, 0xCE, 0xFA], - 'aqua': [0x00, 0xFF, 0xFF], - 'royal_mint': [0x7F, 0xFF, 0xD4], - 'seafoam_green': [0x2E, 0x8B, 0x57], - 'green': [0x00, 0x80, 0x00], - 'lime_green': [0x32, 0xCD, 0x32], - 'yellow': [0xFF, 0xFF, 0x00], - 'yellow_orange': [0xDA, 0xA5, 0x20], - 'orange': [0xFF, 0xA5, 0x00], - 'red': [0xFF, 0x00, 0x00], - 'pink': [0xFF, 0xC0, 0xCB], - 'fusia': [0xFF, 0x00, 0xFF], - 'lilac': [0xDA, 0x70, 0xD6], - 'lavendar': [0xE6, 0xE6, 0xFA], -} +REQUIREMENTS = ['limitlessled==1.0.0'] +RGB_BOUNDARY = 40 +DEFAULT_TRANSITION = 0 +DEFAULT_PORT = 8899 +DEFAULT_VERSION = 5 +DEFAULT_LED_TYPE = 'rgbw' +WHITE = [255, 255, 255] -def _distance_squared(rgb1, rgb2): - """ Return sum of squared distances of each color part. """ - return sum((val1-val2)**2 for val1, val2 in zip(rgb1, rgb2)) - - -def _rgb_to_led_color(rgb_color): - """ Convert an RGB color to the closest color string and color. """ - return sorted((_distance_squared(rgb_color, color), name) - for name, color in COLOR_TABLE.items())[0][1] +def legacy_setup(config, add_devices_callback): + """ Perform setup using legacy format. """ + bridges = config.get('bridges', [config]) + lights = [] + for bridge_conf in bridges: + bridge = Bridge(bridge_conf.get('host')) + for i in range(1, 5): + name_key = 'group_%d_name' % i + if name_key in bridge_conf: + group_type = bridge_conf.get('group_%d_type' % i, + DEFAULT_LED_TYPE) + group = bridge.add_group(i, + bridge_conf.get(name_key), + group_type) + lights.append(LimitlessLEDGroup.factory(group)) + add_devices_callback(lights) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Gets the LimitlessLED lights. """ - import ledcontroller - # Handle old configuration format: - bridges = config.get('bridges', [config]) - - for bridge_id, bridge in enumerate(bridges): - bridge['id'] = bridge_id - - pool = ledcontroller.LedControllerPool([x['host'] for x in bridges]) + # Two legacy configuration formats are supported to + # maintain backwards compatibility. + legacy_setup(config, add_devices_callback) + # Use the expanded configuration format. + if 'bridges' not in config: + return lights = [] - for bridge in bridges: - for i in range(1, 5): - name_key = 'group_%d_name' % i - if name_key in bridge: - group_type = bridge.get('group_%d_type' % i, 'rgbw') - lights.append(LimitlessLED.factory(pool, bridge['id'], i, - bridge[name_key], - group_type)) - + for bridge_conf in config.get('bridges'): + if 'groups' not in bridge_conf: + continue + bridge = Bridge(bridge_conf.get('host'), + port=bridge_conf.get('port', DEFAULT_PORT), + version=bridge_conf.get('version', DEFAULT_VERSION)) + for group_conf in bridge_conf.get('groups'): + group = bridge.add_group(group_conf.get('number'), + group_conf.get('name'), + group_conf.get('type', DEFAULT_LED_TYPE)) + lights.append(LimitlessLEDGroup.factory(group)) add_devices_callback(lights) -class LimitlessLED(Light): - """ Represents a LimitlessLED light """ +def state(new_state): + """ State decorator. + + Specify True (turn on) or False (turn off). + """ + def decorator(function): + """ Decorator function. """ + # pylint: disable=no-member + def wrapper(self, **kwargs): + """ Wrap a group state change. """ + pipeline = Pipeline() + transition_time = DEFAULT_TRANSITION + # Stop any repeating pipeline. + if self.repeating: + self.repeating = False + self.group.stop() + # Not on? Turn on. + if not self.is_on: + pipeline.on() + # Set transition time. + if ATTR_TRANSITION in kwargs: + transition_time = kwargs[ATTR_TRANSITION] + # Do group type-specific work. + function(self, transition_time, pipeline, **kwargs) + # Update state. + self.on_state = new_state + self.group.enqueue(pipeline) + self.update_ha_state() + return wrapper + return decorator + + +class LimitlessLEDGroup(Light): + """ LimitessLED group. """ + def __init__(self, group): + """ Initialize a group. """ + self.group = group + self.repeating = False + self.on_state = False + self._brightness = None @staticmethod - def factory(pool, controller_id, group, name, group_type): - ''' Construct a Limitless LED of the appropriate type ''' - if group_type == 'white': - return WhiteLimitlessLED(pool, controller_id, group, name) - elif group_type == 'rgbw': - return RGBWLimitlessLED(pool, controller_id, group, name) - - # pylint: disable=too-many-arguments - def __init__(self, pool, controller_id, group, name, group_type): - self.pool = pool - self.controller_id = controller_id - self.group = group - - self.pool.execute(self.controller_id, "set_group_type", self.group, - group_type) - - # LimitlessLEDs don't report state, we have track it ourselves. - self.pool.execute(self.controller_id, "off", self.group) - - self._name = name or DEVICE_DEFAULT_NAME - self._state = False + def factory(group): + """ Produce LimitlessLEDGroup objects. """ + if isinstance(group, WhiteGroup): + return LimitlessLEDWhiteGroup(group) + elif isinstance(group, RgbwGroup): + return LimitlessLEDRGBWGroup(group) @property def should_poll(self): - """ No polling needed. """ + """ No polling needed. + + LimitlessLED state cannot be fetched. + """ return False @property def name(self): - """ Returns the name of the device if any. """ - return self._name + """ Returns the name of the group. """ + return self.group.name @property def is_on(self): """ True if device is on. """ - return self._state - - def turn_off(self, **kwargs): - """ Turn the device off. """ - self._state = False - self.pool.execute(self.controller_id, "off", self.group) - self.update_ha_state() - - -class RGBWLimitlessLED(LimitlessLED): - """ Represents a RGBW LimitlessLED light """ - - def __init__(self, pool, controller_id, group, name): - super().__init__(pool, controller_id, group, name, 'rgbw') - - self._brightness = 100 - self._led_color = 'white' + return self.on_state @property def brightness(self): + """ Brightness property. """ return self._brightness + @state(False) + def turn_off(self, transition_time, pipeline, **kwargs): + """ Turn off a group. """ + pipeline.transition(transition_time, brightness=0.0).off() + + +class LimitlessLEDWhiteGroup(LimitlessLEDGroup): + """ LimitlessLED White group. """ + def __init__(self, group): + """ Initialize White group. """ + super().__init__(group) + # Initialize group with known values. + self.group.on = True + self.group.temperature = 1.0 + self.group.brightness = 0.0 + self._brightness = _to_hass_brightness(1.0) + self._temperature = _to_hass_temperature(self.group.temperature) + self.group.on = False + + @property + def color_temp(self): + """ Temperature property. """ + return self._temperature + + @state(True) + def turn_on(self, transition_time, pipeline, **kwargs): + """ Turn on (or adjust property of) a group. """ + # Check arguments. + if ATTR_BRIGHTNESS in kwargs: + self._brightness = kwargs[ATTR_BRIGHTNESS] + if ATTR_COLOR_TEMP in kwargs: + self._temperature = kwargs[ATTR_COLOR_TEMP] + # Set up transition. + pipeline.transition(transition_time, + brightness=_from_hass_brightness( + self._brightness), + temperature=_from_hass_temperature( + self._temperature)) + + +class LimitlessLEDRGBWGroup(LimitlessLEDGroup): + """ LimitlessLED RGBW group. """ + def __init__(self, group): + """ Initialize RGBW group. """ + super().__init__(group) + # Initialize group with known values. + self.group.on = True + self.group.white() + self._color = WHITE + self.group.brightness = 0.0 + self._brightness = _to_hass_brightness(1.0) + self.group.on = False + @property def rgb_color(self): - return COLOR_TABLE[self._led_color] - - def turn_on(self, **kwargs): - """ Turn the device on. """ - self._state = True + """ Color property. """ + return self._color + @state(True) + def turn_on(self, transition_time, pipeline, **kwargs): + """ Turn on (or adjust property of) a group. """ + # Check arguments. if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] - if ATTR_RGB_COLOR in kwargs: - self._led_color = _rgb_to_led_color(kwargs[ATTR_RGB_COLOR]) - - effect = kwargs.get(ATTR_EFFECT) - - if effect == EFFECT_COLORLOOP: - self.pool.execute(self.controller_id, "disco", self.group) - elif effect == EFFECT_WHITE: - self.pool.execute(self.controller_id, "white", self.group) - else: - self.pool.execute(self.controller_id, "set_color", - self._led_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() + self._color = kwargs[ATTR_RGB_COLOR] + # White is a special case. + if min(self._color) > 256 - RGB_BOUNDARY: + pipeline.white() + self._color = WHITE + # Set up transition. + pipeline.transition(transition_time, + brightness=_from_hass_brightness( + self._brightness), + color=_from_hass_color(self._color)) + # Flash. + if ATTR_FLASH in kwargs: + duration = 0 + if kwargs[ATTR_FLASH] == FLASH_LONG: + duration = 1 + pipeline.flash(duration=duration) + # Add effects. + if ATTR_EFFECT in kwargs: + if kwargs[ATTR_EFFECT] == EFFECT_COLORLOOP: + self.repeating = True + pipeline.append(COLORLOOP) + if kwargs[ATTR_EFFECT] == EFFECT_WHITE: + pipeline.white() + self._color = WHITE -class WhiteLimitlessLED(LimitlessLED): - """ Represents a White LimitlessLED light """ +def _from_hass_temperature(temperature): + """ Convert Home Assistant color temperature + units to percentage. + """ + return (temperature - 154) / 346 - def __init__(self, pool, controller_id, group, name): - super().__init__(pool, controller_id, group, name, 'white') - def turn_on(self, **kwargs): - """ Turn the device on. """ - self._state = True - self.pool.execute(self.controller_id, "on", self.group) - self.update_ha_state() +def _to_hass_temperature(temperature): + """ Convert percentage to Home Assistant + color temperature units. + """ + return int(temperature * 346) + 154 + + +def _from_hass_brightness(brightness): + """ Convert Home Assistant brightness units + to percentage. + """ + return brightness / 255 + + +def _to_hass_brightness(brightness): + """ Convert percentage to Home Assistant + brightness units. + """ + return int(brightness * 255) + + +def _from_hass_color(color): + """ Convert Home Assistant RGB list + to Color tuple. + """ + return Color(*tuple(color)) + + +def _to_hass_color(color): + """ Convert from Color tuple to + Home Assistant RGB list. + """ + return list([int(c) for c in color]) diff --git a/requirements_all.txt b/requirements_all.txt index 8ce63e99a01..1ff81bcc64a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -39,7 +39,7 @@ blinkstick==1.1.7 phue==0.8 # homeassistant.components.light.limitlessled -ledcontroller==1.1.0 +limitlessled==1.0.0 # homeassistant.components.light.tellstick # homeassistant.components.sensor.tellstick From 32003daf3f99f1ea636c9361b5d77e79564e09ca Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sat, 28 Nov 2015 12:21:00 -0500 Subject: [PATCH 106/125] refactor legacy; move imports --- .../components/light/limitlessled.py | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index 8700dd85bf2..4952a3144ba 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -14,13 +14,6 @@ from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_FLASH, FLASH_LONG, EFFECT_COLORLOOP, EFFECT_WHITE) -from limitlessled import Color -from limitlessled.bridge import Bridge -from limitlessled.group.rgbw import RgbwGroup -from limitlessled.group.white import WhiteGroup -from limitlessled.pipeline import Pipeline -from limitlessled.presets import COLORLOOP - _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['limitlessled==1.0.0'] @@ -32,38 +25,43 @@ DEFAULT_LED_TYPE = 'rgbw' WHITE = [255, 255, 255] -def legacy_setup(config, add_devices_callback): - """ Perform setup using legacy format. """ +def rewrite_legacy(config): + """ Rewrite legacy configuration to new format. """ bridges = config.get('bridges', [config]) - lights = [] + new_bridges = [] for bridge_conf in bridges: - bridge = Bridge(bridge_conf.get('host')) - for i in range(1, 5): - name_key = 'group_%d_name' % i - if name_key in bridge_conf: - group_type = bridge_conf.get('group_%d_type' % i, - DEFAULT_LED_TYPE) - group = bridge.add_group(i, - bridge_conf.get(name_key), - group_type) - lights.append(LimitlessLEDGroup.factory(group)) - add_devices_callback(lights) + groups = [] + if 'groups' in bridge_conf: + groups = bridge_conf['groups'] + else: + _LOGGER.warn("Legacy configuration format detected") + for i in range(1, 5): + name_key = 'group_%d_name' % i + if name_key in bridge_conf: + groups.append({ + 'number': i, + 'type': bridge_conf.get('group_%d_type' % i, + DEFAULT_LED_TYPE), + 'name': bridge_conf.get(name_key) + }) + new_bridges.append({ + 'host': bridge_conf.get('host'), + 'groups': groups + }) + return {'bridges': new_bridges} def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Gets the LimitlessLED lights. """ + from limitlessled.bridge import Bridge # Two legacy configuration formats are supported to # maintain backwards compatibility. - legacy_setup(config, add_devices_callback) + config = rewrite_legacy(config) # Use the expanded configuration format. - if 'bridges' not in config: - return lights = [] for bridge_conf in config.get('bridges'): - if 'groups' not in bridge_conf: - continue bridge = Bridge(bridge_conf.get('host'), port=bridge_conf.get('port', DEFAULT_PORT), version=bridge_conf.get('version', DEFAULT_VERSION)) @@ -82,9 +80,10 @@ def state(new_state): """ def decorator(function): """ Decorator function. """ - # pylint: disable=no-member + # pylint: disable=no-member,protected-access def wrapper(self, **kwargs): """ Wrap a group state change. """ + from limitlessled.pipeline import Pipeline pipeline = Pipeline() transition_time = DEFAULT_TRANSITION # Stop any repeating pipeline. @@ -100,7 +99,7 @@ def state(new_state): # Do group type-specific work. function(self, transition_time, pipeline, **kwargs) # Update state. - self.on_state = new_state + self._is_on = new_state self.group.enqueue(pipeline) self.update_ha_state() return wrapper @@ -113,12 +112,14 @@ class LimitlessLEDGroup(Light): """ Initialize a group. """ self.group = group self.repeating = False - self.on_state = False + self._is_on = False self._brightness = None @staticmethod def factory(group): """ Produce LimitlessLEDGroup objects. """ + from limitlessled.group.rgbw import RgbwGroup + from limitlessled.group.white import WhiteGroup if isinstance(group, WhiteGroup): return LimitlessLEDWhiteGroup(group) elif isinstance(group, RgbwGroup): @@ -140,7 +141,7 @@ class LimitlessLEDGroup(Light): @property def is_on(self): """ True if device is on. """ - return self.on_state + return self._is_on @property def brightness(self): @@ -208,6 +209,7 @@ class LimitlessLEDRGBWGroup(LimitlessLEDGroup): @state(True) def turn_on(self, transition_time, pipeline, **kwargs): """ Turn on (or adjust property of) a group. """ + from limitlessled.presets import COLORLOOP # Check arguments. if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -270,6 +272,7 @@ def _from_hass_color(color): """ Convert Home Assistant RGB list to Color tuple. """ + from limitlessled import Color return Color(*tuple(color)) From 6e0c30d9f7fe89f8bcdd698df70a27b5be0b25e8 Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sun, 29 Nov 2015 18:29:37 -0500 Subject: [PATCH 107/125] warn -> warning --- 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 4952a3144ba..4cc7b1ab3b7 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -34,7 +34,7 @@ def rewrite_legacy(config): if 'groups' in bridge_conf: groups = bridge_conf['groups'] else: - _LOGGER.warn("Legacy configuration format detected") + _LOGGER.warning("Legacy configuration format detected") for i in range(1, 5): name_key = 'group_%d_name' % i if name_key in bridge_conf: From 72ebb22eba1345013bcea1d77a6d6383eb119a88 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 17:59:59 -0800 Subject: [PATCH 108/125] Update demo entities --- .../components/alarm_control_panel/demo.py | 31 ++----------------- .../components/binary_sensor/demo.py | 12 ++----- homeassistant/components/demo.py | 2 +- 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/demo.py b/homeassistant/components/alarm_control_panel/demo.py index 2d2e36d03bc..0ace53167de 100644 --- a/homeassistant/components/alarm_control_panel/demo.py +++ b/homeassistant/components/alarm_control_panel/demo.py @@ -3,36 +3,11 @@ homeassistant.components.alarm_control_panel.demo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Demo platform that has two fake alarm control panels. """ -import homeassistant.components.alarm_control_panel.manual as Alarm -from homeassistant.const import (STATE_ALARM_DISARMED, - STATE_ALARM_ARMED_AWAY) +import homeassistant.components.alarm_control_panel.manual as manual def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Demo alarm control panels. """ add_devices([ - - DemoAlarmControlPanel(hass, 'Front door', '1234', 2, 4, - STATE_ALARM_DISARMED), - DemoAlarmControlPanel(hass, 'Safe', '1234', 2, 4, - STATE_ALARM_ARMED_AWAY), - ]) - - -# pylint: disable=too-many-arguments -class DemoAlarmControlPanel(Alarm.ManualAlarm): - """ A Demo alarm control panel. """ - - def __init__(self, hass, name, code, pending_time, trigger_time, state): - super().__init__(hass, name, code, pending_time, trigger_time) - self._state = state - - @property - def should_poll(self): - """ No polling needed for a demo panel. """ - return False - - @property - def state(self): - """ Returns the state of the device. """ - return self._state + manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10), + ]) diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py index a24b893c610..087d7405d9b 100644 --- a/homeassistant/components/binary_sensor/demo.py +++ b/homeassistant/components/binary_sensor/demo.py @@ -9,18 +9,17 @@ from homeassistant.components.binary_sensor import BinarySensorDevice def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Demo binary sensors. """ add_devices([ - DemoBinarySensor('Window Bathroom', True, None), - DemoBinarySensor('Floor Basement', False, None), + DemoBinarySensor('Basement Floor Wet', False), + DemoBinarySensor('Movement Backyard', True), ]) class DemoBinarySensor(BinarySensorDevice): """ A Demo binary sensor. """ - def __init__(self, name, state, icon=None): + def __init__(self, name, state): self._name = name self._state = state - self._icon = icon @property def should_poll(self): @@ -32,11 +31,6 @@ class DemoBinarySensor(BinarySensorDevice): """ Returns the name of the binary sensor. """ return self._name - @property - def icon(self): - """ Returns the icon to use for device if any. """ - return self._icon - @property def is_on(self): """ True if the binary sensor is on. """ diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 261c0e1b6a9..de87bfc9fb1 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -18,7 +18,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', - 'thermostat', 'camera', 'binary_sensor', 'alarm_control_panel'] + 'thermostat', 'camera', 'binary_sensor', 'alarm_control_panel', 'lock'] def setup(hass, config): From 5b4f607da19244f7eefc9350352475f9fb347a4e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 22:49:54 -0800 Subject: [PATCH 109/125] Upgrade frontend with lock support --- homeassistant/components/frontend/version.py | 2 +- .../components/frontend/www_static/frontend.html | 14 +++++++------- .../frontend/www_static/home-assistant-polymer | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 04ccb720f6b..a3c09fc4149 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 = "36df87bb6c219a2ee59adf416e3abdfa" +VERSION = "a07269fd3559611af16a4204b7645a64" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index e600c4c5583..36921057fca 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -6054,10 +6054,10 @@ case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return text-rendering: optimizeLegibility; } \ No newline at end of file +}},{key:"batch",value:function(t){this.batchStart(),t(),this.batchEnd()}},{key:"registerStore",value:function(t,e){console.warn("Deprecation warning: `registerStore` will no longer be supported in 1.1, use `registerStores` instead"),this.registerStores(i({},t,e))}},{key:"registerStores",value:function(t){this.reactorState=d["default"].registerStores(this.reactorState,t),this.__notify()}},{key:"serialize",value:function(){return d["default"].serialize(this.reactorState)}},{key:"loadState",value:function(t){this.reactorState=d["default"].loadState(this.reactorState,t),this.__notify()}},{key:"reset",value:function(){var t=d["default"].reset(this.reactorState);this.reactorState=t,this.prevReactorState=t,this.observerState=new y.ObserverState}},{key:"__notify",value:function(){var t=this;if(!(this.__batchDepth>0)){var e=this.reactorState.get("dirtyStores");if(0!==e.size){var n=s["default"].Set().withMutations(function(n){n.union(t.observerState.get("any")),e.forEach(function(e){var r=t.observerState.getIn(["stores",e]);r&&n.union(r)})});n.forEach(function(e){var n=t.observerState.getIn(["observersMap",e]);if(n){var r=n.get("getter"),i=n.get("handler"),o=d["default"].evaluate(t.prevReactorState,r),u=d["default"].evaluate(t.reactorState,r);t.prevReactorState=o.reactorState,t.reactorState=u.reactorState;var a=o.result,c=u.result;s["default"].is(a,c)||i.call(null,c)}});var r=d["default"].resetDirtyStores(this.reactorState);this.prevReactorState=r,this.reactorState=r}}}},{key:"batchStart",value:function(){this.__batchDepth++}},{key:"batchEnd",value:function(){if(this.__batchDepth--,this.__batchDepth<=0){this.__isDispatching=!0;try{this.__notify()}catch(t){throw this.__isDispatching=!1,t}this.__isDispatching=!1}}}]),t}();e["default"]=(0,_.toFactory)(m),t.exports=e["default"]},function(t,e,n){function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n={};return(0,o.each)(e,function(e,r){n[r]=t.evaluate(e)}),n}Object.defineProperty(e,"__esModule",{value:!0});var o=n(4);e["default"]=function(t){return{getInitialState:function(){return i(t,this.getDataBindings())},componentDidMount:function(){var e=this;this.__unwatchFns=[],(0,o.each)(this.getDataBindings(),function(n,i){var o=t.observe(n,function(t){e.setState(r({},i,t))});e.__unwatchFns.push(o)})},componentWillUnmount:function(){for(;this.__unwatchFns.length;)this.__unwatchFns.shift()()}}},t.exports=e["default"]},function(t,e,n){function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){return new b({result:t,reactorState:e})}function o(t){return t}function u(t,e){var n=o(e);return t.getIn(["cache",n])}function a(t,e){var n=u(t,e);if(!n)return!1;var r=n.get("storeStates");return 0===r.size?!1:r.every(function(e,n){return t.getIn(["storeStates",n])===e})}function s(t,e,n){var r=o(e),i=t.get("dispatchId"),u=(0,y.getStoreDeps)(e),a=(0,_.toImmutable)({}).withMutations(function(e){u.forEach(function(n){var r=t.getIn(["storeStates",n]);e.set(n,r)})});return t.setIn(["cache",r],p["default"].Map({value:n,storeStates:a,dispatchId:i}))}function c(t,e){var n=o(e);return t.getIn(["cache",n,"value"])}function l(t){return t.update("dispatchId",function(t){return t+1})}function f(t,e){return t.withMutations(function(t){e.forEach(function(e){var n=t.has(e)?t.get(e)+1:1;t.set(e,n)})})}var d=n(3),p=r(d),h=n(9),v=r(h),_=n(5),y=n(10),m=n(11),g=n(4),b=p["default"].Record({result:null,reactorState:null});e.registerStores=function(t,e){var n=t.get("debug");return t.withMutations(function(t){(0,g.each)(e,function(e,r){t.getIn(["stores",r])&&console.warn("Store already defined for id = "+r);var i=e.getInitialState();if(n&&!(0,_.isImmutableValue)(i))throw new Error("Store getInitialState() must return an immutable value, did you forget to call toImmutable");t.update("stores",function(t){return t.set(r,e)}).update("state",function(t){return t.set(r,i)}).update("dirtyStores",function(t){return t.add(r)}).update("storeStates",function(t){return f(t,[r])})}),l(t)})},e.dispatch=function(t,e,n){var r=t.get("state"),i=t.get("debug"),o=t.get("dirtyStores"),u=r.withMutations(function(r){i&&v["default"].dispatchStart(e,n),t.get("stores").forEach(function(t,u){var a=r.get(u),s=void 0;try{s=t.handle(a,e,n)}catch(c){throw v["default"].dispatchError(c.message),c}if(i&&void 0===s){var l="Store handler must return a value, did you forget a return statement";throw v["default"].dispatchError(l),new Error(l)}r.set(u,s),a!==s&&(o=o.add(u)),i&&v["default"].storeHandled(u,a,s)}),i&&v["default"].dispatchEnd(r)}),a=t.set("state",u).set("dirtyStores",o).update("storeStates",function(t){return f(t,o)});return l(a)},e.loadState=function(t,e){var n=[],r=(0,_.toImmutable)({}).withMutations(function(r){(0,g.each)(e,function(e,i){var o=t.getIn(["stores",i]);if(o){var u=o.deserialize(e);void 0!==u&&(r.set(i,u),n.push(i))}})}),i=p["default"].Set(n);return t.update("state",function(t){return t.merge(r)}).update("dirtyStores",function(t){return t.union(i)}).update("storeStates",function(t){return f(t,n)})},e.addObserver=function(t,e,n){var r=e;(0,m.isKeyPath)(e)&&(e=(0,y.fromKeyPath)(e));var i=t.get("nextId"),o=(0,y.getStoreDeps)(e),u=p["default"].Map({id:i,storeDeps:o,getterKey:r,getter:e,handler:n}),a=void 0;return a=0===o.size?t.update("any",function(t){return t.add(i)}):t.withMutations(function(t){o.forEach(function(e){var n=["stores",e];t.hasIn(n)||t.setIn(n,p["default"].Set([])),t.updateIn(["stores",e],function(t){return t.add(i)})})}),a=a.set("nextId",i+1).setIn(["observersMap",i],u),{observerState:a,entry:u}},e.removeObserver=function(t,n,r){var i=t.get("observersMap").filter(function(t){var e=t.get("getterKey"),i=!r||t.get("handler")===r;return i?(0,m.isKeyPath)(n)&&(0,m.isKeyPath)(e)?(0,m.isEqual)(n,e):n===e:!1});return t.withMutations(function(t){i.forEach(function(n){return e.removeObserverByEntry(t,n)})})},e.removeObserverByEntry=function(t,e){return t.withMutations(function(t){var n=e.get("id"),r=e.get("storeDeps");0===r.size?t.update("any",function(t){return t.remove(n)}):r.forEach(function(e){t.updateIn(["stores",e],function(t){return t?t.remove(n):t})}),t.removeIn(["observersMap",n])})},e.reset=function(t){var n=t.get("debug"),r=t.get("state");return t.withMutations(function(t){var i=t.get("stores"),o=i.keySeq().toJS();i.forEach(function(e,i){var o=r.get(i),u=e.handleReset(o);if(n&&void 0===u)throw new Error("Store handleReset() must return a value, did you forget a return statement");if(n&&!(0,_.isImmutableValue)(u))throw new Error("Store reset state must be an immutable value, did you forget to call toImmutable");t.setIn(["state",i],u)}),t.update("storeStates",function(t){return f(t,o)}),e.resetDirtyStores(t)})},e.evaluate=function O(t,e){var n=t.get("state");if((0,m.isKeyPath)(e))return i(n.getIn(e),t);if(!(0,y.isGetter)(e))throw new Error("evaluate must be passed a keyPath or Getter");if(a(t,e))return i(c(t,e),t);var r=(0,y.getDeps)(e).map(function(e){return O(t,e).result}),o=(0,y.getComputeFn)(e).apply(null,r);return i(o,s(t,e,o))},e.serialize=function(t){var e={};return t.get("stores").forEach(function(n,r){var i=t.getIn(["state",r]),o=n.serialize(i);void 0!==o&&(e[r]=o)}),e},e.resetDirtyStores=function(t){return t.set("dirtyStores",p["default"].Set())}},function(t,e){e.dispatchStart=function(t,e){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){return t&&t.__esModule?t:{"default":t}}function i(t){return(0,d.isArray)(t)&&(0,d.isFunction)(t[t.length-1])}function o(t){return t[t.length-1]}function u(t){return t.slice(0,t.length-1)}function a(t,e){e||(e=f["default"].Set());var n=f["default"].Set().withMutations(function(e){if(!i(t))throw new Error("getFlattenedDeps must be passed a Getter");u(t).forEach(function(t){if((0,p.isKeyPath)(t))e.add((0,l.List)(t));else{if(!i(t))throw new Error("Invalid getter, each dependency must be a KeyPath or Getter");e.union(a(t))}})});return e.union(n)}function s(t){if(!(0,p.isKeyPath)(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,h]}function c(t){if(t.hasOwnProperty("__storeDeps"))return t.__storeDeps;var e=a(t).map(function(t){return t.first()}).filter(function(t){return!!t});return Object.defineProperty(t,"__storeDeps",{enumerable:!1,configurable:!1,writable:!1,value:e}),e}Object.defineProperty(e,"__esModule",{value:!0});var l=n(3),f=r(l),d=n(4),p=n(11),h=function(t){return t};e["default"]={isGetter:i,getComputeFn:o,getFlattenedDeps:a,getStoreDeps:c,getDeps:u,fromKeyPath:s},t.exports=e["default"]},function(t,e,n){function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){return(0,s.isArray)(t)&&!(0,s.isFunction)(t[t.length-1])}function o(t,e){var n=a["default"].List(t),r=a["default"].List(e);return a["default"].is(n,r)}Object.defineProperty(e,"__esModule",{value:!0}),e.isKeyPath=i,e.isEqual=o;var u=n(3),a=r(u),s=n(4)},function(t,e,n){function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(3),o=r(i),u=o["default"].Record({dispatchId:0,state:o["default"].Map(),stores:o["default"].Map(),cache:o["default"].Map(),storeStates:o["default"].Map(),dirtyStores:o["default"].Set(),debug:!1}),a=o["default"].Record({any:o["default"].Set([]),stores:o["default"].Map({}),observersMap:o["default"].Map({}),nextId:1});e["default"]={ReactorState:u,ObserverState:a},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(124),u=r(o);e["default"]=(0,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(133),o=r(i);e.callApi=o["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){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(149),u=i(o),a=n(150),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||(0,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(175),l=r(c),f=n(174),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(82),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(5),o=r(i);e["default"]=(0,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"]=(0,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(176),u=i(o),a=n(58),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"]=(0,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(136),a=i(u),s=n(137),c=i(s),l=n(138),f=i(l),d=n(134),p=r(d),h=n(135),v=r(h),_=p;e.actions=_;var y=v;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;n4?"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(125),a=r(u);n(39),n(123),n(122),n(119),n(121),n(120),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=(0,a["default"])(t);if(e&&(0,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){return t&&t.__esModule?t:{"default":t}}function i(t,e){switch(t){case"alarm_control_panel":return e&&"disarmed"===e?"mdi:bell-outline":"mdi:bell";case"binary_sensor":return e&&"off"===e?"mdi:radiobox-blank":"mdi:checkbox-marked-circle";case"camera":return"mdi:video";case"configurator":return"mdi:settings";case"conversation":return"mdi:text-to-speech";case"device_tracker":return"mdi:account";case"group":return"mdi:google-circles-communities";case"homeassistant":return"mdi:home";case"light":return"mdi:lightbulb";case"lock":return e&&"unlocked"===e?"mdi:lock-open":"mdi:lock";case"media_player":var n="mdi:cast";return e&&"off"!==e&&"idle"!==e&&(n+="-connected"),n;case"notify":return"mdi:comment-alert";case"updater":return"mdi:cloud-upload";case"scene":return"mdi:google-pages";case"script":return"mdi:file-document";case"sensor":return"mdi:eye";case"simple_alarm":return"mdi:bell";case"sun":return"mdi:white-balance-sunny";case"switch":return"mdi:flash";case"thermostat":return"mdi:nest-thermostat";default:return console.warn("Unable to find icon for domain "+t+" ("+e+")"),u["default"]}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(40),u=r(o);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"]=(0,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(141),a=i(u),s=n(142),c=i(s),l=n(139),f=r(l),d=n(140),p=r(d),h=f;e.actions=h;var v=p;e.getters=v},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(153),u=i(o),a=n(154),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"]=(0,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(171),a=i(u),s=n(169),c=r(s),l=n(170),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(183),a=i(u),s=n(179),c=r(s),l=n(180),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"]=(0,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(185),a=i(u),s=n(186),c=i(s),l=n(184),f=r(l),d=n(61),p=r(d),h=f;e.actions=h;var v=p;e.getters=v},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],v=d[2];return new Date(Date.UTC(v,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 Rn)r=Rn[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),zn===!1&&(zn=!0,e.updateOffset(this),zn=!1)}function v(t){return t instanceof h||null!=t&&null!=t._isAMomentObject}function _(t){return 0>t?Math.ceil(t):Math.floor(t)}function y(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=_(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=S(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&m(i,n,!0)>=e-1)break;e--}o++}return null}function S(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}(),w(n)}catch(r){}return Hn[e]}function w(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),w(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=S(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 I(t){return"string"==typeof t?Yn[t]||Yn[t.toLowerCase()]:void 0}function j(t){var e,n,r={};for(n in t)u(t,n)&&(e=I(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=I(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 R(t,e){return t.isValid()?(e=z(e,t.localeData()),Un[e]=Un[e]||N(e),Un[e](t)):t.localeData().invalidDate()}function z(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){_r[t]||(tt(e),_r[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"),St(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 vt(t){var e=lt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")}function _t(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=_t(n,r,i,u,o),t._a[ar]=a.year,t._dayOfYear=a.dayOfYear}function St(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=z(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]=wt(t._locale,t._a[lr],t._meridiem),bt(t),Q(t)}function wt(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=j(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(It(t)));return e._nextDay&&(e.add(1,"d"),e._nextDay=void 0),e}function It(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)),v(e)?new h(Q(e)):(r(n)?Mt(t):n?St(t):i(e)?t._d=e:jt(t),t))}function jt(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 Rt(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 zt(t,n){var r,o;return n._isUTC?(r=n.clone(),o=(v(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=Rt(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(Rt(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=It(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=Ir.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=zt(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=zt(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=I("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=v(t)?t:Dt(t),+this>+t):(n=v(t)?+t:+Dt(t),n<+this.clone().startOf(e))}function ue(t,e){var n;return e=I("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=v(t)?t:Dt(t),+t>+this):(n=v(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=Re(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 vn(t,e){return ln(t,e,"weekdaysMin",7,"day")}function _n(){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(wn(a)+u),u=0,a=0),s.milliseconds=o%1e3,t=_(o/1e3),s.seconds=t%60,e=_(t/60),s.minutes=e%60,n=_(e/60),s.hours=n%24,u+=_(n/24),i=_(Sn(u)),a+=i,u-=bn(wn(i)),r=_(a/12),a%=12,s.days=u,s.months=a,s.years=r,this}function Sn(t){return 4800*t/146097}function wn(t){return 146097*t/4800}function Mn(t){var e,n,r=this._milliseconds;if(t=I(t),"month"===t||"year"===t)return e=this._days+r/864e5,n=this._months+Sn(e),"month"===t?n:n/12;switch(e=this._days+Math.round(wn(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 In(t){return t=I(t),this[t+"s"]()}function jn(t){return function(){return this._data[t]}}function Pn(){return _(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=_(r/60),e=_(t/60),r%=60,t%=60,n=_(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,Rn=e.momentProperties=[],zn=!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("_"),vr="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),_r={};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 Sr={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 wr=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=Rt(t)});var Tr=/([\+\-]|\d\d)/gi;e.updateOffset=function(){};var Er=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,Ir=/^(-)?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 jr=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 Rr=P("Minutes",!1);x("s",["ss",2],0,"second"),E("second","s"),Y("s",$n),Y("ss",$n,qn),U(["s","ss"],dr);var zr=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=jr,Gr.calendar=re,Gr.clone=ie,Gr.diff=ce,Gr.endOf=Oe,Gr.format=pe,Gr.from=he,Gr.fromNow=ve,Gr.to=_e,Gr.toNow=ye,Gr.get=A,Gr.invalidAt=Pe,Gr.isAfter=oe,Gr.isBefore=ue,Gr.isBetween=ae,Gr.isSame=se,Gr.isValid=Ie,Gr.lang=Dr,Gr.locale=me,Gr.localeData=ge,Gr.max=Mr,Gr.min=wr,Gr.parsingFlags=je,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=we,Gr.valueOf=Se,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=vt,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=Rr,Gr.second=Gr.seconds=zr,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=vr,Jr.monthsParse=J,Jr.week=ft,Jr._week=Sr,Jr.firstDayOfYear=pt,Jr.firstDayOfWeek=dt,Jr.weekdays=ze,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,w("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.",w),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=jn("milliseconds"),ui=jn("seconds"),ai=jn("minutes"),si=jn("hours"),ci=jn("days"),li=jn("months"),fi=jn("years"),di=Math.round,pi={s:45,m:45,h:22,d:26,M:11},hi=Math.abs,vi=xt.prototype;vi.abs=_n,vi.add=mn,vi.subtract=gn,vi.as=Mn,vi.asMilliseconds=Zr,vi.asSeconds=Xr,vi.asMinutes=Qr,vi.asHours=ti,vi.asDays=ei,vi.asWeeks=ni,vi.asMonths=ri,vi.asYears=ii,vi.valueOf=Tn,vi._bubble=On,vi.get=In,vi.milliseconds=oi,vi.seconds=ui,vi.minutes=ai,vi.hours=si,vi.days=ci,vi.weeks=Pn,vi.months=li,vi.years=fi,vi.humanize=kn,vi.toISOString=xn,vi.toString=xn,vi.toJSON=xn,vi.locale=me,vi.localeData=ge,vi.toIsoString=et("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",xn),vi.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=w,e.invalid=d,e.duration=Zt,e.isMoment=v,e.weekdays=pn,e.parseZone=Qe,e.localeData=T,e.isDuration=Lt,e.monthsShort=dn,e.weekdaysMin=vn,e.defineLocale=M,e.weekdaysShort=hn,e.normalizeUnits=I,e.relativeTimeThreshold=An;var _i=e;return _i})}).call(e,n(73)(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(1),o=r(i),u=n(22),a=r(u);e["default"]=new o["default"]({is:"domain-icon",properties:{domain:{type:String,value:""},state:{type:String,value:""}},computeIcon:function(t,e){return(0,a["default"])(t,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);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,n=this._checkToggle(this.stateObj);e&&!n?this._call_service(!0):!e&&n&&this._call_service(!1)},stateObjChanged:function(t){t&&this.updateToggle(t)},updateToggle:function(t){this.toggleChecked=this._checkToggle(t)},forceStateChange:function(){var t=this._checkToggle(this.stateObj);this.toggleChecked===t&&(this.toggleChecked=!this.toggleChecked),this.toggleChecked=t},_checkToggle:function(t){return t&&"off"!==t.state&&"unlocked"!==t.state},_call_service:function(t){var e=this,n=void 0,r=void 0;"lock"===this.stateObj.domain?(n="lock",r=t?"lock":"unlock"):(n="homeassistant",r=t?"turn_on":"turn_off"),i.serviceActions.callService(n,r,{entity_id:this.stateObj.entityId}).then(function(){return e.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:{header:{type:String},elevation:{type:Number,value:1,reflectToAttribute:!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(33),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?(0,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(18),n(92),n(91),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(10),e["default"]=new o["default"]({is:"state-card-display",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e["default"]="bookmark",t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){return(0,u["default"])(t).format("LT")}Object.defineProperty(e,"__esModule",{value:!0}), +e["default"]=i;var o=n(33),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,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(_,d),y||p||(d=_=void 0))}function c(){var t=e-(o()-v);0>=t||t>e?s(m,p):y=setTimeout(c,t)}function l(){s(O,y)}function f(){if(d=arguments,v=o(),_=this,m=O&&(y||!S),b===!1)var n=S&&!y;else{p||S||(g=v);var r=b-(v-g),i=0>=r||r>b;i?(p&&(p=clearTimeout(p)),g=v,h=t.apply(_,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(_,d)),!i||y||p||(d=_=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 S=!0;O=!1}else i(n)&&(S=!!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(47),o=n(129),u="Expected a function",a=Math.max;t.exports=r},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(132);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(47),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)||(0,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:v["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(144),a=i(u),s=n(145),c=i(s),l=n(146),f=i(l),d=n(147),p=i(d),h=n(148),v=i(h),_=n(143),y=r(_),m=n(48),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(172),a=n(192),s=i(a),c=n(194),l=i(c),f=n(196),d=i(f),p=n(15),h=r(p),v=n(24),_=r(v),y=n(8),m=r(y),g=n(49),b=r(g),O=n(152),S=r(O),w=n(25),M=r(w),T=n(157),E=r(T),I=n(52),j=r(I),P=n(55),D=r(P),C=n(27),A=r(C),k=n(13),x=r(k),L=n(28),N=r(L),R=n(30),z=r(R),H=n(189),Y=r(H),G=n(9),F=r(G),U=function B(){o(this,B);var t=(0,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:D.urlSync.startSync.bind(null,t)},stopUrlSync:{value:D.urlSync.stopSync.bind(null,t)}}),(0,l["default"])(this,t,{auth:h,config:_,entity:m,entityHistory:b,errorLog:S,event:M,logbook:E,moreInfo:j,navigation:D,notification:A,service:x,stream:N,sync:z,voice:Y,restApi:F})};e["default"]=U,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(65),i=r("length");t.exports=i},function(t,e,n){"use strict";function r(t){return null!=t&&o(i(t))}var i=n(66),o=n(70);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(67),o=n(68),u=n(71);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?(0,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(17),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(126),a=r(u);e["default"]=new o["default"]({is:"ha-state-icon",properties:{stateObj:{type:Object}},computeIcon:function(t){return(0,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(1),o=r(i),u=n(2),a=n(22),s=r(a),c=n(21),l=r(c);n(17),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(),(0,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"binary_sensor":case"script":return"on"===t.state?"blue":"grey";case"updater":return"blue";default:return""}},computeValue:function(t){switch(t.domain){case"binary_sensor":case"device_tracker":case"updater":case"sun":case"scene":case"script":case"alarm_control_panel":return;case"sensor":return t.state;default:return t.state}},computeIcon:function(t){switch(t.domain){case"alarm_control_panel":return"pending"===t.state?"mdi:clock-fast":"armed_away"===t.state?"mdi:nature":"armed_home"===t.state?"mdi:home-variant":(0,s["default"])(t.domain,t.state);case"binary_sensor":case"device_tracker":case"scene":case"updater":case"script":return(0,s["default"])(t.domain,t.state);case"sun":return"above_horizon"===t.state?(0,s["default"])(t.domain):"mdi:brightness-3";default:return}},computeImage:function(t){return t.attributes.entity_picture},computeLabel:function(t){switch(t.domain){case"scene":case"script":return t.domain;case"device_tracker":return"not_home"===t.state?"Away":t.state;case"alarm_control_panel":return"pending"===t.state?"pend":"armed_away"===t.state||"armed_home"===t.state?"armed":"disarm";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);n(80),e["default"]=new o["default"]({is:"state-badge",properties:{stateObj:{type:Object,observer:"updateIconColor"}},updateIconColor:function(t){"light"===t.domain&&"on"===t.state&&t.attributes.rgb_color&&t.attributes.rgb_color.reduce(function(t,e){return t+e},0)<730?this.$.icon.style.color="rgb("+t.attributes.rgb_color.join(",")+")":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"},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)},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(17),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(89),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(93),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 e in t&&t[e].groupEntity},computeStatesOfCard:function(t,e){return e in t&&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(34),n(77),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(34),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(72),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]],(0,u["default"])(1,t.getNumberOfColumns()),(0,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);e["default"]=new u["default"]({is:"ha-voice-command-dialog",behaviors:[s["default"]],properties:{dialogOpen:{type:Boolean,value:!1,observer:"dialogOpenChanged"},finalTranscript:{type:String,bindNuclear:i.voiceGetters.finalTranscript},interimTranscript:{type:String,bindNuclear:i.voiceGetters.extraInterimTranscript},isTransmitting:{type:Boolean,bindNuclear:i.voiceGetters.isTransmitting},isListening:{type:Boolean,bindNuclear:i.voiceGetters.isListening},showListenInterface:{type:Boolean,computed:"computeShowListenInterface(isListening, isTransmitting)", +observer:"showListenInterfaceChanged"},_boundOnBackdropTap:{type:Function,value:function(){return this._onBackdropTap.bind(this)}}},computeShowListenInterface:function(t,e){return t||e},dialogOpenChanged:function(t){t?this.$.dialog.backdropElement.addEventListener("click",this._boundOnBackdropTap):!t&&this.isListening&&i.voiceActions.stop()},showListenInterfaceChanged:function(t){!t&&this.dialogOpen?this.dialogOpen=!1:t&&(this.dialogOpen=!0)},_onBackdropTap:function(){this.$.dialog.backdropElement.removeEventListener("click",this._boundOnBackdropTap),this.isListening&&i.voiceActions.stop()}}),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),n(38),n(110);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(87),n(105),n(103),n(102),n(104),n(98),n(99),n(101),n(100),n(106),n(95),n(94),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")},isSelectedDevInfo:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devInfo")},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(){(0,u.startUrlSync)()},computeForceNarrow:function(t,e){return t||!e},detached:function(){(0,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:{errorMessage:{type:String,bindNuclear:u.authGetters.attemptErrorMessage},isInvalid:{type:Boolean,bindNuclear:u.authGetters.isInvalidAttempt},isValidating:{type:Boolean,observer:"isValidatingChanged",bindNuclear:u.authGetters.isValidating},loadingResources:{type:Boolean,value:!1},forceShowLoading:{type:Boolean,value:!1},showLoading:{type:Boolean,computed:"computeShowSpinner(forceShowLoading, isValidating)"}},listeners:{keydown:"passwordKeyDown","loginButton.click":"validatePassword"},observers:["validatingChanged(isValidating, isInvalid)"],computeShowSpinner:function(t,e){return t||e},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(),(0,l["default"])(this.$.passwordInput.value,this.$.rememberLogin.checked)},attached:function(){var t=document.getElementById("init");t.parentElement.removeChild(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);n(7),n(90),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(7),n(83),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),a=n(4),s=r(a);n(7),e["default"]=new u["default"]({is:"partial-dev-info",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},hassVersion:{type:String,bindNuclear:i.configGetters.serverVersion},polymerVersion:{type:String,value:u["default"].version},nuclearVersion:{type:String,value:"1.2.1"},errorLog:{type:String,value:""}},attached:function(){this.refreshErrorLog()},refreshErrorLog:function(t){var e=this;t&&t.preventDefault(),this.errorLog="Loading error log…",i.errorLogActions.fetchErrorLog().then(function(t){return e.errorLog=t||"No errors have been reported."})}}),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(7),n(78),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(7),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(7),n(86),n(18),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(79),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(7),n(88),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}]},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(){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()},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(18),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(127),a=r(u);n(111),n(112),n(116),n(109),n(117),n(115),n(113),n(114),n(108),n(118),n(107),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=(0,a["default"])(t);if(e&&(0,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","icon","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(19),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(20),s=r(a);n(84);var c=["brightness","rgb_color","color_temp"];e["default"]=new u["default"]({is:"more-info-light",properties:{stateObj:{type:Object,observer:"stateObjChanged"},brightnessSliderValue:{type:Number,value:0},ctSliderValue:{type:Number,value:0}},stateObjChanged:function(t){var e=this;t&&"on"===t.state&&(this.brightnessSliderValue=t.attributes.brightness,this.ctSliderValue=t.attributes.color_temp),this.async(function(){return e.fire("iron-resize")},500)},computeClassNames:function(t){return(0,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}))},ctSliderChanged:function(t){var e=parseInt(t.target.value,10);isNaN(e)||i.serviceActions.callService("light","turn_on",{entity_id:this.stateObj.entityId,color_temp: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(20),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(0,s["default"])(t,c)},computeIsOff:function(t){return"off"===t.state},computeMuteVolumeIcon:function(t){return t?"mdi:volume-off":"mdi:volume-high"},computePlaybackControlIcon:function(){return this.isPlaying?this.supportsPause?"mdi:pause":"mdi:stop":"mdi:play"},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(0,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(20),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(0,s["default"])(t,c)},targetTemperatureSliderChanged:function(t){i.serviceActions.callService("thermostat","set_temperature",{entity_id:this.stateObj.entityId,temperature:t.target.value})},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);e["default"]=new o["default"]({is:"more-info-updater",properties:{}}),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(10),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(10);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(10),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(10),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(10),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:(0,u["default"])(t.entityId)?"toggle":"display"}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(21),u=r(o),a=["thermostat","configurator","scene","media_player"];t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){if(!t)return u["default"];if(t.attributes.icon)return t.attributes.icon;var e=t.attributes.unit_of_measurement;return!e||"sensor"!==t.domain||e!==c.util.temperatureUnits.UNIT_TEMP_C&&e!==c.util.temperatureUnits.UNIT_TEMP_F?(0,s["default"])(t.domain,t.state):"mdi:thermometer"}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(40),u=r(o),a=n(22),s=r(a),c=n(2);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,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(_)}}function d(){return function(){q(_)}}function p(){var t=0,e=new tt(_),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=_,function(){t.port2.postMessage(0)}}function v(){return function(){setTimeout(_,1)}}function _(){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(201);return q=t.runOnLoop||t.runOnContext,d()}catch(e){return v()}}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 S(t,e,n,r){try{t.call(e,n,r); +}catch(i){return i}}function w(t,e,n){Z(function(t){var r=!1,i=S(n,e,function(n){r||(r=!0,e!==n?E(t,n):j(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?j(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?j(t,e):a(n)?w(t,e,n):j(t,e)}}function E(t,e){t===e?P(t,g()):u(e)?T(t,e):j(t,e)}function I(t){t._onerror&&t._onerror(t._result),C(t)}function j(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(I,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=vt)}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(_):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():v();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&&j(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=R,ft=z,dt=H,pt=Y,ht=0,vt=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 _t=B,yt={Promise:vt,polyfill:_t};n(200).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),_t()}).call(void 0)}).call(e,n(198),function(){return this}(),n(197)(t))},function(t,e,n){"use strict";var r=n(44),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(44),i=n(130),o=n(45),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(46),o=n(45),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(128),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(){var e=void 0;try{e="application/json"===r.getResponseHeader("content-type")?JSON.parse(r.responseText):r.responseText}catch(i){e=r.responseText}r.status>199&&r.status<300?t(e):n(e)},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(c.getters.isSupported):r,o=n.rememberAuth,u=void 0===o?!1:o,s=n.host,d=void 0===s?"":s;t.dispatch(a["default"].VALIDATING_AUTH_TOKEN,{authToken:e,host:d}),l.actions.fetchAll(t).then(function(){t.dispatch(a["default"].VALID_AUTH_TOKEN,{authToken:e,host:d,rememberAuth:u}),i?c.actions.start(t,{syncOnInitialConnect:!1}):l.actions.start(t,{skipInitialSync:!0})},function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=e.message,r=void 0===n?f:n;t.dispatch(a["default"].INVALID_AUTH_TOKEN,{errorMessage:r})})}function o(t){(0,s.callApi)(t,"POST","log_out"),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(6),c=n(28),l=n(30),f="Unexpected result from API"},function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=["authAttempt","isValidating"];e.isValidating=n;var r=["authAttempt","isInvalid"];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(0,d.toImmutable)({authToken:n,host:r,isValidating:!0,isInvalid:!1,errorMessage:""})}function s(t,e){return i(e),_.getInitialState()}function c(t,e){var n=e.errorMessage;return t.withMutations(function(t){return t.set("isValidating",!1).set("isInvalid",!0).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 v.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)||(0,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(159),a=i(u),s=n(160),c=i(s),l=n(161),f=i(l),d=n(162),p=i(d),h=n(155),v=r(h),_=n(156),y=r(_),m=v;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 33124030f6d..300f3582a61 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 33124030f6d119ad3a58cb520062f2aa58022c6d +Subproject commit 300f3582a61b5781431778cd2f44bef05d49baf3 From 90eab17ea6acb26bb2824584c2d64b27343922de Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 23:23:27 -0800 Subject: [PATCH 110/125] Fix MQTT light bugs --- homeassistant/components/light/mqtt.py | 8 ++++---- tests/components/light/test_mqtt.py | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 97624ec72f9..30f968d758f 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -18,7 +18,6 @@ DEFAULT_NAME = "MQTT Light" DEFAULT_QOS = 0 DEFAULT_PAYLOAD_ON = "on" DEFAULT_PAYLOAD_OFF = "off" -DEFAULT_RGB_PATTERN = "%d,%d,%d" DEFAULT_OPTIMISTIC = False DEPENDENCIES = ['mqtt'] @@ -137,9 +136,8 @@ class MqttLight(Light): if ATTR_RGB_COLOR in kwargs and \ self._topic["rgb_command_topic"] is not None: - rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) mqtt.publish(self._hass, self._topic["rgb_command_topic"], - rgb, self._qos) + "{},{},{}".format(*kwargs[ATTR_RGB_COLOR]), self._qos) if self._optimistic_rgb: self._rgb = kwargs[ATTR_RGB_COLOR] @@ -147,8 +145,10 @@ class MqttLight(Light): if ATTR_BRIGHTNESS in kwargs and \ self._topic["brightness_command_topic"] is not None: + mqtt.publish(self._hass, self._topic["brightness_command_topic"], - self._brightness, self._qos) + kwargs[ATTR_BRIGHTNESS], self._qos) + if self._optimistic_brightness: self._brightness = kwargs[ATTR_BRIGHTNESS] should_update = True diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index bc0195feff1..8172a6c7c63 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -149,9 +149,7 @@ class TestLightMQTT(unittest.TestCase): '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', @@ -177,3 +175,26 @@ class TestLightMQTT(unittest.TestCase): self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) + + light.turn_on(self.hass, 'light.test', rgb_color=[75, 75, 75], + brightness=50) + self.hass.pool.block_till_done() + + # Calls are threaded so we need to reorder them + bright_call, rgb_call, state_call = \ + sorted((call[1] for call in self.mock_publish.mock_calls[-3:]), + key=lambda call: call[0]) + + self.assertEqual(('test_light_rgb/set', 'on', 2, False), + state_call) + + self.assertEqual(('test_light_rgb/rgb/set', '75,75,75', 2, False), + rgb_call) + + self.assertEqual(('test_light_rgb/brightness/set', 50, 2, False), + bright_call) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual([75, 75, 75], state.attributes['rgb_color']) + self.assertEqual(50, state.attributes['brightness']) From b53993e8c07611befb0b1001eb9dc2f1d2df1393 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 29 Nov 2015 23:55:37 -0800 Subject: [PATCH 111/125] Update Travis to test on Python 3.5 too --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index da3516554ef..f12d318b5d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,10 @@ sudo: false language: python cache: directories: - - $HOME/virtualenv/python3.4.2/ + - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/ python: - - "3.4" + - 3.4.2 + - 3.5.0 install: - script/bootstrap_server script: From ac598471209a96f980460036df9ca625eee3feed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Mon, 30 Nov 2015 09:14:32 +0100 Subject: [PATCH 112/125] Update heat_control.py --- .../components/thermostat/heat_control.py | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/thermostat/heat_control.py b/homeassistant/components/thermostat/heat_control.py index 7080528fd17..66992e223a0 100644 --- a/homeassistant/components/thermostat/heat_control.py +++ b/homeassistant/components/thermostat/heat_control.py @@ -24,6 +24,9 @@ CONF_NAME = 'name' DEFAULT_NAME = 'Heat Control' CONF_HEATER = 'heater' CONF_SENSOR = 'target_sensor' +CONF_MIN_TEMP = 'min_temp' +CONF_MAX_TEMP = 'max_temp' +CONF_TARGET_TEMP = 'target_temp' _LOGGER = logging.getLogger(__name__) @@ -34,27 +37,32 @@ def setup_platform(hass, config, add_devices, discovery_info=None): name = config.get(CONF_NAME, DEFAULT_NAME) heater_entity_id = config.get(CONF_HEATER) sensor_entity_id = config.get(CONF_SENSOR) + min_temp = util.convert(config.get(CONF_MIN_TEMP), float, None) + max_temp = util.convert(config.get(CONF_MAX_TEMP), float, None) + target_temp = util.convert(config.get(CONF_TARGET_TEMP), float, None) if None in (heater_entity_id, sensor_entity_id): _LOGGER.error('Missing required key %s or %s', CONF_HEATER, CONF_SENSOR) return False - add_devices([HeatControl(hass, name, heater_entity_id, sensor_entity_id)]) + add_devices([HeatControl(hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, target_temp)]) # pylint: disable=too-many-instance-attributes class HeatControl(ThermostatDevice): """ Represents a HeatControl device. """ - def __init__(self, hass, name, heater_entity_id, sensor_entity_id): + def __init__(self, hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, target_temp): self.hass = hass self._name = name self.heater_entity_id = heater_entity_id self._active = False self._cur_temp = None - self._target_temp = None + self._min_temp = min_temp + self._max_temp = max_temp + self._target_temp = target_temp self._unit = None track_state_change(hass, sensor_entity_id, self._sensor_changed) @@ -97,6 +105,22 @@ class HeatControl(ThermostatDevice): self._control_heating() self.update_ha_state() + @property + def min_temp(self): + """ Return minimum temperature. """ + if self._min_temp: + return self._min_temp + else: + return ThermostatDevice.min_temp.fget(self) + + @property + def max_temp(self): + """ Return maximum temperature. """ + if self._min_temp: + return self._max_temp + else: + return ThermostatDevice.max_temp.fget(self) + def _sensor_changed(self, entity_id, old_state, new_state): """ Called when temperature changes. """ if new_state is None: From 2732b2030545a857e3e419051b8832c9c2b3e789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Mon, 30 Nov 2015 09:22:04 +0100 Subject: [PATCH 113/125] style fix in heatcontrol --- homeassistant/components/thermostat/heat_control.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/thermostat/heat_control.py b/homeassistant/components/thermostat/heat_control.py index 66992e223a0..93326be384a 100644 --- a/homeassistant/components/thermostat/heat_control.py +++ b/homeassistant/components/thermostat/heat_control.py @@ -46,14 +46,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None): CONF_SENSOR) return False - add_devices([HeatControl(hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, target_temp)]) + add_devices([HeatControl(hass, name, heater_entity_id, sensor_entity_id, + min_temp, max_temp, target_temp)]) # pylint: disable=too-many-instance-attributes class HeatControl(ThermostatDevice): """ Represents a HeatControl device. """ - def __init__(self, hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, target_temp): + def __init__(self, hass, name, heater_entity_id, sensor_entity_id, + min_temp, max_temp, target_temp): self.hass = hass self._name = name self.heater_entity_id = heater_entity_id @@ -111,6 +113,7 @@ class HeatControl(ThermostatDevice): if self._min_temp: return self._min_temp else: + # pylint: disable=no-member return ThermostatDevice.min_temp.fget(self) @property @@ -119,6 +122,7 @@ class HeatControl(ThermostatDevice): if self._min_temp: return self._max_temp else: + # pylint: disable=no-member return ThermostatDevice.max_temp.fget(self) def _sensor_changed(self, entity_id, old_state, new_state): From 48a424e86fb372732e0977a1eb777c7a344c202d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 30 Nov 2015 00:48:39 -0800 Subject: [PATCH 114/125] Frontend upgrade --- homeassistant/components/frontend/version.py | 2 +- homeassistant/components/frontend/www_static/frontend.html | 2 +- .../components/frontend/www_static/home-assistant-polymer | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index a3c09fc4149..789efa8988c 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 = "a07269fd3559611af16a4204b7645a64" +VERSION = "ece94598f1575599c5aefa7322b1423b" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 36921057fca..32df087296c 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -6052,7 +6052,7 @@ case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return font-weight: 300; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - }