Przeglądaj źródła

First version of iaps predictions

Jonas Björkert 2 lat temu
rodzic
commit
7b7022ce05

+ 4 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -21,6 +21,7 @@
 		DD493AE52ACF2383009A6922 /* Treatments.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE42ACF2383009A6922 /* Treatments.swift */; };
 		DD493AE72ACF23CF009A6922 /* DeviceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */; };
 		DD493AE92ACF2445009A6922 /* BGData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE82ACF2445009A6922 /* BGData.swift */; };
+		DD6A935E2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */; };
 		DD7E19842ACDA50C00DBD158 /* Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19832ACDA50C00DBD158 /* Overrides.swift */; };
 		DD7E19862ACDA59700DBD158 /* BGCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19852ACDA59700DBD158 /* BGCheck.swift */; };
 		DD7E19882ACDA5DA00DBD158 /* Notes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19872ACDA5DA00DBD158 /* Notes.swift */; };
@@ -211,6 +212,7 @@
 		DD493AE42ACF2383009A6922 /* Treatments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Treatments.swift; sourceTree = "<group>"; };
 		DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatus.swift; sourceTree = "<group>"; };
 		DD493AE82ACF2445009A6922 /* BGData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGData.swift; sourceTree = "<group>"; };
+		DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusOpenAPS.swift; sourceTree = "<group>"; };
 		DD7E19832ACDA50C00DBD158 /* Overrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overrides.swift; sourceTree = "<group>"; };
 		DD7E19852ACDA59700DBD158 /* BGCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGCheck.swift; sourceTree = "<group>"; };
 		DD7E19872ACDA5DA00DBD158 /* Notes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notes.swift; sourceTree = "<group>"; };
@@ -450,6 +452,7 @@
 				DD493AE42ACF2383009A6922 /* Treatments.swift */,
 				DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */,
 				DD493AE82ACF2445009A6922 /* BGData.swift */,
+				DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */,
 			);
 			path = Nightscout;
 			sourceTree = "<group>";
@@ -1013,6 +1016,7 @@
 				DDF9676E2AD08C6E00C5EB95 /* SiteChange.swift in Sources */,
 				DDCF979C24C14EFB002C9752 /* AdvancedSettingsViewController.swift in Sources */,
 				FC97881C2485969B00A7906C /* MainViewController.swift in Sources */,
+				DD6A935E2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift in Sources */,
 				DD493AD52ACF2109009A6922 /* ResumePump.swift in Sources */,
 				DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */,
 				FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */,

+ 221 - 2
LoopFollow/Controllers/Graphs.swift

