Переглянути джерело

Release 1.5.0 (#27)

* Hide target slider when ratio 1

* Filter out missing CGM readings from TIR (when older than 30 minutes)

* Watch App Colour matching. Now same colours as in iPhone app. 

* Waiter's Notepad clean up and enumerate for Meal presets

* Meal presets. Several bug fixes.

* Profile override adaptive UI. Visualise when using extreme overrides. Improve override summation in confirmation  alert.

* Overrides. Bug fixes.

* Info Panel updates.
Improve original TT format in info panel. Less dead space and less code.
Add % for TTs in info panel (currently only for presets or enacted with sliders).
Remove "Perpetual" to avoid clutter in info panel.

* Bug fixes for TTs.

* Remove delay for added variables,  for instance for weighted average.

* Reduce time for compiling with code changes in HomeRootView. Will fix error when building in GitHub. Thank you, @antoinekh

* Use BackgroundContext again for APSManager...

* Bump version
Jon B Mårtensson 3 роки тому
батько
коміт
91fc844600

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = iAPS
-APP_VERSION = 1.3.0
+APP_VERSION = 1.5.0
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 DEVELOPER_TEAM = ##TEAM_ID##

Різницю між файлами не показано, бо вона завелика
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


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

@@ -81,8 +81,8 @@ final class BaseAPSManager: APSManager, Injectable {
         }
     }
 
-    // let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
-    let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
+    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+    // let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
 
     private var openAPS: OpenAPS!
 
@@ -702,232 +702,10 @@ final class BaseAPSManager: APSManager, Injectable {
                 }
             }
             nightscout.uploadStatus()
-            // Update the TDD value
-            tdd(enacted_: enacted)
-            // Update statistics
             statistics()
         }
     }
 
