Jelajahi Sumber

Merge branch 'bdb_dev' into Crowdin

Jon B.M 3 tahun lalu
induk
melakukan
ae10fab592
100 mengubah file dengan 5925 tambahan dan 129 penghapusan
  1. 30 0
      .github/workflows/add_identifiers.yml
  2. 52 0
      .github/workflows/build_FAX.yml
  3. 30 0
      .github/workflows/create_certs.yml
  4. 1 0
      .gitignore
  5. 4 0
      BGaverages+CoreDataClass.swift
  6. 15 0
      BGaverages+CoreDataProperties.swift
  7. 4 0
      BGmedian+CoreDataClass.swift
  8. 15 0
      BGmedian+CoreDataProperties.swift
  9. 4 0
      Carbohydrates+CoreDataClass.swift
  10. 12 0
      Carbohydrates+CoreDataProperties.swift
  11. 4 1
      Config.xcconfig
  12. 58 0
      Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents
  13. 36 0
      Dependencies/CGMBLEKit/.gitignore
  14. 13 0
      Dependencies/CGMBLEKit/.travis.yml
  15. 140 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/AppDelegate.swift
  16. 98 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/Assets.xcassets/AppIcon.appiconset/Contents.json
  17. 25 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/Base.lproj/LaunchScreen.storyboard
  18. 172 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/Base.lproj/Main.storyboard
  19. 34 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/CommandQueue.swift
  20. 49 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/Info.plist
  21. 39 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/NSUserDefaults.swift
  22. 200 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ViewController.swift
  23. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/da.lproj/Localizable.strings
  24. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/da.lproj/Main.strings
  25. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/de.lproj/Localizable.strings
  26. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/de.lproj/Main.strings
  27. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/en.lproj/Localizable.strings
  28. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/en.lproj/Main.strings
  29. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/es.lproj/Localizable.strings
  30. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/es.lproj/Main.strings
  31. 9 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/fi.lproj/Localizable.strings
  32. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/fi.lproj/Main.strings
  33. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/fr.lproj/Localizable.strings
  34. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/fr.lproj/Main.strings
  35. 1 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/he.lproj/LaunchScreen.strings
  36. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/he.lproj/Localizable.strings
  37. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/he.lproj/Main.strings
  38. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/it.lproj/Localizable.strings
  39. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/it.lproj/Main.strings
  40. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ja.lproj/Localizable.strings
  41. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ja.lproj/Main.strings
  42. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/nb.lproj/Localizable.strings
  43. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/nb.lproj/Main.strings
  44. 9 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/nl.lproj/Localizable.strings
  45. 21 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/nl.lproj/Main.strings
  46. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/pl.lproj/Localizable.strings
  47. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/pl.lproj/Main.strings
  48. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/pt-BR.lproj/Localizable.strings
  49. 26 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/pt-BR.lproj/Main.strings
  50. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ro.lproj/Localizable.strings
  51. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ro.lproj/Main.strings
  52. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ru.lproj/Localizable.strings
  53. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/ru.lproj/Main.strings
  54. 9 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/sv.lproj/Localizable.strings
  55. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/sv.lproj/Main.strings
  56. 1 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/tr.lproj/LaunchScreen.strings
  57. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/tr.lproj/Localizable.strings
  58. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/tr.lproj/Main.strings
  59. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/vi.lproj/Localizable.strings
  60. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/vi.lproj/Main.strings
  61. 8 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/zh-Hans.lproj/Localizable.strings
  62. 27 0
      Dependencies/CGMBLEKit/CGMBLEKit Example/zh-Hans.lproj/Main.strings
  63. 1738 17
      Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/project.pbxproj
  64. 97 0
      Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/CGMBLEKit Example.xcscheme
  65. 87 0
      Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/ResetTransmitter.xcscheme
  66. 76 0
      Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/Shared-watchOS.xcscheme
  67. 137 0
      Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/Shared.xcscheme
  68. 41 23
      Dependencies/CGMBLEKit/CGMBLEKit/Glucose+SensorDisplayable.swift
  69. 7 1
      Dependencies/CGMBLEKit/CGMBLEKit/Glucose.swift
  70. 1 0
      Dependencies/CGMBLEKit/CGMBLEKit/Messages/TransmitterTimeRxMessage.swift
  71. 391 4
      Dependencies/CGMBLEKit/CGMBLEKit/OSLog.swift
  72. 255 81
      Dependencies/CGMBLEKit/CGMBLEKit/TransmitterManager.swift
  73. 39 0
      Dependencies/CGMBLEKit/CGMBLEKit/he.lproj/Localizable.strings
  74. 39 0
      Dependencies/CGMBLEKit/CGMBLEKit/tr.lproj/Localizable.strings
  75. 4 0
      Dependencies/CGMBLEKit/CGMBLEKitG5Plugin/CGMBLEKitG5Plugin-Bridging-Header.h
  76. 25 0
      Dependencies/CGMBLEKit/CGMBLEKitG5Plugin/CGMBLEKitG5Plugin.swift
  77. 30 0
      Dependencies/CGMBLEKit/CGMBLEKitG5Plugin/Info.plist
  78. 4 0
      Dependencies/CGMBLEKit/CGMBLEKitG6Plugin/CGMBLEKitG6Plugin-Bridging-Header.h
  79. 25 0
      Dependencies/CGMBLEKit/CGMBLEKitG6Plugin/CGMBLEKitG6Plugin.swift
  80. 30 0
      Dependencies/CGMBLEKit/CGMBLEKitG6Plugin/Info.plist
  81. 20 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/CalibrationDataRxMessageTests.swift
  82. 433 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/GlucoseBackfillMessageTests.swift
  83. 79 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/GlucoseRxMessageTests.swift
  84. 83 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/GlucoseTests.swift
  85. 24 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/Info.plist
  86. 44 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/SessionStartRxMessageTests.swift
  87. 44 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/SessionStopRxMessageTests.swift
  88. 20 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/TransmitterIDTests.swift
  89. 53 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/TransmitterTimeRxMessageTests.swift
  90. 22 0
      Dependencies/CGMBLEKit/CGMBLEKitTests/TransmitterVersionRxMessageTests.swift
  91. 6 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/Assets.xcassets/Contents.json
  92. 12 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/Assets.xcassets/g6.imageset/Contents.json
  93. TEMPAT SAMPAH
      Dependencies/CGMBLEKit/CGMBLEKitUI/Assets.xcassets/g6.imageset/g6.png
  94. 51 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/Base.lproj/Localizable.strings
  95. 114 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/Base.lproj/TransmitterManagerSetup.storyboard
  96. 19 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/CGMBLEKitUI.h
  97. 24 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/IdentifiableClass.swift
  98. 2 2
      Dependencies/rileylink_ios/Crypto/Info.plist
  99. 151 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/TransmitterIDSetupViewController.swift
  100. 0 0
      Dependencies/CGMBLEKit/CGMBLEKitUI/TransmitterManager+UI.swift

+ 30 - 0
.github/workflows/add_identifiers.yml

@@ -0,0 +1,30 @@
+name: Add Identifiers
+on:
+  workflow_dispatch:
+
+jobs:
+  identifiers:
+    runs-on: macos-12
+    steps:
+      # Uncomment to manually select latest Xcode if needed
+      #- name: Select Latest Xcode
+      #  run: "sudo xcode-select --switch /Applications/Xcode_13.0.app/Contents/Developer"
+      
+      # Checks-out the repo
+      - name: Checkout Repo
+        uses: actions/checkout@v3
+        
+      # Patch Fastlane Match to not print tables
+      - name: Patch Match Tables
+        run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
+        
+      # Create or update identifiers for app
+      - name: Fastlane Provision
+        run: fastlane identifiers
+        env:
+          TEAMID: ${{ secrets.TEAMID }}
+          GH_PAT: ${{ secrets.GH_PAT }}
+          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
+          FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
+          FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
+          FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}

+ 52 - 0
.github/workflows/build_FAX.yml

@@ -0,0 +1,52 @@
+name: Build FAX
+on:
+  workflow_dispatch:
+
+jobs:
+  build:
+    runs-on: macos-12
+    steps:
+      # Uncomment to manually select latest Xcode if needed
+      - name: Select Latest Xcode
+        run: "sudo xcode-select --switch /Applications/Xcode_14.1.app/Contents/Developer"
+
+      # Checks-out the repo
+      - name: Checkout Repo
+        uses: actions/checkout@v3
+        with:
+          submodules: recursive
+      
+      # Patch Fastlane Match to not print tables
+      - name: Patch Match Tables
+        run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
+      
+      # Build signed FreeAPS X IPA file
+      - name: Fastlane Build & Archive
+        run: fastlane build_fax
+        env:
+          TEAMID: ${{ secrets.TEAMID }}
+          GH_PAT: ${{ secrets.GH_PAT }}
+          FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
+          FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
+          FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
+          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
+      
+      # Upload to TestFlight
+      - name: Fastlane upload to TestFlight
+        run: fastlane release
+        env:
+          TEAMID: ${{ secrets.TEAMID }}
+          GH_PAT: ${{ secrets.GH_PAT }}
+          FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
+          FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
+          FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
+          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
+
+      # Upload IPA and Symbols
+      - name: Upload IPA and Symbol artifacts
+        uses: actions/upload-artifact@v3
+        with:
+          name: build-artifacts
+          path: |
+            artifacts
+            buildlog

+ 30 - 0
.github/workflows/create_certs.yml

@@ -0,0 +1,30 @@
+name: Create Certificates
+on:
+  workflow_dispatch:
+
+jobs:
+  certificates:
+    runs-on: macos-12
+    steps:
+      # Uncomment to manually select latest Xcode if needed
+      #- name: Select Latest Xcode
+      #  run: "sudo xcode-select --switch /Applications/Xcode_13.0.app/Contents/Developer"
+      
+      # Checks-out the repo
+      - name: Checkout Repo
+        uses: actions/checkout@v3
+        
+      # Patch Fastlane Match to not print tables
+      - name: Patch Match Tables
+        run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
+        
+      # Create or update certificates for app
+      - name: Create Certificates
+        run: fastlane certs
+        env:
+          TEAMID: ${{ secrets.TEAMID }}
+          GH_PAT: ${{ secrets.GH_PAT }}
+          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
+          FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
+          FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
+          FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}

+ 1 - 0
.gitignore

@@ -9,6 +9,7 @@
 build/
 DerivedData/
 R.generated.swift
+*-old
 
 ## Various settings
 !default.mode1v3

+ 4 - 0
BGaverages+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(BGaverages) public class BGaverages: NSManagedObject {}

+ 15 - 0
BGaverages+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension BGaverages {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<BGaverages> {
+        NSFetchRequest<BGaverages>(entityName: "BGaverages")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var average: NSDecimalNumber?
+    @NSManaged var average_1: NSDecimalNumber?
+    @NSManaged var average_7: NSDecimalNumber?
+    @NSManaged var average_30: NSDecimalNumber?
+    @NSManaged var average_90: NSDecimalNumber?
+}

+ 4 - 0
BGmedian+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(BGmedian) public class BGmedian: NSManagedObject {}

+ 15 - 0
BGmedian+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension BGmedian {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<BGmedian> {
+        NSFetchRequest<BGmedian>(entityName: "BGmedian")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var median: NSDecimalNumber?
+    @NSManaged var median_1: NSDecimalNumber?
+    @NSManaged var median_30: NSDecimalNumber?
+    @NSManaged var median_90: NSDecimalNumber?
+    @NSManaged var median_7: NSDecimalNumber?
+}

+ 4 - 0
Carbohydrates+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(Carbohydrates) public class Carbohydrates: NSManagedObject {}

+ 12 - 0
Carbohydrates+CoreDataProperties.swift

@@ -0,0 +1,12 @@
+import CoreData
+import Foundation
+
+public extension Carbohydrates {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<Carbohydrates> {
+        NSFetchRequest<Carbohydrates>(entityName: "Carbohydrates")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var carbs: NSDecimalNumber?
+    @NSManaged var enteredBy: String?
+}

+ 4 - 1
Config.xcconfig

@@ -1,8 +1,11 @@
 APP_DISPLAY_NAME = FreeAPS X
-BUILD_VERSION = 0.2.6
+APP_VERSION = 1.0.8
+APP_BUILD_NUMBER = 1
+COPYRIGHT_NOTICE =
 DEVELOPER_TEAM = ##TEAM_ID##
 BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS
 APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroup
+APP_ICON = AppIcon
 
 #include? "ConfigOverride.xcconfig"
 #include? "../../ConfigOverride.xcconfig"

+ 58 - 0
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D68" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+    <entity name="BGaverages" representedClassName="BGaverages" syncable="YES" codeGenerationType="class">
+        <attribute name="average" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <entity name="BGmedian" representedClassName="BGmedian" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="median" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
+    <entity name="Carbohydrates" representedClassName="Carbohydrates" syncable="YES" codeGenerationType="class">
+        <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="enteredBy" optional="YES" attributeType="String"/>
+    </entity>
+    <entity name="HbA1c" representedClassName="HbA1c" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="hba1c" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
+    <entity name="InsulinDistribution" representedClassName="InsulinDistribution" syncable="YES" codeGenerationType="class">
+        <attribute name="bolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="scheduledBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="tempBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <relationship name="insulin" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedInsulinDistribution" inverseEntity="Oref0Suggestion"/>
+    </entity>
+    <entity name="LoopStatRecord" representedClassName="LoopStatRecord" syncable="YES" codeGenerationType="class">
+        <attribute name="duration" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <attribute name="end" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="loopStatus" optional="YES" attributeType="String"/>
+        <attribute name="start" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <entity name="Oref0Suggestion" representedClassName="Oref0Suggestion" syncable="YES" codeGenerationType="class">
+        <relationship name="computedInsulinDistribution" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="InsulinDistribution" inverseName="insulin" inverseEntity="InsulinDistribution"/>
+        <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
+    </entity>
+    <entity name="Readings" representedClassName="Readings" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+    </entity>
+    <entity name="TDD" representedClassName="TDD" syncable="YES" codeGenerationType="class">
+        <attribute name="tdd" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <relationship name="computed" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedTDD" inverseEntity="Oref0Suggestion"/>
+    </entity>
+</model>

+ 36 - 0
Dependencies/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

+ 13 - 0
Dependencies/CGMBLEKit/.travis.yml

@@ -0,0 +1,13 @@
+language: objective-c
+osx_image: xcode12.2
+
+before_script:
+    - ./Scripts/carthage.sh bootstrap
+
+script:
+    # Build frameworks and run tests
+    - travis_wait 25 xcodebuild -project CGMBLEKit.xcodeproj -scheme Shared build -destination name="iPhone 8" test | xcpretty
+    # Build apps
+    - xcodebuild -project CGMBLEKit.xcodeproj -scheme "CGMBLEKit Example" build -destination name="iPhone 8" | xcpretty
+    - xcodebuild -project CGMBLEKit.xcodeproj -scheme ResetTransmitter build -destination name="iPhone 8"
+

+ 140 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/AppDelegate.swift

@@ -0,0 +1,140 @@
+//
+//  AppDelegate.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 10/1/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import UIKit
+import CGMBLEKit
+import CoreBluetooth
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate, TransmitterDelegate, TransmitterCommandSource {
+    
+    var window: UIWindow?
+
+    static var sharedDelegate: AppDelegate {
+        return UIApplication.shared.delegate as! AppDelegate
+    }
+
+    var transmitterID: String? {
+        didSet {
+            if let id = transmitterID {
+                transmitter = Transmitter(
+                    id: id,
+                    passiveModeEnabled: UserDefaults.standard.passiveModeEnabled
+                )
+                transmitter?.stayConnected = UserDefaults.standard.stayConnected
+                transmitter?.delegate = self
+                transmitter?.commandSource = self
+
+                UserDefaults.standard.transmitterID = id
+            }
+            glucose = nil
+        }
+    }
+
+    var transmitter: Transmitter?
+
+    let commandQueue = CommandQueue()
+
+    var glucose: Glucose?
+
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+
+        transmitterID = UserDefaults.standard.transmitterID
+
+        return true
+    }
+
+    func applicationWillResignActive(_ application: UIApplication) {
+        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+    }
+
+    func applicationDidEnterBackground(_ application: UIApplication) {
+        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+
+        if let transmitter = transmitter, !transmitter.stayConnected {
+            transmitter.stopScanning()
+        }
+    }
+
+    func applicationWillEnterForeground(_ application: UIApplication) {
+        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+    }
+
+    func applicationDidBecomeActive(_ application: UIApplication) {
+        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+
+        transmitter?.resumeScanning()
+    }
+
+    func applicationWillTerminate(_ application: UIApplication) {
+        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+    }
+
+    // MARK: - TransmitterDelegate
+
+    private let dateFormatter: DateFormatter = {
+        let dateFormatter = DateFormatter()
+
+        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
+        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
+
+        return dateFormatter
+    }()
+
+    func dequeuePendingCommand(for transmitter: Transmitter) -> Command? {
+        return commandQueue.dequeue()
+    }
+
+    func transmitter(_ transmitter: Transmitter, didFail command: Command, with error: Error) {
+        // TODO: implement
+    }
+
+    func transmitter(_ transmitter: Transmitter, didComplete command: Command) {
+        // TODO: implement
+    }
+
+    func transmitter(_ transmitter: Transmitter, didError error: Error) {
+        DispatchQueue.main.async {
+            if let vc = self.window?.rootViewController as? TransmitterDelegate {
+                vc.transmitter(transmitter, didError: error)
+            }
+        }
+    }
+
+    func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) {
+        self.glucose = glucose
+        DispatchQueue.main.async {
+            if let vc = self.window?.rootViewController as? TransmitterDelegate {
+                vc.transmitter(transmitter, didRead: glucose)
+            }
+        }
+    }
+
+    func transmitter(_ transmitter: Transmitter, didReadUnknownData data: Data) {
+        DispatchQueue.main.async {
+            if let vc = self.window?.rootViewController as? TransmitterDelegate {
+                vc.transmitter(transmitter, didReadUnknownData: data)
+            }
+        }
+    }
+    
+    func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose]) {
+        DispatchQueue.main.async {
+            if let vc = self.window?.rootViewController as? TransmitterDelegate {
+                vc.transmitter(transmitter, didReadBackfill: glucose)
+            }
+        }
+    }
+    
+    func transmitterDidConnect(_ transmitter: Transmitter) {
+        // Ignore
+    }
+
+}

