Sfoglia il codice sorgente

Merge branch 'dev' of github.com:nightscout/Trio-dev into stats-wip

Deniz Cengiz 1 anno fa
parent
commit
1ddcd52b97
100 ha cambiato i file con 181990 aggiunte e 51548 eliminazioni
  1. 1 1
      DanaKit
  2. 4 4
      Model/Classes+Properties/TempTargetRunStored+CoreDataProperties.swift
  3. 6 6
      Model/Classes+Properties/TempTargetStored+CoreDataProperties.swift
  4. 12 2
      Model/CoreDataStack.swift
  5. 14 0
      Model/Helper/AdjustmentStored+Helper.swift
  6. 9 6
      Model/Helper/CoreDataError.swift
  7. 0 10
      Model/Helper/OverrideStored+helper.swift
  8. 87 3
      Model/Helper/PumpEvent+helper.swift
  9. 1 1
      OmniBLE
  10. 1 1
      OmniKit
  11. 0 4
      TempTargetRunStored+CoreDataClass.swift
  12. 0 18
      TempTargetRunStored+CoreDataProperties.swift
  13. 0 4
      TempTargetStored+CoreDataClass.swift
  14. 0 22
      TempTargetStored+CoreDataProperties.swift
  15. 11 1
      Trio Watch App Extension/Helper/Helper+ButtonStyles.swift
  16. 5 1
      Trio Watch App Extension/Views/BolusConfirmationView.swift
  17. 13 18
      Trio Watch App Extension/Views/BolusInputView.swift
  18. 21 1
      Trio Watch App Extension/Views/BolusProgressOverlay.swift
  19. 6 16
      Trio Watch App Extension/Views/CarbsInputView.swift
  20. 5 0
      Trio Watch App Extension/Views/GlucoseChartView.swift
  21. 18 4
      Trio Watch App Extension/Views/TrioMainWatchView.swift
  22. 1 3
      Trio Watch App Extension/WatchState+Requests.swift
  23. 20 31
      Trio Watch App Extension/WatchState.swift
  24. 57 143
      Trio.xcodeproj/project.pbxproj
  25. 0 23
      Trio/Resources/Base.lproj/InfoPlist.strings
  26. 1059 0
      Trio/Resources/InfoPlist.xcstrings
  27. 0 20
      Trio/Resources/ar.lproj/InfoPlist.strings
  28. 0 20
      Trio/Resources/ca.lproj/InfoPlist.strings
  29. 0 20
      Trio/Resources/da.lproj/InfoPlist.strings
  30. 0 20
      Trio/Resources/de.lproj/InfoPlist.strings
  31. 0 20
      Trio/Resources/es.lproj/InfoPlist.strings
  32. 0 20
      Trio/Resources/fi.lproj/InfoPlist.strings
  33. 0 20
      Trio/Resources/fr.lproj/InfoPlist.strings
  34. 0 20
      Trio/Resources/he.lproj/InfoPlist.strings
  35. 0 20
      Trio/Resources/hu.lproj/InfoPlist.strings
  36. 0 20
      Trio/Resources/it.lproj/InfoPlist.strings
  37. 1 1
      Trio/Resources/javascript/bundle/meal.js
  38. 1 1
      Trio/Resources/javascript/bundle/profile.js
  39. 1 0
      Trio/Resources/json/defaults/preferences.json
  40. 0 20
      Trio/Resources/nb.lproj/InfoPlist.strings
  41. 0 20
      Trio/Resources/nl.lproj/InfoPlist.strings
  42. 0 20
      Trio/Resources/pl.lproj/InfoPlist.strings
  43. 0 20
      Trio/Resources/pt-BR.lproj/InfoPlist.strings
  44. 0 20
      Trio/Resources/pt-PT.lproj/InfoPlist.strings
  45. 0 20
      Trio/Resources/ru.lproj/InfoPlist.strings
  46. 0 20
      Trio/Resources/sk.lproj/InfoPlist.strings
  47. 0 20
      Trio/Resources/sv.lproj/InfoPlist.strings
  48. 0 20
      Trio/Resources/tr.lproj/InfoPlist.strings
  49. 0 20
      Trio/Resources/uk.lproj/InfoPlist.strings
  50. 0 20
      Trio/Resources/vi.lproj/InfoPlist.strings
  51. 0 20
      Trio/Resources/zh-Hans.lproj/InfoPlist.strings
  52. 9 3
      Trio/Sources/APS/APSManager.swift
  53. 1 1
      Trio/Sources/APS/CGM/AppGroupSource.swift
  54. 10 9
      Trio/Sources/APS/CGM/CGMType.swift
  55. 4 4
      Trio/Sources/APS/CGM/PluginSource.swift
  56. 0 8
      Trio/Sources/APS/DeviceDataManager.swift
  57. 27 4
      Trio/Sources/APS/FetchGlucoseManager.swift
  58. 25 10
      Trio/Sources/APS/OpenAPS/OpenAPS.swift
  59. 1 1
      Trio/Sources/APS/Storage/ContactImageStorage.swift
  60. 2 2
      Trio/Sources/APS/Storage/GlucoseStorage.swift
  61. 3 3
      Trio/Sources/APS/Storage/OverrideStorage.swift
  62. 4 2
      Trio/Sources/APS/Storage/PumpHistoryStorage.swift
  63. 3 3
      Trio/Sources/APS/Storage/TempTargetsStorage.swift
  64. 22 7
      Trio/Sources/Application/TrioApp.swift
  65. 4 4
      Trio/Sources/Helpers/BuildDetails.swift
  66. 15 0
      Trio/Sources/Helpers/CGMOptions.swift
  67. 4 4
      Trio/Sources/Helpers/HKUnit.swift
  68. 180467 0
      Trio/Sources/Localizations/Main/Localizable.xcstrings
  69. 0 2223
      Trio/Sources/Localizations/Main/ar.lproj/Localizable.strings
  70. 0 1917
      Trio/Sources/Localizations/Main/ca.lproj/Localizable.strings
  71. 0 2184
      Trio/Sources/Localizations/Main/da.lproj/Localizable.strings
  72. 0 2229
      Trio/Sources/Localizations/Main/de.lproj/Localizable.strings
  73. 0 2231
      Trio/Sources/Localizations/Main/en.lproj/Localizable.strings
  74. 0 2228
      Trio/Sources/Localizations/Main/es.lproj/Localizable.strings
  75. 0 2229
      Trio/Sources/Localizations/Main/fi.lproj/Localizable.strings
  76. 0 2220
      Trio/Sources/Localizations/Main/fr.lproj/Localizable.strings
  77. 0 2223
      Trio/Sources/Localizations/Main/he.lproj/Localizable.strings
  78. 0 2178
      Trio/Sources/Localizations/Main/hu.lproj/Localizable.strings
  79. 0 2225
      Trio/Sources/Localizations/Main/it.lproj/Localizable.strings
  80. 0 2223
      Trio/Sources/Localizations/Main/nb.lproj/Localizable.strings
  81. 0 2231
      Trio/Sources/Localizations/Main/nl.lproj/Localizable.strings
  82. 0 2225
      Trio/Sources/Localizations/Main/pl.lproj/Localizable.strings
  83. 0 2223
      Trio/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  84. 0 2223
      Trio/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  85. 0 2226
      Trio/Sources/Localizations/Main/ru.lproj/Localizable.strings
  86. 0 2178
      Trio/Sources/Localizations/Main/sk.lproj/Localizable.strings
  87. 0 2223
      Trio/Sources/Localizations/Main/sv.lproj/Localizable.strings
  88. 0 2227
      Trio/Sources/Localizations/Main/tr.lproj/Localizable.strings
  89. 0 2223
      Trio/Sources/Localizations/Main/uk.lproj/Localizable.strings
  90. 0 2180
      Trio/Sources/Localizations/Main/vi.lproj/Localizable.strings
  91. 0 2225
      Trio/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  92. 4 0
      Trio/Sources/Logger/Logger.swift
  93. 16 16
      Trio/Sources/Models/ContactTrickEntry.swift
  94. 1 0
      Trio/Sources/Models/DecimalPickerSettings.swift
  95. 2 2
      Trio/Sources/Models/ForecastDisplayType.swift
  96. 2 2
      Trio/Sources/Models/HbA1cDisplayUnit.swift
  97. 2 2
      Trio/Sources/Models/LockScreenView.swift
  98. 6 0
      Trio/Sources/Models/Preferences.swift
  99. 2 2
      Trio/Sources/Models/TimeInRangeChartStyle.swift
  100. 0 0
      Trio/Sources/Models/TotalInsulinDisplayType.swift

+ 1 - 1
DanaKit

@@ -1 +1 @@
-Subproject commit b07f236677b205d31d7ecf6144970348e8d5a3fe
+Subproject commit b44c5df260a8b38d6fd0b5851cc3aac5da5d9d57

+ 4 - 4
Model/Classes+Properties/TempTargetRunStored+CoreDataProperties.swift

@@ -6,13 +6,13 @@ public extension TempTargetRunStored {
         NSFetchRequest<TempTargetRunStored>(entityName: "TempTargetRunStored")
     }
 
-    @NSManaged var startDate: Date?
-    @NSManaged var target: NSDecimalNumber?
-    @NSManaged var id: UUID?
     @NSManaged var endDate: Date?
+    @NSManaged var id: UUID?
     @NSManaged var isUploadedToNS: Bool
-    @NSManaged var tempTarget: TempTargetStored?
     @NSManaged var name: String?
+    @NSManaged var startDate: Date?
+    @NSManaged var target: NSDecimalNumber?
+    @NSManaged var tempTarget: TempTargetStored?
 }
 
 extension TempTargetRunStored: Identifiable {}

+ 6 - 6
Model/Classes+Properties/TempTargetStored+CoreDataProperties.swift

@@ -6,17 +6,17 @@ public extension TempTargetStored {
         NSFetchRequest<TempTargetStored>(entityName: "TempTargetStored")
     }
 
-    @NSManaged var enabled: Bool
     @NSManaged var date: Date?
     @NSManaged var duration: NSDecimalNumber?
-    @NSManaged var target: NSDecimalNumber?
+    @NSManaged var enabled: Bool
+    @NSManaged var halfBasalTarget: NSDecimalNumber?
     @NSManaged var id: UUID?
-    @NSManaged var name: String?
-    @NSManaged var isUploadedToNS: Bool
     @NSManaged var isPreset: Bool
-    @NSManaged var halfBasalTarget: NSDecimalNumber?
-    @NSManaged var tempTargetRun: TempTargetRunStored?
+    @NSManaged var isUploadedToNS: Bool
+    @NSManaged var name: String?
     @NSManaged var orderPosition: Int16
+    @NSManaged var target: NSDecimalNumber?
+    @NSManaged var tempTargetRun: TempTargetRunStored?
 }
 
 extension TempTargetStored: Identifiable {}

+ 12 - 2
Model/CoreDataStack.swift

@@ -105,7 +105,7 @@ class CoreDataStack: ObservableObject {
     private func fetchPersistentHistoryTransactionsAndChanges() async throws {
         let taskContext = newTaskContext()
         taskContext.name = "persistentHistoryContext"
-//        debugPrint("Start fetching persistent history changes from the store ... \(DebuggingIdentifiers.inProgress)")
+        //        debugPrint("Start fetching persistent history changes from the store ... \(DebuggingIdentifiers.inProgress)")
 
         try await taskContext.perform {
             // Execute the persistent history change since the last transaction
@@ -120,7 +120,7 @@ class CoreDataStack: ObservableObject {
     }
 
     private func mergePersistentHistoryChanges(from history: [NSPersistentHistoryTransaction]) {
-//        debugPrint("Received \(history.count) persistent history transactions")
+        //        debugPrint("Received \(history.count) persistent history transactions")
         // Update view context with objectIDs from history change request
         /// - Tag: mergeChanges
         let viewContext = persistentContainer.viewContext
@@ -150,6 +150,16 @@ class CoreDataStack: ObservableObject {
             }
         }
     }
+
+    func initializeStack() throws {
+        // Force initialization of persistent container
+        let container = persistentContainer
+
+        // Verify the store is loaded and available
+        guard container.persistentStoreCoordinator.persistentStores.isNotEmpty else {
+            throw CoreDataError.storeNotInitializedError
+        }
+    }
 }
 
 // MARK: - Delete

+ 14 - 0
Model/Helper/AdjustmentStored+Helper.swift

@@ -0,0 +1,14 @@
+import CoreData
+import Foundation
+
+extension NSPredicate {
+    static var lastActiveAdjustmentNotYetUploadedToNightscout: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(
+            format: "date >= %@ AND enabled == %@ AND isUploadedToNS == %@",
+            date as NSDate,
+            true as NSNumber,
+            false as NSNumber
+        )
+    }
+}

+ 9 - 6
Model/Helper/CoreDataError.swift

