MockPumpManagerSettingsViewModel.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //
  2. // MockPumpManagerSettingsViewModel.swift
  3. // MockKitUI
  4. //
  5. // Created by Nathaniel Hamming on 2023-05-18.
  6. // Copyright © 2023 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. import LoopKit
  10. import MockKit
  11. class MockPumpManagerSettingsViewModel: ObservableObject {
  12. let pumpManager: MockPumpManager
  13. @Published private(set) var isDeliverySuspended: Bool {
  14. didSet {
  15. transitioningSuspendResumeInsulinDelivery = false
  16. basalDeliveryState = pumpManager.status.basalDeliveryState
  17. }
  18. }
  19. @Published private(set) var transitioningSuspendResumeInsulinDelivery = false
  20. @Published private(set) var suspendedAtString: String? = nil
  21. var suspendResumeInsulinDeliveryLabel: String {
  22. if isDeliverySuspended {
  23. return "Tap to Resume Insulin Delivery"
  24. } else {
  25. return "Suspend Insulin Delivery"
  26. }
  27. }
  28. static private let dateTimeFormatter: DateFormatter = {
  29. let timeFormatter = DateFormatter()
  30. timeFormatter.dateStyle = .short
  31. timeFormatter.timeStyle = .short
  32. return timeFormatter
  33. }()
  34. static private let shortTimeFormatter: DateFormatter = {
  35. let formatter = DateFormatter()
  36. formatter.dateStyle = .none
  37. formatter.timeStyle = .short
  38. return formatter
  39. }()
  40. private var pumpPairedInterval: TimeInterval {
  41. pumpExpirationRemaing - pumpLifeTime
  42. }
  43. var lastPumpPairedDateTimeString: String {
  44. Self.dateTimeFormatter.string(from: Date().addingTimeInterval(pumpPairedInterval))
  45. }
  46. private let pumpExpirationRemaing = TimeInterval(days: 2.0)
  47. private let pumpLifeTime = TimeInterval(days: 3.0)
  48. var pumpExpirationPercentComplete: Double {
  49. (pumpLifeTime - pumpExpirationRemaing) / pumpLifeTime
  50. }
  51. var pumpExpirationDateTimeString: String {
  52. Self.dateTimeFormatter.string(from: Date().addingTimeInterval(pumpExpirationRemaing))
  53. }
  54. var pumpTimeString: String {
  55. Self.shortTimeFormatter.string(from: Date())
  56. }
  57. @Published private(set) var basalDeliveryState: PumpManagerStatus.BasalDeliveryState? {
  58. didSet {
  59. setSuspenededAtString()
  60. }
  61. }
  62. @Published private(set) var basalDeliveryRate: Double?
  63. @Published private(set) var presentDeliveryWarning: Bool?
  64. var isScheduledBasal: Bool {
  65. switch basalDeliveryState {
  66. case .active, .initiatingTempBasal:
  67. return true
  68. case .tempBasal, .cancelingTempBasal, .suspending, .suspended, .resuming, .none:
  69. return false
  70. }
  71. }
  72. var isTempBasal: Bool {
  73. switch basalDeliveryState {
  74. case .tempBasal, .cancelingTempBasal:
  75. return true
  76. case .active, .initiatingTempBasal, .suspending, .suspended, .resuming, .none:
  77. return false
  78. }
  79. }
  80. init(pumpManager: MockPumpManager) {
  81. self.pumpManager = pumpManager
  82. isDeliverySuspended = pumpManager.status.basalDeliveryState?.isSuspended == true
  83. basalDeliveryState = pumpManager.status.basalDeliveryState
  84. basalDeliveryRate = pumpManager.state.basalDeliveryRate(at: Date())
  85. setSuspenededAtString()
  86. pumpManager.addStateObserver(self, queue: .main)
  87. }
  88. private func setSuspenededAtString() {
  89. switch basalDeliveryState {
  90. case .suspended(let suspendedAt):
  91. let formatter = DateFormatter()
  92. formatter.dateStyle = .medium
  93. formatter.timeStyle = .short
  94. formatter.doesRelativeDateFormatting = true
  95. suspendedAtString = formatter.string(from: suspendedAt)
  96. default:
  97. suspendedAtString = nil
  98. }
  99. }
  100. func resumeDelivery(completion: @escaping (Error?) -> Void) {
  101. transitioningSuspendResumeInsulinDelivery = true
  102. pumpManager.resumeDelivery() { [weak self] error in
  103. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  104. if error == nil {
  105. self?.isDeliverySuspended = false
  106. }
  107. completion(error)
  108. }
  109. }
  110. }
  111. func suspendDelivery(completion: @escaping (Error?) -> Void) {
  112. transitioningSuspendResumeInsulinDelivery = true
  113. pumpManager.suspendDelivery() { [weak self] error in
  114. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  115. if error == nil {
  116. self?.isDeliverySuspended = true
  117. }
  118. completion(error)
  119. }
  120. }
  121. }
  122. }
  123. extension MockPumpManagerSettingsViewModel: MockPumpManagerStateObserver {
  124. func mockPumpManager(_ manager: MockKit.MockPumpManager, didUpdate state: MockKit.MockPumpManagerState) {
  125. guard !transitioningSuspendResumeInsulinDelivery else { return }
  126. basalDeliveryRate = state.basalDeliveryRate(at: Date())
  127. basalDeliveryState = manager.status.basalDeliveryState
  128. }
  129. func mockPumpManager(_ manager: MockKit.MockPumpManager, didUpdate status: LoopKit.PumpManagerStatus, oldStatus: LoopKit.PumpManagerStatus) {
  130. guard !transitioningSuspendResumeInsulinDelivery else { return }
  131. basalDeliveryRate = manager.state.basalDeliveryRate(at: Date())
  132. basalDeliveryState = status.basalDeliveryState
  133. }
  134. }
  135. extension MockPumpManagerState {
  136. func basalDeliveryRate(at now: Date) -> Double? {
  137. switch suspendState {
  138. case .resumed:
  139. if let tempBasal = unfinalizedTempBasal, !tempBasal.isFinished(at: now) {
  140. return tempBasal.rate
  141. } else {
  142. return basalRateSchedule?.value(at: now)
  143. }
  144. case .suspended:
  145. return nil
  146. }
  147. }
  148. }