Compare commits
No commits in common. "b88dded928cbcaac83215c4363d99824a358573d" and "6eaccb6bf602f625d4129e486f550d1499037b9c" have entirely different histories.
b88dded928
...
6eaccb6bf6
3 changed files with 27 additions and 41 deletions
|
|
@ -448,18 +448,10 @@ extension DXSmartProvisioner: CBPeripheralDelegate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// For command writes (FFE1/FFE2): the .withResponse write confirmation
|
// Handle write errors for command writes
|
||||||
// IS the ACK. Some commands (e.g. 0x61 Frame1_DevInfo) don't send a
|
if let error, let cont = responseContinuation {
|
||||||
// separate FFE1 notification, so we must resolve here on success too.
|
|
||||||
// If a notification also arrives later, responseContinuation will already
|
|
||||||
// be nil — harmless.
|
|
||||||
if let cont = responseContinuation {
|
|
||||||
responseContinuation = nil
|
responseContinuation = nil
|
||||||
if let error {
|
cont.resume(throwing: error)
|
||||||
cont.resume(throwing: error)
|
|
||||||
} else {
|
|
||||||
cont.resume(returning: Data())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ final class BLEManager: NSObject, ObservableObject {
|
||||||
])
|
])
|
||||||
|
|
||||||
scanTimer = Timer.scheduledTimer(withTimeInterval: duration, repeats: false) { [weak self] _ in
|
scanTimer = Timer.scheduledTimer(withTimeInterval: duration, repeats: false) { [weak self] _ in
|
||||||
DispatchQueue.main.async {
|
Task { @MainActor in
|
||||||
self?.stopScan()
|
self?.stopScan()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -235,27 +235,26 @@ final class BLEManager: NSObject, ObservableObject {
|
||||||
extension BLEManager: CBCentralManagerDelegate {
|
extension BLEManager: CBCentralManagerDelegate {
|
||||||
|
|
||||||
nonisolated func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
nonisolated func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||||
let state = central.state
|
Task { @MainActor in
|
||||||
DispatchQueue.main.async { [weak self] in
|
bluetoothState = central.state
|
||||||
self?.bluetoothState = state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
nonisolated func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
||||||
DispatchQueue.main.async { [weak self] in
|
Task { @MainActor in
|
||||||
self?.onPeripheralConnected?(peripheral)
|
onPeripheralConnected?(peripheral)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
nonisolated func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
||||||
DispatchQueue.main.async { [weak self] in
|
Task { @MainActor in
|
||||||
self?.onPeripheralFailedToConnect?(peripheral, error)
|
onPeripheralFailedToConnect?(peripheral, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
|
nonisolated func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
|
||||||
DispatchQueue.main.async { [weak self] in
|
Task { @MainActor in
|
||||||
self?.onPeripheralDisconnected?(peripheral, error)
|
onPeripheralDisconnected?(peripheral, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,18 +264,13 @@ extension BLEManager: CBCentralManagerDelegate {
|
||||||
advertisementData: [String: Any],
|
advertisementData: [String: Any],
|
||||||
rssi RSSI: NSNumber
|
rssi RSSI: NSNumber
|
||||||
) {
|
) {
|
||||||
// Capture values in nonisolated context before hopping to main
|
Task { @MainActor in
|
||||||
let name = peripheral.name ?? advertisementData[CBAdvertisementDataLocalNameKey] as? String ?? ""
|
let name = peripheral.name ?? advertisementData[CBAdvertisementDataLocalNameKey] as? String ?? ""
|
||||||
let serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]
|
let serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]
|
||||||
let mfgData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
|
let mfgData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
|
||||||
let isConnectable = advertisementData[CBAdvertisementDataIsConnectable] as? Bool ?? false
|
let isConnectable = advertisementData[CBAdvertisementDataIsConnectable] as? Bool ?? false
|
||||||
let peripheralId = peripheral.identifier
|
|
||||||
let rssiValue = RSSI.intValue
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
let type = detectBeaconType(name: name, serviceUUIDs: serviceUUIDs, manufacturerData: mfgData)
|
||||||
guard let self else { return }
|
|
||||||
|
|
||||||
let type = self.detectBeaconType(name: name, serviceUUIDs: serviceUUIDs, manufacturerData: mfgData)
|
|
||||||
|
|
||||||
// Match Android behavior (lines 164-169):
|
// Match Android behavior (lines 164-169):
|
||||||
// Include devices that have a recognized type, OR
|
// Include devices that have a recognized type, OR
|
||||||
|
|
@ -284,31 +278,31 @@ extension BLEManager: CBCentralManagerDelegate {
|
||||||
// are connectable with a name (potential configurable beacon)
|
// are connectable with a name (potential configurable beacon)
|
||||||
if type == .unknown {
|
if type == .unknown {
|
||||||
let hasName = !name.isEmpty
|
let hasName = !name.isEmpty
|
||||||
let hasIBeaconData = mfgData.flatMap { self.parseIBeaconData($0) } != nil
|
let hasIBeaconData = mfgData.flatMap { parseIBeaconData($0) } != nil
|
||||||
if !hasIBeaconData && !(isConnectable && hasName) {
|
if !hasIBeaconData && !(isConnectable && hasName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let idx = self.discoveredBeacons.firstIndex(where: { $0.id == peripheralId }) {
|
if let idx = discoveredBeacons.firstIndex(where: { $0.id == peripheral.identifier }) {
|
||||||
// Update existing
|
// Update existing
|
||||||
self.discoveredBeacons[idx].rssi = rssiValue
|
discoveredBeacons[idx].rssi = RSSI.intValue
|
||||||
self.discoveredBeacons[idx].lastSeen = Date()
|
discoveredBeacons[idx].lastSeen = Date()
|
||||||
} else {
|
} else {
|
||||||
// New beacon
|
// New beacon
|
||||||
let beacon = DiscoveredBeacon(
|
let beacon = DiscoveredBeacon(
|
||||||
id: peripheralId,
|
id: peripheral.identifier,
|
||||||
peripheral: peripheral,
|
peripheral: peripheral,
|
||||||
name: name,
|
name: name,
|
||||||
type: type,
|
type: type,
|
||||||
rssi: rssiValue,
|
rssi: RSSI.intValue,
|
||||||
lastSeen: Date()
|
lastSeen: Date()
|
||||||
)
|
)
|
||||||
self.discoveredBeacons.append(beacon)
|
discoveredBeacons.append(beacon)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep list sorted by RSSI (strongest/closest first)
|
// Keep list sorted by RSSI (strongest/closest first)
|
||||||
self.discoveredBeacons.sort { $0.rssi > $1.rssi }
|
discoveredBeacons.sort { $0.rssi > $1.rssi }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -631,7 +631,7 @@ struct ScanView: View {
|
||||||
// Monitor for unexpected disconnects during provisioning
|
// Monitor for unexpected disconnects during provisioning
|
||||||
bleManager.onPeripheralDisconnected = { [weak provisionLog] peripheral, error in
|
bleManager.onPeripheralDisconnected = { [weak provisionLog] peripheral, error in
|
||||||
if peripheral.identifier == beacon.peripheral.identifier {
|
if peripheral.identifier == beacon.peripheral.identifier {
|
||||||
DispatchQueue.main.async { [weak self] in
|
Task { @MainActor [weak self] in
|
||||||
let reason = error?.localizedDescription ?? "beacon timed out"
|
let reason = error?.localizedDescription ?? "beacon timed out"
|
||||||
provisionLog?.log("disconnect", "Unexpected disconnect: \(reason)", isError: true)
|
provisionLog?.log("disconnect", "Unexpected disconnect: \(reason)", isError: true)
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue