Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions xDripG5/BluetoothManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down Expand Up @@ -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

Expand Down
16 changes: 15 additions & 1 deletion xDripG5/PeripheralManager+G5.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}


Expand Down
50 changes: 21 additions & 29 deletions xDripG5/Transmitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)")
Expand All @@ -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)")
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
}
}
Expand All @@ -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 {
Expand Down