diff --git a/lib/services/lastheard_websocket_client.dart b/lib/services/lastheard_websocket_client.dart index 0cd5a4c..5f74663 100644 --- a/lib/services/lastheard_websocket_client.dart +++ b/lib/services/lastheard_websocket_client.dart @@ -15,8 +15,14 @@ class LastHeardWebSocketClient { static const int _maxReconnectAttempts = 5; static const Duration _reconnectDelay = Duration(seconds: 5); + final List? _radioIds; + int _receivedCount = 0; + static const int _maxItems = 200; + final Completer _readyCompleter = Completer(); + LastHeardWebSocketClient({List? radioIds}) : _radioIds = radioIds; + // Socket.IO Engine.IO v4 WebSocket URL for Last Heard static const String _wsUrl = 'wss://api.brandmeister.network/lh/?EIO=4&transport=websocket'; @@ -37,7 +43,8 @@ class LastHeardWebSocketClient { } _isConnecting = true; - _shouldReconnect = true; + // Don't reconnect if radio IDs are supplied (one-time search query) + _shouldReconnect = _radioIds == null || _radioIds.isEmpty; try { debugPrint('LastHeard WS: Connecting to $_wsUrl'); @@ -71,7 +78,7 @@ class LastHeardWebSocketClient { _handleOpenPacket(messageStr); } else if (messageStr.startsWith('2')) { _sendPong(); - } else if (messageStr == '40') { + } else if (messageStr.startsWith('40')) { _handleNamespaceConnect(); } else if (messageStr.startsWith('42')) { _handleDataPacket(messageStr); @@ -133,11 +140,20 @@ class LastHeardWebSocketClient { return; } + if (_radioIds == null || _radioIds.isEmpty) { + debugPrint('LastHeard WS: No radio IDs provided, skipping search query'); + return; + } + try { + // Build SQL query for multiple radio IDs: SourceID = 123 OR SourceID = 456 + final sqlParts = _radioIds.map((id) => 'SourceID = $id').toList(); + final sql = sqlParts.join(' OR '); + final message = '42${jsonEncode([ 'searchMongo', { - 'query': {'sql': ''}, + 'query': {'sql': sql}, 'amount': 200 } ])}'; @@ -180,6 +196,15 @@ class LastHeardWebSocketClient { _messageController != null && !_messageController!.isClosed) { _messageController!.add(eventData); + + // Track received items and disconnect after receiving max items (for search queries) + if (_radioIds != null && _radioIds.isNotEmpty) { + _receivedCount++; + if (_receivedCount >= _maxItems) { + debugPrint('LastHeard WS: Received $_maxItems items, disconnecting'); + disconnect(); + } + } } } catch (e) { debugPrint('LastHeard WS: Error parsing data packet: $e'); diff --git a/lib/views/device_detail_view.dart b/lib/views/device_detail_view.dart index c43f14d..130bfd4 100644 --- a/lib/views/device_detail_view.dart +++ b/lib/views/device_detail_view.dart @@ -500,7 +500,7 @@ class _DeviceDetailViewState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Last Heard on Talkgroup', + 'Last Activity on Talkgroup', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), diff --git a/lib/views/last_heard_view.dart b/lib/views/last_heard_view.dart index f57a62a..6cff27f 100644 --- a/lib/views/last_heard_view.dart +++ b/lib/views/last_heard_view.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import '../models/last_heard_item.dart'; +import '../services/authentication_manager.dart'; import '../services/lastheard_websocket_client.dart'; class LastHeardView extends StatefulWidget { @@ -36,7 +38,11 @@ class _LastHeardViewState extends State { _isConnecting = true; }); - _wsClient = LastHeardWebSocketClient(); + final authManager = context.read(); + final devices = await authManager.getDevices(); + final radioIds = devices.map((d) => d.id).toList(); + + _wsClient = LastHeardWebSocketClient(radioIds: radioIds); await _wsClient!.connect(); _wsSubscription = _wsClient!.messageStream.listen((message) { @@ -53,7 +59,7 @@ class _LastHeardViewState extends State { void _handleMqttMessage(Map data) { try { final topic = data['topic'] as String?; - if (topic == 'LH') { + if (topic == 'LH-Startup') { final payloadStr = data['payload'] as String?; if (payloadStr != null) { final payload = jsonDecode(payloadStr) as Map; @@ -84,21 +90,7 @@ class _LastHeardViewState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Last Heard'), - actions: [ - if (_items.isNotEmpty) - Padding( - padding: const EdgeInsets.only(right: 16), - child: Center( - child: Text( - '${_items.length} ${_items.length == 1 ? "entry" : "entries"}', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onPrimaryContainer, - ), - ), - ), - ), - ], + title: const Text('Last Activity'), ), body: _buildBody(), ); @@ -112,7 +104,7 @@ class _LastHeardViewState extends State { children: [ CircularProgressIndicator(), SizedBox(height: 16), - Text('Connecting to Last Heard...'), + Text('Connecting to Last Activity...'), ], ), ); @@ -135,7 +127,7 @@ class _LastHeardViewState extends State { ), const SizedBox(height: 8), Text( - 'Waiting for Last Heard data...', + 'Waiting for Last Activity data...', style: TextStyle(color: Colors.grey[600]), ), ], diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 97b1591..852530e 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -40,7 +40,7 @@ class _MainViewState extends State { ), BottomNavigationBarItem( icon: Icon(Icons.history), - label: 'Last Heard', + label: 'Last Activity', ), BottomNavigationBarItem( icon: Icon(Icons.more_horiz),