From cb470d33d2665fcc6f8448d2736777656cf0cbe7 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Tue, 24 Mar 2026 00:25:07 +0100
Subject: [PATCH] feat: MQTT migration, offline catch_up, clean session, image support
---
lib/services/mqtt_service.dart | 23 ++++++++++++++++-------
1 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart
index 32e6fba..f7a51be 100644
--- a/lib/services/mqtt_service.dart
+++ b/lib/services/mqtt_service.dart
@@ -10,8 +10,15 @@
import 'package:uuid/uuid.dart';
import '../models/server_config.dart';
-import 'websocket_service.dart' show ConnectionStatus;
import 'wol_service.dart';
+
+/// Connection status for the MQTT client.
+enum ConnectionStatus {
+ disconnected,
+ connecting,
+ connected,
+ reconnecting,
+}
// Debug log to file (survives release builds)
Future<void> _mqttLog(String msg) async {
@@ -23,11 +30,11 @@
} catch (_) {}
}
-/// MQTT client for PAILot, replacing WebSocketService.
+/// MQTT client for PAILot.
///
/// Connects to the AIBroker daemon's embedded aedes broker.
/// Subscribes to all pailot/ topics and dispatches messages
-/// through the same callback interface as WebSocketService.
+/// through the onMessage callback interface.
class MqttService with WidgetsBindingObserver {
MqttService({required this.config});
@@ -43,7 +50,7 @@
final List<String> _seenMsgIdOrder = [];
static const int _maxSeenIds = 500;
- // Callbacks — same interface as WebSocketService
+ // Callbacks
void Function(ConnectionStatus status)? onStatusChanged;
void Function(Map<String, dynamic> message)? onMessage;
void Function()? onOpen;
@@ -149,9 +156,12 @@
client.onAutoReconnect = _onAutoReconnect;
client.onAutoReconnected = _onAutoReconnected;
- // Persistent session: broker queues QoS 1 messages while client is offline
+ // Clean session: we handle offline delivery ourselves via catch_up protocol.
+ // Persistent sessions cause the broker to flood all queued QoS 1 messages
+ // on reconnect, which overwhelms the client with large voice payloads.
final connMessage = MqttConnectMessage()
.withClientIdentifier(clientId)
+ .startClean()
.authenticateAs('pailot', config.mqttToken ?? '');
client.connectionMessage = connMessage;
@@ -268,7 +278,7 @@
/// Route incoming MQTT messages to the onMessage callback.
/// Translates MQTT topic structure into the flat message format
- /// that chat_screen expects (same as WebSocket messages).
+ /// that chat_screen expects.
void _dispatchMessage(String topic, Map<String, dynamic> json) {
final parts = topic.split('/');
@@ -369,7 +379,6 @@
}
/// Send a message — routes to the appropriate MQTT topic based on content.
- /// Accepts the same message format as WebSocketService.send().
void send(Map<String, dynamic> message) {
final type = message['type'] as String?;
final sessionId = message['sessionId'] as String?;
--
Gitblit v1.3.1