Jon B Mårtensson 3 лет назад
Родитель
Сommit
eb8aae81c4

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = iAPS
 APP_DISPLAY_NAME = iAPS
-APP_VERSION = 1.6.5
+APP_VERSION = 2.0.0
 APP_BUILD_NUMBER = 1
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 COPYRIGHT_NOTICE =
 DEVELOPER_TEAM = ##TEAM_ID##
 DEVELOPER_TEAM = ##TEAM_ID##

+ 30 - 3
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -5,7 +5,6 @@
         <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="average_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
     </entity>
     </entity>
     <entity name="BGmedian" representedClassName="BGmedian" syncable="YES" codeGenerationType="class">
     <entity name="BGmedian" representedClassName="BGmedian" syncable="YES" codeGenerationType="class">
@@ -14,7 +13,6 @@
         <attribute name="median_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="median_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
     </entity>
     </entity>
     <entity name="Carbohydrates" representedClassName="Carbohydrates" syncable="YES" codeGenerationType="class">
     <entity name="Carbohydrates" representedClassName="Carbohydrates" syncable="YES" codeGenerationType="class">
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0"/>
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0"/>
@@ -27,7 +25,6 @@
         <attribute name="hba1c_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="hba1c_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
     </entity>
     </entity>
     <entity name="InsulinDistribution" representedClassName="InsulinDistribution" syncable="YES" codeGenerationType="class">
     <entity name="InsulinDistribution" representedClassName="InsulinDistribution" syncable="YES" codeGenerationType="class">
         <attribute name="bolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="bolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -47,13 +44,43 @@
         <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
         <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
     </entity>
     </entity>
     <entity name="Override" representedClassName="Override" syncable="YES" codeGenerationType="class">
     <entity name="Override" representedClassName="Override" syncable="YES" codeGenerationType="class">
+        <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="end" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="id" optional="YES" attributeType="String"/>
         <attribute name="indefinite" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="indefinite" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="100" usesScalarValueType="YES"/>
         <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="100" usesScalarValueType="YES"/>
+        <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbIsOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbIsOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+        <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="100"/>
         <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="100"/>
+        <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+    </entity>
+    <entity name="OverridePresets" representedClassName="OverridePresets" syncable="YES" codeGenerationType="class">
+        <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="end" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="id" optional="YES" attributeType="String"/>
+        <attribute name="indefinite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="name" optional="YES" attributeType="String"/>
+        <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="smbIsOff" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+        <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
     </entity>
     </entity>
     <entity name="Presets" representedClassName="Presets" syncable="YES" codeGenerationType="class">
     <entity name="Presets" representedClassName="Presets" syncable="YES" codeGenerationType="class">
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


+ 0 - 5
FreeAPS/Sources/APS/APSManager.swift

@@ -757,13 +757,10 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
                 let sortCarbs = NSSortDescriptor(key: "date", ascending: true)
                 let sortCarbs = NSSortDescriptor(key: "date", ascending: true)
                 requestCarbs.sortDescriptors = [sortCarbs]
                 requestCarbs.sortDescriptors = [sortCarbs]
-
                 try? carbs = coredataContext.fetch(requestCarbs)
                 try? carbs = coredataContext.fetch(requestCarbs)
 
 
                 carbTotal = carbs.map({ carbs in carbs.carbs as? Decimal ?? 0 }).reduce(0, +)
                 carbTotal = carbs.map({ carbs in carbs.carbs as? Decimal ?? 0 }).reduce(0, +)
 
 
-                // MARK: Fetch TDD from CoreData
-
                 var tdds = [TDD]()
                 var tdds = [TDD]()
                 var currentTDD: Decimal = 0
                 var currentTDD: Decimal = 0
                 var tddTotalAverage: Decimal = 0
                 var tddTotalAverage: Decimal = 0
@@ -1073,8 +1070,6 @@ final class BaseAPSManager: APSManager, Injectable {
                     total: roundDecimal(Decimal(medianBG), 1)
                     total: roundDecimal(Decimal(medianBG), 1)
                 )
                 )
 
 
-                // MARK: Save to Median to CoreData
-
                 let saveMedianToCoreData = BGmedian(context: self.coredataContext)
                 let saveMedianToCoreData = BGmedian(context: self.coredataContext)
                 saveMedianToCoreData.date = Date()
                 saveMedianToCoreData.date = Date()
                 saveMedianToCoreData.median = median.total as NSDecimalNumber
                 saveMedianToCoreData.median = median.total as NSDecimalNumber

