Jelajahi Sumber

Version 0.5.4
First draft of a statPanel View.
Delete some superfluous code.

Jon Mårtensson 3 tahun lalu
induk
melakukan
2ef9a78ae4

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 0.5.2
+APP_VERSION = 0.5.4
 APP_BUILD_NUMBER = 1
 BRANCH = bdb
 DEVELOPER_TEAM = ##TEAM_ID##

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

@@ -20,5 +20,6 @@
     "highGlucose": 270,
     "carbsRequiredThreshold": 10,
     "useAppleHealth": false,
-    "animatedBackground": false
+    "animatedBackground": false,
+    "displayStatistics": false
 }

+ 31 - 17
FreeAPS/Sources/APS/APSManager.swift

@@ -760,9 +760,9 @@ final class BaseAPSManager: APSManager, Injectable {
             testFile = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self) ?? []
             testIfEmpty = testFile.count
         }
-        // Only run every hour
+        // Only run every 30 minutes
         if testIfEmpty != 0 {
-            guard testFile[0].created_at.addingTimeInterval(1.hours.timeInterval) < Date() else {
+            guard testFile[0].created_at.addingTimeInterval(30.minutes.timeInterval) < Date() else {
                 return
             }
         }
@@ -969,7 +969,7 @@ final class BaseAPSManager: APSManager, Injectable {
         var daysBG = 0.0
         var fullTime = 0.0
 
-        if endIndex >= 0 {
+        if length_ > 0 {
             fullTime = (startDate! - glucose![endIndex].date).timeInterval
             daysBG = fullTime / 8.64E4
         }
@@ -982,28 +982,42 @@ final class BaseAPSManager: APSManager, Injectable {
             var i = -1
             var lastIndex = false
             let endIndex = array.count - 1
+
+            var hypoLimit = settingsManager.preferences.low
+            var hyperLimit = settingsManager.preferences.high
+            if units == .mmolL {
+                hypoLimit = hypoLimit / 0.0555
+                hyperLimit = hyperLimit / 0.0555
+            }
+
+            var full_time = 0.0
+            if endIndex > 0 {
+                full_time = (array[0].date_ - array[endIndex].date_).timeInterval
+            }
+
             while i < endIndex {
                 i += 1
                 let currentTime = array[i].date_
                 var previousTime = currentTime
+
                 if i + 1 <= endIndex {
                     previousTime = array[i + 1].date_
                 } else {
                     lastIndex = true
                 }
-                if array[i].bg_ < 72.0, !lastIndex {
+                if array[i].bg_ < Double(hypoLimit), !lastIndex {
                     timeInHypo += (currentTime - previousTime).timeInterval
-                } else if array[i].bg_ > 180, !lastIndex {
+                } else if array[i].bg_ >= Double(hyperLimit), !lastIndex {
                     timeInHyper += (currentTime - previousTime).timeInterval
                 }
             }
-            if timeInHypo == 0 {
+            if timeInHypo == 0.0 {
                 hypos = 0
-            } else if fullTime != 0.0 { hypos = (timeInHypo / fullTime) * 100
+            } else if full_time != 0.0 { hypos = (timeInHypo / full_time) * 100
             }
-            if timeInHyper == 0 {
+            if timeInHyper == 0.0 {
                 hypers = 0
-            } else if fullTime != 0.0 { hypers = (timeInHyper / fullTime) * 100
+            } else if full_time != 0.0 { hypers = (timeInHyper / full_time) * 100
             }
             let TIR = 100 - (hypos + hypers)
             return (roundDouble(TIR, 1), roundDouble(hypos, 1), roundDouble(hypers, 1))
@@ -1050,7 +1064,7 @@ final class BaseAPSManager: APSManager, Injectable {
                 (NGSPa1CStatisticValue_total - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
         }
 
-        var median = Median(
+        var median = Durations(
             day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))), 1),
             week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))), 1),
             month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))), 1),
@@ -1058,7 +1072,7 @@ final class BaseAPSManager: APSManager, Injectable {
             total: roundDecimal(Decimal(medianBG), 1)
         )
 
