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
pull/81307/head
epenet 2022-11-07 12:31:11 +01:00 committed by GitHub
parent d1fd141e8c
commit 9b2a8901b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 52 deletions

View File

@ -50,7 +50,12 @@ from ..const import (
)
from ..debug_info import log_messages
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 .schema import MQTT_LIGHT_SCHEMA_SCHEMA
@ -450,12 +455,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
@callback
@log_messages(self.hass, self.entity_id)
def brightness_received(msg):
def brightness_received(msg: ReceiveMessage) -> None:
"""Handle new MQTT messages for the brightness."""
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)
return
@ -468,8 +473,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
def _rgbx_received(msg, template, color_mode, convert_color):
"""Handle new MQTT messages for RGBW and RGBWW."""
payload = self._value_templates[template](msg.payload, None)
if not payload:
payload = self._value_templates[template](
msg.payload, PayloadSentinel.DEFAULT
)
if payload is PayloadSentinel.DEFAULT or not payload:
_LOGGER.debug(
"Ignoring empty %s message from '%s'", color_mode, msg.topic
)
@ -533,12 +540,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
@callback
@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."""
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)
return
@ -549,12 +556,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
@callback
@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."""
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)
return
@ -567,12 +574,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
@callback
@log_messages(self.hass, self.entity_id)
def effect_received(msg):
def effect_received(msg: ReceiveMessage) -> None:
"""Handle new MQTT messages for effect."""
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)
return
@ -583,10 +590,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
@callback
@log_messages(self.hass, self.entity_id)
def hs_received(msg):
def hs_received(msg: ReceiveMessage) -> None:
"""Handle new MQTT messages for hs color."""
payload = self._value_templates[CONF_HS_VALUE_TEMPLATE](msg.payload, None)
if not payload:
payload = self._value_templates[CONF_HS_VALUE_TEMPLATE](
msg.payload, PayloadSentinel.DEFAULT
)
if payload is PayloadSentinel.DEFAULT or not payload:
_LOGGER.debug("Ignoring empty hs message from '%s'", msg.topic)
return
try:
@ -602,10 +611,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
@callback
@log_messages(self.hass, self.entity_id)
def xy_received(msg):
def xy_received(msg: ReceiveMessage) -> None:
"""Handle new MQTT messages for xy color."""
payload = self._value_templates[CONF_XY_VALUE_TEMPLATE](msg.payload, None)
if not payload:
payload = self._value_templates[CONF_XY_VALUE_TEMPLATE](
msg.payload, PayloadSentinel.DEFAULT
)
if payload is PayloadSentinel.DEFAULT or not payload:
_LOGGER.debug("Ignoring empty xy-color message from '%s'", msg.topic)
return

View File

@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, TypedDict, Union
import attr
from homeassistant.backports.enum import StrEnum
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers import template
@ -26,7 +27,13 @@ if TYPE_CHECKING:
from .discovery import MQTTDiscoveryPayload
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__)
@ -189,7 +196,7 @@ class MqttValueTemplate:
def async_render_with_possible_json_value(
self,
payload: ReceivePayloadType,
default: ReceivePayloadType | object = _SENTINEL,
default: ReceivePayloadType | PayloadSentinel = PayloadSentinel.NONE,
variables: TemplateVarsType = None,
) -> ReceivePayloadType:
"""Render with possible json value or pass-though a received MQTT value."""
@ -213,7 +220,7 @@ class MqttValueTemplate:
)
values[ATTR_THIS] = self._template_state
if default == _SENTINEL:
if default is PayloadSentinel.NONE:
_LOGGER.debug(
"Rendering incoming payload '%s' with variables %s and %s",
payload,

View File

@ -45,7 +45,7 @@ from .mixins import (
async_setup_platform_helper,
warn_for_legacy_schema,
)
from .models import MqttValueTemplate
from .models import MqttValueTemplate, PayloadSentinel, ReceiveMessage
from .util import get_mqtt_data, valid_subscribe_topic
_LOGGER = logging.getLogger(__name__)
@ -244,7 +244,7 @@ class MqttSensor(MqttEntity, RestoreSensor):
"""(Re)Subscribe to topics."""
topics = {}
def _update_state(msg):
def _update_state(msg: ReceiveMessage) -> None:
# auto-expire enabled?
expire_after = self._config.get(CONF_EXPIRE_AFTER)
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
)
payload = self._template(msg.payload, default=self.native_value)
if payload is not None and self.device_class in (
payload = self._template(msg.payload, default=PayloadSentinel.DEFAULT)
if payload is PayloadSentinel.DEFAULT:
return
if self.device_class not in {
SensorDeviceClass.DATE,
SensorDeviceClass.TIMESTAMP,
):
if (payload := dt_util.parse_datetime(payload)) is None:
_LOGGER.warning(
"Invalid state message '%s' from '%s'", msg.payload, msg.topic
)
elif self.device_class == SensorDeviceClass.DATE:
payload = payload.date()
self._attr_native_value = payload
}:
self._attr_native_value = str(payload)
return
if (payload_datetime := dt_util.parse_datetime(str(payload))) is None:
_LOGGER.warning(
"Invalid state message '%s' from '%s'", msg.payload, msg.topic
)
self._attr_native_value = None
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):
payload = self._last_reset_template(msg.payload)

