Quellcode durchsuchen

OrangeLink patch (batter level info and other features).

(cherry picked from commit 48270e046d3f6d90eb69362eb074b0f830996ec2)
Jon Mårtensson vor 5 Jahren
Ursprung
Commit
253314f787

+ 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")
 }

+ 367 - 3
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,17 @@ 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 +124,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 +147,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 +244,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 +267,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 +329,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 +346,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 +379,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 +449,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 +485,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 +562,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 +583,10 @@ public class RileyLinkDeviceTableViewController: UITableViewController {
             }
         case .commands:
             return device.peripheralState == .connected
+        case .configureCommand:
+            return device.peripheralState == .connected
+        case .alert:
+            return true
         }
     }
 
@@ -342,6 +609,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 +758,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)