Преглед изворни кода

Change duration with button

Jon B.M пре 3 година
родитељ
комит
ed6a48c816

+ 8 - 4
FreeAPS.xcodeproj/project.pbxproj

@@ -19,6 +19,7 @@
 		19788CAF293CE0F0002FC264 /* GlucoseDataForStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */; };
 		19795118275953E50044850D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
+		19854F492961C3E500941627 /* DurationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19854F482961C3E500941627 /* DurationButton.swift */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
 		19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; };
 		19F79FA9283AE7E000646323 /* TDD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F79FA8283AE7E000646323 /* TDD.swift */; };
@@ -463,6 +464,7 @@
 		198377E2266C0AC8004DE65E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E3266C0ADC004DE65E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E4266C13D2004DE65E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
+		19854F482961C3E500941627 /* DurationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationButton.swift; sourceTree = "<group>"; };
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
 		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -1007,6 +1009,7 @@
 				3811DE2A25C9D49500A708ED /* HomeDataFlow.swift */,
 				3811DE2925C9D49500A708ED /* HomeProvider.swift */,
 				3811DE2825C9D49500A708ED /* HomeStateModel.swift */,
+				19854F482961C3E500941627 /* DurationButton.swift */,
 				3811DE2C25C9D49500A708ED /* View */,
 			);
 			path = Home;
@@ -2173,6 +2176,7 @@
 				385CEAC425F2F154002D6D5B /* AnnouncementsStorage.swift in Sources */,
 				38AEE73D25F0200C0013F05B /* FreeAPSSettings.swift in Sources */,
 				38FCF3FD25E997A80078B0D1 /* PumpHistoryStorage.swift in Sources */,
+				19854F492961C3E500941627 /* DurationButton.swift in Sources */,
 				38D0B3B625EBE24900CB6E88 /* Battery.swift in Sources */,
 				38C4D33725E9A1A300D30B77 /* DispatchQueue+Extensions.swift in Sources */,
 				F90692CF274B999A0037068D /* HealthKitDataFlow.swift in Sources */,
@@ -2587,7 +2591,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				IPHONEOS_DEPLOYMENT_TARGET = 15.1;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -2645,7 +2649,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				IPHONEOS_DEPLOYMENT_TARGET = 15.1;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = iphoneos;
@@ -2670,7 +2674,7 @@
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				IPHONEOS_DEPLOYMENT_TARGET = 15.1;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -2706,7 +2710,7 @@
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				IPHONEOS_DEPLOYMENT_TARGET = 15.1;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


+ 12 - 7
FreeAPS/Sources/APS/APSManager.swift

@@ -1116,13 +1116,18 @@ final class BaseAPSManager: APSManager, Injectable {
                 total: roundDecimal(Decimal(medianBG).asMmolL, 1)
             )
 
-            hbs = Durations(
-                day: roundDecimal(IFCCa1CStatisticValue, 1),
-                week: roundDecimal(IFCCa1CStatisticValue_7, 1),
-                month: roundDecimal(IFCCa1CStatisticValue_30, 1),
-                ninetyDays: roundDecimal(IFCCa1CStatisticValue_90, 1),
-                total: roundDecimal(IFCCa1CStatisticValue_total, 1)
-            )
+            let overrideHbA1cUnit = settingsManager.preferences.overrideHbA1cUnit
+
+            // Override if users sets overrideHbA1cUnit: true
+            if !overrideHbA1cUnit {
+                hbs = Durations(
+                    day: roundDecimal(IFCCa1CStatisticValue, 1),
+                    week: roundDecimal(IFCCa1CStatisticValue_7, 1),
+                    month: roundDecimal(IFCCa1CStatisticValue_30, 1),
+                    ninetyDays: roundDecimal(IFCCa1CStatisticValue_90, 1),
+                    total: roundDecimal(IFCCa1CStatisticValue_total, 1)
+                )
+            }
         }
 
         // round output values

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