@@ -7,23 +7,26 @@ enum CoreDataError: Error {
     case persistentHistoryChangeError
     case unexpectedError(error: Error)
     case fetchError
+    case storeNotInitializedError
 }
 
 extension CoreDataError: LocalizedError {
     var errorDescription: String? {
         switch self {
         case .creationError:
-            return NSLocalizedString("Failed to create a new object.", comment: "")
+            return String(localized: "Failed to create a new object.", comment: "")
         case .batchInsertError:
-            return NSLocalizedString("Failed to execute a batch insert request.", comment: "")
+            return String(localized: "Failed to execute a batch insert request.", comment: "")
         case .batchDeleteError:
-            return NSLocalizedString("Failed to execute a batch delete request.", comment: "")
+            return String(localized: "Failed to execute a batch delete request.", comment: "")
         case .persistentHistoryChangeError:
-            return NSLocalizedString("Failed to execute a persistent history change request.", comment: "")
+            return String(localized: "Failed to execute a persistent history change request.", comment: "")
         case let .unexpectedError(error):
-            return NSLocalizedString("Received unexpected error. \(error.localizedDescription)", comment: "")
+            return String(localized: "Received unexpected error. \(error.localizedDescription)", comment: "")
         case .fetchError:
-            return NSLocalizedString("Failed to fetch object \(DebuggingIdentifiers.failed).", comment: "")
+            return String(localized: "Failed to fetch object \(DebuggingIdentifiers.failed).", comment: "")
+        case .storeNotInitializedError:
+            return String(localized: "Failed to initialize Core Data's persistent store.", comment: "")
         }
     }
 }

+ 0 - 10
Model/Helper/OverrideStored+helper.swift

@@ -14,16 +14,6 @@ extension NSPredicate {
             true as NSNumber
         )
     }
-
-    static var lastActiveOverrideNotYetUploadedToNightscout: NSPredicate {
-        let date = Date.oneDayAgo
-        return NSPredicate(
-            format: "date >= %@ AND enabled == %@ AND isUploadedToNS == %@",
-            date as NSDate,
-            true as NSNumber,
-            false as NSNumber
-        )
-    }
 }
 
 extension OverrideStored {

+ 87 - 3
Model/Helper/PumpEvent+helper.swift

@@ -107,7 +107,7 @@ struct BolusDTO: Codable {
     var isExternal: Bool
     var isSMB: Bool
     var duration: Int
-    var _type: String = "Bolus"
+    var _type: String = EventType.bolus.rawValue
 }
 
 struct TempBasalDTO: Codable {
@@ -115,14 +115,14 @@ struct TempBasalDTO: Codable {
     var timestamp: String
     var temp: String
     var rate: Double
-    var _type: String = "TempBasal"
+    var _type: String = EventType.tempBasal.rawValue
 }
 
 struct TempBasalDurationDTO: Codable {
     var id: String
     var timestamp: String
     var duration: Int
-    var _type: String = "TempBasalDuration"
+    var _type: String = EventType.tempBasalDuration.rawValue
 
     private enum CodingKeys: String, CodingKey {
         case id
@@ -132,11 +132,39 @@ struct TempBasalDurationDTO: Codable {
     }
 }
 
+struct SuspendDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.pumpSuspend.rawValue
+}
+
+struct ResumeDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.pumpResume.rawValue
+}
+
+struct RewindDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.rewind.rawValue
+}
+
+struct PrimeDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.prime.rawValue
+}
+
 // Mask distinct DTO subtypes with a common enum that conforms to Encodable
 enum PumpEventDTO: Encodable {
     case bolus(BolusDTO)
     case tempBasal(TempBasalDTO)
     case tempBasalDuration(TempBasalDurationDTO)
+    case suspend(SuspendDTO)
+    case resume(ResumeDTO)
+    case rewind(RewindDTO)
+    case prime(PrimeDTO)
 
     func encode(to encoder: Encoder) throws {
         switch self {
@@ -146,6 +174,14 @@ enum PumpEventDTO: Encodable {
             try tempBasal.encode(to: encoder)
         case let .tempBasalDuration(tempBasalDuration):
             try tempBasalDuration.encode(to: encoder)
+        case let .suspend(suspend):
+            try suspend.encode(to: encoder)
+        case let .resume(resume):
+            try resume.encode(to: encoder)
+        case let .rewind(rewind):
+            try rewind.encode(to: encoder)
+        case let .prime(prime):
+            try prime.encode(to: encoder)
         }
     }
 }
@@ -200,4 +236,52 @@ extension PumpEventStored {
         )
         return .tempBasalDuration(tempBasalDurationDTO)
     }
+
+    func toPumpSuspendDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.pumpSuspend.rawValue else {
+            return nil
+        }
+
+        let suspendDTO = SuspendDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .suspend(suspendDTO)
+    }
+
+    func toPumpResumeDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.pumpResume.rawValue else {
+            return nil
+        }
+
+        let resumeDTO = ResumeDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .resume(resumeDTO)
+    }
+
+    func toRewindDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.rewind.rawValue else {
+            return nil
+        }
+
+        let rewindDTO = RewindDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .rewind(rewindDTO)
+    }
+
+    func toPrimeDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.prime.rawValue else {
+            return nil
+        }
+
+        let primeDTO = PrimeDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .prime(primeDTO)
+    }
 }

+ 1 - 1
OmniBLE

@@ -1 +1 @@
-Subproject commit 1fa2874419225c8c7af0d9afbd9faf823cda34e5
+Subproject commit 6f65cbae4c8089a892911e273204edfc4cc81e9d

+ 1 - 1
OmniKit

@@ -1 +1 @@
-Subproject commit 48a35efa52f42e0b72fe2e984f60d4482a11a75f
+Subproject commit 39915b05fe46b5d73eca52e156dd7efd11193ee8

+ 0 - 4
TempTargetRunStored+CoreDataClass.swift

@@ -1,4 +0,0 @@
-import CoreData
-import Foundation
-
-@objc(TempTargetRunStored) public class TempTargetRunStored: NSManagedObject {}

+ 0 - 18
TempTargetRunStored+CoreDataProperties.swift

@@ -1,18 +0,0 @@
-import CoreData
-import Foundation
-
-public extension TempTargetRunStored {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<TempTargetRunStored> {
-        NSFetchRequest<TempTargetRunStored>(entityName: "TempTargetRunStored")
-    }
-
-    @NSManaged var endDate: Date?
-    @NSManaged var id: UUID?
-    @NSManaged var isUploadedToNS: Bool
-    @NSManaged var name: String?
-    @NSManaged var startDate: Date?
-    @NSManaged var target: NSDecimalNumber?
-    @NSManaged var tempTarget: TempTargetStored?
-}
-
-extension TempTargetRunStored: Identifiable {}

+ 0 - 4
TempTargetStored+CoreDataClass.swift

@@ -1,4 +0,0 @@
-import CoreData
-import Foundation
-
-@objc(TempTargetStored) public class TempTargetStored: NSManagedObject {}

+ 0 - 22
TempTargetStored+CoreDataProperties.swift