@@ -254,6 +254,102 @@ extension MainViewController {
         lineNote.axisDependency = YAxis.AxisDependency.right
         lineNote.valueFormatter = ChartYDataValueFormatter()
         lineNote.drawValuesEnabled = false
+
+        // Setup COB Prediction line details
+        let COBpredictionChartEntry = [ChartDataEntry]()
+        let COBlinePrediction = LineChartDataSet(entries:COBpredictionChartEntry, label: "")
+        COBlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
+        COBlinePrediction.circleColors = [NSUIColor.systemPurple]
+        COBlinePrediction.colors = [NSUIColor.systemPurple]
+        COBlinePrediction.drawCircleHoleEnabled = false
+        COBlinePrediction.axisDependency = YAxis.AxisDependency.right
+        COBlinePrediction.highlightEnabled = true
+        COBlinePrediction.drawValuesEnabled = false
+        
+        if UserDefaultsRepository.showLines.value {
+            COBlinePrediction.lineWidth = 2
+        } else {
+            COBlinePrediction.lineWidth = 0
+        }
+        if UserDefaultsRepository.showDots.value {
+            COBlinePrediction.drawCirclesEnabled = true
+        } else {
+            COBlinePrediction.drawCirclesEnabled = false
+        }
+        COBlinePrediction.setDrawHighlightIndicators(false)
+        COBlinePrediction.valueFont.withSize(50)
+        
+        // Setup IOB Prediction line details
+        let IOBpredictionChartEntry = [ChartDataEntry]()
+        let IOBlinePrediction = LineChartDataSet(entries:IOBpredictionChartEntry, label: "")
+        IOBlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
+        IOBlinePrediction.circleColors = [NSUIColor.systemPurple]
+        IOBlinePrediction.colors = [NSUIColor.systemPurple]
+        IOBlinePrediction.drawCircleHoleEnabled = false
+        IOBlinePrediction.axisDependency = YAxis.AxisDependency.right
+        IOBlinePrediction.highlightEnabled = true
+        IOBlinePrediction.drawValuesEnabled = false
+        
+        if UserDefaultsRepository.showLines.value {
+            IOBlinePrediction.lineWidth = 2
+        } else {
+            IOBlinePrediction.lineWidth = 0
+        }
+        if UserDefaultsRepository.showDots.value {
+            IOBlinePrediction.drawCirclesEnabled = true
+        } else {
+            IOBlinePrediction.drawCirclesEnabled = false
+        }
+        IOBlinePrediction.setDrawHighlightIndicators(false)
+        IOBlinePrediction.valueFont.withSize(50)
+        
+        // Setup UAM Prediction line details
+        let UAMpredictionChartEntry = [ChartDataEntry]()
+        let UAMlinePrediction = LineChartDataSet(entries:UAMpredictionChartEntry, label: "")
+        UAMlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
+        UAMlinePrediction.circleColors = [NSUIColor.systemPurple]
+        UAMlinePrediction.colors = [NSUIColor.systemPurple]
+        UAMlinePrediction.drawCircleHoleEnabled = false
+        UAMlinePrediction.axisDependency = YAxis.AxisDependency.right
+        UAMlinePrediction.highlightEnabled = true
+        UAMlinePrediction.drawValuesEnabled = false
+        
+        if UserDefaultsRepository.showLines.value {
+            UAMlinePrediction.lineWidth = 2
+        } else {
+            UAMlinePrediction.lineWidth = 0
+        }
+        if UserDefaultsRepository.showDots.value {
+            UAMlinePrediction.drawCirclesEnabled = true
+        } else {
+            UAMlinePrediction.drawCirclesEnabled = false
+        }
+        linePrediction.setDrawHighlightIndicators(false)
+        linePrediction.valueFont.withSize(50)
+        
+        // Setup ZT Prediction line details
+        let ZTpredictionChartEntry = [ChartDataEntry]()
+        let ZTlinePrediction = LineChartDataSet(entries:ZTpredictionChartEntry, label: "")
+        ZTlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
+        ZTlinePrediction.circleColors = [NSUIColor.systemPurple]
+        ZTlinePrediction.colors = [NSUIColor.systemPurple]
+        ZTlinePrediction.drawCircleHoleEnabled = false
+        ZTlinePrediction.axisDependency = YAxis.AxisDependency.right
+        ZTlinePrediction.highlightEnabled = true
+        ZTlinePrediction.drawValuesEnabled = false
+        
+        if UserDefaultsRepository.showLines.value {
+            ZTlinePrediction.lineWidth = 2
+        } else {
+            ZTlinePrediction.lineWidth = 0
+        }
+        if UserDefaultsRepository.showDots.value {
+            ZTlinePrediction.drawCirclesEnabled = true
+        } else {
+            ZTlinePrediction.drawCirclesEnabled = false
+        }
+        ZTlinePrediction.setDrawHighlightIndicators(false)
+        ZTlinePrediction.valueFont.withSize(50)
         
         // Setup the chart data of all lines
         let data = LineChartData()
@@ -270,7 +366,11 @@ extension MainViewController {
         data.append(lineResume) // Dataset 9
         data.append(lineSensor) // Dataset 10
         data.append(lineNote) // Dataset 11
-        
+        data.append(COBlinePrediction) // Dataset 12
+        data.append(IOBlinePrediction) // Dataset 13
+        data.append(UAMlinePrediction) // Dataset 14
+        data.append(ZTlinePrediction) // Dataset 15
+
         data.setValueFont(UIFont.systemFont(ofSize: 12))
         
         // Add marker popups for bolus and carbs
@@ -1137,6 +1237,58 @@ extension MainViewController {
         lineNote.axisDependency = YAxis.AxisDependency.right
         lineNote.valueFormatter = ChartYDataValueFormatter()
         lineNote.drawValuesEnabled = false
+
+        // Setup COB Prediction line details
+        var COBpredictionChartEntry = [ChartDataEntry]()
+        let COBlinePrediction = LineChartDataSet(entries:COBpredictionChartEntry, label: "")
+        COBlinePrediction.drawCirclesEnabled = false
+        COBlinePrediction.setColor(NSUIColor.systemPurple)
+        COBlinePrediction.highlightEnabled = true
+        COBlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
+        COBlinePrediction.drawVerticalHighlightIndicatorEnabled = false
+        COBlinePrediction.highlightColor = NSUIColor.label
+        COBlinePrediction.drawValuesEnabled = false
+        COBlinePrediction.lineWidth = 1.5
+        COBlinePrediction.axisDependency = YAxis.AxisDependency.right
+
+        // Setup IOB Prediction line details
+        var IOBpredictionChartEntry = [ChartDataEntry]()
+        let IOBlinePrediction = LineChartDataSet(entries:IOBpredictionChartEntry, label: "")
+        IOBlinePrediction.drawCirclesEnabled = false
+        IOBlinePrediction.setColor(NSUIColor.systemPurple)
+        IOBlinePrediction.highlightEnabled = true
+        IOBlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
+        IOBlinePrediction.drawVerticalHighlightIndicatorEnabled = false
+        IOBlinePrediction.highlightColor = NSUIColor.label
+        IOBlinePrediction.drawValuesEnabled = false
+        IOBlinePrediction.lineWidth = 1.5
+        IOBlinePrediction.axisDependency = YAxis.AxisDependency.right
+
+        // Setup UAM Prediction line details
+        var UAMpredictionChartEntry = [ChartDataEntry]()
+        let UAMlinePrediction = LineChartDataSet(entries:UAMpredictionChartEntry, label: "")
+        UAMlinePrediction.drawCirclesEnabled = false
+        UAMlinePrediction.setColor(NSUIColor.systemPurple)
+        UAMlinePrediction.highlightEnabled = true
+        UAMlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
+        UAMlinePrediction.drawVerticalHighlightIndicatorEnabled = false
+        UAMlinePrediction.highlightColor = NSUIColor.label
+        UAMlinePrediction.drawValuesEnabled = false
+        UAMlinePrediction.lineWidth = 1.5
+        UAMlinePrediction.axisDependency = YAxis.AxisDependency.right
+
+        // Setup ZT Prediction line details
+        var ZTpredictionChartEntry = [ChartDataEntry]()
+        let ZTlinePrediction = LineChartDataSet(entries:ZTpredictionChartEntry, label: "")
+        ZTlinePrediction.drawCirclesEnabled = false
+        ZTlinePrediction.setColor(NSUIColor.systemPurple)
+        ZTlinePrediction.highlightEnabled = true
+        ZTlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
+        ZTlinePrediction.drawVerticalHighlightIndicatorEnabled = false
+        ZTlinePrediction.highlightColor = NSUIColor.label
+        ZTlinePrediction.drawValuesEnabled = false
+        ZTlinePrediction.lineWidth = 1.5
+        ZTlinePrediction.axisDependency = YAxis.AxisDependency.right
         
         // Setup the chart data of all lines
         let data = LineChartData()
@@ -1152,7 +1304,11 @@ extension MainViewController {
         data.append(lineResume) // Dataset 9
         data.append(lineSensor) // Dataset 10
         data.append(lineNote) // Dataset 11
-        
+        data.append(COBlinePrediction) // Dataset 12
+        data.append(IOBlinePrediction) // Dataset 13
+        data.append(UAMlinePrediction) // Dataset 14
+        data.append(ZTlinePrediction) // Dataset 15
+
         BGChartFull.highlightPerDragEnabled = true
         BGChartFull.leftAxis.enabled = false
         BGChartFull.leftAxis.axisMaximum = maxBasal
@@ -1254,4 +1410,67 @@ extension MainViewController {
         return line1 + "\r\n" + formattedDate
     }
   
+    func updatePredictionGraphGeneric(
+        dataIndex: Int,
+        predictionData: [ShareGlucoseData],
+        chartLabel: String,
+        color: UIColor
+    ) {
+        let mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
+        let smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
+        mainChart.clear()
+        smallChart.clear()
+        
+        var colors = [NSUIColor]()
+        let maxBGOffset: Float = 20
+        
+        for i in 0..<predictionData.count {
+            let predictionVal = Double(predictionData[i].sgv)
+            if Float(predictionVal) > topBG - maxBGOffset {
+                topBG = Float(predictionVal) + maxBGOffset
+            }
+            
+            if i == 0 {
+                if UserDefaultsRepository.showDots.value {
+                    colors.append((color).withAlphaComponent(0.0))
+                } else {
+                    colors.append((color).withAlphaComponent(1.0))
+                }
+            } else {
+                colors.append(color)
+            }
+            
+            let value = ChartDataEntry(
+                x: predictionData[i].date,
+                y: predictionVal,
+                data: formatPillText(
+                    line1: chartLabel,
+                    time: predictionData[i].date
+                )
+            )
+            mainChart.addEntry(value)
+            smallChart.addEntry(value)
+        }
+        
+        smallChart.circleColors.removeAll()
+        smallChart.colors.removeAll()
+        mainChart.colors.removeAll()
+        mainChart.circleColors.removeAll()
+        if colors.count > 0 {
+            for color in colors {
+                mainChart.addColor(color)
+                mainChart.circleColors.append(color)
+                smallChart.addColor(color)
+                smallChart.circleColors.append(color)
+            }
+        }
+        
+        BGChart.rightAxis.axisMaximum = Double(topBG)
+        BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
+        BGChart.data?.notifyDataChanged()
+        BGChart.notifyDataSetChanged()
+        BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
+        BGChartFull.data?.notifyDataChanged()
+        BGChartFull.notifyDataSetChanged()
+    }
 }

+ 1 - 134
LoopFollow/Controllers/Nightscout/DeviceStatus.swift

@@ -215,140 +215,7 @@ extension MainViewController {
         } // 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:"%.0f", cobdata["COB"] as! Double)
-                        latestCOB = String(format:"%.0f", cobdata["COB"] as! Double)
-                    }
-                    if let recbolusdata = lastLoopRecord["enacted"] as? [String: AnyObject],
-                       let insulinReq = recbolusdata["insulinReq"] as? Double {
-                        tableData[8].value = String(format: "%.2fU", insulinReq)
-                        UserDefaultsRepository.deviceRecBolus.value = insulinReq
-                    } else {
-                        tableData[8].value = "N/A"
-                        UserDefaultsRepository.deviceRecBolus.value = 0
-                        print("Warning: Failed to extract insulinReq from recbolusdata.")
-                    }
-
-                    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],
-                       let predbgdata = enactdata["predBGs"] as? [String:AnyObject] {
-
-                        if predbgdata["COB"] != nil {
-                            graphtype = "COB"
-                        } else if predbgdata["UAM"] != nil {
-                            graphtype = "UAM"
-                        } else if predbgdata["IOB"] != nil {
-                            graphtype = "IOB"
-                        } else {
-                            graphtype = "ZT"
-                        }
-
-                        // Access the color based on graphtype
-                        var colorName = ""
-                        switch graphtype {
-                        case "COB": colorName = "LoopYellow"
-                        case "UAM": colorName = "UAM"
-                        case "IOB": colorName = "Insulin"
-                        case "ZT": colorName = "ZT"
-                        default: break
-                        }
-
-                        if let selectedColor = UIColor(named: colorName) {
-                            predictioncolor = selectedColor
-                            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") }
-                    }
-                    
-                }
-
-                evaluateNotLooping(lastLoopTime: lastLoopTime)
-            }
+            DeviceStatusOpenAPS(formatter: formatter, lastDeviceStatus: lastDeviceStatus, lastLoopRecord: lastLoopRecord)
         }
         
         var oText = "" as String

