Revert " Upgrade paho-mqtt API to v2 (#137613)"

This reverts commit d6b7762dd65c7814f7b816a28f589bb0a3899233.
This commit is contained in:
jbouwh 2025-02-28 08:52:41 +00:00
parent 8dcd9945e8
commit c476e92bdc
9 changed files with 93 additions and 171 deletions

View File

@ -6,14 +6,7 @@ from functools import lru_cache
from types import TracebackType from types import TracebackType
from typing import Self from typing import Self
from paho.mqtt.client import ( from paho.mqtt.client import Client as MQTTClient
CallbackOnConnect_v2,
CallbackOnDisconnect_v2,
CallbackOnPublish_v2,
CallbackOnSubscribe_v2,
CallbackOnUnsubscribe_v2,
Client as MQTTClient,
)
_MQTT_LOCK_COUNT = 7 _MQTT_LOCK_COUNT = 7
@ -51,12 +44,6 @@ class AsyncMQTTClient(MQTTClient):
that is not needed since we are running in an async event loop. that is not needed since we are running in an async event loop.
""" """
on_connect: CallbackOnConnect_v2
on_disconnect: CallbackOnDisconnect_v2
on_publish: CallbackOnPublish_v2
on_subscribe: CallbackOnSubscribe_v2
on_unsubscribe: CallbackOnUnsubscribe_v2
def setup(self) -> None: def setup(self) -> None:
"""Set up the client. """Set up the client.

View File

