dexcomSourceG7.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import Combine
  2. import Foundation
  3. import G7SensorKit
  4. import LoopKit
  5. import LoopKitUI
  6. final class DexcomSourceG7: GlucoseSource {
  7. private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
  8. private var glucoseStorage: GlucoseStorage!
  9. var glucoseManager: FetchGlucoseManager?
  10. var cgmManager: G7CGMManager?
  11. var cgmHasValidSensorSession: Bool = false
  12. private var promise: Future<[BloodGlucose], Error>.Promise?
  13. init(glucoseStorage: GlucoseStorage, glucoseManager: FetchGlucoseManager) {
  14. self.glucoseStorage = glucoseStorage
  15. self.glucoseManager = glucoseManager
  16. cgmManager = G7CGMManager()
  17. cgmManager?.cgmManagerDelegate = self
  18. }
  19. func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
  20. // dexcomManager.transmitter.resumeScanning()
  21. Just([]).eraseToAnyPublisher()
  22. }
  23. func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
  24. Future<[BloodGlucose], Error> { _ in
  25. self.processQueue.async {
  26. guard let cgmManager = self.cgmManager else { return }
  27. cgmManager.fetchNewDataIfNeeded { result in
  28. self.processCGMReadingResult(cgmManager, readingResult: result, tickBLE: false) {
  29. // nothing to do
  30. }
  31. }
  32. }
  33. }
  34. .timeout(60, scheduler: processQueue, options: nil, customError: nil)
  35. .replaceError(with: [])
  36. .replaceEmpty(with: [])
  37. .eraseToAnyPublisher()
  38. }
  39. deinit {
  40. // dexcomManager.transmitter.stopScanning()
  41. }
  42. }
  43. extension DexcomSourceG7: CGMManagerDelegate {
  44. func deviceManager(
  45. _: LoopKit.DeviceManager,
  46. logEventForDeviceIdentifier _: String?,
  47. type _: LoopKit.DeviceLogEntryType,
  48. message _: String,
  49. completion _: ((Error?) -> Void)?
  50. ) {}
  51. func issueAlert(_: LoopKit.Alert) {}
  52. func retractAlert(identifier _: LoopKit.Alert.Identifier) {}
  53. func doesIssuedAlertExist(identifier _: LoopKit.Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {}
  54. func lookupAllUnretracted(
  55. managerIdentifier _: String,
  56. completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
  57. ) {}
  58. func lookupAllUnacknowledgedUnretracted(
  59. managerIdentifier _: String,
  60. completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
  61. ) {}
  62. func recordRetractedAlert(_: LoopKit.Alert, at _: Date) {}
  63. func cgmManagerWantsDeletion(_: CGMManager) {}
  64. func cgmManager(_ manager: CGMManager, hasNew readingResult: CGMReadingResult) {
  65. dispatchPrecondition(condition: .onQueue(.main))
  66. processCGMReadingResult(manager, readingResult: readingResult, tickBLE: true) {
  67. // nothing to do
  68. }
  69. }
  70. func startDateToFilterNewData(for _: CGMManager) -> Date? {
  71. dispatchPrecondition(condition: .onQueue(.main))
  72. return glucoseStorage.lastGlucoseDate()
  73. }
  74. func cgmManagerDidUpdateState(_: CGMManager) {}
  75. func credentialStoragePrefix(for _: CGMManager) -> String {
  76. // return string unique to this instance of the CGMManager
  77. UUID().uuidString
  78. }
  79. func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
  80. DispatchQueue.main.async {
  81. if self.cgmHasValidSensorSession != status.hasValidSensorSession {
  82. self.cgmHasValidSensorSession = status.hasValidSensorSession
  83. }
  84. }
  85. }
  86. private func processCGMReadingResult(
  87. _: CGMManager,
  88. readingResult: CGMReadingResult,
  89. tickBLE: Bool,
  90. completion: @escaping () -> Void
  91. ) {
  92. warning(.deviceManager, "DEXCOMG7 - Process CGM Reading Result launched")
  93. switch readingResult {
  94. case let .newData(values):
  95. let bloodGlucose = values.compactMap { newGlucoseSample -> BloodGlucose? in
  96. let quantity = newGlucoseSample.quantity
  97. let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
  98. return BloodGlucose(
  99. _id: newGlucoseSample.syncIdentifier,
  100. sgv: value,
  101. direction: .init(trendType: newGlucoseSample.trend),
  102. date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),
  103. dateString: newGlucoseSample.date,
  104. unfiltered: nil,
  105. filtered: nil,
  106. noise: nil,
  107. glucose: value,
  108. type: "sgv"
  109. )
  110. }
  111. if tickBLE {
  112. glucoseManager?.updateGlucoseStore(newBloodGlucose: bloodGlucose)
  113. } else {
  114. promise?(.success(bloodGlucose))
  115. }
  116. completion()
  117. case .unreliableData:
  118. // loopManager.receivedUnreliableCGMReading()
  119. promise?(.failure(GlucoseDataError.unreliableData))
  120. completion()
  121. case .noData:
  122. promise?(.failure(GlucoseDataError.noData))
  123. completion()
  124. case let .error(error):
  125. promise?(.failure(error))
  126. completion()
  127. }
  128. }
  129. }
  130. // extension DexcomSourceG7 {
  131. // func sourceInfo() -> [String: Any]? {
  132. // [GlucoseSourceKey.description.rawValue: "Dexcom tramsmitter ID: \(transmitterID)"]
  133. // }
  134. // }