| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- import Algorithms
- import Combine
- import CoreData
- import DanaKit
- import Foundation
- import LoopKit
- import LoopKitUI
- import MinimedKit
- import MockKit
- import OmniBLE
- import OmniKit
- import ShareClient
- import SwiftDate
- import Swinject
- import UserNotifications
- protocol DeviceDataManager: GlucoseSource {
- var pumpManager: PumpManagerUI? { get set }
- var bluetoothManager: BluetoothStateManager { get }
- var loopInProgress: Bool { get set }
- var pumpDisplayState: CurrentValueSubject<PumpDisplayState?, Never> { get }
- var recommendsLoop: PassthroughSubject<Void, Never> { get }
- var bolusTrigger: PassthroughSubject<Bool, Never> { get }
- var manualTempBasal: PassthroughSubject<Bool, Never> { get }
- var errorSubject: PassthroughSubject<Error, Never> { get }
- var pumpName: CurrentValueSubject<String, Never> { get }
- var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
- func heartbeat(date: Date)
- func createBolusProgressReporter() -> DoseProgressReporter?
- var alertHistoryStorage: AlertHistoryStorage! { get }
- }
- private let staticPumpManagers: [PumpManagerUI.Type] = [
- MinimedPumpManager.self,
- OmnipodPumpManager.self,
- OmniBLEPumpManager.self,
- DanaKitPumpManager.self,
- MockPumpManager.self
- ]
- private let staticPumpManagersByIdentifier: [String: PumpManagerUI.Type] = [
- MinimedPumpManager.pluginIdentifier: MinimedPumpManager.self,
- OmnipodPumpManager.pluginIdentifier: OmnipodPumpManager.self,
- OmniBLEPumpManager.pluginIdentifier: OmniBLEPumpManager.self,
- DanaKitPumpManager.pluginIdentifier: DanaKitPumpManager.self,
- MockPumpManager.pluginIdentifier: MockPumpManager.self
- ]
- private let accessLock = NSRecursiveLock(label: "BaseDeviceDataManager.accessLock")
- final class BaseDeviceDataManager: DeviceDataManager, Injectable {
- private let processQueue = DispatchQueue.markedQueue(label: "BaseDeviceDataManager.processQueue")
- @Injected() private var pumpHistoryStorage: PumpHistoryStorage!
- @Injected() var alertHistoryStorage: AlertHistoryStorage!
- @Injected() private var storage: FileStorage!
- @Injected() private var broadcaster: Broadcaster!
- @Injected() private var glucoseStorage: GlucoseStorage!
- @Injected() private var settingsManager: SettingsManager!
- @Injected() private var bluetoothProvider: BluetoothStateManager!
- @Persisted(key: "BaseDeviceDataManager.lastEventDate") var lastEventDate: Date? = nil
- @SyncAccess(lock: accessLock) @Persisted(key: "BaseDeviceDataManager.lastHeartBeatTime") var lastHeartBeatTime: Date =
- .distantPast
- let recommendsLoop = PassthroughSubject<Void, Never>()
- let bolusTrigger = PassthroughSubject<Bool, Never>()
- let errorSubject = PassthroughSubject<Error, Never>()
- let pumpNewStatus = PassthroughSubject<Void, Never>()
- let manualTempBasal = PassthroughSubject<Bool, Never>()
- private let router = FreeAPSApp.resolver.resolve(Router.self)!
- @SyncAccess private var pumpUpdateCancellable: AnyCancellable?
- private var pumpUpdatePromise: Future<Bool, Never>.Promise?
- @SyncAccess var loopInProgress: Bool = false
- private let privateContext = CoreDataStack.shared.newTaskContext()
- var pumpManager: PumpManagerUI? {
- didSet {
- pumpManager?.pumpManagerDelegate = self
- pumpManager?.delegateQueue = processQueue
- rawPumpManager = pumpManager?.rawValue
- UserDefaults.standard.clearLegacyPumpManagerRawValue()
- if let pumpManager = pumpManager {
- pumpDisplayState.value = PumpDisplayState(name: pumpManager.localizedTitle, image: pumpManager.smallImage)
- pumpName.send(pumpManager.localizedTitle)
- var modifiedPreferences = settingsManager.preferences
- let bolusIncrement = Decimal(
- pumpManager.supportedBolusVolumes.first ??
- Double(
- settingsManager.preferences
- .bolusIncrement
- )
- )
- modifiedPreferences
- .bolusIncrement = bolusIncrement != 0.025 ? bolusIncrement : 0.1
- storage.save(modifiedPreferences, as: OpenAPS.Settings.preferences)
- if let omnipod = pumpManager as? OmnipodPumpManager {
- guard let endTime = omnipod.state.podState?.expiresAt else {
- pumpExpiresAtDate.send(nil)
- return
- }
- pumpExpiresAtDate.send(endTime)
- }
- if let omnipodBLE = pumpManager as? OmniBLEPumpManager {
- guard let endTime = omnipodBLE.state.podState?.expiresAt else {
- pumpExpiresAtDate.send(nil)
- return
- }
- pumpExpiresAtDate.send(endTime)
- }
- if let simulatorPump = pumpManager as? MockPumpManager {
- pumpDisplayState.value = PumpDisplayState(name: simulatorPump.localizedTitle, image: simulatorPump.smallImage)
- pumpName.send(simulatorPump.localizedTitle)
- storage.save(Decimal(simulatorPump.pumpReservoirCapacity), as: OpenAPS.Monitor.reservoir)
- DispatchQueue.main.async {
- self.broadcaster.notify(PumpReservoirObserver.self, on: .main) {
- $0.pumpReservoirDidChange(Decimal(simulatorPump.state.reservoirUnitsRemaining))
- }
- }
- let batteryPercent = Int((simulatorPump.state.pumpBatteryChargeRemaining ?? 1) * 100)
- let battery = Battery(
- percent: batteryPercent,
- voltage: nil,
- string: batteryPercent >= 10 ? .normal : .low,
- display: simulatorPump.state.pumpBatteryChargeRemaining != nil
- )
- Task {
- await self.privateContext.perform {
- let saveBatteryToCoreData = OpenAPS_Battery(context: self.privateContext)
- saveBatteryToCoreData.id = UUID()
- saveBatteryToCoreData.date = Date()
- saveBatteryToCoreData.percent = Int16(batteryPercent)
- saveBatteryToCoreData.voltage = nil
- saveBatteryToCoreData.status = batteryPercent >= 10 ? BatteryState.normal.rawValue : BatteryState
- .low.rawValue
- saveBatteryToCoreData.display = simulatorPump.state.pumpBatteryChargeRemaining != nil
- do {
- guard self.privateContext.hasChanges else { return }
- try self.privateContext.save()
- } catch {
- print(error.localizedDescription)
- }
- }
- }
- DispatchQueue.main.async {
- self.broadcaster.notify(PumpBatteryObserver.self, on: .main) {
- $0.pumpBatteryDidChange(battery)
- }
- }
- }
- } else {
- pumpDisplayState.value = nil
- pumpExpiresAtDate.send(nil)
- pumpName.send("")
- // Reset bolusIncrement setting to default value, which is 0.1 U
- var modifiedPreferences = settingsManager.preferences
- modifiedPreferences.bolusIncrement = 0.1
- storage.save(modifiedPreferences, as: OpenAPS.Settings.preferences)
- // Remove OpenAPS_Battery entries
- Task {
- await self.privateContext.perform {
- let fetchRequest: NSFetchRequest<OpenAPS_Battery> = OpenAPS_Battery.fetchRequest()
- do {
- let batteryEntries = try self.privateContext.fetch(fetchRequest)
- for entry in batteryEntries {
- self.privateContext.delete(entry)
- }
- guard self.privateContext.hasChanges else { return }
- try self.privateContext.save()
- } catch {
- print("Failed to delete OpenAPS_Battery entries: \(error.localizedDescription)")
- }
- }
- }
- }
- }
- }
- @PersistedProperty(key: "PumpManagerState") var rawPumpManager: PumpManager.RawValue?
- var bluetoothManager: BluetoothStateManager { bluetoothProvider }
- var hasBLEHeartbeat: Bool {
- (pumpManager as? MockPumpManager) == nil
- }
- let pumpDisplayState = CurrentValueSubject<PumpDisplayState?, Never>(nil)
- let pumpExpiresAtDate = CurrentValueSubject<Date?, Never>(nil)
- let pumpName = CurrentValueSubject<String, Never>("Pump")
- init(resolver: Resolver) {
- injectServices(resolver)
- setupPumpManager()
- UIDevice.current.isBatteryMonitoringEnabled = true
- broadcaster.register(AlertObserver.self, observer: self)
- }
- func setupPumpManager() {
- if let pumpManagerRawValue = rawPumpManager ?? UserDefaults.standard.legacyPumpManagerRawValue {
- pumpManager = pumpManagerFromRawValue(pumpManagerRawValue)
- } else {
- pumpManager = nil
- }
- }
- func createBolusProgressReporter() -> DoseProgressReporter? {
- pumpManager?.createBolusProgressReporter(reportingOn: processQueue)
- }
- func heartbeat(date: Date) {
- guard pumpUpdateCancellable == nil else {
- warning(.deviceManager, "Pump updating already in progress. Skip updating.")
- return
- }
- guard !loopInProgress else {
- warning(.deviceManager, "Loop in progress. Skip updating.")
- return
- }
- func update(_: Future<Bool, Never>.Promise?) {}
- processQueue.safeSync {
- lastHeartBeatTime = date
- updatePumpData()
- }
- }
- private func updatePumpData() {
- guard let pumpManager = pumpManager else {
- debug(.deviceManager, "Pump is not set, skip updating")
- updateUpdateFinished(false)
- return
- }
- debug(.deviceManager, "Start updating the pump data")
- processQueue.safeSync {
- pumpManager.ensureCurrentPumpData { _ in
- debug(.deviceManager, "Pump data updated.")
- self.updateUpdateFinished(true)
- }
- }
- }
- private func updateUpdateFinished(_ recommendsLoop: Bool) {
- pumpUpdateCancellable = nil
- pumpUpdatePromise = nil
- if !recommendsLoop {
- warning(.deviceManager, "Loop recommendation time out or got error. Trying to loop right now.")
- }
- self.recommendsLoop.send()
- }
- private func pumpManagerFromRawValue(_ rawValue: [String: Any]) -> PumpManagerUI? {
- guard let rawState = rawValue["state"] as? PumpManager.RawStateValue,
- let Manager = pumpManagerTypeFromRawValue(rawValue)
- else {
- return nil
- }
- return Manager.init(rawState: rawState) as? PumpManagerUI
- }
- private func pumpManagerTypeFromRawValue(_ rawValue: [String: Any]) -> PumpManager.Type? {
- guard let managerIdentifier = rawValue["managerIdentifier"] as? String else {
- return nil
- }
- return staticPumpManagersByIdentifier[managerIdentifier]
- }
- // MARK: - GlucoseSource
- @Persisted(key: "BaseDeviceDataManager.lastFetchGlucoseDate") private var lastFetchGlucoseDate: Date = .distantPast
- var glucoseManager: FetchGlucoseManager?
- var cgmManager: CGMManagerUI?
- var cgmType: CGMType = .enlite
- func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
- fetch(nil)
- }
- func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
- guard let medtronic = pumpManager as? MinimedPumpManager else {
- warning(.deviceManager, "Fetch minilink glucose failed: Pump is not Medtronic")
- return Just([]).eraseToAnyPublisher()
- }
- guard lastFetchGlucoseDate.addingTimeInterval(5.minutes.timeInterval) < Date() else {
- return Just([]).eraseToAnyPublisher()
- }
- medtronic.cgmManagerDelegate = self
- return Future<[BloodGlucose], Error> { promise in
- self.processQueue.async {
- medtronic.fetchNewDataIfNeeded { result in
- switch result {
- case .noData:
- debug(.deviceManager, "Minilink glucose is empty")
- promise(.success([]))
- case .unreliableData:
- debug(.deviceManager, "Unreliable data received")
- promise(.success([]))
- case let .newData(glucose):
- let directions: [BloodGlucose.Direction?] = [nil]
- + glucose.windows(ofCount: 2).map { window -> BloodGlucose.Direction? in
- let pair = Array(window)
- guard pair.count == 2 else { return nil }
- let firstValue = Int(pair[0].quantity.doubleValue(for: .milligramsPerDeciliter))
- let secondValue = Int(pair[1].quantity.doubleValue(for: .milligramsPerDeciliter))
- return .init(trend: secondValue - firstValue)
- }
- let results = glucose.enumerated().map { index, sample -> BloodGlucose in
- let value = Int(sample.quantity.doubleValue(for: .milligramsPerDeciliter))
- return BloodGlucose(
- _id: sample.syncIdentifier,
- sgv: value,
- direction: directions[index],
- date: Decimal(Int(sample.date.timeIntervalSince1970 * 1000)),
- dateString: sample.date,
- unfiltered: Decimal(value),
- filtered: nil,
- noise: nil,
- glucose: value,
- type: "sgv"
- )
- }
- if let lastDate = results.last?.dateString {
- self.lastFetchGlucoseDate = lastDate
- }
- promise(.success(results))
- case let .error(error):
- warning(.deviceManager, "Fetch minilink glucose failed", error: error)
- promise(.failure(error))
- }
- }
- }
- }
- .timeout(60 * 3, scheduler: processQueue, options: nil, customError: nil)
- .replaceError(with: [])
- .replaceEmpty(with: [])
- .eraseToAnyPublisher()
- }
- }
- // MARK: - PumpManagerDelegate
- extension BaseDeviceDataManager: PumpManagerDelegate {
- var automaticDosingEnabled: Bool {
- settingsManager.settings.closedLoop // Take if close or open loop
- }
- func pumpManager(
- _: LoopKit.PumpManager,
- didRequestBasalRateScheduleChange _: LoopKit.BasalRateSchedule,
- completion _: @escaping (Error?) -> Void
- ) {
- debug(.deviceManager, "pumpManagerBasalRateChange")
- }
- func pumpManagerPumpWasReplaced(_: PumpManager) {
- debug(.deviceManager, "pumpManagerPumpWasReplaced")
- }
- var detectedSystemTimeOffset: TimeInterval {
- // trustedTimeChecker.detectedSystemTimeOffset
- 0
- }
- func pumpManager(_: PumpManager, didAdjustPumpClockBy adjustment: TimeInterval) {
- debug(.deviceManager, "didAdjustPumpClockBy \(adjustment)")
- }
- func pumpManagerDidUpdateState(_ pumpManager: PumpManager) {
- rawPumpManager = pumpManager.rawValue
- if self.pumpManager == nil, let newPumpManager = pumpManager as? PumpManagerUI {
- self.pumpManager = newPumpManager
- }
- pumpName.send(pumpManager.localizedTitle)
- }
- /// heartbeat with pump occurs some issues in the backgroundtask - so never used
- func pumpManagerBLEHeartbeatDidFire(_: PumpManager) {
- debug(.deviceManager, "Pump Heartbeat: do nothing. Pump connection is OK")
- }
- func pumpManagerMustProvideBLEHeartbeat(_: PumpManager) -> Bool {
- true
- }
- func pumpManager(_ pumpManager: PumpManager, didUpdate status: PumpManagerStatus, oldStatus: PumpManagerStatus) {
- dispatchPrecondition(condition: .onQueue(processQueue))
- debug(.deviceManager, "New pump status Bolus: \(status.bolusState)")
- debug(.deviceManager, "New pump status Basal: \(String(describing: status.basalDeliveryState))")
- if case .inProgress = status.bolusState {
- bolusTrigger.send(true)
- } else {
- bolusTrigger.send(false)
- }
- if status.insulinType != oldStatus.insulinType {
- settingsManager.updateInsulinCurve(status.insulinType)
- }
- broadcaster.notify(PumpTimeZoneObserver.self, on: processQueue) {
- $0.pumpTimeZoneDidChange(status.timeZone)
- }
- if let omnipod = pumpManager as? OmnipodPumpManager {
- let reservoirVal = omnipod.state.podState?.lastInsulinMeasurements?.reservoirLevel ?? 0xDEAD_BEEF
- // TODO: find the value Pod.maximumReservoirReading
- let reservoir = Decimal(reservoirVal) > 50.0 ? 0xDEAD_BEEF : reservoirVal
- storage.save(Decimal(reservoir), as: OpenAPS.Monitor.reservoir)
- broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
- $0.pumpReservoirDidChange(Decimal(reservoir))
- }
- if let tempBasal = omnipod.state.podState?.unfinalizedTempBasal, !tempBasal.isFinished(),
- !tempBasal.automatic
- {
- // the manual basal temp is launch - block every thing
- debug(.deviceManager, "manual temp basal")
- manualTempBasal.send(true)
- } else {
- // no more manual Temp Basal !
- manualTempBasal.send(false)
- }
- guard let endTime = omnipod.state.podState?.expiresAt else {
- pumpExpiresAtDate.send(nil)
- return
- }
- pumpExpiresAtDate.send(endTime)
- if let startTime = omnipod.state.podState?.activatedAt {
- storage.save(startTime, as: OpenAPS.Monitor.podAge)
- }
- }
- if let omnipodBLE = pumpManager as? OmniBLEPumpManager {
- let reservoirVal = omnipodBLE.state.podState?.lastInsulinMeasurements?.reservoirLevel ?? 0xDEAD_BEEF
- // TODO: find the value Pod.maximumReservoirReading
- let reservoir = Decimal(reservoirVal) > 50.0 ? 0xDEAD_BEEF : reservoirVal
- storage.save(Decimal(reservoir), as: OpenAPS.Monitor.reservoir)
- broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
- $0.pumpReservoirDidChange(Decimal(reservoir))
- }
- // manual temp basal on
- if let tempBasal = omnipodBLE.state.podState?.unfinalizedTempBasal, !tempBasal.isFinished(),
- !tempBasal.automatic
- {
- // the manual basal temp is launch - block every thing
- debug(.deviceManager, "manual temp basal")
- manualTempBasal.send(true)
- } else {
- // no more manual Temp Basal !
- manualTempBasal.send(false)
- }
- guard let endTime = omnipodBLE.state.podState?.expiresAt else {
- pumpExpiresAtDate.send(nil)
- return
- }
- pumpExpiresAtDate.send(endTime)
- if let startTime = omnipodBLE.state.podState?.activatedAt {
- storage.save(startTime, as: OpenAPS.Monitor.podAge)
- }
- }
- if let simulatorPump = pumpManager as? MockPumpManager {
- broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
- $0.pumpReservoirDidChange(Decimal(simulatorPump.state.reservoirUnitsRemaining))
- }
- }
- }
- func pumpManagerWillDeactivate(_: PumpManager) {
- dispatchPrecondition(condition: .onQueue(processQueue))
- pumpManager = nil
- broadcaster.notify(PumpDeactivatedObserver.self, on: processQueue) {
- $0.pumpDeactivatedDidChange()
- }
- }
- func pumpManager(_: PumpManager, didUpdatePumpRecordsBasalProfileStartEvents _: Bool) {}
- func pumpManager(_: PumpManager, didError error: PumpManagerError) {
- dispatchPrecondition(condition: .onQueue(processQueue))
- debug(.deviceManager, "error: \(error.localizedDescription), reason: \(String(describing: error.failureReason))")
- errorSubject.send(error)
- }
- func pumpManager(
- _: PumpManager,
- hasNewPumpEvents events: [NewPumpEvent],
- lastReconciliation _: Date?,
- replacePendingEvents _: Bool,
- completion: @escaping (_ error: Error?) -> Void
- ) {
- dispatchPrecondition(condition: .onQueue(processQueue))
- // filter buggy TBRs > maxBasal from MDT
- let events = events.filter {
- // type is optional...
- guard let type = $0.type, type == .tempBasal else { return true }
- return $0.dose?.unitsPerHour ?? 0 <= Double(settingsManager.pumpSettings.maxBasal)
- }
- pumpHistoryStorage.storePumpEvents(events)
- lastEventDate = events.last?.date
- completion(nil)
- }
- func pumpManager(
- _: PumpManager,
- didReadReservoirValue units: Double,
- at date: Date,
- completion: @escaping (Result<
- (newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool),
- Error
- >) -> Void
- ) {
- dispatchPrecondition(condition: .onQueue(processQueue))
- debug(.deviceManager, "Reservoir Value \(units), at: \(date)")
- storage.save(Decimal(units), as: OpenAPS.Monitor.reservoir)
- broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
- $0.pumpReservoirDidChange(Decimal(units))
- }
- completion(.success((
- newValue: Reservoir(startDate: Date(), unitVolume: units),
- lastValue: nil,
- areStoredValuesContinuous: true
- )))
- }
- func pumpManagerRecommendsLoop(_: PumpManager) {
- dispatchPrecondition(condition: .onQueue(processQueue))
- debug(.deviceManager, "Pump recommends loop")
- guard let promise = pumpUpdatePromise else {
- warning(.deviceManager, "We do not waiting for loop recommendation at this time.")
- return
- }
- promise(.success(true))
- }
- func startDateToFilterNewPumpEvents(for _: PumpManager) -> Date {
- lastEventDate?.addingTimeInterval(-15.minutes.timeInterval) ?? Date().addingTimeInterval(-2.hours.timeInterval)
- }
- }
- // MARK: - DeviceManagerDelegate
- extension BaseDeviceDataManager: DeviceManagerDelegate {
- func issueAlert(_ alert: Alert) {
- alertHistoryStorage.storeAlert(
- AlertEntry(
- alertIdentifier: alert.identifier.alertIdentifier,
- primitiveInterruptionLevel: alert.interruptionLevel.storedValue as? Decimal,
- issuedDate: Date(),
- managerIdentifier: alert.identifier.managerIdentifier,
- triggerType: alert.trigger.storedType,
- triggerInterval: alert.trigger.storedInterval as? Decimal,
- contentTitle: alert.foregroundContent?.title,
- contentBody: alert.foregroundContent?.body
- )
- )
- }
- func retractAlert(identifier: Alert.Identifier) {
- alertHistoryStorage.deleteAlert(identifier: identifier.alertIdentifier)
- }
- func doesIssuedAlertExist(identifier _: Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {
- debug(.deviceManager, "doesIssueAlertExist")
- }
- func lookupAllUnretracted(managerIdentifier _: String, completion _: @escaping (Result<[PersistedAlert], Error>) -> Void) {
- debug(.deviceManager, "lookupAllUnretracted")
- }
- func lookupAllUnacknowledgedUnretracted(
- managerIdentifier _: String,
- completion _: @escaping (Result<[PersistedAlert], Error>) -> Void
- ) {}
- func recordRetractedAlert(_: Alert, at _: Date) {}
- func removeNotificationRequests(for _: DeviceManager, identifiers: [String]) {
- DispatchQueue.main.async {
- UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
- }
- }
- func deviceManager(
- _: DeviceManager,
- logEventForDeviceIdentifier _: String?,
- type _: DeviceLogEntryType,
- message: String,
- completion _: ((Error?) -> Void)?
- ) {
- debug(.deviceManager, "Device message: \(message)")
- }
- }
- // MARK: - CGMManagerDelegate
- extension BaseDeviceDataManager: CGMManagerDelegate {
- func startDateToFilterNewData(for _: CGMManager) -> Date? {
- glucoseStorage.syncDate().addingTimeInterval(-10.minutes.timeInterval) // additional time to calculate directions
- }
- func cgmManager(_: CGMManager, hasNew _: CGMReadingResult) {}
- func cgmManager(_: LoopKit.CGMManager, hasNew _: [LoopKit.PersistedCgmEvent]) {}
- func cgmManagerWantsDeletion(_: CGMManager) {}
- func cgmManagerDidUpdateState(_: CGMManager) {}
- func credentialStoragePrefix(for _: CGMManager) -> String { "BaseDeviceDataManager" }
- func cgmManager(_: CGMManager, didUpdate _: CGMManagerStatus) {}
- }
- // MARK: - AlertPresenter
- extension BaseDeviceDataManager: AlertObserver {
- func AlertDidUpdate(_ alerts: [AlertEntry]) {
- alerts.forEach { alert in
- if alert.acknowledgedDate == nil {
- ackAlert(alert: alert)
- }
- }
- }
- private func ackAlert(alert: AlertEntry) {
- let alertIssueDate = alert.issuedDate
- processQueue.async {
- // if not alert in OmniPod/BLE, the acknowledgeAlert didn't do callbacks- Hack to manage this case
- if let omnipodBLE = self.pumpManager as? OmniBLEPumpManager {
- if omnipodBLE.state.activeAlerts.isEmpty {
- // force to ack alert in the alertStorage
- self.alertHistoryStorage.ackAlert(alertIssueDate, nil)
- }
- }
- if let omniPod = self.pumpManager as? OmnipodPumpManager {
- if omniPod.state.activeAlerts.isEmpty {
- // force to ack alert in the alertStorage
- self.alertHistoryStorage.ackAlert(alertIssueDate, nil)
- }
- }
- self.pumpManager?.acknowledgeAlert(alertIdentifier: alert.alertIdentifier) { error in
- if let error = error {
- self.alertHistoryStorage.ackAlert(alertIssueDate, error.localizedDescription)
- debug(.deviceManager, "acknowledge not succeeded with error \(error.localizedDescription)")
- } else {
- self.alertHistoryStorage.ackAlert(alertIssueDate, nil)
- }
- }
- self.broadcaster.notify(pumpNotificationObserver.self, on: self.processQueue) {
- $0.pumpNotification(alert: alert)
- }
- }
- }
- }
- // extension BaseDeviceDataManager: AlertPresenter {
- // func issueAlert(_: Alert) {}
- // func retractAlert(identifier _: Alert.Identifier) {}
- // }
- // MARK: Others
- protocol PumpReservoirObserver {
- func pumpReservoirDidChange(_ reservoir: Decimal)
- }
- protocol PumpBatteryObserver {
- func pumpBatteryDidChange(_ battery: Battery)
- }
- protocol PumpTimeZoneObserver {
- func pumpTimeZoneDidChange(_ timezone: TimeZone)
- }
- protocol PumpDeactivatedObserver {
- func pumpDeactivatedDidChange()
- }
|