Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ fastlane/screenshots
.DS_Store

/archives
.vscode
31 changes: 30 additions & 1 deletion BLEUnlock/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,36 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa
}

func newDevice(device: Device) {
let menuItem = deviceMenu.addItem(withTitle: menuItemTitle(device: device), action:#selector(selectDevice), keyEquivalent: "")
let title = menuItemTitle(device: device)

// Check for duplicate menu items by title (without RSSI) to catch duplicates that slipped through
let titleWithoutRSSI = title.components(separatedBy: " (").first ?? title
var duplicateUUID: UUID? = nil
for (existingUUID, existingItem) in deviceDict {
if existingUUID != device.uuid {
let existingTitle = existingItem.title
let existingTitleWithoutRSSI = existingTitle.components(separatedBy: " (").first ?? existingTitle
if existingTitleWithoutRSSI == titleWithoutRSSI {
duplicateUUID = existingUUID
break
}
}
}

if let dupUUID = duplicateUUID {
// Find the actual device from BLE and remove it properly
if let dupDevice = ble.devices[dupUUID] {
removeDevice(device: dupDevice)
} else {
// Fallback: just remove from menu and dict
if let menuItem = deviceDict[dupUUID] {
menuItem.menu?.removeItem(menuItem)
}
deviceDict.removeValue(forKey: dupUUID)
}
}

let menuItem = deviceMenu.addItem(withTitle: title, action:#selector(selectDevice), keyEquivalent: "")
deviceDict[device.uuid] = menuItem
if (device.uuid == ble.monitoredUUID) {
menuItem.state = .on
Expand Down
41 changes: 41 additions & 0 deletions BLEUnlock/BLE.swift
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,47 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
device.peripheral = peripheral
device.rssi = rssi
device.advData = advertisementData["kCBAdvDataManufacturerData"] as? Data

// Force resolve device info (MAC address, name, etc.) for de-duplication
if device.macAddr == nil {
if let info = getLEDeviceInfoFromUUID(device.uuid.description) {
device.blName = info.name
device.macAddr = info.macAddr
}
if device.macAddr == nil {
device.macAddr = getMACFromUUID(device.uuid.description)
}
}

// Force evaluation of description to get device name
let deviceDesc = device.description

// De-duplicate by multiple criteria:
// 1. Same MAC address (most reliable)
// 2. Same device description/name (fallback if MAC not available)
var duplicateFound: Device? = nil

if let mac = device.macAddr, !mac.isEmpty {
duplicateFound = devices.values.first(where: { $0.macAddr == mac && $0.macAddr != nil && !$0.macAddr!.isEmpty })
}

// If no MAC-based duplicate, check by device description
if duplicateFound == nil && !deviceDesc.isEmpty {
duplicateFound = devices.values.first(where: {
let otherDesc = $0.description
return otherDesc == deviceDesc && otherDesc != "" && !otherDesc.hasPrefix("iBeacon")
})
}

if let dup = duplicateFound {
// Remove old entry/UI before inserting the new one to avoid duplicates
self.delegate?.removeDevice(device: dup)
if let p = dup.peripheral {
self.centralMgr.cancelPeripheralConnection(p)
}
self.devices.removeValue(forKey: dup.uuid)
}

devices[peripheral.identifier] = device
central.connect(peripheral, options: nil)
delegate?.newDevice(device: device)
Expand Down