@@ -1253,6 +1253,33 @@ Enact a temp Basal or a temp target */
 /* Description for update interval for statistics */
 "Default is 30 minutes. How often to update and save the statistics.json and to upload last array, when enabled, to Nightscout. A lower interval than for glucose updates (5 min) is pointless." = "Default is 30 minutes. How often to update and save the statistics.json and to upload last array, when enabled, to Nightscout. A lower interval than for glucose updates (5 min) is pointless.";
 
+/* Duration displayed in statPanel */
+"Past 24 Hours " = "Past 24 Hours ";
+
+/* Duration displayed in statPanel */
+"Past Week " = "Past Week ";
+
+/* Duration displayed in statPanel */
+"Past Month " = "Past Month ";
+
+/* Duration displayed in statPanel */
+"Past 90 Days " = "Past 90 Days ";
+
+/* Duration displayed in statPanel */
+"All Past Days of Data " = "All Past Days of Data ";
+
+/* "Display Loop statistics in statPanel */
+"Display Loop Cycle statistics" = "Display Loop Cycle statistics";
+
+/* Description for Display Loop statistics */
+"Displays Loop statistics in the statPanel in Home View" = "Displays Loop statistics in the statPanel in Home View";
+
+/* Display % */
+"Override HbA1c unit" = "Override HbA1c unit";
+
+/* Description for Override HbA1c unit */
+"Display '%' as HbA1c unit, even when using mmol/l for glucose. StatPanel will be updated with new unit with next statistics.json update" = "Display '%' as HbA1c unit, even when using mmol/l for glucose. StatPanel will be updated with new unit with next statistics.json update";
+
  /* --------------------------------------
 
   Infotexts from openaps.docs and androidaps.docs

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

@@ -1246,6 +1246,33 @@ Enact a temp Basal or a temp target */
 /* Description for update interval for statistics */
 "Default is 30 minutes. How often to update and save the statistics.json and to upload last array, when enabled, to Nightscout. A lower interval than for glucose updates (5 min) is pointless." = "Standardinställning är 30 minuter. Med denna inställning ställer du in hur ofta statistiken uppdateras och hur ofta filen statistics.json sparas och laddas upp till till Nightscout, om aktiverad. Att ha ett lägre intervall än för nytt blodoscker (5 min) är meningslöst.";
 
