瀏覽代碼

Merge branch 'XPM_MySigning' into localization

Jon B Mårtensson 5 年之前
父節點
當前提交
025a0d1d1b
共有 98 個文件被更改,包括 2272 次插入233 次删除
  1. 1 1
      Dependecies/rileylink_ios/MinimedKit/Models/PumpModel.swift
  2. 190 7
      Dependecies/rileylink_ios/MinimedKitUI/RileyLinkMinimedDeviceTableViewController.swift
  3. 24 24
      Dependecies/rileylink_ios/RileyLink.xcodeproj/project.pbxproj
  4. 192 4
      Dependecies/rileylink_ios/RileyLinkBLEKit/PeripheralManager+RileyLink.swift
  5. 22 1
      Dependecies/rileylink_ios/RileyLinkBLEKit/PeripheralManager.swift
  6. 155 4
      Dependecies/rileylink_ios/RileyLinkBLEKit/RileyLinkDevice.swift
  7. 366 4
      Dependecies/rileylink_ios/RileyLinkKitUI/RileyLinkDeviceTableViewController.swift
  8. 13 12
      FreeAPS.xcodeproj/project.pbxproj
  9. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-1024.png
  10. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-1024@1x.png
  11. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-108@2x.png
  12. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-128.png
  13. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-16.png
  14. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-20.png
  15. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-20@2x.png
  16. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-20@3x.png
  17. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-24@2x.png
  18. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-24@3x.png
  19. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-256.png
  20. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-27-5@2x.png
  21. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-29.png
  22. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-29@2x.png
  23. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-29@3x.png
  24. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-32.png
  25. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-40.png
  26. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-40@2x.png
  27. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-40@3x.png
  28. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-44@2x.png
  29. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-50@2x.png
  30. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-512.png
  31. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-60@2x.png
  32. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-60@3x.png
  33. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-64.png
  34. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-76.png
  35. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-76@2x.png
  36. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-83.5@2x.png
  37. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-86@2x.png
  38. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-98@2x.png
  39. 81 123
      FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
  40. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/100.png
  41. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/1024.png
  42. 二進制
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/1024.pxm
  43. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/114.png
  44. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/120.png
  45. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/128.png
  46. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/144.png
  47. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/152.png
  48. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/16.png
  49. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/167.png
  50. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/172.png
  51. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/180.png
  52. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/196.png
  53. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/20.png
  54. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/216.png
  55. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/256.png
  56. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/29.png
  57. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/32.png
  58. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/40.png
  59. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/48.png
  60. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/50.png
  61. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/512.png
  62. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/55.png
  63. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/57.png
  64. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/58.png
  65. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/60.png
  66. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/64.png
  67. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/72.png
  68. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/76.png
  69. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/80.png
  70. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/87.png
  71. 0 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/88.png
  72. 302 0
      FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/Contents.json
  73. 28 0
      FreeAPS/Resources/Assets.xcassets/Colors/Background.colorset/Contents.json
  74. 28 0
      FreeAPS/Resources/Assets.xcassets/Colors/LoopOrange.colorset/Contents.json
  75. 1 1
      FreeAPS/Resources/FreeAPS.entitlements
  76. 7 4
      FreeAPS/Resources/Info.plist
  77. 1 1
      FreeAPS/Resources/javascript/bundle/autosens.js
  78. 1 1
      FreeAPS/Resources/javascript/bundle/autotune-core.js
  79. 1 1
      FreeAPS/Resources/javascript/bundle/autotune-prep.js
  80. 1 1
      FreeAPS/Resources/javascript/bundle/basal-set-temp.js
  81. 1 1
      FreeAPS/Resources/javascript/bundle/determine-basal.js
  82. 1 1
      FreeAPS/Resources/javascript/bundle/glucose-get-last.js
  83. 1 1
      FreeAPS/Resources/javascript/bundle/iob.js
  84. 1 1
      FreeAPS/Resources/javascript/bundle/meal.js
  85. 1 1
      FreeAPS/Resources/javascript/bundle/profile.js
  86. 1 0
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  87. 6 4
      FreeAPS/Sources/APS/Storage/GlucoseStorage.swift
  88. 2 0
      FreeAPS/Sources/Helpers/Color+Extensions.swift
  89. 1 0
      FreeAPS/Sources/Models/FreeAPSSettings.swift
  90. 6 0
      FreeAPS/Sources/Modules/Home/HomeViewModel.swift
  91. 17 13
      FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift
  92. 45 5
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  93. 22 17
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  94. 8 0
      FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigViewModel.swift
  95. 4 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift
  96. 1 0
      FreeAPS/Sources/Services/SettingsManager/SettingsManager.swift
  97. 370 0
      FreeAPS/Sources/en.lproj/Localizable.strings
  98. 370 0
      FreeAPS/Sources/sv.lproj/Localizable.strings

+ 1 - 1
Dependecies/rileylink_ios/MinimedKit/Models/PumpModel.swift

@@ -51,7 +51,7 @@ public enum PumpModel: String {
     }
     
     public var hasMySentry: Bool {
-        return generation >= 23
+        return false
     }
     
     var hasLowSuspend: Bool {

+ 190 - 7
Dependecies/rileylink_ios/MinimedKitUI/RileyLinkMinimedDeviceTableViewController.swift

@@ -53,6 +53,22 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
         }
     }
     
+    private var fw_hw: String? {
+        didSet {
+            guard isViewLoaded else {
+                return
+            }
+            
+            cellForRow(.orl)?.detailTextLabel?.text = fw_hw
+        }
+    }
+    
+    private var disconnectLed: Bool = false
+    private var disconnectVibration: Bool = false
+    
+    private var connectLed: Bool = false
+    private var connectVibration: Bool = false
+    
     private var uptime: TimeInterval? {
         didSet {
             guard isViewLoaded else {
@@ -62,6 +78,17 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
             cellForRow(.uptime)?.setDetailAge(uptime)
         }
     }
+    
+    private var battery: String? {
+        didSet {
+            guard isViewLoaded else {
+                return
+            }
+            
+            cellForRow(.battery)?.setDetailBatteryLevel(battery)
+        }
+    }
+
 
     private var lastIdle: Date? {
         didSet {
@@ -117,12 +144,57 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
             } catch { }
         }
     }
+    
+    func updateBatteryLevel() {
+        device.runSession(withName: "Get battery level") { (session) in
+            let batteryLevel = self.device.getBatterylevel()
+            DispatchQueue.main.async {
+                self.battery = batteryLevel
+            }
+        }
+    }
+    
+    func orangeClose() {
+        device.runSession(withName: "Orange Action Close") { (session) in
+            self.device.orangeClose()
+        }
+    }
+    
+    func orangeReadSet() {
+        device.runSession(withName: "orange Read Set") { (session) in
+            self.device.orangeReadSet()
+        }
+    }
 
+    func writePSW() {
+        device.runSession(withName: "Orange Action PSW") { (session) in
+            self.device.orangeWritePwd()
+        }
+    }
+    
+    func orangeAction(index: Int) {
+        device.runSession(withName: "Orange Action \(index)") { (session) in
+            self.device.orangeAction(mode: index)
+        }
+    }
+    
+    func orangeAction(index: Int, open: Bool) {
+        device.runSession(withName: "Orange Set Action \(index)") { (session) in
+            self.device.orangeSetAction(index: index, open: open)
+        }
+    }
+    
     private func updateDeviceStatus() {
         device.getStatus { (status) in
             DispatchQueue.main.async {
-                self.lastIdle = status.lastIdle
                 self.firmwareVersion = status.firmwareDescription
+                self.fw_hw = status.fw_hw
+                self.connectLed = status.ledOn
+                self.connectVibration = status.vibrationOn
+                self.disconnectLed = status.ledOn
+                self.disconnectVibration = status.vibrationOn
+                
+                self.tableView.reloadData()
             }
         }
     }
@@ -167,7 +239,10 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
                 if let state = note.userInfo?[PumpOps.notificationPumpStateKey] as? PumpState {
                     self?.pumpState = state
                 }
-            }
+            },
+            center.addObserver(forName: .DeviceFW_HWChange, object: device, queue: mainQueue) { [weak self] (note) in
+                self?.updateDeviceStatus()
+            },
         ]
     }
     
@@ -185,6 +260,19 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
         updateRSSI()
         
         updateUptime()
+        
+        updateBatteryLevel()
+        
+        writePSW()
+        
+        orangeAction(index: 9)
+        
+        orangeReadSet()
+    }
+    
+    public override func viewDidDisappear(_ animated: Bool) {
+        super.viewDidDisappear(animated)
+        orangeClose()
     }
     
     public override func viewWillDisappear(_ animated: Bool) {
@@ -238,6 +326,8 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
         case connection
         case uptime
         case idleStatus
+        case battery
+        case orl
     }
 
     private enum PumpRow: Int, CaseCountable {
@@ -259,8 +349,33 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
         case enableLED
         case discoverCommands
         case getStatistics
+        case yellow
+        case red
+        case off
+        case shake
+        case shakeOff
+        case disconnectLed
+        case disconnectVibration
+        case connectLed
+        case connectVibration
+    }
+
+    @objc
+    func switchAction(sender: RileyLinkSwitch) {
+        switch CommandRow(rawValue: sender.index)! {
+        case .connectLed:
+            orangeAction(index: 4, open: sender.isOn)
+        case .connectVibration:
+            orangeAction(index: 5, open: sender.isOn)
+        case .disconnectLed:
+            orangeAction(index: 2, open: sender.isOn)
+        case .disconnectVibration:
+            orangeAction(index: 3, open: sender.isOn)
+        default:
+            break
+        }
     }
-
+    
     private func cellForRow(_ row: DeviceRow) -> UITableViewCell? {
         return tableView.cellForRow(at: IndexPath(row: row.rawValue, section: Section.device.rawValue))
     }
@@ -287,6 +402,10 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
             return CommandRow.count
         }
     }
+    
+    public override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 45
+    }
 
     public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell: UITableViewCell
@@ -295,8 +414,17 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
             cell = reusableCell
         } else {
             cell = UITableViewCell(style: .value1, reuseIdentifier: CellIdentifier)
+            let switchView = RileyLinkSwitch()
+            switchView.tag = 10000
+            switchView.addTarget(self, action: #selector(switchAction(sender:)), for: .valueChanged)
+            switchView.frame = CGRect(x: tableView.frame.width - 51 - 20, y: 7, width: 51, height: 31)
+            cell.contentView.addSubview(switchView)
         }
-
+        
+        let switchView = cell.contentView.viewWithTag(10000) as? RileyLinkSwitch
+        switchView?.isHidden = true
+        switchView?.index = indexPath.row
+        
         cell.accessoryType = .none
 
         switch Section(rawValue: indexPath.section)! {
@@ -321,6 +449,12 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
             case .idleStatus:
                 cell.textLabel?.text = LocalizedString("On Idle", comment: "The title of the cell showing the last idle")
                 cell.setDetailDate(lastIdle, formatter: dateFormatter)
+            case .battery:
+                cell.textLabel?.text = NSLocalizedString("Battery Level", comment: "The title of the cell showing battery level")
+                cell.setDetailBatteryLevel(battery)
+            case .orl:
+                cell.textLabel?.text = NSLocalizedString("ORL", comment: "The title of the cell showing ORL")
+                cell.detailTextLabel?.text = fw_hw
             }
         case .pump:
             switch PumpRow(rawValue: indexPath.row)! {
@@ -385,6 +519,36 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
                 
             case .getStatistics:
                 cell.textLabel?.text = LocalizedString("RileyLink Statistics", comment: "The title of the command to fetch RileyLink statistics")
+            case .yellow:
+                cell.textLabel?.text = NSLocalizedString("Lighten Yellow LED", comment: "The title of the cell showing Lighten Yellow LED")
+            case .red:
+                cell.textLabel?.text = NSLocalizedString("Lighten Red LED", comment: "The title of the cell showing Lighten Red LED")
+            case .off:
+                cell.textLabel?.text = NSLocalizedString("Turn Off LED", comment: "The title of the cell showing Turn Off LED")
+            case .shake:
+                cell.textLabel?.text = NSLocalizedString("Test Vibrator", comment: "The title of the cell showing Test Vibrator")
+            case .shakeOff:
+                cell.textLabel?.text = NSLocalizedString("Stop Vibrator", comment: "The title of the cell showing Stop Vibrator")
+            case .disconnectLed:
+                switchView?.isHidden = false
+                switchView?.isOn = disconnectLed
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Disconnect Led", comment: "The title of the cell showing Stop Vibrator")
+            case .disconnectVibration:
+                switchView?.isHidden = false
+                switchView?.isOn = disconnectVibration
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Disconnect Vibrator", comment: "The title of the cell showing Stop Vibrator")
+            case .connectLed:
+                switchView?.isHidden = false
+                switchView?.isOn = connectLed
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Connect Led", comment: "The title of the cell showing Stop Vibrator")
+            case .connectVibration:
+                switchView?.isHidden = false
+                switchView?.isOn = connectVibration
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Connect Vibrator", comment: "The title of the cell showing Stop Vibrator")
             }
         }
 
@@ -438,7 +602,7 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
                 break
             }
         case .commands:
-            let vc: CommandResponseViewController
+            var vc: CommandResponseViewController?
 
             switch CommandRow(rawValue: indexPath.row)! {
             case .tune:
@@ -465,13 +629,22 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController {
                 vc = .discoverCommands(ops: ops, device: device)
             case .getStatistics:
                 vc = .getStatistics(ops: ops, device: device)
+            case .yellow: orangeAction(index: 1)
+            case .red: orangeAction(index: 2)
+            case .off: orangeAction(index: 3)
+            case .shake: orangeAction(index: 4)
+            case .shakeOff: orangeAction(index: 5)
+            default:
+                break
             }
 
             if let cell = tableView.cellForRow(at: indexPath) {
-                vc.title = cell.textLabel?.text
+                vc?.title = cell.textLabel?.text
             }
 
-            show(vc, sender: indexPath)
+            if let vc = vc {
+                show(vc, sender: indexPath)
+            }
         case .pump:
             break
         }
@@ -516,6 +689,16 @@ private extension TimeInterval {
 
 
 private extension UITableViewCell {
+    
+    func setDetailBatteryLevel(_ batteryLevel: String?) {
+        if let unwrappedBatteryLevel = batteryLevel {
+            detailTextLabel?.text = unwrappedBatteryLevel + " %"
+        } else {
+            detailTextLabel?.text = ""
+        }
+    }
+    
+    
     func setDetailDate(_ date: Date?, formatter: DateFormatter) {
         if let date = date {
             detailTextLabel?.text = formatter.string(from: date)

+ 24 - 24
Dependecies/rileylink_ios/RileyLink.xcodeproj/project.pbxproj

@@ -7,6 +7,12 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		19E9EBAD264A86F100000232 /* PeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9EBAC264A86F100000232 /* PeripheralManager.swift */; };
+		19E9EBB5264A88BD00000232 /* PeripheralManager+RileyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9EBB4264A88BD00000232 /* PeripheralManager+RileyLink.swift */; };
+		19E9EBB7264A88C900000232 /* RileyLinkDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9EBB6264A88C900000232 /* RileyLinkDevice.swift */; };
+		19E9EBB9264A88F500000232 /* RileyLinkDeviceTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9EBB8264A88F500000232 /* RileyLinkDeviceTableViewController.swift */; };
+		19E9EBBB264A890F00000232 /* RileyLinkMinimedDeviceTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9EBBA264A890F00000232 /* RileyLinkMinimedDeviceTableViewController.swift */; };
+		19E9EBBD264A897A00000232 /* PumpModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9EBBC264A897A00000232 /* PumpModel.swift */; };
 		2B19B9881DF3EF68006AB65F /* NewTimePumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B19B9871DF3EF68006AB65F /* NewTimePumpEvent.swift */; };
 		2F962EC11E6872170070EFBD /* TimestampedHistoryEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F962EC01E6872170070EFBD /* TimestampedHistoryEventTests.swift */; };
 		2F962EC81E7074E60070EFBD /* BolusNormalPumpEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F962EC71E7074E60070EFBD /* BolusNormalPumpEventTests.swift */; };
