Compare commits

...

3 Commits

Author SHA1 Message Date
jbouwh
1edea46a4d Revert "Bump paho-mqtt client to version 2.1.0 (#136130)"
This reverts commit 7fa6f7e875.
2025-02-28 08:56:10 +00:00
jbouwh
c476e92bdc Revert " Upgrade paho-mqtt API to v2 (#137613)"
This reverts commit d6b7762dd6.
2025-02-28 08:52:41 +00:00
jbouwh
8dcd9945e8 Revert "Set clean_start=True on connect to MQTT broker (#136026)"
This reverts commit f8ffbf0506.
2025-02-28 08:52:08 +00:00
16 changed files with 128 additions and 288 deletions

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/econet", "documentation": "https://www.home-assistant.io/integrations/econet",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["paho_mqtt", "pyeconet"], "loggers": ["paho_mqtt", "pyeconet"],
"requirements": ["pyeconet==0.1.28"] "requirements": ["pyeconet==0.1.23"]
} }

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.
@@ -64,10 +51,10 @@ class AsyncMQTTClient(MQTTClient):
since the client is running in an async event loop since the client is running in an async event loop
and will never run in multiple threads. and will never run in multiple threads.
""" """
self._in_callback_mutex = NullLock() # type: ignore[assignment] self._in_callback_mutex = NullLock()
self._callback_mutex = NullLock() # type: ignore[assignment] self._callback_mutex = NullLock()
self._msgtime_mutex = NullLock() # type: ignore[assignment] self._msgtime_mutex = NullLock()
self._out_message_mutex = NullLock() # type: ignore[assignment] self._out_message_mutex = NullLock()
self._in_message_mutex = NullLock() # type: ignore[assignment] self._in_message_mutex = NullLock()
self._reconnect_delay_mutex = NullLock() # type: ignore[assignment] self._reconnect_delay_mutex = NullLock()
self._mid_generate_mutex = NullLock() # type: ignore[assignment] self._mid_generate_mutex = NullLock()

View File