+ 91 - 0
LoopFollow/Controllers/Nightscout/DeviceStatusOpenAPS.swift

@@ -0,0 +1,91 @@
+//
+//  DeviceStatusOpenAPS.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2024-05-19.
+//  Copyright © 2024 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension MainViewController {
+    func DeviceStatusOpenAPS(formatter: ISO8601DateFormatter, lastDeviceStatus: [String: AnyObject]?, lastLoopRecord: [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 lastLoopRecord["failureReason"] != nil {
+                LoopStatusLabel.text = "X"
+                latestLoopStatusString = "X"
+                if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Loop Failure: X") }
+            } else {
+                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:"%.0f", cobdata["COB"] as! Double)
+                    latestCOB = String(format:"%.0f", cobdata["COB"] as! Double)
+                }
+                if let recbolusdata = lastLoopRecord["enacted"] as? [String: AnyObject],
+                   let insulinReq = recbolusdata["insulinReq"] as? Double {
+                    tableData[8].value = String(format: "%.2fU", insulinReq)
+                    UserDefaultsRepository.deviceRecBolus.value = insulinReq
+                } else {
+                    tableData[8].value = "N/A"
+                    UserDefaultsRepository.deviceRecBolus.value = 0
+                }
+                
+                if let autosensdata = lastLoopRecord["enacted"] as? [String:AnyObject] {
+                    let sens = autosensdata["sensitivityRatio"] as! Double * 100.0
+                    tableData[11].value = String(format:"%.0f", sens) + "%"
+                }
+                
+                var predictioncolor = UIColor.systemGray
+                PredictionLabel.textColor = predictioncolor
+                
+                if let enactdata = lastLoopRecord["enacted"] as? [String:AnyObject],
+                   let predbgdata = enactdata["predBGs"] as? [String: AnyObject] {
+                    let predictionTypes: [(type: String, colorName: String, dataIndex: Int)] = [
+                        ("COB", "LoopYellow", 12),
+                        ("IOB", "Insulin", 13),
+                        ("UAM", "UAM", 14),
+                        ("ZT", "ZT", 15)
+                    ]
+                    
+                    for (type, colorName, dataIndex) in predictionTypes {
+                        if let graphdata = predbgdata[type] as? [Double] {
+                            var predictionData = [ShareGlucoseData]()
+                            var predictionTime = lastLoopTime
+                            let toLoad = Int(UserDefaultsRepository.predictionToLoad.value * 12)
+                            
+                            for i in 0...toLoad {
+                                if i < graphdata.count {
+                                    let prediction = ShareGlucoseData(sgv: Int(round(graphdata[i])), date: predictionTime, direction: "flat")
+                                    predictionData.append(prediction)
+                                    predictionTime += 300
+                                }
+                            }
+                            
+                            if let color = UIColor(named: colorName) {
+                                updatePredictionGraphGeneric(
+                                    dataIndex: dataIndex,
+                                    predictionData: predictionData,
+                                    chartLabel: type,
+                                    color: color
+                                )
+                            } else {
+                                updatePredictionGraphGeneric(
+                                    dataIndex: dataIndex,
+                                    predictionData: predictionData,
+                                    chartLabel: type,
+                                    color: UIColor.systemPurple
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 6 - 0
LoopFollow/ViewControllers/MainViewController.swift

@@ -112,6 +112,12 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     var carbData: [carbGraphStruct] = []
     var overrideGraphData: [DataStructs.overrideStruct] = []
     var predictionData: [ShareGlucoseData] = []
+    var predictionDataOpenAPS: [String: [ShareGlucoseData]] = [
+        "COB": [],
+        "IOB": [],
+        "UAM": [],
+        "ZT": []
+    ]
     var bgCheckData: [ShareGlucoseData] = []
     var suspendGraphData: [DataStructs.timestampOnlyStruct] = []
     var resumeGraphData: [DataStructs.timestampOnlyStruct] = []