Mqtt rework on value templates (#62105)
* add MqttValueTemplate class * support variables at initiation * pass MqttEntity instead of hass * Use MqttValueTemplace class for value templates * make hass en enitity parameters conditional * remove unused property and remove None assignment * rename self._attr_value_template
This commit is contained in:
parent
bf78ddcadb
commit
3ca18922e6
19 changed files with 253 additions and 227 deletions
|
@ -99,6 +99,8 @@ from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_SENTINEL = object()
|
||||
|
||||
DATA_MQTT = "mqtt"
|
||||
|
||||
SERVICE_PUBLISH = "publish"
|
||||
|
@ -317,6 +319,62 @@ class MqttCommandTemplate:
|
|||
)
|
||||
|
||||
|
||||
class MqttValueTemplate:
|
||||
"""Class for rendering MQTT value template with possible json values."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value_template: template.Template | None,
|
||||
*,
|
||||
hass: HomeAssistant | None = None,
|
||||
entity: Entity | None = None,
|
||||
config_attributes: template.TemplateVarsType = None,
|
||||
) -> None:
|
||||
"""Instantiate a value template."""
|
||||
self._value_template = value_template
|
||||
self._config_attributes = config_attributes
|
||||
if value_template is None:
|
||||
return
|
||||
|
||||
value_template.hass = hass
|
||||
self._entity = entity
|
||||
|
||||
if entity:
|
||||
value_template.hass = entity.hass
|
||||
|
||||
@callback
|
||||
def async_render_with_possible_json_value(
|
||||
self,
|
||||
payload: ReceivePayloadType,
|
||||
default: ReceivePayloadType | object = _SENTINEL,
|
||||
variables: template.TemplateVarsType = None,
|
||||
) -> ReceivePayloadType:
|
||||
"""Render with possible json value or pass-though a received MQTT value."""
|
||||
if self._value_template is None:
|
||||
return payload
|
||||
|
||||
values: dict[str, Any] = {}
|
||||
|
||||
if variables is not None:
|
||||
values.update(variables)
|
||||
|
||||
if self._config_attributes is not None:
|
||||
values.update(self._config_attributes)
|
||||
|
||||
if self._entity:
|
||||
values[ATTR_ENTITY_ID] = self._entity.entity_id
|
||||
values[ATTR_NAME] = self._entity.name
|
||||
|
||||
if default == _SENTINEL:
|
||||
return self._value_template.async_render_with_possible_json_value(
|
||||
payload, variables=values
|
||||
)
|
||||
|
||||
return self._value_template.async_render_with_possible_json_value(
|
||||
payload, default, variables=values
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MqttServiceInfo(BaseServiceInfo):
|
||||
"""Prepared info from mqtt entries."""
|
||||
|
|
|
@ -38,7 +38,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from homeassistant.helpers.reload import async_setup_reload_service
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -165,9 +165,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
|
|||
return DISCOVERY_SCHEMA
|
||||
|
||||
def _setup_from_config(self, config):
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
value_template.hass = self.hass
|
||||
self._value_template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
self._command_template = MqttCommandTemplate(
|
||||
self._config[CONF_COMMAND_TEMPLATE], entity=self
|
||||
).async_render
|
||||
|
@ -179,12 +180,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def message_received(msg):
|
||||
"""Run when new MQTT message has been received."""
|
||||
payload = msg.payload
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
payload = value_template.async_render_with_possible_json_value(
|
||||
msg.payload, self._state
|
||||
)
|
||||
payload = self._value_template(msg.payload)
|
||||
if payload not in (
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
|
|
|
@ -30,7 +30,7 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
|||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import PLATFORMS, subscription
|
||||
from . import PLATFORMS, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, DOMAIN
|
||||
from .debug_info import log_messages
|
||||
|
@ -119,9 +119,10 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity):
|
|||
return DISCOVERY_SCHEMA
|
||||
|
||||
def _setup_from_config(self, config):
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
value_template.hass = self.hass
|
||||
self._value_template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
@ -137,7 +138,6 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def state_message_received(msg):
|
||||
"""Handle a new received MQTT state message."""
|
||||
payload = msg.payload
|
||||
# auto-expire enabled?
|
||||
expire_after = self._config.get(CONF_EXPIRE_AFTER)
|
||||
|
||||
|
@ -159,18 +159,14 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity):
|
|||
self.hass, self._value_is_expired, expiration_at
|
||||
)
|
||||
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
payload = value_template.async_render_with_possible_json_value(
|
||||
payload, variables={"entity_id": self.entity_id}
|
||||
)
|
||||
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._config[CONF_NAME],
|
||||
self._config[CONF_STATE_TOPIC],
|
||||
msg.payload,
|
||||
value_template,
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
)
|
||||
return
|
||||
|
||||
|
@ -180,8 +176,8 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity):
|
|||
self._state = False
|
||||
else: # Payload is not for this entity
|
||||
template_info = ""
|
||||
if value_template is not None:
|
||||
template_info = f", template output: '{payload}', with value template '{str(value_template)}'"
|
||||
if self._config.get(CONF_VALUE_TEMPLATE) is not None:
|
||||
template_info = f", template output: '{payload}', with value template '{str(self._config.get(CONF_VALUE_TEMPLATE))}'"
|
||||
_LOGGER.info(
|
||||
"No matching payload found for entity: %s with state topic: %s. Payload: '%s'%s",
|
||||
self._config[CONF_NAME],
|
||||
|
|
|
@ -56,7 +56,13 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from homeassistant.helpers.reload import async_setup_reload_service
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import MQTT_BASE_PLATFORM_SCHEMA, PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import (
|
||||
MQTT_BASE_PLATFORM_SCHEMA,
|
||||
PLATFORMS,
|
||||
MqttCommandTemplate,
|
||||
MqttValueTemplate,
|
||||
subscription,
|
||||
)
|
||||
from .. import mqtt
|
||||
from .const import CONF_ENCODING, CONF_QOS, CONF_RETAIN, DOMAIN
|
||||
from .debug_info import log_messages
|
||||
|
@ -372,19 +378,20 @@ class MqttClimate(MqttEntity, ClimateEntity):
|
|||
|
||||
value_templates = {}
|
||||
for key in VALUE_TEMPLATE_KEYS:
|
||||
value_templates[key] = lambda value: value
|
||||
value_templates[key] = None
|
||||
if CONF_VALUE_TEMPLATE in config:
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
value_template.hass = self.hass
|
||||
value_templates = {
|
||||
key: value_template.async_render_with_possible_json_value
|
||||
for key in VALUE_TEMPLATE_KEYS
|
||||
key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS
|
||||
}
|
||||
for key in VALUE_TEMPLATE_KEYS & config.keys():
|
||||
tpl = config[key]
|
||||
value_templates[key] = tpl.async_render_with_possible_json_value
|
||||
tpl.hass = self.hass
|
||||
self._value_templates = value_templates
|
||||
value_templates[key] = config[key]
|
||||
self._value_templates = {
|
||||
key: MqttValueTemplate(
|
||||
template,
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
for key, template in value_templates.items()
|
||||
}
|
||||
|
||||
command_templates = {}
|
||||
for key in COMMAND_TEMPLATE_KEYS:
|
||||
|
|
|
@ -40,7 +40,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from homeassistant.helpers.reload import async_setup_reload_service
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -303,27 +303,39 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||
# Force into optimistic tilt mode.
|
||||
self._tilt_optimistic = True
|
||||
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
value_template.hass = self.hass
|
||||
template_config_attributes = {
|
||||
"position_open": self._config[CONF_POSITION_OPEN],
|
||||
"position_closed": self._config[CONF_POSITION_CLOSED],
|
||||
"tilt_min": self._config[CONF_TILT_MIN],
|
||||
"tilt_max": self._config[CONF_TILT_MAX],
|
||||
}
|
||||
|
||||
self._value_template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
self._set_position_template = MqttCommandTemplate(
|
||||
self._config.get(CONF_SET_POSITION_TEMPLATE), entity=self
|
||||
).async_render
|
||||
|
||||
get_position_template = self._config.get(CONF_GET_POSITION_TEMPLATE)
|
||||
if get_position_template is not None:
|
||||
get_position_template.hass = self.hass
|
||||
self._get_position_template = MqttValueTemplate(
|
||||
self._config.get(CONF_GET_POSITION_TEMPLATE),
|
||||
entity=self,
|
||||
config_attributes=template_config_attributes,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
self._set_tilt_template = MqttCommandTemplate(
|
||||
self._config.get(CONF_TILT_COMMAND_TEMPLATE), entity=self
|
||||
).async_render
|
||||
|
||||
tilt_status_template = self._config.get(CONF_TILT_STATUS_TEMPLATE)
|
||||
if tilt_status_template is not None:
|
||||
tilt_status_template.hass = self.hass
|
||||
self._tilt_status_template = MqttValueTemplate(
|
||||
self._config.get(CONF_TILT_STATUS_TEMPLATE),
|
||||
entity=self,
|
||||
config_attributes=template_config_attributes,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self): # noqa: C901
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
topics = {}
|
||||
|
||||
|
@ -331,19 +343,7 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def tilt_message_received(msg):
|
||||
"""Handle tilt updates."""
|
||||
payload = msg.payload
|
||||
template = self._config.get(CONF_TILT_STATUS_TEMPLATE)
|
||||
if template is not None:
|
||||
variables = {
|
||||
"entity_id": self.entity_id,
|
||||
"position_open": self._config[CONF_POSITION_OPEN],
|
||||
"position_closed": self._config[CONF_POSITION_CLOSED],
|
||||
"tilt_min": self._config[CONF_TILT_MIN],
|
||||
"tilt_max": self._config[CONF_TILT_MAX],
|
||||
}
|
||||
payload = template.async_render_with_possible_json_value(
|
||||
payload, variables=variables
|
||||
)
|
||||
payload = self._tilt_status_template(msg.payload)
|
||||
|
||||
if not payload:
|
||||
_LOGGER.debug("Ignoring empty tilt message from '%s'", msg.topic)
|
||||
|
@ -355,13 +355,7 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def state_message_received(msg):
|
||||
"""Handle new MQTT state messages."""
|
||||
payload = msg.payload
|
||||
template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if template is not None:
|
||||
variables = {"entity_id": self.entity_id}
|
||||
payload = template.async_render_with_possible_json_value(
|
||||
payload, variables=variables
|
||||
)
|
||||
payload = self._value_template(msg.payload)
|
||||
|
||||
if not payload:
|
||||
_LOGGER.debug("Ignoring empty state message from '%s'", msg.topic)
|
||||
|
@ -399,25 +393,10 @@ class MqttCover(MqttEntity, CoverEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def position_message_received(msg):
|
||||
"""Handle new MQTT position messages."""
|
||||
payload = msg.payload
|
||||
|
||||
template = self._config.get(CONF_GET_POSITION_TEMPLATE)
|
||||
if template is not None:
|
||||
variables = {
|
||||
"entity_id": self.entity_id,
|
||||
"position_open": self._config[CONF_POSITION_OPEN],
|
||||
"position_closed": self._config[CONF_POSITION_CLOSED],
|
||||
"tilt_min": self._config[CONF_TILT_MIN],
|
||||
"tilt_max": self._config[CONF_TILT_MAX],
|
||||
}
|
||||
payload = template.async_render_with_possible_json_value(
|
||||
payload, variables=variables
|
||||
)
|
||||
payload = self._get_position_template(msg.payload)
|
||||
|
||||
if not payload:
|
||||
_LOGGER.debug(
|
||||
"Ignoring empty position message from '%s'", msg.topic
|
||||
)
|
||||
_LOGGER.debug("Ignoring empty position message from '%s'", msg.topic)
|
||||
return
|
||||
|
||||
try:
|
||||
|
|
|
@ -18,7 +18,7 @@ from homeassistant.const import (
|
|||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .. import subscription
|
||||
from .. import MqttValueTemplate, subscription
|
||||
from ... import mqtt
|
||||
from ..const import CONF_QOS, CONF_STATE_TOPIC
|
||||
from ..debug_info import log_messages
|
||||
|
@ -73,9 +73,9 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
|||
|
||||
def _setup_from_config(self, config):
|
||||
"""(Re)Setup the entity."""
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
value_template.hass = self.hass
|
||||
self._value_template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE), entity=self
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
@ -84,10 +84,7 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def message_received(msg):
|
||||
"""Handle new MQTT messages."""
|
||||
payload = msg.payload
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
payload = value_template.async_render_with_possible_json_value(payload)
|
||||
payload = self._value_template(msg.payload)
|
||||
if payload == self._config[CONF_PAYLOAD_HOME]:
|
||||
self._location_name = STATE_HOME
|
||||
elif payload == self._config[CONF_PAYLOAD_NOT_HOME]:
|
||||
|
|
|
@ -40,7 +40,7 @@ from homeassistant.util.percentage import (
|
|||
ranged_value_to_percentage,
|
||||
)
|
||||
|
||||
from . import PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -356,11 +356,10 @@ class MqttFan(MqttEntity, FanEntity):
|
|||
).async_render
|
||||
|
||||
for key, tpl in self._value_templates.items():
|
||||
if tpl is None:
|
||||
self._value_templates[key] = lambda value: value
|
||||
else:
|
||||
tpl.hass = self.hass
|
||||
self._value_templates[key] = tpl.async_render_with_possible_json_value
|
||||
self._value_templates[key] = MqttValueTemplate(
|
||||
tpl,
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
|
|
@ -30,7 +30,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from homeassistant.helpers.reload import async_setup_reload_service
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -262,11 +262,10 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
|
|||
).async_render
|
||||
|
||||
for key, tpl in self._value_templates.items():
|
||||
if tpl is None:
|
||||
self._value_templates[key] = lambda value: value
|
||||
else:
|
||||
tpl.hass = self.hass
|
||||
self._value_templates[key] = tpl.async_render_with_possible_json_value
|
||||
self._value_templates[key] = MqttValueTemplate(
|
||||
tpl,
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
|
|
@ -51,7 +51,7 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
from .. import MqttCommandTemplate, subscription
|
||||
from .. import MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from ... import mqtt
|
||||
from ..const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -325,12 +325,19 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||
|
||||
value_templates = {}
|
||||
for key in VALUE_TEMPLATE_KEYS:
|
||||
value_templates[key] = lambda value, _: value
|
||||
value_templates[key] = None
|
||||
if CONF_VALUE_TEMPLATE in config:
|
||||
value_templates = {
|
||||
key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS
|
||||
}
|
||||
for key in VALUE_TEMPLATE_KEYS & config.keys():
|
||||
tpl = config[key]
|
||||
value_templates[key] = tpl.async_render_with_possible_json_value
|
||||
tpl.hass = self.hass
|
||||
self._value_templates = value_templates
|
||||
value_templates[key] = config[key]
|
||||
self._value_templates = {
|
||||
key: MqttValueTemplate(
|
||||
template, entity=self
|
||||
).async_render_with_possible_json_value
|
||||
for key, template in value_templates.items()
|
||||
}
|
||||
|
||||
command_templates = {}
|
||||
for key in COMMAND_TEMPLATE_KEYS:
|
||||
|
|
|
@ -33,7 +33,7 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
from .. import subscription
|
||||
from .. import MqttValueTemplate, subscription
|
||||
from ... import mqtt
|
||||
from ..const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -160,7 +160,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
|
|||
"""(Re)Subscribe to topics."""
|
||||
for tpl in self._templates.values():
|
||||
if tpl is not None:
|
||||
tpl.hass = self.hass
|
||||
tpl = MqttValueTemplate(tpl, entity=self)
|
||||
|
||||
last_state = await self.async_get_last_state()
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from homeassistant.helpers.reload import async_setup_reload_service
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, subscription
|
||||
from . import PLATFORMS, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -118,9 +118,10 @@ class MqttLock(MqttEntity, LockEntity):
|
|||
"""(Re)Setup the entity."""
|
||||
self._optimistic = config[CONF_OPTIMISTIC]
|
||||
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
value_template.hass = self.hass
|
||||
self._value_template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE),
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
@ -129,10 +130,7 @@ class MqttLock(MqttEntity, LockEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def message_received(msg):
|
||||
"""Handle new MQTT messages."""
|
||||
payload = msg.payload
|
||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
payload = value_template.async_render_with_possible_json_value(payload)
|
||||
payload = self._value_template(msg.payload)
|
||||
if payload == self._config[CONF_STATE_LOCKED]:
|
||||
self._state = True
|
||||
elif payload == self._config[CONF_STATE_UNLOCKED]:
|
||||
|
|
|
@ -38,7 +38,7 @@ from homeassistant.helpers.entity import (
|
|||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import DATA_MQTT, debug_info, publish, subscription
|
||||
from . import DATA_MQTT, MqttValueTemplate, debug_info, publish, subscription
|
||||
from .const import (
|
||||
ATTR_DISCOVERY_HASH,
|
||||
ATTR_DISCOVERY_PAYLOAD,
|
||||
|
@ -254,17 +254,15 @@ class MqttAttributes(Entity):
|
|||
|
||||
async def _attributes_subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
attr_tpl = self._attributes_config.get(CONF_JSON_ATTRS_TEMPLATE)
|
||||
if attr_tpl is not None:
|
||||
attr_tpl.hass = self.hass
|
||||
attr_tpl = MqttValueTemplate(
|
||||
self._attributes_config.get(CONF_JSON_ATTRS_TEMPLATE), entity=self
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
def attributes_message_received(msg: ReceiveMessage) -> None:
|
||||
try:
|
||||
payload = msg.payload
|
||||
if attr_tpl is not None:
|
||||
payload = attr_tpl.async_render_with_possible_json_value(payload)
|
||||
payload = attr_tpl(msg.payload)
|
||||
json_dict = json.loads(payload) if isinstance(payload, str) else None
|
||||
if isinstance(json_dict, dict):
|
||||
filtered_dict = {
|
||||
|
@ -356,14 +354,10 @@ class MqttAvailability(Entity):
|
|||
topic, # pylint: disable=unused-variable
|
||||
avail_topic_conf,
|
||||
) in self._avail_topics.items():
|
||||
tpl = avail_topic_conf[CONF_AVAILABILITY_TEMPLATE]
|
||||
if tpl is None:
|
||||
avail_topic_conf[CONF_AVAILABILITY_TEMPLATE] = lambda value: value
|
||||
else:
|
||||
tpl.hass = self.hass
|
||||
avail_topic_conf[
|
||||
CONF_AVAILABILITY_TEMPLATE
|
||||
] = tpl.async_render_with_possible_json_value
|
||||
avail_topic_conf[CONF_AVAILABILITY_TEMPLATE] = MqttValueTemplate(
|
||||
avail_topic_conf[CONF_AVAILABILITY_TEMPLATE],
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
self._avail_config = config
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
|||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -157,18 +157,12 @@ class MqttNumber(MqttEntity, NumberEntity, RestoreEntity):
|
|||
CONF_COMMAND_TEMPLATE: MqttCommandTemplate(
|
||||
config.get(CONF_COMMAND_TEMPLATE), entity=self
|
||||
).async_render,
|
||||
CONF_VALUE_TEMPLATE: config.get(CONF_VALUE_TEMPLATE),
|
||||
CONF_VALUE_TEMPLATE: MqttValueTemplate(
|
||||
config.get(CONF_VALUE_TEMPLATE),
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value,
|
||||
}
|
||||
|
||||
value_template = self._templates[CONF_VALUE_TEMPLATE]
|
||||
if value_template is None:
|
||||
self._templates[CONF_VALUE_TEMPLATE] = lambda value: value
|
||||
else:
|
||||
value_template.hass = self.hass
|
||||
self._templates[
|
||||
CONF_VALUE_TEMPLATE
|
||||
] = value_template.async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
|||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, MqttCommandTemplate, subscription
|
||||
from . import PLATFORMS, MqttCommandTemplate, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -123,18 +123,12 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity):
|
|||
CONF_COMMAND_TEMPLATE: MqttCommandTemplate(
|
||||
config.get(CONF_COMMAND_TEMPLATE), entity=self
|
||||
).async_render,
|
||||
CONF_VALUE_TEMPLATE: config.get(CONF_VALUE_TEMPLATE),
|
||||
CONF_VALUE_TEMPLATE: MqttValueTemplate(
|
||||
config.get(CONF_VALUE_TEMPLATE),
|
||||
entity=self,
|
||||
).async_render_with_possible_json_value,
|
||||
}
|
||||
|
||||
value_template = self._templates[CONF_VALUE_TEMPLATE]
|
||||
if value_template is None:
|
||||
self._templates[CONF_VALUE_TEMPLATE] = lambda value: value
|
||||
else:
|
||||
value_template.hass = self.hass
|
||||
self._templates[
|
||||
CONF_VALUE_TEMPLATE
|
||||
] = value_template.async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
|||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import PLATFORMS, subscription
|
||||
from . import PLATFORMS, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import CONF_ENCODING, CONF_QOS, CONF_STATE_TOPIC, DOMAIN
|
||||
from .debug_info import log_messages
|
||||
|
@ -167,19 +167,18 @@ class MqttSensor(MqttEntity, SensorEntity):
|
|||
|
||||
def _setup_from_config(self, config):
|
||||
"""(Re)Setup the entity."""
|
||||
template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if template is not None:
|
||||
template.hass = self.hass
|
||||
last_reset_template = self._config.get(CONF_LAST_RESET_VALUE_TEMPLATE)
|
||||
if last_reset_template is not None:
|
||||
last_reset_template.hass = self.hass
|
||||
self._template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE), entity=self
|
||||
).async_render_with_possible_json_value
|
||||
self._last_reset_template = MqttValueTemplate(
|
||||
self._config.get(CONF_LAST_RESET_VALUE_TEMPLATE), entity=self
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
topics = {}
|
||||
|
||||
def _update_state(msg):
|
||||
payload = msg.payload
|
||||
# auto-expire enabled?
|
||||
expire_after = self._config.get(CONF_EXPIRE_AFTER)
|
||||
if expire_after is not None and expire_after > 0:
|
||||
|
@ -198,14 +197,7 @@ class MqttSensor(MqttEntity, SensorEntity):
|
|||
self.hass, self._value_is_expired, expiration_at
|
||||
)
|
||||
|
||||
template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if template is not None:
|
||||
variables = {"entity_id": self.entity_id}
|
||||
payload = template.async_render_with_possible_json_value(
|
||||
payload,
|
||||
self._state,
|
||||
variables=variables,
|
||||
)
|
||||
payload = self._template(msg.payload)
|
||||
|
||||
if payload is not None and self.device_class in (
|
||||
SensorDeviceClass.DATE,
|
||||
|
@ -221,16 +213,8 @@ class MqttSensor(MqttEntity, SensorEntity):
|
|||
self._state = payload
|
||||
|
||||
def _update_last_reset(msg):
|
||||
payload = msg.payload
|
||||
payload = self._last_reset_template(msg.payload)
|
||||
|
||||
template = self._config.get(CONF_LAST_RESET_VALUE_TEMPLATE)
|
||||
if template is not None:
|
||||
variables = {"entity_id": self.entity_id}
|
||||
payload = template.async_render_with_possible_json_value(
|
||||
payload,
|
||||
self._state,
|
||||
variables=variables,
|
||||
)
|
||||
if not payload:
|
||||
_LOGGER.debug("Ignoring empty last_reset message from '%s'", msg.topic)
|
||||
return
|
||||
|
|
|
@ -24,7 +24,7 @@ from homeassistant.helpers.reload import async_setup_reload_service
|
|||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import PLATFORMS, subscription
|
||||
from . import PLATFORMS, MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
|
@ -128,9 +128,9 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
|
|||
|
||||
self._optimistic = config[CONF_OPTIMISTIC]
|
||||
|
||||
template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if template is not None:
|
||||
template.hass = self.hass
|
||||
self._value_template = MqttValueTemplate(
|
||||
self._config.get(CONF_VALUE_TEMPLATE), entity=self
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def _subscribe_topics(self):
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
@ -139,10 +139,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
|
|||
@log_messages(self.hass, self.entity_id)
|
||||
def state_message_received(msg):
|
||||
"""Handle new MQTT state messages."""
|
||||
payload = msg.payload
|
||||
template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||
if template is not None:
|
||||
payload = template.async_render_with_possible_json_value(payload)
|
||||
payload = self._value_template(msg.payload)
|
||||
if payload == self._state_on:
|
||||
self._state = True
|
||||
elif payload == self._state_off:
|
||||
|
|
|
@ -12,7 +12,7 @@ from homeassistant.helpers.dispatcher import (
|
|||
async_dispatcher_send,
|
||||
)
|
||||
|
||||
from . import subscription
|
||||
from . import MqttValueTemplate, subscription
|
||||
from .. import mqtt
|
||||
from .const import (
|
||||
ATTR_DISCOVERY_HASH,
|
||||
|
@ -143,12 +143,10 @@ class MQTTTagScanner:
|
|||
)
|
||||
|
||||
def _setup_from_config(self, config):
|
||||
self._value_template = lambda value, error_value: value
|
||||
if CONF_VALUE_TEMPLATE in config:
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
value_template.hass = self.hass
|
||||
|
||||
self._value_template = value_template.async_render_with_possible_json_value
|
||||
self._value_template = MqttValueTemplate(
|
||||
config.get(CONF_VALUE_TEMPLATE),
|
||||
hass=self.hass,
|
||||
).async_render_with_possible_json_value
|
||||
|
||||
async def setup(self):
|
||||
"""Set up the MQTT tag scanner."""
|
||||
|
@ -171,7 +169,7 @@ class MQTTTagScanner:
|
|||
"""Subscribe to MQTT topics."""
|
||||
|
||||
async def tag_scanned(msg):
|
||||
tag_id = self._value_template(msg.payload, error_value="").strip()
|
||||
tag_id = self._value_template(msg.payload, "").strip()
|
||||
if not tag_id: # No output from template, ignore
|
||||
return
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ from homeassistant.core import callback
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.icon import icon_for_battery_level
|
||||
|
||||
from .. import subscription
|
||||
from .. import MqttValueTemplate, subscription
|
||||
from ... import mqtt
|
||||
from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN
|
||||
from ..debug_info import log_messages
|
||||
|
@ -244,7 +244,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
"""(Re)Subscribe to topics."""
|
||||
for tpl in self._templates.values():
|
||||
if tpl is not None:
|
||||
tpl.hass = self.hass
|
||||
tpl = MqttValueTemplate(tpl, entity=self)
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
|
@ -256,7 +256,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
):
|
||||
battery_level = self._templates[
|
||||
CONF_BATTERY_LEVEL_TEMPLATE
|
||||
].async_render_with_possible_json_value(msg.payload, error_value=None)
|
||||
].async_render_with_possible_json_value(msg.payload, None)
|
||||
if battery_level:
|
||||
self._battery_level = int(battery_level)
|
||||
|
||||
|
@ -266,7 +266,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
):
|
||||
charging = self._templates[
|
||||
CONF_CHARGING_TEMPLATE
|
||||
].async_render_with_possible_json_value(msg.payload, error_value=None)
|
||||
].async_render_with_possible_json_value(msg.payload, None)
|
||||
if charging:
|
||||
self._charging = cv.boolean(charging)
|
||||
|
||||
|
@ -276,7 +276,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
):
|
||||
cleaning = self._templates[
|
||||
CONF_CLEANING_TEMPLATE
|
||||
].async_render_with_possible_json_value(msg.payload, error_value=None)
|
||||
].async_render_with_possible_json_value(msg.payload, None)
|
||||
if cleaning:
|
||||
self._cleaning = cv.boolean(cleaning)
|
||||
|
||||
|
@ -286,7 +286,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
):
|
||||
docked = self._templates[
|
||||
CONF_DOCKED_TEMPLATE
|
||||
].async_render_with_possible_json_value(msg.payload, error_value=None)
|
||||
].async_render_with_possible_json_value(msg.payload, None)
|
||||
if docked:
|
||||
self._docked = cv.boolean(docked)
|
||||
|
||||
|
@ -296,7 +296,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
):
|
||||
error = self._templates[
|
||||
CONF_ERROR_TEMPLATE
|
||||
].async_render_with_possible_json_value(msg.payload, error_value=None)
|
||||
].async_render_with_possible_json_value(msg.payload, None)
|
||||
if error is not None:
|
||||
self._error = cv.string(error)
|
||||
|
||||
|
@ -318,7 +318,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
|
|||
):
|
||||
fan_speed = self._templates[
|
||||
CONF_FAN_SPEED_TEMPLATE
|
||||
].async_render_with_possible_json_value(msg.payload, error_value=None)
|
||||
].async_render_with_possible_json_value(msg.payload, None)
|
||||
if fan_speed:
|
||||
self._fan_speed = fan_speed
|
||||
|
||||
|
|
|
@ -238,6 +238,35 @@ async def test_command_template_variables(hass, mqtt_mock):
|
|||
assert state.state == "beer"
|
||||
|
||||
|
||||
async def test_value_template_value(hass):
|
||||
"""Test the rendering of MQTT value template."""
|
||||
|
||||
variables = {"id": 1234, "some_var": "beer"}
|
||||
|
||||
# test rendering value
|
||||
tpl = template.Template("{{ value_json.id }}", hass)
|
||||
val_tpl = mqtt.MqttValueTemplate(tpl, hass=hass)
|
||||
assert val_tpl.async_render_with_possible_json_value('{"id": 4321}') == "4321"
|
||||
|
||||
# test variables at rendering
|
||||
tpl = template.Template("{{ value_json.id }} {{ some_var }}", hass)
|
||||
val_tpl = mqtt.MqttValueTemplate(tpl, hass=hass)
|
||||
assert (
|
||||
val_tpl.async_render_with_possible_json_value(
|
||||
'{"id": 4321}', variables=variables
|
||||
)
|
||||
== "4321 beer"
|
||||
)
|
||||
|
||||
# test with default value if an error occurs due to an invalid template
|
||||
tpl = template.Template("{{ value_json.id | as_datetime }}")
|
||||
val_tpl = mqtt.MqttValueTemplate(tpl, hass=hass)
|
||||
assert (
|
||||
val_tpl.async_render_with_possible_json_value('{"otherid": 4321}', "my default")
|
||||
== "my default"
|
||||
)
|
||||
|
||||
|
||||
async def test_service_call_without_topic_does_not_publish(hass, mqtt_mock):
|
||||
"""Test the service call if topic is missing."""
|
||||
with pytest.raises(vol.Invalid):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue