fix: increase pre-auth delay to 2s and add one-shot reconnect on auth disconnect

- PRE_AUTH_DELAY bumped from 0.8s to 2.0s — beacons need more stabilization
  time after characteristic discovery before password write
- If disconnect happens during authenticating state, retry connection once
  with full re-discovery instead of immediate failure
- Reset authDisconnectRetried flag on new provision and cleanup
This commit is contained in:
Schwifty 2026-03-22 04:37:52 +00:00
parent 972291e93b
commit fc13986396

View file

@ -160,6 +160,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
private var dxSmartWriteIndex = 0 private var dxSmartWriteIndex = 0
private var provisioningMacAddress: String? private var provisioningMacAddress: String?
private var isTerminating = false // guards against re-entrant disconnect handling private var isTerminating = false // guards against re-entrant disconnect handling
private var authDisconnectRetried = false // one-shot retry if disconnect during auth
// Read config mode // Read config mode
private enum OperationMode { case provisioning, readingConfig } private enum OperationMode { case provisioning, readingConfig }
@ -198,7 +199,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
// Prevention > recovery: generous delays prevent supervision timeouts. // Prevention > recovery: generous delays prevent supervision timeouts.
private static let INTER_COMMAND_DELAY: Double = 0.5 private static let INTER_COMMAND_DELAY: Double = 0.5
private static let HEAVY_COMMAND_DELAY: Double = 1.0 // After frame select/type changes private static let HEAVY_COMMAND_DELAY: Double = 1.0 // After frame select/type changes
private static let PRE_AUTH_DELAY: Double = 0.8 // After discovery, before first auth write private static let PRE_AUTH_DELAY: Double = 2.0 // After discovery, before first auth write (0.8 was too short beacons drop connection)
private static let POST_AUTH_DELAY: Double = 1.5 // After auth, before first write private static let POST_AUTH_DELAY: Double = 1.5 // After auth, before first write
// Readiness gate don't start writing until we've confirmed all 3 chars // Readiness gate don't start writing until we've confirmed all 3 chars
@ -238,6 +239,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
self.dxSmartWriteIndex = 0 self.dxSmartWriteIndex = 0
self.provisioningMacAddress = nil self.provisioningMacAddress = nil
self.isTerminating = false self.isTerminating = false
self.authDisconnectRetried = false
self.awaitingCommandResponse = false self.awaitingCommandResponse = false
self.requiredCharsConfirmed = false self.requiredCharsConfirmed = false
cancelResponseGateTimeout() cancelResponseGateTimeout()
@ -328,6 +330,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
dxSmartWriteIndex = 0 dxSmartWriteIndex = 0
provisioningMacAddress = nil provisioningMacAddress = nil
isTerminating = false isTerminating = false
authDisconnectRetried = false
requiredCharsConfirmed = false requiredCharsConfirmed = false
connectionRetryCount = 0 connectionRetryCount = 0
currentBeacon = nil currentBeacon = nil
@ -1077,6 +1080,26 @@ extension BeaconProvisioner: CBCentralManagerDelegate {
cancelResponseGateTimeout() cancelResponseGateTimeout()
awaitingCommandResponse = false awaitingCommandResponse = false
// If we disconnect during authentication and haven't retried yet,
// reconnect once the beacon may just need a fresh connection with more settling time.
if state == .authenticating && !authDisconnectRetried {
authDisconnectRetried = true
DebugLog.shared.log("BLE: Disconnect during auth — retrying connection once")
progress = "Reconnecting..."
state = .connecting
passwordIndex = 0
dxSmartNotifySubscribed = false
requiredCharsConfirmed = false
characteristics.removeAll()
configService = nil
// Brief pause before reconnecting
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let self = self, let peripheral = self.peripheral else { return }
self.centralManager.connect(peripheral, options: nil)
}
return
}
// Any other disconnect during active work = fail immediately. // Any other disconnect during active work = fail immediately.
// Prevention philosophy: if the connection dropped, something is wrong. // Prevention philosophy: if the connection dropped, something is wrong.
// Don't try to reconnect and resume let the user retry cleanly. // Don't try to reconnect and resume let the user retry cleanly.