diff --git a/LoopFollow/Controllers/Graphs.swift b/LoopFollow/Controllers/Graphs.swift index 184b67b8f..141addbb2 100644 --- a/LoopFollow/Controllers/Graphs.swift +++ b/LoopFollow/Controllers/Graphs.swift @@ -967,6 +967,30 @@ extension MainViewController { BGChart.notifyDataSetChanged() } + // Removes the Loop forecast line. Used when the active system switches away from Loop. + func clearLoopPredictionGraph() { + guard !predictionData.isEmpty else { return } + predictionData.removeAll() + updatePredictionGraph() + } + + // Removes the Trio/OpenAPS forecast lines (ZT/IOB/COB/UAM). Used when the active system switches away from Trio/OpenAPS. + func clearOpenAPSPredictionGraph() { + let openAPSDataIndices = [12, 13, 14, 15] + for dataIndex in openAPSDataIndices { + let mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet + let smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet + if !mainChart.entries.isEmpty || !smallChart.entries.isEmpty { + updatePredictionGraphGeneric( + dataIndex: dataIndex, + predictionData: [], + chartLabel: "", + color: UIColor.systemGray + ) + } + } + } + func updatePredictionGraph(color: UIColor? = nil) { let dataIndex = 1 var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet diff --git a/LoopFollow/Controllers/Nightscout/DeviceStatus.swift b/LoopFollow/Controllers/Nightscout/DeviceStatus.swift index d2d6920d8..04f6bfa15 100644 --- a/LoopFollow/Controllers/Nightscout/DeviceStatus.swift +++ b/LoopFollow/Controllers/Nightscout/DeviceStatus.swift @@ -66,6 +66,7 @@ extension MainViewController { // NS Device Status Response Processor func updateDeviceStatusDisplay(jsonDeviceStatus: [[String: AnyObject]]) { let previousIOBText = Observable.shared.iobText.value + let previousDeviceWasLoop = Storage.shared.device.value == "Loop" infoManager.clearInfoData(types: [.iob, .cob, .battery, .pump, .pumpBattery, .target, .isf, .carbRatio, .updated, .recBolus, .tdd]) // For Loop, clear the current override here - For Trio, it is handled using treatments @@ -155,6 +156,17 @@ extension MainViewController { // Loop - handle new data if let lastLoopRecord = lastDeviceStatus?["loop"] as! [String: AnyObject]? { + // Some pumps report no `pump.clock`; without it alertLastLoopTime stays 0 + // and the forecast anchors to epoch 0. Fall back to the loop cycle timestamp. + if (lastDeviceStatus?["pump"] as? [String: AnyObject])?["clock"] == nil, + let loopTimestampString = lastLoopRecord["timestamp"] as? String, + let loopTimestamp = formatter.date(from: loopTimestampString)?.timeIntervalSince1970, + loopTimestamp > (Observable.shared.alertLastLoopTime.value ?? 0) + { + Observable.shared.alertLastLoopTime.value = loopTimestamp + Storage.shared.lastLoopTime.value = loopTimestamp + } + DeviceStatusLoop(formatter: formatter, lastLoopRecord: lastLoopRecord) var oText = "" @@ -188,6 +200,17 @@ extension MainViewController { DeviceStatusOpenAPS(formatter: formatter, lastDeviceStatus: lastDeviceStatus, lastLoopRecord: lastLoopRecord) } + // If the active looping system flipped (Loop ⇄ Trio/OpenAPS), drop the previous + // system's forecast so it doesn't linger next to the one just drawn above. + let currentDeviceIsLoop = Storage.shared.device.value == "Loop" + if currentDeviceIsLoop != previousDeviceWasLoop { + if currentDeviceIsLoop { + clearOpenAPSPredictionGraph() + } else { + clearLoopPredictionGraph() + } + } + // Start the timer based on the timestamp let now = dateTimeUtils.getNowTimeIntervalUTC() let secondsAgo = now - (Observable.shared.alertLastLoopTime.value ?? 0) diff --git a/LoopFollow/Controllers/Nightscout/DeviceStatusLoop.swift b/LoopFollow/Controllers/Nightscout/DeviceStatusLoop.swift index 771fdc68d..d3fe72e3b 100644 --- a/LoopFollow/Controllers/Nightscout/DeviceStatusLoop.swift +++ b/LoopFollow/Controllers/Nightscout/DeviceStatusLoop.swift @@ -68,7 +68,7 @@ extension MainViewController { let prediction = predictdata["values"] as! [Double] Observable.shared.predictionText.value = Localizer.toDisplayUnits(String(Int(round(prediction.last!)))) Observable.shared.predictionColor.value = .purple - if Storage.shared.downloadPrediction.value, previousLastLoopTime < lastLoopTime { + if Storage.shared.downloadPrediction.value, previousLastLoopTime < lastLoopTime || predictionData.isEmpty { predictionData.removeAll() var predictionTime = lastLoopTime let toLoad = Int(Storage.shared.predictionToLoad.value * 12) diff --git a/LoopFollow/Task/DeviceStatusTask.swift b/LoopFollow/Task/DeviceStatusTask.swift index 0273937d1..412ffbf0f 100644 --- a/LoopFollow/Task/DeviceStatusTask.swift +++ b/LoopFollow/Task/DeviceStatusTask.swift @@ -15,6 +15,10 @@ extension MainViewController { func deviceStatusAction() { // If no NS config, we wait 60s before trying again: guard IsNightscoutEnabled() else { + // No device-status source (e.g. Dexcom-only): drop any forecast left over + // from a previous Loop/Trio source so it doesn't linger on the chart. + clearLoopPredictionGraph() + clearOpenAPSPredictionGraph() TaskScheduler.shared.rescheduleTask(id: .deviceStatus, to: Date().addingTimeInterval(60)) return } diff --git a/LoopFollow/ViewControllers/MainViewController.swift b/LoopFollow/ViewControllers/MainViewController.swift index 105873f1c..3ec2dab87 100644 --- a/LoopFollow/ViewControllers/MainViewController.swift +++ b/LoopFollow/ViewControllers/MainViewController.swift @@ -565,29 +565,6 @@ class MainViewController: UIViewController, ChartViewDelegate, UNUserNotificatio @objc func refresh() { LogManager.shared.log(category: .general, message: "Refreshing") - // Clear prediction for both Loop or OpenAPS - - // Check if Loop prediction data exists and clear it if necessary - if !predictionData.isEmpty { - predictionData.removeAll() - updatePredictionGraph() - } - - // Check if OpenAPS prediction data exists and clear it if necessary - let openAPSDataIndices = [12, 13, 14, 15] - for dataIndex in openAPSDataIndices { - let mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet - let smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet - if !mainChart.entries.isEmpty || !smallChart.entries.isEmpty { - updatePredictionGraphGeneric( - dataIndex: dataIndex, - predictionData: [], - chartLabel: "", - color: UIColor.systemGray - ) - } - } - Observable.shared.minAgoText.value = "Refreshing" scheduleAllTasks() NightscoutSocketManager.shared.connectIfNeeded()