+ 98 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,98 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "83.5x83.5",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "size" : "1024x1024",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 25 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/Base.lproj/LaunchScreen.storyboard

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>

+ 172 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/Base.lproj/Main.storyboard

@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <device id="retina5_5" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
+        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="CGMBLEKit_Example" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="mqJ-p6-sSU">
+                                <rect key="frame" x="20" y="40" width="374" height="676"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="K5I-e5-MvQ">
+                                        <rect key="frame" x="15.666666666666657" y="0.0" width="343" height="52"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Transmitter ID" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5y6-vU-qC3">
+                                                <rect key="frame" x="8" y="14.333333333333334" width="249" height="20.333333333333329"/>
+                                                <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
+                                                <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oUg-jb-mCK">
+                                                <rect key="frame" x="265" y="42" width="70" height="1"/>
+                                                <color key="backgroundColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="1" id="Rjq-qn-DaB"/>
+                                                </constraints>
+                                            </view>
+                                            <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="500000" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="e0T-ru-tWD">
+                                                <rect key="frame" x="265" y="8" width="70" height="34"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="70" id="1v1-n5-aT4"/>
+                                                    <constraint firstAttribute="height" constant="34" id="CVt-L0-Ts1"/>
+                                                </constraints>
+                                                <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
+                                                <textInputTraits key="textInputTraits" autocapitalizationType="allCharacters" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" returnKeyType="done"/>
+                                                <connections>
+                                                    <outlet property="delegate" destination="BYZ-38-t0r" id="XHs-Ei-i6K"/>
+                                                </connections>
+                                            </textField>
+                                        </subviews>
+                                        <constraints>
+                                            <constraint firstItem="e0T-ru-tWD" firstAttribute="leading" secondItem="5y6-vU-qC3" secondAttribute="trailing" constant="8" id="1Y6-Tc-fCe"/>
+                                            <constraint firstItem="5y6-vU-qC3" firstAttribute="leading" secondItem="K5I-e5-MvQ" secondAttribute="leadingMargin" id="2up-6R-sUw"/>
+                                            <constraint firstItem="e0T-ru-tWD" firstAttribute="top" secondItem="K5I-e5-MvQ" secondAttribute="topMargin" id="9sj-7n-OeZ"/>
+                                            <constraint firstAttribute="trailingMargin" secondItem="e0T-ru-tWD" secondAttribute="trailing" id="BGc-6v-F21"/>
+                                            <constraint firstItem="5y6-vU-qC3" firstAttribute="baseline" secondItem="e0T-ru-tWD" secondAttribute="baseline" id="Ieq-vZ-reG"/>
+                                            <constraint firstItem="e0T-ru-tWD" firstAttribute="leading" secondItem="oUg-jb-mCK" secondAttribute="leading" id="PU1-pi-afh"/>
+                                            <constraint firstAttribute="bottomMargin" secondItem="e0T-ru-tWD" secondAttribute="bottom" constant="2" id="S63-7X-au0"/>
+                                            <constraint firstItem="oUg-jb-mCK" firstAttribute="width" secondItem="e0T-ru-tWD" secondAttribute="width" id="bYX-uk-kK1"/>
+                                            <constraint firstItem="oUg-jb-mCK" firstAttribute="top" secondItem="e0T-ru-tWD" secondAttribute="bottom" id="xJ6-dd-9PN"/>
+                                        </constraints>
+                                    </view>
+                                    <stackView opaque="NO" contentMode="scaleToFill" spacing="8" baselineRelativeArrangement="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4lp-eb-b4C">
+                                        <rect key="frame" x="74.333333333333329" y="129" width="225.33333333333337" height="31"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stay connected" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NSs-9e-3Sl">
+                                                <rect key="frame" x="0.0" y="0.0" width="168.33333333333334" height="31"/>
+                                                <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
+                                                <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="DR7-3h-biq">
+                                                <rect key="frame" x="176.33333333333337" y="0.0" width="51" height="31"/>
+                                                <connections>
+                                                    <action selector="toggleStayConnected:" destination="BYZ-38-t0r" eventType="valueChanged" id="Wci-5s-BJm"/>
+                                                </connections>
+                                            </switch>
+                                        </subviews>
+                                    </stackView>
+                                    <stackView opaque="NO" contentMode="scaleToFill" spacing="8" baselineRelativeArrangement="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lPZ-HJ-Wji">
+                                        <rect key="frame" x="74.333333333333329" y="237.33333333333331" width="225.33333333333337" height="31"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Run alongside G5 app" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3i8-2m-QFG">
+                                                <rect key="frame" x="0.0" y="0.0" width="168.33333333333334" height="31"/>
+                                                <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
+                                                <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="mjL-My-zyc">
+                                                <rect key="frame" x="176.33333333333337" y="0.0" width="51" height="31"/>
+                                                <connections>
+                                                    <action selector="togglePassiveMode:" destination="BYZ-38-t0r" eventType="valueChanged" id="vGB-RZ-H3T"/>
+                                                </connections>
+                                            </switch>
+                                        </subviews>
+                                    </stackView>
+                                    <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="v7k-HB-Hm0">
+                                        <rect key="frame" x="177" y="345.33333333333331" width="20" height="20"/>
+                                    </activityIndicatorView>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="––" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zAF-A7-1Oc">
+                                        <rect key="frame" x="173.66666666666666" y="442.33333333333331" width="27" height="31.333333333333314"/>
+                                        <fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
+                                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="20" baselineRelativeArrangement="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VKn-a6-Gbj">
+                                        <rect key="frame" x="74" y="551" width="226" height="30"/>
+                                        <subviews>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HEQ-Bp-kx7" userLabel="Start">
+                                                <rect key="frame" x="0.0" y="0.0" width="62" height="30"/>
+                                                <state key="normal" title="Start"/>
+                                                <connections>
+                                                    <action selector="start:" destination="BYZ-38-t0r" eventType="touchUpInside" id="CoA-cH-SFe"/>
+                                                </connections>
+                                            </button>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HYW-Ex-7LC">
+                                                <rect key="frame" x="82" y="0.0" width="62" height="30"/>
+                                                <state key="normal" title="Calibrate"/>
+                                                <connections>
+                                                    <action selector="calibrate:" destination="BYZ-38-t0r" eventType="touchUpInside" id="5Ft-BY-tyB"/>
+                                                </connections>
+                                            </button>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JAM-9K-KeE" userLabel="Stop">
+                                                <rect key="frame" x="164" y="0.0" width="62" height="30"/>
+                                                <state key="normal" title="Stop"/>
+                                                <connections>
+                                                    <action selector="stop:" destination="BYZ-38-t0r" eventType="touchUpInside" id="hWH-qi-m3s"/>
+                                                </connections>
+                                            </button>
+                                        </subviews>
+                                    </stackView>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Waiting for first reading" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9p7-LX-EMK">
+                                        <rect key="frame" x="-20" y="658" width="0.0" height="18"/>
+                                        <fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
+                                        <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <constraints>
+                                    <constraint firstItem="NSs-9e-3Sl" firstAttribute="width" secondItem="3i8-2m-QFG" secondAttribute="width" id="drg-Lk-TNg"/>
+                                </constraints>
+                            </stackView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="mqJ-p6-sSU" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="20" id="9eQ-5E-KMl"/>
+                            <constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="mqJ-p6-sSU" secondAttribute="bottom" constant="20" id="A1x-Bz-xwd"/>
+                            <constraint firstAttribute="trailingMargin" secondItem="mqJ-p6-sSU" secondAttribute="trailing" id="DSk-No-ziO"/>
+                            <constraint firstItem="mqJ-p6-sSU" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="m2Y-0g-gsl"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="passiveModeEnabledSwitch" destination="mjL-My-zyc" id="GAn-9j-EwV"/>
+                        <outlet property="scanningIndicatorView" destination="v7k-HB-Hm0" id="HgS-lj-R6v"/>
+                        <outlet property="stayConnectedSwitch" destination="DR7-3h-biq" id="cN5-a1-x8J"/>
+                        <outlet property="subtitleLabel" destination="9p7-LX-EMK" id="jTI-sZ-dNb"/>
+                        <outlet property="titleLabel" destination="zAF-A7-1Oc" id="W9K-V8-bIj"/>
+                        <outlet property="transmitterIDField" destination="e0T-ru-tWD" id="MTX-44-H7O"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="231" y="399"/>
+        </scene>
+    </scenes>
+</document>

+ 34 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/CommandQueue.swift

@@ -0,0 +1,34 @@
+//
+//  CommandQueue.swift
+//  CGMBLEKit Example
+//
+//  Created by Paul Dickens on 25/03/2018.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import Foundation
+import CGMBLEKit
+
+
+class CommandQueue {
+    private var list = Array<Command>()
+    private var lock = os_unfair_lock()
+
+    func enqueue(_ element: Command) {
+        os_unfair_lock_lock(&lock)
+        list.append(element)
+        os_unfair_lock_unlock(&lock)
+    }
+
+    func dequeue() -> Command? {
+        os_unfair_lock_lock(&lock)
+        defer {
+            os_unfair_lock_unlock(&lock)
+        }
+        if !list.isEmpty {
+            return list.removeFirst()
+        } else {
+            return nil
+        }
+    }
+}

+ 49 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/Info.plist

@@ -0,0 +1,49 @@
+<?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>$(DEVELOPMENT_LANGUAGE)</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>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>3.2</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>bluetooth-central</string>
+	</array>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>

+ 39 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/NSUserDefaults.swift

@@ -0,0 +1,39 @@
+//
+//  NSUserDefaults.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 11/24/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+extension UserDefaults {
+    var passiveModeEnabled: Bool {
+        get {
+            return bool(forKey: "passiveModeEnabled")
+        }
+        set {
+            set(newValue, forKey: "passiveModeEnabled")
+        }
+    }
+
+    var stayConnected: Bool {
+        get {
+            return object(forKey: "stayConnected") != nil ? bool(forKey: "stayConnected") : true
+        }
+        set {
+            set(newValue, forKey: "stayConnected")
+        }
+    }
+
+    var transmitterID: String {
+        get {
+            return string(forKey: "transmitterID") ?? "500000"
+        }
+        set {
+            set(newValue, forKey: "transmitterID")
+        }
+    }
+}

+ 200 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ViewController.swift

@@ -0,0 +1,200 @@
+//
+//  ViewController.swift
+//  xDrip5
+//
+//  Created by Nathan Racklyeft on 10/1/15.
+//  Copyright © 2015 Nathan Racklyeft. All rights reserved.
+//
+
+import UIKit
+import HealthKit
+import CGMBLEKit
+
+class ViewController: UIViewController, TransmitterDelegate, UITextFieldDelegate {
+
+    @IBOutlet weak var titleLabel: UILabel!
+
+    @IBOutlet weak var subtitleLabel: UILabel!
+
+    @IBOutlet weak var passiveModeEnabledSwitch: UISwitch!
+
+    @IBOutlet weak var stayConnectedSwitch: UISwitch!
+
+    @IBOutlet weak var transmitterIDField: UITextField!
+
+    @IBOutlet weak var scanningIndicatorView: UIActivityIndicatorView!
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        passiveModeEnabledSwitch.isOn = AppDelegate.sharedDelegate.transmitter?.passiveModeEnabled ?? false
+
+        stayConnectedSwitch.isOn = AppDelegate.sharedDelegate.transmitter?.stayConnected ?? false
+
+        transmitterIDField.text = AppDelegate.sharedDelegate.transmitter?.ID
+    }
+
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        updateIndicatorViewDisplay()
+    }
+
+    // MARK: - Actions
+
+    func updateIndicatorViewDisplay() {
+        if let transmitter = AppDelegate.sharedDelegate.transmitter, transmitter.isScanning {
+            scanningIndicatorView.startAnimating()
+        } else {
+            scanningIndicatorView.stopAnimating()
+        }
+    }
+
+    @IBAction func toggleStayConnected(_ sender: UISwitch) {
+        AppDelegate.sharedDelegate.transmitter?.stayConnected = sender.isOn
+        UserDefaults.standard.stayConnected = sender.isOn
+
+        updateIndicatorViewDisplay()
+    }
+
+    @IBAction func togglePassiveMode(_ sender: UISwitch) {
+        AppDelegate.sharedDelegate.transmitter?.passiveModeEnabled = sender.isOn
+        UserDefaults.standard.passiveModeEnabled = sender.isOn
+    }
+
+    @IBAction func start(_ sender: UIButton) {
+        let dialog = UIAlertController(title: "Confirm", message: "Start sensor session.", preferredStyle: .alert)
+
+        dialog.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction!) in
+            AppDelegate.sharedDelegate.commandQueue.enqueue(.startSensor(at: Date()))
+        }))
+
+        dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
+
+        present(dialog, animated: true, completion: nil)
+    }
+
+    @IBAction func calibrate(_ sender: UIButton) {
+        let dialog = UIAlertController(title: "Enter BG", message: "Calibrate sensor.", preferredStyle: .alert)
+
+        let unit = HKUnit.milligramsPerDeciliter
+
+        dialog.addTextField { (textField : UITextField!) in
+            textField.placeholder = unit.unitString
+            textField.keyboardType = .numberPad
+        }
+
+        dialog.addAction(UIAlertAction(title: "Calibrate", style: .default, handler: { (action: UIAlertAction!) in
+            let textField = dialog.textFields![0] as UITextField
+            let minGlucose = HKQuantity(unit: HKUnit.milligramsPerDeciliter, doubleValue: 40)
+            let maxGlucose = HKQuantity(unit: HKUnit.milligramsPerDeciliter, doubleValue: 400)
+
+            if let text = textField.text, let entry = Double(text) {
+                guard entry >= minGlucose.doubleValue(for: unit) && entry <= maxGlucose.doubleValue(for: unit) else {
+                    // TODO: notify the user if the glucose is not in range
+                    return
+                }
+                let glucose = HKQuantity(unit: unit, doubleValue: Double(entry))
+                AppDelegate.sharedDelegate.commandQueue.enqueue(.calibrateSensor(to: glucose, at: Date()))
+            }
+        }))
+
+        dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
+
+        present(dialog, animated: true, completion: nil)
+    }
+
+    @IBAction func stop(_ sender: UIButton) {
+        let dialog = UIAlertController(title: "Confirm", message: "Stop sensor session.", preferredStyle: .alert)
+
+        dialog.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction!) in
+            AppDelegate.sharedDelegate.commandQueue.enqueue(.stopSensor(at: Date()))
+        }))
+
+        dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
+
+        present(dialog, animated: true, completion: nil)
+    }
+
+    // MARK: - UITextFieldDelegate
+
+    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+        if let text = textField.text {
+            let newString = text.replacingCharacters(in: range.rangeOfString(text), with: string)
+
+            if newString.count > 6 {
+                return false
+            } else if newString.count == 6 {
+                AppDelegate.sharedDelegate.transmitterID = newString
+                textField.text = newString
+
+                textField.resignFirstResponder()
+
+                return false
+            }
+        }
+
+        return true
+    }
+
+    func textFieldDidEndEditing(_ textField: UITextField) {
+        if textField.text?.count != 6 {
+            textField.text = UserDefaults.standard.transmitterID
+        }
+    }
+
+    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
+        return true
+    }
+
+    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+        return true
+    }
+
+    // MARK: - TransmitterDelegate
+
+    func transmitter(_ transmitter: Transmitter, didError error: Error) {
+        print("Transmitter Error: \(error)")
+        titleLabel.text = NSLocalizedString("Error", comment: "Title displayed during error response")
+
+        subtitleLabel.text = "\(error)"
+    }
+
+    func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) {
+        let unit = HKUnit.milligramsPerDeciliter
+        if let value = glucose.glucose?.doubleValue(for: unit) {
+            titleLabel.text = "\(value) \(unit.unitString)"
+        } else {
+            titleLabel.text = String(describing: glucose.state)
+        }
+
+
+        let date = glucose.readDate
+        subtitleLabel.text = DateFormatter.localizedString(from: date, dateStyle: .none, timeStyle: .long)
+    }
+
+    func transmitter(_ transmitter: Transmitter, didReadUnknownData data: Data) {
+        titleLabel.text = NSLocalizedString("Unknown Data", comment: "Title displayed during unknown data response")
+        subtitleLabel.text = data.hexadecimalString
+    }
+    
+    func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose]) {
+        titleLabel.text = NSLocalizedString("Backfill", comment: "Title displayed during backfill response")
+        subtitleLabel.text = String(describing: glucose.map { $0.glucose })
+    }
+    
+    func transmitterDidConnect(_ transmitter: Transmitter) {
+        // Ignore
+    }
+
+}
+
+
+private extension NSRange {
+    func rangeOfString(_ string: String) -> Range<String.Index> {
+        let startIndex = string.index(string.startIndex, offsetBy: location)
+        let endIndex = string.index(startIndex, offsetBy: length)
+        return startIndex..<endIndex
+    }
+}
+

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/da.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Backfill";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unknown Data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/da.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Kør langs G5-appen";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Senders id";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Venter på førstebehandling";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibrer";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Hold op";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Forbliv forbundet";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/de.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Auffüllen";
+
+/* Title displayed during error response */
+"Error" = "Fehler";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unbekannte Daten";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/de.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Neben G5-App ausführen";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Transmitter-ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Warte auf den ersten Wert";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibrieren";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Verbunden bleiben";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/en.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Backfill";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unknown Data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/en.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Run alongside G5 app";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Transmitter ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Waiting for first reading";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibrate";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Stay connected";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/es.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Rellenar";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Datos desconocidos";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/es.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Ejecutar junto con la app del G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Identificador del transmisor";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Esperando por el primero dato";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Comenzar";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibrar";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Parar";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Mantenerse conectado";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 9 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/fi.lproj/Localizable.strings

