LibreTransmitterSource.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import Combine
  2. import Foundation
  3. import LibreTransmitter
  4. import LibreTransmitterUI
  5. import LoopKit
  6. import LoopKitUI
  7. import Swinject
  8. // protocol LibreTransmitterSource: GlucoseSource {
  9. // var manager: LibreTransmitterManager? { get set }
  10. // }
  11. final class LibreTransmitterSource: GlucoseSource {
  12. private let processQueue = DispatchQueue(label: "BaseLibreTransmitterSource.processQueue")
  13. private var glucoseStorage: GlucoseStorage!
  14. var glucoseManager: FetchGlucoseManager?
  15. var cgmManager: CGMManagerUI?
  16. var cgmType: CGMType = .libreTransmitter
  17. var cgmHasValidSensorSession: Bool = false
  18. // @Injected() var glucoseStorage: GlucoseStorage!
  19. // @Injected() var calibrationService: CalibrationService!
  20. private var promise: Future<[BloodGlucose], Error>.Promise?
  21. // @Persisted(key: "LibreTransmitterManager.configured") private(set) var configured = false
  22. init(glucoseStorage: GlucoseStorage, glucoseManager: FetchGlucoseManager) {
  23. self.glucoseStorage = glucoseStorage
  24. self.glucoseManager = glucoseManager
  25. cgmManager = LibreTransmitterManagerV3()
  26. cgmManager?.cgmManagerDelegate = self
  27. cgmManager?.delegateQueue = processQueue
  28. }
  29. func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
  30. Future<[BloodGlucose], Error> { [weak self] promise in
  31. self?.promise = promise
  32. }
  33. .timeout(60, scheduler: processQueue, options: nil, customError: nil)
  34. .replaceError(with: [])
  35. .replaceEmpty(with: [])
  36. .eraseToAnyPublisher()
  37. }
  38. func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
  39. Future<[BloodGlucose], Error> { _ in
  40. self.processQueue.async {
  41. guard let cgmManager = self.cgmManager else { return }
  42. cgmManager.fetchNewDataIfNeeded { result in
  43. self.processCGMReadingResult(cgmManager, readingResult: result) {
  44. // nothing to do
  45. }
  46. }
  47. }
  48. }
  49. .timeout(60, scheduler: processQueue, options: nil, customError: nil)
  50. .replaceError(with: [])
  51. .replaceEmpty(with: [])
  52. .eraseToAnyPublisher()
  53. }
  54. // func sourceInfo() -> [String: Any]? {
  55. // if let battery = manager?.battery {
  56. // return ["transmitterBattery": battery]
  57. // }
  58. // return nil
  59. // }
  60. }
  61. extension LibreTransmitterSource: CGMManagerDelegate {
  62. private func processCGMReadingResult(
  63. _: CGMManager,
  64. readingResult: CGMReadingResult,
  65. completion: @escaping () -> Void
  66. ) {
  67. switch readingResult {
  68. case let .newData(values):
  69. if let libreManager = cgmManager as? LibreTransmitterManagerV3 {
  70. let glucose = values.compactMap { newGlucoseSample -> BloodGlucose in
  71. let quantity = newGlucoseSample.quantity
  72. let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
  73. return BloodGlucose(
  74. _id: UUID().uuidString,
  75. sgv: value,
  76. direction: .init(trendType: newGlucoseSample.trend),
  77. date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),
  78. dateString: newGlucoseSample.date,
  79. unfiltered: Decimal(value),
  80. filtered: nil,
  81. noise: nil,
  82. glucose: value,
  83. type: "sgv",
  84. activationDate: libreManager.sensorInfoObservable.activatedAt,
  85. sessionStartDate: libreManager.sensorInfoObservable.activatedAt,
  86. transmitterID: libreManager.sensorInfoObservable.sensorSerial
  87. )
  88. }
  89. NSLog("Debug Libre \(glucose)")
  90. promise?(.success(glucose))
  91. completion()
  92. }
  93. case .unreliableData:
  94. promise?(.failure(GlucoseDataError.unreliableData))
  95. completion()
  96. case .noData:
  97. promise?(.failure(GlucoseDataError.noData))
  98. completion()
  99. case let .error(error):
  100. promise?(.failure(error))
  101. completion()
  102. }
  103. }
  104. func cgmManager(_ manager: LoopKit.CGMManager, hasNew readingResult: LoopKit.CGMReadingResult) {
  105. dispatchPrecondition(condition: .onQueue(processQueue))
  106. processCGMReadingResult(manager, readingResult: readingResult) {
  107. debug(.deviceManager, "Libre transmitter - Direct return done")
  108. }
  109. }
  110. func cgmManagerDidUpdateState(_: LoopKit.CGMManager) {
  111. // TODO: if useful in regard of configuration
  112. }
  113. func cgmManager(_: LoopKit.CGMManager, didUpdate status: LoopKit.CGMManagerStatus) {
  114. DispatchQueue.main.async {
  115. if self.cgmHasValidSensorSession != status.hasValidSensorSession {
  116. self.cgmHasValidSensorSession = status.hasValidSensorSession
  117. }
  118. }
  119. }
  120. func startDateToFilterNewData(for _: LoopKit.CGMManager) -> Date? {
  121. dispatchPrecondition(condition: .onQueue(processQueue))
  122. return glucoseStorage.lastGlucoseDate()
  123. }
  124. func cgmManager(_: LoopKit.CGMManager, hasNew events: [LoopKit.PersistedCgmEvent]) {
  125. // TODO: Events in APS ?
  126. // currently only display in log the date of the event
  127. events.forEach { debug(.deviceManager, "events from CGM at \($0.date)") }
  128. }
  129. func cgmManagerWantsDeletion(_ manager: LoopKit.CGMManager) {
  130. dispatchPrecondition(condition: .onQueue(processQueue))
  131. debug(.deviceManager, " CGM Manager with identifier \(manager.pluginIdentifier) wants deletion")
  132. glucoseManager?.cgmGlucoseSourceType = nil
  133. }
  134. func credentialStoragePrefix(for _: LoopKit.CGMManager) -> String {
  135. UUID().uuidString
  136. }
  137. func deviceManager(
  138. _: LoopKit.DeviceManager,
  139. logEventForDeviceIdentifier deviceIdentifier: String?,
  140. type _: LoopKit.DeviceLogEntryType,
  141. message: String,
  142. completion _: ((Error?) -> Void)?
  143. ) {
  144. debug(.deviceManager, "device Manager for \(String(describing: deviceIdentifier)) : \(message)")
  145. }
  146. func issueAlert(_: LoopKit.Alert) {}
  147. func retractAlert(identifier _: LoopKit.Alert.Identifier) {}
  148. func doesIssuedAlertExist(identifier _: LoopKit.Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {}
  149. func lookupAllUnretracted(
  150. managerIdentifier _: String,
  151. completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
  152. ) {}
  153. func lookupAllUnacknowledgedUnretracted(
  154. managerIdentifier _: String,
  155. completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
  156. ) {}
  157. func recordRetractedAlert(_: LoopKit.Alert, at _: Date) {}
  158. }