diff --git a/xDripG5/BluetoothManager.swift b/xDripG5/BluetoothManager.swift index 9333b2c2..d0d997e5 100644 --- a/xDripG5/BluetoothManager.swift +++ b/xDripG5/BluetoothManager.swift @@ -95,18 +95,18 @@ class BluetoothManager: NSObject { return } - var connectOptions: [String: Any] = [:] + let currentState = peripheral?.state ?? .disconnected + guard currentState == .disconnected else { + return + } - #if swift(>=4.0.3) + var connectOptions: [String: Any] = [:] if #available(iOS 11.2, watchOS 4.1, *), delay > 0 { connectOptions[CBConnectPeripheralOptionStartDelayKey] = delay } - #else - connectOptions[""] = 0 - #endif if let peripheralID = self.peripheral?.identifier, let peripheral = manager.retrievePeripherals(withIdentifiers: [peripheralID]).first { - log.info("Re-connecting to known peripheral %{public}@ in %zds", peripheral.identifier.uuidString, delay) + log.info("Re-connecting to known peripheral %{public}@ in %.1fs", peripheral.identifier.uuidString, delay) self.peripheral = peripheral self.manager.connect(peripheral, options: connectOptions) } else if let peripheral = manager.retrieveConnectedPeripherals(withServices: [ @@ -195,6 +195,7 @@ extension BluetoothManager: CBCentralManagerDelegate { } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { + log.info("%{public}@: %{public}@", #function, peripheral) if delegate == nil || delegate!.bluetoothManager(self, shouldConnectPeripheral: peripheral) { self.peripheral = peripheral diff --git a/xDripG5/PeripheralManager+G5.swift b/xDripG5/PeripheralManager+G5.swift index 915d309c..75a84df8 100644 --- a/xDripG5/PeripheralManager+G5.swift +++ b/xDripG5/PeripheralManager+G5.swift @@ -43,11 +43,12 @@ extension PeripheralManager { return value } + /// - Throws: PeripheralManagerError func writeValue(_ value: Data, for characteristicUUID: CGMServiceCharacteristicUUID, type: CBCharacteristicWriteType = .withResponse, timeout: TimeInterval = 2, - expectingFirstByte firstByte: UInt8? = nil) throws -> Data + expectingFirstByte firstByte: UInt8?) throws -> Data { guard let characteristic = peripheral.getCharacteristicWithUUID(characteristicUUID) else { throw PeripheralManagerError.unknownCharacteristic @@ -72,6 +73,19 @@ extension PeripheralManager { return value } + + /// - Throws: PeripheralManagerError + func writeValue(_ value: Data, + for characteristicUUID: CGMServiceCharacteristicUUID, + type: CBCharacteristicWriteType = .withResponse, + timeout: TimeInterval = 2) throws + { + guard let characteristic = peripheral.getCharacteristicWithUUID(characteristicUUID) else { + throw PeripheralManagerError.unknownCharacteristic + } + + try writeValue(value, for: characteristic, type: type, timeout: timeout) + } } diff --git a/xDripG5/Transmitter.swift b/xDripG5/Transmitter.swift index 1b48956c..a238bfd8 100644 --- a/xDripG5/Transmitter.swift +++ b/xDripG5/Transmitter.swift @@ -105,14 +105,19 @@ public final class Transmitter: BluetoothManagerDelegate { } } else { do { - try peripheral.authenticate(id: self.id) - let glucose = try peripheral.control() - self.delegateQueue.async { - self.delegate?.transmitter(self, didRead: glucose) + let status = try peripheral.authenticate(id: self.id) + + if status.bonded != 0x1 { + try peripheral.requestBond() + + self.log.info("Bonding request sent. Waiting user to respond.") + } else { + let glucose = try peripheral.control() + self.delegateQueue.async { + self.delegate?.transmitter(self, didRead: glucose) + } } } catch let error { - manager.disconnect() - self.delegateQueue.async { self.delegate?.transmitter(self, didError: error) } @@ -196,12 +201,12 @@ struct TransmitterID { // MARK: - Helpers fileprivate extension PeripheralManager { - func authenticate(id: TransmitterID) throws { + fileprivate func authenticate(id: TransmitterID) throws -> AuthStatusRxMessage { let authMessage = AuthRequestTxMessage() let authRequestRx: Data do { - _ = try writeValue(authMessage.data, for: .authentication) + try writeValue(authMessage.data, for: .authentication) authRequestRx = try readValue(for: .authentication, expectingFirstByte: AuthChallengeRxMessage.opcode) } catch let error { throw TransmitterError.authenticationError("Error writing transmitter challenge: \(error)") @@ -221,7 +226,7 @@ fileprivate extension PeripheralManager { let statusData: Data do { - _ = try writeValue(AuthChallengeTxMessage(challengeHash: challengeHash).data, for: .authentication) + try writeValue(AuthChallengeTxMessage(challengeHash: challengeHash).data, for: .authentication) statusData = try readValue(for: .authentication, expectingFirstByte: AuthStatusRxMessage.opcode) } catch let error { throw TransmitterError.authenticationError("Error writing challenge response: \(error)") @@ -235,37 +240,24 @@ fileprivate extension PeripheralManager { throw TransmitterError.authenticationError("Transmitter rejected auth challenge") } - if status.bonded != 0x1 { - try bond() - } + return status } - private func bond() throws { + fileprivate func requestBond() throws { do { - _ = try writeValue(KeepAliveTxMessage(time: 25).data, for: .authentication) + try writeValue(KeepAliveTxMessage(time: 25).data, for: .authentication) } catch let error { throw TransmitterError.authenticationError("Error writing keep-alive for bond: \(error)") } - let data: Data do { - // Wait for the OS dialog to pop-up before continuing. - _ = try writeValue(BondRequestTxMessage().data, for: .authentication) - data = try readValue(for: .authentication, timeout: 15, expectingFirstByte: AuthStatusRxMessage.opcode) + try writeValue(BondRequestTxMessage().data, for: .authentication) } catch let error { throw TransmitterError.authenticationError("Error writing bond request: \(error)") } - - guard let response = AuthStatusRxMessage(data: data) else { - throw TransmitterError.authenticationError("Unable to parse auth status: \(data)") - } - - guard response.bonded == 0x1 else { - throw TransmitterError.authenticationError("Transmitter failed to bond") - } } - func control() throws -> Glucose { + fileprivate func control() throws -> Glucose { do { try setNotifyValue(true, for: .control) } catch let error { @@ -299,7 +291,7 @@ fileprivate extension PeripheralManager { defer { do { try setNotifyValue(false, for: .control) - _ = try writeValue(DisconnectTxMessage().data, for: .control) + try writeValue(DisconnectTxMessage().data, for: .control) } catch { } } @@ -308,7 +300,7 @@ fileprivate extension PeripheralManager { return Glucose(glucoseMessage: glucoseMessage, timeMessage: timeMessage, activationDate: activationDate) } - func listenToControl() throws { + fileprivate func listenToControl() throws { do { try setNotifyValue(true, for: .control) } catch let error {