Jelajahi Sumber

Merge pull request #217 from loopandlearn/iaps

iAPS support for LoopFollow
Jonas Björkert 2 tahun lalu
induk
melakukan
ffb1532648

+ 10 - 14
LoopFollow/Controllers/Graphs.swift

@@ -548,14 +548,13 @@ extension MainViewController {
         BGChart.moveViewToAnimated(xValue: dateTimeUtils.getNowTimeIntervalUTC() - (BGChart.visibleXRange * 0.7), yValue: 0.0, axis: .right, duration: 1, easingOption: .easeInBack)
     }
     
-    func updatePredictionGraph() {
+    func updatePredictionGraph(color: UIColor? = nil) {
         let dataIndex = 1
         var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
         var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
         mainChart.clear()
         smallChart.clear()
-        if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Graph: print prediction") }
-
+        
         var colors = [NSUIColor]()
         let maxBGOffset: Float = 20
         for i in 0..<predictionData.count {
@@ -563,23 +562,21 @@ extension MainViewController {
             if Float(predictionVal) > topBG - maxBGOffset {
                 topBG = Float(predictionVal) + maxBGOffset
             }
-
+            
             if i == 0 {
                 if UserDefaultsRepository.showDots.value {
-                    colors.append(NSUIColor.systemPurple.withAlphaComponent(0.0))
+                    colors.append((color ?? NSUIColor.systemPurple).withAlphaComponent(0.0))
                 } else {
-                    colors.append(NSUIColor.systemPurple.withAlphaComponent(1.0))
+                    colors.append((color ?? NSUIColor.systemPurple).withAlphaComponent(1.0))
                 }
-                
             } else if predictionVal > 400 {
-                predictionVal = 400
-                colors.append(NSUIColor.systemYellow)
+                colors.append(color ?? NSUIColor.systemYellow)
             } else if predictionVal < 0 {
-                predictionVal = 0
-                colors.append(NSUIColor.systemRed)
+                colors.append(color ?? NSUIColor.systemRed)
             } else {
-                colors.append(NSUIColor.systemPurple)
+                colors.append(color ?? NSUIColor.systemPurple)
             }
+            
             let value = ChartDataEntry(x: predictionData[i].date, y: predictionVal, data: formatPillText(line1: bgUnits.toDisplayUnits(String(predictionData[i].sgv)), time: predictionData[i].date))
             mainChart.addEntry(value)
             smallChart.addEntry(value)
@@ -590,8 +587,7 @@ extension MainViewController {
         mainChart.colors.removeAll()
         mainChart.circleColors.removeAll()
         if colors.count > 0 {
-            if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Graph: prediction colors") }
-            for i in 0..<colors.count{
+            for i in 0..<colors.count {
                 mainChart.addColor(colors[i])
                 mainChart.circleColors.append(colors[i])
                 smallChart.addColor(colors[i])

+ 164 - 16
LoopFollow/Controllers/NightScout.swift

@@ -636,12 +636,139 @@ extension MainViewController {
                 latestLoopTime = lastLoopTime
             } // end lastLoopTime
         } // end lastLoop Record
+
+        if let lastLoopRecord = lastDeviceStatus?["openaps"] as! [String : AnyObject]? {
+            if let lastLoopTime = formatter.date(from: (lastDeviceStatus?["created_at"] as! String))?.timeIntervalSince1970  {
+                UserDefaultsRepository.alertLastLoopTime.value = lastLoopTime
+                if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "lastLoopTime: " + String(lastLoopTime)) }
+                if let failure = lastLoopRecord["failureReason"] {
+                    LoopStatusLabel.text = "X"
+                    latestLoopStatusString = "X"
+                    if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Loop Failure: X") }
+                } else {
+                    var wasEnacted = false
+                    if let enacted = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                        if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Loop: Was Enacted") }
+                        wasEnacted = true
+                        if let lastTempBasal = enacted["rate"] as? Double {
+                            
+                        }
+                    }
+
+                    if let iobdata = lastLoopRecord["iob"] as? [String:AnyObject] {
+                        tableData[0].value = String(format:"%.2f", (iobdata["iob"] as! Double))
+                        latestIOB = String(format:"%.2f", (iobdata["iob"] as! Double))
+                    }
+                    if let cobdata = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                        tableData[1].value = String(format:"%.1f", cobdata["COB"] as! Double)
+                        latestCOB = String(format:"%.1f", cobdata["COB"] as! Double)
+                    }
+                    if let recbolusdata = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                        tableData[8].value = String(format:"%.2fU", recbolusdata["insulinReq"] as! Double)
+                    }
+                    if let autosensdata = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                        let sens = autosensdata["sensitivityRatio"] as! Double * 100.0
+                        tableData[11].value = String(format:"%.0f", sens) + "%"
+                    }
+                    
+                    //Picks COB prediction if available, else UAM, else IOB, else ZT
+                    //Ideal is to predict all 4 in Loop Follow but this is a quick start
+                    var graphtype = ""
+                    var predictioncolor = UIColor.systemGray
+                    PredictionLabel.textColor = predictioncolor
+                    if let enactdata = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                        if let predbgdata = enactdata["predBGs"] as? [String:AnyObject] {
+                            if predbgdata["COB"] != nil {
+                                graphtype="COB"
+                                predictioncolor = UIColor.systemYellow
+                                PredictionLabel.textColor = predictioncolor
+                            }
+                            else if predbgdata["UAM"] != nil {
+                                graphtype="UAM"
+                                predictioncolor = UIColor.systemOrange
+                                PredictionLabel.textColor = predictioncolor
+                            }
+                            else if predbgdata["IOB"] != nil {
+                                graphtype="IOB"
+                                predictioncolor = UIColor.systemBlue
+                                PredictionLabel.textColor = predictioncolor
+                            }
+                            else {
+                                graphtype="ZT"
+                                predictioncolor = UIColor.systemGreen
+                                PredictionLabel.textColor = predictioncolor
+                            }
+                            
+                            let graphdata = predbgdata[graphtype] as! [Double]
+                            
+                            if let eventualdata = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                                if let eventualBGValue = eventualdata["eventualBG"] as? NSNumber {
+                                    let eventualBGStringValue = String(describing: eventualBGValue)
+                                    PredictionLabel.text = bgUnits.toDisplayUnits(eventualBGStringValue)
+                                }
+                            }
+                            
+                            if UserDefaultsRepository.downloadPrediction.value && latestLoopTime < lastLoopTime {
+                                predictionData.removeAll()
+                                var predictionTime = lastLoopTime
+                                let toLoad = Int(UserDefaultsRepository.predictionToLoad.value * 12)
+                                var i = 0
+                                while i <= toLoad {
+                                    if i < graphdata.count {
+                                        let prediction = ShareGlucoseData(sgv: Int(round(graphdata[i])), date: predictionTime, direction: "flat")
+                                        predictionData.append(prediction)
+                                        predictionTime += 300
+                                    }
+                                    i += 1
+                                }
+                                
+                            }
+                            
+                            let predMin = graphdata.min()
+                            let predMax = graphdata.max()
+                            tableData[9].value = bgUnits.toDisplayUnits(String(predMin!)) + "/" + bgUnits.toDisplayUnits(String(predMax!))
+                            
+                            updatePredictionGraph(color: predictioncolor)
+                        }
+                    }
+                    
+                    if let loopStatus = lastLoopRecord["recommendedTempBasal"] as? [String:AnyObject] {
+                        if let tempBasalTime = formatter.date(from: (loopStatus["timestamp"] as! String))?.timeIntervalSince1970 {
+                            var lastBGTime = lastLoopTime
+                            if bgData.count > 0 {
+                                lastBGTime = bgData[bgData.count - 1].date
+                            }
+                            if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "tempBasalTime: " + String(tempBasalTime)) }
+                            if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "lastBGTime: " + String(lastBGTime)) }
+                            if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "wasEnacted: " + String(wasEnacted)) }
+                            if tempBasalTime > lastBGTime && !wasEnacted {
+                                LoopStatusLabel.text = "⏀"
+                                latestLoopStatusString = "⏀"
+                                if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Open Loop: recommended temp. temp time > bg time, was not enacted") }
+                            } else {
+                                LoopStatusLabel.text = "↻"
+                                latestLoopStatusString = "↻"
+                                if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Looping: recommended temp, but temp time is < bg time and/or was enacted") }
+                            }
+                        }
+                    } else {
+                        LoopStatusLabel.text = "↻"
+                        latestLoopStatusString = "↻"
+                        if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Looping: no recommended temp") }
+                    }
+                    
+                }
+                if ((TimeInterval(Date().timeIntervalSince1970) - lastLoopTime) / 60) > 15 {
+                    LoopStatusLabel.text = "⚠"
+                    latestLoopStatusString = "⚠"
+                }
+                latestLoopTime = lastLoopTime
+            }
+        }
         
         var oText = "" as String
         currentOverride = 1.0
         if let lastOverride = lastDeviceStatus?["override"] as! [String : AnyObject]? {
-            if let lastOverrideTime = formatter.date(from: (lastOverride["timestamp"] as! String))?.timeIntervalSince1970  {
-            }
             if lastOverride["active"] as! Bool {
                 
                 let lastCorrection  = lastOverride["currentCorrectionRange"] as! [String: AnyObject]
@@ -720,9 +847,9 @@ extension MainViewController {
         dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
         let utcDateString = dateFormatter.string(from: date)
 
-        var urlString = urlUser + "/api/v1/treatments.json?count=1000&find[eventType]=Carb+Correction&find[timestamp][$gte]=" + utcDateString
+        var urlString = urlUser + "/api/v1/treatments.json?count=1000&find[eventType]=Carb+Correction&find[created_at][$gte]=" + utcDateString
         if token != "" {
-            urlString = urlUser + "/api/v1/treatments.json?token=" + token + "&count=1000&find[eventType]=Carb+Correction&find[timestamp][$gte]=" + utcDateString
+            urlString = urlUser + "/api/v1/treatments.json?token=" + token + "&count=1000&find[eventType]=Carb+Correction&find[created_at][$gte]=" + utcDateString
         }
 
         guard let urlData = URL(string: urlString) else {
@@ -961,18 +1088,20 @@ extension MainViewController {
             return
         }
         if jsonDeviceStatus[keyPath: "message"] != nil { return }
-        let basal = try jsonDeviceStatus[keyPath: "store.Default.basal"] as! NSArray
-        basalProfile.removeAll()
-        for i in 0..<basal.count {
-            let dict = basal[i] as! Dictionary<String, Any>
-            do {
-                let thisValue = try dict[keyPath: "value"] as! Double
-                let thisTime = dict[keyPath: "time"] as! String
-                let thisTimeAsSeconds = dict[keyPath: "timeAsSeconds"] as! Double
-                let entry = basalProfileStruct(value: thisValue, time: thisTime, timeAsSeconds: thisTimeAsSeconds)
-                basalProfile.append(entry)
-            } catch {
-               if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "ERROR: profile wrapped in quotes") }
+        
+        if let basal = jsonDeviceStatus[keyPath: "store.default.basal"] as? NSArray ?? jsonDeviceStatus[keyPath: "store.Default.basal"] as? NSArray {
+            basalProfile.removeAll()
+            for i in 0..<basal.count {
+                let dict = basal[i] as! Dictionary<String, Any>
+                do {
+                    let thisValue = try dict[keyPath: "value"] as! Double
+                    let thisTime = dict[keyPath: "time"] as! String
+                    let thisTimeAsSeconds = dict[keyPath: "timeAsSeconds"] as! Double
+                    let entry = basalProfileStruct(value: thisValue, time: thisTime, timeAsSeconds: thisTimeAsSeconds)
+                    basalProfile.append(entry)
+                } catch {
+                    if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "ERROR: profile wrapped in quotes") }
+                }
             }
         }
         
@@ -1123,6 +1252,8 @@ extension MainViewController {
                     tempBasal.append(entry!)
                 case "Correction Bolus":
                     bolus.append(entry!)
+                case "Bolus":
+                    bolus.append(entry!)
                 case "Meal Bolus":
                     carbs.append(entry!)
                     bolus.append(entry!)
@@ -1130,6 +1261,8 @@ extension MainViewController {
                     carbs.append(entry!)
                 case "Temporary Override":
                     temporaryOverride.append(entry!)
+                case "Temporary Target":
+                    temporaryOverride.append(entry!)
                 case "Note":
                     note.append(entry!)
                     print("Note: \(String(describing: entry))")
@@ -1855,6 +1988,21 @@ extension MainViewController {
                     range.append(high)
                 }
                 
+            } else {
+                let low = currentEntry?["targetBottom"] as? Int
+                let high = currentEntry?["targetTop"] as? Int
+                
+                if (low == nil && high != nil) || (low != nil && high == nil) {
+                    continue
+                }
+                
+                if let l = low {
+                    range.append(l)
+                }
+                
+                if let h = high {
+                    range.append(h)
+                }
             }
                         
             let endDate = dateTimeStamp + (duration)

+ 10 - 3
LoopFollow/ViewControllers/MainViewController.swift

@@ -163,7 +163,8 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         UserDefaultsRepository.infoNames.value.append("Rec. Bolus")
         UserDefaultsRepository.infoNames.value.append("Pred.")
         UserDefaultsRepository.infoNames.value.append("Carbs today")
-
+        UserDefaultsRepository.infoNames.value.append("Autosens")
+        
         // Reset deprecated settings
         UserDefaultsRepository.debugLog.value = false;
         UserDefaultsRepository.alwaysDownloadAllBG.value = true;
@@ -172,7 +173,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         //infoTable.layer.borderColor = UIColor.darkGray.cgColor
         //infoTable.layer.borderWidth = 1.0
         //infoTable.layer.cornerRadius = 6
-        infoTable.rowHeight = 24
+        infoTable.rowHeight = 21
         infoTable.dataSource = self
         infoTable.tableFooterView = UIView(frame: .zero) // get rid of extra rows
         infoTable.bounces = false
@@ -345,10 +346,16 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     }
     
     private func createDerivedData() {
+        let currentCount = UserDefaultsRepository.infoSort.value.count
+        if currentCount < self.tableData.count {
+            for i in currentCount..<self.tableData.count {
+                UserDefaultsRepository.infoSort.value.append(i)
+            }
+        }
         
         self.derivedTableData = []
         while UserDefaultsRepository.infoVisible.value.count < self.tableData.count {
-            UserDefaultsRepository.infoVisible.value.append(true)
+            UserDefaultsRepository.infoVisible.value.append(false)
         }
         for i in 0..<self.tableData.count {
             if(UserDefaultsRepository.infoVisible.value[UserDefaultsRepository.infoSort.value[i]]) {

+ 1 - 1
LoopFollow/ViewControllers/SettingsViewController.swift

@@ -122,7 +122,7 @@ class SettingsViewController: FormViewController {
            row.hidden = "$showNS == false"
        }
        <<< SwitchRow("loopUser"){ row in
-           row.title = "Download Loop/FreeAPS Data"
+           row.title = "Download Loop/iAPS Data"
            row.tag = "loopUser"
            row.value = UserDefaultsRepository.loopUser.value
            row.hidden = "$showNS == false"

+ 4 - 3
LoopFollow/repository/UserDefaults.swift

@@ -30,9 +30,10 @@ class UserDefaultsRepository {
         "CAGE",
         "Rec. Bolus",
         "Pred.",
-        "Carbs today"])
-    static let infoSort = UserDefaultsValue<[Int]>(key: "infoSort", default: [0,1,2,3,4,5,6,7,8,9,10])
-    static let infoVisible = UserDefaultsValue<[Bool]>(key: "infoVisible", default: [true,true,true,true,true,true,true,true,true,true,true])
+        "Carbs today",
+        "Autosens"])
+    static let infoSort = UserDefaultsValue<[Int]>(key: "infoSort", default: [0,1,2,3,4,5,6,7,8,9,10,11])
+    static let infoVisible = UserDefaultsValue<[Bool]>(key: "infoVisible", default: [true,true,true,true,true,true,true,true,true,true,true,false])
     static let hideInfoTable = UserDefaultsValue<Bool>(key: "hideInfoTable", default: false)
     
     // Nightscout Settings