View File

@ -19,7 +19,7 @@ from ..config import MQTT_BASE_SCHEMA
from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN
from ..debug_info import log_messages
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 .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services
@ -246,7 +246,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
@callback
@log_messages(self.hass, self.entity_id)
def message_received(msg):
def message_received(msg: ReceiveMessage) -> None:
"""Handle new MQTT message."""
if (
msg.topic == self._state_topics[CONF_BATTERY_LEVEL_TOPIC]
@ -254,8 +254,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
):
battery_level = self._templates[
CONF_BATTERY_LEVEL_TEMPLATE
].async_render_with_possible_json_value(msg.payload, None)
if battery_level:
].async_render_with_possible_json_value(
msg.payload, PayloadSentinel.DEFAULT
)
if battery_level and battery_level is not PayloadSentinel.DEFAULT:
self._battery_level = int(battery_level)
if (
@ -264,8 +266,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
):
charging = self._templates[
CONF_CHARGING_TEMPLATE
].async_render_with_possible_json_value(msg.payload, None)
if charging:
].async_render_with_possible_json_value(
msg.payload, PayloadSentinel.DEFAULT
)
if charging and charging is not PayloadSentinel.DEFAULT:
self._charging = cv.boolean(charging)
if (
@ -274,8 +278,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
):
cleaning = self._templates[
CONF_CLEANING_TEMPLATE
].async_render_with_possible_json_value(msg.payload, None)
if cleaning:
].async_render_with_possible_json_value(
msg.payload, PayloadSentinel.DEFAULT
)
if cleaning and cleaning is not PayloadSentinel.DEFAULT:
self._cleaning = cv.boolean(cleaning)
if (
@ -284,8 +290,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
):
docked = self._templates[
CONF_DOCKED_TEMPLATE
].async_render_with_possible_json_value(msg.payload, None)
if docked:
].async_render_with_possible_json_value(
msg.payload, PayloadSentinel.DEFAULT
)
if docked and docked is not PayloadSentinel.DEFAULT:
self._docked = cv.boolean(docked)
if (
@ -294,8 +302,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
):
error = self._templates[
CONF_ERROR_TEMPLATE
].async_render_with_possible_json_value(msg.payload, None)
if error is not None:
].async_render_with_possible_json_value(
msg.payload, PayloadSentinel.DEFAULT
)
if error is not PayloadSentinel.DEFAULT:
self._error = cv.string(error)
if self._docked:
@ -316,8 +326,10 @@ class MqttVacuum(MqttEntity, VacuumEntity):
):
fan_speed = self._templates[
CONF_FAN_SPEED_TEMPLATE
].async_render_with_possible_json_value(msg.payload, None)
if fan_speed:
].async_render_with_possible_json_value(
msg.payload, PayloadSentinel.DEFAULT
)
if fan_speed and fan_speed is not PayloadSentinel.DEFAULT:
self._fan_speed = fan_speed
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)