Prevent users from disconnecting from bluetooth when want config is occuring let the timer do its job, clean up want config timer logic to occur in one place

This commit is contained in:
Garth Vander Houwen 2025-06-17 16:33:11 -07:00
parent f191877376
commit db0119bed9
3 changed files with 65 additions and 68 deletions

View file

@ -29004,18 +29004,18 @@
},
"Standard" : {
"localizations" : {
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Predefinito"
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "סטנדרטי"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Predefinito"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",

View file

@ -59,8 +59,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
private var wantConfigTimer: Timer?
private var wantConfigRetryCount = 0
private let maxWantConfigRetries = 3
private let wantConfigTimeoutInterval: TimeInterval = 6.0
private let maxWantConfigRetries = 2
private let wantConfigTimeoutInterval: TimeInterval = 5.0
// MARK: init
private override init() {
@ -193,7 +193,12 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if self.mqttProxyConnected {
self.mqttManager.mqttClientProxy?.disconnect()
}
self.wantConfigTimer?.invalidate()
self.isWaitingForWantConfigResponse = false
if wantConfigTimer != nil {
self.wantConfigTimer?.invalidate()
}
self.wantConfigTimer = nil
self.wantConfigRetryCount = 0
self.automaticallyReconnect = reconnect
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
self.FROMRADIO_characteristic = nil
@ -506,22 +511,22 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
return success
}
func sendWantConfig() {
isWaitingForWantConfigResponse = true
guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return }
if FROMRADIO_characteristic == nil {
Logger.mesh.error("🚨 \("Unsupported Firmware Version Detected, unable to connect to device.".localized, privacy: .public)")
invalidVersion = true
return
} else {
let nodeName = connectedPeripheral?.peripheral.name ?? "Unknown".localized
let logString = String.localizedStringWithFormat("Issuing Want Config to %@".localized, nodeName)
Logger.mesh.info("🛎️ \(logString, privacy: .public)")
// BLE Characteristics discovered, issue wantConfig
var toRadio: ToRadio = ToRadio()
configNonce = UInt32(NONCE_ONLY_DB)
@ -533,11 +538,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
return
}
connectedPeripheral!.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
// Either Read the config complete value or from num notify value
guard connectedPeripheral != nil else { return }
connectedPeripheral!.peripheral.readValue(for: FROMRADIO_characteristic)
// Start timeout timer
startWantConfigTimeout()
}
@ -546,7 +549,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
private func startWantConfigTimeout() {
// Cancel any existing timer
wantConfigTimer?.invalidate()
// Start new timer
wantConfigTimer = Timer.scheduledTimer(withTimeInterval: wantConfigTimeoutInterval, repeats: false) { [weak self] _ in
self?.handleWantConfigTimeout()
@ -555,9 +557,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
private func handleWantConfigTimeout() {
guard isWaitingForWantConfigResponse else { return }
wantConfigRetryCount += 1
if wantConfigRetryCount < maxWantConfigRetries {
Logger.mesh.warning("⏰ Want Config timeout, retrying... (attempt \(self.wantConfigRetryCount + 1)/\(self.maxWantConfigRetries))")
sendWantConfig()
@ -577,15 +577,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
private func forceDisconnect() {
isWaitingForWantConfigResponse = false
wantConfigTimer?.invalidate()
wantConfigTimer = nil
wantConfigRetryCount = 0
disconnectPeripheral(reconnect: false)
lastConnectionError = "Bluetooth connection timeout, keep your node closer.".localized
lastConnectionError = "Bluetooth connection timeout, keep your node closer or reboot your radio if the problem continues.".localized
Logger.mesh.error("💥 [BLE] Forced disconnect due to Want Config timeout")
}

View file

@ -90,40 +90,7 @@ struct Connect: View {
.foregroundColor(Color.gray)
.padding([.top])
.swipeActions {
Button(role: .destructive) {
if let connectedPeripheral = bleManager.connectedPeripheral,
connectedPeripheral.peripheral.state == .connected {
bleManager.disconnectPeripheral(reconnect: false)
}
} label: {
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
}
}
.contextMenu {
if node != nil {
#if !targetEnvironment(macCatalyst)
Button {
if !liveActivityStarted {
#if canImport(ActivityKit)
Logger.services.info("Start live activity.")
startNodeActivity()
#endif
} else {
#if canImport(ActivityKit)
Logger.services.info("Stop live activity.")
endActivity()
#endif
}
} label: {
Label("Mesh Live Activity", systemImage: liveActivityStarted ? "stop" : "play")
}
#endif
Text("Num: \(String(node!.num))")
Text("Short Name: \(node?.user?.shortName ?? "?")")
Text("Long Name: \(node?.user?.longName?.addingVariationSelectors ?? "Unknown".localized)")
Text("BLE RSSI: \(connectedPeripheral.rssi)")
if bleManager.isSubscribed {
Button(role: .destructive) {
if let connectedPeripheral = bleManager.connectedPeripheral,
connectedPeripheral.peripheral.state == .connected {
@ -132,13 +99,51 @@ struct Connect: View {
} label: {
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
}
Button {
if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!) {
Logger.mesh.error("Shutdown Failed")
}
}
}
.contextMenu {
} label: {
Label("Power Off", systemImage: "power")
if node != nil {
#if !targetEnvironment(macCatalyst)
if bleManager.isSubscribed {
Button {
if !liveActivityStarted {
#if canImport(ActivityKit)
Logger.services.info("Start live activity.")
startNodeActivity()
#endif
} else {
#if canImport(ActivityKit)
Logger.services.info("Stop live activity.")
endActivity()
#endif
}
} label: {
Label("Mesh Live Activity", systemImage: liveActivityStarted ? "stop" : "play")
}
}
#endif
Text("Num: \(String(node!.num))")
Text("Short Name: \(node?.user?.shortName ?? "?")")
Text("Long Name: \(node?.user?.longName?.addingVariationSelectors ?? "Unknown".localized)")
Text("BLE RSSI: \(connectedPeripheral.rssi)")
if bleManager.isSubscribed {
Button(role: .destructive) {
if let connectedPeripheral = bleManager.connectedPeripheral,
connectedPeripheral.peripheral.state == .connected {
bleManager.disconnectPeripheral(reconnect: false)
}
} label: {
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
}
Button {
if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!) {
Logger.mesh.error("Shutdown Failed")
}
} label: {
Label("Power Off", systemImage: "power")
}
}
}
}
@ -154,7 +159,6 @@ struct Connect: View {
}
}
} else {
if bleManager.isConnecting {
HStack {
Image(systemName: "antenna.radiowaves.left.and.right")