3131
3232#include < QFile>
3333#include < QTime>
34+ #include < QTimer>
3435
3536/* ************************************************************************** */
3637
@@ -92,6 +93,10 @@ bool MqttManager::connect()
9293 {
9394 // qDebug() << "MqttManager::connect()";
9495
96+ // We intend to stay connected from now on; an unexpected drop should
97+ // arm the auto-reconnect backoff (see scheduleReconnect()).
98+ m_reconnectWanted = true ;
99+
95100 SettingsManager *sm = SettingsManager::getInstance ();
96101 m_mqttclient->setHostname (sm->getMqttHost ());
97102 m_mqttclient->setPort (sm->getMqttPort ());
@@ -139,6 +144,14 @@ void MqttManager::disconnect()
139144{
140145#if defined(ENABLE_MQTT)
141146
147+ // Explicit disconnect (user toggled MQTT off, or a forced reconnect is
148+ // about to re-dial): stop wanting a connection and cancel any pending
149+ // backoff so the timer doesn't re-dial behind the user's back. Reset the
150+ // interval so the next session starts from the base delay.
151+ m_reconnectWanted = false ;
152+ m_reconnectInterval = kReconnectBaseMs ;
153+ if (m_reconnectTimer) m_reconnectTimer->stop ();
154+
142155 if (m_mqttclient)
143156 {
144157 // qDebug() << "MqttManager::disconnect()";
@@ -183,6 +196,63 @@ void MqttManager::reconnect()
183196#endif
184197}
185198
199+ void MqttManager::scheduleReconnect ()
200+ {
201+ #if defined(ENABLE_MQTT)
202+
203+ // Only re-dial if we still want to be connected and MQTT is enabled.
204+ if (!m_reconnectWanted) return ;
205+
206+ SettingsManager *sm = SettingsManager::getInstance ();
207+ if (!sm || !sm->getMQTT ()) return ;
208+
209+ if (!m_reconnectTimer)
210+ {
211+ m_reconnectTimer = new QTimer (this );
212+ m_reconnectTimer->setSingleShot (true );
213+ QObject::connect (m_reconnectTimer, &QTimer::timeout,
214+ this , &MqttManager::reconnectTimerFired);
215+ }
216+
217+ // A burst of Disconnected state-changes must not stack timers or grow the
218+ // backoff multiple times — one armed attempt at a time.
219+ if (m_reconnectTimer->isActive ()) return ;
220+
221+ m_reconnectTimer->start (m_reconnectInterval);
222+ logLine (QString (" auto-reconnect in %1s" ).arg (m_reconnectInterval / 1000 ));
223+
224+ // Exponential backoff for the *next* attempt, capped. Reset to the base
225+ // interval happens on a successful connect (brokerConnected) or an
226+ // explicit disconnect().
227+ m_reconnectInterval = qMin (m_reconnectInterval * 2 , kReconnectMaxMs );
228+
229+ #endif
230+ }
231+
232+ void MqttManager::reconnectTimerFired ()
233+ {
234+ #if defined(ENABLE_MQTT)
235+
236+ if (!m_reconnectWanted) return ;
237+
238+ // Something already (re)connected us in the meantime — nothing to do.
239+ if (m_mqttclient &&
240+ (m_mqttclient->state () == QMqttClient::Connected ||
241+ m_mqttclient->state () == QMqttClient::Connecting))
242+ {
243+ return ;
244+ }
245+
246+ logLine (" auto-reconnect: attempting" );
247+ connect ();
248+
249+ // If this attempt fails, the client transitions back to Disconnected,
250+ // updateStateChange() fires scheduleReconnect() again, and the next
251+ // (longer) backoff interval is armed.
252+
253+ #endif
254+ }
255+
186256/* ************************************************************************** */
187257/* ************************************************************************** */
188258
@@ -292,7 +362,16 @@ void MqttManager::updateStateChange()
292362 // qDebug() << "MqttManager::updateStateChange()" << m_mqttclient->state();
293363 Q_EMIT statusChanged ();
294364
295- if (m_mqttclient->state () == QMqttClient::Disconnected) logLine (" status: disconnected" );
365+ if (m_mqttclient->state () == QMqttClient::Disconnected)
366+ {
367+ logLine (" status: disconnected" );
368+
369+ // Both unexpected drops (broker restart) and failed reconnect
370+ // attempts (broker still down) land here as Disconnected. Arm the
371+ // backoff; scheduleReconnect() no-ops unless we still want to be
372+ // connected, so an explicit disconnect() won't re-dial.
373+ scheduleReconnect ();
374+ }
296375 else if (m_mqttclient->state () == QMqttClient::Connecting) logLine (" status: connecting" );
297376 else if (m_mqttclient->state () == QMqttClient::Connected) logLine (" status: connected" );
298377 }
@@ -308,6 +387,11 @@ void MqttManager::brokerConnected()
308387
309388 if (m_mqttclient)
310389 {
390+ // Connection re-established: cancel any pending auto-reconnect and
391+ // reset the backoff so the next drop starts from the base interval.
392+ m_reconnectInterval = kReconnectBaseMs ;
393+ if (m_reconnectTimer) m_reconnectTimer->stop ();
394+
311395 // Clear drop bookkeeping: the banner/notification hides automatically
312396 // once droppedSinceDisconnect == 0 and disconnectedSince is invalid.
313397 if (m_droppedSinceDisconnect != 0 )
0 commit comments