mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Adjust payload sentinel in mqtt (#81553)
* Adjust payload sentinel in mqtt * Add type hints * Update sensor.py * Adjust vacuum * Add type hints * Adjust schema basic * Remove invalid hint
This commit is contained in:
parent
d1fd141e8c
commit
9b2a8901b1
@ -50,7 +50,12 @@ from ..const import (
|
|||||||
)
|
)
|
||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity
|
||||||
from ..models import MqttCommandTemplate, MqttValueTemplate
|
from ..models import (
|
||||||
|
MqttCommandTemplate,
|
||||||
|
MqttValueTemplate,
|
||||||
|
PayloadSentinel,
|
||||||
|
ReceiveMessage,
|
||||||
|
)
|
||||||
from ..util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
from ..util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic
|
||||||
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
from .schema import MQTT_LIGHT_SCHEMA_SCHEMA
|
||||||
|
|
||||||
@ -450,12 +455,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def brightness_received(msg):
|
def brightness_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages for the brightness."""
|
"""Handle new MQTT messages for the brightness."""
|
||||||
payload = self._value_templates[CONF_BRIGHTNESS_VALUE_TEMPLATE](
|
payload = self._value_templates[CONF_BRIGHTNESS_VALUE_TEMPLATE](
|
||||||
msg.payload, None
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
)
|
)
|
||||||
if not payload:
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug("Ignoring empty brightness message from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty brightness message from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -468,8 +473,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
def _rgbx_received(msg, template, color_mode, convert_color):
|
def _rgbx_received(msg, template, color_mode, convert_color):
|
||||||
"""Handle new MQTT messages for RGBW and RGBWW."""
|
"""Handle new MQTT messages for RGBW and RGBWW."""
|
||||||
payload = self._value_templates[template](msg.payload, None)
|
payload = self._value_templates[template](
|
||||||
if not payload:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Ignoring empty %s message from '%s'", color_mode, msg.topic
|
"Ignoring empty %s message from '%s'", color_mode, msg.topic
|
||||||
)
|
)
|
||||||
@ -533,12 +540,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def color_mode_received(msg):
|
def color_mode_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages for color mode."""
|
"""Handle new MQTT messages for color mode."""
|
||||||
payload = self._value_templates[CONF_COLOR_MODE_VALUE_TEMPLATE](
|
payload = self._value_templates[CONF_COLOR_MODE_VALUE_TEMPLATE](
|
||||||
msg.payload, None
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
)
|
)
|
||||||
if not payload:
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug("Ignoring empty color mode message from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty color mode message from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -549,12 +556,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def color_temp_received(msg):
|
def color_temp_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages for color temperature."""
|
"""Handle new MQTT messages for color temperature."""
|
||||||
payload = self._value_templates[CONF_COLOR_TEMP_VALUE_TEMPLATE](
|
payload = self._value_templates[CONF_COLOR_TEMP_VALUE_TEMPLATE](
|
||||||
msg.payload, None
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
)
|
)
|
||||||
if not payload:
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug("Ignoring empty color temp message from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty color temp message from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -567,12 +574,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def effect_received(msg):
|
def effect_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages for effect."""
|
"""Handle new MQTT messages for effect."""
|
||||||
payload = self._value_templates[CONF_EFFECT_VALUE_TEMPLATE](
|
payload = self._value_templates[CONF_EFFECT_VALUE_TEMPLATE](
|
||||||
msg.payload, None
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
)
|
)
|
||||||
if not payload:
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug("Ignoring empty effect message from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty effect message from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -583,10 +590,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def hs_received(msg):
|
def hs_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages for hs color."""
|
"""Handle new MQTT messages for hs color."""
|
||||||
payload = self._value_templates[CONF_HS_VALUE_TEMPLATE](msg.payload, None)
|
payload = self._value_templates[CONF_HS_VALUE_TEMPLATE](
|
||||||
if not payload:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug("Ignoring empty hs message from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty hs message from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@ -602,10 +611,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def xy_received(msg):
|
def xy_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT messages for xy color."""
|
"""Handle new MQTT messages for xy color."""
|
||||||
payload = self._value_templates[CONF_XY_VALUE_TEMPLATE](msg.payload, None)
|
payload = self._value_templates[CONF_XY_VALUE_TEMPLATE](
|
||||||
if not payload:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if payload is PayloadSentinel.DEFAULT or not payload:
|
||||||
_LOGGER.debug("Ignoring empty xy-color message from '%s'", msg.topic)
|
_LOGGER.debug("Ignoring empty xy-color message from '%s'", msg.topic)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, TypedDict, Union
|
|||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
|
from homeassistant.backports.enum import StrEnum
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
from homeassistant.helpers import template
|
from homeassistant.helpers import template
|
||||||
@ -26,7 +27,13 @@ if TYPE_CHECKING:
|
|||||||
from .discovery import MQTTDiscoveryPayload
|
from .discovery import MQTTDiscoveryPayload
|
||||||
from .tag import MQTTTagScanner
|
from .tag import MQTTTagScanner
|
||||||
|
|
||||||
_SENTINEL = object()
|
|
||||||
|
class PayloadSentinel(StrEnum):
|
||||||
|
"""Sentinel for `async_render_with_possible_json_value`."""
|
||||||
|
|
||||||
|
NONE = "none"
|
||||||
|
DEFAULT = "default"
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -189,7 +196,7 @@ class MqttValueTemplate:
|
|||||||
def async_render_with_possible_json_value(
|
def async_render_with_possible_json_value(
|
||||||
self,
|
self,
|
||||||
payload: ReceivePayloadType,
|
payload: ReceivePayloadType,
|
||||||
default: ReceivePayloadType | object = _SENTINEL,
|
default: ReceivePayloadType | PayloadSentinel = PayloadSentinel.NONE,
|
||||||
variables: TemplateVarsType = None,
|
variables: TemplateVarsType = None,
|
||||||
) -> ReceivePayloadType:
|
) -> ReceivePayloadType:
|
||||||
"""Render with possible json value or pass-though a received MQTT value."""
|
"""Render with possible json value or pass-though a received MQTT value."""
|
||||||
@ -213,7 +220,7 @@ class MqttValueTemplate:
|
|||||||
)
|
)
|
||||||
values[ATTR_THIS] = self._template_state
|
values[ATTR_THIS] = self._template_state
|
||||||
|
|
||||||
if default == _SENTINEL:
|
if default is PayloadSentinel.NONE:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Rendering incoming payload '%s' with variables %s and %s",
|
"Rendering incoming payload '%s' with variables %s and %s",
|
||||||
payload,
|
payload,
|
||||||
|
@ -45,7 +45,7 @@ from .mixins import (
|
|||||||
async_setup_platform_helper,
|
async_setup_platform_helper,
|
||||||
warn_for_legacy_schema,
|
warn_for_legacy_schema,
|
||||||
)
|
)
|
||||||
from .models import MqttValueTemplate
|
from .models import MqttValueTemplate, PayloadSentinel, ReceiveMessage
|
||||||
from .util import get_mqtt_data, valid_subscribe_topic
|
from .util import get_mqtt_data, valid_subscribe_topic
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -244,7 +244,7 @@ class MqttSensor(MqttEntity, RestoreSensor):
|
|||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
topics = {}
|
topics = {}
|
||||||
|
|
||||||
def _update_state(msg):
|
def _update_state(msg: ReceiveMessage) -> None:
|
||||||
# auto-expire enabled?
|
# auto-expire enabled?
|
||||||
expire_after = self._config.get(CONF_EXPIRE_AFTER)
|
expire_after = self._config.get(CONF_EXPIRE_AFTER)
|
||||||
if expire_after is not None and expire_after > 0:
|
if expire_after is not None and expire_after > 0:
|
||||||
@ -262,20 +262,25 @@ class MqttSensor(MqttEntity, RestoreSensor):
|
|||||||
self.hass, self._value_is_expired, expiration_at
|
self.hass, self._value_is_expired, expiration_at
|
||||||
)
|
)
|
||||||
|
|
||||||
payload = self._template(msg.payload, default=self.native_value)
|
payload = self._template(msg.payload, default=PayloadSentinel.DEFAULT)
|
||||||
|
if payload is PayloadSentinel.DEFAULT:
|
||||||
if payload is not None and self.device_class in (
|
return
|
||||||
|
if self.device_class not in {
|
||||||
SensorDeviceClass.DATE,
|
SensorDeviceClass.DATE,
|
||||||
SensorDeviceClass.TIMESTAMP,
|
SensorDeviceClass.TIMESTAMP,
|
||||||
):
|
}:
|
||||||
if (payload := dt_util.parse_datetime(payload)) is None:
|
self._attr_native_value = str(payload)
|
||||||
_LOGGER.warning(
|
return
|
||||||
"Invalid state message '%s' from '%s'", msg.payload, msg.topic
|
if (payload_datetime := dt_util.parse_datetime(str(payload))) is None:
|
||||||
)
|
_LOGGER.warning(
|
||||||
elif self.device_class == SensorDeviceClass.DATE:
|
"Invalid state message '%s' from '%s'", msg.payload, msg.topic
|
||||||
payload = payload.date()
|
)
|
||||||
|
self._attr_native_value = None
|
||||||
self._attr_native_value = payload
|
return
|
||||||
|
if self.device_class == SensorDeviceClass.DATE:
|
||||||
|
self._attr_native_value = payload_datetime.date()
|
||||||
|
return
|
||||||
|
self._attr_native_value = payload_datetime
|
||||||
|
|
||||||
def _update_last_reset(msg):
|
def _update_last_reset(msg):
|
||||||
payload = self._last_reset_template(msg.payload)
|
payload = self._last_reset_template(msg.payload)
|
||||||
|
@ -19,7 +19,7 @@ from ..config import MQTT_BASE_SCHEMA
|
|||||||
from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN
|
from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN
|
||||||
from ..debug_info import log_messages
|
from ..debug_info import log_messages
|
||||||
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema
|
from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema
|
||||||
from ..models import MqttValueTemplate
|
from ..models import MqttValueTemplate, PayloadSentinel, ReceiveMessage
|
||||||
from ..util import get_mqtt_data, valid_publish_topic
|
from ..util import get_mqtt_data, valid_publish_topic
|
||||||
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
|
||||||
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
|
||||||
@ -246,7 +246,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
@log_messages(self.hass, self.entity_id)
|
@log_messages(self.hass, self.entity_id)
|
||||||
def message_received(msg):
|
def message_received(msg: ReceiveMessage) -> None:
|
||||||
"""Handle new MQTT message."""
|
"""Handle new MQTT message."""
|
||||||
if (
|
if (
|
||||||
msg.topic == self._state_topics[CONF_BATTERY_LEVEL_TOPIC]
|
msg.topic == self._state_topics[CONF_BATTERY_LEVEL_TOPIC]
|
||||||
@ -254,8 +254,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
):
|
):
|
||||||
battery_level = self._templates[
|
battery_level = self._templates[
|
||||||
CONF_BATTERY_LEVEL_TEMPLATE
|
CONF_BATTERY_LEVEL_TEMPLATE
|
||||||
].async_render_with_possible_json_value(msg.payload, None)
|
].async_render_with_possible_json_value(
|
||||||
if battery_level:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if battery_level and battery_level is not PayloadSentinel.DEFAULT:
|
||||||
self._battery_level = int(battery_level)
|
self._battery_level = int(battery_level)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -264,8 +266,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
):
|
):
|
||||||
charging = self._templates[
|
charging = self._templates[
|
||||||
CONF_CHARGING_TEMPLATE
|
CONF_CHARGING_TEMPLATE
|
||||||
].async_render_with_possible_json_value(msg.payload, None)
|
].async_render_with_possible_json_value(
|
||||||
if charging:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if charging and charging is not PayloadSentinel.DEFAULT:
|
||||||
self._charging = cv.boolean(charging)
|
self._charging = cv.boolean(charging)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -274,8 +278,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
):
|
):
|
||||||
cleaning = self._templates[
|
cleaning = self._templates[
|
||||||
CONF_CLEANING_TEMPLATE
|
CONF_CLEANING_TEMPLATE
|
||||||
].async_render_with_possible_json_value(msg.payload, None)
|
].async_render_with_possible_json_value(
|
||||||
if cleaning:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if cleaning and cleaning is not PayloadSentinel.DEFAULT:
|
||||||
self._cleaning = cv.boolean(cleaning)
|
self._cleaning = cv.boolean(cleaning)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -284,8 +290,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
):
|
):
|
||||||
docked = self._templates[
|
docked = self._templates[
|
||||||
CONF_DOCKED_TEMPLATE
|
CONF_DOCKED_TEMPLATE
|
||||||
].async_render_with_possible_json_value(msg.payload, None)
|
].async_render_with_possible_json_value(
|
||||||
if docked:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if docked and docked is not PayloadSentinel.DEFAULT:
|
||||||
self._docked = cv.boolean(docked)
|
self._docked = cv.boolean(docked)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -294,8 +302,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
):
|
):
|
||||||
error = self._templates[
|
error = self._templates[
|
||||||
CONF_ERROR_TEMPLATE
|
CONF_ERROR_TEMPLATE
|
||||||
].async_render_with_possible_json_value(msg.payload, None)
|
].async_render_with_possible_json_value(
|
||||||
if error is not None:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if error is not PayloadSentinel.DEFAULT:
|
||||||
self._error = cv.string(error)
|
self._error = cv.string(error)
|
||||||
|
|
||||||
if self._docked:
|
if self._docked:
|
||||||
@ -316,8 +326,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||||||
):
|
):
|
||||||
fan_speed = self._templates[
|
fan_speed = self._templates[
|
||||||
CONF_FAN_SPEED_TEMPLATE
|
CONF_FAN_SPEED_TEMPLATE
|
||||||
].async_render_with_possible_json_value(msg.payload, None)
|
].async_render_with_possible_json_value(
|
||||||
if fan_speed:
|
msg.payload, PayloadSentinel.DEFAULT
|
||||||
|
)
|
||||||
|
if fan_speed and fan_speed is not PayloadSentinel.DEFAULT:
|
||||||
self._fan_speed = fan_speed
|
self._fan_speed = fan_speed
|
||||||
|
|
||||||
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user