Sfoglia il codice sorgente

CGMBLEKit tmp commit

Ivan Valkou 4 anni fa
parent
commit
fbca6b5c25
79 ha cambiato i file con 5339 aggiunte e 0 eliminazioni
  1. 36 0
      Dependecies/CGMBLEKit/.gitignore
  2. 653 0
      Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.pbxproj
  3. 7 0
      Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  4. 8 0
      Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  5. 8 0
      Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  6. 67 0
      Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/CGMBLEKit.xcscheme
  7. 19 0
      Dependecies/CGMBLEKit/CGMBLEKit/AESCrypt.h
  8. 33 0
      Dependecies/CGMBLEKit/CGMBLEKit/AESCrypt.m
  9. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/Base.lproj/Localizable.strings
  10. 350 0
      Dependecies/CGMBLEKit/CGMBLEKit/BluetoothManager.swift
  11. 87 0
      Dependecies/CGMBLEKit/CGMBLEKit/BluetoothServices.swift
  12. 45 0
      Dependecies/CGMBLEKit/CGMBLEKit/CBPeripheral.swift
  13. 17 0
      Dependecies/CGMBLEKit/CGMBLEKit/CGMBLEKit.h
  14. 27 0
      Dependecies/CGMBLEKit/CGMBLEKit/Calibration.swift
  15. 106 0
      Dependecies/CGMBLEKit/CGMBLEKit/CalibrationState.swift
  16. 79 0
      Dependecies/CGMBLEKit/CGMBLEKit/Command.swift
  17. 67 0
      Dependecies/CGMBLEKit/CGMBLEKit/Glucose+SensorDisplayable.swift
  18. 105 0
      Dependecies/CGMBLEKit/CGMBLEKit/Glucose.swift
  19. 26 0
      Dependecies/CGMBLEKit/CGMBLEKit/Info.plist
  20. 28 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthChallengeRxMessage.swift
  21. 20 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthChallengeTxMessage.swift
  22. 28 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthRequestRxMessage.swift
  23. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthRequestTxMessage.swift
  24. 16 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/BatteryStatusTxMessage.swift
  25. 17 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/BondRequestTxMessage.swift
  26. 22 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrateGlucoseRxMessage.swift
  27. 24 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrateGlucoseTxMessage.swift
  28. 28 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrationDataRxMessage.swift
  29. 18 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrationDataTxMessage.swift
  30. 16 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/DisconnectTxMessage.swift
  31. 14 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/FirmwareVersionTxMessage.swift
  32. 122 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseBackfillMessage.swift
  33. 14 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseHistoryTxMessage.swift
  34. 88 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseRxMessage.swift
  35. 18 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseTxMessage.swift
  36. 20 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/KeepAliveTxMessage.swift
  37. 30 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/ResetMessage.swift
  38. 37 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStartRxMessage.swift
  39. 27 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStartTxMessage.swift
  40. 34 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStopRxMessage.swift
  41. 22 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStopTxMessage.swift
  42. 32 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterMessage.swift
  43. 36 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterTimeRxMessage.swift
  44. 18 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterTimeTxMessage.swift
  45. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterVersionRxMessage.swift
  46. 16 0
      Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterVersionTxMessage.swift
  47. 61 0
      Dependecies/CGMBLEKit/CGMBLEKit/NSData+CRC.swift
  48. 50 0
      Dependecies/CGMBLEKit/CGMBLEKit/OSLog.swift
  49. 71 0
      Dependecies/CGMBLEKit/CGMBLEKit/Opcode.swift
  50. 150 0
      Dependecies/CGMBLEKit/CGMBLEKit/PeripheralManager+G5.swift
  51. 471 0
      Dependecies/CGMBLEKit/CGMBLEKit/PeripheralManager.swift
  52. 44 0
      Dependecies/CGMBLEKit/CGMBLEKit/PeripheralManagerError.swift
  53. 576 0
      Dependecies/CGMBLEKit/CGMBLEKit/Transmitter.swift
  54. 345 0
      Dependecies/CGMBLEKit/CGMBLEKit/TransmitterManager.swift
  55. 57 0
      Dependecies/CGMBLEKit/CGMBLEKit/TransmitterStatus.swift
  56. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/da.lproj/Localizable.strings
  57. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/de.lproj/Localizable.strings
  58. 30 0
      Dependecies/CGMBLEKit/CGMBLEKit/es.lproj/Localizable.strings
  59. 42 0
      Dependecies/CGMBLEKit/CGMBLEKit/fi.lproj/Localizable.strings
  60. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/fr.lproj/Localizable.strings
  61. 30 0
      Dependecies/CGMBLEKit/CGMBLEKit/it.lproj/Localizable.strings
  62. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/ja.lproj/Localizable.strings
  63. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/nb.lproj/Localizable.strings
  64. 30 0
      Dependecies/CGMBLEKit/CGMBLEKit/nl.lproj/Localizable.strings
  65. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/pl.lproj/Localizable.strings
  66. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/pt-BR.lproj/Localizable.strings
  67. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/ro.lproj/Localizable.strings
  68. 30 0
      Dependecies/CGMBLEKit/CGMBLEKit/ru.lproj/Localizable.strings
  69. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/sv.lproj/Localizable.strings
  70. 39 0
      Dependecies/CGMBLEKit/CGMBLEKit/vi.lproj/Localizable.strings
  71. 29 0
      Dependecies/CGMBLEKit/CGMBLEKit/zh-Hans.lproj/Localizable.strings
  72. 72 0
      Dependecies/CGMBLEKit/CODE_OF_CONDUCT.md
  73. 99 0
      Dependecies/CGMBLEKit/Common/Data.swift
  74. 20 0
      Dependecies/CGMBLEKit/Common/HKUnit.swift
  75. 21 0
      Dependecies/CGMBLEKit/Common/LocalizedString.swift
  76. 41 0
      Dependecies/CGMBLEKit/Common/Locked.swift
  77. 61 0
      Dependecies/CGMBLEKit/Common/TimeInterval.swift
  78. 22 0
      Dependecies/CGMBLEKit/LICENSE
  79. 55 0
      Dependecies/CGMBLEKit/README.md

+ 36 - 0
Dependecies/CGMBLEKit/.gitignore

@@ -0,0 +1,36 @@
+# OS X
+.DS_Store
+
+# Xcode
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+profile
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+
+# Bundler
+.bundle
+
+Carthage
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
+#
+# Note: if you ignore the Pods directory, make sure to uncomment
+# `pod install` in .travis.yml
+#
+
+Pods/
+Carthage/
+.gitmodules

+ 653 - 0
Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.pbxproj

