From 67d8e1c90649e888b2a4682f589c92a4ab0c4cab Mon Sep 17 00:00:00 2001 From: Janez T Date: Wed, 18 Mar 2026 14:24:38 +0100 Subject: [PATCH] fix: Deduplicate telemetry from 0x8B + 0x8C double delivery When firmware sends telemetry via both pushTelemetryResponse (0x8B) and pushBinaryResponse (0x8C), the same LPP data was parsed and applied twice, causing duplicate sensor entries. Added hash-based dedup: same payload for same contact within 2s is skipped. Also added minimum length check for binary responses. --- lib/providers/app_provider.dart | 11 +++++++---- lib/providers/contacts_provider.dart | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/providers/app_provider.dart b/lib/providers/app_provider.dart index 6e536b6..80ae80c 100644 --- a/lib/providers/app_provider.dart +++ b/lib/providers/app_provider.dart @@ -1338,11 +1338,14 @@ class AppProvider with ChangeNotifier { return; } debugPrint( - '📊 [AppProvider] Binary response (0x8C) received - updating contact telemetry', + '📊 [AppProvider] Binary response (0x8C tag=$tag) received', ); - // Binary response tag 0 = telemetry data (Cayenne LPP format) - // Other tags may be used for different data types in the future - contactsProvider.updateTelemetry(publicKeyPrefix, responseData); + // Binary responses carry Cayenne LPP telemetry data. + // The data starts with a channel byte — valid LPP always has at least + // 3 bytes (channel + type + value). Skip clearly non-telemetry payloads. + if (responseData.length >= 3) { + contactsProvider.updateTelemetry(publicKeyPrefix, responseData); + } }; // When raw binary data is received (PUSH_CODE_RAW_DATA 0x84) diff --git a/lib/providers/contacts_provider.dart b/lib/providers/contacts_provider.dart index 15039f0..c43af78 100644 --- a/lib/providers/contacts_provider.dart +++ b/lib/providers/contacts_provider.dart @@ -926,6 +926,10 @@ class ContactsProvider with ChangeNotifier { } /// Update contact telemetry + // Dedup: track last telemetry data hash per contact to avoid processing + // the same payload twice (0x8B + 0x8C can both fire for the same data). + final Map _lastTelemetryHash = {}; + void updateTelemetry(Uint8List publicKeyPrefix, Uint8List lppData) { debugPrint('📊 [ContactsProvider] updateTelemetry() called'); debugPrint( @@ -933,6 +937,21 @@ class ContactsProvider with ChangeNotifier { ); debugPrint(' LPP data size: ${lppData.length} bytes'); + // Deduplicate: same data for same contact within 2 seconds = skip + final prefixHex = publicKeyPrefix + .map((b) => b.toRadixString(16).padLeft(2, '0')) + .join(); + final dataHash = lppData.fold(0, (h, b) => h * 31 + b); + final now = DateTime.now(); + final last = _lastTelemetryHash[prefixHex]; + if (last != null && + last.$1 == dataHash && + now.difference(last.$2).inSeconds < 2) { + debugPrint(' ⏭️ Duplicate telemetry payload, skipping'); + return; + } + _lastTelemetryHash[prefixHex] = (dataHash, now); + // Find contact by public key prefix final contact = _findContactByPrefix(publicKeyPrefix); if (contact == null) {