Просмотр исходного кода

Merge branch 'core-data-sync-trio' into fix/dana_timezone

marionbarker 1 год назад
Родитель
Сommit
27a60d8503
33 измененных файлов с 248 добавлено и 149 удалено
  1. 13 11
      FreeAPS/Sources/APS/APSManager.swift
  2. 1 1
      FreeAPS/Sources/APS/DeviceDataManager.swift
  3. 2 1
      FreeAPS/Sources/APS/FetchGlucoseManager.swift
  4. 2 1
      FreeAPS/Sources/APS/Storage/GlucoseStorage.swift
  5. 1 0
      FreeAPS/Sources/Application/FreeAPSApp.swift
  6. 71 2
      FreeAPS/Sources/Models/BloodGlucose.swift
  7. 1 1
      FreeAPS/Sources/Models/DecimalPickerSettings.swift
  8. 8 5
      FreeAPS/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+Overrides.swift
  9. 1 1
      FreeAPS/Sources/Modules/Adjustments/View/Overrides/AddOverrideForm.swift
  10. 1 1
      FreeAPS/Sources/Modules/Adjustments/View/Overrides/EditOverrideForm.swift
  11. 11 9
      FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift
  12. 1 1
      FreeAPS/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift
  13. 3 1
      FreeAPS/Sources/Modules/CarbRatioEditor/View/CarbRatioEditorRootView.swift
  14. 2 2
      FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/GlucoseTargetSetup.swift
  15. 1 0
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  16. 1 1
      FreeAPS/Sources/Modules/Home/View/Chart/ChartElements/CarbView.swift
  17. 2 1
      FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift
  18. 1 1
      FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift
  19. 8 1
      FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift
  20. 7 1
      FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift
  21. 12 6
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift
  22. 36 39
      FreeAPS/Sources/Modules/PumpConfig/View/PumpConfigRootView.swift
  23. 6 1
      FreeAPS/Sources/Modules/PumpConfig/View/PumpSetupView.swift
  24. 1 1
      FreeAPS/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift
  25. 2 7
      FreeAPS/Sources/Modules/Stat/View/StatsView.swift
  26. 22 22
      FreeAPS/Sources/Modules/TargetsEditor/View/TargetsEditorRootView.swift
  27. 12 10
      FreeAPS/Sources/Modules/Treatments/View/TreatmentsRootView.swift
  28. 1 1
      FreeAPS/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift
  29. 14 16
      FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigGarminView.swift
  30. 1 1
      Model/Classes+Properties/OpenAPS_Battery+CoreDataProperties.swift
  31. 1 1
      Model/TrioCoreDataPersistentContainer.xcdatamodeld/TrioCoreDataPersistentContainer.xcdatamodel/contents
  32. 1 1
      OmniBLE
  33. 1 1
      OmniKit

+ 13 - 11
FreeAPS/Sources/APS/APSManager.swift

