StateIntentRequest.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import AppIntents
  2. import Foundation
  3. enum StateIntentError: Error {
  4. case StateIntentUnknownError
  5. case NoBG
  6. case NoIOBCOB
  7. }
  8. @available(iOS 16, *) struct StateiAPSResults: AppEntity {
  9. static var defaultQuery = StateBGQuery()
  10. static var typeDisplayRepresentation: TypeDisplayRepresentation = "iAPS State Result"
  11. var displayRepresentation: DisplayRepresentation {
  12. DisplayRepresentation(title: "\(glucose)")
  13. }
  14. var id: UUID
  15. @Property(title: "Glucose") var glucose: String
  16. @Property(title: "Trend") var trend: String
  17. @Property(title: "Delta") var delta: String
  18. @Property(title: "Date") var date: Date
  19. @Property(title: "IOB") var iob: Double?
  20. @Property(title: "COB") var cob: Double?
  21. @Property(title: "unit") var unit: String?
  22. init(glucose: String, trend: String, delta: String, date: Date, iob: Double, cob: Double, unit: GlucoseUnits) {
  23. id = UUID()
  24. self.glucose = glucose
  25. self.trend = trend
  26. self.delta = delta
  27. self.date = date
  28. self.iob = iob
  29. self.cob = cob
  30. self.unit = unit.rawValue
  31. }
  32. }
  33. @available(iOS 16.0, *) struct StateBGQuery: EntityQuery {
  34. func entities(for _: [StateiAPSResults.ID]) async throws -> [StateiAPSResults] {
  35. []
  36. }
  37. func suggestedEntities() async throws -> [StateiAPSResults] {
  38. []
  39. }
  40. }
  41. @available(iOS 16.0, *) final class StateIntentRequest: BaseIntentsRequest {
  42. let moc = CoreDataStack.shared.backgroundContext
  43. func getLastGlucose() throws -> (dateGlucose: Date, glucose: String, trend: String, delta: String) {
  44. do {
  45. let results = try moc.fetch(GlucoseStored.fetch(NSPredicate.predicateFor30MinAgo, ascending: false, fetchLimit: 2))
  46. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.succeeded) fetched latest glucose")
  47. guard let lastValue = results.first else { throw StateIntentError.NoBG }
  48. /// calculate delta
  49. let lastGlucose = lastValue.glucose
  50. let secondLastGlucose = results.dropFirst().first?.glucose
  51. let delta = results.count > 1 ? (lastGlucose - (secondLastGlucose ?? 0)) : nil
  52. /// formatting
  53. let units = settingsManager.settings.units
  54. let glucoseAsString = glucoseFormatter.string(from: Double(
  55. units == .mmolL ? Decimal(lastGlucose)
  56. .asMmolL : Decimal(lastGlucose)
  57. ) as NSNumber)!
  58. let directionAsString = lastValue.direction ?? "none"
  59. let deltaAsString = delta
  60. .map {
  61. self.deltaFormatter
  62. .string(from: Double(
  63. units == .mmolL ? Decimal($0)
  64. .asMmolL : Decimal($0)
  65. ) as NSNumber)!
  66. } ?? "--"
  67. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.succeeded) fetched latest 2 glucose values")
  68. return (lastValue.date ?? Date(), glucoseAsString, directionAsString, deltaAsString)
  69. } catch {
  70. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.failed) failed to fetch latest 2 glucose values")
  71. return (Date(), "", "", "")
  72. }
  73. }
  74. func getIobAndCob() throws -> (iob: Double, cob: Double) {
  75. do {
  76. let results = try moc.fetch(OrefDetermination.fetch(NSPredicate.enactedDetermination))
  77. let iobAsDouble = Double(truncating: (results.first?.iob ?? 0.0) as NSNumber)
  78. let cobAsDouble = Double(truncating: (results.first?.cob ?? 0) as NSNumber)
  79. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.succeeded) fetched latest cob and iob")
  80. return (iobAsDouble, cobAsDouble)
  81. } catch {
  82. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.failed) failed to fetch latest cob and iob")
  83. return (0.0, 0.0)
  84. }
  85. }
  86. private var glucoseFormatter: NumberFormatter {
  87. let formatter = NumberFormatter()
  88. formatter.numberStyle = .decimal
  89. formatter.maximumFractionDigits = 0
  90. if settingsManager.settings.units == .mmolL {
  91. formatter.minimumFractionDigits = 1
  92. formatter.maximumFractionDigits = 1
  93. }
  94. formatter.roundingMode = .halfUp
  95. return formatter
  96. }
  97. private var deltaFormatter: NumberFormatter {
  98. let formatter = NumberFormatter()
  99. formatter.numberStyle = .decimal
  100. formatter.maximumFractionDigits = 1
  101. formatter.positivePrefix = "+"
  102. return formatter
  103. }
  104. }