@@ -0,0 +1,653 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 48;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		38569354270C657B0002C50D /* TransmitterManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4325E9EC210EAEF500969CE5 /* TransmitterManager.swift */; };
+		38569356270C6B1A0002C50D /* WeakSynchronizedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38569355270C6B1A0002C50D /* WeakSynchronizedSet.swift */; };
+		43026D3F2131C5C600A332E2 /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43026D3E2131C5C600A332E2 /* LocalizedString.swift */; };
+		43026D482131C99500A332E2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43026D462131C99500A332E2 /* Localizable.strings */; };
+		430D64C51CB7846A00FCA750 /* NSData+CRC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430D64C41CB7846A00FCA750 /* NSData+CRC.swift */; };
+		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 */; };
+		434B288320649D3C000EE07B /* ResetMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434B288220649D3C000EE07B /* ResetMessage.swift */; };
+		435535D41FB2C1B000CE5A23 /* PeripheralManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435535D31FB2C1B000CE5A23 /* PeripheralManagerError.swift */; };
+		4379CFD2210EB0F700AADC79 /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4379CFD1210EB0F700AADC79 /* TimeInterval.swift */; };
+		4379CFD4210EB14900AADC79 /* Glucose+SensorDisplayable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4379CFD3210EB14900AADC79 /* Glucose+SensorDisplayable.swift */; };
+		4379CFD6210EB19F00AADC79 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4379CFD5210EB19F00AADC79 /* Locked.swift */; };
+		43846AC61D8F896C00799272 /* CalibrationDataRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43846AC51D8F896C00799272 /* CalibrationDataRxMessage.swift */; };
+		43880F981D9E19FC009061A8 /* TransmitterVersionRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43880F971D9E19FC009061A8 /* TransmitterVersionRxMessage.swift */; };
+		43CABDF71C3506F100005705 /* CGMBLEKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 43CABDF61C3506F100005705 /* CGMBLEKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		43CABE121C350B2800005705 /* BluetoothManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE0E1C350B2800005705 /* BluetoothManager.swift */; };
+		43CABE131C350B2800005705 /* BluetoothServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE0F1C350B2800005705 /* BluetoothServices.swift */; };
+		43CABE151C350B2800005705 /* Transmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE111C350B2800005705 /* Transmitter.swift */; };
+		43CABE231C350B3D00005705 /* AuthRequestRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE171C350B3D00005705 /* AuthRequestRxMessage.swift */; };
+		43CABE241C350B3D00005705 /* AuthChallengeTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE181C350B3D00005705 /* AuthChallengeTxMessage.swift */; };
+		43CABE251C350B3D00005705 /* AuthRequestTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE191C350B3D00005705 /* AuthRequestTxMessage.swift */; };
+		43CABE261C350B3D00005705 /* AuthChallengeRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE1A1C350B3D00005705 /* AuthChallengeRxMessage.swift */; };
+		43CABE271C350B3D00005705 /* BondRequestTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE1B1C350B3D00005705 /* BondRequestTxMessage.swift */; };
+		43CABE281C350B3D00005705 /* DisconnectTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE1C1C350B3D00005705 /* DisconnectTxMessage.swift */; };
+		43CABE291C350B3D00005705 /* GlucoseRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE1D1C350B3D00005705 /* GlucoseRxMessage.swift */; };
+		43CABE2A1C350B3D00005705 /* GlucoseTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE1E1C350B3D00005705 /* GlucoseTxMessage.swift */; };
+		43CABE2B1C350B3D00005705 /* KeepAliveTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE1F1C350B3D00005705 /* KeepAliveTxMessage.swift */; };
+		43CABE2C1C350B3D00005705 /* TransmitterMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE201C350B3D00005705 /* TransmitterMessage.swift */; };
+		43CABE2D1C350B3D00005705 /* TransmitterTimeRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE211C350B3D00005705 /* TransmitterTimeRxMessage.swift */; };
+		43CABE2E1C350B3D00005705 /* TransmitterTimeTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CABE221C350B3D00005705 /* TransmitterTimeTxMessage.swift */; };
+		43CE7CC81CA73AEB003CC1B0 /* FirmwareVersionTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CC71CA73AEB003CC1B0 /* FirmwareVersionTxMessage.swift */; };
+		43CE7CCA1CA73B94003CC1B0 /* TransmitterVersionTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CC91CA73B94003CC1B0 /* TransmitterVersionTxMessage.swift */; };
+		43CE7CCC1CA73BCC003CC1B0 /* BatteryStatusTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CCB1CA73BCC003CC1B0 /* BatteryStatusTxMessage.swift */; };
+		43CE7CCE1CA73C22003CC1B0 /* SessionStartTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CCD1CA73C22003CC1B0 /* SessionStartTxMessage.swift */; };
+		43CE7CD01CA73C57003CC1B0 /* SessionStopTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CCF1CA73C57003CC1B0 /* SessionStopTxMessage.swift */; };
+		43CE7CD21CA73CBC003CC1B0 /* CalibrateGlucoseTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CD11CA73CBC003CC1B0 /* CalibrateGlucoseTxMessage.swift */; };
+		43CE7CD41CA73CE8003CC1B0 /* GlucoseHistoryTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CD31CA73CE8003CC1B0 /* GlucoseHistoryTxMessage.swift */; };
+		43CE7CDC1CA77468003CC1B0 /* TransmitterStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CDB1CA77468003CC1B0 /* TransmitterStatus.swift */; };
+		43D140CF2047AA940032346D /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DC87BF1C8B509B005BC30D /* Data.swift */; };
+		43D140D12047BD930032346D /* Opcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D140D02047BD930032346D /* Opcode.swift */; };
+		43E3978B1D5668BD0028E321 /* CalibrationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E3978A1D5668BD0028E321 /* CalibrationState.swift */; };
+		43E3978F1D566B170028E321 /* Glucose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E3978E1D566B170028E321 /* Glucose.swift */; };
+		43E397931D56950C0028E321 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E397921D56950C0028E321 /* HKUnit.swift */; };
+		43E4B1F21F8AF9790038823E /* PeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E4B1F11F8AF9790038823E /* PeripheralManager.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 */; };
+		43F82BCE1D035D5C006F5DD7 /* SessionStartRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F82BCD1D035D5C006F5DD7 /* SessionStartRxMessage.swift */; };
+		43F82BD01D035D68006F5DD7 /* SessionStopRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F82BCF1D035D68006F5DD7 /* SessionStopRxMessage.swift */; };
+		A9D99F48225EE99300073DF6 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9D99F47225EE99300073DF6 /* HealthKit.framework */; };
+		E752B4102063C31B0063027D /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E752B40F2063C31B0063027D /* Command.swift */; };
+		E75DB6AE20419B5D00FBE04E /* CalibrateGlucoseRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E75DB6AD20419B5D00FBE04E /* CalibrateGlucoseRxMessage.swift */; };
+		E76FD69C205C75780056DA5B /* Calibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76FD69B205C75780056DA5B /* Calibration.swift */; };
+		E76FD69E205C7A200056DA5B /* CalibrationDataTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76FD69D205C7A200056DA5B /* CalibrationDataTxMessage.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		38569355270C6B1A0002C50D /* WeakSynchronizedSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeakSynchronizedSet.swift; path = ../../LoopKit/LoopKit/WeakSynchronizedSet.swift; sourceTree = "<group>"; };
+		43026D3E2131C5C600A332E2 /* LocalizedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizedString.swift; sourceTree = "<group>"; };
+		43026D472131C99500A332E2 /* base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D492131CA8C00A332E2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
+		43026D4A2131CA8F00A332E2 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D4B2131CA9000A332E2 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D4C2131CA9100A332E2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D4D2131CA9200A332E2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D4E2131CA9200A332E2 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D4F2131CA9300A332E2 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D502131CA9400A332E2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
+		43026D512131CA9500A332E2 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
+		430D64C41CB7846A00FCA750 /* NSData+CRC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSData+CRC.swift"; sourceTree = "<group>"; };
+		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>"; };
+		4325E9EC210EAEF500969CE5 /* TransmitterManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterManager.swift; sourceTree = "<group>"; };
+		4325E9EE210EAF3F00969CE5 /* ShareClientUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClientUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		433BC81A205CB64A000B1200 /* GlucoseBackfillMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseBackfillMessage.swift; sourceTree = "<group>"; };
+		434B288220649D3C000EE07B /* ResetMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetMessage.swift; sourceTree = "<group>"; };
+		435535D31FB2C1B000CE5A23 /* PeripheralManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralManagerError.swift; sourceTree = "<group>"; };
+		4379CFD1210EB0F700AADC79 /* TimeInterval.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeInterval.swift; sourceTree = "<group>"; };
+		4379CFD3210EB14900AADC79 /* Glucose+SensorDisplayable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Glucose+SensorDisplayable.swift"; sourceTree = "<group>"; };
+		4379CFD5210EB19F00AADC79 /* Locked.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locked.swift; sourceTree = "<group>"; };
+		43846AC51D8F896C00799272 /* CalibrationDataRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationDataRxMessage.swift; sourceTree = "<group>"; };
+		43880F971D9E19FC009061A8 /* TransmitterVersionRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterVersionRxMessage.swift; sourceTree = "<group>"; };
+		43A8EC49210D09BE00A81379 /* LoopKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		43A8EC62210D4D1900A81379 /* LoopKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		43A8EC70210E629300A81379 /* ShareClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		43CABDF31C3506F100005705 /* CGMBLEKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CGMBLEKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		43CABDF61C3506F100005705 /* CGMBLEKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CGMBLEKit.h; sourceTree = "<group>"; };
+		43CABDF81C3506F100005705 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		43CABE0E1C350B2800005705 /* BluetoothManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothManager.swift; sourceTree = "<group>"; };
+		43CABE0F1C350B2800005705 /* BluetoothServices.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothServices.swift; sourceTree = "<group>"; };
+		43CABE111C350B2800005705 /* Transmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transmitter.swift; sourceTree = "<group>"; };
+		43CABE171C350B3D00005705 /* AuthRequestRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthRequestRxMessage.swift; sourceTree = "<group>"; };
+		43CABE181C350B3D00005705 /* AuthChallengeTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthChallengeTxMessage.swift; sourceTree = "<group>"; };
+		43CABE191C350B3D00005705 /* AuthRequestTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthRequestTxMessage.swift; sourceTree = "<group>"; };
+		43CABE1A1C350B3D00005705 /* AuthChallengeRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthChallengeRxMessage.swift; sourceTree = "<group>"; };
+		43CABE1B1C350B3D00005705 /* BondRequestTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BondRequestTxMessage.swift; sourceTree = "<group>"; };
+		43CABE1C1C350B3D00005705 /* DisconnectTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisconnectTxMessage.swift; sourceTree = "<group>"; };
+		43CABE1D1C350B3D00005705 /* GlucoseRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseRxMessage.swift; sourceTree = "<group>"; };
+		43CABE1E1C350B3D00005705 /* GlucoseTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseTxMessage.swift; sourceTree = "<group>"; };
+		43CABE1F1C350B3D00005705 /* KeepAliveTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeepAliveTxMessage.swift; sourceTree = "<group>"; };
+		43CABE201C350B3D00005705 /* TransmitterMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterMessage.swift; sourceTree = "<group>"; };
+		43CABE211C350B3D00005705 /* TransmitterTimeRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterTimeRxMessage.swift; sourceTree = "<group>"; };
+		43CABE221C350B3D00005705 /* TransmitterTimeTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterTimeTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CC71CA73AEB003CC1B0 /* FirmwareVersionTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirmwareVersionTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CC91CA73B94003CC1B0 /* TransmitterVersionTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterVersionTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CCB1CA73BCC003CC1B0 /* BatteryStatusTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryStatusTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CCD1CA73C22003CC1B0 /* SessionStartTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionStartTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CCF1CA73C57003CC1B0 /* SessionStopTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionStopTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CD11CA73CBC003CC1B0 /* CalibrateGlucoseTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrateGlucoseTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CD31CA73CE8003CC1B0 /* GlucoseHistoryTxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseHistoryTxMessage.swift; sourceTree = "<group>"; };
+		43CE7CDB1CA77468003CC1B0 /* TransmitterStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransmitterStatus.swift; sourceTree = "<group>"; };
+		43D140D02047BD930032346D /* Opcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Opcode.swift; sourceTree = "<group>"; };
+		43DC87BF1C8B509B005BC30D /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
+		43E3978A1D5668BD0028E321 /* CalibrationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationState.swift; sourceTree = "<group>"; };
+		43E3978E1D566B170028E321 /* Glucose.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Glucose.swift; sourceTree = "<group>"; };
+		43E397921D56950C0028E321 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = "<group>"; };
+		43E4B1F11F8AF9790038823E /* PeripheralManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralManager.swift; sourceTree = "<group>"; };
+		43EEA70F1D14DC0800CBBDA0 /* AESCrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESCrypt.h; sourceTree = "<group>"; };
+		43EEA7101D14DC0800CBBDA0 /* AESCrypt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AESCrypt.m; sourceTree = "<group>"; };
+		43F82BCD1D035D5C006F5DD7 /* SessionStartRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionStartRxMessage.swift; sourceTree = "<group>"; };
+		43F82BCF1D035D68006F5DD7 /* SessionStopRxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionStopRxMessage.swift; sourceTree = "<group>"; };
+		7D9BF0D32336EE51005DCFD6 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
+		7D9BF0D92336EE5E005DCFD6 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
+		7D9BF0DF2336EE6E005DCFD6 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
+		7D9BF0E52336EE77005DCFD6 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
+		7D9BF0EB2336EE80005DCFD6 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
+		7D9BF0F12336EE89005DCFD6 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
+		7D9BF11F2336FD7C005DCFD6 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
+		A9AD3821225EEEFB0058C179 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS5.1.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
+		A9D99F47225EE99300073DF6 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
+		B4D40D4623A42A1700D7ECB5 /* LoopKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		E752B40F2063C31B0063027D /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
+		E75DB6AD20419B5D00FBE04E /* CalibrateGlucoseRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrateGlucoseRxMessage.swift; sourceTree = "<group>"; };
+		E76FD69B205C75780056DA5B /* Calibration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calibration.swift; sourceTree = "<group>"; };
+		E76FD69D205C7A200056DA5B /* CalibrationDataTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationDataTxMessage.swift; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		43CABDEF1C3506F100005705 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				A9D99F48225EE99300073DF6 /* HealthKit.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		437AFF172038EDF9008C4892 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				B4D40D4623A42A1700D7ECB5 /* LoopKit.framework */,
+				A9D99F47225EE99300073DF6 /* HealthKit.framework */,
+				A9AD3821225EEEFB0058C179 /* HealthKit.framework */,
+				43A8EC62210D4D1900A81379 /* LoopKit.framework */,
+				43A8EC49210D09BE00A81379 /* LoopKitUI.framework */,
+				43A8EC70210E629300A81379 /* ShareClient.framework */,
+				4325E9EE210EAF3F00969CE5 /* ShareClientUI.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		43CABDE91C3506F100005705 = {
+			isa = PBXGroup;
+			children = (
+				43CABDF51C3506F100005705 /* CGMBLEKit */,
+				43D140CD2047AA530032346D /* Common */,
+				43CABDF41C3506F100005705 /* Products */,
+				437AFF172038EDF9008C4892 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		43CABDF41C3506F100005705 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				43CABDF31C3506F100005705 /* CGMBLEKit.framework */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		43CABDF51C3506F100005705 /* CGMBLEKit */ = {
+			isa = PBXGroup;
+			children = (
+				43EEA70F1D14DC0800CBBDA0 /* AESCrypt.h */,
+				43EEA7101D14DC0800CBBDA0 /* AESCrypt.m */,
+				43CABE0E1C350B2800005705 /* BluetoothManager.swift */,
+				43CABE0F1C350B2800005705 /* BluetoothServices.swift */,
+				431CE7621F8EEF6D00255374 /* CBPeripheral.swift */,
+				E76FD69B205C75780056DA5B /* Calibration.swift */,
+				43E3978A1D5668BD0028E321 /* CalibrationState.swift */,
+				E752B40F2063C31B0063027D /* Command.swift */,
+				43E3978E1D566B170028E321 /* Glucose.swift */,
+				4379CFD3210EB14900AADC79 /* Glucose+SensorDisplayable.swift */,
+				43CABDF81C3506F100005705 /* Info.plist */,
+				43026D462131C99500A332E2 /* Localizable.strings */,
+				430D64C41CB7846A00FCA750 /* NSData+CRC.swift */,
+				4323115E1EFC870300B95E62 /* OSLog.swift */,
+				43D140D02047BD930032346D /* Opcode.swift */,
+				43E4B1F11F8AF9790038823E /* PeripheralManager.swift */,
+				435535D31FB2C1B000CE5A23 /* PeripheralManagerError.swift */,
+				431CE7661F91D0B300255374 /* PeripheralManager+G5.swift */,
+				43CABE111C350B2800005705 /* Transmitter.swift */,
+				4325E9EC210EAEF500969CE5 /* TransmitterManager.swift */,
+				43CE7CDB1CA77468003CC1B0 /* TransmitterStatus.swift */,
+				43CABDF61C3506F100005705 /* CGMBLEKit.h */,
+				43CABE161C350B2E00005705 /* Messages */,
+			);
+			path = CGMBLEKit;
+			sourceTree = "<group>";
+		};
+		43CABE161C350B2E00005705 /* Messages */ = {
+			isa = PBXGroup;
+			children = (
+				43CABE191C350B3D00005705 /* AuthRequestTxMessage.swift */,
+				43CABE171C350B3D00005705 /* AuthRequestRxMessage.swift */,
+				43CABE181C350B3D00005705 /* AuthChallengeTxMessage.swift */,
+				43CABE1A1C350B3D00005705 /* AuthChallengeRxMessage.swift */,
+				43CE7CCB1CA73BCC003CC1B0 /* BatteryStatusTxMessage.swift */,
+				43CABE1B1C350B3D00005705 /* BondRequestTxMessage.swift */,
+				43CE7CD11CA73CBC003CC1B0 /* CalibrateGlucoseTxMessage.swift */,
+				E75DB6AD20419B5D00FBE04E /* CalibrateGlucoseRxMessage.swift */,
+				E76FD69D205C7A200056DA5B /* CalibrationDataTxMessage.swift */,
+				43846AC51D8F896C00799272 /* CalibrationDataRxMessage.swift */,
+				43CABE1C1C350B3D00005705 /* DisconnectTxMessage.swift */,
+				43CE7CC71CA73AEB003CC1B0 /* FirmwareVersionTxMessage.swift */,
+				433BC81A205CB64A000B1200 /* GlucoseBackfillMessage.swift */,
+				43CE7CD31CA73CE8003CC1B0 /* GlucoseHistoryTxMessage.swift */,
+				43CABE1D1C350B3D00005705 /* GlucoseRxMessage.swift */,
+				43CABE1E1C350B3D00005705 /* GlucoseTxMessage.swift */,
+				43CABE1F1C350B3D00005705 /* KeepAliveTxMessage.swift */,
+				434B288220649D3C000EE07B /* ResetMessage.swift */,
+				43F82BCD1D035D5C006F5DD7 /* SessionStartRxMessage.swift */,
+				43CE7CCD1CA73C22003CC1B0 /* SessionStartTxMessage.swift */,
+				43F82BCF1D035D68006F5DD7 /* SessionStopRxMessage.swift */,
+				43CE7CCF1CA73C57003CC1B0 /* SessionStopTxMessage.swift */,
+				43CABE201C350B3D00005705 /* TransmitterMessage.swift */,
+				43CABE211C350B3D00005705 /* TransmitterTimeRxMessage.swift */,
+				43CABE221C350B3D00005705 /* TransmitterTimeTxMessage.swift */,
+				43CE7CC91CA73B94003CC1B0 /* TransmitterVersionTxMessage.swift */,
+				43880F971D9E19FC009061A8 /* TransmitterVersionRxMessage.swift */,
+			);
+			path = Messages;
+			sourceTree = "<group>";
+		};
+		43D140CD2047AA530032346D /* Common */ = {
+			isa = PBXGroup;
+			children = (
+				38569355270C6B1A0002C50D /* WeakSynchronizedSet.swift */,
+				43DC87BF1C8B509B005BC30D /* Data.swift */,
+				43E397921D56950C0028E321 /* HKUnit.swift */,
+				43026D3E2131C5C600A332E2 /* LocalizedString.swift */,
+				4379CFD5210EB19F00AADC79 /* Locked.swift */,
+				4379CFD1210EB0F700AADC79 /* TimeInterval.swift */,
+			);
+			path = Common;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		43CABDF01C3506F100005705 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				43CABDF71C3506F100005705 /* CGMBLEKit.h in Headers */,
+				43EEA7111D14DC0800CBBDA0 /* AESCrypt.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		43CABDF21C3506F100005705 /* CGMBLEKit */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 43CABE071C3506F100005705 /* Build configuration list for PBXNativeTarget "CGMBLEKit" */;
+			buildPhases = (
+				43CABDEE1C3506F100005705 /* Sources */,
+				43CABDEF1C3506F100005705 /* Frameworks */,
+				43CABDF01C3506F100005705 /* Headers */,
+				43CABDF11C3506F100005705 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = CGMBLEKit;
+			productName = xDripG5;
+			productReference = 43CABDF31C3506F100005705 /* CGMBLEKit.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		43CABDEA1C3506F100005705 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 0930;
+				LastUpgradeCheck = 1300;
+				ORGANIZATIONNAME = "LoopKit Authors";
+				TargetAttributes = {
+					43CABDF21C3506F100005705 = {
+						CreatedOnToolsVersion = 7.2;
+						LastSwiftMigration = 1020;
+					};
+				};
+			};
+			buildConfigurationList = 43CABDED1C3506F100005705 /* Build configuration list for PBXProject "CGMBLEKit" */;
+			compatibilityVersion = "Xcode 8.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+				base,
+				"zh-Hans",
+				nl,
+				fr,
+				de,
+				it,
+				nb,
+				pl,
+				ru,
+				es,
+				ja,
+				"pt-BR",
+				fi,
+				vi,
+				sv,
+				da,
+				ro,
+			);
+			mainGroup = 43CABDE91C3506F100005705;
+			productRefGroup = 43CABDF41C3506F100005705 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				43CABDF21C3506F100005705 /* CGMBLEKit */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		43CABDF11C3506F100005705 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				43026D482131C99500A332E2 /* Localizable.strings in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		43CABDEE1C3506F100005705 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				43026D3F2131C5C600A332E2 /* LocalizedString.swift in Sources */,
+				43CE7CCA1CA73B94003CC1B0 /* TransmitterVersionTxMessage.swift in Sources */,
+				E76FD69C205C75780056DA5B /* Calibration.swift in Sources */,
+				43E3978B1D5668BD0028E321 /* CalibrationState.swift in Sources */,
+				431CE7631F8EEF6D00255374 /* CBPeripheral.swift in Sources */,
+				E76FD69E205C7A200056DA5B /* CalibrationDataTxMessage.swift in Sources */,
+				38569356270C6B1A0002C50D /* WeakSynchronizedSet.swift in Sources */,
+				43CABE2D1C350B3D00005705 /* TransmitterTimeRxMessage.swift in Sources */,
+				43CABE291C350B3D00005705 /* GlucoseRxMessage.swift in Sources */,
+				43CE7CDC1CA77468003CC1B0 /* TransmitterStatus.swift in Sources */,
+				43CABE271C350B3D00005705 /* BondRequestTxMessage.swift in Sources */,
+				4323115F1EFC870300B95E62 /* OSLog.swift in Sources */,
+				43E4B1F21F8AF9790038823E /* PeripheralManager.swift in Sources */,
+				43CABE231C350B3D00005705 /* AuthRequestRxMessage.swift in Sources */,
+				43CABE261C350B3D00005705 /* AuthChallengeRxMessage.swift in Sources */,
+				43CE7CD41CA73CE8003CC1B0 /* GlucoseHistoryTxMessage.swift in Sources */,
+				43E397931D56950C0028E321 /* HKUnit.swift in Sources */,
+				43846AC61D8F896C00799272 /* CalibrationDataRxMessage.swift in Sources */,
+				43CE7CD01CA73C57003CC1B0 /* SessionStopTxMessage.swift in Sources */,
+				431CE7671F91D0B300255374 /* PeripheralManager+G5.swift in Sources */,
+				43CABE2A1C350B3D00005705 /* GlucoseTxMessage.swift in Sources */,
+				434B288320649D3C000EE07B /* ResetMessage.swift in Sources */,
+				43CE7CC81CA73AEB003CC1B0 /* FirmwareVersionTxMessage.swift in Sources */,
+				43D140D12047BD930032346D /* Opcode.swift in Sources */,
+				43CE7CCC1CA73BCC003CC1B0 /* BatteryStatusTxMessage.swift in Sources */,
+				4379CFD6210EB19F00AADC79 /* Locked.swift in Sources */,
+				43EEA7121D14DC0800CBBDA0 /* AESCrypt.m in Sources */,
+				43CE7CCE1CA73C22003CC1B0 /* SessionStartTxMessage.swift in Sources */,
+				433BC81B205CB64A000B1200 /* GlucoseBackfillMessage.swift in Sources */,
+				43CABE2E1C350B3D00005705 /* TransmitterTimeTxMessage.swift in Sources */,
+				4379CFD4210EB14900AADC79 /* Glucose+SensorDisplayable.swift in Sources */,
+				43880F981D9E19FC009061A8 /* TransmitterVersionRxMessage.swift in Sources */,
+				43CABE2C1C350B3D00005705 /* TransmitterMessage.swift in Sources */,
+				E75DB6AE20419B5D00FBE04E /* CalibrateGlucoseRxMessage.swift in Sources */,
+				43CABE131C350B2800005705 /* BluetoothServices.swift in Sources */,
+				43E3978F1D566B170028E321 /* Glucose.swift in Sources */,
+				43CABE151C350B2800005705 /* Transmitter.swift in Sources */,
+				43CABE281C350B3D00005705 /* DisconnectTxMessage.swift in Sources */,
+				43CABE121C350B2800005705 /* BluetoothManager.swift in Sources */,
+				43CABE2B1C350B3D00005705 /* KeepAliveTxMessage.swift in Sources */,
+				E752B4102063C31B0063027D /* Command.swift in Sources */,
+				43CABE241C350B3D00005705 /* AuthChallengeTxMessage.swift in Sources */,
+				4379CFD2210EB0F700AADC79 /* TimeInterval.swift in Sources */,
+				43CE7CD21CA73CBC003CC1B0 /* CalibrateGlucoseTxMessage.swift in Sources */,
+				38569354270C657B0002C50D /* TransmitterManager.swift in Sources */,
+				43F82BD01D035D68006F5DD7 /* SessionStopRxMessage.swift in Sources */,
+				43F82BCE1D035D5C006F5DD7 /* SessionStartRxMessage.swift in Sources */,
+				435535D41FB2C1B000CE5A23 /* PeripheralManagerError.swift in Sources */,
+				43D140CF2047AA940032346D /* Data.swift in Sources */,
+				43CABE251C350B3D00005705 /* AuthRequestTxMessage.swift in Sources */,
+				430D64C51CB7846A00FCA750 /* NSData+CRC.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		43026D462131C99500A332E2 /* Localizable.strings */ = {
+			isa = PBXVariantGroup;
+			children = (
+				43026D472131C99500A332E2 /* base */,
+				43026D492131CA8C00A332E2 /* zh-Hans */,
+				43026D4A2131CA8F00A332E2 /* nl */,
+				43026D4B2131CA9000A332E2 /* fr */,
+				43026D4C2131CA9100A332E2 /* de */,
+				43026D4D2131CA9200A332E2 /* it */,
+				43026D4E2131CA9200A332E2 /* nb */,
+				43026D4F2131CA9300A332E2 /* pl */,
+				43026D502131CA9400A332E2 /* ru */,
+				43026D512131CA9500A332E2 /* es */,
+				7D9BF0D32336EE51005DCFD6 /* ja */,
+				7D9BF0D92336EE5E005DCFD6 /* pt-BR */,
+				7D9BF0DF2336EE6E005DCFD6 /* fi */,
+				7D9BF0E52336EE77005DCFD6 /* vi */,
+				7D9BF0EB2336EE80005DCFD6 /* sv */,
+				7D9BF0F12336EE89005DCFD6 /* da */,
+				7D9BF11F2336FD7C005DCFD6 /* ro */,
+			);
+			name = Localizable.strings;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		43CABE051C3506F100005705 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 21;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+				LOCALIZED_STRING_MACRO_NAMES = (
+					NSLocalizedString,
+					CFLocalizedString,
+					LocalizedString,
+				);
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
+			};
+			name = Debug;
+		};
+		43CABE061C3506F100005705 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 21;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+				LOCALIZED_STRING_MACRO_NAMES = (
+					NSLocalizedString,
+					CFLocalizedString,
+					LocalizedString,
+				);
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
+			};
+			name = Release;
+		};
+		43CABE081C3506F100005705 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_IDENTITY = "";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 21;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				INFOPLIST_FILE = CGMBLEKit/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = ru.artpancreas.cgmblekit;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		43CABE091C3506F100005705 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_IDENTITY = "";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 21;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				INFOPLIST_FILE = CGMBLEKit/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = ru.artpancreas.cgmblekit;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		43CABDED1C3506F100005705 /* Build configuration list for PBXProject "CGMBLEKit" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				43CABE051C3506F100005705 /* Debug */,
+				43CABE061C3506F100005705 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		43CABE071C3506F100005705 /* Build configuration list for PBXNativeTarget "CGMBLEKit" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				43CABE081C3506F100005705 /* Debug */,
+				43CABE091C3506F100005705 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 43CABDEA1C3506F100005705 /* Project object */;
+}

+ 7 - 0
Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>

+ 8 - 0
Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 8 - 0
Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
+	<false/>
+</dict>
+</plist>

+ 67 - 0
Dependecies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/CGMBLEKit.xcscheme

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1300"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "43CABDF21C3506F100005705"
+               BuildableName = "CGMBLEKit.framework"
+               BlueprintName = "CGMBLEKit"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "43CABDF21C3506F100005705"
+            BuildableName = "CGMBLEKit.framework"
+            BlueprintName = "CGMBLEKit"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 19 - 0
Dependecies/CGMBLEKit/CGMBLEKit/AESCrypt.h

@@ -0,0 +1,19 @@
+//
+//  AESCrypt.h
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 6/17/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface AESCrypt : NSObject
+
+NS_ASSUME_NONNULL_BEGIN
+
++ (nullable NSData *)encryptData:(NSData *)data usingKey:(NSData *)key error:(NSError **)error;
+
+NS_ASSUME_NONNULL_END
+
+@end

+ 33 - 0
Dependecies/CGMBLEKit/CGMBLEKit/AESCrypt.m

@@ -0,0 +1,33 @@
+//
+//  AESCrypt.m
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 6/17/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+#import "AESCrypt.h"
+#import <CommonCrypto/CommonCryptor.h>
+
+@implementation AESCrypt
+
++ (NSData *)encryptData:(NSData *)data usingKey:(NSData *)key error:(NSError * _Nullable __autoreleasing *)error
+{
+    NSMutableData *dataOut = [NSMutableData dataWithLength: data.length + kCCBlockSizeAES128];
+
+    CCCryptorStatus status = CCCrypt(kCCEncrypt,
+                                     kCCAlgorithmAES,
+                                     kCCOptionECBMode,
+                                     key.bytes,
+                                     key.length,
+                                     NULL,
+                                     data.bytes,
+                                     data.length,
+                                     dataOut.mutableBytes,
+                                     dataOut.length,
+                                     NULL);
+
+    return status == kCCSuccess ? dataOut : nil;
+}
+
+@end

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Base.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glucose data is unavailable";
+
+/* Describes a low battery */
+"Low Battery" = "Low Battery";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Peripheral did not respond in time";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Peripheral isnʼt connected";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Sensor calibration is OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Sensor is in unknown state %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensor is stopped";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensor is warming up";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensor needs calibration";
+
+/* Error description */
+"Unknown characteristic" = "Unknown characteristic";
+

+ 350 - 0
Dependecies/CGMBLEKit/CGMBLEKit/BluetoothManager.swift