@@ -0,0 +1,9 @@
+/* Title displayed during backfill response */
+"Backfill" = "Täyttö";
+
+/* Title displayed during error response */
+"Error" = "Virhe";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Tuntematon tieto";
+

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/fi.lproj/Main.strings

@@ -0,0 +1,27 @@
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Käytä G5-sovelluksen rinnalla";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Lähettimen tunniste";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Odotetaan ensimmäistä lukemaa";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Aloita";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibroi";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Pysäytä";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Pidä yhdistettynä";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";
+

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/fr.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Récupération des données antérieures";
+
+/* Title displayed during error response */
+"Error" = "Erreur";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Donnée inconnue";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/fr.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Faire fonctionner l’application G5 en parallèle";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "ID du transmetteur";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "En attente de la première mesure";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Commencer";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Étalonner";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Arrêter";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Rester connecté";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 1 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/he.lproj/LaunchScreen.strings

@@ -0,0 +1 @@
+

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/he.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Backfill";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unknown Data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/he.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Run alongside G5 app";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Transmitter ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Waiting for first reading";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibrate";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Stay connected";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/it.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Backfill";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unknown Data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/it.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Esegui insieme all’app G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "ID trasmettitore";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "In attesa della prima lettura";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Avvia";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibra";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Interrompi";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Rimani connesso";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ja.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "埋め戻し";
+
+/* Title displayed during error response */
+"Error" = "エラー";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "不明なデータ";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ja.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "G5アプリのそばで使用してください";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "トランスミッタ ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "最初の読み取りを待っています";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "開始";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "較正";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "停止";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "接続を維持";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/nb.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Hent historikk";
+
+/* Title displayed during error response */
+"Error" = "Feil";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Ukjent data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/nb.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Kjør parralelt med G5 app";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Sender ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Venter på første måling";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibrer";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Vær tilkoblet";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 9 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/nl.lproj/Localizable.strings

@@ -0,0 +1,9 @@
+/* Title displayed during backfill response */
+"Backfill" = "Aanvullen van data";
+
+/* Title displayed during error response */
+"Error" = "Fout";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Onbekende Data";
+

+ 21 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/nl.lproj/Main.strings

@@ -0,0 +1,21 @@
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Gebruik tegelijk met G5 app";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Zender ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Wachten op eerste meting";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibreren";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Blijf verbonden";
+

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/pl.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Backfill";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unknown Data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/pl.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Przejrzyj aplikację G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "ID nadajnika";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Oczekiwanie na pierwszy odczyt";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibruj";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Nie rozłączaj się";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/pt-BR.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Preenchimento";
+
+/* Title displayed during error response */
+"Error" = "Erro";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Dados Desconhecidos";

+ 26 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/pt-BR.lproj/Main.strings

@@ -0,0 +1,26 @@
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Executa em paralelo com o aplicativo G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "ID do Transmissor";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Esperando a primeira leitura";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Iniciar";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibrar";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Parar";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Permanecer conectado";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ro.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Completare retroactivă";
+
+/* Title displayed during error response */
+"Error" = "Eroare";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Date necunoscute";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ro.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Rulează împreună cu aplicația G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "ID transmițător";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Se așteaptă prima citire";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Pornește";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibrează";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Păstrează conexiunea";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ru.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Заполнение данными";
+
+/* Title displayed during error response */
+"Error" = "Ошибка";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Неизвестные данные";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/ru.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "запустить совместно с приложением G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "идентификатор трансмиттера";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "ожидание данных";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Начать";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Калибровка";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Стоп";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Не разъединяйтесь";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 9 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/sv.lproj/Localizable.strings

@@ -0,0 +1,9 @@
+/* Title displayed during backfill response */
+"Backfill" = "Fyller i historisk data";
+
+/* Title displayed during error response */
+"Error" = "Fel";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Okänd data";
+

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/sv.lproj/Main.strings

@@ -0,0 +1,27 @@
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Kör samtidigt som G5-appen";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Sändar-ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Väntar på första värdet";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Starta";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Kalibrera";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stoppa";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Fortsätt att vara ansluten";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";
+

+ 1 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/tr.lproj/LaunchScreen.strings

@@ -0,0 +1 @@
+

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/tr.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Backfill";
+
+/* Title displayed during error response */
+"Error" = "Error";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Unknown Data";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/tr.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Run alongside G5 app";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Transmitter ID";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Waiting for first reading";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Start";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Calibrate";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Stop";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Stay connected";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/vi.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "Lấp đầy";
+
+/* Title displayed during error response */
+"Error" = "Lỗi";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "Không nhận biết dữ liệu";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/vi.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "Chạy song song cùng ứng dụng G5";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "Số ID của Transmitter";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "Đang đợi đọc kết quả đầu tiên";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "Bắt đầu";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "Hiệu chỉnh";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "Dừng lại";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "Giữ kết  nối";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

+ 8 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/zh-Hans.lproj/Localizable.strings

@@ -0,0 +1,8 @@
+/* Title displayed during backfill response */
+"Backfill" = "数据回填";
+
+/* Title displayed during error response */
+"Error" = "错误";
+
+/* Title displayed during unknown data response */
+"Unknown Data" = "未知数据";

+ 27 - 0
Dependencies/CGMBLEKit/CGMBLEKit Example/zh-Hans.lproj/Main.strings

@@ -0,0 +1,27 @@
+
+/* Class = "UILabel"; text = "Run alongside G5 app"; ObjectID = "3i8-2m-QFG"; */
+"3i8-2m-QFG.text" = "与G5 App同时使用";
+
+/* Class = "UILabel"; text = "Transmitter ID"; ObjectID = "5y6-vU-qC3"; */
+"5y6-vU-qC3.text" = "发射器编号";
+
+/* Class = "UILabel"; text = "Waiting for first reading"; ObjectID = "9p7-LX-EMK"; */
+"9p7-LX-EMK.text" = "输入第一个校准值";
+
+/* Class = "UIButton"; normalTitle = "Start"; ObjectID = "HEQ-Bp-kx7"; */
+"HEQ-Bp-kx7.normalTitle" = "启动";
+
+/* Class = "UIButton"; normalTitle = "Calibrate"; ObjectID = "HYW-Ex-7LC"; */
+"HYW-Ex-7LC.normalTitle" = "校准";
+
+/* Class = "UIButton"; normalTitle = "Stop"; ObjectID = "JAM-9K-KeE"; */
+"JAM-9K-KeE.normalTitle" = "停止";
+
+/* Class = "UILabel"; text = "Stay connected"; ObjectID = "NSs-9e-3Sl"; */
+"NSs-9e-3Sl.text" = "保持连接";
+
+/* Class = "UITextField"; text = "500000"; ObjectID = "e0T-ru-tWD"; */
+"e0T-ru-tWD.text" = "500000";
+
+/* Class = "UILabel"; text = "––"; ObjectID = "zAF-A7-1Oc"; */
+"zAF-A7-1Oc.text" = "––";

File diff ditekan karena terlalu besar
+ 1738 - 17
Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/project.pbxproj


+ 97 - 0
Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/CGMBLEKit Example.xcscheme

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1330"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "437AFEF62038EC43008C4892"
+               BuildableName = "CGMBLEKit Example.app"
+               BlueprintName = "CGMBLEKit Example"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "437AFEF62038EC43008C4892"
+            BuildableName = "CGMBLEKit Example.app"
+            BlueprintName = "CGMBLEKit Example"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "43CABDFC1C3506F100005705"
+               BuildableName = "CGMBLEKitTests.xctest"
+               BlueprintName = "CGMBLEKitTests"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </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">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "437AFEF62038EC43008C4892"
+            BuildableName = "CGMBLEKit Example.app"
+            BlueprintName = "CGMBLEKit Example"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "437AFEF62038EC43008C4892"
+            BuildableName = "CGMBLEKit Example.app"
+            BlueprintName = "CGMBLEKit Example"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 87 - 0
Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/ResetTransmitter.xcscheme

@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1330"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4308103220785FEA00B66384"
+               BuildableName = "ResetTransmitter.app"
+               BlueprintName = "ResetTransmitter"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4308103220785FEA00B66384"
+            BuildableName = "ResetTransmitter.app"
+            BlueprintName = "ResetTransmitter"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <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">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4308103220785FEA00B66384"
+            BuildableName = "ResetTransmitter.app"
+            BlueprintName = "ResetTransmitter"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4308103220785FEA00B66384"
+            BuildableName = "ResetTransmitter.app"
+            BlueprintName = "ResetTransmitter"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 76 - 0
Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/Shared-watchOS.xcscheme

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1330"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "NO">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A9AD37DE225EEE850058C179"
+               BuildableName = "CGMBLEKit.framework"
+               BlueprintName = "CGMBLEKit-watchOS"
+               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">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "A9AD37DE225EEE850058C179"
+            BuildableName = "CGMBLEKit.framework"
+            BlueprintName = "CGMBLEKit-watchOS"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "A9AD37DE225EEE850058C179"
+            BuildableName = "CGMBLEKit.framework"
+            BlueprintName = "CGMBLEKit-watchOS"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 137 - 0
Dependencies/CGMBLEKit/CGMBLEKit.xcodeproj/xcshareddata/xcschemes/Shared.xcscheme

@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1330"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "NO"
+      buildImplicitDependencies = "NO">
+      <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>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "43A8EC51210D0A7400A81379"
+               BuildableName = "CGMBLEKitUI.framework"
+               BlueprintName = "CGMBLEKitUI"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B4D40D3D23A428BC00D7ECB5"
+               BuildableName = "CGMBLEKitG5Plugin.loopplugin"
+               BlueprintName = "CGMBLEKitG5Plugin"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B4D40D2D23A3E91800D7ECB5"
+               BuildableName = "CGMBLEKitG6Plugin.loopplugin"
+               BlueprintName = "CGMBLEKitG6Plugin"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "43CABDF21C3506F100005705"
+            BuildableName = "CGMBLEKit.framework"
+            BlueprintName = "CGMBLEKit"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "43CABDFC1C3506F100005705"
+               BuildableName = "CGMBLEKitTests.xctest"
+               BlueprintName = "CGMBLEKitTests"
+               ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </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">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "43CABDF21C3506F100005705"
+            BuildableName = "CGMBLEKit.framework"
+            BlueprintName = "CGMBLEKit"
+            ReferencedContainer = "container:CGMBLEKit.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </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>

+ 41 - 23
Dependencies/CGMBLEKit/CGMBLEKit/Glucose+SensorDisplayable.swift

@@ -7,9 +7,10 @@
 //
 
 import Foundation
+import LoopKit
 
 
-extension Glucose {
+extension Glucose: GlucoseDisplayable {
     public var isStateValid: Bool {
         return state == .known(.ok) && status == .ok
     }
@@ -38,30 +39,47 @@ extension Glucose {
         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 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
     }
+    
+    // TODO Placeholders. This functionality will come with LOOP-1311
+    public var glucoseRangeCategory: GlucoseRangeCategory? {
+        return nil
+    }
+}
+
+extension Glucose {
+    public var condition: GlucoseCondition? {
+        if glucoseMessage.glucose < GlucoseLimits.minimum {
+            return .belowRange
+        } else if glucoseMessage.glucose > GlucoseLimits.maximum {
+            return .aboveRange
+        } else {
+            return nil
+        }
+    }
 }

+ 7 - 1
Dependencies/CGMBLEKit/CGMBLEKit/Glucose.swift

@@ -9,6 +9,10 @@
 import Foundation
 import HealthKit
 
+enum GlucoseLimits {
+    static var minimum: UInt16 = 40
+    static var maximum: UInt16 = 400
+}
 
 public struct Glucose {
     let glucoseMessage: GlucoseSubMessage
@@ -46,6 +50,7 @@ public struct Glucose {
         self.activationDate = activationDate
 
         sessionStartDate = activationDate.addingTimeInterval(TimeInterval(timeMessage.sessionStartTime))
+        sessionExpDate = activationDate.addingTimeInterval(TimeInterval(timeMessage.sessionStartTime) + (10*24*60*60))
         readDate = activationDate.addingTimeInterval(TimeInterval(glucoseMessage.timestamp))
         lastCalibration = calibrationMessage != nil ? Calibration(calibrationMessage: calibrationMessage!, activationDate: activationDate) : nil
     }
@@ -55,6 +60,7 @@ public struct Glucose {
     public let status: TransmitterStatus
     public let activationDate: Date
     public let sessionStartDate: Date
+    public let sessionExpDate: Date
 
     // MARK: - Glucose Info
     public let lastCalibration: Calibration?
@@ -71,7 +77,7 @@ public struct Glucose {
 
         let unit = HKUnit.milligramsPerDeciliter
 
-        return HKQuantity(unit: unit, doubleValue: Double(glucoseMessage.glucose))
+        return HKQuantity(unit: unit, doubleValue: Double(min(max(glucoseMessage.glucose, GlucoseLimits.minimum), GlucoseLimits.maximum)))
     }
 
     public var state: CalibrationState {

+ 1 - 0
Dependencies/CGMBLEKit/CGMBLEKit/Messages/TransmitterTimeRxMessage.swift

@@ -26,6 +26,7 @@ struct TransmitterTimeRxMessage: TransmitterRxMessage {
         status = data[1]
         currentTime = data[2..<6].toInt()
         sessionStartTime = data[6..<10].toInt()
+
     }
 }
 

+ 391 - 4
Dependencies/CGMBLEKit/CGMBLEKit/OSLog.swift

@@ -4,29 +4,143 @@
 //
 //  Copyright © 2017 LoopKit Authors. All rights reserved.
 //
+//
+//  OSLog.swift
+//  OmniBLE
+//
+//  Copyright © 2017 LoopKit Authors. All rights reserved.
+// OSLog updated for FreeAPSX logs
+//
 
 import os.log
+import Foundation
+
+let storeLoopLog: Bool = false
+
+let loggerLock = NSRecursiveLock()
+let baseReporter: IssueReporter = SimpleLogReporter()
+let category = Logger.Category.CGMBLEKit
+
+extension NSLocking {
+    func perform<T>(_ block: () throws -> T) rethrows -> T {
+        lock()
+        defer { unlock() }
+        return try block()
+    }
+}
+
+extension NSRecursiveLock {
+    convenience init(label: String) {
+        self.init()
+        name = label
+    }
+}
 
+extension NSLock {
+    convenience init(label: String) {
+        self.init()
+        name = label
+    }
+}
 
 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)
+        if (!storeLoopLog) {
+            log(message, type: .debug, args)
+        } else {
+            let msg_format = message.withUTF8Buffer{
+                String(decoding: $0, as: UTF8.self)
+            }
+            
+            let msg = String(format: msg_format.replacingOccurrences(of: "{public}", with: ""), args)
+            DispatchWorkItem(qos: .background, flags: .enforceQoS) {
+                loggerLock.perform {
+                    category.logger.debug(
+                        {msg}(),
+                        printToConsole: true,
+                        file: #file,
+                        function: #function,
+                        line: #line
+                    )
+                }
+            }.perform()
+        }
     }
 
     func info(_ message: StaticString, _ args: CVarArg...) {
-        log(message, type: .info, args)
+        if (!storeLoopLog) {
+            log(message, type: .info, args)
+        } else {
+            let msg_format = message.withUTF8Buffer{
+                String(decoding: $0, as: UTF8.self)
+            }
+            let msg = String(format: msg_format.replacingOccurrences(of: "{public}", with: ""), args)
+            DispatchWorkItem(qos: .background, flags: .enforceQoS) {
+                loggerLock.perform {
+                    category.logger.info(
+                        {msg}(),
+                        file: #file,
+                        function: #function,
+                        line: #line
+                    )
+                }
+            }.perform()
+        }
+        
+        
     }
 
     func `default`(_ message: StaticString, _ args: CVarArg...) {
-        log(message, type: .default, args)
+        if (!storeLoopLog) {
+            log(message, type: .default, args)
+        } else {
+            let msg_format = message.withUTF8Buffer{
+                String(decoding: $0, as: UTF8.self)
+            }
+            let msg = String(format: msg_format.replacingOccurrences(of: "{public}", with: ""), args)
+            DispatchWorkItem(qos: .background, flags: .enforceQoS) {
+                loggerLock.perform {
+                    category.logger.debug(
+                        {msg}(),
+                        printToConsole: true,
+                        file: #file,
+                        function: #function,
+                        line: #line
+                    )
+                }
+            }.perform()
+        }
+        
     }
 
     func error(_ message: StaticString, _ args: CVarArg...) {
-        log(message, type: .error, args)
+        if (!storeLoopLog) {
+            log(message, type: .error, args)
+        } else {
+            let msg_format = message.withUTF8Buffer{
+                String(decoding: $0, as: UTF8.self)
+            }
+            let msg = String(format: msg_format.replacingOccurrences(of: "{public}", with: ""), args)
+            DispatchWorkItem(qos: .background, flags: .enforceQoS) {
+               
+                loggerLock.perform {
+                    category.logger.warning(
+                        {msg}(),
+                        description:  {msg}(),
+                        error: nil,
+                        file: #file,
+                        function: #function,
+                        line: #line
+                    )
+                }
+            }.perform()
+        }
+        
     }
 
     private func log(_ message: StaticString, type: OSLogType, _ args: [CVarArg]) {
@@ -48,3 +162,276 @@ extension OSLog {
         }
     }
 }
