OmnipodPumpManagerState.swift 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. //
  2. // OmnipodPumpManagerState.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 8/4/18.
  6. // Copyright © 2018 Pete Schwamb. All rights reserved.
  7. //
  8. import RileyLinkKit
  9. import RileyLinkBLEKit
  10. import LoopKit
  11. public struct OmnipodPumpManagerState: RawRepresentable, Equatable {
  12. public typealias RawValue = PumpManager.RawStateValue
  13. public static let version = 2
  14. public var podState: PodState?
  15. public var pairingAttemptAddress: UInt32?
  16. public var timeZone: TimeZone
  17. public var basalSchedule: BasalSchedule
  18. public var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState?
  19. public var unstoredDoses: [UnfinalizedDose]
  20. public var expirationReminderDate: Date?
  21. public var confirmationBeeps: Bool
  22. public var automaticBolusBeeps: Bool
  23. // Temporal state not persisted
  24. internal enum EngageablePumpState: Equatable {
  25. case engaging
  26. case disengaging
  27. case stable
  28. }
  29. internal var suspendEngageState: EngageablePumpState = .stable
  30. internal var bolusEngageState: EngageablePumpState = .stable
  31. internal var tempBasalEngageState: EngageablePumpState = .stable
  32. internal var lastPumpDataReportDate: Date?
  33. internal var insulinType: InsulinType
  34. public var rileyLinkBatteryAlertLevel: Int?
  35. public var lastRileyLinkBatteryAlertDate: Date = .distantPast
  36. // MARK: -
  37. public init(podState: PodState?, timeZone: TimeZone, basalSchedule: BasalSchedule, rileyLinkConnectionManagerState: RileyLinkConnectionManagerState?, insulinType: InsulinType) {
  38. self.podState = podState
  39. self.timeZone = timeZone
  40. self.basalSchedule = basalSchedule
  41. self.rileyLinkConnectionManagerState = rileyLinkConnectionManagerState
  42. self.unstoredDoses = []
  43. self.confirmationBeeps = false
  44. self.automaticBolusBeeps = false
  45. self.insulinType = insulinType
  46. }
  47. public init?(rawValue: RawValue) {
  48. guard let version = rawValue["version"] as? Int else {
  49. return nil
  50. }
  51. let basalSchedule: BasalSchedule
  52. if version == 1 {
  53. // migrate: basalSchedule moved from podState to oppm state
  54. if let podStateRaw = rawValue["podState"] as? PodState.RawValue,
  55. let rawBasalSchedule = podStateRaw["basalSchedule"] as? BasalSchedule.RawValue,
  56. let migrateSchedule = BasalSchedule(rawValue: rawBasalSchedule)
  57. {
  58. basalSchedule = migrateSchedule
  59. } else {
  60. return nil
  61. }
  62. } else {
  63. guard let rawBasalSchedule = rawValue["basalSchedule"] as? BasalSchedule.RawValue,
  64. let schedule = BasalSchedule(rawValue: rawBasalSchedule) else
  65. {
  66. return nil
  67. }
  68. basalSchedule = schedule
  69. }
  70. let podState: PodState?
  71. if let podStateRaw = rawValue["podState"] as? PodState.RawValue {
  72. podState = PodState(rawValue: podStateRaw)
  73. } else {
  74. podState = nil
  75. }
  76. let timeZone: TimeZone
  77. if let timeZoneSeconds = rawValue["timeZone"] as? Int,
  78. let tz = TimeZone(secondsFromGMT: timeZoneSeconds) {
  79. timeZone = tz
  80. } else {
  81. timeZone = TimeZone.currentFixed
  82. }
  83. let rileyLinkConnectionManagerState: RileyLinkConnectionManagerState?
  84. if let rileyLinkConnectionManagerStateRaw = rawValue["rileyLinkConnectionManagerState"] as? RileyLinkConnectionManagerState.RawValue {
  85. rileyLinkConnectionManagerState = RileyLinkConnectionManagerState(rawValue: rileyLinkConnectionManagerStateRaw)
  86. } else {
  87. rileyLinkConnectionManagerState = nil
  88. }
  89. var insulinType: InsulinType?
  90. if let rawInsulinType = rawValue["insulinType"] as? InsulinType.RawValue {
  91. insulinType = InsulinType(rawValue: rawInsulinType)
  92. }
  93. self.init(
  94. podState: podState,
  95. timeZone: timeZone,
  96. basalSchedule: basalSchedule,
  97. rileyLinkConnectionManagerState: rileyLinkConnectionManagerState,
  98. insulinType: insulinType ?? .novolog
  99. )
  100. if let expirationReminderDate = rawValue["expirationReminderDate"] as? Date {
  101. self.expirationReminderDate = expirationReminderDate
  102. } else if let expiresAt = podState?.expiresAt {
  103. self.expirationReminderDate = expiresAt.addingTimeInterval(-Pod.expirationReminderAlertDefaultTimeBeforeExpiration)
  104. }
  105. if let rawUnstoredDoses = rawValue["unstoredDoses"] as? [UnfinalizedDose.RawValue] {
  106. self.unstoredDoses = rawUnstoredDoses.compactMap( { UnfinalizedDose(rawValue: $0) } )
  107. } else {
  108. self.unstoredDoses = []
  109. }
  110. self.confirmationBeeps = rawValue["confirmationBeeps"] as? Bool ?? rawValue["bolusBeeps"] as? Bool ?? false
  111. self.automaticBolusBeeps = rawValue["automaticBolusBeeps"] as? Bool ?? false
  112. if let pairingAttemptAddress = rawValue["pairingAttemptAddress"] as? UInt32 {
  113. self.pairingAttemptAddress = pairingAttemptAddress
  114. }
  115. rileyLinkBatteryAlertLevel = rawValue["rileyLinkBatteryAlertLevel"] as? Int
  116. lastRileyLinkBatteryAlertDate = rawValue["lastRileyLinkBatteryAlertDate"] as? Date ?? Date.distantPast
  117. }
  118. public var rawValue: RawValue {
  119. var value: [String : Any] = [
  120. "version": OmnipodPumpManagerState.version,
  121. "timeZone": timeZone.secondsFromGMT(),
  122. "basalSchedule": basalSchedule.rawValue,
  123. "unstoredDoses": unstoredDoses.map { $0.rawValue },
  124. "confirmationBeeps": confirmationBeeps,
  125. "automaticBolusBeeps": automaticBolusBeeps,
  126. "insulinType": insulinType.rawValue,
  127. ]
  128. value["podState"] = podState?.rawValue
  129. value["expirationReminderDate"] = expirationReminderDate
  130. value["rileyLinkConnectionManagerState"] = rileyLinkConnectionManagerState?.rawValue
  131. value["pairingAttemptAddress"] = pairingAttemptAddress
  132. value["rileyLinkBatteryAlertLevel"] = rileyLinkBatteryAlertLevel
  133. value["lastRileyLinkBatteryAlertDate"] = lastRileyLinkBatteryAlertDate
  134. return value
  135. }
  136. }
  137. extension OmnipodPumpManagerState {
  138. var hasActivePod: Bool {
  139. return podState?.isActive == true
  140. }
  141. var hasSetupPod: Bool {
  142. return podState?.isSetupComplete == true
  143. }
  144. var isPumpDataStale: Bool {
  145. let pumpStatusAgeTolerance = TimeInterval(minutes: 6)
  146. let pumpDataAge = -(self.lastPumpDataReportDate ?? .distantPast).timeIntervalSinceNow
  147. return pumpDataAge > pumpStatusAgeTolerance
  148. }
  149. }
  150. extension OmnipodPumpManagerState: CustomDebugStringConvertible {
  151. public var debugDescription: String {
  152. return [
  153. "## OmnipodPumpManagerState",
  154. "* timeZone: \(timeZone)",
  155. "* basalSchedule: \(String(describing: basalSchedule))",
  156. "* expirationReminderDate: \(String(describing: expirationReminderDate))",
  157. "* unstoredDoses: \(String(describing: unstoredDoses))",
  158. "* suspendEngageState: \(String(describing: suspendEngageState))",
  159. "* bolusEngageState: \(String(describing: bolusEngageState))",
  160. "* tempBasalEngageState: \(String(describing: tempBasalEngageState))",
  161. "* lastPumpDataReportDate: \(String(describing: lastPumpDataReportDate))",
  162. "* isPumpDataStale: \(String(describing: isPumpDataStale))",
  163. "* confirmationBeeps: \(String(describing: confirmationBeeps))",
  164. "* automaticBolusBeeps: \(String(describing: automaticBolusBeeps))",
  165. "* pairingAttemptAddress: \(String(describing: pairingAttemptAddress))",
  166. "* insulinType: \(String(describing: insulinType))",
  167. "* rileyLinkBatteryAlertLevel: \(String(describing: rileyLinkBatteryAlertLevel))",
  168. "* lastRileyLinkBatteryAlertDate \(String(describing: lastRileyLinkBatteryAlertDate))",
  169. String(reflecting: podState),
  170. String(reflecting: rileyLinkConnectionManagerState),
  171. ].joined(separator: "\n")
  172. }
  173. }