diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 3110c2091ad..4740858b5fb 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -12,55 +12,81 @@ import voluptuous as vol from homeassistant.core import callback import homeassistant.components.mqtt as mqtt from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_COLOR_TEMP, SUPPORT_BRIGHTNESS, - SUPPORT_RGB_COLOR, SUPPORT_COLOR_TEMP, Light) + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_RGB_COLOR, + ATTR_WHITE_VALUE, ATTR_XY_COLOR, Light, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_RGB_COLOR, + SUPPORT_WHITE_VALUE, SUPPORT_XY_COLOR) from homeassistant.const import ( - CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE, CONF_PAYLOAD_OFF, - CONF_PAYLOAD_ON, CONF_STATE, CONF_BRIGHTNESS, CONF_RGB, - CONF_COLOR_TEMP) + CONF_BRIGHTNESS, CONF_COLOR_TEMP, CONF_EFFECT, CONF_NAME, + CONF_OPTIMISTIC, CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, + CONF_RGB, CONF_STATE, CONF_VALUE_TEMPLATE, CONF_WHITE_VALUE, CONF_XY) from homeassistant.components.mqtt import ( - CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN) + CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC) import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] -CONF_STATE_VALUE_TEMPLATE = 'state_value_template' -CONF_BRIGHTNESS_STATE_TOPIC = 'brightness_state_topic' CONF_BRIGHTNESS_COMMAND_TOPIC = 'brightness_command_topic' -CONF_BRIGHTNESS_VALUE_TEMPLATE = 'brightness_value_template' -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_BRIGHTNESS_STATE_TOPIC = 'brightness_state_topic' +CONF_BRIGHTNESS_VALUE_TEMPLATE = 'brightness_value_template' CONF_COLOR_TEMP_COMMAND_TOPIC = 'color_temp_command_topic' +CONF_COLOR_TEMP_STATE_TOPIC = 'color_temp_state_topic' CONF_COLOR_TEMP_VALUE_TEMPLATE = 'color_temp_value_template' +CONF_EFFECT_COMMAND_TOPIC = 'effect_command_topic' +CONF_EFFECT_LIST = 'effect_list' +CONF_EFFECT_STATE_TOPIC = 'effect_state_topic' +CONF_EFFECT_VALUE_TEMPLATE = 'effect_value_template' +CONF_RGB_COMMAND_TOPIC = 'rgb_command_topic' +CONF_RGB_STATE_TOPIC = 'rgb_state_topic' +CONF_RGB_VALUE_TEMPLATE = 'rgb_value_template' +CONF_STATE_VALUE_TEMPLATE = 'state_value_template' +CONF_XY_COMMAND_TOPIC = 'xy_command_topic' +CONF_XY_STATE_TOPIC = 'xy_state_topic' +CONF_XY_VALUE_TEMPLATE = 'xy_value_template' +CONF_WHITE_VALUE_COMMAND_TOPIC = 'white_value_command_topic' +CONF_WHITE_VALUE_SCALE = 'white_value_scale' +CONF_WHITE_VALUE_STATE_TOPIC = 'white_value_state_topic' +CONF_WHITE_VALUE_TEMPLATE = 'white_value_template' -DEFAULT_NAME = 'MQTT Light' -DEFAULT_PAYLOAD_ON = 'ON' -DEFAULT_PAYLOAD_OFF = 'OFF' -DEFAULT_OPTIMISTIC = False DEFAULT_BRIGHTNESS_SCALE = 255 +DEFAULT_NAME = 'MQTT Light' +DEFAULT_OPTIMISTIC = False +DEFAULT_PAYLOAD_OFF = 'OFF' +DEFAULT_PAYLOAD_ON = 'ON' +DEFAULT_WHITE_VALUE_SCALE = 255 PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, - 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, - vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, - vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE): vol.All(vol.Coerce(int), vol.Range(min=1)), + vol.Optional(CONF_BRIGHTNESS_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_BRIGHTNESS_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_EFFECT_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_EFFECT_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, + vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, + vol.Optional(CONF_RGB_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_RGB_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_RGB_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_WHITE_VALUE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_WHITE_VALUE_SCALE, default=DEFAULT_WHITE_VALUE_SCALE): + vol.All(vol.Coerce(int), vol.Range(min=1)), + vol.Optional(CONF_WHITE_VALUE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_XY_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_XY_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template, }) @@ -72,23 +98,33 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): async_add_devices([MqttLight( config.get(CONF_NAME), + config.get(CONF_EFFECT_LIST), { key: config.get(key) for key in ( - CONF_STATE_TOPIC, - CONF_COMMAND_TOPIC, - CONF_BRIGHTNESS_STATE_TOPIC, CONF_BRIGHTNESS_COMMAND_TOPIC, - CONF_RGB_STATE_TOPIC, - CONF_RGB_COMMAND_TOPIC, + CONF_BRIGHTNESS_STATE_TOPIC, + CONF_COLOR_TEMP_COMMAND_TOPIC, CONF_COLOR_TEMP_STATE_TOPIC, - CONF_COLOR_TEMP_COMMAND_TOPIC + CONF_COMMAND_TOPIC, + CONF_EFFECT_COMMAND_TOPIC, + CONF_EFFECT_STATE_TOPIC, + CONF_RGB_COMMAND_TOPIC, + CONF_RGB_STATE_TOPIC, + CONF_STATE_TOPIC, + CONF_WHITE_VALUE_COMMAND_TOPIC, + CONF_WHITE_VALUE_STATE_TOPIC, + CONF_XY_COMMAND_TOPIC, + CONF_XY_STATE_TOPIC, ) }, { - CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), CONF_BRIGHTNESS: config.get(CONF_BRIGHTNESS_VALUE_TEMPLATE), + CONF_COLOR_TEMP: config.get(CONF_COLOR_TEMP_VALUE_TEMPLATE), + CONF_EFFECT: config.get(CONF_EFFECT_VALUE_TEMPLATE), CONF_RGB: config.get(CONF_RGB_VALUE_TEMPLATE), - CONF_COLOR_TEMP: config.get(CONF_COLOR_TEMP_VALUE_TEMPLATE) + CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), + CONF_WHITE_VALUE: config.get(CONF_WHITE_VALUE_TEMPLATE), + CONF_XY: config.get(CONF_XY_VALUE_TEMPLATE), }, config.get(CONF_QOS), config.get(CONF_RETAIN), @@ -98,16 +134,19 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): }, config.get(CONF_OPTIMISTIC), config.get(CONF_BRIGHTNESS_SCALE), + config.get(CONF_WHITE_VALUE_SCALE), )]) class MqttLight(Light): """MQTT light.""" - def __init__(self, name, topic, templates, qos, retain, payload, - optimistic, brightness_scale): + def __init__(self, name, effect_list, topic, templates, qos, + retain, payload, optimistic, brightness_scale, + white_value_scale): """Initialize MQTT light.""" self._name = name + self._effect_list = effect_list self._topic = topic self._qos = qos self._retain = retain @@ -120,11 +159,21 @@ class MqttLight(Light): optimistic or topic[CONF_BRIGHTNESS_STATE_TOPIC] is None) self._optimistic_color_temp = ( optimistic or topic[CONF_COLOR_TEMP_STATE_TOPIC] is None) + self._optimistic_effect = ( + optimistic or topic[CONF_EFFECT_STATE_TOPIC] is None) + self._optimistic_white_value = ( + optimistic or topic[CONF_WHITE_VALUE_STATE_TOPIC] is None) + self._optimistic_xy = \ + optimistic or topic[CONF_XY_STATE_TOPIC] is None self._brightness_scale = brightness_scale + self._white_value_scale = white_value_scale self._state = False self._brightness = None self._rgb = None self._color_temp = None + self._effect = None + self._white_value = None + self._xy = None self._supported_features = 0 self._supported_features |= ( topic[CONF_RGB_COMMAND_TOPIC] is not None and SUPPORT_RGB_COLOR) @@ -134,6 +183,14 @@ class MqttLight(Light): self._supported_features |= ( topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None and SUPPORT_COLOR_TEMP) + self._supported_features |= ( + topic[CONF_EFFECT_STATE_TOPIC] is not None and + SUPPORT_EFFECT) + self._supported_features |= ( + topic[CONF_WHITE_VALUE_COMMAND_TOPIC] is not None and + SUPPORT_WHITE_VALUE) + self._supported_features |= ( + topic[CONF_XY_COMMAND_TOPIC] is not None and SUPPORT_XY_COLOR) @asyncio.coroutine def async_added_to_hass(self): @@ -215,6 +272,57 @@ class MqttLight(Light): else: self._color_temp = None + @callback + def effect_received(topic, payload, qos): + """A new MQTT message for effect has been received.""" + self._effect = templates[CONF_EFFECT](payload) + self.hass.async_add_job(self.async_update_ha_state()) + + if self._topic[CONF_EFFECT_STATE_TOPIC] is not None: + yield from mqtt.async_subscribe( + self.hass, self._topic[CONF_EFFECT_STATE_TOPIC], + effect_received, self._qos) + self._effect = 'none' + if self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None: + self._effect = 'none' + else: + self._effect = None + + @callback + def white_value_received(topic, payload, qos): + """A new MQTT message for the white value has been received.""" + device_value = float(templates[CONF_WHITE_VALUE](payload)) + percent_white = device_value / self._white_value_scale + self._white_value = int(percent_white * 255) + self.hass.async_add_job(self.async_update_ha_state()) + + if self._topic[CONF_WHITE_VALUE_STATE_TOPIC] is not None: + yield from mqtt.async_subscribe( + self.hass, self._topic[CONF_WHITE_VALUE_STATE_TOPIC], + white_value_received, self._qos) + self._white_value = 255 + elif self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC] is not None: + self._white_value = 255 + else: + self._white_value = None + + @callback + def xy_received(topic, payload, qos): + """A new MQTT message has been received.""" + self._xy = [float(val) for val in + templates[CONF_XY](payload).split(',')] + self.hass.async_add_job(self.async_update_ha_state()) + + if self._topic[CONF_XY_STATE_TOPIC] is not None: + yield from mqtt.async_subscribe( + self.hass, self._topic[CONF_XY_STATE_TOPIC], xy_received, + self._qos) + self._xy = [1, 1] + if self._topic[CONF_XY_COMMAND_TOPIC] is not None: + self._xy = [1, 1] + else: + self._xy = None + @property def brightness(self): """Return the brightness of this light between 0..255.""" @@ -230,6 +338,16 @@ class MqttLight(Light): """Return the color temperature in mired.""" return self._color_temp + @property + def white_value(self): + """Return the white property.""" + return self._white_value + + @property + def xy_color(self): + """Return the RGB color value.""" + return self._xy + @property def should_poll(self): """No polling needed for a MQTT light.""" @@ -250,6 +368,16 @@ class MqttLight(Light): """Return true if we do optimistic updates.""" return self._optimistic + @property + def effect_list(self): + """Return the list of supported effects.""" + return self._effect_list + + @property + def effect(self): + """Return the current effect.""" + return self._effect + @property def supported_features(self): """Flag supported features.""" @@ -297,6 +425,41 @@ class MqttLight(Light): self._color_temp = kwargs[ATTR_COLOR_TEMP] should_update = True + if ATTR_EFFECT in kwargs and \ + self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None: + effect = kwargs[ATTR_EFFECT] + if effect in self._effect_list: + mqtt.async_publish( + self.hass, self._topic[CONF_EFFECT_COMMAND_TOPIC], + effect, self._qos, self._retain) + if self._optimistic_effect: + self._effect = kwargs[ATTR_EFFECT] + should_update = True + + if ATTR_WHITE_VALUE in kwargs and \ + self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC] is not None: + percent_white = float(kwargs[ATTR_WHITE_VALUE]) / 255 + device_white_value = int(percent_white * self._white_value_scale) + mqtt.async_publish( + self.hass, self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC], + device_white_value, self._qos, self._retain) + + if self._optimistic_white_value: + self._white_value = kwargs[ATTR_WHITE_VALUE] + should_update = True + + if ATTR_XY_COLOR in kwargs and \ + self._topic[CONF_XY_COMMAND_TOPIC] is not None: + + mqtt.async_publish( + self.hass, self._topic[CONF_XY_COMMAND_TOPIC], + '{},{}'.format(*kwargs[ATTR_XY_COLOR]), self._qos, + self._retain) + + if self._optimistic_xy: + self._xy = kwargs[ATTR_XY_COLOR] + should_update = True + mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], self._payload['on'], self._qos, self._retain) diff --git a/homeassistant/components/light/mqtt_json.py b/homeassistant/components/light/mqtt_json.py index b9fb6c54cb4..3ec73db87aa 100755 --- a/homeassistant/components/light/mqtt_json.py +++ b/homeassistant/components/light/mqtt_json.py @@ -12,11 +12,14 @@ import voluptuous as vol from homeassistant.core import callback import homeassistant.components.mqtt as mqtt from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_TRANSITION, PLATFORM_SCHEMA, - ATTR_FLASH, FLASH_LONG, FLASH_SHORT, SUPPORT_BRIGHTNESS, SUPPORT_FLASH, - SUPPORT_RGB_COLOR, SUPPORT_TRANSITION, Light) + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, + ATTR_RGB_COLOR, ATTR_TRANSITION, ATTR_WHITE_VALUE, ATTR_XY_COLOR, + FLASH_LONG, FLASH_SHORT, Light, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_RGB_COLOR, + SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, SUPPORT_XY_COLOR) from homeassistant.const import ( - CONF_NAME, CONF_OPTIMISTIC, CONF_BRIGHTNESS, CONF_RGB) + CONF_BRIGHTNESS, CONF_COLOR_TEMP, CONF_EFFECT, + CONF_NAME, CONF_OPTIMISTIC, CONF_RGB, CONF_WHITE_VALUE, CONF_XY) from homeassistant.components.mqtt import ( CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN) import homeassistant.helpers.config_validation as cv @@ -27,34 +30,42 @@ DOMAIN = 'mqtt_json' DEPENDENCIES = ['mqtt'] +DEFAULT_BRIGHTNESS = False +DEFAULT_COLOR_TEMP = False +DEFAULT_EFFECT = False +DEFAULT_FLASH_TIME_LONG = 10 +DEFAULT_FLASH_TIME_SHORT = 2 DEFAULT_NAME = 'MQTT JSON Light' DEFAULT_OPTIMISTIC = False -DEFAULT_BRIGHTNESS = False DEFAULT_RGB = False -DEFAULT_FLASH_TIME_SHORT = 2 -DEFAULT_FLASH_TIME_LONG = 10 +DEFAULT_WHITE_VALUE = False +DEFAULT_XY = False + +CONF_EFFECT_LIST = 'effect_list' -CONF_FLASH_TIME_SHORT = 'flash_time_short' CONF_FLASH_TIME_LONG = 'flash_time_long' - -SUPPORT_MQTT_JSON = (SUPPORT_BRIGHTNESS | SUPPORT_FLASH | SUPPORT_RGB_COLOR | - SUPPORT_TRANSITION) +CONF_FLASH_TIME_SHORT = 'flash_time_short' # Stealing some of these from the base MQTT configs. PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS): - vol.All(vol.Coerce(int), vol.In([0, 1, 2])), - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_BRIGHTNESS, default=DEFAULT_BRIGHTNESS): cv.boolean, - vol.Optional(CONF_RGB, default=DEFAULT_RGB): cv.boolean, + vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean, + vol.Optional(CONF_EFFECT, default=DEFAULT_EFFECT): cv.boolean, + vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_FLASH_TIME_SHORT, default=DEFAULT_FLASH_TIME_SHORT): cv.positive_int, vol.Optional(CONF_FLASH_TIME_LONG, default=DEFAULT_FLASH_TIME_LONG): - cv.positive_int + cv.positive_int, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS): + vol.All(vol.Coerce(int), vol.In([0, 1, 2])), + vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_RGB, default=DEFAULT_RGB): cv.boolean, + vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_WHITE_VALUE, default=DEFAULT_WHITE_VALUE): cv.boolean, + vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean, + vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, }) @@ -63,6 +74,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Setup a MQTT JSON Light.""" async_add_devices([MqttJson( config.get(CONF_NAME), + config.get(CONF_EFFECT_LIST), { key: config.get(key) for key in ( CONF_STATE_TOPIC, @@ -73,7 +85,11 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): config.get(CONF_RETAIN), config.get(CONF_OPTIMISTIC), config.get(CONF_BRIGHTNESS), + config.get(CONF_COLOR_TEMP), + config.get(CONF_EFFECT), config.get(CONF_RGB), + config.get(CONF_WHITE_VALUE), + config.get(CONF_XY), { key: config.get(key) for key in ( CONF_FLASH_TIME_SHORT, @@ -86,10 +102,12 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): class MqttJson(Light): """Representation of a MQTT JSON light.""" - def __init__(self, name, topic, qos, retain, optimistic, brightness, rgb, + def __init__(self, name, effect_list, topic, qos, retain, optimistic, + brightness, color_temp, effect, rgb, white_value, xy, flash_times): """Initialize MQTT JSON light.""" self._name = name + self._effect_list = effect_list self._topic = topic self._qos = qos self._retain = retain @@ -100,13 +118,45 @@ class MqttJson(Light): else: self._brightness = None + if color_temp: + self._color_temp = 150 + else: + self._color_temp = None + + if effect: + self._effect = 'none' + else: + self._effect = None + if rgb: self._rgb = [0, 0, 0] else: self._rgb = None + if white_value: + self._white_value = 255 + else: + self._white_value = None + + if xy: + self._xy = [1, 1] + else: + self._xy = None + self._flash_times = flash_times + self._supported_features = (SUPPORT_TRANSITION | SUPPORT_FLASH) + self._supported_features |= (rgb is not None and SUPPORT_RGB_COLOR) + self._supported_features |= (brightness is not None and + SUPPORT_BRIGHTNESS) + self._supported_features |= (color_temp is not None and + SUPPORT_COLOR_TEMP) + self._supported_features |= (effect is not None and + SUPPORT_EFFECT) + self._supported_features |= (white_value is not None and + SUPPORT_WHITE_VALUE) + self._supported_features |= (xy is not None and SUPPORT_XY_COLOR) + @asyncio.coroutine def async_added_to_hass(self): """Subscribe mqtt events. @@ -133,7 +183,7 @@ class MqttJson(Light): except KeyError: pass except ValueError: - _LOGGER.warning("Invalid color value received") + _LOGGER.warning("Invalid RGB color value received") if self._brightness is not None: try: @@ -143,6 +193,41 @@ class MqttJson(Light): except ValueError: _LOGGER.warning('Invalid brightness value received') + if self._color_temp is not None: + try: + self._color_temp = int(values['color_temp']) + except KeyError: + pass + except ValueError: + _LOGGER.warning('Invalid color temp value received') + + if self._effect is not None: + try: + self._effect = values['effect'] + except KeyError: + pass + except ValueError: + _LOGGER.warning('Invalid effect value received') + + if self._white_value is not None: + try: + self._white_value = int(values['white_value']) + except KeyError: + pass + except ValueError: + _LOGGER.warning('Invalid white value value received') + + if self._xy is not None: + try: + x_color = float(values['color']['x']) + y_color = float(values['color']['y']) + + self._xy = [x_color, y_color] + except KeyError: + pass + except ValueError: + _LOGGER.warning("Invalid XY color value received") + self.hass.async_add_job(self.async_update_ha_state()) if self._topic[CONF_STATE_TOPIC] is not None: @@ -155,11 +240,36 @@ class MqttJson(Light): """Return the brightness of this light between 0..255.""" return self._brightness + @property + def color_temp(self): + """Return the color temperature in mired.""" + return self._color_temp + + @property + def effect(self): + """Return the current effect.""" + return self._effect + + @property + def effect_list(self): + """Return the list of supported effects.""" + return self._effect_list + @property def rgb_color(self): """Return the RGB color value.""" return self._rgb + @property + def white_value(self): + """Return the white property.""" + return self._white_value + + @property + def xy_color(self): + """Return the XY color value.""" + return self._xy + @property def should_poll(self): """No polling needed for a MQTT light.""" @@ -183,7 +293,7 @@ class MqttJson(Light): @property def supported_features(self): """Flag supported features.""" - return SUPPORT_MQTT_JSON + return self._supported_features @asyncio.coroutine def async_turn_on(self, **kwargs): @@ -224,6 +334,37 @@ class MqttJson(Light): self._brightness = kwargs[ATTR_BRIGHTNESS] should_update = True + if ATTR_COLOR_TEMP in kwargs: + message['color_temp'] = int(kwargs[ATTR_COLOR_TEMP]) + + if self._optimistic: + self._color_temp = kwargs[ATTR_COLOR_TEMP] + should_update = True + + if ATTR_EFFECT in kwargs: + message['effect'] = kwargs[ATTR_EFFECT] + + if self._optimistic: + self._effect = kwargs[ATTR_EFFECT] + should_update = True + + if ATTR_WHITE_VALUE in kwargs: + message['white_value'] = int(kwargs[ATTR_WHITE_VALUE]) + + if self._optimistic: + self._white_value = kwargs[ATTR_WHITE_VALUE] + should_update = True + + if ATTR_XY_COLOR in kwargs: + message['color'] = { + 'x': kwargs[ATTR_XY_COLOR][0], + 'y': kwargs[ATTR_XY_COLOR][1] + } + + if self._optimistic: + self._xy = kwargs[ATTR_XY_COLOR] + should_update = True + mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], json.dumps(message), self._qos, self._retain) diff --git a/homeassistant/components/light/mqtt_template.py b/homeassistant/components/light/mqtt_template.py index 2f240ec12a6..d2d9c243c03 100755 --- a/homeassistant/components/light/mqtt_template.py +++ b/homeassistant/components/light/mqtt_template.py @@ -11,9 +11,10 @@ import voluptuous as vol from homeassistant.core import callback import homeassistant.components.mqtt as mqtt from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_EFFECT, ATTR_FLASH, ATTR_RGB_COLOR, ATTR_TRANSITION, - PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_EFFECT, SUPPORT_FLASH, - SUPPORT_RGB_COLOR, SUPPORT_TRANSITION, Light) + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, + ATTR_RGB_COLOR, ATTR_TRANSITION, ATTR_WHITE_VALUE, Light, PLATFORM_SCHEMA, + SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_FLASH, + SUPPORT_RGB_COLOR, SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE) from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC, STATE_ON, STATE_OFF from homeassistant.components.mqtt import ( CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN) @@ -28,36 +29,37 @@ DEPENDENCIES = ['mqtt'] DEFAULT_NAME = 'MQTT Template Light' DEFAULT_OPTIMISTIC = False -CONF_EFFECT_LIST = "effect_list" -CONF_COMMAND_ON_TEMPLATE = 'command_on_template' -CONF_COMMAND_OFF_TEMPLATE = 'command_off_template' -CONF_STATE_TEMPLATE = 'state_template' -CONF_BRIGHTNESS_TEMPLATE = 'brightness_template' -CONF_RED_TEMPLATE = 'red_template' -CONF_GREEN_TEMPLATE = 'green_template' CONF_BLUE_TEMPLATE = 'blue_template' +CONF_BRIGHTNESS_TEMPLATE = 'brightness_template' +CONF_COLOR_TEMP_TEMPLATE = 'color_temp_template' +CONF_COMMAND_OFF_TEMPLATE = 'command_off_template' +CONF_COMMAND_ON_TEMPLATE = 'command_on_template' +CONF_EFFECT_LIST = 'effect_list' CONF_EFFECT_TEMPLATE = 'effect_template' - -SUPPORT_MQTT_TEMPLATE = (SUPPORT_BRIGHTNESS | SUPPORT_EFFECT | SUPPORT_FLASH | - SUPPORT_RGB_COLOR | SUPPORT_TRANSITION) +CONF_GREEN_TEMPLATE = 'green_template' +CONF_RED_TEMPLATE = 'red_template' +CONF_STATE_TEMPLATE = 'state_template' +CONF_WHITE_VALUE_TEMPLATE = 'white_value_template' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template, - vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template, - vol.Optional(CONF_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, - vol.Optional(CONF_RED_TEMPLATE): cv.template, - vol.Optional(CONF_GREEN_TEMPLATE): cv.template, vol.Optional(CONF_BLUE_TEMPLATE): cv.template, + vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, + vol.Optional(CONF_COLOR_TEMP_TEMPLATE): cv.template, + vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_EFFECT_TEMPLATE): cv.template, + vol.Optional(CONF_GREEN_TEMPLATE): cv.template, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + vol.Optional(CONF_RED_TEMPLATE): cv.template, + vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS): vol.All(vol.Coerce(int), vol.In([0, 1, 2])), - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean }) @@ -76,14 +78,16 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): }, { key: config.get(key) for key in ( - CONF_COMMAND_ON_TEMPLATE, - CONF_COMMAND_OFF_TEMPLATE, - CONF_STATE_TEMPLATE, - CONF_BRIGHTNESS_TEMPLATE, - CONF_RED_TEMPLATE, - CONF_GREEN_TEMPLATE, CONF_BLUE_TEMPLATE, - CONF_EFFECT_TEMPLATE + CONF_BRIGHTNESS_TEMPLATE, + CONF_COLOR_TEMP_TEMPLATE, + CONF_COMMAND_OFF_TEMPLATE, + CONF_COMMAND_ON_TEMPLATE, + CONF_EFFECT_TEMPLATE, + CONF_GREEN_TEMPLATE, + CONF_RED_TEMPLATE, + CONF_STATE_TEMPLATE, + CONF_WHITE_VALUE_TEMPLATE, ) }, config.get(CONF_OPTIMISTIC), @@ -114,6 +118,16 @@ class MqttTemplate(Light): else: self._brightness = None + if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: + self._color_temp = 255 + else: + self._color_temp = None + + if self._templates[CONF_WHITE_VALUE_TEMPLATE] is not None: + self._white_value = 255 + else: + self._white_value = None + if (self._templates[CONF_RED_TEMPLATE] is not None and self._templates[CONF_GREEN_TEMPLATE] is not None and self._templates[CONF_BLUE_TEMPLATE] is not None): @@ -156,6 +170,16 @@ class MqttTemplate(Light): except ValueError: _LOGGER.warning('Invalid brightness value received') + # read color temperature + if self._color_temp is not None: + try: + self._color_temp = int( + self._templates[CONF_COLOR_TEMP_TEMPLATE]. + async_render_with_possible_json_value(payload) + ) + except ValueError: + _LOGGER.warning('Invalid color temperature value received') + # read color if self._rgb is not None: try: @@ -171,6 +195,16 @@ class MqttTemplate(Light): except ValueError: _LOGGER.warning('Invalid color value received') + # read white value + if self._white_value is not None: + try: + self._white_value = int( + self._templates[CONF_WHITE_VALUE_TEMPLATE]. + async_render_with_possible_json_value(payload) + ) + except ValueError: + _LOGGER.warning('Invalid white value received') + # read effect if self._templates[CONF_EFFECT_TEMPLATE] is not None: effect = self._templates[CONF_EFFECT_TEMPLATE].\ @@ -194,11 +228,21 @@ class MqttTemplate(Light): """Return the brightness of this light between 0..255.""" return self._brightness + @property + def color_temp(self): + """Return the color temperature in mired.""" + return self._color_temp + @property def rgb_color(self): """Return the RGB color value [int, int, int].""" return self._rgb + @property + def white_value(self): + """Return the white property.""" + return self._white_value + @property def should_poll(self): """Return True if entity has to be polled for state. @@ -250,6 +294,13 @@ class MqttTemplate(Light): if self._optimistic: self._brightness = kwargs[ATTR_BRIGHTNESS] + # color_temp + if ATTR_COLOR_TEMP in kwargs: + values['color_temp'] = int(kwargs[ATTR_COLOR_TEMP]) + + if self._optimistic: + self._color_temp = kwargs[ATTR_COLOR_TEMP] + # color if ATTR_RGB_COLOR in kwargs: values['red'] = kwargs[ATTR_RGB_COLOR][0] @@ -259,6 +310,13 @@ class MqttTemplate(Light): if self._optimistic: self._rgb = kwargs[ATTR_RGB_COLOR] + # white value + if ATTR_WHITE_VALUE in kwargs: + values['white_value'] = int(kwargs[ATTR_WHITE_VALUE]) + + if self._optimistic: + self._white_value = kwargs[ATTR_WHITE_VALUE] + # effect if ATTR_EFFECT in kwargs: values['effect'] = kwargs.get(ATTR_EFFECT) @@ -307,12 +365,16 @@ class MqttTemplate(Light): @property def supported_features(self): """Flag supported features.""" - features = 0 + features = (SUPPORT_FLASH | SUPPORT_TRANSITION) if self._brightness is not None: features = features | SUPPORT_BRIGHTNESS if self._rgb is not None: features = features | SUPPORT_RGB_COLOR if self._effect_list is not None: features = features | SUPPORT_EFFECT + if self._color_temp is not None: + features = features | SUPPORT_COLOR_TEMP + if self._white_value is not None: + features = features | SUPPORT_WHITE_VALUE return features diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index d01fb848eab..0220a562f01 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -20,7 +20,13 @@ _LOGGER = logging.getLogger(__name__) TOPIC_MATCHER = re.compile( r'(?P<prefix_topic>\w+)/(?P<component>\w+)/(?P<object_id>\w+)/config') -SUPPORTED_COMPONENTS = ['binary_sensor', 'sensor'] +SUPPORTED_COMPONENTS = ['binary_sensor', 'light', 'sensor'] + +ALLOWED_PLATFORMS = { + 'binary_sensor': ['mqtt'], + 'light': ['mqtt', 'mqtt_json', 'mqtt_template'], + 'sensor': ['mqtt'] +} @asyncio.coroutine @@ -48,7 +54,13 @@ def async_start(hass, discovery_topic, hass_config): return payload = dict(payload) - payload[CONF_PLATFORM] = 'mqtt' + platform = payload.get(CONF_PLATFORM, 'mqtt') + if platform not in ALLOWED_PLATFORMS.get(component, []): + _LOGGER.warning("Platform %s (component %s) is not allowed", + platform, component) + return + + payload[CONF_PLATFORM] = platform if CONF_STATE_TOPIC not in payload: payload[CONF_STATE_TOPIC] = '{}/{}/{}/state'.format( discovery_topic, component, object_id) diff --git a/homeassistant/const.py b/homeassistant/const.py index bb6b455daf8..2e788ed7969 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -87,6 +87,7 @@ CONF_DISCOVERY = 'discovery' CONF_DISPLAY_OPTIONS = 'display_options' CONF_DOMAIN = 'domain' CONF_DOMAINS = 'domains' +CONF_EFFECT = 'effect' CONF_ELEVATION = 'elevation' CONF_EMAIL = 'email' CONF_ENTITIES = 'entities' @@ -153,6 +154,8 @@ CONF_VALUE_TEMPLATE = 'value_template' CONF_VERIFY_SSL = 'verify_ssl' CONF_WEEKDAY = 'weekday' CONF_WHITELIST = 'whitelist' +CONF_WHITE_VALUE = 'white_value' +CONF_XY = 'xy' CONF_ZONE = 'zone' # #### EVENTS #### diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index 89a74805361..7ef29ff0d8d 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -15,6 +15,21 @@ light: payload_on: "on" payload_off: "off" +Configuration for XY Version with brightness: + +light: + platform: mqtt + name: "Office Light XY" + state_topic: "office/xy1/light/status" + command_topic: "office/xy1/light/switch" + brightness_state_topic: "office/xy1/brightness/status" + brightness_command_topic: "office/xy1/brightness/set" + xy_state_topic: "office/xy1/xy/status" + xy_command_topic: "office/xy1/xy/set" + qos: 0 + payload_on: "on" + payload_off: "off" + config without RGB: light: @@ -72,6 +87,42 @@ light: payload_on: "on" payload_off: "off" +config with brightness and effect + +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 + effect_state_topic: "office/rgb1/effect/status" + effect_command_topic: "office/rgb1/effect/set" + effect_list: + - rainbow + - colorloop + qos: 0 + payload_on: "on" + payload_off: "off" + +config for RGB Version with white value and scale: + +light: + platform: mqtt + name: "Office Light RGB" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + white_value_state_topic: "office/rgb1/white_value/status" + white_value_command_topic: "office/rgb1/white_value/set" + white_value_scale: 99 + rgb_state_topic: "office/rgb1/rgb/status" + rgb_command_topic: "office/rgb1/rgb/set" + rgb_scale: 99 + qos: 0 + payload_on: "on" + payload_off: "off" + """ import unittest from unittest import mock @@ -109,7 +160,7 @@ class TestLightMQTT(unittest.TestCase): }) self.assertIsNone(self.hass.states.get('light.test')) - def test_no_color_or_brightness_or_color_temp_if_no_topics(self): \ + def test_no_color_brightness_color_temp_white_xy_if_no_topics(self): \ # pylint: disable=invalid-name """Test if there is no color and brightness if no topic.""" with assert_setup_component(1): @@ -127,6 +178,8 @@ class TestLightMQTT(unittest.TestCase): 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('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) fire_mqtt_message(self.hass, 'test_light_rgb/status', 'ON') self.hass.block_till_done() @@ -136,6 +189,8 @@ class TestLightMQTT(unittest.TestCase): 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('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) def test_controlling_state_via_topic(self): \ # pylint: disable=invalid-name @@ -151,6 +206,12 @@ class TestLightMQTT(unittest.TestCase): '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', + 'effect_state_topic': 'test_light_rgb/effect/status', + 'effect_command_topic': 'test_light_rgb/effect/set', + 'white_value_state_topic': 'test_light_rgb/white_value/status', + 'white_value_command_topic': 'test_light_rgb/white_value/set', + 'xy_state_topic': 'test_light_rgb/xy/status', + 'xy_command_topic': 'test_light_rgb/xy/set', 'qos': '0', 'payload_on': 1, 'payload_off': 0 @@ -164,6 +225,9 @@ class TestLightMQTT(unittest.TestCase): 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('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) fire_mqtt_message(self.hass, 'test_light_rgb/status', '1') @@ -174,6 +238,9 @@ class TestLightMQTT(unittest.TestCase): 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')) + self.assertEqual('none', state.attributes.get('effect')) + self.assertEqual(255, state.attributes.get('white_value')) + self.assertEqual([1, 1], state.attributes.get('xy_color')) fire_mqtt_message(self.hass, 'test_light_rgb/status', '0') self.hass.block_till_done() @@ -198,6 +265,21 @@ class TestLightMQTT(unittest.TestCase): self.hass.block_till_done() self.assertEqual(300, light_state.attributes['color_temp']) + fire_mqtt_message(self.hass, 'test_light_rgb/effect/status', 'rainbow') + self.hass.block_till_done() + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual('rainbow', light_state.attributes['effect']) + + fire_mqtt_message(self.hass, 'test_light_rgb/white_value/status', + '100') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(100, + light_state.attributes['white_value']) + fire_mqtt_message(self.hass, 'test_light_rgb/status', '1') self.hass.block_till_done() @@ -209,8 +291,16 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual([125, 125, 125], light_state.attributes.get('rgb_color')) - def test_controlling_scale(self): - """Test the controlling scale.""" + fire_mqtt_message(self.hass, 'test_light_rgb/xy/status', + '0.675,0.322') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual([0.675, 0.322], + light_state.attributes.get('xy_color')) + + def test_brightness_controlling_scale(self): + """Test the brightness controlling scale.""" with assert_setup_component(1): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { @@ -256,6 +346,53 @@ class TestLightMQTT(unittest.TestCase): self.assertEqual(255, light_state.attributes['brightness']) + def test_white_value_controlling_scale(self): + """Test the white_value controlling scale.""" + with assert_setup_component(1): + assert setup_component(self.hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'test_scale/status', + 'command_topic': 'test_scale/set', + 'white_value_state_topic': 'test_scale/white_value/status', + 'white_value_command_topic': 'test_scale/white_value/set', + 'white_value_scale': '99', + 'qos': 0, + 'payload_on': 'on', + 'payload_off': 'off' + } + }) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) + + fire_mqtt_message(self.hass, 'test_scale/status', 'on') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('white_value')) + + fire_mqtt_message(self.hass, 'test_scale/status', 'off') + self.hass.block_till_done() + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'test_scale/status', 'on') + self.hass.block_till_done() + + fire_mqtt_message(self.hass, 'test_scale/white_value/status', '99') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(255, + light_state.attributes['white_value']) + def test_controlling_state_via_topic_with_templates(self): \ # pylint: disable=invalid-name """Test the setting og the state with a template.""" @@ -266,11 +403,17 @@ class TestLightMQTT(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'brightness_state_topic': 'test_light_rgb/brightness/status', 'color_temp_state_topic': 'test_light_rgb/color_temp/status', + 'effect_state_topic': 'test_light_rgb/effect/status', 'rgb_state_topic': 'test_light_rgb/rgb/status', + 'white_value_state_topic': 'test_light_rgb/white_value/status', + 'xy_state_topic': 'test_light_rgb/xy/status', 'state_value_template': '{{ value_json.hello }}', 'brightness_value_template': '{{ value_json.hello }}', 'color_temp_value_template': '{{ value_json.hello }}', + 'effect_value_template': '{{ value_json.hello }}', 'rgb_value_template': '{{ value_json.hello | join(",") }}', + 'white_value_template': '{{ value_json.hello }}', + 'xy_value_template': '{{ value_json.hello | join(",") }}', }} with assert_setup_component(1): @@ -289,6 +432,12 @@ class TestLightMQTT(unittest.TestCase): '{"hello": "50"}') fire_mqtt_message(self.hass, 'test_light_rgb/color_temp/status', '{"hello": "300"}') + fire_mqtt_message(self.hass, 'test_light_rgb/effect/status', + '{"hello": "rainbow"}') + fire_mqtt_message(self.hass, 'test_light_rgb/white_value/status', + '{"hello": "75"}') + fire_mqtt_message(self.hass, 'test_light_rgb/xy/status', + '{"hello": [0.123,0.123]}') self.hass.block_till_done() state = self.hass.states.get('light.test') @@ -296,6 +445,9 @@ class TestLightMQTT(unittest.TestCase): 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')) + self.assertEqual('rainbow', state.attributes.get('effect')) + self.assertEqual(75, state.attributes.get('white_value')) + self.assertEqual([0.123, 0.123], state.attributes.get('xy_color')) def test_sending_mqtt_commands_and_optimistic(self): \ # pylint: disable=invalid-name @@ -307,6 +459,9 @@ class TestLightMQTT(unittest.TestCase): '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', + 'effect_command_topic': 'test_light_rgb/effect/set', + 'white_value_command_topic': 'test_light_rgb/white_value/set', + 'xy_command_topic': 'test_light_rgb/xy/set', 'qos': 2, 'payload_on': 'on', 'payload_off': 'off' @@ -337,19 +492,23 @@ class TestLightMQTT(unittest.TestCase): self.mock_publish.reset_mock() light.turn_on(self.hass, 'light.test', rgb_color=[75, 75, 75], - brightness=50) + brightness=50, white_value=80, xy_color=[0.123, 0.123]) self.hass.block_till_done() self.mock_publish().async_publish.assert_has_calls([ - mock.call('test_light_rgb/set', 'on', 2, False), - mock.call('test_light_rgb/rgb/set', '75,75,75', 2, False), - mock.call('test_light_rgb/brightness/set', 50, 2, False), - ], any_order=True) + mock.call('test_light_rgb/set', 'on', 2, False), + mock.call('test_light_rgb/rgb/set', '75,75,75', 2, False), + mock.call('test_light_rgb/brightness/set', 50, 2, False), + mock.call('test_light_rgb/white_value/set', 80, 2, False), + mock.call('test_light_rgb/xy/set', '0.123,0.123', 2, False), + ], any_order=True) state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual((75, 75, 75), state.attributes['rgb_color']) self.assertEqual(50, state.attributes['brightness']) + self.assertEqual(80, state.attributes['white_value']) + self.assertEqual((0.123, 0.123), state.attributes['xy_color']) def test_show_brightness_if_only_command_topic(self): """Test the brightness if only a command topic is present.""" @@ -398,3 +557,75 @@ class TestLightMQTT(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual(150, state.attributes.get('color_temp')) + + def test_show_effect_only_if_command_topic(self): + """Test the color temp only if a command topic is present.""" + config = {light.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'effect_command_topic': 'test_light_rgb/effect/set', + 'command_topic': 'test_light_rgb/set', + 'state_topic': 'test_light_rgb/status' + }} + + with assert_setup_component(1): + assert setup_component(self.hass, light.DOMAIN, config) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('effect')) + + 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('none', state.attributes.get('effect')) + + def test_show_white_value_if_only_command_topic(self): + """Test the white_value if only a command topic is present.""" + config = {light.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'white_value_command_topic': 'test_light_rgb/white_value/set', + 'command_topic': 'test_light_rgb/set', + 'state_topic': 'test_light_rgb/status', + }} + + with assert_setup_component(1): + assert setup_component(self.hass, light.DOMAIN, config) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('white_value')) + + 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(255, state.attributes.get('white_value')) + + def test_show_xy_if_only_command_topic(self): + """Test the xy if only a command topic is present.""" + config = {light.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'xy_command_topic': 'test_light_rgb/xy/set', + 'command_topic': 'test_light_rgb/set', + 'state_topic': 'test_light_rgb/status', + }} + + with assert_setup_component(1): + assert setup_component(self.hass, light.DOMAIN, config) + + state = self.hass.states.get('light.test') + self.assertEqual(STATE_OFF, state.state) + self.assertIsNone(state.attributes.get('xy_color')) + + 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([1, 1], state.attributes.get('xy_color')) diff --git a/tests/components/light/test_mqtt_json.py b/tests/components/light/test_mqtt_json.py index 21c88405a6d..8f2436a24e8 100755 --- a/tests/components/light/test_mqtt_json.py +++ b/tests/components/light/test_mqtt_json.py @@ -1,6 +1,56 @@ """The tests for the MQTT JSON light platform. -Configuration for RGB Version with brightness: +Configuration with RGB, brightness, color temp, effect, white value and XY: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + color_temp: true + effect: true + rgb: true + white_value: true + xy: true + +Configuration with RGB, brightness, color temp, effect, white value: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + color_temp: true + effect: true + rgb: true + white_value: true + +Configuration with RGB, brightness, color temp and effect: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + color_temp: true + effect: true + rgb: true + +Configuration with RGB, brightness and color temp: + +light: + platform: mqtt_json + name: mqtt_json_light_1 + state_topic: "home/rgb1" + command_topic: "home/rgb1/set" + brightness: true + rgb: true + color_temp: true + +Configuration with RGB, brightness: light: platform: mqtt_json @@ -62,9 +112,9 @@ class TestLightMQTTJSON(unittest.TestCase): }) self.assertIsNone(self.hass.states.get('light.test')) - def test_no_color_or_brightness_if_no_config(self): \ + def test_no_color_brightness_color_temp_white_val_if_no_topics(self): \ # pylint: disable=invalid-name - """Test if there is no color and brightness if they aren't defined.""" + """Test for no RGB, brightness, color temp, effect, white val or XY.""" assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', @@ -78,6 +128,10 @@ class TestLightMQTTJSON(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('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"ON"}') self.hass.block_till_done() @@ -86,6 +140,10 @@ class TestLightMQTTJSON(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')) + self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) def test_controlling_state_via_topic(self): \ # pylint: disable=invalid-name @@ -97,7 +155,11 @@ class TestLightMQTTJSON(unittest.TestCase): 'state_topic': 'test_light_rgb', 'command_topic': 'test_light_rgb/set', 'brightness': True, + 'color_temp': True, + 'effect': True, 'rgb': True, + 'white_value': True, + 'xy': True, 'qos': '0' } }) @@ -106,19 +168,31 @@ class TestLightMQTTJSON(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('effect')) + self.assertIsNone(state.attributes.get('white_value')) + self.assertIsNone(state.attributes.get('xy_color')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) # Turn on the light, full white fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"ON",' - '"color":{"r":255,"g":255,"b":255},' - '"brightness":255}') + '"color":{"r":255,"g":255,"b":255,' + '"x":0.123,"y":0.123},' + '"brightness":255,' + '"color_temp":155,' + '"effect":"colorloop",' + '"white_value":150}') self.hass.block_till_done() state = self.hass.states.get('light.test') 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(155, state.attributes.get('color_temp')) + self.assertEqual('colorloop', state.attributes.get('effect')) + self.assertEqual(150, state.attributes.get('white_value')) + self.assertEqual([0.123, 0.123], state.attributes.get('xy_color')) # Turn the light off fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"OFF"}') @@ -146,6 +220,39 @@ class TestLightMQTTJSON(unittest.TestCase): self.assertEqual([125, 125, 125], light_state.attributes.get('rgb_color')) + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color":{"x":0.135,"y":0.135}}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual([0.135, 0.135], + light_state.attributes.get('xy_color')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"color_temp":155}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual(155, light_state.attributes.get('color_temp')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"effect":"colorloop"}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual('colorloop', light_state.attributes.get('effect')) + + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"white_value":155}') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.assertEqual(155, light_state.attributes.get('white_value')) + def test_sending_mqtt_commands_and_optimistic(self): \ # pylint: disable=invalid-name """Test the sending of command in optimistic mode.""" @@ -155,7 +262,10 @@ class TestLightMQTTJSON(unittest.TestCase): 'name': 'test', 'command_topic': 'test_light_rgb/set', 'brightness': True, + 'color_temp': True, + 'effect': True, 'rgb': True, + 'white_value': True, 'qos': 2 } }) @@ -181,7 +291,8 @@ class TestLightMQTTJSON(unittest.TestCase): self.assertEqual(STATE_OFF, state.state) light.turn_on(self.hass, 'light.test', rgb_color=[75, 75, 75], - brightness=50) + brightness=50, color_temp=155, effect='colorloop', + white_value=170) self.hass.block_till_done() self.assertEqual('test_light_rgb/set', @@ -191,15 +302,21 @@ class TestLightMQTTJSON(unittest.TestCase): # Get the sent message message_json = json.loads(self.mock_publish.mock_calls[-2][1][1]) self.assertEqual(50, message_json["brightness"]) + self.assertEqual(155, message_json["color_temp"]) + self.assertEqual('colorloop', message_json["effect"]) self.assertEqual(75, message_json["color"]["r"]) self.assertEqual(75, message_json["color"]["g"]) self.assertEqual(75, message_json["color"]["b"]) + self.assertEqual(170, message_json["white_value"]) self.assertEqual("ON", message_json["state"]) state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual((75, 75, 75), state.attributes['rgb_color']) self.assertEqual(50, state.attributes['brightness']) + self.assertEqual(155, state.attributes['color_temp']) + self.assertEqual('colorloop', state.attributes['effect']) + self.assertEqual(170, state.attributes['white_value']) def test_flash_short_and_long(self): \ # pylint: disable=invalid-name @@ -283,9 +400,9 @@ class TestLightMQTTJSON(unittest.TestCase): self.assertEqual(10, message_json["transition"]) self.assertEqual("OFF", message_json["state"]) - def test_invalid_color_and_brightness_values(self): \ + def test_invalid_color_brightness_and_white_values(self): \ # pylint: disable=invalid-name - """Test that invalid color/brightness values are ignored.""" + """Test that invalid color/brightness/white values are ignored.""" assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { 'platform': 'mqtt_json', @@ -294,6 +411,7 @@ class TestLightMQTTJSON(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'brightness': True, 'rgb': True, + 'white_value': True, 'qos': '0' } }) @@ -302,19 +420,22 @@ class TestLightMQTTJSON(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('white_value')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) # Turn on the light fire_mqtt_message(self.hass, 'test_light_rgb', '{"state":"ON",' '"color":{"r":255,"g":255,"b":255},' - '"brightness": 255}') + '"brightness": 255,' + '"white_value": 255}') self.hass.block_till_done() state = self.hass.states.get('light.test') 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(255, state.attributes.get('white_value')) # Bad color values fire_mqtt_message(self.hass, 'test_light_rgb', @@ -337,3 +458,14 @@ class TestLightMQTTJSON(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual(255, state.attributes.get('brightness')) + + # Bad white value + fire_mqtt_message(self.hass, 'test_light_rgb', + '{"state":"ON",' + '"white_value": "badValue"}') + self.hass.block_till_done() + + # White value should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(STATE_ON, state.state) + self.assertEqual(255, state.attributes.get('white_value')) diff --git a/tests/components/light/test_mqtt_template.py b/tests/components/light/test_mqtt_template.py index 52847a7be9a..b16e0dc84e4 100755 --- a/tests/components/light/test_mqtt_template.py +++ b/tests/components/light/test_mqtt_template.py @@ -12,13 +12,19 @@ light: command_off_template: 'off' state_template: '{{ value.split(",")[0] }}' brightness_template: '{{ value.split(",")[1] }}' - red_template: '{{ value.split(",")[2].split("-")[0] }}' - green_template: '{{ value.split(",")[2].split("-")[1] }}' - blue_template: '{{ value.split(",")[2].split("-")[2] }}' + color_temp_template: '{{ value.split(",")[2] }}' + white_value_template: '{{ value.split(",")[3] }}' + red_template: '{{ value.split(",")[4].split("-")[0] }}' + green_template: '{{ value.split(",")[4].split("-")[1] }}' + blue_template: '{{ value.split(",")[4].split("-")[2] }}' If your light doesn't support brightness feature, omit `brightness_template`. -If your light doesn't support rgb feature, omit `(red|green|blue)_template`. +If your light doesn't support color temp feature, omit `color_temp_template`. + +If your light doesn't support white value feature, omit `white_value_template`. + +If your light doesn't support RGB feature, omit `(red|green|blue)_template`. """ import unittest @@ -66,6 +72,8 @@ class TestLightMQTTTemplate(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'command_on_template': 'on,' '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ white_value|d }},' '{{ red|d }}-' '{{ green|d }}-' '{{ blue|d }}', @@ -78,6 +86,8 @@ class TestLightMQTTTemplate(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('white_value')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) fire_mqtt_message(self.hass, 'test_light_rgb', 'on') @@ -87,10 +97,12 @@ class TestLightMQTTTemplate(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')) + self.assertIsNone(state.attributes.get('white_value')) - def test_state_brightness_color_effect_change_via_topic(self): \ + def test_state_brightness_color_effect_temp_white_change_via_topic(self): \ # pylint: disable=invalid-name - """Test state, brightness, color and effect change via topic.""" + """Test state, bri, color, effect, color temp, white val change.""" with assert_setup_component(1): assert setup_component(self.hass, light.DOMAIN, { light.DOMAIN: { @@ -101,6 +113,8 @@ class TestLightMQTTTemplate(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'command_on_template': 'on,' '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ white_value|d }},' '{{ red|d }}-' '{{ green|d }}-' '{{ blue|d }},' @@ -108,13 +122,15 @@ class TestLightMQTTTemplate(unittest.TestCase): 'command_off_template': 'off', 'state_template': '{{ value.split(",")[0] }}', 'brightness_template': '{{ value.split(",")[1] }}', - 'red_template': '{{ value.split(",")[2].' + 'color_temp_template': '{{ value.split(",")[2] }}', + 'white_value_template': '{{ value.split(",")[3] }}', + 'red_template': '{{ value.split(",")[4].' 'split("-")[0] }}', - 'green_template': '{{ value.split(",")[2].' + 'green_template': '{{ value.split(",")[4].' 'split("-")[1] }}', - 'blue_template': '{{ value.split(",")[2].' + 'blue_template': '{{ value.split(",")[4].' 'split("-")[2] }}', - 'effect_template': '{{ value.split(",")[3] }}' + 'effect_template': '{{ value.split(",")[5] }}' } }) @@ -123,16 +139,21 @@ class TestLightMQTTTemplate(unittest.TestCase): self.assertIsNone(state.attributes.get('rgb_color')) self.assertIsNone(state.attributes.get('brightness')) self.assertIsNone(state.attributes.get('effect')) + self.assertIsNone(state.attributes.get('color_temp')) + self.assertIsNone(state.attributes.get('white_value')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) # turn on the light, full white - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,255-255-255,') + fire_mqtt_message(self.hass, 'test_light_rgb', + 'on,255,145,123,255-255-255,') self.hass.block_till_done() state = self.hass.states.get('light.test') 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(145, state.attributes.get('color_temp')) + self.assertEqual(123, state.attributes.get('white_value')) self.assertIsNone(state.attributes.get('effect')) # turn the light off @@ -150,15 +171,32 @@ class TestLightMQTTTemplate(unittest.TestCase): self.hass.block_till_done() self.assertEqual(100, light_state.attributes['brightness']) + # change the color temp + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,195') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(195, light_state.attributes['color_temp']) + # change the color - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,41-42-43') + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,,41-42-43') self.hass.block_till_done() light_state = self.hass.states.get('light.test') self.assertEqual([41, 42, 43], light_state.attributes.get('rgb_color')) + # change the white value + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,134') + self.hass.block_till_done() + + light_state = self.hass.states.get('light.test') + self.hass.block_till_done() + self.assertEqual(134, light_state.attributes['white_value']) + # change the effect - fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,41-42-43,rainbow') + fire_mqtt_message(self.hass, 'test_light_rgb', + 'on,,,,41-42-43,rainbow') self.hass.block_till_done() light_state = self.hass.states.get('light.test') @@ -175,6 +213,8 @@ class TestLightMQTTTemplate(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'command_on_template': 'on,' '{{ brightness|d }},' + '{{ color_temp|d }},' + '{{ white_value|d }},' '{{ red|d }}-' '{{ green|d }}-' '{{ blue|d }}', @@ -191,7 +231,7 @@ class TestLightMQTTTemplate(unittest.TestCase): light.turn_on(self.hass, 'light.test') self.hass.block_till_done() - self.assertEqual(('test_light_rgb/set', 'on,,--', 2, False), + self.assertEqual(('test_light_rgb/set', 'on,,,,--', 2, False), self.mock_publish.mock_calls[-2][1]) state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) @@ -205,9 +245,9 @@ class TestLightMQTTTemplate(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(STATE_OFF, state.state) - # turn on the light with brightness and color + # turn on the light with brightness, color, color temp and white val light.turn_on(self.hass, 'light.test', brightness=50, - rgb_color=[75, 75, 75]) + rgb_color=[75, 75, 75], color_temp=200, white_value=139) self.hass.block_till_done() self.assertEqual('test_light_rgb/set', @@ -217,13 +257,15 @@ class TestLightMQTTTemplate(unittest.TestCase): # check the payload payload = self.mock_publish.mock_calls[-2][1][1] - self.assertEqual('on,50,75-75-75', payload) + self.assertEqual('on,50,200,139,75-75-75', payload) # check the state state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual((75, 75, 75), state.attributes['rgb_color']) self.assertEqual(50, state.attributes['brightness']) + self.assertEqual(200, state.attributes['color_temp']) + self.assertEqual(139, state.attributes['white_value']) def test_flash(self): \ # pylint: disable=invalid-name @@ -324,6 +366,7 @@ class TestLightMQTTTemplate(unittest.TestCase): 'command_topic': 'test_light_rgb/set', 'command_on_template': 'on,' '{{ brightness|d }},' + '{{ color_temp|d }},' '{{ red|d }}-' '{{ green|d }}-' '{{ blue|d }},' @@ -331,13 +374,15 @@ class TestLightMQTTTemplate(unittest.TestCase): 'command_off_template': 'off', 'state_template': '{{ value.split(",")[0] }}', 'brightness_template': '{{ value.split(",")[1] }}', - 'red_template': '{{ value.split(",")[2].' + 'color_temp_template': '{{ value.split(",")[2] }}', + 'white_value_template': '{{ value.split(",")[3] }}', + 'red_template': '{{ value.split(",")[4].' 'split("-")[0] }}', - 'green_template': '{{ value.split(",")[2].' + 'green_template': '{{ value.split(",")[4].' 'split("-")[1] }}', - 'blue_template': '{{ value.split(",")[2].' + 'blue_template': '{{ value.split(",")[4].' 'split("-")[2] }}', - 'effect_template': '{{ value.split(",")[3] }}', + 'effect_template': '{{ value.split(",")[5] }}', } }) @@ -345,18 +390,22 @@ class TestLightMQTTTemplate(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('effect')) + self.assertIsNone(state.attributes.get('white_value')) self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE)) # turn on the light, full white fire_mqtt_message(self.hass, 'test_light_rgb', - 'on,255,255-255-255,rainbow') + 'on,255,215,222,255-255-255,rainbow') self.hass.block_till_done() state = self.hass.states.get('light.test') self.assertEqual(STATE_ON, state.state) self.assertEqual(255, state.attributes.get('brightness')) + self.assertEqual(215, state.attributes.get('color_temp')) self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + self.assertEqual(222, state.attributes.get('white_value')) self.assertEqual('rainbow', state.attributes.get('effect')) # bad state value @@ -375,6 +424,14 @@ class TestLightMQTTTemplate(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual(255, state.attributes.get('brightness')) + # bad color temp values + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,off,255-255-255') + self.hass.block_till_done() + + # color temp should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(215, state.attributes.get('color_temp')) + # bad color values fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,a-b-c') self.hass.block_till_done() @@ -383,6 +440,14 @@ class TestLightMQTTTemplate(unittest.TestCase): state = self.hass.states.get('light.test') self.assertEqual([255, 255, 255], state.attributes.get('rgb_color')) + # bad white value values + fire_mqtt_message(self.hass, 'test_light_rgb', 'on,,,off,255-255-255') + self.hass.block_till_done() + + # white value should not have changed + state = self.hass.states.get('light.test') + self.assertEqual(222, state.attributes.get('white_value')) + # bad effect value fire_mqtt_message(self.hass, 'test_light_rgb', 'on,255,a-b-c,white') self.hass.block_till_done()