ソースを参照

Version 1.0.0 (#565)

* New slim format for statPanel

* Better format for Apple Watch Home View.
Same size for HR as other items. Longpress the Heart for 1 seconds to make the heart and HR font bigger size. Long press again to reduce to before.
Align Loop Circle and min ago with BG and delta BG horisontally.
Remove inverted logic.

* Change on tap
Change display of Average / Median and CV / SD and Loops/Errors when tapping

* Fix refresh SD/CV and Average/Median values

* Revert to deafault filterTooFrequentGlucose min interval t0 4.5 minutes to prevent too frequent BG readings from GlucoseDirect and xdrip with Libre2

* Crowdin updates

* Don't run function statistics() when disabled in preferences

* Suggested test for red loops with MDT

* Revert alias back to BRANCH

* Delete Insulin from data table and from Nightscout, with required authorization

* Make carbs button same color as carb dots.

* Color glucose in Header View

* Delete code for 90 days (not necessary)

* CGM Readings in statPanel, when tapping.

* Test to increase to 90 seconds scanning when using Dexcom Manager.

* Header View UI improvements.
Dynamic fonts for Header View elements.
More prominent Glucose.
Improved horisontal alignment.
Display time delta instad of timestamp also for current glucose.

* Improve description for Sigmoid Function

* Bump version to 1
Jon B Mårtensson 3 年 前
コミット
03d8b58e75
42 ファイル変更461 行追加390 行削除
  1. 2 2
      Config.xcconfig
  2. 40 40
      Dependencies/rileylink_ios/MinimedKitUI/tr.lproj/Localizable.strings
  3. 1 1
      FreeAPS/Resources/Info.plist
  4. 0 112
      FreeAPS/Resources/json/defaults/monitor/statistics.json
  5. 8 53
      FreeAPS/Sources/APS/APSManager.swift
  6. 1 1
      FreeAPS/Sources/APS/CGM/DexcomSource.swift
  7. 1 1
      FreeAPS/Sources/APS/DeviceDataManager.swift
  8. 15 0
      FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift
  9. 8 2
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  10. 8 2
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  11. 8 2
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  12. 9 3
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  13. 8 2
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  14. 8 2
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  15. 8 2
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  16. 8 2
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  17. 8 2
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  18. 8 2
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  19. 8 2
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  20. 11 5
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  21. 8 2
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  22. 8 2
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  23. 8 2
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  24. 11 5
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  25. 8 2
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  26. 8 2
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  27. 39 33
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  28. 8 2
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  29. 8 2
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  30. 2 1
      FreeAPS/Sources/Models/Statistics.swift
  31. 1 0
      FreeAPS/Sources/Modules/DataTable/DataTableDataFlow.swift
  32. 4 0
      FreeAPS/Sources/Modules/DataTable/DataTableProvider.swift
  33. 11 0
      FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift
  34. 26 0
      FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift
  35. 0 1
      FreeAPS/Sources/Modules/Home/DurationButton.swift
  36. 47 16
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  37. 4 4
      FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift
  38. 13 10
      FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift
  39. 29 67
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  40. 1 1
      FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift
  41. 29 0
      FreeAPS/Sources/Services/Network/NightscoutAPI.swift
  42. 20 0
      FreeAPS/Sources/Services/Network/NightscoutManager.swift

+ 2 - 2
Config.xcconfig

@@ -1,7 +1,7 @@
 APP_DISPLAY_NAME = FreeAPS X
 APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 0.6.4
+APP_VERSION = 1.0.0
 APP_BUILD_NUMBER = 1
 APP_BUILD_NUMBER = 1
-COPYRIGHT_NOTICE = 
+BRANCH = 
 DEVELOPER_TEAM = ##TEAM_ID##
 DEVELOPER_TEAM = ##TEAM_ID##
 BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS
 BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS
 APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroup
 APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroup

ファイルの差分が大きいため隠しています
+ 40 - 40
Dependencies/rileylink_ios/MinimedKitUI/tr.lproj/Localizable.strings


+ 1 - 1
FreeAPS/Resources/Info.plist

@@ -70,7 +70,7 @@
 	<key>NSHealthUpdateUsageDescription</key>
 	<key>NSHealthUpdateUsageDescription</key>
 	<string>Health App is used to store blood glucose data</string>
 	<string>Health App is used to store blood glucose data</string>
 	<key>NSHumanReadableCopyright</key>
 	<key>NSHumanReadableCopyright</key>
-	<string>$(COPYRIGHT_NOTICE)</string>
+	<string>$(BRANCH)</string>
 	<key>UIApplicationSceneManifest</key>
 	<key>UIApplicationSceneManifest</key>
 	<dict>
 	<dict>
 		<key>UIApplicationSupportsMultipleScenes</key>
 		<key>UIApplicationSupportsMultipleScenes</key>

+ 0 - 112
FreeAPS/Resources/json/defaults/monitor/statistics.json

@@ -1,114 +1,2 @@
 [
 [
-  {
-    "createdAt" : "1978-02-22T11:43:54.659Z",
-    "iPhone" : "Default",
-    "iOS" : "Default",
-    "Build_Version" : "Default",
-    "Build_Number2" : "Default",
-    "Branch" : "Default",
-    "Build_Date" : "1978-02-22T11:59:59.659Z",
-    "Algorithm" : "Default",
-    "AdjustmentFactor" : 1,
-    "Pump" : "Default",
-    "CGM" : "Default",
-    "insulinType" : "Default",
-    "peakActivityTime" : 65,
-    "TDD" : 0,
-    "Carbs_24h":  0,
-    "GlucoseStorage_Days" : 0,
-    "Statistics" : {
-      "Distribution" : [
-        {
-          "TIR" : [
-            {
-              "day" : 0,
-              "sevenDays" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays" : 0,
-              "totalDays" : 0
-            }
-          ],
-          "Hypos" : [
-            {
-              "day" : 0,
-              "sevenDays" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays" : 0,
-              "totalDays" : 0
-            }
-          ],
-          "Hypers" : [
-            {
-              "day" : 0,
-              "sevenDays" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays" : 0,
-              "totalDays" : 0
-            }
-          ]
-        }
-      ],
-      "Glucose" : [
-        {
-          "Average" : [
-            {
-              "oneDay_mmol" : 0,
-              "day" : 0,
-              "sevenDays_mmol" : 0,
-              "sevenDays" : 0,
-              "thirtyDays_mmol" : 0,
-              "thirtyDays" : 0,
-              "ninetyDays_mmol" : 0,
-              "ninetyDays" : 0,
-              "totalDays_mmol" : 0,
-              "totalDays" : 0
-            }
-          ],
-          "Median" : [
-            {
-              "oneDay_mmol" : 0,
-              "day" : 0,
-              "sevenDays_mmol" : 0,
-              "sevenDays" : 0,
-              "thirtyDays_mmol":  0,
-              "thirtyDays" : 0,
-              "ninetyDays_mmol" : 0,
-              "ninetyDays" : 0,
-              "totalDays_mmol" : 0,
-              "totalDays" : 0
-            }
-          ]
-        }
-      ],
-      "HbA1c" : [
-        {
-          "oneDay_mmolMol" : 0,
-          "day" : 0,
-          "sevenDays_mmolMol" : 0,
-          "sevenDays" : 0,
-          "thirtyDays_mmolMol" : 0,
-          "thirtyDays" : 0,
-          "ninetyDays_mmolMol" : 0,
-          "ninetyDays" : 0,
-          "totalDays_mmolMol" : 0,
-          "totalDays" : 0
-        }
-      ],
-      "LoopCycles" : [
-        {
-          "loops" : 0,
-          "errors" : 0,
-          "success_rate" : 0,
-          "avg_interval" : 0,
-          "median_interval" : 0,
-          "min_interval" : 0,
-          "max_interval" : 0,
-          "avg_duration" : 0,
-          "median_duration" : 0,
-          "min_duration" : 0,
-          "max_duration" : 0
-        }
-      ]
-    }
-  }
 ]
 ]

+ 8 - 53
FreeAPS/Sources/APS/APSManager.swift

@@ -240,7 +240,9 @@ final class BaseAPSManager: APSManager, Injectable {
         loopStats(loopStatRecord: loopStatRecord)
         loopStats(loopStatRecord: loopStatRecord)
 
 
         // Create a statistics.json
         // Create a statistics.json
-        statistics()
+        if settings.displayStatistics {
+            statistics()
+        }
 
 
         if settings.closedLoop {
         if settings.closedLoop {
             reportEnacted(received: error == nil)
             reportEnacted(received: error == nil)
@@ -763,7 +765,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
         let updateThisOften = Int(settingsManager.preferences.updateInterval)
         let updateThisOften = Int(settingsManager.preferences.updateInterval)
 
 
-        // Only run every 30 minutes or when pressing statPanel
+        // Only run every 30 minutesl
         if testIfEmpty != 0 {
         if testIfEmpty != 0 {
             guard testFile[0].created_at.addingTimeInterval(updateThisOften.minutes.timeInterval) < Date()
             guard testFile[0].created_at.addingTimeInterval(updateThisOften.minutes.timeInterval) < Date()
             else {
             else {
@@ -905,18 +907,15 @@ final class BaseAPSManager: APSManager, Injectable {
         var bgArray_1_: [Double] = []
         var bgArray_1_: [Double] = []
         var bgArray_7_: [Double] = []
         var bgArray_7_: [Double] = []
         var bgArray_30_: [Double] = []
         var bgArray_30_: [Double] = []
-        var bgArray_90_: [Double] = []
         var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
         var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
         var bgArray_1: [(bg_: Double, date_: Date)] = []
         var bgArray_1: [(bg_: Double, date_: Date)] = []
         var bgArray_7: [(bg_: Double, date_: Date)] = []
         var bgArray_7: [(bg_: Double, date_: Date)] = []
         var bgArray_30: [(bg_: Double, date_: Date)] = []
         var bgArray_30: [(bg_: Double, date_: Date)] = []
-        var bgArray_90: [(bg_: Double, date_: Date)] = []
         var medianBG = 0.0
         var medianBG = 0.0
         var nr_bgs: Decimal = 0
         var nr_bgs: Decimal = 0
         var nr_bgs_1: Decimal = 0
         var nr_bgs_1: Decimal = 0
         var nr_bgs_7: Decimal = 0
         var nr_bgs_7: Decimal = 0
         var nr_bgs_30: Decimal = 0
         var nr_bgs_30: Decimal = 0
-        var nr_bgs_90: Decimal = 0
 
 
         var startDate = Date("1978-02-22T11:43:54.659Z")
         var startDate = Date("1978-02-22T11:43:54.659Z")
         if endIndex >= 0 {
         if endIndex >= 0 {
@@ -925,11 +924,9 @@ final class BaseAPSManager: APSManager, Injectable {
         var end1 = false
         var end1 = false
         var end7 = false
         var end7 = false
         var end30 = false
         var end30 = false
-        var end90 = false
         var bg_1: Decimal = 0
         var bg_1: Decimal = 0
         var bg_7: Decimal = 0
         var bg_7: Decimal = 0
         var bg_30: Decimal = 0
         var bg_30: Decimal = 0
-        var bg_90: Decimal = 0
         var bg_total: Decimal = 0
         var bg_total: Decimal = 0
         var j = -1
         var j = -1
 
 
@@ -967,14 +964,6 @@ final class BaseAPSManager: APSManager, Injectable {
                         nr_bgs_30 = nr_bgs
                         nr_bgs_30 = nr_bgs
                         // time_30 = ((startDate ?? Date()) - entry.date).timeInterval
                         // time_30 = ((startDate ?? Date()) - entry.date).timeInterval
                     }
                     }
-                    if (startDate! - entry.date).timeInterval >= 7.776E6, !end90 {
-                        end90 = true
-                        bg_90 = bg / nr_bgs
-                        bgArray_90 = bgArrayForTIR
-                        bgArray_90_ = bgArray
-                        nr_bgs_90 = nr_bgs
-                        // time_90 = ((startDate ?? Date()) - entry.date).timeInterval
-                    }
                 }
                 }
             }
             }
         }
         }
@@ -1067,14 +1056,6 @@ final class BaseAPSManager: APSManager, Injectable {
             IFCCa1CStatisticValue_30 = 10.929 *
             IFCCa1CStatisticValue_30 = 10.929 *
                 (NGSPa1CStatisticValue_30 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
                 (NGSPa1CStatisticValue_30 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
         }
         }
-        // 90 Days
-        var NGSPa1CStatisticValue_90: Decimal = 0.0
-        var IFCCa1CStatisticValue_90: Decimal = 0.0
-        if end90 {
-            NGSPa1CStatisticValue_90 = (46.7 + bg_90) / 28.7 // NGSP (%)
-            IFCCa1CStatisticValue_90 = 10.929 *
-                (NGSPa1CStatisticValue_90 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
-        }
         // Total days
         // Total days
         var NGSPa1CStatisticValue_total: Decimal = 0.0
         var NGSPa1CStatisticValue_total: Decimal = 0.0
         var IFCCa1CStatisticValue_total: Decimal = 0.0
         var IFCCa1CStatisticValue_total: Decimal = 0.0
@@ -1088,7 +1069,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))), 1),
             day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))), 1),
             week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))), 1),
             week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))), 1),
             month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))), 1),
             month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))), 1),