@@ -0,0 +1,350 @@
+//
+//  BluetoothManager.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 10/1/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import CoreBluetooth
+import Foundation
+import os.log
+
+
+protocol BluetoothManagerDelegate: AnyObject {
+
+    /**
+     Tells the delegate that the bluetooth manager has finished connecting to and discovering all required services of its peripheral, or that it failed to do so
+
+     - parameter manager: The bluetooth manager
+     - parameter peripheralManager: The peripheral manager
+     - parameter error:   An error describing why bluetooth setup failed
+     */
+    func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, isReadyWithError error: Error?)
+
+    /**
+     Asks the delegate whether the discovered or restored peripheral should be connected
+
+     - parameter manager:    The bluetooth manager
+     - parameter peripheral: The found peripheral
+
+     - returns: True if the peripheral should connect
+     */
+    func bluetoothManager(_ manager: BluetoothManager, shouldConnectPeripheral peripheral: CBPeripheral) -> Bool
+
+    /// Informs the delegate that the bluetooth manager received new data in the control characteristic
+    ///
+    /// - Parameters:
+    ///   - manager: The bluetooth manager
+    ///   - peripheralManager: The peripheral manager
+    ///   - response: The data received on the control characteristic
+    func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, 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)
+
+    /// Informs the delegate that the bluetooth manager received new data in the authentication characteristic
+    ///
+    /// - Parameters:
+    ///   - manager: The bluetooth manager
+    ///   - peripheralManager: The peripheral manager
+    ///   - response: The data received on the authentication characteristic
+    func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, didReceiveAuthenticationResponse response: Data)
+}
+
+
+class BluetoothManager: NSObject {
+
+    var stayConnected: Bool {
+        get {
+            return lockedStayConnected.value
+        }
+        set {
+            lockedStayConnected.value = newValue
+        }
+    }
+    private let lockedStayConnected: Locked<Bool> = Locked(true)
+
+    weak var delegate: BluetoothManagerDelegate?
+
+    private let log = OSLog(category: "BluetoothManager")
+
+    /// Isolated to `managerQueue`
+    private var manager: CBCentralManager! = nil
+
+    /// Isolated to `managerQueue`
+    private var peripheral: CBPeripheral? {
+        get {
+            return peripheralManager?.peripheral
+        }
+        set {
+            guard let peripheral = newValue else {
+                peripheralManager = nil
+                return
+            }
+
+            if let peripheralManager = peripheralManager {
+                peripheralManager.peripheral = peripheral
+            } else {
+                peripheralManager = PeripheralManager(
+                    peripheral: peripheral,
+                    configuration: .dexcomG5,
+                    centralManager: manager
+                )
+            }
+        }
+    }
+
+    var peripheralIdentifier: UUID? {
+        get {
+            return lockedPeripheralIdentifier.value
+        }
+        set {
+            lockedPeripheralIdentifier.value = newValue
+        }
+    }
+    private let lockedPeripheralIdentifier: Locked<UUID?> = Locked(nil)
+
+    /// Isolated to `managerQueue`
+    private var peripheralManager: PeripheralManager? {
+        didSet {
+            oldValue?.delegate = nil
+            peripheralManager?.delegate = self
+
+            peripheralIdentifier = peripheralManager?.peripheral.identifier
+        }
+    }
+
+    // MARK: - Synchronization
+
+    private let managerQueue = DispatchQueue(label: "com.loudnate.CGMBLEKit.bluetoothManagerQueue", qos: .unspecified)
+
+    override init() {
+        super.init()
+
+        managerQueue.sync {
+            self.manager = CBCentralManager(delegate: self, queue: managerQueue, options: [CBCentralManagerOptionRestoreIdentifierKey: "com.loudnate.CGMBLEKit"])
+        }
+    }
+
+    // MARK: - Actions
+
+    func scanForPeripheral() {
+        dispatchPrecondition(condition: .notOnQueue(managerQueue))
+
+        managerQueue.sync {
+            self.managerQueue_scanForPeripheral()
+        }
+    }
+
+    func disconnect() {
+        dispatchPrecondition(condition: .notOnQueue(managerQueue))
+
+        managerQueue.sync {
+            if manager.isScanning {
+                manager.stopScan()
+            }
+
+            if let peripheral = peripheral {
+                manager.cancelPeripheralConnection(peripheral)
+            }
+        }
+    }
+
+    private func managerQueue_scanForPeripheral() {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+
+        guard manager.state == .poweredOn else {
+            return
+        }
+
+        let currentState = peripheral?.state ?? .disconnected
+        guard currentState != .connected else {
+            return
+        }
+
+        if let peripheralID = peripheralIdentifier, let peripheral = manager.retrievePeripherals(withIdentifiers: [peripheralID]).first {
+            log.debug("Re-connecting to known peripheral %{public}@", peripheral.identifier.uuidString)
+            self.peripheral = peripheral
+            self.manager.connect(peripheral)
+        } else if let peripheral = manager.retrieveConnectedPeripherals(withServices: [
+                TransmitterServiceUUID.advertisement.cbUUID,
+                TransmitterServiceUUID.cgmService.cbUUID
+            ]).first,
+            delegate == nil || delegate!.bluetoothManager(self, shouldConnectPeripheral: peripheral)
+        {
+            log.debug("Found system-connected peripheral: %{public}@", peripheral.identifier.uuidString)
+            self.peripheral = peripheral
+            self.manager.connect(peripheral)
+        } else {
+            log.debug("Scanning for peripherals")
+            manager.scanForPeripherals(withServices: [
+                    TransmitterServiceUUID.advertisement.cbUUID
+                ],
+                options: nil
+            )
+        }
+    }
+
+    /**
+    
+     Persistent connections don't seem to work with the transmitter shutoff: The OS won't re-wake the
+     app unless it's scanning.
+     
+     The sleep gives the transmitter time to shut down, but keeps the app running.
+
+     */
+    fileprivate func scanAfterDelay() {
+        DispatchQueue.global(qos: .utility).async {
+            Thread.sleep(forTimeInterval: 2)
+
+            self.scanForPeripheral()
+        }
+    }
+
+    // MARK: - Accessors
+
+    var isScanning: Bool {
+        dispatchPrecondition(condition: .notOnQueue(managerQueue))
+
+        var isScanning = false
+        managerQueue.sync {
+            isScanning = manager.isScanning
+        }
+        return isScanning
+    }
+
+    override var debugDescription: String {
+        return [
+            "## BluetoothManager",
+            peripheralManager.map(String.init(reflecting:)) ?? "No peripheral",
+        ].joined(separator: "\n")
+    }
+}
+
+
+extension BluetoothManager: CBCentralManagerDelegate {
+    func centralManagerDidUpdateState(_ central: CBCentralManager) {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+
+        peripheralManager?.centralManagerDidUpdateState(central)
+        log.default("%{public}@: %{public}@", #function, String(describing: central.state.rawValue))
+
+        switch central.state {
+        case .poweredOn:
+            managerQueue_scanForPeripheral()
+        case .resetting, .poweredOff, .unauthorized, .unknown, .unsupported:
+            fallthrough
+        @unknown default:
+            if central.isScanning {
+                central.stopScan()
+            }
+        }
+    }
+
+    func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+
+        if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] {
+            for peripheral in peripherals {
+                if delegate == nil || delegate!.bluetoothManager(self, shouldConnectPeripheral: peripheral) {
+                    log.default("Restoring peripheral from state: %{public}@", peripheral.identifier.uuidString)
+                    self.peripheral = peripheral
+                }
+            }
+        }
+    }
+
+    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+
+        log.info("%{public}@: %{public}@", #function, peripheral)
+        if delegate == nil || delegate!.bluetoothManager(self, shouldConnectPeripheral: peripheral) {
+            self.peripheral = peripheral
+
+            central.connect(peripheral, options: nil)
+
+            central.stopScan()
+        }
+    }
+
+    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+
+        log.default("%{public}@: %{public}@", #function, peripheral)
+        if central.isScanning {
+            central.stopScan()
+        }
+
+        peripheralManager?.centralManager(central, didConnect: peripheral)
+
+        if case .poweredOn = manager.state, case .connected = peripheral.state, let peripheralManager = peripheralManager {
+            self.delegate?.bluetoothManager(self, peripheralManager: peripheralManager, isReadyWithError: nil)
+        }
+    }
+
+    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+        log.default("%{public}@: %{public}@", #function, peripheral)
+        // Ignore errors indicating the peripheral disconnected remotely, as that's expected behavior
+        if let error = error as NSError?, CBError(_nsError: error).code != .peripheralDisconnected {
+            log.error("%{public}@: %{public}@", #function, error)
+            if let peripheralManager = peripheralManager {
+                self.delegate?.bluetoothManager(self, peripheralManager: peripheralManager, isReadyWithError: error)
+            }
+        }
+
+        if stayConnected {
+            scanAfterDelay()
+        }
+    }
+
+    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
+        dispatchPrecondition(condition: .onQueue(managerQueue))
+
+        log.error("%{public}@: %{public}@", #function, String(describing: error))
+        if let error = error, let peripheralManager = peripheralManager {
+            self.delegate?.bluetoothManager(self, peripheralManager: peripheralManager, isReadyWithError: error)
+        }
+
+        if stayConnected {
+            scanAfterDelay()
+        }
+    }
+}
+
+
+extension BluetoothManager: PeripheralManagerDelegate {
+    func peripheralManager(_ manager: PeripheralManager, didReadRSSI RSSI: NSNumber, error: Error?) {
+        
+    }
+
+    func peripheralManagerDidUpdateName(_ manager: PeripheralManager) {
+
+    }
+
+    func completeConfiguration(for manager: PeripheralManager) throws {
+
+    }
+
+    func peripheralManager(_ manager: PeripheralManager, didUpdateValueFor characteristic: CBCharacteristic) {
+        guard let value = characteristic.value else {
+            return
+        }
+
+        switch CGMServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString.uppercased()) {
+        case .none, .communication?:
+            return
+        case .control?:
+            self.delegate?.bluetoothManager(self, peripheralManager: manager, didReceiveControlResponse: value)
+        case .backfill?:
+            self.delegate?.bluetoothManager(self, didReceiveBackfillResponse: value)
+        case .authentication?:
+            self.delegate?.bluetoothManager(self, peripheralManager: manager, didReceiveAuthenticationResponse: value)
+        }
+    }
+}

+ 87 - 0
Dependecies/CGMBLEKit/CGMBLEKit/BluetoothServices.swift

@@ -0,0 +1,87 @@
+//
+//  BluetoothServices.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 10/16/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import CoreBluetooth
+
+/*
+G5 BLE attributes, retrieved using LightBlue on 2015-10-01
+
+These are the G4 details, for reference:
+https://github.com/StephenBlackWasAlreadyTaken/xDrip/blob/af20e32652d19aa40becc1a39f6276cad187fdce/app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/DexShareAttributes.java
+*/
+
+protocol CBUUIDRawValue: RawRepresentable {}
+extension CBUUIDRawValue where RawValue == String {
+    var cbUUID: CBUUID {
+        return CBUUID(string: rawValue)
+    }
+}
+
+
+enum TransmitterServiceUUID: String, CBUUIDRawValue {
+    case deviceInfo = "180A"
+    case advertisement = "FEBC"
+    case cgmService = "F8083532-849E-531C-C594-30F1F86A4EA5"
+
+    case serviceB = "F8084532-849E-531C-C594-30F1F86A4EA5"
+}
+
+
+enum DeviceInfoCharacteristicUUID: String, CBUUIDRawValue {
+    // Read
+    // "DexcomUN"
+    case manufacturerNameString = "2A29"
+}
+
+
+enum CGMServiceCharacteristicUUID: String, CBUUIDRawValue {
+
+    // Read/Notify
+    case communication = "F8083533-849E-531C-C594-30F1F86A4EA5"
+
+    // Write/Indicate
+    case control = "F8083534-849E-531C-C594-30F1F86A4EA5"
+
+    // 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"
+}
+
+
+enum ServiceBCharacteristicUUID: String, CBUUIDRawValue {
+    // Write/Indicate
+    case characteristicE = "F8084533-849E-531C-C594-30F1F86A4EA5"
+    // Read/Write/Notify
+    case characteristicF = "F8084534-849E-531C-C594-30F1F86A4EA5"
+}
+
+
+extension PeripheralManager.Configuration {
+    static var dexcomG5: PeripheralManager.Configuration {
+        return PeripheralManager.Configuration(
+            serviceCharacteristics: [
+                TransmitterServiceUUID.cgmService.cbUUID: [
+                    CGMServiceCharacteristicUUID.communication.cbUUID,
+                    CGMServiceCharacteristicUUID.authentication.cbUUID,
+                    CGMServiceCharacteristicUUID.control.cbUUID,
+                    CGMServiceCharacteristicUUID.backfill.cbUUID,
+                ]
+            ],
+            notifyingCharacteristics: [:],
+            valueUpdateMacros: [:]
+        )
+    }
+}

+ 45 - 0
Dependecies/CGMBLEKit/CGMBLEKit/CBPeripheral.swift

@@ -0,0 +1,45 @@
+//
+//  CBPeripheral.swift
+//  xDripG5
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+//
+
+import CoreBluetooth
+
+
+// MARK: - Discovery helpers.
+extension CBPeripheral {
+    func servicesToDiscover(from serviceUUIDs: [CBUUID]) -> [CBUUID] {
+        let knownServiceUUIDs = services?.compactMap({ $0.uuid }) ?? []
+        return serviceUUIDs.filter({ !knownServiceUUIDs.contains($0) })
+    }
+
+    func characteristicsToDiscover(from characteristicUUIDs: [CBUUID], for service: CBService) -> [CBUUID] {
+        let knownCharacteristicUUIDs = service.characteristics?.compactMap({ $0.uuid }) ?? []
+        return characteristicUUIDs.filter({ !knownCharacteristicUUIDs.contains($0) })
+    }
+}
+
+
+extension Collection where Element: CBAttribute {
+    func itemWithUUID(_ uuid: CBUUID) -> Element? {
+        for attribute in self {
+            if attribute.uuid == uuid {
+                return attribute
+            }
+        }
+
+        return nil
+    }
+
+    func itemWithUUIDString(_ uuidString: String) -> Element? {
+        for attribute in self {
+            if attribute.uuid.uuidString == uuidString {
+                return attribute
+            }
+        }
+
+        return nil
+    }
+}

+ 17 - 0
Dependecies/CGMBLEKit/CGMBLEKit/CGMBLEKit.h

@@ -0,0 +1,17 @@
+//
+//  CGMBLEKit.h
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 12/30/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CGMBLEKit/AESCrypt.h>
+
+//! Project version number for CGMBLEKIt.
+FOUNDATION_EXPORT double CGMBLEKitVersionNumber;
+
+//! Project version string for CGMBLEKit.
+FOUNDATION_EXPORT const unsigned char CGMBLEKitVersionString[];
+

+ 27 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Calibration.swift

@@ -0,0 +1,27 @@
+//
+//  Calibration.swift
+//  xDripG5
+//
+//  Created by Paul Dickens on 17/03/2018.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import HealthKit
+
+
+public struct Calibration {
+    init?(calibrationMessage: CalibrationDataRxMessage, activationDate: Date) {
+        guard calibrationMessage.glucose > 0 else {
+            return nil
+        }
+
+        let unit = HKUnit.milligramsPerDeciliter
+
+        glucose = HKQuantity(unit: unit, doubleValue: Double(calibrationMessage.glucose))
+        date = activationDate.addingTimeInterval(TimeInterval(calibrationMessage.timestamp))
+    }
+
+    public let glucose: HKQuantity
+    public let date: Date
+}

+ 106 - 0
Dependecies/CGMBLEKit/CGMBLEKit/CalibrationState.swift

@@ -0,0 +1,106 @@
+//
+//  CalibrationState.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 8/6/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+public enum CalibrationState: RawRepresentable {
+    public typealias RawValue = UInt8
+
+    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)
+
+    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 {
+        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 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
+        }
+    }
+}
+
+extension CalibrationState: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .known(let state):
+            return String(describing: state)
+        case .unknown(let value):
+            return ".unknown(\(value))"
+        }
+    }
+}

+ 79 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Command.swift

@@ -0,0 +1,79 @@
+//
+//  Command.swift
+//  xDripG5
+//
+//  Created by Paul Dickens on 22/03/2018.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import HealthKit
+
+
+public enum Command: RawRepresentable {
+    public typealias RawValue = [String: Any]
+
+    case startSensor(at: Date)
+    case stopSensor(at: Date)
+    case calibrateSensor(to: HKQuantity, at: Date)
+    case resetTransmitter
+
+    public init?(rawValue: RawValue) {
+        guard let action = rawValue["action"] as? Action.RawValue else {
+            return nil
+        }
+
+        let date = rawValue["date"] as? Date
+
+        switch Action(rawValue: action) {
+        case .startSensor?:
+            guard let date = date else {
+                return nil
+            }
+            self = .startSensor(at: date)
+        case .stopSensor?:
+            guard let date = date else {
+                return nil
+            }
+            self = .stopSensor(at: date)
+        case .calibrateSensor?:
+            guard let date = date, let glucose = rawValue["glucose"] as? HKQuantity else {
+                return nil
+            }
+            self = .calibrateSensor(to: glucose, at: date)
+        case .resetTransmitter?:
+            self = .resetTransmitter
+        case .none:
+            return nil
+        }
+    }
+
+    private enum Action: Int {
+        case startSensor, stopSensor, calibrateSensor, resetTransmitter
+    }
+
+    public var rawValue: RawValue {
+        switch self {
+        case .startSensor(let date):
+            return [
+                "action": Action.startSensor.rawValue,
+                "date": date
+            ]
+        case .stopSensor(let date):
+            return [
+                "action": Action.stopSensor.rawValue,
+                "date": date
+            ]
+        case .calibrateSensor(let glucose, let date):
+            return [
+                "action": Action.calibrateSensor.rawValue,
+                "date": date,
+                "glucose": glucose
+            ]
+        case .resetTransmitter:
+            return [
+                "action": Action.resetTransmitter.rawValue
+            ]
+        }
+    }
+}

+ 67 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Glucose+SensorDisplayable.swift

@@ -0,0 +1,67 @@
+//
+//  GlucoseRxMessage.swift
+//  Loop
+//
+//  Created by Nathan Racklyeft on 5/30/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+extension Glucose {
+    public var isStateValid: Bool {
+        return state == .known(.ok) && status == .ok
+    }
+
+    public var stateDescription: String {
+        var messages = [String]()
+
+        switch state {
+        case .known(.ok):
+            break  // Suppress the "OK" message
+        default:
+            messages.append(state.localizedDescription)
+        }
+
+        switch self.status {
+        case .ok:
+            if messages.isEmpty {
+                messages.append(status.localizedDescription)
+            } else {
+                break  // Suppress the "OK" message
+            }
+        case .lowBattery, .unknown:
+            messages.append(status.localizedDescription)
+        }
+
+        return messages.joined(separator: ". ")
+    }
+
+//    public var trendType: GlucoseTrend? {
+//        guard trend < Int(Int8.max) else {
+//            return nil
+//        }
+//
+//        switch trend {
+//        case let x where x <= -30:
+//            return .downDownDown
+//        case let x where x <= -20:
+//            return .downDown
+//        case let x where x <= -10:
+//            return .down
+//        case let x where x < 10:
+//            return .flat
+//        case let x where x < 20:
+//            return .up
+//        case let x where x < 30:
+//            return .upUp
+//        default:
+//            return .upUpUp
+//        }
+//    }
+
+    public var isLocal: Bool {
+        return true
+    }
+}