@@ -15,6 +15,7 @@ import socket
import ssl import ssl
import time import time
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
import uuid
import certifi import certifi
@@ -116,7 +117,7 @@ MAX_UNSUBSCRIBES_PER_CALL = 500
MAX_PACKETS_TO_READ = 500 MAX_PACKETS_TO_READ = 500
type SocketType = socket.socket | ssl.SSLSocket | mqtt._WebsocketWrapper | Any # noqa: SLF001 type SocketType = socket.socket | ssl.SSLSocket | mqtt.WebsocketWrapper | Any
type SubscribePayloadType = str | bytes | bytearray # Only bytes if encoding is None type SubscribePayloadType = str | bytes | bytearray # Only bytes if encoding is None
@@ -298,39 +299,22 @@ class MqttClientSetup:
from .async_client import AsyncMQTTClient from .async_client import AsyncMQTTClient
config = self._config config = self._config
clean_session: bool | None = None
if (protocol := config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL)) == PROTOCOL_31: if (protocol := config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL)) == PROTOCOL_31:
proto = mqtt.MQTTv31 proto = mqtt.MQTTv31
clean_session = True
elif protocol == PROTOCOL_5: elif protocol == PROTOCOL_5:
proto = mqtt.MQTTv5 proto = mqtt.MQTTv5
else: else:
proto = mqtt.MQTTv311 proto = mqtt.MQTTv311
clean_session = True
if (client_id := config.get(CONF_CLIENT_ID)) is None: if (client_id := config.get(CONF_CLIENT_ID)) is None:
# PAHO MQTT relies on the MQTT server to generate random client IDs. # PAHO MQTT relies on the MQTT server to generate random client IDs.
# However, that feature is not mandatory so we generate our own. # However, that feature is not mandatory so we generate our own.
client_id = None client_id = mqtt.base62(uuid.uuid4().int, padding=22)
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, client_id,
client_id=client_id,
# See: https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html
# clean_session (bool defaults to None)
# a boolean that determines the client type.
# If True, the broker will remove all information about this client when it
# disconnects. If False, the client is a persistent client and subscription
# information and queued messages will be retained when the client
# disconnects. Note that a client will never discard its own outgoing
# messages on disconnect. Calling connect() or reconnect() will cause the
# messages to be resent. Use reinitialise() to reset a client to its
# original state. The clean_session argument only applies to MQTT versions
# v3.1.1 and v3.1. It is not accepted if the MQTT version is v5.0 - use the
# clean_start argument on connect() instead.
clean_session=clean_session,
protocol=proto, protocol=proto,
transport=transport, # type: ignore[arg-type] transport=transport,
reconnect_on_failure=False, reconnect_on_failure=False,
) )
self._client.setup() self._client.setup()
@@ -387,7 +371,6 @@ class MQTT:
self.loop = hass.loop self.loop = hass.loop
self.config_entry = config_entry self.config_entry = config_entry
self.conf = conf self.conf = conf
self.is_mqttv5 = conf.get(CONF_PROTOCOL, DEFAULT_PROTOCOL) == PROTOCOL_5
self._simple_subscriptions: defaultdict[str, set[Subscription]] = defaultdict( self._simple_subscriptions: defaultdict[str, set[Subscription]] = defaultdict(
set set
@@ -493,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
@@ -515,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:
@@ -550,7 +533,7 @@ class MQTT:
try: try:
# Some operating systems do not allow us to set the preferred # Some operating systems do not allow us to set the preferred
# buffer size. In that case we try some other size options. # buffer size. In that case we try some other size options.
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, new_buffer_size) # type: ignore[union-attr] sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, new_buffer_size)
except OSError as err: except OSError as err:
if new_buffer_size <= MIN_BUFFER_SIZE: if new_buffer_size <= MIN_BUFFER_SIZE:
_LOGGER.warning( _LOGGER.warning(
@@ -610,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
@@ -669,25 +652,14 @@ class MQTT:
result: int | None = None result: int | None = None
self._available_future = client_available self._available_future = client_available
self._should_reconnect = True self._should_reconnect = True
connect_partial = partial(
self._mqttc.connect,
host=self.conf[CONF_BROKER],
port=self.conf.get(CONF_PORT, DEFAULT_PORT),
keepalive=self.conf.get(CONF_KEEPALIVE, DEFAULT_KEEPALIVE),
# See:
# https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html
# `clean_start` (bool) (MQTT v5.0 only) `True`, `False` or
# `MQTT_CLEAN_START_FIRST_ONLY`. Sets the MQTT v5.0 clean_start flag
# always, never or on the first successful connect only,
# respectively. MQTT session data (such as outstanding messages and
# subscriptions) is cleared on successful connect when the
# clean_start flag is set. For MQTT v3.1.1, the clean_session
# argument of Client should be used for similar result.
clean_start=True if self.is_mqttv5 else mqtt.MQTT_CLEAN_START_FIRST_ONLY,
)
try: try:
async with self._connection_lock, self._async_connect_in_executor(): async with self._connection_lock, self._async_connect_in_executor():
result = await self.hass.async_add_executor_job(connect_partial) result = await self.hass.async_add_executor_job(
self._mqttc.connect,
self.conf[CONF_BROKER],
self.conf.get(CONF_PORT, DEFAULT_PORT),
self.conf.get(CONF_KEEPALIVE, DEFAULT_KEEPALIVE),
)
except (OSError, mqtt.WebsocketConnectionError) as err: except (OSError, mqtt.WebsocketConnectionError) as err:
_LOGGER.error("Failed to connect to MQTT server due to exception: %s", err) _LOGGER.error("Failed to connect to MQTT server due to exception: %s", err)
self._async_connection_result(False) self._async_connection_result(False)
@@ -1011,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.
@@ -1021,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
@@ -1045,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]
@@ -1182,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
@@ -1223,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.
@@ -1255,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
@@ -1268,9 +1216,7 @@ class MQTT:
if not future.done(): if not future.done():
future.set_exception(asyncio.TimeoutError) future.set_exception(asyncio.TimeoutError)
async def _async_wait_for_mid_or_raise( async def _async_wait_for_mid_or_raise(self, mid: int, result_code: int) -> None:
self, mid: int | None, result_code: int
) -> None:
"""Wait for ACK from broker or raise on error.""" """Wait for ACK from broker or raise on error."""
if result_code != 0: if result_code != 0:
# pylint: disable-next=import-outside-toplevel # pylint: disable-next=import-outside-toplevel
@@ -1286,8 +1232,6 @@ class MQTT:
# Create the mid event if not created, either _mqtt_handle_mid or # Create the mid event if not created, either _mqtt_handle_mid or
# _async_wait_for_mid_or_raise may be executed first. # _async_wait_for_mid_or_raise may be executed first.
if TYPE_CHECKING:
assert mid is not None
future = self._async_get_mid_future(mid) future = self._async_get_mid_future(mid)
loop = self.hass.loop loop = self.hass.loop
timer_handle = loop.call_later(TIMEOUT_ACK, self._async_timeout_mid, future) timer_handle = loop.call_later(TIMEOUT_ACK, self._async_timeout_mid, future)
@@ -1325,7 +1269,7 @@ def _matcher_for_topic(subscription: str) -> Callable[[str], bool]:
# pylint: disable-next=import-outside-toplevel # pylint: disable-next=import-outside-toplevel
from paho.mqtt.matcher import MQTTMatcher from paho.mqtt.matcher import MQTTMatcher
matcher = MQTTMatcher() # type: ignore[no-untyped-call] matcher = MQTTMatcher()
matcher[subscription] = True matcher[subscription] = True
return lambda topic: next(matcher.iter_match(topic), False) # type: ignore[no-untyped-call] return lambda topic: next(matcher.iter_match(topic), False)

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

@@ -8,6 +8,6 @@
"documentation": "https://www.home-assistant.io/integrations/mqtt", "documentation": "https://www.home-assistant.io/integrations/mqtt",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["paho-mqtt==2.1.0"], "requirements": ["paho-mqtt==1.6.1"],
"single_config_entry": true "single_config_entry": true
} }