+
+protocol IssueReporter: AnyObject {
+    /// Call this method in `applicationDidFinishLaunching()`.
+    func setup()
+
+    func setUserIdentifier(_: String?)
+
+    func reportNonFatalIssue(withName: String, attributes: [String: String])
+
+    func reportNonFatalIssue(withError: NSError)
+
+    func log(_ category: String, _ message: String, file: String, function: String, line: UInt)
+}
+
+final class Logger {
+    static let `default` = Logger(category: .default, reporter: baseReporter)
+    static let  CGMBLEKit = Logger(category: .CGMBLEKit, reporter: baseReporter)
+
+    enum Category: String {
+        case `default`
+        case CGMBLEKit
+
+        var name: String {
+            rawValue
+        }
+
+        var logger: Logger {
+            switch self {
+            case .default: return .default
+            case .CGMBLEKit: return .CGMBLEKit
+            
+            }
+        }
+
+        fileprivate var log: OSLog {
+            let subsystem = Bundle.main.bundleIdentifier!
+            switch self {
+            case .default: return OSLog.default
+            case .CGMBLEKit: return OSLog(subsystem: subsystem, category: name)
+            }
+        }
+    }
+
+    fileprivate enum Error: Swift.Error {
+        case error(String)
+        case errorWithInnerError(String, Swift.Error)
+        case errorWithDescription(String, String)
+        case errorWithDescriptionAndInnerError(String, String, Swift.Error)
+
+        private func domain() -> String {
+            switch self {
+            case let .error(domain),
+                 let .errorWithDescription(domain, _),
+                 let .errorWithDescriptionAndInnerError(domain, _, _),
+                 let .errorWithInnerError(domain, _):
+                return domain
+            }
+        }
+
+        private func innerError() -> Swift.Error? {
+            switch self {
+            case let .errorWithDescriptionAndInnerError(_, _, error),
+                 let .errorWithInnerError(_, error):
+                return error
+            default: return nil
+            }
+        }
+
+        func asNSError() -> NSError {
+            var info: [String: Any] = ["Description": String(describing: self)]
+
+            if let error = innerError() {
+                info["Error"] = String(describing: error)
+            }
+
+            return NSError(domain: domain(), code: -1, userInfo: info)
+        }
+    }
+
+    private let category: Category
+    private let reporter: IssueReporter
+    let log: OSLog
+
+    private init(category: Category, reporter: IssueReporter) {
+        self.category = category
+        self.reporter = reporter
+        log = category.log
+    }
+
+    static func setup() {
+        loggerLock.perform {
+            baseReporter.setup()
+        }
+    }
+
+    func debug(
+        _ message: @autoclosure () -> String,
+        printToConsole: Bool = true,
+        file: String = #file,
+        function: String = #function,
+        line: UInt = #line
+    ) {
+        let message = "DEV: \(message())"
+        if printToConsole {
+            os_log("%@ - %@ - %d %{public}@", log: log, type: .debug, file.file, function, line, message)
+        }
+        reporter.log(category.name, message, file: file, function: function, line: line)
+    }
+
+    func info(
+        _ message: String,
+        file: String = #file,
+        function: String = #function,
+        line: UInt = #line
+    ) {
+        let printedMessage = "INFO: \(message)"
+        os_log("%@ - %@ - %d %{public}@", log: log, type: .info, file.file, function, line, printedMessage)
+        reporter.log(category.name, printedMessage, file: file, function: function, line: line)
+    }
+
+    func warning(
+        _ message: String,
+        description: String? = nil,
+        error maybeError: Swift.Error? = nil,
+        file: String = #file,
+        function: String = #function,
+        line: UInt = #line
+    ) {
+        let loggerError = maybeError.loggerError(message: message, withDescription: description)
+        let message = "WARN: \(String(describing: loggerError))"
+
+        os_log("%@ - %@ - %d %{public}@", log: log, type: .default, file.file, function, line, message)
+        reporter.log(category.name, message, file: file, function: function, line: line)
+        reporter.reportNonFatalIssue(withError: loggerError.asNSError())
+        
+    }
+
+    func error(
+        _ message: String,
+        description: String? = nil,
+        error maybeError: Swift.Error? = nil,
+        file: String = #file,
+        function: String = #function,
+        line: UInt = #line
+    ) -> Never {
+        errorWithoutFatalError(message, description: description, error: maybeError, file: file, function: function, line: line)
+
+        fatalError(
+            "\(message) @ \(String(describing: description)) @ \(String(describing: maybeError)) @ \(file) @ \(function) @ \(line)"
+        )
+    }
+
+
+    fileprivate func errorWithoutFatalError(
+        _ message: String,
+        description: String? = nil,
+        error maybeError: Swift.Error? = nil,
+        file: String = #file,
+        function: String = #function,
+        line: UInt = #line
+    ) {
+        let loggerError = maybeError.loggerError(message: message, withDescription: description)
+        let message = "ERR: \(String(describing: loggerError))"
+
+        os_log("%@ - %@ - %d %{public}@", log: log, type: .error, file.file, function, line, message)
+        reporter.log(category.name, message, file: file, function: function, line: line)
+        reporter.reportNonFatalIssue(withError: loggerError.asNSError())
+    }
+}
+
+private extension Optional where Wrapped == Swift.Error {
+    func loggerError(message: String, withDescription description: String?) -> Logger.Error {
+        switch (description, self) {
+        case (nil, nil):
+            return .error(message)
+        case let (descr?, nil):
+            return .errorWithDescription(message, descr)
+        case let (nil, error?):
+            return .errorWithInnerError(message, error)
+        case let (descr?, error?):
+            return .errorWithDescriptionAndInnerError(message, descr, error)
+        }
+    }
+}
+
+
+final class SimpleLogReporter: IssueReporter {
+    private let fileManager = FileManager.default
+
+    private var dateFormatter: DateFormatter {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
+        return dateFormatter
+    }
+
+    func setup() {}
+
+    func setUserIdentifier(_: String?) {}
+
+    func reportNonFatalIssue(withName _: String, attributes _: [String: String]) {}
+
+    func reportNonFatalIssue(withError _: NSError) {}
+
+    func log(_ category: String, _ message: String, file: String, function: String, line: UInt) {
+        let now = Date()
+        let startOfDay = Calendar.current.startOfDay(for: now)
+
+        if !fileManager.fileExists(atPath: SimpleLogReporter.logDir) {
+            try? fileManager.createDirectory(
+                atPath: SimpleLogReporter.logDir,
+                withIntermediateDirectories: false,
+                attributes: nil
+            )
+        }
+
+        if !fileManager.fileExists(atPath: SimpleLogReporter.logFile) {
+            createFile(at: startOfDay)
+        } else {
+            if let attributes = try? fileManager.attributesOfItem(atPath: SimpleLogReporter.logFile),
+               let creationDate = attributes[.creationDate] as? Date, creationDate < startOfDay
+            {
+                try? fileManager.removeItem(atPath: SimpleLogReporter.logFilePrev)
+                try? fileManager.moveItem(atPath: SimpleLogReporter.logFile, toPath: SimpleLogReporter.logFilePrev)
+                createFile(at: startOfDay)
+            }
+        }
+
+        let logEntry = "\(dateFormatter.string(from: now)) [\(category)] \(file.file) - \(function) - \(line) - \(message)\n"
+        let data = logEntry.data(using: .utf8)!
+        try? data.append(fileURL: URL(fileURLWithPath: SimpleLogReporter.logFile))
+    }
+
+    private func createFile(at date: Date) {
+        fileManager.createFile(atPath: SimpleLogReporter.logFile, contents: nil, attributes: [.creationDate: date])
+    }
+
+    static var logFile: String {
+        getDocumentsDirectory().appendingPathComponent("logs/log.txt").path
+    }
+
+    static var logDir: String {
+        getDocumentsDirectory().appendingPathComponent("logs").path
+    }
+
+    static var logFilePrev: String {
+        getDocumentsDirectory().appendingPathComponent("logs/log_prev.txt").path
+    }
+
+    static func getDocumentsDirectory() -> URL {
+        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
+        let documentsDirectory = paths[0]
+        return documentsDirectory
+    }
+}
+
+private extension Data {
+    func append(fileURL: URL) throws {
+        if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
+            defer {
+                fileHandle.closeFile()
+            }
+            fileHandle.seekToEndOfFile()
+            fileHandle.write(self)
+        } else {
+            try write(to: fileURL, options: .atomic)
+        }
+    }
+}
+
+private extension String {
+    var file: String { components(separatedBy: "/").last ?? "" }
+}
+

+ 255 - 81
Dependencies/CGMBLEKit/CGMBLEKit/TransmitterManager.swift

@@ -5,15 +5,14 @@
 //  Copyright © 2017 LoopKit Authors. All rights reserved.
 //
 
-import os.log
 import HealthKit
+import LoopKit
+import ShareClient
+import os.log
 
-public protocol TransmitterManagerDelegate: AnyObject {
-    func transmitterManager(_ manager: TransmitterManager, didRead glucose: [Glucose])
-    func transmitterManager(_ manager: TransmitterManager, didFailWith error: Error)
-}
 
