Fix potential deadlock in platform setup that waits for MQTT

This commit is contained in:
jbouwh 2025-04-04 18:11:31 +00:00
parent 3c60bff7dc
commit 451ca92df9
7 changed files with 98 additions and 75 deletions

View File

@ -120,11 +120,16 @@ async def async_setup_platform(
) -> None:
"""Set up the ARWN platform."""
async def _async_setup_mqtt() -> None:
# Make sure MQTT integration is enabled and the client is available
if not await mqtt.async_wait_for_mqtt_client(hass):
_LOGGER.error("MQTT integration is not available")
return
await mqtt.async_subscribe(hass, TOPIC, async_sensor_event_received, 0)
hass.create_task(_async_setup_mqtt(), "arwn setup")
@callback
def async_sensor_event_received(msg: mqtt.ReceiveMessage) -> None:
"""Process events as sensors.
@ -167,8 +172,6 @@ async def async_setup_platform(
)
store[sensor.name].set_event(event)
await mqtt.async_subscribe(hass, TOPIC, async_sensor_event_received, 0)
class ArwnSensor(SensorEntity):
"""Representation of an ARWN sensor."""

View File

@ -199,6 +199,8 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the manual MQTT alarm platform."""
async def _async_setup_entities() -> None:
# Make sure MQTT integration is enabled and the client is available
# We cannot count on dependencies as the alarm_control_panel platform setup
# also will be triggered when mqtt is loading the `alarm_control_panel` platform
@ -228,6 +230,8 @@ async def async_setup_platform(
]
)
hass.create_task(_async_setup_entities(), "manual_mqtt setup")
class ManualMQTTAlarm(AlarmControlPanelEntity):
"""Representation of an alarm status.

View File

@ -48,13 +48,27 @@ async def async_setup_scanner(
discovery_info: DiscoveryInfoType | None = None,
) -> bool:
"""Set up the MQTT JSON tracker."""
async def _async_wait_for_mqtt_and_set_up() -> None:
# Make sure MQTT integration is enabled and the client is available
# We cannot count on dependencies as the device_tracker platform setup
# also will be triggered when mqtt is loading the `device_tracker` platform
if not await mqtt.async_wait_for_mqtt_client(hass):
_LOGGER.error("MQTT integration is not available")
return False
return
await _async_setup_scanner(hass, config, async_see)
hass.create_task(_async_wait_for_mqtt_and_set_up(), "mqtt_json setup")
return True
async def _async_setup_scanner(
hass: HomeAssistant,
config: ConfigType,
async_see: AsyncSeeCallback,
) -> None:
"""Set up MQTT JSON tracker."""
devices = config[CONF_DEVICES]
qos = config[CONF_QOS]
@ -83,8 +97,6 @@ async def async_setup_scanner(
await mqtt.async_subscribe(hass, topic, async_message_received, qos)
return True
def _parse_see_args(dev_id, data):
"""Parse the payload location parameters, into the format see expects."""

View File

@ -81,6 +81,8 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up MQTT room Sensor."""
async def _async_setup_entities() -> None:
# Make sure MQTT integration is enabled and the client is available
# We cannot count on dependencies as the sensor platform setup
# also will be triggered when mqtt is loading the `sensor` platform
@ -100,6 +102,8 @@ async def async_setup_platform(
]
)
hass.create_task(_async_setup_entities(), "mqtt_room setup")
class MQTTRoomSensor(SensorEntity):
"""Representation of a room sensor that is updated via MQTT."""

View File

@ -103,7 +103,7 @@ async def test_no_pending(
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
entity_id = "alarm_control_panel.test"
@ -155,7 +155,7 @@ async def test_no_pending_when_code_not_req(
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
entity_id = "alarm_control_panel.test"
@ -206,7 +206,7 @@ async def test_with_pending(
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
entity_id = "alarm_control_panel.test"

View File

@ -65,7 +65,7 @@ async def test_setup_fails_without_mqtt_being_setup(
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
assert "MQTT integration is not available" in caplog.text
@ -95,7 +95,7 @@ async def test_ensure_device_tracker_platform_validation(hass: HomeAssistant) ->
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
assert mock_sp.call_count == 1
@ -110,7 +110,7 @@ async def test_json_message(hass: HomeAssistant) -> None:
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
state = hass.states.get("device_tracker.zanzito")
@ -131,7 +131,7 @@ async def test_non_json_message(
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
caplog.set_level(logging.ERROR)
caplog.clear()
@ -153,7 +153,7 @@ async def test_incomplete_message(
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
caplog.set_level(logging.ERROR)
caplog.clear()
@ -177,7 +177,7 @@ async def test_single_level_wildcard_topic(hass: HomeAssistant) -> None:
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -198,7 +198,7 @@ async def test_multi_level_wildcard_topic(hass: HomeAssistant) -> None:
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -220,7 +220,7 @@ async def test_single_level_wildcard_topic_not_matching(hass: HomeAssistant) ->
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -240,7 +240,7 @@ async def test_multi_level_wildcard_topic_not_matching(hass: HomeAssistant) -> N
DT_DOMAIN,
{DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()

View File

@ -78,7 +78,7 @@ async def test_no_mqtt(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) ->
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
state = hass.states.get(SENSOR_STATE)
assert state is None
assert "MQTT integration is not available" in caplog.text
@ -100,7 +100,7 @@ async def test_room_update(hass: HomeAssistant, mqtt_mock: MqttMockHAClient) ->
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
await send_message(hass, BEDROOM_TOPIC, FAR_MESSAGE)
await assert_state(hass, BEDROOM)
@ -141,7 +141,7 @@ async def test_unique_id_is_set(
}
},
)
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
state = hass.states.get(SENSOR_STATE)
assert state.state is not None