From 6c106a87f16d506dc12efb1bb4ec705b760e1f18 Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 15 Oct 2015 15:02:09 +0200 Subject: [PATCH 001/278] 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/278] 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/278] 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/278] 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/278] 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/278] 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/278] 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/278] 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 b66e4f1e155801bb4d3b93b1eed2723c4fdaf7e2 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 26 Oct 2015 15:05:01 +0100 Subject: [PATCH 009/278] 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 010/278] 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 85bb828149ec7f9f5af337777d45ebd007f9d5cb Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 29 Oct 2015 21:17:10 +0100 Subject: [PATCH 011/278] 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 31826ab263212fa6821b933899dad9c9f96aba44 Mon Sep 17 00:00:00 2001 From: hexxter Date: Sat, 31 Oct 2015 19:26:03 +0100 Subject: [PATCH 012/278] 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 013/278] 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 168eb8e5a2b3ae12dc90774374684684463ac951 Mon Sep 17 00:00:00 2001 From: hexxter Date: Mon, 2 Nov 2015 17:02:34 +0100 Subject: [PATCH 014/278] 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 015/278] 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 3cd89f84745bd20ea170198268b0677beab1d4ac Mon Sep 17 00:00:00 2001 From: happyleaves Date: Sun, 1 Nov 2015 10:04:23 -0500 Subject: [PATCH 016/278] 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 017/278] 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 018/278] 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 019/278] 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 72b4212b19b32b3afa399ba087497ca9096b7061 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 Nov 2015 00:19:28 -0800 Subject: [PATCH 020/278] 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 021/278] 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 022/278] 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 023/278] 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 @@ -