Explorar el Código

Add a StatConfig in Services section.
Bug fix for app crash when lower limit > upper limit.
Fix formatting of default glucose limits for stat charts

Jon Mårtensson hace 3 años
padre
commit
acb4b339c6

+ 32 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -14,6 +14,10 @@
 		0F7A65FBD2CD8D6477ED4539 /* NotificationsConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E625985B47742D498CB1681A /* NotificationsConfigProvider.swift */; };
 		17A9D0899046B45E87834820 /* CREditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8D5F457B5AFF763F8CF3DF /* CREditorProvider.swift */; };
 		19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19012CDB291D2CB900FB8210 /* LoopStats.swift */; };
+		190EBCC429FF136900BA767D /* StatConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC329FF136900BA767D /* StatConfigDataFlow.swift */; };
+		190EBCC629FF138000BA767D /* StatConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC529FF138000BA767D /* StatConfigProvider.swift */; };
+		190EBCC829FF13AA00BA767D /* StatConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* StatConfigStateModel.swift */; };
+		190EBCCB29FF13CB00BA767D /* StatConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */; };
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		1967DFBE29D052C200759F30 /* Icons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBD29D052C200759F30 /* Icons.swift */; };
@@ -468,6 +472,10 @@
 		10A0C32B0DAB52726EF9B6D9 /* BolusRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusRootView.swift; sourceTree = "<group>"; };
 		12204445D7632AF09264A979 /* PreferencesEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PreferencesEditorDataFlow.swift; sourceTree = "<group>"; };
 		19012CDB291D2CB900FB8210 /* LoopStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopStats.swift; sourceTree = "<group>"; };
+		190EBCC329FF136900BA767D /* StatConfigDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatConfigDataFlow.swift; sourceTree = "<group>"; };
+		190EBCC529FF138000BA767D /* StatConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatConfigProvider.swift; sourceTree = "<group>"; };
+		190EBCC729FF13AA00BA767D /* StatConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatConfigStateModel.swift; sourceTree = "<group>"; };
+		190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatConfigRootView.swift; sourceTree = "<group>"; };
 		1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -962,6 +970,25 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		190EBCC229FF134900BA767D /* StatConfig */ = {
+			isa = PBXGroup;
+			children = (
+				190EBCC329FF136900BA767D /* StatConfigDataFlow.swift */,
+				190EBCC529FF138000BA767D /* StatConfigProvider.swift */,
+				190EBCC729FF13AA00BA767D /* StatConfigStateModel.swift */,
+				190EBCC929FF13AF00BA767D /* View */,
+			);
+			path = StatConfig;
+			sourceTree = "<group>";
+		};
+		190EBCC929FF13AF00BA767D /* View */ = {
+			isa = PBXGroup;
+			children = (
+				190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		192F0FF5276AC36D0085BE4D /* Recovered References */ = {
 			isa = PBXGroup;
 			children = (
@@ -1086,6 +1113,7 @@
 		3811DE0325C9D31700A708ED /* Modules */ = {
 			isa = PBXGroup;
 			children = (
+				190EBCC229FF134900BA767D /* StatConfig */,
 				CE94597C29E9E1CD0047C9C6 /* WatchConfig */,
 				19F95FF129F10F9C00314DDC /* Stat */,
 				19E1F7E629D0828B005C8D20 /* IconConfig */,
@@ -2477,6 +2505,7 @@
 				3811DE1825C9D40400A708ED /* Router.swift in Sources */,
 				CE7950262998056D00FA576E /* CGMSetupView.swift in Sources */,
 				38A0363B25ECF07E00FCBB52 /* GlucoseStorage.swift in Sources */,
+				190EBCC629FF138000BA767D /* StatConfigProvider.swift in Sources */,
 				38E98A2725F52C9300C0CED0 /* CollectionIssueReporter.swift in Sources */,
 				E00EEC0427368630002FF094 /* SecurityAssembly.swift in Sources */,
 				3811DEE825CA063400A708ED /* Injected.swift in Sources */,
@@ -2508,6 +2537,7 @@
 				3811DE6125C9D4D500A708ED /* ViewModifiers.swift in Sources */,
 				3811DEAC25C9D88300A708ED /* NightscoutManager.swift in Sources */,
 				CEB434E528B8FF5D00B70274 /* UIColor.swift in Sources */,
+				190EBCCB29FF13CB00BA767D /* StatConfigRootView.swift in Sources */,
 				3811DEA925C9D88300A708ED /* AppearanceManager.swift in Sources */,
 				CE7950242997D81700FA576E /* CGMSettingsView.swift in Sources */,
 				38D0B3D925EC07C400CB6E88 /* CarbsEntry.swift in Sources */,
@@ -2523,6 +2553,7 @@
 				38BF021B25E7D06400579895 /* PumpSettingsView.swift in Sources */,
 				3862CC05273D152B00BF832C /* CalibrationService.swift in Sources */,
 				3811DEEA25CA063400A708ED /* SyncAccess.swift in Sources */,
+				190EBCC829FF13AA00BA767D /* StatConfigStateModel.swift in Sources */,
 				38BF021F25E7F0DE00579895 /* DeviceDataManager.swift in Sources */,
 				38A504A425DD9C4000C5B9E8 /* UserDefaultsExtensions.swift in Sources */,
 				38FE826A25CC82DB001FF17A /* NetworkService.swift in Sources */,
@@ -2534,6 +2565,7 @@
 				38B4F3CB25E502E200E76A18 /* WeakObjectSet.swift in Sources */,
 				38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */,
 				38E98A2525F52C9300C0CED0 /* IssueReporter.swift in Sources */,
+				190EBCC429FF136900BA767D /* StatConfigDataFlow.swift in Sources */,
 				3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */,
 				3811DE4325C9D4A100A708ED /* SettingsProvider.swift in Sources */,
 				45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */,

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

@@ -21,7 +21,7 @@
     "carbsRequiredThreshold": 10,
     "useAppleHealth": false,
     "animatedBackground": false,
-    "displayStatistics": false,
+    "uploadStats": false,
     "useFPUconversion": true
     "individualAdjustmentFactor": 0.5,
     "timeCap": 8,

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

@@ -731,9 +731,10 @@ final class BaseAPSManager: APSManager, Injectable {
 
     // Add to statistics.JSON
     private func statistics() {
-        if settingsManager.settings.displayStatistics {
+        if settingsManager.settings.uploadStats {
             let now = Date()
-            guard Date().hour > 22 else {
+            let hour = Calendar.current.component(.hour, from: Date())
+            guard hour > 20 else {
                 return
             }
             coredataContext.performAndWait { [self] in
@@ -1009,8 +1010,8 @@ final class BaseAPSManager: APSManager, Injectable {
                     var i = -1
                     var lastIndex = false
                     let endIndex = array.count - 1
-                    var hypoLimit = settingsManager.settings.lowGlucose
-                    var hyperLimit = settingsManager.settings.highGlucose
+                    var hypoLimit = settingsManager.settings.low
+                    var hyperLimit = settingsManager.settings.high
                     if units == .mmolL {
                         hypoLimit = hypoLimit / 0.0555
                         hyperLimit = hyperLimit / 0.0555
@@ -1125,7 +1126,7 @@ final class BaseAPSManager: APSManager, Injectable {
                 try? self.coredataContext.save()
 
                 // Convert to user-preferred unit
-                let overrideHbA1cUnit = settingsManager.preferences.overrideHbA1cUnit
+                let overrideHbA1cUnit = settingsManager.settings.overrideHbA1cUnit
                 if units == .mmolL {
                     // Override if users sets overrideHbA1cUnit: true
                     if !overrideHbA1cUnit {

+ 16 - 6
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -20,11 +20,10 @@ struct FreeAPSSettings: JSON, Equatable {
     var glucoseNotificationsAlways: Bool = false
     var useAlarmSound: Bool = false
     var addSourceInfoToGlucoseNotifications: Bool = false
-    var lowGlucose: Decimal = 70
+    var lowGlucose: Decimal = 72
     var highGlucose: Decimal = 270
     var carbsRequiredThreshold: Decimal = 10
     var animatedBackground: Bool = false
-    var displayStatistics: Bool = false
     var useFPUconversion: Bool = true
     var individualAdjustmentFactor: Decimal = 0.5
     var timeCap: Int = 8
@@ -33,6 +32,9 @@ struct FreeAPSSettings: JSON, Equatable {
     var useAppleHealth: Bool = false
     var smoothGlucose: Bool = false
     var overrideHbA1cUnit: Bool = false
+    var high: Decimal = 145
+    var low: Decimal = 70
+    var uploadStats: Bool = false
 }
 
 extension FreeAPSSettings: Decodable {
@@ -162,14 +164,22 @@ extension FreeAPSSettings: Decodable {
             settings.animatedBackground = animatedBackground
         }
 
-        if let displayStatistics = try? container.decode(Bool.self, forKey: .displayStatistics) {
-            settings.displayStatistics = displayStatistics
-        }
-
         if let smoothGlucose = try? container.decode(Bool.self, forKey: .smoothGlucose) {
             settings.smoothGlucose = smoothGlucose
         }
 
+        if let low = try? container.decode(Decimal.self, forKey: .low) {
+            settings.low = low
+        }
+
+        if let high = try? container.decode(Decimal.self, forKey: .high) {
+            settings.high = high
+        }
+
+        if let uploadStats = try? container.decode(Bool.self, forKey: .uploadStats) {
+            settings.uploadStats = uploadStats
+        }
+
         self = settings
     }
 }

+ 0 - 2
FreeAPS/Sources/Models/Preferences.swift

@@ -53,7 +53,6 @@ struct Preferences: JSON {
     var enableSMB_high_bg_target: Decimal = 110
     var threshold_setting: Decimal = 65
     var updateInterval: Decimal = 20
-    var overrideHbA1cUnit: Bool = false
 }
 
 extension Preferences {
@@ -109,7 +108,6 @@ extension Preferences {
         case enableSMB_high_bg_target
         case threshold_setting
         case updateInterval
-        case overrideHbA1cUnit
     }
 }
 

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

@@ -13,7 +13,7 @@ extension Home {
 
         @Published var glucose: [BloodGlucose] = []
         @Published var suggestion: Suggestion?
-        @Published var displayStatistics = false
+        @Published var uploadStats = false
         @Published var enactedSuggestion: Suggestion?
         @Published var recentGlucose: BloodGlucose?
         @Published var glucoseDelta: Int?
@@ -51,8 +51,8 @@ extension Home {
         @Published var manualTempBasal = false
         @Published var smooth = false
         @Published var maxValue: Decimal = 1.2
-        @Published var lowGlucoseLine: Decimal = 70
-        @Published var highGlucoseLine: Decimal = 145
+        @Published var lowGlucose: Decimal = 4 / 0.0555
+        @Published var highGlucose: Decimal = 10 / 0.0555
         @Published var overrideUnit = false
 
         override func subscribe() {
@@ -68,7 +68,7 @@ extension Home {
             setupReservoir()
 
             suggestion = provider.suggestion
-            displayStatistics = settingsManager.settings.displayStatistics
+            uploadStats = settingsManager.settings.uploadStats
             enactedSuggestion = provider.enactedSuggestion
             units = settingsManager.settings.units
             allowManualTemp = !settingsManager.settings.closedLoop
@@ -81,9 +81,9 @@ extension Home {
             setupCurrentTempTarget()
             smooth = settingsManager.settings.smoothGlucose
             maxValue = settingsManager.preferences.autosensMax
-            lowGlucoseLine = settingsManager.settings.lowGlucose
-            highGlucoseLine = settingsManager.settings.highGlucose
-            overrideUnit = settingsManager.preferences.overrideHbA1cUnit
+            lowGlucose = settingsManager.settings.low
+            highGlucose = settingsManager.settings.high
+            overrideUnit = settingsManager.settings.overrideHbA1cUnit
 
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(SuggestionObserver.self, observer: self)
@@ -380,15 +380,15 @@ extension Home.StateModel:
 
     func settingsDidChange(_ settings: FreeAPSSettings) {
         allowManualTemp = !settings.closedLoop
-        displayStatistics = settingsManager.settings.displayStatistics
+        uploadStats = settingsManager.settings.uploadStats
         closedLoop = settingsManager.settings.closedLoop
         units = settingsManager.settings.units
         animatedBackground = settingsManager.settings.animatedBackground
         manualTempBasal = apsManager.isManualTempBasal
         smooth = settingsManager.settings.smoothGlucose
-        lowGlucoseLine = settingsManager.settings.lowGlucose
-        highGlucoseLine = settingsManager.settings.highGlucose
-        overrideUnit = settingsManager.preferences.overrideHbA1cUnit
+        lowGlucose = settingsManager.settings.low
+        highGlucose = settingsManager.settings.high
+        overrideUnit = settingsManager.settings.overrideHbA1cUnit
         setupGlucose()
     }
 

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

@@ -19,7 +19,7 @@ typealias GlucoseYRange = (minValue: Int, minY: CGFloat, maxValue: Int, maxY: CG
 struct MainChartView: View {
     private enum Config {
         static let endID = "End"
-        static let screenHours = 8
+        static let screenHours = 6
         static let basalHeight: CGFloat = 80
         static let topYPadding: CGFloat = 20
         static let bottomYPadding: CGFloat = 80
@@ -48,8 +48,8 @@ struct MainChartView: View {
     @Binding var timerDate: Date
     @Binding var units: GlucoseUnits
     @Binding var smooth: Bool
-    @Binding var highGlucoseLine: Decimal
-    @Binding var lowGlucoseLine: Decimal
+    @Binding var highGlucose: Decimal
+    @Binding var lowGlucose: Decimal
 
     @State var didAppearTrigger = false
     @State private var glucoseDots: [CGRect] = []
@@ -179,8 +179,8 @@ struct MainChartView: View {
             // horizontal limits
             let range = glucoseYRange
             let topstep = (range.maxY - range.minY) / CGFloat(range.maxValue - range.minValue) *
-                (CGFloat(range.maxValue) - CGFloat(highGlucoseLine))
-            if CGFloat(range.maxValue) > CGFloat(highGlucoseLine) {
+                (CGFloat(range.maxValue) - CGFloat(highGlucose))
+            if CGFloat(range.maxValue) > CGFloat(highGlucose) {
                 Path { path in
                     path.move(to: CGPoint(x: 0, y: range.minY + topstep))
                     path.addLine(to: CGPoint(x: fullSize.width, y: range.minY + topstep))
@@ -188,8 +188,8 @@ struct MainChartView: View {
             }
             let yrange = glucoseYRange
             let bottomstep = (yrange.maxY - yrange.minY) / CGFloat(yrange.maxValue - yrange.minValue) *
-                (CGFloat(yrange.maxValue) - CGFloat(lowGlucoseLine))
-            if CGFloat(yrange.minValue) < CGFloat(lowGlucoseLine) {
+                (CGFloat(yrange.maxValue) - CGFloat(lowGlucose))
+            if CGFloat(yrange.minValue) < CGFloat(lowGlucose) {
                 Path { path in
                     path.move(to: CGPoint(x: 0, y: yrange.minY + bottomstep))
                     path.addLine(to: CGPoint(x: fullSize.width, y: yrange.minY + bottomstep))

+ 9 - 8
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -5,8 +5,8 @@ struct CurrentGlucoseView: View {
     @Binding var delta: Int?
     @Binding var units: GlucoseUnits
     @Binding var alarm: GlucoseAlarm?
-    @Binding var lowGlucoseLine: Decimal
-    @Binding var highGlucoseLine: Decimal
+    @Binding var lowGlucose: Decimal
+    @Binding var highGlucose: Decimal
 
     private var glucoseFormatter: NumberFormatter {
         let formatter = NumberFormatter()
@@ -111,16 +111,17 @@ struct CurrentGlucoseView: View {
     var colorOfGlucose: Color {
         let whichGlucose = recentGlucose?.glucose ?? 0
 
+        guard lowGlucose < highGlucose else { return .primary }
+
         switch whichGlucose {
-        case Int(lowGlucoseLine ?? 70) + 1 ... Int(highGlucoseLine ?? 145) - 1:
-            return .loopGreen
-        case 0 ... Int(lowGlucoseLine ?? 70),
-             201...:
+        case 0 ..< Int(lowGlucose):
             return .loopRed
-        case Int(highGlucoseLine ?? 145) ... 200:
+        case Int(lowGlucose) ..< Int(highGlucose):
+            return .loopGreen
+        case Int(highGlucose)...:
             return .loopYellow
         default:
-            return .primary
+            return .loopYellow
         }
     }
 }

+ 4 - 4
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -119,8 +119,8 @@ extension Home {
                 delta: $state.glucoseDelta,
                 units: $state.units,
                 alarm: $state.alarm,
-                lowGlucoseLine: $state.lowGlucoseLine,
-                highGlucoseLine: $state.highGlucoseLine
+                lowGlucose: $state.lowGlucose,
+                highGlucose: $state.highGlucose
             )
             .onTapGesture {
                 if state.alarm == nil {
@@ -334,8 +334,8 @@ extension Home {
                     timerDate: $state.timerDate,
                     units: $state.units,
                     smooth: $state.smooth,
-                    highGlucoseLine: $state.highGlucoseLine,
-                    lowGlucoseLine: $state.lowGlucoseLine
+                    highGlucose: $state.highGlucose,
+                    lowGlucose: $state.lowGlucose
                 )
             }
             .padding(.bottom)

+ 0 - 20
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift

@@ -7,14 +7,12 @@ extension PreferencesEditor {
         @Published var allowAnnouncements = false
         @Published var insulinReqPercentage: Decimal = 70
         @Published var skipBolusScreenAfterCarbs = false
-        @Published var displayStatistics = false
         @Published var sections: [FieldSection] = []
 
         override func subscribe() {
             preferences = provider.preferences
             subscribeSetting(\.allowAnnouncements, on: $allowAnnouncements) { allowAnnouncements = $0 }
             subscribeSetting(\.insulinReqPercentage, on: $insulinReqPercentage) { insulinReqPercentage = $0 }
-            subscribeSetting(\.displayStatistics, on: $displayStatistics) { displayStatistics = $0 }
             subscribeSetting(\.skipBolusScreenAfterCarbs, on: $skipBolusScreenAfterCarbs) { skipBolusScreenAfterCarbs = $0 }
 
             subscribeSetting(\.units, on: $unitsIndex.map { $0 == 0 ? GlucoseUnits.mgdL : .mmolL }) {
@@ -23,21 +21,6 @@ extension PreferencesEditor {
                 self?.provider.migrateUnits()
             }
 
-            let statFields = [
-                Field(
-                    displayName: NSLocalizedString(
-                        "Override HbA1c unit",
-                        comment: "Display %"
-                    ),
-                    type: .boolean(keypath: \.overrideHbA1cUnit),
-                    infoText: NSLocalizedString(
-                        "Change default HbA1c unit in statPanlel. The unit in statPanel will be updateded with next statistics.json update",
-                        comment: "Description for Override HbA1c unit"
-                    ),
-                    settable: self
-                )
-            ]
-
             let mainFields = [
                 Field(
                     displayName: NSLocalizedString("Insulin curve", comment: "Insulin curve"),
@@ -470,9 +453,6 @@ extension PreferencesEditor {
 
             sections = [
                 FieldSection(
-                    displayName: NSLocalizedString("Statistics", comment: "Options for Statistics"), fields: statFields
-                ),
-                FieldSection(
                     displayName: NSLocalizedString("OpenAPS main settings", comment: "OpenAPS main settings"), fields: mainFields
                 ),
                 FieldSection(

+ 0 - 2
FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift

@@ -36,8 +36,6 @@ extension PreferencesEditor {
                     }
 
                     Toggle("Skip Bolus screen after carbs", isOn: $state.skipBolusScreenAfterCarbs)
-
-                    Toggle("Allow Upload of Statistics to NS", isOn: $state.displayStatistics)
                 }
 
                 ForEach(state.sections.indexed(), id: \.1.id) { sectionIndex, section in

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

@@ -33,6 +33,7 @@ extension Settings {
                     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("Statistics and Glucose").navigationLink(to: .statisticsConfig, from: self)
                 }
 
                 Section(header: Text("Configuration")) {

+ 3 - 3
FreeAPS/Sources/Modules/Stat/StatStateModel.swift

@@ -12,10 +12,10 @@ extension Stat {
         private(set) var units: GlucoseUnits = .mmolL
 
         override func subscribe() {
-            highLimit = settingsManager.settings.highGlucose
-            lowLimit = settingsManager.settings.lowGlucose
+            highLimit = settingsManager.settings.high
+            lowLimit = settingsManager.settings.low
             units = settingsManager.settings.units
-            overrideUnit = settingsManager.preferences.overrideHbA1cUnit
+            overrideUnit = settingsManager.settings.overrideHbA1cUnit
         }
     }
 }

+ 11 - 11
FreeAPS/Sources/Modules/Stat/View/StatRootView.swift

@@ -297,8 +297,8 @@ extension Stat {
 
         var glucoseChart: some View {
             let count = fetchedGlucoseDay.count
-            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
-            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
             return Chart {
                 ForEach(fetchedGlucoseDay.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
                     PointMark(
@@ -344,8 +344,8 @@ extension Stat {
 
         var glucoseChartTwentyFourHours: some View {
             let count = fetchedGlucoseTwentyFourHours.count
-            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
-            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
             return Chart {
                 ForEach(fetchedGlucoseTwentyFourHours.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
                     PointMark(
@@ -390,8 +390,8 @@ extension Stat {
         }
 
         var glucoseChartWeek: some View {
-            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
-            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
             return Chart {
                 ForEach(fetchedGlucoseWeek.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
                     PointMark(
@@ -436,8 +436,8 @@ extension Stat {
         }
 
         var glucoseChartMonth: some View {
-            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
-            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
             return Chart {
                 ForEach(fetchedGlucoseMonth.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
                     PointMark(
@@ -482,8 +482,8 @@ extension Stat {
         }
 
         var glucoseChart90: some View {
-            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
-            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let lowLimit = (state.lowLimit ?? (4 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? (10 * 0.0555)) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
             return Chart {
                 ForEach(fetchedGlucose.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
                     PointMark(
@@ -539,7 +539,7 @@ extension Stat {
 
             let durationArray = loops.compactMap({ each in each.duration })
             let durationArrayCount = durationArray.count
-            var durationAverage = durationArray.reduce(0, +) / Double(durationArrayCount)
+            // var durationAverage = durationArray.reduce(0, +) / Double(durationArrayCount)
 
             let medianDuration = medianCalculationDouble(array: durationArray)
             let successsNR = loops.compactMap({ each in each.loopStatus }).filter({ each in each!.contains("Success") }).count

+ 5 - 0
FreeAPS/Sources/Modules/StatConfig/StatConfigDataFlow.swift

@@ -0,0 +1,5 @@
+enum StatConfig {
+    enum Config {}
+}
+
+protocol StatConfigProvider: Provider {}

+ 3 - 0
FreeAPS/Sources/Modules/StatConfig/StatConfigProvider.swift

@@ -0,0 +1,3 @@
+extension StatConfig {
+    final class Provider: BaseProvider, StatConfigProvider {}
+}

+ 35 - 0
FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift

@@ -0,0 +1,35 @@
+import SwiftUI
+
+extension StatConfig {
+    final class StateModel: BaseStateModel<Provider> {
+        @Published var overrideHbA1cUnit = false
+        @Published var low: Decimal = 4 / 0.0555
+        @Published var high: Decimal = 10 / 0.0555
+        @Published var uploadStats = false
+        var units: GlucoseUnits = .mmolL
+
+        override func subscribe() {
+            let units = settingsManager.settings.units
+            self.units = units
+
+            subscribeSetting(\.overrideHbA1cUnit, on: $overrideHbA1cUnit) { overrideHbA1cUnit = $0 }
+            subscribeSetting(\.uploadStats, on: $uploadStats) { uploadStats = $0 }
+
+            subscribeSetting(\.low, on: $low, initial: {
+                let value = max(min($0, 120), 40)
+                low = units == .mmolL ? value.asMmolL : value
+            }, map: {
+                guard units == .mmolL else { return $0 }
+                return $0.asMgdL
+            })
+
+            subscribeSetting(\.high, on: $high, initial: {
+                let value = max(min($0, 270), 130)
+                high = units == .mmolL ? value.asMmolL : value
+            }, map: {
+                guard units == .mmolL else { return $0 }
+                return $0.asMgdL
+            })
+        }
+    }
+}

+ 53 - 0
FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift

@@ -0,0 +1,53 @@
+import SwiftUI
+import Swinject
+
+extension StatConfig {
+    struct RootView: BaseView {
+        let resolver: Resolver
+        @StateObject var state = StateModel()
+
+        private var glucoseFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 0
+            if state.units == .mmolL {
+                formatter.maximumFractionDigits = 1
+            }
+            formatter.roundingMode = .halfUp
+            return formatter
+        }
+
+        private var carbsFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 0
+            return formatter
+        }
+
+        var body: some View {
+            Form {
+                Section(header: Text("Glucose")) {
+                    Toggle("Change HbA1c Unit", isOn: $state.overrideHbA1cUnit)
+                    Toggle("Allow Upload of Statistics to NS", isOn: $state.uploadStats)
+
+                    HStack {
+                        Text("Low")
+                        Spacer()
+                        DecimalTextField("0", value: $state.low, formatter: glucoseFormatter)
+                        Text(state.units.rawValue).foregroundColor(.secondary)
+                    }
+
+                    HStack {
+                        Text("High")
+                        Spacer()
+                        DecimalTextField("0", value: $state.high, formatter: glucoseFormatter)
+                        Text(state.units.rawValue).foregroundColor(.secondary)
+                    }
+                }
+            }
+            .onAppear(perform: configureView)
+            .navigationBarTitle("Statistics")
+            .navigationBarTitleDisplayMode(.automatic)
+        }
+    }
+}

+ 3 - 0
FreeAPS/Sources/Router/Screen.swift

@@ -31,6 +31,7 @@ enum Screen: Identifiable, Hashable {
     case snooze
     case statistics
     case watch
+    case statisticsConfig
 
     var id: Int { String(reflecting: self).hashValue }
 }
@@ -96,6 +97,8 @@ extension Screen {
             WatchConfig.RootView(resolver: resolver)
         case .statistics:
             Stat.RootView(resolver: resolver)
+        case .statisticsConfig:
+            StatConfig.RootView(resolver: resolver)
         }
     }