-            ninetyDays: roundDecimal(Decimal(medianCalculation(array: bgArray_90.map(\.bg_))), 1),
             total: roundDecimal(Decimal(medianBG), 1)
             total: roundDecimal(Decimal(medianBG), 1)
         )
         )
 
 
@@ -1096,7 +1076,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
             month: roundDecimal(NGSPa1CStatisticValue_30, 1),
             month: roundDecimal(NGSPa1CStatisticValue_30, 1),
-            ninetyDays: roundDecimal(NGSPa1CStatisticValue_90, 1),
             total: roundDecimal(NGSPa1CStatisticValue_total, 1)
             total: roundDecimal(NGSPa1CStatisticValue_total, 1)
         )
         )
 
 
@@ -1107,14 +1086,12 @@ final class BaseAPSManager: APSManager, Injectable {
             bg_1 = bg_1.asMmolL
             bg_1 = bg_1.asMmolL
             bg_7 = bg_7.asMmolL
             bg_7 = bg_7.asMmolL
             bg_30 = bg_30.asMmolL
             bg_30 = bg_30.asMmolL
-            bg_90 = bg_90.asMmolL
             bg_total = bg_total.asMmolL
             bg_total = bg_total.asMmolL
 
 
             median = Durations(
             median = Durations(
                 day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))).asMmolL, 1),
                 day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))).asMmolL, 1),
                 week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.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),
                 month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))).asMmolL, 1),
