From 6580a07308739f30b063e9489f4c90c9c5892204 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 25 May 2024 23:11:07 +0200 Subject: [PATCH] Refactor mqtt callbacks for humidifier (#118116) --- homeassistant/components/mqtt/humidifier.py | 290 ++++++++++---------- 1 file changed, 144 insertions(+), 146 deletions(-) diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index b9f57dfe0ef..7956a05d20a 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections.abc import Callable +from functools import partial import logging from typing import Any @@ -51,12 +52,7 @@ from .const import ( CONF_STATE_VALUE_TEMPLATE, PAYLOAD_NONE, ) -from .debug_info import log_messages -from .mixins import ( - MqttEntity, - async_setup_entity_entry_helper, - write_state_on_attr_change, -) +from .mixins import MqttEntity, async_setup_entity_entry_helper from .models import ( MqttCommandTemplate, MqttValueTemplate, @@ -284,164 +280,166 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): topics: dict[str, dict[str, Any]], topic: str, msg_callback: Callable[[ReceiveMessage], None], + tracked_attributes: set[str], ) -> None: """Add a subscription.""" qos: int = self._config[CONF_QOS] if topic in self._topic and self._topic[topic] is not None: topics[topic] = { "topic": self._topic[topic], - "msg_callback": msg_callback, + "msg_callback": partial( + self._message_callback, msg_callback, tracked_attributes + ), + "entity_id": self.entity_id, "qos": qos, "encoding": self._config[CONF_ENCODING] or None, } + @callback + def _state_received(self, msg: ReceiveMessage) -> None: + """Handle new received MQTT message.""" + payload = self._value_templates[CONF_STATE](msg.payload) + if not payload: + _LOGGER.debug("Ignoring empty state from '%s'", msg.topic) + return + if payload == self._payload["STATE_ON"]: + self._attr_is_on = True + elif payload == self._payload["STATE_OFF"]: + self._attr_is_on = False + elif payload == PAYLOAD_NONE: + self._attr_is_on = None + + @callback + def _action_received(self, msg: ReceiveMessage) -> None: + """Handle new received MQTT message.""" + action_payload = self._value_templates[ATTR_ACTION](msg.payload) + if not action_payload or action_payload == PAYLOAD_NONE: + _LOGGER.debug("Ignoring empty action from '%s'", msg.topic) + return + try: + self._attr_action = HumidifierAction(str(action_payload)) + except ValueError: + _LOGGER.error( + "'%s' received on topic %s. '%s' is not a valid action", + msg.payload, + msg.topic, + action_payload, + ) + return + + @callback + def _current_humidity_received(self, msg: ReceiveMessage) -> None: + """Handle new received MQTT message for the current humidity.""" + rendered_current_humidity_payload = self._value_templates[ + ATTR_CURRENT_HUMIDITY + ](msg.payload) + if rendered_current_humidity_payload == self._payload["HUMIDITY_RESET"]: + self._attr_current_humidity = None + return + if not rendered_current_humidity_payload: + _LOGGER.debug("Ignoring empty current humidity from '%s'", msg.topic) + return + try: + current_humidity = round(float(rendered_current_humidity_payload)) + except ValueError: + _LOGGER.warning( + "'%s' received on topic %s. '%s' is not a valid humidity", + msg.payload, + msg.topic, + rendered_current_humidity_payload, + ) + return + if current_humidity < 0 or current_humidity > 100: + _LOGGER.warning( + "'%s' received on topic %s. '%s' is not a valid humidity", + msg.payload, + msg.topic, + rendered_current_humidity_payload, + ) + return + self._attr_current_humidity = current_humidity + + @callback + def _target_humidity_received(self, msg: ReceiveMessage) -> None: + """Handle new received MQTT message for the target humidity.""" + rendered_target_humidity_payload = self._value_templates[ATTR_HUMIDITY]( + msg.payload + ) + if not rendered_target_humidity_payload: + _LOGGER.debug("Ignoring empty target humidity from '%s'", msg.topic) + return + if rendered_target_humidity_payload == self._payload["HUMIDITY_RESET"]: + self._attr_target_humidity = None + return + try: + target_humidity = round(float(rendered_target_humidity_payload)) + except ValueError: + _LOGGER.warning( + "'%s' received on topic %s. '%s' is not a valid target humidity", + msg.payload, + msg.topic, + rendered_target_humidity_payload, + ) + return + if ( + target_humidity < self._attr_min_humidity + or target_humidity > self._attr_max_humidity + ): + _LOGGER.warning( + "'%s' received on topic %s. '%s' is not a valid target humidity", + msg.payload, + msg.topic, + rendered_target_humidity_payload, + ) + return + self._attr_target_humidity = target_humidity + + @callback + def _mode_received(self, msg: ReceiveMessage) -> None: + """Handle new received MQTT message for mode.""" + mode = str(self._value_templates[ATTR_MODE](msg.payload)) + if mode == self._payload["MODE_RESET"]: + self._attr_mode = None + return + if not mode: + _LOGGER.debug("Ignoring empty mode from '%s'", msg.topic) + return + if not self.available_modes or mode not in self.available_modes: + _LOGGER.warning( + "'%s' received on topic %s. '%s' is not a valid mode", + msg.payload, + msg.topic, + mode, + ) + return + + self._attr_mode = mode + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" topics: dict[str, Any] = {} - @callback - @log_messages(self.hass, self.entity_id) - @write_state_on_attr_change(self, {"_attr_is_on"}) - def state_received(msg: ReceiveMessage) -> None: - """Handle new received MQTT message.""" - payload = self._value_templates[CONF_STATE](msg.payload) - if not payload: - _LOGGER.debug("Ignoring empty state from '%s'", msg.topic) - return - if payload == self._payload["STATE_ON"]: - self._attr_is_on = True - elif payload == self._payload["STATE_OFF"]: - self._attr_is_on = False - elif payload == PAYLOAD_NONE: - self._attr_is_on = None - - self.add_subscription(topics, CONF_STATE_TOPIC, state_received) - - @callback - @log_messages(self.hass, self.entity_id) - @write_state_on_attr_change(self, {"_attr_action"}) - def action_received(msg: ReceiveMessage) -> None: - """Handle new received MQTT message.""" - action_payload = self._value_templates[ATTR_ACTION](msg.payload) - if not action_payload or action_payload == PAYLOAD_NONE: - _LOGGER.debug("Ignoring empty action from '%s'", msg.topic) - return - try: - self._attr_action = HumidifierAction(str(action_payload)) - except ValueError: - _LOGGER.error( - "'%s' received on topic %s. '%s' is not a valid action", - msg.payload, - msg.topic, - action_payload, - ) - return - - self.add_subscription(topics, CONF_ACTION_TOPIC, action_received) - - @callback - @log_messages(self.hass, self.entity_id) - @write_state_on_attr_change(self, {"_attr_current_humidity"}) - def current_humidity_received(msg: ReceiveMessage) -> None: - """Handle new received MQTT message for the current humidity.""" - rendered_current_humidity_payload = self._value_templates[ - ATTR_CURRENT_HUMIDITY - ](msg.payload) - if rendered_current_humidity_payload == self._payload["HUMIDITY_RESET"]: - self._attr_current_humidity = None - return - if not rendered_current_humidity_payload: - _LOGGER.debug("Ignoring empty current humidity from '%s'", msg.topic) - return - try: - current_humidity = round(float(rendered_current_humidity_payload)) - except ValueError: - _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid humidity", - msg.payload, - msg.topic, - rendered_current_humidity_payload, - ) - return - if current_humidity < 0 or current_humidity > 100: - _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid humidity", - msg.payload, - msg.topic, - rendered_current_humidity_payload, - ) - return - self._attr_current_humidity = current_humidity - self.add_subscription( - topics, CONF_CURRENT_HUMIDITY_TOPIC, current_humidity_received + topics, CONF_STATE_TOPIC, self._state_received, {"_attr_is_on"} ) - - @callback - @log_messages(self.hass, self.entity_id) - @write_state_on_attr_change(self, {"_attr_target_humidity"}) - def target_humidity_received(msg: ReceiveMessage) -> None: - """Handle new received MQTT message for the target humidity.""" - rendered_target_humidity_payload = self._value_templates[ATTR_HUMIDITY]( - msg.payload - ) - if not rendered_target_humidity_payload: - _LOGGER.debug("Ignoring empty target humidity from '%s'", msg.topic) - return - if rendered_target_humidity_payload == self._payload["HUMIDITY_RESET"]: - self._attr_target_humidity = None - return - try: - target_humidity = round(float(rendered_target_humidity_payload)) - except ValueError: - _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid target humidity", - msg.payload, - msg.topic, - rendered_target_humidity_payload, - ) - return - if ( - target_humidity < self._attr_min_humidity - or target_humidity > self._attr_max_humidity - ): - _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid target humidity", - msg.payload, - msg.topic, - rendered_target_humidity_payload, - ) - return - self._attr_target_humidity = target_humidity - self.add_subscription( - topics, CONF_TARGET_HUMIDITY_STATE_TOPIC, target_humidity_received + topics, CONF_ACTION_TOPIC, self._action_received, {"_attr_action"} + ) + self.add_subscription( + topics, + CONF_CURRENT_HUMIDITY_TOPIC, + self._current_humidity_received, + {"_attr_current_humidity"}, + ) + self.add_subscription( + topics, + CONF_TARGET_HUMIDITY_STATE_TOPIC, + self._target_humidity_received, + {"_attr_target_humidity"}, + ) + self.add_subscription( + topics, CONF_MODE_STATE_TOPIC, self._mode_received, {"_attr_mode"} ) - - @callback - @log_messages(self.hass, self.entity_id) - @write_state_on_attr_change(self, {"_attr_mode"}) - def mode_received(msg: ReceiveMessage) -> None: - """Handle new received MQTT message for mode.""" - mode = str(self._value_templates[ATTR_MODE](msg.payload)) - if mode == self._payload["MODE_RESET"]: - self._attr_mode = None - return - if not mode: - _LOGGER.debug("Ignoring empty mode from '%s'", msg.topic) - return - if not self.available_modes or mode not in self.available_modes: - _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid mode", - msg.payload, - msg.topic, - mode, - ) - return - - self._attr_mode = mode - - self.add_subscription(topics, CONF_MODE_STATE_TOPIC, mode_received) self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, self._sub_state, topics