@@ -1168,16 +1168,18 @@ final class BaseAPSManager: APSManager, Injectable {
             let hbA1cDisplayUnit = self.settingsManager.settings.hbA1cDisplayUnit
 
             let hbs = Durations(
-                day: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
-                    self.roundDecimal(Decimal(oneDayGlucose.ifcc), 1) : self.roundDecimal(Decimal(oneDayGlucose.ngsp), 1),
-                week: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
-                    self.roundDecimal(Decimal(sevenDaysGlucose.ifcc), 1) : self
-                    .roundDecimal(Decimal(sevenDaysGlucose.ngsp), 1),
-                month: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
-                    self.roundDecimal(Decimal(thirtyDaysGlucose.ifcc), 1) : self
-                    .roundDecimal(Decimal(thirtyDaysGlucose.ngsp), 1),
-                total: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
-                    self.roundDecimal(Decimal(totalDaysGlucose.ifcc), 1) : self.roundDecimal(Decimal(totalDaysGlucose.ngsp), 1)
+                day: hbA1cDisplayUnit == .mmolMol ?
+                    self.roundDecimal(Decimal(oneDayGlucose.ifcc), 1) :
+                    self.roundDecimal(Decimal(oneDayGlucose.ngsp), 1),
+                week: hbA1cDisplayUnit == .mmolMol ?
+                    self.roundDecimal(Decimal(sevenDaysGlucose.ifcc), 1) :
+                    self.roundDecimal(Decimal(sevenDaysGlucose.ngsp), 1),
+                month: hbA1cDisplayUnit == .mmolMol ?
+                    self.roundDecimal(Decimal(thirtyDaysGlucose.ifcc), 1) :
+                    self.roundDecimal(Decimal(thirtyDaysGlucose.ngsp), 1),
+                total: hbA1cDisplayUnit == .mmolMol ?
+                    self.roundDecimal(Decimal(totalDaysGlucose.ifcc), 1) :
+                    self.roundDecimal(Decimal(totalDaysGlucose.ngsp), 1)
             )
 
             var oneDay_: (TIR: Double, hypos: Double, hypers: Double, normal_: Double) = (0.0, 0.0, 0.0, 0.0)
@@ -1387,7 +1389,7 @@ extension BaseAPSManager: PumpManagerStatusObserver {
                 }
 
                 batteryToStore.date = Date()
-                batteryToStore.percent = Int16(percent)
+                batteryToStore.percent = Double(percent)
                 batteryToStore.voltage = nil
                 batteryToStore.status = percent > 10 ? "normal" : "low"
                 batteryToStore.display = status.pumpBatteryChargeRemaining != nil

+ 1 - 1
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -132,7 +132,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                             let saveBatteryToCoreData = OpenAPS_Battery(context: self.privateContext)
                             saveBatteryToCoreData.id = UUID()
                             saveBatteryToCoreData.date = Date()
-                            saveBatteryToCoreData.percent = Int16(batteryPercent)
+                            saveBatteryToCoreData.percent = Double(batteryPercent)
                             saveBatteryToCoreData.voltage = nil
                             saveBatteryToCoreData.status = batteryPercent >= 10 ? BatteryState.normal.rawValue : BatteryState
                                 .low.rawValue

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

@@ -213,7 +213,8 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
                     unfiltered: Decimal(result.glucose),
                     filtered: Decimal(result.glucose),
                     noise: nil,
-                    glucose: Int(result.glucose)
+                    glucose: Int(result.glucose),
+                    type: "sgv"
                 )
             }
         }

+ 2 - 1
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -299,7 +299,8 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                     unfiltered: Decimal(result.glucose),
                     filtered: Decimal(result.glucose),
                     noise: nil,
-                    glucose: Int(result.glucose)
+                    glucose: Int(result.glucose),
+                    type: "sgv"
                 )
             }
         }

+ 1 - 0
FreeAPS/Sources/Application/FreeAPSApp.swift

@@ -49,6 +49,7 @@ import Swinject
         _ = resolver.resolve(CalendarManager.self)!
         _ = resolver.resolve(UserNotificationsManager.self)!
         _ = resolver.resolve(WatchManager.self)!
+        _ = resolver.resolve(ContactImageManager.self)!
         _ = resolver.resolve(HealthKitManager.self)!
         _ = resolver.resolve(BluetoothStateManager.self)!
         _ = resolver.resolve(PluginManager.self)!

+ 71 - 2
FreeAPS/Sources/Models/BloodGlucose.swift

@@ -2,7 +2,7 @@ import Foundation
 import HealthKit
 import LoopKit
 