-        var hbs = Hbs(
+        var hbs = Durations(
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
             month: roundDecimal(NGSPa1CStatisticValue_30, 1),
@@ -1074,7 +1088,7 @@ final class BaseAPSManager: APSManager, Injectable {
             bg_90 = bg_90.asMmolL
             bg_total = bg_total.asMmolL
 
-            median = Median(
+            median = Durations(
                 day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))).asMmolL, 1),
                 week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))).asMmolL, 1),
                 month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))).asMmolL, 1),
@@ -1082,7 +1096,7 @@ final class BaseAPSManager: APSManager, Injectable {
                 total: roundDecimal(Decimal(medianBG).asMmolL, 1)
             )
 
-            hbs = Hbs(
+            hbs = Durations(
                 day: roundDecimal(IFCCa1CStatisticValue, 1),
                 week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                 month: roundDecimal(IFCCa1CStatisticValue_30, 1),
@@ -1133,7 +1147,7 @@ final class BaseAPSManager: APSManager, Injectable {
             totalDays_ = tir(bgArrayForTIR)
         }
 
-        let tir = TIR(
+        let tir = Durations(
             day: roundDecimal(Decimal(oneDay_.TIR), 1),
             week: roundDecimal(Decimal(sevenDays_.TIR), 1),
             month: roundDecimal(Decimal(thirtyDays_.TIR), 1),
@@ -1141,7 +1155,7 @@ final class BaseAPSManager: APSManager, Injectable {
             total: roundDecimal(Decimal(totalDays_.TIR), 1)
         )
 
-        let hypo = Hypos(
+        let hypo = Durations(
             day: Decimal(oneDay_.hypos),
             week: Decimal(sevenDays_.hypos),
             month: Decimal(thirtyDays_.hypos),
@@ -1149,7 +1163,7 @@ final class BaseAPSManager: APSManager, Injectable {
             total: Decimal(totalDays_.hypos)
         )
 
-        let hyper = Hypers(
+        let hyper = Durations(
             day: Decimal(oneDay_.hypers),
             week: Decimal(sevenDays_.hypers),
             month: Decimal(thirtyDays_.hypers),
@@ -1159,7 +1173,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
         let TimeInRange = TIRs(TIR: tir, Hypos: hypo, Hypers: hyper)
 
-        let avgs = Average(
+        let avgs = Durations(
             day: roundDecimal(bg_1, 1),
             week: roundDecimal(bg_7, 1),
             month: roundDecimal(bg_30, 1),

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

@@ -1128,6 +1128,64 @@ Enact a temp Basal or a temp target */
 /* Alert title for suspend error */
 "Failed to Suspend Insulin Delivery" = "Failed to Suspend Insulin Delivery";
 
+//* -----------------------------------------------------------------------*/
+
+/* ----------------------Statistics strings -------------------------------*/
+
+/* Option in preferences */
+"Display Statistics" = "Display Statistics";
+
+/* infoText: Description for Display Statistics */
+"Displays Statistics under the chart view" = "Displays Statistics under the chart view";
+
+/* Low Glucose Limit option in preferences */
+"Low Glucose Limit" = "Low Glucose Limit";
+
+/* High Glucose Limit option in preferences */
+"High Glucose Limit" = "High Glucose Limit";
+
+/* infoText: Description for Low Glucose Limit */
+"BG Under This Value Will Be Displayed As Low Glucose Percentage" = "BG Under This Value Will Be Displayed As Low Glucose Percentage";
+    
+/* infoText: Description for High Glucose Limit */
+"BG Under This Value Will Be Displayed As High Glucose Percentage" = "BG Under This Value Will Be Displayed As High Glucose Percentage";
+
+/* When statistics.json was last updated */
+"Updated" = "Updated";
+
+/* Average BG = */
+"Average" = "Average";
+
+/* Median BG */
+"Median" = "Median";
+
+/* Normal BG (within TIR) */
+"Normal (24h)" = "Normal (24h)";
+
+/* Title High BG in statPanel */
+"High (>" = "High (>";
+
+/* Title Low BG in statPanel */
+"Low (<" = "Low (<";
+
+/* HbA1c estimation for one day of data */
+"HbA1c (24h)" = "HbA1c (24h)";
+
+/* Total number of days of data for HbA1c estimation, part 1/2*/
+"All " = "All ";
+
+/* Total number of days of data for HbA1c estimation, part 2/2*/
+" days" = " days";
+
+/* Nr of loops last 24 hours */
+"Loops" = "Loops";
+
+/* Average loop interval */
+"Average Interval" = "Avgerage Interval";
+
+/* Median loop interval */
+"Median Duration" = "Median Duration";
+
  /* --------------------------------------
 
   Infotexts from openaps.docs and androidaps.docs

+ 58 - 0
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings

@@ -1125,6 +1125,64 @@ Enact a temp Basal or a temp target */
 /* Alert title for suspend error */
 "Failed to Suspend Insulin Delivery" = "Kunde inte pausa pump";
 
+//* -----------------------------------------------------------------------*/
+
+/* ----------------------Statistics strings -------------------------------*/
+
+/* Option in preferences */
+"Display Statistics" = "Visa statistik";
+
+/* infoText: Description for Display Statistics */
+"Displays Statistics under the chart view" = "Visa statistik under diagram";
+
+/* Low Glucose Limit option in preferences */
+"Low Glucose Limit" = "Lågt blodsocker";
+
+/* High Glucose Limit option in preferences */
+"High Glucose Limit" = "Högt blodsocker";
+
+/* infoText: Description for Low Glucose Limit */
+"BG Under This Value Will Be Displayed As Low Glucose Percentage" = "Blodsocker under detta värde kommer att visas som lågt blodsocker i statistiken.";
+    
+/* infoText: Description for High Glucose Limit */
+"BG Under This Value Will Be Displayed As High Glucose Percentage" = "Blodsocker under detta värde kommer att visas som högt blodsocker i statistiken";
+
+/* When statistics.json was last updated */
+"Updated" = "Uppdaterades";
+
+/* Average BG = */
+"Average" = "Medelvärde";
+
+/* Median BG */
+"Median" = "Medianvärde";
+
+/* Normal (percentage within TIR) */
+"Normal (24h)" = "Normalvärde (24h)";
+
+/* HbA1c estimation for one day of data */
+"HbA1c (24h)" = "HbA1c (24h)";
+
+/* Total number of days of data for HbA1c estimation, part 1/2 */
+"All " = "Alla ";
+
+/* Total number of days of data for HbA1c estimation, part 2/2 */
+" days" = " dagar";
+
+/* Nr of loops last 24 hours */
+"Loops" = "Loopar";
+
+/* Average loop interval */
+"Average Interval" = "Medelintervall";
+
+/* Median loop interval */
+"Median Duration" = "Medianduration";
+
+/* Title High BG in statPanel */
+"High (>" = "Högt (>";
+
+/* Title Low BG in statPanel */
+"Low (<" = "Lågt (<";
+
 /* --------------------------------------
 
   Infotexts from openaps.docs and androidaps.docs

+ 5 - 0
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -24,6 +24,7 @@ struct FreeAPSSettings: JSON, Equatable {
     var highGlucose: Decimal = 270
     var carbsRequiredThreshold: Decimal = 10
     var animatedBackground: Bool = false
+    var displayStatistics: Bool = false
 }
 
 extension FreeAPSSettings: Decodable {
@@ -127,6 +128,10 @@ extension FreeAPSSettings: Decodable {
             settings.animatedBackground = animatedBackground
         }
 
+        if let displayStatistics = try? container.decode(Bool.self, forKey: .displayStatistics) {
+            settings.displayStatistics = displayStatistics
+        }
+
         self = settings
     }
 }

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

@@ -53,6 +53,8 @@ struct Preferences: JSON {
     var enableSMB_high_bg: Bool = false
     var enableSMB_high_bg_target: Decimal = 110
     var threshold_setting: Decimal = 65
+    var high: Decimal = 10
+    var low: Decimal = 4
 }
 
 extension Preferences {
@@ -108,6 +110,8 @@ extension Preferences {
         case enableSMB_high_bg
         case enableSMB_high_bg_target
         case threshold_setting
+        case high
+        case low
     }
 }
 

+ 7 - 97
FreeAPS/Sources/Models/Statistics.swift

@@ -99,27 +99,11 @@ struct LoopCycles: JSON, Equatable {
 }
 
 struct Averages: JSON, Equatable {
-    var Average: Average
-    var Median: Median
+    var Average: Durations
+    var Median: Durations
 }
 
-struct Average: JSON, Equatable {
-    var day: Decimal
-    var week: Decimal
-    var month: Decimal
-    var ninetyDays: Decimal
-    var total: Decimal
-}
-
-struct Median: JSON, Equatable {
-    var day: Decimal
-    var week: Decimal
-    var month: Decimal
-    var ninetyDays: Decimal
-    var total: Decimal
-}
-
-struct Hbs: JSON, Equatable {
+struct Durations: JSON, Equatable {
     var day: Decimal
     var week: Decimal
     var month: Decimal
@@ -128,33 +112,9 @@ struct Hbs: JSON, Equatable {
 }
 
 struct TIRs: JSON, Equatable {
-    var TIR: TIR
-    var Hypos: Hypos
-    var Hypers: Hypers
-}
-
-struct TIR: JSON, Equatable {
-    var day: Decimal
-    var week: Decimal
-    var month: Decimal
-    var ninetyDays: Decimal
-    var total: Decimal
-}
-
-struct Hypos: JSON, Equatable {
-    var day: Decimal
-    var week: Decimal
-    var month: Decimal
-    var ninetyDays: Decimal
-    var total: Decimal
-}
-
-struct Hypers: JSON, Equatable {
-    var day: Decimal
-    var week: Decimal
-    var month: Decimal
-    var ninetyDays: Decimal
-    var total: Decimal
+    var TIR: Durations
+    var Hypos: Durations
+    var Hypers: Durations
 }
 
 struct Ins: JSON, Equatable {
@@ -167,7 +127,7 @@ struct Ins: JSON, Equatable {
 struct Stats: JSON, Equatable {
     var Distribution: TIRs
     var Glucose: Averages
-    var HbA1c: Hbs
+    var HbA1c: Durations
     var LoopCycles: LoopCycles
     var Insulin: Ins
 }
@@ -195,36 +155,6 @@ extension Averages {
     }
 }
 
-extension Average {
-    private enum CodingKeys: String, CodingKey {
-        case day
-        case week
-        case month
-        case ninetyDays
-        case total
-    }
-}
-
-extension Median {
-    private enum CodingKeys: String, CodingKey {
-        case day
-        case week
-        case month
-        case ninetyDays
-        case total
-    }
-}
-
-extension Hbs {
-    private enum CodingKeys: String, CodingKey {
-        case day
-        case week
-        case month
-        case ninetyDays
-        case total
-    }
-}
-
 extension TIRs {
     private enum CodingKeys: String, CodingKey {
         case TIR
@@ -233,26 +163,6 @@ extension TIRs {
     }
 }
 
-extension Hypos {
-    private enum CodingKeys: String, CodingKey {
-        case day
-        case week
-        case month
-        case ninetyDays
-        case total
-    }
-}
-
-extension Hypers {
-    private enum CodingKeys: String, CodingKey {
-        case day
-        case week
-        case month
-        case ninetyDays
-        case total
-    }
-}
-
 extension Ins {
     private enum CodingKeys: String, CodingKey {
         case TDD

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

@@ -7,6 +7,7 @@ enum Home {
 
 protocol HomeProvider: Provider {
     var suggestion: Suggestion? { get }
+    var statistics: Statistics? { get }
     var enactedSuggestion: Suggestion? { get }
     func heartbeatNow()
     func filteredGlucose(hours: Int) -> [BloodGlucose]

+ 8 - 0
FreeAPS/Sources/Modules/Home/HomeProvider.swift

@@ -14,6 +14,14 @@ extension Home {
             storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
         }
 
+        var statistics: Statistics? {
+            let stat = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self)
+            if stat?.count ?? 0 != 0 {
+                return stat![0]
+            }
+            return storage.retrieve(OpenAPS.Monitor.statistics, as: Statistics.self)
+        }
+
         var enactedSuggestion: Suggestion? {
             storage.retrieve(OpenAPS.Enact.enacted, as: Suggestion.self)
         }

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

@@ -13,6 +13,8 @@ extension Home {
 
         @Published var glucose: [BloodGlucose] = []
         @Published var suggestion: Suggestion?
+        @Published var statistics: Statistics?
+        @Published var displayStatistics = false
         @Published var enactedSuggestion: Suggestion?
         @Published var recentGlucose: BloodGlucose?
         @Published var glucoseDelta: Int?
@@ -44,6 +46,8 @@ extension Home {
         @Published var carbsRequired: Decimal?
         @Published var allowManualTemp = false
         @Published var units: GlucoseUnits = .mmolL
+        @Published var low: Decimal = 4
+        @Published var high: Decimal = 10
         @Published var pumpDisplayState: PumpDisplayState?
         @Published var alarm: GlucoseAlarm?
         @Published var animatedBackground = false
@@ -60,8 +64,13 @@ extension Home {
             setupCarbs()
             setupBattery()
             setupReservoir()
+            setupStatistics()
 
             suggestion = provider.suggestion
+            statistics = provider.statistics
+            displayStatistics = settingsManager.settings.displayStatistics
+            low = settingsManager.preferences.low
+            high = settingsManager.preferences.high
             enactedSuggestion = provider.enactedSuggestion
             units = settingsManager.settings.units
             allowManualTemp = !settingsManager.settings.closedLoop
@@ -70,7 +79,6 @@ extension Home {
             carbsRequired = suggestion?.carbsReq
             alarm = provider.glucoseStorage.alarm
             manualTempBasal = apsManager.isManualTempBasal
-
             setStatusTitle()
             setupCurrentTempTarget()
 
@@ -305,6 +313,13 @@ extension Home {
             }
         }
 
+        private func setupStatistics() {
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self else { return }
+                self.statistics = self.provider.statistics
+            }
+        }
+
         private func setupBattery() {
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
@@ -348,6 +363,7 @@ extension Home.StateModel:
 {
     func glucoseDidUpdate(_: [BloodGlucose]) {
         setupGlucose()
+        setupStatistics()
     }
 
     func suggestionDidUpdate(_ suggestion: Suggestion) {
@@ -358,11 +374,15 @@ extension Home.StateModel:
 
     func settingsDidChange(_ settings: FreeAPSSettings) {
         allowManualTemp = !settings.closedLoop
+        displayStatistics = settingsManager.settings.displayStatistics
         closedLoop = settingsManager.settings.closedLoop
+        low = settingsManager.preferences.low
+        high = settingsManager.preferences.high
         units = settingsManager.settings.units
         animatedBackground = settingsManager.settings.animatedBackground
         manualTempBasal = apsManager.isManualTempBasal
         setupGlucose()
+        setupStatistics()
     }
 
     func pumpHistoryDidUpdate(_: [PumpHistoryEvent]) {

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

@@ -38,6 +38,7 @@ struct MainChartView: View {
 
     @Binding var glucose: [BloodGlucose]
     @Binding var suggestion: Suggestion?
+    @Binding var statistcs: Statistics?
     @Binding var tempBasals: [PumpHistoryEvent]
     @Binding var boluses: [PumpHistoryEvent]
     @Binding var suspensions: [PumpHistoryEvent]

+ 145 - 2
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -217,7 +217,134 @@ extension Home {
             .frame(maxWidth: .infinity, maxHeight: 30)
         }
 
-        var legendPanal: some View {
+        @ViewBuilder private func statPanel() -> some View {
+            if state.displayStatistics {
+                VStack(alignment: .center, spacing: 6) {
+                    HStack {
+                        Group {
+                            Text("Updated").font(.caption2)
+                                .foregroundColor(.secondary)
+                            Text(
+                                dateFormatter.string(from: state.statistics?.created_at ?? Date())
+                            ).font(.system(size: 12))
+
+                            Text(
+                                NSLocalizedString("Average", comment: "") + " " + state.settingsManager.settings.units.rawValue
+                            ).font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                numberFormatter
+                                    .string(from: (state.statistics?.Statistics.Glucose.Average.day ?? 0) as NSNumber) ??
+                                    ""
+                            ).font(.system(size: 12))
+                            Text("Median")
+                                .font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                numberFormatter
+                                    .string(from: (state.statistics?.Statistics.Glucose.Median.day ?? 0) as NSNumber) ??
+                                    ""
+                            ).font(.system(size: 12))
+                        }
+                    }
+
+                    HStack {
+                        Group {
+                            Text("Normal (24h)").font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                (
+                                    numberFormatter
+                                        .string(from: (state.statistics?.Statistics.Distribution.TIR.day ?? 0) as NSNumber) ??
+                                        "0"
+                                ) + " %"
+                            ).font(.system(size: 12)).foregroundColor(.loopGreen)
+
+                            Text(
+                                NSLocalizedString("Low (<", comment: " ") +
+                                    (numberFormatter.string(from: state.settingsManager.preferences.low as NSNumber) ?? "") + ")"
+                            ).font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                (
+                                    numberFormatter
+                                        .string(from: (
+                                            state.statistics?.Statistics.Distribution.Hypos.day ?? 0
+                                        ) as NSNumber) ??
+                                        "0"
+                                ) + " %"
+                            ).font(.system(size: 12)).foregroundColor(.loopRed)
+                            Text(
+                                NSLocalizedString("High (>", comment: " ") +
+                                    (numberFormatter.string(from: state.settingsManager.preferences.high as NSNumber) ?? "") + ")"
+                            ).font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                (
+                                    numberFormatter
+                                        .string(from: (
+                                            state.statistics?.Statistics.Distribution.Hypers.day ?? 0
+                                        ) as NSNumber) ??
+                                        "0"
+                                ) + " %"
+                            ).font(.system(size: 12)).foregroundColor(.loopYellow)
+                        }
+                    }
+
+                    HStack {
+                        Group {
+                            Text("HbA1c (24h)").font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                numberFormatter
+                                    .string(from: (state.statistics?.Statistics.HbA1c.day ?? 0) as NSNumber) ??
+                                    ""
+                            ).font(.system(size: 12))
+
+                            Text(
+                                NSLocalizedString("All ", comment: "") +
+                                    (
+                                        numberFormatter
+                                            .string(from: (state.statistics?.GlucoseStorage_Days ?? 0) as NSNumber) ?? ""
+                                    ) +
+                                    NSLocalizedString(" days", comment: "")
+                            ).font(.caption2).foregroundColor(.secondary)
+
+                            Text(
+                                numberFormatter
+                                    .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ??
+                                    ""
+                            ).font(.system(size: 12))
+                        }
+                    }
+
+                    HStack {
+                        Group {
+                            Text("Loops").font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                numberFormatter
+                                    .string(from: (state.statistics?.Statistics.LoopCycles.loops ?? 0) as NSNumber) ??
+                                    "0"
+                            ).font(.system(size: 12))
+
+                            Text("Average Interval").font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                numberFormatter
+                                    .string(from: (state.statistics?.Statistics.LoopCycles.avg_interval ?? 0) as NSNumber) ??
+                                    "0"
+                            ).font(.system(size: 12))
+
+                            Text("Median Duration").font(.caption2).foregroundColor(.secondary)
+                            Text(
+                                numberFormatter
+                                    .string(from: (
+                                        state.statistics?.Statistics.LoopCycles
+                                            .median_duration ?? 0
+                                    ) as NSNumber) ??
+                                    "0"
+                            ).font(.system(size: 12))
+                        }
+                    }
+                }
+                .frame(maxWidth: .infinity, maxHeight: 100, alignment: .center)
+            }
+        }
+
+        var legendPanel: some View {
             HStack(alignment: .center) {
                 Group {
                     Circle().fill(Color.loopGreen).frame(width: 8, height: 8)
@@ -272,6 +399,7 @@ extension Home {
                 MainChartView(
                     glucose: $state.glucose,
                     suggestion: $state.suggestion,
+                    statistcs: $state.statistics,
                     tempBasals: $state.tempBasals,
                     boluses: $state.boluses,
                     suspensions: $state.suspensions,
@@ -362,7 +490,8 @@ extension Home {
                     header(geo)
                     infoPanel
                     mainChart
-                    legendPanal
+                    legendPanel
+                    statPanel()
                     bottomPanel(geo)
                 }
                 .edgesIgnoringSafeArea(.vertical)
@@ -413,5 +542,19 @@ extension Home {
                 }
             }
         }
+
+        private func colorOfGlucose(_ glucose: Decimal) -> Color {
+            switch glucose {
+            case 4 ... 8,
+                 30 ... 46,
+                 72 ... 144:
+                return .loopGreen
+            case 0 ... 4,
+                 20 ... 71:
+                return .loopRed
+            default:
+                return .loopYellow
+            }
+        }
     }
 }

+ 33 - 3
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift

@@ -8,15 +8,15 @@ extension PreferencesEditor {
         @Published var insulinReqFraction: Decimal = 2.0
         @Published var skipBolusScreenAfterCarbs = false
         @Published var displayHR = false
-
+        @Published var displayStatistics = false
         @Published var sections: [FieldSection] = []
 
         override func subscribe() {
             preferences = provider.preferences
-
             subscribeSetting(\.allowAnnouncements, on: $allowAnnouncements) { allowAnnouncements = $0 }
             subscribeSetting(\.insulinReqFraction, on: $insulinReqFraction) { insulinReqFraction = $0 }
             subscribeSetting(\.displayHR, on: $displayHR) { displayHR = $0 }
+            subscribeSetting(\.displayStatistics, on: $displayStatistics) { displayStatistics = $0 }
             subscribeSetting(\.skipBolusScreenAfterCarbs, on: $skipBolusScreenAfterCarbs) { skipBolusScreenAfterCarbs = $0 }
 
             subscribeSetting(\.units, on: $unitsIndex.map { $0 == 0 ? GlucoseUnits.mgdL : .mmolL }) {
@@ -25,7 +25,34 @@ extension PreferencesEditor {
                 self?.provider.migrateUnits()
             }
 
-            // MARK: - Main fields
+            let statFields = [
+                Field(
+                    displayName: NSLocalizedString(
+                        "Low Glucose Limit",
+                        comment: "Display As Low Glucose Percantage Under This Value"
+                    ) + " (\(settingsManager.settings.units.rawValue))",
+
+                    type: .decimal(keypath: \.low),
+                    infoText: NSLocalizedString(
+                        "Blood Glucoses Under This Value Will Added To And Displayed as Low Glucose Percantage",
+                        comment: "Description for Low Glucose Limit"
+                    ),
+                    settable: self
+                ),
+                Field(
+                    displayName: NSLocalizedString(
+                        "High Glucose Limit",
+                        comment: "Limit For High Glucose in Statistics View"
+                    ) + " (\(settingsManager.settings.units.rawValue))",
+
+                    type: .decimal(keypath: \.high),
+                    infoText: NSLocalizedString(
+                        "Blood Glucoses Over This Value Will Added To And Displaved as High Glucose Percantage",
+                        comment: "High Glucose Limit"
+                    ),
+                    settable: self
+                )
+            ]
 
             let mainFields = [
                 Field(
@@ -489,6 +516,9 @@ 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(

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

@@ -38,6 +38,8 @@ extension PreferencesEditor {
                     Toggle("Skip Bolus screen after carbs", isOn: $state.skipBolusScreenAfterCarbs)
 
                     Toggle("Display HR on Watch", isOn: $state.displayHR)
+
+                    Toggle("Display Statistics", isOn: $state.displayStatistics)
                 }
 
                 ForEach(state.sections.indexed(), id: \.1.id) { sectionIndex, section in