+ 105 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Glucose.swift

@@ -0,0 +1,105 @@
+//
+//  Glucose.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 8/6/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+import HealthKit
+
+
+public struct Glucose {
+    let glucoseMessage: GlucoseSubMessage
+    let timeMessage: TransmitterTimeRxMessage
+
+    init(
+        transmitterID: String,
+        glucoseMessage: GlucoseRxMessage,
+        timeMessage: TransmitterTimeRxMessage,
+        calibrationMessage: CalibrationDataRxMessage? = nil,
+        activationDate: Date
+    ) {
+        self.init(
+            transmitterID: transmitterID,
+            status: glucoseMessage.status,
+            glucoseMessage: glucoseMessage.glucose,
+            timeMessage: timeMessage,
+            calibrationMessage: calibrationMessage,
+            activationDate: activationDate
+        )
+    }
+
+    init(
+        transmitterID: String,
+        status: UInt8,
+        glucoseMessage: GlucoseSubMessage,
+        timeMessage: TransmitterTimeRxMessage,
+        calibrationMessage: CalibrationDataRxMessage? = nil,
+        activationDate: Date
+    ) {
+        self.transmitterID = transmitterID
+        self.glucoseMessage = glucoseMessage
+        self.timeMessage = timeMessage
+        self.status = TransmitterStatus(rawValue: status)
+        self.activationDate = activationDate
+
+        sessionStartDate = activationDate.addingTimeInterval(TimeInterval(timeMessage.sessionStartTime))
+        readDate = activationDate.addingTimeInterval(TimeInterval(glucoseMessage.timestamp))
+        lastCalibration = calibrationMessage != nil ? Calibration(calibrationMessage: calibrationMessage!, activationDate: activationDate) : nil
+    }
+
+    // MARK: - Transmitter Info
+    public let transmitterID: String
+    public let status: TransmitterStatus
+    public let activationDate: Date
+    public let sessionStartDate: Date
+
+    // MARK: - Glucose Info
+    public let lastCalibration: Calibration?
+    public let readDate: Date
+
+    public var isDisplayOnly: Bool {
+        return glucoseMessage.glucoseIsDisplayOnly
+    }
+
+    public var glucose: HKQuantity? {
+        guard state.hasReliableGlucose && glucoseMessage.glucose >= 39 else { 
+            return nil
+        }
+
+        let unit = HKUnit.milligramsPerDeciliter
+
+        return HKQuantity(unit: unit, doubleValue: Double(glucoseMessage.glucose))
+    }
+
+    public var state: CalibrationState {
+        return CalibrationState(rawValue: glucoseMessage.state)
+    }
+
+    public var trend: Int {
+        return Int(glucoseMessage.trend)
+    }
+
+    public var trendRate: HKQuantity? {
+        guard glucoseMessage.trend < Int8.max && glucoseMessage.trend > Int8.min else {
+            return nil
+        }
+
+        let unit = HKUnit.milligramsPerDeciliterPerMinute
+        return HKQuantity(unit: unit, doubleValue: Double(glucoseMessage.trend) / 10)
+    }
+
+    // An identifier for this reading thatʼs consistent between backfill/live data
+    public var syncIdentifier: String {
+        return "\(transmitterID) \(glucoseMessage.timestamp)"
+    }
+}
+
+
+extension Glucose: Equatable {
+    public static func ==(lhs: Glucose, rhs: Glucose) -> Bool {
+        return lhs.glucoseMessage == rhs.glucoseMessage && lhs.syncIdentifier == rhs.syncIdentifier
+    }
+}

+ 26 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>3.2</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>

+ 28 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthChallengeRxMessage.swift

@@ -0,0 +1,28 @@
+//
+//  AuthChallengeRxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/22/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct AuthChallengeRxMessage: TransmitterRxMessage {
+    let isAuthenticated: Bool
+    let isBonded: Bool
+
+    init?(data: Data) {
+        guard data.count >= 3 else {
+            return nil
+        }
+
+        guard data.starts(with: .authChallengeRx) else {
+            return nil
+        }
+
+        isAuthenticated = data[1] == 0x1
+        isBonded = data[2] == 0x1
+    }
+}

+ 20 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthChallengeTxMessage.swift

@@ -0,0 +1,20 @@
+//
+//  AuthChallengeTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/22/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct AuthChallengeTxMessage: TransmitterTxMessage {
+    let challengeHash: Data
+
+    var data: Data {
+        var data = Data(for: .authChallengeTx)
+        data.append(challengeHash)
+        return data
+    }
+}

+ 28 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthRequestRxMessage.swift

@@ -0,0 +1,28 @@
+//
+//  AuthRequestRxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/22/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct AuthRequestRxMessage: TransmitterRxMessage {
+    let tokenHash: Data
+    let challenge: Data
+
+    init?(data: Data) {
+        guard data.count >= 17 else {
+            return nil
+        }
+
+        guard data.starts(with: .authRequestRx) else {
+            return nil
+        }
+
+        tokenHash = data.subdata(in: 1..<9)
+        challenge = data.subdata(in: 9..<17)
+    }
+}

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/AuthRequestTxMessage.swift

@@ -0,0 +1,29 @@
+//
+//  AuthRequestTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/22/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct AuthRequestTxMessage: TransmitterTxMessage {
+    let singleUseToken: Data
+    let endByte: UInt8 = 0x2
+
+    init() {
+        let uuid = UUID().uuid
+
+        singleUseToken = Data([uuid.0, uuid.1, uuid.2, uuid.3,
+                               uuid.4, uuid.5, uuid.6, uuid.7])
+    }
+
+    var data: Data {
+        var data = Data(for: .authRequestTx)
+        data.append(singleUseToken)
+        data.append(endByte)
+        return data
+    }
+}

+ 16 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/BatteryStatusTxMessage.swift

@@ -0,0 +1,16 @@
+//
+//  BatteryStatusTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct BatteryStatusTxMessage {
+    let opcode: Opcode = .batteryStatusTx
+
+    // Response: 23003c012f01cd021f247bae
+}

+ 17 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/BondRequestTxMessage.swift

@@ -0,0 +1,17 @@
+//
+//  BondRequestTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+/// Initiates a bond with the central
+struct BondRequestTxMessage: TransmitterTxMessage {
+    var data: Data {
+        return Data(for: .bondRequest)
+    }
+}

+ 22 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrateGlucoseRxMessage.swift

@@ -0,0 +1,22 @@
+//
+//  CalibrateGlucoseRxMessage.swift
+//  xDripG5
+//
+//  Created by Paul Dickens on 25/02/2018.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+public struct CalibrateGlucoseRxMessage: TransmitterRxMessage {
+    init?(data: Data) {
+        guard data.count == 5 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .calibrateGlucoseRx) else {
+            return nil
+        }
+    }
+}

+ 24 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrateGlucoseTxMessage.swift

@@ -0,0 +1,24 @@
+//
+//  CalibrateGlucoseTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct CalibrateGlucoseTxMessage: RespondableMessage {
+    typealias Response = CalibrateGlucoseRxMessage
+
+    let time: UInt32
+    let glucose: UInt16
+
+    var data: Data {
+        var data = Data(for: .calibrateGlucoseTx)
+        data.append(glucose)
+        data.append(time)
+        return data.appendingCRC()
+    }
+}

+ 28 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrationDataRxMessage.swift

@@ -0,0 +1,28 @@
+//
+//  CalibrationDataRxMessage.swift
+//  Pods
+//
+//  Created by Nate Racklyeft on 9/18/16.
+//
+//
+
+import Foundation
+
+
+struct CalibrationDataRxMessage: TransmitterRxMessage {
+    let glucose: UInt16
+    let timestamp: UInt32
+
+    init?(data: Data) {
+        guard data.count == 19 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .calibrationDataRx) else {
+            return nil
+        }
+
+        glucose = data[11..<13].toInt() & 0xfff
+        timestamp = data[13..<17].toInt()
+    }
+}

+ 18 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/CalibrationDataTxMessage.swift

@@ -0,0 +1,18 @@
+//
+//  CalibrationDataTxMessage.swift
+//  xDripG5
+//
+//  Created by Paul Dickens on 17/03/2018.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+struct CalibrationDataTxMessage: RespondableMessage {
+    typealias Response = CalibrationDataRxMessage
+
+    var data: Data {
+        return Data(for: .calibrationDataTx).appendingCRC()
+    }
+}

+ 16 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/DisconnectTxMessage.swift

@@ -0,0 +1,16 @@
+//
+//  DisconnectTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct DisconnectTxMessage: TransmitterTxMessage {
+    var data: Data {
+        return Data(for: .disconnectTx)
+    }
+}

+ 14 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/FirmwareVersionTxMessage.swift

@@ -0,0 +1,14 @@
+//
+//  FirmwareVersionTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct FirmwareVersionTxMessage {
+    let opcode: Opcode = .firmwareVersionTx
+}

+ 122 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseBackfillMessage.swift

@@ -0,0 +1,122 @@
+//
+//  GlucoseBackfillMessage.swift
+//  xDripG5
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+// 50 05 02 00 b7ff5200 66045300 00000000 0000 7138
+
+struct GlucoseBackfillTxMessage: RespondableMessage {
+    typealias Response = GlucoseBackfillRxMessage
+
+    let byte1: UInt8
+    let byte2: UInt8
+    let identifier: UInt8
+
+    let startTime: UInt32
+    let endTime: UInt32
+
+    let length: UInt32 = 0
+    let backfillCRC: UInt16 = 0
+
+    var data: Data {
+        var data = Data(for: .glucoseBackfillTx)
+        data.append(contentsOf: [byte1, byte2, identifier])
+        data.append(startTime)
+        data.append(endTime)
+        data.append(length)
+        data.append(backfillCRC)
+
+        return data.appendingCRC()
+    }
+}
+
+// 51 00 01 00 b7ff5200 66045300 32000000 e6cb 9805
+
+struct GlucoseBackfillRxMessage: TransmitterRxMessage {
+    let status: UInt8
+    let backfillStatus: UInt8
+    let identifier: UInt8
+    let startTime: UInt32
+    let endTime: UInt32
+    let bufferLength: UInt32
+    let bufferCRC: UInt16
+
+    init?(data: Data) {
+        guard data.count == 20,
+            data.isCRCValid,
+            data.starts(with: .glucoseBackfillRx)
+        else {
+            return nil
+        }
+
+        status = data[1]
+        backfillStatus = data[2]
+        identifier = data[3]
+        startTime = data[4..<8].toInt()
+        endTime = data[8..<12].toInt()
+        bufferLength = data[12..<16].toInt()
+        bufferCRC = data[16..<18].toInt()
+    }
+}
+
+// 0100bc460000b7ff52008b0006eee30053008500
+// 020006eb0f025300800006ee3a0353007e0006f5
+// 030066045300790006f8
+
+struct GlucoseBackfillFrameBuffer {
+    let identifier: UInt8
+    private var frames: [Data] = []
+
+    init(identifier: UInt8) {
+        self.identifier = identifier
+    }
+
+    mutating func append(_ frame: Data) {
+        // Byte 0 is the frame index
+        // Byte 1 is the identifier
+        guard frame.count > 2,
+            frame[0] == frames.count + 1,
+            frame[1] == identifier else {
+            return
+        }
+
+        frames.append(frame)
+    }
+
+    var count: Int {
+        return frames.reduce(0, { $0 + $1.count })
+    }
+
+    var crc16: UInt16 {
+        return frames.reduce(into: Data(), { $0.append($1) }).crc16
+    }
+
+    var glucose: [GlucoseSubMessage] {
+        // Drop the first 2 bytes from each frame
+        let data = frames.reduce(into: Data(), { $0.append($1.dropFirst(2)) })
+
+        // Drop the first 4 bytes from the combined message
+        // Byte 0: ??
+        // Byte 1: ??
+        // Byte 2: ?? (only seen 0 so far)
+        // Byte 3: ?? (only seen 0 so far)
+        let glucoseData = data.dropFirst(4)
+
+        return stride(
+            from: glucoseData.startIndex,
+            to: glucoseData.endIndex,
+            by: GlucoseSubMessage.size
+        ).compactMap {
+            let range = $0..<$0.advanced(by: GlucoseSubMessage.size)
+            guard glucoseData.endIndex >= range.endIndex else {
+                return nil
+            }
+
+            return GlucoseSubMessage(data: glucoseData[range])
+        }
+    }
+}

+ 14 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseHistoryTxMessage.swift

@@ -0,0 +1,14 @@
+//
+//  GlucoseHistoryTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct GlucoseHistoryTxMessage {
+    let opcode: Opcode = .glucoseHistoryTx
+}

+ 88 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseRxMessage.swift

@@ -0,0 +1,88 @@
+//
+//  GlucoseRxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+public struct GlucoseSubMessage: TransmitterRxMessage {
+    static let size = 8
+
+    public let timestamp: UInt32
+    public let glucoseIsDisplayOnly: Bool
+    public let glucose: UInt16
+    public let state: UInt8
+    public let trend: Int8
+
+    init?(data: Data) {
+        guard data.count >= GlucoseSubMessage.size else {
+            return nil
+        }
+
+        var start = data.startIndex
+        var end = start.advanced(by: 4)
+        timestamp = data[start..<end].toInt()
+
+        start = end
+        end = start.advanced(by: 2)
+        let glucoseBytes = data[start..<end].to(UInt16.self)
+        glucoseIsDisplayOnly = (glucoseBytes & 0xf000) > 0
+        glucose = glucoseBytes & 0xfff
+
+        start = end
+        end = start.advanced(by: 1)
+        state = data[start]
+
+        start = end
+        end = start.advanced(by: 1)
+        trend = Int8(bitPattern: data[start])
+    }
+}
+
+
+public struct GlucoseRxMessage: TransmitterRxMessage {
+    public let status: UInt8
+    public let sequence: UInt32
+    public let glucose: GlucoseSubMessage
+
+    init?(data: Data) {
+        guard data.count >= 16 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .glucoseRx) || data.starts(with: .glucoseG6Rx) else {
+            return nil
+        }
+
+        status = data[1]
+        sequence = data[2..<6].toInt()
+
+        guard let glucose = GlucoseSubMessage(data: data[6...]) else {
+            return nil
+        }
+        self.glucose = glucose
+    }
+}
+
+extension GlucoseSubMessage: Equatable {
+    public static func ==(lhs: GlucoseSubMessage, rhs: GlucoseSubMessage) -> Bool {
+        return lhs.timestamp == rhs.timestamp &&
+            lhs.glucoseIsDisplayOnly == rhs.glucoseIsDisplayOnly &&
+            lhs.glucose == rhs.glucose &&
+            lhs.state == rhs.state &&
+            lhs.trend == rhs.trend
+    }
+}
+
+
+extension GlucoseRxMessage: Equatable {
+    public static func ==(lhs: GlucoseRxMessage, rhs: GlucoseRxMessage) -> Bool {
+        return lhs.status == rhs.status &&
+            lhs.sequence == rhs.sequence &&
+            lhs.glucose == rhs.glucose
+    }
+}

+ 18 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/GlucoseTxMessage.swift

@@ -0,0 +1,18 @@
+//
+//  GlucoseTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct GlucoseTxMessage: RespondableMessage {
+    typealias Response = GlucoseRxMessage
+
+    var data: Data {
+        return Data(for: .glucoseTx).appendingCRC()
+    }
+}

+ 20 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/KeepAliveTxMessage.swift

@@ -0,0 +1,20 @@
+//
+//  KeepAliveTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct KeepAliveTxMessage: TransmitterTxMessage {
+    let time: UInt8
+
+    var data: Data {
+        var data = Data(for: .keepAlive)
+        data.append(time)
+        return data
+    }
+}

+ 30 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/ResetMessage.swift

@@ -0,0 +1,30 @@
+//
+//  ResetMessage.swift
+//  xDripG5
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+
+struct ResetTxMessage: RespondableMessage {
+    typealias Response = ResetRxMessage
+
+    var data: Data {
+        return Data(for: .resetTx).appendingCRC()
+    }
+}
+
+
+struct ResetRxMessage: TransmitterRxMessage {
+    let status: UInt8
+
+    init?(data: Data) {
+        guard data.count >= 2, data.starts(with: .resetRx) else {
+            return nil
+        }
+
+        status = data[1]
+    }
+}

+ 37 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStartRxMessage.swift

@@ -0,0 +1,37 @@
+//
+//  SessionStartRxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 6/4/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct SessionStartRxMessage: TransmitterRxMessage {
+    let status: UInt8
+    let received: UInt8
+
+    // I've only seen examples of these 2 values matching
+    let requestedStartTime: UInt32
+    let sessionStartTime: UInt32
+
+    let transmitterTime: UInt32
+
+    init?(data: Data) {
+        guard data.count == 17 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .sessionStartRx) else {
+            return nil
+        }
+
+        status = data[1]
+        received = data[2]
+        requestedStartTime = data[3..<7].toInt()
+        sessionStartTime = data[7..<11].toInt()
+        transmitterTime = data[11..<15].toInt()
+    }
+}

+ 27 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStartTxMessage.swift

@@ -0,0 +1,27 @@
+//
+//  SessionStartTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct SessionStartTxMessage: RespondableMessage {
+    typealias Response = SessionStartRxMessage
+
+    /// Time since activation in Dex seconds
+    let startTime: UInt32
+
+    /// Time in seconds since Unix Epoch
+    let secondsSince1970: UInt32
+
+    var data: Data {
+        var data = Data(for: .sessionStartTx)
+        data.append(startTime)
+        data.append(secondsSince1970)
+        return data.appendingCRC()
+    }
+}

+ 34 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStopRxMessage.swift

@@ -0,0 +1,34 @@
+//
+//  SessionStopRxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 6/4/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct SessionStopRxMessage: TransmitterRxMessage {
+    let status: UInt8
+    let received: UInt8
+    let sessionStopTime: UInt32
+    let sessionStartTime: UInt32
+    let transmitterTime: UInt32
+
+    init?(data: Data) {
+        guard data.count == 17 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .sessionStopRx) else {
+            return nil
+        }
+
+        status = data[1]
+        received = data[2]
+        sessionStopTime = data[3..<7].toInt()
+        sessionStartTime = data[7..<11].toInt()
+        transmitterTime = data[11..<15].toInt()
+    }
+}

+ 22 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/SessionStopTxMessage.swift

@@ -0,0 +1,22 @@
+//
+//  SessionStopTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct SessionStopTxMessage: RespondableMessage {
+    typealias Response = SessionStopRxMessage
+
+    let stopTime: UInt32
+
+    var data: Data {
+        var data = Data(for: .sessionStopTx)
+        data.append(stopTime)
+        return data.appendingCRC()
+    }
+}

+ 32 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterMessage.swift

@@ -0,0 +1,32 @@
+//
+//  TransmitterCommand.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/22/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+/// A data sequence written to the transmitter
+protocol TransmitterTxMessage {
+
+    /// The data to write
+    var data: Data { get }
+
+}
+
+
+protocol RespondableMessage: TransmitterTxMessage {
+    associatedtype Response: TransmitterRxMessage
+}
+
+
+/// A data sequence received by the transmitter
+protocol TransmitterRxMessage {
+
+
+    init?(data: Data)
+
+}

+ 36 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterTimeRxMessage.swift

