CGMStateModel.swift 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import CGMBLEKit
  2. import Combine
  3. import G7SensorKit
  4. import LoopKitUI
  5. import SwiftUI
  6. struct cgmName: Identifiable, Hashable {
  7. var id: String
  8. var type: CGMType
  9. var displayName: String
  10. var subtitle: String
  11. }
  12. let cgmDefaultName = cgmName(
  13. id: CGMType.none.id,
  14. type: .none,
  15. displayName: CGMType.none.displayName,
  16. subtitle: CGMType.none.subtitle
  17. )
  18. struct EmptyCompletionNotifying: CompletionNotifying {
  19. var completionDelegate: (any LoopKitUI.CompletionDelegate)?
  20. }
  21. extension CGM {
  22. final class StateModel: BaseStateModel<Provider> {
  23. @Injected() var cgmManager: FetchGlucoseManager!
  24. @Injected() var pluginCGMManager: PluginManager!
  25. @Injected() private var broadcaster: Broadcaster!
  26. @Injected() var nightscoutManager: NightscoutManager!
  27. @Published var units: GlucoseUnits = .mgdL
  28. @Published var setupCGM: Bool = false
  29. @Published var cgmCurrent = cgmDefaultName
  30. @Published var smoothGlucose = false
  31. @Published var cgmTransmitterDeviceAddress: String? = nil
  32. @Published var listOfCGM: [cgmName] = []
  33. @Published var url: URL?
  34. override func subscribe() {
  35. units = settingsManager.settings.units
  36. // collect the list of CGM available with plugins and CGMType defined manually
  37. listOfCGM = (
  38. CGMType.allCases.filter { $0 != CGMType.plugin }.map {
  39. cgmName(id: $0.id, type: $0, displayName: $0.displayName, subtitle: $0.subtitle)
  40. } +
  41. pluginCGMManager.availableCGMManagers.map {
  42. cgmName(
  43. id: $0.identifier,
  44. type: CGMType.plugin,
  45. displayName: $0.localizedTitle,
  46. subtitle: $0.localizedTitle
  47. )
  48. }
  49. ).sorted(by: { lhs, rhs in
  50. if lhs.displayName == "None" {
  51. return true
  52. } else if rhs.displayName == "None" {
  53. return false
  54. } else {
  55. return lhs.displayName < rhs.displayName
  56. }
  57. })
  58. switch settingsManager.settings.cgm {
  59. case .plugin:
  60. if let cgmPluginInfo = listOfCGM.first(where: { $0.id == settingsManager.settings.cgmPluginIdentifier }) {
  61. cgmCurrent = cgmName(
  62. id: settingsManager.settings.cgmPluginIdentifier,
  63. type: .plugin,
  64. displayName: cgmPluginInfo.displayName,
  65. subtitle: cgmPluginInfo.subtitle
  66. )
  67. } else {
  68. // no more type of plugin available - restart to defaut
  69. cgmCurrent = cgmDefaultName
  70. }
  71. default:
  72. cgmCurrent = cgmName(
  73. id: settingsManager.settings.cgm.id,
  74. type: settingsManager.settings.cgm,
  75. displayName: settingsManager.settings.cgm.displayName,
  76. subtitle: settingsManager.settings.cgm.subtitle
  77. )
  78. }
  79. url = nightscoutManager.cgmURL
  80. switch url?.absoluteString {
  81. case "http://127.0.0.1:1979":
  82. url = URL(string: "spikeapp://")!
  83. case "http://127.0.0.1:17580":
  84. url = URL(string: "diabox://")!
  85. // case CGMType.libreTransmitter.appURL?.absoluteString:
  86. // showModal(for: .libreConfig)
  87. default: break
  88. }
  89. cgmTransmitterDeviceAddress = UserDefaults.standard.cgmTransmitterDeviceAddress
  90. subscribeSetting(\.smoothGlucose, on: $smoothGlucose, initial: { smoothGlucose = $0 })
  91. }
  92. func displayNameOfApp() -> String? {
  93. guard cgmManager != nil else { return nil }
  94. var nameOfApp = "Open Application"
  95. switch cgmManager.cgmGlucoseSourceType {
  96. case .plugin:
  97. nameOfApp = "Open " + (cgmManager.cgmManager?.localizedTitle ?? "Application")
  98. default:
  99. nameOfApp = "Open " + cgmManager.cgmGlucoseSourceType.displayName
  100. }
  101. return nameOfApp
  102. }
  103. func urlOfApp() -> URL? {
  104. guard cgmManager != nil else { return nil }
  105. switch cgmManager.cgmGlucoseSourceType {
  106. case .plugin:
  107. return cgmManager.cgmManager?.appURL
  108. default:
  109. return cgmManager.cgmGlucoseSourceType.appURL
  110. }
  111. }
  112. func addCGM(cgm: cgmName) {
  113. cgmCurrent = cgm
  114. switch cgmCurrent.type {
  115. case .plugin:
  116. setupCGM.toggle()
  117. default:
  118. cgmManager.cgmGlucoseSourceType = cgmCurrent.type
  119. completionNotifyingDidComplete(EmptyCompletionNotifying())
  120. }
  121. }
  122. func deleteCGM() {
  123. cgmManager.deleteGlucoseSource()
  124. completionNotifyingDidComplete(EmptyCompletionNotifying())
  125. }
  126. }
  127. }
  128. extension CGM.StateModel: CompletionDelegate {
  129. func completionNotifyingDidComplete(_: CompletionNotifying) {
  130. setupCGM = false
  131. // if CGM was deleted
  132. if cgmManager.cgmGlucoseSourceType == .none {
  133. cgmCurrent = cgmDefaultName
  134. settingsManager.settings.cgm = cgmDefaultName.type
  135. settingsManager.settings.cgmPluginIdentifier = cgmDefaultName.id
  136. cgmManager.deleteGlucoseSource()
  137. } else {
  138. settingsManager.settings.cgm = cgmCurrent.type
  139. settingsManager.settings.cgmPluginIdentifier = cgmCurrent.id
  140. cgmManager.updateGlucoseSource(cgmGlucoseSourceType: cgmCurrent.type, cgmGlucosePluginId: cgmCurrent.id)
  141. }
  142. // update if required the Glucose source
  143. DispatchQueue.main.async {
  144. self.broadcaster.notify(GlucoseObserver.self, on: .main) {
  145. $0.glucoseDidUpdate([])
  146. }
  147. }
  148. }
  149. }
  150. extension CGM.StateModel: CGMManagerOnboardingDelegate {
  151. func cgmManagerOnboarding(didCreateCGMManager manager: LoopKitUI.CGMManagerUI) {
  152. // update the glucose source
  153. cgmManager.updateGlucoseSource(
  154. cgmGlucoseSourceType: cgmCurrent.type,
  155. cgmGlucosePluginId: cgmCurrent.id,
  156. newManager: manager
  157. )
  158. }
  159. func cgmManagerOnboarding(didOnboardCGMManager _: LoopKitUI.CGMManagerUI) {
  160. // nothing to do ?
  161. }
  162. }
  163. extension CGM.StateModel {
  164. func settingsDidChange(_: FreeAPSSettings) {
  165. units = settingsManager.settings.units
  166. }
  167. }