fix MQTT retry loop race condition with cleanup disconnect

Previously, the reconnection loop was calling client.disconnect() during cleanup,
which triggered the disconnect callback with code 0 ("Normal disconnection").
The callback would then exit early, preventing further reconnection attempts.

This creates a cleanup flag that prevents the disconnect callback from
stopping reconnection when we're intentionally cleaning up the old client
during retry attempts.

Fixes issue where MQTT would get stuck in retry loop without actually
attempting fresh connections.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Daniel 2025-09-14 19:03:34 -04:00
parent 91d3a7b245
commit b0f189056a

View File

@ -24,6 +24,7 @@ class MqttClient(Communicator):
self._reconnect_thread: Optional[threading.Thread] = None self._reconnect_thread: Optional[threading.Thread] = None
self._reconnect_delay = 10 # Retry every 10 seconds self._reconnect_delay = 10 # Retry every 10 seconds
self._stop_reconnect: bool = False self._stop_reconnect: bool = False
self._cleanup_in_progress: bool = False
def subscribe(self, receiver: Callable[[str, str], None]) -> None: def subscribe(self, receiver: Callable[[str, str], None]) -> None:
"""Wrapper for allowing dispatcher to subscribe.""" """Wrapper for allowing dispatcher to subscribe."""
@ -239,11 +240,15 @@ class MqttClient(Communicator):
f"MQTT disconnected - reason: '{reason_name}', code: {reason_value}, type: {type(reason_code)}" f"MQTT disconnected - reason: '{reason_name}', code: {reason_value}, type: {type(reason_code)}"
) )
# Don't attempt reconnection if we're stopping or if it was a clean disconnect # Don't attempt reconnection if we're stopping, cleaning up, or if it was a clean disconnect
if self._stop_reconnect: if self._stop_reconnect:
logger.error("MQTT not reconnecting - stop flag set") logger.error("MQTT not reconnecting - stop flag set")
return return
if self._cleanup_in_progress:
logger.error("MQTT not reconnecting - cleanup in progress")
return
if reason_code == 0: if reason_code == 0:
logger.error("MQTT not reconnecting - clean disconnect (code 0)") logger.error("MQTT not reconnecting - clean disconnect (code 0)")
return return
@ -379,9 +384,12 @@ class MqttClient(Communicator):
# Clean up old client if it exists # Clean up old client if it exists
if self.client is not None: if self.client is not None:
try: try:
self._cleanup_in_progress = True
self.client.disconnect() self.client.disconnect()
self.client.loop_stop() self.client.loop_stop()
self._cleanup_in_progress = False
except Exception: except Exception:
self._cleanup_in_progress = False
pass # Ignore cleanup errors pass # Ignore cleanup errors
# Create completely fresh client and attempt connection # Create completely fresh client and attempt connection