-                ninetyDays: roundDecimal(Decimal(medianCalculation(array: bgArray_90.map(\.bg_))).asMmolL, 1),
                 total: roundDecimal(Decimal(medianBG).asMmolL, 1)
                 total: roundDecimal(Decimal(medianBG).asMmolL, 1)
             )
             )
 
 
@@ -1124,7 +1101,6 @@ final class BaseAPSManager: APSManager, Injectable {
                     day: roundDecimal(IFCCa1CStatisticValue, 1),
                     day: roundDecimal(IFCCa1CStatisticValue, 1),
                     week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                     week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                     month: roundDecimal(IFCCa1CStatisticValue_30, 1),
                     month: roundDecimal(IFCCa1CStatisticValue_30, 1),
-                    ninetyDays: roundDecimal(IFCCa1CStatisticValue_90, 1),
                     total: roundDecimal(IFCCa1CStatisticValue_total, 1)
                     total: roundDecimal(IFCCa1CStatisticValue_total, 1)
                 )
                 )
             }
             }
@@ -1133,7 +1109,6 @@ final class BaseAPSManager: APSManager, Injectable {
                 day: roundDecimal(IFCCa1CStatisticValue, 1),
                 day: roundDecimal(IFCCa1CStatisticValue, 1),
                 week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                 week: roundDecimal(IFCCa1CStatisticValue_7, 1),
                 month: roundDecimal(IFCCa1CStatisticValue_30, 1),
                 month: roundDecimal(IFCCa1CStatisticValue_30, 1),
-                ninetyDays: roundDecimal(IFCCa1CStatisticValue_90, 1),
                 total: roundDecimal(IFCCa1CStatisticValue_total, 1)
                 total: roundDecimal(IFCCa1CStatisticValue_total, 1)
             )
             )
         }
         }
@@ -1141,9 +1116,13 @@ final class BaseAPSManager: APSManager, Injectable {
         // round output values
         // round output values
         daysBG = roundDouble(daysBG, 1)
         daysBG = roundDouble(daysBG, 1)
 
 
+        let glucose24Hours = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)
+        let nrOfCGMReadings = glucose24Hours?.count ?? 0
+
         let loopstat = LoopCycles(
         let loopstat = LoopCycles(
             loops: Int(successNR + errorNR),
             loops: Int(successNR + errorNR),
             errors: Int(errorNR),
             errors: Int(errorNR),
+            readings: nrOfCGMReadings,
             success_rate: Decimal(round(successRate ?? 0)),
             success_rate: Decimal(round(successRate ?? 0)),
             avg_interval: roundDecimal(Decimal(averageIntervalLoops), 1),
             avg_interval: roundDecimal(Decimal(averageIntervalLoops), 1),
             median_interval: roundDecimal(Decimal(medianInterval), 1),
             median_interval: roundDecimal(Decimal(medianInterval), 1),
@@ -1159,7 +1138,6 @@ final class BaseAPSManager: APSManager, Injectable {
         var oneDay_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var oneDay_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var sevenDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var sevenDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var thirtyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var thirtyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
-        var ninetyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var totalDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
         var totalDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
 
 
         // Get all TIR calcs for every case
         // Get all TIR calcs for every case
@@ -1172,10 +1150,6 @@ final class BaseAPSManager: APSManager, Injectable {
         if end30 {
         if end30 {
             thirtyDays_ = tir(bgArray_30)
             thirtyDays_ = tir(bgArray_30)
         }
         }
-        if end90 {
-            ninetyDays_ = tir(bgArray_90)
-        }
-
         if nr_bgs > 0 {
         if nr_bgs > 0 {
             totalDays_ = tir(bgArrayForTIR)
             totalDays_ = tir(bgArrayForTIR)
         }
         }
@@ -1184,7 +1158,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(Decimal(oneDay_.TIR), 1),
             day: roundDecimal(Decimal(oneDay_.TIR), 1),
             week: roundDecimal(Decimal(sevenDays_.TIR), 1),
             week: roundDecimal(Decimal(sevenDays_.TIR), 1),
             month: roundDecimal(Decimal(thirtyDays_.TIR), 1),
             month: roundDecimal(Decimal(thirtyDays_.TIR), 1),
-            ninetyDays: roundDecimal(Decimal(ninetyDays_.TIR), 1),
             total: roundDecimal(Decimal(totalDays_.TIR), 1)
             total: roundDecimal(Decimal(totalDays_.TIR), 1)
         )
         )
 
 
@@ -1192,7 +1165,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: Decimal(oneDay_.hypos),
             day: Decimal(oneDay_.hypos),
             week: Decimal(sevenDays_.hypos),
             week: Decimal(sevenDays_.hypos),
             month: Decimal(thirtyDays_.hypos),
             month: Decimal(thirtyDays_.hypos),
-            ninetyDays: Decimal(ninetyDays_.hypos),
             total: Decimal(totalDays_.hypos)
             total: Decimal(totalDays_.hypos)
         )
         )
 
 
@@ -1200,7 +1172,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: Decimal(oneDay_.hypers),
             day: Decimal(oneDay_.hypers),
             week: Decimal(sevenDays_.hypers),
             week: Decimal(sevenDays_.hypers),
             month: Decimal(thirtyDays_.hypers),
             month: Decimal(thirtyDays_.hypers),
