fix: skip device info read during provisioning to prevent disconnect failures
The DX-Smart 0x30 device info query consistently causes beacon BLE disconnects, which exhausted the retry counter and failed provisioning. The MAC address is optional — API falls back to iBeacon UUID as hardware ID. Device info read is still available in readConfig/check mode where it doesn't block provisioning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
720c560760
commit
4c1c1adf85
1 changed files with 28 additions and 62 deletions
|
|
@ -413,37 +413,14 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read device info (MAC address) before writing config
|
/// Read device info (MAC address) before writing config
|
||||||
|
/// NOTE: Device info read (0x30 query) is SKIPPED during provisioning because DX-Smart
|
||||||
|
/// beacons frequently drop the BLE connection during this optional query, causing
|
||||||
|
/// provisioning to fail. The MAC address is nice-to-have but not required — the API
|
||||||
|
/// falls back to iBeacon UUID as hardware ID when MAC is unavailable.
|
||||||
|
/// Device info is still read in readConfig/check mode where it doesn't block provisioning.
|
||||||
private func dxSmartReadDeviceInfoBeforeWrite() {
|
private func dxSmartReadDeviceInfoBeforeWrite() {
|
||||||
// If we previously disconnected during device info read, skip it entirely
|
DebugLog.shared.log("BLE: Skipping device info read — proceeding directly to config write (MAC is optional)")
|
||||||
if skipDeviceInfoRead {
|
dxSmartWriteConfig()
|
||||||
DebugLog.shared.log("BLE: Skipping device info read (reconnect after previous disconnect)")
|
|
||||||
skipDeviceInfoRead = false
|
|
||||||
dxSmartWriteConfig()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let commandChar = characteristics[BeaconProvisioner.DXSMART_COMMAND_CHAR] else {
|
|
||||||
DebugLog.shared.log("BLE: FFE2 not found, proceeding without MAC")
|
|
||||||
dxSmartWriteConfig()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
progress = "Reading device info..."
|
|
||||||
awaitingDeviceInfoForProvisioning = true
|
|
||||||
responseBuffer.removeAll()
|
|
||||||
|
|
||||||
// Send device info query (0x30)
|
|
||||||
let packet = buildDXPacket(cmd: .deviceInfo, data: [])
|
|
||||||
DebugLog.shared.log("BLE: Sending device info query to get MAC address")
|
|
||||||
peripheral?.writeValue(packet, for: commandChar, type: .withResponse)
|
|
||||||
|
|
||||||
// Timeout after 3 seconds - proceed with write even if no MAC
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [weak self] in
|
|
||||||
guard let self = self, self.awaitingDeviceInfoForProvisioning else { return }
|
|
||||||
DebugLog.shared.log("BLE: Device info timeout, proceeding without MAC")
|
|
||||||
self.awaitingDeviceInfoForProvisioning = false
|
|
||||||
self.dxSmartWriteConfig()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the full command sequence and start writing
|
/// Build the full command sequence and start writing
|
||||||
|
|
@ -1029,43 +1006,32 @@ extension BeaconProvisioner: CBCentralManagerDelegate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect during device info read (post-auth, pre-write) — beacon dropped
|
// NOTE: Device info read is now skipped entirely during provisioning
|
||||||
// connection during the optional MAC address query. Instead of failing, reconnect
|
// (see dxSmartReadDeviceInfoBeforeWrite). This guard is kept as a safety net
|
||||||
// and skip the device info step (MAC is nice-to-have, not required).
|
// in case device info is re-enabled in the future.
|
||||||
if state == .authenticating && awaitingDeviceInfoForProvisioning && dxSmartAuthenticated {
|
if state == .authenticating && awaitingDeviceInfoForProvisioning && dxSmartAuthenticated {
|
||||||
|
DebugLog.shared.log("BLE: Disconnect during device info read — proceeding without MAC (device info is optional)")
|
||||||
awaitingDeviceInfoForProvisioning = false
|
awaitingDeviceInfoForProvisioning = false
|
||||||
skipDeviceInfoRead = true // on reconnect, go straight to config write
|
skipDeviceInfoRead = true
|
||||||
|
|
||||||
if deviceInfoRetryCount < BeaconProvisioner.MAX_DEVICE_INFO_RETRIES {
|
// Reconnect and skip device info on next attempt
|
||||||
deviceInfoRetryCount += 1
|
dxSmartAuthenticated = false
|
||||||
let delay = Double(deviceInfoRetryCount)
|
dxSmartNotifySubscribed = false
|
||||||
progress = "Reconnecting (skip MAC read)..."
|
dxSmartCommandQueue.removeAll()
|
||||||
DebugLog.shared.log("BLE: Disconnect during device info read — reconnecting (\(deviceInfoRetryCount)/\(BeaconProvisioner.MAX_DEVICE_INFO_RETRIES)), will skip MAC read")
|
dxSmartWriteIndex = 0
|
||||||
|
characteristics.removeAll()
|
||||||
|
responseBuffer.removeAll()
|
||||||
|
state = .connecting
|
||||||
|
|
||||||
// Reset BLE state for reconnect
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
||||||
dxSmartAuthenticated = false
|
guard let self = self, let beacon = self.currentBeacon else { return }
|
||||||
dxSmartNotifySubscribed = false
|
guard self.state == .connecting else { return }
|
||||||
dxSmartCommandQueue.removeAll()
|
let resolvedPeripheral = self.resolvePeripheral(beacon)
|
||||||
dxSmartWriteIndex = 0
|
self.peripheral = resolvedPeripheral
|
||||||
characteristics.removeAll()
|
resolvedPeripheral.delegate = self
|
||||||
responseBuffer.removeAll()
|
self.centralManager.connect(resolvedPeripheral, options: nil)
|
||||||
state = .connecting
|
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
|
|
||||||
guard let self = self, let beacon = self.currentBeacon else { return }
|
|
||||||
guard self.state == .connecting else { return }
|
|
||||||
|
|
||||||
let resolvedPeripheral = self.resolvePeripheral(beacon)
|
|
||||||
self.peripheral = resolvedPeripheral
|
|
||||||
resolvedPeripheral.delegate = self
|
|
||||||
self.centralManager.connect(resolvedPeripheral, options: nil)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
DebugLog.shared.log("BLE: Disconnect during device info read — max device info retries exhausted (\(deviceInfoRetryCount)/\(BeaconProvisioner.MAX_DEVICE_INFO_RETRIES))")
|
|
||||||
fail("Disconnected while reading device information (retries exhausted)", code: .disconnected)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other disconnects are unexpected
|
// All other disconnects are unexpected
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue