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
10 changes: 9 additions & 1 deletion CGMBLEKit Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import CoreBluetooth

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, TransmitterDelegate {

var window: UIWindow?

static var sharedDelegate: AppDelegate {
Expand Down Expand Up @@ -105,4 +105,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, TransmitterDelegate {
}
}
}

func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose]) {
DispatchQueue.main.async {
if let vc = self.window?.rootViewController as? TransmitterDelegate {
vc.transmitter(transmitter, didReadBackfill: glucose)
}
}
}
}
9 changes: 8 additions & 1 deletion CGMBLEKit Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class ViewController: UIViewController, TransmitterDelegate, UITextFieldDelegate
}

func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) {
let unit = HKUnit.milligramsPerDeciliter()
let unit = HKUnit.milligramsPerDeciliter
if let value = glucose.glucose?.doubleValue(for: unit) {
titleLabel.text = "\(value) \(unit.unitString)"
} else {
Expand All @@ -123,6 +123,13 @@ class ViewController: UIViewController, TransmitterDelegate, UITextFieldDelegate
titleLabel.text = NSLocalizedString("Unknown Data", comment: "Title displayed during unknown data response")
subtitleLabel.text = data.hexadecimalString
}

func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose]) {
titleLabel.text = NSLocalizedString("Backfill", comment: "Title displayed during backfill response")
subtitleLabel.text = String(describing: glucose.map { $0.glucose })
}


}


Expand Down
4 changes: 2 additions & 2 deletions xDripG5/HKUnit.swift → Common/HKUnit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import HealthKit