@@ -0,0 +1,36 @@
+//
+//  TransmitterTimeRxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct TransmitterTimeRxMessage: TransmitterRxMessage {
+    let status: UInt8
+    let currentTime: UInt32
+    let sessionStartTime: UInt32
+
+    init?(data: Data) {
+        guard data.count == 16 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .transmitterTimeRx) else {
+            return nil
+        }
+
+        status = data[1]
+        currentTime = data[2..<6].toInt()
+        sessionStartTime = data[6..<10].toInt()
+    }
+}
+
+extension TransmitterTimeRxMessage: Equatable { }
+
+func ==(lhs: TransmitterTimeRxMessage, rhs: TransmitterTimeRxMessage) -> Bool {
+    return lhs.currentTime == rhs.currentTime
+}

+ 18 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterTimeTxMessage.swift

@@ -0,0 +1,18 @@
+//
+//  TransmitterTimeTxMessage.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 11/23/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct TransmitterTimeTxMessage: RespondableMessage {
+    typealias Response = TransmitterTimeRxMessage
+
+    var data: Data {
+        return Data(for: .transmitterTimeTx).appendingCRC()
+    }
+}

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterVersionRxMessage.swift

@@ -0,0 +1,29 @@
+//
+//  TransmitterVersionRxMessage.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 9/29/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct TransmitterVersionRxMessage: TransmitterRxMessage {
+    let status: UInt8
+    let firmwareVersion: [UInt8]
+
+    init?(data: Data) {
+        guard data.count == 19 && data.isCRCValid else {
+            return nil
+        }
+
+        guard data.starts(with: .transmitterVersionRx) else {
+            return nil
+        }
+
+        status = data[1]
+        firmwareVersion = data[2..<6].map { $0 }
+    }
+
+}

+ 16 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Messages/TransmitterVersionTxMessage.swift

@@ -0,0 +1,16 @@
+//
+//  TransmitterVersionTxMessage.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+struct TransmitterVersionTxMessage {
+    typealias Response = TransmitterVersionRxMessage
+
+    let opcode: Opcode = .transmitterVersionTx
+}

+ 61 - 0
Dependecies/CGMBLEKit/CGMBLEKit/NSData+CRC.swift

@@ -0,0 +1,61 @@
+//
+//  NSData+CRC.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 4/7/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+/**
+ CRC-CCITT (XModem)
+
+ [http://www.lammertbies.nl/comm/info/crc-calculation.html]()
+ 
+ [http://web.mit.edu/6.115/www/amulet/xmodem.htm]()
+ */
+extension Collection where Element == UInt8 {
+    private var crcCCITTXModem: UInt16 {
+        var crc: UInt16 = 0
+
+        for byte in self {
+            crc ^= UInt16(byte) << 8
+
+            for _ in 0..<8 {
+                if crc & 0x8000 != 0 {
+                    crc = crc << 1 ^ 0x1021
+                } else {
+                    crc = crc << 1
+                }
+            }
+        }
+
+        return crc
+    }
+
+    var crc16: UInt16 {
+        return crcCCITTXModem
+    }
+}
+
+
+extension UInt8 {
+    var crc16: UInt16 {
+        return [self].crc16
+    }
+}
+
+
+extension Data {
+    var isCRCValid: Bool {
+        return dropLast(2).crc16 == suffix(2).toInt()
+    }
+
+    func appendingCRC() -> Data {
+        var data = self
+        data.append(crc16)
+        return data
+    }
+}

+ 50 - 0
Dependecies/CGMBLEKit/CGMBLEKit/OSLog.swift

@@ -0,0 +1,50 @@
+//
+//  OSLog.swift
+//  Loop
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+//
+
+import os.log
+
+
+extension OSLog {
+    convenience init(category: String) {
+        self.init(subsystem: "com.loopkit.CGMBLEKit", category: category)
+    }
+
+    func debug(_ message: StaticString, _ args: CVarArg...) {
+        log(message, type: .debug, args)
+    }
+
+    func info(_ message: StaticString, _ args: CVarArg...) {
+        log(message, type: .info, args)
+    }
+
+    func `default`(_ message: StaticString, _ args: CVarArg...) {
+        log(message, type: .default, args)
+    }
+
+    func error(_ message: StaticString, _ args: CVarArg...) {
+        log(message, type: .error, args)
+    }
+
+    private func log(_ message: StaticString, type: OSLogType, _ args: [CVarArg]) {
+        switch args.count {
+        case 0:
+            os_log(message, log: self, type: type)
+        case 1:
+            os_log(message, log: self, type: type, args[0])
+        case 2:
+            os_log(message, log: self, type: type, args[0], args[1])
+        case 3:
+            os_log(message, log: self, type: type, args[0], args[1], args[2])
+        case 4:
+            os_log(message, log: self, type: type, args[0], args[1], args[2], args[3])
+        case 5:
+            os_log(message, log: self, type: type, args[0], args[1], args[2], args[3], args[4])
+        default:
+            os_log(message, log: self, type: type, args)
+        }
+    }
+}

+ 71 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Opcode.swift

@@ -0,0 +1,71 @@
+//
+//  Opcode.swift
+//  xDripG5
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+enum Opcode: UInt8 {
+    // Auth
+    case authRequestTx = 0x01
+
+    case authRequestRx = 0x03
+    case authChallengeTx = 0x04
+    case authChallengeRx = 0x05
+    case keepAlive = 0x06 // auth; setAdvertisementParametersTx for control
+    case bondRequest = 0x07
+    
+    // Control
+    case disconnectTx = 0x09
+    
+    case setAdvertisementParametersRx = 0x1c
+
+    case firmwareVersionTx = 0x20
+    case firmwareVersionRx = 0x21
+    case batteryStatusTx = 0x22
+    case batteryStatusRx = 0x23
+    case transmitterTimeTx = 0x24
+    case transmitterTimeRx = 0x25
+    case sessionStartTx = 0x26
+    case sessionStartRx = 0x27
+    case sessionStopTx = 0x28
+    case sessionStopRx = 0x29
+
+    case glucoseTx = 0x30
+    case glucoseRx = 0x31
+    case calibrationDataTx = 0x32
+    case calibrationDataRx = 0x33
+    case calibrateGlucoseTx = 0x34
+    case calibrateGlucoseRx = 0x35
+
+    case glucoseHistoryTx = 0x3e
+
+    case resetTx = 0x42
+    case resetRx = 0x43
+
+    case transmitterVersionTx = 0x4a
+    case transmitterVersionRx = 0x4b
+    
+    case glucoseG6Tx = 0x4e
+    case glucoseG6Rx = 0x4f
+
+    case glucoseBackfillTx = 0x50
+    case glucoseBackfillRx = 0x51
+}
+
+
+extension Data {
+    init(for opcode: Opcode) {
+        self.init([opcode.rawValue])
+    }
+
+    func starts(with opcode: Opcode) -> Bool {
+        guard count > 0 else {
+            return false
+        }
+
+        return self[startIndex] == opcode.rawValue
+    }
+}

+ 150 - 0
Dependecies/CGMBLEKit/CGMBLEKit/PeripheralManager+G5.swift

@@ -0,0 +1,150 @@
+//
+//  PeripheralManager+G5.swift
+//  xDripG5
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+//
+
+import CoreBluetooth
+import os.log
+
+
+private let log = OSLog(category: "PeripheralManager+G5")
+
+
+extension PeripheralManager {
+    private func getCharacteristicWithUUID(_ uuid: CGMServiceCharacteristicUUID) -> CBCharacteristic? {
+        return peripheral.getCharacteristicWithUUID(uuid)
+    }
+
+    func setNotifyValue(_ enabled: Bool,
+        for characteristicUUID: CGMServiceCharacteristicUUID,
+        timeout: TimeInterval = 2) throws
+    {
+        guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
+            throw PeripheralManagerError.unknownCharacteristic
+        }
+
+        try setNotifyValue(enabled, for: characteristic, timeout: timeout)
+    }
+
+    func readMessage<R: TransmitterRxMessage>(
+        for characteristicUUID: CGMServiceCharacteristicUUID,
+        timeout: TimeInterval = 2
+    ) throws -> R
+    {
+        guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
+            throw PeripheralManagerError.unknownCharacteristic
+        }
+
+        var capturedResponse: R?
+
+        try runCommand(timeout: timeout) {
+            addCondition(.valueUpdate(characteristic: characteristic, matching: { (data) -> Bool in
+                guard let value = data else {
+                    return false
+                }
+
+                guard let response = R(data: value) else {
+                    // We don't recognize the contents. Keep listening.
+                    return false
+                }
+
+                capturedResponse = response
+                return true
+            }))
+
+            peripheral.readValue(for: characteristic)
+        }
+
+        guard let response = capturedResponse else {
+            // TODO: This is an "unknown value" issue, not a timeout
+            if let value = characteristic.value {
+                log.error("Unknown response data: %{public}@", value.hexadecimalString)
+            }
+            throw PeripheralManagerError.timeout
+        }
+
+        return response
+    }
+
+    /// - Throws: PeripheralManagerError
+    func writeMessage<T: RespondableMessage>(_ message: T,
+        for characteristicUUID: CGMServiceCharacteristicUUID,
+        type: CBCharacteristicWriteType = .withResponse,
+        timeout: TimeInterval = 2
+    ) throws -> T.Response
+    {
+        guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
+            throw PeripheralManagerError.unknownCharacteristic
+        }
+
+        var capturedResponse: T.Response?
+
+        try runCommand(timeout: timeout) {
+            if case .withResponse = type {
+                addCondition(.write(characteristic: characteristic))
+            }
+
+            if characteristic.isNotifying {
+                addCondition(.valueUpdate(characteristic: characteristic, matching: { (data) -> Bool in
+                    guard let value = data else {
+                        return false
+                    }
+
+                    guard let response = T.Response(data: value) else {
+                        // We don't recognize the contents. Keep listening.
+                        return false
+                    }
+
+                    capturedResponse = response
+                    return true
+                }))
+            }
+
+            peripheral.writeValue(message.data, for: characteristic, type: type)
+        }
+
+        guard let response = capturedResponse else {
+            // TODO: This is an "unknown value" issue, not a timeout
+            if let value = characteristic.value {
+                log.error("Unknown response data: %{public}@", value.hexadecimalString)
+            }
+            throw PeripheralManagerError.timeout
+        }
+
+        return response
+    }
+
+    /// - Throws: PeripheralManagerError
+    func writeMessage(_ message: TransmitterTxMessage,
+        for characteristicUUID: CGMServiceCharacteristicUUID,
+        type: CBCharacteristicWriteType = .withResponse,
+        timeout: TimeInterval = 2) throws
+    {
+        guard let characteristic = getCharacteristicWithUUID(characteristicUUID) else {
+            throw PeripheralManagerError.unknownCharacteristic
+        }
+
+        try writeValue(message.data, for: characteristic, type: type, timeout: timeout)
+    }
+}
+
+
+fileprivate extension CBPeripheral {
+    func getServiceWithUUID(_ uuid: TransmitterServiceUUID) -> CBService? {
+        return services?.itemWithUUIDString(uuid.rawValue)
+    }
+
+    func getCharacteristicForServiceUUID(_ serviceUUID: TransmitterServiceUUID, withUUIDString UUIDString: String) -> CBCharacteristic? {
+        guard let characteristics = getServiceWithUUID(serviceUUID)?.characteristics else {
+            return nil
+        }
+
+        return characteristics.itemWithUUIDString(UUIDString)
+    }
+
+    func getCharacteristicWithUUID(_ uuid: CGMServiceCharacteristicUUID) -> CBCharacteristic? {
+        return getCharacteristicForServiceUUID(.cgmService, withUUIDString: uuid.rawValue)
+    }
+}

+ 471 - 0
Dependecies/CGMBLEKit/CGMBLEKit/PeripheralManager.swift

@@ -0,0 +1,471 @@
+//
+//  PeripheralManager.swift
+//  xDripG5
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+//
+
+import CoreBluetooth
+import Foundation
+import os.log
+
+
+class PeripheralManager: NSObject {
+
+    private let log = OSLog(category: "PeripheralManager")
+
+    ///
+    /// This is mutable, because CBPeripheral instances can seemingly become invalid, and need to be periodically re-fetched from CBCentralManager
+    var peripheral: CBPeripheral {
+        didSet {
+            guard oldValue !== peripheral else {
+                return
+            }
+
+            log.error("Replacing peripheral reference %{public}@ -> %{public}@", oldValue, peripheral)
+
+            oldValue.delegate = nil
+            peripheral.delegate = self
+
+            queue.sync {
+                self.needsConfiguration = true
+            }
+        }
+    }
+
+    /// The dispatch queue used to serialize operations on the peripheral
+    let queue = DispatchQueue(label: "com.loopkit.PeripheralManager.queue", qos: .unspecified)
+
+    /// The condition used to signal command completion
+    private let commandLock = NSCondition()
+
+    /// The required conditions for the operation to complete
+    private var commandConditions = [CommandCondition]()
+
+    /// Any error surfaced during the active operation
+    private var commandError: Error?
+
+    private(set) weak var central: CBCentralManager?
+
+    let configuration: Configuration
+
+    // Confined to `queue`
+    private var needsConfiguration = true
+
+    weak var delegate: PeripheralManagerDelegate? {
+        didSet {
+            queue.sync {
+                needsConfiguration = true
+            }
+        }
+    }
+
+    init(peripheral: CBPeripheral, configuration: Configuration, centralManager: CBCentralManager) {
+        self.peripheral = peripheral
+        self.central = centralManager
+        self.configuration = configuration
+
+        super.init()
+
+        peripheral.delegate = self
+
+        assertConfiguration()
+    }
+}
+
+
+// MARK: - Nested types
+extension PeripheralManager {
+    struct Configuration {
+        var serviceCharacteristics: [CBUUID: [CBUUID]] = [:]
+        var notifyingCharacteristics: [CBUUID: [CBUUID]] = [:]
+        var valueUpdateMacros: [CBUUID: (_ manager: PeripheralManager) -> Void] = [:]
+    }
+
+    enum CommandCondition {
+        case notificationStateUpdate(characteristicUUID: CBUUID, enabled: Bool)
+        case valueUpdate(characteristic: CBCharacteristic, matching: ((Data?) -> Bool)?)
+        case write(characteristic: CBCharacteristic)
+        case discoverServices
+        case discoverCharacteristicsForService(serviceUUID: CBUUID)
+    }
+}
+
+protocol PeripheralManagerDelegate: AnyObject {
+    func peripheralManager(_ manager: PeripheralManager, didUpdateValueFor characteristic: CBCharacteristic)
+
+    func peripheralManager(_ manager: PeripheralManager, didReadRSSI RSSI: NSNumber, error: Error?)
+
+    func peripheralManagerDidUpdateName(_ manager: PeripheralManager)
+
+    func completeConfiguration(for manager: PeripheralManager) throws
+}
+
+
+// MARK: - Operation sequence management
+extension PeripheralManager {
+    func configureAndRun(_ block: @escaping (_ manager: PeripheralManager) -> Void) -> (() -> Void) {
+        return {
+            if !self.needsConfiguration && self.peripheral.services == nil {
+                self.log.error("Configured peripheral has no services. Reconfiguring…")
+            }
+
+            if self.needsConfiguration || self.peripheral.services == nil {
+                do {
+                    try self.applyConfiguration()
+                    self.log.default("Peripheral configuration completed")
+                } catch let error {
+                    self.log.error("Error applying peripheral configuration: %@", String(describing: error))
+                    // Will retry
+                }
+
+                do {
+                    if let delegate = self.delegate {
+                        try delegate.completeConfiguration(for: self)
+                        self.log.default("Delegate configuration completed")
+                        self.needsConfiguration = false
+                    } else {
+                        self.log.error("No delegate set configured")
+                    }
+                } catch let error {
+                    self.log.error("Error applying delegate configuration: %@", String(describing: error))
+                    // Will retry
+                }
+            }
+
+            block(self)
+        }
+    }
+
+    func perform(_ block: @escaping (_ manager: PeripheralManager) -> Void) {
+        queue.async(execute: configureAndRun(block))
+    }
+
+    private func assertConfiguration() {
+        perform { (_) in
+            // Intentionally empty to trigger configuration if necessary
+        }
+    }
+
+    private func applyConfiguration(discoveryTimeout: TimeInterval = 2) throws {
+        try discoverServices(configuration.serviceCharacteristics.keys.map { $0 }, timeout: discoveryTimeout)
+
+        for service in peripheral.services ?? [] {
+            guard let characteristics = configuration.serviceCharacteristics[service.uuid] else {
+                // Not all services may have characteristics
+                continue
+            }
+
+            try discoverCharacteristics(characteristics, for: service, timeout: discoveryTimeout)
+        }
+
+        for (serviceUUID, characteristicUUIDs) in configuration.notifyingCharacteristics {
+            guard let service = peripheral.services?.itemWithUUID(serviceUUID) else {
+                throw PeripheralManagerError.unknownCharacteristic
+            }
+
+            for characteristicUUID in characteristicUUIDs {
+                guard let characteristic = service.characteristics?.itemWithUUID(characteristicUUID) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+
+                guard !characteristic.isNotifying else {
+                    continue
+                }
+
+                try setNotifyValue(true, for: characteristic, timeout: discoveryTimeout)
+            }
+        }
+    }
+}
+
+
+// MARK: - Synchronous Commands
+extension PeripheralManager {
+    /// - Throws: PeripheralManagerError
+    func runCommand(timeout: TimeInterval, command: () -> Void) throws {
+        // Prelude
+        dispatchPrecondition(condition: .onQueue(queue))
+        guard central?.state == .poweredOn && peripheral.state == .connected else {
+            throw PeripheralManagerError.notReady
+        }
+
+        commandLock.lock()
+
+        defer {
+            commandLock.unlock()
+        }
+
+        guard commandConditions.isEmpty else {
+            throw PeripheralManagerError.invalidConfiguration
+        }
+
+        // Run
+        command()
+
+        guard !commandConditions.isEmpty else {
+            // If the command didn't add any conditions, then finish immediately
+            return
+        }
+
+        // Postlude
+        let signaled = commandLock.wait(until: Date(timeIntervalSinceNow: timeout))
+
+        defer {
+            commandError = nil
+            commandConditions = []
+        }
+
+        guard signaled else {
+            throw PeripheralManagerError.timeout
+        }
+
+        if let error = commandError {
+            throw PeripheralManagerError.cbPeripheralError(error)
+        }
+    }
+
+    /// It's illegal to call this without first acquiring the commandLock
+    ///
+    /// - Parameter condition: The condition to add
+    func addCondition(_ condition: CommandCondition) {
+        dispatchPrecondition(condition: .onQueue(queue))
+        commandConditions.append(condition)
+    }
+
+    func discoverServices(_ serviceUUIDs: [CBUUID], timeout: TimeInterval) throws {
+        let servicesToDiscover = peripheral.servicesToDiscover(from: serviceUUIDs)
+
+        guard servicesToDiscover.count > 0 else {
+            return
+        }
+
+        try runCommand(timeout: timeout) {
+            addCondition(.discoverServices)
+
+            peripheral.discoverServices(serviceUUIDs)
+        }
+    }
+
+    func discoverCharacteristics(_ characteristicUUIDs: [CBUUID], for service: CBService, timeout: TimeInterval) throws {
+        let characteristicsToDiscover = peripheral.characteristicsToDiscover(from: characteristicUUIDs, for: service)
+
+        guard characteristicsToDiscover.count > 0 else {
+            return
+        }
+
+        try runCommand(timeout: timeout) {
+            addCondition(.discoverCharacteristicsForService(serviceUUID: service.uuid))
+
+            peripheral.discoverCharacteristics(characteristicsToDiscover, for: service)
+        }
+    }
+
+    /// - Throws: PeripheralManagerError
+    func setNotifyValue(_ enabled: Bool, for characteristic: CBCharacteristic, timeout: TimeInterval) throws {
+        try runCommand(timeout: timeout) {
+            addCondition(.notificationStateUpdate(characteristicUUID: characteristic.uuid, enabled: enabled))
+
+            peripheral.setNotifyValue(enabled, for: characteristic)
+        }
+    }
+
+    /// - Throws: PeripheralManagerError
+    func readValue(for characteristic: CBCharacteristic, timeout: TimeInterval) throws -> Data? {
+        try runCommand(timeout: timeout) {
+            addCondition(.valueUpdate(characteristic: characteristic, matching: nil))
+
+            peripheral.readValue(for: characteristic)
+        }
+
+        return characteristic.value
+    }
+
+    /// - Throws: PeripheralManagerError
+    func wait(for characteristic: CBCharacteristic, timeout: TimeInterval) throws -> Data {
+        try runCommand(timeout: timeout) {
+            addCondition(.valueUpdate(characteristic: characteristic, matching: nil))
+        }
+
+        guard let value = characteristic.value else {
+            throw PeripheralManagerError.timeout
+        }
+
+        return value
+    }
+
+    /// - Throws: PeripheralManagerError
+    func writeValue(_ value: Data, for characteristic: CBCharacteristic, type: CBCharacteristicWriteType, timeout: TimeInterval) throws {
+        try runCommand(timeout: timeout) {
+            if case .withResponse = type {
+                addCondition(.write(characteristic: characteristic))
+            }
+
+            peripheral.writeValue(value, for: characteristic, type: type)
+        }
+    }
+}
+
+
+// MARK: - Delegate methods executed on the central's queue
+extension PeripheralManager: CBPeripheralDelegate {
+
+    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
+        commandLock.lock()
+
+        if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
+            if case .discoverServices = condition {
+                return true
+            } else {
+                return false
+            }
+        }) {
+            commandConditions.remove(at: index)
+            commandError = error
+
+            if commandConditions.isEmpty {
+                commandLock.broadcast()
+            }
+        }
+
+        commandLock.unlock()
+    }
+
+    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
+        commandLock.lock()
+
+        if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
+            if case .discoverCharacteristicsForService(serviceUUID: service.uuid) = condition {
+                return true
+            } else {
+                return false
+            }
+        }) {
+            commandConditions.remove(at: index)
+            commandError = error
+
+            if commandConditions.isEmpty {
+                commandLock.broadcast()
+            }
+        }
+
+        commandLock.unlock()
+    }
+
+    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
+        commandLock.lock()
+
+        if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
+            if case .notificationStateUpdate(characteristicUUID: characteristic.uuid, enabled: characteristic.isNotifying) = condition {
+                return true
+            } else {
+                return false
+            }
+        }) {
+            commandConditions.remove(at: index)
+            commandError = error
+
+            if commandConditions.isEmpty {
+                commandLock.broadcast()
+            }
+        }
+
+        commandLock.unlock()
+    }
+
+    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
+        commandLock.lock()
+
+        if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
+            if case .write(characteristic: characteristic) = condition {
+                return true
+            } else {
+                return false
+            }
+        }) {
+            commandConditions.remove(at: index)
+            commandError = error
+
+            if commandConditions.isEmpty {
+                commandLock.broadcast()
+            }
+        }
+
+        commandLock.unlock()
+    }
+
+    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
+        commandLock.lock()
+
+        var notifyDelegate = false
+
+        if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
+            if case .valueUpdate(characteristic: characteristic, matching: let matching) = condition {
+                return matching?(characteristic.value) ?? true
+            } else {
+                return false
+            }
+        }) {
+            commandConditions.remove(at: index)
+            commandError = error
+
+            if commandConditions.isEmpty {
+                commandLock.broadcast()
+            }
+        } else if let macro = configuration.valueUpdateMacros[characteristic.uuid] {
+            macro(self)
+        } else if commandConditions.isEmpty {
+            notifyDelegate = true // execute after the unlock
+        }
+
+        commandLock.unlock()
+
+        if notifyDelegate {
+            // If we weren't expecting this notification, pass it along to the delegate
+            delegate?.peripheralManager(self, didUpdateValueFor: characteristic)
+        }
+    }
+
+    func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
+        delegate?.peripheralManager(self, didReadRSSI: RSSI, error: error)
+    }
+
+    func peripheralDidUpdateName(_ peripheral: CBPeripheral) {
+        delegate?.peripheralManagerDidUpdateName(self)
+    }
+}
+
+
+extension PeripheralManager: CBCentralManagerDelegate {
+    func centralManagerDidUpdateState(_ central: CBCentralManager) {
+        switch central.state {
+        case .poweredOn:
+            assertConfiguration()
+        default:
+            break
+        }
+    }
+
+    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
+        switch peripheral.state {
+        case .connected:
+            assertConfiguration()
+        default:
+            break
+        }
+    }
+}
+
+
+extension PeripheralManager {
+    public override var debugDescription: String {
+        var items = [
+            "## PeripheralManager",
+            "peripheral: \(peripheral)",
+        ]
+        queue.sync {
+            items.append("needsConfiguration: \(needsConfiguration)")
+        }
+        return items.joined(separator: "\n")
+    }
+}