@ -311,8 +311,8 @@ class MqttClientSetup:
client_id = None client_id = None
transport: str = config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) transport: str = config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT)
self._client = AsyncMQTTClient( self._client = AsyncMQTTClient(
callback_api_version=mqtt.CallbackAPIVersion.VERSION2, mqtt.CallbackAPIVersion.VERSION1,
client_id=client_id, client_id,
protocol=proto, protocol=proto,
transport=transport, # type: ignore[arg-type] transport=transport, # type: ignore[arg-type]
reconnect_on_failure=False, reconnect_on_failure=False,
@ -476,9 +476,9 @@ class MQTT:
mqttc.on_connect = self._async_mqtt_on_connect mqttc.on_connect = self._async_mqtt_on_connect
mqttc.on_disconnect = self._async_mqtt_on_disconnect mqttc.on_disconnect = self._async_mqtt_on_disconnect
mqttc.on_message = self._async_mqtt_on_message mqttc.on_message = self._async_mqtt_on_message
mqttc.on_publish = self._async_mqtt_on_publish mqttc.on_publish = self._async_mqtt_on_callback
mqttc.on_subscribe = self._async_mqtt_on_subscribe_unsubscribe mqttc.on_subscribe = self._async_mqtt_on_callback
mqttc.on_unsubscribe = self._async_mqtt_on_subscribe_unsubscribe mqttc.on_unsubscribe = self._async_mqtt_on_callback
# suppress exceptions at callback # suppress exceptions at callback
mqttc.suppress_exceptions = True mqttc.suppress_exceptions = True
@ -498,7 +498,7 @@ class MQTT:
def _async_reader_callback(self, client: mqtt.Client) -> None: def _async_reader_callback(self, client: mqtt.Client) -> None:
"""Handle reading data from the socket.""" """Handle reading data from the socket."""
if (status := client.loop_read(MAX_PACKETS_TO_READ)) != 0: if (status := client.loop_read(MAX_PACKETS_TO_READ)) != 0:
self._async_handle_callback_exception(status) self._async_on_disconnect(status)
@callback @callback
def _async_start_misc_periodic(self) -> None: def _async_start_misc_periodic(self) -> None:
@ -593,7 +593,7 @@ class MQTT:
def _async_writer_callback(self, client: mqtt.Client) -> None: def _async_writer_callback(self, client: mqtt.Client) -> None:
"""Handle writing data to the socket.""" """Handle writing data to the socket."""
if (status := client.loop_write()) != 0: if (status := client.loop_write()) != 0:
self._async_handle_callback_exception(status) self._async_on_disconnect(status)
def _on_socket_register_write( def _on_socket_register_write(
self, client: mqtt.Client, userdata: Any, sock: SocketType self, client: mqtt.Client, userdata: Any, sock: SocketType
@ -983,9 +983,9 @@ class MQTT:
self, self,
_mqttc: mqtt.Client, _mqttc: mqtt.Client,
_userdata: None, _userdata: None,
_connect_flags: mqtt.ConnectFlags, _flags: dict[str, int],
reason_code: mqtt.ReasonCode, result_code: int,
_properties: mqtt.Properties | None = None, properties: mqtt.Properties | None = None,
) -> None: ) -> None:
"""On connect callback. """On connect callback.
@ -993,20 +993,19 @@ class MQTT:
message. message.
""" """
# pylint: disable-next=import-outside-toplevel # pylint: disable-next=import-outside-toplevel
import paho.mqtt.client as mqtt
if reason_code.is_failure: if result_code != mqtt.CONNACK_ACCEPTED:
# 24: Continue authentication if result_code in (
# 25: Re-authenticate mqtt.CONNACK_REFUSED_BAD_USERNAME_PASSWORD,
# 134: Bad user name or password mqtt.CONNACK_REFUSED_NOT_AUTHORIZED,
# 135: Not authorized ):
# 140: Bad authentication method
if reason_code.value in (24, 25, 134, 135, 140):
self._should_reconnect = False self._should_reconnect = False
self.hass.async_create_task(self.async_disconnect()) self.hass.async_create_task(self.async_disconnect())
self.config_entry.async_start_reauth(self.hass) self.config_entry.async_start_reauth(self.hass)
_LOGGER.error( _LOGGER.error(
"Unable to connect to the MQTT broker: %s", "Unable to connect to the MQTT broker: %s",
reason_code.getName(), # type: ignore[no-untyped-call] mqtt.connack_string(result_code),
) )
self._async_connection_result(False) self._async_connection_result(False)
return return
@ -1017,7 +1016,7 @@ class MQTT:
"Connected to MQTT server %s:%s (%s)", "Connected to MQTT server %s:%s (%s)",
self.conf[CONF_BROKER], self.conf[CONF_BROKER],
self.conf.get(CONF_PORT, DEFAULT_PORT), self.conf.get(CONF_PORT, DEFAULT_PORT),
reason_code, result_code,
) )
birth: dict[str, Any] birth: dict[str, Any]
@ -1154,32 +1153,18 @@ class MQTT:
self._mqtt_data.state_write_requests.process_write_state_requests(msg) self._mqtt_data.state_write_requests.process_write_state_requests(msg)
@callback @callback
def _async_mqtt_on_publish( def _async_mqtt_on_callback(
self, self,
_mqttc: mqtt.Client, _mqttc: mqtt.Client,
_userdata: None, _userdata: None,
mid: int, mid: int,
_reason_code: mqtt.ReasonCode, _granted_qos_reason: tuple[int, ...] | mqtt.ReasonCodes | None = None,
_properties: mqtt.Properties | None, _properties_reason: mqtt.ReasonCodes | None = None,
) -> None: ) -> None:
"""Publish callback."""
self._async_mqtt_on_callback(mid)
@callback
def _async_mqtt_on_subscribe_unsubscribe(
self,
_mqttc: mqtt.Client,
_userdata: None,
mid: int,
_reason_code: list[mqtt.ReasonCode],
_properties: mqtt.Properties | None,
) -> None:
"""Subscribe / Unsubscribe callback."""
self._async_mqtt_on_callback(mid)
@callback
def _async_mqtt_on_callback(self, mid: int) -> None:
"""Publish / Subscribe / Unsubscribe callback.""" """Publish / Subscribe / Unsubscribe callback."""
# The callback signature for on_unsubscribe is different from on_subscribe
# see https://github.com/eclipse/paho.mqtt.python/issues/687
# properties and reason codes are not used in Home Assistant
future = self._async_get_mid_future(mid) future = self._async_get_mid_future(mid)
if future.done() and (future.cancelled() or future.exception()): if future.done() and (future.cancelled() or future.exception()):
# Timed out or cancelled # Timed out or cancelled
@ -1195,28 +1180,19 @@ class MQTT:
self._pending_operations[mid] = future self._pending_operations[mid] = future
return future return future
@callback
def _async_handle_callback_exception(self, status: mqtt.MQTTErrorCode) -> None:
"""Handle a callback exception."""
# 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
_LOGGER.warning(
"Error returned from MQTT server: %s",
mqtt.error_string(status),
)
@callback @callback
def _async_mqtt_on_disconnect( def _async_mqtt_on_disconnect(
self, self,
_mqttc: mqtt.Client, _mqttc: mqtt.Client,
_userdata: None, _userdata: None,
_disconnect_flags: mqtt.DisconnectFlags, result_code: int,
reason_code: mqtt.ReasonCode,
properties: mqtt.Properties | None = None, properties: mqtt.Properties | None = None,
) -> None: ) -> None:
"""Disconnected callback.""" """Disconnected callback."""
self._async_on_disconnect(result_code)
@callback
def _async_on_disconnect(self, result_code: int) -> None:
if not self.connected: if not self.connected:
# This function is re-entrant and may be called multiple times # This function is re-entrant and may be called multiple times
# when there is a broken pipe error. # when there is a broken pipe error.
@ -1227,11 +1203,11 @@ class MQTT:
self.connected = False self.connected = False
async_dispatcher_send(self.hass, MQTT_CONNECTION_STATE, False) async_dispatcher_send(self.hass, MQTT_CONNECTION_STATE, False)
_LOGGER.log( _LOGGER.log(
logging.INFO if reason_code == 0 else logging.DEBUG, logging.INFO if result_code == 0 else logging.DEBUG,
"Disconnected from MQTT server %s:%s (%s)", "Disconnected from MQTT server %s:%s (%s)",
self.conf[CONF_BROKER], self.conf[CONF_BROKER],
self.conf.get(CONF_PORT, DEFAULT_PORT), self.conf.get(CONF_PORT, DEFAULT_PORT),
reason_code, result_code,
) )
@callback @callback

