DexcomSource.swift 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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 ?? "8MBPEY")
  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() -> 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. .eraseToAnyPublisher()
  24. }
  25. deinit {
  26. dexcomManager.transmitter.stopScanning()
  27. }
  28. }
  29. extension DexcomSource: TransmitterManagerDelegate {
  30. func transmitterManager(_: TransmitterManager, didFailWith error: Error) {
  31. promise?(.failure(error))
  32. }
  33. func transmitterManager(_: TransmitterManager, didRead glucose: [CGMBLEKit.Glucose]) {
  34. let bloodGlucose = glucose.compactMap { glucose -> BloodGlucose? in
  35. guard let quantity = glucose.glucose else {
  36. return nil
  37. }
  38. let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
  39. return BloodGlucose(
  40. sgv: value,
  41. direction: .init(trend: glucose.trend),
  42. date: Decimal(Int(glucose.readDate.timeIntervalSince1970 * 1000)),
  43. dateString: glucose.readDate,
  44. filtered: nil,
  45. noise: nil,
  46. glucose: value
  47. )
  48. }
  49. promise?(.success(bloodGlucose))
  50. }
  51. }
  52. extension BloodGlucose.Direction {
  53. init(trend: Int) {
  54. guard trend < Int(Int8.max) else {
  55. self = .none
  56. return
  57. }
  58. switch trend {
  59. case let x where x <= -30:
  60. self = .doubleDown
  61. case let x where x <= -20:
  62. self = .singleDown
  63. case let x where x <= -10:
  64. self = .fortyFiveDown
  65. case let x where x < 10:
  66. self = .flat
  67. case let x where x < 20:
  68. self = .fortyFiveUp
  69. case let x where x < 30:
  70. self = .singleUp
  71. default:
  72. self = .doubleUp
  73. }
  74. }
  75. }