| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- //
- // MinimedPumpSettingsViewModel.swift
- // MinimedKitUI
- //
- // Created by Pete Schwamb on 11/29/22.
- // Copyright © 2022 LoopKit Authors. All rights reserved.
- //
- import Foundation
- import MinimedKit
- import LoopKit
- import SwiftUI
- import LoopKitUI
- import HealthKit
- enum MinimedSettingsViewAlert: Identifiable {
- case suspendError(Error)
- case resumeError(Error)
- case syncTimeError(PumpManagerError)
- var id: String {
- switch self {
- case .suspendError:
- return "suspendError"
- case .resumeError:
- return "resumeError"
- case .syncTimeError:
- return "syncTimeError"
- }
- }
- }
- enum MySentryConfig: Int, Identifiable, CaseIterable {
- var id: RawValue {
- return self.rawValue
- }
- case useMySentry = 0
- case doNotUseMySentry
- var localizedDescription: String {
- switch self {
- case .useMySentry:
- return LocalizedString("Use MySentry", comment: "Description for option to use MySentry")
- case .doNotUseMySentry:
- return LocalizedString("Do not use MySentry", comment: "Description for option to not use MySentry")
- }
- }
- }
- public enum ReservoirLevelHighlightState: String, Equatable {
- case normal
- case warning
- case critical
- }
- class MinimedPumpSettingsViewModel: ObservableObject {
- @Published var suspendResumeTransitioning: Bool = false
- @Published var basalDeliveryState: PumpManagerStatus.BasalDeliveryState?
- @Published var reservoirReading: ReservoirReading?
- @Published var batteryChemistryType: BatteryChemistryType {
- didSet {
- pumpManager.batteryChemistry = batteryChemistryType
- }
- }
- @Published var preferredDataSource: InsulinDataSource {
- didSet {
- pumpManager.preferredInsulinDataSource = preferredDataSource
- }
- }
- @Published var mySentryConfig: MySentryConfig {
- didSet {
- pumpManager.useMySentry = mySentryConfig == .useMySentry
- }
- }
- @Published var activeAlert: MinimedSettingsViewAlert?
- @Published var suspendResumeButtonEnabled: Bool = false
- @Published var synchronizingTime: Bool = false
- var pumpManager: MinimedPumpManager
- var didFinish: (() -> Void)?
- let basalRateFormatter: NumberFormatter = {
- let numberFormatter = NumberFormatter()
- numberFormatter.numberStyle = .decimal
- numberFormatter.minimumFractionDigits = 1
- numberFormatter.minimumIntegerDigits = 1
- return numberFormatter
- }()
- let reservoirVolumeFormatter = {
- let formatter = QuantityFormatter(for: .internationalUnit())
- formatter.numberFormatter.maximumFractionDigits = 1
- return formatter
- }()
- init(pumpManager: MinimedPumpManager) {
- self.pumpManager = pumpManager
- self.basalDeliveryState = pumpManager.status.basalDeliveryState
- self.reservoirReading = pumpManager.state.lastReservoirReading
- self.batteryChemistryType = pumpManager.batteryChemistry
- self.preferredDataSource = pumpManager.preferredInsulinDataSource
- self.mySentryConfig = pumpManager.useMySentry ? .useMySentry : .doNotUseMySentry
- self.pumpManager.addStatusObserver(self, queue: DispatchQueue.main)
- pumpManager.stateObservers.insert(self, queue: .main)
- }
- var pumpImage: UIImage {
- return pumpManager.state.largePumpImage
- }
- func deletePump() {
- pumpManager.deletePump {
- DispatchQueue.main.async {
- self.didFinish?()
- }
- }
- }
- func doneButtonPressed() {
- self.didFinish?()
- }
- func suspendResumeButtonPressed(action: SuspendResumeAction) {
- suspendResumeButtonEnabled = true
- switch action {
- case .resume:
- pumpManager.resumeDelivery { error in
- DispatchQueue.main.async {
- self.suspendResumeButtonEnabled = false
- if let error = error {
- self.activeAlert = .resumeError(error)
- }
- }
- }
- case .suspend:
- pumpManager.suspendDelivery { error in
- DispatchQueue.main.async {
- self.suspendResumeButtonEnabled = false
- if let error = error {
- self.activeAlert = .suspendError(error)
- }
- }
- }
- }
- }
- func didChangeInsulinType(_ newType: InsulinType?) {
- self.pumpManager.insulinType = newType
- }
- var isScheduledBasal: Bool {
- switch basalDeliveryState {
- case .active(_), .initiatingTempBasal:
- return true
- case .tempBasal(_), .cancelingTempBasal, .suspending, .suspended(_), .resuming, .none:
- return false
- }
- }
- var isSuspendedOrResuming: Bool {
- switch basalDeliveryState {
- case .suspended, .resuming:
- return true
- default:
- return false
- }
- }
- func suspendResumeButtonColor(guidanceColors: GuidanceColors) -> Color {
- switch basalDeliveryState {
- case .suspending, .resuming:
- return Color.secondary
- case .suspended:
- return guidanceColors.warning
- default:
- return .accentColor
- }
- }
- var basalDeliveryRate: Double? {
- switch basalDeliveryState {
- case .suspending, .resuming, .suspended, .none, .initiatingTempBasal, .cancelingTempBasal:
- return nil
- case .active:
- // return scheduled basal rate
- var calendar = Calendar(identifier: .gregorian)
- calendar.timeZone = pumpManager.state.timeZone
- return pumpManager.state.basalSchedule.currentRate(using: calendar)
- case .tempBasal(let dose):
- return dose.unitsPerHour
- }
- }
- public var reservoirLevelHighlightState: ReservoirLevelHighlightState? {
- guard let reservoirReading = reservoirReading else {
- return nil
- }
- let value = reservoirReading.units
- if value > pumpManager.lowReservoirWarningLevel {
- return .normal
- } else if value > 0 {
- return .warning
- } else {
- return .critical
- }
- }
- public var reservoirPercentage: Double? {
- guard let reservoirReading = reservoirReading else {
- return nil
- }
- return (reservoirReading.units / pumpManager.pumpReservoirCapacity).clamped(to: 0...1.0)
- }
- func reservoirText(for units: Double) -> String {
- let quantity = HKQuantity(unit: .internationalUnit(), doubleValue: units)
- return reservoirVolumeFormatter.string(from: quantity, for: .internationalUnit()) ?? ""
- }
- var isClockOffset: Bool {
- return pumpManager.isClockOffset
- }
- func changeTimeZoneTapped() {
- synchronizingTime = true
- pumpManager.setTime { (error) in
- DispatchQueue.main.async {
- self.synchronizingTime = false
- if let error = error {
- self.activeAlert = .syncTimeError(error)
- }
- }
- }
- }
- }
- extension MinimedPumpSettingsViewModel: PumpManagerStatusObserver {
- public func pumpManager(_ pumpManager: PumpManager, didUpdate status: PumpManagerStatus, oldStatus: PumpManagerStatus) {
- basalDeliveryState = status.basalDeliveryState
- }
- }
- extension MinimedPumpSettingsViewModel: MinimedPumpManagerStateObserver {
- func didUpdatePumpManagerState(_ state: MinimedKit.MinimedPumpManagerState) {
- reservoirReading = state.lastReservoirReading
- batteryChemistryType = state.batteryChemistry
- preferredDataSource = state.preferredInsulinDataSource
- mySentryConfig = state.useMySentry ? .useMySentry : .doNotUseMySentry
- }
- }
- enum SuspendResumeAction {
- case suspend
- case resume
- }
- extension PumpManagerStatus.BasalDeliveryState {
- var shownAction: SuspendResumeAction {
- switch self {
- case .active, .suspending, .tempBasal, .cancelingTempBasal, .initiatingTempBasal:
- return .suspend
- case .suspended, .resuming:
- return .resume
- }
- }
- var buttonLabelText: String {
- switch self {
- case .active, .tempBasal:
- return LocalizedString("Suspend Delivery", comment: "Title text for button to suspend insulin delivery")
- case .suspending:
- return LocalizedString("Suspending", comment: "Title text for button when insulin delivery is in the process of being stopped")
- case .suspended:
- return LocalizedString("Resume Delivery", comment: "Title text for button to resume insulin delivery")
- case .resuming:
- return LocalizedString("Resuming", comment: "Title text for button when insulin delivery is in the process of being resumed")
- case .initiatingTempBasal:
- return LocalizedString("Starting Temp Basal", comment: "Title text for suspend resume button when temp basal starting")
- case .cancelingTempBasal:
- return LocalizedString("Canceling Temp Basal", comment: "Title text for suspend resume button when temp basal canceling")
- }
- }
- var isTransitioning: Bool {
- switch self {
- case .suspending, .resuming, .initiatingTempBasal, .cancelingTempBasal:
- return true
- default:
- return false
- }
- }
- }
|