From c17292ea4876c5af2b78f5075dcee69f9844a803 Mon Sep 17 00:00:00 2001 From: Sergei Date: Mon, 26 Jan 2026 18:47:17 -0800 Subject: [PATCH] Fix WiFi list duplicates and ignore cancelled operation errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Deduplicate WiFi networks by SSID, keeping strongest signal - Skip empty SSIDs from BLE response - Ignore "Operation was cancelled" (error code 2) which is normal during cleanup when subscription is removed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- services/ble/BLEManager.ts | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/services/ble/BLEManager.ts b/services/ble/BLEManager.ts index 407eaa3..2895645 100644 --- a/services/ble/BLEManager.ts +++ b/services/ble/BLEManager.ts @@ -260,12 +260,23 @@ export class RealBLEManager implements IBLEManager { // Safe reject wrapper to handle null error messages (Android BLE crash fix) const safeReject = (error: any) => { if (responseReceived) return; + + // Extract error code (numeric or string) + const errorCode = error?.errorCode || error?.code || 'BLE_ERROR'; + + // Ignore "Operation was cancelled" (code 2) - this is expected when we cleanup + // This happens when subscription is removed but BLE still tries to send callback + if (errorCode === 2 || errorCode === 'OperationCancelled' || + (error?.message && error.message.includes('cancelled'))) { + console.log('[BLE] Ignoring cancelled operation (normal cleanup)'); + return; + } + responseReceived = true; cleanup(); // Ensure error has a valid message (fixes Android NullPointerException) const errorMessage = error?.message || error?.reason || 'BLE operation failed'; - const errorCode = error?.errorCode || error?.code || 'BLE_ERROR'; reject(new Error(`[${errorCode}] ${errorMessage}`)); }; @@ -366,19 +377,30 @@ export class RealBLEManager implements IBLEManager { } } - const networks: WiFiNetwork[] = []; + // Use Map to deduplicate by SSID, keeping the strongest signal + const networksMap = new Map(); for (let i = 3; i < parts.length; i++) { const [ssid, rssiStr] = parts[i].split(','); if (ssid && rssiStr) { - networks.push({ - ssid: ssid.trim(), - rssi: parseInt(rssiStr, 10), - }); + const trimmedSsid = ssid.trim(); + const rssi = parseInt(rssiStr, 10); + + // Skip empty SSIDs + if (!trimmedSsid) continue; + + // Keep the one with strongest signal if duplicate + const existing = networksMap.get(trimmedSsid); + if (!existing || rssi > existing.rssi) { + networksMap.set(trimmedSsid, { + ssid: trimmedSsid, + rssi: rssi, + }); + } } } - // Sort by signal strength (strongest first) - return networks.sort((a, b) => b.rssi - a.rssi); + // Convert to array and sort by signal strength (strongest first) + return Array.from(networksMap.values()).sort((a, b) => b.rssi - a.rssi); } async setWiFi(deviceId: string, ssid: string, password: string): Promise {