|
|
@@ -30,7 +30,7 @@ extension Home {
|
|
|
CGMSettings.StateModel.shared
|
|
|
}
|
|
|
|
|
|
- private let timer = DispatchTimer(timeInterval: 5)
|
|
|
+ private let timer = DispatchTimer(timeInterval: 30)
|
|
|
private(set) var filteredHours = 24
|
|
|
var startMarker = Date(timeIntervalSinceNow: TimeInterval(hours: -24))
|
|
|
var endMarker = Date(timeIntervalSinceNow: TimeInterval(hours: 3))
|
|
|
@@ -113,6 +113,7 @@ extension Home {
|
|
|
var cgmDisplayState: CgmDisplayState?
|
|
|
var cgmProgressHighlight: DeviceLifecycleProgress?
|
|
|
var cgmSensorExpiresAt: Date?
|
|
|
+ var cgmWarmupEndsAt: Date?
|
|
|
var listOfCGM: [CGMModel] = []
|
|
|
var cgmCurrent = cgmDefaultModel
|
|
|
var pumpInitialSettings = PumpConfig.PumpInitialSettings.default
|
|
|
@@ -327,7 +328,28 @@ extension Home {
|
|
|
|
|
|
timer.eventHandler = {
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
- self?.timerDate = Date()
|
|
|
+ guard let self else { return }
|
|
|
+ self.timerDate = Date()
|
|
|
+ // The publisher only re-emits on state changes; re-pull
|
|
|
+ // so the arc + countdowns + status text advance during
|
|
|
+ // warmup / stabilizing / expiry.
|
|
|
+ let manager = self.fetchGlucoseManager.cgmManager
|
|
|
+ let progress = manager?.cgmLifecycleProgress
|
|
|
+ self.cgmProgressHighlight = progress
|
|
|
+ if let highlight = manager?.cgmStatusHighlight {
|
|
|
+ self.cgmDisplayState = CgmDisplayState(
|
|
|
+ localizedMessage: highlight.localizedMessage,
|
|
|
+ status: CgmDisplayStatus.from(highlight.state)
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ self.cgmDisplayState = nil
|
|
|
+ }
|
|
|
+ self.cgmSensorExpiresAt = Self.resolveSensorExpiresAt(
|
|
|
+ manager: manager,
|
|
|
+ glucoseSource: self.fetchGlucoseManager.glucoseSource,
|
|
|
+ lifecycle: progress
|
|
|
+ )
|
|
|
+ self.cgmWarmupEndsAt = Self.resolveWarmupEndsAt(manager: manager)
|
|
|
}
|
|
|
}
|
|
|
timer.resume()
|
|
|
@@ -346,6 +368,9 @@ extension Home {
|
|
|
glucoseSource: self.fetchGlucoseManager.glucoseSource,
|
|
|
lifecycle: progress
|
|
|
)
|
|
|
+ self.cgmWarmupEndsAt = Self.resolveWarmupEndsAt(
|
|
|
+ manager: self.fetchGlucoseManager.cgmManager
|
|
|
+ )
|
|
|
}
|
|
|
.store(in: &lifetime)
|
|
|
|
|
|
@@ -666,23 +691,9 @@ extension Home {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// Pulls `cgmStatusHighlight` + `cgmLifecycleProgress` off the active
|
|
|
- /// `CGMManagerUI` and derives the bobble's outer-arc/tag display state.
|
|
|
- /// Called from the home timer (every 5s) and on subscribe. Cheap — both
|
|
|
- /// LoopKit properties are computed accessors backed by the manager's
|
|
|
- /// in-memory sensor state.
|
|
|
- ///
|
|
|
- /// Falls back to `GlucoseSimulatorSource`'s synthetic values when no
|
|
|
- /// Best available `sensorExpiresAt` for the home label:
|
|
|
- /// 1. Some managers expose it directly (G7 `sensorExpiresAt`, G5/G6
|
|
|
- /// `latestReading.sessionExpDate`).
|
|
|
- /// 2. Others only expose `activatedAt` — combined with
|
|
|
- /// `lifecycle.percentComplete` we reverse-derive total lifetime.
|
|
|
- /// Critically `activatedAt` must be the *session* start, not the
|
|
|
- /// transmitter activation date, or the math blows up (an Anubis-
|
|
|
- /// modded G6 with `Glucose.activationDate` months old produced
|
|
|
- /// "7337d remaining" — bug, fixed by reading session start instead).
|
|
|
- /// 3. Falls through to nil — UI drops the time label.
|
|
|
+ /// Sensor expiration for the home label. Prefers manager-reported
|
|
|
+ /// dates; reverse-derives from `lifecycle.percentComplete` when not.
|
|
|
+ /// `activatedAt` must be session start, not transmitter activation.
|
|
|
private static func resolveSensorExpiresAt(
|
|
|
manager: CGMManagerUI?,
|
|
|
glucoseSource: GlucoseSource?,
|
|
|
@@ -713,6 +724,25 @@ extension Home {
|
|
|
guard elapsed > 0 else { return nil }
|
|
|
return activatedAt.addingTimeInterval(elapsed / lifecycle.percentComplete)
|
|
|
}
|
|
|
+
|
|
|
+ /// Wall-clock end of the sensor's warmup window; `nil` when not warming up.
|
|
|
+ private static func resolveWarmupEndsAt(manager: CGMManagerUI?) -> Date? {
|
|
|
+ guard let manager else { return nil }
|
|
|
+ if let g7 = manager as? G7CGMManager {
|
|
|
+ guard let ends = g7.sensorFinishesWarmupAt, ends > Date() else { return nil }
|
|
|
+ return ends
|
|
|
+ }
|
|
|
+ if let g6 = manager as? G6CGMManager, let start = g6.latestReading?.sessionStartDate {
|
|
|
+ let window: TimeInterval = g6.isAnubis ? 50 * 60 : 2 * 60 * 60
|
|
|
+ let ends = start.addingTimeInterval(window)
|
|
|
+ return ends > Date() ? ends : nil
|
|
|
+ }
|
|
|
+ if let g5 = manager as? G5CGMManager, let start = g5.latestReading?.sessionStartDate {
|
|
|
+ let ends = start.addingTimeInterval(2 * 60 * 60)
|
|
|
+ return ends > Date() ? ends : nil
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|