mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Fix MQTT trigger where wanted payload may be parsed as an integer (#47162)
This commit is contained in:
parent
6ff3eb0569
commit
19cd29affa
@ -13,6 +13,7 @@ from homeassistant.const import (
|
|||||||
CONF_DOMAIN,
|
CONF_DOMAIN,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
|
CONF_VALUE_TEMPLATE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
@ -66,10 +67,11 @@ TRIGGER_DISCOVERY_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend(
|
|||||||
{
|
{
|
||||||
vol.Required(CONF_AUTOMATION_TYPE): str,
|
vol.Required(CONF_AUTOMATION_TYPE): str,
|
||||||
vol.Required(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
vol.Required(CONF_DEVICE): MQTT_ENTITY_DEVICE_INFO_SCHEMA,
|
||||||
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
|
|
||||||
vol.Optional(CONF_PAYLOAD, default=None): vol.Any(None, cv.string),
|
vol.Optional(CONF_PAYLOAD, default=None): vol.Any(None, cv.string),
|
||||||
vol.Required(CONF_TYPE): cv.string,
|
|
||||||
vol.Required(CONF_SUBTYPE): cv.string,
|
vol.Required(CONF_SUBTYPE): cv.string,
|
||||||
|
vol.Required(CONF_TOPIC): cv.string,
|
||||||
|
vol.Required(CONF_TYPE): cv.string,
|
||||||
|
vol.Optional(CONF_VALUE_TEMPLATE, default=None): vol.Any(None, cv.string),
|
||||||
},
|
},
|
||||||
validate_device_has_at_least_one_identifier,
|
validate_device_has_at_least_one_identifier,
|
||||||
)
|
)
|
||||||
@ -96,6 +98,8 @@ class TriggerInstance:
|
|||||||
}
|
}
|
||||||
if self.trigger.payload:
|
if self.trigger.payload:
|
||||||
mqtt_config[CONF_PAYLOAD] = self.trigger.payload
|
mqtt_config[CONF_PAYLOAD] = self.trigger.payload
|
||||||
|
if self.trigger.value_template:
|
||||||
|
mqtt_config[CONF_VALUE_TEMPLATE] = self.trigger.value_template
|
||||||
mqtt_config = mqtt_trigger.TRIGGER_SCHEMA(mqtt_config)
|
mqtt_config = mqtt_trigger.TRIGGER_SCHEMA(mqtt_config)
|
||||||
|
|
||||||
if self.remove:
|
if self.remove:
|
||||||
@ -121,6 +125,7 @@ class Trigger:
|
|||||||
subtype: str = attr.ib()
|
subtype: str = attr.ib()
|
||||||
topic: str = attr.ib()
|
topic: str = attr.ib()
|
||||||
type: str = attr.ib()
|
type: str = attr.ib()
|
||||||
|
value_template: str = attr.ib()
|
||||||
trigger_instances: List[TriggerInstance] = attr.ib(factory=list)
|
trigger_instances: List[TriggerInstance] = attr.ib(factory=list)
|
||||||
|
|
||||||
async def add_trigger(self, action, automation_info):
|
async def add_trigger(self, action, automation_info):
|
||||||
@ -153,6 +158,7 @@ class Trigger:
|
|||||||
self.qos = config[CONF_QOS]
|
self.qos = config[CONF_QOS]
|
||||||
topic_changed = self.topic != config[CONF_TOPIC]
|
topic_changed = self.topic != config[CONF_TOPIC]
|
||||||
self.topic = config[CONF_TOPIC]
|
self.topic = config[CONF_TOPIC]
|
||||||
|
self.value_template = config[CONF_VALUE_TEMPLATE]
|
||||||
|
|
||||||
# Unsubscribe+subscribe if this trigger is in use and topic has changed
|
# Unsubscribe+subscribe if this trigger is in use and topic has changed
|
||||||
# If topic is same unsubscribe+subscribe will execute in the wrong order
|
# If topic is same unsubscribe+subscribe will execute in the wrong order
|
||||||
@ -245,6 +251,7 @@ async def async_setup_trigger(hass, config, config_entry, discovery_data):
|
|||||||
payload=config[CONF_PAYLOAD],
|
payload=config[CONF_PAYLOAD],
|
||||||
qos=config[CONF_QOS],
|
qos=config[CONF_QOS],
|
||||||
remove_signal=remove_signal,
|
remove_signal=remove_signal,
|
||||||
|
value_template=config[CONF_VALUE_TEMPLATE],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await hass.data[DEVICE_TRIGGERS][discovery_id].update_trigger(
|
await hass.data[DEVICE_TRIGGERS][discovery_id].update_trigger(
|
||||||
@ -325,6 +332,7 @@ async def async_attach_trigger(
|
|||||||
topic=None,
|
topic=None,
|
||||||
payload=None,
|
payload=None,
|
||||||
qos=None,
|
qos=None,
|
||||||
|
value_template=None,
|
||||||
)
|
)
|
||||||
return await hass.data[DEVICE_TRIGGERS][discovery_id].add_trigger(
|
return await hass.data[DEVICE_TRIGGERS][discovery_id].add_trigger(
|
||||||
action, automation_info
|
action, automation_info
|
||||||
|
@ -48,11 +48,13 @@ async def async_attach_trigger(hass, config, action, automation_info):
|
|||||||
|
|
||||||
template.attach(hass, wanted_payload)
|
template.attach(hass, wanted_payload)
|
||||||
if wanted_payload:
|
if wanted_payload:
|
||||||
wanted_payload = wanted_payload.async_render(variables, limited=True)
|
wanted_payload = wanted_payload.async_render(
|
||||||
|
variables, limited=True, parse_result=False
|
||||||
|
)
|
||||||
|
|
||||||
template.attach(hass, topic)
|
template.attach(hass, topic)
|
||||||
if isinstance(topic, template.Template):
|
if isinstance(topic, template.Template):
|
||||||
topic = topic.async_render(variables, limited=True)
|
topic = topic.async_render(variables, limited=True, parse_result=False)
|
||||||
topic = mqtt.util.valid_subscribe_topic(topic)
|
topic = mqtt.util.valid_subscribe_topic(topic)
|
||||||
|
|
||||||
template.attach(hass, value_template)
|
template.attach(hass, value_template)
|
||||||
|
@ -290,6 +290,81 @@ async def test_if_fires_on_mqtt_message(hass, device_reg, calls, mqtt_mock):
|
|||||||
assert calls[1].data["some"] == "long_press"
|
assert calls[1].data["some"] == "long_press"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_if_fires_on_mqtt_message_template(hass, device_reg, calls, mqtt_mock):
|
||||||
|
"""Test triggers firing."""
|
||||||
|
data1 = (
|
||||||
|
'{ "automation_type":"trigger",'
|
||||||
|
' "device":{"identifiers":["0AFFD2"]},'
|
||||||
|
" \"payload\": \"{{ 'foo_press'|regex_replace('foo', 'short') }}\","
|
||||||
|
' "topic": "foobar/triggers/button{{ sqrt(16)|round }}",'
|
||||||
|
' "type": "button_short_press",'
|
||||||
|
' "subtype": "button_1",'
|
||||||
|
' "value_template": "{{ value_json.button }}"}'
|
||||||
|
)
|
||||||
|
data2 = (
|
||||||
|
'{ "automation_type":"trigger",'
|
||||||
|
' "device":{"identifiers":["0AFFD2"]},'
|
||||||
|
" \"payload\": \"{{ 'foo_press'|regex_replace('foo', 'long') }}\","
|
||||||
|
' "topic": "foobar/triggers/button{{ sqrt(16)|round }}",'
|
||||||
|
' "type": "button_long_press",'
|
||||||
|
' "subtype": "button_2",'
|
||||||
|
' "value_template": "{{ value_json.button }}"}'
|
||||||
|
)
|
||||||
|
async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1)
|
||||||
|
async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data2)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
device_entry = device_reg.async_get_device({("mqtt", "0AFFD2")})
|
||||||
|
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: [
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "device",
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"device_id": device_entry.id,
|
||||||
|
"discovery_id": "bla1",
|
||||||
|
"type": "button_short_press",
|
||||||
|
"subtype": "button_1",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {"some": ("short_press")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "device",
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"device_id": device_entry.id,
|
||||||
|
"discovery_id": "bla2",
|
||||||
|
"type": "button_1",
|
||||||
|
"subtype": "button_long_press",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"service": "test.automation",
|
||||||
|
"data_template": {"some": ("long_press")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fake short press.
|
||||||
|
async_fire_mqtt_message(hass, "foobar/triggers/button4", '{"button":"short_press"}')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["some"] == "short_press"
|
||||||
|
|
||||||
|
# Fake long press.
|
||||||
|
async_fire_mqtt_message(hass, "foobar/triggers/button4", '{"button":"long_press"}')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(calls) == 2
|
||||||
|
assert calls[1].data["some"] == "long_press"
|
||||||
|
|
||||||
|
|
||||||
async def test_if_fires_on_mqtt_message_late_discover(
|
async def test_if_fires_on_mqtt_message_late_discover(
|
||||||
hass, device_reg, calls, mqtt_mock
|
hass, device_reg, calls, mqtt_mock
|
||||||
):
|
):
|
||||||
|
@ -81,6 +81,31 @@ async def test_if_fires_on_topic_and_payload_match(hass, calls):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_if_fires_on_topic_and_payload_match2(hass, calls):
|
||||||
|
"""Test if message is fired on topic and payload match.
|
||||||
|
|
||||||
|
Make sure a payload which would render as a non string can still be matched.
|
||||||
|
"""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: {
|
||||||
|
"trigger": {
|
||||||
|
"platform": "mqtt",
|
||||||
|
"topic": "test-topic",
|
||||||
|
"payload": "0",
|
||||||
|
},
|
||||||
|
"action": {"service": "test.automation"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
async_fire_mqtt_message(hass, "test-topic", "0")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_if_fires_on_templated_topic_and_payload_match(hass, calls):
|
async def test_if_fires_on_templated_topic_and_payload_match(hass, calls):
|
||||||
"""Test if message is fired on templated topic and payload match."""
|
"""Test if message is fired on templated topic and payload match."""
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user