-public struct TransmitterManagerState: Equatable {
+public struct TransmitterManagerState: RawRepresentable, Equatable {
+    public typealias RawValue = CGMManager.RawStateValue
 
     public static let version = 1
 
@@ -27,6 +26,24 @@ public struct TransmitterManagerState: Equatable {
         self.transmitterID = transmitterID
         self.shouldSyncToRemoteService = shouldSyncToRemoteService
     }
+
+    public init?(rawValue: RawValue) {
+        guard let transmitterID = rawValue["transmitterID"] as? String
+        else {
+            return nil
+        }
+        
+        let shouldSyncToRemoteService = rawValue["shouldSyncToRemoteService"] as? Bool ?? false
+
+        self.init(transmitterID: transmitterID, shouldSyncToRemoteService: shouldSyncToRemoteService)
+    }
+
+    public var rawValue: RawValue {
+        return [
+            "transmitterID": transmitterID,
+            "shouldSyncToRemoteService": shouldSyncToRemoteService,
+        ]
+    }
 }
 
 
@@ -34,9 +51,8 @@ public protocol TransmitterManagerObserver: AnyObject {
     func transmitterManagerDidUpdateLatestReading(_ manager: TransmitterManager)
 }
 
-public class TransmitterManager: TransmitterDelegate {
-    public weak var delegate: TransmitterManagerDelegate?
 
+public class TransmitterManager: TransmitterDelegate {
     private var state: TransmitterManagerState
 
     private let observers = WeakSynchronizedSet<TransmitterManagerObserver>()
@@ -46,13 +62,89 @@ public class TransmitterManager: TransmitterDelegate {
         // TODO: we should decode and persist transmitter session state
         return !state.transmitterID.isEmpty
     }
-
+    
+    public var cgmManagerStatus: CGMManagerStatus {
+        return CGMManagerStatus(hasValidSensorSession: hasValidSensorSession, device: device)
+    }
 
     public required init(state: TransmitterManagerState) {
         self.state = state
         self.transmitter = Transmitter(id: state.transmitterID, passiveModeEnabled: state.passiveModeEnabled)
+        self.shareManager = ShareClientManager()
 
         self.transmitter.delegate = self
+        
+        #if targetEnvironment(simulator)
+        setupSimulatedSampleGenerator()
+        #endif
+
+    }
+    
+    #if targetEnvironment(simulator)
+    var simulatedSampleGeneratorTimer: DispatchSourceTimer?
+
+    private func setupSimulatedSampleGenerator() {
+
+        let timer = DispatchSource.makeTimerSource(queue: DispatchQueue(label: "com.loopkit.simulatedSampleGenerator"))
+        timer.schedule(deadline: .now() + .seconds(10), repeating: .minutes(5))
+        timer.setEventHandler(handler: { [weak self] in
+            self?.generateSimulatedSample()
+        })
+        self.simulatedSampleGeneratorTimer = timer
+        timer.resume()
+    }
+
+    private func generateSimulatedSample() {
+        let timestamp = Date()
+        let syncIdentifier =  "\(self.state.transmitterID) \(timestamp)"
+        let period = TimeInterval(hours: 3)
+        func glucoseValueFunc(timestamp: Date, period: Double) -> Double {
+            return 100 + 20 * cos(timestamp.timeIntervalSinceReferenceDate.remainder(dividingBy: period) / period * Double.pi * 2)
+        }
+        let glucoseValue = glucoseValueFunc(timestamp: timestamp, period: period)
+        let prevGlucoseValue = glucoseValueFunc(timestamp: timestamp - period, period: period)
+        let trendRateValue = glucoseValue - prevGlucoseValue
+        let trend: GlucoseTrend? = {
+            switch trendRateValue {
+            case -0.01...0.01:
+                return .flat
+            case -2 ..< -0.01:
+                return .down
+            case -5 ..< -2:
+                return .downDown
+            case -Double.greatestFiniteMagnitude ..< -5:
+                return .downDownDown
+            case 0.01...2:
+                return .up
+            case 2...5:
+                return .upUp
+            case 5...Double.greatestFiniteMagnitude:
+                return .upUpUp
+            default:
+                return nil
+            }
+        }()
+
+        let quantity = HKQuantity(unit: .milligramsPerDeciliter, doubleValue: glucoseValue)
+        let trendRate = HKQuantity(unit: .milligramsPerDeciliter, doubleValue: trendRateValue)
+        let sample = NewGlucoseSample(date: timestamp, quantity: quantity, condition: nil, trend: trend, trendRate: trendRate, isDisplayOnly: false, wasUserEntered: false, syncIdentifier: syncIdentifier)
+        self.updateDelegate(with: .newData([sample]))
+    }
+    #endif
+
+    required convenience public init?(rawState: CGMManager.RawStateValue) {
+        guard let state = TransmitterManagerState(rawValue: rawState) else {
+            return nil
+        }
+
+        self.init(state: state)
+    }
+
+    public var rawState: CGMManager.RawStateValue {
+        return state.rawValue
+    }
+    
+    func logDeviceCommunication(_ message: String, type: DeviceLogEntryType = .send) {
     }
 
     public var shouldSyncToRemoteService: Bool {
@@ -65,6 +157,23 @@ public class TransmitterManager: TransmitterDelegate {
         }
     }
 
+    public var cgmManagerDelegate: CGMManagerDelegate? {
+        get {
+            return shareManager.cgmManagerDelegate
+        }
+        set {
+            shareManager.cgmManagerDelegate = newValue
+        }
+    }
+
+    public var delegateQueue: DispatchQueue! {
+        get {
+            return shareManager.delegateQueue
+        }
+        set {
+            shareManager.delegateQueue = newValue
+        }
+    }
 
     private(set) public var latestConnection: Date? {
         get {
@@ -76,6 +185,8 @@ public class TransmitterManager: TransmitterDelegate {
     }
     private let lockedLatestConnection: Locked<Date?> = Locked(nil)
 
+    public let shareManager: ShareClientManager
+
     public let transmitter: Transmitter
     let log = OSLog(category: "TransmitterManager")
 
@@ -83,6 +194,25 @@ public class TransmitterManager: TransmitterDelegate {
         return dataIsFresh
     }
 
+    public var glucoseDisplay: GlucoseDisplayable? {
+        let transmitterDate = latestReading?.readDate ?? .distantPast
+        let shareDate = shareManager.latestBackfill?.startDate ?? .distantPast
+
+        if transmitterDate >= shareDate {
+            return latestReading
+        } else {
+            return shareManager.glucoseDisplay
+        }
+    }
+
+    public var managedDataInterval: TimeInterval? {
+        if transmitter.passiveModeEnabled {
+            return .hours(3)
+        }
+
+        return shareManager.managedDataInterval
+    }
+
     private(set) public var latestReading: Glucose? {
         get {
             return lockedLatestReading.value
@@ -102,18 +232,28 @@ public class TransmitterManager: TransmitterDelegate {
         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 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
+        }
+        
+        if let latestReading = latestReading {
+            log.default("Fetching new glucose from Share because last reading is %{public}.1f minutes old", latestReading.readDate.timeIntervalSinceNow.minutes)
+        } else {
+            log.default("Fetching new glucose from Share because we don't have a previous reading")
+        }
+
+        shareManager.fetchNewDataIfNeeded(completion)
+    }
+
+    public var device: HKDevice? {
+        return nil
+    }
 
     public var debugDescription: String {
         return [
@@ -122,27 +262,28 @@ public class TransmitterManager: TransmitterDelegate {
             "latestConnection: \(String(describing: latestConnection))",
             "dataIsFresh: \(dataIsFresh)",
             "providesBLEHeartbeat: \(providesBLEHeartbeat)",
+            shareManager.debugDescription,
             "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 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)
-//            }
-//        }
+        if let manager = self as? CGMManager {
+            shareManager.delegate.notify { (delegate) in
+                delegate?.cgmManagerDidUpdateState(manager)
+            }
+        }
     }
 
 
@@ -151,79 +292,86 @@ public class TransmitterManager: TransmitterDelegate {
     public func transmitterDidConnect(_ transmitter: Transmitter) {
         log.default("%{public}@", #function)
         latestConnection = Date()
-//        logDeviceCommunication("Connected", type: .connection)
+        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)
+        updateDelegate(with: .error(error))
+        logDeviceCommunication("Error: \(error)", type: .error)
     }
 
     public func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) {
         guard glucose != latestReading else {
-            delegate?.transmitterManager(self, didRead: [])
-//            updateDelegate(with: .noData)
+            updateDelegate(with: .noData)
             return
         }
 
         latestReading = glucose
 
-//        logDeviceCommunication("New reading: \(glucose.readDate)", type: .receive)
+        logDeviceCommunication("New reading: \(glucose.readDate)", type: .receive)
 
         guard glucose.state.hasReliableGlucose else {
             log.default("%{public}@: Unreliable glucose: %{public}@", #function, String(describing: glucose.state))
-            delegate?.transmitterManager(self, didFailWith: CalibrationError.unreliableState(glucose.state))
-//            updateDelegate(with: .error(CalibrationError.unreliableState(glucose.state)))
+            updateDelegate(with: .error(CalibrationError.unreliableState(glucose.state)))
             return
         }
         
-        guard glucose.glucose != nil else {
-            delegate?.transmitterManager(self, didRead: [])
-//            updateDelegate(with: .noData)
+        guard let quantity = glucose.glucose else {
+            updateDelegate(with: .noData)
             return
         }
 
         log.default("%{public}@: New glucose", #function)
 
-        delegate?.transmitterManager(self, didRead: [glucose])
-
-//        updateDelegate(with: .newData([
-//            NewGlucoseSample(
-//                date: glucose.readDate,
-//                quantity: quantity,
-//                trend: glucose.trendType,
-//                isDisplayOnly: glucose.isDisplayOnly,
-//                wasUserEntered: glucose.isDisplayOnly,
-//                syncIdentifier: glucose.syncIdentifier,
-//                device: device
-//            )
-//        ]))
+        updateDelegate(with: .newData([
+            NewGlucoseSample(
+                date: glucose.readDate,
+                quantity: quantity,
+                condition: glucose.condition,
+                trend: glucose.trendType,
+                trendRate: glucose.trendRate,
+                isDisplayOnly: glucose.isDisplayOnly,
+                wasUserEntered: glucose.isDisplayOnly,
+                syncIdentifier: glucose.syncIdentifier,
+                device: device
+            )
+        ]))
     }
 
     public func transmitter(_ transmitter: Transmitter, didReadBackfill glucose: [Glucose]) {
-        let samples = glucose.filter { glucose -> Bool in
-            guard glucose != latestReading, glucose.state.hasReliableGlucose, glucose.glucose != nil else {
-                return false
+        let samples = glucose.compactMap { (glucose) -> NewGlucoseSample? in
+            guard glucose != latestReading, glucose.state.hasReliableGlucose, let quantity = glucose.glucose else {
+                return nil
             }
-            return true
+
+            return NewGlucoseSample(
+                date: glucose.readDate,
+                quantity: quantity,
+                condition: glucose.condition,
+                trend: glucose.trendType,
+                trendRate: glucose.trendRate,
+                isDisplayOnly: glucose.isDisplayOnly,
+                wasUserEntered: glucose.isDisplayOnly,
+                syncIdentifier: glucose.syncIdentifier,
+                device: device
+            )
         }
-        delegate?.transmitterManager(self, didRead: samples)
-//
-//        guard samples.count > 0 else {
-//            return
-//        }
 
-//        updateDelegate(with: .newData(samples))
+        guard samples.count > 0 else {
+            return
+        }
+
+        updateDelegate(with: .newData(samples))
 
-//        logDeviceCommunication("New backfill: \(String(describing: samples.first?.date))", type: .receive)
+        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)
+        logDeviceCommunication("Unknown sensor data: \(data.hexadecimalString)", type: .error)
     }
 }
 
@@ -246,7 +394,7 @@ extension TransmitterManager {
 }
 
 
-public class G5CGMManager: TransmitterManager {
+public class G5CGMManager: TransmitterManager, CGMManager {
     public let managerIdentifier: String = "DexG5Transmitter"
 
     public let localizedTitle = LocalizedString("Dexcom G5", comment: "CGM display title")
@@ -257,7 +405,7 @@ public class G5CGMManager: TransmitterManager {
         return URL(string: "dexcomcgm://")
     }
 
-    public var device: HKDevice? {
+    public override var device: HKDevice? {
         return HKDevice(
             name: "CGMBLEKit",
             manufacturer: "Dexcom",
@@ -270,14 +418,14 @@ public class G5CGMManager: TransmitterManager {
         )
     }
     
-//    func logDeviceCommunication(_ message: String, type: DeviceLogEntryType = .send) {
-//        self.cgmManagerDelegate?.deviceManager(self, logEventForDeviceIdentifier: transmitter.ID, type: type, message: message, completion: nil)
-//    }
+    override 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 class G6CGMManager: TransmitterManager, CGMManager {
     public let managerIdentifier: String = "DexG6Transmitter"
 
     public let localizedTitle = LocalizedString("Dexcom G6", comment: "CGM display title")
@@ -288,7 +436,7 @@ public class G6CGMManager: TransmitterManager {
         return nil
     }
 
-    public var device: HKDevice? {
+    public override var device: HKDevice? {
         return HKDevice(
             name: "CGMBLEKit",
             manufacturer: "Dexcom",
@@ -301,9 +449,9 @@ public class G6CGMManager: TransmitterManager {
         )
     }
     
-//    func logDeviceCommunication(_ message: String, type: DeviceLogEntryType = .send) {
-//        self.cgmManagerDelegate?.deviceManager(self, logEventForDeviceIdentifier: transmitter.ID, type: type, message: message, completion: nil)
-//    }
+    override func logDeviceCommunication(_ message: String, type: DeviceLogEntryType = .send) {
+        self.cgmManagerDelegate?.deviceManager(self, logEventForDeviceIdentifier: transmitter.ID, type: type, message: message, completion: nil)
+    }
 }
 
 
@@ -347,3 +495,29 @@ extension CalibrationState {
     }
 }
 
+// MARK: - AlertResponder implementation
+extension G5CGMManager {
+    public func acknowledgeAlert(alertIdentifier: Alert.AlertIdentifier, completion: @escaping (Error?) -> Void) {
+        completion(nil)
+    }
+}
+
+// MARK: - AlertSoundVendor implementation
+extension G5CGMManager {
+    public func getSoundBaseURL() -> URL? { return nil }
+    public func getSounds() -> [Alert.Sound] { return [] }
+}
+
+// MARK: - AlertResponder implementation
+extension G6CGMManager {
+    public func acknowledgeAlert(alertIdentifier: Alert.AlertIdentifier, completion: @escaping (Error?) -> Void) {
+        completion(nil)
+    }
+}
+
+// MARK: - AlertSoundVendor implementation
+extension G6CGMManager {
+    public func getSoundBaseURL() -> URL? { return nil }
+    public func getSounds() -> [Alert.Sound] { return [] }
+}
+

+ 39 - 0
Dependencies/CGMBLEKit/CGMBLEKit/he.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";
+

+ 39 - 0
Dependencies/CGMBLEKit/CGMBLEKit/tr.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";
+

+ 4 - 0
Dependencies/CGMBLEKit/CGMBLEKitG5Plugin/CGMBLEKitG5Plugin-Bridging-Header.h

@@ -0,0 +1,4 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+

+ 25 - 0
Dependencies/CGMBLEKit/CGMBLEKitG5Plugin/CGMBLEKitG5Plugin.swift

@@ -0,0 +1,25 @@
+//
+//  CGMBLEKitG5Plugin.swift
+//  CGMBLEKitG5Plugin
+//
+//  Created by Nathaniel Hamming on 2019-12-19.
+//  Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import os.log
+import LoopKitUI
+import CGMBLEKit
+import CGMBLEKitUI
+
+class CGMBLEKitG5Plugin: NSObject, CGMManagerUIPlugin {
+    private let log = OSLog(category: "CGMBLEKitG5Plugin")
+    
+    public var cgmManagerType: CGMManagerUI.Type? {
+        return G5CGMManager.self
+    }
+    
+    override init() {
+        super.init()
+        log.default("Instantiated")
+    }
+}

+ 30 - 0
Dependencies/CGMBLEKit/CGMBLEKitG5Plugin/Info.plist

@@ -0,0 +1,30 @@
+<?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>$(DEVELOPMENT_LANGUAGE)</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>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2019 LoopKit Authors. All rights reserved.</string>
+	<key>NSPrincipalClass</key>
+	<string>CGMBLEKitG5Plugin</string>
+	<key>com.loopkit.Loop.CGMManagerDisplayName</key>
+	<string>Dexcom G5</string>
+	<key>com.loopkit.Loop.CGMManagerIdentifier</key>
+	<string>DexG5Transmitter</string>
+</dict>
+</plist>

+ 4 - 0
Dependencies/CGMBLEKit/CGMBLEKitG6Plugin/CGMBLEKitG6Plugin-Bridging-Header.h

@@ -0,0 +1,4 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+

+ 25 - 0
Dependencies/CGMBLEKit/CGMBLEKitG6Plugin/CGMBLEKitG6Plugin.swift

@@ -0,0 +1,25 @@
+//
+//  CGMBLEKitG6Plugin.swift
+//  CGMBLEKitG6Plugin
+//
+//  Created by Nathaniel Hamming on 2019-12-13.
+//  Copyright © 2019 LoopKit Authors. All rights reserved.
+//
+
+import os.log
+import LoopKitUI
+import CGMBLEKit
+import CGMBLEKitUI
+
+class CGMBLEKitG6Plugin: NSObject, CGMManagerUIPlugin {    
+    private let log = OSLog(category: "CGMBLEKitG6Plugin")
+    
+    public var cgmManagerType: CGMManagerUI.Type? {
+        return G6CGMManager.self
+    }
+    
+    override init() {
+        super.init()
+        log.default("Instantiated")
+    }
+}

+ 30 - 0
Dependencies/CGMBLEKit/CGMBLEKitG6Plugin/Info.plist

@@ -0,0 +1,30 @@
+<?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>$(DEVELOPMENT_LANGUAGE)</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>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2019 LoopKit Authors. All rights reserved.</string>
+	<key>NSPrincipalClass</key>
+	<string>CGMBLEKitG6Plugin</string>
+	<key>com.loopkit.Loop.CGMManagerDisplayName</key>
+	<string>Dexcom G6</string>
+	<key>com.loopkit.Loop.CGMManagerIdentifier</key>
+	<string>DexG6Transmitter</string>
+</dict>
+</plist>

+ 20 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/CalibrationDataRxMessageTests.swift

@@ -0,0 +1,20 @@
+//
+//  CalibrationDataRxMessageTests.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 9/18/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+
+class CalibrationDataRxMessageTests: XCTestCase {
+    
+    func testMessage() {
+        let data = Data(hexadecimalString: "33002b290090012900ae00800050e929001225")!
+        XCTAssertNotNil(CalibrationDataRxMessage(data: data))
+    }
+    
+}

+ 433 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/GlucoseBackfillMessageTests.swift

@@ -0,0 +1,433 @@
+//
+//  GlucoseBackfillMessageTests.swift
+//  xDripG5Tests
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+class GlucoseBackfillMessageTests: XCTestCase {
+
+    func testTxMessage() {
+        let message = GlucoseBackfillTxMessage(byte1: 5, byte2: 2, identifier: 0, startTime: 5439415, endTime: 5440614) // 20 minutes
+
+        XCTAssertEqual(Data(hexadecimalString: "50050200b7ff5200660453000000000000007138")!, message.data)
+    }
+
+    func testRxMessage() {
+        let message = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "51000100b7ff52006604530032000000e6cb9805")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: message.status))
+        XCTAssertEqual(1, message.backfillStatus)
+        XCTAssertEqual(0, message.identifier)
+        XCTAssertEqual(5439415, message.startTime)
+        XCTAssertEqual(5440614, message.endTime)
+        XCTAssertEqual(50, message.bufferLength)
+        XCTAssertEqual(0xcbe6, message.bufferCRC)
+
+        // 0xbc46
+        // 0b10111100 01000110
+        var buffer = GlucoseBackfillFrameBuffer(identifier: message.identifier)
+        buffer.append(Data(hexadecimalString: "0100bc460000b7ff52008b0006eee30053008500")!)
+        buffer.append(Data(hexadecimalString: "020006eb0f025300800006ee3a0353007e0006f5")!)
+        buffer.append(Data(hexadecimalString: "030066045300790006f8")!)
+
+        XCTAssertEqual(Int(message.bufferLength), buffer.count)
+        XCTAssertEqual(message.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+        
+        XCTAssertEqual(139, messages[0].glucose)
+        XCTAssertEqual(5439415, messages[0].timestamp)
+        XCTAssertEqual(.known(.ok), CalibrationState(rawValue: messages[0].state))
+        XCTAssertEqual(-18, messages[0].trend)
+
+        XCTAssertEqual(133, messages[1].glucose)
+        XCTAssertEqual(5439715, messages[1].timestamp)
+        XCTAssertEqual(.known(.ok), CalibrationState(rawValue: messages[1].state))
+        XCTAssertEqual(-21, messages[1].trend)
+
+        XCTAssertEqual(128, messages[2].glucose)
+        XCTAssertEqual(5440015, messages[2].timestamp)
+        XCTAssertEqual(.known(.ok), CalibrationState(rawValue: messages[2].state))
+        XCTAssertEqual(-18, messages[2].trend)
+
+        XCTAssertEqual(126, messages[3].glucose)
+        XCTAssertEqual(5440314, messages[3].timestamp)
+        XCTAssertEqual(.known(.ok), CalibrationState(rawValue: messages[3].state))
+        XCTAssertEqual(-11, messages[3].trend)
+
+        XCTAssertEqual(121, messages[4].glucose)
+        XCTAssertEqual(5440614, messages[4].timestamp)
+        XCTAssertEqual(.known(.ok), CalibrationState(rawValue: messages[4].state))
+        XCTAssertEqual(-08, messages[4].trend)
+
+        XCTAssertEqual(message.startTime, messages.first!.timestamp)
+        XCTAssertEqual(message.endTime, messages.last!.timestamp)
+
+        XCTAssertTrue(messages.first!.timestamp <= messages.last!.timestamp)
+    }
+
+    func testGlucoseBackfill2() {
+        let message = GlucoseBackfillTxMessage(byte1: 5, byte2: 2, identifier: 0, startTime: 4648682, endTime: 4650182) // 25 minutes
+
+        XCTAssertEqual(Data(hexadecimalString: "50050200eaee4600c6f446000000000000009f6d")!, message.data, message.data.hexadecimalString)
+
+        let response = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "51000103eaee4600c6f446003a0000004f3ac9e6")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: response.status))
+        XCTAssertEqual(1, response.backfillStatus)
+        XCTAssertEqual(3, response.identifier)
+        XCTAssertEqual(4648682, response.startTime)
+        XCTAssertEqual(4650182, response.endTime)
+        XCTAssertEqual(58, response.bufferLength)
+        XCTAssertEqual(0x3a4f, response.bufferCRC)
+
+        // 0x6e3c
+        // 0b01101110 00111100
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0xc0)
+        buffer.append(Data(hexadecimalString: "01c06e3c0000eaee4600920007fd16f046009500")!)
+        buffer.append(Data(hexadecimalString: "02c0070042f14600960007026ef2460099000704")!)
+        buffer.append(Data(hexadecimalString: "03c09af3460093000700c6f44600900007fc")!)
+
+        XCTAssertEqual(Int(response.bufferLength), buffer.count)
+        XCTAssertEqual(response.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+
+        XCTAssertEqual(response.startTime, messages.first!.timestamp)
+        XCTAssertEqual(response.endTime, messages.last!.timestamp)
+
+        XCTAssertTrue(messages.first!.timestamp <= messages.last!.timestamp)
+
+        XCTAssertEqual(6, messages.count)
+    }
+
+    func testMalformedBackfill() {
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0)
+        buffer.append(Data(hexadecimalString: "0100bc460000b7ff52008b0006eee30053008500")!)
+        buffer.append(Data(hexadecimalString: "020006eb0f025300800006ee3a0353007e0006")!)
+
+        XCTAssertEqual(3, buffer.glucose.count)
+    }
+
+    func testGlucoseBackfill3() {
+        let response = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "510001023d6a0e00c16d0e00280000005b1a9154")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: response.status))
+        XCTAssertEqual(1, response.backfillStatus)
+        XCTAssertEqual(2, response.identifier)
+        XCTAssertEqual(944701, response.startTime)
+        XCTAssertEqual(945601, response.endTime)
+        XCTAssertEqual(40, response.bufferLength)
+        XCTAssertEqual(0x1A5B, response.bufferCRC)
+
+        // 0x440c
+        // 0b01000100 00001100
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0x80)
+        buffer.append(Data(hexadecimalString: "0180440c00003d6a0e005c0007fe696b0e005d00")!)
+        buffer.append(Data(hexadecimalString: "028007ff956c0e005e000700c16d0e005d000700")!)
+
+        XCTAssertEqual(Int(response.bufferLength), buffer.count)
+        XCTAssertEqual(response.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+
+        XCTAssertEqual(response.startTime, messages.first!.timestamp)
+        XCTAssertEqual(response.endTime, messages.last!.timestamp)
+
+        XCTAssertTrue(messages.first!.timestamp <= messages.last!.timestamp)
+
+        XCTAssertEqual(4, messages.count)
+    }
+
+    func testGlucoseBackfill4() {
+        let response = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "51000103c9740e004d780e0028000000235bd94c")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: response.status))
+        XCTAssertEqual(1, response.backfillStatus)
+        XCTAssertEqual(3, response.identifier)
+        XCTAssertEqual(947401, response.startTime)
+        XCTAssertEqual(948301, response.endTime)
+        XCTAssertEqual(40, response.bufferLength)
+        XCTAssertEqual(0x5B23, response.bufferCRC)
+
+        // 0x04d0
+        // 0b00000100 11010000
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0xc0)
+        buffer.append(Data(hexadecimalString: "01c04d0c0000c9740e005a000700f5750e005800")!)
+        buffer.append(Data(hexadecimalString: "02c007ff21770e00590007ff4d780e0059000700")!)
+
+        XCTAssertEqual(Int(response.bufferLength), buffer.count)
+        XCTAssertEqual(response.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+
+        XCTAssertEqual(response.startTime, messages.first!.timestamp)
+        XCTAssertEqual(response.endTime, messages.last!.timestamp)
+
+        XCTAssertTrue(messages.first!.timestamp <= messages.last!.timestamp)
+
+        XCTAssertEqual(4, messages.count)
+    }
+
+    func testNotGlucoseBackfill1() {
+        let response = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "5100010339410e0085a90e00ac06000070ca9143")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: response.status))
+        XCTAssertEqual(1, response.backfillStatus)
+        XCTAssertEqual(3, response.identifier)
+        XCTAssertEqual(934201, response.startTime)
+        XCTAssertEqual(960901, response.endTime)
+        XCTAssertEqual(1708, response.bufferLength)
+        XCTAssertEqual(0xCA70, response.bufferCRC)
+
+        // 0x4a4f
+        // 0b01001010 01001111
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0xc0)
+        buffer.append(Data(hexadecimalString: "01c04a4f4a5558ef554453b7392a0df008571a7f")!)
+        buffer.append(Data(hexadecimalString: "02c0451e0d74bdec596b633cf2b03d511ef3d048")!)
+        buffer.append(Data(hexadecimalString: "03c009145e959ca51f7a1663ca31676b175d7bc7")!)
+        buffer.append(Data(hexadecimalString: "04c0de00c954fcd3281d5163ed873cdc136fca3e")!)
+        buffer.append(Data(hexadecimalString: "05c0c7da188dd5fbb8997206da1cc8d0c22f8434")!)
+        buffer.append(Data(hexadecimalString: "06c04d50b29df06b12e7162f2d73fd553e44e469")!)
+        buffer.append(Data(hexadecimalString: "07c02b4bb61d66cf6e949ee0f07dbe0cc12127ae")!)
+        buffer.append(Data(hexadecimalString: "08c03bf887be09ece7595adfee494b25368103b4")!)
+        buffer.append(Data(hexadecimalString: "09c07eefb9b5398468a53f00355341d19b50c8b1")!)
+        buffer.append(Data(hexadecimalString: "0ac028f0ddb4dc09a2c74deedf7fdff13fcd6b0e")!)
+        buffer.append(Data(hexadecimalString: "0bc0ad2d7311ac9ec1908fb7ee5557c463ea4fea")!)
+        buffer.append(Data(hexadecimalString: "0cc0bf3c62d9aa62d7c3d447c959b51d31fd016d")!)
+        buffer.append(Data(hexadecimalString: "0dc0278116abd1252ad66c894a39ed7c6d72086e")!)
+        buffer.append(Data(hexadecimalString: "0ec0aaee3bf9b05ccb7b23e1c27d777173c4d9fd")!)
+        buffer.append(Data(hexadecimalString: "0fc044048720d76a696249737f999f944995e44e")!)
+        buffer.append(Data(hexadecimalString: "10c0495e4cb7f22327a920a843de1b4522a68108")!)
+        buffer.append(Data(hexadecimalString: "11c058c482389192ed920e322b71900d747a9492")!)
+        buffer.append(Data(hexadecimalString: "12c0eac06906ff4863f0e8da07d1ead29fc15bd3")!)
+        buffer.append(Data(hexadecimalString: "13c0c0be38548fe9e229c64c9c0f3e9b4c4c1d83")!)
+        buffer.append(Data(hexadecimalString: "14c018a936bdde548e4244093e77c87adda0a1cf")!)
+        buffer.append(Data(hexadecimalString: "15c0fb97d1d147dd0bc6552faa4d62ab553e1682")!)
+        buffer.append(Data(hexadecimalString: "16c0f15f8cb77decb934bfe0c711a026dd4bf36b")!)
+        buffer.append(Data(hexadecimalString: "17c0bd268b0eee07ed20a0f3856ea449b1503708")!)
+        buffer.append(Data(hexadecimalString: "18c00872ed5a996a13480b81fc82b6ca1e7dd379")!)
+        buffer.append(Data(hexadecimalString: "19c06fb4c5bc84e63688b0a77edbab85bfb61b45")!)
+        buffer.append(Data(hexadecimalString: "1ac071d29d30edb43db6b8e114bbbcd67f9dd3a9")!)
+        buffer.append(Data(hexadecimalString: "1bc0569e17a8a80c015def11ddce1b8f194ff6e2")!)
+        buffer.append(Data(hexadecimalString: "1cc0df79ffbc1e077fe249b47550feb5dcd53044")!)
+        buffer.append(Data(hexadecimalString: "1dc0b557e2ba03caed61de30221b0330e1cc49b1")!)
+        buffer.append(Data(hexadecimalString: "1ec006f05e739d737939baf8b14a8b7a6faae96e")!)
+        buffer.append(Data(hexadecimalString: "1fc00b82d430e9e75fb8e7e2affbdd292a41fad2")!)
+        buffer.append(Data(hexadecimalString: "20c0fbf8e8f2686aaaf19d2809eecd3bd4f63516")!)
+        buffer.append(Data(hexadecimalString: "21c0a7df809e73538e459c1a9cd27a566f636e22")!)
+        buffer.append(Data(hexadecimalString: "22c0dbb3c23d7d7847dee77311287e6c6b192eb4")!)
+        buffer.append(Data(hexadecimalString: "23c0d30038d70241a80b9e390778a897dd1632cc")!)
+        buffer.append(Data(hexadecimalString: "24c0177b23127b464c07a499abeff05f13e40998")!)
+        buffer.append(Data(hexadecimalString: "25c0855350c7c4a335e95d2e569996639e8341b4")!)
+        buffer.append(Data(hexadecimalString: "26c0d42874475710a50764d4a4166c0e420aff7f")!)
+        buffer.append(Data(hexadecimalString: "27c0facb1d61cb8057de64546fc9f24f93603093")!)
+        buffer.append(Data(hexadecimalString: "28c080befb84f22c60d398f017dde114d0557b27")!)
+        buffer.append(Data(hexadecimalString: "29c07555e92425342c0674b62fa517b13ba0e3b0")!)
+        buffer.append(Data(hexadecimalString: "2ac0923624bce36c89fade1f66bd7ae1e8e7d598")!)
+        buffer.append(Data(hexadecimalString: "2bc0d345ceea668373d31f95b03a6ee7fff1a3b5")!)
+        buffer.append(Data(hexadecimalString: "2cc045e409b8d31dd53ae9d353f35738819fbb79")!)
+        buffer.append(Data(hexadecimalString: "2dc0a5d31fd3c3b7b217d3f79b245d3714b0523d")!)
+        buffer.append(Data(hexadecimalString: "2ec0eb576e0193584bff8ecada0dc54e4ebde86c")!)
+        buffer.append(Data(hexadecimalString: "2fc092b8ef52003f8b76e90d920ca738c998bb70")!)
+        buffer.append(Data(hexadecimalString: "30c07cfa0f7a69d14b79f605d254a164fd67c658")!)
+        buffer.append(Data(hexadecimalString: "31c049a329162e03f41c12db845b73301f5bbb81")!)
+        buffer.append(Data(hexadecimalString: "32c08a21ca0995b5aa413897ea9e2b7c563ced07")!)
+        buffer.append(Data(hexadecimalString: "33c05d51a18e19209f1c55054bd2f74677c71070")!)
+        buffer.append(Data(hexadecimalString: "34c0299e29ae5576a220b0b767fc4e898aaf2df1")!)
+        buffer.append(Data(hexadecimalString: "35c0bbb554546b69c53b4b3a63bd524bfbe728e6")!)
+        buffer.append(Data(hexadecimalString: "36c0cd4e8c6e10e72950e66bfa0d23b954a7aede")!)
+        buffer.append(Data(hexadecimalString: "37c0ea5df836af737298d44b4b156ced47727920")!)
+        buffer.append(Data(hexadecimalString: "38c02303edefc4916cfdba55829426c153d0d30c")!)
+        buffer.append(Data(hexadecimalString: "39c0dfee091fea60c2da239c9aabef8eddbe49b5")!)
+        buffer.append(Data(hexadecimalString: "3ac02788f23fb030e7606329ed24cbee10bc20eb")!)
+        buffer.append(Data(hexadecimalString: "3bc00a601d46c10bab8cdf04513a47550b0e4fe5")!)
+        buffer.append(Data(hexadecimalString: "3cc072ea5e514432c81e325464e1ac2d659378d2")!)
+        buffer.append(Data(hexadecimalString: "3dc0f050e994caa508fdea7202ed70a4acc6e8ab")!)
+        buffer.append(Data(hexadecimalString: "3ec069ab0d13863943415b492569db29b9594dbe")!)
+        buffer.append(Data(hexadecimalString: "3fc02c37277a98b88956f0def9ad866f44ca6d9f")!)
+        buffer.append(Data(hexadecimalString: "40c0e5bd6aa2dbd835fab2ec238de4a635a3f6cb")!)
+        buffer.append(Data(hexadecimalString: "41c0aafa8812d94d5fe722b3ecfb74eb4c12c622")!)
+        buffer.append(Data(hexadecimalString: "42c08c5b4bb2f28069fc6f9dcb26bc84c0cc01c7")!)
+        buffer.append(Data(hexadecimalString: "43c04ad95cefa1f62a18fa2c5a05bac208685cdb")!)
+        buffer.append(Data(hexadecimalString: "44c0ffe910ddc010b30f457578ab24a866b8a94d")!)
+        buffer.append(Data(hexadecimalString: "45c01b0bb36e58f401eb15da2e6710721e39c573")!)
+        buffer.append(Data(hexadecimalString: "46c06165075618fc9626c53acdd9cb8bcfb0719f")!)
+        buffer.append(Data(hexadecimalString: "47c081599f76725e30d4de39cdcc7f7c0c918d68")!)
+        buffer.append(Data(hexadecimalString: "48c0563b99dce4913105b793f4d539fe668feef6")!)
+        buffer.append(Data(hexadecimalString: "49c04ebaaf9f4dfda6cac4d617cd07098fec39f0")!)
+        buffer.append(Data(hexadecimalString: "4ac04c1ae961bc4f3e2cd395396dc8098bbf4bd5")!)
+        buffer.append(Data(hexadecimalString: "4bc0d95ed88f296e8d68c35085af86e5ef8d8bf0")!)
+        buffer.append(Data(hexadecimalString: "4cc0658ccce111259ce8ac5cbedfc46deda77433")!)
+        buffer.append(Data(hexadecimalString: "4dc05fda2f8d2885082db4b1356c5e2a0e830471")!)
+        buffer.append(Data(hexadecimalString: "4ec066c7813ff84a9da11fe343e5a95bbfa3082c")!)
+        buffer.append(Data(hexadecimalString: "4fc03bcfd6fe6d9657d04f06ed7bc461ebe18d47")!)
+        buffer.append(Data(hexadecimalString: "50c035bbe880ba24d7c84f73ae061b33d62a1845")!)
+        buffer.append(Data(hexadecimalString: "51c0650f0a6bbc91b2771549cf49a5a4faf8b278")!)
+        buffer.append(Data(hexadecimalString: "52c07ac551477e6cd10fe6a3b43d62b02569d110")!)
+        buffer.append(Data(hexadecimalString: "53c005f79d6de0ec017e7a0c98961ce6770f885d")!)
+        buffer.append(Data(hexadecimalString: "54c0d05fee0b5f5bf9de8c61b58f8634ecbf3347")!)
+        buffer.append(Data(hexadecimalString: "55c0e0c7d345fbc40f35aed12e82f8ccb0ed9335")!)
+        buffer.append(Data(hexadecimalString: "56c0b1c8b263179e")!)
+
+        XCTAssertEqual(Int(response.bufferLength), buffer.count)
+        XCTAssertEqual(response.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+
+        XCTAssertNotEqual(response.startTime, messages.first!.timestamp)
+        XCTAssertNotEqual(response.endTime, messages.last!.timestamp)
+
+        XCTAssertEqual(191, messages.count)
+    }
+
+    func testNotGlucoseBackfill2() {
+        let response = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "51000102b1aa0e00e5b20e00a000000020a39b7e")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: response.status))
+        XCTAssertEqual(1, response.backfillStatus)
+        XCTAssertEqual(2, response.identifier)
+        XCTAssertEqual(961201, response.startTime)
+        XCTAssertEqual(963301, response.endTime)
+        XCTAssertEqual(160, response.bufferLength)
+        XCTAssertEqual(0xA320, response.bufferCRC)
+
+        // 0xcde3
+        // 0b11001101 11100011
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0x80)
+        buffer.append(Data(hexadecimalString: "0180cde3fd48248e37a7bf6c2d9d78d4bfef6d5b")!)
+        buffer.append(Data(hexadecimalString: "02809f074c9039b6d3b841f422cf36398338f98c")!)
+        buffer.append(Data(hexadecimalString: "038004160a5a1ad37c382f3ca23ea215c644f7b6")!)
+        buffer.append(Data(hexadecimalString: "04802ed7376fa7c83c3ecf0b645233f9b3c80238")!)
+        buffer.append(Data(hexadecimalString: "05805692724e630a703f01b0a942250f725553d2")!)
+        buffer.append(Data(hexadecimalString: "06804ca2727a4051033a550da80905caf77c735d")!)
+        buffer.append(Data(hexadecimalString: "07808f937b4b9602c5dd6fa13ae983e00783b28e")!)
+        buffer.append(Data(hexadecimalString: "088069846e672c106b339159ead9ee1c08e1a159")!)
+
+        XCTAssertEqual(Int(response.bufferLength), buffer.count)
+        XCTAssertEqual(response.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+
+        XCTAssertNotEqual(response.startTime, messages.first!.timestamp)
+        XCTAssertNotEqual(response.endTime, messages.last!.timestamp)
+        XCTAssertFalse(messages.first!.timestamp <= messages.last!.timestamp)
+
+        XCTAssertEqual(17, messages.count)
+    }
+
+    func testNotGlucoseBackfill3() {
+        let response = GlucoseBackfillRxMessage(data: Data(hexadecimalString: "51000102b6a36500010c6600ac0600000147db0a")!)!
+
+        XCTAssertEqual(.ok, TransmitterStatus(rawValue: response.status))
+        XCTAssertEqual(1, response.backfillStatus)
+        XCTAssertEqual(2, response.identifier)
+        XCTAssertEqual(6661046, response.startTime)
+        XCTAssertEqual(6687745, response.endTime)
+        XCTAssertEqual(1708, response.bufferLength)
+        XCTAssertEqual(0x4701, response.bufferCRC)
+
+        var buffer = GlucoseBackfillFrameBuffer(identifier: 0x80)
+        buffer.append(Data(hexadecimalString: "0180e1234bdf92845cec52822a8894854582b2b2")!)
+        buffer.append(Data(hexadecimalString: "02800f8a38cc876ad33ae0acdc25921132cc6f0d")!)
+        buffer.append(Data(hexadecimalString: "038032a6cd9e6d447916dd0b9699e499ae79b8d1")!)
+        buffer.append(Data(hexadecimalString: "048045f4b95e0ad80955d3a899d6083bd142f863")!)
+        buffer.append(Data(hexadecimalString: "05809cf9c189744ab66f6ca5c2833ef27442fa71")!)
+        buffer.append(Data(hexadecimalString: "068053694b279275f0d23eb826681e20e5ebb79d")!)
+        buffer.append(Data(hexadecimalString: "078098b921155eb5aed63119d5faec3ef3e53a37")!)
+        buffer.append(Data(hexadecimalString: "08807c87277557a0828e8dc81ff76f1a6e197103")!)
+        buffer.append(Data(hexadecimalString: "0980b8378b133898ce73f7989d67360123e9fdd8")!)
+        buffer.append(Data(hexadecimalString: "0a80383ce19d943a38796b594ff95a2dc93bd6a2")!)
+        buffer.append(Data(hexadecimalString: "0b806b548c5997dc67ed4fe07bcf236d59dd7f94")!)
+        buffer.append(Data(hexadecimalString: "0c802cb2382f40a06fde5f2dff3f0b8226a11f12")!)
+        buffer.append(Data(hexadecimalString: "0d8029800ae513c5b7bc8ea733544b7da84ded17")!)
+        buffer.append(Data(hexadecimalString: "0e80a95b6c3d36183e4409f916a6f1f775af338e")!)
+        buffer.append(Data(hexadecimalString: "0f80d098732f2abcf4a90628f321a048349142ff")!)
+        buffer.append(Data(hexadecimalString: "108077294e9d029bdc0602c76671d88ff4a87596")!)
+        buffer.append(Data(hexadecimalString: "1180bac50f8d705f6732c34b935a0b06545d6d8f")!)
+        buffer.append(Data(hexadecimalString: "1280cf6b9eb0d2f0059c1a7b5c65acb83eb43836")!)
+        buffer.append(Data(hexadecimalString: "13802f408f68fc7e48858daecf64d01f3f61827e")!)
+        buffer.append(Data(hexadecimalString: "1480cd5975c1062ed45311a2602c0bbc9c78cf21")!)
+        buffer.append(Data(hexadecimalString: "1580b6e27f3350bc7d4eb908313710931cbd4f23")!)
+        buffer.append(Data(hexadecimalString: "168061f70e5e27e8b72faecfbb58b6b6ff65cbf0")!)
+        buffer.append(Data(hexadecimalString: "178066bdd3a0b1e1ed0af8b2af88dcb1f4b1c3a4")!)
+        buffer.append(Data(hexadecimalString: "18801eb9326019bca25b74804d196c04d079e495")!)
+        buffer.append(Data(hexadecimalString: "1980a29097393f81aaef79ef421af54ccd3c35ed")!)
+        buffer.append(Data(hexadecimalString: "1a80a3039b0372ddd79ef65293e4e99484573ab3")!)
+        buffer.append(Data(hexadecimalString: "1b807e755140ea79b1913a7c491e606b7d1e4542")!)
+        buffer.append(Data(hexadecimalString: "1c800c968daf03958bd8784e1cf8cea4fa903a80")!)
+        buffer.append(Data(hexadecimalString: "1d8044c5c7baebadbf8e6877d725ab84484e6755")!)
+        buffer.append(Data(hexadecimalString: "1e8036be160e8a03d2c07552fc513c8869170528")!)
+        buffer.append(Data(hexadecimalString: "1f8038483ab634e7707e9ab8c8e3f87dd67f423f")!)
+        buffer.append(Data(hexadecimalString: "2080f184e4457558d9b7944f21d6421b717ddfb1")!)
+        buffer.append(Data(hexadecimalString: "2180bb4da6197852102a3a04b8acccea3c54f0f9")!)
+        buffer.append(Data(hexadecimalString: "2280da93975f3ea1c39d2aff5dbbc4b183b66044")!)
+        buffer.append(Data(hexadecimalString: "23804678951cdc83923fe5a88bda66221a48360b")!)
+        buffer.append(Data(hexadecimalString: "2480aa9dc3fee16106bd551754d896da72ff772c")!)
+        buffer.append(Data(hexadecimalString: "2580b825bb4eba580b57caadda1b90b449a8f2c5")!)
+        buffer.append(Data(hexadecimalString: "2680117b62c286b395d2bf016848c65953595f19")!)
+        buffer.append(Data(hexadecimalString: "27806d524b2b191bd9582f47fd3956ab851207af")!)
+        buffer.append(Data(hexadecimalString: "2880c7df85c2ee5e9b3f5ae68ffba44a86e237e8")!)
+        buffer.append(Data(hexadecimalString: "2980947fec3646851a510c8a61c0b3b7d90e410b")!)
+        buffer.append(Data(hexadecimalString: "2a8014b04b3ff32e4d9d16f46880533cf4562af4")!)
+        buffer.append(Data(hexadecimalString: "2b80c754e48edfa84f2f3b29976ce59cc110747d")!)
+        buffer.append(Data(hexadecimalString: "2c8095a3ab4b66254954a51ca5e5c92d07be80fc")!)
+        buffer.append(Data(hexadecimalString: "2d80bc4afa73d7f222f1b9e56083171057e32ca3")!)
+        buffer.append(Data(hexadecimalString: "2e80c88dbe9a052d7ffd29d2f665bdd66811712f")!)
+        buffer.append(Data(hexadecimalString: "2f804d2f9ee36fd6f3f48c30429c1629e39bbe3f")!)
+        buffer.append(Data(hexadecimalString: "30808b01f598fc6420d85b3190d15f8d55f43faf")!)
+        buffer.append(Data(hexadecimalString: "31801c171908c8ded10e81123f453c571c8f5199")!)
+        buffer.append(Data(hexadecimalString: "32806275a5652f2447f63f1ab5d0dac84387d80c")!)
+        buffer.append(Data(hexadecimalString: "3380f095361816ab06f0209a6ec3411c8f0c6ce1")!)
+        buffer.append(Data(hexadecimalString: "3480a99ac0dae0c87f6a1d4ee4fe4e19671c29ba")!)
+        buffer.append(Data(hexadecimalString: "3580811db50e1625a3b88305ea5c34b53e20700e")!)
+        buffer.append(Data(hexadecimalString: "36800fbf211b6a454c788aa17b0cf14db76695a9")!)
+        buffer.append(Data(hexadecimalString: "3780dfc186d1c189114f182709efc464f48c6b2f")!)
+        buffer.append(Data(hexadecimalString: "38805e629e8e6457b1ec149897210cb6336b123f")!)
+        buffer.append(Data(hexadecimalString: "398045d4dc9f4c074ec0e926a8d1768ae92b4866")!)
+        buffer.append(Data(hexadecimalString: "3a801edf0d5d1c1a86c90c5eeef69e115fdd513a")!)
+        buffer.append(Data(hexadecimalString: "3b8084223228b158081b465c74454450ec19a4c1")!)
+        buffer.append(Data(hexadecimalString: "3c80fa306d71fc211bd9b9e55aeb16c582d21ec2")!)
+        buffer.append(Data(hexadecimalString: "3d8072d8bbec74f1436958db431a92fc66cf5dd2")!)
+        buffer.append(Data(hexadecimalString: "3e80888ef69a91f8dbb0ce70b6e5ec9289245878")!)
+        buffer.append(Data(hexadecimalString: "3f8069c0d6d14e580be92f87a3255e124b25b451")!)
+        buffer.append(Data(hexadecimalString: "4080b3cbae3d50ea52720bf5029243a4a9fea906")!)
+        buffer.append(Data(hexadecimalString: "4180384321d07a4b5378aa272c9a7247830624b8")!)
+        buffer.append(Data(hexadecimalString: "4280acf0b265dd82b68aeec5114161a34135b30e")!)
+        buffer.append(Data(hexadecimalString: "43802d709c604266db64a4b5a5e6f6d8cfd7ece1")!)
+        buffer.append(Data(hexadecimalString: "44807b48711b0630cd919dbf9ea7bf81efa1e8f1")!)
+        buffer.append(Data(hexadecimalString: "4580c0282b679f9746ece875482d5e9a5ed59cb8")!)
+        buffer.append(Data(hexadecimalString: "46808c7b718de4299f081449cce9aa9afadfcea9")!)
+        buffer.append(Data(hexadecimalString: "478066cd4c36d6e816413b15955c958da4d8e866")!)
+        buffer.append(Data(hexadecimalString: "48809b5170078157c542236bc7a09c96bc559069")!)
+        buffer.append(Data(hexadecimalString: "49800be65a0bce639c69cd3d64db0fa22570756f")!)
+        buffer.append(Data(hexadecimalString: "4a80e5ebd5381b077a8ac56e952b631256a076cc")!)
+        buffer.append(Data(hexadecimalString: "4b80fb32d28e39021d49dc7b7ee65272ca1f28c1")!)
+        buffer.append(Data(hexadecimalString: "4c8004486cc3dcad9f39c602d3ed9030e327cec3")!)
+        buffer.append(Data(hexadecimalString: "4d809a5800c6d647c5f99e40a15327957745dce1")!)
+        buffer.append(Data(hexadecimalString: "4e80d03a0b5368fda78b28d3975500ab160ac693")!)
+        buffer.append(Data(hexadecimalString: "4f80dbc5ea65f540933f858a425ecdb378f62990")!)
+        buffer.append(Data(hexadecimalString: "50802e7980ce9365ad4e434308fb2a8102dc9f6a")!)
+        buffer.append(Data(hexadecimalString: "5180b71311e183ad9feecfd43b68072d5a9ad4af")!)
+        buffer.append(Data(hexadecimalString: "5280e721c37d2b57f95cbf5f51025fb22b6ca60c")!)
+        buffer.append(Data(hexadecimalString: "53805749eb01f070a5b015dcd0f68f5fea0b40c6")!)
+        buffer.append(Data(hexadecimalString: "5480fae4ee747357e4d73265ad9411c565c41865")!)
+        buffer.append(Data(hexadecimalString: "5580b75e9c62c7c2aa3ea3f94d219ef7330077d7")!)
+        buffer.append(Data(hexadecimalString: "5680f2c59ee6b54a")!)
+
+        XCTAssertEqual(Int(response.bufferLength), buffer.count)
+        XCTAssertEqual(response.bufferCRC, buffer.crc16)
+
+        let messages = buffer.glucose
+
+        XCTAssertNotEqual(response.startTime, messages.first!.timestamp)
+        XCTAssertNotEqual(response.endTime, messages.last!.timestamp)
+        XCTAssertFalse(messages.first!.timestamp <= messages.last!.timestamp)
+
+        XCTAssertEqual(191, messages.count)
+    }
+}