@@ -1,22 +0,0 @@
-import CoreData
-import Foundation
-
-public extension TempTargetStored {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<TempTargetStored> {
-        NSFetchRequest<TempTargetStored>(entityName: "TempTargetStored")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var duration: NSDecimalNumber?
-    @NSManaged var enabled: Bool
-    @NSManaged var halfBasalTarget: NSDecimalNumber?
-    @NSManaged var id: UUID?
-    @NSManaged var isPreset: Bool
-    @NSManaged var isUploadedToNS: Bool
-    @NSManaged var name: String?
-    @NSManaged var orderPosition: Int16
-    @NSManaged var target: NSDecimalNumber?
-    @NSManaged var tempTargetRun: TempTargetRunStored?
-}
-
-extension TempTargetStored: Identifiable {}

+ 11 - 1
Trio Watch App Extension/Helper/Helper+ButtonStyles.swift

@@ -5,6 +5,8 @@ struct WatchOSButtonStyle: ButtonStyle {
     var foregroundColor: Color = .white
     var fontSize: Font = .title2
 
+    @Environment(\.isEnabled) private var isEnabled: Bool
+
     private var fontWeight: Font.Weight {
         switch deviceType {
         case .watch40mm:
@@ -44,11 +46,19 @@ struct WatchOSButtonStyle: ButtonStyle {
     }
 
     func makeBody(configuration: Configuration) -> some View {
+        var buttonBackground: Color {
+            if isEnabled {
+                return Color.tabBar.opacity(configuration.isPressed ? 0.8 : 1.0)
+            } else {
+                return Color.tabBar.opacity(0.4)
+            }
+        }
+
         configuration.label
             .font(fontSize)
             .fontWeight(fontWeight)
             .padding(buttonPadding)
-            .background(Color.tabBar.opacity(configuration.isPressed ? 0.8 : 1.0))
+            .background(buttonBackground)
             .clipShape(Circle())
             .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
     }

+ 5 - 1
Trio Watch App Extension/Views/BolusConfirmationView.swift

@@ -103,7 +103,11 @@ struct BolusConfirmationView: View {
                         .wristLocation == .left ? "digitalcrown.arrow.clockwise.fill" : "digitalcrown.arrow.counterclockwise.fill"
                 )
                 .symbolRenderingMode(.palette)
-                .foregroundStyle(Color.primary, Color.insulin)
+                .foregroundStyle(Color.insulin, Color.primary)
+                .symbolEffect(
+                    .variableColor.reversing,
+                    options: .speed(100).repeating
+                )
             }
         }
         .blur(radius: state.showBolusProgressOverlay ? 3 : 0)

+ 13 - 18
Trio Watch App Extension/Views/BolusInputView.swift

@@ -13,16 +13,7 @@ struct BolusInputView: View {
     @FocusState private var isCrownFocused: Bool
 
     private var effectiveBolusLimit: Double {
-        // Extract current IOB from string and convert to Double
-        let currentIOB = Double(state.iob?.replacingOccurrences(of: " U", with: "") ?? "0") ?? 0
-
-        // Calculate available IOB
-        let availableIOB = max(0, Double(truncating: state.maxIOB as NSNumber) - currentIOB)
-
-        return min(
-            Double(truncating: state.maxBolus as NSNumber),
-            availableIOB
-        )
+        Double(truncating: state.maxBolus as NSNumber)
     }
 
     var trioBackgroundColor = LinearGradient(
@@ -37,7 +28,7 @@ struct BolusInputView: View {
                 ProgressView("Calculating Bolus...")
                 Spacer()
             } else {
-                if effectiveBolusLimit == 0 {
+                if effectiveBolusLimit <= 0 {
                     VStack(spacing: 8) {
                         Text("Bolus limit cannot be fetched from phone!").font(.headline)
                         Text("Check device settings, connect to phone, and try again.").font(.caption)
@@ -62,10 +53,10 @@ struct BolusInputView: View {
                         }) {
                             Image(systemName: "minus.circle.fill")
                                 .font(.title3)
-                                .foregroundColor(Color.insulin)
+                                .tint(Color.insulin)
                         }
                         .buttonStyle(.borderless)
-                        .disabled(bolusAmount == 0)
+                        .disabled(bolusAmount <= 0)
 
                         Spacer()
 
@@ -92,11 +83,14 @@ struct BolusInputView: View {
 
                         // "+" Button
                         Button(action: {
-                            bolusAmount += Double(truncating: state.bolusIncrement as NSNumber)
+                            bolusAmount = min(
+                                effectiveBolusLimit,
+                                bolusAmount + Double(truncating: state.bolusIncrement as NSNumber)
+                            )
                         }) {
                             Image(systemName: "plus.circle.fill")
                                 .font(.title3)
-                                .foregroundColor(Color.insulin)
+                                .tint(Color.insulin)
                         }
                         .buttonStyle(.borderless)
                         .disabled(bolusAmount >= effectiveBolusLimit)
@@ -121,7 +115,7 @@ struct BolusInputView: View {
                     }
                     .buttonStyle(.bordered)
                     .tint(Color.insulin)
-                    .disabled(!(bolusAmount > 0.0) || bolusAmount >= effectiveBolusLimit)
+                    .disabled(!(bolusAmount > 0.0) || bolusAmount > effectiveBolusLimit)
 
                     Text(String(format: "Recommended: %.1f U", NSDecimalNumber(decimal: state.recommendedBolus).doubleValue))
                         .font(.footnote)
@@ -160,8 +154,9 @@ struct BolusInputView: View {
             }
         }
         // Add onChange to update bolus amount when recommendation changes
-        .onChange(of: state.recommendedBolus) { _, newValue in
-            if bolusAmount == 0 { // Only update if user hasn't modified the value
+        .onChange(of: state.recommendedBolus) { oldValue, newValue in
+            // Only update if user hasn't modified the value OR if recommendation hasn't changed
+            if bolusAmount == 0 || oldValue != newValue {
                 bolusAmount = Double(truncating: NSDecimalNumber(decimal: newValue))
             }
         }

+ 21 - 1
Trio Watch App Extension/Views/BolusProgressOverlay.swift

@@ -16,6 +16,25 @@ struct BolusProgressOverlay: View {
         endPoint: .trailing
     )
 
+    private var isWatchStateDated: Bool {
+        // If `lastWatchStateUpdate` is nil, treat as "dated"
+        guard let lastUpdateTimestamp = state.lastWatchStateUpdate else {
+            return true
+        }
+        let now = Date().timeIntervalSince1970
+        let secondsSinceUpdate = now - lastUpdateTimestamp
+        // Return true if last update older than 5 min, so 1 loop cycle
+        return secondsSinceUpdate > 5 * 60
+    }
+
+    private var isSessionUnreachable: Bool {
+        guard let session = state.session else {
+            return true // No session at all => unreachable
+        }
+        // Return true if not .activated OR not reachable
+        return session.activationState != .activated
+    }
+
     var body: some View {
         VStack(spacing: 10) {
             VStack {
@@ -45,9 +64,10 @@ struct BolusProgressOverlay: View {
                 }
                 .buttonStyle(.bordered)
                 .padding()
+                .disabled(isWatchStateDated || isSessionUnreachable)
             }
             .padding()
-            .background(Color.black.opacity(0.8))
+            .background(Color.black.opacity(0.9))
             .cornerRadius(10)
         }
         .scenePadding()

+ 6 - 16
Trio Watch App Extension/Views/CarbsInputView.swift

@@ -12,16 +12,7 @@ struct CarbsInputView: View {
     let continueToBolus: Bool
 
     private var effectiveCarbsLimit: Double {
-        // Extract current COB from string and convert to Double
-        let currentCOB = Double(state.cob?.replacingOccurrences(of: " g", with: "") ?? "0") ?? 0
-
-        // Calculate available COB
-        let availableCOB = max(0, Double(truncating: state.maxCOB as NSNumber) - currentCOB)
-
-        return min(
-            Double(truncating: state.maxCarbs as NSNumber),
-            availableCOB
-        )
+        Double(truncating: state.maxCarbs as NSNumber)
     }
 
     var trioBackgroundColor = LinearGradient(
@@ -41,16 +32,15 @@ struct CarbsInputView: View {
                 // "-" Button
                 Button(action: {
                     if carbsAmount > 0 {
-                        let currentAmount = carbsAmount
                         carbsAmount < 5 ? carbsAmount = 0 : (carbsAmount -= 5)
                     }
                 }) {
                     Image(systemName: "minus.circle.fill")
                         .font(.title3)
-                        .foregroundColor(.orange)
+                        .tint(.orange)
                 }
                 .buttonStyle(.borderless)
-                .disabled(carbsAmount < 1)
+                .disabled(carbsAmount <= 0)
 
                 Spacer()
 
@@ -75,11 +65,11 @@ struct CarbsInputView: View {
 
                 // "+" Button
                 Button(action: {
-                    carbsAmount += 5
+                    carbsAmount = min(effectiveCarbsLimit, carbsAmount + 5)
                 }) {
                     Image(systemName: "plus.circle.fill")
                         .font(.title3)
-                        .foregroundColor(.orange)
+                        .tint(.orange)
                 }
                 .buttonStyle(.borderless)
                 .disabled(carbsAmount >= effectiveCarbsLimit)
@@ -109,7 +99,7 @@ struct CarbsInputView: View {
             }
             .buttonStyle(.bordered)
             .tint(.orange)
-            .disabled(!(carbsAmount > 0.0) || carbsAmount >= effectiveCarbsLimit)
+            .disabled(!(carbsAmount > 0.0) || carbsAmount > effectiveCarbsLimit)
         }
         .background(trioBackgroundColor)
         .toolbar {

+ 5 - 0
Trio Watch App Extension/Views/GlucoseChartView.swift

@@ -6,6 +6,8 @@ import SwiftUI
 
 struct GlucoseChartView: View {
     let glucoseValues: [(date: Date, glucose: Double, color: Color)]
+    let minYAxisValue: Decimal
+    let maxYAxisValue: Decimal
     @State private var timeWindow: TimeWindow = .threeHours
 
     enum TimeWindow: Int {
@@ -69,6 +71,9 @@ struct GlucoseChartView: View {
                         }
                     }
                 }
+                .chartYScale(
+                    domain: minYAxisValue ... maxYAxisValue
+                )
                 .chartPlotStyle { plotContent in
                     plotContent
                         .background(

+ 18 - 4
Trio Watch App Extension/Views/TrioMainWatchView.swift

@@ -88,12 +88,21 @@ struct TrioMainWatchView: View {
                 }.tag(0)
 
                 // Page 2: Glucose chart
-                GlucoseChartView(glucoseValues: state.glucoseValues)
-                    .tag(1)
+                GlucoseChartView(
+                    glucoseValues: state.glucoseValues,
+                    minYAxisValue: state.minYAxisValue,
+                    maxYAxisValue: state.maxYAxisValue
+                )
+                .tag(1)
             }
             .onAppear {
+                // Hard reset variables when main view appears
+                /// Reset `bolusProgress` and `activeBolusAmount` to ensure no stale bolus progressbar is stuck on home view
                 state.bolusProgress = 0
                 state.activeBolusAmount = 0
+                /// Reset `bolusAmount` and `recommendedBolus` to ensure no stale / old value is set when user opens bolus input or meal combo the next time.
+                state.bolusAmount = 0
+                state.recommendedBolus = 0
             }
             .background(trioBackgroundColor)
             .tabViewStyle(.verticalPage)
@@ -130,7 +139,9 @@ struct TrioMainWatchView: View {
                     } label: {
                         Image(systemName: "clock.arrow.2.circlepath")
                             .foregroundStyle(Color.primary, isOverrideActive ? Color.primary : Color.purple)
-                    }.tint(isOverrideActive ? Color.purple : nil)
+                    }
+                    .tint(isOverrideActive ? Color.purple : nil)
+                    .disabled(isWatchStateDated || isSessionUnreachable)
 
                     Button {
                         showingTreatmentMenuSheet = true
@@ -140,13 +151,16 @@ struct TrioMainWatchView: View {
                     }
                     .controlSize(.large)
                     .buttonStyle(WatchOSButtonStyle(deviceType: state.deviceType))
+                    .disabled(isWatchStateDated || isSessionUnreachable)
 
                     Button {
                         showingTempTargetSheet = true
                     } label: {
                         Image(systemName: "target")
                             .foregroundStyle(isTempTargetActive ? Color.primary : Color.loopGreen.opacity(0.75))
-                    }.tint(isTempTargetActive ? Color.loopGreen.opacity(0.75) : nil)
+                    }
+                    .tint(isTempTargetActive ? Color.loopGreen.opacity(0.75) : nil)
+                    .disabled(isWatchStateDated || isSessionUnreachable)
                 }
             }
             .fullScreenCover(isPresented: $showingTreatmentMenuSheet) {

+ 1 - 3
Trio Watch App Extension/WatchState+Requests.swift

@@ -145,9 +145,7 @@ extension WatchState {
             print("Error requesting bolus recommendation: \(error.localizedDescription)")
         }
 
-        if bolusAmount == 0 {
-            showBolusCalculationProgress = true
-        }
+        showBolusCalculationProgress = true
     }
 
     func requestWatchStateUpdate() {

+ 20 - 31
Trio Watch App Extension/WatchState.swift

@@ -20,6 +20,8 @@ import WatchConnectivity
     var trend: String? = ""
     var delta: String? = "--"
     var glucoseValues: [(date: Date, glucose: Double, color: Color)] = []
+    var minYAxisValue: Decimal = 39
+    var maxYAxisValue: Decimal = 200
     var cob: String? = "--"
     var iob: String? = "--"
     var lastLoopTime: String? = "--"
@@ -44,8 +46,6 @@ import WatchConnectivity
     var maxCarbs: Decimal = 250
     var maxFat: Decimal = 250
     var maxProtein: Decimal = 250
-    var maxIOB: Decimal = 0
-    var maxCOB: Decimal = 120
 
     // Pump specific dosing increment
     var bolusIncrement: Decimal = 0.05
@@ -69,8 +69,6 @@ import WatchConnectivity
         (!showAcknowledgmentBanner || !showCommsAnimation) && bolusProgress > 0 && bolusProgress < 1.0 && !isBolusCanceled
     }
 
-    
-
     var recommendedBolus: Decimal = 0
 
     // Debouncing and batch processing helpers
@@ -218,13 +216,7 @@ import WatchConnectivity
                 DispatchQueue.main.async {
                     if !self.isBolusCanceled {
                         self.bolusProgress = progress
-
-                        // we only need to grab the active bolus amount from the phone if it is a phone-invoked bolus
-                        // when it comes from the watch, we already have it stored and available
-                        if self.activeBolusAmount == 0 {
-                            self.activeBolusAmount = activeBolusAmount
-                        }
-
+                        self.activeBolusAmount = activeBolusAmount
                         self.deliveredAmount = deliveredAmount
                     }
                 }
@@ -244,6 +236,9 @@ import WatchConnectivity
             DispatchQueue.main.async {
                 self.bolusProgress = 0
                 self.activeBolusAmount = 0
+                self
+                    .isBolusCanceled =
+                    false /// Reset flag to ensure a bolus progress is also shown after canceling bolus from watch
             }
             return
         } else {
@@ -315,13 +310,7 @@ import WatchConnectivity
                 DispatchQueue.main.async {
                     if !self.isBolusCanceled {
                         self.bolusProgress = progress
-
-                        // we only need to grab the active bolus amount from the phone if it is a phone-invoked bolus
-                        // when it comes from the watch, we already have it stored and available
-                        if self.activeBolusAmount == 0 {
-                            self.activeBolusAmount = activeBolusAmount
-                        }
-
+                        self.activeBolusAmount = activeBolusAmount
                         self.deliveredAmount = deliveredAmount
                     }
                 }
@@ -363,7 +352,7 @@ import WatchConnectivity
                 // reset input amounts
                 self.bolusAmount = 0
                 self.carbsAmount = 0
-                
+
                 // reset auth progress
                 self.confirmationProgress = 0
             }
@@ -532,6 +521,18 @@ import WatchConnectivity
             .sorted { $0.date < $1.date }
         }
 
+        if let minYAxisValue = message[WatchMessageKeys.minYAxisValue] {
+            if let decimalValue = (minYAxisValue as? NSNumber)?.decimalValue {
+                self.minYAxisValue = decimalValue
+            }
+        }
+
+        if let maxYAxisValue = message[WatchMessageKeys.maxYAxisValue] {
+            if let decimalValue = (maxYAxisValue as? NSNumber)?.decimalValue {
+                self.maxYAxisValue = decimalValue
+            }
+        }
+
         if let overrideData = message[WatchMessageKeys.overridePresets] as? [[String: Any]] {
             overridePresets = overrideData.compactMap { data in
                 guard let name = data["name"] as? String,
@@ -587,18 +588,6 @@ import WatchConnectivity
             }
         }
 
-        if let maxIOBValue = message[WatchMessageKeys.maxIOB] {
-            if let decimalValue = (maxIOBValue as? NSNumber)?.decimalValue {
-                maxIOB = decimalValue
-            }
-        }
-
-        if let maxCOBValue = message[WatchMessageKeys.maxCOB] {
-            if let decimalValue = (maxCOBValue as? NSNumber)?.decimalValue {
-                maxCOB = decimalValue
-            }
-        }
-
         if let bolusIncrement = message[WatchMessageKeys.bolusIncrement] {
             if let decimalValue = (bolusIncrement as? NSNumber)?.decimalValue {
                 self.bolusIncrement = decimalValue

+ 57 - 143
Trio.xcodeproj/project.pbxproj

@@ -28,7 +28,6 @@
 		190EBCC829FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift */; };
 		190EBCCB29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift */; };
 		191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */; };
-		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
 		195D80B42AF6973A00D25097 /* DynamicSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B32AF6973A00D25097 /* DynamicSettingsRootView.swift */; };
@@ -38,7 +37,6 @@
 		1967DFBE29D052C200759F30 /* Icons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBD29D052C200759F30 /* Icons.swift */; };
 		1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBF29D053AC00759F30 /* IconSelection.swift */; };
 		1967DFC229D053D300759F30 /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFC129D053D300759F30 /* IconImage.swift */; };
-		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		19A910302A24BF6300C8951B /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A9102F2A24BF6300C8951B /* StatsView.swift */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
 		19A910362A24D6D700C8951B /* DateFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A910352A24D6D700C8951B /* DateFilter.swift */; };
@@ -204,10 +202,15 @@
 		38FE826D25CC8461001FF17A /* NightscoutAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FE826C25CC8461001FF17A /* NightscoutAPI.swift */; };
 		38FEF3FA2737E42000574A46 /* BaseStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3F92737E42000574A46 /* BaseStateModel.swift */; };
 		38FEF3FC2737E53800574A46 /* MainStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FB2737E53800574A46 /* MainStateModel.swift */; };
-		38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FD2738083E00574A46 /* CGMProvider.swift */; };
+		38FEF3FE2738083E00574A46 /* CGMSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */; };
 		38FEF413273B317A00574A46 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF412273B317A00574A46 /* HKUnit.swift */; };
 		45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8A87AA037BD079BA3528BA /* ConfigEditorDataFlow.swift */; };
 		45717281F743594AA9D87191 /* ConfigEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */; };