-            ninetyDays: Decimal(ninetyDays_.hypers),
             total: Decimal(totalDays_.hypers)
             total: Decimal(totalDays_.hypers)
         )
         )
 
 
@@ -1210,7 +1181,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(bg_1, 1),
             day: roundDecimal(bg_1, 1),
             week: roundDecimal(bg_7, 1),
             week: roundDecimal(bg_7, 1),
             month: roundDecimal(bg_30, 1),
             month: roundDecimal(bg_30, 1),
-            ninetyDays: roundDecimal(bg_90, 1),
             total: roundDecimal(bg_total, 1)
             total: roundDecimal(bg_total, 1)
         )
         )
 
 
@@ -1231,7 +1201,6 @@ final class BaseAPSManager: APSManager, Injectable {
         var sumOfSquares_1: Decimal = 0
         var sumOfSquares_1: Decimal = 0
         var sumOfSquares_7: Decimal = 0
         var sumOfSquares_7: Decimal = 0
         var sumOfSquares_30: Decimal = 0
         var sumOfSquares_30: Decimal = 0
-        var sumOfSquares_90: Decimal = 0
 
 
         // Total
         // Total
         for array in bgArray {
         for array in bgArray {
@@ -1257,12 +1226,6 @@ final class BaseAPSManager: APSManager, Injectable {
                 sumOfSquares_30 += pow(Decimal(array_30).asMmolL - bg_30, 2)
                 sumOfSquares_30 += pow(Decimal(array_30).asMmolL - bg_30, 2)
             } else { sumOfSquares_30 += pow(Decimal(array_30) - bg_30, 2) }
             } else { sumOfSquares_30 += pow(Decimal(array_30) - bg_30, 2) }
         }
         }
-        // 90 days
-        for array_90 in bgArray_90_ {
-            if units == .mmolL {
-                sumOfSquares_90 += pow(Decimal(array_90).asMmolL - bg_90, 2)
-            } else { sumOfSquares_90 += pow(Decimal(array_90) - bg_90, 2) }
-        }
 
 
         // Standard deviation and Coefficient of variation
         // Standard deviation and Coefficient of variation
         var sd_total = 0.0
         var sd_total = 0.0
@@ -1273,8 +1236,6 @@ final class BaseAPSManager: APSManager, Injectable {
         var cv_7 = 0.0
         var cv_7 = 0.0
         var sd_30 = 0.0
         var sd_30 = 0.0
         var cv_30 = 0.0
         var cv_30 = 0.0
-        var sd_90 = 0.0
-        var cv_90 = 0.0
 
 
         // Avoid division by zero
         // Avoid division by zero
         if avgs.total < 1 || nr_bgs < 1 { sd_total = 0
         if avgs.total < 1 || nr_bgs < 1 { sd_total = 0
@@ -1300,17 +1261,12 @@ final class BaseAPSManager: APSManager, Injectable {
             cv_30 = 0 } else { sd_30 = sqrt(Double(sumOfSquares_30 / nr_bgs_30))
             cv_30 = 0 } else { sd_30 = sqrt(Double(sumOfSquares_30 / nr_bgs_30))
             cv_30 = sd_30 / Double(bg_30) * 100
             cv_30 = sd_30 / Double(bg_30) * 100
         }
         }
-        if avgs.ninetyDays < 1 || nr_bgs_90 < 1 { sd_90 = 0
-            cv_90 = 0 } else { sd_90 = sqrt(Double(sumOfSquares_90 / nr_bgs_90))
-            cv_90 = sd_90 / Double(bg_90) * 100
-        }
 
 
         // Standard Deviations
         // Standard Deviations
         let standardDeviations = Durations(
         let standardDeviations = Durations(
             day: roundDecimal(Decimal(sd_1), 1),
             day: roundDecimal(Decimal(sd_1), 1),
             week: roundDecimal(Decimal(sd_7), 1),
             week: roundDecimal(Decimal(sd_7), 1),
             month: roundDecimal(Decimal(sd_30), 1),
             month: roundDecimal(Decimal(sd_30), 1),
-            ninetyDays: roundDecimal(Decimal(sd_90), 1),
             total: roundDecimal(Decimal(sd_total), 1)
             total: roundDecimal(Decimal(sd_total), 1)
         )
         )
 
 
@@ -1319,7 +1275,6 @@ final class BaseAPSManager: APSManager, Injectable {
             day: roundDecimal(Decimal(cv_1), 1),
             day: roundDecimal(Decimal(cv_1), 1),
             week: roundDecimal(Decimal(cv_7), 1),
             week: roundDecimal(Decimal(cv_7), 1),
             month: roundDecimal(Decimal(cv_30), 1),
             month: roundDecimal(Decimal(cv_30), 1),
-            ninetyDays: roundDecimal(Decimal(cv_90), 1),
             total: roundDecimal(Decimal(cv_total), 1)
             total: roundDecimal(Decimal(cv_total), 1)
         )
         )
 
 

+ 1 - 1
FreeAPS/Sources/APS/CGM/DexcomSource.swift

@@ -24,7 +24,7 @@ final class DexcomSource: GlucoseSource {
         return Future<[BloodGlucose], Error> { [weak self] promise in
         return Future<[BloodGlucose], Error> { [weak self] promise in
             self?.promise = promise
             self?.promise = promise
         }
         }
-        .timeout(60, scheduler: processQueue, options: nil, customError: nil)
+        .timeout(90, scheduler: processQueue, options: nil, customError: nil)
         .replaceError(with: [])
         .replaceError(with: [])
         .replaceEmpty(with: [])
         .replaceEmpty(with: [])
         .eraseToAnyPublisher()
         .eraseToAnyPublisher()

+ 1 - 1
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -163,7 +163,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                 }
                 }
             }
             }
         }
         }
-        .timeout(20, scheduler: processQueue)
+        .timeout(30, scheduler: processQueue)
         .replaceError(with: false)
         .replaceError(with: false)
         .replaceEmpty(with: false)
         .replaceEmpty(with: false)
         .sink(receiveValue: updateUpdateFinished)
         .sink(receiveValue: updateUpdateFinished)

+ 15 - 0
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -14,6 +14,7 @@ protocol PumpHistoryStorage {
     func recent() -> [PumpHistoryEvent]
     func recent() -> [PumpHistoryEvent]
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment]
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment]
     func saveCancelTempEvents()
     func saveCancelTempEvents()
+    func deleteInsulin(at date: Date)
 }
 }
 
 
 final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
 final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
@@ -194,6 +195,20 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
         storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self)?.reversed() ?? []
         storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self)?.reversed() ?? []
     }
     }
 
 
