diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 372cc76becc..b96a5e4a815 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -8,7 +8,9 @@ from homeassistant.components.light import ( ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, + ATTR_RGB_COLOR, ATTR_WHITE_VALUE, + ATTR_XY_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, @@ -167,13 +169,13 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self._command_templates = None self._value_templates = None self._optimistic = False - self._optimistic_rgb = False + self._optimistic_rgb_color = False self._optimistic_brightness = False self._optimistic_color_temp = False self._optimistic_effect = False - self._optimistic_hs = False + self._optimistic_hs_color = False self._optimistic_white_value = False - self._optimistic_xy = False + self._optimistic_xy_color = False MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -228,7 +230,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): 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 + self._optimistic_rgb_color = optimistic or topic[CONF_RGB_STATE_TOPIC] is None self._optimistic_brightness = ( optimistic or ( @@ -244,11 +246,15 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): optimistic or topic[CONF_COLOR_TEMP_STATE_TOPIC] is None ) self._optimistic_effect = optimistic or topic[CONF_EFFECT_STATE_TOPIC] is None - self._optimistic_hs = optimistic or topic[CONF_HS_STATE_TOPIC] is None + self._optimistic_hs_color = optimistic or topic[CONF_HS_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._optimistic_xy_color = optimistic or topic[CONF_XY_STATE_TOPIC] is None + + def _is_optimistic(self, attribute): + """Return True if the attribute is optimistically updated.""" + return getattr(self, f"_optimistic_{attribute}") async def _subscribe_topics(self): # noqa: C901 """(Re)Subscribe to topics.""" @@ -265,9 +271,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): "qos": self._config[CONF_QOS], } - def restore_state(attribute, condition): + def restore_state(attribute, condition_attribute=None): """Restore a state attribute.""" - if condition and last_state and last_state.attributes.get(attribute): + if condition_attribute is None: + condition_attribute = attribute + optimistic = self._is_optimistic(condition_attribute) + if optimistic and last_state and last_state.attributes.get(attribute): setattr(self, f"_{attribute}", last_state.attributes[attribute]) @callback @@ -313,7 +322,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.async_write_ha_state() add_topic(CONF_BRIGHTNESS_STATE_TOPIC, brightness_received) - restore_state(ATTR_BRIGHTNESS, self._optimistic_brightness) + restore_state(ATTR_BRIGHTNESS) @callback @log_messages(self.hass, self.entity_id) @@ -332,7 +341,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.async_write_ha_state() add_topic(CONF_RGB_STATE_TOPIC, rgb_received) - restore_state(ATTR_HS_COLOR, self._optimistic_rgb) + restore_state(ATTR_HS_COLOR, ATTR_RGB_COLOR) @callback @log_messages(self.hass, self.entity_id) @@ -349,7 +358,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.async_write_ha_state() add_topic(CONF_COLOR_TEMP_STATE_TOPIC, color_temp_received) - restore_state(ATTR_COLOR_TEMP, self._optimistic_color_temp) + restore_state(ATTR_COLOR_TEMP) @callback @log_messages(self.hass, self.entity_id) @@ -366,7 +375,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.async_write_ha_state() add_topic(CONF_EFFECT_STATE_TOPIC, effect_received) - restore_state(ATTR_EFFECT, self._optimistic_effect) + restore_state(ATTR_EFFECT) @callback @log_messages(self.hass, self.entity_id) @@ -385,7 +394,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Failed to parse hs state update: '%s'", payload) add_topic(CONF_HS_STATE_TOPIC, hs_received) - restore_state(ATTR_HS_COLOR, self._optimistic_hs) + restore_state(ATTR_HS_COLOR) @callback @log_messages(self.hass, self.entity_id) @@ -404,7 +413,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.async_write_ha_state() add_topic(CONF_WHITE_VALUE_STATE_TOPIC, white_value_received) - restore_state(ATTR_WHITE_VALUE, self._optimistic_white_value) + restore_state(ATTR_WHITE_VALUE) @callback @log_messages(self.hass, self.entity_id) @@ -420,7 +429,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.async_write_ha_state() add_topic(CONF_XY_STATE_TOPIC, xy_received) - restore_state(ATTR_HS_COLOR, self._optimistic_xy) + restore_state(ATTR_HS_COLOR, ATTR_XY_COLOR) self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, topics @@ -523,7 +532,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): return supported_features - async def async_turn_on(self, **kwargs): # noqa: C901 + async def async_turn_on(self, **kwargs): """Turn the device on. This method is a coroutine. @@ -541,6 +550,15 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self._config[CONF_RETAIN], ) + def set_optimistic(attribute, value, condition_attribute=None): + """Optimistically update a state attribute.""" + if condition_attribute is None: + condition_attribute = attribute + if not self._is_optimistic(condition_attribute): + return False + setattr(self, f"_{attribute}", value) + return True + if on_command_type == "first": publish(CONF_COMMAND_TOPIC, self._payload["on"]) should_update = True @@ -573,28 +591,18 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): rgb_color_str = f"{rgb[0]},{rgb[1]},{rgb[2]}" publish(CONF_RGB_COMMAND_TOPIC, rgb_color_str) - - if self._optimistic_rgb: - self._hs_color = kwargs[ATTR_HS_COLOR] - should_update = True + should_update |= set_optimistic(ATTR_HS_COLOR, hs_color, ATTR_RGB_COLOR) if ATTR_HS_COLOR in kwargs and self._topic[CONF_HS_COMMAND_TOPIC] is not None: - hs_color = kwargs[ATTR_HS_COLOR] publish(CONF_HS_COMMAND_TOPIC, f"{hs_color[0]},{hs_color[1]}") - - if self._optimistic_hs: - self._hs_color = kwargs[ATTR_HS_COLOR] - should_update = True + should_update |= set_optimistic(ATTR_HS_COLOR, hs_color) if ATTR_HS_COLOR in kwargs and self._topic[CONF_XY_COMMAND_TOPIC] is not None: xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) publish(CONF_XY_COMMAND_TOPIC, f"{xy_color[0]},{xy_color[1]}") - - if self._optimistic_xy: - self._hs_color = kwargs[ATTR_HS_COLOR] - should_update = True + should_update |= set_optimistic(ATTR_HS_COLOR, hs_color, ATTR_XY_COLOR) if ( ATTR_BRIGHTNESS in kwargs @@ -608,10 +616,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): # Make sure the brightness is not rounded down to 0 device_brightness = max(device_brightness, 1) publish(CONF_BRIGHTNESS_COMMAND_TOPIC, device_brightness) - - if self._optimistic_brightness: - self._brightness = kwargs[ATTR_BRIGHTNESS] - should_update = True + should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) elif ( ATTR_BRIGHTNESS in kwargs and ATTR_HS_COLOR not in kwargs @@ -628,10 +633,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): rgb_color_str = f"{rgb[0]},{rgb[1]},{rgb[2]}" publish(CONF_RGB_COMMAND_TOPIC, rgb_color_str) - - if self._optimistic_brightness: - self._brightness = kwargs[ATTR_BRIGHTNESS] - should_update = True + should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) if ( ATTR_COLOR_TEMP in kwargs @@ -644,19 +646,13 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): color_temp = tpl({"value": color_temp}) publish(CONF_COLOR_TEMP_COMMAND_TOPIC, color_temp) - - if self._optimistic_color_temp: - self._color_temp = kwargs[ATTR_COLOR_TEMP] - should_update = True + should_update |= set_optimistic(ATTR_COLOR_TEMP, kwargs[ATTR_COLOR_TEMP]) if ATTR_EFFECT in kwargs and self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None: effect = kwargs[ATTR_EFFECT] if effect in self._config.get(CONF_EFFECT_LIST): publish(CONF_EFFECT_COMMAND_TOPIC, effect) - - if self._optimistic_effect: - self._effect = kwargs[ATTR_EFFECT] - should_update = True + should_update |= set_optimistic(ATTR_EFFECT, effect) if ( ATTR_WHITE_VALUE in kwargs @@ -666,10 +662,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): white_scale = self._config[CONF_WHITE_VALUE_SCALE] device_white_value = min(round(percent_white * white_scale), white_scale) publish(CONF_WHITE_VALUE_COMMAND_TOPIC, device_white_value) - - if self._optimistic_white_value: - self._white_value = kwargs[ATTR_WHITE_VALUE] - should_update = True + should_update |= set_optimistic(ATTR_WHITE_VALUE, kwargs[ATTR_WHITE_VALUE]) if on_command_type == "last": publish(CONF_COMMAND_TOPIC, self._payload["on"])