+		491D6FBD2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */; };
+		491D6FBE2D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FB92D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift */; };
+		491D6FBF2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */; };
+		491D6FC02D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */; };
+		49B9B57F2D5768D2009C6B59 /* AdjustmentStored+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */; };
 		5075C1608E6249A51495C422 /* TargetsEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */; };
 		53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8630D58BDAD6D9C650B9B39 /* PumpConfigProvider.swift */; };
 		581516A42BCED84A00BF67D7 /* DebuggingIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */; };
@@ -236,10 +239,6 @@
 		5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5887527B2BD986E1008B081D /* OpenAPSBattery.swift */; };
 		58A3D53A2C96D4DE003F90FC /* AddTempTargetForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3D5392C96D4DE003F90FC /* AddTempTargetForm.swift */; };
 		58A3D5442C96DE11003F90FC /* TempTargetStored+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3D5432C96DE11003F90FC /* TempTargetStored+Helper.swift */; };
-		58A3D5512C96EFA8003F90FC /* TempTargetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3D54D2C96EFA8003F90FC /* TempTargetStored+CoreDataClass.swift */; };
-		58A3D5522C96EFA8003F90FC /* TempTargetStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3D54E2C96EFA8003F90FC /* TempTargetStored+CoreDataProperties.swift */; };
-		58A3D5532C96EFA8003F90FC /* TempTargetRunStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3D54F2C96EFA8003F90FC /* TempTargetRunStored+CoreDataClass.swift */; };
-		58A3D5542C96EFA8003F90FC /* TempTargetRunStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3D5502C96EFA8003F90FC /* TempTargetRunStored+CoreDataProperties.swift */; };
 		58D08B222C8DAA8E00AA37D3 /* OverrideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B212C8DAA8E00AA37D3 /* OverrideView.swift */; };
 		58D08B302C8DEA7500AA37D3 /* ForecastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B2F2C8DEA7500AA37D3 /* ForecastView.swift */; };
 		58D08B322C8DF88900AA37D3 /* DummyCharts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B312C8DF88900AA37D3 /* DummyCharts.swift */; };
@@ -277,6 +276,8 @@
 		7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A401509D21F7F35D4E109EDA /* DataTableDataFlow.swift */; };
 		8194B80890CDD6A3C13B0FEE /* SnoozeStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26904AACA8D9C15D229D675 /* SnoozeStateModel.swift */; };
 		88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB3BAE7494CB771ABAC7B8B /* ISFEditorRootView.swift */; };
+		8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A9134292D63D9A1007F8874 /* Localizable.xcstrings */; };
+		8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */; };
 		8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */; };
 		9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */; };
 		9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */; };
@@ -286,7 +287,7 @@
 		B7C465E9472624D8A2BE2A6A /* (null) in Sources */ = {isa = PBXBuildFile; };
 		B958F1B72BA0711600484851 /* MKRingProgressView in Frameworks */ = {isa = PBXBuildFile; productRef = B958F1B62BA0711600484851 /* MKRingProgressView */; };
 		B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */ = {isa = PBXBuildFile; fileRef = B9CAAEFB2AE70836000F68BC /* branch.txt */; };
-		BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C018D1680307A31C9ED7120 /* CGMStateModel.swift */; };
+		BA00D96F7B2FF169A06FB530 /* CGMSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C018D1680307A31C9ED7120 /* CGMSettingsStateModel.swift */; };
 		BD04ECCE2D29952A008C5FEB /* BolusProgressOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD04ECCD2D299522008C5FEB /* BolusProgressOverlay.swift */; };
 		BD0B2EF32C5998E600B3298F /* MealPresetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0B2EF22C5998E600B3298F /* MealPresetView.swift */; };
 		BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1661302B82ADAB00256551 /* CustomProgressView.swift */; };
@@ -384,6 +385,7 @@
 		CE1F6DE72BAF1A180064EB8D /* BuildDetails.plist in Resources */ = {isa = PBXBuildFile; fileRef = CE1F6DE62BAF1A180064EB8D /* BuildDetails.plist */; };
 		CE1F6DE92BAF37C90064EB8D /* TidepoolConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1F6DE82BAF37C90064EB8D /* TidepoolConfigView.swift */; };
 		CE2FAD3A297D93F0001A872C /* BloodGlucoseExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2FAD39297D93F0001A872C /* BloodGlucoseExtensions.swift */; };
+		CE3EEF9A2D463717001944DD /* CustomCGMOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3EEF992D46370A001944DD /* CustomCGMOptionsView.swift */; };
 		CE48C86428CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE48C86328CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift */; };
 		CE48C86628CA6B48007C0598 /* OmniPodManagerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE48C86528CA6B48007C0598 /* OmniPodManagerExtensions.swift */; };
 		CE51DD1C2A01970900F163F7 /* ConnectIQ 2.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE51DD1B2A01970800F163F7 /* ConnectIQ 2.xcframework */; };
@@ -431,6 +433,7 @@
 		CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */; };
 		CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */; };
 		CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */; };
+		CEF1ED6B2D58FB5800FAF41E /* CGMOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */; };
 		D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */; };
 		D6DEC113821A7F1056C4AA1E /* NightscoutConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */; };
 		DBA5254DBB2586C98F61220C /* ISFEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F137F126D9F8DEB799F26 /* ISFEditorProvider.swift */; };
@@ -582,7 +585,7 @@
 		E592A3792CEEC038009A472C /* ContactImageRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3712CEEC038009A472C /* ContactImageRootView.swift */; };
 		E592A37A2CEEC038009A472C /* ContactImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3742CEEC038009A472C /* ContactImageProvider.swift */; };
 		E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD22C985B79A2F0D2EA3D9D /* PumpConfigRootView.swift */; };
-		F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */; };
+		F5CA3DB1F9DC8B05792BBFAA /* CGMSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B5C0607505A38F256BF99A /* CGMSettingsDataFlow.swift */; };
 		F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */; };
 		F816825E28DB441200054060 /* HeartBeatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F816825D28DB441200054060 /* HeartBeatManager.swift */; };
 		F816826028DB441800054060 /* BluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F816825F28DB441800054060 /* BluetoothTransmitter.swift */; };
@@ -704,33 +707,8 @@
 		190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsProvider.swift; sourceTree = "<group>"; };
 		190EBCC729FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsStateModel.swift; sourceTree = "<group>"; };
 		190EBCCA29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsRootView.swift; sourceTree = "<group>"; };