+    func deleteInsulin(at date: Date) {
+        processQueue.sync {
+            var allValues = storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self) ?? []
+            guard let entryIndex = allValues.firstIndex(where: { $0.timestamp == date }) else {
+                return
+            }
+            allValues.remove(at: entryIndex)
+            storage.save(allValues, as: OpenAPS.Monitor.pumpHistory)
+            broadcaster.notify(PumpHistoryObserver.self, on: processQueue) {
+                $0.pumpHistoryDidUpdate(allValues)
+            }
+        }
+    }
+
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment] {
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment] {
         let events = recent()
         let events = recent()
         guard !events.isEmpty else { return [] }
         guard !events.isEmpty else { return [] }

ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 9 - 3
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 11 - 5
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 11 - 5
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 39 - 33
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 8 - 2
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 2 - 1
FreeAPS/Sources/Models/Statistics.swift

@@ -87,6 +87,7 @@ extension Statistics {
 struct LoopCycles: JSON, Equatable {
 struct LoopCycles: JSON, Equatable {
     var loops: Int
     var loops: Int
     var errors: Int
     var errors: Int
+    var readings: Int
     var success_rate: Decimal
     var success_rate: Decimal
     var avg_interval: Decimal
     var avg_interval: Decimal
     var median_interval: Decimal
     var median_interval: Decimal
@@ -107,7 +108,6 @@ struct Durations: JSON, Equatable {
     var day: Decimal
     var day: Decimal
     var week: Decimal
     var week: Decimal
     var month: Decimal
     var month: Decimal
-    var ninetyDays: Decimal
     var total: Decimal
     var total: Decimal
 }
 }
 
 
@@ -142,6 +142,7 @@ extension LoopCycles {
     private enum CodingKeys: String, CodingKey {
     private enum CodingKeys: String, CodingKey {
         case loops
         case loops
         case errors
         case errors
+        case readings
         case success_rate
         case success_rate
         case avg_interval
         case avg_interval
         case median_interval
         case median_interval

+ 1 - 0
FreeAPS/Sources/Modules/DataTable/DataTableDataFlow.swift

@@ -173,5 +173,6 @@ protocol DataTableProvider: Provider {
     func carbs() -> [CarbsEntry]
     func carbs() -> [CarbsEntry]
     func glucose() -> [BloodGlucose]
     func glucose() -> [BloodGlucose]
     func deleteCarbs(at date: Date)
     func deleteCarbs(at date: Date)
+    func deleteInsulin(at date: Date)
     func deleteGlucose(id: String)
     func deleteGlucose(id: String)
 }
 }

+ 4 - 0
FreeAPS/Sources/Modules/DataTable/DataTableProvider.swift

@@ -25,6 +25,10 @@ extension DataTable {
             nightscoutManager.deleteCarbs(at: date)
             nightscoutManager.deleteCarbs(at: date)
         }
         }
 
 
+        func deleteInsulin(at date: Date) {
+            nightscoutManager.deleteInsulin(at: date)
+        }
+
         func glucose() -> [BloodGlucose] {
         func glucose() -> [BloodGlucose] {
             glucoseStorage.recent().sorted { $0.date > $1.date }
             glucoseStorage.recent().sorted { $0.date > $1.date }
         }
         }

+ 11 - 0
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -3,6 +3,8 @@ import SwiftUI
 extension DataTable {
 extension DataTable {
     final class StateModel: BaseStateModel<Provider> {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
         @Injected() var broadcaster: Broadcaster!
+        @Injected() var unlockmanager: UnlockManager!
+
         @Published var mode: Mode = .treatments
         @Published var mode: Mode = .treatments
         @Published var treatments: [Treatment] = []
         @Published var treatments: [Treatment] = []
         @Published var glucose: [Glucose] = []
         @Published var glucose: [Glucose] = []
@@ -92,6 +94,15 @@ extension DataTable {
             provider.deleteCarbs(at: date)
             provider.deleteCarbs(at: date)
         }
         }
 
 
+        func deleteInsulin(at date: Date) {
+            unlockmanager.unlock()
+                .sink { _ in } receiveValue: { [weak self] _ in
+                    guard let self = self else { return }
+                    self.provider.deleteInsulin(at: date)
+                }
+                .store(in: &lifetime)
+        }
+
         func deleteGlucose(at index: Int) {
         func deleteGlucose(at index: Int) {
             let id = glucose[index].id
             let id = glucose[index].id
             provider.deleteGlucose(id: id)
             provider.deleteGlucose(id: id)

+ 26 - 0
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -5,9 +5,13 @@ extension DataTable {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
         @StateObject var state = StateModel()
         @StateObject var state = StateModel()
+
         @State private var isRemoveCarbsAlertPresented = false
         @State private var isRemoveCarbsAlertPresented = false
         @State private var removeCarbsAlert: Alert?
         @State private var removeCarbsAlert: Alert?
 
 
+        @State private var isRemoveInsulinAlertPresented = false
+        @State private var removeInsulinAlert: Alert?
+
         private var glucoseFormatter: NumberFormatter {
         private var glucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
             formatter.numberStyle = .decimal
@@ -100,6 +104,28 @@ extension DataTable {
                             removeCarbsAlert!
                             removeCarbsAlert!
                         }
                         }
                 }
                 }
+
+                if item.type == .bolus {
+                    Spacer()
+                    Image(systemName: "xmark.circle").foregroundColor(.secondary)
+                        .contentShape(Rectangle())
+                        .padding(.vertical)
+                        .onTapGesture {
+                            removeInsulinAlert = Alert(
+                                title: Text("Delete insulin?"),
+                                message: Text(item.amountText),
+                                primaryButton: .destructive(
+                                    Text("Delete"),
+                                    action: { state.deleteInsulin(at: item.date) }
+                                ),
+                                secondaryButton: .cancel()
+                            )
+                            isRemoveInsulinAlertPresented = true
+                        }
+                        .alert(isPresented: $isRemoveInsulinAlertPresented) {
+                            removeInsulinAlert!
+                        }
+                }
             }
             }
         }
         }
 
 

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

@@ -14,7 +14,6 @@ enum durationState: String, DurationButton {
     case day = "Past 24 Hours "
     case day = "Past 24 Hours "
     case week = "Past Week "
     case week = "Past Week "
     case month = "Past Month "
     case month = "Past Month "
-    case ninetyDays = "Past 90 Days "
     case total = "All Past Days of Data "
     case total = "All Past Days of Data "
 }
 }
 
 

+ 47 - 16
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -27,6 +27,14 @@ struct CurrentGlucoseView: View {
         return formatter
         return formatter
     }
     }
 
 
+    private var timaAgoFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        formatter.negativePrefix = ""
+        return formatter
+    }
+
     private var dateFormatter: DateFormatter {
     private var dateFormatter: DateFormatter {
         let formatter = DateFormatter()
         let formatter = DateFormatter()
         formatter.timeStyle = .short
         formatter.timeStyle = .short
@@ -34,8 +42,8 @@ struct CurrentGlucoseView: View {
     }
     }
 
 
     var body: some View {
     var body: some View {
-        VStack(alignment: .center, spacing: 6) {
-            HStack(spacing: 8) {
+        VStack(alignment: .center) {
+            HStack {
                 Text(
                 Text(
                     recentGlucose?.glucose
                     recentGlucose?.glucose
                         .map {
                         .map {
@@ -43,24 +51,30 @@ struct CurrentGlucoseView: View {
                                 .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! }
                                 .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! }
                         ?? "--"
                         ?? "--"
                 )
                 )
-                .font(.system(size: 24, weight: .bold))
-                .fixedSize()
-                .foregroundColor(alarm == nil ? .primary : .loopRed)
-                image.padding(.bottom, 2)
+                .font(.title).fontWeight(.bold)
+                .foregroundColor(alarm == nil ? colorOfGlucose : .loopRed)
 
 
-            }.padding(.leading, 4)
-            HStack(alignment: .lastTextBaseline, spacing: 2) {
+                image
+            }
+            HStack {
+                let minutes = (recentGlucose?.dateString.timeIntervalSinceNow ?? 0) / 60
+                let text = timaAgoFormatter.string(for: Double(minutes)) ?? ""
                 Text(
                 Text(
-                    recentGlucose.map { dateFormatter.string(from: $0.dateString) } ?? "--"
-                ).font(.caption2).foregroundColor(.secondary)
+                    text == "0" ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
+                        text + " " +
+                            NSLocalizedString("min", comment: "Short form for minutes") + " "
+                    )
+                )
+                .font(.caption2).foregroundColor(.secondary)
+
                 Text(
                 Text(
                     delta
                     delta
-                        .map { deltaFormatter.string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)!
-                        } ??
-                        "--"
-
-                ).font(.system(size: 12, weight: .bold))
-            }
+                        .map {
+                            deltaFormatter.string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)!
+                        } ?? "--"
+                )
+                .font(.caption2).foregroundColor(.secondary)
+            }.frame(alignment: .top)
         }
         }
     }
     }
 
 
