CGMStateModel.swift 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. extension CGM {
  19. final class StateModel: BaseStateModel<Provider> {
  20. @Injected() var cgmManager: FetchGlucoseManager!
  21. @Injected() var pluginCGMManager: PluginManager!
  22. @Injected() private var broadcaster: Broadcaster!
  23. @Injected() var nightscoutManager: NightscoutManager!
  24. @Published var setupCGM: Bool = false
  25. @Published var cgmCurrent = cgmDefaultName
  26. @Published var smoothGlucose = false
  27. @Published var cgmTransmitterDeviceAddress: String? = nil
  28. @Published var listOfCGM: [cgmName] = []
  29. @Published var url: URL?
  30. override func subscribe() {
  31. // collect the list of CGM available with plugins and CGMType defined manually
  32. listOfCGM = CGMType.allCases.filter { $0 != CGMType.plugin }.map {
  33. cgmName(id: $0.id, type: $0, displayName: $0.displayName, subtitle: $0.subtitle)
  34. } +
  35. pluginCGMManager.availableCGMManagers.map {
  36. cgmName(id: $0.identifier, type: CGMType.plugin, displayName: $0.localizedTitle, subtitle: $0.localizedTitle)
  37. }
  38. switch settingsManager.settings.cgm {
  39. case .plugin:
  40. if let cgmPluginInfo = listOfCGM.first(where: { $0.id == settingsManager.settings.cgmPluginIdentifier }) {
  41. cgmCurrent = cgmName(
  42. id: settingsManager.settings.cgmPluginIdentifier,
  43. type: .plugin,
  44. displayName: cgmPluginInfo.displayName,
  45. subtitle: cgmPluginInfo.subtitle
  46. )
  47. } else {
  48. // no more type of plugin available - restart to defaut
  49. cgmCurrent = cgmDefaultName
  50. }
  51. default:
  52. cgmCurrent = cgmName(
  53. id: settingsManager.settings.cgm.id,
  54. type: settingsManager.settings.cgm,
  55. displayName: settingsManager.settings.cgm.displayName,
  56. subtitle: settingsManager.settings.cgm.subtitle
  57. )
  58. }
  59. url = nightscoutManager.cgmURL
  60. switch url?.absoluteString {
  61. case "http://127.0.0.1:1979":
  62. url = URL(string: "spikeapp://")!
  63. case "http://127.0.0.1:17580":
  64. url = URL(string: "diabox://")!
  65. // case CGMType.libreTransmitter.appURL?.absoluteString:
  66. // showModal(for: .libreConfig)
  67. default: break
  68. }
  69. cgmTransmitterDeviceAddress = UserDefaults.standard.cgmTransmitterDeviceAddress
  70. subscribeSetting(\.smoothGlucose, on: $smoothGlucose, initial: { smoothGlucose = $0 })
  71. $cgmCurrent
  72. .removeDuplicates()
  73. .sink { [weak self] value in
  74. guard let self = self else { return }
  75. guard self.cgmManager.cgmGlucoseSourceType != nil else {
  76. self.settingsManager.settings.cgm = .none
  77. return
  78. }
  79. if value.type != self.settingsManager.settings.cgm ||
  80. value.id != self.settingsManager.settings.cgmPluginIdentifier
  81. {
  82. self.settingsManager.settings.cgm = value.type
  83. self.settingsManager.settings.cgmPluginIdentifier = value.id
  84. self.cgmManager.updateGlucoseSource(
  85. cgmGlucoseSourceType: value.type,
  86. cgmGlucosePluginId: value.id
  87. )
  88. self.setupCGM = false
  89. }
  90. }
  91. .store(in: &lifetime)
  92. }
  93. func displayNameOfApp() -> String? {
  94. guard cgmManager != nil else { return nil }
  95. var nameOfApp = "Open Application"
  96. switch cgmManager.cgmGlucoseSourceType {
  97. case .plugin:
  98. nameOfApp = "Open " + (cgmManager.cgmManager?.localizedTitle ?? "Application")
  99. default:
  100. nameOfApp = "Open " + cgmManager.cgmGlucoseSourceType.displayName
  101. }
  102. return nameOfApp
  103. }
  104. func urlOfApp() -> URL? {
  105. guard cgmManager != nil else { return nil }
  106. switch cgmManager.cgmGlucoseSourceType {
  107. case .plugin:
  108. return cgmManager.cgmManager?.appURL
  109. default:
  110. return cgmManager.cgmGlucoseSourceType.appURL
  111. }
  112. }
  113. }
  114. }
  115. extension CGM.StateModel: CompletionDelegate {
  116. func completionNotifyingDidComplete(_: CompletionNotifying) {
  117. setupCGM = false
  118. // if CGM was deleted
  119. if cgmManager.cgmGlucoseSourceType == nil {
  120. cgmCurrent = cgmDefaultName
  121. settingsManager.settings.cgm = cgmDefaultName.type
  122. settingsManager.settings.cgmPluginIdentifier = cgmDefaultName.id
  123. cgmManager.deleteGlucoseSource()
  124. } else {
  125. cgmManager.updateGlucoseSource(cgmGlucoseSourceType: cgmCurrent.type, cgmGlucosePluginId: cgmCurrent.id)
  126. }
  127. // update if required the Glucose source
  128. DispatchQueue.main.async {
  129. self.broadcaster.notify(GlucoseObserver.self, on: .main) {
  130. $0.glucoseDidUpdate([])
  131. }
  132. }
  133. }
  134. }
  135. extension CGM.StateModel: CGMManagerOnboardingDelegate {
  136. func cgmManagerOnboarding(didCreateCGMManager manager: LoopKitUI.CGMManagerUI) {
  137. // update the glucose source
  138. cgmManager.updateGlucoseSource(
  139. cgmGlucoseSourceType: cgmCurrent.type,
  140. cgmGlucosePluginId: cgmCurrent.id,
  141. newManager: manager
  142. )
  143. }
  144. func cgmManagerOnboarding(didOnboardCGMManager _: LoopKitUI.CGMManagerUI) {
  145. // nothing to do ?
  146. }
  147. }