Sfoglia il codice sorgente

Merge pull request #279 from nightscout/alpha

Merge alpha into dev
bjornoleh 1 anno fa
parent
commit
32b5e95377

+ 1 - 0
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -44,5 +44,6 @@
   "maxFat": 250,
   "maxProtein": 250,
   "displayFatAndProteinOnWatch": false,
+  "confirmBolusFaster": false,
   "lockScreenView": "simple"
 }

+ 10 - 49
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -43,10 +43,6 @@ private let staticPumpManagersByIdentifier: [String: PumpManagerUI.Type] = [
     MockPumpManager.pluginIdentifier: MockPumpManager.self
 ]
 
-// private let staticPumpManagersByIdentifier: [String: PumpManagerUI.Type] = staticPumpManagers.reduce(into: [:]) { map, Type in
-//    map[Type.managerIdentifier] = Type
-// }
-
 private let accessLock = NSRecursiveLock(label: "BaseDeviceDataManager.accessLock")
 
 final class BaseDeviceDataManager: DeviceDataManager, Injectable {
@@ -78,7 +74,8 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
         didSet {
             pumpManager?.pumpManagerDelegate = self
             pumpManager?.delegateQueue = processQueue
-            UserDefaults.standard.pumpManagerRawValue = pumpManager?.rawValue
+            rawPumpManager = pumpManager?.rawValue
+            UserDefaults.standard.clearLegacyPumpManagerRawValue()
             if let pumpManager = pumpManager {
                 pumpDisplayState.value = PumpDisplayState(name: pumpManager.localizedTitle, image: pumpManager.smallImage)
                 pumpName.send(pumpManager.localizedTitle)
@@ -105,6 +102,8 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
         }
     }
 
+    @PersistedProperty(key: "PumpManagerState") var rawPumpManager: PumpManager.RawValue?
+
     var bluetoothManager: BluetoothStateManager { bluetoothProvider }
 
     var hasBLEHeartbeat: Bool {
@@ -123,7 +122,11 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
     }
 
     func setupPumpManager() {
-        pumpManager = UserDefaults.standard.pumpManagerRawValue.flatMap { pumpManagerFromRawValue($0) }
+        if let pumpManagerRawValue = rawPumpManager ?? UserDefaults.standard.legacyPumpManagerRawValue {
+            pumpManager = pumpManagerFromRawValue(pumpManagerRawValue)
+        } else {
+            pumpManager = nil
+        }
     }
 
     func createBolusProgressReporter() -> DoseProgressReporter? {
@@ -163,20 +166,6 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                 self.updateUpdateFinished(true)
             }
         }
-
-//        pumpUpdateCancellable = Future<Bool, Never> { [unowned self] promise in
-//            pumpUpdatePromise = promise
-//            debug(.deviceManager, "Waiting for pump update and loop recommendation")
-//            processQueue.safeSync {
-//                pumpManager.ensureCurrentPumpData { _ in
-//                    debug(.deviceManager, "Pump data updated.")
-//                }
-//            }
-//        }
-//        .timeout(30, scheduler: processQueue)
-//        .replaceError(with: false)
-//        .replaceEmpty(with: false)
-//        .sink(receiveValue: updateUpdateFinished)
     }
 
     private func updateUpdateFinished(_ recommendsLoop: Bool) {
@@ -186,11 +175,6 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
             warning(.deviceManager, "Loop recommendation time out or got error. Trying to loop right now.")
         }
 
-        // directly in loop() function
-//        guard !loopInProgress else {
-//            warning(.deviceManager, "Loop already in progress. Skip recommendation.")
-//            return
-//        }
         self.recommendsLoop.send()
     }
 