+ 26 - 33
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -124,6 +124,8 @@ final class OpenAPS {
             let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
             let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
             var hbt_ = preferences?.halfBasalExerciseTarget ?? 160
             var hbt_ = preferences?.halfBasalExerciseTarget ?? 160
             let wp = preferences?.weightPercentage ?? 1
             let wp = preferences?.weightPercentage ?? 1
+            let smbMinutes = (preferences?.maxSMBBasalMinutes ?? 30) as NSDecimalNumber
+            let uamMinutes = (preferences?.maxUAMSMBBasalMinutes ?? 30) as NSDecimalNumber
 
 
             let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
             let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
             let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
             let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
@@ -146,7 +148,7 @@ final class OpenAPS {
             let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
             let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
             let sortOverride = NSSortDescriptor(key: "date", ascending: false)
             let sortOverride = NSSortDescriptor(key: "date", ascending: false)
             requestOverrides.sortDescriptors = [sortOverride]
             requestOverrides.sortDescriptors = [sortOverride]
-            requestOverrides.fetchLimit = 1
+            // requestOverrides.fetchLimit = 1
             try? overrideArray = coredataContext.fetch(requestOverrides)
             try? overrideArray = coredataContext.fetch(requestOverrides)
 
 
             var tempTargetsArray = [TempTargets]()
             var tempTargetsArray = [TempTargets]()
@@ -193,6 +195,7 @@ final class OpenAPS {
             if useOverride {
             if useOverride {
                 duration = (overrideArray.first?.duration ?? 0) as Decimal
                 duration = (overrideArray.first?.duration ?? 0) as Decimal
                 overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
                 overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
+                let advancedSettings = overrideArray.first?.advancedSettings ?? false
                 let addedMinutes = Int(duration)
                 let addedMinutes = Int(duration)
                 let date = overrideArray.first?.date ?? Date()
                 let date = overrideArray.first?.date ?? Date()
                 if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
                 if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
@@ -204,38 +207,11 @@ final class OpenAPS {
                     saveToCoreData.date = Date()
                     saveToCoreData.date = Date()
                     saveToCoreData.duration = 0
                     saveToCoreData.duration = 0
                     saveToCoreData.indefinite = false
                     saveToCoreData.indefinite = false
-                    saveToCoreData.percentage = Double(overridePercentage)
-                    try? self.coredataContext.save()
-                } else if overrideArray.first?.indefinite ?? false {
-                    let saveToCoreData = Override(context: self.coredataContext)
-                    saveToCoreData.enabled = true
-                    saveToCoreData.date = Date()
-                    saveToCoreData.duration = 0
-                    saveToCoreData.indefinite = true
-                    saveToCoreData.percentage = Double(overridePercentage)
-                    saveToCoreData.target = overrideTarget as NSDecimalNumber
-                    saveToCoreData.smbIsOff = disableSMBs
-                    try? self.coredataContext.save()
-                } else {
-                    newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
-                    let saveToCoreData = Override(context: self.coredataContext)
-                    saveToCoreData.enabled = true
-                    saveToCoreData.date = Date()
-                    saveToCoreData.duration = newDuration as NSDecimalNumber
-                    saveToCoreData.indefinite = false
-                    saveToCoreData.percentage = Double(overridePercentage)
-                    saveToCoreData.target = overrideTarget as NSDecimalNumber
-                    saveToCoreData.smbIsOff = disableSMBs
+                    saveToCoreData.percentage = 100
                     try? self.coredataContext.save()
                     try? self.coredataContext.save()
                 }
                 }
             }
             }
 
 
-            if newDuration < 0 {
-                newDuration = 0
-            } else {
-                duration = newDuration
-            }
-
             if !useOverride {
             if !useOverride {
                 unlimited = true
                 unlimited = true
                 overridePercentage = 100
                 overridePercentage = 100
@@ -244,7 +220,7 @@ final class OpenAPS {
                 disableSMBs = false
                 disableSMBs = false
             }
             }
 
 
-            if temptargetActive /* || isPercentageEnabled */ {
+            if temptargetActive {
                 var duration_ = 0
                 var duration_ = 0
                 var hbt = Double(hbt_)
                 var hbt = Double(hbt_)
                 var dd = 0.0
                 var dd = 0.0
@@ -258,7 +234,6 @@ final class OpenAPS {
 
 
                     if dd > 0.1 {
                     if dd > 0.1 {
                         hbt_ = Decimal(hbt)
                         hbt_ = Decimal(hbt)
-                        // isPercentageEnabled = false
                         temptargetActive = true
                         temptargetActive = true
                     } else {
                     } else {
                         temptargetActive = false
                         temptargetActive = false
@@ -280,7 +255,16 @@ final class OpenAPS {
                     unlimited: unlimited,
                     unlimited: unlimited,
                     hbt: hbt_,
                     hbt: hbt_,
                     overrideTarget: overrideTarget,
                     overrideTarget: overrideTarget,
-                    smbIsOff: disableSMBs
+                    smbIsOff: disableSMBs,
+                    advancedSettings: overrideArray.first?.advancedSettings ?? false,
+                    isfAndCr: overrideArray.first?.isfAndCr ?? false,
+                    isf: overrideArray.first?.isf ?? false,
+                    cr: overrideArray.first?.cr ?? false,
+                    smbIsAlwaysOff: overrideArray.first?.smbIsAlwaysOff ?? false,
+                    start: (overrideArray.first?.start ?? 0) as Decimal,
+                    end: (overrideArray.first?.end ?? 0) as Decimal,
+                    smbMinutes: (overrideArray.first?.smbMinutes ?? smbMinutes) as Decimal,
+                    uamMinutes: (overrideArray.first?.uamMinutes ?? uamMinutes) as Decimal
                 )
                 )
                 storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
                 storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
                 print("Test time for oref2_variables: \(-now.timeIntervalSinceNow) seconds")
                 print("Test time for oref2_variables: \(-now.timeIntervalSinceNow) seconds")
@@ -300,7 +284,16 @@ final class OpenAPS {
                     unlimited: unlimited,
                     unlimited: unlimited,
                     hbt: hbt_,
                     hbt: hbt_,
                     overrideTarget: overrideTarget,
                     overrideTarget: overrideTarget,
-                    smbIsOff: disableSMBs
+                    smbIsOff: disableSMBs,
+                    advancedSettings: overrideArray.first?.advancedSettings ?? false,
+                    isfAndCr: overrideArray.first?.isfAndCr ?? false,
+                    isf: overrideArray.first?.isf ?? false,
+                    cr: overrideArray.first?.cr ?? false,
+                    smbIsAlwaysOff: overrideArray.first?.smbIsAlwaysOff ?? false,
+                    start: (overrideArray.first?.start ?? 0) as Decimal,
+                    end: (overrideArray.first?.end ?? 0) as Decimal,
+                    smbMinutes: (overrideArray.first?.smbMinutes ?? smbMinutes) as Decimal,
+                    uamMinutes: (overrideArray.first?.uamMinutes ?? uamMinutes) as Decimal
                 )
                 )
                 storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
                 storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
                 return self.loadFileFromStorage(name: Monitor.oref2_variables)
                 return self.loadFileFromStorage(name: Monitor.oref2_variables)

+ 6 - 0
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings

@@ -1163,6 +1163,12 @@ Enact a temp Basal or a temp target */
 "Override Profiles" = "Override Profiles";
 "Override Profiles" = "Override Profiles";
 
 
 /* */
 /* */
+"Normal " = "Normal ";
+
+/* */
+"Add / Delete" = "Add / Delete";
+
+/* */
 "Currently no Override active" = "Currently no Override active";
 "Currently no Override active" = "Currently no Override active";
 
 
 /* */
 /* */

+ 37 - 1
FreeAPS/Sources/Models/Oref2_variables.swift

@@ -14,6 +14,15 @@ struct Oref2_variables: JSON, Equatable {
     var hbt: Decimal
     var hbt: Decimal
     var overrideTarget: Decimal
     var overrideTarget: Decimal
     var smbIsOff: Bool
     var smbIsOff: Bool
+    var advancedSettings: Bool
+    var isfAndCr: Bool
+    var isf: Bool
+    var cr: Bool
+    var smbIsAlwaysOff: Bool
+    var start: Decimal
+    var end: Decimal
+    var smbMinutes: Decimal
+    var uamMinutes: Decimal
 
 
     init(
     init(
         average_total_data: Decimal,
         average_total_data: Decimal,
@@ -28,7 +37,16 @@ struct Oref2_variables: JSON, Equatable {
         unlimited: Bool,
         unlimited: Bool,
         hbt: Decimal,
         hbt: Decimal,
         overrideTarget: Decimal,
         overrideTarget: Decimal,
-        smbIsOff: Bool
+        smbIsOff: Bool,
+        advancedSettings: Bool,
+        isfAndCr: Bool,
+        isf: Bool,
+        cr: Bool,
+        smbIsAlwaysOff: Bool,
+        start: Decimal,
+        end: Decimal,
+        smbMinutes: Decimal,
+        uamMinutes: Decimal
     ) {
     ) {
         self.average_total_data = average_total_data
         self.average_total_data = average_total_data
         self.weightedAverage = weightedAverage
         self.weightedAverage = weightedAverage
@@ -43,6 +61,15 @@ struct Oref2_variables: JSON, Equatable {
         self.hbt = hbt
         self.hbt = hbt
         self.overrideTarget = overrideTarget
         self.overrideTarget = overrideTarget
         self.smbIsOff = smbIsOff
         self.smbIsOff = smbIsOff
+        self.advancedSettings = advancedSettings
+        self.isfAndCr = isfAndCr
+        self.isf = isf
+        self.cr = cr
+        self.smbIsAlwaysOff = smbIsAlwaysOff
+        self.start = start
+        self.end = end
+        self.smbMinutes = smbMinutes
+        self.uamMinutes = uamMinutes
     }
     }
 }
 }
 
 
@@ -61,5 +88,14 @@ extension Oref2_variables {
         case hbt
         case hbt
         case overrideTarget
         case overrideTarget
         case smbIsOff
         case smbIsOff
+        case advancedSettings
+        case isfAndCr
+        case isf
+        case cr
+        case smbIsAlwaysOff
+        case start
+        case end
+        case smbMinutes
+        case uamMinutes
     }
     }
 }
 }

+ 0 - 1
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -18,7 +18,6 @@ extension AddCarbs {
         @Published var summation: [String] = []
         @Published var summation: [String] = []
 
 
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
-        // @Environment(\.managedObjectContext) var moc
 
 
         override func subscribe() {
         override func subscribe() {
             subscribeSetting(\.useFPUconversion, on: $useFPUconversion) { useFPUconversion = $0 }
             subscribeSetting(\.useFPUconversion, on: $useFPUconversion) { useFPUconversion = $0 }

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

@@ -1,4 +1,5 @@
 import Combine
 import Combine
+import CoreData
 import LoopKitUI
 import LoopKitUI
 import SwiftDate
 import SwiftDate
 import SwiftUI
 import SwiftUI
@@ -10,7 +11,6 @@ extension Home {
         @Injected() var nightscoutManager: NightscoutManager!
         @Injected() var nightscoutManager: NightscoutManager!
         private let timer = DispatchTimer(timeInterval: 5)
         private let timer = DispatchTimer(timeInterval: 5)
         private(set) var filteredHours = 24
         private(set) var filteredHours = 24
-
         @Published var glucose: [BloodGlucose] = []
         @Published var glucose: [BloodGlucose] = []
         @Published var suggestion: Suggestion?
         @Published var suggestion: Suggestion?
         @Published var uploadStats = false
         @Published var uploadStats = false
@@ -59,6 +59,8 @@ extension Home {
         @Published var displayYgridLines: Bool = false
         @Published var displayYgridLines: Bool = false
         @Published var thresholdLines: Bool = false
         @Published var thresholdLines: Bool = false
 
 
+        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
+
         override func subscribe() {
         override func subscribe() {
             setupGlucose()
             setupGlucose()
             setupBasals()
             setupBasals()
@@ -201,6 +203,15 @@ extension Home {
             apsManager.cancelBolus()
             apsManager.cancelBolus()
         }
         }
 
 
+        func cancelProfile() {
+            coredataContext.perform { [self] in
+                let profiles = Override(context: self.coredataContext)
+                profiles.enabled = false
+                profiles.date = Date()
+                try? self.coredataContext.save()
+            }
+        }
+
         private func setupGlucose() {
         private func setupGlucose() {
             DispatchQueue.main.async { [weak self] in
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
                 guard let self = self else { return }

+ 104 - 23
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -10,20 +10,10 @@ extension Home {
 
 
         @StateObject var state = StateModel()
         @StateObject var state = StateModel()
         @State var isStatusPopupPresented = false
         @State var isStatusPopupPresented = false
+        @State var showCancelAlert = false
 
 
-        // Average/Median/Readings and CV/SD titles and values switches when you tap them
-        @State var averageOrMedianTitle = NSLocalizedString("Average", comment: "")
-        @State var median_ = ""
-        @State var average_ = ""
-        @State var readings = ""
-
-        @State var averageOrmedian = ""
-        @State var CV_or_SD_Title = NSLocalizedString("CV", comment: "CV")
-        @State var cv_ = ""
-        @State var sd_ = ""
-        @State var CVorSD = ""
-        // Switch between Loops and Errors when tapping in statPanel
-        @State var loopStatTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
+        @Environment(\.managedObjectContext) var moc
+        @Environment(\.colorScheme) var colorScheme
 
 
         @FetchRequest(
         @FetchRequest(
             entity: Override.entity(),
             entity: Override.entity(),
@@ -31,6 +21,13 @@ extension Home {
         ) var fetchedPercent: FetchedResults<Override>
         ) var fetchedPercent: FetchedResults<Override>
 
 
         @FetchRequest(
         @FetchRequest(
+            entity: OverridePresets.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(
+                format: "name != %@", "" as String
+            )
+        ) var fetchedProfiles: FetchedResults<OverridePresets>
+
+        @FetchRequest(
             entity: TempTargets.entity(),
             entity: TempTargets.entity(),
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
         ) var sliderTTpresets: FetchedResults<TempTargets>
         ) var sliderTTpresets: FetchedResults<TempTargets>
@@ -96,8 +93,8 @@ extension Home {
                 Spacer()
                 Spacer()
             }
             }
             .frame(maxWidth: .infinity)
             .frame(maxWidth: .infinity)
-            .padding(.top, geo.safeAreaInsets.top)
-            .padding(.bottom, 6)
+            .padding(.top, 10 + geo.safeAreaInsets.top)
+            .padding(.bottom, 10)
             .background(Color.gray.opacity(0.2))
             .background(Color.gray.opacity(0.2))
         }
         }
 
 
@@ -211,10 +208,7 @@ extension Home {
             if sliderTTpresets.first?.active ?? false {
             if sliderTTpresets.first?.active ?? false {
                 let hbt = sliderTTpresets.first?.hbt ?? 0
                 let hbt = sliderTTpresets.first?.hbt ?? 0
                 string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
                 string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
-            } /* else if enactedSliderTT.first?.enabled ?? false {
-                 let hbt = enactedSliderTT.first?.hbt ?? 0
-                 string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
-             } */
+            }
 
 
             let percentString = state
             let percentString = state
                 .units == .mmolL ? (unitString + " mmol/L" + string) : (rawString + (string == "0" ? "" : string))
                 .units == .mmolL ? (unitString + " mmol/L" + string) : (rawString + (string == "0" ? "" : string))
@@ -235,10 +229,27 @@ extension Home {
             var targetString = (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " + unit
             var targetString = (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " + unit
             if tempTargetString != nil || target == 0 { targetString = "" }
             if tempTargetString != nil || target == 0 { targetString = "" }
             percentString = percentString == "100 %" ? "" : percentString
             percentString = percentString == "100 %" ? "" : percentString
+
+            let duration = (fetchedPercent.first?.duration ?? 0) as Decimal
+            let addedMinutes = Int(duration)
+            let date = fetchedPercent.first?.date ?? Date()
+            var newDuration: Decimal = 0
+
+            if date.addingTimeInterval(addedMinutes.minutes.timeInterval) > Date() {
+                newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
+            }
+
             var durationString = indefinite ?
             var durationString = indefinite ?
-                "" : ((tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min")
-            let smbToggleString = (fetchedPercent.first?.smbIsOff ?? false) ? " \u{20e0}" : ""
+                "" : newDuration >= 1 ?
+                (newDuration.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) + " min") :
+                (
+                    newDuration > 0 ? (
+                        newDuration.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) + "min"
+                    ) :
+                        ""
+                )
 
 
+            let smbToggleString = (fetchedPercent.first?.smbIsOff ?? false) ? " \u{20e0}" : ""
             var comma1 = ", "
             var comma1 = ", "
             var comma2 = comma1
             var comma2 = comma1
             var comma3 = comma1
             var comma3 = comma1
@@ -262,6 +273,10 @@ extension Home {
             if smbToggleString == "" {
             if smbToggleString == "" {
                 comma3 = ""
                 comma3 = ""
             }
             }
+
+            if durationString == "", !indefinite {
+                return nil
+            }
             return percentString + comma1 + targetString + comma2 + durationString + comma3 + smbToggleString
             return percentString + comma1 + targetString + comma2 + durationString + comma3 + smbToggleString
         }
         }
 
 
@@ -287,9 +302,9 @@ extension Home {
                 Spacer()
                 Spacer()
 
 
                 if let overrideString = overrideString {
                 if let overrideString = overrideString {
-                    Text(overrideString)
+                    Text("👤 " + overrideString)
                         .font(.system(size: 12))
                         .font(.system(size: 12))
-                        .foregroundColor(.orange)
+                        .foregroundColor(.secondary)
                         .padding(.trailing, 8)
                         .padding(.trailing, 8)
                 }
                 }
 
 
@@ -389,6 +404,71 @@ extension Home {
             .modal(for: .dataTable, from: self)
             .modal(for: .dataTable, from: self)
         }
         }
 
 
+        @ViewBuilder private func profiles(_: GeometryProxy) -> some View {
+            let colour: Color = colorScheme == .dark ? .black : .white
+            // Rectangle().fill(colour).frame(maxHeight: 1)
+            ZStack {
+                Rectangle().fill(Color.gray.opacity(0.2)).frame(maxHeight: 40)
+                let cancel = fetchedPercent.first?.enabled ?? false
+                HStack(spacing: cancel ? 25 : 15) {
+                    Text(selectedProfile().name).foregroundColor(.secondary)
+                    if cancel, selectedProfile().isOn {
+                        Button { showCancelAlert.toggle() }
+                        label: {
+                            Image(systemName: "xmark")
+                                .foregroundStyle(.secondary)
+                        }
+                    }
+                    Button { state.showModal(for: .overrideProfilesConfig) }
+                    label: {
+                        Image(systemName: "person.3.sequence.fill")
+                            .symbolRenderingMode(.palette)
+                            .foregroundStyle(
+                                !(fetchedPercent.first?.enabled ?? false) ? .green : .cyan,
+                                !(fetchedPercent.first?.enabled ?? false) ? .cyan : .green,
+                                .purple
+                            )
+                    }
+                }
+            }
+            .alert(
+                "Return to Normal?", isPresented: $showCancelAlert,
+                actions: {
+                    Button("No", role: .cancel) {}
+                    Button("Yes", role: .destructive) {
+                        state.cancelProfile()
+                    }
+                }, message: { Text("This will change settings back to your normal profile.") }
+            )
+            Rectangle().fill(colour).frame(maxHeight: 1)
+        }
+
+        private func selectedProfile() -> (name: String, isOn: Bool) {
+            var profileString = ""
+            var display: Bool = false
+
+            let duration = (fetchedPercent.first?.duration ?? 0) as Decimal
+            let indefinite = fetchedPercent.first?.indefinite ?? false
+            let addedMinutes = Int(duration)
+            let date = fetchedPercent.first?.date ?? Date()
+            if date.addingTimeInterval(addedMinutes.minutes.timeInterval) > Date() || indefinite {
+                display.toggle()
+            }
+
+            if fetchedPercent.first?.enabled ?? false, !(fetchedPercent.first?.isPreset ?? false) {
+                profileString = NSLocalizedString("Custom Profile", comment: "Custom but unsaved Profile")
+            } else if !(fetchedPercent.first?.enabled ?? false) || !display {
+                profileString = NSLocalizedString("Normal Profile", comment: "Your normal Profile. Use a short string")
+            } else {
+                let id_ = fetchedPercent.first?.id ?? ""
+                let profile = fetchedProfiles.filter({ $0.id == id_ }).first
+                if profile != nil {
+                    profileString = profile?.name?.description ?? ""
+                }
+            }
+            return (name: profileString, isOn: display)
+        }
+
         @ViewBuilder private func bottomPanel(_ geo: GeometryProxy) -> some View {
         @ViewBuilder private func bottomPanel(_ geo: GeometryProxy) -> some View {
             ZStack {
             ZStack {
                 Rectangle().fill(Color.gray.opacity(0.2)).frame(height: 50 + geo.safeAreaInsets.bottom)
                 Rectangle().fill(Color.gray.opacity(0.2)).frame(height: 50 + geo.safeAreaInsets.bottom)
@@ -473,6 +553,7 @@ extension Home {
                     infoPanel
                     infoPanel
                     mainChart
                     mainChart
                     legendPanel
                     legendPanel
+                    profiles(geo)
                     bottomPanel(geo)
                     bottomPanel(geo)
                 }
                 }
                 .edgesIgnoringSafeArea(.vertical)
                 .edgesIgnoringSafeArea(.vertical)

+ 176 - 2
FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift

@@ -10,11 +10,29 @@ extension OverrideProfilesConfig {
         @Published var target: Decimal = 0
         @Published var target: Decimal = 0
         @Published var override_target: Bool = false
         @Published var override_target: Bool = false
         @Published var smbIsOff: Bool = false
         @Published var smbIsOff: Bool = false
+        @Published var id: String = ""
+        @Published var profileName: String = ""
+        @Published var isPreset: Bool = false
+        @Published var presets: [OverridePresets] = []
+        @Published var selection: OverridePresets?
+        @Published var isPromtPresented: Bool = false
+        @Published var advancedSettings: Bool = false
+        @Published var isfAndCr: Bool = true
+        @Published var isf: Bool = true
+        @Published var cr: Bool = true
+        @Published var smbIsAlwaysOff: Bool = false
+        @Published var start: Decimal = 0
+        @Published var end: Decimal = 23
+        @Published var smbMinutes: Decimal = 0
+        @Published var uamMinutes: Decimal = 0
 
 
         var units: GlucoseUnits = .mmolL
         var units: GlucoseUnits = .mmolL
 
 
         override func subscribe() {
         override func subscribe() {
             units = settingsManager.settings.units
             units = settingsManager.settings.units
+            smbMinutes = settingsManager.preferences.maxSMBBasalMinutes
+            uamMinutes = settingsManager.preferences.maxUAMSMBBasalMinutes
+            presets = [OverridePresets(context: coredataContext)]
         }
         }
 
 
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
@@ -25,8 +43,12 @@ extension OverrideProfilesConfig {
                 saveOverride.duration = self.duration as NSDecimalNumber
                 saveOverride.duration = self.duration as NSDecimalNumber
                 saveOverride.indefinite = self._indefinite
                 saveOverride.indefinite = self._indefinite
                 saveOverride.percentage = self.percentage
                 saveOverride.percentage = self.percentage
-                saveOverride.enabled = self.isEnabled
+                saveOverride.enabled = true
                 saveOverride.smbIsOff = self.smbIsOff
                 saveOverride.smbIsOff = self.smbIsOff
+                if self.isPreset {
+                    saveOverride.isPreset = true
+                    saveOverride.id = id
+                } else { saveOverride.isPreset = false }
                 saveOverride.date = Date()
                 saveOverride.date = Date()
                 if override_target {
                 if override_target {
                     if units == .mmolL {
                     if units == .mmolL {
@@ -34,6 +56,116 @@ extension OverrideProfilesConfig {
                     }
                     }
                     saveOverride.target = target as NSDecimalNumber
                     saveOverride.target = target as NSDecimalNumber
                 } else { saveOverride.target = 0 }
                 } else { saveOverride.target = 0 }
+
+                if advancedSettings {
+                    saveOverride.advancedSettings = true
+
+                    if !isfAndCr {
+                        saveOverride.isfAndCr = false
+                        saveOverride.isf = isf
+                        saveOverride.cr = cr
+                    } else { saveOverride.isfAndCr = true }
+                    if smbIsAlwaysOff {
+                        saveOverride.smbIsAlwaysOff = true
+                        saveOverride.start = start as NSDecimalNumber
+                        saveOverride.end = end as NSDecimalNumber
+                    } else { saveOverride.smbIsAlwaysOff = false }
+                    if smbMinutes != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        saveOverride.smbMinutes = smbMinutes as NSDecimalNumber
+                    }
+                    if uamMinutes != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        saveOverride.uamMinutes = uamMinutes as NSDecimalNumber
+                    }
+                }
+                try? self.coredataContext.save()
+            }
+        }
+
+        func savePreset() {
+            coredataContext.perform { [self] in
+                let saveOverride = OverridePresets(context: self.coredataContext)
+                saveOverride.duration = self.duration as NSDecimalNumber
+                saveOverride.indefinite = self._indefinite
+                saveOverride.percentage = self.percentage
+                saveOverride.smbIsOff = self.smbIsOff
+                saveOverride.name = self.profileName
+                id = UUID().uuidString
+                self.isPreset.toggle()
+                saveOverride.id = id
+                saveOverride.date = Date()
+                if override_target {
+                    if units == .mmolL {
+                        target = target.asMgdL
+                    }
+                    saveOverride.target = target as NSDecimalNumber
+                } else { saveOverride.target = 0 }
+
+                if advancedSettings {
+                    saveOverride.advancedSettings = true
+
+                    if !isfAndCr {
+                        saveOverride.isfAndCr = false
+                        saveOverride.isf = isf
+                        saveOverride.cr = cr
+                    } else { saveOverride.isfAndCr = true }
+                    if smbIsAlwaysOff {
+                        saveOverride.smbIsAlwaysOff = true
+                        saveOverride.start = start as NSDecimalNumber
+                        saveOverride.end = end as NSDecimalNumber
+                    } else { smbIsAlwaysOff = false }
+                    if smbMinutes != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        saveOverride.smbMinutes = smbMinutes as NSDecimalNumber
+                    }
+                    if uamMinutes != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        saveOverride.uamMinutes = uamMinutes as NSDecimalNumber
+                    }
+                }
+                try? self.coredataContext.save()
+                isPromtPresented = false
+            }
+        }
+
+        func selectProfile(id_: String) {
+            guard id_ != "" else { return }
+            coredataContext.performAndWait {
+                var profileArray = [OverridePresets]()
+                let requestProfiles = OverridePresets.fetchRequest() as NSFetchRequest<OverridePresets>
+                try? profileArray = coredataContext.fetch(requestProfiles)
+
+                guard let profile = profileArray.filter({ $0.id == id_ }).first else { return }
+
+                let saveOverride = Override(context: self.coredataContext)
+                saveOverride.duration = (profile.duration ?? 0) as NSDecimalNumber
+                saveOverride.indefinite = profile.indefinite
+                saveOverride.percentage = profile.percentage
+                saveOverride.enabled = true
+                saveOverride.smbIsOff = profile.smbIsOff
+                saveOverride.isPreset = true
+                saveOverride.date = Date()
+                saveOverride.target = profile.target
+                saveOverride.id = id_
+
+                if profile.advancedSettings {
+                    if !isfAndCr {
+                        saveOverride.isfAndCr = false
+                        saveOverride.isf = profile.isf
+                        saveOverride.cr = profile.cr
+                    } else { saveOverride.isfAndCr = true }
+                    if profile.smbIsAlwaysOff {
+                        saveOverride.smbIsAlwaysOff = true
+                        saveOverride.start = profile.start
+                        saveOverride.end = profile.end
+                    } else { saveOverride.smbIsAlwaysOff = false }
+
+                    let smb = (profile.smbMinutes ?? 30) as Decimal
+                    if smb != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        saveOverride.smbMinutes = profile.smbMinutes
+                    }
+                    let uam = (profile.uamMinutes ?? 30) as Decimal
+                    if uam != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        saveOverride.uamMinutes = profile.uamMinutes
+                    }
+                }
                 try? self.coredataContext.save()
                 try? self.coredataContext.save()
             }
             }
         }
         }
@@ -44,13 +176,37 @@ extension OverrideProfilesConfig {
                 let requestEnabled = Override.fetchRequest() as NSFetchRequest<Override>
                 let requestEnabled = Override.fetchRequest() as NSFetchRequest<Override>
                 let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
                 let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
                 requestEnabled.sortDescriptors = [sortIsEnabled]
                 requestEnabled.sortDescriptors = [sortIsEnabled]
-                requestEnabled.fetchLimit = 1
+                // requestEnabled.fetchLimit = 1
                 try? overrideArray = coredataContext.fetch(requestEnabled)
                 try? overrideArray = coredataContext.fetch(requestEnabled)
                 isEnabled = overrideArray.first?.enabled ?? false
                 isEnabled = overrideArray.first?.enabled ?? false
                 percentage = overrideArray.first?.percentage ?? 100
                 percentage = overrideArray.first?.percentage ?? 100
                 _indefinite = overrideArray.first?.indefinite ?? true
                 _indefinite = overrideArray.first?.indefinite ?? true
                 duration = (overrideArray.first?.duration ?? 0) as Decimal
                 duration = (overrideArray.first?.duration ?? 0) as Decimal
                 smbIsOff = overrideArray.first?.smbIsOff ?? false
                 smbIsOff = overrideArray.first?.smbIsOff ?? false
+                advancedSettings = overrideArray.first?.advancedSettings ?? false
+                isfAndCr = overrideArray.first?.isfAndCr ?? true
+                smbIsAlwaysOff = overrideArray.first?.smbIsAlwaysOff ?? false
+
+                if advancedSettings {
+                    if !isfAndCr {
+                        isf = overrideArray.first?.isf ?? false
+                        cr = overrideArray.first?.cr ?? false
+                    }
+                    if smbIsAlwaysOff {
+                        start = (overrideArray.first?.start ?? 0) as Decimal
+                        end = (overrideArray.first?.end ?? 0) as Decimal
+                    }
+
+                    let smb = (overrideArray.first?.smbMinutes ?? 30) as Decimal
+                    if smb != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        smbMinutes = (overrideArray.first?.smbMinutes ?? 30) as Decimal
+                    }
+                    let uam = (overrideArray.first?.uamMinutes ?? 30) as Decimal
+                    if uam != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        uamMinutes = (overrideArray.first?.uamMinutes ?? 30) as Decimal
+                    }
+                }
+
                 let overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
                 let overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
 
 
                 var newDuration = Double(duration)
                 var newDuration = Double(duration)
@@ -77,8 +233,26 @@ extension OverrideProfilesConfig {
                     target = 0
                     target = 0
                     override_target = false
                     override_target = false
                     smbIsOff = false
                     smbIsOff = false
+                    advancedSettings = false
                 }
                 }
             }
             }
         }
         }
+
+        func cancelProfile() {
+            _indefinite = true
+            isEnabled = false
+            percentage = 100
+            duration = 0
+            target = 0
+            override_target = false
+            smbIsOff = false
+            advancedSettings = false
+            coredataContext.perform { [self] in
+                let profiles = Override(context: self.coredataContext)
+                profiles.enabled = false
+                profiles.date = Date()
+                try? self.coredataContext.save()
+            }
+        }
     }
     }
 }
 }

+ 238 - 70
FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift

@@ -10,9 +10,17 @@ extension OverrideProfilesConfig {
         @State private var isEditing = false
         @State private var isEditing = false
         @State private var showAlert = false
         @State private var showAlert = false
         @State private var showingDetail = false
         @State private var showingDetail = false
-        @State private var isPresented = true
         @State private var alertSring = ""
         @State private var alertSring = ""
+
         @Environment(\.dismiss) var dismiss
         @Environment(\.dismiss) var dismiss
+        @Environment(\.managedObjectContext) var moc
+
+        @FetchRequest(
+            entity: OverridePresets.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(
+                format: "name != %@", "" as String
+            )
+        ) var fetchedProfiles: FetchedResults<OverridePresets>
 
 
         private var formatter: NumberFormatter {
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
@@ -32,79 +40,148 @@ extension OverrideProfilesConfig {
             return formatter
             return formatter
         }
         }
 
 
+        var presetPopover: some View {
+            Form {
+                Section(header: Text("Enter Profile Name")) {
+                    TextField("Name Of Profile", text: $state.profileName)
+                    Button {
+                        state.savePreset()
+                    }
+
+                    label: { Text("Save") }
+                        .disabled(
+                            state.profileName == "" ||
+                                fetchedProfiles.filter({ $0.name == state.profileName }).isNotEmpty
+                        )
+                    Button {
+                        state.isPromtPresented = false }
+                    label: { Text("Cancel") }
+                }
+            }
+        }
+
         var body: some View {
         var body: some View {
             Form {
             Form {
-                Section(
-                    header: Text("Override your Basal, ISF, CR and Target profiles"),
-                    footer: Text("" + (!state.isEnabled ? NSLocalizedString("Currently no Override active", comment: "") : ""))
-                ) {
-                    Toggle(isOn: $state.isEnabled) {
-                        Text("Override Profiles")
-                    }._onBindingChange($state.isEnabled, perform: { _ in
-                        if !state.isEnabled {
-                            state.duration = 0
-                            state.percentage = 100
-                            state._indefinite = true
-                            state.override_target = false
-                            state.saveSettings()
-                        }
-                    })
+                if state.presets.isNotEmpty {
+                    Section {
+                        ForEach(fetchedProfiles) { preset in
+                            profilesView(for: preset)
+                        }.onDelete(perform: removeProfile)
+                    }
                 }
                 }
-                if state.isEnabled {
-                    Section(
-                        header: Text("Total Insulin Adjustment"),
-                        footer: Text(
-                            "Your profile basal insulin will be adjusted with the override percentage and your profile ISF and CR will be inversly adjusted with the percentage.\n\nIf you toggle off the override every profile setting will return to normal."
-                        )
-                    ) {
-                        VStack {
-                            Slider(
-                                value: $state.percentage,
-                                in: 10 ... 200,
-                                step: 1,
-                                onEditingChanged: { editing in
-                                    isEditing = editing
-                                }
-                            ).accentColor(state.percentage >= 130 ? .red : .blue)
-                            Text("\(state.percentage.formatted(.number)) %")
-                                .foregroundColor(
-                                    state
-                                        .percentage >= 130 ? .red :
-                                        (isEditing ? .orange : .blue)
-                                )
-                                .font(.largeTitle)
-                            Spacer()
-                            Toggle(isOn: $state._indefinite) {
-                                Text("Enable indefinitely")
+                Section {
+                    VStack {
+                        Slider(
+                            value: $state.percentage,
+                            in: 10 ... 200,
+                            step: 1,
+                            onEditingChanged: { editing in
+                                isEditing = editing
                             }
                             }
+                        ).accentColor(state.percentage >= 130 ? .red : .blue)
+                        Text("\(state.percentage.formatted(.number)) %")
+                            .foregroundColor(
+                                state
+                                    .percentage >= 130 ? .red :
+                                    (isEditing ? .orange : .blue)
+                            )
+                            .font(.largeTitle)
+                        Spacer()
+                        Toggle(isOn: $state._indefinite) {
+                            Text("Enable indefinitely")
                         }
                         }
-                        if !state._indefinite {
-                            HStack {
-                                Text("Duration")
-                                DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: false)
-                                Text("minutes").foregroundColor(.secondary)
-                            }
+                    }
+                    if !state._indefinite {
+                        HStack {
+                            Text("Duration")
+                            DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: false)
+                            Text("minutes").foregroundColor(.secondary)
                         }
                         }
+                    }
 
 
+                    HStack {
+                        Toggle(isOn: $state.override_target) {
+                            Text("Override Profile Target")
+                        }
+                    }
+                    if state.override_target {
                         HStack {
                         HStack {
-                            Toggle(isOn: $state.override_target) {
-                                Text("Override Profile Target")
+                            Text("Target Glucose")
+                            DecimalTextField("0", value: $state.target, formatter: glucoseFormatter, cleanInput: false)
+                            Text(state.units.rawValue).foregroundColor(.secondary)
+                        }
+                    }
+                    HStack {
+                        Toggle(isOn: $state.advancedSettings) {
+                            Text("More options")
+                        }
+                    }
+                    if state.advancedSettings {
+                        HStack {
+                            Toggle(isOn: $state.smbIsOff) {
+                                Text("Disable SMBs")
                             }
                             }
                         }
                         }
-                        if state.override_target {
+                        HStack {
+                            Toggle(isOn: $state.smbIsAlwaysOff) {
+                                Text("Schedule when SMBs are Off")
+                            }.disabled(!state.smbIsOff)
+                        }
+                        if state.smbIsAlwaysOff {
+                            HStack {
+                                Text("First Hour SMBs are Off (24 hours)")
+                                DecimalTextField("0", value: $state.start, formatter: formatter, cleanInput: false)
+                                Text("hour").foregroundColor(.secondary)
+                            }
                             HStack {
                             HStack {
-                                Text("Target Glucose")
-                                DecimalTextField("0", value: $state.target, formatter: glucoseFormatter, cleanInput: false)
-                                Text(state.units.rawValue).foregroundColor(.secondary)
+                                Text("Last Hour SMBs are Off (24 hours)")
+                                DecimalTextField("0", value: $state.end, formatter: formatter, cleanInput: false)
+                                Text("hour").foregroundColor(.secondary)
                             }
                             }
                         }
                         }
                         HStack {
                         HStack {
-                            Toggle(isOn: $state.smbIsOff) {
-                                Text("Disable SMBs")
+                            Toggle(isOn: $state.isfAndCr) {
+                                Text("Change ISF and CR")
+                            }
+                        }
+                        if !state.isfAndCr {
+                            HStack {
+                                Toggle(isOn: $state.isf) {
+                                    Text("Change ISF")
+                                }
+                            }
+                            HStack {
+                                Toggle(isOn: $state.cr) {
+                                    Text("Change CR")
+                                }
                             }
                             }
                         }
                         }
+                        HStack {
+                            Text("SMB Minutes")
+                            let minutes = state.settingsManager.preferences.maxSMBBasalMinutes
+                            DecimalTextField(
+                                minutes.formatted(),
+                                value: $state.smbMinutes,
+                                formatter: formatter,
+                                cleanInput: false
+                            )
+                            Text("minutes").foregroundColor(.secondary)
+                        }
+                        HStack {
+                            Text("UAM SMB Minutes")
+                            let uam_minutes = state.settingsManager.preferences.maxUAMSMBBasalMinutes
+                            DecimalTextField(
+                                uam_minutes.formatted(),
+                                value: $state.uamMinutes,
+                                formatter: formatter,
+                                cleanInput: false
+                            )
+                            Text("minutes").foregroundColor(.secondary)
+                        }
+                    }
 
 
-                        Button("Save") {
+                    HStack {
+                        Button("Start new Profile") {
                             showAlert.toggle()
                             showAlert.toggle()
                             alertSring = "\(state.percentage.formatted(.number)) %, " +
                             alertSring = "\(state.percentage.formatted(.number)) %, " +
                                 (
                                 (
@@ -126,29 +203,24 @@ extension OverrideProfilesConfig {
                                 +
                                 +
                                 "\n\n"
                                 "\n\n"
                                 +
                                 +
-                                "Saving this override will change your Profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping save will start your new overide or edit your current active override."
+                                "Starting this override will change your Profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping ”Start” will start your new overide or edit your current active override."
                         }
                         }
                         .disabled(
                         .disabled(
-                            !state
-                                .isEnabled || (state.percentage == 100 && !state.override_target && !state.smbIsOff) ||
-                                (!state._indefinite && state.duration == 0 || (state.override_target && state.target == 0))
+                            (state.percentage == 100 && !state.override_target && !state.smbIsOff) ||
+                                (!state._indefinite && state.duration == 0) || (state.override_target && state.target == 0)
                         )
                         )
-                        .accentColor(.orange)
+                        // .tint(.blue)
                         .buttonStyle(BorderlessButtonStyle())
                         .buttonStyle(BorderlessButtonStyle())
                         .font(.callout)
                         .font(.callout)
-                        .frame(maxWidth: .infinity, alignment: .center)
                         .controlSize(.mini)
                         .controlSize(.mini)
                         .alert(
                         .alert(
-                            "Save Override",
+                            "Start Profile",
                             isPresented: $showAlert,
                             isPresented: $showAlert,
                             actions: {
                             actions: {
-                                Button("Cancel", role: .cancel) {}
+                                Button("Cancel", role: .cancel) { state.isEnabled = false }
                                 Button("Start Override", role: .destructive) {
                                 Button("Start Override", role: .destructive) {
-                                    if state._indefinite {
-                                        state.duration = 0
-                                    } else if state.duration == 0 {
-                                        state.isEnabled = false
-                                    }
+                                    if state._indefinite { state.duration = 0 }
+                                    state.isEnabled.toggle()
                                     state.saveSettings()
                                     state.saveSettings()
                                     dismiss()
                                     dismiss()
                                 }
                                 }
@@ -157,11 +229,107 @@ extension OverrideProfilesConfig {
                                 Text(alertSring)
                                 Text(alertSring)
                             }
                             }
                         )
                         )
+                        Button {
+                            state.isPromtPresented.toggle()
+                        }
+                        label: { Text("Save as Profile") }
+                            .tint(.orange)
+                            .frame(maxWidth: .infinity, alignment: .trailing)
+                            .buttonStyle(BorderlessButtonStyle())
+                            .controlSize(.mini)
+                            .disabled(
+                                (state.percentage == 100 && !state.override_target && !state.smbIsOff) ||
+                                    (!state._indefinite && state.duration == 0) || (state.override_target && state.target == 0)
+                            )
+                    }
+                    .popover(isPresented: $state.isPromtPresented) {
+                        presetPopover
                     }
                     }
                 }
                 }
+
+                header: { Text("Insulin") }
+                footer: {
+                    Text(
+                        "Your profile basal insulin will be adjusted with the override percentage and your profile ISF and CR will be inversly adjusted with the percentage."
+                    )
+                }
+
+                Button("Return to Normal") {
+                    state.cancelProfile()
+                    dismiss()
+                }
+                .frame(maxWidth: .infinity, alignment: .center)
+                .buttonStyle(BorderlessButtonStyle())
+                .disabled(!state.isEnabled)
+                .tint(.red)
             }
             }
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)
             .onAppear { state.savedSettings() }
             .onAppear { state.savedSettings() }
+            .navigationBarTitle("Profiles")
+            .navigationBarTitleDisplayMode(.automatic)
+            .navigationBarItems(leading: Button("Close", action: state.hideModal))
+        }
+
+        @ViewBuilder private func profilesView(for preset: OverridePresets) -> some View {
+            let target = state.units == .mmolL ? (((preset.target ?? 0) as NSDecimalNumber) as Decimal)
+                .asMmolL : (preset.target ?? 0) as Decimal
+            let duration = (preset.duration ?? 0) as Decimal
+            let name = ((preset.name ?? "") == "") || (preset.name?.isEmpty ?? true) ? "" : preset.name!
+            let percent = preset.percentage / 100
+            let perpetual = preset.indefinite
+            let durationString = perpetual ? "" : "\(formatter.string(from: duration as NSNumber)!)"
+            let scheduledSMBstring = (preset.smbIsOff && preset.smbIsAlwaysOff) ? "Scheduled SMBs" : ""
+            let smbString = (preset.smbIsOff && scheduledSMBstring == "") ? "SMBs are off" : ""
+            let targetString = target != 0 ? "\(formatter.string(from: target as NSNumber)!)" : ""
+
+            if name != "" {
+                HStack {
+                    VStack {
+                        HStack {
+                            Text(name)
+                            Spacer()
+                        }
+                        HStack(spacing: 5) {
+                            Text(percent.formatted(.percent.grouping(.never).rounded().precision(.fractionLength(0))))
+                                .foregroundColor(.secondary)
+                                .font(.caption)
+                            if targetString != "" {
+                                Text(targetString)
+                                    .foregroundColor(.secondary)
+                                    .font(.caption)
+                                Text(targetString != "" ? state.units.rawValue : "")
+                                    .foregroundColor(.secondary)
+                                    .font(.caption) }
+                            if durationString != "" {
+                                Text(durationString + (perpetual ? "" : "min"))
+                                    .foregroundColor(.secondary)
+                                    .font(.caption) }
+                            if smbString != "" { Text(smbString).foregroundColor(.secondary).font(.caption) }
+                            Text(scheduledSMBstring)
+                                .foregroundColor(.secondary)
+                                .font(.caption)
+                            Spacer()
+                        }.padding(.top, 2)
+                    }
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        state.selectProfile(id_: preset.id ?? "")
+                        state.hideModal()
+                    }
+                }
+            }
+        }
+
+        private func removeProfile(at offsets: IndexSet) {
+            for index in offsets {
+                let language = fetchedProfiles[index]
+                moc.delete(language)
+            }
+            do {
+                try moc.save()
+            } catch {
+                // To do: add error
+            }
         }
         }
     }
     }
 }
 }

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

@@ -31,7 +31,6 @@ extension Settings {
                     }
                     }
                     Text("Notifications").navigationLink(to: .notificationsConfig, from: self)
                     Text("Notifications").navigationLink(to: .notificationsConfig, from: self)
                     Text("Fat And Protein Conversion").navigationLink(to: .fpuConfig, from: self)
                     Text("Fat And Protein Conversion").navigationLink(to: .fpuConfig, from: self)
-                    Text("Profile Override").navigationLink(to: .overrideProfilesConfig, from: self)
                     Text("App Icons").navigationLink(to: .iconConfig, from: self)
                     Text("App Icons").navigationLink(to: .iconConfig, from: self)
                     Text("Statistics and Home View").navigationLink(to: .statisticsConfig, from: self)
                     Text("Statistics and Home View").navigationLink(to: .statisticsConfig, from: self)
                 }
                 }