diff --git a/LoopFollow/Controllers/Graphs.swift b/LoopFollow/Controllers/Graphs.swift index 9789e9c46..6b2d261cd 100644 --- a/LoopFollow/Controllers/Graphs.swift +++ b/LoopFollow/Controllers/Graphs.swift @@ -27,6 +27,7 @@ enum GraphDataIndex: Int { case smb = 16 case tempTarget = 17 case predictionCone = 18 + case yesterday = 19 } extension GraphDataIndex { @@ -51,6 +52,7 @@ extension GraphDataIndex { case .smb: return "SMB" case .tempTarget: return "Temp Target" case .predictionCone: return "Prediction Cone" + case .yesterday: return "Yesterday" } } } @@ -622,6 +624,16 @@ extension MainViewController { lineCone.axisDependency = YAxis.AxisDependency.right data.append(lineCone) + // Dataset 19: Yesterday's BG comparison overlay (thin dimmed gray line, no dots) + let lineYesterday = LineChartDataSet(entries: [ChartDataEntry](), label: "") + lineYesterday.lineWidth = 1.5 + lineYesterday.setColor(NSUIColor.systemGray, alpha: 0.4) + lineYesterday.drawCirclesEnabled = false + lineYesterday.drawValuesEnabled = false + lineYesterday.highlightEnabled = false + lineYesterday.axisDependency = YAxis.AxisDependency.right + data.append(lineYesterday) + data.setValueFont(UIFont.systemFont(ofSize: 12)) // Add marker popups for bolus and carbs @@ -813,6 +825,11 @@ extension MainViewController { BGChart.data?.notifyDataChanged() BGChart.notifyDataSetChanged() + // Reflect the yesterday overlay toggle immediately, and reload the BG window + // so the extra day of history is fetched (or dropped) when the toggle changed. + updateYesterdayBGGraph() + TaskScheduler.shared.rescheduleTask(id: .fetchBG, to: Date()) + // Re-render prediction display in case display type changed updateOpenAPSPredictionDisplay() } @@ -882,6 +899,8 @@ extension MainViewController { BGChartFull.data?.notifyDataChanged() BGChartFull.notifyDataSetChanged() + updateYesterdayBGGraph() + if firstGraphLoad { var scaleX = CGFloat(Storage.shared.chartScaleX.value) if scaleX > CGFloat(ScaleXMax) { @@ -899,6 +918,32 @@ extension MainViewController { } } + // Populates (or clears) the dimmed "yesterday" comparison overlay on the main graph. + // Points in yesterdayBGData are already shifted +24h so they align with today's clock time. + func updateYesterdayBGGraph() { + let dataIndex = GraphDataIndex.yesterday.rawValue + guard let lineData = BGChart.lineData, + dataIndex < lineData.dataSets.count, + let dataSet = lineData.dataSets[dataIndex] as? LineChartDataSet + else { + return + } + + dataSet.removeAll(keepingCapacity: false) + + if Storage.shared.showYesterdayLine.value { + for entry in yesterdayBGData { + // Clamp the plotted y-value to the same bounds the main BG line uses. + let plottedSgv = Double(min(max(entry.sgv, globalVariables.minDisplayGlucose), globalVariables.maxDisplayGlucose)) + dataSet.append(ChartDataEntry(x: entry.date, y: plottedSgv)) + } + } + + BGChart.data?.dataSets[dataIndex].notifyDataSetChanged() + BGChart.data?.notifyDataChanged() + BGChart.notifyDataSetChanged() + } + func updatePredictionGraph(color: UIColor? = nil) { let dataIndex = 1 var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet diff --git a/LoopFollow/Controllers/Nightscout/BGData.swift b/LoopFollow/Controllers/Nightscout/BGData.swift index d97aba24c..966ddacb1 100644 --- a/LoopFollow/Controllers/Nightscout/BGData.swift +++ b/LoopFollow/Controllers/Nightscout/BGData.swift @@ -5,11 +5,19 @@ import Foundation import UIKit extension MainViewController { + /// Number of days of BG history to request from the source. One extra day is + /// added when the "Show Yesterday's BG" overlay is enabled (Nightscout only), + /// so the overlay can display the same clock time from the day before. + var bgFetchDays: Int { + let extraDay = (Storage.shared.showYesterdayLine.value && IsNightscoutEnabled()) ? 1 : 0 + return Storage.shared.downloadDays.value + extraDay + } + // Dex Share Web Call func webLoadDexShare() { // Dexcom Share only returns 24 hrs of data as of now // Requesting more just for consistency with NS - let graphHours = 24 * Storage.shared.downloadDays.value + let graphHours = 24 * bgFetchDays let count = graphHours * 12 dexShare?.fetchData(count) { err, result in if let error = err { @@ -51,8 +59,8 @@ extension MainViewController { } var parameters: [String: String] = [:] - let date = Calendar.current.date(byAdding: .day, value: -1 * Storage.shared.downloadDays.value, to: Date())! - parameters["count"] = "\(Storage.shared.downloadDays.value * 2 * 24 * 60 / 5)" + let date = Calendar.current.date(byAdding: .day, value: -1 * bgFetchDays, to: Date())! + parameters["count"] = "\(bgFetchDays * 2 * 24 * 60 / 5)" parameters["find[date][$gte]"] = "\(Int(date.timeIntervalSince1970 * 1000))" // Exclude 'cal' entries @@ -207,6 +215,27 @@ extension MainViewController { LogManager.shared.log(category: .nightscout, message: "Graph data updated with \(bgData.count) entries.", isDebug: true) + + // Build the optional "yesterday" comparison overlay. Every fetched reading is + // shifted +24h so it lines up with the same clock time today; the extra day of + // history pulled by bgFetchDays provides the portion that falls inside the + // visible window. The overlay is capped to "now + hours of prediction" so it + // never extends further into the future than the prediction line. + yesterdayBGData.removeAll() + if Storage.shared.showYesterdayLine.value, IsNightscoutEnabled() { + let cutoff = dateTimeUtils.getTimeIntervalNHoursAgo(N: 24 * bgFetchDays) + let futureLimit = dateTimeUtils.getNowTimeIntervalUTC() + Storage.shared.predictionToLoad.value * 3600 + for i in 0 ..< data.count { + let reading = data[data.count - 1 - i] + guard reading.date >= cutoff, reading.sgv <= 600 else { continue } + let shiftedDate = reading.date + 24 * 60 * 60 + guard shiftedDate <= futureLimit else { continue } + yesterdayBGData.append(ShareGlucoseData(sgv: reading.sgv, + date: shiftedDate, + direction: reading.direction)) + } + } + viewUpdateNSBG(sourceName: sourceName) } diff --git a/LoopFollow/Settings/GraphSettingsView.swift b/LoopFollow/Settings/GraphSettingsView.swift index 534e00698..a9b07d666 100644 --- a/LoopFollow/Settings/GraphSettingsView.swift +++ b/LoopFollow/Settings/GraphSettingsView.swift @@ -12,6 +12,7 @@ struct GraphSettingsView: View { @ObservedObject private var show30MinLine = Storage.shared.show30MinLine @ObservedObject private var show90MinLine = Storage.shared.show90MinLine @ObservedObject private var showMidnightLines = Storage.shared.showMidnightLines + @ObservedObject private var showYesterdayLine = Storage.shared.showYesterdayLine @ObservedObject private var smallGraphTreatments = Storage.shared.smallGraphTreatments @ObservedObject private var smallGraphHeight = Storage.shared.smallGraphHeight @@ -43,6 +44,9 @@ struct GraphSettingsView: View { Toggle("Show −90 min Line", isOn: $show90MinLine.value) .onChange(of: show90MinLine.value) { _ in markDirty() } + + Toggle("Show Yesterday's BG", isOn: $showYesterdayLine.value) + .onChange(of: showYesterdayLine.value) { _ in markDirty() } } Toggle("Show Midnight Lines", isOn: $showMidnightLines.value) diff --git a/LoopFollow/Storage/Storage.swift b/LoopFollow/Storage/Storage.swift index 6ee01522b..e5110b1f0 100644 --- a/LoopFollow/Storage/Storage.swift +++ b/LoopFollow/Storage/Storage.swift @@ -129,6 +129,7 @@ class Storage { var show30MinLine = StorageValue(key: "show30MinLine", defaultValue: false) var show90MinLine = StorageValue(key: "show90MinLine", defaultValue: false) var showMidnightLines = StorageValue(key: "showMidnightMarkers", defaultValue: false) + var showYesterdayLine = StorageValue(key: "showYesterdayLine", defaultValue: false) var smallGraphTreatments = StorageValue(key: "smallGraphTreatments", defaultValue: true) var smallGraphHeight = StorageValue(key: "smallGraphHeight", defaultValue: 40) diff --git a/LoopFollow/ViewControllers/MainViewController.swift b/LoopFollow/ViewControllers/MainViewController.swift index 6abea5ab4..272823fe7 100644 --- a/LoopFollow/ViewControllers/MainViewController.swift +++ b/LoopFollow/ViewControllers/MainViewController.swift @@ -77,6 +77,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele var profileManager = ProfileManager.shared var bgData: [ShareGlucoseData] = [] + var yesterdayBGData: [ShareGlucoseData] = [] // readings already shifted +24h for the comparison overlay var basalProfile: [basalProfileStruct] = [] var basalData: [basalGraphStruct] = [] var basalScheduleData: [basalGraphStruct] = []