@@ -91,4 +105,21 @@ struct CurrentGlucoseView: View {
             return Image(systemName: "arrow.left.and.right")
             return Image(systemName: "arrow.left.and.right")
         }
         }
     }
     }
+
+    var colorOfGlucose: Color {
+        let whichGlucose = recentGlucose?.glucose ?? 0
+
+        switch whichGlucose {
+        case 71 ... 145:
+            return .loopGreen
+        case 1 ... 55,
+             217...:
+            return .loopRed
+        case 56 ... 70,
+             146 ... 216:
+            return .loopYellow
+        default:
+            return .primary
+        }
+    }
 }
 }

+ 4 - 4
FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift

@@ -21,13 +21,13 @@ struct LoopView: View {
         return formatter
         return formatter
     }
     }
 
 
-    private let rect = CGRect(x: 0, y: 0, width: 32, height: 32)
+    private let rect = CGRect(x: 0, y: 0, width: 28, height: 28)
     var body: some View {
     var body: some View {
         VStack(alignment: .center) {
         VStack(alignment: .center) {
             ZStack {
             ZStack {
                 Circle()
                 Circle()
-                    .strokeBorder(color, lineWidth: 6)
-                    .frame(width: rect.width, height: rect.height)
+                    .strokeBorder(color, lineWidth: 5)
+                    .frame(width: rect.width, height: rect.height, alignment: .bottom)
                     .mask(mask(in: rect).fill(style: FillStyle(eoFill: true)))
                     .mask(mask(in: rect).fill(style: FillStyle(eoFill: true)))
                 if isLooping {
                 if isLooping {
                     ProgressView()
                     ProgressView()
@@ -51,7 +51,7 @@ struct LoopView: View {
         if minAgo > 1440 {
         if minAgo > 1440 {
             return "--"
             return "--"
         }
         }
-        return "\(minAgo) " + NSLocalizedString("min ago", comment: "Minutes ago since last loop")
+        return "\(minAgo) " + NSLocalizedString("min", comment: "Minutes ago since last loop")
     }
     }
 
 
     private var color: Color {
     private var color: Color {

+ 13 - 10
FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift

@@ -27,28 +27,30 @@ struct PumpView: View {
                     Image(systemName: "drop.fill")
                     Image(systemName: "drop.fill")
                         .resizable()
                         .resizable()
                         .aspectRatio(contentMode: .fit)
                         .aspectRatio(contentMode: .fit)
-                        .frame(height: 8)
+                        .frame(maxHeight: 10)
                         .foregroundColor(reservoirColor)
                         .foregroundColor(reservoirColor)
                     if reservoir == 0xDEAD_BEEF {
                     if reservoir == 0xDEAD_BEEF {
-                        Text("50+ " + NSLocalizedString("U", comment: "Insulin unit")).font(.system(size: 12, weight: .bold))
+                        Text("50+ " + NSLocalizedString("U", comment: "Insulin unit")).font(.footnote)
+                            .fontWeight(.bold)
                     } else {
                     } else {
                         Text(
                         Text(
                             reservoirFormatter
                             reservoirFormatter
                                 .string(from: reservoir as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
                                 .string(from: reservoir as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
                         )
                         )
-                        .font(.system(size: 12, weight: .bold))
+                        .font(.footnote).fontWeight(.bold)
                     }
                     }
-                }
+                }.frame(alignment: .top)
             }
             }
             if let battery = battery, battery.display ?? false, expiresAtDate == nil {
             if let battery = battery, battery.display ?? false, expiresAtDate == nil {
                 HStack {
                 HStack {
                     Image(systemName: "battery.100")
                     Image(systemName: "battery.100")
                         .resizable()
                         .resizable()
                         .aspectRatio(contentMode: .fit)
                         .aspectRatio(contentMode: .fit)
-                        .frame(height: 8)
+                        .frame(maxHeight: 10)
                         .foregroundColor(batteryColor)
                         .foregroundColor(batteryColor)
-                    Text("\(Int(battery.percent ?? 100)) %").font(.system(size: 12, weight: .bold))
-                }
+                    Text("\(Int(battery.percent ?? 100)) %").font(.footnote)
+                        .fontWeight(.bold)
+                }.frame(alignment: .bottom)
             }
             }
 
 
             if let date = expiresAtDate {
             if let date = expiresAtDate {
@@ -56,10 +58,11 @@ struct PumpView: View {
                     Image(systemName: "stopwatch.fill")
                     Image(systemName: "stopwatch.fill")
                         .resizable()
                         .resizable()
                         .aspectRatio(contentMode: .fit)
                         .aspectRatio(contentMode: .fit)
-                        .frame(height: 8)
+                        .frame(maxHeight: 10)
                         .foregroundColor(timerColor)
                         .foregroundColor(timerColor)
-                    Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.system(size: 12, weight: .bold))
-                }
+                    Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.footnote)
+                        .fontWeight(.bold)
+                }.frame(alignment: .bottom)
             }
             }
         }
         }
     }
     }

+ 29 - 67
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -11,20 +11,19 @@ extension Home {
         @State var isStatusPopupPresented = false
         @State var isStatusPopupPresented = false
         @State var selectedState: durationState
         @State var selectedState: durationState
 
 
-        // Average/Median and CV/SD titles and values switches when you click them
+        // Average/Median/Readings and CV/SD titles and values switches when you tap them
         @State var averageOrMedianTitle = NSLocalizedString("Average", comment: "")
         @State var averageOrMedianTitle = NSLocalizedString("Average", comment: "")
         @State var median_ = ""
         @State var median_ = ""
         @State var average_ = ""
         @State var average_ = ""
+        @State var readings = ""
+
         @State var averageOrmedian = ""
         @State var averageOrmedian = ""
         @State var CV_or_SD_Title = NSLocalizedString("CV", comment: "CV")
         @State var CV_or_SD_Title = NSLocalizedString("CV", comment: "CV")
         @State var cv_ = ""
         @State var cv_ = ""
         @State var sd_ = ""
         @State var sd_ = ""
         @State var CVorSD = ""
         @State var CVorSD = ""
-        // Switch between Loops and Errors when tapping in ststPanel
+        // Switch between Loops and Errors when tapping in statPanel
         @State var loopStatTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
         @State var loopStatTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
-        @State var loopsOrerrors = ""
-
-        public let paddingSpace: CGFloat = 15
 
 
         private var numberFormatter: NumberFormatter {
         private var numberFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
@@ -73,29 +72,29 @@ extension Home {
                 Spacer()
                 Spacer()
             }
             }
             .frame(maxWidth: .infinity)
             .frame(maxWidth: .infinity)
-            .frame(maxHeight: 70)
             .padding(.top, geo.safeAreaInsets.top)
             .padding(.top, geo.safeAreaInsets.top)
+            .padding(.bottom)
             .background(Color.gray.opacity(0.2))
             .background(Color.gray.opacity(0.2))
         }
         }
 
 
         var cobIobView: some View {
         var cobIobView: some View {
             VStack(alignment: .leading, spacing: 12) {
             VStack(alignment: .leading, spacing: 12) {
                 HStack {
                 HStack {
-                    Text("IOB").font(.caption2).foregroundColor(.secondary)
+                    Text("IOB").font(.footnote).foregroundColor(.secondary)
                     Text(
                     Text(
                         (numberFormatter.string(from: (state.suggestion?.iob ?? 0) as NSNumber) ?? "0") +
                         (numberFormatter.string(from: (state.suggestion?.iob ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" U", comment: "Insulin unit")
                             NSLocalizedString(" U", comment: "Insulin unit")
                     )
                     )
-                    .font(.system(size: 12, weight: .bold))
-                }
+                    .font(.footnote).fontWeight(.bold)
+                }.frame(alignment: .top)
                 HStack {
                 HStack {
-                    Text("COB").font(.caption2).foregroundColor(.secondary)
+                    Text("COB").font(.footnote).foregroundColor(.secondary)
                     Text(
                     Text(
                         (numberFormatter.string(from: (state.suggestion?.cob ?? 0) as NSNumber) ?? "0") +
                         (numberFormatter.string(from: (state.suggestion?.cob ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" g", comment: "gram of carbs")
                             NSLocalizedString(" g", comment: "gram of carbs")
                     )
                     )
-                    .font(.system(size: 12, weight: .bold))
-                }
+                    .font(.footnote).fontWeight(.bold)
+                }.frame(alignment: .bottom)
             }
             }
         }
         }
 
 
@@ -313,37 +312,6 @@ extension Home {
 
 
                         averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
                         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_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Average.ninetyDays ?? 0) as NSNumber) ??
-                            ""
-                        let median_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Median.ninetyDays ?? 0) as NSNumber) ??
-                            ""
-                        let tir_low = tirFormatter
-                            .string(
-                                from: (state.statistics?.Statistics.Distribution.Hypos.ninetyDays ?? 0) as NSNumber
-                            ) ??
-                            ""
-                        let tir_high = tirFormatter
-                            .string(
-                                from: (state.statistics?.Statistics.Distribution.Hypers.ninetyDays ?? 0) as NSNumber
-                            ) ??
-                            ""
-                        let tir_ = tirFormatter
-                            .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_ = tirFormatter
-                            .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:
                     case .total:
                         let hba1c_all = numberFormatter
                         let hba1c_all = numberFormatter
                             .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
                             .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
@@ -403,19 +371,30 @@ extension Home {
                     // Average as default. Changes to Median when clicking.
                     // Average as default. Changes to Median when clicking.
                     let textAverageTitle = NSLocalizedString("Average", comment: "")
                     let textAverageTitle = NSLocalizedString("Average", comment: "")
                     let textMedianTitle = NSLocalizedString("Median", comment: "")
                     let textMedianTitle = NSLocalizedString("Median", comment: "")
+                    let cgmReadingsTitle = NSLocalizedString("Readings", comment: "CGM readings in statPanel")
 
 
                     HStack {
                     HStack {
                         Text(averageOrMedianTitle).font(.footnote).foregroundColor(.secondary)
                         Text(averageOrMedianTitle).font(.footnote).foregroundColor(.secondary)
                         if averageOrMedianTitle == textAverageTitle {
                         if averageOrMedianTitle == textAverageTitle {
                             Text(averageOrmedian == "" ? average_ : average_).font(.footnote)
                             Text(averageOrmedian == "" ? average_ : average_).font(.footnote)
-                        } else {
+                        } else if averageOrMedianTitle == textMedianTitle {
                             Text(averageOrmedian == "" ? median_ : median_).font(.footnote)
                             Text(averageOrmedian == "" ? median_ : median_).font(.footnote)
+                        } else if averageOrMedianTitle == cgmReadingsTitle {
+                            Text(
+                                averageOrmedian != "0" ? tirFormatter
+                                    .string(from: (state.statistics?.Statistics.LoopCycles.readings ?? 0) as NSNumber) ?? "" : ""
+                            )
+                            .font(.footnote)
                         }
                         }
                     }.onTapGesture {
                     }.onTapGesture {
                         if averageOrMedianTitle == textAverageTitle {
                         if averageOrMedianTitle == textAverageTitle {
                             averageOrMedianTitle = textMedianTitle
                             averageOrMedianTitle = textMedianTitle
                             averageOrmedian = median_
                             averageOrmedian = median_
-                        } else {
+                        } else if averageOrMedianTitle == textMedianTitle {
+                            averageOrMedianTitle = cgmReadingsTitle
+                            averageOrmedian = tirFormatter
+                                .string(from: (state.statistics?.Statistics.LoopCycles.readings ?? 0) as NSNumber) ?? ""
+                        } else if averageOrMedianTitle == cgmReadingsTitle {
                             averageOrMedianTitle = textAverageTitle
                             averageOrMedianTitle = textAverageTitle
                             averageOrmedian = average_
                             averageOrmedian = average_
                         }
                         }
@@ -480,19 +459,16 @@ extension Home {
                         HStack {
                         HStack {
                             Text(loopStatTitle).font(.footnote).foregroundColor(.secondary)
                             Text(loopStatTitle).font(.footnote).foregroundColor(.secondary)
                             Text(
                             Text(
-                                loopsOrerrors == "" ? tirFormatter
+                                loopStatTitle == loopTitle ? tirFormatter
                                     .string(from: (state.statistics?.Statistics.LoopCycles.loops ?? 0) as NSNumber) ?? "" :
                                     .string(from: (state.statistics?.Statistics.LoopCycles.loops ?? 0) as NSNumber) ?? "" :
-                                    loopsOrerrors
+                                    tirFormatter
+                                    .string(from: (state.statistics?.Statistics.LoopCycles.errors ?? 0) as NSNumber) ?? ""
                             ).font(.footnote)
                             ).font(.footnote)
                         }.onTapGesture {
                         }.onTapGesture {
                             if loopStatTitle == loopTitle {
                             if loopStatTitle == loopTitle {
                                 loopStatTitle = errorTitle
                                 loopStatTitle = errorTitle
-                                loopsOrerrors = tirFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.errors ?? 0) as NSNumber) ?? ""
                             } else if loopStatTitle == errorTitle {
                             } else if loopStatTitle == errorTitle {
                                 loopStatTitle = loopTitle
                                 loopStatTitle = loopTitle
-                                loopsOrerrors = tirFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.loops ?? 0) as NSNumber) ?? ""
                             }
                             }
                         }
                         }
 
 
@@ -609,7 +585,7 @@ extension Home {
                                 .renderingMode(.template)
                                 .renderingMode(.template)
                                 .resizable()
                                 .resizable()
                                 .frame(width: 24, height: 24)
                                 .frame(width: 24, height: 24)
-                                .foregroundColor(.loopGreen)
+                                .foregroundColor(.loopYellow)
                                 .padding(8)
                                 .padding(8)
                             if let carbsReq = state.carbsRequired {
                             if let carbsReq = state.carbsRequired {
                                 Text(numberFormatter.string(from: carbsReq as NSNumber)!)
                                 Text(numberFormatter.string(from: carbsReq as NSNumber)!)
@@ -628,7 +604,7 @@ extension Home {
                             .resizable()
                             .resizable()
                             .frame(width: 24, height: 24)
                             .frame(width: 24, height: 24)
                             .padding(8)
                             .padding(8)
-                    }.foregroundColor(.loopYellow)
+                    }.foregroundColor(.loopGreen)
                     Spacer()
                     Spacer()
                     Button { state.showModal(for: .bolus(waitForSuggestion: false)) }
                     Button { state.showModal(for: .bolus(waitForSuggestion: false)) }
                     label: {
                     label: {
@@ -724,19 +700,5 @@ 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
-            }
-        }
     }
     }
 }
 }

