diff --git a/CGMBLEKit/BluetoothManager.swift b/CGMBLEKit/BluetoothManager.swift index 03e43805..4edddacd 100644 --- a/CGMBLEKit/BluetoothManager.swift +++ b/CGMBLEKit/BluetoothManager.swift @@ -45,6 +45,13 @@ protocol BluetoothManagerDelegate: class { /// - manager: The bluetooth manager /// - response: The data received on the backfill characteristic func bluetoothManager(_ manager: BluetoothManager, didReceiveBackfillResponse response: Data) + + /// Informs the delegate that the bluetooth manager received new data in the authentication characteristic + /// + /// - Parameters: + /// - manager: The bluetooth manager + /// - response: The data received on the authentication characteristic + func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, didReceiveAuthenticationResponse response: Data) } @@ -320,12 +327,14 @@ extension BluetoothManager: PeripheralManagerDelegate { } switch CGMServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString.uppercased()) { - case .none, .communication?, .authentication?: + case .none, .communication?: return case .control?: self.delegate?.bluetoothManager(self, didReceiveControlResponse: value) case .backfill?: self.delegate?.bluetoothManager(self, didReceiveBackfillResponse: value) + case .authentication?: + self.delegate?.bluetoothManager(self, peripheralManager: manager, didReceiveAuthenticationResponse: value) } } } diff --git a/CGMBLEKit/BluetoothServices.swift b/CGMBLEKit/BluetoothServices.swift index 4733bc77..85cce9cc 100644 --- a/CGMBLEKit/BluetoothServices.swift +++ b/CGMBLEKit/BluetoothServices.swift @@ -40,15 +40,24 @@ enum DeviceInfoCharacteristicUUID: String, CBUUIDRawValue { enum CGMServiceCharacteristicUUID: String, CBUUIDRawValue { + // Read/Notify case communication = "F8083533-849E-531C-C594-30F1F86A4EA5" + // Write/Indicate case control = "F8083534-849E-531C-C594-30F1F86A4EA5" - // Read/Write/Indicate + + // Write/Indicate case authentication = "F8083535-849E-531C-C594-30F1F86A4EA5" // Read/Write/Notify case backfill = "F8083536-849E-531C-C594-30F1F86A4EA5" + +// // Unknown attribute present on older G6 transmitters +// case unknown1 = "F8083537-849E-531C-C594-30F1F86A4EA5" +// +// // Updated G6 characteristic (read/notify) +// case unknown2 = "F8083538-849E-531C-C594-30F1F86A4EA5" } diff --git a/CGMBLEKit/Messages/AuthChallengeRxMessage.swift b/CGMBLEKit/Messages/AuthChallengeRxMessage.swift index 5ebce938..40904d7f 100644 --- a/CGMBLEKit/Messages/AuthChallengeRxMessage.swift +++ b/CGMBLEKit/Messages/AuthChallengeRxMessage.swift @@ -10,8 +10,8 @@ import Foundation struct AuthChallengeRxMessage: TransmitterRxMessage { - let authenticated: UInt8 - let bonded: UInt8 + let isAuthenticated: Bool + let isBonded: Bool init?(data: Data) { guard data.count >= 3 else { @@ -22,7 +22,7 @@ struct AuthChallengeRxMessage: TransmitterRxMessage { return nil } - authenticated = data[1] - bonded = data[2] + isAuthenticated = data[1] == 0x1 + isBonded = data[2] == 0x1 } } diff --git a/CGMBLEKit/Transmitter.swift b/CGMBLEKit/Transmitter.swift index 154cbe1a..56ab40dd 100644 --- a/CGMBLEKit/Transmitter.swift +++ b/CGMBLEKit/Transmitter.swift @@ -149,9 +149,9 @@ public final class Transmitter: BluetoothManagerDelegate { peripheralManager.perform { (peripheral) in if self.passiveModeEnabled { - self.log.debug("Listening for control commands in passive mode") + self.log.debug("Listening for authentication responses in passive mode") do { - try peripheral.listenToControl() + try peripheral.listenToCharacteristic(.authentication) } catch let error { self.delegateQueue.async { self.delegate?.transmitter(self, didError: error) @@ -162,14 +162,14 @@ public final class Transmitter: BluetoothManagerDelegate { self.log.debug("Authenticating with transmitter") let status = try peripheral.authenticate(id: self.id) - if status.bonded != 0x1 { + if !status.isBonded { self.log.debug("Requesting bond") try peripheral.requestBond() self.log.debug("Bonding request sent. Waiting user to respond.") } - try peripheral.enableNotify(shouldWaitForBond: status.bonded != 0x1) + try peripheral.enableNotify(shouldWaitForBond: !status.isBonded) defer { self.log.debug("Initiating a disconnect") peripheral.disconnect() @@ -319,6 +319,28 @@ public final class Transmitter: BluetoothManagerDelegate { self.backfillBuffer?.append(response) } + + func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, didReceiveAuthenticationResponse response: Data) { + + if let message = AuthChallengeRxMessage(data: response), message.isBonded, message.isAuthenticated { + self.log.debug("Observed authenticated session. enabling notifications for control characteristic.") + peripheralManager.perform { (peripheral) in + do { + try peripheral.listenToCharacteristic(.control) + try peripheral.listenToCharacteristic(.backfill) + } catch let error { + self.log.error("Error trying to enable notifications on control characteristic: %{public}@", String(describing: error)) + } + do { + try peripheral.stopListeningToCharacteristic(.authentication) + } catch let error { + self.log.error("Error trying to disable notifications on authentication characteristic: %{public}@", String(describing: error)) + } + } + } else { + self.log.debug("Ignoring authentication response: %{public}@", response.hexadecimalString) + } + } } @@ -390,7 +412,7 @@ fileprivate extension PeripheralManager { throw TransmitterError.authenticationError("Unable to parse auth status: \(error)") } - guard challengeResponse.authenticated == 1 else { + guard challengeResponse.isAuthenticated else { throw TransmitterError.authenticationError("Transmitter rejected auth challenge") } @@ -478,12 +500,19 @@ fileprivate extension PeripheralManager { } } - func listenToControl() throws { + func listenToCharacteristic(_ characteristic: CGMServiceCharacteristicUUID) throws { do { - try setNotifyValue(true, for: .control) - try setNotifyValue(true, for: .backfill) + try setNotifyValue(true, for: characteristic) } catch let error { - throw TransmitterError.controlError("Error enabling notification: \(error)") + throw TransmitterError.controlError("Error enabling notification for \(characteristic): \(error)") + } + } + + func stopListeningToCharacteristic(_ characteristic: CGMServiceCharacteristicUUID) throws { + do { + try setNotifyValue(false, for: characteristic) + } catch let error { + throw TransmitterError.controlError("Error disabling notification for \(characteristic): \(error)") } } }