+ 79 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/GlucoseRxMessageTests.swift

@@ -0,0 +1,79 @@
+//
+//  GlucoseRxMessageTests.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 3/5/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+
+class GlucoseRxMessageTests: XCTestCase {
+
+    func testMessageData() {
+        let data = Data(hexadecimalString: "3100680a00008a715700cc0006ffc42a")!
+        let message = GlucoseRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(2664, message.sequence)
+        XCTAssertEqual(5730698, message.glucose.timestamp)
+        XCTAssertFalse(message.glucose.glucoseIsDisplayOnly)
+        XCTAssertEqual(204, message.glucose.glucose)
+        XCTAssertEqual(6, message.glucose.state)
+        XCTAssertEqual(-1, message.glucose.trend)
+    }
+
+    func testNegativeTrend() {
+        let data = Data(hexadecimalString: "31006f0a0000be7957007a0006e4818d")!
+        let message = GlucoseRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(2671, message.sequence)
+        XCTAssertEqual(5732798, message.glucose.timestamp)
+        XCTAssertFalse(message.glucose.glucoseIsDisplayOnly)
+        XCTAssertEqual(122, message.glucose.glucose)
+        XCTAssertEqual(6, message.glucose.state)
+        XCTAssertEqual(-28, message.glucose.trend)
+    }
+
+    func testDisplayOnly() {
+        let data = Data(hexadecimalString: "3100700a0000f17a5700584006e3cee9")!
+        let message = GlucoseRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(2672, message.sequence)
+        XCTAssertEqual(5733105, message.glucose.timestamp)
+        XCTAssertTrue(message.glucose.glucoseIsDisplayOnly)
+        XCTAssertEqual(88, message.glucose.glucose)
+        XCTAssertEqual(6, message.glucose.state)
+        XCTAssertEqual(-29, message.glucose.trend)
+    }
+
+    func testOldTransmitter() {
+        let data = Data(hexadecimalString: "3100aa00000095a078008b00060a8b34")!
+        let message = GlucoseRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(170, message.sequence)
+        XCTAssertEqual(7905429, message.glucose.timestamp)  // 90 days, status is still OK
+        XCTAssertFalse(message.glucose.glucoseIsDisplayOnly)
+        XCTAssertEqual(139, message.glucose.glucose)
+        XCTAssertEqual(6, message.glucose.state)
+        XCTAssertEqual(10, message.glucose.trend)
+    }
+
+    func testZeroSequence() {
+        let data = Data(hexadecimalString: "3100000000008eb14d00820006f6a038")!
+        let message = GlucoseRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(0, message.sequence)
+        XCTAssertEqual(5091726, message.glucose.timestamp)
+        XCTAssertFalse(message.glucose.glucoseIsDisplayOnly)
+        XCTAssertEqual(130, message.glucose.glucose)
+        XCTAssertEqual(6, message.glucose.state)
+        XCTAssertEqual(-10, message.glucose.trend)
+    }
+}

