Matthias Nott
2026-03-24 25e6fc1cee45f976069069481ce575dae0e87258
fix: use ping health check on resume instead of force-disconnect
1 files modified
changed files
lib/services/mqtt_service.dart patch | view | blame | history
lib/services/mqtt_service.dart
....@@ -515,22 +515,34 @@
515515 switch (state) {
516516 case AppLifecycleState.resumed:
517517 if (_intentionalClose) break;
518
- // iOS kills the TCP socket during suspend. The MQTT client may still
519
- // think it's connected until the next ping timeout (30s+). Force a
520
- // fresh connection to avoid the "dead but looks alive" state.
521
- final client = _client;
522
- if (client != null && client.connectionStatus?.state == MqttConnectionState.connected) {
523
- // Verify the connection is actually alive by checking disconnect state
524
- // If auto-reconnect already fired, this is a no-op
525
- _mqttLog('MQTT: app resumed, verifying connection...');
526
- // Force disconnect + reconnect to get a clean state
527
- client.autoReconnect = false;
528
- client.disconnect();
529
- _client = null;
530
- _setStatus(ConnectionStatus.reconnecting);
531
- Future.delayed(const Duration(milliseconds: 500), () => connect());
532
- } else if (_status != ConnectionStatus.connected) {
518
+ _mqttLog('MQTT: app resumed, status=$_status');
519
+ if (_status != ConnectionStatus.connected) {
520
+ // Already knows it's disconnected — just reconnect
533521 connect();
522
+ } else {
523
+ // Thinks it's connected — verify by sending a ping command.
524
+ // If the connection is dead, the publish will fail or we won't
525
+ // get a pong back. Set a watchdog timer.
526
+ _mqttLog('MQTT: sending health check...');
527
+ _publish('pailot/control/in', {
528
+ 'type': 'command',
529
+ 'command': 'ping',
530
+ 'msgId': DateTime.now().millisecondsSinceEpoch.toString(),
531
+ 'ts': DateTime.now().millisecondsSinceEpoch,
532
+ }, MqttQos.atLeastOnce);
533
+ // If no pong within 3s, force reconnect
534
+ Future.delayed(const Duration(seconds: 3), () {
535
+ if (_status == ConnectionStatus.connected) {
536
+ // Check if client is still actually connected
537
+ final client = _client;
538
+ if (client == null || client.connectionStatus?.state != MqttConnectionState.connected) {
539
+ _mqttLog('MQTT: health check failed, reconnecting...');
540
+ _client = null;
541
+ _setStatus(ConnectionStatus.reconnecting);
542
+ connect();
543
+ }
544
+ }
545
+ });
534546 }
535547 case AppLifecycleState.paused:
536548 break;