From 2793e9a60678e8aad02e170eb535d6d597905262 Mon Sep 17 00:00:00 2001 From: kristinkand <163904051+kristinkand@users.noreply.github.com> Date: Wed, 11 Nov 2026 07:11:55 +0000 Subject: [PATCH 1/3] Show Trio's unreliable G7 readings (hollow orange dots, never used as BG) Companion to Trio's "Show Unreliable G7 Readings" (off by default in Trio): during a sensor issue the Dexcom G7 marks its values unreliable, and Trio, with that setting on, uploads each as an "Unreliable Glucose" treatment, never as an entry. LoopFollow now: - draws them as hollow orange dots at the normal dot size (also with dots turned off); - shows the newest under the BG, in orange with a warning sign, while it is newer than the last reliable reading; - lists them in a tap on the chart after the reliable readings, and shows one in the scrub pill where no reliable reading is. They are never alarmed on, counted in the statistics or used as BG. Without Trio's setting nothing is uploaded and nothing changes in LoopFollow. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UUzqfqfsM8BQYJ2yoRaLz8 --- LoopFollow/Charts/BGChartModel.swift | 5 +++ LoopFollow/Charts/BGChartView.swift | 32 +++++++++++++++++-- .../Controllers/Nightscout/Treatments.swift | 5 +++ .../Treatments/UnreliableGlucose.swift | 29 +++++++++++++++++ LoopFollow/Storage/Observable.swift | 4 +++ .../ViewControllers/BGDisplayView.swift | 18 +++++++++++ .../ViewControllers/MainViewController.swift | 2 ++ 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 LoopFollow/Controllers/Nightscout/Treatments/UnreliableGlucose.swift diff --git a/LoopFollow/Charts/BGChartModel.swift b/LoopFollow/Charts/BGChartModel.swift index 525fcfcb2..f84052aba 100644 --- a/LoopFollow/Charts/BGChartModel.swift +++ b/LoopFollow/Charts/BGChartModel.swift @@ -116,6 +116,8 @@ final class BGChartModel: ObservableObject { } @Published var bg: [BGPoint] = [] + /// G7 readings the sensor marked unreliable: hollow orange dots. + @Published var unreliable: [BGPoint] = [] /// Scrub timeline on the BG cadence; rebuilt together with `bg`. private(set) var scrubSlots = BGChartScrubSlots(readingDates: []) @Published var bgRuns: [BGRun] = [] @@ -451,6 +453,9 @@ final class BGChartModel: ObservableObject { scrubSlots = BGChartScrubSlots(readingDates: vc.bgData.map { Date(timeIntervalSince1970: $0.date) }) bg = vc.bgData.map { BGPoint(date: Date(timeIntervalSince1970: $0.date), value: clampSgv($0.sgv), color: colorFor($0.sgv, thresholds: thresholds)) } bgRuns = Self.makeRuns(bg) + unreliable = vc.unreliableGraphData.map { + BGPoint(date: Date(timeIntervalSince1970: $0.date), value: clampSgv($0.sgv), color: .orange) + } // Yesterday comparison overlay (#665): already +24h shifted, dimmed gray, no dots. if Storage.shared.showYesterdayLine.value { diff --git a/LoopFollow/Charts/BGChartView.swift b/LoopFollow/Charts/BGChartView.swift index e2949839a..e5c5c47bf 100644 --- a/LoopFollow/Charts/BGChartView.swift +++ b/LoopFollow/Charts/BGChartView.swift @@ -819,6 +819,11 @@ private struct MainBGChart: View { "BG\n\(Localizer.toDisplayUnits(String(Int(point.value))))\n\(model.pillTimeString(for: point.date))" } + /// A reading the G7 marked unreliable: its warning in place of "BG". + private func unreliablePillText(for point: BGChartModel.BGPoint) -> String { + "\(model.pillTimeString(for: point.date))\n⚠︎ not reliable\n\(Localizer.toDisplayUnits(String(Int(point.value))))" + } + private func bandPillTexts(at date: Date) -> [String] { var texts: [String] = [] if let band = model.overrides @@ -889,11 +894,17 @@ private struct MainBGChart: View { } treatments.sort { $0.date < $1.date } let readings = model.bg.filter { slot.contains($0.date) } + // readings the G7 marked unreliable: listed after the reliable ones, and on a scrub shown in the pill + // where no reliable reading is, since the BG display has nothing for that time + let unreliable = model.unreliable.filter { slot.contains($0.date) } let bandTexts = bandPillTexts(at: mark) // Scrubbing shows no pill: the BG display and info table show the - // values at the scrubbed time. A tap lists what happened there. - let texts = purpose == .tap ? treatments.map(\.pillText) + readings.map(bgPillText) + bandTexts : [] + // values at the scrubbed time. A tap lists what happened there, readings the G7 marked unreliable after the + // reliable ones; a scrub over a block with no reliable reading shows the unreliable one. + let texts = purpose == .tap + ? treatments.map(\.pillText) + readings.map(bgPillText) + unreliable.map(unreliablePillText) + bandTexts + : (readings.isEmpty ? unreliable.map(unreliablePillText) : []) func distanceToMark(_ date: Date) -> TimeInterval { abs(date.timeIntervalSince(mark)) } @@ -901,6 +912,8 @@ private struct MainBGChart: View { let nearestReading = readings.min(by: { distanceToMark($0.date) < distanceToMark($1.date) }) if let reading = nearestReading { value = reading.value + } else if let nearest = unreliable.min(by: { distanceToMark($0.date) < distanceToMark($1.date) }) { + value = nearest.value } else if let nearest = treatments.min(by: { distanceToMark($0.date) < distanceToMark($1.date) }) { value = nearest.sgv } else if !bandTexts.isEmpty { @@ -938,6 +951,10 @@ private struct MainBGChart: View { for p in model.bg { consider(p.date, p.value, bgPillText(for: p), isReading: true) } + // the hollow dots the G7 marked unreliable + for p in model.unreliable { + consider(p.date, p.value, unreliablePillText(for: p)) + } } if best == nil { let date = interaction.scrollPosition.addingTimeInterval( @@ -1495,6 +1512,17 @@ private struct BGChartCanvas: View, Equatable { .foregroundStyle(pt.color) } } + // readings the G7 marked unreliable, also with dots off: hollow, so they never read as real ones + ForEach(windowed(model.unreliable) { $0.date }) { pt in + PointMark( + x: .value("time", pt.date), + y: .value("bg", pt.value) + ) + // the size of the normal dots, only hollow + .symbol(.circle.strokeBorder(lineWidth: 1.5)) + .symbolSize(isSmall ? 14 : 30) + .foregroundStyle(pt.color) + } } @ChartContentBuilder diff --git a/LoopFollow/Controllers/Nightscout/Treatments.swift b/LoopFollow/Controllers/Nightscout/Treatments.swift index 969039ab6..f62a89fc6 100644 --- a/LoopFollow/Controllers/Nightscout/Treatments.swift +++ b/LoopFollow/Controllers/Nightscout/Treatments.swift @@ -76,6 +76,7 @@ extension MainViewController { var carbs: [[String: AnyObject]] = [] var temporaryOverride: [[String: AnyObject]] = [] var temporaryTarget: [[String: AnyObject]] = [] + var unreliable: [[String: AnyObject]] = [] var note: [[String: AnyObject]] = [] var bgCheck: [[String: AnyObject]] = [] var suspendPump: [[String: AnyObject]] = [] @@ -107,6 +108,8 @@ extension MainViewController { carbs.append(entry) case "Temporary Override", "Exercise": temporaryOverride.append(entry) + case "Unreliable Glucose": + unreliable.append(entry) case "Temporary Target": temporaryTarget.append(entry) case "Note": @@ -191,6 +194,8 @@ extension MainViewController { processNSOverrides(entries: temporaryOverride) } + processNSUnreliableGlucose(entries: unreliable) + if temporaryTarget.count == 0, tempTargetGraphData.count > 0 { clearOldTempTarget() } diff --git a/LoopFollow/Controllers/Nightscout/Treatments/UnreliableGlucose.swift b/LoopFollow/Controllers/Nightscout/Treatments/UnreliableGlucose.swift new file mode 100644 index 000000000..ea8cffcff --- /dev/null +++ b/LoopFollow/Controllers/Nightscout/Treatments/UnreliableGlucose.swift @@ -0,0 +1,29 @@ +// LoopFollow +// UnreliableGlucose.swift + +import Foundation + +extension MainViewController { + /// Trio ("Show Unreliable G7 Readings") uploads each Dexcom G7 reading the sensor marks unreliable as an + /// "Unreliable Glucose" treatment, never as an entry. Drawn as hollow orange dots, and the newest shown in orange + /// with a warning sign under the BG while it is newer than the last reliable reading. Never alarmed on, counted in + /// the statistics or used as BG. + func processNSUnreliableGlucose(entries: [[String: AnyObject]]) { + var readings: [ShareGlucoseData] = [] + for entry in entries { + guard let dateString = entry["timestamp"] as? String ?? entry["created_at"] as? String, + let date = NightscoutUtils.parseDate(dateString)?.timeIntervalSince1970 + else { continue } + let value = (entry["glucose"] as? NSNumber)?.doubleValue ?? Double(entry["glucose"] as? String ?? "") + guard var mgdl = value, mgdl > 0 else { continue } + if let units = entry["units"] as? String, units.lowercased().hasPrefix("mmol") { mgdl *= 18.0182 } + readings.append(ShareGlucoseData(sgv: Int(mgdl.rounded()), date: date, direction: nil)) + } + readings.sort { $0.date < $1.date } + guard readings.map(\.date) != unreliableGraphData.map(\.date) else { return } + unreliableGraphData = readings + Observable.shared.unreliableBG.value = readings.last?.sgv + Observable.shared.unreliableBGDate.value = readings.last?.date + chartModel.rebuild() + } +} diff --git a/LoopFollow/Storage/Observable.swift b/LoopFollow/Storage/Observable.swift index 229ee6c65..6f5cf3826 100644 --- a/LoopFollow/Storage/Observable.swift +++ b/LoopFollow/Storage/Observable.swift @@ -23,6 +23,10 @@ class Observable { var minAgoText = ObservableValue(default: "?? min ago") var bgText = ObservableValue(default: "BG") var bg = ObservableValue(default: nil) + /// The newest G7 reading the sensor marked unreliable, from Trio's "Unreliable Glucose" treatments (mg/dL and + /// epoch seconds): shown greyed with a warning, never alarmed on or counted. + var unreliableBG = ObservableValue(default: nil) + var unreliableBGDate = ObservableValue(default: nil) var bgStale = ObservableValue(default: true) var bgTextColor = ObservableValue(default: .primary) var directionText = ObservableValue(default: "-") diff --git a/LoopFollow/ViewControllers/BGDisplayView.swift b/LoopFollow/ViewControllers/BGDisplayView.swift index 036a92e56..65c2ce74e 100644 --- a/LoopFollow/ViewControllers/BGDisplayView.swift +++ b/LoopFollow/ViewControllers/BGDisplayView.swift @@ -18,6 +18,17 @@ struct BGDisplayView: View { @ObservedObject var isNotLooping = Observable.shared.isNotLooping @ObservedObject var cgmSensorState = Observable.shared.cgmSensorState @ObservedObject var retro = RetroSelection.shared + @ObservedObject var unreliableBG = Observable.shared.unreliableBG + @ObservedObject var unreliableBGDate = Observable.shared.unreliableBGDate + + /// The newest reading the G7 marked unreliable, while fresh and newer than the last reliable one. + private var unreliableText: String? { + _ = minAgoText.value + guard let mgdl = unreliableBG.value, let date = unreliableBGDate.value else { return nil } + let age = Date().timeIntervalSince1970 - date + guard age < 12 * 60, date > (Storage.shared.lastBgReadingTimeSeconds.value ?? 0) else { return nil } + return "\(Localizer.toDisplayUnits(String(mgdl))) not reliable · \(Int(age / 60)) min ago" + } var onRefresh: (() -> Void)? @@ -65,6 +76,13 @@ struct BGDisplayView: View { Text(minAgoText.value) .font(.system(size: 17)) + if let unreliableText { + Label(unreliableText, systemImage: "exclamationmark.triangle.fill") + .font(.system(size: 17, weight: .semibold)) + .foregroundColor(.orange) + .frame(maxWidth: .infinity) + } + if let state = cgmSensorState.value { Text("CGM: \(state.summary)") .font(.system(size: 17, weight: .semibold)) diff --git a/LoopFollow/ViewControllers/MainViewController.swift b/LoopFollow/ViewControllers/MainViewController.swift index 501736488..bd1533e2e 100644 --- a/LoopFollow/ViewControllers/MainViewController.swift +++ b/LoopFollow/ViewControllers/MainViewController.swift @@ -86,6 +86,8 @@ class MainViewController: UIViewController, UNUserNotificationCenterDelegate { var statsBasalData: [basalGraphStruct] = [] var overrideGraphData: [DataStructs.overrideStruct] = [] var tempTargetGraphData: [DataStructs.tempTargetStruct] = [] + /// G7 readings the sensor marked unreliable (Trio's "Unreliable Glucose" treatments), oldest first: hollow dots. + var unreliableGraphData: [ShareGlucoseData] = [] var predictionData: [ShareGlucoseData] = [] var ztPredictionData: [ShareGlucoseData] = [] var iobPredictionData: [ShareGlucoseData] = [] From 65bf1a18e2971ad396f81b29faf6d1c5942d1b1d Mon Sep 17 00:00:00 2001 From: kristinkand <163904051+kristinkand@users.noreply.github.com> Date: Wed, 11 Nov 2026 08:04:15 +0000 Subject: [PATCH 2/3] Watch: G7 readings marked unreliable, as on the phone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trio (with "Show Unreliable G7 Readings" on) uploads each G7 reading the sensor marks unreliable as an "Unreliable Glucose" treatment; the phone already draws them. The watch now gets them with its state from the phone, or reads them itself from Nightscout while the phone is away, and shows them as on the phone: hollow orange dots on the chart and the sparkline, the newest under the BG ("… not reliable · n min ago") while it is fresh and newer than the last reliable reading, and in the scrub line where no reliable reading is. Never used as BG or alarmed on. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UUzqfqfsM8BQYJ2yoRaLz8 --- LoopFollow/Watch/WatchStateBuilder.swift | 7 +++++- LoopFollowWatch/ChartPage.swift | 12 +++++++++ LoopFollowWatch/GlucosePage.swift | 31 +++++++++++++++++++++++- LoopFollowWatch/StandaloneFetcher.swift | 18 +++++++++++++- LoopFollowWatch/WatchChartView.swift | 21 +++++++++++++++- WatchShared/WatchState.swift | 3 +++ 6 files changed, 88 insertions(+), 4 deletions(-) diff --git a/LoopFollow/Watch/WatchStateBuilder.swift b/LoopFollow/Watch/WatchStateBuilder.swift index 12a379c99..118cfced0 100644 --- a/LoopFollow/Watch/WatchStateBuilder.swift +++ b/LoopFollow/Watch/WatchStateBuilder.swift @@ -27,6 +27,10 @@ enum WatchStateBuilder { .filter { $0.date >= cutoff } .map { WatchReading(t: $0.date, v: $0.value) } + let unreliable = (vc?.unreliableGraphData ?? []) + .filter { $0.date >= cutoff && $0.sgv > 0 } + .map { WatchReading(t: $0.date, v: Double($0.sgv)) } + let overrides = (vc?.overrideGraphData ?? []) .filter { $0.endDate >= cutoff } .map { WatchOverride(start: $0.date, end: $0.endDate, name: $0.reason.isEmpty ? "Override" : $0.reason, percent: overridePercent($0)) } @@ -67,7 +71,8 @@ enum WatchStateBuilder { stats: vc.map { stats($0.statsDisplayModel) }, device: Storage.shared.device.value, alarm: currentAlarm(), - remote: remoteConfig() + remote: remoteConfig(), + unreliable: unreliable ) } diff --git a/LoopFollowWatch/ChartPage.swift b/LoopFollowWatch/ChartPage.swift index d8f17d7f9..31f653d92 100644 --- a/LoopFollowWatch/ChartPage.swift +++ b/LoopFollowWatch/ChartPage.swift @@ -79,6 +79,11 @@ struct ChartScrubView: View { let marker = markerDate(now: now) let nearest = state.readings.min { abs($0.date.timeIntervalSince(marker)) < abs($1.date.timeIntervalSince(marker)) } let reading = nearest.flatMap { abs($0.date.timeIntervalSince(marker)) <= 450 ? $0 : nil } + // without a reliable reading there, one the G7 marked unreliable, hollow as on the chart + let nearestUnsure = (state.unreliable ?? []).min { + abs($0.date.timeIntervalSince(marker)) < abs($1.date.timeIntervalSince(marker)) + } + let unsure = reading == nil ? nearestUnsure.flatMap { abs($0.date.timeIntervalSince(marker)) <= 450 ? $0 : nil } : nil let carbs = total(of: state.carbs, near: marker) let insulin = total(of: state.boluses, near: marker) return HStack(spacing: 8) { @@ -90,6 +95,13 @@ struct ChartScrubView: View { .fill(WatchFormat.bgColor(reading.v, lowMgdl: state.lowMgdl, highMgdl: state.highMgdl)) .frame(width: 7, height: 7) Text(WatchFormat.glucose(reading.v, unit: state.snapshot?.unit ?? .mgdl)) + } else if let unsure { + Circle() + .strokeBorder(Color.orange, lineWidth: 1) + .frame(width: 7, height: 7) + Text(WatchFormat.glucose(unsure.v, unit: state.snapshot?.unit ?? .mgdl)) + .foregroundStyle(.secondary) + .accessibilityLabel("\(WatchFormat.glucose(unsure.v, unit: state.snapshot?.unit ?? .mgdl)), not reliable") } else { Text("—") } diff --git a/LoopFollowWatch/GlucosePage.swift b/LoopFollowWatch/GlucosePage.swift index d07086dd2..391161192 100644 --- a/LoopFollowWatch/GlucosePage.swift +++ b/LoopFollowWatch/GlucosePage.swift @@ -19,6 +19,7 @@ struct GlucosePage: View { AlarmBanner(alarm: alarm) } glucose(now: context.date) + unreliableLine(now: context.date) if !isLuminanceReduced { details overrideLine(now: context.date) @@ -72,6 +73,25 @@ struct GlucosePage: View { } } + /// The newest reading the G7 marked unreliable, while fresh and newer than the last reliable one, as on the phone. + @ViewBuilder + private func unreliableLine(now: Date) -> some View { + if let unsure = state.unreliable?.last, + now.timeIntervalSince(unsure.date) < WatchFormat.staleAfter, + unsure.date > (state.snapshot?.updatedAt ?? .distantPast) + { + Label { + Text("\(WatchFormat.glucose(unsure.v, unit: state.snapshot?.unit ?? .mgdl)) not reliable · \(WatchFormat.minutesAgo(unsure.date, now: now))") + } icon: { + Image(systemName: "exclamationmark.triangle.fill").foregroundStyle(.orange) + } + .font(.footnote) + .foregroundStyle(.secondary) + .lineLimit(1) + .minimumScaleFactor(0.7) + } + } + /// Age of the reading, with a marker while the watch fetches on its own or cannot reach the phone. @ViewBuilder private func age(now: Date) -> some View { @@ -199,7 +219,8 @@ struct Sparkline: View { var body: some View { let start = now.addingTimeInterval(-2 * 3600) let readings = state.readings.filter { $0.date >= start } - let values = readings.map(\.v) + let unreliable = (state.unreliable ?? []).filter { $0.date >= start } + let values = (readings + unreliable).map(\.v) let bottom = min(values.min() ?? state.lowMgdl, state.lowMgdl) - 10 let top = max(values.max() ?? state.highMgdl, state.highMgdl) + 10 Chart { @@ -214,6 +235,14 @@ struct Sparkline: View { .symbolSize(12) .foregroundStyle(WatchFormat.bgColor(reading.v, lowMgdl: state.lowMgdl, highMgdl: state.highMgdl)) } + ForEach(unreliable, id: \.t) { reading in + PointMark(x: .value("Time", reading.date), y: .value("BG", reading.v)) + .symbol { + Circle() + .strokeBorder(Color.orange, lineWidth: 1) + .frame(width: WatchChartView.diameter(12), height: WatchChartView.diameter(12)) + } + } } .chartXScale(domain: start ... now) .chartYScale(domain: bottom ... top) diff --git a/LoopFollowWatch/StandaloneFetcher.swift b/LoopFollowWatch/StandaloneFetcher.swift index 0617371c0..b2a17567a 100644 --- a/LoopFollowWatch/StandaloneFetcher.swift +++ b/LoopFollowWatch/StandaloneFetcher.swift @@ -150,10 +150,26 @@ enum StandaloneFetcher { stats: base?.stats, device: base?.device ?? "", alarm: nil, - remote: remote + remote: remote, + unreliable: unreliableReadings(treatments, cutoff: cutoff) ) } + /// Trio's "Unreliable Glucose" treatments, as the phone reads them (Untethered.swift), in mg/dL. + private static func unreliableReadings(_ treatments: [[String: Any]], cutoff: TimeInterval) -> [WatchReading] { + treatments.compactMap { entry -> WatchReading? in + guard entry["eventType"] as? String == "Unreliable Glucose", + let at = DeviceStatusSummary.date(entry["timestamp"] ?? entry["created_at"]), + at.timeIntervalSince1970 >= cutoff + else { return nil } + let value = (entry["glucose"] as? NSNumber)?.doubleValue ?? Double(entry["glucose"] as? String ?? "") + guard var mgdl = value, mgdl > 0 else { return nil } + if let units = entry["units"] as? String, units.lowercased().hasPrefix("mmol") { mgdl *= 18.0182 } + return WatchReading(t: at.timeIntervalSince1970, v: mgdl.rounded()) + } + .sorted { $0.t < $1.t } + } + private static func reading(_ treatment: [String: Any], key: String, cutoff: TimeInterval) -> WatchReading? { guard let value = treatment[key] as? Double, value > 0, let at = DeviceStatusSummary.date(treatment["timestamp"] ?? treatment["created_at"]), diff --git a/LoopFollowWatch/WatchChartView.swift b/LoopFollowWatch/WatchChartView.swift index 71cc3225f..963327b5f 100644 --- a/LoopFollowWatch/WatchChartView.swift +++ b/LoopFollowWatch/WatchChartView.swift @@ -21,12 +21,13 @@ struct WatchChartView: View { var body: some View { let window = visibleWindow let readings = state.readings.filter { window.contains($0.date) } + let unreliable = (state.unreliable ?? []).filter { window.contains($0.date) } let predictions = state.predictions.map { line in (line.kind, line.points.filter { window.contains($0.date) }) } let cone = state.cone.filter { window.contains($0.date) } let overrides = (state.overrides ?? []).filter { $0.endDate > window.lowerBound && $0.startDate < window.upperBound } - let top = yTop(readings: readings, predictions: predictions.flatMap(\.1) + cone.map { WatchReading(t: $0.t, v: $0.high) }) + let top = yTop(readings: readings + unreliable, predictions: predictions.flatMap(\.1) + cone.map { WatchReading(t: $0.t, v: $0.high) }) Chart { RuleMark(y: .value("High", state.highMgdl)) @@ -86,6 +87,19 @@ struct WatchChartView: View { .foregroundStyle(WatchFormat.bgColor(reading.v, lowMgdl: state.lowMgdl, highMgdl: state.highMgdl)) } + // readings the G7 marked unreliable: hollow, so they never read as real ones + ForEach(unreliable, id: \.t) { reading in + PointMark( + x: .value("Time", reading.date), + y: .value("BG", min(reading.v, top)) + ) + .symbol { + Circle() + .strokeBorder(Color.orange, lineWidth: 1) + .frame(width: Self.diameter(spanHours <= 3 ? 14 : 6), height: Self.diameter(spanHours <= 3 ? 14 : 6)) + } + } + ForEach(state.carbs.filter { window.contains($0.date) }, id: \.t) { carb in PointMark( x: .value("Time", carb.date), @@ -187,6 +201,11 @@ struct WatchChartView: View { return min(nearest?.v ?? 100, top - 30) } + /// The width of a filled dot of this symbol size (an area), for a hollow one to match. + static func diameter(_ symbolSize: CGFloat) -> CGFloat { + max(3, (symbolSize / .pi).squareRoot() * 2) + } + static func color(for kind: WatchPredictionLine.Kind) -> Color { switch kind { case .loop: .purple diff --git a/WatchShared/WatchState.swift b/WatchShared/WatchState.swift index 91c73c69c..2f6ad324b 100644 --- a/WatchShared/WatchState.swift +++ b/WatchShared/WatchState.swift @@ -41,6 +41,9 @@ struct WatchState: Codable, Equatable { var alarm: WatchAlarm? /// Present when remote commands from the watch are enabled on the phone. var remote: WatchRemoteConfig? + /// G7 readings the sensor marked unreliable (Trio's "Unreliable Glucose" treatments), mg/dL, oldest first: + /// hollow orange dots, never used as BG; nil from a phone that doesn't send them. + var unreliable: [WatchReading]? = nil func encoded() -> Data? { try? JSONEncoder().encode(self) From 36275be5b3fab9ff23b6d41d6639f5bd01092edf Mon Sep 17 00:00:00 2001 From: kristinkand <163904051+kristinkand@users.noreply.github.com> Date: Wed, 11 Nov 2026 08:16:53 +0000 Subject: [PATCH 3/3] Watch: keep SwiftFormat's lint from reading two comparisons as generics Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UUzqfqfsM8BQYJ2yoRaLz8 --- LoopFollowWatch/GlucosePage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopFollowWatch/GlucosePage.swift b/LoopFollowWatch/GlucosePage.swift index 391161192..744b37fff 100644 --- a/LoopFollowWatch/GlucosePage.swift +++ b/LoopFollowWatch/GlucosePage.swift @@ -78,7 +78,7 @@ struct GlucosePage: View { private func unreliableLine(now: Date) -> some View { if let unsure = state.unreliable?.last, now.timeIntervalSince(unsure.date) < WatchFormat.staleAfter, - unsure.date > (state.snapshot?.updatedAt ?? .distantPast) + (state.snapshot?.updatedAt ?? .distantPast) < unsure.date { Label { Text("\(WatchFormat.glucose(unsure.v, unit: state.snapshot?.unit ?? .mgdl)) not reliable · \(WatchFormat.minutesAgo(unsure.date, now: now))")