+/* Duration displayed in statPanel */
+"Past 24 Hours " = "Senaste 24 timmarna ";
+
+/* Duration displayed in statPanel */
+"Past Week " = "Senaste veckan ";
+
+/* Duration displayed in statPanel */
+"Past Month " = "Senaste månaden ";
+
+/* Duration displayed in statPanel */
+"Past 90 Days " = "Senaste 90 dagarna ";
+
+/* Duration displayed in statPanel */
+"All Past Days of Data " = "Alla dagar med data ";
+
+/* "Display Loop statistics in statPanel */
+"Display Loop Cycle statistics" = "Visa Loop-statistik";
+
+/* Description for Display Loop statistics */
+"Displays Loop statistics in the statPanel in Home View" = "Visar Loop-statistik i statistiken på hemskärmen";
+
+/* Display % */
+"Override HbA1c unit" = "Ändra HbA1c-enhet";
+
+/* Description for Override HbA1c unit */
+"Display '%' as HbA1c unit, even when using mmol/l for glucose. StatPanel will be updated with new unit with next statistics.json update" = "Visa '%' som HbA1c-enhet, även när du använder mmol/l för blodsocker. Statistiken kommer att uppdateras nästa gång filen statistics.json uppdateras";
+
 /* --------------------------------------
 
   Infotexts from openaps.docs and androidaps.docs

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

@@ -57,6 +57,8 @@ struct Preferences: JSON {
     var low: Decimal = 4
     var updateInterval: Decimal = 30
     var displaySD: Bool = false
+    var overrideHbA1cUnit: Bool = false
+    var displayLoops: Bool = false
 }
 
 extension Preferences {
@@ -116,6 +118,8 @@ extension Preferences {
         case low
         case updateInterval
         case displaySD
+        case overrideHbA1cUnit
+        case displayLoops
     }
 }
 

+ 36 - 0
FreeAPS/Sources/Modules/Home/DurationButton.swift

@@ -0,0 +1,36 @@
+import SwiftUI
+
+protocol DurationButton: CaseIterable {
+    var title: String { get }
+}
+
+extension DurationButton where Self: RawRepresentable, RawValue == String {
+    var title: String {
+        rawValue
+    }
+}
+
+enum durationState: String, DurationButton {
+    case day = "Past 24 Hours "
+    case week = "Past Week "
+    case month = "Past Month "
+    case ninetyDays = "Past 90 Days "
+    case total = "All Past Days of Data "
+}
+
+struct durationButton<T: DurationButton>: View {
+    let states: [T]
+    @State var currentIndex = 0
+    @Binding var selectedState: T
+
+    var body: some View {
+        Button {
+            currentIndex = currentIndex < states.count - 1 ? currentIndex + 1 : 0
+            selectedState = states[currentIndex]
+        } label: {
+            Text(NSLocalizedString(states[currentIndex].title, comment: "Duration displayed in statPanel")).font(.caption2)
+                .foregroundColor(.secondary)
+                .fontWeight(.bold)
+        }
+    }
+}

+ 1 - 1
FreeAPS/Sources/Modules/Home/HomeProvider.swift

@@ -19,7 +19,7 @@ extension Home {
             if stat?.count ?? 0 != 0 {
                 return stat![0]
             }
-            return storage.retrieve(OpenAPS.Monitor.statistics, as: Statistics.self)
+            return nil
         }
 
         var enactedSuggestion: Suggestion? {

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

@@ -49,6 +49,7 @@ extension Home {
         @Published var low: Decimal = 4
         @Published var high: Decimal = 10
         @Published var displaySD = false
+        @Published var displayLoops = false
         @Published var pumpDisplayState: PumpDisplayState?
         @Published var alarm: GlucoseAlarm?
         @Published var animatedBackground = false
@@ -73,6 +74,7 @@ extension Home {
             low = settingsManager.preferences.low
             high = settingsManager.preferences.high
             displaySD = settingsManager.preferences.displaySD
+            displayLoops = settingsManager.preferences.displayLoops
             enactedSuggestion = provider.enactedSuggestion
             units = settingsManager.settings.units
             allowManualTemp = !settingsManager.settings.closedLoop
@@ -383,6 +385,7 @@ extension Home.StateModel:
         low = settingsManager.preferences.low
         high = settingsManager.preferences.high
         displaySD = settingsManager.preferences.displaySD
+        displayLoops = settingsManager.preferences.displayLoops
         units = settingsManager.settings.units
         animatedBackground = settingsManager.settings.animatedBackground
         manualTempBasal = apsManager.isManualTempBasal
@@ -415,6 +418,7 @@ extension Home.StateModel:
     func enactedSuggestionDidUpdate(_ suggestion: Suggestion) {
         enactedSuggestion = suggestion
         setStatusTitle()
+        setupStatistics()
     }
 
     func pumpBatteryDidChange(_: Battery) {

+ 269 - 187
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -9,6 +9,7 @@ extension Home {
 
         @StateObject var state = StateModel()
         @State var isStatusPopupPresented = false
+        @State var selectedState: durationState
 
         private var numberFormatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -226,217 +227,296 @@ extension Home {
 
         @ViewBuilder private func statPanel() -> some View {
             if state.displayStatistics {
-                VStack(alignment: .center, spacing: 6) {
+                VStack(alignment: .center, spacing: 5) {
                     HStack {
                         Group {
-                            Text("Updated").font(.caption2)
-                                .foregroundColor(.secondary)
-                            Text(
-                                dateFormatter.string(from: state.statistics?.created_at ?? Date())
-                            ).font(.system(size: 12))
+                            durationButton(states: durationState.allCases, selectedState: $selectedState)
 
-                            Text(
-                                NSLocalizedString("Average", comment: "") + " " + state.settingsManager.settings.units.rawValue
-                            ).font(.caption2).foregroundColor(.secondary)
-                            if state.units == .mmolL {
-                                Text(
-                                    targetFormatter
-                                        .string(from: (state.statistics?.Statistics.Glucose.Average.day ?? 0) as NSNumber) ??
-                                        ""
-                                ).font(.system(size: 12))
-                            } else {
-                                Text(
-                                    tirFormatter
-                                        .string(from: (state.statistics?.Statistics.Glucose.Average.day ?? 0) as NSNumber) ??
-                                        ""
-                                ).font(.system(size: 12))
-                            }
-                            Text("Median")
-                                .font(.caption2).foregroundColor(.secondary)
-                            if state.units == .mmolL {
-                                Text(
-                                    targetFormatter
-                                        .string(from: (state.statistics?.Statistics.Glucose.Median.day ?? 0) as NSNumber) ??
-                                        ""
-                                ).font(.system(size: 12))
-                            } else {
-                                Text(
-                                    tirFormatter
-                                        .string(from: (state.statistics?.Statistics.Glucose.Median.day ?? 0) as NSNumber) ??
-                                        ""
-                                ).font(.system(size: 12))
-                            }
+                            Text("Updated").font(.caption2).foregroundColor(.secondary)
+                            Text(dateFormatter.string(from: state.statistics?.created_at ?? Date())).font(.system(size: 12))
                         }
                     }
 
-                    HStack {
-                        Group {
-                            Text(
-                                NSLocalizedString("Low (<", comment: " ") +
-                                    (targetFormatter.string(from: state.settingsManager.preferences.low as NSNumber) ?? "") + ")"
-                            ).font(.caption2).foregroundColor(.secondary)
-                            Text(
-                                (
-                                    tirFormatter
-                                        .string(from: (
-                                            state.statistics?.Statistics.Distribution.Hypos.day ?? 0
-                                        ) as NSNumber) ??
-                                        "0"
-                                ) + " %"
-                            ).font(.system(size: 12)).foregroundColor(.loopRed)
-                            Text("Normal (24h)").font(.caption2).foregroundColor(.secondary)
-                            Text(
-                                (
-                                    tirFormatter
-                                        .string(from: (state.statistics?.Statistics.Distribution.TIR.day ?? 0) as NSNumber) ??
-                                        "0"
-                                ) + " %"
-                            ).font(.system(size: 12)).foregroundColor(.loopGreen)
-                            Text(
-                                NSLocalizedString("High (>", comment: " ") +
-                                    (targetFormatter.string(from: state.settingsManager.preferences.high as NSNumber) ?? "") + ")"
-                            ).font(.caption2).foregroundColor(.secondary)
-                            Text(
-                                (
-                                    tirFormatter
-                                        .string(from: (
-                                            state.statistics?.Statistics.Distribution.Hypers.day ?? 0
-                                        ) as NSNumber) ??
-                                        "0"
-                                ) + " %"
-                            ).font(.system(size: 12)).foregroundColor(.loopYellow)
-                        }
+                    switch selectedState {
+                    case .day:
+
+                        let hba1c_all = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
+                        let average_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Average.day ?? 0) as NSNumber) ?? ""
+                        let median_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Median.day ?? 0) as NSNumber) ?? ""
+                        let tir_low = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.day ?? 0) as NSNumber) ?? ""
+                        let tir_high = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.day ?? 0) as NSNumber) ?? ""
+                        let tir_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.TIR.day ?? 0) as NSNumber) ?? ""
+                        let hba1c_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.day ?? 0) as NSNumber) ?? ""
+                        let sd_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.SD.day ?? 0) as NSNumber) ?? ""
+                        let cv_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.CV.day ?? 0) as NSNumber) ?? ""
+
+                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
+
+                    case .week:
+                        let hba1c_all = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
+                        let average_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Average.week ?? 0) as NSNumber) ?? ""
+                        let median_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Median.week ?? 0) as NSNumber) ?? ""
+                        let tir_low = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.week ?? 0) as NSNumber) ?? ""
+                        let tir_high = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.week ?? 0) as NSNumber) ?? ""
+                        let tir_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.TIR.week ?? 0) as NSNumber) ?? ""
+                        let hba1c_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.week ?? 0) as NSNumber) ?? ""
+                        let sd_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.SD.week ?? 0) as NSNumber) ?? ""
+                        let cv_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.CV.week ?? 0) as NSNumber) ?? ""
+
+                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
+
+                    case .month:
+                        let hba1c_all = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
+                        let average_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Average.month ?? 0) as NSNumber) ?? ""
+                        let median_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Median.month ?? 0) as NSNumber) ?? ""
+                        let tir_low = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.month ?? 0) as NSNumber) ?? ""
+                        let tir_high = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.month ?? 0) as NSNumber) ??
+                            ""
+                        let tir_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.TIR.month ?? 0) as NSNumber) ?? ""
+                        let hba1c_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.month ?? 0) as NSNumber) ?? ""
+                        let sd_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.SD.month ?? 0) as NSNumber) ?? ""
+                        let cv_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.CV.month ?? 0) as NSNumber) ?? ""
+
+                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
+
+                    case .ninetyDays:
+                        let hba1c_all = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
+                        let average_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Average.ninetyDays ?? 0) as NSNumber) ??
+                            ""
+                        let median_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Median.ninetyDays ?? 0) as NSNumber) ??
+                            ""
+                        let tir_low = numberFormatter
+                            .string(
+                                from: (state.statistics?.Statistics.Distribution.Hypos.ninetyDays ?? 0) as NSNumber
+                            ) ??
+                            ""
+                        let tir_high = numberFormatter
+                            .string(
+                                from: (state.statistics?.Statistics.Distribution.Hypers.ninetyDays ?? 0) as NSNumber
+                            ) ??
+                            ""
+                        let tir_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.TIR.ninetyDays ?? 0) as NSNumber) ??
+                            ""
+                        let hba1c_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.ninetyDays ?? 0) as NSNumber) ?? ""
+                        let sd_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.SD.ninetyDays ?? 0) as NSNumber) ?? ""
+                        let cv_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.CV.ninetyDays ?? 0) as NSNumber) ?? ""
+
+                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
+
+                    case .total:
+                        let hba1c_all = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
+                        let average_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Average.total ?? 0) as NSNumber) ?? ""
+                        let median_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Glucose.Median.total ?? 0) as NSNumber) ?? ""
+                        let tir_low = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.total ?? 0) as NSNumber) ?? ""
+                        let tir_high = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.total ?? 0) as NSNumber) ??
+                            ""
+                        let tir_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Distribution.TIR.total ?? 0) as NSNumber) ?? ""
+                        let hba1c_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
+                        let sd_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.SD.total ?? 0) as NSNumber) ?? ""
+                        let cv_ = numberFormatter
+                            .string(from: (state.statistics?.Statistics.Variance.CV.total ?? 0) as NSNumber) ?? ""
+
+                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
                     }
+                }
+                .frame(maxWidth: .infinity, maxHeight: 120, alignment: .center)
+            }
+        }
 
-                    HStack {
-                        Group {
-                            Text("HbA1c (24h)").font(.caption2).foregroundColor(.secondary)
-                            Text(
+        @ViewBuilder private func averageTIRhca1c(
+            _ hba1c_all: String,
+            _ average_: String,
+            _ median_: String,
+            _ tir_low: String,
+            _ tir_high: String,
+            _ tir_: String,
+            _ hba1c_: String,
+            _ sd_: String,
+            _ cv_: String
+        ) -> some View {
+            HStack {
+                Group {
+                    Text(NSLocalizedString("Average", comment: "")).font(.caption2).foregroundColor(.secondary)
+
+                    Text(average_).font(.system(size: 12))
+
+                    Text("Median")
+                        .font(.caption2).foregroundColor(.secondary)
+
+                    Text(median_).font(.system(size: 12))
+                }
+            }
+            HStack {
+                Group {
+                    Text(
+                        NSLocalizedString("Low (<", comment: " ") +
+                            (
                                 targetFormatter
-                                    .string(from: (state.statistics?.Statistics.HbA1c.day ?? 0) as NSNumber) ??
-                                    ""
-                            ).font(.system(size: 12))
+                                    .string(from: state.settingsManager.preferences.low as NSNumber) ?? ""
+                            ) + ")"
+                    ).font(.caption2)
+                        .foregroundColor(.secondary)
 
-                            Text(
-                                NSLocalizedString("All ", comment: "") +
-                                    (
-                                        targetFormatter
-                                            .string(from: (state.statistics?.GlucoseStorage_Days ?? 0) as NSNumber) ?? ""
-                                    ) +
-                                    NSLocalizedString(" days", comment: "")
-                            ).font(.caption2).foregroundColor(.secondary)
+                    Text(tir_low + " %").font(.system(size: 12)).foregroundColor(.loopRed)
 
-                            Text(
+                    Text("Normal").font(.caption2).foregroundColor(.secondary)
+
+                    Text(tir_ + " %").font(.system(size: 12)).foregroundColor(.loopGreen)
+
+                    Text(
+                        NSLocalizedString("High (>", comment: " ") +
+                            (
                                 targetFormatter
-                                    .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ??
-                                    ""
-                            ).font(.system(size: 12))
-
-                            if !state.settingsManager.preferences.displaySD {
-                                Text(
-                                    NSLocalizedString("CV (%)", comment: "CV")
-                                ).font(.caption2).foregroundColor(.secondary)
-
-                                Text(
-                                    tirFormatter
-                                        .string(from: (state.statistics?.Statistics.Variance.CV.total ?? 0) as NSNumber) ??
-                                        ""
-                                ).font(.system(size: 12))
-                            } else {
-                                Text(
-                                    NSLocalizedString("SD (", comment: "SD") + state.settingsManager.settings.units.rawValue + ")"
-                                ).font(.caption2).foregroundColor(.secondary)
-                                if state.units == .mmolL {
-                                    Text(
-                                        targetFormatter
-                                            .string(from: (state.statistics?.Statistics.Variance.SD.total ?? 0) as NSNumber) ??
-                                            ""
-                                    ).font(.system(size: 12))
-                                } else {
-                                    Text(
-                                        tirFormatter
-                                            .string(from: (state.statistics?.Statistics.Variance.SD.total ?? 0) as NSNumber) ??
-                                            ""
-                                    ).font(.system(size: 12))
-                                }
-                            }
-                        }
+                                    .string(from: state.settingsManager.preferences.high as NSNumber) ?? ""
+                            ) + ")"
+                    )
+                    .font(.caption2).foregroundColor(.secondary)
+
+                    Text(tir_high + " %").font(.system(size: 12)).foregroundColor(.loopYellow)
+                }
+            }
+            HStack {
+                Group {
+                    Text("HbA1c").font(.caption2).foregroundColor(.secondary)
+                    Text(hba1c_).font(.system(size: 12))
+
+                    if !state.settingsManager.preferences.displaySD {
+                        Text(
+                            NSLocalizedString("CV", comment: "CV")
+                        ).font(.caption2).foregroundColor(.secondary)
+
+                        Text(cv_).font(.system(size: 12))
+                    } else {
+                        Text(
+                            NSLocalizedString("SD", comment: "SD")
+                        ).font(.caption2).foregroundColor(.secondary)
+                        Text(sd_).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(
+                        NSLocalizedString("All ", comment: "") +
+                            // getString(state.statistics?.GlucoseStorage_Days, false) +
+                            NSLocalizedString(" days", comment: "")
+                    ).font(.caption2).foregroundColor(.secondary)
 
-                            Text("Average Interval").font(.caption2).foregroundColor(.secondary)
-                            Text(
-                                targetFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.avg_interval ?? 0) as NSNumber) ??
-                                    "0"
-                            ).font(.system(size: 12))
+                    Text(hba1c_all).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))
-                        }
+            if state.settingsManager.preferences.displayLoops {
+                HStack {
+                    Group {
+                        Text("Loops").font(.caption2).foregroundColor(.secondary)
+                        Text(
+                            tirFormatter
+                                .string(from: (state.statistics?.Statistics.LoopCycles.loops ?? 0) as NSNumber) ?? ""
+                        ).font(.system(size: 12))
+                        Text("Average Interval").font(.caption2)
+                            .foregroundColor(.secondary)
+                        Text(
+                            targetFormatter
+                                .string(from: (state.statistics?.Statistics.LoopCycles.avg_interval ?? 0) as NSNumber) ??
+                                ""
+                        ).font(.system(size: 12))
+                        Text("Median Duration").font(.caption2)
+                            .foregroundColor(.secondary)
+                        Text(
+                            numberFormatter
+                                .string(
+                                    from: (state.statistics?.Statistics.LoopCycles.median_duration ?? 0) as NSNumber
+                                ) ?? ""
+                        ).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)
-                    Text("BG")
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGreen)
-                }
-                Group {
-                    Circle().fill(Color.insulin).frame(width: 8, height: 8)
-                        .padding(.leading, 8)
-                    Text("IOB")
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
-                }
-                Group {
-                    Circle().fill(Color.zt).frame(width: 8, height: 8)
-                        .padding(.leading, 8)
-                    Text("ZT")
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.zt)
-                }
-                Group {
-                    Circle().fill(Color.loopYellow).frame(width: 8, height: 8)
-                        .padding(.leading, 8)
-                    Text("COB")
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.loopYellow)
-                }
-                Group {
-                    Circle().fill(Color.uam).frame(width: 8, height: 8)
-                        .padding(.leading, 8)
-                    Text("UAM")
-                        .font(.system(size: 12, weight: .bold)).foregroundColor(.uam)
-                }
+            ZStack {
+                HStack(alignment: .center) {
+                    Group {
+                        Circle().fill(Color.loopGreen).frame(width: 8, height: 8)
+                        Text("BG")
+                            .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGreen)
+                    }
+                    Group {
+                        Circle().fill(Color.insulin).frame(width: 8, height: 8)
+                            .padding(.leading, 8)
+                        Text("IOB")
+                            .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
+                    }
+                    Group {
+                        Circle().fill(Color.zt).frame(width: 8, height: 8)
+                            .padding(.leading, 8)
+                        Text("ZT")
+                            .font(.system(size: 12, weight: .bold)).foregroundColor(.zt)
+                    }
+                    Group {
+                        Circle().fill(Color.loopYellow).frame(width: 8, height: 8)
+                            .padding(.leading, 8)
+                        Text("COB")
+                            .font(.system(size: 12, weight: .bold)).foregroundColor(.loopYellow)
+                    }
+                    Group {
+                        Circle().fill(Color.uam).frame(width: 8, height: 8)
+                            .padding(.leading, 8)
+                        Text("UAM")
+                            .font(.system(size: 12, weight: .bold)).foregroundColor(.uam)
+                    }
 
-                if let eventualBG = state.eventualBG {
-                    Text(
-                        "⇢ " + numberFormatter.string(
-                            from: (state.units == .mmolL ? eventualBG.asMmolL : Decimal(eventualBG)) as NSNumber
-                        )!
-                    )
-                    .font(.system(size: 12, weight: .bold)).foregroundColor(.secondary)
+                    if let eventualBG = state.eventualBG {
+                        Text(
+                            "⇢ " + numberFormatter.string(
+                                from: (state.units == .mmolL ? eventualBG.asMmolL : Decimal(eventualBG)) as NSNumber
+                            )!
+                        )
+                        .font(.system(size: 12, weight: .bold)).foregroundColor(.secondary)
+                    }
                 }
+                .frame(maxWidth: .infinity)
             }
-            .frame(maxWidth: .infinity, maxHeight: 30)
         }
 
         var mainChart: some View {
@@ -464,7 +544,7 @@ extension Home {
                     units: $state.units
                 )
             }
-            .padding(.bottom)
+            // .padding(.bottom)
             .modal(for: .dataTable, from: self)
         }
 
@@ -578,7 +658,9 @@ extension Home {
                     .padding(.bottom, 4)
                 if let suggestion = state.suggestion {
                     TagCloudView(tags: suggestion.reasonParts).animation(.none, value: false)
+
                     Text(suggestion.reasonConclusion.capitalizingFirstLetter()).font(.caption).foregroundColor(.white)
+
                 } else {
                     Text("No sugestion found").font(.body).foregroundColor(.white)
                 }

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

@@ -76,6 +76,30 @@ extension PreferencesEditor {
                         comment: "Description for display SD"
                     ),
                     settable: self
+                ),
+                Field(
+                    displayName: NSLocalizedString(
+                        "Display Loop Cycle statistics",
+                        comment: "Display Display Loop Cycle statistics in statPanel"
+                    ),
+                    type: .boolean(keypath: \.displayLoops),
+                    infoText: NSLocalizedString(
+                        "Displays Loop statistics in the statPanel in Home View",
+                        comment: "Description for Display Loop statistics"
+                    ),
+                    settable: self
+                ),
+                Field(
+                    displayName: NSLocalizedString(
+                        "Override HbA1c unit",
+                        comment: "Display %"
+                    ),
+                    type: .boolean(keypath: \.overrideHbA1cUnit),
+                    infoText: NSLocalizedString(
+                        "Display '%' as HbA1c unit, even when using mmol/l for glucose. StatPanel will be updated with new unit with next statistics.json update",
+                        comment: "Description for Override HbA1c unit"
+                    ),
+                    settable: self
                 )
             ]
 

+ 4 - 1
FreeAPS/Sources/Router/Screen.swift

@@ -36,7 +36,10 @@ extension Screen {
         case .loading:
             ProgressView()
         case .home:
-            Home.RootView(resolver: resolver)
+            Home.RootView(
+                resolver: resolver,
+                selectedState: .day
+            )
         case .settings:
             Settings.RootView(resolver: resolver)
         case let .configEditor(file):

+ 6 - 4
FreeAPS/Sources/Views/TagCloudView.swift

@@ -57,11 +57,13 @@ struct TagCloudView: View {
             switch textTag {
             case textTag where textTag.contains("SMB Delivery Ratio:"):
                 return .uam
-            case textTag where textTag.contains("Weighted avg:"),
-                 textTag where textTag.contains("Total data avg:"):
-                return .gray
             case textTag where textTag.contains("TDD:"),
-                 textTag where textTag.contains("tdd_factor"),
+                 textTag where textTag.contains("Total insulin:"),
+                 textTag where textTag.contains("Bolus insulin:"),
+                 textTag where textTag.contains("Temp. basal insulin:"),
+                 textTag where textTag.contains("Scheduled"):
+                return .green
+            case textTag where textTag.contains("tdd_factor"),
                  textTag where textTag.contains("Sigmoid function"),
                  textTag where textTag.contains("Logarithmic formula"),
                  textTag where textTag.contains("AF:"),