ファイルの差分が大きいため隠しています
+ 1 - 1
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift


+ 29 - 0
FreeAPS/Sources/Services/Network/NightscoutAPI.swift

@@ -166,6 +166,35 @@ extension NightscoutAPI {
             .eraseToAnyPublisher()
             .eraseToAnyPublisher()
     }
     }
 
 
+    func deleteInsulin(at date: Date) -> AnyPublisher<Void, Swift.Error> {
+        var components = URLComponents()
+        components.scheme = url.scheme
+        components.host = url.host
+        components.port = url.port
+        components.path = Config.treatmentsPath
+        components.queryItems = [
+            URLQueryItem(name: "find[bolus][$exists]", value: "true"),
+            URLQueryItem(
+                name: "find[created_at][$eq]",
+                value: Formatter.iso8601withFractionalSeconds.string(from: date)
+            )
+        ]
+
+        var request = URLRequest(url: components.url!)
+        request.allowsConstrainedNetworkAccess = false
+        request.timeoutInterval = Config.timeout
+        request.httpMethod = "DELETE"
+
+        if let secret = secret {
+            request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
+        }
+
+        return service.run(request)
+            .retry(Config.retryCount)
+            .map { _ in () }
+            .eraseToAnyPublisher()
+    }
+
     func fetchTempTargets(sinceDate: Date? = nil) -> AnyPublisher<[TempTarget], Swift.Error> {
     func fetchTempTargets(sinceDate: Date? = nil) -> AnyPublisher<[TempTarget], Swift.Error> {
         var components = URLComponents()
         var components = URLComponents()
         components.scheme = url.scheme
         components.scheme = url.scheme

+ 20 - 0
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -9,6 +9,7 @@ protocol NightscoutManager: GlucoseSource {
     func fetchTempTargets() -> AnyPublisher<[TempTarget], Never>
     func fetchTempTargets() -> AnyPublisher<[TempTarget], Never>
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never>
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never>
     func deleteCarbs(at date: Date)
     func deleteCarbs(at date: Date)
+    func deleteInsulin(at date: Date)
     func uploadStatus()
     func uploadStatus()
     func uploadStatistics(dailystat: Statistics)
     func uploadStatistics(dailystat: Statistics)
     func uploadPreferences()
     func uploadPreferences()
@@ -180,6 +181,25 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             .store(in: &lifetime)
             .store(in: &lifetime)
     }
     }
 
 
+    func deleteInsulin(at date: Date) {
+        guard let nightscout = nightscoutAPI, isUploadEnabled else {
+            pumpHistoryStorage.deleteInsulin(at: date)
+            return
+        }
+
+        nightscout.deleteInsulin(at: date)
+            .sink { completion in
+                switch completion {
+                case .finished:
+                    self.pumpHistoryStorage.deleteInsulin(at: date)
+                    debug(.nightscout, "Carbs deleted")
+                case let .failure(error):
+                    debug(.nightscout, error.localizedDescription)
+                }
+            } receiveValue: {}
+            .store(in: &lifetime)
+    }
+
     func uploadStatistics(dailystat: Statistics) {
     func uploadStatistics(dailystat: Statistics) {
         let stats = NightscoutStatistics(
         let stats = NightscoutStatistics(
             dailystats: dailystat
             dailystats: dailystat