+ 83 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/GlucoseTests.swift

@@ -0,0 +1,83 @@
+//
+//  GlucoseTests.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 8/6/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+import HealthKit
+@testable import CGMBLEKit
+
+class GlucoseTests: XCTestCase {
+
+    var timeMessage: TransmitterTimeRxMessage!
+    var calendar = Calendar(identifier: .gregorian)
+    var activationDate: Date!
+
+    override func setUp() {
+        super.setUp()
+
+        let data = Data(hexadecimalString: "2500470272007cff710001000000fa1d")!
+        timeMessage = TransmitterTimeRxMessage(data: data)!
+
+        calendar.timeZone = TimeZone(identifier: "UTC")!
+
+        activationDate = calendar.date(from: DateComponents(year: 2016, month: 10, day: 1))!
+    }
+
+    func testMessageData() {
+        let data = Data(hexadecimalString: "3100680a00008a715700cc0006ffc42a")!
+        let message = GlucoseRxMessage(data: data)!
+        let glucose = Glucose(transmitterID: "123456", glucoseMessage: message, timeMessage: timeMessage, activationDate: activationDate)
+
+        XCTAssertEqual(TransmitterStatus.ok, glucose.status)
+        XCTAssertEqual(calendar.date(from: DateComponents(year: 2016, month: 12, day: 6, hour: 7, minute: 51, second: 38))!, glucose.readDate)
+        XCTAssertEqual(calendar.date(from: DateComponents(year: 2016, month: 12, day: 26, hour: 11, minute: 16, second: 12))!, glucose.sessionStartDate)
+        XCTAssertFalse(glucose.isDisplayOnly)
+        XCTAssertEqual(204, glucose.glucose?.doubleValue(for: .milligramsPerDeciliter))
+        XCTAssertEqual(.known(.ok), glucose.state)
+        XCTAssertEqual(-1, glucose.trend)
+    }
+
+    func testNegativeTrend() {
+        let data = Data(hexadecimalString: "31006f0a0000be7957007a0006e4818d")!
+        let message = GlucoseRxMessage(data: data)!
+        let glucose = Glucose(transmitterID: "123456", glucoseMessage: message, timeMessage: timeMessage, activationDate: activationDate)
+
+        XCTAssertEqual(TransmitterStatus.ok, glucose.status)
+        XCTAssertEqual(calendar.date(from: DateComponents(year: 2016, month: 12, day: 6, hour: 8, minute: 26, second: 38))!, glucose.readDate)
+        XCTAssertFalse(glucose.isDisplayOnly)
+        XCTAssertEqual(122, glucose.glucose?.doubleValue(for: .milligramsPerDeciliter))
+        XCTAssertEqual(.known(.ok), glucose.state)
+        XCTAssertEqual(-28, glucose.trend)
+    }
+
+    func testDisplayOnly() {
+        let data = Data(hexadecimalString: "3100700a0000f17a5700584006e3cee9")!
+        let message = GlucoseRxMessage(data: data)!
+        let glucose = Glucose(transmitterID: "123456", glucoseMessage: message, timeMessage: timeMessage, activationDate: activationDate)
+
+        XCTAssertEqual(TransmitterStatus.ok, glucose.status)
+        XCTAssertEqual(calendar.date(from: DateComponents(year: 2016, month: 12, day: 6, hour: 8, minute: 31, second: 45))!, glucose.readDate)
+        XCTAssertTrue(glucose.isDisplayOnly)
+        XCTAssertEqual(88, glucose.glucose?.doubleValue(for: .milligramsPerDeciliter))
+        XCTAssertEqual(.known(.ok), glucose.state)
+        XCTAssertEqual(-29, message.glucose.trend)
+    }
+
+    func testOldTransmitter() {
+        let data = Data(hexadecimalString: "3100aa00000095a078008b00060a8b34")!
+        let message = GlucoseRxMessage(data: data)!
+        let glucose = Glucose(transmitterID: "123456", glucoseMessage: message, timeMessage: timeMessage, activationDate: activationDate)
+
+        XCTAssertEqual(TransmitterStatus.ok, glucose.status)
+        XCTAssertEqual(calendar.date(from: DateComponents(year: 2016, month: 12, day: 31, hour: 11, minute: 57, second: 09))!, glucose.readDate)  // 90 days, status is still OK
+        XCTAssertFalse(glucose.isDisplayOnly)
+        XCTAssertEqual(139, glucose.glucose?.doubleValue(for: .milligramsPerDeciliter))
+        XCTAssertEqual(.known(.ok), glucose.state)
+        XCTAssertEqual(10, message.glucose.trend)
+    }
+    
+}

+ 24 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/Info.plist

@@ -0,0 +1,24 @@
+<?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>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>3.2</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+</dict>
+</plist>

+ 44 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/SessionStartRxMessageTests.swift

@@ -0,0 +1,44 @@
+//
+//  SessionStartRxMessageTests.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 6/4/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+/// Thanks to https://github.com/mthatcher for the fixtures!
+class SessionStartRxMessageTests: XCTestCase {
+
+    func testSuccessfulStart() {
+        var data = Data(hexadecimalString: "2700014bf871004bf87100e9f8710095d9")!
+        var message = SessionStartRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(1, message.received)
+        XCTAssertEqual(7469131, message.requestedStartTime)
+        XCTAssertEqual(7469131, message.sessionStartTime)
+        XCTAssertEqual(7469289, message.transmitterTime)
+
+        data = Data(hexadecimalString: "2700012bfd71002bfd710096fd71000f6a")!
+        message = SessionStartRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(1, message.received)
+        XCTAssertEqual(7470379, message.requestedStartTime)
+        XCTAssertEqual(7470379, message.sessionStartTime)
+        XCTAssertEqual(7470486, message.transmitterTime)
+
+        data = Data(hexadecimalString: "2700017cff71007cff7100eeff7100aeed")!
+        message = SessionStartRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(1, message.received)
+        XCTAssertEqual(7470972, message.requestedStartTime)
+        XCTAssertEqual(7470972, message.sessionStartTime)
+        XCTAssertEqual(7471086, message.transmitterTime)
+    }
+
+}