@@ -20,16 +26,13 @@
 		431CE7811F98564200255374 /* RileyLinkBLEKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 431CE7711F98564100255374 /* RileyLinkBLEKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		431CE7841F98564200255374 /* RileyLinkBLEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 431CE76F1F98564100255374 /* RileyLinkBLEKit.framework */; };
 		431CE7851F98564200255374 /* RileyLinkBLEKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 431CE76F1F98564100255374 /* RileyLinkBLEKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
-		431CE78D1F985B5400255374 /* PeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE78C1F985B5400255374 /* PeripheralManager.swift */; };
 		431CE78F1F985B6E00255374 /* CBPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE78E1F985B6E00255374 /* CBPeripheral.swift */; };
 		431CE7911F985D8D00255374 /* RileyLinkDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7901F985D8D00255374 /* RileyLinkDeviceManager.swift */; };
-		431CE7931F985DE700255374 /* PeripheralManager+RileyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7921F985DE700255374 /* PeripheralManager+RileyLink.swift */; };
 		431CE7961F9B0F0200255374 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7941F9B0DAE00255374 /* OSLog.swift */; };
 		431CE7971F9B0F0200255374 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7941F9B0DAE00255374 /* OSLog.swift */; };
 		431CE7981F9B0F0200255374 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7941F9B0DAE00255374 /* OSLog.swift */; };
 		431CE7991F9B0F0200255374 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7941F9B0DAE00255374 /* OSLog.swift */; };
 		431CE79A1F9B0F1600255374 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7941F9B0DAE00255374 /* OSLog.swift */; };
-		431CE79C1F9B21BA00255374 /* RileyLinkDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE79B1F9B21BA00255374 /* RileyLinkDevice.swift */; };
 		431CE79E1F9BE73900255374 /* BLEFirmwareVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE79D1F9BE73900255374 /* BLEFirmwareVersion.swift */; };
 		431CE79F1F9C670600255374 /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43EBE4501EAD238C0073A0B5 /* TimeInterval.swift */; };
 		431CE7A11F9D195600255374 /* CBCentralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431CE7A01F9D195600255374 /* CBCentralManager.swift */; };
@@ -65,7 +68,6 @@
 		4352A73220DEC9D600CAC200 /* CommandResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4352A73120DEC9D600CAC200 /* CommandResponseViewController.swift */; };
 		4352A73320DEC9FC00CAC200 /* RileyLinkKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D5E78E1FAF7BFB004ACDB7 /* RileyLinkKitUI.framework */; };
 		4352A73420DECAE000CAC200 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43FB610A20DDF55E002B996B /* LoopKitUI.framework */; };
-		4352A73920DECBAA00CAC200 /* RileyLinkMinimedDeviceTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4352A73820DECBAA00CAC200 /* RileyLinkMinimedDeviceTableViewController.swift */; };
 		4352A73A20DECDB300CAC200 /* MinimedKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C10D9BC11C8269D500378342 /* MinimedKit.framework */; };
 		4352A73F20DED02C00CAC200 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4352A73D20DED01700CAC200 /* HKUnit.swift */; };
 		4352A74120DED23100CAC200 /* RileyLinkDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4352A74020DED23000CAC200 /* RileyLinkDeviceManager.swift */; };
@@ -148,7 +150,6 @@
 		43D5E7951FAF7BFB004ACDB7 /* RileyLinkKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D5E78E1FAF7BFB004ACDB7 /* RileyLinkKitUI.framework */; };
 		43D5E7961FAF7BFB004ACDB7 /* RileyLinkKitUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 43D5E78E1FAF7BFB004ACDB7 /* RileyLinkKitUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		43D5E79A1FAF7C47004ACDB7 /* RileyLinkDeviceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439731261CF21C3C00F474E5 /* RileyLinkDeviceTableViewCell.swift */; };
-		43D5E79C1FAF7C47004ACDB7 /* RileyLinkDeviceTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C170C9981CECD80000F3D8E5 /* RileyLinkDeviceTableViewController.swift */; };
 		43D5E79F1FAF7C98004ACDB7 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431185AE1CF25A590059ED98 /* IdentifiableClass.swift */; };
 		43D5E7A01FAF7CCA004ACDB7 /* NumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43323EA61FA81A0F003FB0FA /* NumberFormatter.swift */; };
 		43D5E7A11FAF7CE0004ACDB7 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B4A9581D1E6357003B8985 /* UITableViewCell.swift */; };
@@ -358,7 +359,6 @@
 		C17EDC4F2134D0CC0031D9F0 /* TimeZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345D1CD1DA16AF300BAAD22 /* TimeZone.swift */; };
 		C1814B88225F0A3D008D2D8E /* ExpirationReminderDateTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1814B87225F0A3D008D2D8E /* ExpirationReminderDateTableViewCell.swift */; };
 		C1814B8A225FADFA008D2D8E /* ExpirationReminderDateTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1814B89225FADFA008D2D8E /* ExpirationReminderDateTableViewCell.xib */; };
-		C1842BBB1C8E184300DB42AC /* PumpModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1842BBA1C8E184300DB42AC /* PumpModel.swift */; };
 		C1842BBD1C8E7C6E00DB42AC /* PumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1842BBC1C8E7C6E00DB42AC /* PumpEvent.swift */; };
 		C1842BBF1C8E855A00DB42AC /* PumpEventType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1842BBE1C8E855A00DB42AC /* PumpEventType.swift */; };
 		C1842BC11C8E8B2500DB42AC /* UnabsorbedInsulinPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1842BC01C8E8B2500DB42AC /* UnabsorbedInsulinPumpEvent.swift */; };
@@ -879,6 +879,12 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		19E9EBAC264A86F100000232 /* PeripheralManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeripheralManager.swift; sourceTree = "<group>"; };
+		19E9EBB4264A88BD00000232 /* PeripheralManager+RileyLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PeripheralManager+RileyLink.swift"; sourceTree = "<group>"; };
+		19E9EBB6264A88C900000232 /* RileyLinkDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkDevice.swift; sourceTree = "<group>"; };
+		19E9EBB8264A88F500000232 /* RileyLinkDeviceTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkDeviceTableViewController.swift; sourceTree = "<group>"; };
+		19E9EBBA264A890F00000232 /* RileyLinkMinimedDeviceTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkMinimedDeviceTableViewController.swift; sourceTree = "<group>"; };
+		19E9EBBC264A897A00000232 /* PumpModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpModel.swift; sourceTree = "<group>"; };
 		2B19B9871DF3EF68006AB65F /* NewTimePumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewTimePumpEvent.swift; sourceTree = "<group>"; };
 		2F962EBE1E678BAA0070EFBD /* PumpOpsSynchronousTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PumpOpsSynchronousTests.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		2F962EC01E6872170070EFBD /* TimestampedHistoryEventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimestampedHistoryEventTests.swift; sourceTree = "<group>"; };
@@ -895,12 +901,9 @@
 		431CE7721F98564100255374 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		431CE7771F98564200255374 /* RileyLinkBLEKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RileyLinkBLEKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		431CE7801F98564200255374 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		431CE78C1F985B5400255374 /* PeripheralManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralManager.swift; sourceTree = "<group>"; };
 		431CE78E1F985B6E00255374 /* CBPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBPeripheral.swift; sourceTree = "<group>"; };
 		431CE7901F985D8D00255374 /* RileyLinkDeviceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RileyLinkDeviceManager.swift; sourceTree = "<group>"; };
-		431CE7921F985DE700255374 /* PeripheralManager+RileyLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PeripheralManager+RileyLink.swift"; sourceTree = "<group>"; };
 		431CE7941F9B0DAE00255374 /* OSLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
-		431CE79B1F9B21BA00255374 /* RileyLinkDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RileyLinkDevice.swift; sourceTree = "<group>"; };
 		431CE79D1F9BE73900255374 /* BLEFirmwareVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEFirmwareVersion.swift; sourceTree = "<group>"; };
 		431CE7A01F9D195600255374 /* CBCentralManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBCentralManager.swift; sourceTree = "<group>"; };
 		431CE7A21F9D737F00255374 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
@@ -921,7 +924,6 @@
 		4352A72720DEC9B700CAC200 /* MinimedKitUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MinimedKitUI.h; sourceTree = "<group>"; };
 		4352A72820DEC9B700CAC200 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		4352A73120DEC9D600CAC200 /* CommandResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandResponseViewController.swift; sourceTree = "<group>"; };
-		4352A73820DECBAA00CAC200 /* RileyLinkMinimedDeviceTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RileyLinkMinimedDeviceTableViewController.swift; sourceTree = "<group>"; };
 		4352A73D20DED01700CAC200 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = "<group>"; };
 		4352A74020DED23000CAC200 /* RileyLinkDeviceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkDeviceManager.swift; sourceTree = "<group>"; };
 		435535D51FB6D98400CE5A23 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
@@ -1297,7 +1299,6 @@
 		C16E61212208C7A80069F357 /* ReservoirReading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservoirReading.swift; sourceTree = "<group>"; };
 		C16E61252208EC580069F357 /* MinimedHUDProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MinimedHUDProvider.swift; sourceTree = "<group>"; };
 		C170C98D1CECD6F300F3D8E5 /* CBPeripheralState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBPeripheralState.swift; sourceTree = "<group>"; };
-		C170C9981CECD80000F3D8E5 /* RileyLinkDeviceTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkDeviceTableViewController.swift; sourceTree = "<group>"; };
 		C1711A551C94F13400CB25BD /* ButtonPressCarelinkMessageBody.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonPressCarelinkMessageBody.swift; sourceTree = "<group>"; };
 		C1711A591C952D2900CB25BD /* GetPumpModelCarelinkMessageBody.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetPumpModelCarelinkMessageBody.swift; sourceTree = "<group>"; };
 		C1711A5B1C953F3000CB25BD /* GetBatteryCarelinkMessageBody.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetBatteryCarelinkMessageBody.swift; sourceTree = "<group>"; };
@@ -1307,7 +1308,6 @@
 		C17884601D519F1E00405663 /* BatteryIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryIndicator.swift; sourceTree = "<group>"; };
 		C1814B87225F0A3D008D2D8E /* ExpirationReminderDateTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpirationReminderDateTableViewCell.swift; sourceTree = "<group>"; };
 		C1814B89225FADFA008D2D8E /* ExpirationReminderDateTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ExpirationReminderDateTableViewCell.xib; sourceTree = "<group>"; };
-		C1842BBA1C8E184300DB42AC /* PumpModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpModel.swift; sourceTree = "<group>"; };
 		C1842BBC1C8E7C6E00DB42AC /* PumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpEvent.swift; sourceTree = "<group>"; };
 		C1842BBE1C8E855A00DB42AC /* PumpEventType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PumpEventType.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		C1842BC01C8E8B2500DB42AC /* UnabsorbedInsulinPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UnabsorbedInsulinPumpEvent.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
@@ -1733,14 +1733,14 @@
 				431CE78E1F985B6E00255374 /* CBPeripheral.swift */,
 				431CE7A21F9D737F00255374 /* Command.swift */,
 				431CE7A61F9D98F700255374 /* CommandSession.swift */,
-				431CE78C1F985B5400255374 /* PeripheralManager.swift */,
 				43D5E7871FAEDAC4004ACDB7 /* PeripheralManagerError.swift */,
-				431CE7921F985DE700255374 /* PeripheralManager+RileyLink.swift */,
+				19E9EBAC264A86F100000232 /* PeripheralManager.swift */,
+				19E9EBB4264A88BD00000232 /* PeripheralManager+RileyLink.swift */,
 				431CE7A41F9D78F500255374 /* RFPacket.swift */,
 				432847C21FA57C0F00CDE69C /* RadioFirmwareVersion.swift */,
 				43BA719A202591A70058961E /* Response.swift */,
 				43BA719C2026C9B00058961E /* ResponseBuffer.swift */,
-				431CE79B1F9B21BA00255374 /* RileyLinkDevice.swift */,
+				19E9EBB6264A88C900000232 /* RileyLinkDevice.swift */,
 				433ABFFB2016FDF700E6C1FF /* RileyLinkDeviceError.swift */,
 				431CE7901F985D8D00255374 /* RileyLinkDeviceManager.swift */,
 				C12572972125FA390061BA2F /* RileyLinkConnectionManager.swift */,
@@ -1765,7 +1765,7 @@
 			children = (
 				C1C73F1C1DE6306A0022FC89 /* BatteryChemistryType.swift */,
 				43D8708A20DE1BC9006B549E /* PumpColor.swift */,
-				C1842BBA1C8E184300DB42AC /* PumpModel.swift */,
+				19E9EBBC264A897A00000232 /* PumpModel.swift */,
 				C1274F851D8242BE0002912B /* PumpRegion.swift */,
 			);
 			path = Models;
@@ -1809,9 +1809,9 @@
 				43709AE020DF1D5400F941B3 /* MinimedPumpManager.storyboard */,
 				43709ACC20DF1CC900F941B3 /* Setup */,
 				4352A73120DEC9D600CAC200 /* CommandResponseViewController.swift */,
-				4352A73820DECBAA00CAC200 /* RileyLinkMinimedDeviceTableViewController.swift */,
 				43709AC320DF1C8B00F941B3 /* MinimedPumpManager+UI.swift */,
 				43709AC220DF1C8B00F941B3 /* MinimedPumpSettingsViewController.swift */,
+				19E9EBBA264A890F00000232 /* RileyLinkMinimedDeviceTableViewController.swift */,
 				43709AC020DF1C8B00F941B3 /* PumpModel.swift */,
 				43709AC120DF1C8B00F941B3 /* RadioSelectionTableViewController.swift */,
 				C16E61252208EC580069F357 /* MinimedHUDProvider.swift */,
@@ -1878,7 +1878,7 @@
 				43709AEB20E0056F00F941B3 /* RileyLinkKitUI.xcassets */,
 				C170C98D1CECD6F300F3D8E5 /* CBPeripheralState.swift */,
 				439731261CF21C3C00F474E5 /* RileyLinkDeviceTableViewCell.swift */,
-				C170C9981CECD80000F3D8E5 /* RileyLinkDeviceTableViewController.swift */,
+				19E9EBB8264A88F500000232 /* RileyLinkDeviceTableViewController.swift */,
 				435D26B320DA0AAE00891C17 /* RileyLinkDevicesHeaderView.swift */,
 				435D26B520DA0BCC00891C17 /* RileyLinkDevicesTableViewDataSource.swift */,
 				43709ABB20DF1C6400F941B3 /* RileyLinkManagerSetupViewController.swift */,
@@ -3527,18 +3527,18 @@
 				43BA719B202591A70058961E /* Response.swift in Sources */,
 				43D5E7881FAEDAC4004ACDB7 /* PeripheralManagerError.swift in Sources */,
 				C12572982125FA390061BA2F /* RileyLinkConnectionManager.swift in Sources */,
-				431CE79C1F9B21BA00255374 /* RileyLinkDevice.swift in Sources */,
 				431CE7A71F9D98F700255374 /* CommandSession.swift in Sources */,
 				431CE7A31F9D737F00255374 /* Command.swift in Sources */,
+				19E9EBAD264A86F100000232 /* PeripheralManager.swift in Sources */,
 				431CE7A11F9D195600255374 /* CBCentralManager.swift in Sources */,
 				431CE7911F985D8D00255374 /* RileyLinkDeviceManager.swift in Sources */,