-struct BloodGlucose: JSON, Identifiable, Hashable {
+struct BloodGlucose: JSON, Identifiable, Hashable, Codable {
     enum Direction: String, JSON {
         case tripleUp = "TripleUp"
         case doubleUp = "DoubleUp"
@@ -59,6 +59,76 @@ struct BloodGlucose: JSON, Identifiable, Hashable {
         }
     }
 
+    enum CodingKeys: String, CodingKey {
+        case _id
+        case sgv
+        case direction
+        case date
+        case dateString
+        case unfiltered
+        case filtered
+        case noise
+        case glucose
+        case type
+        case activationDate
+        case sessionStartDate
+        case transmitterID
+    }
+
+    init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        _id = try container.decode(String.self, forKey: ._id)
+
+        do {
+            sgv = try container.decode(Int.self, forKey: .sgv)
+        } catch {
+            // The nightscout API returns a double instead of an int
+            sgv = Int(try container.decode(Double.self, forKey: .sgv))
+        }
+
+        direction = try container.decodeIfPresent(Direction.self, forKey: .direction)
+        date = try container.decode(Decimal.self, forKey: .date)
+        dateString = try container.decode(Date.self, forKey: .dateString)
+        unfiltered = try container.decodeIfPresent(Decimal.self, forKey: .unfiltered)
+        filtered = try container.decodeIfPresent(Decimal.self, forKey: .filtered)
+        noise = try container.decodeIfPresent(Int.self, forKey: .noise)
+        glucose = try container.decodeIfPresent(Int.self, forKey: .glucose)
+        type = try container.decodeIfPresent(String.self, forKey: .type)
+        activationDate = try container.decodeIfPresent(Date.self, forKey: .activationDate)
+        sessionStartDate = try container.decodeIfPresent(Date.self, forKey: .sessionStartDate)
+        transmitterID = try container.decodeIfPresent(String.self, forKey: .transmitterID)
+    }
+
+    init(
+        _id: String = UUID().uuidString,
+        sgv: Int? = nil,
+        direction: Direction? = nil,
+        date: Decimal,
+        dateString: Date,
+        unfiltered: Decimal? = nil,
+        filtered: Decimal? = nil,
+        noise: Int? = nil,
+        glucose: Int? = nil,
+        type: String? = nil,
+        activationDate: Date? = nil,
+        sessionStartDate: Date? = nil,
+        transmitterID: String? = nil
+    ) {
+        self._id = _id
+        self.sgv = sgv
+        self.direction = direction
+        self.date = date
+        self.dateString = dateString
+        self.unfiltered = unfiltered
+        self.filtered = filtered
+        self.noise = noise
+        self.glucose = glucose
+        self.type = type
+        self.activationDate = activationDate
+        self.sessionStartDate = sessionStartDate
+        self.transmitterID = transmitterID
+    }
+
     var _id: String?
     var id: String {
         _id ?? UUID().uuidString
@@ -76,7 +146,6 @@ struct BloodGlucose: JSON, Identifiable, Hashable {
     var activationDate: Date? = nil
     var sessionStartDate: Date? = nil
     var transmitterID: String? = nil
-
     var isStateValid: Bool { sgv ?? 0 >= 39 && noise ?? 1 != 4 }
 
     static func == (lhs: BloodGlucose, rhs: BloodGlucose) -> Bool {

+ 1 - 1
FreeAPS/Sources/Models/DecimalPickerSettings.swift

@@ -136,7 +136,7 @@ struct DecimalPickerSettings {
     var hours = PickerSetting(value: 6, step: 0.5, min: 2, max: 24, type: PickerSetting.PickerSettingType.hour)
     var dia = PickerSetting(value: 10, step: 0.5, min: 5, max: 10, type: PickerSetting.PickerSettingType.hour)
     var maxBolus = PickerSetting(value: 10, step: 0.5, min: 1, max: 30, type: PickerSetting.PickerSettingType.insulinUnit)
-    var maxBasal = PickerSetting(value: 10, step: 0.5, min: 1, max: 30, type: PickerSetting.PickerSettingType.insulinUnit)
+    var maxBasal = PickerSetting(value: 10, step: 0.5, min: 0.5, max: 30, type: PickerSetting.PickerSettingType.insulinUnit)
 }
 
 struct PickerSetting {

+ 8 - 5
FreeAPS/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+Overrides.swift

@@ -187,11 +187,14 @@ extension Adjustments.StateModel {
     /// Then unpack it on the view context and update the State variables which can be used on in the View for some Logic
     /// This also needs to be called when we cancel an Override via the Home View to update the State of the Button for this case
     func updateLatestOverrideConfiguration() {
-        Task {
-            let id = await overrideStorage.loadLatestOverrideConfigurations(fetchLimit: 1)
-            async let updateState: () = updateLatestOverrideConfigurationOfState(from: id)
-            async let setOverride: () = setCurrentOverride(from: id)
-            _ = await (updateState, setOverride)
+        Task { [weak self] in
+            guard let self = self else { return }
+
+            let id = await self.overrideStorage.loadLatestOverrideConfigurations(fetchLimit: 1)
+
+            // execute sequentially instead of concurrently
+            await self.updateLatestOverrideConfigurationOfState(from: id)
+            await self.setCurrentOverride(from: id)
         }
     }
 

+ 1 - 1
FreeAPS/Sources/Modules/Adjustments/View/Overrides/AddOverrideForm.swift

@@ -74,7 +74,7 @@ struct AddOverrideForm: View {
             Section(footer: state.percentageDescription(state.overridePercentage)) {
                 // Percentage Picker
                 HStack {
-                    Text("Change Basal Rate by")
+                    Text("Basal Rate Adjustment")
                     Spacer()
                     Text("\(state.overridePercentage.formatted(.number)) %")
                         .foregroundColor(!displayPickerPercentage ? .primary : .accentColor)

+ 1 - 1
FreeAPS/Sources/Modules/Adjustments/View/Overrides/EditOverrideForm.swift

@@ -140,7 +140,7 @@ struct EditOverrideForm: View {
             // Percentage Picker
             Section(footer: state.percentageDescription(percentage)) {
                 HStack {
-                    Text("Change Basal Rate by")
+                    Text("Basal Rate Adjustment")
                     Spacer()
                     Text("\(percentage.formatted(.number)) %")
                         .foregroundColor(!displayPickerPercentage ? .primary : .accentColor)

+ 11 - 9
FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -84,23 +84,25 @@ extension BasalProfileEditor {
 
                 Group {
                     HStack {
-                        Button {
+                        Button(action: {
                             let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
                             impactHeavy.impactOccurred()
                             state.save()
-                        } label: {
+                        }, label: {
                             HStack {
                                 if state.syncInProgress {
                                     ProgressView().padding(.trailing, 10)
                                 }
                                 Text(state.syncInProgress ? "Saving..." : "Save")
-                            }.padding(10)
-                        }
-                        .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
-                        .disabled(shouldDisableButton)
-                        .background(shouldDisableButton ? Color(.systemGray4) : Color(.systemBlue))
-                        .tint(.white)
-                        .clipShape(RoundedRectangle(cornerRadius: 8))
+                            }
+                            .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
+                            .padding(10)
+                        })
+                            .frame(width: UIScreen.main.bounds.width * 0.9, height: 40, alignment: .center)
+                            .disabled(shouldDisableButton)
+                            .background(shouldDisableButton ? Color(.systemGray4) : Color(.systemBlue))
+                            .tint(.white)
+                            .clipShape(RoundedRectangle(cornerRadius: 8))
                     }
                 }.padding(5)
             }

+ 1 - 1
FreeAPS/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift

@@ -138,7 +138,7 @@ extension BolusCalculatorConfig {
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
-                        Text("Default Percent: 200%").bold()
+                        Text("Default Percent: 100%").bold()
                         Text("Do not enable this feature until you have optimized your CR (carb ratio) setting.").bold()
                         Text(
                             "Enabling this setting adds a \"Super Bolus\" option to the bolus calculator. Once this feature is enabled, a percentage setting will appear for you to select."

+ 3 - 1
FreeAPS/Sources/Modules/CarbRatioEditor/View/CarbRatioEditorRootView.swift

@@ -53,7 +53,9 @@ extension CarbRatioEditor {
                                         ProgressView().padding(.trailing, 10)
                                     }
                                     Text(state.shouldDisplaySaving ? "Saving..." : "Save")
-                                }.padding(10)
+                                }
+                                .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
+                                .padding(10)
                             }
                         }
                         .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)

+ 2 - 2
FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/GlucoseTargetSetup.swift

@@ -20,7 +20,7 @@ extension Home.StateModel {
 
         // Ensure there are targets to process
         guard !rawTargets.targets.isEmpty else {
-            print("Warning: No targets to process in rawTargets.")
+            debugPrint("\(DebuggingIdentifiers.failed) Warning: No targets to process in rawTargets.")
             return []
         }
 
@@ -37,7 +37,7 @@ extension Home.StateModel {
 
             // Validate target index to ensure safety
             guard targetIndex < targets.count else {
-                print("Error: Invalid target index \(targetIndex).")
+                debugPrint("\(DebuggingIdentifiers.failed) Error: Invalid target index \(targetIndex).")
                 continue
             }
 

+ 1 - 0
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -580,6 +580,7 @@ extension Home.StateModel:
         highGlucose = settingsManager.settings.high
         Task {
             await getCurrentGlucoseTarget()
+            await setupGlucoseTargets()
         }
         hbA1cDisplayUnit = settingsManager.settings.hbA1cDisplayUnit
         glucoseColorScheme = settingsManager.settings.glucoseColorScheme

+ 1 - 1
FreeAPS/Sources/Modules/Home/View/Chart/ChartElements/CarbView.swift

@@ -50,7 +50,7 @@ struct CarbView: ChartContent {
         ForEach(fpuData, id: \.id) { fpu in
             let fpuAmount = fpu.carbs
             let size = (MainChartHelper.Config.fpuSize + CGFloat(fpuAmount) * MainChartHelper.Config.carbsScale) * 1.8
-            let yPosition = minValue
+            let yPosition = minValue // value is parsed to mmol/L when passed into struct based on user settings
 
             PointMark(
                 x: .value("Time", fpu.date ?? Date(), unit: .second),

+ 2 - 1
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -157,7 +157,8 @@ extension MainChartView {
                     units: state.units,
                     carbData: state.carbsFromPersistence,
                     fpuData: state.fpusFromPersistence,
-                    minValue: state.minYAxisValue
+                    minValue: units == .mgdL ? state.minYAxisValue : state.minYAxisValue
+                        .asMmolL
                 )
 
                 ForecastView(

+ 1 - 1
FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift

@@ -91,7 +91,7 @@ struct PumpView: View {
                         Image(systemName: "battery.100")
                             .font(.callout)
                             .foregroundStyle(batteryColor)
-                        Text("\(Int(battery.first?.percent ?? 100)) %")
+                        Text("\(Formatter.integerFormatter.string(for: battery.first?.percent ?? 100) ?? "100") %")
                             .font(.callout).fontWeight(.bold).fontDesign(.rounded)
                     }
                 }

+ 8 - 1
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -46,7 +46,14 @@ extension ISFEditor {
                                     state.shouldDisplaySaving = false
                                 }
                             } label: {
-                                Text(state.shouldDisplaySaving ? "Saving..." : "Save").padding(10)
+                                HStack {
+                                    if state.shouldDisplaySaving {
+                                        ProgressView().padding(.trailing, 10)
+                                    }
+                                    Text(state.shouldDisplaySaving ? "Saving..." : "Save")
+                                }
+                                .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
+                                .padding(10)
                             }
                         }
                         .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)

+ 7 - 1
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -20,6 +20,7 @@ extension NightscoutConfig {
         @Published var url = ""
         @Published var secret = ""
         @Published var message = ""
+        @Published var isValidURL: Bool = false
         @Published var connecting = false
         @Published var backfilling = false
         @Published var isUploadEnabled = false // Allow uploads
@@ -88,12 +89,17 @@ extension NightscoutConfig {
                 let fixedURL = url.dropLast()
                 url = String(fixedURL)
             }
-            guard let url = URL(string: url) else {
+
+            guard let url = URL(string: url), self.url.hasPrefix("https://") else {
                 message = "Invalid URL"
+                isValidURL = false
                 return
             }
+
             connecting = true
+            isValidURL = true
             message = ""
+
             provider.checkConnection(url: url, secret: secret.isEmpty ? nil : secret)
                 .receive(on: DispatchQueue.main)
                 .sink { completion in

+ 12 - 6
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift

@@ -19,17 +19,23 @@ struct NightscoutConnectView: View {
             Section(
                 header: Text("Connect to Nightscout"),
                 content: {
-                    TextField("URL", text: $state.url)
-                        .disableAutocorrection(true)
-                        .textContentType(.URL)
-                        .autocapitalization(.none)
-                        .keyboardType(.URL)
+                    HStack {
+                        TextField("URL", text: $state.url)
+                            .disableAutocorrection(true)
+                            .textContentType(.URL)
+                            .autocapitalization(.none)
+                            .keyboardType(.URL)
+                        if state.message.isNotEmpty && !state.isValidURL {
+                            Image(systemName: "exclamationmark.triangle.fill")
+                                .foregroundStyle(.orange)
+                        }
+                    }
                     SecureField("API secret", text: $state.secret)
                         .disableAutocorrection(true)
                         .autocapitalization(.none)
                         .textContentType(.password)
                         .keyboardType(.asciiCapable)
-                    if !state.message.isEmpty {
+                    if state.message.isNotEmpty {
                         Text(state.message)
                     }
                     if state.connecting {

+ 36 - 39
FreeAPS/Sources/Modules/PumpConfig/View/PumpConfigRootView.swift

@@ -57,25 +57,6 @@ extension PumpConfig {
                                         Spacer()
                                         Button(
                                             action: {
-                                                hintLabel = "Pump Pairing to Trio"
-                                                selectedVerboseHint =
-                                                    AnyView(
-                                                        VStack(alignment: .leading, spacing: 10) {
-                                                            Text(
-                                                                "Current Pump Models Supported:"
-                                                            )
-                                                            VStack(alignment: .leading) {
-                                                                Text("• Medtronic")
-                                                                Text("• Omnipod Eros")
-                                                                Text("• Omnipod Dash")
-                                                                Text("• Dana (RS/-i)")
-                                                                Text("• Pump Simulator")
-                                                            }
-                                                            Text(
-                                                                "Note: If using a pump simulator, you will not have continuous readings from the CGM in Trio. Using a pump simulator is only advisable for becoming familiar with the app user interface. It will not give you insight on how the algorithm will respond."
-                                                            )
-                                                        }
-                                                    )
                                                 shouldDisplayHint.toggle()
                                             },
                                             label: {
@@ -97,12 +78,46 @@ extension PumpConfig {
                 .navigationTitle("Insulin Pump")
                 .navigationBarTitleDisplayMode(.automatic)
                 .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
+                .sheet(isPresented: $state.setupPump) {
+                    if let pumpManager = state.provider.apsManager.pumpManager {
+                        PumpSettingsView(
+                            pumpManager: pumpManager,
+                            bluetoothManager: state.provider.apsManager.bluetoothManager!,
+                            completionDelegate: state,
+                            setupDelegate: state
+                        )
+                    } else {
+                        PumpSetupView(
+                            pumpType: state.setupPumpType,
+                            pumpInitialSettings: state.initialSettings,
+                            bluetoothManager: state.provider.apsManager.bluetoothManager!,
+                            completionDelegate: state,
+                            setupDelegate: state
+                        )
+                    }
+                }
                 .sheet(isPresented: $shouldDisplayHint) {
                     SettingInputHintView(
                         hintDetent: $hintDetent,
                         shouldDisplayHint: $shouldDisplayHint,
-                        hintLabel: hintLabel ?? "",
-                        hintText: selectedVerboseHint ?? AnyView(EmptyView()),
+                        hintLabel: "Pump Pairing to Trio",
+                        hintText: AnyView(
+                            VStack(alignment: .leading, spacing: 10) {
+                                Text(
+                                    "Current Pump Models Supported:"
+                                )
+                                VStack(alignment: .leading) {
+                                    Text("• Medtronic")
+                                    Text("• Omnipod Eros")
+                                    Text("• Omnipod Dash")
+                                    Text("• Dana (RS/-i)")
+                                    Text("• Pump Simulator")
+                                }
+                                Text(
+                                    "Note: If using a pump simulator, you will not have continuous readings from the CGM in Trio. Using a pump simulator is only advisable for becoming familiar with the app user interface. It will not give you insight on how the algorithm will respond."
+                                )
+                            }
+                        ),
                         sheetTitle: "Help"
                     )
                 }
@@ -114,24 +129,6 @@ extension PumpConfig {
                     Button("Pump Simulator") { state.addPump(.simulator) }
                 } message: { Text("Select Pump Model") }
             }
-            .sheet(isPresented: $state.setupPump) {
-                if let pumpManager = state.provider.apsManager.pumpManager {
-                    PumpSettingsView(
-                        pumpManager: pumpManager,
-                        bluetoothManager: state.provider.apsManager.bluetoothManager!,
-                        completionDelegate: state,
-                        setupDelegate: state
-                    )
-                } else {
-                    PumpSetupView(
-                        pumpType: state.setupPumpType,
-                        pumpInitialSettings: state.initialSettings,
-                        bluetoothManager: state.provider.apsManager.bluetoothManager!,
-                        completionDelegate: state,
-                        setupDelegate: state
-                    )
-                }
-            }
         }
     }
 }

+ 6 - 1
FreeAPS/Sources/Modules/PumpConfig/View/PumpSetupView.swift

@@ -87,7 +87,12 @@ extension PumpConfig {
             case let .createdAndOnboarded(pumpManagerUI):
                 debug(.default, "Pump manager  created and onboarded")
                 setupDelegate?.pumpManagerOnboarding(didCreatePumpManager: pumpManagerUI)
-                return UIViewController()
+                var vc = pumpManagerUI.settingsViewController(
+                    bluetoothProvider: bluetoothManager,
+                    pumpManagerOnboardingDelegate: setupDelegate
+                )
+                vc.completionDelegate = completionDelegate
+                return vc
             }
         }
 

+ 1 - 1
FreeAPS/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift

@@ -306,7 +306,7 @@ extension SMBSettings {
                         }
                         VStack(alignment: .leading, spacing: 10) {
                             Text(
-                                "Warning: Increasing this value above 90 minutes may impact Trio's ability to effectively zero temp and prevent lows."
+                                "Warning: Increasing this value above 60 minutes may impact Trio's ability to effectively zero temp and prevent lows."
                             ).bold()
                             Text("Note: UAM SMBs must be enabled to use this limit.")
                         }

+ 2 - 7
FreeAPS/Sources/Modules/Stat/View/StatsView.swift

@@ -128,13 +128,8 @@ struct StatsView: View {
     var hba1c: some View {
         HStack(spacing: 50) {
             let useUnit: GlucoseUnits = {
-                if units == .mmolL && hbA1cDisplayUnit == .mmolMol {
-                    return .mgdL
-                } else if (units == .mgdL && hbA1cDisplayUnit == .mmolMol) || units == .mmolL {
-                    return .mmolL
-                } else {
-                    return .mgdL
-                }
+                if hbA1cDisplayUnit == .mmolMol { return .mmolL }
+                else { return .mgdL }
             }()
 
             let hba1cs = glucoseStats()

+ 22 - 22
FreeAPS/Sources/Modules/TargetsEditor/View/TargetsEditorRootView.swift

@@ -31,30 +31,30 @@ extension TargetsEditor {
 
                 Group {
                     HStack {
-                        HStack {
-                            Button {
-                                let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
-                                impactHeavy.impactOccurred()
-                                state.save()
-
-                                // deactivate saving display after 1.25 seconds
-                                DispatchQueue.main.asyncAfter(deadline: .now() + 1.25) {
-                                    state.shouldDisplaySaving = false
+                        Button(action: {
+                            let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
+                            impactHeavy.impactOccurred()
+                            state.save()
+
+                            // deactivate saving display after 1.25 seconds
+                            DispatchQueue.main.asyncAfter(deadline: .now() + 1.25) {
+                                state.shouldDisplaySaving = false
+                            }
+                        }, label: {
+                            HStack {
+                                if state.shouldDisplaySaving {
+                                    ProgressView().padding(.trailing, 10)
                                 }
-                            } label: {
-                                HStack {
-                                    if state.shouldDisplaySaving {
-                                        ProgressView().padding(.trailing, 10)
-                                    }
-                                    Text(state.shouldDisplaySaving ? "Saving..." : "Save")
-                                }.padding(10)
+                                Text(state.shouldDisplaySaving ? "Saving..." : "Save")
                             }
-                        }
-                        .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
-                        .disabled(shouldDisableButton)
-                        .background(shouldDisableButton ? Color(.systemGray4) : Color(.systemBlue))
-                        .tint(.white)
-                        .clipShape(RoundedRectangle(cornerRadius: 8))
+                            .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
+                            .padding(10)
+                        })
+                            .frame(width: UIScreen.main.bounds.width * 0.9, height: 40, alignment: .center)
+                            .disabled(shouldDisableButton)
+                            .background(shouldDisableButton ? Color(.systemGray4) : Color(.systemBlue))
+                            .tint(.white)
+                            .clipShape(RoundedRectangle(cornerRadius: 8))
                     }
                 }.padding(5)
             }

+ 12 - 10
FreeAPS/Sources/Modules/Treatments/View/TreatmentsRootView.swift

@@ -316,15 +316,17 @@ extension Treatments {
                         Text("Close")
                     }
                 }
-                ToolbarItem(placement: .topBarTrailing) {
-                    Button(action: {
-                        showPresetSheet = true
-                    }, label: {
-                        HStack {
-                            Text("Presets")
-                            Image(systemName: "plus")
-                        }
-                    })
+                if state.displayPresets {
+                    ToolbarItem(placement: .topBarTrailing) {
+                        Button(action: {
+                            showPresetSheet = true
+                        }, label: {
+                            HStack {
+                                Text("Presets")
+                                Image(systemName: "plus")
+                            }
+                        })
+                    }
                 }
             })
             .onAppear {
@@ -396,7 +398,7 @@ extension Treatments {
 
         private var taskButtonLabel: some View {
             if pumpBolusLimitExceeded {
-                return Text("Max Bolus of \(state.maxBolus.description) U E== 0xceeded")
+                return Text("Max Bolus of \(state.maxBolus.description) U Exceeded")
             } else if externalBolusLimitExceeded {
                 return Text("Max External Bolus of \(state.maxExternal.description) U Exceeded")
             } else if carbLimitExceeded {

+ 1 - 1
FreeAPS/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift

@@ -413,7 +413,7 @@ extension UserInterfaceSettings {
                                                 VStack(alignment: .leading, spacing: 5) {
                                                     Text("Total Insulin in Scope:").bold()
                                                     Text(
-                                                        "Displays the total insulin administered since midnight, both basal and bolus."
+                                                        "Displays the total amount of insulin given as a bolus (manual or SMB) and through temporary basal rates above zero during the selected timeframe of the main chart."
                                                     )
                                                 }
                                             }

+ 14 - 16
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigGarminView.swift

@@ -19,7 +19,7 @@ struct WatchConfigGarminView: View {
     }
 
     var body: some View {
-        List {
+        Form {
             Section(
                 header: Text("Garmin Configuration"),
                 content:
@@ -43,13 +43,6 @@ struct WatchConfigGarminView: View {
                             Spacer()
                             Button(
                                 action: {
-                                    hintLabel = "Add Device"
-                                    selectedVerboseHint =
-                                        AnyView(
-                                            Text(
-                                                "Add Garmin Device to Trio. Please look at the docs to see which devices are supported."
-                                            )
-                                        )
                                     shouldDisplayHint.toggle()
                                 },
                                 label: {
@@ -64,14 +57,17 @@ struct WatchConfigGarminView: View {
             ).listRowBackground(Color.chart)
 
             if !state.devices.isEmpty {
-                Section(header: Text("Garmin Watch")) {
-                    List {
-                        ForEach(state.devices, id: \.uuid) { device in
-                            Text(device.friendlyName)
+                Section(
+                    header: Text("Garmin Watch"),
+                    content: {
+                        List {
+                            ForEach(state.devices, id: \.uuid) { device in
+                                Text(device.friendlyName)
+                            }
+                            .onDelete(perform: onDelete)
                         }
-                        .onDelete(perform: onDelete)
                     }
-                }.listRowBackground(Color.chart)
+                ).listRowBackground(Color.chart)
             }
         }
         .listSectionSpacing(sectionSpacing)
@@ -79,8 +75,10 @@ struct WatchConfigGarminView: View {
             SettingInputHintView(
                 hintDetent: $hintDetent,
                 shouldDisplayHint: $shouldDisplayHint,
-                hintLabel: hintLabel ?? "",
-                hintText: selectedVerboseHint ?? AnyView(EmptyView()),
+                hintLabel: "Add Device",
+                hintText: Text(
+                    "Add Garmin Device to Trio. Please look at the docs to see which devices are supported."
+                ),
                 sheetTitle: "Help"
             )
         }

+ 1 - 1
Model/Classes+Properties/OpenAPS_Battery+CoreDataProperties.swift

@@ -9,7 +9,7 @@ public extension OpenAPS_Battery {
     @NSManaged var date: Date?
     @NSManaged var display: Bool
     @NSManaged var id: UUID?
-    @NSManaged var percent: Int16
+    @NSManaged var percent: Double
     @NSManaged var status: String?
     @NSManaged var voltage: NSDecimalNumber?
 }

+ 1 - 1
Model/TrioCoreDataPersistentContainer.xcdatamodeld/TrioCoreDataPersistentContainer.xcdatamodel/contents

@@ -96,7 +96,7 @@
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="display" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
-        <attribute name="percent" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="percent" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
         <attribute name="status" optional="YES" attributeType="String"/>
         <attribute name="voltage" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <fetchIndex name="byDate">

+ 1 - 1
OmniBLE

@@ -1 +1 @@
-Subproject commit fee22b34644a6c349db92b55ce835114c377e4d7
+Subproject commit 1fa2874419225c8c7af0d9afbd9faf823cda34e5

+ 1 - 1
OmniKit

@@ -1 +1 @@
-Subproject commit 0857ad8d71d4d588ff7f5470e78fe8d6b5055924
+Subproject commit 48a35efa52f42e0b72fe2e984f60d4482a11a75f