+ 44 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/SessionStopRxMessageTests.swift

@@ -0,0 +1,44 @@
+//
+//  SessionStopRxMessageTests.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 6/4/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+/// Thanks to https://github.com/mthatcher for the fixtures!
+class SessionStopRxMessageTests: XCTestCase {
+    
+    func testSuccessfulStop() {
+        var data = Data(hexadecimalString: "29000128027200ffffffff47027200ba85")!
+        var message = SessionStopRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(1, message.received)
+        XCTAssertEqual(7471656, message.sessionStopTime)
+        XCTAssertEqual(0xffffffff, message.sessionStartTime)
+        XCTAssertEqual(7471687, message.transmitterTime)
+
+        data = Data(hexadecimalString: "2900013ffe7100ffffffffc2fe71008268")!
+        message = SessionStopRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(1, message.received)
+        XCTAssertEqual(7470655, message.sessionStopTime)
+        XCTAssertEqual(0xffffffff, message.sessionStartTime)
+        XCTAssertEqual(7470786, message.transmitterTime)
+
+        data = Data(hexadecimalString: "290001f5fb7100ffffffff6afc7100fa8a")!
+        message = SessionStopRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(1, message.received)
+        XCTAssertEqual(7470069, message.sessionStopTime)
+        XCTAssertEqual(0xffffffff, message.sessionStartTime)
+        XCTAssertEqual(7470186, message.transmitterTime)
+    }
+    
+}

+ 20 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/TransmitterIDTests.swift

@@ -0,0 +1,20 @@
+//
+//  TransmitterIDTests.swift
+//  xDripG5Tests
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+class TransmitterIDTests: XCTestCase {
+
+    /// Sanity check the hash computation path
+    func testComputeHash() {
+        let id = TransmitterID(id: "123456")
+
+        XCTAssertEqual("e60d4a7999b0fbb2", id.computeHash(of: Data(hexadecimalString: "0123456789abcdef")!)!.hexadecimalString)
+    }
+    
+}

+ 53 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/TransmitterTimeRxMessageTests.swift

@@ -0,0 +1,53 @@
+//
+//  TransmitterTimeRxMessageTests.swift
+//  xDripG5
+//
+//  Created by Nathan Racklyeft on 6/4/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+/// Thanks to https://github.com/mthatcher for the fixtures!
+class TransmitterTimeRxMessageTests: XCTestCase {
+
+    func testNoSession() {
+        var data = Data(hexadecimalString: "2500e8f87100ffffffff010000000a70")!
+        var message = TransmitterTimeRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(7469288, message.currentTime)
+        XCTAssertEqual(0xffffffff, message.sessionStartTime)
+
+        data = Data(hexadecimalString: "250096fd7100ffffffff01000000226d")!
+        message = TransmitterTimeRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(7470486, message.currentTime)
+        XCTAssertEqual(0xffffffff, message.sessionStartTime)
+
+        data = Data(hexadecimalString: "2500eeff7100ffffffff010000008952")!
+        message = TransmitterTimeRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(7471086, message.currentTime)
+        XCTAssertEqual(0xffffffff, message.sessionStartTime)
+    }
+
+    func testInSession() {
+        var data = Data(hexadecimalString: "2500470272007cff710001000000fa1d")!
+        var message = TransmitterTimeRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(7471687, message.currentTime)
+        XCTAssertEqual(7470972, message.sessionStartTime)
+
+        data = Data(hexadecimalString: "2500beb24d00f22d4d000100000083c0")!
+        message = TransmitterTimeRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual(5092030, message.currentTime)
+        XCTAssertEqual(5058034, message.sessionStartTime)
+    }
+}

+ 22 - 0
Dependencies/CGMBLEKit/CGMBLEKitTests/TransmitterVersionRxMessageTests.swift

@@ -0,0 +1,22 @@
+//
+//  TransmitterVersionRxMessageTests.swift
+//  xDripG5
+//
+//  Created by Nate Racklyeft on 9/29/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import XCTest
+@testable import CGMBLEKit
+
+class TransmitterVersionRxMessageTests: XCTestCase {
+    
+    func testRxMessage() {
+        let data = Data(hexadecimalString: "4b0001000011df2900005100037000f00009b6")!
+        let message = TransmitterVersionRxMessage(data: data)!
+
+        XCTAssertEqual(0, message.status)
+        XCTAssertEqual([1, 0, 0, 17], message.firmwareVersion)
+    }
+
+}

+ 6 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 12 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/Assets.xcassets/g6.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "g6.png",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

TEMPAT SAMPAH
Dependencies/CGMBLEKit/CGMBLEKitUI/Assets.xcassets/g6.imageset/g6.png


+ 51 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/Base.lproj/Localizable.strings

@@ -0,0 +1,51 @@
+/* Format string for glucose trend per minute. (1: glucose value and unit) */
+"%@/min" = "%@/min";
+
+/* Confirmation message for deleting a CGM */
+"Are you sure you want to delete this CGM?" = "Are you sure you want to delete this CGM?";
+
+/* The title of the cancel action in an action sheet */
+"Cancel" = "Cancel";
+
+/* Title describing glucose date */
+"Date" = "Date";
+
+/* Button title to delete CGM
+Title text for the button to remove a CGM from Loop */
+"Delete CGM" = "Delete CGM";
+
+/* Title describing glucose value */
+"Glucose" = "Glucose";
+
+/* Describes a glucose value adjusted to reflect a recent calibration */
+"Glucose (Adjusted)" = "Glucose (Adjusted)";
+
+/* Section title for latest glucose calibration */
+"Latest Calibration" = "Latest Calibration";
+
+/* Section title for latest glucose reading */
+"Latest Reading" = "Latest Reading";
+
+/* Button title to open CGM app */
+"Open App" = "Open App";
+
+/* Title describing sensor session age */
+"Session Age" = "Session Age";
+
+/* Title describing sensor expiration */
+"Sensor Expires" = "Sensor Expires";
+
+/* Title describing past sensor expiration */
+"Sensor Expired" = "Sensor Expired";
+
+/* Title describing CGM calibration and battery state */
+"Status" = "Status";
+
+/* Title describing transmitter session age */
+"Transmitter Age" = "Transmitter Age";
+
+/* The title text for the Dexcom G5/G6 transmitter ID config value */
+"Transmitter ID" = "Transmitter ID";
+
+/* Title describing glucose trend */
+"Trend" = "Trend";

+ 114 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/Base.lproj/TransmitterManagerSetup.storyboard

@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7N1-NK-BqO">
+    <device id="retina4_7" orientation="portrait" appearance="dark"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Transmitter Setup-->
+        <scene sceneID="teK-Jz-HaX">
+            <objects>
+                <tableViewController title="Transmitter Setup" id="Dds-49-o7G" customClass="TransmitterIDSetupViewController" customModule="CGMBLEKitUI" customModuleProvider="target" sceneMemberID="viewController">
+                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="FOH-yM-6UM">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="623"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                        <sections>
+                            <tableViewSection headerTitle="Transmitter ID" id="Qub-6B-0aB">
+                                <string key="footerTitle">The transmitter ID can be found printed on the back of the device, on the side of the box it came in, and from within the settings menus of the receiver and mobile app.</string>
+                                <cells>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="af6-El-QDO">
+                                        <rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="af6-El-QDO" id="16F-lP-knO">
+                                            <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Enter the 6-digit transmitter ID" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="nKX-TW-GhD" customClass="PaddedTextField" customModule="LoopKitUI">
+                                                    <rect key="frame" x="16" y="0.0" width="343" height="44"/>
+                                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
+                                                    <textInputTraits key="textInputTraits" autocapitalizationType="allCharacters" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" returnKeyType="done" smartDashesType="no" smartQuotesType="no"/>
+                                                    <userDefinedRuntimeAttributes>
+                                                        <userDefinedRuntimeAttribute type="size" keyPath="textInset">
+                                                            <size key="value" width="0.0" height="12"/>
+                                                        </userDefinedRuntimeAttribute>
+                                                    </userDefinedRuntimeAttributes>
+                                                    <connections>
+                                                        <outlet property="delegate" destination="Dds-49-o7G" id="C8g-MC-3F3"/>
+                                                    </connections>
+                                                </textField>
+                                            </subviews>
+                                            <constraints>
+                                                <constraint firstItem="nKX-TW-GhD" firstAttribute="trailing" secondItem="16F-lP-knO" secondAttribute="trailingMargin" id="3PT-cp-nfZ"/>
+                                                <constraint firstAttribute="bottom" secondItem="nKX-TW-GhD" secondAttribute="bottom" id="B7C-R4-5fg"/>
+                                                <constraint firstItem="nKX-TW-GhD" firstAttribute="leading" secondItem="16F-lP-knO" secondAttribute="leadingMargin" id="GjG-gf-0yR"/>
+                                                <constraint firstItem="nKX-TW-GhD" firstAttribute="top" secondItem="16F-lP-knO" secondAttribute="top" id="QwV-R0-aUq"/>
+                                            </constraints>
+                                        </tableViewCellContentView>
+                                    </tableViewCell>
+                                </cells>
+                            </tableViewSection>
+                            <tableViewSection headerTitle="Dexcom Share" footerTitle="Data can be downloaded over the Internet from Share when the transmitter connection fails." id="k1N-Rg-XDy">
+                                <cells>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="5oU-vK-JHQ" detailTextLabel="GOT-KQ-cEh" style="IBUITableViewCellStyleValue1" id="VKg-iR-eRk" customClass="SettingsTableViewCell" customModule="LoopKitUI">
+                                        <rect key="frame" x="0.0" y="223" width="375" height="44"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VKg-iR-eRk" id="GZu-iC-2kt">
+                                            <rect key="frame" x="0.0" y="0.0" width="347.5" height="44"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Credentials" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="5oU-vK-JHQ">
+                                                    <rect key="frame" x="16" y="12" width="87" height="20.5"/>
+                                                    <autoresizingMask key="autoresizingMask"/>
+                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                                    <nil key="textColor"/>
+                                                    <nil key="highlightedColor"/>
+                                                </label>
+                                                <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="GOT-KQ-cEh">
+                                                    <rect key="frame" x="295.5" y="12" width="44" height="20.5"/>
+                                                    <autoresizingMask key="autoresizingMask"/>
+                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                                    <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                    <nil key="highlightedColor"/>
+                                                </label>
+                                            </subviews>
+                                        </tableViewCellContentView>
+                                    </tableViewCell>
+                                </cells>
+                            </tableViewSection>
+                        </sections>
+                        <connections>
+                            <outlet property="dataSource" destination="Dds-49-o7G" id="PGK-sX-2c8"/>
+                            <outlet property="delegate" destination="Dds-49-o7G" id="Xp2-eI-dvm"/>
+                        </connections>
+                    </tableView>
+                    <connections>
+                        <outlet property="shareUsernameLabel" destination="GOT-KQ-cEh" id="j8W-Tr-9lH"/>
+                        <outlet property="transmitterIDTextField" destination="nKX-TW-GhD" id="ZjP-P7-x3Y"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="3nA-P9-fpH" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="12" y="87"/>
+        </scene>
+        <!--Transmitter Setup View Controller-->
+        <scene sceneID="MSW-kN-lDN">
+            <objects>
+                <navigationController id="7N1-NK-BqO" customClass="TransmitterSetupViewController" customModule="CGMBLEKitUI" customModuleProvider="target" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translucent="NO" largeTitles="YES" id="oCu-Q8-T22">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="Dds-49-o7G" kind="relationship" relationship="rootViewController" id="Bah-Mf-Thd"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="HBr-Z5-zYq" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1137" y="87"/>
+        </scene>
+    </scenes>
+</document>

+ 19 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/CGMBLEKitUI.h

@@ -0,0 +1,19 @@
+//
+//  CGMBLEKitUI.h
+//  CGMBLEKitUI
+//
+//  Created by Nathan Racklyeft on 7/28/18.
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for CGMBLEKitUI.
+FOUNDATION_EXPORT double CGMBLEKitUIVersionNumber;
+
+//! Project version string for CGMBLEKitUI.
+FOUNDATION_EXPORT const unsigned char CGMBLEKitUIVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <CGMBLEKitUI/PublicHeader.h>
+
+

+ 24 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/IdentifiableClass.swift

@@ -0,0 +1,24 @@
+//
+//  IdentifiableClass.swift
+//  Naterade
+//
+//  Created by Nathan Racklyeft on 5/22/16.
+//  Copyright © 2016 Nathan Racklyeft. All rights reserved.
+//
+
+import Foundation
+
+
+protocol IdentifiableClass: AnyObject {
+    static var className: String { get }
+}
+
+
+extension IdentifiableClass {
+    static var className: String {
+        return NSStringFromClass(self).components(separatedBy: ".").last!
+    }
+}
+
+
+extension UITableViewCell: IdentifiableClass { }

+ 2 - 2
Dependencies/rileylink_ios/Crypto/Info.plist

@@ -3,7 +3,7 @@
 <plist version="1.0">
 <dict>
 	<key>CFBundleDevelopmentRegion</key>
-	<string>en</string>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
 	<key>CFBundleIdentifier</key>
@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>3.0</string>
+	<string>3.2</string>
 	<key>CFBundleVersion</key>
 	<string>$(CURRENT_PROJECT_VERSION)</string>
 	<key>NSPrincipalClass</key>

+ 151 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/TransmitterIDSetupViewController.swift

@@ -0,0 +1,151 @@
+//
+//  TransmitterIDSetupViewController.swift
+//  CGMBLEKitUI
+//
+//  Copyright © 2018 LoopKit Authors. All rights reserved.
+//
+
+import UIKit
+import LoopKit
+import LoopKitUI
+import CGMBLEKit
+import ShareClient
+
+class TransmitterIDSetupViewController: SetupTableViewController {
+
+    lazy private(set) var shareManager = ShareClientManager()
+
+    private func updateShareUsername() {
+        shareUsernameLabel.text = shareManager.shareService.username ?? SettingsTableViewCell.TapToSetString
+    }
+
+    private(set) var transmitterID: String? {
+        get {
+            return transmitterIDTextField.text
+        }
+        set {
+            transmitterIDTextField.text = newValue
+        }
+    }
+
+    private func updateStateForSettings() {
+        let isReadyToRead = transmitterID?.count == 6
+
+        if isReadyToRead {
+            continueState = .completed
+        } else {
+            continueState = .inputSettings
+        }
+    }
+
+    private enum State {
+        case loadingView
+        case inputSettings
+        case completed
+    }
+
+    private var continueState: State = .loadingView {
+        didSet {
+            switch continueState {
+            case .loadingView:
+                updateStateForSettings()
+            case .inputSettings:
+                footerView.primaryButton.isEnabled = false
+            case .completed:
+                footerView.primaryButton.isEnabled = true
+            }
+        }
+    }
+
+    override func continueButtonPressed(_ sender: Any) {
+        if continueState == .completed,
+            let setupViewController = navigationController as? TransmitterSetupViewController,
+            let transmitterID = transmitterID
+        {
+            setupViewController.completeSetup(state: TransmitterManagerState(transmitterID: transmitterID))
+        }
+    }
+
+    override func cancelButtonPressed(_ sender: Any) {
+        if transmitterIDTextField.isFirstResponder {
+            transmitterIDTextField.resignFirstResponder()
+        } else {
+            super.cancelButtonPressed(sender)
+        }
+    }
+
+    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
+        return continueState == .completed
+    }
+
+    // MARK: -
+
+    @IBOutlet private var shareUsernameLabel: UILabel!
+
+    @IBOutlet private var transmitterIDTextField: UITextField!
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        updateShareUsername()
+
+        continueState = .inputSettings
+    }
+
+    // MARK: - UITableViewDelegate
+
+    private enum Section: Int {
+        case transmitterID
+        case share
+    }
+
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        switch Section(rawValue: indexPath.section)! {
+        case .transmitterID:
+            tableView.deselectRow(at: indexPath, animated: false)
+        case .share:
+            let authVC = AuthenticationViewController(authentication: shareManager.shareService)
+            authVC.authenticationObserver = { [weak self] (service) in
+                self?.shareManager.shareService = service
+                self?.updateShareUsername()
+            }
+
+            show(authVC, sender: nil)
+        }
+    }
+}
+
+
+extension TransmitterIDSetupViewController: UITextFieldDelegate {
+    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+        guard let text = textField.text, let stringRange = Range(range, in: text) else {
+            updateStateForSettings()
+            return true
+        }
+
+        let newText = text.replacingCharacters(in: stringRange, with: string)
+
+        if newText.count >= 6 {
+            if newText.count == 6 {
+                textField.text = newText
+                textField.resignFirstResponder()
+            }
+
+            updateStateForSettings()
+            return false
+        }
+
+        textField.text = newText
+        updateStateForSettings()
+        return false
+    }
+
+    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
+        return true
+    }
+
+    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+        textField.resignFirstResponder()
+        return true
+    }
+}

+ 0 - 0
Dependencies/CGMBLEKit/CGMBLEKitUI/TransmitterManager+UI.swift


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini