BloodGlucose.swift 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import Foundation
  2. import HealthKit
  3. import LoopKit
  4. struct BloodGlucose: JSON, Identifiable, Hashable {
  5. enum Direction: String, JSON {
  6. case tripleUp = "TripleUp"
  7. case doubleUp = "DoubleUp"
  8. case singleUp = "SingleUp"
  9. case fortyFiveUp = "FortyFiveUp"
  10. case flat = "Flat"
  11. case fortyFiveDown = "FortyFiveDown"
  12. case singleDown = "SingleDown"
  13. case doubleDown = "DoubleDown"
  14. case tripleDown = "TripleDown"
  15. case none = "NONE"
  16. case notComputable = "NOT COMPUTABLE"
  17. case rateOutOfRange = "RATE OUT OF RANGE"
  18. init?(from string: String) {
  19. switch string {
  20. case "\u{2191}\u{2191}\u{2191}",
  21. "↑↑↑",
  22. "TripleUp":
  23. self = .tripleUp
  24. case "\u{2191}\u{2191}",
  25. "↑↑",
  26. "DoubleUp":
  27. self = .doubleUp
  28. case "\u{2191}",
  29. "↑",
  30. "SingleUp":
  31. self = .singleUp
  32. case "\u{2197}",
  33. "↗︎",
  34. "FortyFiveUp":
  35. self = .fortyFiveUp
  36. case "\u{2192}",
  37. "→",
  38. "Flat":
  39. self = .flat
  40. case "\u{2198}",
  41. "↘︎",
  42. "FortyFiveDown":
  43. self = .fortyFiveDown
  44. case "\u{2193}",
  45. "↓",
  46. "SingleDown":
  47. self = .singleDown
  48. case "\u{2193}\u{2193}",
  49. "↓↓",
  50. "DoubleDown":
  51. self = .doubleDown
  52. case "\u{2193}\u{2193}\u{2193}",
  53. "↓↓↓",
  54. "TripleDown":
  55. self = .tripleDown
  56. case "\u{2194}",
  57. "↔︎",
  58. "NONE":
  59. self = .none
  60. case "NOT COMPUTABLE":
  61. self = .notComputable
  62. case "RATE OUT OF RANGE":
  63. self = .rateOutOfRange
  64. default:
  65. return nil
  66. }
  67. }
  68. }
  69. var _id: String?
  70. var id: String {
  71. _id ?? UUID().uuidString
  72. }
  73. var sgv: Int?
  74. var direction: Direction?
  75. let date: Decimal
  76. let dateString: Date
  77. let unfiltered: Decimal?
  78. let filtered: Decimal?
  79. let noise: Int?
  80. var glucose: Int?
  81. var type: String? = nil
  82. var activationDate: Date? = nil
  83. var sessionStartDate: Date? = nil
  84. var transmitterID: String? = nil
  85. var isStateValid: Bool { sgv ?? 0 >= 39 && noise ?? 1 != 4 }
  86. static func == (lhs: BloodGlucose, rhs: BloodGlucose) -> Bool {
  87. lhs.dateString == rhs.dateString
  88. }
  89. func hash(into hasher: inout Hasher) {
  90. hasher.combine(dateString)
  91. }
  92. }
  93. enum GlucoseUnits: String, JSON, Equatable {
  94. case mgdL = "mg/dL"
  95. case mmolL = "mmol/L"
  96. static let exchangeRate: Decimal = 0.0555
  97. }
  98. extension Int {
  99. var asMmolL: Decimal {
  100. Decimal(self) * GlucoseUnits.exchangeRate
  101. }
  102. }
  103. extension Decimal {
  104. var asMmolL: Decimal {
  105. self * GlucoseUnits.exchangeRate
  106. }
  107. var asMgdL: Decimal {
  108. self / GlucoseUnits.exchangeRate
  109. }
  110. }
  111. extension Double {
  112. var asMmolL: Decimal {
  113. Decimal(self) * GlucoseUnits.exchangeRate
  114. }
  115. var asMgdL: Decimal {
  116. Decimal(self) / GlucoseUnits.exchangeRate
  117. }
  118. }
  119. extension BloodGlucose: SavitzkyGolaySmoothable {
  120. var value: Double {
  121. get {
  122. Double(glucose ?? 0)
  123. }
  124. set {
  125. glucose = Int(newValue)
  126. sgv = Int(newValue)
  127. }
  128. }
  129. }
  130. extension BloodGlucose {
  131. func convertStoredGlucoseSample(device: HKDevice?) -> StoredGlucoseSample {
  132. StoredGlucoseSample(
  133. syncIdentifier: id,
  134. startDate: dateString.date,
  135. quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: Double(glucose!)),
  136. device: device
  137. )
  138. }
  139. }