-		1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = "<group>"; };
-		1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EB2744611900347C69 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8EC2744611A00347C69 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8ED2744611B00347C69 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EE2744611C00347C69 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EF2744611D00347C69 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F02744611E00347C69 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F12744611E00347C69 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F22744611F00347C69 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F32744612000347C69 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F42744612100347C69 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F52744612100347C69 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8F62744612200347C69 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8F72744612300347C69 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F82744612400347C69 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F92744612400347C69 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FA2744612500347C69 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FB2744612600347C69 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* Oref2_variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Oref2_variables.swift; sourceTree = "<group>"; };
-		193F1E392B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		193F1E3A2B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
-		193F1E3B2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		193F1E3C2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = "<group>"; };
 		195D80B32AF6973A00D25097 /* DynamicSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicSettingsRootView.swift; sourceTree = "<group>"; };
 		195D80B62AF697B800D25097 /* DynamicSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicSettingsDataFlow.swift; sourceTree = "<group>"; };
@@ -739,30 +717,12 @@
 		1967DFBD29D052C200759F30 /* Icons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icons.swift; sourceTree = "<group>"; };
 		1967DFBF29D053AC00759F30 /* IconSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSelection.swift; sourceTree = "<group>"; };
 		1967DFC129D053D300759F30 /* IconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImage.swift; sourceTree = "<group>"; };
-		198377D3266BFFF6004DE65E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D5266C0A05004DE65E /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D6266C0A0A004DE65E /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D7266C0A15004DE65E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
-		198377D8266C0A1C004DE65E /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D9266C0A21004DE65E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DA266C0A2B004DE65E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DB266C0A32004DE65E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DC266C0A3C004DE65E /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DD266C0A51004DE65E /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DE266C0A69004DE65E /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DF266C0A7F004DE65E /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E0266C0AB5004DE65E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E1266C0ABF004DE65E /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E2266C0AC8004DE65E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E3266C0ADC004DE65E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E4266C13D2004DE65E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
 		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
+		19A9102F2A24BF6300C8951B /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
 		19A910352A24D6D700C8951B /* DateFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFilter.swift; sourceTree = "<group>"; };
 		19B0EF2028F6D66200069496 /* Statistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics.swift; sourceTree = "<group>"; };
-		19C166682756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		19C166692756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		19D466A229AA2B80004D5F33 /* MealSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsDataFlow.swift; sourceTree = "<group>"; };
 		19D466A429AA2BD4004D5F33 /* MealSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsProvider.swift; sourceTree = "<group>"; };
 		19D466A629AA2C22004D5F33 /* MealSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -943,7 +903,7 @@
 		38FE826C25CC8461001FF17A /* NightscoutAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutAPI.swift; sourceTree = "<group>"; };
 		38FEF3F92737E42000574A46 /* BaseStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseStateModel.swift; sourceTree = "<group>"; };
 		38FEF3FB2737E53800574A46 /* MainStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainStateModel.swift; sourceTree = "<group>"; };
-		38FEF3FD2738083E00574A46 /* CGMProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMProvider.swift; sourceTree = "<group>"; };
+		38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMSettingsProvider.swift; sourceTree = "<group>"; };
 		38FEF412273B317A00574A46 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = "<group>"; };
 		3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorProvider.swift; sourceTree = "<group>"; };
 		3BF768BD6264FF7D71D66767 /* NightscoutConfigProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigProvider.swift; sourceTree = "<group>"; };
@@ -951,6 +911,11 @@
 		3F8A87AA037BD079BA3528BA /* ConfigEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorDataFlow.swift; sourceTree = "<group>"; };
 		42369F66CF91F30624C0B3A6 /* BasalProfileEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorProvider.swift; sourceTree = "<group>"; };
 		44080E4709E3AE4B73054563 /* ConfigEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorProvider.swift; sourceTree = "<group>"; };
+		491D6FB92D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetRunStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetRunStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdjustmentStored+Helper.swift"; sourceTree = "<group>"; };
 		4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorRootView.swift; sourceTree = "<group>"; };
 		505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorStateModel.swift; sourceTree = "<group>"; };
 		581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggingIdentifiers.swift; sourceTree = "<group>"; };
@@ -979,10 +944,6 @@
 		5887527B2BD986E1008B081D /* OpenAPSBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAPSBattery.swift; sourceTree = "<group>"; };
 		58A3D5392C96D4DE003F90FC /* AddTempTargetForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTempTargetForm.swift; sourceTree = "<group>"; };
 		58A3D5432C96DE11003F90FC /* TempTargetStored+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+Helper.swift"; sourceTree = "<group>"; };
-		58A3D54D2C96EFA8003F90FC /* TempTargetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		58A3D54E2C96EFA8003F90FC /* TempTargetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		58A3D54F2C96EFA8003F90FC /* TempTargetRunStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetRunStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		58A3D5502C96EFA8003F90FC /* TempTargetRunStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetRunStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		58D08B212C8DAA8E00AA37D3 /* OverrideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideView.swift; sourceTree = "<group>"; };
 		58D08B2F2C8DEA7500AA37D3 /* ForecastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastView.swift; sourceTree = "<group>"; };
 		58D08B312C8DF88900AA37D3 /* DummyCharts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyCharts.swift; sourceTree = "<group>"; };
@@ -993,7 +954,7 @@
 		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
 		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
 		5A2325572BFCC168003518CA /* NightscoutConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutConnectView.swift; sourceTree = "<group>"; };
-		5C018D1680307A31C9ED7120 /* CGMStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMStateModel.swift; sourceTree = "<group>"; };
+		5C018D1680307A31C9ED7120 /* CGMSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMSettingsStateModel.swift; sourceTree = "<group>"; };
 		5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorStateModel.swift; sourceTree = "<group>"; };
 		60744C3E9BB3652895C908CC /* DataTableProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableProvider.swift; sourceTree = "<group>"; };
 		64AA5E04A2761F6EEA6568E1 /* CarbRatioEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorStateModel.swift; sourceTree = "<group>"; };
@@ -1016,6 +977,8 @@
 		7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorDataFlow.swift; sourceTree = "<group>"; };
 		8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigRootView.swift; sourceTree = "<group>"; };
 		881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableRootView.swift; sourceTree = "<group>"; };
+		8A9134292D63D9A1007F8874 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
+		8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
 		920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorRootView.swift; sourceTree = "<group>"; };
 		9455FA2D92E77A6C4AFED8A3 /* DataTableStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableStateModel.swift; sourceTree = "<group>"; };
 		96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalDataFlow.swift; sourceTree = "<group>"; };
@@ -1027,7 +990,7 @@
 		AAFF91130F2FCCC7EBBA11AD /* BasalProfileEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorStateModel.swift; sourceTree = "<group>"; };
 		AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpConfigDataFlow.swift; sourceTree = "<group>"; };
 		B5822B15939E719628E9FF7C /* SnoozeRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeRootView.swift; sourceTree = "<group>"; };
-		B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMDataFlow.swift; sourceTree = "<group>"; };
+		B9B5C0607505A38F256BF99A /* CGMSettingsDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMSettingsDataFlow.swift; sourceTree = "<group>"; };
 		B9CAAEFB2AE70836000F68BC /* branch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = branch.txt; sourceTree = SOURCE_ROOT; };
 		BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorDataFlow.swift; sourceTree = "<group>"; };
 		BD04ECCD2D299522008C5FEB /* BolusProgressOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusProgressOverlay.swift; sourceTree = "<group>"; };
@@ -1129,6 +1092,7 @@
 		CE398D012977349800DF218F /* CryptoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoKit.framework; path = System/Library/Frameworks/CryptoKit.framework; sourceTree = SDKROOT; };
 		CE398D17297C9EE800DF218F /* G7SensorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = G7SensorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		CE398D1A297D69A900DF218F /* ShareClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		CE3EEF992D46370A001944DD /* CustomCGMOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCGMOptionsView.swift; sourceTree = "<group>"; };
 		CE48C86328CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniBLEPumpManagerExtensions.swift; sourceTree = "<group>"; };
 		CE48C86528CA6B48007C0598 /* OmniPodManagerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniPodManagerExtensions.swift; sourceTree = "<group>"; };
 		CE51DD1B2A01970800F163F7 /* ConnectIQ 2.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "ConnectIQ 2.xcframework"; path = "Dependencies/ConnectIQ 2.xcframework"; sourceTree = "<group>"; };
@@ -1176,6 +1140,7 @@
 		CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationsDataFlow.swift; sourceTree = "<group>"; };
 		CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationService.swift; sourceTree = "<group>"; };
 		CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationsTests.swift; sourceTree = "<group>"; };
+		CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMOptions.swift; sourceTree = "<group>"; };
 		CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalStateModel.swift; sourceTree = "<group>"; };
 		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
 		DC2C6489D29ECCCAD78E0721 /* GlucoseNotificationSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -1434,6 +1399,7 @@
 		0D76BBC81CEDC1A0050F45EF /* View */ = {
 			isa = PBXGroup;
 			children = (
+				CE3EEF992D46370A001944DD /* CustomCGMOptionsView.swift */,
 				38569352270B5E350002C50D /* CGMRootView.swift */,
 				CE7950232997D81700FA576E /* CGMSettingsView.swift */,
 				CE7950252998056D00FA576E /* CGMSetupView.swift */,
@@ -1554,7 +1520,7 @@
 		19D440A926B6FEBD008DA6C8 /* Main */ = {
 			isa = PBXGroup;
 			children = (
-				198377D4266BFFF6004DE65E /* Localizable.strings */,
+				8A9134292D63D9A1007F8874 /* Localizable.xcstrings */,
 			);
 			path = Main;
 			sourceTree = "<group>";
@@ -1651,7 +1617,7 @@
 				DD09D4792C5986BA003FEA5D /* CalendarEventSettings */,
 				CEE9A64D2BBB411C00EB5194 /* Calibrations */,
 				E42231DBF0DBE2B4B92D1B15 /* CarbRatioEditor */,
-				F75CB57ED6971B46F8756083 /* CGM */,
+				F75CB57ED6971B46F8756083 /* CGMSettings */,
 				0610F7D6D2EC00E3BA1569F0 /* ConfigEditor */,
 				E592A3762CEEC038009A472C /* ContactImage */,
 				9E56E3626FAD933385101B76 /* DataTable */,
@@ -1876,7 +1842,7 @@
 				388E596E25AD96040019842D /* javascript */,
 				3811DEC725C9DA7300A708ED /* Trio.entitlements */,
 				388E596425AD948E0019842D /* Info.plist */,
-				1927C8E82744606D00347C69 /* InfoPlist.strings */,
+				8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */,
 				B9CAAEFB2AE70836000F68BC /* branch.txt */,
 				19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */,
 			);
@@ -2154,6 +2120,7 @@
 			children = (
 				BD249DA62D42FE3800412DEB /* Calendar+GlucoseStatsChart.swift */,
 				BD249DA42D42FD9500412DEB /* CustomDatePicker.swift */,
+				CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */,
 				C2A0A42E2CE0312C003B98E8 /* ConstantValues.swift */,
 				DD940BAB2CA75889000830A5 /* DynamicGlucoseColor.swift */,
 				38F37827261260DC009DB701 /* Color+Extensions.swift */,
@@ -2383,6 +2350,7 @@
 		5825D1622BD405AE00F36E9B /* Helper */ = {
 			isa = PBXGroup;
 			children = (
+				49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */,
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
@@ -3094,10 +3062,10 @@
 				DDE179412C910127003CDDB7 /* StatsData+CoreDataProperties.swift */,
 				DDE179482C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift */,
 				DDE179492C910127003CDDB7 /* TempBasalStored+CoreDataProperties.swift */,
-				58A3D54F2C96EFA8003F90FC /* TempTargetRunStored+CoreDataClass.swift */,
-				58A3D5502C96EFA8003F90FC /* TempTargetRunStored+CoreDataProperties.swift */,
-				58A3D54D2C96EFA8003F90FC /* TempTargetStored+CoreDataClass.swift */,
-				58A3D54E2C96EFA8003F90FC /* TempTargetStored+CoreDataProperties.swift */,
+				491D6FB92D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift */,
+				491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */,
+				491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */,
+				491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */,
 			);
 			path = "Classes+Properties";
 			sourceTree = "<group>";
@@ -3205,15 +3173,15 @@
 			path = GlucoseNotificationSettings;
 			sourceTree = "<group>";
 		};
-		F75CB57ED6971B46F8756083 /* CGM */ = {
+		F75CB57ED6971B46F8756083 /* CGMSettings */ = {
 			isa = PBXGroup;
 			children = (
-				B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */,
-				38FEF3FD2738083E00574A46 /* CGMProvider.swift */,
-				5C018D1680307A31C9ED7120 /* CGMStateModel.swift */,
+				B9B5C0607505A38F256BF99A /* CGMSettingsDataFlow.swift */,
+				38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */,
+				5C018D1680307A31C9ED7120 /* CGMSettingsStateModel.swift */,
 				0D76BBC81CEDC1A0050F45EF /* View */,
 			);
-			path = CGM;
+			path = CGMSettings;
 			sourceTree = "<group>";
 		};
 		F90692A8274B7A980037068D /* HealthKit */ = {
@@ -3467,15 +3435,15 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				198377D2266BFFF6004DE65E /* Localizable.strings in Resources */,
+				8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */,
 				CE1F6DE72BAF1A180064EB8D /* BuildDetails.plist in Resources */,
 				38DF178D27733E6800B3528F /* snow.sks in Resources */,
 				388E597225AD9CF10019842D /* json in Resources */,
 				38DF178E27733E6800B3528F /* Assets.xcassets in Resources */,
 				19DA48E829CD339B00EEA1E7 /* Assets.xcassets in Resources */,
+				8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */,
 				388E596F25AD96040019842D /* javascript in Resources */,
 				B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */,
-				1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -3621,7 +3589,6 @@
 				CE7CA3552A064973004BE681 /* ListStateIntent.swift in Sources */,
 				BDF530D82B40F8AC002CAF43 /* LockScreenView.swift in Sources */,
 				195D80B72AF697B800D25097 /* DynamicSettingsDataFlow.swift in Sources */,
-				58A3D5512C96EFA8003F90FC /* TempTargetStored+CoreDataClass.swift in Sources */,
 				3862CC2E2743F9F700BF832C /* CalendarManager.swift in Sources */,
 				CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */,
 				38B4F3C325E2A20B00E76A18 /* PumpSetupView.swift in Sources */,
@@ -3667,7 +3634,6 @@
 				DD1745552C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift in Sources */,
 				384E803825C388640086DB71 /* Script.swift in Sources */,
 				CE94597E29E9E1EE0047C9C6 /* GarminManager.swift in Sources */,
-				58A3D5542C96EFA8003F90FC /* TempTargetRunStored+CoreDataProperties.swift in Sources */,
 				3883583425EEB38000E024B2 /* PumpSettings.swift in Sources */,
 				38DAB280260CBB7F00F74C1A /* PumpView.swift in Sources */,
 				DDD1631C2C4C697400CD525A /* AddOverrideForm.swift in Sources */,
@@ -3710,6 +3676,7 @@
 				58A3D5522C96EFA8003F90FC /* TempTargetStored+CoreDataProperties.swift in Sources */,
 				BD249D942D42FC5E00412DEB /* TDDChart.swift in Sources */,
 				BD249D902D42FC4500412DEB /* MealStatsView.swift in Sources */,
+				19A910382A24EF3200C8951B /* ChartsView.swift in Sources */,
 				DD32CF9A2CC8247B003686D6 /* TrioRemoteControl+Meal.swift in Sources */,
 				BDF34F832C10C5B600D51995 /* DataManager.swift in Sources */,
 				38B4F3C625E5017E00E76A18 /* NotificationCenter.swift in Sources */,
@@ -3853,6 +3820,7 @@
 				DD1745522C55CA5D00211FAC /* UnitsLimitsSettingsStateModel.swift in Sources */,
 				DD2CC85C2D25DA1000445446 /* GlucoseTargetsView.swift in Sources */,
 				190EBCC429FF136900BA767D /* UserInterfaceSettingsDataFlow.swift in Sources */,
+				CE3EEF9A2D463717001944DD /* CustomCGMOptionsView.swift in Sources */,
 				5A2325582BFCC168003518CA /* NightscoutConnectView.swift in Sources */,
 				3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */,
 				110AEDE42C5193D200615CC9 /* BolusIntentRequest.swift in Sources */,
@@ -3884,6 +3852,10 @@
 				9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */,
 				DDA6E3222D25901100C2988C /* TempTargetHelpView.swift in Sources */,
 				58645B9D2CA2D275008AFCE7 /* DeterminationSetup.swift in Sources */,
+				491D6FBD2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift in Sources */,
+				491D6FBE2D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift in Sources */,
+				491D6FBF2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift in Sources */,
+				491D6FC02D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift in Sources */,
 				DD1745442C55C60E00211FAC /* AutosensSettingsDataFlow.swift in Sources */,
 				BD249DA72D42FE4600412DEB /* Calendar+GlucoseStatsChart.swift in Sources */,
 				BDCAF2382C639F35002DC907 /* SettingItems.swift in Sources */,
@@ -3897,6 +3869,7 @@
 				53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */,
 				5D16287A969E64D18CE40E44 /* PumpConfigStateModel.swift in Sources */,
 				19D466AA29AA3099004D5F33 /* MealSettingsRootView.swift in Sources */,
+				CEF1ED6B2D58FB5800FAF41E /* CGMOptions.swift in Sources */,
 				E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */,
 				DD1745502C55CA5500211FAC /* UnitsLimitsSettingsProvider.swift in Sources */,
 				581AC4392BE22ED10038760C /* JSONConverter.swift in Sources */,
@@ -3908,7 +3881,7 @@
 				E06B911A275B5EEA003C04B6 /* Array+Extension.swift in Sources */,
 				38EA0600262091870064E39B /* BolusProgressViewStyle.swift in Sources */,
 				389ECDFE2601061500D86C4F /* View+Snapshot.swift in Sources */,
-				38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */,
+				38FEF3FE2738083E00574A46 /* CGMSettingsProvider.swift in Sources */,
 				38E98A3725F5509500C0CED0 /* String+Extensions.swift in Sources */,
 				CC76E9512BD4812E008BEB61 /* Forecast+helper.swift in Sources */,
 				DD1745242C55526000211FAC /* SMBSettingsStateModel.swift in Sources */,
@@ -3974,7 +3947,6 @@
 				38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */,
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
 				58A3D5442C96DE11003F90FC /* TempTargetStored+Helper.swift in Sources */,
-				58A3D5532C96EFA8003F90FC /* TempTargetRunStored+CoreDataClass.swift in Sources */,
 				DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */,
 				DD5DC9F32CF3D9DD00AB8703 /* AdjustmentsStateModel+TempTargets.swift in Sources */,
 				F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */,
@@ -4031,6 +4003,7 @@
 				DD1745222C55524800211FAC /* SMBSettingsProvider.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
 				583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */,
+				49B9B57F2D5768D2009C6B59 /* AdjustmentStored+Helper.swift in Sources */,
 				F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */,
 				BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */,
 				C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */,
@@ -4056,9 +4029,9 @@
 				D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */,
 				DD1745292C55642100211FAC /* SettingInputSection.swift in Sources */,
 				38569349270B5DFB0002C50D /* AppGroupSource.swift in Sources */,
-				F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */,
+				F5CA3DB1F9DC8B05792BBFAA /* CGMSettingsDataFlow.swift in Sources */,
 				BDF34F952C10D27300D51995 /* DeterminationData.swift in Sources */,
-				BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */,
+				BA00D96F7B2FF169A06FB530 /* CGMSettingsStateModel.swift in Sources */,
 				BD7DA9A52AE06DFC00601B20 /* BolusCalculatorConfigDataFlow.swift in Sources */,
 				6EADD581738D64431902AC0A /* (null) in Sources */,
 				CE94598729E9E4110047C9C6 /* WatchConfigRootView.swift in Sources */,
@@ -4216,69 +4189,6 @@
 		};
 /* End PBXTargetDependency section */
 
-/* Begin PBXVariantGroup section */
-		1927C8E82744606D00347C69 /* InfoPlist.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				1927C8E92744611700347C69 /* ar */,
-				1927C8EA2744611800347C69 /* ca */,
-				1927C8EB2744611900347C69 /* zh-Hans */,
-				1927C8EC2744611A00347C69 /* da */,
-				1927C8ED2744611B00347C69 /* fi */,
-				1927C8EE2744611C00347C69 /* nl */,
-				1927C8EF2744611D00347C69 /* fr */,
-				1927C8F02744611E00347C69 /* de */,
-				1927C8F12744611E00347C69 /* he */,
-				1927C8F22744611F00347C69 /* it */,
-				1927C8F32744612000347C69 /* nb */,
-				1927C8F42744612100347C69 /* pl */,
-				1927C8F52744612100347C69 /* pt-BR */,
-				1927C8F62744612200347C69 /* pt-PT */,
-				1927C8F72744612300347C69 /* ru */,
-				1927C8F82744612400347C69 /* es */,
-				1927C8F92744612400347C69 /* sv */,
-				1927C8FA2744612500347C69 /* tr */,
-				1927C8FB2744612600347C69 /* uk */,
-				1927C8FE274489BA00347C69 /* Base */,
-				19C166682756EFBD00ED12E3 /* sk */,
-				193F1E392B44C13B00525770 /* hu */,
-				193F1E3B2B44C14800525770 /* vi */,
-			);
-			name = InfoPlist.strings;
-			sourceTree = "<group>";
-		};
-		198377D4266BFFF6004DE65E /* Localizable.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				198377D3266BFFF6004DE65E /* en */,
-				198377D5266C0A05004DE65E /* ar */,
-				198377D6266C0A0A004DE65E /* ca */,
-				198377D7266C0A15004DE65E /* zh-Hans */,
-				198377D8266C0A1C004DE65E /* da */,
-				198377D9266C0A21004DE65E /* nl */,
-				198377DA266C0A2B004DE65E /* fr */,
-				198377DB266C0A32004DE65E /* de */,
-				198377DC266C0A3C004DE65E /* he */,
-				198377DD266C0A51004DE65E /* it */,
-				198377DE266C0A69004DE65E /* nb */,
-				198377DF266C0A7F004DE65E /* pl */,
-				198377E0266C0AB5004DE65E /* ru */,
-				198377E1266C0ABF004DE65E /* es */,
-				198377E2266C0AC8004DE65E /* sv */,
-				198377E3266C0ADC004DE65E /* tr */,
-				198377E4266C13D2004DE65E /* uk */,
-				1918333A26ADA46800F45722 /* fi */,
-				199732B4271B72DD00129A3F /* pt-PT */,
-				199732B5271B9EE900129A3F /* pt-BR */,
-				19C166692756EFBD00ED12E3 /* sk */,
-				193F1E3A2B44C13B00525770 /* hu */,
-				193F1E3C2B44C14800525770 /* vi */,
-			);
-			name = Localizable.strings;
-			sourceTree = "<group>";
-		};
-/* End PBXVariantGroup section */
-
 /* Begin XCBuildConfiguration section */
 		388E596525AD948E0019842D /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -4336,12 +4246,14 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 			};
 			name = Debug;
