Add this
object to MQTT templates (#77142)
* Add `this` object to MQTT templates * Only set once, remove hass guard * Set once if there is a state * Add tests TemplateStateFromEntityId calls once
This commit is contained in:
parent
c76dec138a
commit
be2366d773
2 changed files with 50 additions and 6 deletions
|
@ -17,6 +17,8 @@ from homeassistant.helpers.typing import TemplateVarsType
|
||||||
|
|
||||||
_SENTINEL = object()
|
_SENTINEL = object()
|
||||||
|
|
||||||
|
ATTR_THIS = "this"
|
||||||
|
|
||||||
PublishPayloadType = Union[str, bytes, int, float, None]
|
PublishPayloadType = Union[str, bytes, int, float, None]
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +59,8 @@ class MqttCommandTemplate:
|
||||||
entity: Entity | None = None,
|
entity: Entity | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Instantiate a command template."""
|
"""Instantiate a command template."""
|
||||||
self._attr_command_template = command_template
|
self._template_state: template.TemplateStateFromEntityId | None = None
|
||||||
|
self._command_template = command_template
|
||||||
if command_template is None:
|
if command_template is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -91,17 +94,23 @@ class MqttCommandTemplate:
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
if self._attr_command_template is None:
|
if self._command_template is None:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
values = {"value": value}
|
values: dict[str, Any] = {"value": value}
|
||||||
if self._entity:
|
if self._entity:
|
||||||
values[ATTR_ENTITY_ID] = self._entity.entity_id
|
values[ATTR_ENTITY_ID] = self._entity.entity_id
|
||||||
values[ATTR_NAME] = self._entity.name
|
values[ATTR_NAME] = self._entity.name
|
||||||
|
if not self._template_state:
|
||||||
|
self._template_state = template.TemplateStateFromEntityId(
|
||||||
|
self._command_template.hass, self._entity.entity_id
|
||||||
|
)
|
||||||
|
values[ATTR_THIS] = self._template_state
|
||||||
|
|
||||||
if variables is not None:
|
if variables is not None:
|
||||||
values.update(variables)
|
values.update(variables)
|
||||||
return _convert_outgoing_payload(
|
return _convert_outgoing_payload(
|
||||||
self._attr_command_template.async_render(values, parse_result=False)
|
self._command_template.async_render(values, parse_result=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +126,7 @@ class MqttValueTemplate:
|
||||||
config_attributes: TemplateVarsType = None,
|
config_attributes: TemplateVarsType = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Instantiate a value template."""
|
"""Instantiate a value template."""
|
||||||
|
self._template_state: template.TemplateStateFromEntityId | None = None
|
||||||
self._value_template = value_template
|
self._value_template = value_template
|
||||||
self._config_attributes = config_attributes
|
self._config_attributes = config_attributes
|
||||||
if value_template is None:
|
if value_template is None:
|
||||||
|
@ -150,6 +160,11 @@ class MqttValueTemplate:
|
||||||
if self._entity:
|
if self._entity:
|
||||||
values[ATTR_ENTITY_ID] = self._entity.entity_id
|
values[ATTR_ENTITY_ID] = self._entity.entity_id
|
||||||
values[ATTR_NAME] = self._entity.name
|
values[ATTR_NAME] = self._entity.name
|
||||||
|
if not self._template_state and self._value_template.hass:
|
||||||
|
self._template_state = template.TemplateStateFromEntityId(
|
||||||
|
self._value_template.hass, self._entity.entity_id
|
||||||
|
)
|
||||||
|
values[ATTR_THIS] = self._template_state
|
||||||
|
|
||||||
if default == _SENTINEL:
|
if default == _SENTINEL:
|
||||||
return self._value_template.async_render_with_possible_json_value(
|
return self._value_template.async_render_with_possible_json_value(
|
||||||
|
|
|
@ -299,7 +299,7 @@ async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config
|
||||||
"command_topic": topic,
|
"command_topic": topic,
|
||||||
"name": "Test Select",
|
"name": "Test Select",
|
||||||
"options": ["milk", "beer"],
|
"options": ["milk", "beer"],
|
||||||
"command_template": '{"option": "{{ value }}", "entity_id": "{{ entity_id }}", "name": "{{ name }}"}',
|
"command_template": '{"option": "{{ value }}", "entity_id": "{{ entity_id }}", "name": "{{ name }}", "this_object_state": "{{ this.state }}"}',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -319,7 +319,7 @@ async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config
|
||||||
|
|
||||||
mqtt_mock.async_publish.assert_called_once_with(
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
topic,
|
topic,
|
||||||
'{"option": "beer", "entity_id": "select.test_select", "name": "Test Select"}',
|
'{"option": "beer", "entity_id": "select.test_select", "name": "Test Select", "this_object_state": "milk"}',
|
||||||
0,
|
0,
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
|
@ -327,6 +327,20 @@ async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config
|
||||||
state = hass.states.get("select.test_select")
|
state = hass.states.get("select.test_select")
|
||||||
assert state.state == "beer"
|
assert state.state == "beer"
|
||||||
|
|
||||||
|
# Test that TemplateStateFromEntityId is not called again
|
||||||
|
with patch(
|
||||||
|
"homeassistant.helpers.template.TemplateStateFromEntityId", MagicMock()
|
||||||
|
) as template_state_calls:
|
||||||
|
await hass.services.async_call(
|
||||||
|
"select",
|
||||||
|
"select_option",
|
||||||
|
{"entity_id": "select.test_select", "option": "milk"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert template_state_calls.call_count == 0
|
||||||
|
state = hass.states.get("select.test_select")
|
||||||
|
assert state.state == "milk"
|
||||||
|
|
||||||
|
|
||||||
async def test_value_template_value(hass):
|
async def test_value_template_value(hass):
|
||||||
"""Test the rendering of MQTT value template."""
|
"""Test the rendering of MQTT value template."""
|
||||||
|
@ -359,10 +373,25 @@ async def test_value_template_value(hass):
|
||||||
# test value template with entity
|
# test value template with entity
|
||||||
entity = Entity()
|
entity = Entity()
|
||||||
entity.hass = hass
|
entity.hass = hass
|
||||||
|
entity.entity_id = "select.test"
|
||||||
tpl = template.Template("{{ value_json.id }}")
|
tpl = template.Template("{{ value_json.id }}")
|
||||||
val_tpl = mqtt.MqttValueTemplate(tpl, entity=entity)
|
val_tpl = mqtt.MqttValueTemplate(tpl, entity=entity)
|
||||||
assert val_tpl.async_render_with_possible_json_value('{"id": 4321}') == "4321"
|
assert val_tpl.async_render_with_possible_json_value('{"id": 4321}') == "4321"
|
||||||
|
|
||||||
|
# test this object in a template
|
||||||
|
tpl2 = template.Template("{{ this.entity_id }}")
|
||||||
|
val_tpl2 = mqtt.MqttValueTemplate(tpl2, entity=entity)
|
||||||
|
assert val_tpl2.async_render_with_possible_json_value("bla") == "select.test"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.helpers.template.TemplateStateFromEntityId", MagicMock()
|
||||||
|
) as template_state_calls:
|
||||||
|
tpl3 = template.Template("{{ this.entity_id }}")
|
||||||
|
val_tpl3 = mqtt.MqttValueTemplate(tpl3, entity=entity)
|
||||||
|
val_tpl3.async_render_with_possible_json_value("call1")
|
||||||
|
val_tpl3.async_render_with_possible_json_value("call2")
|
||||||
|
assert template_state_calls.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_service_call_without_topic_does_not_publish(
|
async def test_service_call_without_topic_does_not_publish(
|
||||||
hass, mqtt_mock_entry_no_yaml_config
|
hass, mqtt_mock_entry_no_yaml_config
|
||||||
|
|
Loading…
Add table
Reference in a new issue