diff --git a/homeassistant/components/mqtt/async_client.py b/homeassistant/components/mqtt/async_client.py index c0b847f35a1..882e910d7e8 100644 --- a/homeassistant/components/mqtt/async_client.py +++ b/homeassistant/components/mqtt/async_client.py @@ -44,7 +44,7 @@ class AsyncMQTTClient(MQTTClient): that is not needed since we are running in an async event loop. """ - def async_setup(self) -> None: + def setup(self) -> None: """Set up the client. All the threading locks are replaced with NullLock diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 13f33c44047..ace2293e7a6 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -277,9 +277,22 @@ class Subscription: class MqttClientSetup: """Helper class to setup the paho mqtt client from config.""" - def __init__(self, config: ConfigType) -> None: - """Initialize the MQTT client setup helper.""" + _client: AsyncMQTTClient + def __init__(self, config: ConfigType) -> None: + """Initialize the MQTT client setup helper. + + self.setup must be run in an executor job. + """ + + self._config = config + + def setup(self) -> None: + """Set up the MQTT client. + + The setup of the MQTT client should be run in an executor job, + because it accesses files, so it does IO. + """ # We don't import on the top because some integrations # should be able to optionally rely on MQTT. import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel @@ -287,6 +300,7 @@ class MqttClientSetup: # pylint: disable-next=import-outside-toplevel from .async_client import AsyncMQTTClient + config = self._config if (protocol := config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL)) == PROTOCOL_31: proto = mqtt.MQTTv31 elif protocol == PROTOCOL_5: @@ -298,11 +312,14 @@ class MqttClientSetup: # PAHO MQTT relies on the MQTT server to generate random client IDs. # However, that feature is not mandatory so we generate our own. client_id = mqtt.base62(uuid.uuid4().int, padding=22) - transport = config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) + transport: str = config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) self._client = AsyncMQTTClient( - client_id, protocol=proto, transport=transport, reconnect_on_failure=False + client_id, + protocol=proto, + transport=transport, + reconnect_on_failure=False, ) - self._client.async_setup() + self._client.setup() # Enable logging self._client.enable_logger() @@ -544,7 +561,9 @@ class MQTT: self.hass, "homeassistant.components.mqtt.async_client" ) - mqttc = MqttClientSetup(self.conf).client + mqttc_setup = MqttClientSetup(self.conf) + await self.hass.async_add_executor_job(mqttc_setup.setup) + mqttc = mqttc_setup.client # on_socket_unregister_write and _async_on_socket_close # are only ever called in the event loop mqttc.on_socket_close = self._async_on_socket_close diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 2c5d921e1db..17dfc6512b3 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -834,7 +834,9 @@ def try_connection( # should be able to optionally rely on MQTT. import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel - client = MqttClientSetup(user_input).client + mqtt_client_setup = MqttClientSetup(user_input) + mqtt_client_setup.setup() + client = mqtt_client_setup.client result: queue.Queue[bool] = queue.Queue(maxsize=1)