-				431CE78D1F985B5400255374 /* PeripheralManager.swift in Sources */,
 				431CE79E1F9BE73900255374 /* BLEFirmwareVersion.swift in Sources */,
 				432847C31FA57C0F00CDE69C /* RadioFirmwareVersion.swift in Sources */,
-				431CE7931F985DE700255374 /* PeripheralManager+RileyLink.swift in Sources */,
 				431CE79A1F9B0F1600255374 /* OSLog.swift in Sources */,
 				43047FC91FAECA8700508343 /* Data.swift in Sources */,
+				19E9EBB7264A88C900000232 /* RileyLinkDevice.swift in Sources */,
 				431CE7A51F9D78F500255374 /* RFPacket.swift in Sources */,
+				19E9EBB5264A88BD00000232 /* PeripheralManager+RileyLink.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -3576,8 +3576,8 @@
 				4352A74320DED3F200CAC200 /* NumberFormatter.swift in Sources */,
 				4352A73220DEC9D600CAC200 /* CommandResponseViewController.swift in Sources */,
 				C1DD51D525A0BC6000DE27AE /* InsulinTypeConfirmation.swift in Sources */,
+				19E9EBBB264A890F00000232 /* RileyLinkMinimedDeviceTableViewController.swift in Sources */,
 				43709AC520DF1C8B00F941B3 /* RadioSelectionTableViewController.swift in Sources */,
-				4352A73920DECBAA00CAC200 /* RileyLinkMinimedDeviceTableViewController.swift in Sources */,
 				4352A74220DED3D200CAC200 /* CaseCountable.swift in Sources */,
 				43709AC620DF1C8B00F941B3 /* MinimedPumpSettingsViewController.swift in Sources */,
 				43709AC720DF1C8B00F941B3 /* MinimedPumpManager+UI.swift in Sources */,
@@ -3614,6 +3614,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				7D2366F6212527DA0028B67D /* LocalizedString.swift in Sources */,
+				19E9EBB9264A88F500000232 /* RileyLinkDeviceTableViewController.swift in Sources */,
 				43BF58B31FF6079600499C46 /* TimeInterval.swift in Sources */,
 				43D5E7A11FAF7CE0004ACDB7 /* UITableViewCell.swift in Sources */,
 				43709AEE20E008F300F941B3 /* SetupImageTableViewCell.swift in Sources */,
@@ -3628,7 +3629,6 @@
 				435D26B420DA0AAE00891C17 /* RileyLinkDevicesHeaderView.swift in Sources */,
 				43709ABF20DF1C6400F941B3 /* RileyLinkSettingsViewController.swift in Sources */,
 				43709ABD20DF1C6400F941B3 /* RileyLinkSetupTableViewController.swift in Sources */,
-				43D5E79C1FAF7C47004ACDB7 /* RileyLinkDeviceTableViewController.swift in Sources */,
 				43709ABE20DF1C6400F941B3 /* RileyLinkManagerSetupViewController.swift in Sources */,
 				43D5E7A01FAF7CCA004ACDB7 /* NumberFormatter.swift in Sources */,
 				43709AE420DF20D500F941B3 /* OSLog.swift in Sources */,
@@ -3745,6 +3745,7 @@
 				C1A721661EC4BCE30080FAD7 /* PartialDecode.swift in Sources */,
 				43B0ADC21D12454700AAD278 /* TimestampedHistoryEvent.swift in Sources */,
 				C1EAD6C91C826B92006DBA60 /* MySentryAckMessageBody.swift in Sources */,
+				19E9EBBD264A897A00000232 /* PumpModel.swift in Sources */,
 				C1F000521EBE73F400F65163 /* BasalSchedule.swift in Sources */,
 				C1EAD6CE1C826B92006DBA60 /* ReadSettingsCarelinkMessageBody.swift in Sources */,
 				C1F0004C1EBE68A600F65163 /* DataFrameMessageBody.swift in Sources */,
@@ -3764,7 +3765,6 @@
 				C1842BC71C8F8DC200DB42AC /* CalBGForPHPumpEvent.swift in Sources */,
 				C1842C251C8FA45100DB42AC /* AlarmSensorPumpEvent.swift in Sources */,
 				43D8709320DE1C80006B549E /* PumpOpsSession+LoopKit.swift in Sources */,
-				C1842BBB1C8E184300DB42AC /* PumpModel.swift in Sources */,
 				C16E61222208C7A80069F357 /* ReservoirReading.swift in Sources */,
 				C14D2B091C9F5EDA00C98E4C /* ChangeTempBasalTypePumpEvent.swift in Sources */,
 				C1842C1E1C8FA45100DB42AC /* ChangeBasalProfilePumpEvent.swift in Sources */,

+ 192 - 4
Dependecies/rileylink_ios/RileyLinkBLEKit/PeripheralManager+RileyLink.swift