@@ -4396,11 +4308,13 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				VALIDATE_PRODUCT = YES;
 			};

+ 0 - 23
Trio/Resources/Base.lproj/InfoPlist.strings

@@ -1,23 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Contacts Usage Description */
-"NSContactsUsageDescription" = "Allows Trio to access your contacts for live updates to your Apple Watch contact complication using the 'Contact Trick' feature.";

File diff suppressed because it is too large
+ 1059 - 0
Trio/Resources/InfoPlist.xcstrings


+ 0 - 20
Trio/Resources/ar.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";

+ 0 - 20
Trio/Resources/ca.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/da.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC bruges til at scanne Libre sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autoriseret adgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender bruges til at oprette en ny glucose begivenheder.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";

+ 0 - 20
Trio/Resources/de.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wird zum Scannen von Libre Sensoren benutzt.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Autorisierung für Bolusabgabe";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "BZ-Werte werden im Kalender als temporärer Termin erstellt.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";

+ 0 - 20
Trio/Resources/es.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/fi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/fr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC est utilisé pour scanner les capteurs Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pour les accès autorisés au bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Le calendrier est utilisé pour créer un nouvel événement de glycémie.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";

+ 0 - 20
Trio/Resources/he.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/hu.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/it.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC è usato per scansionare i sensori di Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Il bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Il Bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Per accesso autorizzato al bolo";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Il calendario è usato per creare nuovi eventi di glicemia.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";

File diff suppressed because it is too large
+ 1 - 1
Trio/Resources/javascript/bundle/meal.js


File diff suppressed because it is too large
+ 1 - 1
Trio/Resources/javascript/bundle/profile.js


+ 1 - 0
Trio/Resources/json/defaults/preferences.json

@@ -14,6 +14,7 @@
   "exercise_mode" : false,
   "half_basal_exercise_target" : 160,
   "maxCOB" : 120,
+  "maxMealAbsorptionTime" : 6,
   "wide_bg_target_range" : false,
   "skip_neutral_temps" : false,
   "unsuspend_if_no_temp" : false,

+ 0 - 20
Trio/Resources/nb.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC brukes til å skanne Libre-sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor-enheter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autorisert tilgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender brukes til å opprette nye blodsukker-oppføringer.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";

+ 0 - 20
Trio/Resources/nl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wordt gebruikt voor het scannen van Libre sensoren.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Voor geautoriseerde toegang tot bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Agenda wordt gebruikt om nieuwe glucose gebeurtenissen aan te maken.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";

+ 0 - 20
Trio/Resources/pl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/pt-BR.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/pt-PT.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/ru.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC используется для сканирования сенсоров Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованного болюса";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Календарь используется для создания новых событий о глюкозе.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";

+ 0 - 20
Trio/Resources/sk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC sa používa na skenovanie snímačov Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pre autorizovaný prístup k bolusu";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendár slúži na vytvorenie novej udalosti s glukózou.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";

+ 0 - 20
Trio/Resources/sv.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC används för att skanna Libre-sensorn vid uppstart.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth används för att kommunicera med pump och kontinuerlig glukosmätare";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth används för att kommunicera med insulinpumpen och kontinuerliga glukosmätare";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "För auktoriserad åtkomst till bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendern används för att skapa kalenderhändelser för glukosvärden.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin samt kolhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin och kolhydrater.";

+ 0 - 20
Trio/Resources/tr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC Libre sensörlerini taramak için kullanılır.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Bolus'a yetkili erişim için";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Takvim, yeni bir glikoz olayı oluşturmak için kullanılır.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/uk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC використовується для сканування сенсорів Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованого болюсу";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Для створення нових подій глюкози використовується календар.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";

+ 0 - 20
Trio/Resources/vi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC được sử dụng để quét các cảm biến Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Được cấp quyền truy cập vào bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Lịch \nCalendar được sử dụng để tạo ra một sự kiện glucose mới.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";

+ 0 - 20
Trio/Resources/zh-Hans.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC 用于扫描 Libre 传感器。";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "用于输注胰岛素授权";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "日历用于创建一个新的葡萄糖事件。";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 9 - 3
Trio/Sources/APS/APSManager.swift

@@ -22,7 +22,7 @@ protocol APSManager {
     func enactTempBasal(rate: Double, duration: TimeInterval) async
     func determineBasal() async -> Bool
     func determineBasalSync() async
-    func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination?
+    func simulateDetermineBasal(simulatedCarbsAmount: Decimal, simulatedBolusAmount: Decimal) async -> Determination?
     func roundBolus(amount: Decimal) -> Decimal
     var lastError: CurrentValueSubject<Error?, Never> { get }
     func cancelBolus(_ callback: ((Bool, String) -> Void)?) async
@@ -413,10 +413,16 @@ final class BaseAPSManager: APSManager, Injectable {
         _ = await determineBasal()
     }
 
-    func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination? {
+    func simulateDetermineBasal(simulatedCarbsAmount: Decimal, simulatedBolusAmount: Decimal) async -> Determination? {
         do {
             let temp = await fetchCurrentTempBasal(date: Date.now)
-            return try await openAPS.determineBasal(currentTemp: temp, clock: Date(), carbs: carbs, iob: iob, simulation: true)
+            return try await openAPS.determineBasal(
+                currentTemp: temp,
+                clock: Date(),
+                simulatedCarbsAmount: simulatedCarbsAmount,
+                simulatedBolusAmount: simulatedBolusAmount,
+                simulation: true
+            )
         } catch {
             debugPrint(
                 "\(DebuggingIdentifiers.failed) \(#file) \(#function) Error occurred in invokeDummyDetermineBasalSync: \(error)"

+ 1 - 1
Trio/Sources/APS/CGM/AppGroupSource.swift

@@ -115,7 +115,7 @@ struct AppGroupSource: GlucoseSource {
     }
 
     func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? "Not set"))"]
+        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? String(localized: "Not set"))"]
     }
 }
 

+ 10 - 9
Trio/Sources/APS/CGM/CGMType.swift

@@ -14,15 +14,15 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
         case .none:
             return "None"
         case .nightscout:
-            return "Nightscout"
+            return "Nightscout as CGM"
         case .xdrip:
             return "xDrip4iOS"
         case .simulator:
-            return NSLocalizedString("Glucose Simulator", comment: "Glucose Simulator CGM type")
+            return String(localized: "Glucose Simulator", comment: "Glucose Simulator CGM type")
         case .enlite:
             return "Medtronic Enlite"
         case .plugin:
-            return "plugin CGM"
+            return "Plugin CGM"
         }
     }
 
@@ -52,20 +52,21 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
     var subtitle: String {
         switch self {
         case .none:
-            return NSLocalizedString("None", comment: "No CGM choiced")
+            return String(localized: "None", comment: "No CGM selected")
         case .nightscout:
-            return NSLocalizedString("Online or internal server", comment: "Online or internal server")
+            return String(localized: "Uses your Nightscout as CGM", comment: "Online or internal server")
         case .xdrip:
-            return NSLocalizedString(
+            return String(
+                localized:
                 "Using shared app group with external CGM app xDrip4iOS",
                 comment: "Shared app group xDrip4iOS"
             )
         case .simulator:
-            return NSLocalizedString("Simple simulator", comment: "Simple simulator")
+            return String(localized: "Glucose Simulator for Demo Only", comment: "Simple simulator")
         case .enlite:
-            return NSLocalizedString("Minilink transmitter", comment: "Minilink transmitter")
+            return String(localized: "Minilink transmitter", comment: "Minilink transmitter")
         case .plugin:
-            return NSLocalizedString("Plugin CGM", comment: "Plugin CGM")
+            return String(localized: "Plugin CGM", comment: "Plugin CGM")
         }
     }
 }

+ 4 - 4
Trio/Sources/APS/CGM/PluginSource.swift