+ 44 - 0
Dependecies/CGMBLEKit/CGMBLEKit/PeripheralManagerError.swift

@@ -0,0 +1,44 @@
+//
+//  PeripheralManagerError.swift
+//  xDripG5
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+//
+
+import CoreBluetooth
+
+
+enum PeripheralManagerError: Error {
+    case cbPeripheralError(Error)
+    case notReady
+    case invalidConfiguration
+    case timeout
+    case unknownCharacteristic
+}
+
+
+extension PeripheralManagerError: LocalizedError {
+    var errorDescription: String? {
+        switch self {
+        case .cbPeripheralError(let error):
+            return error.localizedDescription
+        case .notReady:
+            return LocalizedString("Peripheral isnʼt connected", comment: "Not ready error description")
+        case .invalidConfiguration:
+            return LocalizedString("Peripheral command was invalid", comment: "invlid config error description")
+        case .timeout:
+            return LocalizedString("Peripheral did not respond in time", comment: "Timeout error description")
+        case .unknownCharacteristic:
+            return LocalizedString("Unknown characteristic", comment: "Error description")
+        }
+    }
+
+    var failureReason: String? {
+        switch self {
+        case .cbPeripheralError(let error as NSError):
+            return error.localizedFailureReason
+        default:
+            return errorDescription
+        }
+    }
+}

+ 576 - 0
Dependecies/CGMBLEKit/CGMBLEKit/Transmitter.swift

@@ -0,0 +1,576 @@
+//
+//  Transmitter.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 11/22/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+import CoreBluetooth
+import HealthKit
+import os.log
+
+
+public protocol TransmitterDelegate: AnyObject {
+    func transmitterDidConnect(_ transmitter: Transmitter)
+
+    func transmitter(_ transmitter: Transmitter, didError error: Error)
+
+    func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose)
+
+    func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose])
+
+    func transmitter(_ transmitter: Transmitter, didReadUnknownData data: Data)
+}
+
+/// These methods are called on a private background queue. It is the responsibility of the client to ensure thread-safety.
+public protocol TransmitterCommandSource: AnyObject {
+    func dequeuePendingCommand(for transmitter: Transmitter) -> Command?
+
+    func transmitter(_ transmitter: Transmitter, didFail command: Command, with error: Error)
+
+    func transmitter(_ transmitter: Transmitter, didComplete command: Command)
+}
+
+public enum TransmitterError: Error {
+    case authenticationError(String)
+    case controlError(String)
+    case observationError(String)
+}
+
+extension TransmitterError: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .authenticationError(let description):
+            return description
+        case .controlError(let description):
+            return description
+        case .observationError(let description):
+            return description
+        }
+    }
+}
+
+
+public final class Transmitter: BluetoothManagerDelegate {
+
+    /// The ID of the transmitter to connect to
+    public var ID: String {
+        return id.id
+    }
+
+    private var id: TransmitterID
+
+    public var passiveModeEnabled: Bool
+
+    public weak var delegate: TransmitterDelegate?
+
+    public weak var commandSource: TransmitterCommandSource?
+
+    // MARK: - Passive observation state, confined to `bluetoothManager.managerQueue`
+
+    /// The initial activation date of the transmitter
+    private var activationDate: Date?
+
+    /// The last-observed time message
+    private var lastTimeMessage: TransmitterTimeRxMessage? {
+        didSet {
+            if let time = lastTimeMessage {
+                activationDate = Date(timeIntervalSinceNow: -TimeInterval(time.currentTime))
+            } else {
+                activationDate = nil
+            }
+        }
+    }
+
+    /// The last-observed calibration message
+    private var lastCalibrationMessage: CalibrationDataRxMessage?
+
+    /// The backfill data buffer
+    private var backfillBuffer: GlucoseBackfillFrameBuffer?
+
+    // MARK: -
+
+    private let log = OSLog(category: "Transmitter")
+
+    private let bluetoothManager = BluetoothManager()
+
+    private let delegateQueue = DispatchQueue(label: "com.loudnate.CGMBLEKit.delegateQueue", qos: .unspecified)
+
+    public init(id: String, peripheralIdentifier: UUID? = nil, passiveModeEnabled: Bool = false) {
+        self.id = TransmitterID(id: id)
+        self.passiveModeEnabled = passiveModeEnabled
+
+        bluetoothManager.peripheralIdentifier = peripheralIdentifier
+        bluetoothManager.delegate = self
+    }
+
+    public func resumeScanning() {
+        if stayConnected {
+            bluetoothManager.scanForPeripheral()
+        }
+    }
+
+    public func stopScanning() {
+        bluetoothManager.disconnect()
+    }
+
+    public var isScanning: Bool {
+        return bluetoothManager.isScanning
+    }
+
+    public var peripheralIdentifier: UUID? {
+        get {
+            return bluetoothManager.peripheralIdentifier
+        }
+        set {
+            bluetoothManager.peripheralIdentifier = newValue
+        }
+    }
+
+    public var stayConnected: Bool {
+        get {
+            return bluetoothManager.stayConnected
+        }
+        set {
+            bluetoothManager.stayConnected = newValue
+
+            if newValue {
+                bluetoothManager.scanForPeripheral()
+            }
+        }
+    }
+
+    // MARK: - BluetoothManagerDelegate
+
+    func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, isReadyWithError error: Error?) {
+        if let error = error {
+            delegateQueue.async {
+                self.delegate?.transmitter(self, didError: error)
+            }
+            return
+        }
+
+        delegateQueue.async {
+            self.delegate?.transmitterDidConnect(self)
+        }
+
+        peripheralManager.perform { (peripheral) in
+            if self.passiveModeEnabled {
+                self.log.debug("Listening for authentication responses in passive mode")
+                do {
+                    try peripheral.listenToCharacteristic(.authentication)
+                } catch let error {
+                    self.delegateQueue.async {
+                        self.delegate?.transmitter(self, didError: error)
+                    }
+                }
+            } else {
+                do {
+                    self.log.debug("Authenticating with transmitter")
+                    let status = try peripheral.authenticate(id: self.id)
+
+                    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.isBonded)
+                    defer {
+                        self.log.debug("Initiating a disconnect")
+                        peripheral.disconnect()
+                    }
+
+                    self.log.debug("Reading time")
+                    let timeMessage = try peripheral.readTimeMessage()
+
+                    let activationDate = Date(timeIntervalSinceNow: -TimeInterval(timeMessage.currentTime))
+                    self.log.debug("Determined activation date: %@", String(describing: activationDate))
+
+                    while let command = self.commandSource?.dequeuePendingCommand(for: self) {
+                        self.log.debug("Sending command: %@", String(describing: command))
+                        do {
+                            _ = try peripheral.sendCommand(command, activationDate: activationDate)
+                            self.commandSource?.transmitter(self, didComplete: command)
+                        } catch let error {
+                            self.commandSource?.transmitter(self, didFail: command, with: error)
+                        }
+                    }
+
+                    self.log.debug("Reading glucose")
+                    let glucoseMessage = try peripheral.readGlucose()
+                    self.log.debug("Reading calibration data")
+                    let calibrationMessage = try? peripheral.readCalibrationData()
+
+                    let glucose = Glucose(
+                        transmitterID: self.id.id,
+                        glucoseMessage: glucoseMessage,
+                        timeMessage: timeMessage,
+                        calibrationMessage: calibrationMessage,
+                        activationDate: activationDate
+                    )
+
+                    self.delegateQueue.async {
+                        self.delegate?.transmitter(self, didRead: glucose)
+                    }
+                } catch let error {
+                    self.delegateQueue.async {
+                        self.delegate?.transmitter(self, didError: error)
+                    }
+                }
+            }
+        }
+    }
+
+    func bluetoothManager(_ manager: BluetoothManager, shouldConnectPeripheral peripheral: CBPeripheral) -> Bool {
+        /// The Dexcom G5 advertises a peripheral name of "DexcomXX"
+        /// where "XX" is the last-two characters of the transmitter ID.
+        if let name = peripheral.name, name.suffix(2) == id.id.suffix(2) {
+            return true
+        } else {
+            self.log.info("Not connecting to peripheral: %{public}@", peripheral.name ?? String(describing: peripheral))
+            return false
+        }
+    }
+
+    func bluetoothManager(_ manager: BluetoothManager, peripheralManager: PeripheralManager, didReceiveControlResponse response: Data) {
+        guard passiveModeEnabled else { return }
+
+        guard response.count > 0 else { return }
+
+        switch Opcode(rawValue: response[0]) {
+        case .glucoseRx?, .glucoseG6Rx?:
+            if  let glucoseMessage = GlucoseRxMessage(data: response),
+                let timeMessage = lastTimeMessage,
+                let activationDate = activationDate
+            {
+                delegateQueue.async {
+                    self.delegate?.transmitter(self, didRead: Glucose(transmitterID: self.id.id, glucoseMessage: glucoseMessage, timeMessage: timeMessage, calibrationMessage: self.lastCalibrationMessage, activationDate: activationDate))
+                }
+            } else {
+                delegateQueue.async {
+                    self.delegate?.transmitter(self, didError: TransmitterError.observationError("Unable to handle glucose control response"))
+                }
+            }
+
+            peripheralManager.perform { (peripheral) in
+                // Subscribe to backfill updates
+                do {
+                    try peripheral.listenToCharacteristic(.backfill)
+                } catch let error {
+                    self.log.error("Error trying to enable notifications on backfill characteristic: %{public}@", String(describing: error))
+                    self.delegateQueue.async {
+                        self.delegate?.transmitter(self, didError: error)
+                    }
+                }
+            }
+        case .transmitterTimeRx?:
+            if let timeMessage = TransmitterTimeRxMessage(data: response) {
+                self.lastTimeMessage = timeMessage
+            }
+        case .glucoseBackfillRx?:
+            guard let backfillMessage = GlucoseBackfillRxMessage(data: response) else {
+                break
+            }
+
+            guard let backfillBuffer = backfillBuffer else {
+                log.error("Received GlucoseBackfillRxMessage %{public}@ but backfillBuffer is nil", String(describing: backfillMessage))
+                self.delegateQueue.async {
+                    self.delegate?.transmitter(self, didError: TransmitterError.observationError("Received GlucoseBackfillRxMessage but backfillBuffer is nil"))
+                }
+                break
+            }
+
+            guard let timeMessage = lastTimeMessage, let activationDate = activationDate else {
+                log.error("Received GlucoseBackfillRxMessage %{public}@ but activationDate is unknown", String(describing: backfillMessage))
+                self.delegateQueue.async {
+                    self.delegate?.transmitter(self, didError: TransmitterError.observationError("Received GlucoseBackfillRxMessage but activationDate is unknown"))
+                }
+                break
+            }
+
+            guard backfillMessage.bufferLength == backfillBuffer.count else {
+                log.error("GlucoseBackfillRxMessage expected buffer length %d, but was %d", backfillMessage.bufferLength, backfillBuffer.count)
+                self.delegateQueue.async {
+                    self.delegate?.transmitter(self, didError: TransmitterError.observationError("GlucoseBackfillRxMessage expected buffer length \(backfillMessage.bufferLength), but was \(backfillBuffer.count): \(response.hexadecimalString) "))
+                }
+                break
+            }
+
+            guard backfillMessage.bufferCRC == backfillBuffer.crc16 else {
+                log.error("GlucoseBackfillRxMessage expected CRC %04x, but was %04x", backfillMessage.bufferCRC, backfillBuffer.crc16)
+                self.delegateQueue.async {
+                    self.delegate?.transmitter(self, didError: TransmitterError.observationError("GlucoseBackfillRxMessage expected CRC \(backfillMessage.bufferCRC), but was \(backfillBuffer.crc16)"))
+                }
+                break
+            }
+
+            let glucose = backfillBuffer.glucose.map {
+                Glucose(transmitterID: id.id, status: backfillMessage.status, glucoseMessage: $0, timeMessage: timeMessage, activationDate: activationDate)
+            }
+
+            guard glucose.count > 0 else {
+                break
+            }
+
+            guard glucose.first!.glucoseMessage.timestamp == backfillMessage.startTime,
+                glucose.last!.glucoseMessage.timestamp == backfillMessage.endTime,
+                glucose.first!.glucoseMessage.timestamp <= glucose.last!.glucoseMessage.timestamp
+            else {
+                log.error("GlucoseBackfillRxMessage time interval not reflected in glucose: %{public}@, buffer: %{public}@", response.hexadecimalString, String(reflecting: backfillBuffer))
+
+                self.delegateQueue.async {
+                    self.delegate?.transmitter(self, didError: TransmitterError.observationError("GlucoseBackfillRxMessage time interval not reflected in glucose: \(backfillMessage.startTime) - \(backfillMessage.endTime), buffer: \(glucose.first!.glucoseMessage.timestamp) - \(glucose.last!.glucoseMessage.timestamp)"))
+                }
+                break
+            }
+
+            delegateQueue.async {
+                self.delegate?.transmitter(self, didReadBackfill: glucose)
+            }
+        case .calibrationDataRx?:
+            guard let calibrationDataMessage = CalibrationDataRxMessage(data: response) else {
+                break
+            }
+
+            lastCalibrationMessage = calibrationDataMessage
+        case .none:
+            delegateQueue.async {
+                self.delegate?.transmitter(self, didReadUnknownData: response)
+            }
+        default:
+            // We ignore all other known opcodes
+            break
+        }
+    }
+
+    func bluetoothManager(_ manager: BluetoothManager, didReceiveBackfillResponse response: Data) {
+        guard response.count > 2 else {
+            return
+        }
+
+        if response[0] == 1 {
+            log.info("Starting new backfill buffer with ID %d", response[1])
+
+            self.backfillBuffer = GlucoseBackfillFrameBuffer(identifier: response[1])
+        }
+        
+        log.info("appending to backfillBuffer: %@", response.hexadecimalString)
+
+        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
+                // Stopping updates from authentication simultaneously with Dexcom's app causes CoreBluetooth to get into a weird state.
+                /*
+                do {
+                    try peripheral.stopListeningToCharacteristic(.authentication)
+                } catch let error {
+                    self.log.error("Error trying to disable notifications on authentication characteristic: %{public}@", String(describing: error))
+                }
+                */
+
+                do {
+                    try peripheral.listenToCharacteristic(.control)
+                } catch let error {
+                    self.log.error("Error trying to enable notifications on control characteristic: %{public}@", String(describing: error))
+                    self.delegateQueue.async {
+                        self.delegate?.transmitter(self, didError: error)
+                    }
+                }
+            }
+        } else {
+            self.log.debug("Ignoring authentication response: %{public}@", response.hexadecimalString)
+        }
+    }
+}
+
+extension Transmitter: CustomDebugStringConvertible {
+    public var debugDescription: String {
+        return [
+            "## Transmitter",
+            String(reflecting: bluetoothManager),
+        ].joined(separator: "\n")
+    }
+}
+
+
+struct TransmitterID {
+    let id: String
+
+    init(id: String) {
+        self.id = id
+    }
+
+    private var cryptKey: Data? {
+        return "00\(id)00\(id)".data(using: .utf8)
+    }
+
+    func computeHash(of data: Data) -> Data? {
+        guard data.count == 8, let key = cryptKey else {
+            return nil
+        }
+
+        var doubleData = Data(capacity: data.count * 2)
+        doubleData.append(data)
+        doubleData.append(data)
+
+        guard let outData = try? AESCrypt.encryptData(doubleData, usingKey: key) else {
+            return nil
+        }
+
+        return outData[0..<8]
+    }
+}
+
+
+// MARK: - Helpers
+fileprivate extension PeripheralManager {
+    func authenticate(id: TransmitterID) throws -> AuthChallengeRxMessage {
+        let authMessage = AuthRequestTxMessage()
+
+        do {
+            try writeMessage(authMessage, for: .authentication)
+        } catch let error {
+            throw TransmitterError.authenticationError("Error writing transmitter challenge: \(error)")
+        }
+
+        let authResponse: AuthRequestRxMessage
+        do {
+            authResponse = try readMessage(for: .authentication)
+        } catch let error {
+            throw TransmitterError.authenticationError("Unable to parse auth challenge: \(error)")
+        }
+
+        guard authResponse.tokenHash == id.computeHash(of: authMessage.singleUseToken) else {
+            throw TransmitterError.authenticationError("Transmitter failed auth challenge")
+        }
+
+        guard let challengeHash = id.computeHash(of: authResponse.challenge) else {
+            throw TransmitterError.authenticationError("Failed to compute challenge hash for transmitter ID")
+        }
+
+        do {
+            try writeMessage(AuthChallengeTxMessage(challengeHash: challengeHash), for: .authentication)
+        } catch let error {
+            throw TransmitterError.authenticationError("Error writing challenge response: \(error)")
+        }
+
+        let challengeResponse: AuthChallengeRxMessage
+        do {
+            challengeResponse = try readMessage(for: .authentication)
+        } catch let error {
+            throw TransmitterError.authenticationError("Unable to parse auth status: \(error)")
+        }
+
+        guard challengeResponse.isAuthenticated else {
+            throw TransmitterError.authenticationError("Transmitter rejected auth challenge")
+        }
+
+        return challengeResponse
+    }
+
+    func requestBond() throws {
+        do {
+            try writeMessage(KeepAliveTxMessage(time: 25), for: .authentication)
+        } catch let error {
+            throw TransmitterError.authenticationError("Error writing keep-alive for bond: \(error)")
+        }
+
+        do {
+            try writeMessage(BondRequestTxMessage(), for: .authentication)
+        } catch let error {
+            throw TransmitterError.authenticationError("Error writing bond request: \(error)")
+        }
+    }
+
+    func enableNotify(shouldWaitForBond: Bool = false) throws {
+        do {
+            if shouldWaitForBond {
+                try setNotifyValue(true, for: .control, timeout: 15)
+            } else {
+                try setNotifyValue(true, for: .control)
+            }
+        } catch let error {
+            throw TransmitterError.controlError("Error enabling notification: \(error)")
+        }
+    }
+
+    func readTimeMessage() throws -> TransmitterTimeRxMessage {
+        do {
+            return try writeMessage(TransmitterTimeTxMessage(), for: .control)
+        } catch let error {
+            throw TransmitterError.controlError("Error getting time: \(error)")
+        }
+    }
+
+    /// - Throws: TransmitterError.controlError
+    func sendCommand(_ command: Command, activationDate: Date) throws -> TransmitterRxMessage {
+        do {
+            switch command {
+            case .startSensor(let date):
+                let startTime = UInt32(date.timeIntervalSince(activationDate))
+                let secondsSince1970 = UInt32(date.timeIntervalSince1970)
+                return try writeMessage(SessionStartTxMessage(startTime: startTime, secondsSince1970: secondsSince1970), for: .control)
+            case .stopSensor(let date):
+                let stopTime = UInt32(date.timeIntervalSince(activationDate))
+                return try writeMessage(SessionStopTxMessage(stopTime: stopTime), for: .control)
+            case .calibrateSensor(let glucose, let date):
+                let glucoseValue = UInt16(glucose.doubleValue(for: .milligramsPerDeciliter).rounded())
+                let time = UInt32(date.timeIntervalSince(activationDate))
+                return try writeMessage(CalibrateGlucoseTxMessage(time: time, glucose: glucoseValue), for: .control)
+            case .resetTransmitter:
+                return try writeMessage(ResetTxMessage(), for: .control)
+            }
+        } catch let error {
+            throw TransmitterError.controlError("Error during \(command): \(error)")
+        }
+    }
+
+    func readGlucose() throws -> GlucoseRxMessage {
+        do {
+            return try writeMessage(GlucoseTxMessage(), for: .control)
+        } catch let error {
+            throw TransmitterError.controlError("Error getting glucose: \(error)")
+        }
+    }
+
+    func readCalibrationData() throws -> CalibrationDataRxMessage {
+        do {
+            return try writeMessage(CalibrationDataTxMessage(), for: .control)
+        } catch let error {
+            throw TransmitterError.controlError("Error getting calibration data: \(error)")
+        }
+    }
+
+    func disconnect() {
+        do {
+            try setNotifyValue(false, for: .control)
+            try writeMessage(DisconnectTxMessage(), for: .control)
+        } catch {
+        }
+    }
+
+    func listenToCharacteristic(_ characteristic: CGMServiceCharacteristicUUID) throws {
+        do {
+            try setNotifyValue(true, for: characteristic)
+        } catch let 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)")
+        }
+    }
+}

