DexcomSource.swift 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import CGMBLEKit
  2. import Combine
  3. import Foundation
  4. final class DexcomSource: GlucoseSource {
  5. private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
  6. private let dexcomManager = TransmitterManager(
  7. state: TransmitterManagerState(transmitterID: UserDefaults.standard.dexcomTransmitterID ?? "000000")
  8. )
  9. private var promise: Future<[BloodGlucose], Error>.Promise?
  10. init() {
  11. dexcomManager.delegate = self
  12. }
  13. var transmitterID: String {
  14. dexcomManager.transmitter.ID
  15. }
  16. func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
  17. dexcomManager.transmitter.resumeScanning()
  18. return Future<[BloodGlucose], Error> { [weak self] promise in
  19. self?.promise = promise
  20. }
  21. .timeout(60, scheduler: processQueue, options: nil, customError: nil)
  22. .replaceError(with: [])
  23. .replaceEmpty(with: [])
  24. .eraseToAnyPublisher()
  25. }
  26. deinit {
  27. dexcomManager.transmitter.stopScanning()
  28. }
  29. }
  30. extension DexcomSource: TransmitterManagerDelegate {
  31. func transmitterManager(_: TransmitterManager, didFailWith error: Error) {
  32. promise?(.failure(error))
  33. }
  34. func transmitterManager(_: TransmitterManager, didRead glucose: [CGMBLEKit.Glucose]) {
  35. let bloodGlucose = glucose.compactMap { glucose -> BloodGlucose? in
  36. guard let quantity = glucose.glucose else {
  37. return nil
  38. }
  39. let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
  40. return BloodGlucose(
  41. _id: glucose.syncIdentifier,
  42. sgv: value,
  43. direction: .init(trend: glucose.trend),
  44. date: Decimal(Int(glucose.readDate.timeIntervalSince1970 * 1000)),
  45. dateString: glucose.readDate,
  46. unfiltered: nil,
  47. filtered: nil,
  48. noise: nil,
  49. glucose: value,
  50. type: "sgv",
  51. activationDate: glucose.activationDate,
  52. sessionStartDate: glucose.sessionStartDate,
  53. transmitterID: glucose.transmitterID
  54. )
  55. }
  56. promise?(.success(bloodGlucose))
  57. }
  58. func sourceInfo() -> [String: Any]? {
  59. [GlucoseSourceKey.description.rawValue: "Dexcom tramsmitter ID: \(transmitterID)"]
  60. }
  61. }
  62. extension BloodGlucose.Direction {
  63. init(trend: Int) {
  64. guard trend < Int(Int8.max) else {
  65. self = .none
  66. return
  67. }
  68. switch trend {
  69. case let x where x <= -30:
  70. self = .doubleDown
  71. case let x where x <= -20:
  72. self = .singleDown
  73. case let x where x <= -10:
  74. self = .fortyFiveDown
  75. case let x where x < 10:
  76. self = .flat
  77. case let x where x < 20:
  78. self = .fortyFiveUp
  79. case let x where x < 30:
  80. self = .singleUp
  81. default:
  82. self = .doubleUp
  83. }
  84. }
  85. }