-    private func tdd(enacted_: Suggestion) {
-        let tddStartedAt = Date()
-        let preferences = settingsManager.preferences
-        let currentTDD = enacted_.tdd ?? 0
-
-        var booleanArray = [TempTargetsSlider]()
-        var overrideArray = [Override]()
-        var tempTargetsArray = [TempTargets]()
-        var isPercentageEnabled = false
-        var useOverride = false
-        var overridePercentage: Decimal = 100
-        var duration: Decimal = 0
-        var unlimited: Bool = false
-        var newDuration: Decimal = 0
-        var hbtSetting: Decimal = 160
-
-        if currentTDD > 0 {
-            let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
-            let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
-
-            var uniqEvents = [TDD]()
-            var total: Decimal = 0
-            var totalAmount: Decimal = 0
-            var indeces: Int = 0
-            var nrOfIndeces: Int = 0
-
-            coredataContext.performAndWait {
-                let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
-                requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
-                let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
-                requestTDD.sortDescriptors = [sortTDD]
-                try? uniqEvents = coredataContext.fetch(requestTDD)
-
-                let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest<TempTargetsSlider>
-                let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
-                requestIsEnbled.sortDescriptors = [sortIsEnabled]
-                // requestIsEnbled.fetchLimit = 1
-                try? booleanArray = coredataContext.fetch(requestIsEnbled)
-
-                let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
-                let sortOverride = NSSortDescriptor(key: "date", ascending: false)
-                requestOverrides.sortDescriptors = [sortOverride]
-                requestOverrides.fetchLimit = 1
-                try? overrideArray = coredataContext.fetch(requestOverrides)
-
-                let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest<TempTargets>
-                let sortTT = NSSortDescriptor(key: "date", ascending: false)
-                requestTempTargets.sortDescriptors = [sortTT]
-                requestTempTargets.fetchLimit = 1
-                try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
-
-                total = uniqEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
-                indeces = uniqEvents.count
-                // Only fetch once. Use same (previous) fetch
-                let twoHoursArray = uniqEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
-                nrOfIndeces = twoHoursArray.count
-                totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
-            }
-
-            if indeces == 0 {
-                indeces = 1
-            }
-            if nrOfIndeces == 0 {
-                nrOfIndeces = 1
-            }
-
-            let average2hours = totalAmount / Decimal(nrOfIndeces)
-            let average14 = total / Decimal(indeces)
-            let weight = preferences.weightPercentage
-            let weighted_average = weight * average2hours + (1 - weight) * average14
-
-            isPercentageEnabled = booleanArray.first?.enabled ?? false
-            useOverride = overrideArray.first?.enabled ?? false
-            overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
-            unlimited = overrideArray.first?.indefinite ?? true
-            hbtSetting = Decimal(booleanArray.first?.hbt ?? 160)
-
-            if useOverride {
-                duration = (overrideArray.first?.duration ?? 0) as Decimal
-                let addedMinutes = Int(duration)
-                let date = overrideArray.first?.date ?? Date()
-                if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
-                   !unlimited
-                { useOverride = false }
-
-                newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
-            }
-
-            if newDuration < 0 {
-                newDuration = 0
-            } else { duration = newDuration }
-
-            if !useOverride {
-                unlimited = true
-                overridePercentage = 100
-                duration = 0
-            }
-
-            if tempTargetsArray.first?.active ?? false || booleanArray.first?.enabled ?? false {
-                var duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
-                var hbt = tempTargetsArray.first?.hbt ?? 160
-                if booleanArray.first?.enabled ?? false, !(booleanArray.first?.isPreset ?? false) {
-                    duration_ = Int(truncating: booleanArray.first?.duration ?? 0)
-                    hbt = booleanArray.first?.hbt ?? 160
-                }
-                let startDate = tempTargetsArray.first?.startDate ?? Date()
-                let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
-                let dd = durationPlusStart.timeIntervalSinceNow.minutes
-                if dd > 0 {
-                    hbtSetting = Decimal(hbt)
-                    isPercentageEnabled = true
-                } else { isPercentageEnabled = false }
-            }
-
-            let averages = Oref2_variables(
-                average_total_data: roundDecimal(average14, 1),
-                weightedAverage: roundDecimal(weighted_average, 1),
-                past2hoursAverage: roundDecimal(average2hours, 1),
-                date: Date(),
-                isEnabled: isPercentageEnabled,
-                overridePercentage: overridePercentage,
-                useOverride: useOverride,
-                duration: duration,
-                unlimited: unlimited,
-                hbt: hbtSetting
-            )
-            storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
-
-            print("Test time of TDD: \(-1 * tddStartedAt.timeIntervalSinceNow) s")
-        } else {
-            coredataContext.performAndWait {
-                let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest<TempTargetsSlider>
-                let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
-                requestIsEnbled.sortDescriptors = [sortIsEnabled]
-                try? booleanArray = coredataContext.fetch(requestIsEnbled)
-
-                let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
-                let sortOverride = NSSortDescriptor(key: "date", ascending: false)
-                requestOverrides.sortDescriptors = [sortOverride]
-                requestOverrides.fetchLimit = 1
-                try? overrideArray = coredataContext.fetch(requestOverrides)
-
-                let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest<TempTargets>
-                let sortTT = NSSortDescriptor(key: "date", ascending: false)
-                requestTempTargets.sortDescriptors = [sortTT]
-                requestTempTargets.fetchLimit = 1
-                try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
-            }
-
-            isPercentageEnabled = booleanArray.first?.enabled ?? false
-            useOverride = overrideArray.first?.enabled ?? false
-            overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
-            unlimited = overrideArray.first?.indefinite ?? true
-            hbtSetting = Decimal(booleanArray.first?.hbt ?? 160)
-
-            if useOverride {
-                duration = (overrideArray.first?.duration ?? 0) as Decimal
-                let addedMinutes = Int(duration)
-                let date = overrideArray.first?.date ?? Date()
-                if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
-                   !unlimited
-                { useOverride = false }
-
-                newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
-            }
-
-            if newDuration < 0 {
-                newDuration = 0
-            } else { duration = newDuration }
-
-            if !useOverride {
-                unlimited = true
-                overridePercentage = 100
-                duration = 0
-            }
-
-            if tempTargetsArray.first?.active ?? false || booleanArray.first?.enabled ?? false {
-                var duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
-                var hbt = tempTargetsArray.first?.hbt ?? 160
-                if booleanArray.first?.enabled ?? false, !(booleanArray.first?.isPreset ?? false) {
-                    duration_ = Int(truncating: booleanArray.first?.duration ?? 0)
-                    hbt = booleanArray.first?.hbt ?? 160
-                }
-                let startDate = tempTargetsArray.first?.startDate ?? Date()
-                let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
-                let dd = durationPlusStart.timeIntervalSinceNow.minutes
-
-                if dd > 0 {
-                    hbtSetting = Decimal(hbt)
-                    isPercentageEnabled = true
-                } else { isPercentageEnabled = false }
-            }
-
-            let averages = Oref2_variables(
-                average_total_data: 0,
-                weightedAverage: 1,
-                past2hoursAverage: 0,
-                date: Date(),
-                isEnabled: isPercentageEnabled,
-                overridePercentage: overridePercentage,
-                useOverride: useOverride,
-                duration: duration,
-                unlimited: unlimited,
-                hbt: hbtSetting
-            )
-            storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
-        }
-
-        coredataContext.performAndWait {
-            let saveNewUseOverride = Override(context: self.coredataContext)
-            saveNewUseOverride.date = Date()
-            saveNewUseOverride.enabled = useOverride
-            saveNewUseOverride.percentage = Double(overridePercentage)
-            saveNewUseOverride.duration = newDuration as NSDecimalNumber
-            saveNewUseOverride.indefinite = unlimited
-            try? self.coredataContext.save()
-        }
-    }
-
     private func roundDecimal(_ decimal: Decimal, _ digits: Double) -> Decimal {
         let rounded = round(Double(decimal) * pow(10, digits)) / pow(10, digits)
         return Decimal(rounded)
@@ -1258,9 +1036,10 @@ final class BaseAPSManager: APSManager, Injectable {
                     lastIndex = true
                 }
                 if array[i].bg_ < Double(hypoLimit), !lastIndex {
-                    timeInHypo += (currentTime - previousTime).timeInterval
+                    // Exclude duration between CGM readings which are more than 30 minutes
+                    timeInHypo += min((currentTime - previousTime).timeInterval, 30.minutes.timeInterval)
                 } else if array[i].bg_ >= Double(hyperLimit), !lastIndex {
-                    timeInHyper += (currentTime - previousTime).timeInterval
+                    timeInHyper += min((currentTime - previousTime).timeInterval, 30.minutes.timeInterval)
                 }
             }
             if timeInHypo == 0.0 {

+ 181 - 3
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -9,7 +9,7 @@ final class OpenAPS {
 
     private let storage: FileStorage
 
-    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+    let coredataContext = CoreDataStack.shared.persistentContainer.viewContext // newBackgroundContext()
 
     init(storage: FileStorage) {
         self.storage = storage
@@ -44,8 +44,6 @@ final class OpenAPS {
 
                 self.storage.save(meal, as: Monitor.meal)
 
-                let oref2_variables = self.loadFileFromStorage(name: OpenAPS.Monitor.oref2_variables)
-
                 // iob
                 let autosens = self.loadFileFromStorage(name: Settings.autosense)
                 let iob = self.iob(
@@ -62,6 +60,9 @@ final class OpenAPS {
 
                 let preferences = self.loadFileFromStorage(name: Settings.preferences)
 
+                // oref2
+                let oref2_variables = self.oref2()
+
                 let suggested = self.determineBasal(
                     glucose: glucose,
                     currentTemp: tempBasal,
@@ -117,6 +118,183 @@ final class OpenAPS {
         }
     }
 
+    func oref2() -> RawJSON {
+        coredataContext.performAndWait {
+            let now = Date()
+            let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
+            var hbt_ = preferences?.halfBasalExerciseTarget ?? 160
+            let wp = preferences?.weightPercentage ?? 1
+
+            let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
+            let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
+
+            var uniqueEvents = [TDD]()
+            let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
+            requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
+            let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
+            requestTDD.sortDescriptors = [sortTDD]
+            try? uniqueEvents = coredataContext.fetch(requestTDD)
+
+            var sliderArray = [TempTargetsSlider]()
+            let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest<TempTargetsSlider>
+            let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
+            requestIsEnbled.sortDescriptors = [sortIsEnabled]
+            // requestIsEnbled.fetchLimit = 1
+            try? sliderArray = coredataContext.fetch(requestIsEnbled)
+
+            var overrideArray = [Override]()
+            let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
+            let sortOverride = NSSortDescriptor(key: "date", ascending: false)
+            requestOverrides.sortDescriptors = [sortOverride]
+            requestOverrides.fetchLimit = 1
+            try? overrideArray = coredataContext.fetch(requestOverrides)
+
+            var tempTargetsArray = [TempTargets]()
+            let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest<TempTargets>
+            let sortTT = NSSortDescriptor(key: "date", ascending: false)
+            requestTempTargets.sortDescriptors = [sortTT]
+            requestTempTargets.fetchLimit = 1
+            try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
+
+            let total = uniqueEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
+            var indeces = uniqueEvents.count
+            // Only fetch once. Use same (previous) fetch
+            let twoHoursArray = uniqueEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
+            var nrOfIndeces = twoHoursArray.count
+            let totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
+
+            var temptargetActive = tempTargetsArray.first?.active ?? false
+            var isPercentageEnabled = sliderArray.first?.enabled ?? false
+
+            var useOverride = overrideArray.first?.enabled ?? false
+            var overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
+            var unlimited = overrideArray.first?.indefinite ?? true
+
+            let currentTDD = (uniqueEvents.last?.tdd ?? 0) as Decimal
+
+            if indeces == 0 {
+                indeces = 1
+            }
+            if nrOfIndeces == 0 {
+                nrOfIndeces = 1
+            }
+
+            let average2hours = totalAmount / Decimal(nrOfIndeces)
+            let average14 = total / Decimal(indeces)
+
+            let weight = wp
+            let weighted_average = weight * average2hours + (1 - weight) * average14
+
+            var duration: Decimal = 0
+            var newDuration: Decimal = 0
+
+            if useOverride {
+                duration = (overrideArray.first?.duration ?? 0) as Decimal
+                let addedMinutes = Int(duration)
+                let date = overrideArray.first?.date ?? Date()
+                if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
+                   !unlimited
+                {
+                    useOverride = false
+                    let saveToCoreData = Override(context: self.coredataContext)
+                    saveToCoreData.enabled = false
+                    saveToCoreData.date = Date()
+                    saveToCoreData.duration = 0
+                    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)
+                    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)
+                    try? self.coredataContext.save()
+                }
+            }
+
+            if newDuration < 0 {
+                newDuration = 0
+            } else {
+                duration = newDuration
+            }
+
+            if !useOverride {
+                unlimited = true
+                overridePercentage = 100
+                duration = 0
+            }
+
+            if temptargetActive /* || isPercentageEnabled */ {
+                var duration_ = 0
+                var hbt = Double(hbt_)
+                var dd = 0.0
+
+                if temptargetActive {
+                    duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
+                    hbt = tempTargetsArray.first?.hbt ?? Double(hbt_)
+                    let startDate = tempTargetsArray.first?.startDate ?? Date()
+                    let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
+                    dd = durationPlusStart.timeIntervalSinceNow.minutes
+
+                    if dd > 0.1 {
+                        hbt_ = Decimal(hbt)
+                        // isPercentageEnabled = false
+                        temptargetActive = true
+                    } else {
+                        temptargetActive = false
+                    }
+                }
+            }
+
+            if currentTDD > 0 {
+                let averages = Oref2_variables(
+                    average_total_data: average14,
+                    weightedAverage: weighted_average,
+                    past2hoursAverage: average2hours,
+                    date: Date(),
+                    isEnabled: temptargetActive,
+                    presetActive: isPercentageEnabled,
+                    overridePercentage: overridePercentage,
+                    useOverride: useOverride,
+                    duration: duration,
+                    unlimited: unlimited,
+                    hbt: hbt_
+                )
+                storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
+                print("Test time for oref2_variables: \(-now.timeIntervalSinceNow) seconds")
+                return self.loadFileFromStorage(name: Monitor.oref2_variables)
+
+            } else {
+                let averages = Oref2_variables(
+                    average_total_data: 0,
+                    weightedAverage: 1,
+                    past2hoursAverage: 0,
+                    date: Date(),
+                    isEnabled: temptargetActive,
+                    presetActive: isPercentageEnabled,
+                    overridePercentage: overridePercentage,
+                    useOverride: useOverride,
+                    duration: duration,
+                    unlimited: unlimited,
+                    hbt: hbt_
+                )
+                storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
+                return self.loadFileFromStorage(name: Monitor.oref2_variables)
+            }
+        }
+    }
+
     func autosense() -> Future<Autosens?, Never> {
         Future { promise in
             self.processQueue.async {

+ 4 - 0
FreeAPS/Sources/Models/Oref2_variables.swift

@@ -6,6 +6,7 @@ struct Oref2_variables: JSON, Equatable {
     var past2hoursAverage: Decimal
     var date: Date
     var isEnabled: Bool
+    var presetActive: Bool
     var overridePercentage: Decimal
     var useOverride: Bool
     var duration: Decimal
@@ -18,6 +19,7 @@ struct Oref2_variables: JSON, Equatable {
         past2hoursAverage: Decimal,
         date: Date,
         isEnabled: Bool,
+        presetActive: Bool,
         overridePercentage: Decimal,
         useOverride: Bool,
         duration: Decimal,
@@ -29,6 +31,7 @@ struct Oref2_variables: JSON, Equatable {
         self.past2hoursAverage = past2hoursAverage
         self.date = date
         self.isEnabled = isEnabled
+        self.presetActive = presetActive
         self.overridePercentage = overridePercentage
         self.useOverride = useOverride
         self.duration = duration
@@ -44,6 +47,7 @@ extension Oref2_variables {
         case past2hoursAverage
         case date
         case isEnabled
+        case presetActive
         case overridePercentage
         case useOverride
         case duration

+ 80 - 10
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -1,4 +1,5 @@
 import CoreData
+import Foundation
 import SwiftUI
 
 extension AddCarbs {
@@ -128,13 +129,6 @@ extension AddCarbs {
             if a != nil, summation[a ?? 0] != "" {
                 summation.remove(at: a!)
             }
-            if (selection?.carbs ?? 0) as Decimal == carbs, (selection?.fat ?? 0) as Decimal == fat,
-               (selection?.protein ?? 0) as Decimal == protein
-            {
-                carbs = 0
-                fat = 0
-                protein = 0
-            }
         }
 
         func addPresetToNewMeal() {
@@ -144,9 +138,85 @@ extension AddCarbs {
             }
         }
 
-        func fullMeal() -> [String] {
-            let filteredArray = summation.filter { !$0.isEmpty }
-            return filteredArray
+        func addNewPresetToWaitersNotepad(_ dish: String) {
+            summation.append(dish)
+        }
+
+        func addToSummation() {
+            summation.append(selection?.dish ?? "")
+        }
+
+        func waitersNotepad() -> String {
+            var filteredArray = summation.filter { !$0.isEmpty }
+
+            if carbs == 0, protein == 0, fat == 0 {
+                filteredArray = []
+            }
+
+            guard filteredArray != [] else {
+                return ""
+            }
+            var carbs_: Decimal = 0.0
+            var fat_: Decimal = 0.0
+            var protein_: Decimal = 0.0
+            var presetArray = [Presets]()
+
+            coredataContext.performAndWait {
+                let requestPresets = Presets.fetchRequest() as NSFetchRequest<Presets>
+                try? presetArray = coredataContext.fetch(requestPresets)
+            }
+            var waitersNotepad = [String]()
+            var stringValue = ""
+
+            for each in filteredArray {
+                let countedSet = NSCountedSet(array: filteredArray)
+                let count = countedSet.count(for: each)
+                if each != stringValue {
+                    waitersNotepad.append("\(count) \(each)")
+                }
+                stringValue = each
+
+                for sel in presetArray {
+                    if sel.dish == each {
+                        carbs_ += (sel.carbs)! as Decimal
+                        fat_ += (sel.fat)! as Decimal
+                        protein_ += (sel.protein)! as Decimal
+                        break
+                    }
+                }
+            }
+            let extracarbs = carbs - carbs_
+            let extraFat = fat - fat_
+            let extraProtein = protein - protein_
+            var addedString = ""
+
+            if extracarbs > 0, filteredArray.isNotEmpty {
+                addedString += "Additional carbs: \(extracarbs) "
+            } else if extracarbs < 0 { addedString += "Removed carbs: \(extracarbs) " }
+
+            if extraFat > 0, filteredArray.isNotEmpty {
+                addedString += "Additional fat: \(extraFat) "
+            } else if extraFat < 0 { addedString += "Removed fat: \(extraFat) " }
+
+            if extraProtein > 0, filteredArray.isNotEmpty {
+                addedString += "Additional protein: \(extraProtein) "
+            } else if extraProtein < 0 { addedString += "Removed protein: \(extraProtein) " }
+
+            if addedString != "" {
+                waitersNotepad.append(addedString)
+            }
+            var waitersNotepadString = ""
+
+            if waitersNotepad.count == 1 {
+                waitersNotepadString = waitersNotepad[0]
+            } else if waitersNotepad.count > 1 {
+                for each in waitersNotepad {
+                    if each != waitersNotepad.last {
+                        waitersNotepadString += " " + each + ","
+                    } else { waitersNotepadString += " " + each }
+                }
+            }
+            return waitersNotepadString
         }
     }
 }

+ 17 - 8
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -86,7 +86,7 @@ extension AddCarbs {
                     DatePicker("Date", selection: $state.date)
                 }
 
-                Section(footer: Text(state.fullMeal().description != "[]" ? state.fullMeal().description : "")) {
+                Section(footer: Text(state.waitersNotepad().description)) {
                     Button { state.add() }
                     label: { Text("Save and continue") }
                         .disabled(state.carbs <= 0 && state.fat <= 0 && state.protein <= 0)
@@ -138,7 +138,7 @@ extension AddCarbs {
                         state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
                         state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
                         state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
-                        state.summation.append(state.selection?.dish ?? "")
+                        state.addToSummation()
                     }
                 }
                 HStack {
@@ -155,6 +155,12 @@ extension AddCarbs {
                             Button("No", role: .cancel) {}
                             Button("Yes", role: .destructive) {
                                 state.deletePreset()
+
+                                state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
+                                state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
+                                state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
+
+                                state.addPresetToNewMeal()
                             }
                         }
                     )
@@ -178,14 +184,17 @@ extension AddCarbs {
                         } else { state.protein = 0 }
 
                         state.removePresetFromNewMeal()
+                        if state.carbs == 0, state.fat == 0, state.protein == 0 { state.summation = [] }
+
                     }
                     label: { Text("[ -1 ]") }
-                        .disabled(state.selection == nil || (
-                            (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
-                                .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
-                                .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
-                                .protein
-                        ))
+                        .disabled(
+                            state
+                                .selection == nil ||
+                                (
+                                    !state.summation.contains(state.selection?.dish ?? "") && (state.selection?.dish ?? "") != ""
+                                )
+                        )
                         .buttonStyle(BorderlessButtonStyle())
                         .frame(maxWidth: .infinity, alignment: .trailing)
                         .accentColor(.minus)

+ 23 - 5
FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift

@@ -38,13 +38,30 @@ extension AddTempTarget {
 
             if viewPercantage {
                 lowTarget = computeTarget()
+                coredataContext.performAndWait {
+                    let saveToCoreData = TempTargets(context: self.coredataContext)
+                    saveToCoreData.id = UUID().uuidString
+                    saveToCoreData.active = true
+                    saveToCoreData.hbt = hbt
+                    saveToCoreData.date = Date()
+                    saveToCoreData.duration = duration as NSDecimalNumber
+                    saveToCoreData.startDate = Date()
+                    try? self.coredataContext.save()
+                }
                 saveSettings = true
+            } else {
+                coredataContext.performAndWait {
+                    let saveToCoreData = TempTargets(context: coredataContext)
+                    saveToCoreData.active = false
+                    saveToCoreData.date = Date()
+                    try? coredataContext.save()
+                }
             }
             var highTarget = lowTarget
 
             if units == .mmolL, !viewPercantage {
-                lowTarget = lowTarget.asMgdL
-                highTarget = highTarget.asMgdL
+                lowTarget = Decimal(round(Double(lowTarget.asMgdL)))
+                highTarget = lowTarget
             }
 
             let entry = TempTarget(
@@ -72,6 +89,8 @@ extension AddTempTarget {
 
                 let setHBT = TempTargetsSlider(context: self.coredataContext)
                 setHBT.enabled = false
+                setHBT.date = Date()
+
                 try? self.coredataContext.save()
             }
         }
@@ -90,8 +109,8 @@ extension AddTempTarget {
             var highTarget = lowTarget
 
             if units == .mmolL, !viewPercantage {
-                lowTarget = lowTarget.asMgdL
-                highTarget = highTarget.asMgdL
+                lowTarget = Decimal(round(Double(lowTarget.asMgdL)))
+                highTarget = lowTarget
             }
 
             let entry = TempTarget(
@@ -115,7 +134,6 @@ extension AddTempTarget {
                     saveToCoreData.isPreset = true
                     saveToCoreData.enabled = true
                     saveToCoreData.hbt = hbt
-                    saveToCoreData.enabled = true
                     saveToCoreData.date = Date()
                     saveToCoreData.duration = duration as NSDecimalNumber
                     try? self.coredataContext.save()

+ 0 - 17
FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift

@@ -147,23 +147,6 @@ extension AddTempTarget {
             .navigationTitle("Enact Temp Target")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(leading: Button("Close", action: state.hideModal))
-            .onDisappear {
-                if state.viewPercantage, state.saveSettings {
-                    let isEnabledMoc = TempTargetsSlider(context: moc)
-                    isEnabledMoc.enabled = true
-                    isEnabledMoc.date = Date()
-                    isEnabledMoc.hbt = state.hbt
-                    isEnabledMoc.duration = state.duration as NSDecimalNumber
-                    isEnabledMoc.isPreset = false
-                    try? moc.save()
-                } else {
-                    let isEnabledMoc = TempTargetsSlider(context: moc)
-                    isEnabledMoc.enabled = false
-                    isEnabledMoc.date = Date()
-                    // isEnabledMoc.hbt = isEnabledArray.first?.hbt ?? 160
-                    try? moc.save()
-                }
-            }
         }
 
         private func presetView(for preset: TempTarget) -> some View {

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

@@ -54,6 +54,7 @@ extension Home {
         @Published var animatedBackground = false
         @Published var manualTempBasal = false
         @Published var smooth = false
+        @Published var maxValue: Decimal = 1.2
 
         override func subscribe() {
             setupGlucose()
@@ -85,6 +86,7 @@ extension Home {
             setStatusTitle()
             setupCurrentTempTarget()
             smooth = settingsManager.settings.smoothGlucose
+            maxValue = settingsManager.preferences.autosensMax
 
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(SuggestionObserver.self, observer: self)
@@ -351,6 +353,15 @@ extension Home {
             }
             UIApplication.shared.open(url, options: [:], completionHandler: nil)
         }
+
+        func infoPanelTTPercentage(_ hbt_: Double, _ target: Decimal) -> Decimal {
+            guard hbt_ != 0 || target != 0 else {
+                return 0
+            }
+            let c = Decimal(hbt_ - 100)
+            let ratio = min(c / (target + c - 100), maxValue)
+            return (ratio * 100)
+        }
     }
 }
 

+ 72 - 73
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -31,6 +31,16 @@ extension Home {
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
         ) var fetchedPercent: FetchedResults<Override>
 
+        @FetchRequest(
+            entity: TempTargets.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
+        ) var sliderTTpresets: FetchedResults<TempTargets>
+
+        @FetchRequest(
+            entity: TempTargetsSlider.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
+        ) var enactedSliderTT: FetchedResults<TempTargetsSlider>
+
         private var numberFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
@@ -162,93 +172,82 @@ extension Home {
             }
         }
 
+        var tempBasalString: String? {
+            guard let tempRate = state.tempRate else {
+                return nil
+            }
+            let rateString = numberFormatter.string(from: tempRate as NSNumber) ?? "0"
+            var manualBasalString = ""
+
+            if state.apsManager.isManualTempBasal {
+                manualBasalString = NSLocalizedString(
+                    " - Manual Basal ⚠️",
+                    comment: "Manual Temp basal"
+                )
+            }
+            return rateString + NSLocalizedString(" U/hr", comment: "Unit per hour with space") + manualBasalString
+        }
+
+        var tempTargetString: String? {
+            guard let tempTarget = state.tempTarget else {
+                return nil
+            }
+            let target = tempTarget.targetBottom ?? 0
+            let unitString = targetFormatter.string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber) ?? ""
+            let rawString = (tirFormatter.string(from: (tempTarget.targetBottom ?? 0) as NSNumber) ?? "") + " " + state.units
+                .rawValue
+
+            var string = ""
+            if sliderTTpresets.first?.active ?? false {
+                let hbt = sliderTTpresets.first?.hbt ?? 0
+                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
+                .units == .mmolL ? (unitString + " mmol/L" + string) : (rawString + (string == "0" ? "" : string))
+            return tempTarget.displayName + " " + percentString
+        }
+
+        var overrideString: String? {
+            guard fetchedPercent.first?.enabled ?? false else {
+                return nil
+            }
+            let percentString = "\((fetchedPercent.first?.percentage ?? 100).formatted(.number)) %"
+            let durationString = (fetchedPercent.first?.indefinite ?? false) ?
+                "" : ", " + (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min"
+
+            return percentString + durationString
+        }
+
         var infoPanel: some View {
             HStack(alignment: .center) {
                 if state.pumpSuspended {
                     Text("Pump suspended")
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
                         .padding(.leading, 8)
-                } else if let tempRate = state.tempRate {
-                    if state.apsManager.isManualTempBasal {
-                        Text(
-                            (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
-                                NSLocalizedString(" U/hr", comment: "Unit per hour with space") +
-                                NSLocalizedString(" -  Manual Basal ⚠️", comment: "Manual Temp basal")
-                        )
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
-                        .padding(.leading, 8)
-                    } else {
-                        Text(
-                            (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
-                                NSLocalizedString(" U/hr", comment: "Unit per hour with space")
-                        )
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
+                } else if let tempBasalString = tempBasalString {
+                    Text(tempBasalString)
+                        .font(.system(size: 12, weight: .bold))
+                        .foregroundColor(.insulin)
                         .padding(.leading, 8)
-                    }
                 }
 
-                if let tempTarget = state.tempTarget {
-                    Text(tempTarget.displayName).font(.caption).foregroundColor(.secondary)
-                    if state.units == .mmolL {
-                        Text(
-                            targetFormatter
-                                .string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber)!
-                        )
+                if let tempTargetString = tempTargetString {
+                    Text(tempTargetString)
                         .font(.caption)
                         .foregroundColor(.secondary)
-                        if tempTarget.targetBottom != tempTarget.targetTop {
-                            Text("-").font(.caption)
-                                .foregroundColor(.secondary)
-                            Text(
-                                targetFormatter
-                                    .string(from: (tempTarget.targetTop?.asMmolL ?? 0) as NSNumber)! +
-                                    " \(state.units.rawValue)"
-                            )
-                            .font(.caption)
-                            .foregroundColor(.secondary)
-                        } else {
-                            Text(state.units.rawValue).font(.caption)
-                                .foregroundColor(.secondary)
-                        }
-
-                    } else {
-                        Text(targetFormatter.string(from: (tempTarget.targetBottom ?? 0) as NSNumber)!)
-                            .font(.caption)
-                            .foregroundColor(.secondary)
-                        if tempTarget.targetBottom != tempTarget.targetTop {
-                            Text("-").font(.caption)
-                                .foregroundColor(.secondary)
-                            Text(
-                                targetFormatter
-                                    .string(from: (tempTarget.targetTop ?? 0) as NSNumber)! + " \(state.units.rawValue)"
-                            )
-                            .font(.caption)
-                            .foregroundColor(.secondary)
-                        } else {
-                            Text(state.units.rawValue).font(.caption)
-                                .foregroundColor(.secondary)
-                        }
-                    }
                 }
 
                 Spacer()
-
-                Text(
-                    (fetchedPercent.first?.enabled ?? false) ?
-                        "\((fetchedPercent.first?.percentage ?? 100).formatted(.number)) % " : ""
-                )
-                .font(.system(size: 12, weight: .bold))
-                .foregroundColor(.orange)
-                .padding(.trailing, 2)
-                if fetchedPercent.first?.enabled ?? false {
-                    Text(
-                        (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") == "0" ?
-                            "Perpetual" :
-                            (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min"
-                    )
-                    .font(.system(size: 12))
-                    .foregroundColor(.orange)
-                    .padding(.trailing, 8)
+                
+                if let overrideString = overrideString {
+                    Text(overrideString)
+                        .font(.system(size: 12))
+                        .foregroundColor(.orange)
+                        .padding(.trailing, 8)
                 }
 
                 if let progress = state.bolusProgress {

+ 9 - 3
FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift

@@ -52,9 +52,13 @@ extension OverrideProfilesConfig {
                                 onEditingChanged: { editing in
                                     isEditing = editing
                                 }
-                            )
+                            ).accentColor(state.percentage >= 130 ? .red : .blue)
                             Text("\(state.percentage.formatted(.number)) %")
-                                .foregroundColor(isEditing ? .orange : .blue)
+                                .foregroundColor(
+                                    state
+                                        .percentage >= 130 ? .red :
+                                        (isEditing ? .orange : .blue)
+                                )
                                 .font(.largeTitle)
                             Spacer()
                             Toggle(isOn: $state._indefinite) {
@@ -81,7 +85,9 @@ extension OverrideProfilesConfig {
                         .frame(maxWidth: .infinity, alignment: .center)
                         .controlSize(.mini)
                         .alert(
-                            "Saving this override will change your basal insulin, ISF and CR during the entire selected duration. Tapping save will start your new overide or edit your current active override.",
+                            "Selected Override:\n\n\(state.percentage.formatted(.number)) %, " +
+                                (state.duration > 0 ? "\(state.duration) min" : " infinite duration.") + "\n\n" +
+                                "Saving this override will change your basal insulin, ISF and CR during the entire selected duration. Tapping save will start your new overide or edit your current active override.",
                             isPresented: $showAlert,
                             actions: {
                                 Button("Cancel", role: .cancel) {}

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

@@ -60,7 +60,7 @@ struct CarbsView: View {
                             .renderingMode(.template)
                             .resizable()
                             .frame(width: 24, height: 24)
-                            .foregroundColor(.loopGreen)
+                            .foregroundColor(.loopYellow)
                         Text("Add Carbs ")
                     }
                 }

+ 3 - 5
FreeAPSWatch WatchKit Extension/Views/MainView.swift

@@ -102,10 +102,9 @@ struct MainView: View {
                     .scaledToFill()
                     .foregroundColor(Color.white)
                     .minimumScaleFactor(0.5)
-                Text("g").foregroundColor(.loopGreen)
+                Text("g").foregroundColor(.loopYellow)
                     .font(.caption2)
                     .scaledToFill()
-                    .foregroundColor(.loopGreen)
                     .minimumScaleFactor(0.5)
                 Spacer()
                 Text(iobFormatter.string(from: (state.iob ?? 0) as NSNumber)!)
@@ -117,7 +116,6 @@ struct MainView: View {
                 Text("U").foregroundColor(.insulin)
                     .font(.caption2)
                     .scaledToFill()
-                    .foregroundColor(.loopGreen)
                     .minimumScaleFactor(0.5)
 
                 if state.displayHR {
@@ -220,7 +218,7 @@ struct MainView: View {
                     .renderingMode(.template)
                     .resizable()
                     .frame(width: 24, height: 24)
-                    .foregroundColor(.loopGreen)
+                    .foregroundColor(.loopYellow)
             }
 
             NavigationLink(isActive: $state.isTempTargetViewActive) {
@@ -232,7 +230,7 @@ struct MainView: View {
                         .renderingMode(.template)
                         .resizable()
                         .frame(width: 24, height: 24)
-                        .foregroundColor(.loopYellow)
+                        .foregroundColor(.loopGreen)
                     if let until = state.tempTargets.compactMap(\.until).first, until > Date() {
                         Text(until, style: .timer)
                             .scaledToFill()