+ 345 - 0
Dependecies/CGMBLEKit/CGMBLEKit/TransmitterManager.swift

@@ -0,0 +1,345 @@
+//
+//  TransmitterManager.swift
+//  Loop
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+//
+
+import os.log
+import HealthKit
+
+public struct TransmitterManagerState: Equatable {
+
+    public static let version = 1
+
+    public var transmitterID: String
+
+    public var passiveModeEnabled: Bool = true
+    
+    public var shouldSyncToRemoteService: Bool
+
+    public init(transmitterID: String, shouldSyncToRemoteService: Bool = false) {
+        self.transmitterID = transmitterID
+        self.shouldSyncToRemoteService = shouldSyncToRemoteService
+    }
+}
+
+
+public protocol TransmitterManagerObserver: AnyObject {
+    func transmitterManagerDidUpdateLatestReading(_ manager: TransmitterManager)
+}
+
+public class TransmitterManager: TransmitterDelegate {
+    private var state: TransmitterManagerState
+
+    private let observers = WeakSynchronizedSet<TransmitterManagerObserver>()
+
+
+    public var hasValidSensorSession: Bool {
+        // TODO: we should decode and persist transmitter session state
+        return !state.transmitterID.isEmpty
+    }
+
+
+    public required init(state: TransmitterManagerState) {
+        self.state = state
+        self.transmitter = Transmitter(id: state.transmitterID, passiveModeEnabled: state.passiveModeEnabled)
+
+        self.transmitter.delegate = self
+    }
+
+    public var shouldSyncToRemoteService: Bool {
+        get {
+            return state.shouldSyncToRemoteService
+        }
+        set {
+            self.state.shouldSyncToRemoteService = newValue
+            notifyDelegateOfStateChange()
+        }
+    }
+
+
+    private(set) public var latestConnection: Date? {
+        get {
+            return lockedLatestConnection.value
+        }
+        set {
+            lockedLatestConnection.value = newValue
+        }
+    }
+    private let lockedLatestConnection: Locked<Date?> = Locked(nil)
+
+    public let transmitter: Transmitter
+    let log = OSLog(category: "TransmitterManager")
+
+    public var providesBLEHeartbeat: Bool {
+        return dataIsFresh
+    }
+
+    private(set) public var latestReading: Glucose? {
+        get {
+            return lockedLatestReading.value
+        }
+        set {
+            lockedLatestReading.value = newValue
+        }
+    }
+    private let lockedLatestReading: Locked<Glucose?> = Locked(nil)
+
+    private var dataIsFresh: Bool {
+        guard let latestGlucose = latestReading,
+            latestGlucose.readDate > Date(timeIntervalSinceNow: .minutes(-4.5)) else {
+            return false
+        }
+
+        return true
+    }
+
+//    public func fetchNewDataIfNeeded(_ completion: @escaping (CGMReadingResult) -> Void) {
+//        // Ensure our transmitter connection is active
+//        transmitter.resumeScanning()
+//
+//        // If our last glucose was less than 4.5 minutes ago, don't fetch.
+//        guard !dataIsFresh else {
+//            completion(.noData)
+//            return
+//        }
+//
+//        log.default("Fetching new glucose from Share because last reading is %{public}.1f minutes old", latestReading?.readDate.timeIntervalSinceNow.minutes ?? 0)
+//    }
+
+    public var debugDescription: String {
+        return [
+            "## \(String(describing: type(of: self)))",
+            "latestReading: \(String(describing: latestReading))",
+            "latestConnection: \(String(describing: latestConnection))",
+            "dataIsFresh: \(dataIsFresh)",
+            "providesBLEHeartbeat: \(providesBLEHeartbeat)",
+            "observers.count: \(observers.cleanupDeallocatedElements().count)",
+            String(reflecting: transmitter),
+        ].joined(separator: "\n")
+    }
+
+//    private func updateDelegate(with result: CGMReadingResult) {
+//        if let manager = self as? CGMManager {
+//            shareManager.delegate.notify { (delegate) in
+//                delegate?.cgmManager(manager, hasNew: result)
+//            }
+//        }
+//
+//        notifyObserversOfLatestReading()
+//    }
+    
+    private func notifyDelegateOfStateChange() {
+//        if let manager = self as? CGMManager {
+//            shareManager.delegate.notify { (delegate) in
+//                delegate?.cgmManagerDidUpdateState(manager)
+//            }
+//        }
+    }
+
+
+    // MARK: - TransmitterDelegate
+
+    public func transmitterDidConnect(_ transmitter: Transmitter) {
+        log.default("%{public}@", #function)
+        latestConnection = Date()
+//        logDeviceCommunication("Connected", type: .connection)
+    }
+
+    public func transmitter(_ transmitter: Transmitter, didError error: Error) {
+        log.error("%{public}@: %{public}@", #function, String(describing: error))
+//        updateDelegate(with: .error(error))
+//        logDeviceCommunication("Error: \(error)", type: .error)
+    }
+
+    public func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) {
+        guard glucose != latestReading else {
+//            updateDelegate(with: .noData)
+            return
+        }
+
+        latestReading = glucose
+
+//        logDeviceCommunication("New reading: \(glucose.readDate)", type: .receive)
+
+        guard glucose.state.hasReliableGlucose else {
+            log.default("%{public}@: Unreliable glucose: %{public}@", #function, String(describing: glucose.state))
+//            updateDelegate(with: .error(CalibrationError.unreliableState(glucose.state)))
+            return
+        }
+        
+        guard let quantity = glucose.glucose else {
+//            updateDelegate(with: .noData)
+            return
+        }
+
+        log.default("%{public}@: New glucose", #function)
+
+//        updateDelegate(with: .newData([
+//            NewGlucoseSample(
+//                date: glucose.readDate,
+//                quantity: quantity,
+//                trend: glucose.trendType,
+//                isDisplayOnly: glucose.isDisplayOnly,
+//                wasUserEntered: glucose.isDisplayOnly,
+//                syncIdentifier: glucose.syncIdentifier,
+//                device: device
+//            )
+//        ]))
+    }
+
+    public func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose]) {
+//        let samples = glucose.compactMap { (glucose) -> NewGlucoseSample? in
+//            guard glucose != latestReading, glucose.state.hasReliableGlucose, let quantity = glucose.glucose else {
+//                return nil
+//            }
+//
+//            return NewGlucoseSample(
+//                date: glucose.readDate,
+//                quantity: quantity,
+//                trend: glucose.trendType,
+//                isDisplayOnly: glucose.isDisplayOnly,
+//                wasUserEntered: glucose.isDisplayOnly,
+//                syncIdentifier: glucose.syncIdentifier,
+//                device: device
+//            )
+//        }
+//
+//        guard samples.count > 0 else {
+//            return
+//        }
+
+//        updateDelegate(with: .newData(samples))
+
+//        logDeviceCommunication("New backfill: \(String(describing: samples.first?.date))", type: .receive)
+    }
+
+    public func transmitter(_ transmitter: Transmitter, didReadUnknownData data: Data) {
+        log.error("Unknown sensor data: %{public}@", data.hexadecimalString)
+        // This can be used for protocol discovery, but isn't necessary for normal operation
+
+//        logDeviceCommunication("Unknown sensor data: \(data.hexadecimalString)", type: .error)
+    }
+}
+
+
+// MARK: - Observer management
+extension TransmitterManager {
+    public func addObserver(_ observer: TransmitterManagerObserver, queue: DispatchQueue) {
+        observers.insert(observer, queue: queue)
+    }
+
+    public func removeObserver(_ observer: TransmitterManagerObserver) {
+        observers.removeElement(observer)
+    }
+
+    private func notifyObserversOfLatestReading() {
+        observers.forEach { (observer) in
+            observer.transmitterManagerDidUpdateLatestReading(self)
+        }
+    }
+}
+
+
+public class G5CGMManager: TransmitterManager {
+    public let managerIdentifier: String = "DexG5Transmitter"
+
+    public let localizedTitle = LocalizedString("Dexcom G5", comment: "CGM display title")
+
+    public let isOnboarded = true   // No distinction between created and onboarded
+
+    public var appURL: URL? {
+        return URL(string: "dexcomcgm://")
+    }
+
+    public var device: HKDevice? {
+        return HKDevice(
+            name: "CGMBLEKit",
+            manufacturer: "Dexcom",
+            model: "G5 Mobile",
+            hardwareVersion: nil,
+            firmwareVersion: nil,
+            softwareVersion: String(CGMBLEKitVersionNumber),
+            localIdentifier: nil,
+            udiDeviceIdentifier: "00386270000002"
+        )
+    }
+    
+//    func logDeviceCommunication(_ message: String, type: DeviceLogEntryType = .send) {
+//        self.cgmManagerDelegate?.deviceManager(self, logEventForDeviceIdentifier: transmitter.ID, type: type, message: message, completion: nil)
+//    }
+    
+}
+
+
+public class G6CGMManager: TransmitterManager {
+    public let managerIdentifier: String = "DexG6Transmitter"
+
+    public let localizedTitle = LocalizedString("Dexcom G6", comment: "CGM display title")
+
+    public let isOnboarded = true   // No distinction between created and onboarded
+
+    public var appURL: URL? {
+        return nil
+    }
+
+    public var device: HKDevice? {
+        return HKDevice(
+            name: "CGMBLEKit",
+            manufacturer: "Dexcom",
+            model: "G6",
+            hardwareVersion: nil,
+            firmwareVersion: nil,
+            softwareVersion: String(CGMBLEKitVersionNumber),
+            localIdentifier: nil,
+            udiDeviceIdentifier: "00386270000385"
+        )
+    }
+    
+//    func logDeviceCommunication(_ message: String, type: DeviceLogEntryType = .send) {
+//        self.cgmManagerDelegate?.deviceManager(self, logEventForDeviceIdentifier: transmitter.ID, type: type, message: message, completion: nil)
+//    }
+}
+
+
+enum CalibrationError: Error {
+    case unreliableState(CalibrationState)
+}
+
+extension CalibrationError: LocalizedError {
+    var errorDescription: String? {
+        switch self {
+        case .unreliableState:
+            return LocalizedString("Glucose data is unavailable", comment: "Error description for unreliable state")
+        }
+    }
+
+    var failureReason: String? {
+        switch self {
+        case .unreliableState(let state):
+            return state.localizedDescription
+        }
+    }
+}
+
+extension CalibrationState {
+    public var localizedDescription: String {
+        switch self {
+        case .known(let state):
+            switch state {
+            case .needCalibration7, .needCalibration14, .needFirstInitialCalibration, .needSecondInitialCalibration, .calibrationError8, .calibrationError9, .calibrationError10, .calibrationError13:
+                return LocalizedString("Sensor needs calibration", comment: "The description of sensor calibration state when sensor needs calibration.")
+            case .ok:
+                return LocalizedString("Sensor calibration is OK", comment: "The description of sensor calibration state when sensor calibration is ok.")
+            case .stopped, .sensorFailure11, .sensorFailure12, .sessionFailure15, .sessionFailure16, .sessionFailure17:
+                return LocalizedString("Sensor is stopped", comment: "The description of sensor calibration state when sensor sensor is stopped.")
+            case .warmup, .questionMarks:
+                return LocalizedString("Sensor is warming up", comment: "The description of sensor calibration state when sensor sensor is warming up.")
+            }
+        case .unknown(let rawValue):
+            return String(format: LocalizedString("Sensor is in unknown state %1$d", comment: "The description of sensor calibration state when raw value is unknown. (1: missing data details)"), rawValue)
+        }
+    }
+}
+

+ 57 - 0
Dependecies/CGMBLEKit/CGMBLEKit/TransmitterStatus.swift

@@ -0,0 +1,57 @@
+//
+//  TransmitterStatus.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/26/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+public enum TransmitterStatus {
+    public typealias RawValue = UInt8
+
+    case ok
+    case lowBattery
+    case unknown(RawValue)
+
+    init(rawValue: RawValue) {
+        switch rawValue {
+        case 0:
+            self = .ok
+        case 0x81:
+            self = .lowBattery
+        default:
+            self = .unknown(rawValue)
+        }
+    }
+}
+
+
+extension TransmitterStatus: Equatable { }
+
+public func ==(lhs: TransmitterStatus, rhs: TransmitterStatus) -> Bool {
+    switch (lhs, rhs) {
+    case (.ok, .ok), (.lowBattery, .lowBattery):
+        return true
+    case (.unknown(let left), .unknown(let right)) where left == right:
+        return true
+    default:
+        return false
+    }
+}
+
+
+extension TransmitterStatus {
+    public var localizedDescription: String {
+        switch self {
+        case .ok:
+            return LocalizedString("OK", comment: "Describes a functioning transmitter")
+        case .lowBattery:
+            return LocalizedString("Low Battery", comment: "Describes a low battery")
+        case .unknown(let value):
+            return "TransmitterStatus.unknown(\(value))"
+        }
+    }
+}

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/da.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glukosedata er ikke tilgængelige";
+
+/* Describes a low battery */
+"Low Battery" = "lavt batteri";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Perifere reagerede ikke i tide";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Perifert udstyr er ikke tilsluttet";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Sensorkalibrering er OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Sensoren er i en ukendt tilstand %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensoren er stoppet";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensoren varmer op";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensoren har brug for kalibrering";
+
+/* Error description */
+"Unknown characteristic" = "Ukendt egenskab";
+

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/de.lproj/Localizable.strings

