mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-16 22:00:50 +10:00
Merge branch 'dev' of https://github.com/zjs81/meshcore-open into dev
This commit is contained in:
@@ -2207,6 +2207,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_bleInitialSyncStarted = true;
|
_bleInitialSyncStarted = true;
|
||||||
|
_pendingInitialContactsSync = true;
|
||||||
|
|
||||||
await _requestDeviceInfo();
|
await _requestDeviceInfo();
|
||||||
_startBatteryPolling();
|
_startBatteryPolling();
|
||||||
@@ -4184,7 +4185,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
if (_contacts.isEmpty) return 0;
|
if (_contacts.isEmpty) return 0;
|
||||||
var latest = 0;
|
var latest = 0;
|
||||||
for (final contact in _contacts) {
|
for (final contact in _contacts) {
|
||||||
final seconds = contact.lastSeen.millisecondsSinceEpoch ~/ 1000;
|
// prefer lastmod per spec, fallback to lastseen
|
||||||
|
final source = contact.lastModified ?? contact.lastSeen;
|
||||||
|
final seconds = source.millisecondsSinceEpoch ~/ 1000;
|
||||||
if (seconds > latest) {
|
if (seconds > latest) {
|
||||||
latest = seconds;
|
latest = seconds;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -735,10 +735,15 @@ Uint8List buildUpdateContactPathFrame(
|
|||||||
writer.writeInt32LE((longitude * 1e6).round());
|
writer.writeInt32LE((longitude * 1e6).round());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastModified != null) {
|
final hasLocation = lat != null && lon != null;
|
||||||
// Last modified
|
if (hasLocation || lastModified != null) {
|
||||||
final lastModifiedTimestamp = lastModified.millisecondsSinceEpoch ~/ 1000;
|
writer.writeInt32LE(hasLocation ? (lat * 1e6).round() : 0);
|
||||||
writer.writeUInt32LE(lastModifiedTimestamp);
|
writer.writeInt32LE(hasLocation ? (lon * 1e6).round() : 0);
|
||||||
|
if (lastModified != null) {
|
||||||
|
// Last modified
|
||||||
|
final lastModifiedTimestamp = lastModified.millisecondsSinceEpoch ~/ 1000;
|
||||||
|
writer.writeUInt32LE(lastModifiedTimestamp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return writer.toBytes();
|
return writer.toBytes();
|
||||||
|
|||||||
+28
-3
@@ -17,6 +17,7 @@ class Contact {
|
|||||||
final double? longitude;
|
final double? longitude;
|
||||||
final DateTime lastSeen;
|
final DateTime lastSeen;
|
||||||
final DateTime lastMessageAt;
|
final DateTime lastMessageAt;
|
||||||
|
final DateTime? lastModified;
|
||||||
final bool isActive;
|
final bool isActive;
|
||||||
final bool wasPulled;
|
final bool wasPulled;
|
||||||
final Uint8List? rawPacket;
|
final Uint8List? rawPacket;
|
||||||
@@ -33,6 +34,7 @@ class Contact {
|
|||||||
this.latitude,
|
this.latitude,
|
||||||
this.longitude,
|
this.longitude,
|
||||||
required this.lastSeen,
|
required this.lastSeen,
|
||||||
|
this.lastModified,
|
||||||
DateTime? lastMessageAt,
|
DateTime? lastMessageAt,
|
||||||
this.isActive = true,
|
this.isActive = true,
|
||||||
this.wasPulled = false,
|
this.wasPulled = false,
|
||||||
@@ -86,6 +88,7 @@ class Contact {
|
|||||||
double? longitude,
|
double? longitude,
|
||||||
DateTime? lastSeen,
|
DateTime? lastSeen,
|
||||||
DateTime? lastMessageAt,
|
DateTime? lastMessageAt,
|
||||||
|
DateTime? lastModified,
|
||||||
bool? isActive,
|
bool? isActive,
|
||||||
Uint8List? rawPacket,
|
Uint8List? rawPacket,
|
||||||
}) {
|
}) {
|
||||||
@@ -106,6 +109,7 @@ class Contact {
|
|||||||
longitude: longitude ?? this.longitude,
|
longitude: longitude ?? this.longitude,
|
||||||
lastSeen: lastSeen ?? this.lastSeen,
|
lastSeen: lastSeen ?? this.lastSeen,
|
||||||
lastMessageAt: lastMessageAt ?? this.lastMessageAt,
|
lastMessageAt: lastMessageAt ?? this.lastMessageAt,
|
||||||
|
lastModified: lastModified ?? this.lastModified,
|
||||||
isActive: isActive ?? this.isActive,
|
isActive: isActive ?? this.isActive,
|
||||||
rawPacket: rawPacket ?? this.rawPacket,
|
rawPacket: rawPacket ?? this.rawPacket,
|
||||||
);
|
);
|
||||||
@@ -174,16 +178,34 @@ class Contact {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastMod = reader.readUInt32LE();
|
// mandatory last_advert_timestamp
|
||||||
|
final lastAdvertTimestamp = reader.readUInt32LE();
|
||||||
|
|
||||||
double? lat, lon;
|
double? lat, lon;
|
||||||
if (reader.remaining >= 8) {
|
DateTime? lastModified;
|
||||||
|
if (reader.remaining >= 12) {
|
||||||
|
final latRaw = reader.readInt32LE();
|
||||||
|
final lonRaw = reader.readInt32LE();
|
||||||
|
final lastModRaw = reader.readUInt32LE();
|
||||||
|
// TODO: should this be &&?
|
||||||
|
if (latRaw != 0 || lonRaw != 0) {
|
||||||
|
lat = latRaw / 1e6;
|
||||||
|
lon = lonRaw / 1e6;
|
||||||
|
}
|
||||||
|
if (lastModRaw != 0) {
|
||||||
|
lastModified = DateTime.fromMillisecondsSinceEpoch(lastModRaw * 1000);
|
||||||
|
}
|
||||||
|
} else if (reader.remaining >= 8) {
|
||||||
|
// Old layout: gps without lastmod
|
||||||
final latRaw = reader.readInt32LE();
|
final latRaw = reader.readInt32LE();
|
||||||
final lonRaw = reader.readInt32LE();
|
final lonRaw = reader.readInt32LE();
|
||||||
if (latRaw != 0 || lonRaw != 0) {
|
if (latRaw != 0 || lonRaw != 0) {
|
||||||
lat = latRaw / 1e6;
|
lat = latRaw / 1e6;
|
||||||
lon = lonRaw / 1e6;
|
lon = lonRaw / 1e6;
|
||||||
}
|
}
|
||||||
|
appLogger.info(
|
||||||
|
'Contact ${pubKeyToHex(pubKey).substring(0, 8)} has gps but no lastmod (legacy firmware layout)',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Contact(
|
return Contact(
|
||||||
@@ -195,7 +217,10 @@ class Contact {
|
|||||||
path: pathBytes,
|
path: pathBytes,
|
||||||
latitude: lat,
|
latitude: lat,
|
||||||
longitude: lon,
|
longitude: lon,
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastMod * 1000),
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
lastAdvertTimestamp * 1000,
|
||||||
|
),
|
||||||
|
lastModified: lastModified,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
rawPacket: null,
|
rawPacket: null,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class ContactDiscoveryStore {
|
|||||||
'latitude': contact.latitude,
|
'latitude': contact.latitude,
|
||||||
'longitude': contact.longitude,
|
'longitude': contact.longitude,
|
||||||
'lastSeen': contact.lastSeen.millisecondsSinceEpoch,
|
'lastSeen': contact.lastSeen.millisecondsSinceEpoch,
|
||||||
|
'lastModified': contact.lastModified?.millisecondsSinceEpoch,
|
||||||
'lastMessageAt': contact.lastMessageAt.millisecondsSinceEpoch,
|
'lastMessageAt': contact.lastMessageAt.millisecondsSinceEpoch,
|
||||||
'rawPacket': contact.rawPacket != null
|
'rawPacket': contact.rawPacket != null
|
||||||
? base64Encode(contact.rawPacket!)
|
? base64Encode(contact.rawPacket!)
|
||||||
@@ -53,6 +54,7 @@ class ContactDiscoveryStore {
|
|||||||
Contact _fromJson(Map<String, dynamic> json) {
|
Contact _fromJson(Map<String, dynamic> json) {
|
||||||
final lastSeenMs = json['lastSeen'] as int? ?? 0;
|
final lastSeenMs = json['lastSeen'] as int? ?? 0;
|
||||||
final lastMessageMs = json['lastMessageAt'] as int?;
|
final lastMessageMs = json['lastMessageAt'] as int?;
|
||||||
|
final lastModifiedMs = json['lastModified'] as int?;
|
||||||
return Contact(
|
return Contact(
|
||||||
publicKey: Uint8List.fromList(base64Decode(json['publicKey'] as String)),
|
publicKey: Uint8List.fromList(base64Decode(json['publicKey'] as String)),
|
||||||
name: json['name'] as String? ?? 'Unknown',
|
name: json['name'] as String? ?? 'Unknown',
|
||||||
@@ -71,6 +73,9 @@ class ContactDiscoveryStore {
|
|||||||
latitude: (json['latitude'] as num?)?.toDouble(),
|
latitude: (json['latitude'] as num?)?.toDouble(),
|
||||||
longitude: (json['longitude'] as num?)?.toDouble(),
|
longitude: (json['longitude'] as num?)?.toDouble(),
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastSeenMs),
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastSeenMs),
|
||||||
|
lastModified: lastModifiedMs == null
|
||||||
|
? null
|
||||||
|
: DateTime.fromMillisecondsSinceEpoch(lastModifiedMs),
|
||||||
lastMessageAt: DateTime.fromMillisecondsSinceEpoch(
|
lastMessageAt: DateTime.fromMillisecondsSinceEpoch(
|
||||||
lastMessageMs ?? lastSeenMs,
|
lastMessageMs ?? lastSeenMs,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class ContactStore {
|
|||||||
'latitude': contact.latitude,
|
'latitude': contact.latitude,
|
||||||
'longitude': contact.longitude,
|
'longitude': contact.longitude,
|
||||||
'lastSeen': contact.lastSeen.millisecondsSinceEpoch,
|
'lastSeen': contact.lastSeen.millisecondsSinceEpoch,
|
||||||
|
'lastModified': contact.lastModified?.millisecondsSinceEpoch,
|
||||||
'lastMessageAt': contact.lastMessageAt.millisecondsSinceEpoch,
|
'lastMessageAt': contact.lastMessageAt.millisecondsSinceEpoch,
|
||||||
'isActive': contact.isActive,
|
'isActive': contact.isActive,
|
||||||
'rawPacket': contact.rawPacket != null
|
'rawPacket': contact.rawPacket != null
|
||||||
@@ -86,6 +87,7 @@ class ContactStore {
|
|||||||
Contact _fromJson(Map<String, dynamic> json) {
|
Contact _fromJson(Map<String, dynamic> json) {
|
||||||
final lastSeenMs = json['lastSeen'] as int? ?? 0;
|
final lastSeenMs = json['lastSeen'] as int? ?? 0;
|
||||||
final lastMessageMs = json['lastMessageAt'] as int?;
|
final lastMessageMs = json['lastMessageAt'] as int?;
|
||||||
|
final lastModifiedMs = json['lastModified'] as int?;
|
||||||
return Contact(
|
return Contact(
|
||||||
publicKey: Uint8List.fromList(base64Decode(json['publicKey'] as String)),
|
publicKey: Uint8List.fromList(base64Decode(json['publicKey'] as String)),
|
||||||
name: json['name'] as String? ?? 'Unknown',
|
name: json['name'] as String? ?? 'Unknown',
|
||||||
@@ -104,6 +106,9 @@ class ContactStore {
|
|||||||
latitude: (json['latitude'] as num?)?.toDouble(),
|
latitude: (json['latitude'] as num?)?.toDouble(),
|
||||||
longitude: (json['longitude'] as num?)?.toDouble(),
|
longitude: (json['longitude'] as num?)?.toDouble(),
|
||||||
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastSeenMs),
|
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastSeenMs),
|
||||||
|
lastModified: lastModifiedMs == null
|
||||||
|
? null
|
||||||
|
: DateTime.fromMillisecondsSinceEpoch(lastModifiedMs),
|
||||||
lastMessageAt: DateTime.fromMillisecondsSinceEpoch(
|
lastMessageAt: DateTime.fromMillisecondsSinceEpoch(
|
||||||
lastMessageMs ?? lastSeenMs,
|
lastMessageMs ?? lastSeenMs,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user