Create bound callback_message_received method for handling mqtt callbacks (#117951)
* Create bound callback_message_received method for handling mqtt callbacks * refactor a bit * fix ruff * reduce overhead * cleanup * cleanup * Revert changes alarm_control_panel * Add sensor and binary sensor * use same pattern for MqttAttributes/MqttAvailability * remove useless function since we did not need to add to it * code cleanup * collapse --------- Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
d4df86da06
commit
9333965b23
5 changed files with 210 additions and 152 deletions
|
@ -3,6 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
|
@ -37,13 +38,7 @@ from homeassistant.util import dt as dt_util
|
|||
from . import subscription
|
||||
from .config import MQTT_RO_SCHEMA
|
||||
from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, PAYLOAD_NONE
|
||||
from .debug_info import log_messages
|
||||
from .mixins import (
|
||||
MqttAvailability,
|
||||
MqttEntity,
|
||||
async_setup_entity_entry_helper,
|
||||
write_state_on_attr_change,
|
||||
)
|
||||
from .mixins import MqttAvailability, MqttEntity, async_setup_entity_entry_helper
|
||||
from .models import MqttValueTemplate, ReceiveMessage
|
||||
from .schemas import MQTT_ENTITY_COMMON_SCHEMA
|
||||
|
||||
|
@ -162,92 +157,95 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity):
|
|||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
@callback
|
||||
def _off_delay_listener(self, now: datetime) -> None:
|
||||
"""Switch device off after a delay."""
|
||||
self._delay_listener = None
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
def _state_message_received(self, msg: ReceiveMessage) -> None:
|
||||
"""Handle a new received MQTT state message."""
|
||||
|
||||
# auto-expire enabled?
|
||||
if self._expire_after:
|
||||
# When expire_after is set, and we receive a message, assume device is
|
||||
# not expired since it has to be to receive the message
|
||||
self._expired = False
|
||||
|
||||
# Reset old trigger
|
||||
if self._expiration_trigger:
|
||||
self._expiration_trigger()
|
||||
|
||||
# Set new trigger
|
||||
self._expiration_trigger = async_call_later(
|
||||
self.hass, self._expire_after, self._value_is_expired
|
||||
)
|
||||
|
||||
payload = self._value_template(msg.payload)
|
||||
if not payload.strip(): # No output from template, ignore
|
||||
_LOGGER.debug(
|
||||
(
|
||||
"Empty template output for entity: %s with state topic: %s."
|
||||
" Payload: '%s', with value template '%s'"
|
||||
),
|
||||
self.entity_id,
|
||||
self._config[CONF_STATE_TOPIC],
|
||||
msg.payload,
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
)
|
||||
return
|
||||
|
||||
if payload == self._config[CONF_PAYLOAD_ON]:
|
||||
self._attr_is_on = True
|
||||
elif payload == self._config[CONF_PAYLOAD_OFF]:
|
||||
self._attr_is_on = False
|
||||
elif payload == PAYLOAD_NONE:
|
||||
self._attr_is_on = None
|
||||
else: # Payload is not for this entity
|
||||
template_info = ""
|
||||
if self._config.get(CONF_VALUE_TEMPLATE) is not None:
|
||||
template_info = (
|
||||
f", template output: '{payload!s}', with value template"
|
||||
f" '{self._config.get(CONF_VALUE_TEMPLATE)!s}'"
|
||||
)
|
||||
_LOGGER.info(
|
||||
(
|
||||
"No matching payload found for entity: %s with state topic: %s."
|
||||
" Payload: '%s'%s"
|
||||
),
|
||||
self.entity_id,
|
||||
self._config[CONF_STATE_TOPIC],
|
||||
msg.payload,
|
||||
template_info,
|
||||
)
|
||||
return
|
||||
|
||||
if self._delay_listener is not None:
|
||||
self._delay_listener()
|
||||
self._delay_listener = None
|
||||
|
||||
off_delay: int | None = self._config.get(CONF_OFF_DELAY)
|
||||
if self._attr_is_on and off_delay is not None:
|
||||
self._delay_listener = evt.async_call_later(
|
||||
self.hass, off_delay, self._off_delay_listener
|
||||
)
|
||||
|
||||
def _prepare_subscribe_topics(self) -> None:
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
||||
@callback
|
||||
def off_delay_listener(now: datetime) -> None:
|
||||
"""Switch device off after a delay."""
|
||||
self._delay_listener = None
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
@write_state_on_attr_change(self, {"_attr_is_on", "_expired"})
|
||||
def state_message_received(msg: ReceiveMessage) -> None:
|
||||
"""Handle a new received MQTT state message."""
|
||||
# auto-expire enabled?
|
||||
if self._expire_after:
|
||||
# When expire_after is set, and we receive a message, assume device is
|
||||
# not expired since it has to be to receive the message
|
||||
self._expired = False
|
||||
|
||||
# Reset old trigger
|
||||
if self._expiration_trigger:
|
||||
self._expiration_trigger()
|
||||
|
||||
# Set new trigger
|
||||
self._expiration_trigger = async_call_later(
|
||||
self.hass, self._expire_after, self._value_is_expired
|
||||
)
|
||||
|
||||
payload = self._value_template(msg.payload)
|
||||
if not payload.strip(): # No output from template, ignore
|
||||
_LOGGER.debug(
|
||||
(
|
||||
"Empty template output for entity: %s with state topic: %s."
|
||||
" Payload: '%s', with value template '%s'"
|
||||
),
|
||||
self.entity_id,
|
||||
self._config[CONF_STATE_TOPIC],
|
||||
msg.payload,
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
)
|
||||
return
|
||||
|
||||
if payload == self._config[CONF_PAYLOAD_ON]:
|
||||
self._attr_is_on = True
|
||||
elif payload == self._config[CONF_PAYLOAD_OFF]:
|
||||
self._attr_is_on = False
|
||||
elif payload == PAYLOAD_NONE:
|
||||
self._attr_is_on = None
|
||||
else: # Payload is not for this entity
|
||||
template_info = ""
|
||||
if self._config.get(CONF_VALUE_TEMPLATE) is not None:
|
||||
template_info = (
|
||||
f", template output: '{payload!s}', with value template"
|
||||
f" '{self._config.get(CONF_VALUE_TEMPLATE)!s}'"
|
||||
)
|
||||
_LOGGER.info(
|
||||
(
|
||||
"No matching payload found for entity: %s with state topic: %s."
|
||||
" Payload: '%s'%s"
|
||||
),
|
||||
self.entity_id,
|
||||
self._config[CONF_STATE_TOPIC],
|
||||
msg.payload,
|
||||
template_info,
|
||||
)
|
||||
return
|
||||
|
||||
if self._delay_listener is not None:
|
||||
self._delay_listener()
|
||||
self._delay_listener = None
|
||||
|
||||
off_delay: int | None = self._config.get(CONF_OFF_DELAY)
|
||||
if self._attr_is_on and off_delay is not None:
|
||||
self._delay_listener = evt.async_call_later(
|
||||
self.hass, off_delay, off_delay_listener
|
||||
)
|
||||
|
||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||
self.hass,
|
||||
self._sub_state,
|
||||
{
|
||||
"state_topic": {
|
||||
"topic": self._config[CONF_STATE_TOPIC],
|
||||
"msg_callback": state_message_received,
|
||||
"msg_callback": partial(
|
||||
self._message_callback,
|
||||
self._state_message_received,
|
||||
{"_attr_is_on", "_expired"},
|
||||
),
|
||||
"entity_id": self.entity_id,
|
||||
"qos": self._config[CONF_QOS],
|
||||
"encoding": self._config[CONF_ENCODING] or None,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue