From 64e3684209effca86bf9ff70437ed4f92b617345 Mon Sep 17 00:00:00 2001 From: Schwifty Date: Sat, 21 Mar 2026 23:35:15 +0000 Subject: [PATCH] fix: use dedicated retry counter for device info disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device info disconnect handler was sharing connectionRetryCount with the initial connection retry logic. If earlier connection attempts burned through retries, the device info handler had zero retries left and immediately hit "retries exhausted" — causing the "Disconnected while reading device information" error John reported. Now uses a separate deviceInfoRetryCount (max 2) so device info retries are independent of connection retries. Co-Authored-By: Claude Opus 4.6 (1M context) --- PayfritBeacon/BeaconProvisioner.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/PayfritBeacon/BeaconProvisioner.swift b/PayfritBeacon/BeaconProvisioner.swift index 35100d5..71a3e50 100644 --- a/PayfritBeacon/BeaconProvisioner.swift +++ b/PayfritBeacon/BeaconProvisioner.swift @@ -178,7 +178,9 @@ class BeaconProvisioner: NSObject, ObservableObject { // Connection retry state private var connectionRetryCount = 0 + private var deviceInfoRetryCount = 0 private static let MAX_CONNECTION_RETRIES = 3 + private static let MAX_DEVICE_INFO_RETRIES = 2 private var currentBeacon: DiscoveredBeacon? override init() { @@ -219,6 +221,7 @@ class BeaconProvisioner: NSObject, ObservableObject { self.skipDeviceInfoRead = false self.isTerminating = false self.connectionRetryCount = 0 + self.deviceInfoRetryCount = 0 self.currentBeacon = beacon state = .connecting @@ -266,6 +269,7 @@ class BeaconProvisioner: NSObject, ObservableObject { self.dxReadQueryIndex = 0 self.allDiscoveredServices.removeAll() self.connectionRetryCount = 0 + self.deviceInfoRetryCount = 0 self.isTerminating = false self.currentBeacon = beacon self.servicesToExplore.removeAll() @@ -303,6 +307,7 @@ class BeaconProvisioner: NSObject, ObservableObject { skipDeviceInfoRead = false isTerminating = false connectionRetryCount = 0 + deviceInfoRetryCount = 0 currentBeacon = nil state = .idle progress = "" @@ -905,6 +910,7 @@ class BeaconProvisioner: NSObject, ObservableObject { configService = nil characteristics.removeAll() connectionRetryCount = 0 + deviceInfoRetryCount = 0 currentBeacon = nil operationMode = .provisioning state = .idle @@ -1030,11 +1036,11 @@ extension BeaconProvisioner: CBCentralManagerDelegate { awaitingDeviceInfoForProvisioning = false skipDeviceInfoRead = true // on reconnect, go straight to config write - if connectionRetryCount < BeaconProvisioner.MAX_CONNECTION_RETRIES { - connectionRetryCount += 1 - let delay = Double(connectionRetryCount) + if deviceInfoRetryCount < BeaconProvisioner.MAX_DEVICE_INFO_RETRIES { + deviceInfoRetryCount += 1 + let delay = Double(deviceInfoRetryCount) progress = "Reconnecting (skip MAC read)..." - DebugLog.shared.log("BLE: Disconnect during device info read — reconnecting (\(connectionRetryCount)/\(BeaconProvisioner.MAX_CONNECTION_RETRIES)), will skip MAC read") + DebugLog.shared.log("BLE: Disconnect during device info read — reconnecting (\(deviceInfoRetryCount)/\(BeaconProvisioner.MAX_DEVICE_INFO_RETRIES)), will skip MAC read") // Reset BLE state for reconnect dxSmartAuthenticated = false @@ -1056,7 +1062,7 @@ extension BeaconProvisioner: CBCentralManagerDelegate { } return } else { - DebugLog.shared.log("BLE: Disconnect during device info read — max retries exhausted") + 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 }