From 25e6fc1cee45f976069069481ce575dae0e87258 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Tue, 24 Mar 2026 02:17:13 +0100
Subject: [PATCH] fix: use ping health check on resume instead of force-disconnect

---
 lib/services/mqtt_service.dart |   42 +++++++++++++++++++++++++++---------------
 1 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart
index 35464de..4891638 100644
--- a/lib/services/mqtt_service.dart
+++ b/lib/services/mqtt_service.dart
@@ -515,22 +515,34 @@
     switch (state) {
       case AppLifecycleState.resumed:
         if (_intentionalClose) break;
-        // iOS kills the TCP socket during suspend. The MQTT client may still
-        // think it's connected until the next ping timeout (30s+). Force a
-        // fresh connection to avoid the "dead but looks alive" state.
-        final client = _client;
-        if (client != null && client.connectionStatus?.state == MqttConnectionState.connected) {
-          // Verify the connection is actually alive by checking disconnect state
-          // If auto-reconnect already fired, this is a no-op
-          _mqttLog('MQTT: app resumed, verifying connection...');
-          // Force disconnect + reconnect to get a clean state
-          client.autoReconnect = false;
-          client.disconnect();
-          _client = null;
-          _setStatus(ConnectionStatus.reconnecting);
-          Future.delayed(const Duration(milliseconds: 500), () => connect());
-        } else if (_status != ConnectionStatus.connected) {
+        _mqttLog('MQTT: app resumed, status=$_status');
+        if (_status != ConnectionStatus.connected) {
+          // Already knows it's disconnected — just reconnect
           connect();
+        } else {
+          // Thinks it's connected — verify by sending a ping command.
+          // If the connection is dead, the publish will fail or we won't
+          // get a pong back. Set a watchdog timer.
+          _mqttLog('MQTT: sending health check...');
+          _publish('pailot/control/in', {
+            'type': 'command',
+            'command': 'ping',
+            'msgId': DateTime.now().millisecondsSinceEpoch.toString(),
+            'ts': DateTime.now().millisecondsSinceEpoch,
+          }, MqttQos.atLeastOnce);
+          // If no pong within 3s, force reconnect
+          Future.delayed(const Duration(seconds: 3), () {
+            if (_status == ConnectionStatus.connected) {
+              // Check if client is still actually connected
+              final client = _client;
+              if (client == null || client.connectionStatus?.state != MqttConnectionState.connected) {
+                _mqttLog('MQTT: health check failed, reconnecting...');
+                _client = null;
+                _setStatus(ConnectionStatus.reconnecting);
+                connect();
+              }
+            }
+          });
         }
       case AppLifecycleState.paused:
         break;

--
Gitblit v1.3.1