diff --git a/ios/Runner.app.dSYM.zip b/ios/Runner.app.dSYM.zip index 571e481..2d0a1fe 100644 Binary files a/ios/Runner.app.dSYM.zip and b/ios/Runner.app.dSYM.zip differ diff --git a/ios/Runner.ipa b/ios/Runner.ipa index f20f106..0ce6b15 100644 Binary files a/ios/Runner.ipa and b/ios/Runner.ipa differ diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ad54059..8e7b361 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -489,7 +489,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = JND55328G8; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -511,7 +511,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = JND55328G8; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; @@ -530,7 +530,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = JND55328G8; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; @@ -547,7 +547,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = JND55328G8; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; @@ -679,7 +679,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = JND55328G8; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -702,7 +702,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = JND55328G8; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 13a4ca8..d073400 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -43,7 +43,7 @@ CFBundleSignature ???? CFBundleVersion - 41 + 42 LSRequiresIPhoneOS NSBluetoothAlwaysUsageDescription diff --git a/lib/providers/app_provider.dart b/lib/providers/app_provider.dart index b3d0fca..d7b3ea4 100644 --- a/lib/providers/app_provider.dart +++ b/lib/providers/app_provider.dart @@ -245,6 +245,25 @@ class AppProvider with ChangeNotifier { }); }; + // When an advertisement is received (PUSH_CODE_ADVERT 0x80) + // This may be sent by the radio for existing contacts instead of PUSH_CODE_NEW_ADVERT (0x8A) + connectionProvider.onAdvertReceived = (publicKey) { + debugPrint('๐Ÿ“ก [AppProvider] Advertisement received: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...'); + // Check if this is an existing contact that might have updated location + final contact = contactsProvider.findContactByKey(publicKey); + if (contact != null) { + debugPrint(' Existing contact "${contact.advName}" - triggering contact sync for updates'); + // Trigger a contact sync to get the updated contact information + Future.delayed(const Duration(milliseconds: 100), () { + if (connectionProvider.deviceInfo.isConnected) { + connectionProvider.getContacts(); + } + }); + } else { + debugPrint(' New contact - waiting for PUSH_CODE_NEW_ADVERT (0x8A) with full details'); + } + }; + // When a message is sent (RESP_CODE_SENT received) connectionProvider.onMessageSent = (messageId, expectedAckTag, suggestedTimeoutMs) { debugPrint('๐Ÿ“ค [AppProvider] Message sent - Message ID: $messageId, ACK tag: $expectedAckTag'); diff --git a/lib/providers/connection_provider.dart b/lib/providers/connection_provider.dart index 38423d0..db8aa1d 100644 --- a/lib/providers/connection_provider.dart +++ b/lib/providers/connection_provider.dart @@ -130,6 +130,7 @@ class ConnectionProvider with ChangeNotifier { Function(int channelIdx, String channelName)? onChannelInfoReceived; Function(Uint8List publicKeyPrefix, int tag, Uint8List responseData)? onBinaryResponse; + Function(Uint8List publicKey)? onAdvertReceived; Function(Uint8List publicKey)? onPathUpdated; Function(Uint8List publicKeyPrefix, int permissions, bool isAdmin, int tag)? onLoginSuccess; @@ -262,10 +263,14 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onContactReceived = (contact) { + debugPrint('๐Ÿ“ฅ [Provider] Contact received (0x8A): "${contact.advName}"'); + debugPrint(' Forwarding to AppProvider via onContactReceived callback'); onContactReceived?.call(contact); }; _bleService.onContactsComplete = (contacts) { + debugPrint('๐Ÿ“ฅ [Provider] Contacts sync complete: ${contacts.length} contacts'); + debugPrint(' Forwarding to AppProvider via onContactsComplete callback'); onContactsComplete?.call(contacts); }; @@ -286,8 +291,14 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onTelemetryReceived = (publicKey, lppData) { + debugPrint('๐Ÿ“ฅ [Provider] Telemetry response (0x8B) received'); + debugPrint( + ' Public key: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...', + ); + debugPrint(' LPP data: ${lppData.length} bytes'); // Mark ping as successful if this was a ping request _pingTracker.markPingSuccessful(publicKey); + debugPrint(' Forwarding to AppProvider via onTelemetryReceived callback'); onTelemetryReceived?.call(publicKey, lppData); }; @@ -362,11 +373,10 @@ class ConnectionProvider with ChangeNotifier { debugPrint( ' Public key: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...', ); - debugPrint( - ' Note: Waiting for PUSH_CODE_NEW_ADVERT (0x8A) with full contact details', - ); - // The companion radio will automatically send PUSH_CODE_NEW_ADVERT if manual_add_contacts=0 - // which will trigger onContactReceived callback and add/update the contact + // Forward to AppProvider to trigger contact update + // The radio may send only PUSH_CODE_ADVERT (0x80) for existing contacts + // instead of PUSH_CODE_NEW_ADVERT (0x8A), so we need to handle this + onAdvertReceived?.call(publicKey); }; _bleService.onPathUpdated = (publicKey) { diff --git a/lib/services/ble/ble_response_handler.dart b/lib/services/ble/ble_response_handler.dart index 5ad285d..3133e58 100644 --- a/lib/services/ble/ble_response_handler.dart +++ b/lib/services/ble/ble_response_handler.dart @@ -409,11 +409,13 @@ class BleResponseHandler { } } - /// Handle Advert push + /// Handle Advert push (0x80) - basic advertisement with public key only void _handleAdvert(BufferReader reader) { try { final publicKey = FrameParser.parseAdvert(reader); if (publicKey != null) { + final shortKey = publicKey.sublist(0, 8).map((b) => b.toRadixString(16).padLeft(2, '0')).join(''); + debugPrint(' โœ… [Advert 0x80] From node: $shortKey... (public key only, no location data)'); onAdvertReceived?.call(publicKey); } debugPrint(' โœ… [Advert] Parsed successfully'); @@ -856,14 +858,20 @@ class BleResponseHandler { debugPrint(' ๐Ÿงน [Echo] Cleaned up ${toRemove.length} old trackers'); } - /// Handle NewAdvert push + /// Handle NewAdvert push (0x8A) - full contact info with location void _handleNewAdvert(BufferReader reader) { try { final contact = FrameParser.parseContact(reader); - debugPrint(' โœ… [NewAdvert] Parsed successfully: ${contact.advName}'); + debugPrint(' โœ… [NewAdvert 0x8A] Parsed successfully: ${contact.advName}'); debugPrint( ' outPathLen: ${contact.outPathLen} (${contact.pathDescription})', ); + if (contact.advLat != 0 || contact.advLon != 0) { + final location = contact.advertLocation; + if (location != null) { + debugPrint(' ๐Ÿ“ Location: ${location.latitude}, ${location.longitude}'); + } + } onContactReceived?.call(contact); } catch (e) { debugPrint(' โŒ [NewAdvert] Parsing error: $e'); diff --git a/lib/services/meshcore_ble_service.dart b/lib/services/meshcore_ble_service.dart index 4cac04b..98041ca 100644 --- a/lib/services/meshcore_ble_service.dart +++ b/lib/services/meshcore_ble_service.dart @@ -122,15 +122,19 @@ class MeshCoreBleService { // Response handler callbacks _responseHandler.onContactReceived = (contact) { + debugPrint('๐Ÿ”” [BleService] onContactReceived - "${contact.advName}" - forwarding to ConnectionProvider'); onContactReceived?.call(contact); }; _responseHandler.onContactsComplete = (contacts) { + debugPrint('๐Ÿ”” [BleService] onContactsComplete - ${contacts.length} contacts - forwarding to ConnectionProvider'); onContactsComplete?.call(contacts); }; _responseHandler.onMessageReceived = (message) { + debugPrint('๐Ÿ”” [BleService] onMessageReceived - forwarding to ConnectionProvider'); onMessageReceived?.call(message); }; _responseHandler.onTelemetryReceived = (publicKey, lppData) { + debugPrint('๐Ÿ”” [BleService] onTelemetryReceived - ${lppData.length} bytes - forwarding to ConnectionProvider'); onTelemetryReceived?.call(publicKey, lppData); }; _responseHandler.onSelfInfoReceived = (selfInfo) { @@ -160,9 +164,11 @@ class MeshCoreBleService { onLoginFail?.call(publicKeyPrefix); }; _responseHandler.onAdvertReceived = (publicKey) { + debugPrint('๐Ÿ”” [BleService] onAdvertReceived - forwarding to ConnectionProvider'); onAdvertReceived?.call(publicKey); }; _responseHandler.onPathUpdated = (publicKey) { + debugPrint('๐Ÿ”” [BleService] onPathUpdated - forwarding to ConnectionProvider'); onPathUpdated?.call(publicKey); }; _responseHandler.onMessageSent =