View File

@@ -46,7 +46,7 @@ lru-dict==1.3.0
mutagen==1.47.0 mutagen==1.47.0
orjson==3.10.12 orjson==3.10.12
packaging>=23.1 packaging>=23.1
paho-mqtt==2.1.0 paho-mqtt==1.6.1
Pillow==11.1.0 Pillow==11.1.0
propcache==0.3.0 propcache==0.3.0
psutil-home-assistant==0.0.1 psutil-home-assistant==0.0.1

4
requirements_all.txt generated
View File

@@ -1622,7 +1622,7 @@ ovoenergy==2.0.0
p1monitor==3.1.0 p1monitor==3.1.0
# homeassistant.components.mqtt # homeassistant.components.mqtt
paho-mqtt==2.1.0 paho-mqtt==1.6.1
# homeassistant.components.panasonic_bluray # homeassistant.components.panasonic_bluray
panacotta==0.2 panacotta==0.2
@@ -1918,7 +1918,7 @@ pyebox==1.1.4
pyecoforest==0.4.0 pyecoforest==0.4.0
# homeassistant.components.econet # homeassistant.components.econet
pyeconet==0.1.28 pyeconet==0.1.23
# homeassistant.components.ista_ecotrend # homeassistant.components.ista_ecotrend
pyecotrend-ista==3.3.1 pyecotrend-ista==3.3.1

View File

@@ -41,6 +41,7 @@ types-beautifulsoup4==4.12.0.20250204
types-caldav==1.3.0.20241107 types-caldav==1.3.0.20241107
types-chardet==0.1.5 types-chardet==0.1.5
types-decorator==5.1.8.20250121 types-decorator==5.1.8.20250121
types-paho-mqtt==1.6.0.20240321
types-pexpect==4.9.0.20241208 types-pexpect==4.9.0.20241208
types-pillow==10.2.0.20240822 types-pillow==10.2.0.20240822
types-protobuf==5.29.1.20241207 types-protobuf==5.29.1.20241207

View File

@@ -1352,7 +1352,7 @@ ovoenergy==2.0.0
p1monitor==3.1.0 p1monitor==3.1.0
# homeassistant.components.mqtt # homeassistant.components.mqtt
paho-mqtt==2.1.0 paho-mqtt==1.6.1
# homeassistant.components.panasonic_viera # homeassistant.components.panasonic_viera
panasonic-viera==0.4.2 panasonic-viera==0.4.2
@@ -1565,7 +1565,7 @@ pydroid-ipcam==2.0.0
pyecoforest==0.4.0 pyecoforest==0.4.0
# homeassistant.components.econet # homeassistant.components.econet
pyeconet==0.1.28 pyeconet==0.1.23
# homeassistant.components.ista_ecotrend # homeassistant.components.ista_ecotrend
pyecotrend-ista==3.3.1 pyecotrend-ista==3.3.1

View File

