From bc5f0ff0b34fb8d12b29eeb6fb59c9aeb739e298 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 10 Apr 2019 11:16:41 +0200 Subject: [PATCH] Use dict[key] for required config keys and keys with default values of MQTT light (#22834) * Use dict[key] for required config keys and keys with default values. * Improve tests * Lint * Improve tests of JSON data --- .../components/mqtt/light/schema_basic.py | 84 +++--- .../components/mqtt/light/schema_json.py | 53 ++-- .../components/mqtt/light/schema_template.py | 23 +- tests/components/mqtt/test_light_json.py | 282 +++++++++++++++++- 4 files changed, 357 insertions(+), 85 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index a985a707485..d5aa4480139 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -81,6 +81,7 @@ PLATFORM_SCHEMA_BASIC = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ 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_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, 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, @@ -89,7 +90,8 @@ PLATFORM_SCHEMA_BASIC = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HS_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_HS_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_ON_COMMAND_TYPE, default=DEFAULT_ON_COMMAND_TYPE): + vol.In(VALUES_ON_COMMAND_TYPE), 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, @@ -98,6 +100,7 @@ PLATFORM_SCHEMA_BASIC = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ 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_UNIQUE_ID): cv.string, 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)), @@ -106,9 +109,6 @@ PLATFORM_SCHEMA_BASIC = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ 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, - vol.Optional(CONF_ON_COMMAND_TYPE, default=DEFAULT_ON_COMMAND_TYPE): - vol.In(VALUES_ON_COMMAND_TYPE), - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema).extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) @@ -202,8 +202,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, } self._topic = topic self._payload = { - 'on': config.get(CONF_PAYLOAD_ON), - 'off': config.get(CONF_PAYLOAD_OFF), + 'on': config[CONF_PAYLOAD_ON], + 'off': config[CONF_PAYLOAD_OFF], } self._templates = { CONF_BRIGHTNESS: config.get(CONF_BRIGHTNESS_VALUE_TEMPLATE), @@ -219,7 +219,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, CONF_XY: config.get(CONF_XY_VALUE_TEMPLATE), } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic or topic[CONF_STATE_TOPIC] is None self._optimistic_rgb = \ optimistic or topic[CONF_RGB_STATE_TOPIC] is None @@ -272,7 +272,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_STATE_TOPIC] = { 'topic': self._topic[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} elif self._optimistic and last_state: self._state = last_state.state == STATE_ON @@ -287,7 +287,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, device_value = float(payload) percent_bright = \ - device_value / self._config.get(CONF_BRIGHTNESS_SCALE) + device_value / self._config[CONF_BRIGHTNESS_SCALE] self._brightness = percent_bright * 255 self.async_write_ha_state() @@ -295,7 +295,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_BRIGHTNESS_STATE_TOPIC] = { 'topic': self._topic[CONF_BRIGHTNESS_STATE_TOPIC], 'msg_callback': brightness_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._brightness = 255 elif self._optimistic_brightness and last_state\ and last_state.attributes.get(ATTR_BRIGHTNESS): @@ -326,7 +326,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_RGB_STATE_TOPIC] = { 'topic': self._topic[CONF_RGB_STATE_TOPIC], 'msg_callback': rgb_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._hs = (0, 0) if self._optimistic_rgb and last_state\ and last_state.attributes.get(ATTR_HS_COLOR): @@ -350,7 +350,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_COLOR_TEMP_STATE_TOPIC] = { 'topic': self._topic[CONF_COLOR_TEMP_STATE_TOPIC], 'msg_callback': color_temp_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._color_temp = 150 if self._optimistic_color_temp and last_state\ and last_state.attributes.get(ATTR_COLOR_TEMP): @@ -376,7 +376,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_EFFECT_STATE_TOPIC] = { 'topic': self._topic[CONF_EFFECT_STATE_TOPIC], 'msg_callback': effect_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._effect = 'none' if self._optimistic_effect and last_state\ and last_state.attributes.get(ATTR_EFFECT): @@ -406,7 +406,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_HS_STATE_TOPIC] = { 'topic': self._topic[CONF_HS_STATE_TOPIC], 'msg_callback': hs_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._hs = (0, 0) if self._optimistic_hs and last_state\ and last_state.attributes.get(ATTR_HS_COLOR): @@ -425,7 +425,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, device_value = float(payload) percent_white = \ - device_value / self._config.get(CONF_WHITE_VALUE_SCALE) + device_value / self._config[CONF_WHITE_VALUE_SCALE] self._white_value = percent_white * 255 self.async_write_ha_state() @@ -433,7 +433,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_WHITE_VALUE_STATE_TOPIC] = { 'topic': self._topic[CONF_WHITE_VALUE_STATE_TOPIC], 'msg_callback': white_value_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._white_value = 255 elif self._optimistic_white_value and last_state\ and last_state.attributes.get(ATTR_WHITE_VALUE): @@ -460,7 +460,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, topics[CONF_XY_STATE_TOPIC] = { 'topic': self._topic[CONF_XY_STATE_TOPIC], 'msg_callback': xy_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._hs = (0, 0) if self._optimistic_xy and last_state\ and last_state.attributes.get(ATTR_HS_COLOR): @@ -513,7 +513,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @property def name(self): """Return the name of the device if any.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -572,13 +572,13 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, This method is a coroutine. """ should_update = False - on_command_type = self._config.get(CONF_ON_COMMAND_TYPE) + on_command_type = self._config[CONF_ON_COMMAND_TYPE] if on_command_type == 'first': mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], - self._payload['on'], self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._payload['on'], self._config[CONF_QOS], + self._config[CONF_RETAIN]) should_update = True # If brightness is being used instead of an on command, make sure @@ -616,8 +616,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topic[CONF_RGB_COMMAND_TOPIC], - rgb_color_str, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + rgb_color_str, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_rgb: self._hs = kwargs[ATTR_HS_COLOR] @@ -629,8 +629,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, hs_color = kwargs[ATTR_HS_COLOR] mqtt.async_publish( self.hass, self._topic[CONF_HS_COMMAND_TOPIC], - '{},{}'.format(*hs_color), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + '{},{}'.format(*hs_color), self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_hs: self._hs = kwargs[ATTR_HS_COLOR] @@ -642,8 +642,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) mqtt.async_publish( self.hass, self._topic[CONF_XY_COMMAND_TOPIC], - '{},{}'.format(*xy_color), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + '{},{}'.format(*xy_color), self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_xy: self._hs = kwargs[ATTR_HS_COLOR] @@ -652,13 +652,13 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, if ATTR_BRIGHTNESS in kwargs and \ self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None: percent_bright = float(kwargs[ATTR_BRIGHTNESS]) / 255 - brightness_scale = self._config.get(CONF_BRIGHTNESS_SCALE) + brightness_scale = self._config[CONF_BRIGHTNESS_SCALE] device_brightness = \ min(round(percent_bright * brightness_scale), brightness_scale) mqtt.async_publish( self.hass, self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC], - device_brightness, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + device_brightness, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_brightness: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -679,8 +679,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topic[CONF_RGB_COMMAND_TOPIC], - rgb_color_str, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + rgb_color_str, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_brightness: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -698,8 +698,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC], - color_temp, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + color_temp, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_color_temp: self._color_temp = kwargs[ATTR_COLOR_TEMP] @@ -711,8 +711,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, if effect in self._config.get(CONF_EFFECT_LIST): mqtt.async_publish( self.hass, self._topic[CONF_EFFECT_COMMAND_TOPIC], - effect, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + effect, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_effect: self._effect = kwargs[ATTR_EFFECT] @@ -721,13 +721,13 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, 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 - white_scale = self._config.get(CONF_WHITE_VALUE_SCALE) + white_scale = self._config[CONF_WHITE_VALUE_SCALE] device_white_value = \ min(round(percent_white * white_scale), white_scale) mqtt.async_publish( self.hass, self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC], - device_white_value, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + device_white_value, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_white_value: self._white_value = kwargs[ATTR_WHITE_VALUE] @@ -735,8 +735,8 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, if on_command_type == 'last': mqtt.async_publish(self.hass, self._topic[CONF_COMMAND_TOPIC], - self._payload['on'], self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._payload['on'], self._config[CONF_QOS], + self._config[CONF_RETAIN]) should_update = True if self._optimistic: @@ -754,7 +754,7 @@ class MqttLight(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, """ mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], self._payload['off'], - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that the light has changed state. diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 12f688afbf7..a52f3c58d0e 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -62,25 +62,24 @@ PLATFORM_SCHEMA_JSON = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE): vol.All(vol.Coerce(int), vol.Range(min=1)), vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, 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, + vol.Optional(CONF_FLASH_TIME_SHORT, default=DEFAULT_FLASH_TIME_SHORT): + cv.positive_int, + vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_UNIQUE_ID): 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_UNIQUE_ID): cv.string, vol.Optional(CONF_WHITE_VALUE, default=DEFAULT_WHITE_VALUE): cv.boolean, vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean, - vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean, - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema).extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) @@ -148,34 +147,34 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, CONF_COMMAND_TOPIC ) } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None - brightness = config.get(CONF_BRIGHTNESS) + brightness = config[CONF_BRIGHTNESS] if brightness: self._brightness = 255 else: self._brightness = None - color_temp = config.get(CONF_COLOR_TEMP) + color_temp = config[CONF_COLOR_TEMP] if color_temp: self._color_temp = 150 else: self._color_temp = None - effect = config.get(CONF_EFFECT) + effect = config[CONF_EFFECT] if effect: self._effect = 'none' else: self._effect = None - white_value = config.get(CONF_WHITE_VALUE) + white_value = config[CONF_WHITE_VALUE] if white_value: self._white_value = 255 else: self._white_value = None - if config.get(CONF_HS) or config.get(CONF_RGB) or config.get(CONF_XY): + if config[CONF_HS] or config[CONF_RGB] or config[CONF_XY]: self._hs = [0, 0] else: self._hs = None @@ -188,13 +187,13 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, } self._supported_features = (SUPPORT_TRANSITION | SUPPORT_FLASH) - self._supported_features |= (config.get(CONF_RGB) and SUPPORT_COLOR) + self._supported_features |= (config[CONF_RGB] and SUPPORT_COLOR) self._supported_features |= (brightness and SUPPORT_BRIGHTNESS) self._supported_features |= (color_temp and SUPPORT_COLOR_TEMP) self._supported_features |= (effect and SUPPORT_EFFECT) self._supported_features |= (white_value and SUPPORT_WHITE_VALUE) - self._supported_features |= (config.get(CONF_XY) and SUPPORT_COLOR) - self._supported_features |= (config.get(CONF_HS) and SUPPORT_COLOR) + self._supported_features |= (config[CONF_XY] and SUPPORT_COLOR) + self._supported_features |= (config[CONF_HS] and SUPPORT_COLOR) async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -246,7 +245,7 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, try: self._brightness = int( values['brightness'] / - float(self._config.get(CONF_BRIGHTNESS_SCALE)) * 255) + float(self._config[CONF_BRIGHTNESS_SCALE]) * 255) except KeyError: pass except ValueError: @@ -283,7 +282,7 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, self.hass, self._sub_state, {'state_topic': {'topic': self._topic[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) if self._optimistic and last_state: self._state = last_state.state == STATE_ON @@ -343,7 +342,7 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @property def name(self): """Return the name of the device if any.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -375,11 +374,11 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, message = {'state': 'ON'} if ATTR_HS_COLOR in kwargs and ( - self._config.get(CONF_HS) or self._config.get(CONF_RGB) - or self._config.get(CONF_XY)): + self._config[CONF_HS] or self._config[CONF_RGB] + or self._config[CONF_XY]): hs_color = kwargs[ATTR_HS_COLOR] message['color'] = {} - if self._config.get(CONF_RGB): + if self._config[CONF_RGB]: # If there's a brightness topic set, we don't want to scale the # RGB values given using the brightness. if self._brightness is not None: @@ -393,11 +392,11 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, message['color']['r'] = rgb[0] message['color']['g'] = rgb[1] message['color']['b'] = rgb[2] - if self._config.get(CONF_XY): + if self._config[CONF_XY]: xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) message['color']['x'] = xy_color[0] message['color']['y'] = xy_color[1] - if self._config.get(CONF_HS): + if self._config[CONF_HS]: message['color']['h'] = hs_color[0] message['color']['s'] = hs_color[1] @@ -416,10 +415,10 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, if ATTR_TRANSITION in kwargs: message['transition'] = int(kwargs[ATTR_TRANSITION]) - if ATTR_BRIGHTNESS in kwargs: + if ATTR_BRIGHTNESS in kwargs and self._brightness is not None: message['brightness'] = int( kwargs[ATTR_BRIGHTNESS] / float(DEFAULT_BRIGHTNESS_SCALE) * - self._config.get(CONF_BRIGHTNESS_SCALE)) + self._config[CONF_BRIGHTNESS_SCALE]) if self._optimistic: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -448,7 +447,7 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], json.dumps(message), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that the light has changed state. @@ -470,7 +469,7 @@ class MqttLightJson(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], json.dumps(message), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that the light has changed state. diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 27c1fb00441..49cba082401 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -51,23 +51,18 @@ PLATFORM_SCHEMA_TEMPLATE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_BLUE_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, vol.Optional(CONF_COLOR_TEMP_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, 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_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema).extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) @@ -150,7 +145,7 @@ class MqttTemplate(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, CONF_WHITE_VALUE_TEMPLATE, ) } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic \ or self._topics[CONF_STATE_TOPIC] is None \ or self._templates[CONF_STATE_TEMPLATE] is None @@ -257,7 +252,7 @@ class MqttTemplate(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, self.hass, self._sub_state, {'state_topic': {'topic': self._topics[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) if self._optimistic and last_state: self._state = last_state.state == STATE_ON @@ -310,7 +305,7 @@ class MqttTemplate(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @property def name(self): """Return the name of the entity.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -396,7 +391,7 @@ class MqttTemplate(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topics[CONF_COMMAND_TOPIC], self._templates[CONF_COMMAND_ON_TEMPLATE].async_render(**values), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN) + self._config[CONF_QOS], self._config[CONF_RETAIN] ) if self._optimistic: @@ -417,7 +412,7 @@ class MqttTemplate(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, mqtt.async_publish( self.hass, self._topics[CONF_COMMAND_TOPIC], self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render(**values), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN) + self._config[CONF_QOS], self._config[CONF_RETAIN] ) if self._optimistic: diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 172e6dbd8cf..1e325cec5ab 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -88,6 +88,7 @@ light: brightness_scale: 99 """ import json +from unittest import mock from unittest.mock import ANY, patch from homeassistant.components import light, mqtt @@ -101,6 +102,19 @@ from homeassistant.setup import async_setup_component from tests.common import ( MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component, mock_coro, mock_registry) +from tests.components.light import common + + +class JsonValidator(object): + """Helper to compare JSON.""" + + def __init__(self, jsondata): + """Constructor.""" + self.jsondata = jsondata + + def __eq__(self, other): + """Compare JSON data.""" + return json.loads(self.jsondata) == json.loads(other) async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): @@ -295,7 +309,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): 'brightness': True, 'color_temp': True, 'effect': True, + 'hs': True, 'rgb': True, + 'xy': True, 'white_value': True, 'qos': 2 } @@ -311,6 +327,65 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert 191 == state.attributes.get(ATTR_SUPPORTED_FEATURES) assert state.attributes.get(ATTR_ASSUMED_STATE) + common.async_turn_on(hass, 'light.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', '{"state": "ON"}', 2, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + + common.async_turn_off(hass, 'light.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', '{"state": "OFF"}', 2, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + mqtt_mock.reset_mock() + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 123, "b": 255,' + ' "x": 0.14, "y": 0.131, "h": 210.824, "s": 100.0},' + ' "brightness": 50}'), + 2, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 56, "b": 59,' + ' "x": 0.654, "y": 0.301, "h": 359.0, "s": 78.0},' + ' "brightness": 50}'), + 2, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0,' + ' "x": 0.611, "y": 0.375, "h": 30.118, "s": 100.0},' + ' "white_value": 80}'), + 2, False), + ], any_order=True) + + state = hass.states.get('light.test') + assert STATE_ON == state.state + assert (255, 128, 0) == state.attributes['rgb_color'] + assert 50 == state.attributes['brightness'] + assert (30.118, 100) == state.attributes['hs_color'] + assert 80 == state.attributes['white_value'] + assert (0.611, 0.375) == state.attributes['xy_color'] + async def test_sending_hs_color(hass, mqtt_mock): """Test light.turn_on with hs color sends hs color parameters.""" @@ -320,6 +395,7 @@ async def test_sending_hs_color(hass, mqtt_mock): 'schema': 'json', 'name': 'test', 'command_topic': 'test_light_rgb/set', + 'brightness': True, 'hs': True, } }) @@ -327,6 +403,170 @@ async def test_sending_hs_color(hass, mqtt_mock): state = hass.states.get('light.test') assert STATE_OFF == state.state + mqtt_mock.reset_mock() + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"h": 210.824, "s": 100.0},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"h": 359.0, "s": 78.0},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"h": 30.118, "s": 100.0},' + ' "white_value": 80}'), + 0, False), + ], any_order=True) + + +async def test_sending_rgb_color_no_brightness(hass, mqtt_mock): + """Test light.turn_on with hs color sends rgb color parameters.""" + assert await async_setup_component(hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'schema': 'json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'rgb': True, + } + }) + + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + brightness=255) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 24, "b": 50}}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 50, "g": 11, "b": 11}}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0}}'), + 0, False), + ], any_order=True) + + +async def test_sending_rgb_color_with_brightness(hass, mqtt_mock): + """Test light.turn_on with hs color sends rgb color parameters.""" + assert await async_setup_component(hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'schema': 'json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'rgb': True, + } + }) + + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 123, "b": 255},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 56, "b": 59},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0},' + ' "white_value": 80}'), + 0, False), + ], any_order=True) + + +async def test_sending_xy_color(hass, mqtt_mock): + """Test light.turn_on with hs color sends xy color parameters.""" + assert await async_setup_component(hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'schema': 'json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'xy': True, + } + }) + + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"x": 0.14, "y": 0.131},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"x": 0.654, "y": 0.301},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"x": 0.611, "y": 0.375},' + ' "white_value": 80}'), + 0, False), + ], any_order=True) + async def test_flash_short_and_long(hass, mqtt_mock): """Test for flash length being sent when included.""" @@ -335,7 +575,6 @@ async def test_flash_short_and_long(hass, mqtt_mock): 'platform': 'mqtt', 'schema': 'json', 'name': 'test', - 'state_topic': 'test_light_rgb', 'command_topic': 'test_light_rgb/set', 'flash_time_short': 5, 'flash_time_long': 15, @@ -347,6 +586,26 @@ async def test_flash_short_and_long(hass, mqtt_mock): assert STATE_OFF == state.state assert 40 == state.attributes.get(ATTR_SUPPORTED_FEATURES) + common.async_turn_on(hass, 'light.test', flash='short') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "ON", "flash": 5}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + + common.async_turn_on(hass, 'light.test', flash='long') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "ON", "flash": 15}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + async def test_transition(hass, mqtt_mock): """Test for transition time being sent when included.""" @@ -355,7 +614,6 @@ async def test_transition(hass, mqtt_mock): 'platform': 'mqtt', 'schema': 'json', 'name': 'test', - 'state_topic': 'test_light_rgb', 'command_topic': 'test_light_rgb/set', 'qos': 0 } @@ -365,6 +623,26 @@ async def test_transition(hass, mqtt_mock): assert STATE_OFF == state.state assert 40 == state.attributes.get(ATTR_SUPPORTED_FEATURES) + common.async_turn_on(hass, 'light.test', transition=15) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "ON", "transition": 15}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + + common.async_turn_off(hass, 'light.test', transition=30) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "OFF", "transition": 30}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_OFF == state.state + async def test_brightness_scale(hass, mqtt_mock): """Test for brightness scaling."""