extension HKUnit {
static func milligramsPerDeciliter() -> HKUnit {
static let milligramsPerDeciliter: HKUnit = {
return HKUnit.gramUnit(with: .milli).unitDivided(by: HKUnit.literUnit(with: .deci))
}
}()
}
18 changes: 13 additions & 5 deletions xDripG5.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
431CE7631F8EEF6D00255374 /* CBPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7621F8EEF6D00255374 /* CBPeripheral.swift */; };
431CE7671F91D0B300255374 /* PeripheralManager+G5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7661F91D0B300255374 /* PeripheralManager+G5.swift */; };
4323115F1EFC870300B95E62 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4323115E1EFC870300B95E62 /* OSLog.swift */; };
433BC81B205CB64A000B1200 /* GlucoseBackfillMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433BC81A205CB64A000B1200 /* GlucoseBackfillMessage.swift */; };
433BC81D205CBB16000B1200 /* GlucoseBackfillMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433BC81C205CBB16000B1200 /* GlucoseBackfillMessageTests.swift */; };
43460F88200B30D10030C0E3 /* TransmitterIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43460F87200B30D10030C0E3 /* TransmitterIDTests.swift */; };
435535D41FB2C1B000CE5A23 /* PeripheralManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435535D31FB2C1B000CE5A23 /* PeripheralManagerError.swift */; };
437AFEFA2038EC43008C4892 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437AFEF92038EC43008C4892 /* AppDelegate.swift */; };
437AFEFC2038EC43008C4892 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437AFEFB2038EC43008C4892 /* ViewController.swift */; };
437AFF012038EC43008C4892 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 437AFF002038EC43008C4892 /* Assets.xcassets */; };
437AFF042038EC43008C4892 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 437AFF022038EC43008C4892 /* LaunchScreen.storyboard */; };
437AFF0F2038ED71008C4892 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437AFF0E2038ED71008C4892 /* HKUnit.swift */; };
437AFF122038EDEC008C4892 /* xDripG5.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43CABDF31C3506F100005705 /* xDripG5.framework */; };
437AFF132038EDEC008C4892 /* xDripG5.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 43CABDF31C3506F100005705 /* xDripG5.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
437AFF182038EDF9008C4892 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43E3978C1D566AEA0028E321 /* HealthKit.framework */; };
Expand Down Expand Up @@ -61,6 +62,8 @@
43E397911D5692080028E321 /* GlucoseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E397901D5692080028E321 /* GlucoseTests.swift */; };
43E397931D56950C0028E321 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E397921D56950C0028E321 /* HKUnit.swift */; };
43E4B1F21F8AF9790038823E /* PeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E4B1F11F8AF9790038823E /* PeripheralManager.swift */; };
43E5292C2060C4FA00ACEB3B /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E397921D56950C0028E321 /* HKUnit.swift */; };
43E5292D2060C50800ACEB3B /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E397921D56950C0028E321 /* HKUnit.swift */; };
43EEA7111D14DC0800CBBDA0 /* AESCrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 43EEA70F1D14DC0800CBBDA0 /* AESCrypt.h */; settings = {ATTRIBUTES = (Public, ); }; };
43EEA7121D14DC0800CBBDA0 /* AESCrypt.m in Sources */ = {isa = PBXBuildFile; fileRef = 43EEA7101D14DC0800CBBDA0 /* AESCrypt.m */; };
43F82BCC1D035AA4006F5DD7 /* TransmitterTimeRxMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F82BCB1D035AA4006F5DD7 /* TransmitterTimeRxMessageTests.swift */; };
Expand Down Expand Up @@ -117,6 +120,8 @@
431CE7621F8EEF6D00255374 /* CBPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBPeripheral.swift; sourceTree = "<group>"; };
431CE7661F91D0B300255374 /* PeripheralManager+G5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PeripheralManager+G5.swift"; sourceTree = "<group>"; };
4323115E1EFC870300B95E62 /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
433BC81A205CB64A000B1200 /* GlucoseBackfillMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseBackfillMessage.swift; sourceTree = "<group>"; };
433BC81C205CBB16000B1200 /* GlucoseBackfillMessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseBackfillMessageTests.swift; sourceTree = "<group>"; };
43460F87200B30D10030C0E3 /* TransmitterIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransmitterIDTests.swift; sourceTree = "<group>"; };
435535D31FB2C1B000CE5A23 /* PeripheralManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralManagerError.swift; sourceTree = "<group>"; };
437AFEF72038EC43008C4892 /* CGMBLEKit Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "CGMBLEKit Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand All @@ -125,7 +130,6 @@
437AFF002038EC43008C4892 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
437AFF032038EC43008C4892 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
437AFF052038EC43008C4892 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
437AFF0E2038ED71008C4892 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HKUnit.swift; path = xDripG5/HKUnit.swift; sourceTree = SOURCE_ROOT; };
43846AC51D8F896C00799272 /* CalibrationDataRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationDataRxMessage.swift; sourceTree = "<group>"; };
43846AC71D8F89BE00799272 /* CalibrationDataRxMessageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationDataRxMessageTests.swift; sourceTree = "<group>"; };
43880F971D9E19FC009061A8 /* TransmitterVersionRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterVersionRxMessage.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -212,7 +216,6 @@
children = (
C1CD8B0A203931AD00A8F498 /* NSUserDefaults.swift */,
437AFEF92038EC43008C4892 /* AppDelegate.swift */,
437AFF0E2038ED71008C4892 /* HKUnit.swift */,
437AFEFB2038EC43008C4892 /* ViewController.swift */,
437AFF002038EC43008C4892 /* Assets.xcassets */,
C19084B9203932BD00AA47F3 /* Main.storyboard */,
Expand Down Expand Up @@ -262,7 +265,6 @@
431CE7621F8EEF6D00255374 /* CBPeripheral.swift */,
43E3978A1D5668BD0028E321 /* CalibrationState.swift */,
43E3978E1D566B170028E321 /* Glucose.swift */,
43E397921D56950C0028E321 /* HKUnit.swift */,
43CABDF81C3506F100005705 /* Info.plist */,
430D64C41CB7846A00FCA750 /* NSData+CRC.swift */,
4323115E1EFC870300B95E62 /* OSLog.swift */,
Expand All @@ -282,6 +284,7 @@
isa = PBXGroup;
children = (
43846AC71D8F89BE00799272 /* CalibrationDataRxMessageTests.swift */,
433BC81C205CBB16000B1200 /* GlucoseBackfillMessageTests.swift */,
43DC87C11C8B520F005BC30D /* GlucoseRxMessageTests.swift */,
43E397901D5692080028E321 /* GlucoseTests.swift */,
43CABE041C3506F100005705 /* Info.plist */,
Expand All @@ -307,6 +310,7 @@
43846AC51D8F896C00799272 /* CalibrationDataRxMessage.swift */,
43CABE1C1C350B3D00005705 /* DisconnectTxMessage.swift */,
43CE7CC71CA73AEB003CC1B0 /* FirmwareVersionTxMessage.swift */,
433BC81A205CB64A000B1200 /* GlucoseBackfillMessage.swift */,
43CE7CD31CA73CE8003CC1B0 /* GlucoseHistoryTxMessage.swift */,
43CABE1D1C350B3D00005705 /* GlucoseRxMessage.swift */,
43CABE1E1C350B3D00005705 /* GlucoseTxMessage.swift */,
Expand All @@ -328,6 +332,7 @@
isa = PBXGroup;
children = (
43DC87BF1C8B509B005BC30D /* Data.swift */,
43E397921D56950C0028E321 /* HKUnit.swift */,
);
path = Common;
sourceTree = "<group>";
Expand Down Expand Up @@ -484,11 +489,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
43E5292C2060C4FA00ACEB3B /* HKUnit.swift in Sources */,
C1CD8B0C203931AD00A8F498 /* NSUserDefaults.swift in Sources */,
43D140CE2047AA930032346D /* Data.swift in Sources */,
437AFEFC2038EC43008C4892 /* ViewController.swift in Sources */,
437AFEFA2038EC43008C4892 /* AppDelegate.swift in Sources */,
437AFF0F2038ED71008C4892 /* HKUnit.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -518,6 +523,7 @@
43CE7CCC1CA73BCC003CC1B0 /* BatteryStatusTxMessage.swift in Sources */,
43EEA7121D14DC0800CBBDA0 /* AESCrypt.m in Sources */,
43CE7CCE1CA73C22003CC1B0 /* SessionStartTxMessage.swift in Sources */,
433BC81B205CB64A000B1200 /* GlucoseBackfillMessage.swift in Sources */,
43CABE2E1C350B3D00005705 /* TransmitterTimeTxMessage.swift in Sources */,
43880F981D9E19FC009061A8 /* TransmitterVersionRxMessage.swift in Sources */,
43CABE2C1C350B3D00005705 /* TransmitterMessage.swift in Sources */,
Expand All @@ -544,6 +550,8 @@
files = (
43460F88200B30D10030C0E3 /* TransmitterIDTests.swift in Sources */,
43F82BCC1D035AA4006F5DD7 /* TransmitterTimeRxMessageTests.swift in Sources */,
433BC81D205CBB16000B1200 /* GlucoseBackfillMessageTests.swift in Sources */,
43E5292D2060C50800ACEB3B /* HKUnit.swift in Sources */,
43F82BD41D037227006F5DD7 /* SessionStartRxMessageTests.swift in Sources */,
43846AC81D8F89BE00799272 /* CalibrationDataRxMessageTests.swift in Sources */,
43DC87C01C8B509B005BC30D /* Data.swift in Sources */,
Expand Down
25 changes: 20 additions & 5 deletions xDripG5/BluetoothManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,19 @@ protocol BluetoothManagerDelegate: class {
*/
func bluetoothManager(_ manager: BluetoothManager, shouldConnectPeripheral peripheral: CBPeripheral) -> Bool

/// Tells the delegate that the bluetooth manager received new data in the control characteristic.
/// Informs the delegate that the bluetooth manager received new data in the control characteristic
///
/// - parameter manager: The bluetooth manager
/// - parameter didReceiveControlResponse: The data received on the control characteristic
/// - Parameters:
/// - manager: The bluetooth manager
/// - response: The data received on the control characteristic
func bluetoothManager(_ manager: BluetoothManager, didReceiveControlResponse response: Data)

/// Informs the delegate that the bluetooth manager received new data in the backfill characteristic
///
/// - Parameters:
/// - manager: The bluetooth manager
/// - response: The data received on the backfill characteristic
func bluetoothManager(_ manager: BluetoothManager, didReceiveBackfillResponse response: Data)
}


Expand Down Expand Up @@ -96,7 +104,7 @@ class BluetoothManager: NSObject {
}

let currentState = peripheral?.state ?? .disconnected
guard currentState == .disconnected else {
guard currentState != .connected else {
return
}

Expand Down Expand Up @@ -255,6 +263,13 @@ extension BluetoothManager: PeripheralManagerDelegate {
return
}

self.delegate?.bluetoothManager(self, didReceiveControlResponse: value)
switch CGMServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString.uppercased()) {
case .none, .communication?, .authentication?:
return
case .control?:
self.delegate?.bluetoothManager(self, didReceiveControlResponse: value)
case .backfill?:
self.delegate?.bluetoothManager(self, didReceiveBackfillResponse: value)
}
}
}
5 changes: 3 additions & 2 deletions xDripG5/BluetoothServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ enum CGMServiceCharacteristicUUID: String, CBUUIDRawValue {
case authentication = "F8083535-849E-531C-C594-30F1F86A4EA5"

// Read/Write/Notify
case probablyBackfill = "F8083536-849E-531C-C594-30F1F86A4EA5"
case backfill = "F8083536-849E-531C-C594-30F1F86A4EA5"
}