@@ -0,0 +1,29 @@
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glukosedaten sind nicht verfügbar";
+
+/* Describes a low battery */
+"Low Battery" = "Niedriger Batteriestatus";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Peripheres Gerät antwortet nicht rechtzeitig";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Peripheres Gerät ist nicht verbunden";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Sensorkalibrierung ist OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Sensor ist in unbekanntem Zustand %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensor ist gestoppt";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensor erwärmt sich";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensor benötigt Kalibrierung";
+
+/* Error description */
+"Unknown characteristic" = "Unbekannte Charakteristik";

+ 30 - 0
Dependecies/CGMBLEKit/CGMBLEKit/es.lproj/Localizable.strings

@@ -0,0 +1,30 @@
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Los datos de glucosa no están disponibles";
+
+/* Describes a low battery */
+"Low Battery" = "Batería Baja";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "El dispositivo periférico no respondió a tiempo";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "El dispositivo periférico no está conectado";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Calibración de sensor está OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Estado desconocido de sensor %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensor se ha detenido";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Preparación de sensor";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensor se necesita de calibración";
+
+/* Error description */
+"Unknown characteristic" = "Característica desconocida";

+ 42 - 0
Dependecies/CGMBLEKit/CGMBLEKit/fi.lproj/Localizable.strings

@@ -0,0 +1,42 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glukoositietoja ei ole saatavilla";
+
+/* Describes a low battery */
+"Low Battery" = "Akku lähes tyhjä";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* invlid config error description */
+"Peripheral command was invalid" = "Oheislaitekomento oli virheellinen";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Oheislaite ei vastannut ajoissa";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Oheislaitetta ei ole kytketty";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Anturin kalibrointi onnistui";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Anturi on tuntemattomassa tilassa %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Anturi on pysäytetty";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Anturi lämpenee";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Anturi tarvitsee kalibroinnin";
+
+/* Error description */
+"Unknown characteristic" = "Tuntematon ominaisuus";
+

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/fr.lproj/Localizable.strings

@@ -0,0 +1,29 @@
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Les données de glucose ne sont pas disponibles";
+
+/* Describes a low battery */
+"Low Battery" = "Batterie faible";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Le périphérique n’a pas répondu à temps";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Le périphérique n’est pas connecté";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Calibration de capteur OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Capteur est dans un état inconnu %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Capteur est arrêté";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Capteur est en période de réchauffement";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Capteur a besoin de calibration";
+
+/* Error description */
+"Unknown characteristic" = "Caractéristique inconnue";

+ 30 - 0
Dependecies/CGMBLEKit/CGMBLEKit/it.lproj/Localizable.strings

@@ -0,0 +1,30 @@
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "I dati sulla glicemia non sono disponibili";
+
+/* Describes a low battery */
+"Low Battery" = "Batteria Bassa";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "La periferica non ha risposto nel tempo limite";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "La periferica non e' connessa";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "La calibrazione del sensore e' valida";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Il sensore presenta stato sconosciuto %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Il sensore e' spento";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Il sensore e' in fase di avvio";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Il sensore necessita di calibrazione";
+
+/* Error description */
+"Unknown characteristic" = "Caratteristiche sconosciute";

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/ja.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "グルコースデータがありません";
+
+/* Describes a low battery */
+"Low Battery" = "電池残量低下";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "周辺機器が時間内に反応しませんでした";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "周辺機器が接続されていません";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "センサーの較正はできています";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "センサーの状態が不明です %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "センサーが停止しています";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "センサーが準備中です";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "センサーの較正が必要です";
+
+/* Error description */
+"Unknown characteristic" = "不明な特徴";
+

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/nb.lproj/Localizable.strings

@@ -0,0 +1,29 @@
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glukose data er utilgjengelig";
+
+/* Describes a low battery */
+"Low Battery" = "Lite batteri";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Tilbehøret svarte ikke i tide";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Tilbehøret er ikke tilkoblet";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Sensorkalibrering er OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Sensor er i ukjent status %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensor er stoppet";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensor varmer opp";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensor trenger kalibrering";
+
+/* Error description */
+"Unknown characteristic" = "Ukjent karakteristikk";

+ 30 - 0
Dependecies/CGMBLEKit/CGMBLEKit/nl.lproj/Localizable.strings

@@ -0,0 +1,30 @@
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glucose gegevens niet beschikbaar";
+
+/* Describes a low battery */
+"Low Battery" = "Lage batterij";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Apparaat reageerde niet op tijd";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Apparaat is niet verbonden";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Sensor callibratie is geslaagd";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Senor is in onbekende status %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensor is gestopt";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensor is aan het opwarmen";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensor heeft callibratie nodig";
+
+/* Error description */
+"Unknown characteristic" = "Onbekende eigenschap";

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/pl.lproj/Localizable.strings

@@ -0,0 +1,29 @@
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Dane o glukozie są niedostępne";
+
+/* Describes a low battery */
+"Low Battery" = "Słaba bateria";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Urz. peryferyjne nie odpowiada";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Urz. peryferyjne jest niepodłączone";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Kalibracja sensora powiodła się";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Sensor jest w nieznanym stanie %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensor jest wstrzymany";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensor jest w trakcie uruchamiania";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensor wymaga kalibracji";
+
+/* Error description */
+"Unknown characteristic" = "Nieznany błąd";

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/pt-BR.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Os dados de glicose não estão disponíveis";
+
+/* Describes a low battery */
+"Low Battery" = "Bateria Fraca";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Periférico não respondeu a tempo";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "O periférico não está conectado";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "A calibração do sensor está OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "O sensor está em um estado desconhecido %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "O sensor está parado";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "O sensor está aquecendo";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "O sensor precisa de calibração";
+
+/* Error description */
+"Unknown characteristic" = "Característica desconhecida";
+

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/ro.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Datele cu glucoză nu sunt disponibile";
+
+/* Describes a low battery */
+"Low Battery" = "Baterie descarcata";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Perifericul nu a răspuns la timp";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Perifericul nu este conectat";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Calibrarea senzorului este OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Senzorul este într-o stare necunoscută %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Senzorul este oprit";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Senzorul se încălzește";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Senzorul are nevoie de calibrare";
+
+/* Error description */
+"Unknown characteristic" = "Caracteristică necunoscută";
+

+ 30 - 0
Dependecies/CGMBLEKit/CGMBLEKit/ru.lproj/Localizable.strings

@@ -0,0 +1,30 @@
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Данные глюкозы недоступны";
+
+/* Describes a low battery */
+"Low Battery" = "Низкий заряд батареи";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Периферийное устройство не отозвалось вовремя";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Соединение с периферийным устройством не установлено";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Калибровка сенсора ОК";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Состояние сенсора неизвестно %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Сенсор остановлен";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Сенсор прогревается";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Сенсор требует калибровки";
+
+/* Error description */
+"Unknown characteristic" = "Неизвестная характеристика";

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/sv.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Glukosvärden är inte tillgängliga";
+
+/* Describes a low battery */
+"Low Battery" = "Lågt batteri";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Enheten svarade inte i tid";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Enheten är inte ansluten";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Sensorkalibrering lyckades";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Sensorns status är okänd %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Sensorn har stoppats";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Sensorn värmer upp";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Sensorn behöver kalibreras";
+
+/* Error description */
+"Unknown characteristic" = "Okänd data";
+

+ 39 - 0
Dependecies/CGMBLEKit/CGMBLEKit/vi.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+/* CGM display title */
+"Dexcom G5" = "Dexcom G5";
+
+/* CGM display title */
+"Dexcom G6" = "Dexcom G6";
+
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "Dữ liệu glucose không có sẵn";
+
+/* Describes a low battery */
+"Low Battery" = "pin yếu";
+
+/* Describes a functioning transmitter */
+"OK" = "OK";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "Ngoại vi không đáp ứng kịp thời";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "Ngoại vi không được kết nối";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "Hiệu chuẩn cảm biến là OK";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "Cảm biến ở trạng thái không xác định %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "Cảm biến bị dừng";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "Cảm biến đang nóng lên";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "Cảm biến cần hiệu chuẩn";
+
+/* Error description */
+"Unknown characteristic" = "Đặc điểm không xác định";
+

+ 29 - 0
Dependecies/CGMBLEKit/CGMBLEKit/zh-Hans.lproj/Localizable.strings

@@ -0,0 +1,29 @@
+/* Error description for unreliable state */
+"Glucose data is unavailable" = "葡萄糖数据不可用";
+
+/* Describes a low battery */
+"Low Battery" = "电量低";
+
+/* Timeout error description */
+"Peripheral did not respond in time" = "外设没有及时响应";
+
+/* Not ready error description */
+"Peripheral isnʼt connected" = "外围设备未连接";
+
+/* The description of sensor calibration state when sensor calibration is ok. */
+"Sensor calibration is OK" = "传感器校准成功";
+
+/* The description of sensor calibration state when raw value is unknown. (1: missing data details) */
+"Sensor is in unknown state %1$d" = "传感器处于未知状态 %1$d";
+
+/* The description of sensor calibration state when sensor sensor is stopped. */
+"Sensor is stopped" = "传感器停止";
+
+/* The description of sensor calibration state when sensor sensor is warming up. */
+"Sensor is warming up" = "传感器在启动中";
+
+/* The description of sensor calibration state when sensor needs calibration. */
+"Sensor needs calibration" = "传感器需要校准";
+
+/* Error description */
+"Unknown characteristic" = "未知特性";

+ 72 - 0
Dependecies/CGMBLEKit/CODE_OF_CONDUCT.md

@@ -0,0 +1,72 @@
+# Code of Conduct
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+  address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the maintaner [via email](mailto:loudnate@gmail.com). All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/

+ 99 - 0
Dependecies/CGMBLEKit/Common/Data.swift

@@ -0,0 +1,99 @@
+//
+//  NSData.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/5/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+extension Data {
+    private func toDefaultEndian<T: FixedWidthInteger>(_: T.Type) -> T {
+        return self.withUnsafeBytes({ (rawBufferPointer: UnsafeRawBufferPointer) -> T in
+            let bufferPointer = rawBufferPointer.bindMemory(to: T.self)
+            guard let pointer = bufferPointer.baseAddress else {
+                return 0
+            }
+            return T(pointer.pointee)
+        })
+    }
+
+    func to<T: FixedWidthInteger>(_ type: T.Type) -> T {
+        return T(littleEndian: toDefaultEndian(type))
+    }
+
+    func toInt<T: FixedWidthInteger>() -> T {
+        return to(T.self)
+    }
+
+    func toBigEndian<T: FixedWidthInteger>(_ type: T.Type) -> T {
+        return T(bigEndian: toDefaultEndian(type))
+    }
+
+    mutating func append<T: FixedWidthInteger>(_ newElement: T) {
+        withUnsafePointer(to: newElement.littleEndian) { (ptr: UnsafePointer<T>) in
+            append(UnsafeBufferPointer(start: ptr, count: 1))
+        }
+    }
+
+    mutating func appendBigEndian<T: FixedWidthInteger>(_ newElement: T) {
+        withUnsafePointer(to: newElement.bigEndian) { (ptr: UnsafePointer<T>) in
+            append(UnsafeBufferPointer(start: ptr, count: 1))
+        }
+    }
+
+    init<T: FixedWidthInteger>(_ value: T) {
+        self = withUnsafePointer(to: value.littleEndian) { (ptr: UnsafePointer<T>) -> Data in
+            return Data(buffer: UnsafeBufferPointer(start: ptr, count: 1))
+        }
+    }
+
+    init<T: FixedWidthInteger>(bigEndian value: T) {
+        self = withUnsafePointer(to: value.bigEndian) { (ptr: UnsafePointer<T>) -> Data in
+            return Data(buffer: UnsafeBufferPointer(start: ptr, count: 1))
+        }
+    }
+}
+
+
+// String conversion methods, adapted from https://stackoverflow.com/questions/40276322/hex-binary-string-conversion-in-swift/40278391#40278391
+extension Data {
+    init?(hexadecimalString: String) {
+        self.init(capacity: hexadecimalString.utf16.count / 2)
+
+        // Convert 0 ... 9, a ... f, A ...F to their decimal value,
+        // return nil for all other input characters
+        func decodeNibble(u: UInt16) -> UInt8? {
+            switch u {
+            case 0x30 ... 0x39:  // '0'-'9'
+                return UInt8(u - 0x30)
+            case 0x41 ... 0x46:  // 'A'-'F'
+                return UInt8(u - 0x41 + 10)  // 10 since 'A' is 10, not 0
+            case 0x61 ... 0x66:  // 'a'-'f'
+                return UInt8(u - 0x61 + 10)  // 10 since 'a' is 10, not 0
+            default:
+                return nil
+            }
+        }
+
+        var even = true
+        var byte: UInt8 = 0
+        for c in hexadecimalString.utf16 {
+            guard let val = decodeNibble(u: c) else { return nil }
+            if even {
+                byte = val << 4
+            } else {
+                byte += val
+                self.append(byte)
+            }
+            even = !even
+        }
+        guard even else { return nil }
+    }
+
+    var hexadecimalString: String {
+        return map { String(format: "%02hhx", $0) }.joined()
+    }
+}

+ 20 - 0
Dependecies/CGMBLEKit/Common/HKUnit.swift

@@ -0,0 +1,20 @@
+//
+//  HKUnit.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 8/6/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import HealthKit
+
+
+extension HKUnit {
+    static let milligramsPerDeciliter: HKUnit = {
+        return HKUnit.gramUnit(with: .milli).unitDivided(by: HKUnit.literUnit(with: .deci))
+    }()
+
+    static let milligramsPerDeciliterPerMinute: HKUnit = {
+        return HKUnit.milligramsPerDeciliter.unitDivided(by: .minute())
+    }()
+}

+ 21 - 0
Dependecies/CGMBLEKit/Common/LocalizedString.swift

@@ -0,0 +1,21 @@
+//
+//  LocalizedString.swift
+//  LoopUI
+//
+//  Created by Kathryn DiSimone on 8/15/18.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+
+internal class FrameworkBundle {
+    static let main = Bundle(for: FrameworkBundle.self)
+}
+
+func LocalizedString(_ key: String, tableName: String? = nil, value: String? = nil, comment: String) -> String {
+    if let value = value {
+        return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, value: value, comment: comment)
+    } else {
+        return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, comment: comment)
+    }
+}

+ 41 - 0
Dependecies/CGMBLEKit/Common/Locked.swift

@@ -0,0 +1,41 @@
+//
+//  Locked.swift
+//  LoopKit
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import os.lock
+
+
+internal class Locked<T> {
+    private var lock = os_unfair_lock()
+    private var _value: T
+
+    init(_ value: T) {
+        os_unfair_lock_lock(&lock)
+        defer { os_unfair_lock_unlock(&lock) }
+        _value = value
+    }
+
+    var value: T {
+        get {
+            os_unfair_lock_lock(&lock)
+            defer { os_unfair_lock_unlock(&lock) }
+            return _value
+        }
+        set {
+            os_unfair_lock_lock(&lock)
+            defer { os_unfair_lock_unlock(&lock) }
+            _value = newValue
+        }
+    }
+
+    @discardableResult
+    func mutate(_ changes: (_ value: inout T) -> Void) -> T {
+        os_unfair_lock_lock(&lock)
+        defer { os_unfair_lock_unlock(&lock) }
+        changes(&_value)
+        return _value
+    }
+}

+ 61 - 0
Dependecies/CGMBLEKit/Common/TimeInterval.swift

@@ -0,0 +1,61 @@
+//
+//  NSTimeInterval.swift
+//  Naterade
+//
+//  Created by Nathan Racklyeft on 1/9/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+extension TimeInterval {
+    static func hours(_ hours: Double) -> TimeInterval {
+        return self.init(hours: hours)
+    }
+
+    static func minutes(_ minutes: Int) -> TimeInterval {
+        return self.init(minutes: Double(minutes))
+    }
+
+    static func minutes(_ minutes: Double) -> TimeInterval {
+        return self.init(minutes: minutes)
+    }
+
+    static func seconds(_ seconds: Double) -> TimeInterval {
+        return self.init(seconds)
+    }
+
+    static func milliseconds(_ milliseconds: Double) -> TimeInterval {
+        return self.init(milliseconds / 1000)
+    }
+
+    init(minutes: Double) {
+        self.init(minutes * 60)
+    }
+
+    init(hours: Double) {
+        self.init(minutes: hours * 60)
+    }
+
+    init(seconds: Double) {
+        self.init(seconds)
+    }
+
+    init(milliseconds: Double) {
+        self.init(milliseconds / 1000)
+    }
+
+    var milliseconds: Double {
+        return self * 1000
+    }
+
+    var minutes: Double {
+        return self / 60.0
+    }
+
+    var hours: Double {
+        return minutes / 60.0
+    }
+    
+}

+ 22 - 0
Dependecies/CGMBLEKit/LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Nathan Racklyeft
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

+ 55 - 0
Dependecies/CGMBLEKit/README.md

@@ -0,0 +1,55 @@
+# CGMBLEKit
+
+[![CI Status](http://img.shields.io/travis/LoopKit/CGMBLEKit.svg?style=flat)](https://travis-ci.org/LoopKit/CGMBLEKit)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+
+A iOS framework providing an interface for communicating with the G5 and G6 glucose transmitters over Bluetooth.
+
+*Please note this project is neither created nor backed by Dexcom, Inc. This software is not intended for use in therapy.*
+
+## Requirements
+
+This framework connects to a G5 or G6 Mobile Transmitter via Bluetooth LE. It does not connect to the G4 Share Receiver or any earlier CGM products.
+
+## Frameworks Installation
+
+### Carthage
+
+CGMBLEKit is available through [Carthage](https://github.com/Carthage/Carthage). To install it, add the following line to your Cartfile:
+
+```ruby
+github "LoopKit/CGMBLEKit"
+```
+
+Note that you'll need to confgure your target to link against `CommonCrypto.framework` in addition to `CGMBLEKit.framework`
+
+## Usage
+
+If you plan to run your app alongside the G5 Mobile application, make sure to set `passiveModeEnabled` to true.
+
+### Examples
+
+[glucose-badge](https://github.com/dennisgove/glucose-badge) – Display the latest glucose values as an app icon badge
+
+## ResetTransmitter App Installation
+
+Download the CGMBLEKit code by clicking on the green `Clone or Download` button (scroll up on this page and you'll find it), then select `Download Zip`
+
+![ResetTransmitter help](https://github.com/Kdisimone/images/blob/master/resetTransmitter-first.png)
+
+Then navigate to the `CGMBLEKit` folder that just downloaded to your computer.  Double-click on the `CGMBLEKit.xcodeproj` file to open the project in Xcode.
+
+![ResetTransmitter help](https://github.com/Kdisimone/images/blob/master/resetTransmitter-download.png)
+
+To install the ResetTransmitter App on your iPhone, simply make sure to sign the ResetTransmitter target and then select just the `ResetTransmitter` scheme in the build area.  Make sure your iPhone is plugged into the computer, select your iPhone from the top of the `Devices` in the 4th circled area, screenshot below.  Note: You do not have to change bundle IDs or anything beyond the steps listed.
+
+![ResetTransmitter help](https://github.com/Kdisimone/images/blob/master/resetTransmitter.png)
+
+
+## Code of Conduct
+
+Please note that this project is released with a [Contributor Code of Conduct](https://github.com/LoopKit/LoopKit/blob/master/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
+
+## License
+
+CGMBLEKit is available under the MIT license. See the LICENSE file for more info.