From fc13986396e8b0ca5df70fe66b6bbea064de8ed9 Mon Sep 17 00:00:00 2001 From: Schwifty Date: Sun, 22 Mar 2026 04:37:52 +0000 Subject: [PATCH] fix: increase pre-auth delay to 2s and add one-shot reconnect on auth disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- PayfritBeacon/BeaconProvisioner.swift | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/PayfritBeacon/BeaconProvisioner.swift b/PayfritBeacon/BeaconProvisioner.swift index 82617ba..64ed005 100644 --- a/PayfritBeacon/BeaconProvisioner.swift +++ b/PayfritBeacon/BeaconProvisioner.swift @@ -160,6 +160,7 @@ class BeaconProvisioner: NSObject, ObservableObject { private var dxSmartWriteIndex = 0 private var provisioningMacAddress: String? private var isTerminating = false // guards against re-entrant disconnect handling + private var authDisconnectRetried = false // one-shot retry if disconnect during auth // Read config mode private enum OperationMode { case provisioning, readingConfig } @@ -198,7 +199,7 @@ class BeaconProvisioner: NSObject, ObservableObject { // Prevention > recovery: generous delays prevent supervision timeouts. 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 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 // 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.provisioningMacAddress = nil self.isTerminating = false + self.authDisconnectRetried = false self.awaitingCommandResponse = false self.requiredCharsConfirmed = false cancelResponseGateTimeout() @@ -328,6 +330,7 @@ class BeaconProvisioner: NSObject, ObservableObject { dxSmartWriteIndex = 0 provisioningMacAddress = nil isTerminating = false + authDisconnectRetried = false requiredCharsConfirmed = false connectionRetryCount = 0 currentBeacon = nil @@ -1077,6 +1080,26 @@ extension BeaconProvisioner: CBCentralManagerDelegate { cancelResponseGateTimeout() 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. // Prevention philosophy: if the connection dropped, something is wrong. // Don't try to reconnect and resume — let the user retry cleanly.