From 6c106a87f16d506dc12efb1bb4ec705b760e1f18 Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 15 Oct 2015 15:02:09 +0200 Subject: [PATCH 001/165] had to change to let this work on windows. --- script/bootstrap_server | 4 ++-- script/release | 2 +- script/server | 2 +- script/setup | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/script/bootstrap_server b/script/bootstrap_server index 8d71e01fa78..91a6a36027c 100755 --- a/script/bootstrap_server +++ b/script/bootstrap_server @@ -4,7 +4,7 @@ echo "Update the submodule to latest version..." git submodule update echo "Installing dependencies..." -python3 -m pip install --upgrade -r requirements_all.txt +python -m pip install --upgrade -r requirements_all.txt echo "Installing development dependencies.." -python3 -m pip install --upgrade flake8 pylint coveralls pytest pytest-cov +python -m pip install --upgrade flake8 pylint coveralls pytest pytest-cov diff --git a/script/release b/script/release index 40d906b17bf..31d298ddf8b 100755 --- a/script/release +++ b/script/release @@ -18,4 +18,4 @@ then exit 1 fi -python3 setup.py sdist bdist_wheel upload +python setup.py sdist bdist_wheel upload diff --git a/script/server b/script/server index 0904bfd728e..e0949b2d098 100755 --- a/script/server +++ b/script/server @@ -5,4 +5,4 @@ cd "$(dirname "$0")/.." -python3 -m homeassistant -c config +python -m homeassistant -c config diff --git a/script/setup b/script/setup index 6d3a774dd54..614cadd9b98 100755 --- a/script/setup +++ b/script/setup @@ -2,4 +2,4 @@ cd "$(dirname "$0")/.." git submodule init script/bootstrap -python3 setup.py develop +python setup.py develop From 076b3db5e81ebf796aff6f43daadb86f68818f7e Mon Sep 17 00:00:00 2001 From: sander Date: Wed, 21 Oct 2015 19:00:15 +0200 Subject: [PATCH 002/165] first try --- .../thermostat/honeywell_round_connected.py | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 homeassistant/components/thermostat/honeywell_round_connected.py diff --git a/homeassistant/components/thermostat/honeywell_round_connected.py b/homeassistant/components/thermostat/honeywell_round_connected.py new file mode 100644 index 00000000000..c26efd51624 --- /dev/null +++ b/homeassistant/components/thermostat/honeywell_round_connected.py @@ -0,0 +1,175 @@ +__author__ = 'sander' + +""" +homeassistant.components.thermostat.honeywell_round_connected +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Adds support for Honeywell Round Connected thermostats. +""" +import socket +import logging + +from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, + STATE_IDLE, STATE_HEAT) +from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS) + +REQUIREMENTS = ['evohomeclient'] + +from . import ATTR_CURRENT_TEMPERATURE,ATTR_TEMPERATURE + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the nest thermostat. """ + logger = logging.getLogger(__name__) + + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + if username is None or password is None: + logger.error("Missing required configuration items %s or %s", + CONF_USERNAME, CONF_PASSWORD) + return + + try: + from evohomeclient import EvohomeClient + except ImportError: + logger.exception( + "Error while importing dependency nest. " + "Did you maybe not install the python-nest dependency?") + + return + + evo_api = EvohomeClient(username, password) + try: + add_devices([ + RoundThermostat(evo_api) + ]) + except socket.error: + logger.error( + "Connection error logging into the nest web service" + ) + + +class RoundThermostat(ThermostatDevice): + """ Represents a Nest thermostat. """ + + def __init__(self, device): + #self.structure = structure + self.device = device + + + @property + def name(self): + """ Returns the name of the nest, if any. """ + return 'round' + + @property + def unit_of_measurement(self): + """ Unit of measurement this thermostat expresses itself in. """ + return TEMP_CELCIUS + + @property + def device_state_attributes(self): + """ Returns device specific state attributes. """ + # Move these to Thermostat Device and make them global + data = self.device.temperatures(force_refresh=True)[0] + return { + ATTR_CURRENT_TEMPERATURE: data['temp'], + ATTR_TEMPERATURE: data['setpoint'] + } + + + + # @property + # def current_temperature(self): + # """ Returns the current temperature. """ + # return round(self.device.temperature, 1) + + # @property + # def operation(self): + # """ Returns current operation ie. heat, cool, idle """ + # if self.device.hvac_ac_state is True: + # return STATE_COOL + # elif self.device.hvac_heater_state is True: + # return STATE_HEAT + # else: + # return STATE_IDLE + + # @property + # def target_temperature(self): + # """ Returns the temperature we try to reach. """ + # target = self.device.target + # + # if self.device.mode == 'range': + # low, high = target + # if self.operation == STATE_COOL: + # temp = high + # elif self.operation == STATE_HEAT: + # temp = low + # else: + # range_average = (low + high)/2 + # if self.current_temperature < range_average: + # temp = low + # elif self.current_temperature >= range_average: + # temp = high + # else: + # temp = target + # + # return round(temp, 1) + + # @property + # def target_temperature_low(self): + # """ Returns the lower bound temperature we try to reach. """ + # if self.device.mode == 'range': + # return round(self.device.target[0], 1) + # return round(self.target_temperature, 1) + # + # @property + # def target_temperature_high(self): + # """ Returns the upper bound temperature we try to reach. """ + # if self.device.mode == 'range': + # return round(self.device.target[1], 1) + # return round(self.target_temperature, 1) + # + # @property + # def is_away_mode_on(self): + # """ Returns if away mode is on. """ + # return self.structure.away + + # def set_temperature(self, temperature): + # """ Set new target temperature """ + # if self.device.mode == 'range': + # if self.target_temperature == self.target_temperature_low: + # temperature = (temperature, self.target_temperature_high) + # elif self.target_temperature == self.target_temperature_high: + # temperature = (self.target_temperature_low, temperature) + # self.device.target = temperature + # + # def turn_away_mode_on(self): + # """ Turns away on. """ + # self.structure.away = True + # + # def turn_away_mode_off(self): + # """ Turns away off. """ + # self.structure.away = False + # + # @property + # def min_temp(self): + # """ Identifies min_temp in Nest API or defaults if not available. """ + # temp = self.device.away_temperature.low + # if temp is None: + # return super().min_temp + # else: + # return temp + + # @property + # def max_temp(self): + # """ Identifies mxn_temp in Nest API or defaults if not available. """ + # temp = self.device.away_temperature.high + # if temp is None: + # return super().max_temp + # else: + # return temp + + def update(self): + """ Python-nest has its own mechanism for staying up to date. """ + pass From f376061e2337a67029f050358051708dc0c5d37d Mon Sep 17 00:00:00 2001 From: sander Date: Wed, 21 Oct 2015 19:00:23 +0200 Subject: [PATCH 003/165] Revert "had to change to let this work on windows." This reverts commit 6c106a87f16d506dc12efb1bb4ec705b760e1f18. --- script/bootstrap_server | 4 ++-- script/release | 2 +- script/server | 2 +- script/setup | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/script/bootstrap_server b/script/bootstrap_server index 91a6a36027c..8d71e01fa78 100755 --- a/script/bootstrap_server +++ b/script/bootstrap_server @@ -4,7 +4,7 @@ echo "Update the submodule to latest version..." git submodule update echo "Installing dependencies..." -python -m pip install --upgrade -r requirements_all.txt +python3 -m pip install --upgrade -r requirements_all.txt echo "Installing development dependencies.." -python -m pip install --upgrade flake8 pylint coveralls pytest pytest-cov +python3 -m pip install --upgrade flake8 pylint coveralls pytest pytest-cov diff --git a/script/release b/script/release index 31d298ddf8b..40d906b17bf 100755 --- a/script/release +++ b/script/release @@ -18,4 +18,4 @@ then exit 1 fi -python setup.py sdist bdist_wheel upload +python3 setup.py sdist bdist_wheel upload diff --git a/script/server b/script/server index e0949b2d098..0904bfd728e 100755 --- a/script/server +++ b/script/server @@ -5,4 +5,4 @@ cd "$(dirname "$0")/.." -python -m homeassistant -c config +python3 -m homeassistant -c config diff --git a/script/setup b/script/setup index 614cadd9b98..6d3a774dd54 100755 --- a/script/setup +++ b/script/setup @@ -2,4 +2,4 @@ cd "$(dirname "$0")/.." git submodule init script/bootstrap -python setup.py develop +python3 setup.py develop From 863955e1bdda5fb8c142080632d37870f9d2fa03 Mon Sep 17 00:00:00 2001 From: sander Date: Wed, 21 Oct 2015 21:48:21 +0200 Subject: [PATCH 004/165] got the basics working --- .../thermostat/honeywell_round_connected.py | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/thermostat/honeywell_round_connected.py b/homeassistant/components/thermostat/honeywell_round_connected.py index c26efd51624..15f27e0d9aa 100644 --- a/homeassistant/components/thermostat/honeywell_round_connected.py +++ b/homeassistant/components/thermostat/honeywell_round_connected.py @@ -14,7 +14,7 @@ from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS) REQUIREMENTS = ['evohomeclient'] -from . import ATTR_CURRENT_TEMPERATURE,ATTR_TEMPERATURE +# from . import ATTR_CURRENT_TEMPERATURE,ATTR_TEMPERATURE # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return try: - from evohomeclient import EvohomeClient + from evohomeclient2 import EvohomeClient except ImportError: logger.exception( "Error while importing dependency nest. " @@ -55,6 +55,9 @@ class RoundThermostat(ThermostatDevice): def __init__(self, device): #self.structure = structure self.device = device + self._current_temperature=None + self._target_temperature=None + self.update() @property @@ -71,18 +74,20 @@ class RoundThermostat(ThermostatDevice): def device_state_attributes(self): """ Returns device specific state attributes. """ # Move these to Thermostat Device and make them global - data = self.device.temperatures(force_refresh=True)[0] - return { - ATTR_CURRENT_TEMPERATURE: data['temp'], - ATTR_TEMPERATURE: data['setpoint'] - } + return {} - # @property - # def current_temperature(self): - # """ Returns the current temperature. """ - # return round(self.device.temperature, 1) + + @property + def current_temperature(self): + """ Returns the current temperature. """ + return self._current_temperature + #return round(self.device.temperature, 1) + + @current_temperature.setter + def current_temparature(self,value): + self._current_temperature=value # @property # def operation(self): @@ -94,10 +99,16 @@ class RoundThermostat(ThermostatDevice): # else: # return STATE_IDLE - # @property - # def target_temperature(self): - # """ Returns the temperature we try to reach. """ - # target = self.device.target + @property + def target_temperature(self): + """ Returns the temperature we try to reach. """ + return self._target_temperature + + @target_temperature.setter + def target_temperature(self,value): + self._target_temperature=value + + # target = self.device.target # # if self.device.mode == 'range': # low, high = target @@ -170,6 +181,12 @@ class RoundThermostat(ThermostatDevice): # else: # return temp + @property + def should_poll(self): + """ No polling needed for a demo thermostat. """ + return True + def update(self): - """ Python-nest has its own mechanism for staying up to date. """ - pass + for dev in self.device.temperatures(): + self._current_temperature=dev['temp'] + self._target_temperature=dev['setpoint'] From 469d0619ba0bc777f7ace316aa7a7d1f3bfb9aa5 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2015 21:48:01 +0100 Subject: [PATCH 005/165] mqtt light component --- homeassistant/components/light/mqtt.py | 152 +++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 homeassistant/components/light/mqtt.py diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py new file mode 100644 index 00000000000..2fc9fef7d50 --- /dev/null +++ b/homeassistant/components/light/mqtt.py @@ -0,0 +1,152 @@ +""" +homeassistant.components.light.mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a MQTT light. +""" +import logging +import homeassistant.components.mqtt as mqtt +from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_XY_COLOR) + +import random + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "MQTT Light" +DEFAULT_QOS = 0 +DEFAULT_PAYLOAD_ON = "on" +DEFAULT_PAYLOAD_OFF = "off" +DEFAULT_RGB = [ 255, 255, 255 ] +DEFAULT_RGB_PATTERN = "%d,%d,%d" +DEFAULT_BRIGHTNESS = 120 + +DEFAULT_STATE_TOPIC = "homeassistant/light/state" +DEFAULT_COMMAND_TOPIC = "homeassistant/light/switch" + +DEFAULT_STATE_BRIGHTNESS = "homeassistant/light/brightness/state" +DEFAULT_COMMAND_BRIGHTNESS = "homeassistant/light/brightness/set" + +DEFAULT_STATE_RGB = "homeassistant/light/rgb/state" +DEFAULT_COMMAND_RGB = "homeassistant/light/rgb/set" + +DEPENDENCIES = ['mqtt'] + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Add MQTT Light. """ + + if config.get('command_topic') is None: + _LOGGER.error("Missing required variable: command_topic") + return False + + add_devices_callback([MqttLight( + hass, + config.get('name', DEFAULT_NAME), + config.get('state_topic', DEFAULT_STATE_TOPIC), + config.get('command_topic', DEFAULT_COMMAND_TOPIC), + config.get('brightness_state_topic', DEFAULT_STATE_BRIGHTNESS), + config.get('brightness_command_topic', DEFAULT_COMMAND_BRIGHTNESS), + config.get('rgb_state_topic', DEFAULT_STATE_RGB), + config.get('rgb_command_topic', DEFAULT_COMMAND_RGB), + config.get('rgb', DEFAULT_RGB ), + config.get('qos', DEFAULT_QOS), + config.get('payload_on', DEFAULT_PAYLOAD_ON), + config.get('payload_off', DEFAULT_PAYLOAD_OFF), + config.get('brightness', DEFAULT_BRIGHTNESS))]) + +class MqttLight(Light): + """ Provides a demo switch. """ + def __init__(self, hass, name, state_topic, command_topic, brightness_state_topic, brightness_command_topic, rgb_state_topic, rgb_command_topic, rgb, qos, payload_on, payload_off, brightness): + self._name = name + self._hass = hass + self._state = False + self._command_topic = command_topic + self._state_topic = state_topic + self._brightness_state_topic = brightness_state_topic + self._brightness_command_topic = brightness_command_topic + self._rgb_state_topic = rgb_state_topic + self._rgb_command_topic = rgb_command_topic + self._qos = qos + self._payload_on = payload_on + self._payload_off = payload_off + self._rgb = rgb + self._brightness = brightness + self._xy = [[ 0.5, 0.5 ]] + + 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() + + def brightness_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._brightness = int(payload) + self.update_ha_state() + + def rgb_received(topic, payload, qos): + """ A new MQTT message has been received. """ + rgb = payload.split( "," ) + self._rgb = list(map(int, rgb)) + self.update_ha_state() + + # subscribe the state_topic + mqtt.subscribe(self._hass, self._state_topic, message_received, self._qos) + mqtt.subscribe(self._hass, self._brightness_state_topic, brightness_received, self._qos) + mqtt.subscribe(self._hass, self._rgb_state_topic, rgb_received, self._qos) + + @property + def should_poll(self): + """ No polling needed for a demo light. """ + return False + + @property + def name(self): + """ Returns the name of the device if any. """ + return self._name + + @property + def brightness(self): + """ Brightness of this light between 0..255. """ + return self._brightness + + @property + def rgb_color(self): + """ RGB color value. """ + return self._rgb + + @property + def color_xy(self): + """ RGB color value. """ + return self._xy + + @property + def is_on(self): + """ True if device is on. """ + return self._state + + def turn_on(self, **kwargs): + + if ATTR_RGB_COLOR in kwargs: + self._rgb = kwargs[ATTR_RGB_COLOR] + rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) + mqtt.publish(self._hass, self._rgb_command_topic, rgb, self._qos) + + if ATTR_BRIGHTNESS in kwargs: + self._brightness = kwargs[ATTR_BRIGHTNESS] + mqtt.publish(self._hass, self._brightness_command_topic, self._brightness, self._qos) + + if not self._state: + """ Turn the device on. """ + self._state = True + mqtt.publish(self._hass, self._command_topic, self._payload_on, self._qos) + self.update_ha_state() + + def turn_off(self, **kwargs): + """ Turn the device off. """ + self._state = False + mqtt.publish(self._hass, self._command_topic, self._payload_off,self._qos) + self.update_ha_state() + From 7cfce94dfbf095f4123e7f85cd119c3ebbe221b3 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2015 22:58:07 +0100 Subject: [PATCH 006/165] pylint rework for light/mqtt --- homeassistant/components/light/mqtt.py | 75 +++++++++++++++----------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 2fc9fef7d50..4df0347d506 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -5,9 +5,8 @@ Allows to configure a MQTT light. """ import logging import homeassistant.components.mqtt as mqtt -from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_XY_COLOR) - -import random +from homeassistant.components.light import (Light, + ATTR_BRIGHTNESS, ATTR_RGB_COLOR) _LOGGER = logging.getLogger(__name__) @@ -15,7 +14,7 @@ DEFAULT_NAME = "MQTT Light" DEFAULT_QOS = 0 DEFAULT_PAYLOAD_ON = "on" DEFAULT_PAYLOAD_OFF = "off" -DEFAULT_RGB = [ 255, 255, 255 ] +DEFAULT_RGB = [255, 255, 255] DEFAULT_RGB_PATTERN = "%d,%d,%d" DEFAULT_BRIGHTNESS = 120 @@ -47,7 +46,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('brightness_command_topic', DEFAULT_COMMAND_BRIGHTNESS), config.get('rgb_state_topic', DEFAULT_STATE_RGB), config.get('rgb_command_topic', DEFAULT_COMMAND_RGB), - config.get('rgb', DEFAULT_RGB ), + config.get('rgb', DEFAULT_RGB), config.get('qos', DEFAULT_QOS), config.get('payload_on', DEFAULT_PAYLOAD_ON), config.get('payload_off', DEFAULT_PAYLOAD_OFF), @@ -55,47 +54,61 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class MqttLight(Light): """ Provides a demo switch. """ - def __init__(self, hass, name, state_topic, command_topic, brightness_state_topic, brightness_command_topic, rgb_state_topic, rgb_command_topic, rgb, qos, payload_on, payload_off, brightness): - self._name = name + + # pylint: disable=too-many-instance-attributes,too-many-arguments,too-many-locals,bad-builtin + # Eight is reasonable in this case. + + def __init__(self, hass, name, + state_topic, command_topic, + brightness_state_topic, brightness_command_topic, + rgb_state_topic, rgb_command_topic, + rgb, qos, + payload_on, payload_off, + brightness): + self._hass = hass - self._state = False - self._command_topic = command_topic + self._name = name self._state_topic = state_topic + self._command_topic = command_topic self._brightness_state_topic = brightness_state_topic self._brightness_command_topic = brightness_command_topic self._rgb_state_topic = rgb_state_topic self._rgb_command_topic = rgb_command_topic + self._rgb = rgb self._qos = qos self._payload_on = payload_on self._payload_off = payload_off - self._rgb = rgb self._brightness = brightness - self._xy = [[ 0.5, 0.5 ]] - + self._xy = [[0.5, 0.5]] + self._state = False + 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() + self._hass.update_ha_state() elif payload == self._payload_off: self._state = False - self.update_ha_state() - + self._hass.update_ha_state() + def brightness_received(topic, payload, qos): """ A new MQTT message has been received. """ self._brightness = int(payload) - self.update_ha_state() - + self._hass.update_ha_state() + def rgb_received(topic, payload, qos): """ A new MQTT message has been received. """ - rgb = payload.split( "," ) + rgb = payload.split(",") self._rgb = list(map(int, rgb)) - self.update_ha_state() + self._hass.update_ha_state() # subscribe the state_topic - mqtt.subscribe(self._hass, self._state_topic, message_received, self._qos) - mqtt.subscribe(self._hass, self._brightness_state_topic, brightness_received, self._qos) - mqtt.subscribe(self._hass, self._rgb_state_topic, rgb_received, self._qos) + mqtt.subscribe(self._hass, self._state_topic, + message_received, self._qos) + mqtt.subscribe(self._hass, self._brightness_state_topic, + brightness_received, self._qos) + mqtt.subscribe(self._hass, self._rgb_state_topic, + rgb_received, self._qos) @property def should_poll(self): @@ -116,7 +129,7 @@ class MqttLight(Light): def rgb_color(self): """ RGB color value. """ return self._rgb - + @property def color_xy(self): """ RGB color value. """ @@ -128,6 +141,7 @@ class MqttLight(Light): return self._state def turn_on(self, **kwargs): + """ Turn the device on. """ if ATTR_RGB_COLOR in kwargs: self._rgb = kwargs[ATTR_RGB_COLOR] @@ -136,17 +150,18 @@ class MqttLight(Light): if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] - mqtt.publish(self._hass, self._brightness_command_topic, self._brightness, self._qos) + mqtt.publish(self._hass, self._brightness_command_topic, + self._brightness, self._qos) if not self._state: - """ Turn the device on. """ self._state = True - mqtt.publish(self._hass, self._command_topic, self._payload_on, self._qos) - self.update_ha_state() + mqtt.publish(self._hass, self._command_topic, + self._payload_on, self._qos) + self._hass.update_ha_state() def turn_off(self, **kwargs): """ Turn the device off. """ self._state = False - mqtt.publish(self._hass, self._command_topic, self._payload_off,self._qos) - self.update_ha_state() - + mqtt.publish(self._hass, self._command_topic, + self._payload_off, self._qos) + self._hass.update_ha_state() From 538f8545f7954be296e001f9698312b809861342 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2015 23:04:43 +0100 Subject: [PATCH 007/165] fix a bug after the pylint rework --- homeassistant/components/light/mqtt.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 4df0347d506..290ea6813ee 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -86,21 +86,21 @@ class MqttLight(Light): """ A new MQTT message has been received. """ if payload == self._payload_on: self._state = True - self._hass.update_ha_state() + self.update_ha_state() elif payload == self._payload_off: self._state = False - self._hass.update_ha_state() + self.update_ha_state() def brightness_received(topic, payload, qos): """ A new MQTT message has been received. """ self._brightness = int(payload) - self._hass.update_ha_state() + self.update_ha_state() def rgb_received(topic, payload, qos): """ A new MQTT message has been received. """ rgb = payload.split(",") self._rgb = list(map(int, rgb)) - self._hass.update_ha_state() + self.update_ha_state() # subscribe the state_topic mqtt.subscribe(self._hass, self._state_topic, @@ -157,11 +157,11 @@ class MqttLight(Light): self._state = True mqtt.publish(self._hass, self._command_topic, self._payload_on, self._qos) - self._hass.update_ha_state() + self.update_ha_state() def turn_off(self, **kwargs): """ Turn the device off. """ self._state = False mqtt.publish(self._hass, self._command_topic, self._payload_off, self._qos) - self._hass.update_ha_state() + self.update_ha_state() From a8c2cc4c33094daa3062bf28ce431c090d9a3440 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2015 23:38:24 +0100 Subject: [PATCH 008/165] rework for flake8 errors done --- homeassistant/components/light/mqtt.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 290ea6813ee..078a9a4075f 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -30,6 +30,8 @@ DEFAULT_COMMAND_RGB = "homeassistant/light/rgb/set" DEPENDENCIES = ['mqtt'] # pylint: disable=unused-argument + + def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Add MQTT Light. """ @@ -52,10 +54,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('payload_off', DEFAULT_PAYLOAD_OFF), config.get('brightness', DEFAULT_BRIGHTNESS))]) + class MqttLight(Light): """ Provides a demo switch. """ - # pylint: disable=too-many-instance-attributes,too-many-arguments,too-many-locals,bad-builtin + # pylint: disable=too-many-instance-attributes + # pylint: disable=too-many-arguments,too-many-locals,bad-builtin # Eight is reasonable in this case. def __init__(self, hass, name, From 49f4d92c62a28a3ebddf7f36b0c7983b603fccbb Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 10:51:23 +0000 Subject: [PATCH 009/165] Add dimmer as switch --- homeassistant/components/light/vera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index dc447f2e4b5..2c9cac640f0 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): controller = veraApi.VeraController(base_url) devices = [] try: - devices = controller.get_devices(['Switch', 'On/Off Switch']) + devices = controller.get_devices(['Switch', 'On/Off Switch', 'Dimmable Switch']) except RequestException: # There was a network related error connecting to the vera controller _LOGGER.exception("Error communicating with Vera API") From b66e4f1e155801bb4d3b93b1eed2723c4fdaf7e2 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 26 Oct 2015 15:05:01 +0100 Subject: [PATCH 010/165] two different demo lights on without RGB and one with RGB support. and code cleanup more pylint aligned --- homeassistant/components/light/mqtt.py | 236 ++++++++++++++++--------- 1 file changed, 151 insertions(+), 85 deletions(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 078a9a4075f..ff78ecb3ca8 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -2,6 +2,33 @@ homeassistant.components.light.mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a MQTT light. + +config for RGB Version with brightness: + +light: + platform: mqtt + name: "Office Light RGB" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + rgb_state_topic: "office/rgb1/rgb/status" + rgb_command_topic: "office/rgb1/rgb/set" + qos: 0 + payload_on: "on" + payload_off: "off" + +config without RGB: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + qos: 0 + payload_on: "on" + payload_off: "off" + """ import logging import homeassistant.components.mqtt as mqtt @@ -17,15 +44,7 @@ DEFAULT_PAYLOAD_OFF = "off" DEFAULT_RGB = [255, 255, 255] DEFAULT_RGB_PATTERN = "%d,%d,%d" DEFAULT_BRIGHTNESS = 120 - -DEFAULT_STATE_TOPIC = "homeassistant/light/state" -DEFAULT_COMMAND_TOPIC = "homeassistant/light/switch" - -DEFAULT_STATE_BRIGHTNESS = "homeassistant/light/brightness/state" -DEFAULT_COMMAND_BRIGHTNESS = "homeassistant/light/brightness/set" - -DEFAULT_STATE_RGB = "homeassistant/light/rgb/state" -DEFAULT_COMMAND_RGB = "homeassistant/light/rgb/set" +DEFAULT_OPTIMISTIC = False DEPENDENCIES = ['mqtt'] @@ -39,80 +58,70 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): _LOGGER.error("Missing required variable: command_topic") return False - add_devices_callback([MqttLight( - hass, - config.get('name', DEFAULT_NAME), - config.get('state_topic', DEFAULT_STATE_TOPIC), - config.get('command_topic', DEFAULT_COMMAND_TOPIC), - config.get('brightness_state_topic', DEFAULT_STATE_BRIGHTNESS), - config.get('brightness_command_topic', DEFAULT_COMMAND_BRIGHTNESS), - config.get('rgb_state_topic', DEFAULT_STATE_RGB), - config.get('rgb_command_topic', DEFAULT_COMMAND_RGB), - config.get('rgb', DEFAULT_RGB), - config.get('qos', DEFAULT_QOS), - config.get('payload_on', DEFAULT_PAYLOAD_ON), - config.get('payload_off', DEFAULT_PAYLOAD_OFF), - config.get('brightness', DEFAULT_BRIGHTNESS))]) + if config.get('rgb_command_topic') is not None: + add_devices_callback([MqttLightRGB( + hass, + config.get('name', DEFAULT_NAME), + {"state_topic": config.get('state_topic'), + "command_topic": config.get('command_topic'), + "brightness_state_topic": config.get('brightness_state_topic'), + "brightness_command_topic": + config.get('brightness_command_topic'), + "rgb_state_topic": config.get('rgb_state_topic'), + "rgb_command_topic": config.get('rgb_command_topic')}, + config.get('rgb', DEFAULT_RGB), + config.get('qos', DEFAULT_QOS), + {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), + "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, + config.get('brightness', DEFAULT_BRIGHTNESS), + config.get('optimistic', DEFAULT_OPTIMISTIC))]) + + else: + add_devices_callback([MqttLight( + hass, + config.get('name', DEFAULT_NAME), + {"state_topic": config.get('state_topic'), + "command_topic": config.get('command_topic')}, + config.get('qos', DEFAULT_QOS), + {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), + "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, + config.get('optimistic', DEFAULT_OPTIMISTIC))]) class MqttLight(Light): - """ Provides a demo switch. """ - - # pylint: disable=too-many-instance-attributes - # pylint: disable=too-many-arguments,too-many-locals,bad-builtin - # Eight is reasonable in this case. + """ Provides a demo light. """ + # pylint: disable=too-many-arguments def __init__(self, hass, name, - state_topic, command_topic, - brightness_state_topic, brightness_command_topic, - rgb_state_topic, rgb_command_topic, - rgb, qos, - payload_on, payload_off, - brightness): + topic, + qos, + payload, + optimistic): self._hass = hass self._name = name - self._state_topic = state_topic - self._command_topic = command_topic - self._brightness_state_topic = brightness_state_topic - self._brightness_command_topic = brightness_command_topic - self._rgb_state_topic = rgb_state_topic - self._rgb_command_topic = rgb_command_topic - self._rgb = rgb + self._topic = topic self._qos = qos - self._payload_on = payload_on - self._payload_off = payload_off - self._brightness = brightness - self._xy = [[0.5, 0.5]] + self._payload = payload + self._optimistic = optimistic self._state = False def message_received(topic, payload, qos): """ A new MQTT message has been received. """ - if payload == self._payload_on: + if payload == self._payload["on"]: self._state = True - self.update_ha_state() - elif payload == self._payload_off: + elif payload == self._payload["off"]: self._state = False - self.update_ha_state() - def brightness_received(topic, payload, qos): - """ A new MQTT message has been received. """ - self._brightness = int(payload) self.update_ha_state() - def rgb_received(topic, payload, qos): - """ A new MQTT message has been received. """ - rgb = payload.split(",") - self._rgb = list(map(int, rgb)) - self.update_ha_state() - - # subscribe the state_topic - mqtt.subscribe(self._hass, self._state_topic, - message_received, self._qos) - mqtt.subscribe(self._hass, self._brightness_state_topic, - brightness_received, self._qos) - mqtt.subscribe(self._hass, self._rgb_state_topic, - rgb_received, self._qos) + if self._topic["state_topic"] is None: + # force optimistic mode + self._optimistic = True + else: + # subscribe the state_topic + mqtt.subscribe(self._hass, self._topic["state_topic"], + message_received, self._qos) @property def should_poll(self): @@ -124,6 +133,68 @@ class MqttLight(Light): """ Returns the name of the device if any. """ return self._name + @property + def is_on(self): + """ True if device is on. """ + return self._state + + def turn_on(self, **kwargs): + """ Turn the device on. """ + + mqtt.publish(self._hass, self._topic["command_topic"], + self._payload["on"], self._qos) + + if self._optimistic: + # optimistically assume that switch has changed state + self._state = True + self.update_ha_state() + + def turn_off(self, **kwargs): + """ Turn the device off. """ + mqtt.publish(self._hass, self._topic["command_topic"], + self._payload["off"], self._qos) + + if self._optimistic: + # optimistically assume that switch has changed state + self._state = False + self.update_ha_state() + + +class MqttLightRGB(MqttLight): + """ Provides a demo RGB light. """ + + # pylint: disable=too-many-arguments + def __init__(self, hass, name, + topic, + rgb, qos, + payload, + brightness, optimistic): + + super().__init__(hass, name, topic, qos, + payload, optimistic) + + self._rgb = rgb + self._brightness = brightness + self._xy = [[0.5, 0.5]] + + def brightness_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._brightness = int(payload) + self.update_ha_state() + + def rgb_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._rgb = [int(val) for val in payload.split(',')] + self.update_ha_state() + + if self._topic["brightness_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["brightness_state_topic"], + brightness_received, self._qos) + + if self._topic["rgb_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["rgb_state_topic"], + rgb_received, self._qos) + @property def brightness(self): """ Brightness of this light between 0..255. """ @@ -139,33 +210,28 @@ class MqttLight(Light): """ RGB color value. """ return self._xy - @property - def is_on(self): - """ True if device is on. """ - return self._state - def turn_on(self, **kwargs): """ Turn the device on. """ - if ATTR_RGB_COLOR in kwargs: + if ATTR_RGB_COLOR in kwargs and \ + self._topic["rgb_command_topic"] is not None: + self._rgb = kwargs[ATTR_RGB_COLOR] rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) - mqtt.publish(self._hass, self._rgb_command_topic, rgb, self._qos) + mqtt.publish(self._hass, self._topic["rgb_command_topic"], + rgb, self._qos) + + if ATTR_BRIGHTNESS in kwargs and \ + self._topic["brightness_command_topic"] is not None: - if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] - mqtt.publish(self._hass, self._brightness_command_topic, + mqtt.publish(self._hass, self._topic["brightness_command_topic"], self._brightness, self._qos) - if not self._state: - self._state = True - mqtt.publish(self._hass, self._command_topic, - self._payload_on, self._qos) - self.update_ha_state() + mqtt.publish(self._hass, self._topic["command_topic"], + self._payload["on"], self._qos) - def turn_off(self, **kwargs): - """ Turn the device off. """ - self._state = False - mqtt.publish(self._hass, self._command_topic, - self._payload_off, self._qos) - self.update_ha_state() + if self._optimistic: + # optimistically assume that switch has changed state + self._state = True + self.update_ha_state() From 4fcd27e9050051371c9efa7f7850dbd486267c99 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 27 Oct 2015 16:52:43 +0100 Subject: [PATCH 011/165] light/mqtt to .coveragerc --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 28b4b926eab..bef52d57248 100644 --- a/.coveragerc +++ b/.coveragerc @@ -48,6 +48,7 @@ omit = homeassistant/components/downloader.py homeassistant/components/keyboard.py homeassistant/components/light/hue.py + homeassistant/components/light/mqtt.py homeassistant/components/light/limitlessled.py homeassistant/components/light/blinksticklight.py homeassistant/components/light/hyperion.py From e25503bc4a4fbc3f8f894480d197bb4f12c1dcd3 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 27 Oct 2015 22:34:49 +0000 Subject: [PATCH 012/165] Hue device capabilities. Color temperature support for light component and hue platform --- homeassistant/components/light/__init__.py | 25 +++++++++++-- homeassistant/components/light/hue.py | 37 +++++++++++++++++--- homeassistant/components/light/services.yaml | 4 +++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index c1b1579b4b5..051218bde24 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -43,6 +43,9 @@ Supports following parameters: A list containing three integers representing the xy color you want the light to be. + - ct_color + An INT in mireds represending the color temperature you want the light to be + - brightness Integer between 0 and 255 representing how bright you want the light to be. @@ -77,6 +80,7 @@ ATTR_TRANSITION = "transition" # lists holding color values ATTR_RGB_COLOR = "rgb_color" ATTR_XY_COLOR = "xy_color" +ATTR_CT_COLOR = "ct_color" # int with value 0 .. 255 representing brightness of the light ATTR_BRIGHTNESS = "brightness" @@ -105,6 +109,7 @@ DISCOVERY_PLATFORMS = { PROP_TO_ATTR = { 'brightness': ATTR_BRIGHTNESS, 'color_xy': ATTR_XY_COLOR, + 'color_ct': ATTR_CT_COLOR, } _LOGGER = logging.getLogger(__name__) @@ -119,8 +124,8 @@ def is_on(hass, entity_id=None): # pylint: disable=too-many-arguments def turn_on(hass, entity_id=None, transition=None, brightness=None, - rgb_color=None, xy_color=None, profile=None, flash=None, - effect=None): + rgb_color=None, xy_color=None, ct_color=None, profile=None, + flash=None, effect=None): """ Turns all or specified light on. """ data = { key: value for key, value in [ @@ -130,6 +135,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None, (ATTR_BRIGHTNESS, brightness), (ATTR_RGB_COLOR, rgb_color), (ATTR_XY_COLOR, xy_color), + (ATTR_CT_COLOR, ct_color), (ATTR_FLASH, flash), (ATTR_EFFECT, effect), ] if value is not None @@ -240,6 +246,16 @@ def setup(hass, config): # ValueError if value could not be converted to float pass + if ATTR_CT_COLOR in dat: + # ct_color should be an int of mirads value + ctcolor = dat.get(ATTR_CT_COLOR) + + # Without this check, a ctcolor with value '99' would work + # These values are based on Philips Hue, may need ajustment later + if isinstance(ctcolor, int): + if 154 <= ctcolor <= 500: + params[ATTR_CT_COLOR] = ctcolor + if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints @@ -301,6 +317,11 @@ class Light(ToggleEntity): """ XY color value [float, float]. """ return None + @property + def color_ct(self): + """ CT color value in mirads. """ + return None + @property def device_state_attributes(self): """ Returns device specific state attributes. """ diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index eff9aef4f36..88db7b990ee 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -14,9 +14,9 @@ from homeassistant.loader import get_component import homeassistant.util as util from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION, - ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT, - EFFECT_COLORLOOP) + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR, + ATTR_TRANSITION, ATTR_FLASH, FLASH_LONG, FLASH_SHORT, + ATTR_EFFECT, EFFECT_COLORLOOP) REQUIREMENTS = ['phue==0.8'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) @@ -147,6 +147,16 @@ class HueLight(Light): self.bridge = bridge self.update_lights = update_lights + # Hue can control multiple type of lights + capability_map = { + 'Dimmable light': ['bri'], + 'Dimmable plug-in unit': ['bri'], + 'Color light': ['bri', 'xy'], + 'Extended color light': ['bri', 'xy', 'ct'], + 'unknown': []} + self.capabilities = capability_map.get( + self.info['type'], 'unknown') + @property def unique_id(self): """ Returns the id of this Hue light """ @@ -161,12 +171,26 @@ class HueLight(Light): @property def brightness(self): """ Brightness of this light between 0..255. """ - return self.info['state']['bri'] + if 'bri' in self.capabilities: + return self.info['state']['bri'] + else: + return None @property def color_xy(self): """ XY color value. """ - return self.info['state'].get('xy') + if 'xy' in self.capabilities: + return self.info['state'].get('xy') + else: + return None + + @property + def color_ct(self): + """ CT color value. """ + if 'ct' in self.capabilities: + return self.info['state'].get('ct') + else: + return None @property def is_on(self): @@ -190,6 +214,9 @@ class HueLight(Light): if ATTR_XY_COLOR in kwargs: command['xy'] = kwargs[ATTR_XY_COLOR] + if ATTR_CT_COLOR in kwargs: + command['ct'] = kwargs[ATTR_CT_COLOR] + flash = kwargs.get(ATTR_FLASH) if flash == FLASH_LONG: diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index ed8b4b663ea..17e06a038af 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -20,6 +20,10 @@ turn_on: description: Color for the light in XY-format example: '[0.52, 0.43]' + ct_color: + description: Color temperature for the light in mireds (154-500) + example: '250' + brightness: description: Number between 0..255 indicating brightness example: 120 From e4d33bc6d4f5e9a0e16cdba5ac0526178275b977 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 27 Oct 2015 22:45:35 +0000 Subject: [PATCH 013/165] Included ct_color in code coverage --- homeassistant/components/light/demo.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 40a8cc023c5..0d72ef0dafd 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -8,7 +8,7 @@ Demo platform that implements lights. import random from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR) + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR) LIGHT_COLORS = [ @@ -16,22 +16,25 @@ LIGHT_COLORS = [ [0.460, 0.470], ] +LIGHT_TEMPS = [160, 300, 500] + def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return demo lights. """ add_devices_callback([ DemoLight("Bed Light", False), - DemoLight("Ceiling Lights", True, LIGHT_COLORS[0]), - DemoLight("Kitchen Lights", True, LIGHT_COLORS[1]) + DemoLight("Ceiling Lights", True, LIGHT_TEMPS[1], LIGHT_COLORS[0]), + DemoLight("Kitchen Lights", True, LIGHT_TEMPS[0], LIGHT_COLORS[1]) ]) class DemoLight(Light): """ Provides a demo switch. """ - def __init__(self, name, state, xy=None, brightness=180): + def __init__(self, name, state, xy=None, ct=None, brightness=180): self._name = name self._state = state self._xy = xy or random.choice(LIGHT_COLORS) + self._ct = ct or random.choice(LIGHT_TEMPS) self._brightness = brightness @property @@ -54,6 +57,11 @@ class DemoLight(Light): """ XY color value. """ return self._xy + @property + def color_ct(self): + """ CT color temperature. """ + return self._ct + @property def is_on(self): """ True if device is on. """ @@ -66,6 +74,9 @@ class DemoLight(Light): if ATTR_XY_COLOR in kwargs: self._xy = kwargs[ATTR_XY_COLOR] + if ATTR_CT_COLOR in kwargs: + self._ct = kwargs[ATTR_CT_COLOR] + if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] From 805aecd6f90b4ff0fbc1f9394e4bfe9e2ed30446 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 27 Oct 2015 22:49:45 +0000 Subject: [PATCH 014/165] pylint & flake cleanup --- homeassistant/components/light/demo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 0d72ef0dafd..c3c70da5418 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -30,6 +30,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class DemoLight(Light): """ Provides a demo switch. """ + # pylint: disable=too-many-arguments def __init__(self, name, state, xy=None, ct=None, brightness=180): self._name = name self._state = state From 6ef0d089eae6dc7bc5ab754aefc36d47a84c0706 Mon Sep 17 00:00:00 2001 From: pavoni Date: Tue, 27 Oct 2015 23:18:46 +0000 Subject: [PATCH 015/165] Add VeraLight class based on VeraSwitch - add dimmer support --- homeassistant/components/light/vera.py | 29 +++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index 2c9cac640f0..21df4485527 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -7,9 +7,13 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.vera.html """ import logging +import time + from requests.exceptions import RequestException from homeassistant.components.switch.vera import VeraSwitch +from homeassistant.components.light import Light, ATTR_BRIGHTNESS + REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/' 'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip' '#python-vera==0.1'] @@ -47,6 +51,29 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): exclude = extra_data.get('exclude', False) if exclude is not True: - lights.append(VeraSwitch(device, extra_data)) + lights.append(VeraLight(device, extra_data)) add_devices_callback(lights) + +class VeraLight(VeraSwitch): + """ Represents a Vera Light, including dimmable. """ + + @property + def state_attributes(self): + attr = super().state_attributes or {} + + if self.vera_device.is_dimmable: + attr[ATTR_BRIGHTNESS] = self.vera_device.get_brightness() + + return attr + + + def turn_on(self, **kwargs): + if ATTR_BRIGHTNESS in kwargs and self.vera_device.is_dimmable: + self.vera_device.set_brightness(kwargs[ATTR_BRIGHTNESS]) + else: + self.vera_device.switch_on() + + self.last_command_send = time.time() + self.is_on_status = True + From de027609d8696a15ff57ee3f13b3868ec63f058e Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 12:27:58 +0100 Subject: [PATCH 016/165] Fixed entity_id for the script component. Alias now does not override the entity_id Fixed issue: #561 --- homeassistant/components/script.py | 5 +++-- homeassistant/helpers/entity_component.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 3f892fdfa80..e5bfe61bfa5 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -75,7 +75,7 @@ def setup(hass, config): _LOGGER.warn("Missing key 'sequence' for script %s", name) continue alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, cfg[CONF_SEQUENCE]) + script = Script(hass, alias, name, cfg[CONF_SEQUENCE]) component.add_entities((script,)) _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) @@ -100,9 +100,10 @@ def setup(hass, config): class Script(ToggleEntity): """ Represents a script. """ - def __init__(self, hass, name, sequence): + def __init__(self, hass, name, entity_id, sequence): self.hass = hass self._name = name + self.entity_id = entity_id self.sequence = sequence self._lock = threading.Lock() self._cur = -1 diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 708bf6e93a9..5223b7f0ca3 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -65,8 +65,10 @@ class EntityComponent(object): if entity is not None and entity not in self.entities.values(): entity.hass = self.hass - entity.entity_id = generate_entity_id( - self.entity_id_format, entity.name, self.entities.keys()) + if entity.entity_id is None: + entity.entity_id = generate_entity_id( + self.entity_id_format, entity.name, + self.entities.keys()) self.entities[entity.entity_id] = entity From 48bfc98acb45b0114ad7c004fc1aaa69ad823ab4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 19:52:09 +0100 Subject: [PATCH 017/165] Fixed entity name --- homeassistant/components/script.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index e5bfe61bfa5..9849a243a41 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -75,7 +75,8 @@ def setup(hass, config): _LOGGER.warn("Missing key 'sequence' for script %s", name) continue alias = cfg.get(CONF_ALIAS, name) - script = Script(hass, alias, name, cfg[CONF_SEQUENCE]) + entity_id = "{}.{}".format(DOMAIN, name) + script = Script(hass, alias, entity_id, cfg[CONF_SEQUENCE]) component.add_entities((script,)) _, object_id = split_entity_id(script.entity_id) hass.services.register(DOMAIN, object_id, service_handler) From 10c95b435250ccc7b754aa726153413238026ad7 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 20:17:17 +0100 Subject: [PATCH 018/165] Added pylint hint --- homeassistant/components/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 9849a243a41..084f5502426 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -99,6 +99,7 @@ def setup(hass, config): return True +# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ def __init__(self, hass, name, entity_id, sequence): From 2eb65c8eb8e3aca6021a04abf0a8a84e9762fa24 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Oct 2015 12:45:57 -0700 Subject: [PATCH 019/165] Version bump to 0.8.0.dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 00f8ee408a3..49825cee094 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.7" +__version__ = "0.8.0.dev0" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From f3500542dd85df362710aa921a5c2e2b777cb4cb Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 20:17:17 +0100 Subject: [PATCH 020/165] Added pylint hint --- homeassistant/components/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index 6d5f0ea37a1..a14919cab67 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -101,6 +101,7 @@ def setup(hass, config): return True +# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ # pylint: disable=too-many-instance-attributes From 30de5af445772775401f894d76c0c8a095aed2c8 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 22:08:04 +0100 Subject: [PATCH 021/165] Fix for Philio Zwave devices which don't send an off event. --- homeassistant/components/sensor/zwave.py | 63 +++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 0cd136421a0..10d8734bcac 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -7,15 +7,27 @@ For more details about the zwave component, please refer to the documentation at https://home-assistant.io/components/zwave.html """ # pylint: disable=import-error +from homeassistant.helpers.event import track_point_in_time from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - +import datetime +import homeassistant.util.dt as dt_util import homeassistant.components.zwave as zwave from homeassistant.helpers.entity import Entity from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF, TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) +PHILIO = '013c' +PHILIO_SLIM_SENSOR = '0002' +PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) + +WORKAROUND_NO_TRIGGER_OFF_EVENT = 'trigger_no_off_event' + +SPECIFIC_DEVICE_MAPPINGS = [ + (WORKAROUND_NO_TRIGGER_OFF_EVENT, PHILIO_SLIM_SENSOR_MOTION), +] + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ @@ -24,10 +36,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value.set_change_verified(False) - # if 1 in groups and (zwave.NETWORK.controller.node_id not in - # groups[1].associations): - # node.groups[1].add_association(zwave.NETWORK.controller.node_id) + # Check workaround mappings for specific devices + for workaround_definition in SPECIFIC_DEVICE_MAPPINGS: + workaround, sensor_specification = workaround_definition + if sensor_specification == ( + value.command_class, value.node.manufacturer_id, + value.node.manufacturer_id, value.node.manufacturer_id): + if workaround == WORKAROUND_NO_TRIGGER_OFF_EVENT: + add_devices([ZWaveTriggerSensor(value, hass)]) + return + # generic Device mappings if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) @@ -37,12 +56,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ZWaveSensor(Entity): """ Represents a Z-Wave sensor. """ + def __init__(self, sensor_value): self._value = sensor_value self._node = sensor_value.node dispatcher.connect( - self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) + self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) @property def should_poll(self): @@ -90,7 +110,7 @@ class ZWaveSensor(Entity): def unit_of_measurement(self): return self._value.units - def _value_changed(self, value): + def value_changed(self, value): """ Called when a value has changed on the network. """ if self._value.value_id == value.value_id: self.update_ha_state() @@ -106,6 +126,37 @@ class ZWaveBinarySensor(ZWaveSensor): return STATE_ON if self._value.data else STATE_OFF +class ZWaveTriggerSensor(ZWaveSensor): + """ Represents a stateless sensor which triggers events within Z-Wave. """ + + def __init__(self, sensor_value, hass): + super(ZWaveTriggerSensor, self).__init__(sensor_value) + self._hass = hass + self.invalidate_after = None + + def value_changed(self, value): + """ Called when a value has changed on the network. """ + if self._value.value_id == value.value_id: + self.update_ha_state() + if value.data: + # only allow this value to be true for 60 secs + self.invalidate_after = dt_util.utcnow() + datetime.timedelta( + seconds=60) + track_point_in_time( + self._hass, self.update_ha_state, + self.invalidate_after) + + @property + def state(self): + """ Returns the state of the sensor. """ + if not self._value.data or \ + (self.invalidate_after is not None and + self.invalidate_after <= dt_util.utcnow()): + return STATE_OFF + + return STATE_ON + + class ZWaveMultilevelSensor(ZWaveSensor): """ Represents a multi level sensor Z-Wave sensor. """ From d578bf3494c0416c5c361f02242e0a6d027ebff4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Wed, 28 Oct 2015 22:17:17 +0100 Subject: [PATCH 022/165] Removed extra pylint hint from a previous merge --- homeassistant/components/script.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index a14919cab67..6d5f0ea37a1 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -101,7 +101,6 @@ def setup(hass, config): return True -# pylint: disable=too-many-instance-attributes class Script(ToggleEntity): """ Represents a script. """ # pylint: disable=too-many-instance-attributes From 3b37a7b73785940793e01eb183509a7b05c2a678 Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 28 Oct 2015 17:20:15 -0400 Subject: [PATCH 023/165] bugfix for actiontec device tracker --- .../components/device_tracker/actiontec.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index a2f94f34c3a..1c13fedec48 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -30,7 +30,9 @@ _LOGGER = logging.getLogger(__name__) _LEASES_REGEX = re.compile( r'(?P([0-9]{1,3}[\.]){3}[0-9]{1,3})' + - r'\smac:\s(?P([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))') + r'\smac:\s(?P([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))' + + r'\svalid\sfor:\s(?P(-?\d+))' + + r'\ssec') # pylint: disable=unused-argument @@ -40,9 +42,7 @@ def get_scanner(hass, config): {DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]}, _LOGGER): return None - scanner = ActiontecDeviceScanner(config[DOMAIN]) - return scanner if scanner.success_init else None Device = namedtuple("Device", ["mac", "ip", "last_update"]) @@ -60,11 +60,8 @@ class ActiontecDeviceScanner(object): self.password = config[CONF_PASSWORD] minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0) self.home_interval = timedelta(minutes=minutes) - self.lock = threading.Lock() - self.last_results = [] - # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None @@ -109,7 +106,6 @@ class ActiontecDeviceScanner(object): exclude_targets.add(host) if len(exclude_targets) > 0: exclude_target_list = [t.ip for t in exclude_targets] - actiontec_data = self.get_actiontec_data() if not actiontec_data: return False @@ -118,8 +114,9 @@ class ActiontecDeviceScanner(object): if client in actiontec_data: actiontec_data.pop(client) for name, data in actiontec_data.items(): - device = Device(data['mac'], name, now) - self.last_results.append(device) + if data['timevalid'] > 0: + device = Device(data['mac'], name, now) + self.last_results.append(device) self.last_results.extend(exclude_targets) _LOGGER.info("actiontec scan successful") return True @@ -153,6 +150,7 @@ class ActiontecDeviceScanner(object): if match is not None: devices[match.group('ip')] = { 'ip': match.group('ip'), - 'mac': match.group('mac').upper() + 'mac': match.group('mac').upper(), + 'timevalid': int(match.group('timevalid')) } return devices From 8eeca945171b1975a3097f5ebc959d51f33a4dac Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 28 Oct 2015 17:43:41 -0400 Subject: [PATCH 024/165] removed home_interval option since it was added to the main device_tracker component --- .../components/device_tracker/actiontec.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index 1c13fedec48..38f5f78907f 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -23,9 +23,6 @@ from homeassistant.components.device_tracker import DOMAIN # Return cached results if last scan was less then this time ago MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) -# Interval in minutes to exclude devices from a scan while they are home -CONF_HOME_INTERVAL = "home_interval" - _LOGGER = logging.getLogger(__name__) _LEASES_REGEX = re.compile( @@ -58,16 +55,12 @@ class ActiontecDeviceScanner(object): self.host = config[CONF_HOST] self.username = config[CONF_USERNAME] self.password = config[CONF_PASSWORD] - minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0) - self.home_interval = timedelta(minutes=minutes) self.lock = threading.Lock() self.last_results = [] # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None _LOGGER.info("actiontec scanner initialized") - if self.home_interval: - _LOGGER.info("home_interval set to: %s", self.home_interval) def scan_devices(self): """ @@ -100,12 +93,6 @@ class ActiontecDeviceScanner(object): exclude_targets = set() exclude_target_list = [] now = dt_util.now() - if self.home_interval: - for host in self.last_results: - if host.last_update + self.home_interval > now: - exclude_targets.add(host) - if len(exclude_targets) > 0: - exclude_target_list = [t.ip for t in exclude_targets] actiontec_data = self.get_actiontec_data() if not actiontec_data: return False From bcb2451752e6a2603e98a36bed5475ea58c05519 Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Wed, 28 Oct 2015 17:47:13 -0400 Subject: [PATCH 025/165] fix pylint warning --- homeassistant/components/device_tracker/actiontec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index 38f5f78907f..c627c6fe118 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -17,7 +17,7 @@ import telnetlib import homeassistant.util.dt as dt_util from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers import validate_config -from homeassistant.util import Throttle, convert +from homeassistant.util import Throttle from homeassistant.components.device_tracker import DOMAIN # Return cached results if last scan was less then this time ago From 6bad702db4a3dd80787057543d4dbee5db0298f4 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 28 Oct 2015 23:12:16 +0000 Subject: [PATCH 026/165] Renamed to color_temp, removed capabilities (not needed afterall) --- homeassistant/components/light/__init__.py | 24 +++++++------- homeassistant/components/light/demo.py | 8 ++--- homeassistant/components/light/hue.py | 35 +++++--------------- homeassistant/components/light/services.yaml | 2 +- 4 files changed, 25 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 051218bde24..d9ec71c460b 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -43,7 +43,7 @@ Supports following parameters: A list containing three integers representing the xy color you want the light to be. - - ct_color + - color_temp An INT in mireds represending the color temperature you want the light to be - brightness @@ -80,7 +80,7 @@ ATTR_TRANSITION = "transition" # lists holding color values ATTR_RGB_COLOR = "rgb_color" ATTR_XY_COLOR = "xy_color" -ATTR_CT_COLOR = "ct_color" +ATTR_COLOR_TEMP = "color_temp" # int with value 0 .. 255 representing brightness of the light ATTR_BRIGHTNESS = "brightness" @@ -109,7 +109,7 @@ DISCOVERY_PLATFORMS = { PROP_TO_ATTR = { 'brightness': ATTR_BRIGHTNESS, 'color_xy': ATTR_XY_COLOR, - 'color_ct': ATTR_CT_COLOR, + 'color_temp': ATTR_COLOR_TEMP, } _LOGGER = logging.getLogger(__name__) @@ -124,7 +124,7 @@ def is_on(hass, entity_id=None): # pylint: disable=too-many-arguments def turn_on(hass, entity_id=None, transition=None, brightness=None, - rgb_color=None, xy_color=None, ct_color=None, profile=None, + rgb_color=None, xy_color=None, color_temp=None, profile=None, flash=None, effect=None): """ Turns all or specified light on. """ data = { @@ -135,7 +135,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None, (ATTR_BRIGHTNESS, brightness), (ATTR_RGB_COLOR, rgb_color), (ATTR_XY_COLOR, xy_color), - (ATTR_CT_COLOR, ct_color), + (ATTR_COLOR_TEMP, color_temp), (ATTR_FLASH, flash), (ATTR_EFFECT, effect), ] if value is not None @@ -246,15 +246,15 @@ def setup(hass, config): # ValueError if value could not be converted to float pass - if ATTR_CT_COLOR in dat: - # ct_color should be an int of mirads value - ctcolor = dat.get(ATTR_CT_COLOR) + if ATTR_COLOR_TEMP in dat: + # color_temp should be an int of mirads value + colortemp = dat.get(ATTR_COLOR_TEMP) # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later - if isinstance(ctcolor, int): - if 154 <= ctcolor <= 500: - params[ATTR_CT_COLOR] = ctcolor + if isinstance(colortemp, int): + if 154 <= colortemp <= 500: + params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: try: @@ -318,7 +318,7 @@ class Light(ToggleEntity): return None @property - def color_ct(self): + def color_temp(self): """ CT color value in mirads. """ return None diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index c3c70da5418..76ac07a0073 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -8,7 +8,7 @@ Demo platform that implements lights. import random from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR) + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_COLOR_TEMP) LIGHT_COLORS = [ @@ -59,7 +59,7 @@ class DemoLight(Light): return self._xy @property - def color_ct(self): + def color_temp(self): """ CT color temperature. """ return self._ct @@ -75,8 +75,8 @@ class DemoLight(Light): if ATTR_XY_COLOR in kwargs: self._xy = kwargs[ATTR_XY_COLOR] - if ATTR_CT_COLOR in kwargs: - self._ct = kwargs[ATTR_CT_COLOR] + if ATTR_COLOR_TEMP in kwargs: + self._ct = kwargs[ATTR_COLOR_TEMP] if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index 88db7b990ee..dcd05b9f4c9 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -14,7 +14,7 @@ from homeassistant.loader import get_component import homeassistant.util as util from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_CT_COLOR, + Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_COLOR_TEMP, ATTR_TRANSITION, ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT, EFFECT_COLORLOOP) @@ -124,7 +124,7 @@ def request_configuration(host, hass, add_devices_callback): _CONFIGURING[host], "Failed to register, please try again.") return - + # pylint: disable=unused-argument def hue_configuration_callback(data): """ Actions to do when our configuration callback is called. """ setup_bridge(host, hass, add_devices_callback) @@ -147,16 +147,6 @@ class HueLight(Light): self.bridge = bridge self.update_lights = update_lights - # Hue can control multiple type of lights - capability_map = { - 'Dimmable light': ['bri'], - 'Dimmable plug-in unit': ['bri'], - 'Color light': ['bri', 'xy'], - 'Extended color light': ['bri', 'xy', 'ct'], - 'unknown': []} - self.capabilities = capability_map.get( - self.info['type'], 'unknown') - @property def unique_id(self): """ Returns the id of this Hue light """ @@ -171,26 +161,17 @@ class HueLight(Light): @property def brightness(self): """ Brightness of this light between 0..255. """ - if 'bri' in self.capabilities: - return self.info['state']['bri'] - else: - return None + return self.info['state']['bri'] @property def color_xy(self): """ XY color value. """ - if 'xy' in self.capabilities: - return self.info['state'].get('xy') - else: - return None + return self.info['state'].get('xy') @property - def color_ct(self): + def color_temp(self): """ CT color value. """ - if 'ct' in self.capabilities: - return self.info['state'].get('ct') - else: - return None + return self.info['state'].get('ct') @property def is_on(self): @@ -214,8 +195,8 @@ class HueLight(Light): if ATTR_XY_COLOR in kwargs: command['xy'] = kwargs[ATTR_XY_COLOR] - if ATTR_CT_COLOR in kwargs: - command['ct'] = kwargs[ATTR_CT_COLOR] + if ATTR_COLOR_TEMP in kwargs: + command['ct'] = kwargs[ATTR_COLOR_TEMP] flash = kwargs.get(ATTR_FLASH) diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index 17e06a038af..8a0c5b8fded 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -20,7 +20,7 @@ turn_on: description: Color for the light in XY-format example: '[0.52, 0.43]' - ct_color: + color_temp: description: Color temperature for the light in mireds (154-500) example: '250' From f456d2ff23dde27bad930939deb7a6960763e5ce Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 28 Oct 2015 23:16:25 +0000 Subject: [PATCH 027/165] styling fix --- homeassistant/components/light/hue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index dcd05b9f4c9..cd9ad21984f 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -124,6 +124,7 @@ def request_configuration(host, hass, add_devices_callback): _CONFIGURING[host], "Failed to register, please try again.") return + # pylint: disable=unused-argument def hue_configuration_callback(data): """ Actions to do when our configuration callback is called. """ From c7a0b5800cafbc986e9b839bb1c5cbf9cd3846ad Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 29 Oct 2015 00:23:05 -0700 Subject: [PATCH 028/165] Update links in introduction component --- homeassistant/components/introduction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/introduction.py b/homeassistant/components/introduction.py index 4c367703903..5e5dcbd77c2 100644 --- a/homeassistant/components/introduction.py +++ b/homeassistant/components/introduction.py @@ -26,13 +26,13 @@ def setup(hass, config=None): Here are some resources to get started: - Configuring Home Assistant: - https://home-assistant.io/getting-started/configuration.html + https://home-assistant.io/getting-started/configuration/ - Available components: https://home-assistant.io/components/ - Troubleshooting your configuration: - https://home-assistant.io/getting-started/troubleshooting-configuration.html + https://home-assistant.io/getting-started/troubleshooting-configuration/ - Getting help: https://home-assistant.io/help/ From 85bb828149ec7f9f5af337777d45ebd007f9d5cb Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 29 Oct 2015 21:17:10 +0100 Subject: [PATCH 029/165] changed requirements to the latest evohome version. --- .../thermostat/honeywell_round_connected.py | 86 +++++++------------ 1 file changed, 32 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/thermostat/honeywell_round_connected.py b/homeassistant/components/thermostat/honeywell_round_connected.py index 15f27e0d9aa..f4c4af4d010 100644 --- a/homeassistant/components/thermostat/honeywell_round_connected.py +++ b/homeassistant/components/thermostat/honeywell_round_connected.py @@ -12,7 +12,7 @@ from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS) -REQUIREMENTS = ['evohomeclient'] +REQUIREMENTS = ['evohomeclient==0.2.3'] # from . import ATTR_CURRENT_TEMPERATURE,ATTR_TEMPERATURE @@ -30,11 +30,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return try: - from evohomeclient2 import EvohomeClient + from evohomeclient import EvohomeClient except ImportError: logger.exception( - "Error while importing dependency nest. " - "Did you maybe not install the python-nest dependency?") + "Error while importing dependency evohomeclient. " + "Did you maybe not install the python evohomeclient dependency?") return @@ -45,25 +45,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ]) except socket.error: logger.error( - "Connection error logging into the nest web service" + "Connection error logging into the honeywell evohome web service" ) class RoundThermostat(ThermostatDevice): - """ Represents a Nest thermostat. """ + """ Represents a Honeywell Round Connected thermostat. """ def __init__(self, device): #self.structure = structure self.device = device self._current_temperature=None self._target_temperature=None + self._name="round connected" + self._sensorid=None self.update() @property def name(self): """ Returns the name of the nest, if any. """ - return 'round' + return self._name + + @name.setter + def name(self,value): + self._name=value @property def unit_of_measurement(self): @@ -77,17 +83,11 @@ class RoundThermostat(ThermostatDevice): return {} - - @property def current_temperature(self): """ Returns the current temperature. """ return self._current_temperature - #return round(self.device.temperature, 1) - @current_temperature.setter - def current_temparature(self,value): - self._current_temperature=value # @property # def operation(self): @@ -104,28 +104,7 @@ class RoundThermostat(ThermostatDevice): """ Returns the temperature we try to reach. """ return self._target_temperature - @target_temperature.setter - def target_temperature(self,value): - self._target_temperature=value - # target = self.device.target - # - # if self.device.mode == 'range': - # low, high = target - # if self.operation == STATE_COOL: - # temp = high - # elif self.operation == STATE_HEAT: - # temp = low - # else: - # range_average = (low + high)/2 - # if self.current_temperature < range_average: - # temp = low - # elif self.current_temperature >= range_average: - # temp = high - # else: - # temp = target - # - # return round(temp, 1) # @property # def target_temperature_low(self): @@ -144,25 +123,21 @@ class RoundThermostat(ThermostatDevice): # @property # def is_away_mode_on(self): # """ Returns if away mode is on. """ - # return self.structure.away + # return True + + def set_temperature(self, temperature): + """ Set new target temperature """ + self.device.set_temperature(self._name,temperature) + + + def turn_away_mode_on(self): + """ Turns away on. """ + self.structure.away = True + + def turn_away_mode_off(self): + """ Turns away off. """ + self.structure.away = False - # def set_temperature(self, temperature): - # """ Set new target temperature """ - # if self.device.mode == 'range': - # if self.target_temperature == self.target_temperature_low: - # temperature = (temperature, self.target_temperature_high) - # elif self.target_temperature == self.target_temperature_high: - # temperature = (self.target_temperature_low, temperature) - # self.device.target = temperature - # - # def turn_away_mode_on(self): - # """ Turns away on. """ - # self.structure.away = True - # - # def turn_away_mode_off(self): - # """ Turns away off. """ - # self.structure.away = False - # # @property # def min_temp(self): # """ Identifies min_temp in Nest API or defaults if not available. """ @@ -183,10 +158,13 @@ class RoundThermostat(ThermostatDevice): @property def should_poll(self): - """ No polling needed for a demo thermostat. """ + """ Should poll the evohome cloud service """ return True def update(self): - for dev in self.device.temperatures(): + for dev in self.device.temperatures(force_refresh=True): self._current_temperature=dev['temp'] self._target_temperature=dev['setpoint'] + self._name=dev['name'] + self._sensorid=dev['id'] + From 031d5ce2553120b23f38019d66f919469cc9c4d3 Mon Sep 17 00:00:00 2001 From: pavoni Date: Tue, 27 Oct 2015 23:43:06 +0000 Subject: [PATCH 030/165] Fix style issues, update pyvera version. --- homeassistant/components/light/vera.py | 16 +++++++++------- homeassistant/components/switch/vera.py | 6 +++--- requirements_all.txt | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index 21df4485527..2e9e02ebbe5 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -12,11 +12,11 @@ import time from requests.exceptions import RequestException from homeassistant.components.switch.vera import VeraSwitch -from homeassistant.components.light import Light, ATTR_BRIGHTNESS +from homeassistant.components.light import ATTR_BRIGHTNESS -REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/' - 'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip' - '#python-vera==0.1'] +REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' + '90e203b58c5897930a9567252943b7c96c39652b.zip' + '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) @@ -39,7 +39,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): controller = veraApi.VeraController(base_url) devices = [] try: - devices = controller.get_devices(['Switch', 'On/Off Switch', 'Dimmable Switch']) + devices = controller.get_devices([ + 'Switch', + 'On/Off Switch', + 'Dimmable Switch']) except RequestException: # There was a network related error connecting to the vera controller _LOGGER.exception("Error communicating with Vera API") @@ -55,6 +58,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): add_devices_callback(lights) + class VeraLight(VeraSwitch): """ Represents a Vera Light, including dimmable. """ @@ -67,7 +71,6 @@ class VeraLight(VeraSwitch): return attr - def turn_on(self, **kwargs): if ATTR_BRIGHTNESS in kwargs and self.vera_device.is_dimmable: self.vera_device.set_brightness(kwargs[ATTR_BRIGHTNESS]) @@ -76,4 +79,3 @@ class VeraLight(VeraSwitch): self.last_command_send = time.time() self.is_on_status = True - diff --git a/homeassistant/components/switch/vera.py b/homeassistant/components/switch/vera.py index ecf922e8cfa..55e519d8616 100644 --- a/homeassistant/components/switch/vera.py +++ b/homeassistant/components/switch/vera.py @@ -15,9 +15,9 @@ from homeassistant.helpers.entity import ToggleEntity from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME) -REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/' - 'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip' - '#python-vera==0.1'] +REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' + '90e203b58c5897930a9567252943b7c96c39652b.zip' + '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 11d91043d12..d04ea496cea 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -128,7 +128,7 @@ pyfttt==0.3 https://github.com/balloob/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 # Vera (*.vera) -https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip#python-vera==0.1 +https://github.com/pavoni/home-assistant-vera-api/archive/90e203b58c5897930a9567252943b7c96c39652b.zip#python-vera==0.1.1 # Sonos (media_player.sonos) SoCo==0.11.1 From 0269be581355f811a98326e73f188ac5e4f0f7fc Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 30 Oct 2015 09:30:22 +0000 Subject: [PATCH 031/165] Update pyvera version --- homeassistant/components/light/vera.py | 2 +- homeassistant/components/switch/vera.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/vera.py b/homeassistant/components/light/vera.py index 2e9e02ebbe5..f4c46976626 100644 --- a/homeassistant/components/light/vera.py +++ b/homeassistant/components/light/vera.py @@ -15,7 +15,7 @@ from homeassistant.components.switch.vera import VeraSwitch from homeassistant.components.light import ATTR_BRIGHTNESS REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' - '90e203b58c5897930a9567252943b7c96c39652b.zip' + 'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip' '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/switch/vera.py b/homeassistant/components/switch/vera.py index 55e519d8616..51d682bcdc1 100644 --- a/homeassistant/components/switch/vera.py +++ b/homeassistant/components/switch/vera.py @@ -16,7 +16,7 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME) REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/' - '90e203b58c5897930a9567252943b7c96c39652b.zip' + 'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip' '#python-vera==0.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index d04ea496cea..6b7f57d79bc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -128,7 +128,7 @@ pyfttt==0.3 https://github.com/balloob/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 # Vera (*.vera) -https://github.com/pavoni/home-assistant-vera-api/archive/90e203b58c5897930a9567252943b7c96c39652b.zip#python-vera==0.1.1 +https://github.com/pavoni/home-assistant-vera-api/archive/efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip#python-vera==0.1.1 # Sonos (media_player.sonos) SoCo==0.11.1 From e961dd5f95aba15994be339bfa24d95b844a3f24 Mon Sep 17 00:00:00 2001 From: Nolan Gilley Date: Fri, 30 Oct 2015 07:00:35 -0400 Subject: [PATCH 032/165] increase valid for time to 60 since I was having some issues. removed deprecated lines. --- .../components/device_tracker/actiontec.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index c627c6fe118..6296d766456 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -57,7 +57,6 @@ class ActiontecDeviceScanner(object): self.password = config[CONF_PASSWORD] self.lock = threading.Lock() self.last_results = [] - # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None _LOGGER.info("actiontec scanner initialized") @@ -90,21 +89,13 @@ class ActiontecDeviceScanner(object): return False with self.lock: - exclude_targets = set() - exclude_target_list = [] now = dt_util.now() actiontec_data = self.get_actiontec_data() if not actiontec_data: return False - self.last_results = [] - for client in exclude_target_list: - if client in actiontec_data: - actiontec_data.pop(client) - for name, data in actiontec_data.items(): - if data['timevalid'] > 0: - device = Device(data['mac'], name, now) - self.last_results.append(device) - self.last_results.extend(exclude_targets) + self.last_results = [Device(data['mac'], name, now) + for name, data in actiontec_data.items() + if data['timevalid'] > -60] _LOGGER.info("actiontec scan successful") return True From a56173676e43d0ba4b082aa393818b21af756367 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 30 Oct 2015 15:28:06 +0100 Subject: [PATCH 033/165] Fixed the workaround match logic --- homeassistant/components/sensor/zwave.py | 33 +++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 10d8734bcac..d6b55f93641 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -22,12 +22,11 @@ PHILIO = '013c' PHILIO_SLIM_SENSOR = '0002' PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) -WORKAROUND_NO_TRIGGER_OFF_EVENT = 'trigger_no_off_event' - -SPECIFIC_DEVICE_MAPPINGS = [ - (WORKAROUND_NO_TRIGGER_OFF_EVENT, PHILIO_SLIM_SENSOR_MOTION), -] +WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event' +DEVICE_MAPPINGS = { + PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, +} def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ @@ -36,17 +35,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None): value.set_change_verified(False) - # Check workaround mappings for specific devices - for workaround_definition in SPECIFIC_DEVICE_MAPPINGS: - workaround, sensor_specification = workaround_definition - if sensor_specification == ( - value.command_class, value.node.manufacturer_id, - value.node.manufacturer_id, value.node.manufacturer_id): - if workaround == WORKAROUND_NO_TRIGGER_OFF_EVENT: - add_devices([ZWaveTriggerSensor(value, hass)]) - return + # if 1 in groups and (zwave.NETWORK.controller.node_id not in + # groups[1].associations): + # node.groups[1].add_association(zwave.NETWORK.controller.node_id) - # generic Device mappings + specific_sensor_key = (value.node.manufacturer_id, + value.node.product_id, + value.index) + + # Check workaround mappings for specific devices + if specific_sensor_key in DEVICE_MAPPINGS: + if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: + add_devices([ZWaveTriggerSensor(value, hass)]) + return + + # generic Device mappings if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) From 2ad647bb097b72cf06e155d328e5a20ee20dc0b3 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Fri, 30 Oct 2015 15:30:08 +0100 Subject: [PATCH 034/165] Style fixes --- homeassistant/components/sensor/zwave.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index d6b55f93641..da5c8839d58 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -25,9 +25,10 @@ PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0) WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event' DEVICE_MAPPINGS = { - PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, + PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT, } + def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up Z-Wave sensors. """ node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] From 194c6343ac89b754dbf5e4105ab850c3480cd92d Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Fri, 30 Oct 2015 19:01:42 +0000 Subject: [PATCH 035/165] Minor corrections to light and light/demo --- homeassistant/components/light/__init__.py | 3 +-- homeassistant/components/light/demo.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index d9ec71c460b..7c8a01407ac 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -252,8 +252,7 @@ def setup(hass, config): # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later - if isinstance(colortemp, int): - if 154 <= colortemp <= 500: + if isinstance(colortemp, int) and 154 <= colortemp <= 500: params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 76ac07a0073..d12a288ccc3 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -16,7 +16,7 @@ LIGHT_COLORS = [ [0.460, 0.470], ] -LIGHT_TEMPS = [160, 300, 500] +LIGHT_TEMPS = [160, 500] def setup_platform(hass, config, add_devices_callback, discovery_info=None): From b76471c4b3267e2ed805cf82027d36559bc2fe22 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Fri, 30 Oct 2015 19:15:38 +0000 Subject: [PATCH 036/165] :( .. pyliny --- homeassistant/components/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 7c8a01407ac..3bb2b1ab239 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -253,7 +253,7 @@ def setup(hass, config): # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later if isinstance(colortemp, int) and 154 <= colortemp <= 500: - params[ATTR_COLOR_TEMP] = colortemp + params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: try: From 31826ab263212fa6821b933899dad9c9f96aba44 Mon Sep 17 00:00:00 2001 From: hexxter Date: Sat, 31 Oct 2015 19:26:03 +0100 Subject: [PATCH 037/165] redesigned mqtt light an first steps with the unittest system --- homeassistant/components/light/mqtt.py | 187 +++++++++++-------------- tests/components/light/test_mqtt.py | 130 +++++++++++++++++ 2 files changed, 209 insertions(+), 108 deletions(-) create mode 100644 tests/components/light/test_mqtt.py diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index ff78ecb3ca8..4e9b58e272e 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -31,6 +31,8 @@ light: """ import logging + +import homeassistant.util.color as color_util import homeassistant.components.mqtt as mqtt from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR) @@ -41,9 +43,7 @@ DEFAULT_NAME = "MQTT Light" DEFAULT_QOS = 0 DEFAULT_PAYLOAD_ON = "on" DEFAULT_PAYLOAD_OFF = "off" -DEFAULT_RGB = [255, 255, 255] DEFAULT_RGB_PATTERN = "%d,%d,%d" -DEFAULT_BRIGHTNESS = 120 DEFAULT_OPTIMISTIC = False DEPENDENCIES = ['mqtt'] @@ -58,53 +58,46 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): _LOGGER.error("Missing required variable: command_topic") return False - if config.get('rgb_command_topic') is not None: - add_devices_callback([MqttLightRGB( - hass, - config.get('name', DEFAULT_NAME), - {"state_topic": config.get('state_topic'), - "command_topic": config.get('command_topic'), - "brightness_state_topic": config.get('brightness_state_topic'), - "brightness_command_topic": + add_devices_callback([MqttLight( + hass, + config.get('name', DEFAULT_NAME), + {"state_topic": config.get('state_topic'), + "command_topic": config.get('command_topic'), + "brightness_state_topic": config.get('brightness_state_topic'), + "brightness_command_topic": config.get('brightness_command_topic'), - "rgb_state_topic": config.get('rgb_state_topic'), - "rgb_command_topic": config.get('rgb_command_topic')}, - config.get('rgb', DEFAULT_RGB), - config.get('qos', DEFAULT_QOS), - {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), - "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, - config.get('brightness', DEFAULT_BRIGHTNESS), - config.get('optimistic', DEFAULT_OPTIMISTIC))]) + "rgb_state_topic": config.get('rgb_state_topic'), + "rgb_command_topic": config.get('rgb_command_topic')}, + config.get('rgb', [255, 255, 255]), + config.get('qos', DEFAULT_QOS), + {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), + "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, + config.get('brightness'), + config.get('optimistic', DEFAULT_OPTIMISTIC))]) - else: - add_devices_callback([MqttLight( - hass, - config.get('name', DEFAULT_NAME), - {"state_topic": config.get('state_topic'), - "command_topic": config.get('command_topic')}, - config.get('qos', DEFAULT_QOS), - {"on": config.get('payload_on', DEFAULT_PAYLOAD_ON), - "off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)}, - config.get('optimistic', DEFAULT_OPTIMISTIC))]) +# pylint: disable=too-many-instance-attributes class MqttLight(Light): - """ Provides a demo light. """ + """ Provides a demo Mqtt light. """ # pylint: disable=too-many-arguments def __init__(self, hass, name, topic, - qos, + rgb, qos, payload, - optimistic): + brightness, optimistic): self._hass = hass self._name = name self._topic = topic + self._rgb = rgb self._qos = qos self._payload = payload + self._brightness = brightness self._optimistic = optimistic self._state = False + self._xy = None def message_received(topic, payload, qos): """ A new MQTT message has been received. """ @@ -123,6 +116,48 @@ class MqttLight(Light): mqtt.subscribe(self._hass, self._topic["state_topic"], message_received, self._qos) + def brightness_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._brightness = int(payload) + self.update_ha_state() + + def rgb_received(topic, payload, qos): + """ A new MQTT message has been received. """ + self._rgb = [int(val) for val in payload.split(',')] + self._xy = color_util.color_RGB_to_xy(int(self._rgb[0]), + int(self._rgb[1]), + int(self._rgb[2])) + self.update_ha_state() + + if self._topic["brightness_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["brightness_state_topic"], + brightness_received, self._qos) + self._brightness = 255 + else: + self._brightness = None + + if self._topic["rgb_state_topic"] is not None: + mqtt.subscribe(self._hass, self._topic["rgb_state_topic"], + rgb_received, self._qos) + self._xy = [0, 0] + else: + self._xy = None + + @property + def brightness(self): + """ Brightness of this light between 0..255. """ + return self._brightness + + @property + def rgb_color(self): + """ RGB color value. """ + return self._rgb + + @property + def color_xy(self): + """ RGB color value. """ + return self._xy + @property def should_poll(self): """ No polling needed for a demo light. """ @@ -141,6 +176,19 @@ class MqttLight(Light): def turn_on(self, **kwargs): """ Turn the device on. """ + if ATTR_RGB_COLOR in kwargs and \ + self._topic["rgb_command_topic"] is not None: + self._rgb = kwargs[ATTR_RGB_COLOR] + rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) + mqtt.publish(self._hass, self._topic["rgb_command_topic"], + rgb, self._qos) + + if ATTR_BRIGHTNESS in kwargs and \ + self._topic["brightness_command_topic"] is not None: + self._brightness = kwargs[ATTR_BRIGHTNESS] + mqtt.publish(self._hass, self._topic["brightness_command_topic"], + self._brightness, self._qos) + mqtt.publish(self._hass, self._topic["command_topic"], self._payload["on"], self._qos) @@ -158,80 +206,3 @@ class MqttLight(Light): # optimistically assume that switch has changed state self._state = False self.update_ha_state() - - -class MqttLightRGB(MqttLight): - """ Provides a demo RGB light. """ - - # pylint: disable=too-many-arguments - def __init__(self, hass, name, - topic, - rgb, qos, - payload, - brightness, optimistic): - - super().__init__(hass, name, topic, qos, - payload, optimistic) - - self._rgb = rgb - self._brightness = brightness - self._xy = [[0.5, 0.5]] - - def brightness_received(topic, payload, qos): - """ A new MQTT message has been received. """ - self._brightness = int(payload) - self.update_ha_state() - - def rgb_received(topic, payload, qos): - """ A new MQTT message has been received. """ - self._rgb = [int(val) for val in payload.split(',')] - self.update_ha_state() - - if self._topic["brightness_state_topic"] is not None: - mqtt.subscribe(self._hass, self._topic["brightness_state_topic"], - brightness_received, self._qos) - - if self._topic["rgb_state_topic"] is not None: - mqtt.subscribe(self._hass, self._topic["rgb_state_topic"], - rgb_received, self._qos) - - @property - def brightness(self): - """ Brightness of this light between 0..255. """ - return self._brightness - - @property - def rgb_color(self): - """ RGB color value. """ - return self._rgb - - @property - def color_xy(self): - """ RGB color value. """ - return self._xy - - def turn_on(self, **kwargs): - """ Turn the device on. """ - - if ATTR_RGB_COLOR in kwargs and \ - self._topic["rgb_command_topic"] is not None: - - self._rgb = kwargs[ATTR_RGB_COLOR] - rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb) - mqtt.publish(self._hass, self._topic["rgb_command_topic"], - rgb, self._qos) - - if ATTR_BRIGHTNESS in kwargs and \ - self._topic["brightness_command_topic"] is not None: - - self._brightness = kwargs[ATTR_BRIGHTNESS] - mqtt.publish(self._hass, self._topic["brightness_command_topic"], - self._brightness, self._qos) - - mqtt.publish(self._hass, self._topic["command_topic"], - self._payload["on"], self._qos) - - if self._optimistic: - # optimistically assume that switch has changed state - self._state = True - self.update_ha_state() diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py new file mode 100644 index 00000000000..28c0e75e256 --- /dev/null +++ b/tests/components/light/test_mqtt.py @@ -0,0 +1,130 @@ +""" +tests.components.light.test_mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests mqtt light. + +config for RGB Version with brightness: + +light: + platform: mqtt + name: "Office Light RGB" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + rgb_state_topic: "office/rgb1/rgb/status" + rgb_command_topic: "office/rgb1/rgb/set" + qos: 0 + payload_on: "on" + payload_off: "off" + +config without RGB: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + qos: 0 + payload_on: "on" + payload_off: "off" + +config without RGB and brightness: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + qos: 0 + payload_on: "on" + payload_off: "off" +""" +import unittest + +from homeassistant.const import STATE_ON, STATE_OFF +import homeassistant.core as ha +import homeassistant.components.light as light +from tests.common import mock_mqtt_component, fire_mqtt_message + + +class TestLightMQTT(unittest.TestCase): + """ Test the MQTT light. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + self.mock_publish = mock_mqtt_component(self.hass) + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass.stop() + + def test_controlling_state_via_topic(self): + self.assertTrue(light.setup(self.hass, { + 'switch': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'test_light_rgb/status', + 'command_topic': 'test_light_rgb/set', + 'brightness_state_topic': 'test_light_rgb/brightness/status', + 'brightness_command_topic': 'test_light_rgb/brightness/set', + 'rgb_state_topic': 'test_light_rgb/rgb/status', + 'rgb_command_topic': 'test_light_rgb/rgb/set', + 'qos': 0, + 'payload_on': 'on', + 'payload_off': 'off' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test', 'on') + self.hass.pool.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + fire_mqtt_message(self.hass, 'test', 'off') + self.hass.pool.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + def test_sending_mqtt_commands_and_optimistic(self): + self.assertTrue(light.setup(self.hass, { + 'switch': { + 'platform': 'mqtt', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness_state_topic': 'test_light_rgb/brightness/status', + 'brightness_command_topic': 'test_light_rgb/brightness/set', + 'rgb_state_topic': 'test_light_rgb/rgb/status', + 'rgb_command_topic': 'test_light_rgb/rgb/set', + 'qos': 2, + 'payload_on': 'on', + 'payload_off': 'off' + } + })) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + switch.turn_on(self.hass, 'light.test') + self.hass.pool.block_till_done() + + self.assertEqual(('test_light_rgb/set', 'on', 2), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + + switch.turn_off(self.hass, 'light.test') + self.hass.pool.block_till_done() + + self.assertEqual(('test_light_rgb/set', 'off', 2), + self.mock_publish.mock_calls[-1][1]) + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) From efacd66bec297abdfb6db78c286c0d3873aee815 Mon Sep 17 00:00:00 2001 From: sander Date: Sat, 31 Oct 2015 20:35:23 +0100 Subject: [PATCH 038/165] linting and flakeing.. --- .../thermostat/honeywell_round_connected.py | 92 ++----------------- 1 file changed, 10 insertions(+), 82 deletions(-) diff --git a/homeassistant/components/thermostat/honeywell_round_connected.py b/homeassistant/components/thermostat/honeywell_round_connected.py index f4c4af4d010..f5d741a4f47 100644 --- a/homeassistant/components/thermostat/honeywell_round_connected.py +++ b/homeassistant/components/thermostat/honeywell_round_connected.py @@ -1,5 +1,3 @@ -__author__ = 'sander' - """ homeassistant.components.thermostat.honeywell_round_connected ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -7,14 +5,11 @@ Adds support for Honeywell Round Connected thermostats. """ import socket import logging - -from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, - STATE_IDLE, STATE_HEAT) +from homeassistant.components.thermostat import ThermostatDevice from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS) REQUIREMENTS = ['evohomeclient==0.2.3'] -# from . import ATTR_CURRENT_TEMPERATURE,ATTR_TEMPERATURE # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): @@ -53,24 +48,18 @@ class RoundThermostat(ThermostatDevice): """ Represents a Honeywell Round Connected thermostat. """ def __init__(self, device): - #self.structure = structure self.device = device - self._current_temperature=None - self._target_temperature=None - self._name="round connected" - self._sensorid=None + self._current_temperature = None + self._target_temperature = None + self._name = "round connected" + self._sensorid = None self.update() - @property def name(self): """ Returns the name of the nest, if any. """ return self._name - @name.setter - def name(self,value): - self._name=value - @property def unit_of_measurement(self): """ Unit of measurement this thermostat expresses itself in. """ @@ -82,79 +71,19 @@ class RoundThermostat(ThermostatDevice): # Move these to Thermostat Device and make them global return {} - @property def current_temperature(self): """ Returns the current temperature. """ return self._current_temperature - - # @property - # def operation(self): - # """ Returns current operation ie. heat, cool, idle """ - # if self.device.hvac_ac_state is True: - # return STATE_COOL - # elif self.device.hvac_heater_state is True: - # return STATE_HEAT - # else: - # return STATE_IDLE - @property def target_temperature(self): """ Returns the temperature we try to reach. """ return self._target_temperature - - - # @property - # def target_temperature_low(self): - # """ Returns the lower bound temperature we try to reach. """ - # if self.device.mode == 'range': - # return round(self.device.target[0], 1) - # return round(self.target_temperature, 1) - # - # @property - # def target_temperature_high(self): - # """ Returns the upper bound temperature we try to reach. """ - # if self.device.mode == 'range': - # return round(self.device.target[1], 1) - # return round(self.target_temperature, 1) - # - # @property - # def is_away_mode_on(self): - # """ Returns if away mode is on. """ - # return True - def set_temperature(self, temperature): """ Set new target temperature """ - self.device.set_temperature(self._name,temperature) - - - def turn_away_mode_on(self): - """ Turns away on. """ - self.structure.away = True - - def turn_away_mode_off(self): - """ Turns away off. """ - self.structure.away = False - - # @property - # def min_temp(self): - # """ Identifies min_temp in Nest API or defaults if not available. """ - # temp = self.device.away_temperature.low - # if temp is None: - # return super().min_temp - # else: - # return temp - - # @property - # def max_temp(self): - # """ Identifies mxn_temp in Nest API or defaults if not available. """ - # temp = self.device.away_temperature.high - # if temp is None: - # return super().max_temp - # else: - # return temp + self.device.set_temperature(self._name, temperature) @property def should_poll(self): @@ -163,8 +92,7 @@ class RoundThermostat(ThermostatDevice): def update(self): for dev in self.device.temperatures(force_refresh=True): - self._current_temperature=dev['temp'] - self._target_temperature=dev['setpoint'] - self._name=dev['name'] - self._sensorid=dev['id'] - + self._current_temperature = dev['temp'] + self._target_temperature = dev['setpoint'] + self._name = dev['name'] + self._sensorid = dev['id'] From cae8932b180caef8039125747ed5ad07e95880a4 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 21:23:33 +0100 Subject: [PATCH 039/165] Z-Wave workaround - Connected to the timeout to the configured node value "9. Turn Off Light Time" --- homeassistant/components/sensor/zwave.py | 24 +++++++++++++++++------- homeassistant/components/zwave.py | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index da5c8839d58..1880efccd57 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -47,11 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Check workaround mappings for specific devices if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: - add_devices([ZWaveTriggerSensor(value, hass)]) - return + re_arm_time = (zwave.get_config_value(node, 9) * 8) + add_devices([ZWaveTriggerSensor(value, hass, re_arm_time)]) - # generic Device mappings - if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: + # generic Device mappings + elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value)]) elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: @@ -131,12 +131,22 @@ class ZWaveBinarySensor(ZWaveSensor): class ZWaveTriggerSensor(ZWaveSensor): - """ Represents a stateless sensor which triggers events within Z-Wave. """ + """ + Represents a stateless sensor which + triggers events just 'On' within Z-Wave. + """ - def __init__(self, sensor_value, hass): + def __init__(self, sensor_value, hass, re_arm_sec=60): + """ + :param sensor_value: The z-wave node + :param hass: + :param re_arm_sec: Set state to Off re_arm_sec after the last On event + :return: + """ super(ZWaveTriggerSensor, self).__init__(sensor_value) self._hass = hass self.invalidate_after = None + self.re_arm_sec = re_arm_sec def value_changed(self, value): """ Called when a value has changed on the network. """ @@ -145,7 +155,7 @@ class ZWaveTriggerSensor(ZWaveSensor): if value.data: # only allow this value to be true for 60 secs self.invalidate_after = dt_util.utcnow() + datetime.timedelta( - seconds=60) + seconds=self.re_arm_sec) track_point_in_time( self._hass, self.update_ha_state, self.invalidate_after) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 86d65d1c42e..755908ad2d4 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -58,6 +58,15 @@ def nice_print_node(node): print("\n\n\n") +def get_config_value(node, value_index): + """ Returns the current config value for a specific index """ + + for _, value in node.values.items(): + # 112 == config command class + if value.command_class == 112 and value.index == value_index: + return value.data + + def setup(hass, config): """ Setup Z-wave. From c4261ae2e00d2f70de6a9019993db2e357985b68 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 23:03:40 +0100 Subject: [PATCH 040/165] Z-Wave workaround - Added a default value if we did not get any config value. --- homeassistant/components/sensor/zwave.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/zwave.py b/homeassistant/components/sensor/zwave.py index 1880efccd57..0cfc0682454 100644 --- a/homeassistant/components/sensor/zwave.py +++ b/homeassistant/components/sensor/zwave.py @@ -47,8 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Check workaround mappings for specific devices if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: - re_arm_time = (zwave.get_config_value(node, 9) * 8) - add_devices([ZWaveTriggerSensor(value, hass, re_arm_time)]) + # Default the multiplier to 4 + re_arm_multiplier = (zwave.get_config_value(value.node, 9) or 4) + add_devices([ + ZWaveTriggerSensor(value, hass, re_arm_multiplier * 8) + ]) # generic Device mappings elif value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: @@ -145,7 +148,7 @@ class ZWaveTriggerSensor(ZWaveSensor): """ super(ZWaveTriggerSensor, self).__init__(sensor_value) self._hass = hass - self.invalidate_after = None + self.invalidate_after = dt_util.utcnow() self.re_arm_sec = re_arm_sec def value_changed(self, value): From ec732becfce6eb68b666997cb68ab7de0131d9b6 Mon Sep 17 00:00:00 2001 From: Stefan Jonasson Date: Sat, 31 Oct 2015 23:34:19 +0100 Subject: [PATCH 041/165] Fixed the get_config_value method when the zwave node was changed while reading it. --- homeassistant/components/zwave.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zwave.py b/homeassistant/components/zwave.py index 755908ad2d4..9f7df64312d 100644 --- a/homeassistant/components/zwave.py +++ b/homeassistant/components/zwave.py @@ -61,10 +61,15 @@ def nice_print_node(node): def get_config_value(node, value_index): """ Returns the current config value for a specific index """ - for _, value in node.values.items(): - # 112 == config command class - if value.command_class == 112 and value.index == value_index: - return value.data + try: + for value in node.values.values(): + # 112 == config command class + if value.command_class == 112 and value.index == value_index: + return value.data + except RuntimeError: + # If we get an runtime error the dict has changed while + # we was looking for a value, just do it again + return get_config_value(node, value_index) def setup(hass, config): From 82aec895a0cec4245122bd704c5babf438d1b52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 12:40:41 +0100 Subject: [PATCH 042/165] Minor bug in rfxtrx --- homeassistant/components/sensor/rfxtrx.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 07912b719d2..0ecaaf839c9 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -11,12 +11,14 @@ from collections import OrderedDict from homeassistant.const import (TEMP_CELCIUS) from homeassistant.helpers.entity import Entity -import homeassistant.components.rfxtrx as rfxtrx from RFXtrx import SensorEvent from homeassistant.util import slugify DEPENDENCIES = ['rfxtrx'] +import homeassistant.components.rfxtrx as rfxtrx + + DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), ('Humidity', '%'), From 92b05389f210f9db343f6c7dd5840ca6d067f2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 12:41:21 +0100 Subject: [PATCH 043/165] Update rfxtrx.py --- homeassistant/components/sensor/rfxtrx.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 0ecaaf839c9..217e2066dda 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -18,7 +18,6 @@ DEPENDENCIES = ['rfxtrx'] import homeassistant.components.rfxtrx as rfxtrx - DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), ('Humidity', '%'), From 77539a5b892ebbb3cf56408dfc7e8ffd59b893c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 12:51:09 +0100 Subject: [PATCH 044/165] revert prev commit --- homeassistant/components/sensor/rfxtrx.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 217e2066dda..40f79c97c42 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -12,12 +12,11 @@ from collections import OrderedDict from homeassistant.const import (TEMP_CELCIUS) from homeassistant.helpers.entity import Entity from RFXtrx import SensorEvent +import homeassistant.components.rfxtrx as rfxtrx from homeassistant.util import slugify DEPENDENCIES = ['rfxtrx'] -import homeassistant.components.rfxtrx as rfxtrx - DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), ('Humidity', '%'), From 0ff6a460c2d6871c95c30f9a84b48b8835d208cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 1 Nov 2015 14:20:11 +0100 Subject: [PATCH 045/165] Update rfxtrx.py --- homeassistant/components/sensor/rfxtrx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 40f79c97c42..220d4ddbca7 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): def sensor_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ - if isinstance(event.device, SensorEvent): + if isinstance(event, SensorEvent): entity_id = slugify(event.device.id_string.lower()) # Add entity if not exist and the automatic_add is True From 728cd8bb5e65896deb8c291dc83c658574097d3b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 Nov 2015 00:02:17 -0800 Subject: [PATCH 046/165] Upgrade Vincenty to latest version --- requirements.txt | 1 + requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1b7d2396971..1bcfe79c2d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ requests>=2,<3 pyyaml>=3.11,<4 pytz>=2015.4 pip>=7.0.0 +vincenty==0.1.3 diff --git a/requirements_all.txt b/requirements_all.txt index 6b5ba8837ce..74fb53357c0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -3,7 +3,7 @@ requests>=2,<3 pyyaml>=3.11,<4 pytz>=2015.4 pip>=7.0.0 -vincenty==0.1.2 +vincenty==0.1.3 # Optional, needed for specific components From c6b5a043124c48a5106a35ae5b950cfc19bc8bcd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 Nov 2015 00:03:44 -0800 Subject: [PATCH 047/165] Allow more static files to be fingerprinted --- homeassistant/components/frontend/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index b327e510cd8..a15244ac52f 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -25,6 +25,8 @@ FRONTEND_URLS = [ '/devEvent'] STATES_URL = re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)') +_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE) + def setup(hass, config): """ Setup serving the frontend. """ @@ -80,9 +82,10 @@ def _handle_get_static(handler, path_match, data): """ Returns a static file for the frontend. """ req_file = util.sanitize_path(path_match.group('file')) - # Strip md5 hash out of frontend filename - if re.match(r'^frontend-[A-Za-z0-9]{32}\.html$', req_file): - req_file = "frontend.html" + # Strip md5 hash out + fingerprinted = _FINGERPRINT.match(req_file) + if fingerprinted: + req_file = "{}.{}".format(*fingerprinted.groups()) path = os.path.join(os.path.dirname(__file__), 'www_static', req_file) From 168eb8e5a2b3ae12dc90774374684684463ac951 Mon Sep 17 00:00:00 2001 From: hexxter Date: Mon, 2 Nov 2015 17:02:34 +0100 Subject: [PATCH 048/165] mqtt light test is working more test should be written --- tests/components/light/__init__.py | 0 tests/components/light/test_mqtt.py | 14 +++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 tests/components/light/__init__.py diff --git a/tests/components/light/__init__.py b/tests/components/light/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 28c0e75e256..98c3615aeba 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -64,7 +64,7 @@ class TestLightMQTT(unittest.TestCase): def test_controlling_state_via_topic(self): self.assertTrue(light.setup(self.hass, { - 'switch': { + 'light': { 'platform': 'mqtt', 'name': 'test', 'state_topic': 'test_light_rgb/status', @@ -82,21 +82,21 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - fire_mqtt_message(self.hass, 'test', 'on') + fire_mqtt_message(self.hass, 'test_light_rgb/status', 'on') self.hass.pool.block_till_done() state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) - fire_mqtt_message(self.hass, 'test', 'off') + fire_mqtt_message(self.hass, 'test_light_rgb/status', 'off') self.hass.pool.block_till_done() state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - + def test_sending_mqtt_commands_and_optimistic(self): self.assertTrue(light.setup(self.hass, { - 'switch': { + 'light': { 'platform': 'mqtt', 'name': 'test', 'command_topic': 'test_light_rgb/set', @@ -113,7 +113,7 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - switch.turn_on(self.hass, 'light.test') + light.turn_on(self.hass, 'light.test') self.hass.pool.block_till_done() self.assertEqual(('test_light_rgb/set', 'on', 2), @@ -121,7 +121,7 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) - switch.turn_off(self.hass, 'light.test') + light.turn_off(self.hass, 'light.test') self.hass.pool.block_till_done() self.assertEqual(('test_light_rgb/set', 'off', 2), From 186f68cce30a1f0200b632aa21ee2d75269ad7a1 Mon Sep 17 00:00:00 2001 From: hexxter Date: Mon, 2 Nov 2015 20:16:36 +0100 Subject: [PATCH 049/165] not working mqtt light unittest --- tests/components/light/test_mqtt.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 98c3615aeba..0d97b61441c 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -93,6 +93,13 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test_light_rgb/brightness/status', '100') + self.hass.pool.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual(100, + light_state.attributes) def test_sending_mqtt_commands_and_optimistic(self): self.assertTrue(light.setup(self.hass, { From 218a05356a44c0d24ed32351f6d921ac908db6cf Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 30 Oct 2015 10:54:04 +0100 Subject: [PATCH 050/165] Add docstrings --- homeassistant/components/sensor/cpuspeed.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/sensor/cpuspeed.py b/homeassistant/components/sensor/cpuspeed.py index 7f03e21c7a8..ed5d57597d9 100644 --- a/homeassistant/components/sensor/cpuspeed.py +++ b/homeassistant/components/sensor/cpuspeed.py @@ -47,6 +47,7 @@ class CpuSpeedSensor(Entity): @property def name(self): + """ The name of the sensor. """ return self._name @property @@ -56,6 +57,7 @@ class CpuSpeedSensor(Entity): @property def unit_of_measurement(self): + """ Unit the value is expressed in. """ return self._unit_of_measurement @property From 3cd89f84745bd20ea170198268b0677beab1d4ac Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sun, 1 Nov 2015 10:04:23 -0500 Subject: [PATCH 051/165] add disco, white effects --- homeassistant/components/light/__init__.py | 3 +++ .../components/light/limitlessled.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 3bb2b1ab239..a7865e1f514 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -96,6 +96,7 @@ FLASH_LONG = "long" # Apply an effect to the light, can be EFFECT_COLORLOOP ATTR_EFFECT = "effect" EFFECT_COLORLOOP = "colorloop" +EFFECT_WHITE = "white" LIGHT_PROFILES_FILE = "light_profiles.csv" @@ -282,6 +283,8 @@ def setup(hass, config): if ATTR_EFFECT in dat: if dat[ATTR_EFFECT] == EFFECT_COLORLOOP: params[ATTR_EFFECT] = EFFECT_COLORLOOP + if dat[ATTR_EFFECT] == EFFECT_WHITE: + params[ATTR_EFFECT] = EFFECT_WHITE for light in target_lights: light.turn_on(**params) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index b35ed379047..be9a59ede89 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -18,7 +18,8 @@ import logging from homeassistant.const import DEVICE_DEFAULT_NAME from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, - ATTR_XY_COLOR) + ATTR_XY_COLOR, ATTR_EFFECT, + EFFECT_COLORLOOP, EFFECT_WHITE) from homeassistant.util.color import color_RGB_to_xy _LOGGER = logging.getLogger(__name__) @@ -159,10 +160,22 @@ class RGBWLimitlessLED(LimitlessLED): if ATTR_XY_COLOR in kwargs: self._xy_color = kwargs[ATTR_XY_COLOR] - self.pool.execute(self.controller_id, "set_color", - self._xy_to_led_color(self._xy_color), self.group) + effect = kwargs.get(ATTR_EFFECT) + + if effect: + if effect == EFFECT_COLORLOOP: + self.pool.execute(self.controller_id, "disco", self.group) + if effect == EFFECT_WHITE: + self.pool.execute(self.controller_id, "white", self.group) + else: + self.pool.execute(self.controller_id, "set_color", + self._xy_to_led_color(self._xy_color), + self.group) + + # Brightness can be set independently of color self.pool.execute(self.controller_id, "set_brightness", self._brightness / 255.0, self.group) + self.update_ha_state() From 566712023dac96ebb88d95b8a123822a3f82a09d Mon Sep 17 00:00:00 2001 From: happyleaves Date: Mon, 2 Nov 2015 17:24:24 -0500 Subject: [PATCH 052/165] consolidate conditionals --- homeassistant/components/light/limitlessled.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index be9a59ede89..fef6cd0b8b1 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -162,11 +162,10 @@ class RGBWLimitlessLED(LimitlessLED): effect = kwargs.get(ATTR_EFFECT) - if effect: - if effect == EFFECT_COLORLOOP: - self.pool.execute(self.controller_id, "disco", self.group) - if effect == EFFECT_WHITE: - self.pool.execute(self.controller_id, "white", self.group) + if effect == EFFECT_COLORLOOP: + self.pool.execute(self.controller_id, "disco", self.group) + if effect == EFFECT_WHITE: + self.pool.execute(self.controller_id, "white", self.group) else: self.pool.execute(self.controller_id, "set_color", self._xy_to_led_color(self._xy_color), From 4d958c6d18e6016979b44cfe3ef72a6996c60100 Mon Sep 17 00:00:00 2001 From: happyleaves Date: Mon, 2 Nov 2015 17:51:17 -0500 Subject: [PATCH 053/165] style fix --- homeassistant/components/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index a7865e1f514..ab39474c093 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -157,7 +157,7 @@ def turn_off(hass, entity_id=None, transition=None): hass.services.call(DOMAIN, SERVICE_TURN_OFF, data) -# pylint: disable=too-many-branches, too-many-locals +# pylint: disable=too-many-branches, too-many-locals, too-many-statements def setup(hass, config): """ Exposes light control via statemachine and services. """ From 7b968f6a6b6b289a2d7f8b3823565d74341f7d09 Mon Sep 17 00:00:00 2001 From: happyleaves Date: Mon, 2 Nov 2015 18:11:58 -0500 Subject: [PATCH 054/165] re-fix conditionals --- homeassistant/components/light/limitlessled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index fef6cd0b8b1..0e2ce2230ac 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -164,7 +164,7 @@ class RGBWLimitlessLED(LimitlessLED): if effect == EFFECT_COLORLOOP: self.pool.execute(self.controller_id, "disco", self.group) - if effect == EFFECT_WHITE: + elif effect == EFFECT_WHITE: self.pool.execute(self.controller_id, "white", self.group) else: self.pool.execute(self.controller_id, "set_color", From 5fce381b893754e7a99c84dfe6647fbc8a52e419 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 3 Nov 2015 08:50:27 +0100 Subject: [PATCH 055/165] Remove empty point --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fed012402c..e1528e393bd 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat Examples of devices it can interface it: * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT - * - * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors + * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv)) * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) * Interaction with [IFTTT](https://ifttt.com/) From 72b4212b19b32b3afa399ba087497ca9096b7061 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:19:28 -0800 Subject: [PATCH 056/165] Demo uses device tracker demo platform --- homeassistant/components/demo.py | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 388a869ae0c..7c873e834bd 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -10,14 +10,15 @@ import homeassistant.core as ha import homeassistant.bootstrap as bootstrap import homeassistant.loader as loader from homeassistant.const import ( - CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME) + CONF_PLATFORM, ATTR_ENTITY_ID) DOMAIN = "demo" -DEPENDENCIES = ['introduction', 'conversation'] +DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ - 'switch', 'light', 'sensor', 'thermostat', 'media_player', 'notify'] + 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', + 'thermostat'] def setup(hass, config): @@ -110,25 +111,6 @@ def setup(hass, config): }}, ]}) - # Setup fake device tracker - hass.states.set("device_tracker.paulus", "home", - {ATTR_ENTITY_PICTURE: - "http://graph.facebook.com/297400035/picture", - ATTR_FRIENDLY_NAME: 'Paulus'}) - hass.states.set("device_tracker.anne_therese", "not_home", - {ATTR_FRIENDLY_NAME: 'Anne Therese', - 'latitude': hass.config.latitude + 0.002, - 'longitude': hass.config.longitude + 0.002}) - - hass.states.set("group.all_devices", "home", - { - "auto": True, - ATTR_ENTITY_ID: [ - "device_tracker.paulus", - "device_tracker.anne_therese" - ] - }) - # Setup configurator configurator_ids = [] From 77f4fc8c2228e5acdb579161307dee181eacbe2f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:20:20 -0800 Subject: [PATCH 057/165] Frontend: Add materialdesignicons --- homeassistant/components/frontend/__init__.py | 3 +- .../components/frontend/index.html.template | 2 +- .../components/frontend/mdi_version.py | 2 + .../components/frontend/www_static/mdi.html | 1 + script/update_mdi.py | 92 +++++++++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/frontend/mdi_version.py create mode 100644 homeassistant/components/frontend/www_static/mdi.html create mode 100755 script/update_mdi.py diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index a15244ac52f..d51a7623767 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -8,7 +8,7 @@ import re import os import logging -from . import version +from . import version, mdi_version import homeassistant.util as util from homeassistant.const import URL_ROOT, HTTP_OK from homeassistant.config import get_default_config_dir @@ -74,6 +74,7 @@ def _handle_get_root(handler, path_match, data): template_html = template_html.replace('{{ app_url }}', app_url) template_html = template_html.replace('{{ auth }}', auth) + template_html = template_html.replace('{{ icons }}', mdi_version.VERSION) handler.wfile.write(template_html.encode("UTF-8")) diff --git a/homeassistant/components/frontend/index.html.template b/homeassistant/components/frontend/index.html.template index 8906e8902a0..409ea6752db 100644 --- a/homeassistant/components/frontend/index.html.template +++ b/homeassistant/components/frontend/index.html.template @@ -46,6 +46,6 @@ - + diff --git a/homeassistant/components/frontend/mdi_version.py b/homeassistant/components/frontend/mdi_version.py new file mode 100644 index 00000000000..c9d06a4b300 --- /dev/null +++ b/homeassistant/components/frontend/mdi_version.py @@ -0,0 +1,2 @@ +""" DO NOT MODIFY. Auto-generated by update_mdi script """ +VERSION = "38EF63D0474411E4B3CF842B2B6CFE1B" diff --git a/homeassistant/components/frontend/www_static/mdi.html b/homeassistant/components/frontend/www_static/mdi.html new file mode 100644 index 00000000000..42212a3a301 --- /dev/null +++ b/homeassistant/components/frontend/www_static/mdi.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/script/update_mdi.py b/script/update_mdi.py new file mode 100755 index 00000000000..f7899be3964 --- /dev/null +++ b/script/update_mdi.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Downloads the latest Polymer v1 iconset version for materialdesignicons.com +""" + +import os +import re +import requests +import sys + +GETTING_STARTED_URL = ('https://raw.githubusercontent.com/Templarian/' + 'MaterialDesign/master/site/getting-started.savvy') +DOWNLOAD_LINK = re.compile(r'(/api/download/polymer/v1/([A-Z0-9-]{36}))') +START_ICONSET = ' Date: Tue, 3 Nov 2015 00:20:48 -0800 Subject: [PATCH 058/165] Add icon support to entity --- homeassistant/components/switch/demo.py | 12 +++++++++--- homeassistant/components/zone.py | 16 ++++++++-------- homeassistant/const.py | 3 +++ homeassistant/helpers/entity.py | 10 +++++++++- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/switch/demo.py b/homeassistant/components/switch/demo.py index 4ee1dc82413..16676cb239e 100644 --- a/homeassistant/components/switch/demo.py +++ b/homeassistant/components/switch/demo.py @@ -12,16 +12,17 @@ from homeassistant.const import DEVICE_DEFAULT_NAME def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return demo switches. """ add_devices_callback([ - DemoSwitch('Decorative Lights', True), - DemoSwitch('AC', False) + DemoSwitch('Decorative Lights', True, None), + DemoSwitch('AC', False, 'mdi:air-conditioner') ]) class DemoSwitch(SwitchDevice): """ Provides a demo switch. """ - def __init__(self, name, state): + def __init__(self, name, state, icon): self._name = name or DEVICE_DEFAULT_NAME self._state = state + self._icon = icon @property def should_poll(self): @@ -33,6 +34,11 @@ class DemoSwitch(SwitchDevice): """ Returns the name of the device if any. """ return self._name + @property + def icon(self): + """ Returns the icon to use for device if any. """ + return self._icon + @property def current_power_mwh(self): """ Current power usage in mwh. """ diff --git a/homeassistant/components/zone.py b/homeassistant/components/zone.py index 9ec7829316d..e978d963c5a 100644 --- a/homeassistant/components/zone.py +++ b/homeassistant/components/zone.py @@ -9,7 +9,7 @@ https://home-assistant.io/components/zone.html import logging from homeassistant.const import ( - ATTR_HIDDEN, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_NAME) + ATTR_HIDDEN, ATTR_ICON, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_NAME) from homeassistant.helpers import extract_domain_configs, generate_entity_id from homeassistant.helpers.entity import Entity from homeassistant.util.location import distance @@ -25,8 +25,7 @@ DEFAULT_NAME = 'Unnamed zone' ATTR_RADIUS = 'radius' DEFAULT_RADIUS = 100 -ATTR_ICON = 'icon' -ICON_HOME = 'home' +ICON_HOME = 'mdi:home' def active_zone(hass, latitude, longitude, radius=0): @@ -110,7 +109,7 @@ class Zone(Entity): self.latitude = latitude self.longitude = longitude self.radius = radius - self.icon = icon + self._icon = icon def should_poll(self): return False @@ -124,14 +123,15 @@ class Zone(Entity): """ The state property really does nothing for a zone. """ return STATE + @property + def icon(self): + return self._icon + @property def state_attributes(self): - attr = { + return { ATTR_HIDDEN: True, ATTR_LATITUDE: self.latitude, ATTR_LONGITUDE: self.longitude, ATTR_RADIUS: self.radius, } - if self.icon: - attr[ATTR_ICON] = self.icon - return attr diff --git a/homeassistant/const.py b/homeassistant/const.py index 49825cee094..7762f4acc6a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -74,6 +74,9 @@ ATTR_FRIENDLY_NAME = "friendly_name" # A picture to represent entity ATTR_ENTITY_PICTURE = "entity_picture" +# Icon to use in the frontend +ATTR_ICON = "icon" + # The unit of measurement if applicable ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement" diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 82dafac5576..fd2611889c9 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -10,7 +10,7 @@ from collections import defaultdict from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.const import ( - ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_UNIT_OF_MEASUREMENT, + ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_UNIT_OF_MEASUREMENT, ATTR_ICON, DEVICE_DEFAULT_NAME, STATE_ON, STATE_OFF, STATE_UNKNOWN, TEMP_CELCIUS, TEMP_FAHRENHEIT) @@ -61,6 +61,11 @@ class Entity(object): """ Unit of measurement of this entity, if any. """ return None + @property + def icon(self): + """ Icon to use in the frontend, if any. """ + return None + @property def hidden(self): """ Suggestion if the entity should be hidden from UIs. """ @@ -102,6 +107,9 @@ class Entity(object): if ATTR_UNIT_OF_MEASUREMENT not in attr and self.unit_of_measurement: attr[ATTR_UNIT_OF_MEASUREMENT] = self.unit_of_measurement + if ATTR_ICON not in attr and self.icon: + attr[ATTR_ICON] = self.icon + if self.hidden: attr[ATTR_HIDDEN] = self.hidden From be6dd2023617603495f654299820c9d2cfd7af81 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:20:59 -0800 Subject: [PATCH 059/165] Update frontend with new icons --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 1546 +++++++++-------- .../www_static/home-assistant-polymer | 2 +- 3 files changed, 784 insertions(+), 766 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 1c753d1638e..1012508d53f 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "beb922c55bb26ea576581b453f6d7c04" +VERSION = "5e61b80689feebb3a7043de07fc01971" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 7343bd3afd0..5d68c878b66 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,6 @@ -