Matthias Nott
2026-03-24 cb470d33d2665fcc6f8448d2736777656cf0cbe7
lib/services/mqtt_service.dart
....@@ -10,8 +10,15 @@
1010 import 'package:uuid/uuid.dart';
1111
1212 import '../models/server_config.dart';
13
-import 'websocket_service.dart' show ConnectionStatus;
1413 import 'wol_service.dart';
14
+
15
+/// Connection status for the MQTT client.
16
+enum ConnectionStatus {
17
+ disconnected,
18
+ connecting,
19
+ connected,
20
+ reconnecting,
21
+}
1522
1623 // Debug log to file (survives release builds)
1724 Future<void> _mqttLog(String msg) async {
....@@ -23,11 +30,11 @@
2330 } catch (_) {}
2431 }
2532
26
-/// MQTT client for PAILot, replacing WebSocketService.
33
+/// MQTT client for PAILot.
2734 ///
2835 /// Connects to the AIBroker daemon's embedded aedes broker.
2936 /// Subscribes to all pailot/ topics and dispatches messages
30
-/// through the same callback interface as WebSocketService.
37
+/// through the onMessage callback interface.
3138 class MqttService with WidgetsBindingObserver {
3239 MqttService({required this.config});
3340
....@@ -43,7 +50,7 @@
4350 final List<String> _seenMsgIdOrder = [];
4451 static const int _maxSeenIds = 500;
4552
46
- // Callbacks — same interface as WebSocketService
53
+ // Callbacks
4754 void Function(ConnectionStatus status)? onStatusChanged;
4855 void Function(Map<String, dynamic> message)? onMessage;
4956 void Function()? onOpen;
....@@ -149,9 +156,12 @@
149156 client.onAutoReconnect = _onAutoReconnect;
150157 client.onAutoReconnected = _onAutoReconnected;
151158
152
- // Persistent session: broker queues QoS 1 messages while client is offline
159
+ // Clean session: we handle offline delivery ourselves via catch_up protocol.
160
+ // Persistent sessions cause the broker to flood all queued QoS 1 messages
161
+ // on reconnect, which overwhelms the client with large voice payloads.
153162 final connMessage = MqttConnectMessage()
154163 .withClientIdentifier(clientId)
164
+ .startClean()
155165 .authenticateAs('pailot', config.mqttToken ?? '');
156166
157167 client.connectionMessage = connMessage;
....@@ -268,7 +278,7 @@
268278
269279 /// Route incoming MQTT messages to the onMessage callback.
270280 /// Translates MQTT topic structure into the flat message format
271
- /// that chat_screen expects (same as WebSocket messages).
281
+ /// that chat_screen expects.
272282 void _dispatchMessage(String topic, Map<String, dynamic> json) {
273283 final parts = topic.split('/');
274284
....@@ -369,7 +379,6 @@
369379 }
370380
371381 /// Send a message — routes to the appropriate MQTT topic based on content.
372
- /// Accepts the same message format as WebSocketService.send().
373382 void send(Map<String, dynamic> message) {
374383 final type = message['type'] as String?;
375384 final sessionId = message['sessionId'] as String?;