@@ -7,7 +7,7 @@ import LoopKit
 import LoopKitUI
 
 final class PluginSource: GlucoseSource {
-    private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
+    private let processQueue = DispatchQueue(label: "CGMPluginSource.processQueue")
     private let glucoseStorage: GlucoseStorage!
     var glucoseManager: FetchGlucoseManager?
 
@@ -107,8 +107,7 @@ extension PluginSource: CGMManagerDelegate {
     func cgmManagerWantsDeletion(_ manager: CGMManager) {
         dispatchPrecondition(condition: .onQueue(processQueue))
         debug(.deviceManager, " CGM Manager with identifier \(manager.pluginIdentifier) wants deletion")
-        // TODO:
-        glucoseManager?.cgmGlucoseSourceType = .none
+        glucoseManager?.deleteGlucoseSource()
     }
 
     func cgmManager(_: CGMManager, hasNew readingResult: CGMReadingResult) {
@@ -161,7 +160,8 @@ extension PluginSource: CGMManagerDelegate {
     }
 
     func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
-        debug(.deviceManager, "DEBUG DID UPDATE STATE")
+        debug(.deviceManager, "CGM Manager did update state to \(status)")
+
         processQueue.async {
             if self.cgmHasValidSensorSession != status.hasValidSensorSession {
                 self.cgmHasValidSensorSession = status.hasValidSensorSession

+ 0 - 8
Trio/Sources/APS/DeviceDataManager.swift

@@ -415,10 +415,6 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
             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
@@ -700,10 +696,6 @@ protocol PumpBatteryObserver {
     func pumpBatteryDidChange(_ battery: Battery)
 }
 
-protocol PumpTimeZoneObserver {
-    func pumpTimeZoneDidChange(_ timezone: TimeZone)
-}
-
 protocol PumpDeactivatedObserver {
     func pumpDeactivatedDidChange()
 }

+ 27 - 4
Trio/Sources/APS/FetchGlucoseManager.swift

@@ -126,10 +126,15 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
         // if plugin, if not the same pluginID, need to reset the cgmManager
         // if plugin and newManager provides, update cgmManager
         debug(.apsManager, "plugin : \(String(describing: cgmManager?.pluginIdentifier))")
-        if let manager = newManager
-        {
-            cgmManager = manager
-            removeCalibrations()
+
+        if let manager = newManager {
+            // If the pointer to manager is the *same* as our current `cgmManager`, skip re-init
+            if manager !== cgmManager {
+                // or do a more thorough check to see if it is the same class & state
+                removeCalibrations()
+                cgmManager = manager
+                glucoseSource = nil
+            }
         } else if self.cgmGlucoseSourceType == .plugin, cgmManager == nil, let rawCGMManager = rawCGMManager {
             cgmManager = cgmManagerFromRawValue(rawCGMManager)
             updateManagerUnits(cgmManager)
@@ -338,6 +343,24 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
     }
 }
 
+extension FetchGlucoseManager {
+    /// Dispatches given `functionToInvoke` to the CGM manager's queue (if any).
+    func performOnCGMManagerQueue(_ functionToInvoke: @escaping () -> Void) {
+        // If a CGM manager exists and it defines a delegate queue, use it
+        if let cgmManager = self.cgmManager,
+           let managerQueue = cgmManager.delegateQueue
+        {
+            managerQueue.async {
+                functionToInvoke()
+            }
+        } else {
+            // If there's no cgmManager or no queue, just run the block immediately
+            // This possibly executes `functionToInvoke` on main thread
+            functionToInvoke()
+        }
+    }
+}
+
 extension CGMManager {
     typealias RawValue = [String: Any]
 

+ 25 - 10
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -189,7 +189,10 @@ final class OpenAPS {
         }
     }
 
-    private func parsePumpHistory(_ pumpHistoryObjectIDs: [NSManagedObjectID], iob: Decimal? = nil) async -> String {
+    private func parsePumpHistory(
+        _ pumpHistoryObjectIDs: [NSManagedObjectID],
+        simulatedBolusAmount: Decimal? = nil
+    ) async -> String {
         // Return an empty JSON object if the list of object IDs is empty
         guard !pumpHistoryObjectIDs.isEmpty else { return "{}" }
 
@@ -199,9 +202,9 @@ final class OpenAPS {
             var dtos = self.loadAndMapPumpEvents(pumpHistoryObjectIDs)
 
             // Optionally add the IOB as a DTO
-            if let iob = iob {
-                let iobDTO = self.createIOBDTO(iob: iob)
-                dtos.insert(iobDTO, at: 0)
+            if let simulatedBolusAmount = simulatedBolusAmount {
+                let simulatedBolusDTO = self.createSimulatedBolusDTO(simulatedBolusAmount: simulatedBolusAmount)
+                dtos.insert(simulatedBolusDTO, at: 0)
             }
 
             // Convert the DTOs to JSON
@@ -226,12 +229,24 @@ final class OpenAPS {
             if let tempBasalDTO = event.toTempBasalDTOEnum() {
                 eventDTOs.append(tempBasalDTO)
             }
+            if let pumpSuspendDTO = event.toPumpSuspendDTO() {
+                eventDTOs.append(pumpSuspendDTO)
+            }
+            if let pumpResumeDTO = event.toPumpResumeDTO() {
+                eventDTOs.append(pumpResumeDTO)
+            }
+            if let rewindDTO = event.toRewindDTO() {
+                eventDTOs.append(rewindDTO)
+            }
+            if let primeDTO = event.toPrimeDTO() {
+                eventDTOs.append(primeDTO)
+            }
             return eventDTOs
         }
         return dtos
     }
 
-    private func createIOBDTO(iob: Decimal) -> PumpEventDTO {
+    private func createSimulatedBolusDTO(simulatedBolusAmount: Decimal) -> PumpEventDTO {
         let oneSecondAgo = Calendar.current
             .date(
                 byAdding: .second,
@@ -243,7 +258,7 @@ final class OpenAPS {
         let bolusDTO = BolusDTO(
             id: UUID().uuidString,
             timestamp: dateFormatted,
-            amount: Double(iob),
+            amount: Double(simulatedBolusAmount),
             isExternal: false,
             isSMB: true,
             duration: 0,
@@ -255,8 +270,8 @@ final class OpenAPS {
     func determineBasal(
         currentTemp: TempBasal,
         clock: Date = Date(),
-        carbs: Decimal? = nil,
-        iob: Decimal? = nil,
+        simulatedCarbsAmount: Decimal? = nil,
+        simulatedBolusAmount: Decimal? = nil,
         simulation: Bool = false
     ) async throws -> Determination? {
         debug(.openAPS, "Start determineBasal")
@@ -266,7 +281,7 @@ final class OpenAPS {
 
         // Perform asynchronous calls in parallel
         async let pumpHistoryObjectIDs = fetchPumpHistoryObjectIDs() ?? []
-        async let carbs = fetchAndProcessCarbs(additionalCarbs: carbs ?? 0)
+        async let carbs = fetchAndProcessCarbs(additionalCarbs: simulatedCarbsAmount ?? 0)
         async let glucose = fetchAndProcessGlucose()
         async let oref2 = oref2()
         async let profileAsync = loadFileFromStorageAsync(name: Settings.profile)
@@ -287,7 +302,7 @@ final class OpenAPS {
             reservoir,
             preferences
         ) = await (
-            parsePumpHistory(await pumpHistoryObjectIDs, iob: iob),
+            parsePumpHistory(await pumpHistoryObjectIDs, simulatedBolusAmount: simulatedBolusAmount),
             carbs,
             glucose,
             oref2,

+ 1 - 1
Trio/Sources/APS/Storage/ContactImageStorage.swift

@@ -39,7 +39,7 @@ final class BaseContactImageStorage: ContactImageStorage, Injectable {
 
             return fetchedContactImageEntries.compactMap { entry in
                 ContactImageEntry(
-                    name: entry.name ?? "No name provided",
+                    name: entry.name ?? String(localized: "No name provided"),
                     layout: ContactImageLayout(rawValue: entry.layout ?? "Default") ?? .default,
                     ring: ContactImageLargeRing(rawValue: entry.ring ?? "Hidden") ?? .none,
                     primary: ContactImageValue(rawValue: entry.primary ?? "Glucose Reading") ?? .glucose,

+ 2 - 2
Trio/Sources/APS/Storage/GlucoseStorage.swift

@@ -538,9 +538,9 @@ enum GlucoseAlarm {
     var displayName: String {
         switch self {
         case .high:
-            return NSLocalizedString("LOWALERT!", comment: "LOWALERT!")
+            return String(localized: "LOWALERT!", comment: "LOWALERT!")
         case .low:
-            return NSLocalizedString("HIGHALERT!", comment: "HIGHALERT!")
+            return String(localized: "HIGHALERT!", comment: "HIGHALERT!")
         }
     }
 }

+ 3 - 3
Trio/Sources/APS/Storage/OverrideStorage.swift

@@ -210,7 +210,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: OverrideStored.self,
             onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout,
+            predicate: NSPredicate.lastActiveAdjustmentNotYetUploadedToNightscout,
             key: "date",
             ascending: false
         )
@@ -225,7 +225,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
                     eventType: OverrideStored.EventType.nsExercise,
                     createdAt: override.date ?? Date(),
                     enteredBy: NightscoutExercise.local,
-                    notes: override.name ?? "Custom Override",
+                    notes: override.name ?? String(localized: "Custom Override"),
                     id: UUID(uuidString: override.id ?? UUID().uuidString)
                 )
             }
@@ -256,7 +256,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
                     eventType: OverrideStored.EventType.nsExercise,
                     createdAt: (overrideRun.startDate ?? overrideRun.override?.date) ?? Date(),
                     enteredBy: NightscoutExercise.local,
-                    notes: overrideRun.name ?? "Custom Override",
+                    notes: overrideRun.name ?? String(localized: "Custom Override"),
                     id: overrideRun.id
                 )
             }

+ 4 - 2
Trio/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -94,7 +94,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
 
                         let newPumpEvent = PumpEventStored(context: self.context)
                         newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
+                        // restrict entry to now or past
+                        newPumpEvent.timestamp = event.date > Date() ? Date() : event.date
                         newPumpEvent.type = PumpEvent.bolus.rawValue
                         newPumpEvent.isUploadedToNS = false
                         newPumpEvent.isUploadedToHealth = false
@@ -232,7 +233,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
             // create pump event
             let newPumpEvent = PumpEventStored(context: self.context)
             newPumpEvent.id = UUID().uuidString
-            newPumpEvent.timestamp = timestamp
+            // restrict entry to now or past
+            newPumpEvent.timestamp = timestamp > Date() ? Date() : timestamp
             newPumpEvent.type = PumpEvent.bolus.rawValue
             newPumpEvent.isUploadedToNS = false
             newPumpEvent.isUploadedToHealth = false

+ 3 - 3
Trio/Sources/APS/Storage/TempTargetsStorage.swift

@@ -14,7 +14,7 @@ protocol TempTargetsStorage {
     func fetchScheduledTempTargets() async -> [NSManagedObjectID]
     func fetchScheduledTempTarget(for targetDate: Date) async -> [NSManagedObjectID]
     func copyRunningTempTarget(_ tempTarget: TempTargetStored) async -> NSManagedObjectID
-    func deleteOverridePreset(_ objectID: NSManagedObjectID) async
+    func deleteTempTargetPreset(_ objectID: NSManagedObjectID) async
     func loadLatestTempTargetConfigurations(fetchLimit: Int) async -> [NSManagedObjectID]
     func syncDate() -> Date
     func recent() -> [TempTarget]
@@ -216,7 +216,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
         return newTempTarget.objectID
     }
 
-    @MainActor func deleteOverridePreset(_ objectID: NSManagedObjectID) async {
+    @MainActor func deleteTempTargetPreset(_ objectID: NSManagedObjectID) async {
         await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
     }
 
@@ -246,7 +246,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: TempTargetStored.self,
             onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout, // TODO: create adjustment predicate (OR+TT)
+            predicate: NSPredicate.lastActiveAdjustmentNotYetUploadedToNightscout,
             key: "date",
             ascending: false
         )

+ 22 - 7
Trio/Sources/Application/TrioApp.swift

@@ -13,7 +13,7 @@ import Swinject
     // Read the color scheme preference from UserDefaults; defaults to system default setting
     @AppStorage("colorSchemePreference") private var colorSchemePreference: ColorSchemeOption = .systemDefault
 
-    let coreDataStack = CoreDataStack.shared
+    let coreDataStack: CoreDataStack
 
     @State private var appState = AppState()
 
@@ -68,14 +68,29 @@ import Swinject
             "Trio Started: v\(Bundle.main.releaseVersionNumber ?? "")(\(Bundle.main.buildVersionNumber ?? "")) [buildDate: \(String(describing: BuildDetails.default.buildDate()))] [buildExpires: \(String(describing: BuildDetails.default.calculateExpirationDate()))]"
         )
 
-        // Load services
-        loadServices()
+        // Setup up the Core Data Stack
+        coreDataStack = CoreDataStack.shared
 
-        // Fix bug in iOS 18 related to the translucent tab bar
-        configureTabBarAppearance()
+        do {
+            // Explicitly initialize Core Data Stacak
+            try coreDataStack.initializeStack()
+
+            // Load services
+            loadServices()
+
+            // Fix bug in iOS 18 related to the translucent tab bar
+            configureTabBarAppearance()
 
-        // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
-        cleanupOldData()
+            // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
+            cleanupOldData()
+        } catch {
+            debug(
+                .coreData,
+                "Failed to initialize Core Data Stack: \(error.localizedDescription)"
+            )
+            // Handle initialization failure
+            fatalError("Core Data Stack initialization failed: \(error.localizedDescription)")
+        }
     }
 
     var body: some Scene {

+ 4 - 4
Trio/Sources/Helpers/BuildDetails.swift

@@ -27,8 +27,8 @@ class BuildDetails {
     }
 
     var branchAndSha: String {
-        let branch = dict["com-trio-branch"] as? String ?? "Unknown"
-        let sha = dict["com-trio-commit-sha"] as? String ?? "Unknown"
+        let branch = dict["com-trio-branch"] as? String ?? String(localized: "Unknown")
+        let sha = dict["com-trio-commit-sha"] as? String ?? String(localized: "Unknown")
         return "\(branch) \(sha)"
     }
 
@@ -75,9 +75,9 @@ class BuildDetails {
     // Expiration header based on build type
     var expirationHeaderString: String {
         if isTestFlightBuild() {
-            return "Beta (TestFlight) Expires"
+            return String(localized: "Beta (TestFlight) Expires")
         } else {
-            return "App Expires"
+            return String(localized: "App Expires")
         }
     }
 }

+ 15 - 0
Trio/Sources/Helpers/CGMOptions.swift

@@ -0,0 +1,15 @@
+let cgmOptions: [CGMOption] = [
+    CGMOption(name: "Dexcom G5", predicate: { $0.type == .plugin && $0.displayName.contains("G5") }),
+    CGMOption(name: "Dexcom G6 / ONE", predicate: { $0.type == .plugin && $0.displayName.contains("G6") }),
+    CGMOption(name: "Dexcom G7 / ONE+", predicate: { $0.type == .plugin && $0.displayName.contains("G7") }),
+    CGMOption(name: "Dexcom Share", predicate: { $0.type == .plugin && $0.displayName.contains("Dexcom Share") }),
+    CGMOption(name: "FreeStyle Libre", predicate: { $0.type == .plugin && $0.displayName == "FreeStyle Libre" }),
+    CGMOption(
+        name: "FreeStyle Libre Demo",
+        predicate: { $0.type == .plugin && $0.displayName == "FreeStyle Libre Demo" }
+    ),
+    CGMOption(name: "Glucose Simulator", predicate: { $0.type == .simulator }),
+    CGMOption(name: "Medtronic Enlite", predicate: { $0.type == .enlite }),
+    CGMOption(name: "Nightscout as CGM", predicate: { $0.type == .nightscout }),
+    CGMOption(name: "xDrip4iOS", predicate: { $0.type == .xdrip })
+]

+ 4 - 4
Trio/Sources/Helpers/HKUnit.swift

@@ -44,13 +44,13 @@ extension HKUnit {
 
     var localizedShortUnitString: String {
         if self == HKUnit.millimolesPerLiter {
-            return NSLocalizedString("mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
+            return String(localized: "mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
         } else if self == .milligramsPerDeciliter {
-            return NSLocalizedString("mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
+            return String(localized: "mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
         } else if self == .internationalUnit() {
-            return NSLocalizedString("U", comment: "The short unit display string for international units of insulin")
+            return String(localized: "U", comment: "The short unit display string for international units of insulin")
         } else if self == .gram() {
-            return NSLocalizedString("g", comment: "The short unit display string for grams")
+            return String(localized: "g", comment: "The short unit display string for grams")
         } else {
             return String(describing: self)
         }

File diff suppressed because it is too large
+ 180467 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/ar.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 1917
Trio/Sources/Localizations/Main/ca.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2184
Trio/Sources/Localizations/Main/da.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2229
Trio/Sources/Localizations/Main/de.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2231
Trio/Sources/Localizations/Main/en.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2228
Trio/Sources/Localizations/Main/es.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2229
Trio/Sources/Localizations/Main/fi.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2220
Trio/Sources/Localizations/Main/fr.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/he.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2178
Trio/Sources/Localizations/Main/hu.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2225
Trio/Sources/Localizations/Main/it.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/nb.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2231
Trio/Sources/Localizations/Main/nl.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2225
Trio/Sources/Localizations/Main/pl.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2226
Trio/Sources/Localizations/Main/ru.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2178
Trio/Sources/Localizations/Main/sk.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/sv.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2227
Trio/Sources/Localizations/Main/tr.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2223
Trio/Sources/Localizations/Main/uk.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2180
Trio/Sources/Localizations/Main/vi.lproj/Localizable.strings


File diff suppressed because it is too large
+ 0 - 2225
Trio/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 4 - 0
Trio/Sources/Logger/Logger.swift

@@ -115,6 +115,7 @@ final class Logger {
     static let remoteControl = Logger(category: .remoteControl, reporter: baseReporter)
     static let bolusState = Logger(category: .bolusState, reporter: baseReporter)
     static let watchManager = Logger(category: .watchManager, reporter: baseReporter)
+    static let coreData = Logger(category: .coreData, reporter: baseReporter)
 
     enum Category: String {
         case `default`
@@ -127,6 +128,7 @@ final class Logger {
         case remoteControl
         case bolusState
         case watchManager
+        case coreData
 
         var name: String {
             rawValue.capitalizingFirstLetter()
@@ -144,6 +146,7 @@ final class Logger {
             case .remoteControl: return .remoteControl
             case .bolusState: return .bolusState
             case .watchManager: return .watchManager
+            case .coreData: return .coreData
             }
         }
 
@@ -154,6 +157,7 @@ final class Logger {
             case .apsManager,
                  .bolusState,
                  .businessLogic,
+                 .coreData,
                  .deviceManager,
                  .nightscout,
                  .openAPS,

+ 16 - 16
Trio/Sources/Models/ContactTrickEntry.swift

@@ -130,23 +130,23 @@ enum ContactImageValue: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .none:
-            return NSLocalizedString("None", comment: "")
+            return String(localized: "None", comment: "")
         case .glucose:
-            return NSLocalizedString("Glucose Reading", comment: "")
+            return String(localized: "Glucose Reading", comment: "")
         case .eventualBG:
-            return NSLocalizedString("Eventual Glucose", comment: "")
+            return String(localized: "Eventual Glucose", comment: "")
         case .delta:
-            return NSLocalizedString("Glucose Delta", comment: "")
+            return String(localized: "Glucose Delta", comment: "")
         case .trend:
-            return NSLocalizedString("Glucose Trend", comment: "")
+            return String(localized: "Glucose Trend", comment: "")
         case .lastLoopDate:
-            return NSLocalizedString("Last Loop Time", comment: "")
+            return String(localized: "Last Loop Time", comment: "")
         case .cob:
-            return NSLocalizedString("COB", comment: "")
+            return String(localized: "COB", comment: "")
         case .iob:
-            return NSLocalizedString("IOB", comment: "")
+            return String(localized: "IOB", comment: "")
         case .ring:
-            return NSLocalizedString("Loop Status", comment: "")
+            return String(localized: "Loop Status", comment: "")
         }
     }
 }
@@ -159,9 +159,9 @@ enum ContactImageLayout: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .default:
-            return NSLocalizedString("Default", comment: "")
+            return String(localized: "Default", comment: "")
         case .split:
-            return NSLocalizedString("Split", comment: "")
+            return String(localized: "Split", comment: "")
         }
     }
 }
@@ -178,15 +178,15 @@ enum ContactImageLargeRing: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .none:
-            return NSLocalizedString("Hidden", comment: "")
+            return String(localized: "Hidden", comment: "")
         case .loop:
-            return NSLocalizedString("Loop Status", comment: "")
+            return String(localized: "Loop Status", comment: "")
 //        case .iob:
-//            return NSLocalizedString("Insulin on Board (IOB)", comment: "")
+//            return String(localized: "Insulin on Board (IOB)", comment: "")
 //        case .cob:
-//            return NSLocalizedString("Carbs on Board (COB)", comment: "")
+//            return String(localized: "Carbs on Board (COB)", comment: "")
 //        case .iobcob:
-//            return NSLocalizedString("IOB + COB", comment: "")
+//            return String(localized: "IOB + COB", comment: "")
         }
     }
 }

+ 1 - 0
Trio/Sources/Models/DecimalPickerSettings.swift

@@ -77,6 +77,7 @@ struct DecimalPickerSettings {
         type: PickerSetting.PickerSettingType.glucose
     )
     var maxCOB = PickerSetting(value: 120, step: 5, min: 0, max: 300, type: PickerSetting.PickerSettingType.gram)
+    var maxMealAbsorptionTime = PickerSetting(value: 6, step: 1, min: 4, max: 10, type: PickerSetting.PickerSettingType.hour)
     var min5mCarbimpact = PickerSetting(value: 8, step: 1, min: 1, max: 20, type: PickerSetting.PickerSettingType.glucose)
     var remainingCarbsFraction = PickerSetting(
         value: 1.0,

+ 2 - 2
Trio/Sources/Models/ForecastDisplayType.swift

@@ -7,10 +7,10 @@ enum ForecastDisplayType: String, JSON, CaseIterable, Identifiable, Codable, Has
     var displayName: String {
         switch self {
         case .cone:
-            return NSLocalizedString("Cone", comment: "")
+            return String(localized: "Cone", comment: "")
 
         case .lines:
-            return NSLocalizedString("Lines", comment: "")
+            return String(localized: "Lines", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/HbA1cDisplayUnit.swift

@@ -8,9 +8,9 @@ enum HbA1cDisplayUnit: String, JSON, CaseIterable, Identifiable, Codable, Hashab
     var displayName: String {
         switch self {
         case .percent:
-            return NSLocalizedString("Percent", comment: "")
+            return String(localized: "Percent", comment: "")
         case .mmolMol:
-            return NSLocalizedString("mmol/mol", comment: "")
+            return String(localized: "mmol/mol", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/LockScreenView.swift

@@ -7,9 +7,9 @@ enum LockScreenView: String, JSON, CaseIterable, Identifiable, Codable, Hashable
     var displayName: String {
         switch self {
         case .simple:
-            return NSLocalizedString("Simple", comment: "")
+            return String(localized: "Simple", comment: "")
         case .detailed:
-            return NSLocalizedString("Detailed", comment: "")
+            return String(localized: "Detailed", comment: "")
         }
     }
 }

+ 6 - 0
Trio/Sources/Models/Preferences.swift

@@ -16,6 +16,7 @@ struct Preferences: JSON, Equatable {
     var exerciseMode: Bool = false
     var halfBasalExerciseTarget: Decimal = 160
     var maxCOB: Decimal = 120
+    var maxMealAbsorptionTime: Decimal = 6
     var wideBGTargetRange: Bool = false
     var skipNeutralTemps: Bool = false
     var unsuspendIfNoTemp: Bool = false
@@ -72,6 +73,7 @@ extension Preferences {
         case exerciseMode = "exercise_mode"
         case halfBasalExerciseTarget = "half_basal_exercise_target"
         case maxCOB
+        case maxMealAbsorptionTime
         case wideBGTargetRange = "wide_bg_target_range"
         case skipNeutralTemps = "skip_neutral_temps"
         case unsuspendIfNoTemp = "unsuspend_if_no_temp"
@@ -184,6 +186,10 @@ extension Preferences: Decodable {
             preferences.maxCOB = maxCOB
         }
 
+        if let maxMealAbsorptionTime = try? container.decode(Decimal.self, forKey: .maxMealAbsorptionTime) {
+            preferences.maxMealAbsorptionTime = maxMealAbsorptionTime
+        }
+
         if let wideBGTargetRange = try? container.decode(Bool.self, forKey: .wideBGTargetRange) {
             preferences.wideBGTargetRange = wideBGTargetRange
         }

+ 2 - 2
Trio/Sources/Models/TimeInRangeChartStyle.swift

@@ -8,9 +8,9 @@ enum TimeInRangeChartStyle: String, JSON, CaseIterable, Identifiable, Codable, H
     var displayName: String {
         switch self {
         case .vertical:
-            return NSLocalizedString("Vertical", comment: "")
+            return String(localized: "Vertical", comment: "")
         case .horizontal:
-            return NSLocalizedString("Horizontal", comment: "")
+            return String(localized: "Horizontal", comment: "")
         }
     }
 }

+ 0 - 0
Trio/Sources/Models/TotalInsulinDisplayType.swift


Some files were not shown because too many files changed in this diff