@@ -199,6 +199,7 @@ EXCEPTIONS = {
"pigpio", # https://github.com/joan2937/pigpio/pull/608 "pigpio", # https://github.com/joan2937/pigpio/pull/608
"pymitv", # MIT "pymitv", # MIT
"pybbox", # https://github.com/HydrelioxGitHub/pybbox/pull/5 "pybbox", # https://github.com/HydrelioxGitHub/pybbox/pull/5
"pyeconet", # https://github.com/w1ll1am23/pyeconet/pull/41
"pysabnzbd", # https://github.com/jeradM/pysabnzbd/pull/6 "pysabnzbd", # https://github.com/jeradM/pysabnzbd/pull/6
"pyvera", # https://github.com/maximvelichko/pyvera/pull/164 "pyvera", # https://github.com/maximvelichko/pyvera/pull/164
"repoze.lru", "repoze.lru",

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
@@ -1271,7 +1258,7 @@ async def test_publish_error(
with patch( with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient" "homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client: ) as mock_client:
mock_client().connect = lambda **kwargs: 1 mock_client().connect = lambda *args: 1
mock_client().publish().rc = 1 mock_client().publish().rc = 1
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
with pytest.raises(HomeAssistantError): with pytest.raises(HomeAssistantError):
@@ -1330,7 +1317,7 @@ async def test_handle_message_callback(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("mqtt_config_entry_data", "protocol", "clean_session"), ("mqtt_config_entry_data", "protocol"),
[ [
( (
{ {
@@ -1338,7 +1325,6 @@ async def test_handle_message_callback(
CONF_PROTOCOL: "3.1", CONF_PROTOCOL: "3.1",
}, },
3, 3,
True,
), ),
( (
{ {
@@ -1346,7 +1332,6 @@ async def test_handle_message_callback(
CONF_PROTOCOL: "3.1.1", CONF_PROTOCOL: "3.1.1",
}, },
4, 4,
True,
), ),
( (
{ {
@@ -1354,72 +1339,22 @@ async def test_handle_message_callback(
CONF_PROTOCOL: "5", CONF_PROTOCOL: "5",
}, },
5, 5,
None,
), ),
], ],
ids=["v3.1", "v3.1.1", "v5"],
) )
async def test_setup_mqtt_client_clean_session_and_protocol( async def test_setup_mqtt_client_protocol(
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator, protocol: int
mqtt_mock_entry: MqttMockHAClientGenerator,
mqtt_client_mock: MqttMockPahoClient,
protocol: int,
clean_session: bool | None,
) -> None: ) -> None:
"""Test MQTT client clean_session and protocol setup.""" """Test MQTT client protocol setup."""
with patch( with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient" "homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client: ) as mock_client:
await mqtt_mock_entry() await mqtt_mock_entry()
# check if clean_session was correctly
assert mock_client.call_args[1]["clean_session"] == clean_session
# check if protocol setup was correctly # check if protocol setup was correctly
assert mock_client.call_args[1]["protocol"] == protocol assert mock_client.call_args[1]["protocol"] == protocol
@pytest.mark.parametrize(
("mqtt_config_entry_data", "connect_args"),
[
(
{
mqtt.CONF_BROKER: "mock-broker",
CONF_PROTOCOL: "3.1",
},
call(host="mock-broker", port=1883, keepalive=60, clean_start=3),
),
(
{
mqtt.CONF_BROKER: "mock-broker",
CONF_PROTOCOL: "3.1.1",
},
call(host="mock-broker", port=1883, keepalive=60, clean_start=3),
),
(
{
mqtt.CONF_BROKER: "mock-broker",
CONF_PROTOCOL: "5",
},
call(host="mock-broker", port=1883, keepalive=60, clean_start=True),
),
],
ids=["v3.1", "v3.1.1", "v5"],
)
async def test_setup_mqtt_client_clean_start(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
mqtt_client_mock: MqttMockPahoClient,
connect_args: tuple[Any],
) -> None:
"""Test MQTT client protocol connects with `clean_start` set correctly."""
await mqtt_mock_entry()
# check if clean_start was set correctly
assert len(mqtt_client_mock.connect.mock_calls) == 1
assert mqtt_client_mock.connect.mock_calls[0] == connect_args
@patch("homeassistant.components.mqtt.client.TIMEOUT_ACK", 0.2) @patch("homeassistant.components.mqtt.client.TIMEOUT_ACK", 0.2)
async def test_handle_mqtt_timeout_on_callback( async def test_handle_mqtt_timeout_on_callback(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, mock_debouncer: asyncio.Event hass: HomeAssistant, caplog: pytest.LogCaptureFixture, mock_debouncer: asyncio.Event
@@ -1453,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
), ),
) )
@@ -1842,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)
@@ -1902,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
@@ -1933,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)
@@ -2033,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")
@@ -2054,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(
@@ -2096,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()
@@ -2147,7 +2082,7 @@ async def test_server_sock_buffer_size_with_websocket(
client.setblocking(False) client.setblocking(False)
server.setblocking(False) server.setblocking(False)
class FakeWebsocket(paho_mqtt._WebsocketWrapper): class FakeWebsocket(paho_mqtt.WebsocketWrapper):
def _do_handshake(self, *args, **kwargs): def _do_handshake(self, *args, **kwargs):
pass pass
@@ -2234,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()