Expand All @@ -67,7 +67,8 @@ extension PeripheralManager.Configuration {
TransmitterServiceUUID.cgmService.cbUUID: [
CGMServiceCharacteristicUUID.communication.cbUUID,
CGMServiceCharacteristicUUID.authentication.cbUUID,
CGMServiceCharacteristicUUID.control.cbUUID
CGMServiceCharacteristicUUID.control.cbUUID,
CGMServiceCharacteristicUUID.backfill.cbUUID,
]
],
notifyingCharacteristics: [:],
Expand Down
103 changes: 70 additions & 33 deletions xDripG5/CalibrationState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,50 +9,87 @@
import Foundation


public enum CalibrationState {
public enum CalibrationState: RawRepresentable {
public typealias RawValue = UInt8

case stopped
case warmup
case needFirstInitialCalibration
case needSecondInitialCalibration
case ok
case needCalibration
public enum State: RawValue {
case stopped = 1
case warmup = 2

case needFirstInitialCalibration = 4
case needSecondInitialCalibration = 5
case ok = 6
case needCalibration7 = 7
case calibrationError8 = 8
case calibrationError9 = 9
case calibrationError10 = 10
case sensorFailure11 = 11
case sensorFailure12 = 12
case calibrationError13 = 13
case needCalibration14 = 14
case sessionFailure15 = 15
case sessionFailure16 = 16
case sessionFailure17 = 17
case questionMarks = 18
}

case known(State)
case unknown(RawValue)

init(rawValue: UInt8) {
switch rawValue {
case 1:
self = .stopped
case 2:
self = .warmup
case 4:
self = .needFirstInitialCalibration
case 5:
self = .needSecondInitialCalibration
case 6:
self = .ok
case 7:
self = .needCalibration
default:
public init(rawValue: RawValue) {
guard let state = State(rawValue: rawValue) else {
self = .unknown(rawValue)
return
}

self = .known(state)
}

public var rawValue: RawValue {
switch self {
case .known(let state):
return state.rawValue
case .unknown(let rawValue):
return rawValue
}
}

public var hasReliableGlucose: Bool {
return self == .ok || self == .needCalibration
guard case .known(let state) = self else {
return false
}

switch state {
case .stopped,
.warmup,
.needFirstInitialCalibration,
.needSecondInitialCalibration,
.calibrationError8,
.calibrationError9,
.calibrationError10,
.sensorFailure11,
.sensorFailure12,
.calibrationError13,
.sessionFailure15,
.sessionFailure16,
.sessionFailure17,
.questionMarks:
return false
case .ok, .needCalibration7, .needCalibration14:
return true
}
}
}

extension CalibrationState: Equatable { }

public func ==(lhs: CalibrationState, rhs: CalibrationState) -> Bool {
switch (lhs, rhs) {
case (.stopped, .stopped), (.warmup, .warmup), (.needFirstInitialCalibration, .needFirstInitialCalibration), (.needSecondInitialCalibration, .needSecondInitialCalibration), (.ok, .ok), (.needCalibration, .needCalibration):
return true
case let (.unknown(lhsRaw), .unknown(rhsRaw)):
return lhsRaw == rhsRaw
default:
return false
extension CalibrationState: Equatable {
public static func ==(lhs: CalibrationState, rhs: CalibrationState) -> Bool {
switch (lhs, rhs) {
case (.known(let lhs), .known(let rhs)):
return lhs == rhs
case (.unknown(let lhs), .unknown(let rhs)):
return lhs == rhs
default:
return false
}
}
}
Loading