@@ -319,7 +303,7 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
     }
 
     func pumpManagerDidUpdateState(_ pumpManager: PumpManager) {
-        UserDefaults.standard.pumpManagerRawValue = pumpManager.rawValue
+        rawPumpManager = pumpManager.rawValue
         if self.pumpManager == nil, let newPumpManager = pumpManager as? PumpManagerUI {
             self.pumpManager = newPumpManager
         }
@@ -540,29 +524,6 @@ extension BaseDeviceDataManager: DeviceManagerDelegate {
 
     func recordRetractedAlert(_: Alert, at _: Date) {}
 
-//    func scheduleNotification(
-//        for _: DeviceManager,
-//        identifier: String,
-//        content: UNNotificationContent,
-//        trigger: UNNotificationTrigger?
-//    ) {
-//        let request = UNNotificationRequest(
-//            identifier: identifier,
-//            content: content,
-//            trigger: trigger
-//        )
-//
-//        DispatchQueue.main.async {
-//            UNUserNotificationCenter.current().add(request)
-//        }
-//    }
-//
-//    func clearNotification(for _: DeviceManager, identifier: String) {
-//        DispatchQueue.main.async {
-//            UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
-//        }
-//    }
-
     func removeNotificationRequests(for _: DeviceManager, identifiers: [String]) {
         DispatchQueue.main.async {
             UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)

+ 2 - 16
FreeAPS/Sources/APS/Extensions/PumpManagerExtensions.swift

@@ -2,6 +2,8 @@ import LoopKit
 import LoopKitUI
 
 extension PumpManager {
+    typealias RawValue = [String: Any]
+
     var rawValue: [String: Any] {
         [
             "managerIdentifier": pluginIdentifier, // "managerIdentifier": type(of: self).managerIdentifier,
@@ -11,14 +13,6 @@ extension PumpManager {
 }
 
 extension PumpManagerUI {
-//    static func setupViewController() -> PumpManagerSetupViewController & UIViewController & CompletionNotifying {
-//        setupViewController(
-//            insulinTintColor: .accentColor,
-//            guidanceColors: GuidanceColors(acceptable: .green, warning: .orange, critical: .red),
-//            allowedInsulinTypes: [.apidra, .humalog, .novolog, .fiasp, .lyumjev]
-//        )
-//    }
-
     func settingsViewController(
         bluetoothProvider: BluetoothProvider,
         pumpManagerOnboardingDelegate: PumpManagerOnboardingDelegate?
@@ -32,14 +26,6 @@ extension PumpManagerUI {
         vc.pumpManagerOnboardingDelegate = pumpManagerOnboardingDelegate
         return vc
     }
-
-//    func settingsViewController() -> UIViewController & CompletionNotifying {
-//        settingsViewController(
-//            insulinTintColor: .accentColor,
-//            guidanceColors: GuidanceColors(acceptable: .green, warning: .orange, critical: .red),
-//            allowedInsulinTypes: [.apidra, .humalog, .novolog, .fiasp, .lyumjev]
-//        )
-//    }
 }
 
 protocol PumpSettingsBuilder {

+ 19 - 10
FreeAPS/Sources/APS/Extensions/UserDefaultsExtensions.swift

@@ -5,17 +5,10 @@ import RileyLinkKit
 
 extension UserDefaults {
     private enum Key: String {
-        case pumpManagerRawValue = "com.rileylink.PumpManagerRawValue"
+        case legacyPumpManagerRawValue = "com.rileylink.PumpManagerRawValue"
         case rileyLinkConnectionManagerState = "com.rileylink.RileyLinkConnectionManagerState"
-    }
-
-    var pumpManagerRawValue: PumpManager.RawStateValue? {
-        get {
-            dictionary(forKey: Key.pumpManagerRawValue.rawValue)
-        }
-        set {
-            set(newValue, forKey: Key.pumpManagerRawValue.rawValue)
-        }
+        case legacyPumpManagerState = "com.loopkit.Loop.PumpManagerState"
+        case legacyCGMManagerState = "com.loopkit.Loop.CGMManagerState"
     }
 
     var rileyLinkConnectionManagerState: RileyLinkConnectionState? {
@@ -30,4 +23,20 @@ extension UserDefaults {
             set(newValue?.rawValue, forKey: Key.rileyLinkConnectionManagerState.rawValue)
         }
     }
+
+    var legacyPumpManagerRawValue: PumpManager.RawValue? {
+        dictionary(forKey: Key.legacyPumpManagerRawValue.rawValue)
+    }
+
+    func clearLegacyPumpManagerRawValue() {
+        set(nil, forKey: Key.legacyPumpManagerRawValue.rawValue)
+    }
+
+    var legacyCGMManagerRawValue: CGMManager.RawValue? {
+        dictionary(forKey: Key.legacyCGMManagerState.rawValue)
+    }
+
+    func clearLegacyCGMManagerRawValue() {
+        set(nil, forKey: Key.legacyCGMManagerState.rawValue)
+    }
 }

+ 1 - 0
FreeAPS/Sources/APS/FetchGlucoseManager.swift

@@ -45,6 +45,7 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
     var cgmManager: CGMManagerUI? {
         didSet {
             rawCGMManager = cgmManager?.rawValue
+            UserDefaults.standard.clearLegacyCGMManagerRawValue()
         }
     }
 

+ 2 - 2
FreeAPS/Sources/APS/Storage/CarbsStorage.swift

@@ -232,8 +232,8 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
                 bolus: nil,
                 insulin: nil,
                 carbs: $0.carbs,
-                fat: nil,
-                protein: nil,
+                fat: $0.fat,
+                protein: $0.protein,
                 foodType: $0.note,
                 targetTop: nil,
                 targetBottom: nil

+ 2 - 2
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -332,8 +332,8 @@ extension NightscoutTreatment {
                 insulin: nil,
                 notes: nil,
                 carbs: Decimal(event.carbInput ?? 0),
-                fat: nil,
-                protein: nil,
+                fat: Decimal(event.fatInput ?? 0),
+                protein: Decimal(event.proteinInput ?? 0),
                 targetTop: nil,
                 targetBottom: nil
             )

+ 0 - 1
FreeAPS/Sources/Helpers/PropertyWrappers/PersistedProperty.swift

@@ -57,7 +57,6 @@ import Foundation
         self.key = key
 
         let documents: URL
-
         guard let localDocuments = try? FileManager.default.url(
             for: .documentDirectory,
             in: .userDomainMask,

+ 5 - 0
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -45,6 +45,7 @@ struct FreeAPSSettings: JSON, Equatable {
     var maxFat: Decimal = 250
     var maxProtein: Decimal = 250
     var displayFatAndProteinOnWatch: Bool = false
+    var confirmBolusFaster: Bool = false
     var onlyAutotuneBasals: Bool = false
     var useLiveActivity: Bool = false
     var lockScreenView: LockScreenView = .simple
@@ -237,6 +238,10 @@ extension FreeAPSSettings: Decodable {
             settings.displayFatAndProteinOnWatch = displayFatAndProteinOnWatch
         }
 
+        if let confirmBolusFaster = try? container.decode(Bool.self, forKey: .confirmBolusFaster) {
+            settings.confirmBolusFaster = confirmBolusFaster
+        }
+
         if let onlyAutotuneBasals = try? container.decode(Bool.self, forKey: .onlyAutotuneBasals) {
             settings.onlyAutotuneBasals = onlyAutotuneBasals
         }

+ 8 - 0
FreeAPS/Sources/Models/PumpHistoryEvent.swift

@@ -11,6 +11,8 @@ struct PumpHistoryEvent: JSON, Equatable {
     let rate: Decimal?
     let temp: TempType?
     let carbInput: Int?
+    let fatInput: Int?
+    let proteinInput: Int?
     let note: String?
     let isSMB: Bool?
     let isExternalInsulin: Bool?
@@ -25,6 +27,8 @@ struct PumpHistoryEvent: JSON, Equatable {
         rate: Decimal? = nil,
         temp: TempType? = nil,
         carbInput: Int? = nil,
+        fatInput: Int? = nil,
+        proteinInput: Int? = nil,
         note: String? = nil,
         isSMB: Bool? = nil,
         isExternalInsulin: Bool? = nil
@@ -38,6 +42,8 @@ struct PumpHistoryEvent: JSON, Equatable {
         self.rate = rate
         self.temp = temp
         self.carbInput = carbInput
+        self.fatInput = fatInput
+        self.proteinInput = proteinInput
         self.note = note
         self.isSMB = isSMB
         self.isExternalInsulin = isExternalInsulin
@@ -88,6 +94,8 @@ extension PumpHistoryEvent {
         case rate
         case temp
         case carbInput = "carb_input"
+        case fatInput
+        case proteinInput
         case note
         case isSMB
         case isExternalInsulin

+ 20 - 4
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorDataFlow.swift

@@ -11,7 +11,11 @@ enum PreferencesEditor {
 
     enum FieldType {
         case boolean(keypath: WritableKeyPath<Preferences, Bool>)
-        case decimal(keypath: WritableKeyPath<Preferences, Decimal>)
+        case decimal(
+            keypath: WritableKeyPath<Preferences, Decimal>,
+            minVal: WritableKeyPath<Preferences, Decimal>? = nil,
+            maxVal: WritableKeyPath<Preferences, Decimal>? = nil
+        )
         case insulinCurve(keypath: WritableKeyPath<Preferences, InsulinCurve>)
     }
 
@@ -34,7 +38,7 @@ enum PreferencesEditor {
         var decimalValue: Decimal {
             get {
                 switch type {
-                case let .decimal(keypath):
+                case let .decimal(keypath, _, _):
                     return settable?.get(keypath) ?? 0
                 default: return 0
                 }
@@ -57,8 +61,20 @@ enum PreferencesEditor {
             switch (type, value) {
             case let (.boolean(keypath), value as Bool):
                 settable?.set(keypath, value: value)
-            case let (.decimal(keypath), value as Decimal):
-                settable?.set(keypath, value: value)
+            case let (.decimal(keypath, minVal, maxVal), value as Decimal):
+                let constrainedValue: Decimal
+                if let minValue = minVal, let minValueDecimal: Decimal = settable?.get(minValue), let maxValue = maxVal,
+                   let maxValueDecimal: Decimal = settable?.get(maxValue)
+                {
+                    constrainedValue = min(max(value, minValueDecimal), maxValueDecimal)
+                } else if let minValue = minVal, let minValueDecimal: Decimal = settable?.get(minValue) {
+                    constrainedValue = max(value, minValueDecimal)
+                } else if let maxValue = maxVal, let maxValueDecimal: Decimal = settable?.get(maxValue) {
+                    constrainedValue = min(value, maxValueDecimal)
+                } else {
+                    constrainedValue = value
+                }
+                settable?.set(keypath, value: constrainedValue)
             case let (.insulinCurve(keypath), value as InsulinCurve):
                 settable?.set(keypath, value: value)
             default: break

+ 17 - 4
FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorProvider.swift

@@ -19,7 +19,7 @@ extension PumpSettingsEditor {
         }
 
         func save(settings: PumpSettings) -> AnyPublisher<Void, Error> {
-            func save() {
+            func save(_ settings: PumpSettings) {
                 storage.save(settings, as: OpenAPS.Settings.settings)
                 processQueue.async {
                     self.broadcaster.notify(PumpSettingsObserver.self, on: self.processQueue) {
@@ -29,7 +29,7 @@ extension PumpSettingsEditor {
             }
 
             guard let pump = deviceManager?.pumpManager else {
-                save()
+                save(settings)
                 return Just(()).setFailureType(to: Error.self).eraseToAnyPublisher()
             }
             // Don't ask why 🤦‍♂️
@@ -44,8 +44,21 @@ extension PumpSettingsEditor {
                 self.processQueue.async {
                     pump.syncDeliveryLimits(limits: limits) { result in
                         switch result {
-                        case .success:
-                            save()
+                        case let .success(actual):
+                            // Store the limits from the pumpManager to ensure the correct values
+                            // Example: Dana pumps don't allow to set these limits, only to fetch them
+                            // This will ensure we always have the correct values stored
+                            save(PumpSettings(
+                                insulinActionCurve: settings.insulinActionCurve,
+                                maxBolus: Decimal(
+                                    actual.maximumBolus?
+                                        .doubleValue(for: .internationalUnit()) ?? Double(settings.maxBolus)
+                                ),
+                                maxBasal: Decimal(
+                                    actual.maximumBasalRate?
+                                        .doubleValue(for: .internationalUnitsPerHour) ?? Double(settings.maxBasal)
+                                )
+                            ))
                             promise(.success(()))
                         case let .failure(error):
                             promise(.failure(error))

+ 3 - 0
FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift

@@ -25,7 +25,10 @@ extension PumpSettingsEditor {
             provider.save(settings: settings)
                 .receive(on: DispatchQueue.main)
                 .sink { _ in
+                    let settings = self.provider.settings()
                     self.syncInProgress = false
+                    self.maxBasal = settings.maxBasal
+                    self.maxBolus = settings.maxBolus
 
                 } receiveValue: {}
                 .store(in: &lifetime)

+ 17 - 0
FreeAPS/Sources/Modules/Settings/SettingsStateModel.swift

@@ -62,6 +62,23 @@ extension Settings {
         func hideSettingsModal() {
             hideModal()
         }
+
+        // Commenting this out for now, as not needed and possibly dangerous for users to be able to nuke their pump pairing informations via the debug menu
+        // Leaving it in here, as it may be a handy functionality for further testing or developers.
+        // See https://github.com/nightscout/Trio/pull/277 for more information
+//
+//        func resetLoopDocuments() {
+//            guard let localDocuments = try? FileManager.default.url(
+//                for: .documentDirectory,
+//                in: .userDomainMask,
+//                appropriateFor: nil,
+//                create: true
+//            ) else {
+//                preconditionFailure("Could not get a documents directory URL.")
+//            }
+//            let storageURL = localDocuments.appendingPathComponent("PumpManagerState" + ".plist")
+//            try? FileManager.default.removeItem(at: storageURL)
+//        }
     }
 }
 

+ 10 - 0
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -61,6 +61,16 @@ extension Settings {
                                     .frame(maxWidth: .infinity, alignment: .trailing)
                                     .buttonStyle(.borderedProminent)
                             }
+                            // Commenting this out for now, as not needed and possibly dangerous for users to be able to nuke their pump pairing informations via the debug menu
+                            // Leaving it in here, as it may be a handy functionality for further testing or developers.
+                            // See https://github.com/nightscout/Trio/pull/277 for more information
+//
+//                            HStack {
+//                                Text("Delete Stored Pump State Binary Files")
+//                                Button("Delete") { state.resetLoopDocuments() }
+//                                    .frame(maxWidth: .infinity, alignment: .trailing)
+//                                    .buttonStyle(.borderedProminent)
+//                            }
                         }
                         Group {
                             Text("Preferences")

+ 2 - 2
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigRootView.swift

@@ -17,10 +17,10 @@ extension WatchConfig {
                             Text(v.displayName).tag(v)
                         }
                     }
+                    Toggle("Display Protein & Fat", isOn: $state.displayFatAndProteinOnWatch)
+                    Toggle("Confirm Bolus Faster", isOn: $state.confirmBolusFaster)
                 }
 
-                Toggle("Display Protein & Fat", isOn: $state.displayFatAndProteinOnWatch)
-
                 Section(header: Text("Garmin Watch")) {
                     List {
                         ForEach(state.devices, id: \.uuid) { device in

+ 2 - 0
FreeAPS/Sources/Modules/WatchConfig/WatchConfigStateModel.swift

@@ -31,6 +31,7 @@ extension WatchConfig {
         @Published var devices: [IQDevice] = []
         @Published var selectedAwConfig: AwConfig = .HR
         @Published var displayFatAndProteinOnWatch = false
+        @Published var confirmBolusFaster = false
 
         private(set) var preferences = Preferences()
 
@@ -38,6 +39,7 @@ extension WatchConfig {
             preferences = provider.preferences
 
             subscribeSetting(\.displayFatAndProteinOnWatch, on: $displayFatAndProteinOnWatch) { displayFatAndProteinOnWatch = $0 }
+            subscribeSetting(\.confirmBolusFaster, on: $confirmBolusFaster) { confirmBolusFaster = $0 }
             subscribeSetting(\.displayOnWatch, on: $selectedAwConfig) { selectedAwConfig = $0 }
             didSet: { [weak self] value in
                 // for compatibility with old displayHR

+ 1 - 0
FreeAPS/Sources/Services/WatchManager/WatchManager.swift

@@ -106,6 +106,7 @@ final class BaseWatchManager: NSObject, WatchManager, Injectable {
             self.state.bolusAfterCarbs = !self.settingsManager.settings.skipBolusScreenAfterCarbs
             self.state.displayOnWatch = self.settingsManager.settings.displayOnWatch
             self.state.displayFatAndProteinOnWatch = self.settingsManager.settings.displayFatAndProteinOnWatch
+            self.state.confirmBolusFaster = self.settingsManager.settings.confirmBolusFaster
 
             let eBG = self.evetualBGStraing()
             self.state.eventualBG = eBG.map { "⇢ " + $0 }

+ 1 - 0
FreeAPSWatch WatchKit Extension/DataFlow.swift

@@ -22,6 +22,7 @@ struct WatchState: Codable {
     var eventualBGRaw: String?
     var displayOnWatch: AwConfig?
     var displayFatAndProteinOnWatch: Bool?
+    var confirmBolusFaster: Bool?
     var isf: Decimal?
     var override: String?
 }

+ 1 - 1
FreeAPSWatch WatchKit Extension/Views/BolusConfirmationView.swift

@@ -75,7 +75,7 @@ struct BolusConfirmationView: View {
             $crownProgress,
             from: 0.0,
             through: 100.0,
-            by: 0.5,
+            by: state.confirmBolusFaster ? 5 : 0.5,
             sensitivity: .high,
             isContinuous: false,
             isHapticFeedbackEnabled: true

+ 2 - 0
FreeAPSWatch WatchKit Extension/WatchStateModel.swift

@@ -34,6 +34,7 @@ class WatchStateModel: NSObject, ObservableObject {
     @Published var isBolusViewActive = false
     @Published var displayOnWatch: AwConfig = .BGTarget
     @Published var displayFatAndProteinOnWatch = false
+    @Published var confirmBolusFaster = false
     @Published var eventualBG = ""
     @Published var isConfirmationViewActive = false {
         didSet {
@@ -174,6 +175,7 @@ class WatchStateModel: NSObject, ObservableObject {
         eventualBG = state.eventualBG ?? ""
         displayOnWatch = state.displayOnWatch ?? .BGTarget
         displayFatAndProteinOnWatch = state.displayFatAndProteinOnWatch ?? false
+        confirmBolusFaster = state.confirmBolusFaster ?? false
         isf = state.isf
         override = state.override
     }