feat: Implement room login state management and enhance SAR message handling

This commit is contained in:
Janez T
2025-10-14 22:55:38 +02:00
parent fd54392d3c
commit 846cc9dce4
16 changed files with 2652 additions and 208 deletions

303
ADVERT_SYSTEM.md Normal file
View File

@@ -0,0 +1,303 @@
# MeshCore Advertisement System
This document explains how the MeshCore mesh network advertisement system works and how your app receives and processes contact updates.
## Overview
The MeshCore mesh network uses a broadcast advertisement system where nodes periodically announce their presence, location, and metadata to the network. Your Flutter app receives these advertisements and automatically updates the contact list.
## Advertisement Flow
### 1. Node Broadcasts Advertisement
When a node in the mesh network wants to announce its presence:
- The node broadcasts an advertisement packet over LoRa
- Advertisement contains: public key, name, location, type, etc.
- Advertisements are typically sent every few minutes or when data changes
### 2. Companion Radio Receives Advertisement
Your BLE-connected companion radio listens to the mesh network and receives these advertisements:
```
[Mesh Network] ───(LoRa)──→ [Companion Radio]
```
### 3. Companion Radio Notifies App
The companion radio forwards advertisement notifications to your app via BLE:
#### Step 3a: PUSH_CODE_ADVERT (0x80)
First, you receive a simple notification that an advert was received:
```dart
flutter: 📥 [RX] Received: ADVERT (0x80)
flutter: Data size: 33 bytes
flutter: Payload: 32 bytes
flutter: Handling Advert push
flutter: [Advert] Parsing advert push notification...
flutter: 📡 ADVERT RECEIVED FROM NODE:
flutter: Public key prefix (6 bytes): a5:9c:36:02:c0:d7
flutter: Public key (full 32 bytes): a5:9c:36:02:c0:d7:e4:c3:...
flutter: This indicates the node is broadcasting its presence
flutter: The companion radio will automatically update contact info
flutter: Expected follow-up:
flutter: - If manual_add_contacts=0: PUSH_CODE_NEW_ADVERT with full details
flutter: - If manual_add_contacts=1: Call CMD_GET_CONTACTS to sync
```
**Protocol Format:**
```
[0x80] - PUSH_CODE_ADVERT
[32 bytes] - Public key of advertising node
```
#### Step 3b: PUSH_CODE_NEW_ADVERT (0x8A) - Automatic Contact Update
If your device has `manual_add_contacts=0` (automatic mode), the companion radio automatically sends the full contact details:
```dart
flutter: 📥 [RX] Received: NEW_ADVERT (0x8A)
flutter: Data size: 145 bytes
flutter: Payload: 144 bytes
flutter: Handling NewAdvert push
flutter: [NewAdvert] Parsing new advertisement...
flutter: Public key prefix: a5:9c:36:02:c0:d7
flutter: Type byte: 1 Type: ContactType.chat
flutter: Advertised name: "SAR Team Alpha"
flutter: Latitude: 46.056900°
flutter: Longitude: 14.505800°
flutter: [NewAdvert] Parsed successfully - new contact advertised on network
```
**Protocol Format:**
```
[0x8A] - PUSH_CODE_NEW_ADVERT
[32 bytes] - Public key
[1 byte] - Type (ADV_TYPE_*)
[1 byte] - Flags
[1 byte] - Out path length
[64 bytes] - Out path
[32 bytes] - Advertised name (null-terminated)
[4 bytes] - Last advert timestamp (uint32)
[4 bytes] - Latitude * 1E6 (int32)
[4 bytes] - Longitude * 1E6 (int32)
[4 bytes] - Last modified timestamp (uint32)
```
The app automatically adds/updates this contact via the `onContactReceived` callback!
## Manual vs Automatic Contact Management
Your companion radio has a setting called `manual_add_contacts`:
### Automatic Mode (manual_add_contacts=0) - RECOMMENDED
**Behavior:**
1. ✅ PUSH_CODE_ADVERT (0x80) received → just informational
2. ✅ PUSH_CODE_NEW_ADVERT (0x8A) received → **contact automatically added to app**
3. ✅ No action needed from app
**Advantages:**
- Contacts appear instantly when they advertise
- No need to manually sync
- Perfect for SAR operations where team members join dynamically
### Manual Mode (manual_add_contacts=1)
**Behavior:**
1. ✅ PUSH_CODE_ADVERT (0x80) received → informational
2. ❌ PUSH_CODE_NEW_ADVERT (0x8A) NOT sent
3. 📞 App must call `CMD_GET_CONTACTS` to sync
**When to use:**
- When you want control over which contacts are added
- When bandwidth is very limited
- When you have a static team roster
## Implementation in Your App
### Current Implementation
The app is already fully configured to handle advertisements automatically:
```dart
// In MeshCoreBleService (_handleNewAdvert)
final contact = Contact(
publicKey: publicKey,
type: type,
flags: flags,
outPathLen: outPathLen,
outPath: outPath,
advName: advName,
lastAdvert: lastAdvert,
advLat: advLat,
advLon: advLon,
lastMod: lastMod,
);
// This callback automatically updates the contact list
onContactReceived?.call(contact);
```
```dart
// In AppProvider (_setupCallbacks)
connectionProvider.onContactReceived = (contact) {
// Automatically add or update contact in the list
contactsProvider.addOrUpdateContact(contact);
};
```
### Event Flow
```
[Mesh Node Advertises]
[Companion Radio Receives via LoRa]
[PUSH_CODE_ADVERT (0x80) sent to app]
↓ (if manual_add_contacts=0)
[PUSH_CODE_NEW_ADVERT (0x8A) sent to app]
[onContactReceived callback fired]
[contactsProvider.addOrUpdateContact(contact)]
[UI automatically updates via notifyListeners()]
```
## Checking Your Device Settings
To see if your device is in automatic or manual mode:
```dart
// Check the manualAddContacts field from SelfInfo
final manualMode = connectionProvider.deviceInfo.manualAddContacts;
if (manualMode == false) {
print('✅ Automatic mode: Contacts will appear automatically');
} else {
print('⚠️ Manual mode: You need to call getContacts() after adverts');
}
```
You can change this setting:
```dart
await connectionProvider.setOtherParams(
manualAddContacts: 0, // 0 = automatic, 1 = manual
telemetryModes: currentTelemetryModes,
advertLocationPolicy: currentLocationPolicy,
);
```
## Troubleshooting
### "I receive ADVERT (0x80) but no NEW_ADVERT (0x8A)"
**Cause:** Your device is in manual mode (`manual_add_contacts=1`)
**Solution:**
1. Check device settings via SelfInfo
2. Change to automatic mode, OR
3. Call `CMD_GET_CONTACTS` after receiving adverts
### "Contacts don't appear on the map"
**Possible causes:**
1. Contact type is not `ContactType.chat` (only chat contacts show on map)
2. Contact has no GPS coordinates (lat/lon = 0)
3. Contact hasn't advertised recently
**Debug:**
```dart
// Check contact properties
print('Contact: ${contact.advName}');
print('Type: ${contact.type}'); // Should be ContactType.chat
print('Lat: ${contact.latitude}'); // Should not be null
print('Lon: ${contact.longitude}'); // Should not be null
```
### "Room contact not found for login"
**Cause:** Room hasn't advertised yet or wasn't synced
**Solution:**
```dart
// Force sync contacts first
await connectionProvider.getContacts();
// Small delay to ensure contacts are loaded
await Future.delayed(const Duration(milliseconds: 500));
// Then try login
await connectionProvider.loginToRoom(
roomPublicKey: roomContact.publicKey,
password: 'your_password',
);
```
## Best Practices
1. **Use automatic mode for SAR operations** - Team members will appear as they join
2. **Sync contacts on first connect** - Always call `getContacts()` after connecting
3. **Handle both modes gracefully** - Check `manual_add_contacts` setting
4. **Monitor advert activity** - Use `onAdvertReceived` callback to show network activity
5. **Cache contacts locally** - Don't rely solely on live advertisements
## Protocol Summary
| Push Code | Name | When Sent | Contains | Action Required |
|-----------|------|-----------|----------|-----------------|
| 0x80 | PUSH_CODE_ADVERT | When any node advertises | Just public key | None (informational) |
| 0x8A | PUSH_CODE_NEW_ADVERT | After ADVERT, if manual_add_contacts=0 | Full contact details | None (auto-added) |
## Example Logs
### Successful Automatic Flow
```
📥 [RX] Received: ADVERT (0x80)
📡 ADVERT RECEIVED FROM NODE:
Public key prefix (6 bytes): a5:9c:36:02:c0:d7
📥 [Provider] Advert received from node
Note: Waiting for PUSH_CODE_NEW_ADVERT (0x8A) with full contact details
📥 [RX] Received: NEW_ADVERT (0x8A)
[NewAdvert] Parsing new advertisement...
Advertised name: "SAR Team Alpha"
Latitude: 46.056900°
Longitude: 14.505800°
✅ [NewAdvert] Parsed successfully
✅ [Provider] Contact added: SAR Team Alpha
```
### Manual Mode Flow
```
📥 [RX] Received: ADVERT (0x80)
📡 ADVERT RECEIVED FROM NODE:
Public key prefix (6 bytes): a5:9c:36:02:c0:d7
📥 [Provider] Advert received from node
Note: manual_add_contacts=1, you need to call CMD_GET_CONTACTS
📤 [TX] Sending command: GET_CONTACTS (0x04)
📥 [RX] Received: CONTACTS_START (0x02)
📥 [RX] Received: CONTACT (0x03)
[Contact] Parsing contact...
Advertised name: "SAR Team Alpha"
✅ [Provider] Contact added: SAR Team Alpha
```
## Related Files
- `lib/services/meshcore_ble_service.dart:876` - `_handleAdvert()` implementation
- `lib/services/meshcore_ble_service.dart:924` - `_handleNewAdvert()` implementation
- `lib/providers/connection_provider.dart:164` - `onAdvertReceived` callback setup
- `lib/providers/app_provider.dart:44` - Contact sync setup
- `lib/models/contact.dart` - Contact data model
## Further Reading
- [MeshCore Protocol Documentation](CLAUDE.md) - Full protocol specification
- [Contact Management](lib/providers/contacts_provider.dart) - Contact provider implementation