From f3bd13d179471e7d7d862d05364f98e8bbb9376b Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 29 Oct 2021 15:59:16 +0200 Subject: [PATCH] Fix regression in MQTT discovery (#58684) * Fix regression in MQTT discovery * Update test --- homeassistant/components/mqtt/discovery.py | 5 +- tests/components/mqtt/test_discovery.py | 70 ++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index a237ea7aea1..ffc0c1de435 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -10,6 +10,7 @@ import time from homeassistant.const import CONF_DEVICE, CONF_PLATFORM from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import RESULT_TYPE_ABORT +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -141,7 +142,9 @@ async def async_start( # noqa: C901 if value[-1] == TOPIC_BASE and key.endswith("topic"): payload[key] = f"{value[:-1]}{base}" if payload.get(CONF_AVAILABILITY): - for availability_conf in payload[CONF_AVAILABILITY]: + for availability_conf in cv.ensure_list(payload[CONF_AVAILABILITY]): + if not isinstance(availability_conf, dict): + continue if topic := availability_conf.get(CONF_TOPIC): if topic[0] == TOPIC_BASE: availability_conf[CONF_TOPIC] = f"{base}{topic[1:]}" diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 8d2106c90fa..60c3961477b 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -504,6 +504,76 @@ async def test_discovery_expansion(hass, mqtt_mock, caplog): assert state.state == STATE_UNAVAILABLE +async def test_discovery_expansion_2(hass, mqtt_mock, caplog): + """Test expansion of abbreviated discovery payload.""" + data = ( + '{ "~": "some/base/topic",' + ' "name": "DiscoveryExpansionTest1",' + ' "stat_t": "test_topic/~",' + ' "cmd_t": "~/test_topic",' + ' "availability": {' + ' "topic":"~/avail_item1",' + ' "payload_available": "available",' + ' "payload_not_available": "not_available"' + " }," + ' "dev":{' + ' "ids":["5706DF"],' + ' "name":"DiscoveryExpansionTest1 Device",' + ' "mdl":"Generic",' + ' "sw":"1.2.3.4",' + ' "mf":"None",' + ' "sa":"default_area"' + " }" + "}" + ) + + async_fire_mqtt_message(hass, "homeassistant/switch/bla/config", data) + await hass.async_block_till_done() + + state = hass.states.get("switch.DiscoveryExpansionTest1") + assert state.state == STATE_UNAVAILABLE + + async_fire_mqtt_message(hass, "some/base/topic/avail_item1", "available") + await hass.async_block_till_done() + + state = hass.states.get("switch.DiscoveryExpansionTest1") + assert state is not None + assert state.name == "DiscoveryExpansionTest1" + assert ("switch", "bla") in hass.data[ALREADY_DISCOVERED] + assert state.state == STATE_OFF + + +@pytest.mark.no_fail_on_log_exception +async def test_discovery_expansion_3(hass, mqtt_mock, caplog): + """Test expansion of broken discovery payload.""" + data = ( + '{ "~": "some/base/topic",' + ' "name": "DiscoveryExpansionTest1",' + ' "stat_t": "test_topic/~",' + ' "cmd_t": "~/test_topic",' + ' "availability": "incorrect",' + ' "dev":{' + ' "ids":["5706DF"],' + ' "name":"DiscoveryExpansionTest1 Device",' + ' "mdl":"Generic",' + ' "sw":"1.2.3.4",' + ' "mf":"None",' + ' "sa":"default_area"' + " }" + "}" + ) + + async_fire_mqtt_message(hass, "homeassistant/switch/bla/config", data) + await hass.async_block_till_done() + assert hass.states.get("switch.DiscoveryExpansionTest1") is None + # Make sure the malformed availability data does not trip up discovery by asserting + # there are schema valdiation errors in the log + assert ( + "voluptuous.error.MultipleInvalid: expected a dictionary @ data['availability'][0]" + in caplog.text + ) + + ABBREVIATIONS_WHITE_LIST = [ # MQTT client/server/trigger settings "CONF_BIRTH_MESSAGE",