mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 22:57:17 +00:00
MQTT climate preset_modes rework (#66062)
* MQTT climate preset_modes rework * Set deprection date to 2022.9 (6 months) * add valid_preset_mode_configuration for discovery * Update deprecation date
This commit is contained in:
parent
4236764fd5
commit
83846bb5cc
@ -80,6 +80,7 @@ CONF_ACTION_TOPIC = "action_topic"
|
|||||||
CONF_AUX_COMMAND_TOPIC = "aux_command_topic"
|
CONF_AUX_COMMAND_TOPIC = "aux_command_topic"
|
||||||
CONF_AUX_STATE_TEMPLATE = "aux_state_template"
|
CONF_AUX_STATE_TEMPLATE = "aux_state_template"
|
||||||
CONF_AUX_STATE_TOPIC = "aux_state_topic"
|
CONF_AUX_STATE_TOPIC = "aux_state_topic"
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_AWAY_MODE_COMMAND_TOPIC = "away_mode_command_topic"
|
CONF_AWAY_MODE_COMMAND_TOPIC = "away_mode_command_topic"
|
||||||
CONF_AWAY_MODE_STATE_TEMPLATE = "away_mode_state_template"
|
CONF_AWAY_MODE_STATE_TEMPLATE = "away_mode_state_template"
|
||||||
CONF_AWAY_MODE_STATE_TOPIC = "away_mode_state_topic"
|
CONF_AWAY_MODE_STATE_TOPIC = "away_mode_state_topic"
|
||||||
@ -90,6 +91,7 @@ CONF_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic"
|
|||||||
CONF_FAN_MODE_LIST = "fan_modes"
|
CONF_FAN_MODE_LIST = "fan_modes"
|
||||||
CONF_FAN_MODE_STATE_TEMPLATE = "fan_mode_state_template"
|
CONF_FAN_MODE_STATE_TEMPLATE = "fan_mode_state_template"
|
||||||
CONF_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic"
|
CONF_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic"
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_HOLD_COMMAND_TEMPLATE = "hold_command_template"
|
CONF_HOLD_COMMAND_TEMPLATE = "hold_command_template"
|
||||||
CONF_HOLD_COMMAND_TOPIC = "hold_command_topic"
|
CONF_HOLD_COMMAND_TOPIC = "hold_command_topic"
|
||||||
CONF_HOLD_STATE_TEMPLATE = "hold_state_template"
|
CONF_HOLD_STATE_TEMPLATE = "hold_state_template"
|
||||||
@ -104,7 +106,12 @@ CONF_POWER_COMMAND_TOPIC = "power_command_topic"
|
|||||||
CONF_POWER_STATE_TEMPLATE = "power_state_template"
|
CONF_POWER_STATE_TEMPLATE = "power_state_template"
|
||||||
CONF_POWER_STATE_TOPIC = "power_state_topic"
|
CONF_POWER_STATE_TOPIC = "power_state_topic"
|
||||||
CONF_PRECISION = "precision"
|
CONF_PRECISION = "precision"
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
CONF_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic"
|
||||||
|
CONF_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic"
|
||||||
|
CONF_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template"
|
||||||
|
CONF_PRESET_MODE_COMMAND_TEMPLATE = "preset_mode_command_template"
|
||||||
|
CONF_PRESET_MODES_LIST = "preset_modes"
|
||||||
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
CONF_SEND_IF_OFF = "send_if_off"
|
CONF_SEND_IF_OFF = "send_if_off"
|
||||||
CONF_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_command_template"
|
CONF_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_command_template"
|
||||||
CONF_SWING_MODE_COMMAND_TOPIC = "swing_mode_command_topic"
|
CONF_SWING_MODE_COMMAND_TOPIC = "swing_mode_command_topic"
|
||||||
@ -155,13 +162,16 @@ MQTT_CLIMATE_ATTRIBUTES_BLOCKED = frozenset(
|
|||||||
|
|
||||||
VALUE_TEMPLATE_KEYS = (
|
VALUE_TEMPLATE_KEYS = (
|
||||||
CONF_AUX_STATE_TEMPLATE,
|
CONF_AUX_STATE_TEMPLATE,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_AWAY_MODE_STATE_TEMPLATE,
|
CONF_AWAY_MODE_STATE_TEMPLATE,
|
||||||
CONF_CURRENT_TEMP_TEMPLATE,
|
CONF_CURRENT_TEMP_TEMPLATE,
|
||||||
CONF_FAN_MODE_STATE_TEMPLATE,
|
CONF_FAN_MODE_STATE_TEMPLATE,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_HOLD_STATE_TEMPLATE,
|
CONF_HOLD_STATE_TEMPLATE,
|
||||||
CONF_MODE_STATE_TEMPLATE,
|
CONF_MODE_STATE_TEMPLATE,
|
||||||
CONF_POWER_STATE_TEMPLATE,
|
CONF_POWER_STATE_TEMPLATE,
|
||||||
CONF_ACTION_TEMPLATE,
|
CONF_ACTION_TEMPLATE,
|
||||||
|
CONF_PRESET_MODE_VALUE_TEMPLATE,
|
||||||
CONF_SWING_MODE_STATE_TEMPLATE,
|
CONF_SWING_MODE_STATE_TEMPLATE,
|
||||||
CONF_TEMP_HIGH_STATE_TEMPLATE,
|
CONF_TEMP_HIGH_STATE_TEMPLATE,
|
||||||
CONF_TEMP_LOW_STATE_TEMPLATE,
|
CONF_TEMP_LOW_STATE_TEMPLATE,
|
||||||
@ -170,29 +180,48 @@ VALUE_TEMPLATE_KEYS = (
|
|||||||
|
|
||||||
COMMAND_TEMPLATE_KEYS = {
|
COMMAND_TEMPLATE_KEYS = {
|
||||||
CONF_FAN_MODE_COMMAND_TEMPLATE,
|
CONF_FAN_MODE_COMMAND_TEMPLATE,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_HOLD_COMMAND_TEMPLATE,
|
CONF_HOLD_COMMAND_TEMPLATE,
|
||||||
CONF_MODE_COMMAND_TEMPLATE,
|
CONF_MODE_COMMAND_TEMPLATE,
|
||||||
|
CONF_PRESET_MODE_COMMAND_TEMPLATE,
|
||||||
CONF_SWING_MODE_COMMAND_TEMPLATE,
|
CONF_SWING_MODE_COMMAND_TEMPLATE,
|
||||||
CONF_TEMP_COMMAND_TEMPLATE,
|
CONF_TEMP_COMMAND_TEMPLATE,
|
||||||
CONF_TEMP_HIGH_COMMAND_TEMPLATE,
|
CONF_TEMP_HIGH_COMMAND_TEMPLATE,
|
||||||
CONF_TEMP_LOW_COMMAND_TEMPLATE,
|
CONF_TEMP_LOW_COMMAND_TEMPLATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
DEPRECATED_INVALID = [
|
||||||
|
CONF_AWAY_MODE_COMMAND_TOPIC,
|
||||||
|
CONF_AWAY_MODE_STATE_TEMPLATE,
|
||||||
|
CONF_AWAY_MODE_STATE_TOPIC,
|
||||||
|
CONF_HOLD_COMMAND_TEMPLATE,
|
||||||
|
CONF_HOLD_COMMAND_TOPIC,
|
||||||
|
CONF_HOLD_STATE_TEMPLATE,
|
||||||
|
CONF_HOLD_STATE_TOPIC,
|
||||||
|
CONF_HOLD_LIST,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
TOPIC_KEYS = (
|
TOPIC_KEYS = (
|
||||||
|
CONF_ACTION_TOPIC,
|
||||||
CONF_AUX_COMMAND_TOPIC,
|
CONF_AUX_COMMAND_TOPIC,
|
||||||
CONF_AUX_STATE_TOPIC,
|
CONF_AUX_STATE_TOPIC,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_AWAY_MODE_COMMAND_TOPIC,
|
CONF_AWAY_MODE_COMMAND_TOPIC,
|
||||||
CONF_AWAY_MODE_STATE_TOPIC,
|
CONF_AWAY_MODE_STATE_TOPIC,
|
||||||
CONF_CURRENT_TEMP_TOPIC,
|
CONF_CURRENT_TEMP_TOPIC,
|
||||||
CONF_FAN_MODE_COMMAND_TOPIC,
|
CONF_FAN_MODE_COMMAND_TOPIC,
|
||||||
CONF_FAN_MODE_STATE_TOPIC,
|
CONF_FAN_MODE_STATE_TOPIC,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
CONF_HOLD_COMMAND_TOPIC,
|
CONF_HOLD_COMMAND_TOPIC,
|
||||||
CONF_HOLD_STATE_TOPIC,
|
CONF_HOLD_STATE_TOPIC,
|
||||||
CONF_MODE_COMMAND_TOPIC,
|
CONF_MODE_COMMAND_TOPIC,
|
||||||
CONF_MODE_STATE_TOPIC,
|
CONF_MODE_STATE_TOPIC,
|
||||||
CONF_POWER_COMMAND_TOPIC,
|
CONF_POWER_COMMAND_TOPIC,
|
||||||
CONF_POWER_STATE_TOPIC,
|
CONF_POWER_STATE_TOPIC,
|
||||||
CONF_ACTION_TOPIC,
|
CONF_PRESET_MODE_COMMAND_TOPIC,
|
||||||
|
CONF_PRESET_MODE_STATE_TOPIC,
|
||||||
CONF_SWING_MODE_COMMAND_TOPIC,
|
CONF_SWING_MODE_COMMAND_TOPIC,
|
||||||
CONF_SWING_MODE_STATE_TOPIC,
|
CONF_SWING_MODE_STATE_TOPIC,
|
||||||
CONF_TEMP_COMMAND_TOPIC,
|
CONF_TEMP_COMMAND_TOPIC,
|
||||||
@ -203,12 +232,27 @@ TOPIC_KEYS = (
|
|||||||
CONF_TEMP_STATE_TOPIC,
|
CONF_TEMP_STATE_TOPIC,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def valid_preset_mode_configuration(config):
|
||||||
|
"""Validate that the preset mode reset payload is not one of the preset modes."""
|
||||||
|
if PRESET_NONE in config.get(CONF_PRESET_MODES_LIST):
|
||||||
|
raise ValueError("preset_modes must not include preset mode 'none'")
|
||||||
|
if config.get(CONF_PRESET_MODE_COMMAND_TOPIC):
|
||||||
|
for config_parameter in DEPRECATED_INVALID:
|
||||||
|
if config.get(config_parameter):
|
||||||
|
raise vol.MultipleInvalid(
|
||||||
|
"preset_modes cannot be used with deprecated away or hold mode config options"
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
SCHEMA_BASE = CLIMATE_PLATFORM_SCHEMA.extend(MQTT_BASE_PLATFORM_SCHEMA.schema)
|
SCHEMA_BASE = CLIMATE_PLATFORM_SCHEMA.extend(MQTT_BASE_PLATFORM_SCHEMA.schema)
|
||||||
_PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend(
|
_PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template,
|
vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_AWAY_MODE_STATE_TEMPLATE): cv.template,
|
vol.Optional(CONF_AWAY_MODE_STATE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
@ -222,6 +266,7 @@ _PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend(
|
|||||||
): cv.ensure_list,
|
): cv.ensure_list,
|
||||||
vol.Optional(CONF_FAN_MODE_STATE_TEMPLATE): cv.template,
|
vol.Optional(CONF_FAN_MODE_STATE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
vol.Optional(CONF_HOLD_COMMAND_TEMPLATE): cv.template,
|
vol.Optional(CONF_HOLD_COMMAND_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(CONF_HOLD_STATE_TEMPLATE): cv.template,
|
vol.Optional(CONF_HOLD_STATE_TEMPLATE): cv.template,
|
||||||
@ -252,10 +297,20 @@ _PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend(
|
|||||||
[PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE]
|
[PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE]
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean,
|
vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean,
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
vol.Optional(CONF_SEND_IF_OFF, default=True): cv.boolean,
|
vol.Optional(CONF_SEND_IF_OFF, default=True): cv.boolean,
|
||||||
vol.Optional(CONF_ACTION_TEMPLATE): cv.template,
|
vol.Optional(CONF_ACTION_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_ACTION_TOPIC): mqtt.valid_subscribe_topic,
|
vol.Optional(CONF_ACTION_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
|
# CONF_PRESET_MODE_COMMAND_TOPIC and CONF_PRESET_MODES_LIST must be used together
|
||||||
|
vol.Inclusive(
|
||||||
|
CONF_PRESET_MODE_COMMAND_TOPIC, "preset_modes"
|
||||||
|
): mqtt.valid_publish_topic,
|
||||||
|
vol.Inclusive(
|
||||||
|
CONF_PRESET_MODES_LIST, "preset_modes", default=[]
|
||||||
|
): cv.ensure_list,
|
||||||
|
vol.Optional(CONF_PRESET_MODE_COMMAND_TEMPLATE): cv.template,
|
||||||
|
vol.Optional(CONF_PRESET_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||||
|
vol.Optional(CONF_PRESET_MODE_VALUE_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_SWING_MODE_COMMAND_TEMPLATE): cv.template,
|
vol.Optional(CONF_SWING_MODE_COMMAND_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
@ -285,17 +340,37 @@ _PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend(
|
|||||||
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
||||||
|
|
||||||
PLATFORM_SCHEMA = vol.All(
|
PLATFORM_SCHEMA = vol.All(
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
|
||||||
cv.deprecated(CONF_SEND_IF_OFF),
|
|
||||||
_PLATFORM_SCHEMA_BASE,
|
_PLATFORM_SCHEMA_BASE,
|
||||||
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
|
cv.deprecated(CONF_SEND_IF_OFF),
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
cv.deprecated(CONF_AWAY_MODE_COMMAND_TOPIC),
|
||||||
|
cv.deprecated(CONF_AWAY_MODE_STATE_TEMPLATE),
|
||||||
|
cv.deprecated(CONF_AWAY_MODE_STATE_TOPIC),
|
||||||
|
cv.deprecated(CONF_HOLD_COMMAND_TEMPLATE),
|
||||||
|
cv.deprecated(CONF_HOLD_COMMAND_TOPIC),
|
||||||
|
cv.deprecated(CONF_HOLD_STATE_TEMPLATE),
|
||||||
|
cv.deprecated(CONF_HOLD_STATE_TOPIC),
|
||||||
|
cv.deprecated(CONF_HOLD_LIST),
|
||||||
|
valid_preset_mode_configuration,
|
||||||
)
|
)
|
||||||
|
|
||||||
_DISCOVERY_SCHEMA_BASE = _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA)
|
_DISCOVERY_SCHEMA_BASE = _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA)
|
||||||
|
|
||||||
DISCOVERY_SCHEMA = vol.All(
|
DISCOVERY_SCHEMA = vol.All(
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
|
||||||
cv.deprecated(CONF_SEND_IF_OFF),
|
|
||||||
_DISCOVERY_SCHEMA_BASE,
|
_DISCOVERY_SCHEMA_BASE,
|
||||||
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
|
cv.deprecated(CONF_SEND_IF_OFF),
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
cv.deprecated(CONF_AWAY_MODE_COMMAND_TOPIC),
|
||||||
|
cv.deprecated(CONF_AWAY_MODE_STATE_TEMPLATE),
|
||||||
|
cv.deprecated(CONF_AWAY_MODE_STATE_TOPIC),
|
||||||
|
cv.deprecated(CONF_HOLD_COMMAND_TEMPLATE),
|
||||||
|
cv.deprecated(CONF_HOLD_COMMAND_TOPIC),
|
||||||
|
cv.deprecated(CONF_HOLD_STATE_TEMPLATE),
|
||||||
|
cv.deprecated(CONF_HOLD_STATE_TOPIC),
|
||||||
|
cv.deprecated(CONF_HOLD_LIST),
|
||||||
|
valid_preset_mode_configuration,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -346,12 +421,15 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
self._current_swing_mode = None
|
self._current_swing_mode = None
|
||||||
self._current_temp = None
|
self._current_temp = None
|
||||||
self._hold = None
|
self._hold = None
|
||||||
|
self._preset_mode = None
|
||||||
self._target_temp = None
|
self._target_temp = None
|
||||||
self._target_temp_high = None
|
self._target_temp_high = None
|
||||||
self._target_temp_low = None
|
self._target_temp_low = None
|
||||||
self._topic = None
|
self._topic = None
|
||||||
self._value_templates = None
|
self._value_templates = None
|
||||||
self._command_templates = None
|
self._command_templates = None
|
||||||
|
self._feature_preset_mode = False
|
||||||
|
self._optimistic_preset_mode = None
|
||||||
|
|
||||||
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
MqttEntity.__init__(self, hass, config, config_entry, discovery_data)
|
||||||
|
|
||||||
@ -384,7 +462,14 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
self._current_swing_mode = HVAC_MODE_OFF
|
self._current_swing_mode = HVAC_MODE_OFF
|
||||||
if self._topic[CONF_MODE_STATE_TOPIC] is None:
|
if self._topic[CONF_MODE_STATE_TOPIC] is None:
|
||||||
self._current_operation = HVAC_MODE_OFF
|
self._current_operation = HVAC_MODE_OFF
|
||||||
|
self._feature_preset_mode = CONF_PRESET_MODE_COMMAND_TOPIC in config
|
||||||
|
if self._feature_preset_mode:
|
||||||
|
self._preset_modes = config[CONF_PRESET_MODES_LIST]
|
||||||
|
else:
|
||||||
|
self._preset_modes = []
|
||||||
|
self._optimistic_preset_mode = CONF_PRESET_MODE_STATE_TOPIC not in config
|
||||||
self._action = None
|
self._action = None
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
self._away = False
|
self._away = False
|
||||||
self._hold = None
|
self._hold = None
|
||||||
self._aux = False
|
self._aux = False
|
||||||
@ -582,6 +667,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def handle_away_mode_received(msg):
|
def handle_away_mode_received(msg):
|
||||||
@ -598,6 +684,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
|
|
||||||
add_subscription(topics, CONF_AUX_STATE_TOPIC, handle_aux_mode_received)
|
add_subscription(topics, CONF_AUX_STATE_TOPIC, handle_aux_mode_received)
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def handle_hold_mode_received(msg):
|
def handle_hold_mode_received(msg):
|
||||||
@ -608,10 +695,38 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
payload = None
|
payload = None
|
||||||
|
|
||||||
self._hold = payload
|
self._hold = payload
|
||||||
|
self._preset_mode = None
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
add_subscription(topics, CONF_HOLD_STATE_TOPIC, handle_hold_mode_received)
|
add_subscription(topics, CONF_HOLD_STATE_TOPIC, handle_hold_mode_received)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@log_messages(self.hass, self.entity_id)
|
||||||
|
def handle_preset_mode_received(msg):
|
||||||
|
"""Handle receiving preset mode via MQTT."""
|
||||||
|
preset_mode = render_template(msg, CONF_PRESET_MODE_VALUE_TEMPLATE)
|
||||||
|
if preset_mode in [PRESET_NONE, PAYLOAD_NONE]:
|
||||||
|
self._preset_mode = None
|
||||||
|
self.async_write_ha_state()
|
||||||
|
return
|
||||||
|
if not preset_mode:
|
||||||
|
_LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic)
|
||||||
|
return
|
||||||
|
if preset_mode not in self._preset_modes:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"'%s' received on topic %s. '%s' is not a valid preset mode",
|
||||||
|
msg.payload,
|
||||||
|
msg.topic,
|
||||||
|
preset_mode,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._preset_mode = preset_mode
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
add_subscription(
|
||||||
|
topics, CONF_PRESET_MODE_STATE_TOPIC, handle_preset_mode_received
|
||||||
|
)
|
||||||
|
|
||||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||||
self.hass, self._sub_state, topics
|
self.hass, self._sub_state, topics
|
||||||
)
|
)
|
||||||
@ -668,8 +783,11 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
return self._config[CONF_TEMP_STEP]
|
return self._config[CONF_TEMP_STEP]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_mode(self):
|
def preset_mode(self) -> str | None:
|
||||||
"""Return preset mode."""
|
"""Return preset mode."""
|
||||||
|
if self._feature_preset_mode and self._preset_mode is not None:
|
||||||
|
return self._preset_mode
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
if self._hold:
|
if self._hold:
|
||||||
return self._hold
|
return self._hold
|
||||||
if self._away:
|
if self._away:
|
||||||
@ -677,10 +795,12 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
return PRESET_NONE
|
return PRESET_NONE
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_modes(self):
|
def preset_modes(self) -> list:
|
||||||
"""Return preset modes."""
|
"""Return preset modes."""
|
||||||
presets = []
|
presets = []
|
||||||
|
presets.extend(self._preset_modes)
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
if (self._topic[CONF_AWAY_MODE_STATE_TOPIC] is not None) or (
|
if (self._topic[CONF_AWAY_MODE_STATE_TOPIC] is not None) or (
|
||||||
self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None
|
self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None
|
||||||
):
|
):
|
||||||
@ -726,7 +846,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
# optimistic mode
|
# optimistic mode
|
||||||
setattr(self, attr, temp)
|
setattr(self, attr, temp)
|
||||||
|
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
if (
|
if (
|
||||||
self._config[CONF_SEND_IF_OFF]
|
self._config[CONF_SEND_IF_OFF]
|
||||||
or self._current_operation != HVAC_MODE_OFF
|
or self._current_operation != HVAC_MODE_OFF
|
||||||
@ -769,7 +889,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
|
|
||||||
async def async_set_swing_mode(self, swing_mode):
|
async def async_set_swing_mode(self, swing_mode):
|
||||||
"""Set new swing mode."""
|
"""Set new swing mode."""
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
if self._config[CONF_SEND_IF_OFF] or self._current_operation != HVAC_MODE_OFF:
|
if self._config[CONF_SEND_IF_OFF] or self._current_operation != HVAC_MODE_OFF:
|
||||||
payload = self._command_templates[CONF_SWING_MODE_COMMAND_TEMPLATE](
|
payload = self._command_templates[CONF_SWING_MODE_COMMAND_TEMPLATE](
|
||||||
swing_mode
|
swing_mode
|
||||||
@ -782,7 +902,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
|
|
||||||
async def async_set_fan_mode(self, fan_mode):
|
async def async_set_fan_mode(self, fan_mode):
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.4
|
# CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9
|
||||||
if self._config[CONF_SEND_IF_OFF] or self._current_operation != HVAC_MODE_OFF:
|
if self._config[CONF_SEND_IF_OFF] or self._current_operation != HVAC_MODE_OFF:
|
||||||
payload = self._command_templates[CONF_FAN_MODE_COMMAND_TEMPLATE](fan_mode)
|
payload = self._command_templates[CONF_FAN_MODE_COMMAND_TEMPLATE](fan_mode)
|
||||||
await self._publish(CONF_FAN_MODE_COMMAND_TOPIC, payload)
|
await self._publish(CONF_FAN_MODE_COMMAND_TOPIC, payload)
|
||||||
@ -817,11 +937,29 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
"""List of available swing modes."""
|
"""List of available swing modes."""
|
||||||
return self._config[CONF_SWING_MODE_LIST]
|
return self._config[CONF_SWING_MODE_LIST]
|
||||||
|
|
||||||
async def async_set_preset_mode(self, preset_mode):
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||||
"""Set a preset mode."""
|
"""Set a preset mode."""
|
||||||
# Track if we should optimistic update the state
|
if self._feature_preset_mode:
|
||||||
|
if preset_mode not in self.preset_modes and preset_mode is not PRESET_NONE:
|
||||||
|
_LOGGER.warning("'%s' is not a valid preset mode", preset_mode)
|
||||||
|
return
|
||||||
|
mqtt_payload = self._command_templates[CONF_PRESET_MODE_COMMAND_TEMPLATE](
|
||||||
|
preset_mode
|
||||||
|
)
|
||||||
|
await self._publish(
|
||||||
|
CONF_PRESET_MODE_COMMAND_TOPIC,
|
||||||
|
mqtt_payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._optimistic_preset_mode:
|
||||||
|
self._preset_mode = preset_mode if preset_mode != PRESET_NONE else None
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update hold or away mode: Track if we should optimistic update the state
|
||||||
optimistic_update = await self._set_away_mode(preset_mode == PRESET_AWAY)
|
optimistic_update = await self._set_away_mode(preset_mode == PRESET_AWAY)
|
||||||
hold_mode = preset_mode
|
hold_mode: str | None = preset_mode
|
||||||
if preset_mode in [PRESET_NONE, PRESET_AWAY]:
|
if preset_mode in [PRESET_NONE, PRESET_AWAY]:
|
||||||
hold_mode = None
|
hold_mode = None
|
||||||
optimistic_update = await self._set_hold_mode(hold_mode) or optimistic_update
|
optimistic_update = await self._set_hold_mode(hold_mode) or optimistic_update
|
||||||
@ -829,6 +967,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
if optimistic_update:
|
if optimistic_update:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def _set_away_mode(self, state):
|
async def _set_away_mode(self, state):
|
||||||
"""Set away mode.
|
"""Set away mode.
|
||||||
|
|
||||||
@ -909,8 +1048,10 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||||||
):
|
):
|
||||||
support |= SUPPORT_SWING_MODE
|
support |= SUPPORT_SWING_MODE
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
if (
|
if (
|
||||||
(self._topic[CONF_AWAY_MODE_STATE_TOPIC] is not None)
|
self._feature_preset_mode
|
||||||
|
or (self._topic[CONF_AWAY_MODE_STATE_TOPIC] is not None)
|
||||||
or (self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None)
|
or (self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None)
|
||||||
or (self._topic[CONF_HOLD_STATE_TOPIC] is not None)
|
or (self._topic[CONF_HOLD_STATE_TOPIC] is not None)
|
||||||
or (self._topic[CONF_HOLD_COMMAND_TOPIC] is not None)
|
or (self._topic[CONF_HOLD_COMMAND_TOPIC] is not None)
|
||||||
|
@ -82,9 +82,34 @@ DEFAULT_CONFIG = {
|
|||||||
"temperature_high_command_topic": "temperature-high-topic",
|
"temperature_high_command_topic": "temperature-high-topic",
|
||||||
"fan_mode_command_topic": "fan-mode-topic",
|
"fan_mode_command_topic": "fan-mode-topic",
|
||||||
"swing_mode_command_topic": "swing-mode-topic",
|
"swing_mode_command_topic": "swing-mode-topic",
|
||||||
|
"aux_command_topic": "aux-topic",
|
||||||
|
"preset_mode_command_topic": "preset-mode-topic",
|
||||||
|
"preset_modes": [
|
||||||
|
"eco",
|
||||||
|
"away",
|
||||||
|
"boost",
|
||||||
|
"comfort",
|
||||||
|
"home",
|
||||||
|
"sleep",
|
||||||
|
"activity",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
DEFAULT_LEGACY_CONFIG = {
|
||||||
|
CLIMATE_DOMAIN: {
|
||||||
|
"platform": "mqtt",
|
||||||
|
"name": "test",
|
||||||
|
"mode_command_topic": "mode-topic",
|
||||||
|
"temperature_command_topic": "temperature-topic",
|
||||||
|
"temperature_low_command_topic": "temperature-low-topic",
|
||||||
|
"temperature_high_command_topic": "temperature-high-topic",
|
||||||
|
"fan_mode_command_topic": "fan-mode-topic",
|
||||||
|
"swing_mode_command_topic": "swing-mode-topic",
|
||||||
|
"aux_command_topic": "aux-topic",
|
||||||
"away_mode_command_topic": "away-mode-topic",
|
"away_mode_command_topic": "away-mode-topic",
|
||||||
"hold_command_topic": "hold-topic",
|
"hold_command_topic": "hold-topic",
|
||||||
"aux_command_topic": "aux-topic",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +128,42 @@ async def test_setup_params(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("max_temp") == DEFAULT_MAX_TEMP
|
assert state.attributes.get("max_temp") == DEFAULT_MAX_TEMP
|
||||||
|
|
||||||
|
|
||||||
|
async def test_preset_none_in_preset_modes(hass, mqtt_mock, caplog):
|
||||||
|
"""Test the preset mode payload reset configuration."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
|
||||||
|
config["preset_modes"].append("none")
|
||||||
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert "Invalid config for [climate.mqtt]: not a valid value" in caplog.text
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state is None
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"parameter,config_value",
|
||||||
|
[
|
||||||
|
("away_mode_command_topic", "away-mode-command-topic"),
|
||||||
|
("away_mode_state_topic", "away-mode-state-topic"),
|
||||||
|
("away_mode_state_template", "{{ value_json }}"),
|
||||||
|
("hold_mode_command_topic", "hold-mode-command-topic"),
|
||||||
|
("hold_mode_command_template", "hold-mode-command-template"),
|
||||||
|
("hold_mode_state_topic", "hold-mode-state-topic"),
|
||||||
|
("hold_mode_state_template", "{{ value_json }}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_preset_modes_deprecation_guard(
|
||||||
|
hass, mqtt_mock, caplog, parameter, config_value
|
||||||
|
):
|
||||||
|
"""Test the configuration for invalid legacy parameters."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
|
||||||
|
config[parameter] = config_value
|
||||||
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state is None
|
||||||
|
|
||||||
|
|
||||||
async def test_supported_features(hass, mqtt_mock):
|
async def test_supported_features(hass, mqtt_mock):
|
||||||
"""Test the supported_features."""
|
"""Test the supported_features."""
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
|
||||||
@ -469,9 +530,99 @@ async def test_handle_action_received(hass, mqtt_mock):
|
|||||||
assert hvac_action == action
|
assert hvac_action == action
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_preset_mode_optimistic(hass, mqtt_mock, caplog):
|
||||||
|
"""Test setting of the preset mode."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
await common.async_set_preset_mode(hass, "away", ENTITY_CLIMATE)
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
"preset-mode-topic", "away", 0, False
|
||||||
|
)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "away"
|
||||||
|
|
||||||
|
await common.async_set_preset_mode(hass, "eco", ENTITY_CLIMATE)
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
"preset-mode-topic", "eco", 0, False
|
||||||
|
)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "eco"
|
||||||
|
|
||||||
|
await common.async_set_preset_mode(hass, "none", ENTITY_CLIMATE)
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
"preset-mode-topic", "none", 0, False
|
||||||
|
)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
await common.async_set_preset_mode(hass, "comfort", ENTITY_CLIMATE)
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
"preset-mode-topic", "comfort", 0, False
|
||||||
|
)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "comfort"
|
||||||
|
|
||||||
|
await common.async_set_preset_mode(hass, "invalid", ENTITY_CLIMATE)
|
||||||
|
assert "'invalid' is not a valid preset mode" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_preset_mode_pessimistic(hass, mqtt_mock, caplog):
|
||||||
|
"""Test setting of the preset mode."""
|
||||||
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
config["climate"]["preset_mode_state_topic"] = "preset-mode-state"
|
||||||
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "away")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "away"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "eco")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "eco"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "none")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "comfort")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "comfort"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "None")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "home")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "home"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "preset-mode-state", "nonsense")
|
||||||
|
assert (
|
||||||
|
"'nonsense' received on topic preset-mode-state. 'nonsense' is not a valid preset mode"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "home"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_away_mode_pessimistic(hass, mqtt_mock):
|
async def test_set_away_mode_pessimistic(hass, mqtt_mock):
|
||||||
"""Test setting of the away mode."""
|
"""Test setting of the away mode."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_LEGACY_CONFIG)
|
||||||
config["climate"]["away_mode_state_topic"] = "away-state"
|
config["climate"]["away_mode_state_topic"] = "away-state"
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -496,9 +647,10 @@ async def test_set_away_mode_pessimistic(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("preset_mode") == "none"
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_away_mode(hass, mqtt_mock):
|
async def test_set_away_mode(hass, mqtt_mock):
|
||||||
"""Test setting of the away mode."""
|
"""Test setting of the away mode."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_LEGACY_CONFIG)
|
||||||
config["climate"]["payload_on"] = "AN"
|
config["climate"]["payload_on"] = "AN"
|
||||||
config["climate"]["payload_off"] = "AUS"
|
config["climate"]["payload_off"] = "AUS"
|
||||||
|
|
||||||
@ -537,9 +689,10 @@ async def test_set_away_mode(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("preset_mode") == "away"
|
assert state.attributes.get("preset_mode") == "away"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_hold_pessimistic(hass, mqtt_mock):
|
async def test_set_hold_pessimistic(hass, mqtt_mock):
|
||||||
"""Test setting the hold mode in pessimistic mode."""
|
"""Test setting the hold mode in pessimistic mode."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_LEGACY_CONFIG)
|
||||||
config["climate"]["hold_state_topic"] = "hold-state"
|
config["climate"]["hold_state_topic"] = "hold-state"
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -560,9 +713,10 @@ async def test_set_hold_pessimistic(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("preset_mode") == "none"
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_hold(hass, mqtt_mock):
|
async def test_set_hold(hass, mqtt_mock):
|
||||||
"""Test setting the hold mode."""
|
"""Test setting the hold mode."""
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_LEGACY_CONFIG)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
@ -591,9 +745,10 @@ async def test_set_hold(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("preset_mode") == "none"
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_preset_away(hass, mqtt_mock):
|
async def test_set_preset_away(hass, mqtt_mock):
|
||||||
"""Test setting the hold mode and away mode."""
|
"""Test setting the hold mode and away mode."""
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_LEGACY_CONFIG)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
@ -624,9 +779,10 @@ async def test_set_preset_away(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("preset_mode") == "hold-on-again"
|
assert state.attributes.get("preset_mode") == "hold-on-again"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_preset_away_pessimistic(hass, mqtt_mock):
|
async def test_set_preset_away_pessimistic(hass, mqtt_mock):
|
||||||
"""Test setting the hold mode and away mode in pessimistic mode."""
|
"""Test setting the hold mode and away mode in pessimistic mode."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_LEGACY_CONFIG)
|
||||||
config["climate"]["hold_state_topic"] = "hold-state"
|
config["climate"]["hold_state_topic"] = "hold-state"
|
||||||
config["climate"]["away_mode_state_topic"] = "away-state"
|
config["climate"]["away_mode_state_topic"] = "away-state"
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
@ -674,9 +830,10 @@ async def test_set_preset_away_pessimistic(hass, mqtt_mock):
|
|||||||
assert state.attributes.get("preset_mode") == "hold-on-again"
|
assert state.attributes.get("preset_mode") == "hold-on-again"
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
async def test_set_preset_mode_twice(hass, mqtt_mock):
|
async def test_set_preset_mode_twice(hass, mqtt_mock):
|
||||||
"""Test setting of the same mode twice only publishes once."""
|
"""Test setting of the same mode twice only publishes once."""
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_LEGACY_CONFIG)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
@ -804,21 +961,19 @@ async def test_get_with_templates(hass, mqtt_mock, caplog):
|
|||||||
# By default, just unquote the JSON-strings
|
# By default, just unquote the JSON-strings
|
||||||
config["climate"]["value_template"] = "{{ value_json }}"
|
config["climate"]["value_template"] = "{{ value_json }}"
|
||||||
config["climate"]["action_template"] = "{{ value_json }}"
|
config["climate"]["action_template"] = "{{ value_json }}"
|
||||||
# Something more complicated for hold mode
|
|
||||||
config["climate"]["hold_state_template"] = "{{ value_json.attribute }}"
|
|
||||||
# Rendering to a bool for aux heat
|
# Rendering to a bool for aux heat
|
||||||
config["climate"]["aux_state_template"] = "{{ value == 'switchmeon' }}"
|
config["climate"]["aux_state_template"] = "{{ value == 'switchmeon' }}"
|
||||||
|
# Rendering preset_mode
|
||||||
|
config["climate"]["preset_mode_value_template"] = "{{ value_json.attribute }}"
|
||||||
|
|
||||||
config["climate"]["action_topic"] = "action"
|
config["climate"]["action_topic"] = "action"
|
||||||
config["climate"]["mode_state_topic"] = "mode-state"
|
config["climate"]["mode_state_topic"] = "mode-state"
|
||||||
config["climate"]["fan_mode_state_topic"] = "fan-state"
|
config["climate"]["fan_mode_state_topic"] = "fan-state"
|
||||||
config["climate"]["swing_mode_state_topic"] = "swing-state"
|
config["climate"]["swing_mode_state_topic"] = "swing-state"
|
||||||
config["climate"]["temperature_state_topic"] = "temperature-state"
|
config["climate"]["temperature_state_topic"] = "temperature-state"
|
||||||
config["climate"]["away_mode_state_topic"] = "away-state"
|
|
||||||
config["climate"]["hold_state_topic"] = "hold-state"
|
|
||||||
config["climate"]["aux_state_topic"] = "aux-state"
|
config["climate"]["aux_state_topic"] = "aux-state"
|
||||||
config["climate"]["current_temperature_topic"] = "current-temperature"
|
config["climate"]["current_temperature_topic"] = "current-temperature"
|
||||||
|
config["climate"]["preset_mode_state_topic"] = "current-preset-mode"
|
||||||
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@ -854,31 +1009,18 @@ async def test_get_with_templates(hass, mqtt_mock, caplog):
|
|||||||
# ... but the actual value stays unchanged.
|
# ... but the actual value stays unchanged.
|
||||||
assert state.attributes.get("temperature") == 1031
|
assert state.attributes.get("temperature") == 1031
|
||||||
|
|
||||||
# Away Mode
|
# Preset Mode
|
||||||
assert state.attributes.get("preset_mode") == "none"
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
async_fire_mqtt_message(hass, "away-state", '"ON"')
|
async_fire_mqtt_message(hass, "current-preset-mode", '{"attribute": "eco"}')
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
assert state.attributes.get("preset_mode") == "away"
|
assert state.attributes.get("preset_mode") == "eco"
|
||||||
|
# Test with an empty json
|
||||||
# Away Mode with JSON values
|
|
||||||
async_fire_mqtt_message(hass, "away-state", "false")
|
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
|
||||||
assert state.attributes.get("preset_mode") == "none"
|
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "away-state", "true")
|
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
|
||||||
assert state.attributes.get("preset_mode") == "away"
|
|
||||||
|
|
||||||
# Hold Mode
|
|
||||||
async_fire_mqtt_message(
|
async_fire_mqtt_message(
|
||||||
hass,
|
hass, "current-preset-mode", '{"other_attribute": "some_value"}'
|
||||||
"hold-state",
|
|
||||||
"""
|
|
||||||
{ "attribute": "somemode" }
|
|
||||||
""",
|
|
||||||
)
|
)
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
assert state.attributes.get("preset_mode") == "somemode"
|
assert "Ignoring empty preset_mode from 'current-preset-mode'"
|
||||||
|
assert state.attributes.get("preset_mode") == "eco"
|
||||||
|
|
||||||
# Aux mode
|
# Aux mode
|
||||||
assert state.attributes.get("aux_heat") == "off"
|
assert state.attributes.get("aux_heat") == "off"
|
||||||
@ -911,12 +1053,60 @@ async def test_get_with_templates(hass, mqtt_mock, caplog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_set_with_templates(hass, mqtt_mock, caplog):
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
async def test_get_with_hold_and_away_mode_and_templates(hass, mqtt_mock, caplog):
|
||||||
|
"""Test getting various for hold and away mode attributes with templates."""
|
||||||
|
config = copy.deepcopy(DEFAULT_LEGACY_CONFIG)
|
||||||
|
config["climate"]["mode_state_topic"] = "mode-state"
|
||||||
|
# By default, just unquote the JSON-strings
|
||||||
|
config["climate"]["value_template"] = "{{ value_json }}"
|
||||||
|
# Something more complicated for hold mode
|
||||||
|
config["climate"]["hold_state_template"] = "{{ value_json.attribute }}"
|
||||||
|
config["climate"]["away_mode_state_topic"] = "away-state"
|
||||||
|
config["climate"]["hold_state_topic"] = "hold-state"
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Operation Mode
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
async_fire_mqtt_message(hass, "mode-state", '"cool"')
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.state == "cool"
|
||||||
|
|
||||||
|
# Away Mode
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
async_fire_mqtt_message(hass, "away-state", '"ON"')
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "away"
|
||||||
|
|
||||||
|
# Away Mode with JSON values
|
||||||
|
async_fire_mqtt_message(hass, "away-state", "false")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "none"
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "away-state", "true")
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "away"
|
||||||
|
|
||||||
|
# Hold Mode
|
||||||
|
async_fire_mqtt_message(
|
||||||
|
hass,
|
||||||
|
"hold-state",
|
||||||
|
"""
|
||||||
|
{ "attribute": "somemode" }
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == "somemode"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_and_templates(hass, mqtt_mock, caplog):
|
||||||
"""Test setting various attributes with templates."""
|
"""Test setting various attributes with templates."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
# Create simple templates
|
# Create simple templates
|
||||||
config["climate"]["fan_mode_command_template"] = "fan_mode: {{ value }}"
|
config["climate"]["fan_mode_command_template"] = "fan_mode: {{ value }}"
|
||||||
config["climate"]["hold_command_template"] = "hold: {{ value }}"
|
config["climate"]["preset_mode_command_template"] = "preset_mode: {{ value }}"
|
||||||
config["climate"]["mode_command_template"] = "mode: {{ value }}"
|
config["climate"]["mode_command_template"] = "mode: {{ value }}"
|
||||||
config["climate"]["swing_mode_command_template"] = "swing_mode: {{ value }}"
|
config["climate"]["swing_mode_command_template"] = "swing_mode: {{ value }}"
|
||||||
config["climate"]["temperature_command_template"] = "temp: {{ value }}"
|
config["climate"]["temperature_command_template"] = "temp: {{ value }}"
|
||||||
@ -935,11 +1125,12 @@ async def test_set_with_templates(hass, mqtt_mock, caplog):
|
|||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
assert state.attributes.get("fan_mode") == "high"
|
assert state.attributes.get("fan_mode") == "high"
|
||||||
|
|
||||||
# Hold Mode
|
# Preset Mode
|
||||||
await common.async_set_preset_mode(hass, PRESET_ECO, ENTITY_CLIMATE)
|
await common.async_set_preset_mode(hass, PRESET_ECO, ENTITY_CLIMATE)
|
||||||
mqtt_mock.async_publish.call_count == 2
|
mqtt_mock.async_publish.call_count == 1
|
||||||
mqtt_mock.async_publish.assert_any_call("away-mode-topic", "OFF", 0, False)
|
mqtt_mock.async_publish.assert_any_call(
|
||||||
mqtt_mock.async_publish.assert_any_call("hold-topic", "hold: eco", 0, False)
|
"preset-mode-topic", "preset_mode: eco", 0, False
|
||||||
|
)
|
||||||
mqtt_mock.async_publish.reset_mock()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
state = hass.states.get(ENTITY_CLIMATE)
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
assert state.attributes.get("preset_mode") == PRESET_ECO
|
assert state.attributes.get("preset_mode") == PRESET_ECO
|
||||||
@ -987,6 +1178,26 @@ async def test_set_with_templates(hass, mqtt_mock, caplog):
|
|||||||
assert state.attributes.get("target_temp_high") == 23
|
assert state.attributes.get("target_temp_high") == 23
|
||||||
|
|
||||||
|
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
async def test_set_with_away_and_hold_modes_and_templates(hass, mqtt_mock, caplog):
|
||||||
|
"""Test setting various attributes on hold and away mode with templates."""
|
||||||
|
config = copy.deepcopy(DEFAULT_LEGACY_CONFIG)
|
||||||
|
# Create simple templates
|
||||||
|
config["climate"]["hold_command_template"] = "hold: {{ value }}"
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Hold Mode
|
||||||
|
await common.async_set_preset_mode(hass, PRESET_ECO, ENTITY_CLIMATE)
|
||||||
|
mqtt_mock.async_publish.call_count == 2
|
||||||
|
mqtt_mock.async_publish.assert_any_call("away-mode-topic", "OFF", 0, False)
|
||||||
|
mqtt_mock.async_publish.assert_any_call("hold-topic", "hold: eco", 0, False)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_CLIMATE)
|
||||||
|
assert state.attributes.get("preset_mode") == PRESET_ECO
|
||||||
|
|
||||||
|
|
||||||
async def test_min_temp_custom(hass, mqtt_mock):
|
async def test_min_temp_custom(hass, mqtt_mock):
|
||||||
"""Test a custom min temp."""
|
"""Test a custom min temp."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
@ -1118,9 +1329,11 @@ async def test_unique_id(hass, mqtt_mock):
|
|||||||
("action_topic", "heating", ATTR_HVAC_ACTION, "heating"),
|
("action_topic", "heating", ATTR_HVAC_ACTION, "heating"),
|
||||||
("action_topic", "cooling", ATTR_HVAC_ACTION, "cooling"),
|
("action_topic", "cooling", ATTR_HVAC_ACTION, "cooling"),
|
||||||
("aux_state_topic", "ON", ATTR_AUX_HEAT, "on"),
|
("aux_state_topic", "ON", ATTR_AUX_HEAT, "on"),
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
("away_mode_state_topic", "ON", ATTR_PRESET_MODE, "away"),
|
("away_mode_state_topic", "ON", ATTR_PRESET_MODE, "away"),
|
||||||
("current_temperature_topic", "22.1", ATTR_CURRENT_TEMPERATURE, 22.1),
|
("current_temperature_topic", "22.1", ATTR_CURRENT_TEMPERATURE, 22.1),
|
||||||
("fan_mode_state_topic", "low", ATTR_FAN_MODE, "low"),
|
("fan_mode_state_topic", "low", ATTR_FAN_MODE, "low"),
|
||||||
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
("hold_state_topic", "mode1", ATTR_PRESET_MODE, "mode1"),
|
("hold_state_topic", "mode1", ATTR_PRESET_MODE, "mode1"),
|
||||||
("mode_state_topic", "cool", None, None),
|
("mode_state_topic", "cool", None, None),
|
||||||
("mode_state_topic", "fan_only", None, None),
|
("mode_state_topic", "fan_only", None, None),
|
||||||
@ -1135,7 +1348,11 @@ async def test_encoding_subscribable_topics(
|
|||||||
):
|
):
|
||||||
"""Test handling of incoming encoded payload."""
|
"""Test handling of incoming encoded payload."""
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
|
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
|
||||||
config["hold_modes"] = ["mode1", "mode2"]
|
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
|
||||||
|
if topic in ["hold_state_topic", "away_mode_state_topic"]:
|
||||||
|
config["hold_modes"] = ["mode1", "mode2"]
|
||||||
|
del config["preset_modes"]
|
||||||
|
del config["preset_mode_command_topic"]
|
||||||
await help_test_encoding_subscribable_topics(
|
await help_test_encoding_subscribable_topics(
|
||||||
hass,
|
hass,
|
||||||
mqtt_mock,
|
mqtt_mock,
|
||||||
@ -1317,6 +1534,13 @@ async def test_precision_whole(hass, mqtt_mock):
|
|||||||
"cool",
|
"cool",
|
||||||
"mode_command_template",
|
"mode_command_template",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
climate.SERVICE_SET_PRESET_MODE,
|
||||||
|
"preset_mode_command_topic",
|
||||||
|
{"preset_mode": "sleep"},
|
||||||
|
"sleep",
|
||||||
|
"preset_mode_command_template",
|
||||||
|
),
|
||||||
(
|
(
|
||||||
climate.SERVICE_SET_PRESET_MODE,
|
climate.SERVICE_SET_PRESET_MODE,
|
||||||
"away_mode_command_topic",
|
"away_mode_command_topic",
|
||||||
@ -1334,8 +1558,8 @@ async def test_precision_whole(hass, mqtt_mock):
|
|||||||
(
|
(
|
||||||
climate.SERVICE_SET_PRESET_MODE,
|
climate.SERVICE_SET_PRESET_MODE,
|
||||||
"hold_command_topic",
|
"hold_command_topic",
|
||||||
{"preset_mode": "some_hold_mode"},
|
{"preset_mode": "comfort"},
|
||||||
"some_hold_mode",
|
"comfort",
|
||||||
"hold_command_template",
|
"hold_command_template",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@ -1402,7 +1626,10 @@ async def test_publishing_with_custom_encoding(
|
|||||||
):
|
):
|
||||||
"""Test publishing MQTT payload with different encoding."""
|
"""Test publishing MQTT payload with different encoding."""
|
||||||
domain = climate.DOMAIN
|
domain = climate.DOMAIN
|
||||||
config = DEFAULT_CONFIG[domain]
|
config = copy.deepcopy(DEFAULT_CONFIG[domain])
|
||||||
|
if topic != "preset_mode_command_topic":
|
||||||
|
del config["preset_mode_command_topic"]
|
||||||
|
del config["preset_modes"]
|
||||||
|
|
||||||
await help_test_publishing_with_custom_encoding(
|
await help_test_publishing_with_custom_encoding(
|
||||||
hass,
|
hass,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user