DeviceDataManager.swift 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import Combine
  2. import Foundation
  3. import LoopKit
  4. import LoopKitUI
  5. import MinimedKit
  6. import MockKit
  7. import OmniKit
  8. import SwiftDate
  9. import Swinject
  10. import UserNotifications
  11. protocol DeviceDataManager {
  12. var pumpManager: PumpManagerUI? { get set }
  13. var pumpDisplayState: CurrentValueSubject<PumpDisplayState?, Never> { get }
  14. var recommendsLoop: PassthroughSubject<Void, Never> { get }
  15. }
  16. private let staticPumpManagers: [PumpManagerUI.Type] = [
  17. MinimedPumpManager.self,
  18. OmnipodPumpManager.self,
  19. MockPumpManager.self
  20. ]
  21. private let staticPumpManagersByIdentifier: [String: PumpManagerUI.Type] = staticPumpManagers.reduce(into: [:]) { map, Type in
  22. map[Type.managerIdentifier] = Type
  23. }
  24. final class BaseDeviceDataManager: DeviceDataManager, Injectable {
  25. private let processQueue = DispatchQueue(label: "BaseDeviceDataManager.processQueue")
  26. @Injected() private var pumpHistoryStorage: PumpHistoryStorage!
  27. @Injected() private var storage: FileStorage!
  28. @Persisted(key: "BaseDeviceDataManager.lastEventDate") var lastEventDate: Date? = nil
  29. @Persisted(key: "BaseDeviceDataManager.lastHeartBeatTime") var lastHeartBeatTime: Date = .distantPast
  30. let recommendsLoop = PassthroughSubject<Void, Never>()
  31. var pumpManager: PumpManagerUI? {
  32. didSet {
  33. pumpManager?.pumpManagerDelegate = self
  34. pumpManager?.delegateQueue = processQueue
  35. UserDefaults.standard.pumpManagerRawValue = pumpManager?.rawValue
  36. if let pumpManager = pumpManager {
  37. pumpDisplayState.value = PumpDisplayState(name: pumpManager.localizedTitle, image: pumpManager.smallImage)
  38. } else {
  39. pumpDisplayState.value = nil
  40. }
  41. }
  42. }
  43. let pumpDisplayState = CurrentValueSubject<PumpDisplayState?, Never>(nil)
  44. var heartBeat: Timer!
  45. init(resolver: Resolver) {
  46. injectServices(resolver)
  47. setupPumpManager()
  48. UIDevice.current.isBatteryMonitoringEnabled = true
  49. if pumpManager is MockPumpManager {
  50. heartBeat = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
  51. debug(.deviceManager, "Timer Heartbeat")
  52. self.updatePumpData()
  53. }
  54. }
  55. updatePumpData()
  56. }
  57. func setupPumpManager() {
  58. if let pumpManagerRawValue = UserDefaults.standard.pumpManagerRawValue {
  59. pumpManager = pumpManagerFromRawValue(pumpManagerRawValue)
  60. }
  61. }
  62. private func updatePumpData() {
  63. let now = Date()
  64. guard now.timeIntervalSince(lastHeartBeatTime) >= Config.loopInterval else { return }
  65. pumpManager?.ensureCurrentPumpData {
  66. debug(.deviceManager, "Pump Data updated")
  67. self.lastHeartBeatTime = now
  68. }
  69. }
  70. private func pumpManagerFromRawValue(_ rawValue: [String: Any]) -> PumpManagerUI? {
  71. guard let rawState = rawValue["state"] as? PumpManager.RawStateValue,
  72. let Manager = pumpManagerTypeFromRawValue(rawValue)
  73. else {
  74. return nil
  75. }
  76. return Manager.init(rawState: rawState) as? PumpManagerUI
  77. }
  78. private func pumpManagerTypeFromRawValue(_ rawValue: [String: Any]) -> PumpManager.Type? {
  79. guard let managerIdentifier = rawValue["managerIdentifier"] as? String else {
  80. return nil
  81. }
  82. return staticPumpManagersByIdentifier[managerIdentifier]
  83. }
  84. }
  85. extension BaseDeviceDataManager: PumpManagerDelegate {
  86. func pumpManager(_: PumpManager, didAdjustPumpClockBy _: TimeInterval) {
  87. // log.debug("didAdjustPumpClockBy %@", adjustment)
  88. }
  89. func pumpManagerDidUpdateState(_ pumpManager: PumpManager) {
  90. UserDefaults.standard.pumpManagerRawValue = pumpManager.rawValue
  91. }
  92. func pumpManagerBLEHeartbeatDidFire(_: PumpManager) {
  93. debug(.deviceManager, "Pump Heartbeat")
  94. updatePumpData()
  95. }
  96. func pumpManagerMustProvideBLEHeartbeat(_: PumpManager) -> Bool {
  97. true
  98. }
  99. func pumpManager(_: PumpManager, didUpdate status: PumpManagerStatus, oldStatus _: PumpManagerStatus) {
  100. debug(.deviceManager, "New pump status Bolus: \(status.bolusState)")
  101. debug(.deviceManager, "New pump status Basal: \(String(describing: status.basalDeliveryState))")
  102. }
  103. func pumpManagerWillDeactivate(_: PumpManager) {
  104. pumpManager = nil
  105. }
  106. func pumpManager(_: PumpManager, didUpdatePumpRecordsBasalProfileStartEvents _: Bool) {}
  107. func pumpManager(_: PumpManager, didError error: PumpManagerError) {
  108. info(.deviceManager, "error: \(error.localizedDescription)")
  109. }
  110. func pumpManager(
  111. _: PumpManager,
  112. hasNewPumpEvents events: [NewPumpEvent],
  113. lastReconciliation _: Date?,
  114. completion: @escaping (_ error: Error?) -> Void
  115. ) {
  116. dispatchPrecondition(condition: .onQueue(processQueue))
  117. pumpHistoryStorage.storePumpEvents(events)
  118. lastEventDate = events.last?.date
  119. completion(nil)
  120. }
  121. func pumpManager(
  122. _: PumpManager,
  123. didReadReservoirValue units: Double,
  124. at date: Date,
  125. completion: @escaping (Result<
  126. (newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool),
  127. Error
  128. >) -> Void
  129. ) {
  130. dispatchPrecondition(condition: .onQueue(processQueue))
  131. debug(.deviceManager, "Reservoir Value \(units), at: \(date)")
  132. storage.save(Decimal(units), as: OpenAPS.Monitor.reservoir)
  133. let batteryPercent = Int((pumpManager?.status.pumpBatteryChargeRemaining ?? 1) * 100)
  134. let battery = Battery(percent: batteryPercent, voltage: nil, string: batteryPercent >= 10 ? .normal : .low)
  135. storage.save(battery, as: OpenAPS.Monitor.battery)
  136. completion(.success((
  137. newValue: Reservoir(startDate: Date(), unitVolume: units),
  138. lastValue: nil,
  139. areStoredValuesContinuous: true
  140. )))
  141. }
  142. func pumpManagerRecommendsLoop(_: PumpManager) {
  143. dispatchPrecondition(condition: .onQueue(processQueue))
  144. debug(.deviceManager, "Recomends loop")
  145. recommendsLoop.send()
  146. }
  147. func startDateToFilterNewPumpEvents(for _: PumpManager) -> Date {
  148. lastEventDate ?? Date().addingTimeInterval(-2.hours.timeInterval)
  149. }
  150. }
  151. // MARK: - DeviceManagerDelegate
  152. extension BaseDeviceDataManager: DeviceManagerDelegate {
  153. func scheduleNotification(
  154. for _: DeviceManager,
  155. identifier: String,
  156. content: UNNotificationContent,
  157. trigger: UNNotificationTrigger?
  158. ) {
  159. let request = UNNotificationRequest(
  160. identifier: identifier,
  161. content: content,
  162. trigger: trigger
  163. )
  164. DispatchQueue.main.async {
  165. UNUserNotificationCenter.current().add(request)
  166. }
  167. }
  168. func clearNotification(for _: DeviceManager, identifier: String) {
  169. DispatchQueue.main.async {
  170. UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
  171. }
  172. }
  173. func removeNotificationRequests(for _: DeviceManager, identifiers: [String]) {
  174. DispatchQueue.main.async {
  175. UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
  176. }
  177. }
  178. func deviceManager(
  179. _: DeviceManager,
  180. logEventForDeviceIdentifier _: String?,
  181. type _: DeviceLogEntryType,
  182. message _: String,
  183. completion _: ((Error?) -> Void)?
  184. ) {}
  185. }
  186. // MARK: - AlertPresenter
  187. extension BaseDeviceDataManager: AlertPresenter {
  188. func issueAlert(_: Alert) {}
  189. func retractAlert(identifier _: Alert.Identifier) {}
  190. }