Add current_humidity attribute to mqtt humidifier (#94955)

pull/95007/head
Jan Bouwhuis 2023-06-21 19:19:26 +02:00 committed by GitHub
parent 492ed1b544
commit 31f845bfe0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 2 deletions

View File

@ -52,6 +52,8 @@ from homeassistant.util.unit_conversion import TemperatureConverter
from . import subscription
from .config import DEFAULT_RETAIN, MQTT_BASE_SCHEMA
from .const import (
CONF_CURRENT_HUMIDITY_TEMPLATE,
CONF_CURRENT_HUMIDITY_TOPIC,
CONF_CURRENT_TEMP_TEMPLATE,
CONF_CURRENT_TEMP_TOPIC,
CONF_ENCODING,
@ -94,8 +96,6 @@ CONF_AUX_COMMAND_TOPIC = "aux_command_topic"
CONF_AUX_STATE_TEMPLATE = "aux_state_template"
CONF_AUX_STATE_TOPIC = "aux_state_topic"
CONF_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template"
CONF_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"
CONF_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_command_template"
CONF_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic"
CONF_FAN_MODE_LIST = "fan_modes"

View File

@ -29,6 +29,8 @@ CONF_WS_HEADERS = "ws_headers"
CONF_WILL_MESSAGE = "will_message"
CONF_PAYLOAD_RESET = "payload_reset"
CONF_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template"
CONF_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"
CONF_CURRENT_TEMP_TEMPLATE = "current_temperature_template"
CONF_CURRENT_TEMP_TOPIC = "current_temperature_topic"
CONF_MODE_COMMAND_TEMPLATE = "mode_command_template"

View File

@ -10,6 +10,7 @@ import voluptuous as vol
from homeassistant.components import humidifier
from homeassistant.components.humidifier import (
ATTR_CURRENT_HUMIDITY,
ATTR_HUMIDITY,
ATTR_MODE,
DEFAULT_MAX_HUMIDITY,
@ -37,6 +38,8 @@ from .config import MQTT_RW_SCHEMA
from .const import (
CONF_COMMAND_TEMPLATE,
CONF_COMMAND_TOPIC,
CONF_CURRENT_HUMIDITY_TEMPLATE,
CONF_CURRENT_HUMIDITY_TOPIC,
CONF_ENCODING,
CONF_QOS,
CONF_RETAIN,
@ -117,6 +120,8 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend(
): cv.ensure_list,
vol.Inclusive(CONF_MODE_COMMAND_TOPIC, "available_modes"): valid_publish_topic,
vol.Optional(CONF_COMMAND_TEMPLATE): cv.template,
vol.Optional(CONF_CURRENT_HUMIDITY_TEMPLATE): cv.template,
vol.Optional(CONF_CURRENT_HUMIDITY_TOPIC): valid_subscribe_topic,
vol.Optional(
CONF_DEVICE_CLASS, default=HumidifierDeviceClass.HUMIDIFIER
): vol.In(
@ -224,6 +229,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
for key in (
CONF_STATE_TOPIC,
CONF_COMMAND_TOPIC,
CONF_CURRENT_HUMIDITY_TOPIC,
CONF_TARGET_HUMIDITY_STATE_TOPIC,
CONF_TARGET_HUMIDITY_COMMAND_TOPIC,
CONF_MODE_STATE_TOPIC,
@ -263,6 +269,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
self._value_templates = {}
value_templates: dict[str, Template | None] = {
ATTR_CURRENT_HUMIDITY: config.get(CONF_CURRENT_HUMIDITY_TEMPLATE),
CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE),
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_STATE_TEMPLATE),
ATTR_MODE: config.get(CONF_MODE_STATE_TEMPLATE),
@ -301,6 +308,49 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
"encoding": self._config[CONF_ENCODING] or None,
}
@callback
@log_messages(self.hass, self.entity_id)
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
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
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
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
if self._topic[CONF_CURRENT_HUMIDITY_TOPIC] is not None:
topics[CONF_CURRENT_HUMIDITY_TOPIC] = {
"topic": self._topic[CONF_CURRENT_HUMIDITY_TOPIC],
"msg_callback": current_humidity_received,
"qos": self._config[CONF_QOS],
"encoding": self._config[CONF_ENCODING] or None,
}
@callback
@log_messages(self.hass, self.entity_id)
def target_humidity_received(msg: ReceiveMessage) -> None:

View File

@ -8,12 +8,14 @@ from voluptuous.error import MultipleInvalid
from homeassistant.components import humidifier, mqtt
from homeassistant.components.humidifier import (
ATTR_CURRENT_HUMIDITY,
ATTR_HUMIDITY,
ATTR_MODE,
DOMAIN,
SERVICE_SET_HUMIDITY,
SERVICE_SET_MODE,
)
from homeassistant.components.mqtt.const import CONF_CURRENT_HUMIDITY_TOPIC
from homeassistant.components.mqtt.humidifier import (
CONF_MODE_COMMAND_TOPIC,
CONF_MODE_STATE_TOPIC,
@ -151,6 +153,7 @@ async def test_fail_setup_if_no_command_topic(
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
"current_humidity_topic": "current-humidity-topic",
"payload_off": "StAtE_OfF",
"payload_on": "StAtE_On",
"target_humidity_state_topic": "humidity-state-topic",
@ -220,6 +223,26 @@ async def test_controlling_state_via_topic(
assert "not a valid mode" in caplog.text
caplog.clear()
async_fire_mqtt_message(hass, "current-humidity-topic", "48")
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 48
async_fire_mqtt_message(hass, "current-humidity-topic", "101")
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 48
async_fire_mqtt_message(hass, "current-humidity-topic", "-1.6")
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 48
async_fire_mqtt_message(hass, "current-humidity-topic", "43.6")
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 44
async_fire_mqtt_message(hass, "current-humidity-topic", "invalid")
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 44
async_fire_mqtt_message(hass, "mode-state-topic", "auto")
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_MODE) == "auto"
@ -258,6 +281,7 @@ async def test_controlling_state_via_topic(
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
"current_humidity_topic": "current-humidity-topic",
"target_humidity_state_topic": "humidity-state-topic",
"target_humidity_command_topic": "humidity-command-topic",
"mode_state_topic": "mode-state-topic",
@ -267,6 +291,7 @@ async def test_controlling_state_via_topic(
"eco",
"baby",
],
"current_humidity_template": "{{ value_json.val }}",
"state_value_template": "{{ value_json.val }}",
"target_humidity_state_template": "{{ value_json.val }}",
"mode_state_template": "{{ value_json.val }}",
@ -312,6 +337,22 @@ async def test_controlling_state_via_topic_and_json_message(
assert state.attributes.get(humidifier.ATTR_HUMIDITY) is None
caplog.clear()
async_fire_mqtt_message(hass, "current-humidity-topic", '{"val": 1}')
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 1
async_fire_mqtt_message(hass, "current-humidity-topic", '{"val": 100}')
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) == 100
async_fire_mqtt_message(hass, "current-humidity-topic", '{"val": "None"}')
state = hass.states.get("humidifier.test")
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) is None
async_fire_mqtt_message(hass, "current-humidity-topic", '{"otherval": 100}')
assert state.attributes.get(humidifier.ATTR_CURRENT_HUMIDITY) is None
caplog.clear()
async_fire_mqtt_message(hass, "mode-state-topic", '{"val": "low"}')
assert "not a valid mode" in caplog.text
caplog.clear()
@ -746,6 +787,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
("state_topic", "ON", None, "on"),
(CONF_MODE_STATE_TOPIC, "auto", ATTR_MODE, "auto"),
(CONF_TARGET_HUMIDITY_STATE_TOPIC, "45", ATTR_HUMIDITY, 45),
(CONF_CURRENT_HUMIDITY_TOPIC, "39", ATTR_CURRENT_HUMIDITY, 39),
],
)
async def test_encoding_subscribable_topics(