View File

@ -1023,14 +1023,14 @@ def try_connection(
result: queue.Queue[bool] = queue.Queue(maxsize=1) result: queue.Queue[bool] = queue.Queue(maxsize=1)
def on_connect( def on_connect(
_mqttc: mqtt.Client, client_: mqtt.Client,
_userdata: None, userdata: None,
_connect_flags: mqtt.ConnectFlags, flags: dict[str, Any],
reason_code: mqtt.ReasonCode, result_code: int,
_properties: mqtt.Properties | None = None, properties: mqtt.Properties | None = None,
) -> None: ) -> None:
"""Handle connection result.""" """Handle connection result."""
result.put(not reason_code.is_failure) result.put(result_code == mqtt.CONNACK_ACCEPTED)
client.on_connect = on_connect client.on_connect = on_connect

View File

@ -410,25 +410,6 @@ def async_mock_intent(hass: HomeAssistant, intent_typ: str) -> list[intent.Inten
return intents return intents
class MockMqttReasonCode:
"""Class to fake a MQTT ReasonCode."""
value: int
is_failure: bool
def __init__(
self, value: int = 0, is_failure: bool = False, name: str = "Success"
) -> None:
"""Initialize the mock reason code."""
self.value = value
self.is_failure = is_failure
self._name = name
def getName(self) -> str:
"""Return the name of the reason code."""
return self._name
@callback @callback
def async_fire_mqtt_message( def async_fire_mqtt_message(
hass: HomeAssistant, hass: HomeAssistant,

View File

@ -32,7 +32,6 @@ from .test_common import help_all_subscribe_calls
from tests.common import ( from tests.common import (
MockConfigEntry, MockConfigEntry,
MockMqttReasonCode,
async_fire_mqtt_message, async_fire_mqtt_message,
async_fire_time_changed, async_fire_time_changed,
) )
@ -95,7 +94,7 @@ async def test_mqtt_await_ack_at_disconnect(hass: HomeAssistant) -> None:
mqtt_client.connect = MagicMock( mqtt_client.connect = MagicMock(
return_value=0, return_value=0,
side_effect=lambda *args, **kwargs: hass.loop.call_soon_threadsafe( side_effect=lambda *args, **kwargs: hass.loop.call_soon_threadsafe(
mqtt_client.on_connect, mqtt_client, None, 0, MockMqttReasonCode() mqtt_client.on_connect, mqtt_client, None, 0, 0, 0
), ),
) )
mqtt_client.publish = MagicMock(return_value=FakeInfo()) mqtt_client.publish = MagicMock(return_value=FakeInfo())
@ -120,7 +119,7 @@ async def test_mqtt_await_ack_at_disconnect(hass: HomeAssistant) -> None:
) )
await asyncio.sleep(0) await asyncio.sleep(0)
# Simulate late ACK callback from client with mid 100 # Simulate late ACK callback from client with mid 100
mqtt_client.on_publish(0, 0, 100, MockMqttReasonCode(), None) mqtt_client.on_publish(0, 0, 100)
# disconnect the MQTT client # disconnect the MQTT client
await hass.async_stop() await hass.async_stop()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -779,10 +778,10 @@ async def test_replaying_payload_same_topic(
calls_a = [] calls_a = []
calls_b = [] calls_b = []
mqtt_client_mock.reset_mock() mqtt_client_mock.reset_mock()
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
mqtt_client_mock.subscribe.assert_called() mqtt_client_mock.subscribe.assert_called()
# Simulate a (retained) message played back after reconnecting # Simulate a (retained) message played back after reconnecting
@ -909,10 +908,10 @@ async def test_replaying_payload_wildcard_topic(
calls_a = [] calls_a = []
calls_b = [] calls_b = []
mqtt_client_mock.reset_mock() mqtt_client_mock.reset_mock()
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
mqtt_client_mock.subscribe.assert_called() mqtt_client_mock.subscribe.assert_called()
@ -1046,7 +1045,7 @@ async def test_restore_subscriptions_on_reconnect(
assert ("test/state", 0) in help_all_subscribe_calls(mqtt_client_mock) assert ("test/state", 0) in help_all_subscribe_calls(mqtt_client_mock)
mqtt_client_mock.reset_mock() mqtt_client_mock.reset_mock()
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
# Test to subscribe orther topic while the client is not connected # Test to subscribe orther topic while the client is not connected
await mqtt.async_subscribe(hass, "test/other", record_calls) await mqtt.async_subscribe(hass, "test/other", record_calls)
@ -1054,7 +1053,7 @@ async def test_restore_subscriptions_on_reconnect(
assert ("test/other", 0) not in help_all_subscribe_calls(mqtt_client_mock) assert ("test/other", 0) not in help_all_subscribe_calls(mqtt_client_mock)
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
# Assert all subscriptions are performed at the broker # Assert all subscriptions are performed at the broker
assert ("test/state", 0) in help_all_subscribe_calls(mqtt_client_mock) assert ("test/state", 0) in help_all_subscribe_calls(mqtt_client_mock)
@ -1090,10 +1089,10 @@ async def test_restore_all_active_subscriptions_on_reconnect(
unsub() unsub()
assert mqtt_client_mock.unsubscribe.call_count == 0 assert mqtt_client_mock.unsubscribe.call_count == 0
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
# wait for cooldown # wait for cooldown
await mock_debouncer.wait() await mock_debouncer.wait()
@ -1161,37 +1160,27 @@ async def test_logs_error_if_no_connect_broker(
) -> None: ) -> None:
"""Test for setup failure if connection to broker is missing.""" """Test for setup failure if connection to broker is missing."""
mqtt_client_mock = setup_with_birth_msg_client_mock mqtt_client_mock = setup_with_birth_msg_client_mock
# test with reason code = 136 -> server unavailable # test with rc = 3 -> broker unavailable
mqtt_client_mock.on_disconnect(Mock(), None, None, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(Mock(), None, 0)
mqtt_client_mock.on_connect( mqtt_client_mock.on_connect(Mock(), None, None, 3)
Mock(),
None,
None,
MockMqttReasonCode(value=136, is_failure=True, name="Server unavailable"),
)
await hass.async_block_till_done() await hass.async_block_till_done()
assert "Unable to connect to the MQTT broker: Server unavailable" in caplog.text assert (
"Unable to connect to the MQTT broker: Connection Refused: broker unavailable."
in caplog.text
)
@pytest.mark.parametrize( @pytest.mark.parametrize("return_code", [4, 5])
"reason_code",
[
MockMqttReasonCode(
value=134, is_failure=True, name="Bad user name or password"
),
MockMqttReasonCode(value=135, is_failure=True, name="Not authorized"),
],
)
async def test_triggers_reauth_flow_if_auth_fails( async def test_triggers_reauth_flow_if_auth_fails(
hass: HomeAssistant, hass: HomeAssistant,
setup_with_birth_msg_client_mock: MqttMockPahoClient, setup_with_birth_msg_client_mock: MqttMockPahoClient,
reason_code: MockMqttReasonCode, return_code: int,
) -> None: ) -> None:
"""Test re-auth is triggered if authentication is failing.""" """Test re-auth is triggered if authentication is failing."""
mqtt_client_mock = setup_with_birth_msg_client_mock mqtt_client_mock = setup_with_birth_msg_client_mock
# test with rc = 4 -> CONNACK_REFUSED_NOT_AUTHORIZED and 5 -> CONNACK_REFUSED_BAD_USERNAME_PASSWORD # test with rc = 4 -> CONNACK_REFUSED_NOT_AUTHORIZED and 5 -> CONNACK_REFUSED_BAD_USERNAME_PASSWORD
mqtt_client_mock.on_disconnect(Mock(), None, 0, MockMqttReasonCode(), None) mqtt_client_mock.on_disconnect(Mock(), None, 0)
mqtt_client_mock.on_connect(Mock(), None, None, reason_code) mqtt_client_mock.on_connect(Mock(), None, None, return_code)
await hass.async_block_till_done() await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress() flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1 assert len(flows) == 1
@ -1208,9 +1197,7 @@ async def test_handle_mqtt_on_callback(
mqtt_client_mock = setup_with_birth_msg_client_mock mqtt_client_mock = setup_with_birth_msg_client_mock
with patch.object(mqtt_client_mock, "get_mid", return_value=100): with patch.object(mqtt_client_mock, "get_mid", return_value=100):
# Simulate an ACK for mid == 100, this will call mqtt_mock._async_get_mid_future(mid) # Simulate an ACK for mid == 100, this will call mqtt_mock._async_get_mid_future(mid)
mqtt_client_mock.on_publish( mqtt_client_mock.on_publish(mqtt_client_mock, None, 100)
mqtt_client_mock, None, 100, MockMqttReasonCode(), None
)
await hass.async_block_till_done() await hass.async_block_till_done()
# Make sure the ACK has been received # Make sure the ACK has been received
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1232,7 +1219,7 @@ async def test_handle_mqtt_on_callback_after_cancellation(
# Simulate the mid future getting a cancellation # Simulate the mid future getting a cancellation
mqtt_mock()._async_get_mid_future(101).cancel() mqtt_mock()._async_get_mid_future(101).cancel()
# Simulate an ACK for mid == 101, being received after the cancellation # Simulate an ACK for mid == 101, being received after the cancellation
mqtt_client_mock.on_publish(mqtt_client_mock, None, 101, MockMqttReasonCode(), None) mqtt_client_mock.on_publish(mqtt_client_mock, None, 101)
await hass.async_block_till_done() await hass.async_block_till_done()
assert "No ACK from MQTT server" not in caplog.text assert "No ACK from MQTT server" not in caplog.text
assert "InvalidStateError" not in caplog.text assert "InvalidStateError" not in caplog.text
@ -1249,7 +1236,7 @@ async def test_handle_mqtt_on_callback_after_timeout(
# Simulate the mid future getting a timeout # Simulate the mid future getting a timeout
mqtt_mock()._async_get_mid_future(101).set_exception(asyncio.TimeoutError) mqtt_mock()._async_get_mid_future(101).set_exception(asyncio.TimeoutError)
# Simulate an ACK for mid == 101, being received after the timeout # Simulate an ACK for mid == 101, being received after the timeout
mqtt_client_mock.on_publish(mqtt_client_mock, None, 101, MockMqttReasonCode(), None) mqtt_client_mock.on_publish(mqtt_client_mock, None, 101)
await hass.async_block_till_done() await hass.async_block_till_done()
assert "No ACK from MQTT server" not in caplog.text assert "No ACK from MQTT server" not in caplog.text
assert "InvalidStateError" not in caplog.text assert "InvalidStateError" not in caplog.text
@ -1401,7 +1388,7 @@ async def test_handle_mqtt_timeout_on_callback(
mock_client.connect = MagicMock( mock_client.connect = MagicMock(
return_value=0, return_value=0,
side_effect=lambda *args, **kwargs: hass.loop.call_soon_threadsafe( side_effect=lambda *args, **kwargs: hass.loop.call_soon_threadsafe(
mock_client.on_connect, mock_client, None, 0, MockMqttReasonCode() mock_client.on_connect, mock_client, None, 0, 0, 0
), ),
) )
@ -1790,12 +1777,12 @@ async def test_mqtt_subscribes_topics_on_connect(
await mqtt.async_subscribe(hass, "still/pending", record_calls, 1) await mqtt.async_subscribe(hass, "still/pending", record_calls, 1)
await mock_debouncer.wait() await mock_debouncer.wait()
mqtt_client_mock.on_disconnect(Mock(), None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(Mock(), None, 0)
mqtt_client_mock.reset_mock() mqtt_client_mock.reset_mock()
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(Mock(), None, 0, MockMqttReasonCode()) mqtt_client_mock.on_connect(Mock(), None, 0, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
subscribe_calls = help_all_subscribe_calls(mqtt_client_mock) subscribe_calls = help_all_subscribe_calls(mqtt_client_mock)
@ -1850,12 +1837,12 @@ async def test_mqtt_subscribes_wildcard_topics_in_correct_order(
# Assert the initial wildcard topic subscription order # Assert the initial wildcard topic subscription order
_assert_subscription_order() _assert_subscription_order()
mqtt_client_mock.on_disconnect(Mock(), None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(Mock(), None, 0)
mqtt_client_mock.reset_mock() mqtt_client_mock.reset_mock()
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(Mock(), None, 0, MockMqttReasonCode()) mqtt_client_mock.on_connect(Mock(), None, 0, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
# Assert the wildcard topic subscription order after a reconnect # Assert the wildcard topic subscription order after a reconnect
@ -1881,12 +1868,12 @@ async def test_mqtt_discovery_not_subscribes_when_disabled(
assert (f"homeassistant/{component}/+/config", 0) not in subscribe_calls assert (f"homeassistant/{component}/+/config", 0) not in subscribe_calls
assert (f"homeassistant/{component}/+/+/config", 0) not in subscribe_calls assert (f"homeassistant/{component}/+/+/config", 0) not in subscribe_calls
mqtt_client_mock.on_disconnect(Mock(), None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(Mock(), None, 0)
mqtt_client_mock.reset_mock() mqtt_client_mock.reset_mock()
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(Mock(), None, 0, MockMqttReasonCode()) mqtt_client_mock.on_connect(Mock(), None, 0, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
subscribe_calls = help_all_subscribe_calls(mqtt_client_mock) subscribe_calls = help_all_subscribe_calls(mqtt_client_mock)
@ -1981,7 +1968,7 @@ async def test_auto_reconnect(
mqtt_client_mock.reconnect.reset_mock() mqtt_client_mock.reconnect.reset_mock()
mqtt_client_mock.disconnect() mqtt_client_mock.disconnect()
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
mqtt_client_mock.reconnect.side_effect = exception("foo") mqtt_client_mock.reconnect.side_effect = exception("foo")
@ -2002,7 +1989,7 @@ async def test_auto_reconnect(
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
mqtt_client_mock.disconnect() mqtt_client_mock.disconnect()
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
async_fire_time_changed( async_fire_time_changed(
@ -2044,7 +2031,7 @@ async def test_server_sock_connect_and_disconnect(
mqtt_client_mock.loop_misc.return_value = paho_mqtt.MQTT_ERR_CONN_LOST mqtt_client_mock.loop_misc.return_value = paho_mqtt.MQTT_ERR_CONN_LOST
mqtt_client_mock.on_socket_unregister_write(mqtt_client_mock, None, client) mqtt_client_mock.on_socket_unregister_write(mqtt_client_mock, None, client)
mqtt_client_mock.on_socket_close(mqtt_client_mock, None, client) mqtt_client_mock.on_socket_close(mqtt_client_mock, None, client)
mqtt_client_mock.on_disconnect(mqtt_client_mock, None, None, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(mqtt_client_mock, None, client)
await hass.async_block_till_done() await hass.async_block_till_done()
mock_debouncer.clear() mock_debouncer.clear()
unsub() unsub()
@ -2182,4 +2169,4 @@ async def test_loop_write_failure(
# Final for the disconnect callback # Final for the disconnect callback
await hass.async_block_till_done() await hass.async_block_till_done()
assert "Error returned from MQTT server: The connection was lost." in caplog.text assert "Disconnected from MQTT server test-broker:1883" in caplog.text

View File

@ -28,7 +28,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from tests.common import MockConfigEntry, MockMqttReasonCode from tests.common import MockConfigEntry
from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient
ADD_ON_DISCOVERY_INFO = { ADD_ON_DISCOVERY_INFO = {
@ -143,16 +143,16 @@ def mock_try_connection_success() -> Generator[MqttMockPahoClient]:
def loop_start(): def loop_start():
"""Simulate connect on loop start.""" """Simulate connect on loop start."""
mock_client().on_connect(mock_client, None, None, MockMqttReasonCode(), None) mock_client().on_connect(mock_client, None, None, 0)
def _subscribe(topic, qos=0): def _subscribe(topic, qos=0):
mid = get_mid() mid = get_mid()
mock_client().on_subscribe(mock_client, 0, mid, [MockMqttReasonCode()], None) mock_client().on_subscribe(mock_client, 0, mid)
return (0, mid) return (0, mid)
def _unsubscribe(topic): def _unsubscribe(topic):
mid = get_mid() mid = get_mid()
mock_client().on_unsubscribe(mock_client, 0, mid, [MockMqttReasonCode()], None) mock_client().on_unsubscribe(mock_client, 0, mid)
return (0, mid) return (0, mid)
with patch( with patch(

View File

@ -45,7 +45,6 @@ from tests.common import (
MockConfigEntry, MockConfigEntry,
MockEntity, MockEntity,
MockEntityPlatform, MockEntityPlatform,
MockMqttReasonCode,
async_fire_mqtt_message, async_fire_mqtt_message,
async_fire_time_changed, async_fire_time_changed,
mock_restore_cache, mock_restore_cache,
@ -1573,7 +1572,6 @@ async def test_subscribe_connection_status(
setup_with_birth_msg_client_mock: MqttMockPahoClient, setup_with_birth_msg_client_mock: MqttMockPahoClient,
) -> None: ) -> None:
"""Test connextion status subscription.""" """Test connextion status subscription."""
mqtt_client_mock = setup_with_birth_msg_client_mock mqtt_client_mock = setup_with_birth_msg_client_mock
mqtt_connected_calls_callback: list[bool] = [] mqtt_connected_calls_callback: list[bool] = []
mqtt_connected_calls_async: list[bool] = [] mqtt_connected_calls_async: list[bool] = []
@ -1591,7 +1589,7 @@ async def test_subscribe_connection_status(
assert mqtt.is_connected(hass) is True assert mqtt.is_connected(hass) is True
# Mock disconnect status # Mock disconnect status
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
assert mqtt.is_connected(hass) is False assert mqtt.is_connected(hass) is False
@ -1605,12 +1603,12 @@ async def test_subscribe_connection_status(
# Mock connect status # Mock connect status
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, 0, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
assert mqtt.is_connected(hass) is True assert mqtt.is_connected(hass) is True
# Mock disconnect status # Mock disconnect status
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
assert mqtt.is_connected(hass) is False assert mqtt.is_connected(hass) is False
@ -1620,7 +1618,7 @@ async def test_subscribe_connection_status(
# Mock connect status # Mock connect status
mock_debouncer.clear() mock_debouncer.clear()
mqtt_client_mock.on_connect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, 0, 0)
await mock_debouncer.wait() await mock_debouncer.wait()
assert mqtt.is_connected(hass) is True assert mqtt.is_connected(hass) is True

View File

@ -27,7 +27,7 @@ from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from tests.common import MockMqttReasonCode, async_fire_mqtt_message from tests.common import async_fire_mqtt_message
from tests.typing import MqttMockHAClient, MqttMockPahoClient, WebSocketGenerator from tests.typing import MqttMockHAClient, MqttMockPahoClient, WebSocketGenerator
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
@ -165,7 +165,7 @@ async def help_test_availability_when_connection_lost(
# Disconnected from MQTT server -> state changed to unavailable # Disconnected from MQTT server -> state changed to unavailable
mqtt_mock.connected = False mqtt_mock.connected = False
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -174,7 +174,7 @@ async def help_test_availability_when_connection_lost(
# Reconnected to MQTT server -> state still unavailable # Reconnected to MQTT server -> state still unavailable
mqtt_mock.connected = True mqtt_mock.connected = True
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -226,7 +226,7 @@ async def help_test_deep_sleep_availability_when_connection_lost(
# Disconnected from MQTT server -> state changed to unavailable # Disconnected from MQTT server -> state changed to unavailable
mqtt_mock.connected = False mqtt_mock.connected = False
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -235,7 +235,7 @@ async def help_test_deep_sleep_availability_when_connection_lost(
# Reconnected to MQTT server -> state no longer unavailable # Reconnected to MQTT server -> state no longer unavailable
mqtt_mock.connected = True mqtt_mock.connected = True
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -478,7 +478,7 @@ async def help_test_availability_poll_state(
# Disconnected from MQTT server # Disconnected from MQTT server
mqtt_mock.connected = False mqtt_mock.connected = False
mqtt_client_mock.on_disconnect(None, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_disconnect(None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -486,7 +486,7 @@ async def help_test_availability_poll_state(
# Reconnected to MQTT server # Reconnected to MQTT server
mqtt_mock.connected = True mqtt_mock.connected = True
mqtt_client_mock.on_connect(None, None, None, MockMqttReasonCode()) mqtt_client_mock.on_connect(None, None, None, 0)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -120,7 +120,6 @@ from .common import ( # noqa: E402, isort:skip
CLIENT_ID, CLIENT_ID,
INSTANCES, INSTANCES,
MockConfigEntry, MockConfigEntry,
MockMqttReasonCode,
MockUser, MockUser,
async_fire_mqtt_message, async_fire_mqtt_message,
async_test_home_assistant, async_test_home_assistant,
@ -972,23 +971,17 @@ def mqtt_client_mock(hass: HomeAssistant) -> Generator[MqttMockPahoClient]:
def _async_fire_mqtt_message(topic, payload, qos, retain): def _async_fire_mqtt_message(topic, payload, qos, retain):
async_fire_mqtt_message(hass, topic, payload or b"", qos, retain) async_fire_mqtt_message(hass, topic, payload or b"", qos, retain)
mid = get_mid() mid = get_mid()
hass.loop.call_soon( hass.loop.call_soon(mock_client.on_publish, 0, 0, mid)
mock_client.on_publish, Mock(), 0, mid, MockMqttReasonCode(), None
)
return FakeInfo(mid) return FakeInfo(mid)
def _subscribe(topic, qos=0): def _subscribe(topic, qos=0):
mid = get_mid() mid = get_mid()
hass.loop.call_soon( hass.loop.call_soon(mock_client.on_subscribe, 0, 0, mid)
mock_client.on_subscribe, Mock(), 0, mid, [MockMqttReasonCode()], None
)
return (0, mid) return (0, mid)
def _unsubscribe(topic): def _unsubscribe(topic):
mid = get_mid() mid = get_mid()
hass.loop.call_soon( hass.loop.call_soon(mock_client.on_unsubscribe, 0, 0, mid)
mock_client.on_unsubscribe, Mock(), 0, mid, [MockMqttReasonCode()], None
)
return (0, mid) return (0, mid)
def _connect(*args, **kwargs): def _connect(*args, **kwargs):
@ -997,7 +990,7 @@ def mqtt_client_mock(hass: HomeAssistant) -> Generator[MqttMockPahoClient]:
# the behavior. # the behavior.
mock_client.reconnect() mock_client.reconnect()
hass.loop.call_soon_threadsafe( hass.loop.call_soon_threadsafe(
mock_client.on_connect, mock_client, None, 0, MockMqttReasonCode() mock_client.on_connect, mock_client, None, 0, 0, 0
) )
mock_client.on_socket_open( mock_client.on_socket_open(
mock_client, None, Mock(fileno=Mock(return_value=-1)) mock_client, None, Mock(fileno=Mock(return_value=-1))
@ -1074,7 +1067,7 @@ async def _mqtt_mock_entry(
# connected set to True to get a more realistic behavior when subscribing # connected set to True to get a more realistic behavior when subscribing
mock_mqtt_instance.connected = True mock_mqtt_instance.connected = True
mqtt_client_mock.on_connect(mqtt_client_mock, None, 0, MockMqttReasonCode()) mqtt_client_mock.on_connect(mqtt_client_mock, None, 0, 0, 0)
async_dispatcher_send(hass, mqtt.MQTT_CONNECTION_STATE, True) async_dispatcher_send(hass, mqtt.MQTT_CONNECTION_STATE, True)
await hass.async_block_till_done() await hass.async_block_till_done()