diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index a28b6285bdd..e0464533a1b 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -10,11 +10,12 @@ import voluptuous as vol import homeassistant.components.mqtt as mqtt from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_RGB_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR, - Light) + ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_COLOR_TEMP, SUPPORT_BRIGHTNESS, + SUPPORT_RGB_COLOR, SUPPORT_COLOR_TEMP, Light) from homeassistant.const import ( CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE, CONF_PAYLOAD_OFF, - CONF_PAYLOAD_ON, CONF_STATE, CONF_BRIGHTNESS, CONF_RGB) + CONF_PAYLOAD_ON, CONF_STATE, CONF_BRIGHTNESS, CONF_RGB, + CONF_COLOR_TEMP) from homeassistant.components.mqtt import ( CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN) import homeassistant.helpers.config_validation as cv @@ -31,6 +32,9 @@ CONF_RGB_STATE_TOPIC = 'rgb_state_topic' CONF_RGB_COMMAND_TOPIC = 'rgb_command_topic' CONF_RGB_VALUE_TEMPLATE = 'rgb_value_template' CONF_BRIGHTNESS_SCALE = 'brightness_scale' +CONF_COLOR_TEMP_STATE_TOPIC = 'color_temp_state_topic' +CONF_COLOR_TEMP_COMMAND_TOPIC = 'color_temp_command_topic' +CONF_COLOR_TEMP_VALUE_TEMPLATE = 'color_temp_value_template' DEFAULT_NAME = 'MQTT Light' DEFAULT_PAYLOAD_ON = 'ON' @@ -44,6 +48,9 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_BRIGHTNESS_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_BRIGHTNESS_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_RGB_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_RGB_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_RGB_VALUE_TEMPLATE): cv.template, @@ -70,12 +77,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None): CONF_BRIGHTNESS_COMMAND_TOPIC, CONF_RGB_STATE_TOPIC, CONF_RGB_COMMAND_TOPIC, + CONF_COLOR_TEMP_STATE_TOPIC, + CONF_COLOR_TEMP_COMMAND_TOPIC ) }, { CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), CONF_BRIGHTNESS: config.get(CONF_BRIGHTNESS_VALUE_TEMPLATE), - CONF_RGB: config.get(CONF_RGB_VALUE_TEMPLATE) + CONF_RGB: config.get(CONF_RGB_VALUE_TEMPLATE), + CONF_COLOR_TEMP: config.get(CONF_COLOR_TEMP_VALUE_TEMPLATE) }, config.get(CONF_QOS), config.get(CONF_RETAIN), @@ -92,6 +102,7 @@ class MqttLight(Light): """MQTT light.""" # pylint: disable=too-many-arguments,too-many-instance-attributes + # pylint: disable=too-many-locals,too-many-branches def __init__(self, hass, name, topic, templates, qos, retain, payload, optimistic, brightness_scale): """Initialize MQTT light.""" @@ -106,6 +117,8 @@ class MqttLight(Light): optimistic or topic[CONF_RGB_STATE_TOPIC] is None self._optimistic_brightness = ( optimistic or topic[CONF_BRIGHTNESS_STATE_TOPIC] is None) + self._optimistic_color_temp = ( + optimistic or topic[CONF_COLOR_TEMP_STATE_TOPIC] is None) self._brightness_scale = brightness_scale self._state = False self._supported_features = 0 @@ -114,6 +127,9 @@ class MqttLight(Light): self._supported_features |= ( topic[CONF_BRIGHTNESS_STATE_TOPIC] is not None and SUPPORT_BRIGHTNESS) + self._supported_features |= ( + topic[CONF_COLOR_TEMP_STATE_TOPIC] is not None and + SUPPORT_COLOR_TEMP) for key, tpl in list(templates.items()): if tpl is None: @@ -168,6 +184,21 @@ class MqttLight(Light): else: self._rgb = None + def color_temp_received(topic, payload, qos): + """A new MQTT message for color temp has been received.""" + self._color_temp = int(templates[CONF_COLOR_TEMP](payload)) + self.update_ha_state() + + if self._topic[CONF_COLOR_TEMP_STATE_TOPIC] is not None: + mqtt.subscribe( + self._hass, self._topic[CONF_COLOR_TEMP_STATE_TOPIC], + color_temp_received, self._qos) + self._color_temp = 150 + if self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None: + self._color_temp = 150 + else: + self._color_temp = None + @property def brightness(self): """Return the brightness of this light between 0..255.""" @@ -178,6 +209,11 @@ class MqttLight(Light): """Return the RGB color value.""" return self._rgb + @property + def color_temp(self): + """Return the color temperature in mired.""" + return self._color_temp + @property def should_poll(self): """No polling needed for a MQTT light.""" @@ -230,6 +266,16 @@ class MqttLight(Light): self._brightness = kwargs[ATTR_BRIGHTNESS] should_update = True + if ATTR_COLOR_TEMP in kwargs and \ + self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None: + color_temp = int(kwargs[ATTR_COLOR_TEMP]) + mqtt.publish( + self._hass, self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC], + color_temp, self._qos, self._retain) + if self._optimistic_color_temp: + self._color_temp = kwargs[ATTR_COLOR_TEMP] + should_update = True + mqtt.publish(self._hass, self._topic[CONF_COMMAND_TOPIC], self._payload['on'], self._qos, self._retain) diff --git a/homeassistant/const.py b/homeassistant/const.py index c50a9b7dc4f..b8cd3a83353 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -63,6 +63,7 @@ CONF_BELOW = 'below' CONF_BLACKLIST = 'blacklist' CONF_BRIGHTNESS = 'brightness' CONF_CODE = 'code' +CONF_COLOR_TEMP = 'color_temp' CONF_COMMAND = 'command' CONF_COMMAND_CLOSE = 'command_close' CONF_COMMAND_OFF = 'command_off' diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index c6fc07fa438..375a4a45905 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -55,6 +55,23 @@ light: qos: 0 payload_on: "on" payload_off: "off" + +config with brightness and color temp + +light: + platform: mqtt + name: "Office Light Color Temp" + 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" + brightness_scale: 99 + color_temp_state_topic: "office/rgb1/color_temp/status" + color_temp_command_topic: "office/rgb1/color_temp/set" + qos: 0 + payload_on: "on" + payload_off: "off" + """ import unittest @@ -88,7 +105,7 @@ class TestLightMQTT(unittest.TestCase): }) self.assertIsNone(self.hass.states.get('light.test')) - def test_no_color_or_brightness_if_no_topics(self): + def test_no_color_or_brightness_or_color_temp_if_no_topics(self): """Test if there is no color and brightness if no topic.""" self.hass.config.components = ['mqtt'] assert _setup_component(self.hass, light.DOMAIN, { @@ -104,6 +121,7 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual(STATE_OFF, state.state) self.assertIsNone(state.attributes.get('rgb_color')) self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) fire_mqtt_message(self.hass, 'test_light_rgb/status', 'ON') self.hass.block_till_done() @@ -112,6 +130,7 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual(STATE_ON, state.state) self.assertIsNone(state.attributes.get('rgb_color')) self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) def test_controlling_state_via_topic(self): """Test the controlling of the state via topic.""" @@ -126,6 +145,8 @@ class TestLightMQTT(unittest.TestCase): 'brightness_command_topic': 'test_light_rgb/brightness/set', 'rgb_state_topic': 'test_light_rgb/rgb/status', 'rgb_command_topic': 'test_light_rgb/rgb/set', + 'color_temp_state_topic': 'test_light_rgb/color_temp/status', + 'color_temp_command_topic': 'test_light_rgb/color_temp/set', 'qos': '0', 'payload_on': 1, 'payload_off': 0 @@ -136,6 +157,7 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual(STATE_OFF, state.state) self.assertIsNone(state.attributes.get('rgb_color')) self.assertIsNone(state.attributes.get('brightness')) + self.assertIsNone(state.attributes.get('color_temp')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) fire_mqtt_message(self.hass, 'test_light_rgb/status', '1') @@ -145,6 +167,7 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual(STATE_ON, state.state) self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) self.assertEqual(255, state.attributes.get('brightness')) + self.assertEqual(150, state.attributes.get('color_temp')) fire_mqtt_message(self.hass, 'test_light_rgb/status', '0') self.hass.block_till_done() @@ -163,6 +186,12 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual(100, light_state.attributes['brightness']) + fire_mqtt_message(self.hass, 'test_light_rgb/color_temp/status', '300') + self.hass.block_till_done() + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(300, light_state.attributes['color_temp']) + fire_mqtt_message(self.hass, 'test_light_rgb/status', '1') self.hass.block_till_done() @@ -231,9 +260,11 @@ class TestLightMQTT(unittest.TestCase): 'state_topic': 'test_light_rgb/status', 'command_topic': 'test_light_rgb/set', 'brightness_state_topic': 'test_light_rgb/brightness/status', + 'color_temp_state_topic': 'test_light_rgb/color_temp/status', 'rgb_state_topic': 'test_light_rgb/rgb/status', 'state_value_template': '{{ value_json.hello }}', 'brightness_value_template': '{{ value_json.hello }}', + 'color_temp_value_template': '{{ value_json.hello }}', 'rgb_value_template': '{{ value_json.hello | join(",") }}', } }) @@ -249,12 +280,15 @@ class TestLightMQTT(unittest.TestCase): '{"hello": "ON"}') fire_mqtt_message(self.hass, 'test_light_rgb/brightness/status', '{"hello": "50"}') + fire_mqtt_message(self.hass, 'test_light_rgb/color_temp/status', + '{"hello": "300"}') self.hass.block_till_done() state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual(50, state.attributes.get('brightness')) self.assertEqual([1, 2, 3], state.attributes.get('rgb_color')) + self.assertEqual(300, state.attributes.get('color_temp')) def test_sending_mqtt_commands_and_optimistic(self): """Test the sending of command in optimistic mode.""" @@ -266,6 +300,7 @@ class TestLightMQTT(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'brightness_command_topic': 'test_light_rgb/brightness/set', 'rgb_command_topic': 'test_light_rgb/rgb/set', + 'color_temp_command_topic': 'test_light_rgb/color_temp/set', 'qos': 2, 'payload_on': 'on', 'payload_off': 'off' @@ -338,3 +373,27 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual(255, state.attributes.get('brightness')) + + def test_show_color_temp_only_if_command_topic(self): + """Test the color temp only if a command topic is present.""" + self.hass.config.components = ['mqtt'] + assert _setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'color_temp_command_topic': 'test_light_rgb/brightness/set', + 'command_topic': 'test_light_rgb/set', + 'state_topic': 'test_light_rgb/status' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('color_temp')) + + fire_mqtt_message(self.hass, 'test_light_rgb/status', 'ON') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(150, state.attributes.get('color_temp'))