@@ -12,13 +12,15 @@ import os.log
 protocol CBUUIDRawValue: RawRepresentable {}
 extension CBUUIDRawValue where RawValue == String {
     var cbUUID: CBUUID {
-        return CBUUID(string: rawValue)
+        return CBUUID(string: rawValue.uppercased())
     }
 }
 
 
 enum RileyLinkServiceUUID: String, CBUUIDRawValue {
-    case main = "0235733B-99C5-4197-B856-69219C2A3845"
+    case main    = "0235733B-99C5-4197-B856-69219C2A3845"
+    case battery = "180F"
+    case orange  = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
 }
 
 enum MainServiceCharacteristicUUID: String, CBUUIDRawValue {
@@ -30,6 +32,24 @@ enum MainServiceCharacteristicUUID: String, CBUUIDRawValue {
     case ledMode         = "C6D84241-F1A7-4F9C-A25F-FCE16732F14E"
 }
 
+enum BatteryServiceCharacteristicUUID: String, CBUUIDRawValue {
+    case battery_level   = "2A19"
+}
+
+enum OrangeServiceCharacteristicUUID: String, CBUUIDRawValue {
+    case orange         = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
+    case orangeNotif    = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
+}
+
+enum RileyLinkOrangeMode: UInt8 {
+    case yellow  = 0x1
+    case red   = 0x2
+    case off = 0x3
+    case shake = 0x4
+    case shakeOff = 0x5
+    case fw_hw = 0x9
+}
+
 enum RileyLinkLEDMode: UInt8 {
     case off  = 0x00
     case on   = 0x01
@@ -48,12 +68,21 @@ extension PeripheralManager.Configuration {
                     MainServiceCharacteristicUUID.timerTick.cbUUID,
                     MainServiceCharacteristicUUID.firmwareVersion.cbUUID,
                     MainServiceCharacteristicUUID.ledMode.cbUUID
+                ],
+                RileyLinkServiceUUID.battery.cbUUID: [
+                    BatteryServiceCharacteristicUUID.battery_level.cbUUID
+                ],
+                RileyLinkServiceUUID.orange.cbUUID: [
+                    OrangeServiceCharacteristicUUID.orange.cbUUID,
+                    OrangeServiceCharacteristicUUID.orangeNotif.cbUUID,
                 ]
             ],
             notifyingCharacteristics: [
                 RileyLinkServiceUUID.main.cbUUID: [
                     MainServiceCharacteristicUUID.responseCount.cbUUID
-                    // TODO: Should timer tick default to on?
+                ],
+                RileyLinkServiceUUID.orange.cbUUID: [
+                    OrangeServiceCharacteristicUUID.orangeNotif.cbUUID,
                 ]
             ],
             valueUpdateMacros: [
@@ -82,6 +111,26 @@ fileprivate extension CBPeripheral {
     }
 }
 
+fileprivate extension CBPeripheral {
+    func getBatteryCharacteristic(_ uuid: BatteryServiceCharacteristicUUID, serviceUUID: RileyLinkServiceUUID = .battery) -> CBCharacteristic? {
+        guard let service = services?.itemWithUUID(serviceUUID.cbUUID) else {
+            return nil
+        }
+
+        return service.characteristics?.itemWithUUID(uuid.cbUUID)
+    }
+}
+
+fileprivate extension CBPeripheral {
+    func getOrangeCharacteristic(_ uuid: OrangeServiceCharacteristicUUID, serviceUUID: RileyLinkServiceUUID = .orange) -> CBCharacteristic? {
+        guard let service = services?.itemWithUUID(serviceUUID.cbUUID) else {
+            return nil
+        }
+
+        return service.characteristics?.itemWithUUID(uuid.cbUUID)
+    }
+}
+
 
 extension CBCentralManager {
     func scanForPeripherals(withOptions options: [String: Any]? = nil) {
@@ -295,7 +344,146 @@ extension PeripheralManager {
 
 // MARK: - Lower-level helper operations
 extension PeripheralManager {
-
+    
+    func readBatteryLevel(timeout: TimeInterval) throws -> String {
+        guard let characteristic = peripheral.getBatteryCharacteristic(.battery_level) else {
+            throw RileyLinkDeviceError.peripheralManagerError(.unknownCharacteristic)
+        }
+        
+        do {
+            guard let data = try readValue(for: characteristic, timeout: timeout) else {
+                // TODO: This is an "unknown value" issue, not a timeout
+                throw RileyLinkDeviceError.peripheralManagerError(.timeout)
+            }
+            
+            let battery_level = "\(data[0])"
+            
+            return battery_level
+        } catch let error as PeripheralManagerError {
+            throw RileyLinkDeviceError.peripheralManagerError(error)
+        }
+    }
+    
+    func setOrangeNotifyOn() throws {
+        perform { [self] (manager) in
+            guard let characteristicNotif = peripheral.getOrangeCharacteristic(.orangeNotif) else {
+                return
+            }
+            
+            add(log: "setOrangeNotifyOn: \(characteristicNotif.uuid.uuidString)")
+            do {
+                try setNotifyValue(true, for: characteristicNotif, timeout: 2)
+            } catch {
+                add(log: "setOrangeNotifyOn Error: \(error.localizedDescription)")
+            }
+        }
+    }
+    
+    func orangeAction(mode: RileyLinkOrangeMode) {
+        if mode != .off, mode != .shakeOff {
+            orangeWritePwd()
+        }
+        perform { [self] (manager) in
+            do {
+                guard let characteristic = peripheral.getOrangeCharacteristic(.orange) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+                let value = Data([0xbb, mode.rawValue])
+                add(log: "write: \(value.hexadecimalString)")
+                try writeValue(value, for: characteristic, type: .withResponse, timeout: PeripheralManager.expectedMaxBLELatency)
+            } catch (_) {
+                add(log: "orangeAction failed")
+            }
+        }
+        if mode == .off, mode == .shakeOff {
+            orangeClose()
+        }
+    }
+    
+    
+    
+    func setAction(index: Int, open: Bool) {
+        perform { [self] (manager) in
+            do {
+                guard let characteristic = peripheral.getOrangeCharacteristic(.orange) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+                if index == 0 {
+                    setDatas[2] = 0
+                    setDatas[3] = open ? 1 : 0
+                } else if index == 1 {
+                    setDatas[2] = 1
+                    setDatas[3] = open ? 1 : 0
+                }
+                let value = Data(setDatas)
+                add(log: "write: \(value.hexadecimalString)")
+                try writeValue(value, for: characteristic, type: .withResponse, timeout: PeripheralManager.expectedMaxBLELatency)
+            } catch (_) {
+                add(log: "setAction failed")
+            }
+        }
+    }
+    
+    func orangeWritePwd() {
+        perform { [self] (manager) in
+            do {
+                guard let characteristic = peripheral.getOrangeCharacteristic(.orange) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+                let value = Data([0xAA])
+                add(log: "write: \(value.hexadecimalString)")
+                try writeValue(value, for: characteristic, type: .withResponse, timeout: PeripheralManager.expectedMaxBLELatency)
+            } catch (_) {
+                add(log: "orangeWritePwd failed")
+            }
+        }
+    }
+    
+    func orangeReadSet() {
+        perform { [self] (manager) in
+            do {
+                guard let characteristic = peripheral.getOrangeCharacteristic(.orange) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+                let value = Data([0xdd, 0x01])
+                add(log: "write: \(value.hexadecimalString)")
+                try writeValue(value, for: characteristic, type: .withResponse, timeout: PeripheralManager.expectedMaxBLELatency)
+            } catch (_) {
+                add(log: "orangeReadSet failed")
+            }
+        }
+    }
+    
+    func orangeReadVDC() {
+        perform { [self] (manager) in
+            do {
+                guard let characteristic = peripheral.getOrangeCharacteristic(.orange) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+                let value = Data([0xdd, 0x03])
+                add(log: "write: \(value.hexadecimalString)")
+                try writeValue(value, for: characteristic, type: .withResponse, timeout: PeripheralManager.expectedMaxBLELatency)
+            } catch (_) {
+                add(log: "orangeReadSet failed")
+            }
+        }
+    }
+    
+    func orangeClose() {
+        perform { [self] (manager) in
+            do {
+                guard let characteristic = peripheral.getOrangeCharacteristic(.orange) else {
+                    throw PeripheralManagerError.unknownCharacteristic
+                }
+                let value = Data([0xcc])
+                add(log: "write: \(value.hexadecimalString)")
+                try writeValue(value, for: characteristic, type: .withResponse, timeout: PeripheralManager.expectedMaxBLELatency)
+            } catch (_) {
+                add(log: "orangeClose failed")
+            }
+        }
+    }
+    
     /// Writes command data expecting a single response
     ///
     /// - Parameters:

+ 22 - 1
Dependecies/rileylink_ios/RileyLinkBLEKit/PeripheralManager.swift

@@ -51,6 +51,8 @@ class PeripheralManager: NSObject {
 
     // Confined to `queue`
     private var needsConfiguration = true
+    
+    var logString = ""
 
     weak var delegate: PeripheralManagerDelegate? {
         didSet {
@@ -59,6 +61,8 @@ class PeripheralManager: NSObject {
             }
         }
     }
+    
+    var setDatas: [UInt8] = [0xdd, 0x02, 0x00, 0x00]
 
     // Called from RileyLinkDeviceManager.managerQueue
     init(peripheral: CBPeripheral, configuration: Configuration, centralManager: CBCentralManager, queue: DispatchQueue) {
@@ -95,6 +99,8 @@ extension PeripheralManager {
 
 protocol PeripheralManagerDelegate: class {
     func peripheralManager(_ manager: PeripheralManager, didUpdateValueFor characteristic: CBCharacteristic)
+    
+    func peripheralManager(_ manager: PeripheralManager, didUpdateNotificationStateFor characteristic: CBCharacteristic)
 
     func peripheralManager(_ manager: PeripheralManager, didReadRSSI RSSI: NSNumber, error: Error?)
 
@@ -167,7 +173,10 @@ extension PeripheralManager {
                 throw PeripheralManagerError.unknownCharacteristic
             }
 
+            add(log: "serviceUUID: \(serviceUUID.uuidString)")
+            
             for characteristicUUID in characteristicUUIDs {
+                add(log: "characteristicUUID: \(characteristicUUID.uuidString)")
                 guard let characteristic = service.characteristics?.itemWithUUID(characteristicUUID) else {
                     throw PeripheralManagerError.unknownCharacteristic
                 }
@@ -266,6 +275,7 @@ extension PeripheralManager {
 
     /// - Throws: PeripheralManagerError
     func setNotifyValue(_ enabled: Bool, for characteristic: CBCharacteristic, timeout: TimeInterval) throws {
+        add(log: "setNotifyValue: \(characteristic.uuid.uuidString)")
         try runCommand(timeout: timeout) {
             addCondition(.notificationStateUpdate(characteristic: characteristic, enabled: enabled))
 
@@ -374,6 +384,7 @@ extension PeripheralManager: CBPeripheralDelegate {
         }
 
         commandLock.unlock()
+        delegate?.peripheralManager(self, didUpdateNotificationStateFor: characteristic)
     }
 
     func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
@@ -417,7 +428,7 @@ extension PeripheralManager: CBPeripheralDelegate {
             }
         } else if let macro = configuration.valueUpdateMacros[characteristic.uuid] {
             macro(self)
-        } else if commandConditions.isEmpty {
+        } else {
             notifyDelegate = true // execute after the unlock
         }
 
@@ -461,10 +472,20 @@ extension PeripheralManager: CBCentralManagerDelegate {
 
 
 extension PeripheralManager {
+    
+    func add(log: String) {
+        print("LLLLLL: \(log)")
+        logString += "\(Date())\n\(log)\n"
+        if logString.count > 10000 {
+            logString.removeFirst(1000)
+        }
+    }
+    
     public override var debugDescription: String {
         var items = [
             "## PeripheralManager",
             "peripheral: \(peripheral)",
+            "log: \(logString)"
         ]
         queue.sync {
             items.append("needsConfiguration: \(needsConfiguration)")

+ 155 - 4
Dependecies/rileylink_ios/RileyLinkBLEKit/RileyLinkDevice.swift

@@ -33,9 +33,17 @@ public class RileyLinkDevice {
 
     // Confined to `lock`
     private var isTimerTickEnabled = true
+    
+    // Confined to `lock`
+    private var logs = ""
 
     /// Serializes access to device state
     private var lock = os_unfair_lock()
+    
+    private var fw_hw = "FW/HW"
+    public var ledOn: Bool = false
+    public var vibrationOn: Bool = false
+    public var voltage = ""
 
     /// The queue used to serialize sessions and observe when they've drained
     private let sessionQueue: OperationQueue = {
@@ -93,6 +101,43 @@ extension RileyLinkDevice {
         manager.setCustomName(name)
     }
     
+    public func getBatterylevel() -> String {
+        do {
+            return try manager.readBatteryLevel(timeout: 1)
+        } catch {}
+        return ""
+    }
+    
+    public func orangeAction(mode: Int) {
+        add(log: "orangeAction: \(mode)")
+        manager.orangeAction(mode: RileyLinkOrangeMode(rawValue: UInt8(mode))!)
+    }
+    
+    public func orangeSetAction(index: Int, open: Bool) {
+        add(log: "orangeSetAction: \(index), \(open)")
+        manager.setAction(index: index, open: open)
+    }
+    
+    public func orangeWritePwd() {
+        add(log: "orangeWritePwd")
+        manager.orangeWritePwd()
+    }
+    
+    public func orangeClose() {
+        add(log: "orangeClose")
+        manager.orangeClose()
+    }
+    
+    public func orangeReadSet() {
+        add(log: "orangeReadSet")
+        manager.orangeReadSet()
+    }
+    
+    public func orangeReadVDC() {
+        add(log: "orangeReadVDC")
+        manager.orangeReadVDC()
+    }
+    
     public func enableBLELEDs() {
         manager.setLEDMode(mode: .on)
     }
@@ -134,6 +179,12 @@ extension RileyLinkDevice {
         public let bleFirmwareVersion: BLEFirmwareVersion?
 
         public let radioFirmwareVersion: RadioFirmwareVersion?
+        
+        public let fw_hw: String?
+        
+        public var ledOn: Bool = false
+        public var vibrationOn: Bool = false
+        public var voltage = ""
     }
 
     public func getStatus(_ completion: @escaping (_ status: Status) -> Void) {
@@ -146,7 +197,11 @@ extension RileyLinkDevice {
                 lastIdle: lastIdle,
                 name: self.name,
                 bleFirmwareVersion: self.bleFirmwareVersion,
-                radioFirmwareVersion: self.radioFirmwareVersion
+                radioFirmwareVersion: self.radioFirmwareVersion,
+                fw_hw: self.fw_hw,
+                ledOn: self.ledOn,
+                vibrationOn: self.vibrationOn,
+                voltage: self.voltage
             ))
         }
     }
@@ -278,7 +333,6 @@ extension RileyLinkDevice {
         }
 
         manager.centralManager(central, didConnect: peripheral)
-
         NotificationCenter.default.post(name: .DeviceConnectionStateDidChange, object: self)
     }
 
@@ -295,9 +349,22 @@ extension RileyLinkDevice {
 
 
 extension RileyLinkDevice: PeripheralManagerDelegate {
+    func peripheralManager(_ manager: PeripheralManager, didUpdateNotificationStateFor characteristic: CBCharacteristic) {
+        add(log: "didUpdate: \(characteristic.uuid.uuidString)")
+//        switch OrangeServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString) {
+//        case .orange, .orangeNotif:
+//            manager.writePsw = true
+//            orangeWritePwd()
+//        default:
+//            break
+//        }
+        log.debug("Did didUpdateNotificationStateFor %@", characteristic)
+    }
+    
     // This is called from the central's queue
     func peripheralManager(_ manager: PeripheralManager, didUpdateValueFor characteristic: CBCharacteristic) {
         log.debug("Did UpdateValueFor %@", characteristic)
+        add(log: "Did UpdateValueFor: \(characteristic.uuid.uuidString), value: \(characteristic.value?.hexadecimalString ?? "")")
         switch MainServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString) {
         case .data?:
             guard let value = characteristic.value, value.count > 0 else {
@@ -338,18 +405,86 @@ extension RileyLinkDevice: PeripheralManagerDelegate {
                     self.log.error("Skipping parsing characteristic value update due to missing BLE firmware version")
                 }
 
+                self.resetBatteryAlert()
+                self.orangeReadVDC()
                 self.assertIdleListening(forceRestart: true)
             }
         case .responseCount?:
             // PeripheralManager.Configuration.valueUpdateMacros is responsible for handling this response.
+            self.resetBatteryAlert()
+            self.orangeReadVDC()
             break
         case .timerTick?:
             NotificationCenter.default.post(name: .DeviceTimerDidTick, object: self)
-
+            self.resetBatteryAlert()
+            self.orangeReadVDC()
             assertIdleListening(forceRestart: false)
         case .customName?, .firmwareVersion?, .ledMode?, .none:
             break
         }
+        
+        switch OrangeServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString) {
+        case .orange, .orangeNotif:
+            guard let data = characteristic.value, !data.isEmpty else { return }
+            if data.first == 0xbb {
+                guard let data = characteristic.value, data.count > 6 else { return }
+                if data[1] == 0x09, data[2] == 0xaa {
+                    fw_hw = "FW\(data[3]).\(data[4])/HW\(data[5]).\(data[6])"
+                    NotificationCenter.default.post(name: .DeviceFW_HWChange, object: self)
+                }
+            } else if data.first == 0xdd {
+                guard let data = characteristic.value, data.count > 2 else { return }
+                if data[1] == 0x01 {
+                    guard let data = characteristic.value, data.count > 5 else { return }
+                    ledOn = (data[3] != 0)
+                    vibrationOn = (data[4] != 0)
+                    NotificationCenter.default.post(name: .DeviceFW_HWChange, object: self)
+                } else if data[1] == 0x03 {
+                    guard var data = characteristic.value, data.count > 4 else { return }
+                    data = Data(data[3...4])
+                    let int = UInt16(bigEndian: data.withUnsafeBytes { $0.load(as: UInt16.self) })
+                    voltage = String(format: "%.1f%", Float(int) / 1000)
+                    NotificationCenter.default.post(name: .DeviceFW_HWChange, object: self)
+                    
+                    guard Date() > Date(timeIntervalSince1970: UserDefaults.standard.double(forKey: "voltage_date")).addingTimeInterval(60 * 60),
+                          UserDefaults.standard.double(forKey: "voltage_alert_value") != 0 else { return }
+                    
+                    UserDefaults.standard.setValue(Date().timeIntervalSince1970, forKey: "voltage_date")
+                    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
+                        let value = UserDefaults.standard.double(forKey: "voltage_alert_value")
+                        if (Double(self.voltage) ?? 100) <= value {
+                            let content = UNMutableNotificationContent()
+                            content.title = "Low Voltage"
+                            content.subtitle = self.voltage
+                            let request = UNNotificationRequest.init(identifier: "Orange Low Voltage", content: content, trigger: nil)
+                            UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
+                        }
+                    }
+                }
+            }
+        default:
+            break
+        }
+    }
+    
+    func resetBatteryAlert() {
+        guard Date() > Date(timeIntervalSince1970: UserDefaults.standard.double(forKey: "battery_date")).addingTimeInterval(60 * 60) else { return }
+        if UserDefaults.standard.integer(forKey: "battery_alert_value") != 0 {
+            manager.queue.async {
+                UserDefaults.standard.setValue(Date().timeIntervalSince1970, forKey: "battery_date")
+                let batteryLevel = self.getBatterylevel()
+                DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
+                    let value = UserDefaults.standard.integer(forKey: "battery_alert_value")
+                    if (Int(batteryLevel) ?? 100) <= value {
+                        let content = UNMutableNotificationContent()
+                        content.title = "Low Battery"
+                        content.subtitle = batteryLevel
+                        let request = UNNotificationRequest.init(identifier: "Orange Low Battery", content: content, trigger: nil)
+                        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
+                    }
+                }
+            }
+        }
     }
 
     func peripheralManager(_ manager: PeripheralManager, didReadRSSI RSSI: NSNumber, error: Error?) {
@@ -376,11 +511,23 @@ extension RileyLinkDevice: PeripheralManagerDelegate {
 
         let radioVersionString = try manager.readRadioFirmwareVersion(timeout: 1, responseType: bleFirmwareVersion?.responseType ?? .buffered)
         radioFirmwareVersion = RadioFirmwareVersion(versionString: radioVersionString)
+        
+        try manager.setOrangeNotifyOn()
     }
 }
 
 
 extension RileyLinkDevice: CustomDebugStringConvertible {
+    
+    public func add(log: String) {
+        os_unfair_lock_lock(&lock)
+        if self.logs.count > 10000 {
+            self.logs.removeLast(1000)
+        }
+        self.logs.append("\(Date())\n\(log)\n")
+        os_unfair_lock_unlock(&lock)
+    }
+    
     public var debugDescription: String {
         os_unfair_lock_lock(&lock)
         let lastIdle = self.lastIdle
@@ -398,7 +545,9 @@ extension RileyLinkDevice: CustomDebugStringConvertible {
             "* radioFirmware: \(String(describing: radioFirmwareVersion))",
             "* bleFirmware: \(String(describing: bleFirmwareVersion))",
             "* peripheralManager: \(manager)",
-            "* sessionQueue.operationCount: \(sessionQueue.operationCount)"
+            "* sessionQueue.operationCount: \(sessionQueue.operationCount)",
+            "* logs: \(logs)",
+            "* manager logs: \(manager.logString)"
         ].joined(separator: "\n")
     }
 }
@@ -423,4 +572,6 @@ extension Notification.Name {
     public static let DeviceRSSIDidChange = Notification.Name(rawValue: "com.rileylink.RileyLinkBLEKit.RSSIDidChange")
 
     public static let DeviceTimerDidTick = Notification.Name(rawValue: "com.rileylink.RileyLinkBLEKit.TimerTickDidChange")
+    
+    public static let DeviceFW_HWChange = Notification.Name(rawValue: "com.rileylink.RileyLinkBLEKit.DeviceFW_HWChange")
 }

+ 366 - 4
Dependecies/rileylink_ios/RileyLinkKitUI/RileyLinkDeviceTableViewController.swift

@@ -14,6 +14,12 @@ import os.log
 
 let CellIdentifier = "Cell"
 
+public class RileyLinkSwitch: UISwitch {
+    
+    public var index: Int = 0
+    public var section: Int = 0
+}
+
 public class RileyLinkDeviceTableViewController: UITableViewController {
 
     private let log = OSLog(category: "RileyLinkDeviceTableViewController")
@@ -32,6 +38,16 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         }
     }
     
+    private var fw_hw: String? {
+        didSet {
+            guard isViewLoaded else {
+                return
+            }
+            
+            cellForRow(.orl)?.detailTextLabel?.text = fw_hw
+        }
+    }
+    
     private var uptime: TimeInterval? {
         didSet {
             guard isViewLoaded else {
@@ -42,6 +58,16 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         }
     }
     
+    private var battery: String? {
+        didSet {
+            guard isViewLoaded else {
+                return
+            }
+            cellForRow(.battery)?.setDetailBatteryLevel(battery)
+        }
+    }
+    
+    
     private var frequency: Measurement<UnitFrequency>? {
         didSet {
             guard isViewLoaded else {
@@ -97,6 +123,12 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         device.getStatus { (status) in
             DispatchQueue.main.async {
                 self.firmwareVersion = status.firmwareDescription
+                self.fw_hw = status.fw_hw
+                self.ledOn = status.ledOn
+                self.vibrationOn = status.vibrationOn
+                self.voltage = status.voltage
+                
+                self.tableView.reloadData()
             }
         }
     }
@@ -114,6 +146,52 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         }
     }
     
+    func updateBatteryLevel() {
+        device.runSession(withName: "Get battery level") { (session) in
+            let batteryLevel = self.device.getBatterylevel()
+            DispatchQueue.main.async {
+                self.battery = batteryLevel
+            }
+        }
+    }
+    
+    func orangeClose() {
+        device.runSession(withName: "Orange Action Close") { (session) in
+            self.device.orangeClose()
+        }
+    }
+    
+    func orangeReadSet() {
+        device.runSession(withName: "orange Read Set") { (session) in
+            self.device.orangeReadSet()
+        }
+    }
+    
+    func orangeReadVDC() {
+        device.runSession(withName: "orange Read Set") { (session) in
+            self.device.orangeReadVDC()
+        }
+    }
+
+    func writePSW() {
+        device.runSession(withName: "Orange Action PSW") { (session) in
+            self.device.orangeWritePwd()
+        }
+    }
+    
+    func orangeAction(index: Int) {
+        device.runSession(withName: "Orange Action \(index)") { (session) in
+            self.device.orangeAction(mode: index)
+        }
+    }
+    
+    func orangeAction(index: Int, open: Bool) {
+        device.runSession(withName: "Orange Set Action \(index)") { (session) in
+            self.device.orangeSetAction(index: index, open: open)
+        }
+    }
+
+    
     func updateFrequency() {
 
         device.runSession(withName: "Get base frequency") { (session) in
@@ -165,6 +243,9 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
             center.addObserver(forName: .DeviceDidStartIdle, object: device, queue: mainQueue) { [weak self] (note) in
                 self?.updateDeviceStatus()
             },
+            center.addObserver(forName: .DeviceFW_HWChange, object: device, queue: mainQueue) { [weak self] (note) in
+                self?.updateDeviceStatus()
+            },
         ]
     }
     
@@ -185,6 +266,26 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
 
         updateUptime()
         
+        updateBatteryLevel()
+        
+        writePSW()
+        
+        orangeReadSet()
+        
+        orangeReadVDC()
+        
+        orangeAction(index: 9)
+    }
+    
+    public override func viewDidDisappear(_ animated: Bool) {
+        super.viewDidDisappear(animated)
+        if redOn || yellowOn {
+            orangeAction(index: 3)
+        }
+        
+        if shakeOn {
+            orangeAction(index: 5)
+        }
     }
     
     public override func viewWillDisappear(_ animated: Bool) {
@@ -227,8 +328,15 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
 
     private enum Section: Int, CaseCountable {
         case device
+        case alert
+        case configureCommand
         case commands
     }
+    
+    private enum AlertRow: Int, CaseCountable {
+        case battery
+        case voltage
+    }
 
     private enum DeviceRow: Int, CaseCountable {
         case customName
@@ -237,11 +345,29 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         case connection
         case uptime
         case frequency
+        case battery
+        case orl
+        case voltage
+    }
+    
+    private enum CommandRow: Int, CaseCountable {
+        case yellow
+        case red
+        case shake
+    }
+    
+    private enum ConfigureCommandRow: Int, CaseCountable {
+        case led
+        case vibration
     }
 
     private func cellForRow(_ row: DeviceRow) -> UITableViewCell? {
         return tableView.cellForRow(at: IndexPath(row: row.rawValue, section: Section.device.rawValue))
     }
+    
+    private func cellForRow(_ row: CommandRow) -> UITableViewCell? {
+        return tableView.cellForRow(at: IndexPath(row: row.rawValue, section: Section.commands.rawValue))
+    }
 
     public override func numberOfSections(in tableView: UITableView) -> Int {
         return Section.count
@@ -252,9 +378,68 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         case .device:
             return DeviceRow.count
         case .commands:
-            return 0
+            return CommandRow.count
+        case .configureCommand:
+            return ConfigureCommandRow.count
+        case .alert:
+            return AlertRow.count
         }
     }
+    
+    @objc
+    func switchAction(sender: RileyLinkSwitch) {
+        switch Section(rawValue: sender.section)! {
+        case .commands:
+            switch CommandRow(rawValue: sender.index)! {
+            case .yellow:
+                if sender.isOn {
+                    orangeAction(index: 1)
+                } else {
+                    orangeAction(index: 3)
+                }
+                yellowOn = sender.isOn
+                redOn = false
+            case .red:
+                if sender.isOn {
+                    orangeAction(index: 2)
+                } else {
+                    orangeAction(index: 3)
+                }
+                yellowOn = false
+                redOn = sender.isOn
+            case .shake:
+                if sender.isOn {
+                    orangeAction(index: 4)
+                } else {
+                    orangeAction(index: 5)
+                }
+                shakeOn = sender.isOn
+            }
+        case .configureCommand:
+            switch ConfigureCommandRow(rawValue: sender.index)! {
+            case .led:
+                orangeAction(index: 0, open: sender.isOn)
+                ledOn = sender.isOn
+            case .vibration:
+                orangeAction(index: 1, open: sender.isOn)
+                vibrationOn = sender.isOn
+            }
+        default:
+            break
+        }
+        tableView.reloadData()
+    }
+    
+    public override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 45
+    }
+    
+    var yellowOn = false
+    var redOn = false
+    var shakeOn = false
+    private var ledOn: Bool = false
+    private var vibrationOn: Bool = false
+    var voltage = ""
 
     public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell: UITableViewCell
@@ -263,8 +448,18 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
             cell = reusableCell
         } else {
             cell = UITableViewCell(style: .value1, reuseIdentifier: CellIdentifier)
+            let switchView = RileyLinkSwitch()
+            switchView.tag = 10000
+            switchView.addTarget(self, action: #selector(switchAction(sender:)), for: .valueChanged)
+            switchView.frame = CGRect(x: tableView.frame.width - 51 - 20, y: 7, width: 51, height: 31)
+            cell.contentView.addSubview(switchView)
         }
-
+        
+        let switchView = cell.contentView.viewWithTag(10000) as? RileyLinkSwitch
+        switchView?.isHidden = true
+        switchView?.index = indexPath.row
+        switchView?.section = indexPath.section
+        
         cell.accessoryType = .none
 
         switch Section(rawValue: indexPath.section)! {
@@ -289,10 +484,73 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
             case .frequency:
                 cell.textLabel?.text = LocalizedString("Frequency", comment: "The title of the cell showing current rileylink frequency")
                 cell.setDetailFrequency(frequency, formatter: frequencyFormatter)
+            case .battery:
+                cell.textLabel?.text = NSLocalizedString("Battery level", comment: "The title of the cell showing battery level")
+                cell.setDetailBatteryLevel(battery)
+            case .orl:
+                cell.textLabel?.text = NSLocalizedString("ORL", comment: "The title of the cell showing ORL")
+                cell.detailTextLabel?.text = fw_hw
+            case .voltage:
+                cell.textLabel?.text = NSLocalizedString("Voltage", comment: "The title of the cell showing ORL")
+                cell.detailTextLabel?.text = voltage
+            }
+        case .alert:
+            switch AlertRow(rawValue: indexPath.row)! {
+            case .battery:
+                var value = "OFF"
+                let v = UserDefaults.standard.integer(forKey: "battery_alert_value")
+                if v != 0 {
+                    value = "\(v)%"
+                }
+                
+                cell.accessoryType = .disclosureIndicator
+                cell.textLabel?.text = NSLocalizedString("Low Battery Alert", comment: "The title of the cell showing battery level")
+                cell.detailTextLabel?.text = "\(value)"
+            case .voltage:
+                var value = "OFF"
+                let v = UserDefaults.standard.double(forKey: "voltage_alert_value")
+                if v != 0 {
+                    value = String(format: "%.1f%", v)
+                }
+                
+                cell.accessoryType = .disclosureIndicator
+                cell.textLabel?.text = NSLocalizedString("Low Voltage Alert", comment: "The title of the cell showing voltage level")
+                cell.detailTextLabel?.text = "\(value)"
             }
         case .commands:
             cell.accessoryType = .disclosureIndicator
             cell.detailTextLabel?.text = nil
+            
+            switch CommandRow(rawValue: indexPath.row)! {
+            case .yellow:
+                switchView?.isHidden = false
+                cell.accessoryType = .none
+                switchView?.isOn = yellowOn
+                cell.textLabel?.text = NSLocalizedString("Lighten Yellow LED", comment: "The title of the cell showing Lighten Yellow LED")
+            case .red:
+                switchView?.isHidden = false
+                cell.accessoryType = .none
+                switchView?.isOn = redOn
+                cell.textLabel?.text = NSLocalizedString("Lighten Red LED", comment: "The title of the cell showing Lighten Red LED")
+            case .shake:
+                switchView?.isHidden = false
+                switchView?.isOn = shakeOn
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Test Vibrator", comment: "The title of the cell showing Test Vibrator")
+            }
+        case .configureCommand:
+            switch ConfigureCommandRow(rawValue: indexPath.row)! {
+            case .led:
+                switchView?.isHidden = false
+                switchView?.isOn = ledOn
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Enable Connection State LED", comment: "The title of the cell showing Stop Vibrator")
+            case .vibration:
+                switchView?.isHidden = false
+                switchView?.isOn = vibrationOn
+                cell.accessoryType = .none
+                cell.textLabel?.text = NSLocalizedString("Enable Connection State Vibrator", comment: "The title of the cell showing Stop Vibrator")
+            }
         }
 
         return cell
@@ -303,7 +561,11 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
         case .device:
             return LocalizedString("Device", comment: "The title of the section describing the device")
         case .commands:
-            return LocalizedString("Commands", comment: "The title of the section describing commands")
+            return LocalizedString("Test Commands", comment: "The title of the section describing commands")
+        case .configureCommand:
+            return LocalizedString("Configure Commands", comment: "The title of the section describing commands")
+        case .alert:
+            return LocalizedString("Alert", comment: "The title of the section describing commands")
         }
     }
 
@@ -320,6 +582,10 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
             }
         case .commands:
             return device.peripheralState == .connected
+        case .configureCommand:
+            return device.peripheralState == .connected
+        case .alert:
+            return true
         }
     }
 
@@ -342,6 +608,95 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
             }
         case .commands:
             break
+        case .configureCommand:
+            break
+        case .alert:
+            switch AlertRow(rawValue: indexPath.row)! {
+            case .battery:
+                let alert = UIAlertController.init(title: "Battery level Alert", message: nil, preferredStyle: .actionSheet)
+                
+                let action = UIAlertAction.init(title: "OFF", style: .default) { _ in
+                    UserDefaults.standard.setValue(0, forKey: "battery_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action1 = UIAlertAction.init(title: "20", style: .default) { _ in
+                    UserDefaults.standard.setValue(20, forKey: "battery_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action2 = UIAlertAction.init(title: "30", style: .default) { _ in
+                    UserDefaults.standard.setValue(30, forKey: "battery_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action3 = UIAlertAction.init(title: "40", style: .default) { _ in
+                    UserDefaults.standard.setValue(40, forKey: "battery_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action4 = UIAlertAction.init(title: "50", style: .default) { _ in
+                    UserDefaults.standard.setValue(50, forKey: "battery_alert_value")
+                    self.tableView.reloadData()
+                }
+                alert.addAction(action)
+                alert.addAction(action1)
+                alert.addAction(action2)
+                alert.addAction(action3)
+                alert.addAction(action4)
+                present(alert, animated: true, completion: nil)
+            case .voltage:
+                let alert = UIAlertController.init(title: "Voltage level Alert", message: nil, preferredStyle: .actionSheet)
+                
+                let action = UIAlertAction.init(title: "OFF", style: .default) { _ in
+                    UserDefaults.standard.setValue(0, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action1 = UIAlertAction.init(title: "2.4", style: .default) { _ in
+                    UserDefaults.standard.setValue(2.4, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action2 = UIAlertAction.init(title: "2.5", style: .default) { _ in
+                    UserDefaults.standard.setValue(2.5, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action3 = UIAlertAction.init(title: "2.6", style: .default) { _ in
+                    UserDefaults.standard.setValue(2.6, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action4 = UIAlertAction.init(title: "2.7", style: .default) { _ in
+                    UserDefaults.standard.setValue(2.7, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action5 = UIAlertAction.init(title: "2.8", style: .default) { _ in
+                    UserDefaults.standard.setValue(2.8, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action6 = UIAlertAction.init(title: "2.9", style: .default) { _ in
+                    UserDefaults.standard.setValue(2.9, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                
+                let action7 = UIAlertAction.init(title: "3.0", style: .default) { _ in
+                    UserDefaults.standard.setValue(3.0, forKey: "voltage_alert_value")
+                    self.tableView.reloadData()
+                }
+                alert.addAction(action)
+                alert.addAction(action1)
+                alert.addAction(action2)
+                alert.addAction(action3)
+                alert.addAction(action4)
+                alert.addAction(action5)
+                alert.addAction(action6)
+                alert.addAction(action7)
+                present(alert, animated: true, completion: nil)
+            }
         }
     }
 }
@@ -402,6 +757,14 @@ private extension UITableViewCell {
         }
     }
     
+    func setDetailBatteryLevel(_ batteryLevel: String?) {
+        if let unwrappedBatteryLevel = batteryLevel {
+            detailTextLabel?.text = unwrappedBatteryLevel + " %"
+        } else {
+            detailTextLabel?.text = ""
+        }
+    }
+    
     func setDetailFrequency(_ frequency: Measurement<UnitFrequency>?, formatter: MeasurementFormatter) {
         if let frequency = frequency {
             detailTextLabel?.text = formatter.string(from: frequency)
@@ -409,5 +772,4 @@ private extension UITableViewCell {
             detailTextLabel?.text = ""
         }
     }
-
 }

+ 13 - 12
FreeAPS.xcodeproj/project.pbxproj

@@ -712,6 +712,7 @@
 				3811DE1425C9D40400A708ED /* Router */,
 				3811DE9125C9D88200A708ED /* Services */,
 				3883582E25EEAFC000E024B2 /* Views */,
+				1952206026652474009B4D4F /* Localizable.strings */,
 			);
 			path = Sources;
 			sourceTree = "<group>";
@@ -2056,14 +2057,14 @@
 		388E596825AD948E0019842D /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APP_GROUP_ID = "group.$(PRODUCT_BUNDLE_IDENTIFIER)";
+				APP_GROUP_ID = group.com.T7VZ6LU6H3.xdripswift.JBMgroup;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = FreeAPS/Resources/FreeAPS.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = "0.2.0 XPM";
 				DEVELOPMENT_ASSET_PATHS = "";
-				DEVELOPMENT_TEAM = BA7ZHP4963;
+				DEVELOPMENT_TEAM = T7VZ6LU6H3;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -2071,8 +2072,8 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = "$(CURRENT_PROJECT_VERSION)";
-				PRODUCT_BUNDLE_IDENTIFIER = ru.artpancreas.FreeAPS;
+				MARKETING_VERSION = 1;
+				PRODUCT_BUNDLE_IDENTIFIER = com.T7VZ6LU6H3.aps;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
@@ -2082,14 +2083,14 @@
 		388E596925AD948E0019842D /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APP_GROUP_ID = "group.$(PRODUCT_BUNDLE_IDENTIFIER)";
+				APP_GROUP_ID = group.com.T7VZ6LU6H3.xdripswift.JBMgroup;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = FreeAPS/Resources/FreeAPS.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = "0.2.0 XPM";
 				DEVELOPMENT_ASSET_PATHS = "";
-				DEVELOPMENT_TEAM = BA7ZHP4963;
+				DEVELOPMENT_TEAM = T7VZ6LU6H3;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -2097,8 +2098,8 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = "$(CURRENT_PROJECT_VERSION)";
-				PRODUCT_BUNDLE_IDENTIFIER = ru.artpancreas.FreeAPS;
+				MARKETING_VERSION = 1;
+				PRODUCT_BUNDLE_IDENTIFIER = com.T7VZ6LU6H3.aps;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
@@ -2110,7 +2111,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = 777258T3K8;
+				DEVELOPMENT_TEAM = T7VZ6LU6H3;
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -2131,7 +2132,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = 777258T3K8;
+				DEVELOPMENT_TEAM = T7VZ6LU6H3;
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
 				LD_RUNPATH_SEARCH_PATHS = (

二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-1024.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-1024@1x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-108@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-128.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-16.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-20.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-20@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-20@3x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-24@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-24@3x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-256.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-27-5@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-29.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-29@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-29@3x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-32.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-40.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-40@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-40@3x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-44@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-50@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-512.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-60@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-60@3x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-64.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-76.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-76@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-83.5@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-86@2x.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024_-98@2x.png


+ 81 - 123
FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -1,298 +1,256 @@
 {
   "images" : [
     {
-      "filename" : "40.png",
+      "filename" : "1024_-20@2x.png",
       "idiom" : "iphone",
       "scale" : "2x",
       "size" : "20x20"
     },
     {
-      "filename" : "60.png",
+      "filename" : "1024_-20@3x.png",
       "idiom" : "iphone",
       "scale" : "3x",
       "size" : "20x20"
     },
     {
-      "filename" : "29.png",
-      "idiom" : "iphone",
-      "scale" : "1x",
-      "size" : "29x29"
-    },
-    {
-      "filename" : "58.png",
+      "filename" : "1024_-29@2x.png",
       "idiom" : "iphone",
       "scale" : "2x",
       "size" : "29x29"
     },
     {
-      "filename" : "87.png",
+      "filename" : "1024_-29@3x.png",
       "idiom" : "iphone",
       "scale" : "3x",
       "size" : "29x29"
     },
     {
-      "filename" : "80.png",
+      "filename" : "1024_-40@2x.png",
       "idiom" : "iphone",
       "scale" : "2x",
       "size" : "40x40"
     },
     {
-      "filename" : "120.png",
+      "filename" : "1024_-40@3x.png",
       "idiom" : "iphone",
       "scale" : "3x",
       "size" : "40x40"
     },
     {
-      "filename" : "57.png",
-      "idiom" : "iphone",
-      "scale" : "1x",
-      "size" : "57x57"
-    },
-    {
-      "filename" : "114.png",
-      "idiom" : "iphone",
-      "scale" : "2x",
-      "size" : "57x57"
-    },
-    {
-      "filename" : "120.png",
+      "filename" : "1024_-60@2x.png",
       "idiom" : "iphone",
       "scale" : "2x",
       "size" : "60x60"
     },
     {
-      "filename" : "180.png",
+      "filename" : "1024_-60@3x.png",
       "idiom" : "iphone",
       "scale" : "3x",
       "size" : "60x60"
     },
     {
-      "filename" : "20.png",
+      "filename" : "1024_-20.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "20x20"
     },
     {
-      "filename" : "40.png",
+      "filename" : "1024_-20@2x.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "20x20"
     },
     {
-      "filename" : "29.png",
+      "filename" : "1024_-29.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "29x29"
     },
     {
-      "filename" : "58.png",
+      "filename" : "1024_-29@2x.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "29x29"
     },
     {
-      "filename" : "40.png",
+      "filename" : "1024_-40.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "40x40"
     },
     {
-      "filename" : "80.png",
+      "filename" : "1024_-40@2x.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "40x40"
     },
     {
-      "filename" : "50.png",
-      "idiom" : "ipad",
-      "scale" : "1x",
-      "size" : "50x50"
-    },
-    {
-      "filename" : "100.png",
-      "idiom" : "ipad",
-      "scale" : "2x",
-      "size" : "50x50"
-    },
-    {
-      "filename" : "72.png",
-      "idiom" : "ipad",
-      "scale" : "1x",
-      "size" : "72x72"
-    },
-    {
-      "filename" : "144.png",
-      "idiom" : "ipad",
-      "scale" : "2x",
-      "size" : "72x72"
-    },
-    {
-      "filename" : "76.png",
+      "filename" : "1024_-76.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "76x76"
     },
     {
-      "filename" : "152.png",
+      "filename" : "1024_-76@2x.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "76x76"
     },
     {
-      "filename" : "167.png",
+      "filename" : "1024_-83.5@2x.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "83.5x83.5"
     },
     {
-      "filename" : "1024.png",
+      "filename" : "1024_-1024.png",
       "idiom" : "ios-marketing",
       "scale" : "1x",
       "size" : "1024x1024"
     },
     {
-      "filename" : "48.png",
+      "size" : "24x24",
       "idiom" : "watch",
-      "role" : "notificationCenter",
+      "filename" : "1024_-24@2x.png",
       "scale" : "2x",
-      "size" : "24x24",
+      "role" : "notificationCenter",
       "subtype" : "38mm"
     },
     {
-      "filename" : "55.png",
+      "size" : "27.5x27.5",
       "idiom" : "watch",
-      "role" : "notificationCenter",
+      "filename" : "1024_-27-5@2x.png",
       "scale" : "2x",
-      "size" : "27.5x27.5",
+      "role" : "notificationCenter",
       "subtype" : "42mm"
     },
     {
-      "filename" : "58.png",
+      "size" : "29x29",
       "idiom" : "watch",
+      "filename" : "1024_-29@2x.png",
       "role" : "companionSettings",
-      "scale" : "2x",
-      "size" : "29x29"
+      "scale" : "2x"
     },
     {
-      "filename" : "87.png",
+      "size" : "29x29",
       "idiom" : "watch",
+      "filename" : "1024_-24@3x.png",
       "role" : "companionSettings",
-      "scale" : "3x",
-      "size" : "29x29"
+      "scale" : "3x"
     },
     {
-      "filename" : "80.png",
+      "size" : "40x40",
       "idiom" : "watch",
-      "role" : "appLauncher",
+      "filename" : "1024_-40@2x.png",
       "scale" : "2x",
-      "size" : "40x40",
+      "role" : "appLauncher",
       "subtype" : "38mm"
     },
     {
-      "filename" : "88.png",
+      "size" : "44x44",
       "idiom" : "watch",
-      "role" : "appLauncher",
+      "filename" : "1024_-44@2x.png",
       "scale" : "2x",
-      "size" : "44x44",
+      "role" : "appLauncher",
       "subtype" : "40mm"
     },
     {
-      "filename" : "100.png",
+      "size" : "50x50",
       "idiom" : "watch",
-      "role" : "appLauncher",
+      "filename" : "1024_-50@2x.png",
       "scale" : "2x",
-      "size" : "50x50",
+      "role" : "appLauncher",
       "subtype" : "44mm"
     },
     {
-      "filename" : "172.png",
+      "size" : "86x86",
       "idiom" : "watch",
-      "role" : "quickLook",
+      "filename" : "1024_-86@2x.png",
       "scale" : "2x",
-      "size" : "86x86",
+      "role" : "quickLook",
       "subtype" : "38mm"
     },
     {
-      "filename" : "196.png",
+      "size" : "98x98",
       "idiom" : "watch",
-      "role" : "quickLook",
+      "filename" : "1024_-98@2x.png",
       "scale" : "2x",
-      "size" : "98x98",
+      "role" : "quickLook",
       "subtype" : "42mm"
     },
     {
-      "filename" : "216.png",
+      "size" : "108x108",
       "idiom" : "watch",
-      "role" : "quickLook",
+      "filename" : "1024_-108@2x.png",
       "scale" : "2x",
-      "size" : "108x108",
+      "role" : "quickLook",
       "subtype" : "44mm"
     },
     {
-      "filename" : "1024.png",
       "idiom" : "watch-marketing",
-      "scale" : "1x",
-      "size" : "1024x1024"
+      "filename" : "1024_-1024@1x.png",
+      "size" : "1024x1024",
+      "scale" : "1x"
     },
     {
-      "filename" : "16.png",
+      "size" : "16x16",
       "idiom" : "mac",
-      "scale" : "1x",
-      "size" : "16x16"
+      "filename" : "1024_-16.png",
+      "scale" : "1x"
     },
     {
-      "filename" : "32.png",
+      "size" : "16x16",
       "idiom" : "mac",
-      "scale" : "2x",
-      "size" : "16x16"
+      "filename" : "1024_-32.png",
+      "scale" : "2x"
     },
     {
-      "filename" : "32.png",
+      "size" : "32x32",
       "idiom" : "mac",
-      "scale" : "1x",
-      "size" : "32x32"
+      "filename" : "1024_-32.png",
+      "scale" : "1x"
     },
     {
-      "filename" : "64.png",
+      "size" : "32x32",
       "idiom" : "mac",
-      "scale" : "2x",
-      "size" : "32x32"
+      "filename" : "1024_-64.png",
+      "scale" : "2x"
     },
     {
-      "filename" : "128.png",
+      "size" : "128x128",
       "idiom" : "mac",
-      "scale" : "1x",
-      "size" : "128x128"
+      "filename" : "1024_-128.png",
+      "scale" : "1x"
     },
     {
-      "filename" : "256.png",
+      "size" : "128x128",
       "idiom" : "mac",
-      "scale" : "2x",
-      "size" : "128x128"
+      "filename" : "1024_-256.png",
+      "scale" : "2x"
     },
     {
-      "filename" : "256.png",
+      "size" : "256x256",
       "idiom" : "mac",
-      "scale" : "1x",
-      "size" : "256x256"
+      "filename" : "1024_-256.png",
+      "scale" : "1x"
     },
     {
-      "filename" : "512.png",
+      "size" : "256x256",
       "idiom" : "mac",
-      "scale" : "2x",
-      "size" : "256x256"
+      "filename" : "1024_-512.png",
+      "scale" : "2x"
     },
     {
-      "filename" : "512.png",
+      "size" : "512x512",
       "idiom" : "mac",
-      "scale" : "1x",
-      "size" : "512x512"
+      "filename" : "1024_-512.png",
+      "scale" : "1x"
     },
     {
-      "filename" : "1024.png",
+      "size" : "512x512",
       "idiom" : "mac",
-      "scale" : "2x",
-      "size" : "512x512"
+      "filename" : "1024_-1024.png",
+      "scale" : "2x"
     }
   ],
   "info" : {

FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/100.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/100.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/1024.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/1024.png


二進制
FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/1024.pxm


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/114.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/114.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/120.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/120.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/128.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/128.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/144.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/144.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/152.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/152.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/16.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/16.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/167.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/167.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/172.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/172.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/180.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/180.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/196.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/196.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/20.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/20.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/216.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/216.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/256.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/256.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/29.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/29.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/32.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/32.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/40.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/40.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/48.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/48.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/50.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/50.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/512.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/512.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/55.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/55.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/57.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/57.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/58.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/58.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/60.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/60.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/64.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/64.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/72.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/72.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/76.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/76.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/80.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/80.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/87.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/87.png


FreeAPS/Resources/Assets.xcassets/AppIcon.appiconset/88.png → FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/88.png


+ 302 - 0
FreeAPS/Resources/Assets.xcassets/AppIcon_Orig.appiconset/Contents.json

@@ -0,0 +1,302 @@
+{
+  "images" : [
+    {
+      "filename" : "40.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "60.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "29.png",
+      "idiom" : "iphone",
+      "scale" : "1x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "58.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "87.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "80.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "120.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "57.png",
+      "idiom" : "iphone",
+      "scale" : "1x",
+      "size" : "57x57"
+    },
+    {
+      "filename" : "114.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "57x57"
+    },
+    {
+      "filename" : "120.png",
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "60x60"
+    },
+    {
+      "filename" : "180.png",
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "60x60"
+    },
+    {
+      "filename" : "20.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "40.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "29.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "58.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "40.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "80.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "50.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "50x50"
+    },
+    {
+      "filename" : "100.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "50x50"
+    },
+    {
+      "filename" : "72.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "72x72"
+    },
+    {
+      "filename" : "144.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "72x72"
+    },
+    {
+      "filename" : "76.png",
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "76x76"
+    },
+    {
+      "filename" : "152.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "76x76"
+    },
+    {
+      "filename" : "167.png",
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "83.5x83.5"
+    },
+    {
+      "filename" : "1024.png",
+      "idiom" : "ios-marketing",
+      "scale" : "1x",
+      "size" : "1024x1024"
+    },
+    {
+      "filename" : "48.png",
+      "idiom" : "watch",
+      "role" : "notificationCenter",
+      "scale" : "2x",
+      "size" : "24x24",
+      "subtype" : "38mm"
+    },
+    {
+      "filename" : "55.png",
+      "idiom" : "watch",
+      "role" : "notificationCenter",
+      "scale" : "2x",
+      "size" : "27.5x27.5",
+      "subtype" : "42mm"
+    },
+    {
+      "filename" : "58.png",
+      "idiom" : "watch",
+      "role" : "companionSettings",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "87.png",
+      "idiom" : "watch",
+      "role" : "companionSettings",
+      "scale" : "3x",
+      "size" : "29x29"
+    },
+    {
+      "filename" : "80.png",
+      "idiom" : "watch",
+      "role" : "appLauncher",
+      "scale" : "2x",
+      "size" : "40x40",
+      "subtype" : "38mm"
+    },
+    {
+      "filename" : "88.png",
+      "idiom" : "watch",
+      "role" : "appLauncher",
+      "scale" : "2x",
+      "size" : "44x44",
+      "subtype" : "40mm"
+    },
+    {
+      "filename" : "100.png",
+      "idiom" : "watch",
+      "role" : "appLauncher",
+      "scale" : "2x",
+      "size" : "50x50",
+      "subtype" : "44mm"
+    },
+    {
+      "filename" : "172.png",
+      "idiom" : "watch",
+      "role" : "quickLook",
+      "scale" : "2x",
+      "size" : "86x86",
+      "subtype" : "38mm"
+    },
+    {
+      "filename" : "196.png",
+      "idiom" : "watch",
+      "role" : "quickLook",
+      "scale" : "2x",
+      "size" : "98x98",
+      "subtype" : "42mm"
+    },
+    {
+      "filename" : "216.png",
+      "idiom" : "watch",
+      "role" : "quickLook",
+      "scale" : "2x",
+      "size" : "108x108",
+      "subtype" : "44mm"
+    },
+    {
+      "filename" : "1024.png",
+      "idiom" : "watch-marketing",
+      "scale" : "1x",
+      "size" : "1024x1024"
+    },
+    {
+      "filename" : "16.png",
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "16x16"
+    },
+    {
+      "filename" : "32.png",
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "16x16"
+    },
+    {
+      "filename" : "32.png",
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "32x32"
+    },
+    {
+      "filename" : "64.png",
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "32x32"
+    },
+    {
+      "filename" : "128.png",
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "128x128"
+    },
+    {
+      "filename" : "256.png",
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "128x128"
+    },
+    {
+      "filename" : "256.png",
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "256x256"
+    },
+    {
+      "filename" : "512.png",
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "256x256"
+    },
+    {
+      "filename" : "512.png",
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "512x512"
+    },
+    {
+      "filename" : "1024.png",
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "512x512"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 28 - 0
FreeAPS/Resources/Assets.xcassets/Colors/Background.colorset/Contents.json

@@ -0,0 +1,28 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "platform" : "ios",
+        "reference" : "systemBackgroundColor"
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "platform" : "ios",
+        "reference" : "systemBackgroundColor"
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 28 - 0
FreeAPS/Resources/Assets.xcassets/Colors/LoopOrange.colorset/Contents.json

@@ -0,0 +1,28 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "platform" : "ios",
+        "reference" : "systemOrangeColor"
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "platform" : "osx",
+        "reference" : "systemOrangeColor"
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 1
FreeAPS/Resources/FreeAPS.entitlements

@@ -4,7 +4,7 @@
 <dict>
 	<key>com.apple.security.application-groups</key>
 	<array>
-		<string>$(APP_GROUP_ID)</string>
+		<string>group.com.T7VZ6LU6H3.xdripswift.JBMgroup</string>
 	</array>
 </dict>
 </plist>

+ 7 - 4
FreeAPS/Resources/Info.plist

@@ -2,12 +2,14 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-	<key>LSSupportsOpeningDocumentsInPlace</key>
-	<true/>
 	<key>AppGroupID</key>
-	<string>$(APP_GROUP_ID)</string>
+	<string>group.com.T7VZ6LU6H3.xdripswift.JBMgroup</string>
+	<key>AppGroupIdentifier</key>
+	<string>$(APP_GROUP_IDENTIFIER)</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>iAPS</string>
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
 	<key>CFBundleIdentifier</key>
@@ -21,7 +23,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>$(MARKETING_VERSION)</string>
 	<key>CFBundleVersion</key>
-	<string>$(BUILD_VERSION)</string>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<false/>
 	<key>LSApplicationQueriesSchemes</key>
@@ -31,6 +33,7 @@
 		<string>dexcomshare</string>
 		<string>diabox</string>
 		<string>spikeapp</string>
+		<string>xdrip</string>
 	</array>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>

File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/autosens.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/autotune-core.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/autotune-prep.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/basal-set-temp.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/glucose-get-last.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/iob.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/meal.js


File diff suppressed because it is too large
+ 1 - 1
FreeAPS/Resources/javascript/bundle/profile.js


+ 1 - 0
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -5,6 +5,7 @@
     "useAutotune": false
     "isUploadEnabled": false,
     "useLocalGlucoseSource": false,
+    "dontShowNS": false,
     "localGlucosePort": 8080,
     "debugOptions": false,
     "insulinReqFraction": 0.7,

+ 6 - 4
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -18,7 +18,7 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
     @Injected() private var broadcaster: Broadcaster!
 
     private enum Config {
-        static let filterTime: TimeInterval = 4.75 * 60
+        static let filterTime: TimeInterval = 2.75 * 60 // Before edit this was 4.75
     }
 
     init(resolver: Resolver) {
@@ -82,11 +82,13 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
     }
 
     func isGlucoseNotFlat() -> Bool {
-        let last3 = recent().suffix(3)
-        guard last3.count == 3 else { return true }
+        return true // To completely avoid more "To Flat" errors (I don't understand Why I still get them?
+
+        let last5 = recent().suffix(5)
+        guard last5.count == 5 else { return true }
 
         return Array(
-            last3
+            last5
                 .compactMap { $0.filtered ?? 0 }
                 .filter { $0 != 0 }
                 .uniqued()

+ 2 - 0
FreeAPS/Sources/Helpers/Color+Extensions.swift

@@ -4,10 +4,12 @@ extension Color {
     static let loopGray = Color("LoopGray")
     static let loopGreen = Color("LoopGreen")
     static let loopYellow = Color("LoopYellow")
+    static let loopOrange = Color("LoopOrange")
     static let loopRed = Color("LoopRed")
     static let insulin = Color("Insulin")
     static let uam = Color("UAM")
     static let zt = Color("ZT")
     static let tempBasal = Color("TempBasal")
     static let basal = Color("Basal")
+    static let backgroundColor = Color("Background")
 }

+ 1 - 0
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -7,6 +7,7 @@ struct FreeAPSSettings: JSON, Equatable {
     var useAutotune: Bool
     var isUploadEnabled: Bool?
     var useLocalGlucoseSource: Bool?
+    var dontShowNS: Bool
     var localGlucosePort: Int?
     var debugOptions: Bool?
     var insulinReqFraction: Decimal?

+ 6 - 0
FreeAPS/Sources/Modules/Home/HomeViewModel.swift

@@ -257,6 +257,12 @@ extension Home {
         }
 
         func openCGM() {
+            guard !settingsManager.settings.dontShowNS else {
+                let url = URL(string: "xdripswift://")!
+                UIApplication.shared.open(url, options: [:], completionHandler: nil)
+                return
+            }
+
             guard var url = nightscoutManager.cgmURL else { return }
 
             switch url.absoluteString {

+ 17 - 13
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -19,14 +19,14 @@ typealias GlucoseYRange = (minValue: Int, minY: CGFloat, maxValue: Int, maxY: CG
 struct MainChartView: View {
     private enum Config {
         static let endID = "End"
-        static let screenHours = 5
-        static let basalHeight: CGFloat = 70
-        static let topYPadding: CGFloat = 20
+        static let screenHours = 6
+        static let basalHeight: CGFloat = 100
+        static let topYPadding: CGFloat = 50
         static let bottomYPadding: CGFloat = 50
         static let minAdditionalWidth: CGFloat = 150
-        static let maxGlucose = 450
+        static let maxGlucose = 300 // 450
         static let minGlucose = 70
-        static let yLinesCount = 5
+        static let yLinesCount = 4 // Test
         static let bolusSize: CGFloat = 8
         static let bolusScale: CGFloat = 2.5
         static let carbsSize: CGFloat = 10
@@ -143,7 +143,7 @@ struct MainChartView: View {
                 path.move(to: CGPoint(x: 0, y: range.minY + CGFloat(line) * step))
                 path.addLine(to: CGPoint(x: fullSize.width, y: range.minY + CGFloat(line) * step))
             }
-        }.stroke(Color.secondary, lineWidth: 0.2)
+        }.stroke(Color.secondary, lineWidth: 0.1)
     }
 
     private func glucoseLabelsView(fullSize: CGSize) -> some View {
@@ -156,17 +156,16 @@ struct MainChartView: View {
 
             return Text(glucoseFormatter.string(from: value as NSNumber)!)
                 .position(CGPoint(x: fullSize.width - 12, y: range.minY + CGFloat(line) * yStep))
-                .font(.caption2)
+                .font(.caption) // Before: .font(.caption2)
                 .asAny()
         }
     }
 
     private func basalView(fullSize: CGSize) -> some View {
         ZStack {
-            tempBasalPath.fill(Color.tempBasal)
-            tempBasalPath.stroke(Color.tempBasal, lineWidth: 1)
-            suspensionsPath.fill(Color.loopGray)
-            regularBasalPath.stroke(Color.basal, lineWidth: 1)
+            tempBasalPath.fill(Color.tempBasal.opacity(0.5)).scaleEffect(x: 1, y: -1)
+//            tempBasalPath.stroke(Color.tempBasal, lineWidth: 1).scaleEffect(x: 1, y: -1)          // removed the Y=0 line, not needed when having icicles
+            regularBasalPath.stroke(Color.tempBasal, style: StrokeStyle(lineWidth: 1, dash: [3])).scaleEffect(x: 1, y: -1)
         }
         .frame(width: fullGlucoseWidth(viewWidth: fullSize.width) + additionalWidth(viewWidth: fullSize.width))
         .frame(maxHeight: Config.basalHeight)
@@ -204,6 +203,8 @@ struct MainChartView: View {
         .frame(width: fullGlucoseWidth(viewWidth: fullSize.width) + additionalWidth(viewWidth: fullSize.width))
     }
 
+    @Environment(\.colorScheme) var colorScheme
+
     private func xGridView(fullSize: CGSize) -> some View {
         ZStack {
             Path { path in
@@ -215,14 +216,17 @@ struct MainChartView: View {
                     path.addLine(to: CGPoint(x: x, y: fullSize.height - 20))
                 }
             }
-            .stroke(Color.secondary, lineWidth: 0.2)
+            .stroke(Color.secondary, lineWidth: 0)
 
             Path { path in
                 let x = timeToXCoordinate(timerDate.timeIntervalSince1970, fullSize: fullSize)
                 path.move(to: CGPoint(x: x, y: 0))
                 path.addLine(to: CGPoint(x: x, y: fullSize.height - 20))
             }
-            .stroke(Color.secondary, style: StrokeStyle(lineWidth: 0.5, dash: [5]))
+            .stroke(
+                colorScheme == .dark ? Color.white : Color.black, // current time as vertical line
+                style: StrokeStyle(lineWidth: 1, dash: [2])
+            )
         }
     }
 

+ 45 - 5
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -19,7 +19,7 @@ struct CurrentGlucoseView: View {
     private var deltaFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 2
+        formatter.maximumFractionDigits = 1
         formatter.positivePrefix = "+"
         return formatter
     }
@@ -42,21 +42,61 @@ struct CurrentGlucoseView: View {
                 )
                 .font(.system(size: 24, weight: .bold))
                 .fixedSize()
+                .foregroundColor(colorOfGlucose)
                 image.padding(.bottom, 2)
 
             }.padding(.leading, 4)
             HStack(alignment: .lastTextBaseline, spacing: 2) {
                 Text(
-                    recentGlucose.map { dateFormatter.string(from: $0.dateString) } ?? "--"
-                ).font(.caption2).foregroundColor(.secondary)
+                    "\(minutesAgo)m "
+                ).font(.caption2).foregroundColor(colorOfMinutesAgo(minutesAgo))
                 Text(
                     delta
                         .map { deltaFormatter.string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)!
                         } ??
                         "--"
+                ).font(.system(size: 12, weight: .bold)) }
+        }
+    }
+
+    var colorOfGlucose: Color {
+        guard let recentBG = recentGlucose?.glucose
+        else { return .loopYellow }
+
+//        recentBG = Int(recentBG.asMmolL) // convert to mmol/l for calculation
+
+        switch recentBG {
+        case 73 ... 144:
+            return .loopGreen
+        case 63 ... 72,
+             145 ... 180:
+            return .loopYellow
+        case 54 ... 62,
+             181 ... 207:
+            return .loopOrange
+        default:
+            return .loopRed
+        }
+    }
+
+    var minutesAgo: Int {
+        let lastGlucoseDateString = recentGlucose.map { dateFormatter.string(from: $0.dateString) } ?? "--"
+        let LastGlucoseDate = Date(lastGlucoseDateString) ?? Date()
+        let now = Date()
+        let diff = Int(now.timeIntervalSince1970 - LastGlucoseDate.timeIntervalSince1970)
+        let hoursDiff = diff / 3600
+        let minutesDiff = (diff - hoursDiff * 3600) / 60
+        return minutesDiff
+    }
 
-                ).font(.system(size: 12, weight: .bold))
-            }
+    func colorOfMinutesAgo(_ minutes: Int) -> Color {
+        switch minutes {
+        case 0 ... 5:
+            return .loopGreen
+        case 6 ... 9:
+            return .loopYellow
+        default:
+            return .loopRed
         }
     }
 

+ 22 - 17
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -30,16 +30,16 @@ extension Home {
             HStack(alignment: .bottom) {
                 Spacer()
                 VStack(alignment: .leading, spacing: 12) {
-                    HStack {
-                        Text("IOB").font(.caption2).foregroundColor(.secondary)
-                        Text((numberFormatter.string(from: (viewModel.suggestion?.iob ?? 0) as NSNumber) ?? "0") + " U")
-                            .font(.system(size: 12, weight: .bold))
-                    }
-                    HStack {
-                        Text("COB").font(.caption2).foregroundColor(.secondary)
-                        Text((numberFormatter.string(from: (viewModel.suggestion?.cob ?? 0) as NSNumber) ?? "0") + " g")
-                            .font(.system(size: 12, weight: .bold))
-                    }
+                    // HStack {
+                    // Text("").font(.caption2).foregroundColor(.secondary) // Was "IOB"
+                    Text((numberFormatter.string(from: (viewModel.suggestion?.iob ?? 0) as NSNumber) ?? "0") + " U")
+                        .font(.system(size: 12, weight: .bold))
+                    // }
+                    // HStack {
+                    // Text("").font(.caption2).foregroundColor(.secondary) // Was "COB"
+                    Text((numberFormatter.string(from: (viewModel.suggestion?.cob ?? 0) as NSNumber) ?? "0") + " g")
+                        .font(.system(size: 12, weight: .bold))
+                    // }
                 }
                 Spacer()
 
@@ -204,9 +204,12 @@ extension Home {
                     header
                         .frame(maxHeight: 70)
                         .padding(.top, geo.safeAreaInsets.top)
-                        .background(Color.gray.opacity(0.2))
-
+                        .background(Color.backgroundColor)
+                    //  .background(Color.gray.opacity(0.2))
+                    Divider().background(Color.gray) // Added 29/4
                     infoPanal
+                        .background(Color.secondary.opacity(0.05))
+                    // Divider().background(Color.gray) // Added 29/4
                     MainChartView(
                         glucose: $viewModel.glucose,
                         suggestion: $viewModel.suggestion,
@@ -222,14 +225,16 @@ extension Home {
                         timerDate: $viewModel.timerDate,
                         units: $viewModel.units
                     )
-                    .padding(.bottom)
+                    .background(Color.gray.opacity(0.05))
+                    // .padding(.bottom)
                     .modal(for: .dataTable, from: self)
-
+                    Divider().background(Color.gray) // Added 29/4
                     legendPanal
-
+                        .background(Color.secondary.opacity(0.05))
+                    Divider().background(Color.gray) // Added 29/4
                     ZStack {
-                        Rectangle().fill(Color.gray.opacity(0.2)).frame(height: 50 + geo.safeAreaInsets.bottom)
-
+                        Rectangle().fill(Color.backgroundColor)
+                            .frame(height: 50 + geo.safeAreaInsets.bottom)
                         HStack {
                             Button { viewModel.showModal(for: .addCarbs) }
                             label: {

+ 8 - 0
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigViewModel.swift

@@ -14,6 +14,7 @@ extension NightscoutConfig {
 
         @Published var useLocalSource = false
         @Published var localPort: Decimal = 0
+        @Published var dontShowNS = false
 
         override func subscribe() {
             url = keychain.getValue(String.self, forKey: Config.urlKey) ?? ""
@@ -21,6 +22,7 @@ extension NightscoutConfig {
             isUploadEnabled = settingsManager.settings.isUploadEnabled ?? false
             useLocalSource = settingsManager.settings.useLocalGlucoseSource ?? false
             localPort = Decimal(settingsManager.settings.localGlucosePort ?? 8080)
+            dontShowNS = settingsManager.settings.dontShowNS
 
             $isUploadEnabled
                 .removeDuplicates()
@@ -39,6 +41,12 @@ extension NightscoutConfig {
                 .sink { [weak self] port in
                     self?.settingsManager.settings.localGlucosePort = Int(port)
                 }.store(in: &lifetime)
+
+            $dontShowNS
+                .removeDuplicates()
+                .sink { [weak self] enabled in
+                    self?.settingsManager.settings.dontShowNS = enabled
+                }.store(in: &lifetime)
         }
 
         func connect() {

+ 4 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -52,6 +52,10 @@ extension NightscoutConfig {
                         DecimalTextField("", value: $viewModel.localPort, formatter: portFormater)
                     }
                 }
+
+                Section(header: Text("Show xDrip4iOS or other CGM instead of NS")) {
+                    Toggle("Don't show NS", isOn: $viewModel.dontShowNS)
+                }
             }
             .navigationBarTitle("Nightscout Config", displayMode: .automatic)
         }

+ 1 - 0
FreeAPS/Sources/Services/SettingsManager/SettingsManager.swift

@@ -38,6 +38,7 @@ final class BaseSettingsManager: SettingsManager, Injectable {
                 useAutotune: false,
                 isUploadEnabled: false,
                 useLocalGlucoseSource: false,
+                dontShowNS: false,
                 localGlucosePort: nil,
                 debugOptions: false
             )

+ 370 - 0
FreeAPS/Sources/en.lproj/Localizable.strings

@@ -0,0 +1,370 @@
+/*
+  Localizable.strings
+  FreeAPS X
+
+*/
+/* Add insulin without actually bolusing */
+"Add insulin without actually bolusing" = "Add insulin without actually bolusing";
+
+/* Amount */
+"Amount Bolus" = "Amount Bolus";
+
+/* Bolus */
+"Bolus" = "Bolus";
+
+/* Close */
+"Close" = "Close";
+
+/* Continue without bolus */
+"Continue without bolus" = "Continue without bolus";
+
+/* Enact Bolus */
+"Enact bolus" = "Enact bolus";
+
+/* Enact Bolus title */
+"Enact Bolus Title" = "Enact Bolus";
+
+/* Insulin recommended */
+"Insulin recommended" = "Insulin recommended";
+
+/* Insulin required */
+"Insulin required" = "Insulin required";
+
+/* StringRecommendation */
+"Recommendation" = "Recommendation";
+
+/* Wait please */
+"Wait please" = "Wait please";
+
+/* Agree and continue */
+"Agree and Continue" = "Agree and Continue";
+
+/* Disclaimer */
+"Disclaimer" = "Disclaimer";
+
+/* Disclaimer Description */
+"Disclaimer Description" = "FreeAPS X is in an active development state. We do not recommend to use the system for everyday control of blood glucose! Use it for testing purposes only at your own risk. We are not responsible for your decisions and actions.";
+
+/* No comment provided by engineer. */
+"%d min ago" = " %d min ago";
+
+/* Enacted at a date */
+"Enacted at %@" = "Enacted at %@";
+
+/* Home title */
+"Home" = "Home";
+
+/* Looping in progress */
+"looping" = "looping";
+
+/* Status Title : No Suggestion */
+"No suggestion" = "No suggestion";
+
+/* Replace pod */
+"Replace pod" = "Replace pod";
+
+/* Status Title : Suggest */
+"Suggested" = "Suggested";
+
+/* Suggested at a date */
+"Suggested at %@" = "Suggested at %@";
+
+/* Add Carbs */
+"Add Carbs" = "Add Carbs";
+
+/* Add Carbs Title */
+"Add Carbs Title" = "Add Carbs Title";
+
+/* Amount Carbs */
+"Amount Carbs" = "Amount Carbs";
+
+/* Grams unit */
+"grams" = "grams";
+
+/* A you sure delete preset? */
+"A you sure?" = "A you sure?";
+
+/* Bottom target temp */
+"Bottom target" = "Bottom target";
+
+/* Cancel preset name */
+"Cancel" = "Cancel";
+
+/* Cancel temp target */
+"Cancel temp target" = "Cancel temp target";
+
+/* Custom target temp */
+"Custom" = "Custom";
+
+/* Date */
+"Date" = "Date";
+
+/* Delete */
+"Delete" = "Delete";
+
+/* Delete preset %@ */
+"Delete preset %@" = "Delete preset %@";
+
+/* Duration target temp */
+"Duration" = "Duration";
+
+/* Enact temp target */
+"Enact Temp Target" = "Enact Temp Target";
+
+/* Enter preset name */
+"Enter preset name" = "Enter preset name";
+
+/* minutes target temp */
+"minutes" = "minutes";
+
+/* Presets title */
+"Presets" = "Presets";
+
+/* Save preset name */
+"Save" = "Save";
+
+/* Save as preset */
+"Save as preset" = "Save as preset";
+
+/* Top target temp */
+"Top target" = "Top target";
+
+/* Autotune Title */
+"Autotune" = "Autotune";
+
+/* Basal profile */
+"Basal profile" = "Basal profile";
+
+/* Carb ratio */
+"Carb ratio" = "Carb ratio";
+
+/* Delete autotune data */
+"Delete autotune data" = "Delete autotune data";
+
+/* Run now */
+"Run now" = "Run now";
+
+/* Sensitivity */
+"Sensitivity" = "Sensitivity";
+
+/* Use Autotune */
+"Use Autotune" = "use Autotune";
+
+/* Add profile basal */
+"Add" = "Add";
+
+/* Basal Profile */
+"Basal Profile" = "Basal Profile";
+
+/* Rate basal profile */
+"Rate" = "Rate";
+
+/* Save on Pump */
+"Save on Pump" = "Save on Pump";
+
+/* Saving... */
+"Saving..." = "Saving...";
+
+/* Schedule */
+"Schedule" = "Schedule";
+
+/* starts at */
+"starts at" = "starts at";
+
+/* Time basal profile */
+"Time" = "Time";
+
+/* Calculated Ratio */
+"Calculated Ratio" = "Calculated Ratio";
+
+/* Carb Ratios Title */
+"Carb Ratios" = "Carb Ratios";
+
+/* Ratio Carb */
+"Ratio" = "Ratio";
+
+/* Autosens */
+"Autosens" = "Autosens";
+
+/* Calculated Sensitivity */
+"Calculated Sensitivity" = "Calculated Sensitivity";
+
+/* Insulin Sensitivities */
+"Insulin Sensitivities" = "Insulin Sensitivities";
+
+/* Sensitivity Ratio */
+"Sensitivity Ratio" = "Sensitivity Ratio";
+
+/* Dismiss */
+"Dismiss" = "Dismiss";
+
+/* Important message */
+"Important message" = "Important message";
+
+/* Amount */
+"Amount" = "Amount";
+
+/* Cancel Temp Basal */
+"Cancel Temp Basal" = "Cancel Temp Basal";
+
+/* Duration */
+"Duration" = "Duration";
+
+/* Enact temp Basal*/
+"Enact" = "Enact";
+
+/* Manual Temp Basal */
+"Manual Temp Basal" = "Manual Temp Basal";
+
+/* Allow uploads */
+"Allow uploads" = "Allow uploads";
+
+/* API secret */
+"API secret" = "API secret";
+
+/* Connect */
+"Connect" = "Connect";
+
+/* Connected! */
+"Connected!" = "Connected!";
+
+/* Connecting... */
+"Connecting..." = "Connecting...";
+
+/* Invalid URL */
+"Invalid URL" = "Invalid URL";
+
+/* Local glucose source */
+"Local glucose source" = "Local glucose source";
+
+/* Nightscout Config */
+"Nightscout Config" = "Nightscout Config";
+
+/* Port */
+"Port" = "Port";
+
+/* URL */
+"URL" = "URL";
+
+/* Use local glucose server */
+"Use local glucose server" = "Use local glucose server";
+
+/* Edit settings json */
+"Edit settings json" = "Edit settings json";
+
+/* Glucose units */
+"Glucose units" = "Glucose units";
+
+/* Preferences */
+"Preferences" = "Preferences";
+
+/* Recommended Insulin Fraction */
+"Recommended Insulin Fraction" = "Recommended Insulin Fraction";
+
+/* Remote control */
+"Remote control" = "Remote control";
+
+/* Add Medtronic */
+"Add Medtronic" = "Add Medtronic";
+
+/* Add Omnipod */
+"Add Omnipod" = "Add Omnipod";
+
+/* Add Simulator */
+"Add Simulator" = "Add Simulator";
+
+/* Model */
+"Model" = "Model";
+
+/* Pump config */
+"Pump config" = "Pump config";
+
+/* Delivery limits */
+"Delivery limits" = "Delivery limits";
+
+/* Duration of Insulin Action */
+"Duration of Insulin Action" = "Duration of Insulin Action";
+
+/* hours */
+"hours" = "hours";
+
+/* Max Basal */
+"Max Basal" = "Max Basal";
+
+/* Max Bolus */
+"Max Bolus" = "Max Bolus";
+
+/* Pump Settings */
+"Pump Settings" = "Pump Settings";
+
+/* Save on Pump */
+"Save on Pump" = "Save on Pump";
+
+/* U/hr */
+"U/hr" = "U/hr";
+
+/* RequestPermissions */
+"RequestPermissions" = "RequestPermissions";
+
+/* RequestPermissions screen */
+"RequestPermissions screen" = "RequestPermissions screen";
+
+/* Autotune */
+"Autotune Conf" = "Autotune";
+
+/* Basal Profile */
+"Basal Profile Conf" = "Basal Profile";
+
+/* Carb Ratios */
+"Carb Ratios Conf" = "Carb Ratios";
+
+/* Closed loop */
+"Closed loop" = "Closed loop";
+
+/* Configuration */
+"Configuration" = "Configuration";
+
+/* Devices */
+"Devices" = "Devices";
+
+/* Insulin Sensitivities */
+"Insulin Sensitivities Conf" = "Insulin Sensitivities";
+
+/* Preferences */
+"Preferences Conf" = "Preferences";
+
+/* Pump */
+"Pump" = "Pump";
+
+/* Pump Settings */
+"Pump Settings Conf" = "Pump Settings";
+
+/* Services */
+"Services" = "Services";
+
+/* Settings */
+"Settings" = "Settings";
+
+/* Share logs */
+"Share logs" = "Share logs";
+
+/* Target Ranges */
+"Target Ranges Conf" = "Target Ranges";
+
+/* High target */
+"High target" = "High target";
+
+/* Low target */
+"Low target" = "Low target";
+
+/* Target Ranges */
+"Target Ranges" = "Target Ranges";
+
+/* Bolusing.. */
+"Bolusing" = "Bolusing";
+
+/* Pump suspended */
+"Pump suspended" = "Pump suspended";
+
+/* middleware */
+"Middleware" = "Middleware";

+ 370 - 0
FreeAPS/Sources/sv.lproj/Localizable.strings

@@ -0,0 +1,370 @@
+/*
+  Localizable.strings
+  FreeAPS X
+
+*/
+/* Add insulin without actually bolusing */
+"Add insulin without actually bolusing" = "Lägg till insulin utan att ge bolus";
+
+/* Amount */
+"Amount Bolus" = "Mängd insulin";
+
+/* Bolus */
+"Bolus" = "Bolus";
+
+/* Close */
+"Close" = "Stäng";
+
+/* Continue without bolus */
+"Continue without bolus" = "Fortsätt utan bolus";
+
+/* Enact Bolus */
+"Enact bolus" = "Ge bolus";
+
+/* Enact Bolus title */
+"Enact Bolus" = "Ge bolus";
+
+/* Insulin recommended */
+"Insulin recommended" = "Rekommenderad insulindos";
+
+/* Insulin required */
+"Insulin required" = "Insulin som krävs";
+
+/* StringRecommendation */
+"Recommendation" = "Rekommendation";
+
+/* Wait please */
+"Wait please" = "Vänta...";
+
+/* Agree and continue */
+"Agree and Continue" = "Godkänn och fortsätt";
+
+/* Disclaimer */
+"Disclaimer" = "Varning";
+
+/* Disclaimer Description */
+"Disclaimer Description" = "FreeAPS X är under utveckling. Vi rekommederar inte att använda denna app för behandling av ditt blodsocker i detta tidiga skede. Använd för test och på egen risk. Vi tar inget ansvar för dina beslut eller handlingar.";
+
+/* No comment provided by engineer. */
+"%d min ago" = "%d min sedan";
+
+/* Enacted at a date */
+"Enacted at %@" = "Gavs kl %@";
+
+/* Home title */
+"Home" = "Hem";
+
+/* Looping in progress */
+"looping" = "loopar";
+
+/* Status Title : No Suggestion */
+"No suggestion" = "Inget förslag";
+
+/* Replace pod */
+"Replace pod" = "Byt podd";
+
+/* Status Title : Suggest */
+"Suggested" = "Föreslagen";
+
+/* Suggested at a date */
+"Suggested at %@" = "Föreslagen kl %@";
+
+/* Add Carbs */
+"Add Carbs" = "Lägg till kolhydrater";
+
+/* Add Carbs Title */
+"Add Carbs Title" = "Lägg till kolhydrater";
+
+/* Amount Carbs */
+"Amount Carbs" = "Mängd kolhydrater";
+
+/* Grams unit */
+"grams" = "gram";
+
+/* A you sure delete preset? */
+"A you sure?" = "Är du säker?";
+
+/* Bottom target temp */
+"Bottom target" = "Nedre mål";
+
+/* Cancel preset name */
+"Cancel" = "Avbryt";
+
+/* Cancel temp target */
+"Cancel temp target" = "Avbryt temporär basal";
+
+/* Custom target temp */
+"Custom" = "Anpassad";
+
+/* Date */
+"Date" = "Datum";
+
+/* Delete */
+"Delete" = "Radera";
+
+/* Delete preset %@ */
+"Delete preset %@" = "Ta bort förval %@";
+
+/* Duration target temp */
+"Duration" = "Duration";
+
+/* Enact temp target */
+"Enact temp target" = "Starta tillfälligt mål";
+
+/* Enter preset name */
+"Enter preset name" = "Ange namn för förval";
+
+/* minutes target temp */
+"minutes" = "minuter";
+
+/* Presets title */
+"Presets" = "Förinställningar";
+
+/* Save preset name */
+"Save" = "Spara";
+
+/* Save as preset */
+"Save as preset" = "Spara som förval";
+
+/* Top target temp */
+"Top target" = "Övre mål";
+
+/* Autotune Title */
+"Autotune" = "Autotune";
+
+/* Basal profile */
+"Basal profile" = "Basalinställningar";
+
+/* Carb ratio */
+"Carb ratio" = "Insulinkvoter";
+
+/* Delete autotune data */
+"Delete autotune data" = "Rensa autotune-data";
+
+/* Run now */
+"Run now" = "Kör nu";
+
+/* Sensitivity */
+"Sensitivity" = "Insulinkänslighet";
+
+/* Use Autotune */
+"Use Autotune" = "använd Autotune";
+
+/* Add profile basal */
+"Add" = "Lägg till";
+
+/* Basal Profile */
+"Basal Profile" = "Basalinställningar";
+
+/* Rate basal profile */
+"Rate" = "Rate";
+
+/* Save on Pump */
+"Save on Pump" = "Spara på pump";
+
+/* Saving... */
+"Saving..." = "Sparar...";
+
+/* Schedule */
+"Schedule" = "Schema";
+
+/* starts at */
+"starts at" = "startar kl";
+
+/* Time basal profile */
+"Time" = "Tid";
+
+/* Calculated Ratio */
+"Calculated Ratio" = "Beräknad kvot";
+
+/* Carb Ratios Title */
+"Carb Ratios" = "Insulinkvoter";
+
+/* Ratio Carb */
+"Ratio" = "Kvot";
+
+/* Autosens */
+"Autosens" = "Autosens";
+
+/* Calculated Sensitivity */
+"Calculated Sensitivity" = "Beräknad känslighet";
+
+/* Insulin Sensitivities */
+"Insulin Sensitivities" = "Insulinkänslighet";
+
+/* Sensitivity Ratio */
+"Sensitivity Ratio" = "Insulinkänslighetskvot";
+
+/* Dismiss */
+"Dismiss" = "Avfärda";
+
+/* Important message */
+"Important message" = "Viktigt meddelande";
+
+/* Amount */
+"Amount" = "Mängd";
+
+/* Cancel Temp Basal */
+"Cancel Temp Basal" = "Avbryt temporär basal";
+
+/* Duration */
+"Duration" = "Duration";
+
+/* Enact temp Basal*/
+"Enact" = "Ge";
+
+/* Manual Temp Basal */
+"Manual Temp Basal" = "Manuell temporär basal";
+
+/* Allow uploads */
+"Allow uploads" = "Tillåt uppladdning";
+
+/* API secret */
+"API secret" = "API-hemlighet";
+
+/* Connect */
+"Connect" = "Anslut";
+
+/* Connected! */
+"Connected!" = "Ansluten!";
+
+/* Connecting... */
+"Connecting..." = "Ansluter...";
+
+/* Invalid URL */
+"Invalid URL" = "Ogiltig URL";
+
+/* Local glucose source */
+"Local glucose source" = "Lokal glukoskälla";
+
+/* Nightscout Config */
+"Nightscout Config" = "Ställ in Nightscout";
+
+/* Port */
+"Port" = "Port";
+
+/* URL */
+"URL" = "URL";
+
+/* Use local glucose server */
+"Use local glucose server" = "Använd lokal server som glukoskälla";
+
+/* Edit settings json */
+"Edit settings json" = "Ändra inställningar för json";
+
+/* Glucose units */
+"Glucose units" = "Glukosenheter";
+
+/* Preferences */
+"Preferences" = "Inställningar";
+
+/* Recommended Insulin Fraction */
+"Recommended Insulin Fraction" = "Kvot av rekommenderad mängd insulin";
+
+/* Remote control */
+"Remote control" = "Fjärrstyrning";
+
+/* Add Medtronic */
+"Add Medtronic" = "Lägg till Medtronic";
+
+/* Add Omnipod */
+"Add Omnipod" = "Lägg till Omnipod";
+
+/* Add Simulator */
+"Add Simulator" = "Lägg till simulator";
+
+/* Model */
+"Model" = "Modell";
+
+/* Pump config */
+"Pump config" = "Pumpkonfiguration";
+
+/* Delivery limits */
+"Delivery limits" = "Maxdoser";
+
+/* Duration of Insulin Action */
+"Duration of Insulin Action" = "Insulinets verkningstid";
+
+/* hours */
+"hours" = "timmar";
+
+/* Max Basal */
+"Max Basal" = "Max basal";
+
+/* Max Bolus */
+"Max Bolus" = "Max bolus";
+
+/* Pump Settings */
+"Pump Settings" = "Pumpinställningar";
+
+/* Save on Pump */
+"Save on Pump" = "Spara på pump";
+
+/* U/hr */
+"U/hr" = "E/h";
+
+/* RequestPermissions */
+"RequestPermissions" = "RequestPermissions";
+
+/* RequestPermissions screen */
+"RequestPermissions screen" = "RequestPermissions screen";
+
+/* Autotune */
+"Autotune Conf" = "Autotune";
+
+/* Basal Profile */
+"Basal Profile Conf" = "Basalinställningar";
+
+/* Carb Ratios */
+"Carb Ratios Conf" = "Insulinkvoter";
+
+/* Closed loop */
+"Closed loop" = "Closed loop";
+
+/* Configuration */
+"Configuration" = "Konfiguration";
+
+/* Devices */
+"Devices" = "Enheter";
+
+/* Insulin Sensitivities */
+"Insulin Sensitivities Conf" = "Insulinkänslighet";
+
+/* Preferences */
+"Preferences Conf" = "Inställningar";
+
+/* Pump */
+"Pump" = "Pump";
+
+/* Pump Settings */
+"Pump Settings Conf" = "Pumpinställningar";
+
+/* Services */
+"Services" = "Tjänster";
+
+/* Settings */
+"Settings" = "Inställningar";
+
+/* Share logs */
+"Share logs" = "Dela loggar";
+
+/* Target Ranges */
+"Target Ranges Conf" = "Målområde";
+
+/* High target */
+"High target" = "Övre målvärde";
+
+/* Low target */
+"Low target" = "Undre målvärde";
+
+/* Target Ranges */
+"Target Ranges" = "Målområden";
+
+/* Bolusing.. */
+"Bolusing" = "Ger bolus";
+
+/* Pump suspended */
+"Pump suspended" = "Pump pausad";
+
+/* middleware */
+"Middleware" = "Middleware";