Jelajahi Sumber

Pull in latest 'core-data-sync-trio' branch

polscm32 aka Marvout 1 tahun lalu
induk
melakukan
45224cde96
100 mengubah file dengan 2441 tambahan dan 1636 penghapusan
  1. 208 144
      FreeAPS.xcodeproj/project.pbxproj
  2. 9 0
      FreeAPS/Sources/APS/APSManager.swift
  3. 1 1
      FreeAPS/Sources/APS/CGM/CGMType.swift
  4. 99 0
      FreeAPS/Sources/Helpers/MainChartHelper.swift
  5. 1 1
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  6. 1 1
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  7. 1 1
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  8. 1 1
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  9. 1 1
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  10. 1 1
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  11. 1 1
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  12. 1 1
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  13. 1 1
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  14. 1 1
      FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings
  15. 1 1
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  16. 1 1
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  17. 1 1
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  18. 1 1
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  19. 1 1
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  20. 1 1
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  21. 1 1
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  22. 1 1
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  23. 1 1
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  24. 1 1
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  25. 1 1
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  26. 1 1
      FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings
  27. 1 1
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  28. 2 2
      FreeAPS/Sources/Models/BGTargets.swift
  29. 2 2
      FreeAPS/Sources/Models/DecimalPickerSettings.swift
  30. 10 10
      FreeAPS/Sources/Models/Determination.swift
  31. 3 3
      FreeAPS/Sources/Models/InsulinSensitivities.swift
  32. 68 100
      FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift
  33. 1 1
      FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift
  34. 19 31
      FreeAPS/Sources/Modules/Bolus/View/ForeCastChart.swift
  35. 14 2
      FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift
  36. 1 1
      FreeAPS/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift
  37. 132 114
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  38. 249 0
      FreeAPS/Sources/Modules/Home/View/Chart/BasalChart.swift
  39. 63 0
      FreeAPS/Sources/Modules/Home/View/Chart/CarbView.swift
  40. 67 0
      FreeAPS/Sources/Modules/Home/View/Chart/CobChart.swift
  41. 54 0
      FreeAPS/Sources/Modules/Home/View/Chart/DummyCharts.swift
  42. 91 0
      FreeAPS/Sources/Modules/Home/View/Chart/ForecastView.swift
  43. 66 0
      FreeAPS/Sources/Modules/Home/View/Chart/GlucoseChartView.swift
  44. 42 0
      FreeAPS/Sources/Modules/Home/View/Chart/InsulinView.swift
  45. 63 0
      FreeAPS/Sources/Modules/Home/View/Chart/IobChart.swift
  46. 171 960
      FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift
  47. 50 0
      FreeAPS/Sources/Modules/Home/View/Chart/OverrideView.swift
  48. 82 0
      FreeAPS/Sources/Modules/Home/View/Chart/TempTargets.swift
  49. 11 16
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  50. 49 26
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  51. 7 1
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorProvider.swift
  52. 1 1
      FreeAPS/Sources/Modules/MealSettings/View/MealSettingsRootView.swift
  53. 20 15
      FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift
  54. 16 43
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift
  55. 3 9
      FreeAPS/Sources/Modules/NightscoutConfig/View/ProfileImport/NightscoutImportResultView.swift
  56. 1 1
      FreeAPS/Sources/Modules/NightscoutConfig/View/ProfileImport/ReviewInsulinActionView.swift
  57. 12 1
      FreeAPS/Sources/Modules/OverrideConfig/View/OverrideRootView.swift
  58. 2 1
      FreeAPS/Sources/Modules/Settings/SettingItems.swift
  59. 7 2
      FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift
  60. 1 3
      FreeAPS/Sources/Modules/Settings/View/Subviews/ServicesView.swift
  61. 4 1
      FreeAPS/Sources/Modules/Settings/View/TidepoolStartView.swift
  62. 13 1
      FreeAPS/Sources/Modules/Stat/View/StatRootView.swift
  63. 8 1
      FreeAPS/Sources/Modules/TargetsEditor/TargetsEditorProvider.swift
  64. 3 0
      FreeAPS/Sources/Router/Screen.swift
  65. 3 2
      FreeAPS/Sources/Services/Calendar/CalendarManager.swift
  66. 125 2
      FreeAPS/Sources/Services/Network/NightscoutManager.swift
  67. 112 1
      FreeAPS/Sources/Services/Storage/FileStorage.swift
  68. 14 0
      FreeAPS/Sources/Shortcuts/AppShortcuts.swift
  69. 2 0
      FreeAPS/Sources/Shortcuts/BaseIntentsRequest.swift
  70. 88 0
      FreeAPS/Sources/Shortcuts/Override/ApplyOverridePresetIntent.swift
  71. 25 0
      FreeAPS/Sources/Shortcuts/Override/CancelOverrideIntent.swift
  72. 30 0
      FreeAPS/Sources/Shortcuts/Override/ListOverridePresetIntent.swift
  73. 33 0
      FreeAPS/Sources/Shortcuts/Override/OverridePresetEntity.swift
  74. 171 0
      FreeAPS/Sources/Shortcuts/Override/OverridePresetsIntentRequest.swift
  75. 89 97
      FreeAPS/Sources/Views/TagCloudView.swift
  76. 0 4
      ImportError+CoreDataClass.swift
  77. 0 13
      ImportError+CoreDataProperties.swift
  78. 0 0
      Model/Classes+Properties/BolusStored+CoreDataClass.swift
  79. 0 0
      Model/Classes+Properties/BolusStored+CoreDataProperties.swift
  80. 0 0
      Model/Classes+Properties/CarbEntryStored+CoreDataClass.swift
  81. 0 0
      Model/Classes+Properties/CarbEntryStored+CoreDataProperties.swift
  82. 0 0
      Model/Classes+Properties/Forecast+CoreDataClass.swift
  83. 0 0
      Model/Classes+Properties/Forecast+CoreDataProperties.swift
  84. 0 0
      Model/Classes+Properties/ForecastValue+CoreDataClass.swift
  85. 0 0
      Model/Classes+Properties/ForecastValue+CoreDataProperties.swift
  86. 0 0
      Model/Classes+Properties/GlucoseStored+CoreDataClass.swift
  87. 0 0
      Model/Classes+Properties/GlucoseStored+CoreDataProperties.swift
  88. 0 0
      Model/Classes+Properties/LoopStatRecord+CoreDataClass.swift
  89. 0 0
      Model/Classes+Properties/LoopStatRecord+CoreDataProperties.swift
  90. 0 0
      Model/Classes+Properties/MealPresetStored+CoreDataClass.swift
  91. 0 0
      Model/Classes+Properties/MealPresetStored+CoreDataProperties.swift
  92. 0 0
      Model/Classes+Properties/OpenAPS_Battery+CoreDataClass.swift
  93. 0 0
      Model/Classes+Properties/OpenAPS_Battery+CoreDataProperties.swift
  94. 0 0
      Model/Classes+Properties/OrefDetermination+CoreDataClass.swift
  95. 1 1
      OrefDetermination+CoreDataProperties.swift
  96. 0 0
      Model/Classes+Properties/OverrideRunStored+CoreDataClass.swift
  97. 0 0
      Model/Classes+Properties/OverrideRunStored+CoreDataProperties.swift
  98. 0 0
      Model/Classes+Properties/OverrideStored+CoreDataClass.swift
  99. 0 0
      Model/Classes+Properties/OverrideStored+CoreDataProperties.swift
  100. 0 0
      PumpEventStored+CoreDataClass.swift

+ 208 - 144
FreeAPS.xcodeproj/project.pbxproj

@@ -17,6 +17,11 @@
 		110AEDEC2C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE72C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift */; };
 		110AEDEC2C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE72C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift */; };
 		110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE82C51A0AE00615CC9 /* ShortcutsConfigProvider.swift */; };
 		110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE82C51A0AE00615CC9 /* ShortcutsConfigProvider.swift */; };
 		110AEDEE2C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE92C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift */; };
 		110AEDEE2C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE92C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift */; };
+		118DF76A2C5ECBC60067FEB7 /* ApplyOverridePresetIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 118DF7642C5ECBC60067FEB7 /* ApplyOverridePresetIntent.swift */; };
+		118DF76B2C5ECBC60067FEB7 /* CancelOverrideIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 118DF7652C5ECBC60067FEB7 /* CancelOverrideIntent.swift */; };
+		118DF76C2C5ECBC60067FEB7 /* ListOverridePresetIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 118DF7662C5ECBC60067FEB7 /* ListOverridePresetIntent.swift */; };
+		118DF76D2C5ECBC60067FEB7 /* OverridePresetEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 118DF7672C5ECBC60067FEB7 /* OverridePresetEntity.swift */; };
+		118DF76E2C5ECBC60067FEB7 /* OverridePresetsIntentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 118DF7682C5ECBC60067FEB7 /* OverridePresetsIntentRequest.swift */; };
 		17A9D0899046B45E87834820 /* CarbRatioEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8D5F457B5AFF763F8CF3DF /* CarbRatioEditorProvider.swift */; };
 		17A9D0899046B45E87834820 /* CarbRatioEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8D5F457B5AFF763F8CF3DF /* CarbRatioEditorProvider.swift */; };
 		19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19012CDB291D2CB900FB8210 /* LoopStats.swift */; };
 		19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19012CDB291D2CB900FB8210 /* LoopStats.swift */; };
 		190EBCC429FF136900BA767D /* UserInterfaceSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC329FF136900BA767D /* UserInterfaceSettingsDataFlow.swift */; };
 		190EBCC429FF136900BA767D /* UserInterfaceSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC329FF136900BA767D /* UserInterfaceSettingsDataFlow.swift */; };
@@ -242,6 +247,10 @@
 		581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A82BCEEDF800BF67D7 /* NSPredicates.swift */; };
 		581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A82BCEEDF800BF67D7 /* NSPredicates.swift */; };
 		581AC4392BE22ED10038760C /* JSONConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581AC4382BE22ED10038760C /* JSONConverter.swift */; };
 		581AC4392BE22ED10038760C /* JSONConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581AC4382BE22ED10038760C /* JSONConverter.swift */; };
 		58237D9E2BCF0A6B00A47A79 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58237D9D2BCF0A6B00A47A79 /* PopupView.swift */; };
 		58237D9E2BCF0A6B00A47A79 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58237D9D2BCF0A6B00A47A79 /* PopupView.swift */; };
+		582DF9752C8CDB92001F516D /* GlucoseChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582DF9742C8CDB92001F516D /* GlucoseChartView.swift */; };
+		582DF9772C8CDBE7001F516D /* InsulinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582DF9762C8CDBE7001F516D /* InsulinView.swift */; };
+		582DF9792C8CE1E5001F516D /* MainChartHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582DF9782C8CE1E5001F516D /* MainChartHelper.swift */; };
+		582DF97B2C8CE209001F516D /* CarbView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582DF97A2C8CE209001F516D /* CarbView.swift */; };
 		582FAE432C05102C00D1C13F /* CoreDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582FAE422C05102C00D1C13F /* CoreDataError.swift */; };
 		582FAE432C05102C00D1C13F /* CoreDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582FAE422C05102C00D1C13F /* CoreDataError.swift */; };
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
@@ -250,6 +259,13 @@
 		5864E8592C42CFAE00294306 /* DeterminationStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864E8582C42CFAE00294306 /* DeterminationStorage.swift */; };
 		5864E8592C42CFAE00294306 /* DeterminationStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864E8582C42CFAE00294306 /* DeterminationStorage.swift */; };
 		587DA1F62B77F3DD00B28F8A /* SettingsRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */; };
 		587DA1F62B77F3DD00B28F8A /* SettingsRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */; };
 		5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5887527B2BD986E1008B081D /* OpenAPSBattery.swift */; };
 		5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5887527B2BD986E1008B081D /* OpenAPSBattery.swift */; };
+		58D08B222C8DAA8E00AA37D3 /* OverrideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B212C8DAA8E00AA37D3 /* OverrideView.swift */; };
+		58D08B302C8DEA7500AA37D3 /* ForecastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B2F2C8DEA7500AA37D3 /* ForecastView.swift */; };
+		58D08B322C8DF88900AA37D3 /* DummyCharts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B312C8DF88900AA37D3 /* DummyCharts.swift */; };
+		58D08B342C8DF9A700AA37D3 /* CobChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B332C8DF9A700AA37D3 /* CobChart.swift */; };
+		58D08B362C8DFAC600AA37D3 /* IobChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B352C8DFAC600AA37D3 /* IobChart.swift */; };
+		58D08B382C8DFB6000AA37D3 /* BasalChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B372C8DFB6000AA37D3 /* BasalChart.swift */; };
+		58D08B3A2C8DFECD00AA37D3 /* TempTargets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B392C8DFECD00AA37D3 /* TempTargets.swift */; };
 		58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F107732BD1A4D000B1A680 /* Determination+helper.swift */; };
 		58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F107732BD1A4D000B1A680 /* Determination+helper.swift */; };
 		5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */; };
 		5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */; };
 		5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */; };
 		5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */; };
@@ -418,40 +434,6 @@
 		DD1745552C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */; };
 		DD1745552C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */; };
 		DD1DB7CC2BECCA1F0048B367 /* BuildDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */; };
 		DD1DB7CC2BECCA1F0048B367 /* BuildDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */; };
 		DD21FCB52C6952AD00AF2C25 /* DecimalPickerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */; };
 		DD21FCB52C6952AD00AF2C25 /* DecimalPickerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */; };
-		DD57C4B22C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4902C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift */; };
-		DD57C4B32C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4912C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift */; };
-		DD57C4B42C4C7103001A5B28 /* MealPresetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4922C4C7103001A5B28 /* MealPresetStored+CoreDataClass.swift */; };
-		DD57C4B52C4C7103001A5B28 /* MealPresetStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4932C4C7103001A5B28 /* MealPresetStored+CoreDataProperties.swift */; };
-		DD57C4B62C4C7103001A5B28 /* TempTargets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4942C4C7103001A5B28 /* TempTargets+CoreDataClass.swift */; };
-		DD57C4B72C4C7103001A5B28 /* TempTargets+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4952C4C7103001A5B28 /* TempTargets+CoreDataProperties.swift */; };
-		DD57C4B82C4C7103001A5B28 /* ForecastValue+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4962C4C7103001A5B28 /* ForecastValue+CoreDataClass.swift */; };
-		DD57C4B92C4C7103001A5B28 /* ForecastValue+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4972C4C7103001A5B28 /* ForecastValue+CoreDataProperties.swift */; };
-		DD57C4BA2C4C7103001A5B28 /* OrefDetermination+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4982C4C7103001A5B28 /* OrefDetermination+CoreDataClass.swift */; };
-		DD57C4BB2C4C7103001A5B28 /* OrefDetermination+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4992C4C7103001A5B28 /* OrefDetermination+CoreDataProperties.swift */; };
-		DD57C4BC2C4C7103001A5B28 /* PumpEventStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C49A2C4C7103001A5B28 /* PumpEventStored+CoreDataClass.swift */; };
-		DD57C4BD2C4C7103001A5B28 /* PumpEventStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C49B2C4C7103001A5B28 /* PumpEventStored+CoreDataProperties.swift */; };
-		DD57C4BE2C4C7103001A5B28 /* TempBasalStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C49C2C4C7103001A5B28 /* TempBasalStored+CoreDataClass.swift */; };
-		DD57C4BF2C4C7103001A5B28 /* TempBasalStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C49D2C4C7103001A5B28 /* TempBasalStored+CoreDataProperties.swift */; };
-		DD57C4C02C4C7103001A5B28 /* TempTargetsSlider+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C49E2C4C7103001A5B28 /* TempTargetsSlider+CoreDataClass.swift */; };
-		DD57C4C12C4C7103001A5B28 /* TempTargetsSlider+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C49F2C4C7103001A5B28 /* TempTargetsSlider+CoreDataProperties.swift */; };
-		DD57C4C22C4C7103001A5B28 /* Forecast+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A02C4C7103001A5B28 /* Forecast+CoreDataClass.swift */; };
-		DD57C4C32C4C7103001A5B28 /* Forecast+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A12C4C7103001A5B28 /* Forecast+CoreDataProperties.swift */; };
-		DD57C4C42C4C7103001A5B28 /* BolusStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A22C4C7103001A5B28 /* BolusStored+CoreDataClass.swift */; };
-		DD57C4C52C4C7103001A5B28 /* BolusStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A32C4C7103001A5B28 /* BolusStored+CoreDataProperties.swift */; };
-		DD57C4C62C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A42C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift */; };
-		DD57C4C72C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A52C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift */; };
-		DD57C4C82C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A62C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift */; };
-		DD57C4C92C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A72C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift */; };
-		DD57C4CA2C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A82C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift */; };
-		DD57C4CB2C4C7103001A5B28 /* GlucoseStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4A92C4C7103001A5B28 /* GlucoseStored+CoreDataProperties.swift */; };
-		DD57C4CC2C4C7103001A5B28 /* OverrideStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4AA2C4C7103001A5B28 /* OverrideStored+CoreDataClass.swift */; };
-		DD57C4CD2C4C7103001A5B28 /* OverrideStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4AB2C4C7103001A5B28 /* OverrideStored+CoreDataProperties.swift */; };
-		DD57C4CE2C4C7103001A5B28 /* OverrideRunStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4AC2C4C7103001A5B28 /* OverrideRunStored+CoreDataClass.swift */; };
-		DD57C4CF2C4C7103001A5B28 /* OverrideRunStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4AD2C4C7103001A5B28 /* OverrideRunStored+CoreDataProperties.swift */; };
-		DD57C4D02C4C7103001A5B28 /* ImportError+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4AE2C4C7103001A5B28 /* ImportError+CoreDataClass.swift */; };
-		DD57C4D12C4C7103001A5B28 /* ImportError+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4AF2C4C7103001A5B28 /* ImportError+CoreDataProperties.swift */; };
-		DD57C4D22C4C7103001A5B28 /* StatsData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4B02C4C7103001A5B28 /* StatsData+CoreDataClass.swift */; };
-		DD57C4D32C4C7103001A5B28 /* StatsData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4B12C4C7103001A5B28 /* StatsData+CoreDataProperties.swift */; };
 		DD68889D2C386E17006E3C44 /* NightscoutExercise.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD68889C2C386E17006E3C44 /* NightscoutExercise.swift */; };
 		DD68889D2C386E17006E3C44 /* NightscoutExercise.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD68889C2C386E17006E3C44 /* NightscoutExercise.swift */; };
 		DD6B7CB22C7B6F0800B75029 /* Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB12C7B6F0800B75029 /* Rounding.swift */; };
 		DD6B7CB22C7B6F0800B75029 /* Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB12C7B6F0800B75029 /* Rounding.swift */; };
 		DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */; };
 		DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */; };
@@ -466,6 +448,38 @@
 		DDD1631A2C4C695E00CD525A /* EditOverrideForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD163192C4C695E00CD525A /* EditOverrideForm.swift */; };
 		DDD1631A2C4C695E00CD525A /* EditOverrideForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD163192C4C695E00CD525A /* EditOverrideForm.swift */; };
 		DDD1631C2C4C697400CD525A /* AddOverrideForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD1631B2C4C697400CD525A /* AddOverrideForm.swift */; };
 		DDD1631C2C4C697400CD525A /* AddOverrideForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD1631B2C4C697400CD525A /* AddOverrideForm.swift */; };
 		DDD1631F2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DDD1631D2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld */; };
 		DDD1631F2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DDD1631D2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld */; };
+		DDE179522C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179322C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift */; };
+		DDE179532C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179332C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift */; };
+		DDE179542C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179342C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift */; };
+		DDE179552C910127003CDDB7 /* LoopStatRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179352C910127003CDDB7 /* LoopStatRecord+CoreDataProperties.swift */; };
+		DDE179562C910127003CDDB7 /* BolusStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179362C910127003CDDB7 /* BolusStored+CoreDataClass.swift */; };
+		DDE179572C910127003CDDB7 /* BolusStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179372C910127003CDDB7 /* BolusStored+CoreDataProperties.swift */; };
+		DDE179582C910127003CDDB7 /* ForecastValue+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179382C910127003CDDB7 /* ForecastValue+CoreDataClass.swift */; };
+		DDE179592C910127003CDDB7 /* ForecastValue+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179392C910127003CDDB7 /* ForecastValue+CoreDataProperties.swift */; };
+		DDE1795A2C910127003CDDB7 /* CarbEntryStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1793A2C910127003CDDB7 /* CarbEntryStored+CoreDataClass.swift */; };
+		DDE1795B2C910127003CDDB7 /* CarbEntryStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1793B2C910127003CDDB7 /* CarbEntryStored+CoreDataProperties.swift */; };
+		DDE1795C2C910127003CDDB7 /* TempTargets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1793C2C910127003CDDB7 /* TempTargets+CoreDataClass.swift */; };
+		DDE1795D2C910127003CDDB7 /* TempTargets+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1793D2C910127003CDDB7 /* TempTargets+CoreDataProperties.swift */; };
+		DDE1795E2C910127003CDDB7 /* PumpEventStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1793E2C910127003CDDB7 /* PumpEventStored+CoreDataClass.swift */; };
+		DDE1795F2C910127003CDDB7 /* PumpEventStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1793F2C910127003CDDB7 /* PumpEventStored+CoreDataProperties.swift */; };
+		DDE179602C910127003CDDB7 /* StatsData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179402C910127003CDDB7 /* StatsData+CoreDataClass.swift */; };
+		DDE179612C910127003CDDB7 /* StatsData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179412C910127003CDDB7 /* StatsData+CoreDataProperties.swift */; };
+		DDE179622C910127003CDDB7 /* Forecast+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179422C910127003CDDB7 /* Forecast+CoreDataClass.swift */; };
+		DDE179632C910127003CDDB7 /* Forecast+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179432C910127003CDDB7 /* Forecast+CoreDataProperties.swift */; };
+		DDE179642C910127003CDDB7 /* GlucoseStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179442C910127003CDDB7 /* GlucoseStored+CoreDataClass.swift */; };
+		DDE179652C910127003CDDB7 /* GlucoseStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179452C910127003CDDB7 /* GlucoseStored+CoreDataProperties.swift */; };
+		DDE179662C910127003CDDB7 /* OpenAPS_Battery+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179462C910127003CDDB7 /* OpenAPS_Battery+CoreDataClass.swift */; };
+		DDE179672C910127003CDDB7 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179472C910127003CDDB7 /* OpenAPS_Battery+CoreDataProperties.swift */; };
+		DDE179682C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179482C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift */; };
+		DDE179692C910127003CDDB7 /* TempBasalStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179492C910127003CDDB7 /* TempBasalStored+CoreDataProperties.swift */; };
+		DDE1796A2C910127003CDDB7 /* TempTargetsSlider+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794A2C910127003CDDB7 /* TempTargetsSlider+CoreDataClass.swift */; };
+		DDE1796B2C910127003CDDB7 /* TempTargetsSlider+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794B2C910127003CDDB7 /* TempTargetsSlider+CoreDataProperties.swift */; };
+		DDE1796C2C910127003CDDB7 /* OverrideRunStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794C2C910127003CDDB7 /* OverrideRunStored+CoreDataClass.swift */; };
+		DDE1796D2C910127003CDDB7 /* OverrideRunStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794D2C910127003CDDB7 /* OverrideRunStored+CoreDataProperties.swift */; };
+		DDE1796E2C910127003CDDB7 /* OrefDetermination+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794E2C910127003CDDB7 /* OrefDetermination+CoreDataClass.swift */; };
+		DDE1796F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift */; };
+		DDE179702C910127003CDDB7 /* OverrideStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179502C910127003CDDB7 /* OverrideStored+CoreDataClass.swift */; };
+		DDE179712C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179512C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift */; };
 		DDF847DD2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DC2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift */; };
 		DDF847DD2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DC2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift */; };
 		DDF847DF2C5C28780049BB3B /* LiveActivitySettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */; };
 		DDF847DF2C5C28780049BB3B /* LiveActivitySettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */; };
 		DDF847E12C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */; };
 		DDF847E12C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */; };
@@ -593,6 +607,11 @@
 		110AEDE72C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConfigDataFlow.swift; sourceTree = "<group>"; };
 		110AEDE72C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConfigDataFlow.swift; sourceTree = "<group>"; };
 		110AEDE82C51A0AE00615CC9 /* ShortcutsConfigProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConfigProvider.swift; sourceTree = "<group>"; };
 		110AEDE82C51A0AE00615CC9 /* ShortcutsConfigProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConfigProvider.swift; sourceTree = "<group>"; };
 		110AEDE92C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConfigStateModel.swift; sourceTree = "<group>"; };
 		110AEDE92C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConfigStateModel.swift; sourceTree = "<group>"; };
+		118DF7642C5ECBC60067FEB7 /* ApplyOverridePresetIntent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplyOverridePresetIntent.swift; sourceTree = "<group>"; };
+		118DF7652C5ECBC60067FEB7 /* CancelOverrideIntent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CancelOverrideIntent.swift; sourceTree = "<group>"; };
+		118DF7662C5ECBC60067FEB7 /* ListOverridePresetIntent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListOverridePresetIntent.swift; sourceTree = "<group>"; };
+		118DF7672C5ECBC60067FEB7 /* OverridePresetEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverridePresetEntity.swift; sourceTree = "<group>"; };
+		118DF7682C5ECBC60067FEB7 /* OverridePresetsIntentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverridePresetsIntentRequest.swift; sourceTree = "<group>"; };
 		19012CDB291D2CB900FB8210 /* LoopStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopStats.swift; sourceTree = "<group>"; };
 		19012CDB291D2CB900FB8210 /* LoopStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopStats.swift; sourceTree = "<group>"; };
 		190EBCC329FF136900BA767D /* UserInterfaceSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsDataFlow.swift; sourceTree = "<group>"; };
 		190EBCC329FF136900BA767D /* UserInterfaceSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsDataFlow.swift; sourceTree = "<group>"; };
 		190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsProvider.swift; sourceTree = "<group>"; };
 		190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsProvider.swift; sourceTree = "<group>"; };
@@ -880,6 +899,10 @@
 		581516A82BCEEDF800BF67D7 /* NSPredicates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPredicates.swift; sourceTree = "<group>"; };
 		581516A82BCEEDF800BF67D7 /* NSPredicates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPredicates.swift; sourceTree = "<group>"; };
 		581AC4382BE22ED10038760C /* JSONConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONConverter.swift; sourceTree = "<group>"; };
 		581AC4382BE22ED10038760C /* JSONConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONConverter.swift; sourceTree = "<group>"; };
 		58237D9D2BCF0A6B00A47A79 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = "<group>"; };
 		58237D9D2BCF0A6B00A47A79 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = "<group>"; };
+		582DF9742C8CDB92001F516D /* GlucoseChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseChartView.swift; sourceTree = "<group>"; };
+		582DF9762C8CDBE7001F516D /* InsulinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsulinView.swift; sourceTree = "<group>"; };
+		582DF9782C8CE1E5001F516D /* MainChartHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainChartHelper.swift; sourceTree = "<group>"; };
+		582DF97A2C8CE209001F516D /* CarbView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbView.swift; sourceTree = "<group>"; };
 		582FAE422C05102C00D1C13F /* CoreDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataError.swift; sourceTree = "<group>"; };
 		582FAE422C05102C00D1C13F /* CoreDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataError.swift; sourceTree = "<group>"; };
 		583684052BD178DB00070A60 /* GlucoseStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+helper.swift"; sourceTree = "<group>"; };
 		583684052BD178DB00070A60 /* GlucoseStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+helper.swift"; sourceTree = "<group>"; };
 		583684072BD195A700070A60 /* Determination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Determination.swift; sourceTree = "<group>"; };
 		583684072BD195A700070A60 /* Determination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Determination.swift; sourceTree = "<group>"; };
@@ -888,6 +911,13 @@
 		5864E8582C42CFAE00294306 /* DeterminationStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeterminationStorage.swift; sourceTree = "<group>"; };
 		5864E8582C42CFAE00294306 /* DeterminationStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeterminationStorage.swift; sourceTree = "<group>"; };
 		587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowView.swift; sourceTree = "<group>"; };
 		587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowView.swift; sourceTree = "<group>"; };
 		5887527B2BD986E1008B081D /* OpenAPSBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAPSBattery.swift; sourceTree = "<group>"; };
 		5887527B2BD986E1008B081D /* OpenAPSBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAPSBattery.swift; sourceTree = "<group>"; };
+		58D08B212C8DAA8E00AA37D3 /* OverrideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideView.swift; sourceTree = "<group>"; };
+		58D08B2F2C8DEA7500AA37D3 /* ForecastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastView.swift; sourceTree = "<group>"; };
+		58D08B312C8DF88900AA37D3 /* DummyCharts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyCharts.swift; sourceTree = "<group>"; };
+		58D08B332C8DF9A700AA37D3 /* CobChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CobChart.swift; sourceTree = "<group>"; };
+		58D08B352C8DFAC600AA37D3 /* IobChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IobChart.swift; sourceTree = "<group>"; };
+		58D08B372C8DFB6000AA37D3 /* BasalChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasalChart.swift; sourceTree = "<group>"; };
+		58D08B392C8DFECD00AA37D3 /* TempTargets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargets.swift; sourceTree = "<group>"; };
 		58F107732BD1A4D000B1A680 /* Determination+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Determination+helper.swift"; sourceTree = "<group>"; };
 		58F107732BD1A4D000B1A680 /* Determination+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Determination+helper.swift"; sourceTree = "<group>"; };
 		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
 		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
 		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
 		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
@@ -1059,40 +1089,6 @@
 		DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsLimitsSettingsRootView.swift; sourceTree = "<group>"; };
 		DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsLimitsSettingsRootView.swift; sourceTree = "<group>"; };
 		DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildDetails.swift; sourceTree = "<group>"; };
 		DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildDetails.swift; sourceTree = "<group>"; };
 		DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalPickerSettings.swift; sourceTree = "<group>"; };
 		DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalPickerSettings.swift; sourceTree = "<group>"; };
-		DD57C4902C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4912C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4922C4C7103001A5B28 /* MealPresetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4932C4C7103001A5B28 /* MealPresetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4942C4C7103001A5B28 /* TempTargets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargets+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4952C4C7103001A5B28 /* TempTargets+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargets+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4962C4C7103001A5B28 /* ForecastValue+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForecastValue+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4972C4C7103001A5B28 /* ForecastValue+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForecastValue+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4982C4C7103001A5B28 /* OrefDetermination+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4992C4C7103001A5B28 /* OrefDetermination+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C49A2C4C7103001A5B28 /* PumpEventStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEventStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C49B2C4C7103001A5B28 /* PumpEventStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEventStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C49C2C4C7103001A5B28 /* TempBasalStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C49D2C4C7103001A5B28 /* TempBasalStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C49E2C4C7103001A5B28 /* TempTargetsSlider+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetsSlider+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C49F2C4C7103001A5B28 /* TempTargetsSlider+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetsSlider+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A02C4C7103001A5B28 /* Forecast+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A12C4C7103001A5B28 /* Forecast+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A22C4C7103001A5B28 /* BolusStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BolusStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A32C4C7103001A5B28 /* BolusStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BolusStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A42C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A52C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A62C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAPS_Battery+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A72C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAPS_Battery+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A82C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4A92C4C7103001A5B28 /* GlucoseStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4AA2C4C7103001A5B28 /* OverrideStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4AB2C4C7103001A5B28 /* OverrideStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4AC2C4C7103001A5B28 /* OverrideRunStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideRunStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4AD2C4C7103001A5B28 /* OverrideRunStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideRunStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4AE2C4C7103001A5B28 /* ImportError+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImportError+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4AF2C4C7103001A5B28 /* ImportError+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImportError+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4B02C4C7103001A5B28 /* StatsData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		DD57C4B12C4C7103001A5B28 /* StatsData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		DD68889C2C386E17006E3C44 /* NightscoutExercise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutExercise.swift; sourceTree = "<group>"; };
 		DD68889C2C386E17006E3C44 /* NightscoutExercise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutExercise.swift; sourceTree = "<group>"; };
 		DD6B7CB12C7B6F0800B75029 /* Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rounding.swift; sourceTree = "<group>"; };
 		DD6B7CB12C7B6F0800B75029 /* Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rounding.swift; sourceTree = "<group>"; };
 		DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDisplayType.swift; sourceTree = "<group>"; };
 		DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDisplayType.swift; sourceTree = "<group>"; };
@@ -1107,6 +1103,38 @@
 		DDD163192C4C695E00CD525A /* EditOverrideForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditOverrideForm.swift; sourceTree = "<group>"; };
 		DDD163192C4C695E00CD525A /* EditOverrideForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditOverrideForm.swift; sourceTree = "<group>"; };
 		DDD1631B2C4C697400CD525A /* AddOverrideForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOverrideForm.swift; sourceTree = "<group>"; };
 		DDD1631B2C4C697400CD525A /* AddOverrideForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOverrideForm.swift; sourceTree = "<group>"; };
 		DDD1631E2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TrioCoreDataPersistentContainer.xcdatamodel; sourceTree = "<group>"; };
 		DDD1631E2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TrioCoreDataPersistentContainer.xcdatamodel; sourceTree = "<group>"; };
+		DDE179322C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179332C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179342C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179352C910127003CDDB7 /* LoopStatRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179362C910127003CDDB7 /* BolusStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BolusStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179372C910127003CDDB7 /* BolusStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BolusStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179382C910127003CDDB7 /* ForecastValue+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForecastValue+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179392C910127003CDDB7 /* ForecastValue+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForecastValue+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE1793A2C910127003CDDB7 /* CarbEntryStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE1793B2C910127003CDDB7 /* CarbEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE1793C2C910127003CDDB7 /* TempTargets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargets+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE1793D2C910127003CDDB7 /* TempTargets+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargets+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE1793E2C910127003CDDB7 /* PumpEventStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEventStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE1793F2C910127003CDDB7 /* PumpEventStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEventStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179402C910127003CDDB7 /* StatsData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179412C910127003CDDB7 /* StatsData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179422C910127003CDDB7 /* Forecast+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179432C910127003CDDB7 /* Forecast+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179442C910127003CDDB7 /* GlucoseStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179452C910127003CDDB7 /* GlucoseStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179462C910127003CDDB7 /* OpenAPS_Battery+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAPS_Battery+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179472C910127003CDDB7 /* OpenAPS_Battery+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAPS_Battery+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179482C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179492C910127003CDDB7 /* TempBasalStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE1794A2C910127003CDDB7 /* TempTargetsSlider+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetsSlider+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE1794B2C910127003CDDB7 /* TempTargetsSlider+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetsSlider+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE1794C2C910127003CDDB7 /* OverrideRunStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideRunStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE1794D2C910127003CDDB7 /* OverrideRunStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideRunStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE1794E2C910127003CDDB7 /* OrefDetermination+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE1794F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDE179502C910127003CDDB7 /* OverrideStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataClass.swift"; sourceTree = "<group>"; };
+		DDE179512C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		DDF847DC2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsDataFlow.swift; sourceTree = "<group>"; };
 		DDF847DC2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsDataFlow.swift; sourceTree = "<group>"; };
 		DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsProvider.swift; sourceTree = "<group>"; };
 		DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsProvider.swift; sourceTree = "<group>"; };
 		DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsStateModel.swift; sourceTree = "<group>"; };
 		DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsStateModel.swift; sourceTree = "<group>"; };
@@ -1256,6 +1284,18 @@
 			path = ShortcutsConfig;
 			path = ShortcutsConfig;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		118DF7692C5ECBC60067FEB7 /* Override */ = {
+			isa = PBXGroup;
+			children = (
+				118DF7642C5ECBC60067FEB7 /* ApplyOverridePresetIntent.swift */,
+				118DF7652C5ECBC60067FEB7 /* CancelOverrideIntent.swift */,
+				118DF7662C5ECBC60067FEB7 /* ListOverridePresetIntent.swift */,
+				118DF7672C5ECBC60067FEB7 /* OverridePresetEntity.swift */,
+				118DF7682C5ECBC60067FEB7 /* OverridePresetsIntentRequest.swift */,
+			);
+			path = Override;
+			sourceTree = "<group>";
+		};
 		18B49BC9587A59E3A347C1CD /* View */ = {
 		18B49BC9587A59E3A347C1CD /* View */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -1738,6 +1778,16 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				BD3CC0712B0B89D50013189E /* MainChartView.swift */,
 				BD3CC0712B0B89D50013189E /* MainChartView.swift */,
+				582DF9742C8CDB92001F516D /* GlucoseChartView.swift */,
+				582DF9762C8CDBE7001F516D /* InsulinView.swift */,
+				582DF97A2C8CE209001F516D /* CarbView.swift */,
+				58D08B212C8DAA8E00AA37D3 /* OverrideView.swift */,
+				58D08B2F2C8DEA7500AA37D3 /* ForecastView.swift */,
+				58D08B312C8DF88900AA37D3 /* DummyCharts.swift */,
+				58D08B332C8DF9A700AA37D3 /* CobChart.swift */,
+				58D08B352C8DFAC600AA37D3 /* IobChart.swift */,
+				58D08B372C8DFB6000AA37D3 /* BasalChart.swift */,
+				58D08B392C8DFECD00AA37D3 /* TempTargets.swift */,
 			);
 			);
 			path = Chart;
 			path = Chart;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -1925,6 +1975,7 @@
 				581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */,
 				581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */,
 				DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */,
 				DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */,
 				DD6B7CB12C7B6F0800B75029 /* Rounding.swift */,
 				DD6B7CB12C7B6F0800B75029 /* Rounding.swift */,
+				582DF9782C8CE1E5001F516D /* MainChartHelper.swift */,
 			);
 			);
 			path = Helpers;
 			path = Helpers;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2198,7 +2249,7 @@
 		587A54C82BCDCE0F009D38E2 /* Model */ = {
 		587A54C82BCDCE0F009D38E2 /* Model */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
-				DD57C46C2C4C7003001A5B28 /* Classes+Properties */,
+				DDE179112C9100FA003CDDB7 /* Classes+Properties */,
 				BDF34F8F2C10CF8C00D51995 /* CoreDataStack.swift */,
 				BDF34F8F2C10CF8C00D51995 /* CoreDataStack.swift */,
 				5825D1622BD405AE00F36E9B /* Helper */,
 				5825D1622BD405AE00F36E9B /* Helper */,
 				DDD1631D2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld */,
 				DDD1631D2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld */,
@@ -2357,6 +2408,7 @@
 		CE7CA3422A064973004BE681 /* Shortcuts */ = {
 		CE7CA3422A064973004BE681 /* Shortcuts */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				118DF7692C5ECBC60067FEB7 /* Override */,
 				110AEDE22C5193D100615CC9 /* Bolus */,
 				110AEDE22C5193D100615CC9 /* Bolus */,
 				CE1856F32ADC4835007E39C7 /* Carbs */,
 				CE1856F32ADC4835007E39C7 /* Carbs */,
 				CE7CA3432A064973004BE681 /* AppShortcuts.swift */,
 				CE7CA3432A064973004BE681 /* AppShortcuts.swift */,
@@ -2595,47 +2647,6 @@
 			path = View;
 			path = View;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
-		DD57C46C2C4C7003001A5B28 /* Classes+Properties */ = {
-			isa = PBXGroup;
-			children = (
-				DD57C4902C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift */,
-				DD57C4912C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift */,
-				DD57C4922C4C7103001A5B28 /* MealPresetStored+CoreDataClass.swift */,
-				DD57C4932C4C7103001A5B28 /* MealPresetStored+CoreDataProperties.swift */,
-				DD57C4942C4C7103001A5B28 /* TempTargets+CoreDataClass.swift */,
-				DD57C4952C4C7103001A5B28 /* TempTargets+CoreDataProperties.swift */,
-				DD57C4962C4C7103001A5B28 /* ForecastValue+CoreDataClass.swift */,
-				DD57C4972C4C7103001A5B28 /* ForecastValue+CoreDataProperties.swift */,
-				DD57C4982C4C7103001A5B28 /* OrefDetermination+CoreDataClass.swift */,
-				DD57C4992C4C7103001A5B28 /* OrefDetermination+CoreDataProperties.swift */,
-				DD57C49A2C4C7103001A5B28 /* PumpEventStored+CoreDataClass.swift */,
-				DD57C49B2C4C7103001A5B28 /* PumpEventStored+CoreDataProperties.swift */,
-				DD57C49C2C4C7103001A5B28 /* TempBasalStored+CoreDataClass.swift */,
-				DD57C49D2C4C7103001A5B28 /* TempBasalStored+CoreDataProperties.swift */,
-				DD57C49E2C4C7103001A5B28 /* TempTargetsSlider+CoreDataClass.swift */,
-				DD57C49F2C4C7103001A5B28 /* TempTargetsSlider+CoreDataProperties.swift */,
-				DD57C4A02C4C7103001A5B28 /* Forecast+CoreDataClass.swift */,
-				DD57C4A12C4C7103001A5B28 /* Forecast+CoreDataProperties.swift */,
-				DD57C4A22C4C7103001A5B28 /* BolusStored+CoreDataClass.swift */,
-				DD57C4A32C4C7103001A5B28 /* BolusStored+CoreDataProperties.swift */,
-				DD57C4A42C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift */,
-				DD57C4A52C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift */,
-				DD57C4A62C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift */,
-				DD57C4A72C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift */,
-				DD57C4A82C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift */,
-				DD57C4A92C4C7103001A5B28 /* GlucoseStored+CoreDataProperties.swift */,
-				DD57C4AA2C4C7103001A5B28 /* OverrideStored+CoreDataClass.swift */,
-				DD57C4AB2C4C7103001A5B28 /* OverrideStored+CoreDataProperties.swift */,
-				DD57C4AC2C4C7103001A5B28 /* OverrideRunStored+CoreDataClass.swift */,
-				DD57C4AD2C4C7103001A5B28 /* OverrideRunStored+CoreDataProperties.swift */,
-				DD57C4AE2C4C7103001A5B28 /* ImportError+CoreDataClass.swift */,
-				DD57C4AF2C4C7103001A5B28 /* ImportError+CoreDataProperties.swift */,
-				DD57C4B02C4C7103001A5B28 /* StatsData+CoreDataClass.swift */,
-				DD57C4B12C4C7103001A5B28 /* StatsData+CoreDataProperties.swift */,
-			);
-			path = "Classes+Properties";
-			sourceTree = "<group>";
-		};
 		DD6B7CB72C7BAC1B00B75029 /* ProfileImport */ = {
 		DD6B7CB72C7BAC1B00B75029 /* ProfileImport */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -2666,6 +2677,45 @@
 			path = View;
 			path = View;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		DDE179112C9100FA003CDDB7 /* Classes+Properties */ = {
+			isa = PBXGroup;
+			children = (
+				DDE179322C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift */,
+				DDE179332C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift */,
+				DDE179342C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift */,
+				DDE179352C910127003CDDB7 /* LoopStatRecord+CoreDataProperties.swift */,
+				DDE179362C910127003CDDB7 /* BolusStored+CoreDataClass.swift */,
+				DDE179372C910127003CDDB7 /* BolusStored+CoreDataProperties.swift */,
+				DDE179382C910127003CDDB7 /* ForecastValue+CoreDataClass.swift */,
+				DDE179392C910127003CDDB7 /* ForecastValue+CoreDataProperties.swift */,
+				DDE1793A2C910127003CDDB7 /* CarbEntryStored+CoreDataClass.swift */,
+				DDE1793B2C910127003CDDB7 /* CarbEntryStored+CoreDataProperties.swift */,
+				DDE1793C2C910127003CDDB7 /* TempTargets+CoreDataClass.swift */,
+				DDE1793D2C910127003CDDB7 /* TempTargets+CoreDataProperties.swift */,
+				DDE1793E2C910127003CDDB7 /* PumpEventStored+CoreDataClass.swift */,
+				DDE1793F2C910127003CDDB7 /* PumpEventStored+CoreDataProperties.swift */,
+				DDE179402C910127003CDDB7 /* StatsData+CoreDataClass.swift */,
+				DDE179412C910127003CDDB7 /* StatsData+CoreDataProperties.swift */,
+				DDE179422C910127003CDDB7 /* Forecast+CoreDataClass.swift */,
+				DDE179432C910127003CDDB7 /* Forecast+CoreDataProperties.swift */,
+				DDE179442C910127003CDDB7 /* GlucoseStored+CoreDataClass.swift */,
+				DDE179452C910127003CDDB7 /* GlucoseStored+CoreDataProperties.swift */,
+				DDE179462C910127003CDDB7 /* OpenAPS_Battery+CoreDataClass.swift */,
+				DDE179472C910127003CDDB7 /* OpenAPS_Battery+CoreDataProperties.swift */,
+				DDE179482C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift */,
+				DDE179492C910127003CDDB7 /* TempBasalStored+CoreDataProperties.swift */,
+				DDE1794A2C910127003CDDB7 /* TempTargetsSlider+CoreDataClass.swift */,
+				DDE1794B2C910127003CDDB7 /* TempTargetsSlider+CoreDataProperties.swift */,
+				DDE1794C2C910127003CDDB7 /* OverrideRunStored+CoreDataClass.swift */,
+				DDE1794D2C910127003CDDB7 /* OverrideRunStored+CoreDataProperties.swift */,
+				DDE1794E2C910127003CDDB7 /* OrefDetermination+CoreDataClass.swift */,
+				DDE1794F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift */,
+				DDE179502C910127003CDDB7 /* OverrideStored+CoreDataClass.swift */,
+				DDE179512C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift */,
+			);
+			path = "Classes+Properties";
+			sourceTree = "<group>";
+		};
 		DDF847DB2C5C28550049BB3B /* LiveActivitySettings */ = {
 		DDF847DB2C5C28550049BB3B /* LiveActivitySettings */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -3115,6 +3165,7 @@
 				CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */,
 				CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */,
 				38B4F3C325E2A20B00E76A18 /* PumpSetupView.swift in Sources */,
 				38B4F3C325E2A20B00E76A18 /* PumpSetupView.swift in Sources */,
 				38E4453C274E411700EC9A94 /* Disk+Codable.swift in Sources */,
 				38E4453C274E411700EC9A94 /* Disk+Codable.swift in Sources */,
+				58D08B322C8DF88900AA37D3 /* DummyCharts.swift in Sources */,
 				19E1F7EF29D08EBA005C8D20 /* IconConfigRootWiew.swift in Sources */,
 				19E1F7EF29D08EBA005C8D20 /* IconConfigRootWiew.swift in Sources */,
 				1967DFC229D053D300759F30 /* IconImage.swift in Sources */,
 				1967DFC229D053D300759F30 /* IconImage.swift in Sources */,
 				382C134B25F14E3700715CE1 /* BGTargets.swift in Sources */,
 				382C134B25F14E3700715CE1 /* BGTargets.swift in Sources */,
@@ -3133,6 +3184,7 @@
 				DD1745262C55526F00211FAC /* SMBSettingsRootView.swift in Sources */,
 				DD1745262C55526F00211FAC /* SMBSettingsRootView.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
+				118DF76E2C5ECBC60067FEB7 /* OverridePresetsIntentRequest.swift in Sources */,
 				CEE9A6552BBB418300EB5194 /* CalibrationsProvider.swift in Sources */,
 				CEE9A6552BBB418300EB5194 /* CalibrationsProvider.swift in Sources */,
 				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
@@ -3223,43 +3275,13 @@
 				3811DE5D25C9D4D500A708ED /* Publisher.swift in Sources */,
 				3811DE5D25C9D4D500A708ED /* Publisher.swift in Sources */,
 				E00EEC0727368630002FF094 /* APSAssembly.swift in Sources */,
 				E00EEC0727368630002FF094 /* APSAssembly.swift in Sources */,
 				38B4F3AF25E2979F00E76A18 /* IndexedCollection.swift in Sources */,
 				38B4F3AF25E2979F00E76A18 /* IndexedCollection.swift in Sources */,
-				DD57C4B22C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift in Sources */,
-				DD57C4B32C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift in Sources */,
-				DD57C4B42C4C7103001A5B28 /* MealPresetStored+CoreDataClass.swift in Sources */,
-				DD57C4B52C4C7103001A5B28 /* MealPresetStored+CoreDataProperties.swift in Sources */,
-				DD57C4B62C4C7103001A5B28 /* TempTargets+CoreDataClass.swift in Sources */,
-				DD57C4B72C4C7103001A5B28 /* TempTargets+CoreDataProperties.swift in Sources */,
-				DD57C4B82C4C7103001A5B28 /* ForecastValue+CoreDataClass.swift in Sources */,
-				DD57C4B92C4C7103001A5B28 /* ForecastValue+CoreDataProperties.swift in Sources */,
-				DD57C4BA2C4C7103001A5B28 /* OrefDetermination+CoreDataClass.swift in Sources */,
-				DD57C4BB2C4C7103001A5B28 /* OrefDetermination+CoreDataProperties.swift in Sources */,
-				DD57C4BC2C4C7103001A5B28 /* PumpEventStored+CoreDataClass.swift in Sources */,
-				DD57C4BD2C4C7103001A5B28 /* PumpEventStored+CoreDataProperties.swift in Sources */,
-				DD57C4BE2C4C7103001A5B28 /* TempBasalStored+CoreDataClass.swift in Sources */,
-				DD57C4BF2C4C7103001A5B28 /* TempBasalStored+CoreDataProperties.swift in Sources */,
+				58D08B222C8DAA8E00AA37D3 /* OverrideView.swift in Sources */,
 				BD0B2EF32C5998E600B3298F /* MealPresetView.swift in Sources */,
 				BD0B2EF32C5998E600B3298F /* MealPresetView.swift in Sources */,
-				DD57C4C02C4C7103001A5B28 /* TempTargetsSlider+CoreDataClass.swift in Sources */,
-				DD57C4C12C4C7103001A5B28 /* TempTargetsSlider+CoreDataProperties.swift in Sources */,
-				DD57C4C22C4C7103001A5B28 /* Forecast+CoreDataClass.swift in Sources */,
-				DD57C4C32C4C7103001A5B28 /* Forecast+CoreDataProperties.swift in Sources */,
-				DD57C4C42C4C7103001A5B28 /* BolusStored+CoreDataClass.swift in Sources */,
+				582DF9752C8CDB92001F516D /* GlucoseChartView.swift in Sources */,
 				DD1745302C55AE5300211FAC /* TargetBehaviorProvider.swift in Sources */,
 				DD1745302C55AE5300211FAC /* TargetBehaviorProvider.swift in Sources */,
-				DD57C4C52C4C7103001A5B28 /* BolusStored+CoreDataProperties.swift in Sources */,
-				DD57C4C62C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift in Sources */,
-				DD57C4C72C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift in Sources */,
-				DD57C4C82C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
-				DD57C4C92C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */,
-				DD57C4CA2C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift in Sources */,
-				DD57C4CB2C4C7103001A5B28 /* GlucoseStored+CoreDataProperties.swift in Sources */,
-				DD57C4CC2C4C7103001A5B28 /* OverrideStored+CoreDataClass.swift in Sources */,
-				DD57C4CD2C4C7103001A5B28 /* OverrideStored+CoreDataProperties.swift in Sources */,
-				DD57C4CE2C4C7103001A5B28 /* OverrideRunStored+CoreDataClass.swift in Sources */,
-				DD57C4CF2C4C7103001A5B28 /* OverrideRunStored+CoreDataProperties.swift in Sources */,
-				DD57C4D02C4C7103001A5B28 /* ImportError+CoreDataClass.swift in Sources */,
+				58D08B382C8DFB6000AA37D3 /* BasalChart.swift in Sources */,
+				118DF76D2C5ECBC60067FEB7 /* OverridePresetEntity.swift in Sources */,
 				DD17454E2C55CA4D00211FAC /* UnitsLimitsSettingsDataFlow.swift in Sources */,
 				DD17454E2C55CA4D00211FAC /* UnitsLimitsSettingsDataFlow.swift in Sources */,
-				DD57C4D12C4C7103001A5B28 /* ImportError+CoreDataProperties.swift in Sources */,
-				DD57C4D22C4C7103001A5B28 /* StatsData+CoreDataClass.swift in Sources */,
-				DD57C4D32C4C7103001A5B28 /* StatsData+CoreDataProperties.swift in Sources */,
 				DDF847E62C5D66490049BB3B /* AddMealPresetView.swift in Sources */,
 				DDF847E62C5D66490049BB3B /* AddMealPresetView.swift in Sources */,
 				3811DEAE25C9D88300A708ED /* Cache.swift in Sources */,
 				3811DEAE25C9D88300A708ED /* Cache.swift in Sources */,
 				383420D625FFE38C002D46C1 /* LoopView.swift in Sources */,
 				383420D625FFE38C002D46C1 /* LoopView.swift in Sources */,
@@ -3268,6 +3290,7 @@
 				CE48C86628CA6B48007C0598 /* OmniPodManagerExtensions.swift in Sources */,
 				CE48C86628CA6B48007C0598 /* OmniPodManagerExtensions.swift in Sources */,
 				CEB434E728B9053300B70274 /* LoopUIColorPalette+Default.swift in Sources */,
 				CEB434E728B9053300B70274 /* LoopUIColorPalette+Default.swift in Sources */,
 				19F95FF329F10FBC00314DDC /* StatDataFlow.swift in Sources */,
 				19F95FF329F10FBC00314DDC /* StatDataFlow.swift in Sources */,
+				582DF97B2C8CE209001F516D /* CarbView.swift in Sources */,
 				3811DE2225C9D48300A708ED /* MainProvider.swift in Sources */,
 				3811DE2225C9D48300A708ED /* MainProvider.swift in Sources */,
 				3811DE0C25C9D32F00A708ED /* BaseProvider.swift in Sources */,
 				3811DE0C25C9D32F00A708ED /* BaseProvider.swift in Sources */,
 				CE95BF5A2BA62E4A00DC3DE3 /* PluginSource.swift in Sources */,
 				CE95BF5A2BA62E4A00DC3DE3 /* PluginSource.swift in Sources */,
@@ -3277,6 +3300,7 @@
 				CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */,
 				CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */,
 				3811DE3525C9D49500A708ED /* HomeRootView.swift in Sources */,
 				3811DE3525C9D49500A708ED /* HomeRootView.swift in Sources */,
 				38E98A2925F52C9300C0CED0 /* Error+Extensions.swift in Sources */,
 				38E98A2925F52C9300C0CED0 /* Error+Extensions.swift in Sources */,
+				118DF76C2C5ECBC60067FEB7 /* ListOverridePresetIntent.swift in Sources */,
 				38EA05DA261F6E7C0064E39B /* SimpleLogReporter.swift in Sources */,
 				38EA05DA261F6E7C0064E39B /* SimpleLogReporter.swift in Sources */,
 				3811DE6125C9D4D500A708ED /* ViewModifiers.swift in Sources */,
 				3811DE6125C9D4D500A708ED /* ViewModifiers.swift in Sources */,
 				3811DEAC25C9D88300A708ED /* NightscoutManager.swift in Sources */,
 				3811DEAC25C9D88300A708ED /* NightscoutManager.swift in Sources */,
@@ -3312,6 +3336,7 @@
 				38FE826A25CC82DB001FF17A /* NetworkService.swift in Sources */,
 				38FE826A25CC82DB001FF17A /* NetworkService.swift in Sources */,
 				FE66D16B291F74F8005D6F77 /* Bundle+Extensions.swift in Sources */,
 				FE66D16B291F74F8005D6F77 /* Bundle+Extensions.swift in Sources */,
 				3883581C25EE79BB00E024B2 /* TextFieldWithToolBar.swift in Sources */,
 				3883581C25EE79BB00E024B2 /* TextFieldWithToolBar.swift in Sources */,
+				58D08B302C8DEA7500AA37D3 /* ForecastView.swift in Sources */,
 				6B1A8D2E2B156EEF00E76752 /* LiveActivityBridge.swift in Sources */,
 				6B1A8D2E2B156EEF00E76752 /* LiveActivityBridge.swift in Sources */,
 				581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */,
 				581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */,
 				DD6B7CB22C7B6F0800B75029 /* Rounding.swift in Sources */,
 				DD6B7CB22C7B6F0800B75029 /* Rounding.swift in Sources */,
@@ -3321,6 +3346,7 @@
 				BDCD47AF2C1F3F1700F8BCD5 /* OverrideStored+helper.swift in Sources */,
 				BDCD47AF2C1F3F1700F8BCD5 /* OverrideStored+helper.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsStateModel.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsStateModel.swift in Sources */,
 				CE7CA3582A064E2F004BE681 /* ListStateView.swift in Sources */,
 				CE7CA3582A064E2F004BE681 /* ListStateView.swift in Sources */,
+				58D08B362C8DFAC600AA37D3 /* IobChart.swift in Sources */,
 				193F6CDD2A512C8F001240FD /* Loops.swift in Sources */,
 				193F6CDD2A512C8F001240FD /* Loops.swift in Sources */,
 				38B4F3CB25E502E200E76A18 /* WeakObjectSet.swift in Sources */,
 				38B4F3CB25E502E200E76A18 /* WeakObjectSet.swift in Sources */,
 				38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */,
 				38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */,
@@ -3359,6 +3385,7 @@
 				38A43598262E0E4900E80935 /* FetchAnnouncementsManager.swift in Sources */,
 				38A43598262E0E4900E80935 /* FetchAnnouncementsManager.swift in Sources */,
 				DD1745442C55C60E00211FAC /* AutosensSettingsDataFlow.swift in Sources */,
 				DD1745442C55C60E00211FAC /* AutosensSettingsDataFlow.swift in Sources */,
 				BDCAF2382C639F35002DC907 /* SettingItems.swift in Sources */,
 				BDCAF2382C639F35002DC907 /* SettingItems.swift in Sources */,
+				58D08B342C8DF9A700AA37D3 /* CobChart.swift in Sources */,
 				642F76A05A4FF530463A9FD0 /* NightscoutConfigRootView.swift in Sources */,
 				642F76A05A4FF530463A9FD0 /* NightscoutConfigRootView.swift in Sources */,
 				BD7DA9AC2AE06EB900601B20 /* BolusCalculatorConfigRootView.swift in Sources */,
 				BD7DA9AC2AE06EB900601B20 /* BolusCalculatorConfigRootView.swift in Sources */,
 				AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */,
 				AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */,
@@ -3373,6 +3400,7 @@
 				BD4064D12C4ED26900582F43 /* CoreDataObserver.swift in Sources */,
 				BD4064D12C4ED26900582F43 /* CoreDataObserver.swift in Sources */,
 				38E44536274E411700EC9A94 /* Disk.swift in Sources */,
 				38E44536274E411700EC9A94 /* Disk.swift in Sources */,
 				19A910362A24D6D700C8951B /* DateFilter.swift in Sources */,
 				19A910362A24D6D700C8951B /* DateFilter.swift in Sources */,
+				582DF9792C8CE1E5001F516D /* MainChartHelper.swift in Sources */,
 				E06B911A275B5EEA003C04B6 /* Array+Extension.swift in Sources */,
 				E06B911A275B5EEA003C04B6 /* Array+Extension.swift in Sources */,
 				38EA0600262091870064E39B /* BolusProgressViewStyle.swift in Sources */,
 				38EA0600262091870064E39B /* BolusProgressViewStyle.swift in Sources */,
 				389ECDFE2601061500D86C4F /* View+Snapshot.swift in Sources */,
 				389ECDFE2601061500D86C4F /* View+Snapshot.swift in Sources */,
@@ -3410,6 +3438,7 @@
 				BDF34EBE2C0A31D100D51995 /* CustomNotification.swift in Sources */,
 				BDF34EBE2C0A31D100D51995 /* CustomNotification.swift in Sources */,
 				BDC2EA472C3045AD00E5BBD0 /* Override.swift in Sources */,
 				BDC2EA472C3045AD00E5BBD0 /* Override.swift in Sources */,
 				1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */,
 				1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */,
+				582DF9772C8CDBE7001F516D /* InsulinView.swift in Sources */,
 				F816826028DB441800054060 /* BluetoothTransmitter.swift in Sources */,
 				F816826028DB441800054060 /* BluetoothTransmitter.swift in Sources */,
 				DD68889D2C386E17006E3C44 /* NightscoutExercise.swift in Sources */,
 				DD68889D2C386E17006E3C44 /* NightscoutExercise.swift in Sources */,
 				5864E8592C42CFAE00294306 /* DeterminationStorage.swift in Sources */,
 				5864E8592C42CFAE00294306 /* DeterminationStorage.swift in Sources */,
@@ -3423,12 +3452,14 @@
 				A33352ED40476125EBAC6EE0 /* CarbRatioEditorDataFlow.swift in Sources */,
 				A33352ED40476125EBAC6EE0 /* CarbRatioEditorDataFlow.swift in Sources */,
 				17A9D0899046B45E87834820 /* CarbRatioEditorProvider.swift in Sources */,
 				17A9D0899046B45E87834820 /* CarbRatioEditorProvider.swift in Sources */,
 				69B9A368029F7EB39F525422 /* CarbRatioEditorStateModel.swift in Sources */,
 				69B9A368029F7EB39F525422 /* CarbRatioEditorStateModel.swift in Sources */,
+				118DF76B2C5ECBC60067FEB7 /* CancelOverrideIntent.swift in Sources */,
 				38E44538274E411700EC9A94 /* Disk+[Data].swift in Sources */,
 				38E44538274E411700EC9A94 /* Disk+[Data].swift in Sources */,
 				98641AF4F92123DA668AB931 /* CarbRatioEditorRootView.swift in Sources */,
 				98641AF4F92123DA668AB931 /* CarbRatioEditorRootView.swift in Sources */,
 				BDF34F902C10CF8C00D51995 /* CoreDataStack.swift in Sources */,
 				BDF34F902C10CF8C00D51995 /* CoreDataStack.swift in Sources */,
 				CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */,
 				CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */,
 				110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */,
 				110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */,
 				38E4453D274E411700EC9A94 /* Disk+Errors.swift in Sources */,
 				38E4453D274E411700EC9A94 /* Disk+Errors.swift in Sources */,
+				58D08B3A2C8DFECD00AA37D3 /* TempTargets.swift in Sources */,
 				38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */,
 				38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */,
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
 				DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */,
 				DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */,
@@ -3477,6 +3508,7 @@
 				BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */,
 				BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */,
 				C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */,
 				C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */,
 				38E4453B274E411700EC9A94 /* Disk+VolumeInformation.swift in Sources */,
 				38E4453B274E411700EC9A94 /* Disk+VolumeInformation.swift in Sources */,
+				118DF76A2C5ECBC60067FEB7 /* ApplyOverridePresetIntent.swift in Sources */,
 				7BCFACB97C821041BA43A114 /* ManualTempBasalRootView.swift in Sources */,
 				7BCFACB97C821041BA43A114 /* ManualTempBasalRootView.swift in Sources */,
 				38E44534274E411700EC9A94 /* Disk+InternalHelpers.swift in Sources */,
 				38E44534274E411700EC9A94 /* Disk+InternalHelpers.swift in Sources */,
 				38A00B2325FC2B55006BC0B0 /* LRUCache.swift in Sources */,
 				38A00B2325FC2B55006BC0B0 /* LRUCache.swift in Sources */,
@@ -3515,6 +3547,38 @@
 				E3A08AAE59538BC8A8ABE477 /* GlucoseNotificationSettingsDataFlow.swift in Sources */,
 				E3A08AAE59538BC8A8ABE477 /* GlucoseNotificationSettingsDataFlow.swift in Sources */,
 				0F7A65FBD2CD8D6477ED4539 /* GlucoseNotificationSettingsProvider.swift in Sources */,
 				0F7A65FBD2CD8D6477ED4539 /* GlucoseNotificationSettingsProvider.swift in Sources */,
 				3171D2818C7C72CD1584BB5E /* GlucoseNotificationSettingsStateModel.swift in Sources */,
 				3171D2818C7C72CD1584BB5E /* GlucoseNotificationSettingsStateModel.swift in Sources */,
+				DDE179522C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift in Sources */,
+				DDE179532C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift in Sources */,
+				DDE179542C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift in Sources */,
+				DDE179552C910127003CDDB7 /* LoopStatRecord+CoreDataProperties.swift in Sources */,
+				DDE179562C910127003CDDB7 /* BolusStored+CoreDataClass.swift in Sources */,
+				DDE179572C910127003CDDB7 /* BolusStored+CoreDataProperties.swift in Sources */,
+				DDE179582C910127003CDDB7 /* ForecastValue+CoreDataClass.swift in Sources */,
+				DDE179592C910127003CDDB7 /* ForecastValue+CoreDataProperties.swift in Sources */,
+				DDE1795A2C910127003CDDB7 /* CarbEntryStored+CoreDataClass.swift in Sources */,
+				DDE1795B2C910127003CDDB7 /* CarbEntryStored+CoreDataProperties.swift in Sources */,
+				DDE1795C2C910127003CDDB7 /* TempTargets+CoreDataClass.swift in Sources */,
+				DDE1795D2C910127003CDDB7 /* TempTargets+CoreDataProperties.swift in Sources */,
+				DDE1795E2C910127003CDDB7 /* PumpEventStored+CoreDataClass.swift in Sources */,
+				DDE1795F2C910127003CDDB7 /* PumpEventStored+CoreDataProperties.swift in Sources */,
+				DDE179602C910127003CDDB7 /* StatsData+CoreDataClass.swift in Sources */,
+				DDE179612C910127003CDDB7 /* StatsData+CoreDataProperties.swift in Sources */,
+				DDE179622C910127003CDDB7 /* Forecast+CoreDataClass.swift in Sources */,
+				DDE179632C910127003CDDB7 /* Forecast+CoreDataProperties.swift in Sources */,
+				DDE179642C910127003CDDB7 /* GlucoseStored+CoreDataClass.swift in Sources */,
+				DDE179652C910127003CDDB7 /* GlucoseStored+CoreDataProperties.swift in Sources */,
+				DDE179662C910127003CDDB7 /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
+				DDE179672C910127003CDDB7 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */,
+				DDE179682C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift in Sources */,
+				DDE179692C910127003CDDB7 /* TempBasalStored+CoreDataProperties.swift in Sources */,
+				DDE1796A2C910127003CDDB7 /* TempTargetsSlider+CoreDataClass.swift in Sources */,
+				DDE1796B2C910127003CDDB7 /* TempTargetsSlider+CoreDataProperties.swift in Sources */,
+				DDE1796C2C910127003CDDB7 /* OverrideRunStored+CoreDataClass.swift in Sources */,
+				DDE1796D2C910127003CDDB7 /* OverrideRunStored+CoreDataProperties.swift in Sources */,
+				DDE1796E2C910127003CDDB7 /* OrefDetermination+CoreDataClass.swift in Sources */,
+				DDE1796F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift in Sources */,
+				DDE179702C910127003CDDB7 /* OverrideStored+CoreDataClass.swift in Sources */,
+				DDE179712C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift in Sources */,
 				CD78BB94E43B249D60CC1A1B /* GlucoseNotificationSettingsRootView.swift in Sources */,
 				CD78BB94E43B249D60CC1A1B /* GlucoseNotificationSettingsRootView.swift in Sources */,
 				CE7CA3502A064973004BE681 /* CancelTempPresetIntent.swift in Sources */,
 				CE7CA3502A064973004BE681 /* CancelTempPresetIntent.swift in Sources */,
 				6B1F539F9FF75646D1606066 /* SnoozeDataFlow.swift in Sources */,
 				6B1F539F9FF75646D1606066 /* SnoozeDataFlow.swift in Sources */,

+ 9 - 0
FreeAPS/Sources/APS/APSManager.swift

@@ -131,6 +131,15 @@ final class BaseAPSManager: APSManager, Injectable {
     }
     }
 
 
     private func subscribe() {
     private func subscribe() {
+        if settingsManager.settings.units == .mmolL {
+            let wasParsed = storage.parseOnFileSettingsToMgdL()
+            if wasParsed {
+                Task {
+                    try await makeProfiles()
+                }
+            }
+        }
+
         deviceDataManager.recommendsLoop
         deviceDataManager.recommendsLoop
             .receive(on: processQueue)
             .receive(on: processQueue)
             .sink { [weak self] in
             .sink { [weak self] in

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

@@ -44,7 +44,7 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
     var externalLink: URL? {
     var externalLink: URL? {
         switch self {
         switch self {
         case .xdrip:
         case .xdrip:
-            return URL(string: "https://github.com/JohanDegraeve/xdripswift")!
+            return URL(string: "https://xdrip4ios.readthedocs.io/")!
         default: return nil
         default: return nil
         }
         }
     }
     }

+ 99 - 0
FreeAPS/Sources/Helpers/MainChartHelper.swift

@@ -0,0 +1,99 @@
+import CoreData
+import Foundation
+
+enum MainChartHelper {
+    // Calculates the glucose value thats the nearest to parameter 'time'
+    /// -Returns: A NSManagedObject of GlucoseStored
+    /// it is thread safe as everything is executed on the main thread
+    static func timeToNearestGlucose(glucoseValues: [GlucoseStored], time: TimeInterval) -> GlucoseStored? {
+        guard !glucoseValues.isEmpty else {
+            return nil
+        }
+
+        var low = 0
+        var high = glucoseValues.count - 1
+        var closestGlucose: GlucoseStored?
+
+        // binary search to find next glucose
+        while low <= high {
+            let mid = low + (high - low) / 2
+            let midTime = glucoseValues[mid].date?.timeIntervalSince1970 ?? 0
+
+            if midTime == time {
+                return glucoseValues[mid]
+            } else if midTime < time {
+                low = mid + 1
+            } else {
+                high = mid - 1
+            }
+
+            // update if necessary
+            if closestGlucose == nil || abs(midTime - time) < abs(closestGlucose!.date?.timeIntervalSince1970 ?? 0 - time) {
+                closestGlucose = glucoseValues[mid]
+            }
+        }
+
+        return closestGlucose
+    }
+
+    enum Config {
+        static let bolusSize: CGFloat = 5
+        static let bolusScale: CGFloat = 1.8
+        static let carbsSize: CGFloat = 5
+        static let maxCarbSize: CGFloat = 30
+        static let carbsScale: CGFloat = 0.3
+        static let fpuSize: CGFloat = 10
+        static let maxGlucose = 270
+        static let minGlucose = 45
+    }
+
+    static var bolusFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.minimumIntegerDigits = 0
+        formatter.maximumFractionDigits = 2
+        formatter.decimalSeparator = "."
+        return formatter
+    }
+
+    static var carbsFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        return formatter
+    }
+
+    static func bolusOffset(units: GlucoseUnits) -> Decimal {
+        units == .mgdL ? 30 : 1.66
+    }
+
+    static func calculateDuration(objectID: NSManagedObjectID, context: NSManagedObjectContext) -> TimeInterval? {
+        do {
+            if let override = try context.existingObject(with: objectID) as? OverrideStored,
+               let overrideDuration = override.duration as? Double, overrideDuration != 0
+            {
+                return TimeInterval(overrideDuration * 60) // return seconds
+            }
+        } catch {
+            debugPrint(
+                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to calculate Override Target with error: \(error.localizedDescription)"
+            )
+        }
+        return nil
+    }
+
+    static func calculateTarget(objectID: NSManagedObjectID, context: NSManagedObjectContext) -> Decimal? {
+        do {
+            if let override = try context.existingObject(with: objectID) as? OverrideStored,
+               let overrideTarget = override.target, overrideTarget != 0
+            {
+                return overrideTarget.decimalValue
+            }
+        } catch {
+            debugPrint(
+                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to calculate Override Target with error: \(error.localizedDescription)"
+            )
+        }
+        return nil
+    }
+}

File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings


File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 2 - 2
FreeAPS/Sources/Models/BGTargets.swift

@@ -1,8 +1,8 @@
 import Foundation
 import Foundation
 
 
 struct BGTargets: JSON {
 struct BGTargets: JSON {
-    let units: GlucoseUnits
-    let userPreferredUnits: GlucoseUnits
+    var units: GlucoseUnits
+    var userPreferredUnits: GlucoseUnits
     var targets: [BGTargetEntry]
     var targets: [BGTargetEntry]
 }
 }
 
 

+ 2 - 2
FreeAPS/Sources/Models/DecimalPickerSettings.swift

@@ -75,8 +75,8 @@ struct DecimalPickerSettings {
     var autotuneISFAdjustmentFraction = PickerSetting(
     var autotuneISFAdjustmentFraction = PickerSetting(
         value: 1.0,
         value: 1.0,
         step: 0.05,
         step: 0.05,
-        min: 0.5,
-        max: 2,
+        min: 0,
+        max: 1,
         type: PickerSetting.PickerSettingType.factor
         type: PickerSetting.PickerSettingType.factor
     )
     )
     var remainingCarbsFraction = PickerSetting(
     var remainingCarbsFraction = PickerSetting(

+ 10 - 10
FreeAPS/Sources/Models/Determination.swift

@@ -2,10 +2,10 @@ import Foundation
 
 
 struct Determination: JSON, Equatable {
 struct Determination: JSON, Equatable {
     let id: UUID?
     let id: UUID?
-    let reason: String
+    var reason: String
     let units: Decimal?
     let units: Decimal?
     let insulinReq: Decimal?
     let insulinReq: Decimal?
-    let eventualBG: Int?
+    var eventualBG: Int?
     let sensitivityRatio: Decimal?
     let sensitivityRatio: Decimal?
     let rate: Decimal?
     let rate: Decimal?
     let duration: Decimal?
     let duration: Decimal?
@@ -15,20 +15,20 @@ struct Determination: JSON, Equatable {
     var deliverAt: Date?
     var deliverAt: Date?
     let carbsReq: Decimal?
     let carbsReq: Decimal?
     let temp: TempType?
     let temp: TempType?
-    let bg: Decimal?
+    var bg: Decimal?
     let reservoir: Decimal?
     let reservoir: Decimal?
-    let isf: Decimal?
+    var isf: Decimal?
     var timestamp: Date?
     var timestamp: Date?
     let tdd: Decimal?
     let tdd: Decimal?
     let insulin: Insulin?
     let insulin: Insulin?
-    let current_target: Decimal?
+    var current_target: Decimal?
     let insulinForManualBolus: Decimal?
     let insulinForManualBolus: Decimal?
     let manualBolusErrorString: Decimal?
     let manualBolusErrorString: Decimal?
-    let minDelta: Decimal?
-    let expectedDelta: Decimal?
-    let minGuardBG: Decimal?
-    let minPredBG: Decimal?
-    let threshold: Decimal?
+    var minDelta: Decimal?
+    var expectedDelta: Decimal?
+    var minGuardBG: Decimal?
+    var minPredBG: Decimal?
+    var threshold: Decimal?
     let carbRatio: Decimal?
     let carbRatio: Decimal?
     let received: Bool?
     let received: Bool?
 }
 }

+ 3 - 3
FreeAPS/Sources/Models/InsulinSensitivities.swift

@@ -1,9 +1,9 @@
 import Foundation
 import Foundation
 
 
 struct InsulinSensitivities: JSON {
 struct InsulinSensitivities: JSON {
-    let units: GlucoseUnits
-    let userPreferredUnits: GlucoseUnits
-    let sensitivities: [InsulinSensitivityEntry]
+    var units: GlucoseUnits
+    var userPreferredUnits: GlucoseUnits
+    var sensitivities: [InsulinSensitivityEntry]
 }
 }
 
 
 extension InsulinSensitivities {
 extension InsulinSensitivities {

+ 68 - 100
FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift

@@ -109,7 +109,7 @@ extension Bolus {
         @Published var maxForecast: [Int] = []
         @Published var maxForecast: [Int] = []
         @Published var minCount: Int = 12 // count of Forecasts drawn in 5 min distances, i.e. 12 means a min of 1 hour
         @Published var minCount: Int = 12 // count of Forecasts drawn in 5 min distances, i.e. 12 means a min of 1 hour
         @Published var forecastDisplayType: ForecastDisplayType = .cone
         @Published var forecastDisplayType: ForecastDisplayType = .cone
-        @Published var smooth: Bool = false
+        @Published var isSmoothingEnabled: Bool = false
         @Published var stops: [Gradient.Stop] = []
         @Published var stops: [Gradient.Stop] = []
 
 
         let now = Date.now
         let now = Date.now
@@ -122,51 +122,38 @@ extension Bolus {
         typealias PumpEvent = PumpEventStored.EventType
         typealias PumpEvent = PumpEventStored.EventType
 
 
         override func subscribe() {
         override func subscribe() {
-            setupGlucoseNotification()
-            coreDataObserver = CoreDataObserver()
-            registerHandlers()
-            setupGlucoseArray()
-
             Task {
             Task {
-                async let getAllSettingsDefaults: () = getAllSettingsValues()
-                async let setupDeterminations: () = setupDeterminationsArray()
-
-                await getAllSettingsDefaults
-                await setupDeterminations
-
-                // Determination has updated, so we can use this to draw the initial Forecast Chart
-                let forecastData = await mapForecastsForChart()
-                await updateForecasts(with: forecastData)
-            }
-
-            broadcaster.register(DeterminationObserver.self, observer: self)
-            broadcaster.register(BolusFailureObserver.self, observer: self)
-            units = settingsManager.settings.units
-            fraction = settings.settings.overrideFactor
-            fattyMeals = settings.settings.fattyMeals
-            fattyMealFactor = settings.settings.fattyMealFactor
-            sweetMeals = settings.settings.sweetMeals
-            sweetMealFactor = settings.settings.sweetMealFactor
-            displayPresets = settings.settings.displayPresets
-
-            forecastDisplayType = settings.settings.forecastDisplayType
-
-            lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
-            highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
+                await withTaskGroup(of: Void.self) { group in
+                    group.addTask {
+                        self.setupGlucoseNotification()
+                    }
+                    group.addTask {
+                        self.registerHandlers()
+                    }
+                    group.addTask {
+                        self.setupGlucoseArray()
+                    }
+                    group.addTask {
+                        self.setupDeterminationsAndForecasts()
+                    }
+                    group.addTask {
+                        await self.setupSettings()
+                    }
+                    group.addTask {
+                        self.registerObservers()
+                    }
 
 
-            maxCarbs = settings.settings.maxCarbs
-            maxFat = settings.settings.maxFat
-            maxProtein = settings.settings.maxProtein
-            useFPUconversion = settingsManager.settings.useFPUconversion
-            smooth = settingsManager.settings.smoothGlucose
-
-            if waitForSuggestionInitial {
-                Task {
-                    let ok = await apsManager.determineBasal()
-                    if !ok {
-                        self.waitForSuggestion = false
-                        self.insulinRequired = 0
-                        self.insulinRecommended = 0
+                    if self.waitForSuggestionInitial {
+                        group.addTask {
+                            let isDetermineBasalSuccessful = await self.apsManager.determineBasal()
+                            if !isDetermineBasalSuccessful {
+                                await MainActor.run {
+                                    self.waitForSuggestion = false
+                                    self.insulinRequired = 0
+                                    self.insulinRecommended = 0
+                                }
+                            }
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -204,6 +191,43 @@ extension Bolus {
             }
             }
         }
         }
 
 
+        private func setupDeterminationsAndForecasts() {
+            Task {
+                async let getAllSettingsDefaults: () = getAllSettingsValues()
+                async let setupDeterminations: () = setupDeterminationsArray()
+
+                await getAllSettingsDefaults
+                await setupDeterminations
+
+                // Determination has updated, so we can use this to draw the initial Forecast Chart
+                let forecastData = await mapForecastsForChart()
+                await updateForecasts(with: forecastData)
+            }
+        }
+
+        private func registerObservers() {
+            broadcaster.register(DeterminationObserver.self, observer: self)
+            broadcaster.register(BolusFailureObserver.self, observer: self)
+        }
+
+        @MainActor private func setupSettings() async {
+            units = settingsManager.settings.units
+            fraction = settings.settings.overrideFactor
+            fattyMeals = settings.settings.fattyMeals
+            fattyMealFactor = settings.settings.fattyMealFactor
+            sweetMeals = settings.settings.sweetMeals
+            sweetMealFactor = settings.settings.sweetMealFactor
+            displayPresets = settings.settings.displayPresets
+            forecastDisplayType = settings.settings.forecastDisplayType
+            lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
+            highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
+            maxCarbs = settings.settings.maxCarbs
+            maxFat = settings.settings.maxFat
+            maxProtein = settings.settings.maxProtein
+            useFPUconversion = settingsManager.settings.useFPUconversion
+            isSmoothingEnabled = settingsManager.settings.smoothGlucose
+        }
+
         private func getCurrentSettingValue(for type: SettingType) async {
         private func getCurrentSettingValue(for type: SettingType) async {
             let now = Date()
             let now = Date()
             let calendar = Calendar.current
             let calendar = Calendar.current
@@ -439,54 +463,6 @@ extension Bolus {
             }
             }
         }
         }
 
 
-        private func calculateGradientStops() async -> [Gradient.Stop] {
-            let low = Double(lowGlucose)
-            let high = Double(highGlucose)
-
-            let glucoseValues = glucoseFromPersistence
-                .map { units == .mgdL ? Decimal($0.glucose) : Decimal($0.glucose).asMmolL }
-
-            let minimum = glucoseValues.min() ?? 0.0
-            let maximum = glucoseValues.max() ?? 0.0
-
-            // Handle edge case where minimum and maximum are equal
-            guard minimum != maximum else {
-                return [
-                    Gradient.Stop(color: .green, location: 0.0),
-                    Gradient.Stop(color: .green, location: 1.0)
-                ]
-            }
-
-            // Calculate positions for gradient
-            let lowPosition = (low - Double(truncating: minimum as NSNumber)) /
-                (Double(truncating: maximum as NSNumber) - Double(truncating: minimum as NSNumber))
-            let highPosition = (high - Double(truncating: minimum as NSNumber)) /
-                (Double(truncating: maximum as NSNumber) - Double(truncating: minimum as NSNumber))
-
-            // Ensure positions are in bounds [0, 1]
-            let clampedLowPosition = max(0.0, min(lowPosition, 1.0))
-            let clampedHighPosition = max(0.0, min(highPosition, 1.0))
-
-            // Ensure lowPosition is less than highPosition
-            let epsilon: CGFloat = 0.0001
-            let sortedPositions = [clampedLowPosition, clampedHighPosition].sorted()
-            var adjustedHighPosition = sortedPositions[1]
-
-            if adjustedHighPosition - sortedPositions[0] < epsilon {
-                adjustedHighPosition = min(1.0, sortedPositions[0] + epsilon)
-            }
-
-            return [
-                Gradient.Stop(color: .red, location: 0.0),
-                Gradient.Stop(color: .red, location: sortedPositions[0]), // draw red gradient till lowGlucose
-                Gradient.Stop(color: .green, location: sortedPositions[0] + epsilon),
-                // draw green above lowGlucose till highGlucose
-                Gradient.Stop(color: .green, location: adjustedHighPosition),
-                Gradient.Stop(color: .orange, location: adjustedHighPosition + epsilon), // draw orange above highGlucose
-                Gradient.Stop(color: .orange, location: 1.0)
-            ]
-        }
-
         // MARK: - Carbs
         // MARK: - Carbs
 
 
         func saveMeal() async {
         func saveMeal() async {
@@ -624,14 +600,6 @@ extension Bolus.StateModel {
             let ids = await self.fetchGlucose()
             let ids = await self.fetchGlucose()
             let glucoseObjects: [GlucoseStored] = await CoreDataStack.shared.getNSManagedObject(with: ids, context: viewContext)
             let glucoseObjects: [GlucoseStored] = await CoreDataStack.shared.getNSManagedObject(with: ids, context: viewContext)
             await updateGlucoseArray(with: glucoseObjects)
             await updateGlucoseArray(with: glucoseObjects)
-
-            if smooth {
-                let newStops = await self.calculateGradientStops()
-
-                await MainActor.run {
-                    self.stops = newStops
-                }
-            }
         }
         }
     }
     }
 
 

+ 1 - 1
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -171,7 +171,7 @@ extension Bolus {
                 VStack {
                 VStack {
                     Form {
                     Form {
                         Section {
                         Section {
-                            ForeCastChart(state: state, units: $state.units, stops: state.stops)
+                            ForeCastChart(state: state, units: $state.units)
                                 .padding(.vertical)
                                 .padding(.vertical)
                         }.listRowBackground(Color.chart)
                         }.listRowBackground(Color.chart)
 
 

+ 19 - 31
FreeAPS/Sources/Modules/Bolus/View/ForeCastChart.swift

@@ -7,7 +7,6 @@ struct ForeCastChart: View {
     @StateObject var state: Bolus.StateModel
     @StateObject var state: Bolus.StateModel
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
     @Binding var units: GlucoseUnits
     @Binding var units: GlucoseUnits
-    var stops: [Gradient.Stop]
 
 
     @State private var startMarker = Date(timeIntervalSinceNow: -4 * 60 * 60)
     @State private var startMarker = Date(timeIntervalSinceNow: -4 * 60 * 60)
 
 
@@ -122,39 +121,28 @@ struct ForeCastChart: View {
 
 
     private func drawGlucose() -> some ChartContent {
     private func drawGlucose() -> some ChartContent {
         ForEach(state.glucoseFromPersistence) { item in
         ForEach(state.glucoseFromPersistence) { item in
-            let glucoseToDisplay = units == .mgdL ? Decimal(item.glucose) : Decimal(item.glucose).asMmolL
-
-            if state.smooth {
-                LineMark(
-                    x: .value("Time", item.date ?? Date()),
+            let glucoseToDisplay = state.units == .mgdL ? Decimal(item.glucose) : Decimal(item.glucose).asMmolL
+            let pointMarkColor: Color = glucoseToDisplay > state.highGlucose ? Color.orange :
+                glucoseToDisplay < state.lowGlucose ? Color.red :
+                Color.green
+
+            if !state.isSmoothingEnabled {
+                PointMark(
+                    x: .value("Time", item.date ?? Date(), unit: .second),
                     y: .value("Value", glucoseToDisplay)
                     y: .value("Value", glucoseToDisplay)
                 )
                 )
-                .foregroundStyle(
-                    .linearGradient(stops: stops, startPoint: .bottom, endPoint: .top)
-                )
-                .symbol(.circle).symbolSize(34)
+                .foregroundStyle(pointMarkColor)
+                .symbolSize(20)
             } else {
             } else {
-                if item.glucose > Int(state.highGlucose) {
-                    PointMark(
-                        x: .value("Time", item.date ?? Date(), unit: .second),
-                        y: .value("Value", glucoseToDisplay)
-                    )
-                    .foregroundStyle(Color.orange.gradient)
-                    .symbolSize(20)
-                } else if item.glucose < Int(state.lowGlucose) {
-                    PointMark(
-                        x: .value("Time", item.date ?? Date(), unit: .second),
-                        y: .value("Value", glucoseToDisplay)
-                    )
-                    .foregroundStyle(Color.red.gradient)
-                    .symbolSize(20)
-                } else {
-                    PointMark(
-                        x: .value("Time", item.date ?? Date(), unit: .second),
-                        y: .value("Value", glucoseToDisplay)
-                    )
-                    .foregroundStyle(Color.green.gradient)
-                    .symbolSize(20)
+                PointMark(
+                    x: .value("Time", item.date ?? Date(), unit: .second),
+                    y: .value("Value", glucoseToDisplay)
+                )
+                .symbol {
+                    Image(systemName: "record.circle.fill")
+                        .font(.system(size: 8))
+                        .bold()
+                        .foregroundStyle(pointMarkColor)
                 }
                 }
             }
             }
         }
         }

+ 14 - 2
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -143,12 +143,24 @@ extension DataTable {
                 .navigationTitle("History")
                 .navigationTitle("History")
                 .navigationBarTitleDisplayMode(.large)
                 .navigationBarTitleDisplayMode(.large)
                 .toolbar {
                 .toolbar {
-                    ToolbarItem(placement: .topBarTrailing) {
+                    ToolbarItem(placement: .topBarLeading, content: {
+                        Button(
+                            action: { state.showModal(for: .statistics) },
+                            label: {
+                                HStack {
+                                    Text("Statistics")
+                                }
+                            }
+                        )
+                    })
+                }
+                .toolbar {
+                    ToolbarItem(placement: .topBarTrailing, content: {
                         addButton({
                         addButton({
                             showManualGlucose = true
                             showManualGlucose = true
                             state.manualGlucose = 0
                             state.manualGlucose = 0
                         })
                         })
-                    }
+                    })
                 }
                 }
                 .sheet(isPresented: $showManualGlucose) {
                 .sheet(isPresented: $showManualGlucose) {
                     addGlucoseView()
                     addGlucoseView()

File diff ditekan karena terlalu besar
+ 1 - 1
FreeAPS/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift


+ 132 - 114
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -44,7 +44,7 @@ extension Home {
         @Published var pumpDisplayState: PumpDisplayState?
         @Published var pumpDisplayState: PumpDisplayState?
         @Published var alarm: GlucoseAlarm?
         @Published var alarm: GlucoseAlarm?
         @Published var manualTempBasal = false
         @Published var manualTempBasal = false
-        @Published var smooth = false
+        @Published var isSmoothingEnabled = false
         @Published var maxValue: Decimal = 1.2
         @Published var maxValue: Decimal = 1.2
         @Published var lowGlucose: Decimal = 70
         @Published var lowGlucose: Decimal = 70
         @Published var highGlucose: Decimal = 180
         @Published var highGlucose: Decimal = 180
@@ -59,12 +59,11 @@ extension Home {
         @Published var isLegendPresented: Bool = false
         @Published var isLegendPresented: Bool = false
         @Published var legendSheetDetent = PresentationDetent.large
         @Published var legendSheetDetent = PresentationDetent.large
         @Published var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
         @Published var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
-        @Published var isTempTargetActive: Bool = false
         @Published var roundedTotalBolus: String = ""
         @Published var roundedTotalBolus: String = ""
         @Published var selectedTab: Int = 0
         @Published var selectedTab: Int = 0
         @Published var waitForSuggestion: Bool = false
         @Published var waitForSuggestion: Bool = false
         @Published var glucoseFromPersistence: [GlucoseStored] = []
         @Published var glucoseFromPersistence: [GlucoseStored] = []
-        @Published var manualGlucoseFromPersistence: [GlucoseStored] = []
+        @Published var latestTwoGlucoseValues: [GlucoseStored] = []
         @Published var carbsFromPersistence: [CarbEntryStored] = []
         @Published var carbsFromPersistence: [CarbEntryStored] = []
         @Published var fpusFromPersistence: [CarbEntryStored] = []
         @Published var fpusFromPersistence: [CarbEntryStored] = []
         @Published var determinationsFromPersistence: [OrefDetermination] = []
         @Published var determinationsFromPersistence: [OrefDetermination] = []
@@ -96,49 +95,116 @@ extension Home {
         typealias PumpEvent = PumpEventStored.EventType
         typealias PumpEvent = PumpEventStored.EventType
 
 
         override func subscribe() {
         override func subscribe() {
-            setupNotification()
             coreDataObserver = CoreDataObserver()
             coreDataObserver = CoreDataObserver()
-            registerHandlers()
-            setupGlucoseArray()
-            setupManualGlucoseArray()
-            setupCarbsArray()
-            setupFPUsArray()
-            setupDeterminationsArray()
-            setupInsulinArray()
-            setupLastBolus()
-            setupBatteryArray()
-            setupPumpSettings()
-            setupBasalProfile()
-            setupTempTargets()
-            setupReservoir()
-            setupAnnouncements()
-            setupCurrentPumpTimezone()
-            setupOverrides()
-            setupOverrideRunStored()
-
-            // TODO: isUploadEnabled the right var here??
-            uploadStats = settingsManager.settings.isUploadEnabled
-            units = settingsManager.settings.units
-            allowManualTemp = !settingsManager.settings.closedLoop
-            closedLoop = settingsManager.settings.closedLoop
-            lastLoopDate = apsManager.lastLoopDate
-            alarm = provider.glucoseStorage.alarm
-            manualTempBasal = apsManager.isManualTempBasal
-            setupCurrentTempTarget()
-            smooth = settingsManager.settings.smoothGlucose
-            maxValue = settingsManager.preferences.autosensMax
-            lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
-            highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
-            overrideUnit = settingsManager.settings.overrideHbA1cUnit
-            displayXgridLines = settingsManager.settings.xGridLines
-            displayYgridLines = settingsManager.settings.yGridLines
-            thresholdLines = settingsManager.settings.rulerMarks
-            totalInsulinDisplayType = settingsManager.settings.totalInsulinDisplayType
-            cgmAvailable = fetchGlucoseManager.cgmGlucoseSourceType != CGMType.none
-            showCarbsRequiredBadge = settingsManager.settings.showCarbsRequiredBadge
 
 
-            forecastDisplayType = settingsManager.settings.forecastDisplayType
+            // Parallelize Setup functions
+            setupHomeViewConcurrently()
+        }
 
 
+        private func setupHomeViewConcurrently() {
+            Task {
+                await withTaskGroup(of: Void.self) { group in
+                    group.addTask {
+                        self.setupNotification()
+                    }
+                    group.addTask {
+                        self.registerHandlers()
+                    }
+                    group.addTask {
+                        self.setupGlucoseArray()
+                    }
+                    group.addTask {
+                        self.setupCarbsArray()
+                    }
+                    group.addTask {
+                        self.setupFPUsArray()
+                    }
+                    group.addTask {
+                        self.setupDeterminationsArray()
+                    }
+                    group.addTask {
+                        self.setupInsulinArray()
+                    }
+                    group.addTask {
+                        self.setupLastBolus()
+                    }
+                    group.addTask {
+                        self.setupBatteryArray()
+                    }
+                    group.addTask {
+                        self.setupPumpSettings()
+                    }
+                    group.addTask {
+                        self.setupBasalProfile()
+                    }
+                    group.addTask {
+                        self.setupTempTargets()
+                    }
+                    group.addTask {
+                        self.setupReservoir()
+                    }
+                    group.addTask {
+                        self.setupAnnouncements()
+                    }
+                    group.addTask {
+                        self.setupCurrentPumpTimezone()
+                    }
+                    group.addTask {
+                        self.setupOverrides()
+                    }
+                    group.addTask {
+                        self.setupOverrideRunStored()
+                    }
+                    group.addTask {
+                        await self.setupSettings()
+                    }
+                    group.addTask {
+                        self.registerObservers()
+                    }
+                }
+            }
+        }
+
+        private func registerHandlers() {
+            coreDataObserver?.registerHandler(for: "OrefDetermination") { [weak self] in
+                guard let self = self else { return }
+                self.setupDeterminationsArray()
+            }
+
+            coreDataObserver?.registerHandler(for: "GlucoseStored") { [weak self] in
+                guard let self = self else { return }
+                self.setupGlucoseArray()
+            }
+
+            coreDataObserver?.registerHandler(for: "CarbEntryStored") { [weak self] in
+                guard let self = self else { return }
+                self.setupCarbsArray()
+            }
+
+            coreDataObserver?.registerHandler(for: "PumpEventStored") { [weak self] in
+                guard let self = self else { return }
+                self.setupInsulinArray()
+                self.setupLastBolus()
+                self.displayPumpStatusHighlightMessage()
+            }
+
+            coreDataObserver?.registerHandler(for: "OpenAPS_Battery") { [weak self] in
+                guard let self = self else { return }
+                self.setupBatteryArray()
+            }
+
+            coreDataObserver?.registerHandler(for: "OverrideStored") { [weak self] in
+                guard let self = self else { return }
+                self.setupOverrides()
+            }
+
+            coreDataObserver?.registerHandler(for: "OverrideRunStored") { [weak self] in
+                guard let self = self else { return }
+                self.setupOverrideRunStored()
+            }
+        }
+
+        private func registerObservers() {
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(DeterminationObserver.self, observer: self)
             broadcaster.register(DeterminationObserver.self, observer: self)
             broadcaster.register(SettingsObserver.self, observer: self)
             broadcaster.register(SettingsObserver.self, observer: self)
@@ -213,44 +279,26 @@ extension Home {
                 .store(in: &lifetime)
                 .store(in: &lifetime)
         }
         }
 
 
-        private func registerHandlers() {
-            coreDataObserver?.registerHandler(for: "OrefDetermination") { [weak self] in
-                guard let self = self else { return }
-                self.setupDeterminationsArray()
-            }
-
-            coreDataObserver?.registerHandler(for: "GlucoseStored") { [weak self] in
-                guard let self = self else { return }
-                self.setupGlucoseArray()
-                self.setupManualGlucoseArray()
-            }
-
-            coreDataObserver?.registerHandler(for: "CarbEntryStored") { [weak self] in
-                guard let self = self else { return }
-                self.setupCarbsArray()
-            }
-
-            coreDataObserver?.registerHandler(for: "PumpEventStored") { [weak self] in
-                guard let self = self else { return }
-                self.setupInsulinArray()
-                self.setupLastBolus()
-                self.displayPumpStatusHighlightMessage()
-            }
-
-            coreDataObserver?.registerHandler(for: "OpenAPS_Battery") { [weak self] in
-                guard let self = self else { return }
-                self.setupBatteryArray()
-            }
-
-            coreDataObserver?.registerHandler(for: "OverrideStored") { [weak self] in
-                guard let self = self else { return }
-                self.setupOverrides()
-            }
-
-            coreDataObserver?.registerHandler(for: "OverrideRunStored") { [weak self] in
-                guard let self = self else { return }
-                self.setupOverrideRunStored()
-            }
+        @MainActor private func setupSettings() async {
+            units = settingsManager.settings.units
+            allowManualTemp = !settingsManager.settings.closedLoop
+            closedLoop = settingsManager.settings.closedLoop
+            lastLoopDate = apsManager.lastLoopDate
+            alarm = provider.glucoseStorage.alarm
+            manualTempBasal = apsManager.isManualTempBasal
+            setupCurrentTempTarget()
+            isSmoothingEnabled = settingsManager.settings.smoothGlucose
+            maxValue = settingsManager.preferences.autosensMax
+            lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
+            highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
+            overrideUnit = settingsManager.settings.overrideHbA1cUnit
+            displayXgridLines = settingsManager.settings.xGridLines
+            displayYgridLines = settingsManager.settings.yGridLines
+            thresholdLines = settingsManager.settings.rulerMarks
+            totalInsulinDisplayType = settingsManager.settings.totalInsulinDisplayType
+            cgmAvailable = fetchGlucoseManager.cgmGlucoseSourceType != CGMType.none
+            showCarbsRequiredBadge = settingsManager.settings.showCarbsRequiredBadge
+            forecastDisplayType = settingsManager.settings.forecastDisplayType
         }
         }
 
 
         func addPump(_ type: PumpConfig.PumpType) {
         func addPump(_ type: PumpConfig.PumpType) {
@@ -445,7 +493,7 @@ extension Home.StateModel:
         closedLoop = settingsManager.settings.closedLoop
         closedLoop = settingsManager.settings.closedLoop
         units = settingsManager.settings.units
         units = settingsManager.settings.units
         manualTempBasal = apsManager.isManualTempBasal
         manualTempBasal = apsManager.isManualTempBasal
-        smooth = settingsManager.settings.smoothGlucose
+        isSmoothingEnabled = settingsManager.settings.smoothGlucose
         lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
         lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
         highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
         highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
         overrideUnit = settingsManager.settings.overrideHbA1cUnit
         overrideUnit = settingsManager.settings.overrideHbA1cUnit
@@ -566,7 +614,7 @@ extension Home.StateModel {
             onContext: context,
             onContext: context,
             predicate: NSPredicate.glucose,
             predicate: NSPredicate.glucose,
             key: "date",
             key: "date",
-            ascending: false,
+            ascending: true,
             fetchLimit: 288
             fetchLimit: 288
         )
         )
 
 
@@ -579,37 +627,7 @@ extension Home.StateModel {
 
 
     @MainActor private func updateGlucoseArray(with objects: [GlucoseStored]) {
     @MainActor private func updateGlucoseArray(with objects: [GlucoseStored]) {
         glucoseFromPersistence = objects
         glucoseFromPersistence = objects
-    }
-
-    // Setup Manual Glucose
-    private func setupManualGlucoseArray() {
-        Task {
-            let ids = await self.fetchManualGlucose()
-            let manualGlucoseObjects: [GlucoseStored] = await CoreDataStack.shared
-                .getNSManagedObject(with: ids, context: viewContext)
-            await updateManualGlucoseArray(with: manualGlucoseObjects)
-        }
-    }
-
-    private func fetchManualGlucose() async -> [NSManagedObjectID] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: context,
-            predicate: NSPredicate.manualGlucose,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-        return await context.perform {
-            return fetchedResults.map(\.objectID)
-        }
-    }
-
-    @MainActor private func updateManualGlucoseArray(with objects: [GlucoseStored]) {
-        manualGlucoseFromPersistence = objects
+        latestTwoGlucoseValues = Array(objects.suffix(2))
     }
     }
 
 
     // Setup Carbs
     // Setup Carbs

+ 249 - 0
FreeAPS/Sources/Modules/Home/View/Chart/BasalChart.swift

@@ -0,0 +1,249 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct BasalProfile: Hashable {
+    let amount: Double
+    var isOverwritten: Bool
+    let startDate: Date
+    let endDate: Date?
+    init(amount: Double, isOverwritten: Bool, startDate: Date, endDate: Date? = nil) {
+        self.amount = amount
+        self.isOverwritten = isOverwritten
+        self.startDate = startDate
+        self.endDate = endDate
+    }
+}
+
+extension MainChartView {
+    var basalChart: some View {
+        VStack {
+            Chart {
+                drawStartRuleMark()
+                drawEndRuleMark()
+                drawCurrentTimeMarker()
+                drawTempBasals(dummy: false)
+                drawBasalProfile()
+                drawSuspensions()
+            }.onChange(of: state.tempBasals) { _ in
+                calculateBasals()
+                calculateTempBasalsInBackground()
+            }
+            .onChange(of: state.maxBasal) { _ in
+                calculateBasals()
+            }
+            .onChange(of: state.autotunedBasalProfile) { _ in
+                calculateBasals()
+            }
+            .onChange(of: state.basalProfile) { _ in
+                calculateBasals()
+            }
+            .frame(minHeight: geo.size.height * 0.05)
+            .frame(width: fullWidth(viewWidth: screenSize.width))
+            .chartXScale(domain: startMarker ... endMarker)
+            .chartXAxis { basalChartXAxis }
+            .chartXAxis(.hidden)
+            .chartYAxis(.hidden)
+            .chartPlotStyle { basalChartPlotStyle($0) }
+        }
+    }
+}
+
+// MARK: - Draw functions
+
+extension MainChartView {
+    func drawTempBasals(dummy: Bool) -> some ChartContent {
+        ForEach(preparedTempBasals, id: \.rate) { basal in
+            if dummy {
+                RectangleMark(
+                    xStart: .value("start", basal.start),
+                    xEnd: .value("end", basal.end),
+                    yStart: .value("rate-start", 0),
+                    yEnd: .value("rate-end", basal.rate)
+                ).foregroundStyle(Color.clear)
+
+                LineMark(x: .value("Start Date", basal.start), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.clear)
+
+                LineMark(x: .value("End Date", basal.end), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.clear)
+            } else {
+                RectangleMark(
+                    xStart: .value("start", basal.start),
+                    xEnd: .value("end", basal.end),
+                    yStart: .value("rate-start", 0),
+                    yEnd: .value("rate-end", basal.rate)
+                ).foregroundStyle(
+                    LinearGradient(
+                        gradient: Gradient(
+                            colors: [
+                                Color.insulin.opacity(0.6),
+                                Color.insulin.opacity(0.1)
+                            ]
+                        ),
+                        startPoint: .top,
+                        endPoint: .bottom
+                    )
+                )
+
+                LineMark(x: .value("Start Date", basal.start), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+
+                LineMark(x: .value("End Date", basal.end), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+            }
+        }
+    }
+
+    func drawBasalProfile() -> some ChartContent {
+        /// dashed profile line
+        ForEach(basalProfiles, id: \.self) { profile in
+            LineMark(
+                x: .value("Start Date", profile.startDate),
+                y: .value("Amount", profile.amount),
+                series: .value("profile", "profile")
+            ).lineStyle(.init(lineWidth: 2, dash: [2, 4])).foregroundStyle(Color.insulin)
+            LineMark(
+                x: .value("End Date", profile.endDate ?? endMarker),
+                y: .value("Amount", profile.amount),
+                series: .value("profile", "profile")
+            ).lineStyle(.init(lineWidth: 2.5, dash: [2, 4])).foregroundStyle(Color.insulin)
+        }
+    }
+
+    func drawSuspensions() -> some ChartContent {
+        let suspensions = state.suspensions
+        return ForEach(suspensions) { suspension in
+            let now = Date()
+
+            if let type = suspension.type, type == EventType.pumpSuspend.rawValue, let suspensionStart = suspension.timestamp {
+                let suspensionEnd = min(
+                    (
+                        suspensions
+                            .first(where: {
+                                $0.timestamp ?? now > suspensionStart && $0.type == EventType.pumpResume.rawValue })?
+                            .timestamp
+                    ) ?? now,
+                    now
+                )
+
+                let basalProfileDuringSuspension = basalProfiles.first(where: { $0.startDate <= suspensionStart })
+                let suspensionMarkHeight = basalProfileDuringSuspension?.amount ?? 1
+
+                RectangleMark(
+                    xStart: .value("start", suspensionStart),
+                    xEnd: .value("end", suspensionEnd),
+                    yStart: .value("suspend-start", 0),
+                    yEnd: .value("suspend-end", suspensionMarkHeight)
+                )
+                .foregroundStyle(Color.loopGray.opacity(colorScheme == .dark ? 0.3 : 0.8))
+            }
+        }
+    }
+}
+
+// MARK: - Calculation
+
+extension MainChartView {
+    func calculateTempBasalsInBackground() {
+        Task {
+            let basals = await prepareTempBasals()
+            await MainActor.run {
+                preparedTempBasals = basals
+            }
+        }
+    }
+
+    func prepareTempBasals() async -> [(start: Date, end: Date, rate: Double)] {
+        let now = Date()
+        let tempBasals = state.tempBasals
+
+        return tempBasals.compactMap { temp -> (start: Date, end: Date, rate: Double)? in
+            let duration = temp.tempBasal?.duration ?? 0
+            let timestamp = temp.timestamp ?? Date()
+            let end = min(timestamp + duration.minutes, now)
+            let isInsulinSuspended = state.suspensions.contains { $0.timestamp ?? now >= timestamp && $0.timestamp ?? now <= end }
+
+            let rate = Double(truncating: temp.tempBasal?.rate ?? Decimal.zero as NSDecimalNumber) * (isInsulinSuspended ? 0 : 1)
+
+            // Check if there's a subsequent temp basal to determine the end time
+            guard let nextTemp = state.tempBasals.first(where: { $0.timestamp ?? .distantPast > timestamp }) else {
+                return (timestamp, end, rate)
+            }
+            return (timestamp, nextTemp.timestamp ?? Date(), rate)
+        }
+    }
+
+    func findRegularBasalPoints(
+        timeBegin: TimeInterval,
+        timeEnd: TimeInterval,
+        autotuned: Bool
+    ) async -> [BasalProfile] {
+        guard timeBegin < timeEnd else { return [] }
+
+        let beginDate = Date(timeIntervalSince1970: timeBegin)
+        let startOfDay = Calendar.current.startOfDay(for: beginDate)
+        let profile = autotuned ? state.autotunedBasalProfile : state.basalProfile
+        var basalPoints: [BasalProfile] = []
+
+        // Iterate over the next three days, multiplying the time intervals
+        for dayOffset in 0 ..< 3 {
+            let dayTimeOffset = TimeInterval(dayOffset * 24 * 60 * 60) // One Day in seconds
+            for entry in profile {
+                let basalTime = startOfDay.addingTimeInterval(entry.minutes.minutes.timeInterval + dayTimeOffset)
+                let basalTimeInterval = basalTime.timeIntervalSince1970
+
+                // Only append points within the timeBegin and timeEnd range
+                if basalTimeInterval >= timeBegin, basalTimeInterval < timeEnd {
+                    basalPoints.append(BasalProfile(
+                        amount: Double(entry.rate),
+                        isOverwritten: false,
+                        startDate: basalTime
+                    ))
+                }
+            }
+        }
+
+        return basalPoints
+    }
+
+    func calculateBasals() {
+        Task {
+            let dayAgoTime = Date().addingTimeInterval(-1.days.timeInterval).timeIntervalSince1970
+
+            // Get Regular and Autotuned Basal parallel
+            async let getRegularBasalPoints = findRegularBasalPoints(
+                timeBegin: dayAgoTime,
+                timeEnd: endMarker.timeIntervalSince1970,
+                autotuned: false
+            )
+
+            async let getAutotunedBasalPoints = findRegularBasalPoints(
+                timeBegin: dayAgoTime,
+                timeEnd: endMarker.timeIntervalSince1970,
+                autotuned: true
+            )
+
+            let (regularPoints, autotunedBasalPoints) = await (getRegularBasalPoints, getAutotunedBasalPoints)
+
+            var totalBasal = regularPoints + autotunedBasalPoints
+            totalBasal.sort {
+                $0.startDate.timeIntervalSince1970 < $1.startDate.timeIntervalSince1970
+            }
+
+            var basals: [BasalProfile] = []
+            totalBasal.indices.forEach { index in
+                basals.append(BasalProfile(
+                    amount: totalBasal[index].amount,
+                    isOverwritten: totalBasal[index].isOverwritten,
+                    startDate: totalBasal[index].startDate,
+                    endDate: totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker
+                ))
+            }
+
+            await MainActor.run {
+                basalProfiles = basals
+            }
+        }
+    }
+}

+ 63 - 0
FreeAPS/Sources/Modules/Home/View/Chart/CarbView.swift

@@ -0,0 +1,63 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct CarbView: ChartContent {
+    let glucoseData: [GlucoseStored]
+    let units: GlucoseUnits
+    let carbData: [CarbEntryStored]
+    let fpuData: [CarbEntryStored]
+    let minValue: Decimal
+
+    var body: some ChartContent {
+        drawCarbs()
+        drawFpus()
+    }
+
+    private func drawCarbs() -> some ChartContent {
+        ForEach(carbData) { carb in
+            let carbAmount = carb.carbs
+            let carbDate = carb.date ?? Date()
+
+            if let glucose = MainChartHelper.timeToNearestGlucose(
+                glucoseValues: glucoseData,
+                time: carbDate.timeIntervalSince1970
+            )?.glucose {
+                let yPosition = (units == .mgdL ? Decimal(glucose) : Decimal(glucose).asMmolL) - MainChartHelper
+                    .bolusOffset(units: units)
+                let size = min(
+                    MainChartHelper.Config.carbsSize + CGFloat(carbAmount) * MainChartHelper.Config.carbsScale,
+                    MainChartHelper.Config.maxCarbSize
+                )
+
+                PointMark(
+                    x: .value("Time", carbDate, unit: .second),
+                    y: .value("Value", yPosition)
+                )
+                .symbol {
+                    Image(systemName: "arrowtriangle.down.fill").font(.system(size: size)).foregroundStyle(Color.orange)
+                        .rotationEffect(.degrees(180))
+                }
+                .annotation(position: .bottom) {
+                    Text(MainChartHelper.carbsFormatter.string(from: carbAmount as NSNumber)!).font(.caption2)
+                        .foregroundStyle(Color.primary)
+                }
+            }
+        }
+    }
+
+    private func drawFpus() -> some ChartContent {
+        ForEach(fpuData, id: \.id) { fpu in
+            let fpuAmount = fpu.carbs
+            let size = (MainChartHelper.Config.fpuSize + CGFloat(fpuAmount) * MainChartHelper.Config.carbsScale) * 1.8
+            let yPosition = minValue
+
+            PointMark(
+                x: .value("Time", fpu.date ?? Date(), unit: .second),
+                y: .value("Value", yPosition)
+            )
+            .symbolSize(size)
+            .foregroundStyle(Color.brown)
+        }
+    }
+}

+ 67 - 0
FreeAPS/Sources/Modules/Home/View/Chart/CobChart.swift

@@ -0,0 +1,67 @@
+import Charts
+import Foundation
+import SwiftUI
+
+extension MainChartView {
+    var cobChart: some View {
+        Chart {
+            drawCurrentTimeMarker()
+            drawCOB(dummy: false)
+
+            if #available(iOS 17, *) {
+                if let selectedCOBValue {
+                    PointMark(
+                        x: .value("Time", selectedCOBValue.deliverAt ?? now, unit: .minute),
+                        y: .value("Value", selectedCOBValue.cob)
+                    )
+                    .symbolSize(CGSize(width: 15, height: 15))
+                    .foregroundStyle(Color.orange.opacity(0.8))
+
+                    PointMark(
+                        x: .value("Time", selectedCOBValue.deliverAt ?? now, unit: .minute),
+                        y: .value("Value", selectedCOBValue.cob)
+                    )
+                    .symbolSize(CGSize(width: 6, height: 6))
+                    .foregroundStyle(Color.primary)
+                }
+            }
+        }
+        .frame(minHeight: geo.size.height * 0.12)
+        .frame(width: fullWidth(viewWidth: screenSize.width))
+        .chartXScale(domain: startMarker ... endMarker)
+        .backport.chartXSelection(value: $selection)
+        .chartXAxis { basalChartXAxis }
+        .chartYAxis { cobChartYAxis }
+        .chartYScale(domain: minValueCobChart ... maxValueCobChart)
+    }
+
+    func drawCOB(dummy: Bool) -> some ChartContent {
+        ForEach(state.enactedAndNonEnactedDeterminations) { cob in
+            let amount = Int(cob.cob)
+            let date: Date = cob.deliverAt ?? Date()
+
+            if dummy {
+                LineMark(x: .value("Time", date), y: .value("Value", amount))
+                    .foregroundStyle(Color.clear)
+                AreaMark(x: .value("Time", date), y: .value("Value", amount)).foregroundStyle(
+                    Color.clear
+                )
+            } else {
+                LineMark(x: .value("Time", date), y: .value("Value", amount))
+                    .foregroundStyle(Color.orange.gradient)
+                AreaMark(x: .value("Time", date), y: .value("Value", amount)).foregroundStyle(
+                    LinearGradient(
+                        gradient: Gradient(
+                            colors: [
+                                Color.orange.opacity(0.8),
+                                Color.orange.opacity(0.01)
+                            ]
+                        ),
+                        startPoint: .top,
+                        endPoint: .bottom
+                    )
+                )
+            }
+        }
+    }
+}

+ 54 - 0
FreeAPS/Sources/Modules/Home/View/Chart/DummyCharts.swift

@@ -0,0 +1,54 @@
+import Charts
+import Foundation
+import SwiftUI
+
+extension MainChartView {
+    /// empty chart that just shows the Y axis and Y grid lines. Created separately from `mainChart` to allow main chart to scroll horizontally while having a fixed Y axis
+    var staticYAxisChart: some View {
+        Chart {
+            /// high and low threshold lines
+            if thresholdLines {
+                RuleMark(y: .value("High", highGlucose)).foregroundStyle(Color.loopYellow)
+                    .lineStyle(.init(lineWidth: 1, dash: [5]))
+                RuleMark(y: .value("Low", lowGlucose)).foregroundStyle(Color.loopRed)
+                    .lineStyle(.init(lineWidth: 1, dash: [5]))
+            }
+        }
+        .id("DummyMainChart")
+        .frame(minHeight: geo.size.height * 0.28)
+        .frame(width: screenSize.width - 10)
+        .chartXAxis { mainChartXAxis }
+        .chartXScale(domain: startMarker ... endMarker)
+        .chartXAxis(.hidden)
+        .chartYAxis { mainChartYAxis }
+        .chartYScale(domain: units == .mgdL ? minValue ... maxValue : minValue.asMmolL ... maxValue.asMmolL)
+        .chartLegend(.hidden)
+    }
+
+    var dummyBasalChart: some View {
+        Chart {}
+            .id("DummyBasalChart")
+            .frame(minHeight: geo.size.height * 0.05)
+            .frame(width: screenSize.width - 10)
+            .chartXAxis { basalChartXAxis }
+            .chartXAxis(.hidden)
+            .chartYAxis(.hidden)
+            .chartLegend(.hidden)
+    }
+
+    var dummyCobChart: some View {
+        Chart {
+            drawCOB(dummy: true)
+        }
+        .id("DummyCobChart")
+        .frame(minHeight: geo.size.height * 0.12)
+        .frame(width: screenSize.width - 10)
+        .chartXScale(domain: startMarker ... endMarker)
+        .chartXAxis { basalChartXAxis }
+        .chartXAxis(.hidden)
+        .chartYAxis { cobChartYAxis }
+        .chartYAxis(.hidden)
+        .chartYScale(domain: minValueCobChart ... maxValueCobChart)
+        .chartLegend(.hidden)
+    }
+}

+ 91 - 0
FreeAPS/Sources/Modules/Home/View/Chart/ForecastView.swift

@@ -0,0 +1,91 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct ForecastView: ChartContent {
+    let preprocessedData: [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)]
+    let minForecast: [Int]
+    let maxForecast: [Int]
+    let units: GlucoseUnits
+    let maxValue: Decimal
+    let forecastDisplayType: ForecastDisplayType
+
+    var body: some ChartContent {
+        if forecastDisplayType == .lines {
+            drawForecastsLines()
+        } else {
+            drawForecastsCone()
+        }
+    }
+
+    private func timeForIndex(_ index: Int32) -> Date {
+        let currentTime = Date()
+        let timeInterval = TimeInterval(index * 300)
+        return currentTime.addingTimeInterval(timeInterval)
+    }
+
+    private func drawForecastsCone() -> some ChartContent {
+        // Draw AreaMark for the forecast bounds
+        ForEach(0 ..< max(minForecast.count, maxForecast.count), id: \.self) { index in
+            if index < minForecast.count, index < maxForecast.count {
+                let yMinMaxDelta = Decimal(minForecast[index] - maxForecast[index])
+                let xValue = timeForIndex(Int32(index))
+
+                // if distance between respective min and max is 0, provide a default range
+                if yMinMaxDelta == 0 {
+                    let yMinValue = units == .mgdL ? Decimal(minForecast[index] - 1) :
+                        Decimal(minForecast[index] - 1)
+                        .asMmolL
+                    let yMaxValue = units == .mgdL ? Decimal(minForecast[index] + 1) :
+                        Decimal(minForecast[index] + 1)
+                        .asMmolL
+
+                    if xValue <= Date(timeIntervalSinceNow: TimeInterval(hours: 2.5)) {
+                        AreaMark(
+                            x: .value("Time", xValue),
+                            // maxValue is already parsed to user units, no need to parse
+                            yStart: .value("Min Value", yMinValue <= maxValue ? yMinValue : maxValue),
+                            yEnd: .value("Max Value", yMaxValue <= maxValue ? yMaxValue : maxValue)
+                        )
+                        .foregroundStyle(Color.blue.opacity(0.5))
+                        .interpolationMethod(.catmullRom)
+                    }
+                } else {
+                    let yMinValue = units == .mgdL ? Decimal(minForecast[index]) : Decimal(minForecast[index])
+                        .asMmolL
+                    let yMaxValue = units == .mgdL ? Decimal(maxForecast[index]) : Decimal(maxForecast[index])
+                        .asMmolL
+
+                    if xValue <= Date(timeIntervalSinceNow: TimeInterval(hours: 2.5)) {
+                        AreaMark(
+                            x: .value("Time", xValue),
+                            // maxValue is already parsed to user units, no need to parse
+                            yStart: .value("Min Value", yMinValue <= maxValue ? yMinValue : maxValue),
+                            yEnd: .value("Max Value", yMaxValue <= maxValue ? yMaxValue : maxValue)
+                        )
+                        .foregroundStyle(Color.blue.opacity(0.5))
+                        .interpolationMethod(.catmullRom)
+                    }
+                }
+            }
+        }
+    }
+
+    private func drawForecastsLines() -> some ChartContent {
+        ForEach(preprocessedData, id: \.id) { tuple in
+            let forecastValue = tuple.forecastValue
+            let forecast = tuple.forecast
+            let valueAsDecimal = Decimal(forecastValue.value)
+            let displayValue = units == .mmolL ? valueAsDecimal.asMmolL : valueAsDecimal
+            let xValue = timeForIndex(forecastValue.index)
+
+            if xValue <= Date(timeIntervalSinceNow: TimeInterval(hours: 2.5)) {
+                LineMark(
+                    x: .value("Time", xValue),
+                    y: .value("Value", displayValue)
+                )
+                .foregroundStyle(by: .value("Predictions", forecast.type ?? ""))
+            }
+        }
+    }
+}

+ 66 - 0
FreeAPS/Sources/Modules/Home/View/Chart/GlucoseChartView.swift

@@ -0,0 +1,66 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct GlucoseChartView: ChartContent {
+    let glucoseData: [GlucoseStored]
+    let units: GlucoseUnits
+    let highGlucose: Decimal
+    let lowGlucose: Decimal
+    let isSmoothingEnabled: Bool
+
+    var body: some ChartContent {
+        drawGlucoseChart()
+    }
+
+    private func drawGlucoseChart() -> some ChartContent {
+        ForEach(glucoseData) { item in
+            let glucoseToDisplay = units == .mgdL ? Decimal(item.glucose) : Decimal(item.glucose).asMmolL
+            let pointMarkColor: Color = glucoseToDisplay > highGlucose ? Color.orange :
+                glucoseToDisplay < lowGlucose ? Color.red :
+                Color.green
+
+            if !isSmoothingEnabled {
+                PointMark(
+                    x: .value("Time", item.date ?? Date(), unit: .second),
+                    y: .value("Value", glucoseToDisplay)
+                )
+                .foregroundStyle(pointMarkColor)
+                .symbolSize(20)
+                .symbol {
+                    if item.isManual {
+                        Image(systemName: "drop.fill")
+                            .font(.system(size: 10))
+                            .symbolRenderingMode(.monochrome)
+                            .bold()
+                            .foregroundStyle(.red)
+                    } else {
+                        Image(systemName: "circle.fill")
+                            .font(.system(size: 5))
+                            .bold()
+                            .foregroundStyle(pointMarkColor)
+                    }
+                }
+            } else {
+                PointMark(
+                    x: .value("Time", item.date ?? Date(), unit: .second),
+                    y: .value("Value", glucoseToDisplay)
+                )
+                .symbol {
+                    if item.isManual {
+                        Image(systemName: "drop.fill")
+                            .font(.system(size: 10))
+                            .symbolRenderingMode(.monochrome)
+                            .bold()
+                            .foregroundStyle(.red)
+                    } else {
+                        Image(systemName: "record.circle.fill")
+                            .font(.system(size: 8))
+                            .bold()
+                            .foregroundStyle(pointMarkColor)
+                    }
+                }
+            }
+        }
+    }
+}

+ 42 - 0
FreeAPS/Sources/Modules/Home/View/Chart/InsulinView.swift

@@ -0,0 +1,42 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct InsulinView: ChartContent {
+    let glucoseData: [GlucoseStored]
+    let insulinData: [PumpEventStored]
+    let units: GlucoseUnits
+
+    var body: some ChartContent {
+        drawBoluses()
+    }
+
+    private func drawBoluses() -> some ChartContent {
+        ForEach(insulinData) { insulin in
+            let amount = insulin.bolus?.amount ?? 0 as NSDecimalNumber
+            let bolusDate = insulin.timestamp ?? Date()
+
+            if amount != 0, let glucose = MainChartHelper.timeToNearestGlucose(
+                glucoseValues: glucoseData,
+                time: bolusDate.timeIntervalSince1970
+            )?.glucose {
+                let yPosition = (units == .mgdL ? Decimal(glucose) : Decimal(glucose).asMmolL) + MainChartHelper
+                    .bolusOffset(units: units)
+                let size = (MainChartHelper.Config.bolusSize + CGFloat(truncating: amount) * MainChartHelper.Config.bolusScale)
+
+                PointMark(
+                    x: .value("Time", bolusDate, unit: .second),
+                    y: .value("Value", yPosition)
+                )
+                .symbol {
+                    Image(systemName: "arrowtriangle.down.fill").font(.system(size: size)).foregroundStyle(Color.insulin)
+                }
+                .annotation(position: .top) {
+                    Text(MainChartHelper.bolusFormatter.string(from: amount) ?? "")
+                        .font(.caption2)
+                        .foregroundStyle(Color.primary)
+                }
+            }
+        }
+    }
+}

+ 63 - 0
FreeAPS/Sources/Modules/Home/View/Chart/IobChart.swift

@@ -0,0 +1,63 @@
+import Charts
+import Foundation
+import SwiftUI
+
+extension MainChartView {
+    var iobChart: some View {
+        VStack {
+            Chart {
+                drawIOB()
+
+                if #available(iOS 17, *) {
+                    if let selectedIOBValue {
+                        PointMark(
+                            x: .value("Time", selectedIOBValue.deliverAt ?? now, unit: .minute),
+                            y: .value("Value", Int(truncating: selectedIOBValue.iob ?? 0))
+                        )
+                        .symbolSize(CGSize(width: 15, height: 15))
+                        .foregroundStyle(Color.darkerBlue.opacity(0.8))
+
+                        PointMark(
+                            x: .value("Time", selectedIOBValue.deliverAt ?? now, unit: .minute),
+                            y: .value("Value", Int(truncating: selectedIOBValue.iob ?? 0))
+                        )
+                        .symbolSize(CGSize(width: 6, height: 6))
+                        .foregroundStyle(Color.primary)
+                    }
+                }
+            }
+            .frame(minHeight: geo.size.height * 0.12)
+            .frame(width: fullWidth(viewWidth: screenSize.width))
+            .chartXScale(domain: startMarker ... endMarker)
+            .backport.chartXSelection(value: $selection)
+            .chartXAxis { basalChartXAxis }
+            .chartYAxis { cobChartYAxis }
+            .chartYScale(domain: minValueIobChart ... maxValueIobChart)
+            .chartYAxis(.hidden)
+        }
+    }
+
+    func drawIOB() -> some ChartContent {
+        ForEach(state.enactedAndNonEnactedDeterminations) { iob in
+            let rawAmount = iob.iob?.doubleValue ?? 0
+            let amount: Double = rawAmount > 0 ? rawAmount : rawAmount * 2 // weigh negative iob with factor 2
+            let date: Date = iob.deliverAt ?? Date()
+
+            LineMark(x: .value("Time", date), y: .value("Amount", amount))
+                .foregroundStyle(Color.darkerBlue)
+            AreaMark(x: .value("Time", date), y: .value("Amount", amount))
+                .foregroundStyle(
+                    LinearGradient(
+                        gradient: Gradient(
+                            colors: [
+                                Color.darkerBlue.opacity(0.8),
+                                Color.darkerBlue.opacity(0.01)
+                            ]
+                        ),
+                        startPoint: .top,
+                        endPoint: .bottom
+                    )
+                )
+        }
+    }
+}

File diff ditekan karena terlalu besar
+ 171 - 960
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift


+ 50 - 0
FreeAPS/Sources/Modules/Home/View/Chart/OverrideView.swift

@@ -0,0 +1,50 @@
+import Charts
+import CoreData
+import Foundation
+import SwiftUI
+
+struct OverrideView: ChartContent {
+    let overrides: [OverrideStored]
+    let overrideRunStored: [OverrideRunStored]
+    let units: GlucoseUnits
+    let viewContext: NSManagedObjectContext
+
+    var body: some ChartContent {
+        drawActiveOverrides()
+        drawOverrideRunStored()
+    }
+
+    private func drawActiveOverrides() -> some ChartContent {
+        ForEach(overrides) { override in
+            if let duration = MainChartHelper.calculateDuration(objectID: override.objectID, context: viewContext) {
+                let start: Date = override.date ?? .distantPast
+                let end: Date = start.addingTimeInterval(duration)
+
+                if let target = MainChartHelper.calculateTarget(objectID: override.objectID, context: viewContext) {
+                    RuleMark(
+                        xStart: .value("Start", start, unit: .second),
+                        xEnd: .value("End", end, unit: .second),
+                        y: .value("Value", units == .mgdL ? target : target.asMmolL)
+                    )
+                    .foregroundStyle(Color.purple.opacity(0.4))
+                    .lineStyle(.init(lineWidth: 8))
+                }
+            }
+        }
+    }
+
+    private func drawOverrideRunStored() -> some ChartContent {
+        ForEach(overrideRunStored) { overrideRunStored in
+            let start: Date = overrideRunStored.startDate ?? .distantPast
+            let end: Date = overrideRunStored.endDate ?? Date()
+            let target = overrideRunStored.target?.decimalValue ?? 100
+            RuleMark(
+                xStart: .value("Start", start, unit: .second),
+                xEnd: .value("End", end, unit: .second),
+                y: .value("Value", units == .mgdL ? target : target.asMmolL)
+            )
+            .foregroundStyle(Color.purple.opacity(0.25))
+            .lineStyle(.init(lineWidth: 8))
+        }
+    }
+}

+ 82 - 0
FreeAPS/Sources/Modules/Home/View/Chart/TempTargets.swift

@@ -0,0 +1,82 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct ChartTempTarget: Hashable {
+    let amount: Decimal
+    let start: Date
+    let end: Date
+}
+
+extension MainChartView {
+    func drawTempTargets() -> some ChartContent {
+        ForEach(chartTempTargets, id: \.self) { target in
+            let targetLimited = min(max(target.amount, 0), upperLimit)
+
+            RuleMark(
+                xStart: .value("Start", target.start),
+                xEnd: .value("End", target.end),
+                y: .value("Value", targetLimited)
+            )
+            .foregroundStyle(Color.purple.opacity(0.75)).lineStyle(.init(lineWidth: 8))
+        }
+    }
+
+    // Calculations for temp target bar mark
+    func calculateTempTargets() async {
+        // Perform calculations off the main thread
+        let calculatedTTs = await Task.detached { () -> [ChartTempTarget] in
+            var groupedPackages: [[TempTarget]] = []
+            var currentPackage: [TempTarget] = []
+            var calculatedTTs: [ChartTempTarget] = []
+
+            for target in await tempTargets {
+                if target.duration > 0 {
+                    if !currentPackage.isEmpty {
+                        groupedPackages.append(currentPackage)
+                        currentPackage = []
+                    }
+                    currentPackage.append(target)
+                } else if let lastNonZeroTempTarget = currentPackage.last(where: { $0.duration > 0 }) {
+                    // Ensure this cancel target is within the valid time range
+                    if target.createdAt >= lastNonZeroTempTarget.createdAt,
+                       target.createdAt <= lastNonZeroTempTarget.createdAt
+                       .addingTimeInterval(TimeInterval(lastNonZeroTempTarget.duration * 60))
+                    {
+                        currentPackage.append(target)
+                    }
+                }
+            }
+
+            // Append the last group, if any
+            if !currentPackage.isEmpty {
+                groupedPackages.append(currentPackage)
+            }
+
+            for package in groupedPackages {
+                guard let firstNonZeroTarget = package.first(where: { $0.duration > 0 }) else { continue }
+
+                var end = firstNonZeroTarget.createdAt.addingTimeInterval(TimeInterval(firstNonZeroTarget.duration * 60))
+
+                let earliestCancelTarget = package.filter({ $0.duration == 0 }).min(by: { $0.createdAt < $1.createdAt })
+
+                if let earliestCancelTarget = earliestCancelTarget {
+                    end = min(earliestCancelTarget.createdAt, end)
+                }
+
+                if let targetTop = firstNonZeroTarget.targetTop {
+                    let adjustedTarget = await units == .mgdL ? targetTop : targetTop.asMmolL
+                    calculatedTTs
+                        .append(ChartTempTarget(amount: adjustedTarget, start: firstNonZeroTarget.createdAt, end: end))
+                }
+            }
+
+            return calculatedTTs
+        }.value
+
+        // Update chartTempTargets on the main thread
+        await MainActor.run {
+            self.chartTempTargets = calculatedTTs
+        }
+    }
+}

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

@@ -9,8 +9,7 @@ struct CurrentGlucoseView: View {
     @Binding var highGlucose: Decimal
     @Binding var highGlucose: Decimal
     @Binding var cgmAvailable: Bool
     @Binding var cgmAvailable: Bool
 
 
-    var glucose: [GlucoseStored]
-    var manualGlucose: [GlucoseStored]
+    var glucose: [GlucoseStored] // This contains the last two glucose values, no matter if its manual or a cgm reading
 
 
     @State private var rotationDegrees: Double = 0.0
     @State private var rotationDegrees: Double = 0.0
     @State private var angularGradient = AngularGradient(colors: [
     @State private var angularGradient = AngularGradient(colors: [
@@ -24,12 +23,6 @@ struct CurrentGlucoseView: View {
 
 
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
 
 
-    private var combinedGlucoseValues: [GlucoseStored] {
-        // Combine and sort the glucose values
-        let combined = (glucose + manualGlucose).sorted { $0.date ?? Date() > $1.date ?? Date() }
-        return combined
-    }
-
     private var glucoseFormatter: NumberFormatter {
     private var glucoseFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
         formatter.numberStyle = .decimal
@@ -83,7 +76,7 @@ struct CurrentGlucoseView: View {
 
 
                 VStack(alignment: .center) {
                 VStack(alignment: .center) {
                     HStack {
                     HStack {
-                        if let glucoseValue = combinedGlucoseValues.first?.glucose {
+                        if let glucoseValue = glucose.last?.glucose {
                             let displayGlucose = units == .mgdL ? Decimal(glucoseValue).description : Decimal(glucoseValue)
                             let displayGlucose = units == .mgdL ? Decimal(glucoseValue).description : Decimal(glucoseValue)
                                 .formattedAsMmolL
                                 .formattedAsMmolL
                             Text(
                             Text(
@@ -98,7 +91,7 @@ struct CurrentGlucoseView: View {
                         }
                         }
                     }
                     }
                     HStack {
                     HStack {
-                        let minutesAgo = -1 * (combinedGlucoseValues.first?.date?.timeIntervalSinceNow ?? 0) / 60
+                        let minutesAgo = -1 * (glucose.last?.date?.timeIntervalSinceNow ?? 0) / 60
                         let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? ""
                         let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? ""
                         Text(
                         Text(
                             minutesAgo <= 1 ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
                             minutesAgo <= 1 ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
@@ -115,7 +108,7 @@ struct CurrentGlucoseView: View {
                     }.frame(alignment: .top)
                     }.frame(alignment: .top)
                 }
                 }
             }
             }
-            .onChange(of: combinedGlucoseValues.first?.directionEnum) { newDirection in
+            .onChange(of: glucose.last?.directionEnum) { newDirection in
                 withAnimation {
                 withAnimation {
                     switch newDirection {
                     switch newDirection {
                     case .doubleUp,
                     case .doubleUp,
@@ -156,20 +149,22 @@ struct CurrentGlucoseView: View {
     }
     }
 
 
     private var delta: String {
     private var delta: String {
-        guard combinedGlucoseValues.count >= 2 else {
+        guard glucose.count >= 2 else {
             return "--"
             return "--"
         }
         }
 
 
-        let lastGlucose = combinedGlucoseValues.first?.glucose ?? 0
-        let secondLastGlucose = combinedGlucoseValues.dropFirst().first?.glucose ?? 0
+        let lastGlucose = glucose.last?.glucose ?? 0
+        let secondLastGlucose = glucose.first?.glucose ?? 0
         let delta = lastGlucose - secondLastGlucose
         let delta = lastGlucose - secondLastGlucose
         let deltaAsDecimal = units == .mmolL ? Decimal(delta).asMmolL : Decimal(delta)
         let deltaAsDecimal = units == .mmolL ? Decimal(delta).asMmolL : Decimal(delta)
         return deltaFormatter.string(from: deltaAsDecimal as NSNumber) ?? "--"
         return deltaFormatter.string(from: deltaAsDecimal as NSNumber) ?? "--"
     }
     }
 
 
     var glucoseDisplayColor: Color {
     var glucoseDisplayColor: Color {
-        // Fetch the first glucose reading and convert it to Int for comparison
-        let whichGlucose = Int(combinedGlucoseValues.first?.glucose ?? 0)
+        guard let lastGlucose = glucose.last?.glucose else { return .primary }
+
+        // Convert the lastest glucose value to Int for comparison
+        let whichGlucose = Int(lastGlucose)
 
 
         // Define default color based on the color scheme
         // Define default color based on the color scheme
         let defaultColor: Color = colorScheme == .dark ? .white : .black
         let defaultColor: Color = colorScheme == .dark ? .white : .black

+ 49 - 26
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -103,13 +103,6 @@ extension Home {
             return dateFormatter
             return dateFormatter
         }
         }
 
 
-        private var spriteScene: SKScene {
-            let scene = SnowScene()
-            scene.scaleMode = .resizeFill
-            scene.backgroundColor = .clear
-            return scene
-        }
-
         private var color: LinearGradient {
         private var color: LinearGradient {
             colorScheme == .dark ? LinearGradient(
             colorScheme == .dark ? LinearGradient(
                 gradient: Gradient(colors: [
                 gradient: Gradient(colors: [
@@ -143,8 +136,7 @@ extension Home {
                 lowGlucose: $state.lowGlucose,
                 lowGlucose: $state.lowGlucose,
                 highGlucose: $state.highGlucose,
                 highGlucose: $state.highGlucose,
                 cgmAvailable: $state.cgmAvailable,
                 cgmAvailable: $state.cgmAvailable,
-                glucose: state.glucoseFromPersistence,
-                manualGlucose: state.manualGlucoseFromPersistence
+                glucose: state.latestTwoGlucoseValues
             ).scaleEffect(0.9)
             ).scaleEffect(0.9)
                 .onTapGesture {
                 .onTapGesture {
                     state.openCGM()
                     state.openCGM()
@@ -345,24 +337,18 @@ extension Home {
                 MainChartView(
                 MainChartView(
                     geo: geo,
                     geo: geo,
                     units: $state.units,
                     units: $state.units,
-                    announcement: $state.announcement,
                     hours: .constant(state.filteredHours),
                     hours: .constant(state.filteredHours),
-                    maxBasal: $state.maxBasal,
-                    autotunedBasalProfile: $state.autotunedBasalProfile,
-                    basalProfile: $state.basalProfile,
                     tempTargets: $state.tempTargets,
                     tempTargets: $state.tempTargets,
-                    smooth: $state.smooth,
                     highGlucose: $state.highGlucose,
                     highGlucose: $state.highGlucose,
                     lowGlucose: $state.lowGlucose,
                     lowGlucose: $state.lowGlucose,
                     screenHours: $state.hours,
                     screenHours: $state.hours,
                     displayXgridLines: $state.displayXgridLines,
                     displayXgridLines: $state.displayXgridLines,
                     displayYgridLines: $state.displayYgridLines,
                     displayYgridLines: $state.displayYgridLines,
                     thresholdLines: $state.thresholdLines,
                     thresholdLines: $state.thresholdLines,
-                    isTempTargetActive: $state.isTempTargetActive,
                     state: state
                     state: state
                 )
                 )
             }
             }
-            .padding(.bottom)
+            .padding(.bottom, UIDevice.adjustPadding(min: 0, max: nil))
         }
         }
 
 
         func highlightButtons() {
         func highlightButtons() {
@@ -574,7 +560,7 @@ extension Home {
                             showCancelAlert = true
                             showCancelAlert = true
                         }
                         }
                     }
                     }
-            }.padding(.horizontal, 10).padding(.bottom, 10)
+            }.padding(.horizontal, 10).padding(.bottom, UIDevice.adjustPadding(min: nil, max: 10))
                 .overlay {
                 .overlay {
                     /// just show temp target if no profile is already active
                     /// just show temp target if no profile is already active
                     if overrideString == nil, let tempTargetString = tempTargetString {
                     if overrideString == nil, let tempTargetString = tempTargetString {
@@ -606,7 +592,7 @@ extension Home {
                                     .font(.subheadline)
                                     .font(.subheadline)
                                 Spacer()
                                 Spacer()
                             }.padding(.horizontal, 10)
                             }.padding(.horizontal, 10)
-                        }.padding(.horizontal, 10).padding(.bottom, 10)
+                        }.padding(.horizontal, 10).padding(.bottom, UIDevice.adjustPadding(min: nil, max: 10))
                     }
                     }
                 }
                 }
         }
         }
@@ -688,7 +674,7 @@ extension Home {
                     }.padding(.horizontal, 10)
                     }.padding(.horizontal, 10)
                         .padding(.trailing, 8)
                         .padding(.trailing, 8)
 
 
-                }.padding(.horizontal, 10).padding(.bottom, 10)
+                }.padding(.horizontal, 10).padding(.bottom, UIDevice.adjustPadding(min: nil, max: 10))
                     .overlay(alignment: .bottom) {
                     .overlay(alignment: .bottom) {
                         bolusProgressBar(progress).padding(.horizontal, 18).offset(y: 48)
                         bolusProgressBar(progress).padding(.horizontal, 18).offset(y: 48)
                     }.clipShape(RoundedRectangle(cornerRadius: 15))
                     }.clipShape(RoundedRectangle(cornerRadius: 15))
@@ -715,16 +701,18 @@ extension Home {
                         }.padding(.leading, 20)
                         }.padding(.leading, 20)
                     }.padding(.top, 10)
                     }.padding(.top, 10)
 
 
-                    mealPanel(geo).padding(.top, 30).padding(.bottom, 20)
+                    mealPanel(geo).padding(.top, UIDevice.adjustPadding(min: nil, max: 30))
+                        .padding(.bottom, UIDevice.adjustPadding(min: nil, max: 20))
 
 
                     mainChart(geo: geo)
                     mainChart(geo: geo)
 
 
-                    timeInterval.padding(.top, 12).padding(.bottom, 12)
+                    timeInterval.padding(.top, UIDevice.adjustPadding(min: 0, max: 12))
+                        .padding(.bottom, UIDevice.adjustPadding(min: 0, max: 12))
 
 
                     if let progress = state.bolusProgress {
                     if let progress = state.bolusProgress {
-                        bolusView(geo: geo, progress).padding(.bottom, 40)
+                        bolusView(geo: geo, progress).padding(.bottom, UIDevice.adjustPadding(min: nil, max: 40))
                     } else {
                     } else {
-                        profileView(geo: geo).padding(.bottom, 40)
+                        profileView(geo: geo).padding(.bottom, UIDevice.adjustPadding(min: nil, max: 40))
                     }
                     }
                 }
                 }
                 .background(color)
                 .background(color)
@@ -822,7 +810,7 @@ extension Home {
                         List {
                         List {
                             DefinitionRow(
                             DefinitionRow(
                                 term: "Cone of Uncertainty",
                                 term: "Cone of Uncertainty",
-                                definition: "For simplicity reasons, oref's various forecast curves are displayed as a \"Cone of Uncertainty\" that depicts a possible, forecasted range of future glucose fluctuation based on the current data and the algothim's result.",
+                                definition: "For simplicity reasons, oref's various forecast curves are displayed as a \"Cone of Uncertainty\" that depicts a possible, forecasted range of future glucose fluctuation based on the current data and the algothim's result.\n\nTo modify the forecast display type, go to Trio Settings > Features > User Interface > Forecast Display Type.",
                                 color: Color.blue.opacity(0.5)
                                 color: Color.blue.opacity(0.5)
                             )
                             )
                         }
                         }
@@ -962,8 +950,13 @@ extension Home {
                         Text("Invalid CGM reading (HIGH).").font(.callout).bold().foregroundColor(.loopRed).padding(.top, 8)
                         Text("Invalid CGM reading (HIGH).").font(.callout).bold().foregroundColor(.loopRed).padding(.top, 8)
                         Text("SMBs and High Temps Disabled.").font(.caption).foregroundColor(.white).padding(.bottom, 4)
                         Text("SMBs and High Temps Disabled.").font(.caption).foregroundColor(.white).padding(.bottom, 4)
                     } else {
                     } else {
-                        TagCloudView(tags: determination.reasonParts, shouldParseToMmolL: state.units == .mmolL)
-                            .animation(.none, value: false)
+                        let tags = !state.isSmoothingEnabled ? determination.reasonParts : determination
+                            .reasonParts + ["Smoothing: On"]
+                        TagCloudView(
+                            tags: tags,
+                            shouldParseToMmolL: state.units == .mmolL
+                        )
+                        .animation(.none, value: false)
 
 
                         Text(
                         Text(
                             self
                             self
@@ -1003,3 +996,33 @@ extension Home {
         }
         }
     }
     }
 }
 }
+
+extension UIDevice {
+    public enum DeviceSize: CGFloat {
+        case smallDevice = 667 // Height for 4" iPhone SE
+        case largeDevice = 852 // Height for 6.1" iPhone 15 Pro
+    }
+
+    @usableFromInline static func adjustPadding(min: CGFloat? = nil, max: CGFloat? = nil) -> CGFloat? {
+        if UIScreen.screenHeight > UIDevice.DeviceSize.smallDevice.rawValue {
+            if UIScreen.screenHeight >= UIDevice.DeviceSize.largeDevice.rawValue {
+                return max
+            } else {
+                return min != nil ?
+                    (max != nil ? max! * (UIScreen.screenHeight / UIDevice.DeviceSize.largeDevice.rawValue) : nil) : nil
+            }
+        } else {
+            return min
+        }
+    }
+}
+
+extension UIScreen {
+    static var screenHeight: CGFloat {
+        UIScreen.main.bounds.height
+    }
+
+    static var screenWidth: CGFloat {
+        UIScreen.main.bounds.width
+    }
+}

+ 7 - 1
FreeAPS/Sources/Modules/ISFEditor/ISFEditorProvider.swift

@@ -1,3 +1,5 @@
+import Foundation
+
 extension ISFEditor {
 extension ISFEditor {
     final class Provider: BaseProvider, ISFEditorProvider {
     final class Provider: BaseProvider, ISFEditorProvider {
         var profile: InsulinSensitivities {
         var profile: InsulinSensitivities {
@@ -12,7 +14,11 @@ extension ISFEditor {
             // migrate existing mmol/L Trio users from mmol/L settings to pure mg/dl settings
             // migrate existing mmol/L Trio users from mmol/L settings to pure mg/dl settings
             if retrievedSensitivities.units == .mmolL || retrievedSensitivities.userPreferredUnits == .mmolL {
             if retrievedSensitivities.units == .mmolL || retrievedSensitivities.userPreferredUnits == .mmolL {
                 let convertedSensitivities = retrievedSensitivities.sensitivities.map { isf in
                 let convertedSensitivities = retrievedSensitivities.sensitivities.map { isf in
-                    InsulinSensitivityEntry(sensitivity: isf.sensitivity.asMgdL, offset: isf.offset, start: isf.start)
+                    InsulinSensitivityEntry(
+                        sensitivity: storage.parseSettingIfMmolL(value: isf.sensitivity),
+                        offset: isf.offset,
+                        start: isf.start
+                    )
                 }
                 }
                 retrievedSensitivities = InsulinSensitivities(
                 retrievedSensitivities = InsulinSensitivities(
                     units: .mgdL,
                     units: .mgdL,

+ 1 - 1
FreeAPS/Sources/Modules/MealSettings/View/MealSettingsRootView.swift

@@ -115,7 +115,7 @@ extension MealSettings {
 
 
                                 if displayPickerMaxFat {
                                 if displayPickerMaxFat {
                                     let setting = PickerSettingsProvider.shared.settings.maxFat
                                     let setting = PickerSettingsProvider.shared.settings.maxFat
-                                    Picker(selection: $state.maxCarbs, label: Text("")) {
+                                    Picker(selection: $state.maxFat, label: Text("")) {
                                         ForEach(
                                         ForEach(
                                             PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
                                             PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
                                             id: \.self
                                             id: \.self

+ 20 - 15
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -140,7 +140,7 @@ extension NightscoutConfig {
                     throw NSError(
                     throw NSError(
                         domain: "ImportError",
                         domain: "ImportError",
                         code: 1,
                         code: 1,
-                        userInfo: [NSLocalizedDescriptionKey: "Can't find the default Nightscout Profile."]
+                        userInfo: [NSLocalizedDescriptionKey: "Cannot find the default Nightscout Profile."]
                     )
                     )
                 }
                 }
 
 
@@ -159,7 +159,6 @@ extension NightscoutConfig {
 
 
                 if carbratios.contains(where: { $0.ratio <= 0 }) {
                 if carbratios.contains(where: { $0.ratio <= 0 }) {
                     importStatus = .failed
                     importStatus = .failed
-
                     throw NSError(
                     throw NSError(
                         domain: "ImportError",
                         domain: "ImportError",
                         code: 2,
                         code: 2,
@@ -181,22 +180,20 @@ extension NightscoutConfig {
 
 
                 if pumpName != "Omnipod DASH", basals.contains(where: { $0.rate <= 0 }) {
                 if pumpName != "Omnipod DASH", basals.contains(where: { $0.rate <= 0 }) {
                     importStatus = .failed
                     importStatus = .failed
-
                     throw NSError(
                     throw NSError(
                         domain: "ImportError",
                         domain: "ImportError",
                         code: 3,
                         code: 3,
-                        userInfo: [NSLocalizedDescriptionKey: "Invalid Nightscout Basal Settings. Import aborted."]
+                        userInfo: [NSLocalizedDescriptionKey: "Invalid Nightscout basal rates found. Import aborted."]
                     )
                     )
                 }
                 }
 
 
                 if pumpName == "Omnipod DASH", basals.reduce(0, { $0 + $1.rate }) <= 0 {
                 if pumpName == "Omnipod DASH", basals.reduce(0, { $0 + $1.rate }) <= 0 {
                     importStatus = .failed
                     importStatus = .failed
-
                     throw NSError(
                     throw NSError(
                         domain: "ImportError",
                         domain: "ImportError",
                         code: 4,
                         code: 4,
                         userInfo: [
                         userInfo: [
-                            NSLocalizedDescriptionKey: "Total Basal insulin amount is 0 or lower in Nightscout Profile settings. Import aborted."
+                            NSLocalizedDescriptionKey: "Invalid Nightscout basal rates found. Basal rate total cannot be 0 U/hr. Import aborted."
                         ]
                         ]
                     )
                     )
                 }
                 }
@@ -213,11 +210,10 @@ extension NightscoutConfig {
 
 
                 if sensitivities.contains(where: { $0.sensitivity <= 0 }) {
                 if sensitivities.contains(where: { $0.sensitivity <= 0 }) {
                     importStatus = .failed
                     importStatus = .failed
-
                     throw NSError(
                     throw NSError(
                         domain: "ImportError",
                         domain: "ImportError",
                         code: 5,
                         code: 5,
-                        userInfo: [NSLocalizedDescriptionKey: "Invalid Nightscout Sensitivities Settings. Import aborted."]
+                        userInfo: [NSLocalizedDescriptionKey: "Invalid Nightscout insulin sensitivity profile. Import aborted."]
                     )
                     )
                 }
                 }
 
 
@@ -227,8 +223,6 @@ extension NightscoutConfig {
                     sensitivities: sensitivities
                     sensitivities: sensitivities
                 )
                 )
 
 
-                debug(.nightscout, "FETCHED SENSITIVITIES: \(sensitivitiesProfile)")
-
                 // Targets
                 // Targets
                 let targets = fetchedProfile.target_low.map { target in
                 let targets = fetchedProfile.target_low.map { target in
                     BGTargetEntry(
                     BGTargetEntry(
@@ -241,8 +235,6 @@ extension NightscoutConfig {
 
 
                 let targetsProfile = BGTargets(units: .mgdL, userPreferredUnits: .mgdL, targets: targets)
                 let targetsProfile = BGTargets(units: .mgdL, userPreferredUnits: .mgdL, targets: targets)
 
 
-                debug(.nightscout, "FETCHED TARGETS: \(targetsProfile)")
-
                 // Save to storage and pump
                 // Save to storage and pump
                 if let pump = apsManager.pumpManager {
                 if let pump = apsManager.pumpManager {
                     let syncValues = basals.map {
                     let syncValues = basals.map {
@@ -261,11 +253,21 @@ extension NightscoutConfig {
                             )
                             )
                         case .failure:
                         case .failure:
                             self.importErrors.append(
                             self.importErrors.append(
-                                "Settings were imported but the Basals couldn't be saved to pump (communication error)."
+                                "Settings were imported but the basal rates could not be saved to pump (communication error)."
                             )
                             )
                             self.importStatus = .failed
                             self.importStatus = .failed
                         }
                         }
                     }
                     }
+
+                    if importErrors.isNotEmpty, importStatus == .failed {
+                        throw NSError(
+                            domain: "ImportError",
+                            code: 6,
+                            userInfo: [
+                                NSLocalizedDescriptionKey: "Settings were imported but the basal rates could not be saved to pump (communication error)."
+                            ]
+                        )
+                    }
                 } else {
                 } else {
                     storage.save(basals, as: OpenAPS.Settings.basalProfile)
                     storage.save(basals, as: OpenAPS.Settings.basalProfile)
                     finalizeImport(
                     finalizeImport(
@@ -276,8 +278,10 @@ extension NightscoutConfig {
                     )
                     )
                 }
                 }
             } catch {
             } catch {
-                importErrors.append(error.localizedDescription)
-                debug(.service, "Settings import failed with error: \(error.localizedDescription)")
+                DispatchQueue.main.async {
+                    self.importErrors.append(error.localizedDescription)
+                    debug(.service, "Settings import failed with error: \(error.localizedDescription)")
+                }
             }
             }
         }
         }
 
 
@@ -375,5 +379,6 @@ extension NightscoutConfig.StateModel {
         case running
         case running
         case finished
         case finished
         case failed
         case failed
+        case noPumpConnected
     }
     }
 }
 }

+ 16 - 43
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -35,20 +35,6 @@ extension NightscoutConfig {
                 )
                 )
         }
         }
 
 
-        @FetchRequest(
-            entity: ImportError.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
-                format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate
-            )
-        ) var fetchedErrors: FetchedResults<ImportError>
-
-        private var portFormater: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.allowsFloats = false
-            formatter.usesGroupingSeparator = false
-            return formatter
-        }
-
         var body: some View {
         var body: some View {
             ZStack {
             ZStack {
                 Form {
                 Form {
@@ -67,16 +53,26 @@ extension NightscoutConfig {
                                 importAlert = Alert(
                                 importAlert = Alert(
                                     title: Text("Import Therapy Settings?"),
                                     title: Text("Import Therapy Settings?"),
                                     message: Text(
                                     message: Text(
-                                        NSLocalizedString(
-                                            "This will replace some or all of your current therapy settings. Are you sure you want to import profile settings from Nightscout?",
-                                            comment: "Nightscout Settings Import Alert"
-                                        )
+                                        "Are you sure you want to import profile settings from Nightscout?\n\nThis will overwrite the following Trio therapy settings: Basal Rates, Insulin Sensitivities, Carb Ratios, Target Glucose, and Duration of Insulin Action."
                                     ),
                                     ),
                                     primaryButton: .default(
                                     primaryButton: .default(
                                         Text("Yes, Import!"),
                                         Text("Yes, Import!"),
                                         action: {
                                         action: {
                                             Task {
                                             Task {
                                                 await state.importSettings()
                                                 await state.importSettings()
+                                                // Check the import status and errors after the import process finishes
+                                                if state.importStatus == .failed, state.importErrors.isNotEmpty,
+                                                   let errorMessage = state.importErrors.first
+                                                {
+                                                    DispatchQueue.main.async {
+                                                        importAlert = Alert(
+                                                            title: Text("Import Failed"),
+                                                            message: Text(errorMessage.description),
+                                                            dismissButton: .default(Text("OK"))
+                                                        )
+                                                        isImportAlertPresented = true
+                                                    }
+                                                }
                                             }
                                             }
                                         }
                                         }
                                     ),
                                     ),
@@ -102,7 +98,7 @@ extension NightscoutConfig {
                                     action: {
                                     action: {
                                         hintLabel = "Import Settings from Nightscout"
                                         hintLabel = "Import Settings from Nightscout"
                                         selectedVerboseHint =
                                         selectedVerboseHint =
-                                            "Importing settings from Nightscout will overwrite the following Trio therapy settings: \n • DIA (Pump settings) \n • Basal Profile \n • Insulin Sensitivities \n • Carb Ratios \n • Target Glucose"
+                                            "This will overwrite the following Trio therapy settings: \n • Basal Rates \n • Insulin Sensitivities \n • Carb Ratios \n • Target Glucose \n • Duration of Insulin Action"
                                         shouldDisplayHint.toggle()
                                         shouldDisplayHint.toggle()
                                     },
                                     },
                                     label: {
                                     label: {
@@ -160,29 +156,6 @@ extension NightscoutConfig {
                 if state.importStatus == .running {
                 if state.importStatus == .running {
                     CustomProgressView(text: "Importing Profile...")
                     CustomProgressView(text: "Importing Profile...")
                 }
                 }
-                //            .alert(isPresented: $importedHasRun) {
-                //                Alert(
-                //                    title: Text(
-                //                        (fetchedErrors.first?.error ?? "")
-                //                            .count < 4 ? "Settings imported" : "Import Error"
-                //                    ),
-                //                    message: Text(
-                //                        (fetchedErrors.first?.error ?? "").count < 4 ?
-                //                            NSLocalizedString(
-                //                                "\nNow please verify all of your new settings thoroughly: \n\n • DIA (Pump settings)\n • Basal Rates\n • Insulin Sensitivities\n • Carb Ratios\n • Target Glucose\n\n in Trio Settings -> Configuration.\n\nBad or invalid profile settings could have disastrous effects.",
-                //                                comment: "Imported Profiles Alert"
-                //                            ) :
-                //                            NSLocalizedString(
-                //                                fetchedErrors.first?.error ?? "",
-                //                                comment: "Import Error"
-                //                            )
-                //                    ),
-                //                    primaryButton: .destructive(
-                //                        Text("OK")
-                //                    ),
-                //                    secondaryButton: .cancel()
-                //                )
-                //            }
             }
             }
             .fullScreenCover(isPresented: $state.isImportResultReviewPresented, content: {
             .fullScreenCover(isPresented: $state.isImportResultReviewPresented, content: {
                 NightscoutImportResultView(resolver: resolver, state: state)
                 NightscoutImportResultView(resolver: resolver, state: state)
@@ -199,7 +172,7 @@ extension NightscoutConfig {
             .navigationBarTitle("Nightscout")
             .navigationBarTitle("Nightscout")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
             .alert(isPresented: $isImportAlertPresented) {
             .alert(isPresented: $isImportAlertPresented) {
-                importAlert!
+                importAlert ?? Alert(title: Text("Unknown Error"))
             }
             }
             .scrollContentBackground(.hidden).background(color)
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)

+ 3 - 9
FreeAPS/Sources/Modules/NightscoutConfig/View/ProfileImport/NightscoutImportResultView.swift

@@ -50,8 +50,9 @@ struct NightscoutImportResultView: BaseView {
                     header: Text("Imported Nightscout Data"),
                     header: Text("Imported Nightscout Data"),
                     content: {
                     content: {
                         Text(
                         Text(
-                            "Trio has successfully imported your default Nightscout profile and applied it as therapy settings. This has replaced your previous therapy settings."
-                        )
+                            "Trio has successfully imported your default Nightscout profile and stored it as therapy settings. "
+                        ) +
+                            Text("This has replaced your previous therapy settings.").bold().foregroundColor(.accentColor)
                         Text("Please review the following settings:").bold()
                         Text("Please review the following settings:").bold()
                     }
                     }
                 ).listRowBackground(Color.chart)
                 ).listRowBackground(Color.chart)
@@ -131,13 +132,6 @@ struct NightscoutImportResultView: BaseView {
                     }
                     }
                 }.listRowBackground(allViewsVisited ? Color(.systemBlue) : Color(.systemGray4))
                 }.listRowBackground(allViewsVisited ? Color(.systemBlue) : Color(.systemGray4))
             }
             }
-//            .toolbar(content: {
-//                ToolbarItem(placement: .topBarLeading) {
-//                    Button(action: { state.isImportResultReviewPresented = false }, label: {
-//                        Text("Cancel")
-//                    })
-//                }
-//            })
             .navigationTitle("Review Import")
             .navigationTitle("Review Import")
             .navigationBarTitleDisplayMode(.large)
             .navigationBarTitleDisplayMode(.large)
             .scrollContentBackground(.hidden).background(color)
             .scrollContentBackground(.hidden).background(color)

+ 1 - 1
FreeAPS/Sources/Modules/NightscoutConfig/View/ProfileImport/ReviewInsulinActionView.swift

@@ -65,7 +65,7 @@ struct ReviewInsulinActionView: BaseView {
         }
         }
         .scrollContentBackground(.hidden).background(color)
         .scrollContentBackground(.hidden).background(color)
         .onAppear(perform: configureView)
         .onAppear(perform: configureView)
-        .navigationTitle("DIA")
+        .navigationTitle("Duration of Insulin Action")
         .navigationBarTitleDisplayMode(.automatic)
         .navigationBarTitleDisplayMode(.automatic)
         .onDisappear {
         .onDisappear {
             state.saveReviewedInsulinAction()
             state.saveReviewedInsulinAction()

+ 12 - 1
FreeAPS/Sources/Modules/OverrideConfig/View/OverrideRootView.swift

@@ -51,6 +51,17 @@ extension OverrideConfig {
             return formatter
             return formatter
         }
         }
 
 
+        private var glucoseFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 0
+            if state.units == .mmolL {
+                formatter.maximumFractionDigits = 1
+            }
+            formatter.roundingMode = .halfUp
+            return formatter
+        }
+
         var body: some View {
         var body: some View {
             VStack {
             VStack {
                 Picker("Tab", selection: $state.selectedTab) {
                 Picker("Tab", selection: $state.selectedTab) {
@@ -271,7 +282,7 @@ extension OverrideConfig {
                     HStack {
                     HStack {
                         Text("Target")
                         Text("Target")
                         Spacer()
                         Spacer()
-                        TextFieldWithToolBar(text: $state.low, placeholder: "0", numberFormatter: formatter)
+                        TextFieldWithToolBar(text: $state.low, placeholder: "0", numberFormatter: glucoseFormatter)
                         Text(state.units.rawValue).foregroundColor(.secondary)
                         Text(state.units.rawValue).foregroundColor(.secondary)
                     }
                     }
                     HStack {
                     HStack {

+ 2 - 1
FreeAPS/Sources/Modules/Settings/SettingItems.swift

@@ -190,7 +190,8 @@ enum SettingItems {
                 "Override HbA1c Unit",
                 "Override HbA1c Unit",
                 "Standing / Laying TIR Chart",
                 "Standing / Laying TIR Chart",
                 "Show Carbs Required Badge",
                 "Show Carbs Required Badge",
-                "Carbs Required Threshold"
+                "Carbs Required Threshold",
+                "Forecast Display Type"
             ],
             ],
             path: ["Features", "User Interface"]
             path: ["Features", "User Interface"]
         ),
         ),

+ 7 - 2
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -80,8 +80,6 @@ extension Settings {
                                         }
                                         }
                                     }
                                     }
                                 }
                                 }
-
-                                Text("Statistics").navigationLink(to: .statistics, from: self)
                             }
                             }
                         }
                         }
                     ).listRowBackground(Color.chart)
                     ).listRowBackground(Color.chart)
@@ -221,6 +219,13 @@ extension Settings {
 //                        .navigationLink(to: .configEditor(file: OpenAPS.Settings.bgTargets), from: self)
 //                        .navigationLink(to: .configEditor(file: OpenAPS.Settings.bgTargets), from: self)
 //                    Text("Sensitivities")
 //                    Text("Sensitivities")
 //                        .navigationLink(to: .configEditor(file: OpenAPS.Settings.insulinSensitivities), from: self)
 //                        .navigationLink(to: .configEditor(file: OpenAPS.Settings.insulinSensitivities), from: self)
+//                    Text("Profile")
+//                        .navigationLink(to: .configEditor(file: OpenAPS.Settings.profile), from: self)
+//                    Text("Preferences")
+//                        .navigationLink(
+//                            to: .configEditor(file: OpenAPS.Settings.preferences),
+//                            from: self
+//                        )
 //                }.listRowBackground(Color.chart)
 //                }.listRowBackground(Color.chart)
 
 
                 // TODO: remove this more or less entirely; add build-time flag to enable Middleware; add settings export feature
                 // TODO: remove this more or less entirely; add build-time flag to enable Middleware; add settings export feature

+ 1 - 3
FreeAPS/Sources/Modules/Settings/View/Subviews/ServicesView.swift

@@ -38,9 +38,7 @@ struct ServicesView: BaseView {
                 header: Text("Connected Services"),
                 header: Text("Connected Services"),
                 content: {
                 content: {
                     Text("Nightscout").navigationLink(to: .nighscoutConfig, from: self)
                     Text("Nightscout").navigationLink(to: .nighscoutConfig, from: self)
-                    NavigationLink(destination: TidepoolStartView(state: state)) {
-                        Text("Tidepool")
-                    }
+                    Text("Tidepool").navigationLink(to: .tidepoolConfig, from: self)
                     if HKHealthStore.isHealthDataAvailable() {
                     if HKHealthStore.isHealthDataAvailable() {
                         Text("Apple Health").navigationLink(to: .healthkit, from: self)
                         Text("Apple Health").navigationLink(to: .healthkit, from: self)
                     }
                     }

+ 4 - 1
FreeAPS/Sources/Modules/Settings/View/TidepoolStartView.swift

@@ -1,7 +1,9 @@
 
 
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
-struct TidepoolStartView: View {
+struct TidepoolStartView: BaseView {
+    let resolver: Resolver
     @ObservedObject var state: Settings.StateModel
     @ObservedObject var state: Settings.StateModel
 
 
     @State private var shouldDisplayHint: Bool = false
     @State private var shouldDisplayHint: Bool = false
@@ -94,5 +96,6 @@ struct TidepoolStartView: View {
         .scrollContentBackground(.hidden).background(color)
         .scrollContentBackground(.hidden).background(color)
         .navigationTitle("Tidepool")
         .navigationTitle("Tidepool")
         .navigationBarTitleDisplayMode(.automatic)
         .navigationBarTitleDisplayMode(.automatic)
+        .onAppear(perform: configureView)
     }
     }
 }
 }

+ 13 - 1
FreeAPS/Sources/Modules/Stat/View/StatRootView.swift

@@ -148,7 +148,19 @@ extension Stat {
             }.background(color)
             }.background(color)
                 .onAppear(perform: configureView)
                 .onAppear(perform: configureView)
                 .navigationBarTitle("Statistics")
                 .navigationBarTitle("Statistics")
-                .navigationBarTitleDisplayMode(.large)
+                .navigationBarTitleDisplayMode(.automatic)
+                .toolbar {
+                    ToolbarItem(placement: .topBarLeading, content: {
+                        Button(
+                            action: { state.hideModal() },
+                            label: {
+                                HStack {
+                                    Text("Close")
+                                }
+                            }
+                        )
+                    })
+                }
         }
         }
     }
     }
 }
 }

+ 8 - 1
FreeAPS/Sources/Modules/TargetsEditor/TargetsEditorProvider.swift

@@ -1,3 +1,5 @@
+import Foundation
+
 extension TargetsEditor {
 extension TargetsEditor {
     final class Provider: BaseProvider, TargetsEditorProvider {
     final class Provider: BaseProvider, TargetsEditorProvider {
         var profile: BGTargets {
         var profile: BGTargets {
@@ -8,7 +10,12 @@ extension TargetsEditor {
             // migrate existing mmol/L Trio users from mmol/L settings to pure mg/dl settings
             // migrate existing mmol/L Trio users from mmol/L settings to pure mg/dl settings
             if retrievedTargets.units == .mmolL || retrievedTargets.userPreferredUnits == .mmolL {
             if retrievedTargets.units == .mmolL || retrievedTargets.userPreferredUnits == .mmolL {
                 let convertedTargets = retrievedTargets.targets.map { target in
                 let convertedTargets = retrievedTargets.targets.map { target in
-                    BGTargetEntry(low: target.low.asMgdL, high: target.high.asMgdL, start: target.start, offset: target.offset)
+                    BGTargetEntry(
+                        low: storage.parseSettingIfMmolL(value: target.low),
+                        high: storage.parseSettingIfMmolL(value: target.high),
+                        start: target.start,
+                        offset: target.offset
+                    )
                 }
                 }
                 retrievedTargets = BGTargets(units: .mgdL, userPreferredUnits: .mgdL, targets: convertedTargets)
                 retrievedTargets = BGTargets(units: .mgdL, userPreferredUnits: .mgdL, targets: convertedTargets)
                 saveProfile(retrievedTargets)
                 saveProfile(retrievedTargets)

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

@@ -8,6 +8,7 @@ enum Screen: Identifiable, Hashable {
     case configEditor(file: String)
     case configEditor(file: String)
     case nighscoutConfig
     case nighscoutConfig
     case nighscoutConfigDirect
     case nighscoutConfigDirect
+    case tidepoolConfig
     case pumpConfig
     case pumpConfig
     case pumpConfigDirect
     case pumpConfigDirect
     case basalProfileEditor
     case basalProfileEditor
@@ -66,6 +67,8 @@ extension Screen {
             NightscoutConfig.RootView(resolver: resolver, displayClose: false)
             NightscoutConfig.RootView(resolver: resolver, displayClose: false)
         case .nighscoutConfigDirect:
         case .nighscoutConfigDirect:
             NightscoutConfig.RootView(resolver: resolver, displayClose: true)
             NightscoutConfig.RootView(resolver: resolver, displayClose: true)
+        case .tidepoolConfig:
+            TidepoolStartView(resolver: resolver, state: Settings.StateModel())
         case .pumpConfig:
         case .pumpConfig:
             PumpConfig.RootView(resolver: resolver, displayClose: false)
             PumpConfig.RootView(resolver: resolver, displayClose: false)
         case .pumpConfigDirect:
         case .pumpConfigDirect:

+ 3 - 2
FreeAPS/Sources/Services/Calendar/CalendarManager.swift

@@ -180,7 +180,7 @@ final class BaseCalendarManager: CalendarManager, Injectable {
             propertiesToFetch: ["timestamp", "cob", "iob", "objectID"]
             propertiesToFetch: ["timestamp", "cob", "iob", "objectID"]
         )
         )
 
 
-        guard let fetchedResults = results as? [[String: Any]], !fetchedResults.isEmpty else { return nil }
+        guard let fetchedResults = results as? [[String: Any]] /* , !fetchedResults.isEmpty */ else { return nil }
 
 
         return await backgroundContext.perform {
         return await backgroundContext.perform {
             return fetchedResults.first?["objectID"] as? NSManagedObjectID
             return fetchedResults.first?["objectID"] as? NSManagedObjectID
@@ -193,7 +193,8 @@ final class BaseCalendarManager: CalendarManager, Injectable {
             onContext: backgroundContext,
             onContext: backgroundContext,
             predicate: NSPredicate.predicateFor30MinAgo,
             predicate: NSPredicate.predicateFor30MinAgo,
             key: "date",
             key: "date",
-            ascending: false
+            ascending: false,
+            propertiesToFetch: ["objectID", "glucose"]
         )
         )
 
 
         guard let fetchedResults = results as? [[String: Any]] else { return [] }
         guard let fetchedResults = results as? [[String: Any]] else { return [] }

+ 125 - 2
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -79,9 +79,12 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         subscribe()
         subscribe()
         coreDataObserver = CoreDataObserver()
         coreDataObserver = CoreDataObserver()
         registerHandlers()
         registerHandlers()
+        setupNotification()
     }
     }
 
 
     private func subscribe() {
     private func subscribe() {
+        broadcaster.register(TempTargetsObserver.self, observer: self)
+
         _ = reachabilityManager.startListening(onQueue: processQueue) { status in
         _ = reachabilityManager.startListening(onQueue: processQueue) { status in
             debug(.nightscout, "Network status: \(status)")
             debug(.nightscout, "Network status: \(status)")
         }
         }
@@ -126,6 +129,21 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         }
         }
     }
     }
 
 
+    func setupNotification() {
+        Foundation.NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(handleOverrideConfigurationUpdate),
+            name: .didUpdateOverrideConfiguration,
+            object: nil
+        )
+    }
+
+    @objc private func handleOverrideConfigurationUpdate() {
+        Task.detached {
+            await self.uploadOverrides()
+        }
+    }
+
     func sourceInfo() -> [String: Any]? {
     func sourceInfo() -> [String: Any]? {
         if let ping = ping {
         if let ping = ping {
             return [GlucoseSourceKey.nightscoutPing.rawValue: ping]
             return [GlucoseSourceKey.nightscoutPing.rawValue: ping]
@@ -321,7 +339,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             return
             return
         }
         }
 
 
-        // Suggested/ Enacted
+        // Suggested / Enacted
         async let enactedDeterminationID = determinationStorage
         async let enactedDeterminationID = determinationStorage
             .fetchLastDeterminationObjectID(predicate: NSPredicate.enactedDeterminationsNotYetUploadedToNightscout)
             .fetchLastDeterminationObjectID(predicate: NSPredicate.enactedDeterminationsNotYetUploadedToNightscout)
         async let suggestedDeterminationID = determinationStorage
         async let suggestedDeterminationID = determinationStorage
@@ -333,7 +351,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         async let fetchedIOBEntry = storage.retrieveAsync(OpenAPS.Monitor.iob, as: [IOBEntry].self)
         async let fetchedIOBEntry = storage.retrieveAsync(OpenAPS.Monitor.iob, as: [IOBEntry].self)
         async let fetchedPumpStatus = storage.retrieveAsync(OpenAPS.Monitor.status, as: PumpStatus.self)
         async let fetchedPumpStatus = storage.retrieveAsync(OpenAPS.Monitor.status, as: PumpStatus.self)
 
 
-        let (fetchedEnactedDetermination, fetchedSuggestedDetermination) = await (
+        var (fetchedEnactedDetermination, fetchedSuggestedDetermination) = await (
             determinationStorage.getOrefDeterminationNotYetUploadedToNightscout(enactedDeterminationID),
             determinationStorage.getOrefDeterminationNotYetUploadedToNightscout(enactedDeterminationID),
             determinationStorage.getOrefDeterminationNotYetUploadedToNightscout(suggestedDeterminationID)
             determinationStorage.getOrefDeterminationNotYetUploadedToNightscout(suggestedDeterminationID)
         )
         )
@@ -351,6 +369,10 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         var modifiedSuggestedDetermination = fetchedSuggestedDetermination
         var modifiedSuggestedDetermination = fetchedSuggestedDetermination
         if var suggestion = fetchedSuggestedDetermination {
         if var suggestion = fetchedSuggestedDetermination {
             suggestion.timestamp = suggestion.deliverAt
             suggestion.timestamp = suggestion.deliverAt
+
+            if settingsManager.settings.units == .mmolL {
+                suggestion.reason = parseReasonGlucoseValuesToMmolL(suggestion.reason)
+            }
             // Check whether the last suggestion that was uploaded is the same that is fetched again when we are attempting to upload the enacted determination
             // Check whether the last suggestion that was uploaded is the same that is fetched again when we are attempting to upload the enacted determination
             // Apparently we are too fast; so the flag update is not fast enough to have the predicate filter last suggestion out
             // Apparently we are too fast; so the flag update is not fast enough to have the predicate filter last suggestion out
             // If this check is truthy, set suggestion to nil so it's not uploaded again
             // If this check is truthy, set suggestion to nil so it's not uploaded again
@@ -361,6 +383,20 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             }
             }
         }
         }
 
 
+        if var fetchedEnacted = fetchedEnactedDetermination, settingsManager.settings.units == .mmolL {
+            var modifiedFetchedEnactedDetermination = fetchedEnactedDetermination
+            modifiedFetchedEnactedDetermination?
+                .reason = parseReasonGlucoseValuesToMmolL(fetchedEnacted.reason)
+
+            modifiedFetchedEnactedDetermination?.bg = fetchedEnacted.bg?.asMmolL
+            modifiedFetchedEnactedDetermination?.current_target = fetchedEnacted.current_target?.asMmolL
+            modifiedFetchedEnactedDetermination?.minGuardBG = fetchedEnacted.minGuardBG?.asMmolL
+            modifiedFetchedEnactedDetermination?.minPredBG = fetchedEnacted.minPredBG?.asMmolL
+            modifiedFetchedEnactedDetermination?.threshold = fetchedEnacted.threshold?.asMmolL
+
+            fetchedEnactedDetermination = modifiedFetchedEnactedDetermination
+        }
+
         // Gather all relevant data for OpenAPS Status
         // Gather all relevant data for OpenAPS Status
         let iob = await fetchedIOBEntry
         let iob = await fetchedIOBEntry
         let openapsStatus = OpenAPSStatus(
         let openapsStatus = OpenAPSStatus(
@@ -896,3 +932,90 @@ extension Array {
         }
         }
     }
     }
 }
 }
+
+extension BaseNightscoutManager: TempTargetsObserver {
+    func tempTargetsDidUpdate(_: [TempTarget]) {
+        Task.detached {
+            await self.uploadTempTargets()
+        }
+    }
+}
+
+extension BaseNightscoutManager {
+    /**
+     Converts glucose-related values in the given `reason` string to mmol/L, including ranges (e.g., `ISF: 54→54`), comparisons (e.g., `maxDelta 37 > 20% of BG 95`), and both positive and negative values (e.g., `Dev: -36`).
+
+     - Parameters:
+       - reason: The string containing glucose-related values to be converted.
+
+     - Returns:
+       A string with glucose values converted to mmol/L.
+
+     - Glucose tags handled: `ISF:`, `Target:`, `minPredBG`, `minGuardBG`, `IOBpredBG`, `COBpredBG`, `UAMpredBG`, `Dev:`, `maxDelta`, `BG`.
+     */
+    func parseReasonGlucoseValuesToMmolL(_ reason: String) -> String {
+        // Updated pattern to handle cases like minGuardBG 34, minGuardBG 34<70, and "maxDelta 37 > 20% of BG 95", and ensure "Target:" is handled correctly
+        let pattern =
+            "(ISF:\\s*-?\\d+→-?\\d+|Dev:\\s*-?\\d+|Target:\\s*-?\\d+|(?:minPredBG|minGuardBG|IOBpredBG|COBpredBG|UAMpredBG|maxDelta|BG)\\s*-?\\d+(?:<\\d+)?(?:>\\s*\\d+%\\s*of\\s*BG\\s*\\d+)?)"
+
+        let regex = try! NSRegularExpression(pattern: pattern)
+
+        func convertToMmolL(_ value: String) -> String {
+            if let glucoseValue = Double(value.replacingOccurrences(of: "[^\\d.-]", with: "", options: .regularExpression)) {
+                return glucoseValue.asMmolL.description
+            }
+            return value
+        }
+
+        let matches = regex.matches(in: reason, range: NSRange(reason.startIndex..., in: reason))
+        var updatedReason = reason
+
+        for match in matches.reversed() {
+            if let range = Range(match.range, in: reason) {
+                let glucoseValueString = String(reason[range])
+
+                if glucoseValueString.contains("→") {
+                    // Handle ISF case with an arrow (e.g., ISF: 54→54)
+                    let values = glucoseValueString.components(separatedBy: "→")
+                    let firstValue = convertToMmolL(values[0])
+                    let secondValue = convertToMmolL(values[1])
+                    let formattedGlucoseValueString = "\(values[0].components(separatedBy: ":")[0]): \(firstValue)→\(secondValue)"
+                    updatedReason.replaceSubrange(range, with: formattedGlucoseValueString)
+                } else if glucoseValueString.contains("<") {
+                    // Handle range case for minGuardBG like "minGuardBG 34<70"
+                    let values = glucoseValueString.components(separatedBy: "<")
+                    let firstValue = convertToMmolL(values[0])
+                    let secondValue = convertToMmolL(values[1])
+                    let formattedGlucoseValueString = "\(values[0].components(separatedBy: ":")[0]) \(firstValue)<\(secondValue)"
+                    updatedReason.replaceSubrange(range, with: formattedGlucoseValueString)
+                } else if glucoseValueString.contains(">"), glucoseValueString.contains("BG") {
+                    // Handle cases like "maxDelta 37 > 20% of BG 95"
+                    let pattern = "(\\d+) > \\d+% of BG (\\d+)"
+                    let matches = try! NSRegularExpression(pattern: pattern)
+                        .matches(in: glucoseValueString, range: NSRange(glucoseValueString.startIndex..., in: glucoseValueString))
+
+                    if let match = matches.first, match.numberOfRanges == 3 {
+                        let firstValueRange = Range(match.range(at: 1), in: glucoseValueString)!
+                        let secondValueRange = Range(match.range(at: 2), in: glucoseValueString)!
+
+                        let firstValue = convertToMmolL(String(glucoseValueString[firstValueRange]))
+                        let secondValue = convertToMmolL(String(glucoseValueString[secondValueRange]))
+
+                        let formattedGlucoseValueString = glucoseValueString.replacingOccurrences(
+                            of: "\(glucoseValueString[firstValueRange]) > 20% of BG \(glucoseValueString[secondValueRange])",
+                            with: "\(firstValue) > 20% of BG \(secondValue)"
+                        )
+                        updatedReason.replaceSubrange(range, with: formattedGlucoseValueString)
+                    }
+                } else {
+                    // General case for single glucose values like "Target: 100" or "minGuardBG 34"
+                    let parts = glucoseValueString.components(separatedBy: CharacterSet(charactersIn: ": "))
+                    let formattedValue = convertToMmolL(parts.last!.trimmingCharacters(in: .whitespaces))
+                    updatedReason.replaceSubrange(range, with: "\(parts[0]): \(formattedValue)")
+                }
+            }
+        }
+
+        return updatedReason
+    }
+}

+ 112 - 1
FreeAPS/Sources/Services/Storage/FileStorage.swift

@@ -14,8 +14,8 @@ protocol FileStorage {
     func remove(_ name: String)
     func remove(_ name: String)
     func rename(_ name: String, to newName: String)
     func rename(_ name: String, to newName: String)
     func transaction(_ exec: (FileStorage) -> Void)
     func transaction(_ exec: (FileStorage) -> Void)
-
     func urlFor(file: String) -> URL?
     func urlFor(file: String) -> URL?
+    func parseOnFileSettingsToMgdL() -> Bool
 }
 }
 
 
 final class BaseFileStorage: FileStorage {
 final class BaseFileStorage: FileStorage {
@@ -150,3 +150,114 @@ final class BaseFileStorage: FileStorage {
         try? Disk.url(for: file, in: .documents)
         try? Disk.url(for: file, in: .documents)
     }
     }
 }
 }
+
+extension FileStorage {
+    private func correctUnitParsingOffsets(_ parsedValue: Decimal) -> Decimal {
+        Int(parsedValue) % 2 == 0 ? parsedValue : parsedValue + 1
+    }
+
+    func parseSettingIfMmolL(value: Decimal, threshold: Decimal = 39) -> Decimal {
+        value < threshold ? correctUnitParsingOffsets(value.asMgdL) : value
+    }
+
+    /// Parses mmol/L settings stored on file to mg/dL if necessary and updates the preferences, settings,  insulin sensitivities, and glucose targets.
+    /// - Returns: A boolean indicating whether any settings were parsed and updated.
+    func parseOnFileSettingsToMgdL() -> Bool {
+        debug(.businessLogic, "Check for mmol/L settings stored on file.")
+        var wasParsed = false
+
+        // Retrieve and parse preferences (Preferences struct)
+        if var preferences = retrieve(OpenAPS.Settings.preferences, as: Preferences.self) {
+            let initialThreshold = preferences.threshold_setting
+            let initialSMBTarget = preferences.enableSMB_high_bg_target
+            let initialExerciseTarget = preferences.halfBasalExerciseTarget
+
+            preferences.threshold_setting = parseSettingIfMmolL(value: preferences.threshold_setting)
+            preferences.enableSMB_high_bg_target = parseSettingIfMmolL(value: preferences.enableSMB_high_bg_target)
+            preferences.halfBasalExerciseTarget = parseSettingIfMmolL(value: preferences.halfBasalExerciseTarget)
+
+            if preferences.threshold_setting != initialThreshold ||
+                preferences.enableSMB_high_bg_target != initialSMBTarget ||
+                preferences.halfBasalExerciseTarget != initialExerciseTarget
+            {
+                debug(.businessLogic, "Preferences found in mmol/L. Parsing to mg/dL.")
+                save(preferences, as: OpenAPS.Settings.preferences)
+                wasParsed = true
+            } else {
+                debug(.businessLogic, "Preferences stored in mg/dL; no parsing required.")
+            }
+        }
+
+        // Retrieve and parse settings (FreeAPSSettings struct)
+        if var settings = retrieve(OpenAPS.Settings.settings, as: FreeAPSSettings.self) {
+            let initialHigh = settings.high
+            let initialLow = settings.low
+            let initialHighGlucose = settings.highGlucose
+            let initialLowGlucose = settings.lowGlucose
+
+            settings.high = parseSettingIfMmolL(value: settings.high)
+            settings.low = parseSettingIfMmolL(value: settings.low)
+            settings.highGlucose = parseSettingIfMmolL(value: settings.highGlucose)
+            settings.lowGlucose = parseSettingIfMmolL(value: settings.lowGlucose)
+
+            if settings.high != initialHigh ||
+                settings.low != initialLow ||
+                settings.highGlucose != initialHighGlucose ||
+                settings.lowGlucose != initialLowGlucose
+            {
+                debug(.businessLogic, "FreeAPSSettings found in mmol/L. Parsing to mg/dL.")
+                save(settings, as: OpenAPS.Settings.settings)
+                wasParsed = true
+            } else {
+                debug(.businessLogic, "FreeAPSSettings stored in mg/dL; no parsing required.")
+            }
+        }
+
+        // Retrieve and parse insulin sensitivities
+        if var sensitivities = retrieve(OpenAPS.Settings.insulinSensitivities, as: InsulinSensitivities.self),
+           sensitivities.units == .mmolL || sensitivities.userPreferredUnits == .mmolL
+        {
+            debug(.businessLogic, "Insulin sensitivities found in mmol/L. Parsing to mg/dL.")
+
+            sensitivities.sensitivities = sensitivities.sensitivities.map { isf in
+                InsulinSensitivityEntry(
+                    sensitivity: parseSettingIfMmolL(value: isf.sensitivity),
+                    offset: isf.offset,
+                    start: isf.start
+                )
+            }
+            sensitivities.units = .mgdL
+            sensitivities.userPreferredUnits = .mgdL
+
+            save(sensitivities, as: OpenAPS.Settings.insulinSensitivities)
+            wasParsed = true
+        } else {
+            debug(.businessLogic, "Insulin sensitivities stored in mg/dL; no parsing required.")
+        }
+
+        // Retrieve and parse glucose targets
+        if var glucoseTargets = retrieve(OpenAPS.Settings.bgTargets, as: BGTargets.self),
+           glucoseTargets.units == .mmolL || glucoseTargets.userPreferredUnits == .mmolL
+        {
+            debug(.businessLogic, "Glucose target profile found in mmol/L. Parsing to mg/dL.")
+
+            glucoseTargets.targets = glucoseTargets.targets.map { target in
+                BGTargetEntry(
+                    low: parseSettingIfMmolL(value: target.low),
+                    high: parseSettingIfMmolL(value: target.high),
+                    start: target.start,
+                    offset: target.offset
+                )
+            }
+            glucoseTargets.units = .mgdL
+            glucoseTargets.userPreferredUnits = .mgdL
+
+            save(glucoseTargets, as: OpenAPS.Settings.bgTargets)
+            wasParsed = true
+        } else {
+            debug(.businessLogic, "Glucose target profile stored in mg/dL; no parsing required.")
+        }
+
+        return wasParsed
+    }
+}

+ 14 - 0
FreeAPS/Sources/Shortcuts/AppShortcuts.swift

@@ -31,5 +31,19 @@ import Foundation
                 "\(.applicationName) allows to add carbs"
                 "\(.applicationName) allows to add carbs"
             ]
             ]
         )
         )
+        AppShortcut(
+            intent: ApplyOverridePresetIntent(),
+            phrases: [
+                "Activate \(.applicationName) override",
+                "Activates an available \(.applicationName) override"
+            ]
+        )
+        AppShortcut(
+            intent: CancelOverrideIntent(),
+            phrases: [
+                "Cancel \(.applicationName) override",
+                "Cancels an active \(.applicationName) override"
+            ]
+        )
     }
     }
 }
 }

+ 2 - 0
FreeAPS/Sources/Shortcuts/BaseIntentsRequest.swift

@@ -13,10 +13,12 @@ import Swinject
     @Injected() var carbsStorage: CarbsStorage!
     @Injected() var carbsStorage: CarbsStorage!
     @Injected() var glucoseStorage: GlucoseStorage!
     @Injected() var glucoseStorage: GlucoseStorage!
     @Injected() var apsManager: APSManager!
     @Injected() var apsManager: APSManager!
+    @Injected() var overrideStorage: OverrideStorage!
 
 
     let resolver: Resolver
     let resolver: Resolver
 
 
     let coredataContext = CoreDataStack.shared.newTaskContext()
     let coredataContext = CoreDataStack.shared.newTaskContext()
+    let viewContext = CoreDataStack.shared.persistentContainer.viewContext
 
 
     override init() {
     override init() {
         resolver = FreeAPSApp.resolver
         resolver = FreeAPSApp.resolver

+ 88 - 0
FreeAPS/Sources/Shortcuts/Override/ApplyOverridePresetIntent.swift

@@ -0,0 +1,88 @@
+import AppIntents
+import Foundation
+
+@available(iOS 16.0, *) struct ApplyOverridePresetIntent: AppIntent {
+    // Title of the action in the Shortcuts app
+    static var title = LocalizedStringResource("Activate an override", table: "ShortcutsDetail")
+
+    // Description of the action in the Shortcuts app
+    static var description = IntentDescription(.init("Activate an override", table: "ShortcutsDetail"))
+
+    internal var intentRequest: OverridePresetsIntentRequest
+
+    init() {
+        intentRequest = OverridePresetsIntentRequest()
+    }
+
+    @Parameter(
+        title: LocalizedStringResource("Override", table: "ShortcutsDetail"),
+        description: LocalizedStringResource("Override choice", table: "ShortcutsDetail")
+    ) var preset: OverridePreset?
+
+    @Parameter(
+        title: LocalizedStringResource("Confirm Before applying", table: "ShortcutsDetail"),
+        description: LocalizedStringResource("If toggled, you will need to confirm before applying", table: "ShortcutsDetail"),
+        default: true
+    ) var confirmBeforeApplying: Bool
+
+    static var parameterSummary: some ParameterSummary {
+        When(\ApplyOverridePresetIntent.$confirmBeforeApplying, .equalTo, true, {
+            Summary("Applying \(\.$preset) override", table: "ShortcutsDetail") {
+                \.$confirmBeforeApplying
+            }
+        }, otherwise: {
+            Summary("Immediately applying \(\.$preset) override", table: "ShortcutsDetail") {
+                \.$confirmBeforeApplying
+            }
+        })
+    }
+
+    @MainActor func perform() async throws -> some ProvidesDialog {
+        do {
+            let presetToApply: OverridePreset
+            if let preset = preset {
+                presetToApply = preset
+            } else {
+                presetToApply = try await $preset.requestDisambiguation(
+                    among: await intentRequest.fetchAndProcessOverrides(),
+                    dialog: IntentDialog(LocalizedStringResource("Select override", table: "ShortcutsDetail"))
+                )
+            }
+
+            let displayName: String = presetToApply.name
+            if confirmBeforeApplying {
+                try await requestConfirmation(
+                    result: .result(
+                        dialog: IntentDialog(LocalizedStringResource(
+                            "Confirm to apply override '\(displayName)'",
+                            table: "ShortcutsDetail"
+                        ))
+                    )
+                )
+            }
+
+            if await intentRequest.enactOverride(presetToApply) {
+                return .result(
+                    dialog: IntentDialog(
+                        LocalizedStringResource(
+                            "Override '\(presetToApply.name)' applied",
+                            table: "ShortcutsDetail"
+                        )
+                    )
+                )
+            } else {
+                return .result(
+                    dialog: IntentDialog(
+                        LocalizedStringResource(
+                            "Override '\(presetToApply.name)' failed",
+                            table: "ShortcutsDetail"
+                        )
+                    )
+                )
+            }
+
+        } catch {
+            throw error
+        }
+    }
+}

+ 25 - 0
FreeAPS/Sources/Shortcuts/Override/CancelOverrideIntent.swift

@@ -0,0 +1,25 @@
+import AppIntents
+import Foundation
+
+@available(iOS 16.0, *) struct CancelOverrideIntent: AppIntent {
+    // Title of the action in the Shortcuts app
+    static var title = LocalizedStringResource("Cancel override", table: "ShortcutsDetail")
+
+    // Description of the action in the Shortcuts app
+    static var description = IntentDescription(.init("Cancel an active override", table: "ShortcutsDetail"))
+
+    internal var intentRequest: OverridePresetsIntentRequest
+
+    init() {
+        intentRequest = OverridePresetsIntentRequest()
+    }
+
+    @MainActor func perform() async throws -> some ProvidesDialog {
+        do {
+            await intentRequest.cancelOverride()
+            return .result(
+                dialog: IntentDialog(LocalizedStringResource("Override canceled", table: "ShortcutsDetail"))
+            )
+        }
+    }
+}

+ 30 - 0
FreeAPS/Sources/Shortcuts/Override/ListOverridePresetIntent.swift

@@ -0,0 +1,30 @@
+import AppIntents
+import Foundation
+
+@available(iOS 16.0, *) struct ListOverridePresetsIntent: AppIntent {
+    // Title of the action in the Shortcuts app
+    static var title = LocalizedStringResource("List overrides", table: "ShortcutsDetail")
+
+    // Description of the action in the Shortcuts app
+    static var description = IntentDescription(
+        .init(
+            "Allow to list and choose a specific override",
+            table: "ShortcutsDetail"
+        )
+    )
+
+    @Parameter(
+        title: LocalizedStringResource("Override", table: "ShortcutsDetail"),
+        description: LocalizedStringResource("Override choice", table: "ShortcutsDetail")
+    ) var preset: OverridePreset?
+
+    static var parameterSummary: some ParameterSummary {
+        Summary("Choose the override  \(\.$preset)", table: "ShortcutsDetail")
+    }
+
+    @MainActor func perform() async throws -> some ReturnsValue<OverridePreset> {
+        .result(
+            value: preset!
+        )
+    }
+}

+ 33 - 0
FreeAPS/Sources/Shortcuts/Override/OverridePresetEntity.swift

@@ -0,0 +1,33 @@
+import AppIntents
+import Foundation
+import Intents
+import Swinject
+
+@available(iOS 16.0, *) struct OverridePreset: AppEntity, Identifiable {
+    static var defaultQuery = OverridePresetsQuery()
+
+    var id: String
+    var name: String
+
+    var displayRepresentation: DisplayRepresentation {
+        DisplayRepresentation(title: "\(name)")
+    }
+
+    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Override"
+}
+
+@available(iOS 16.0, *) struct OverridePresetsQuery: EntityQuery {
+    internal var intentRequest: OverridePresetsIntentRequest
+
+    init() {
+        intentRequest = OverridePresetsIntentRequest()
+    }
+
+    func entities(for identifiers: [OverridePreset.ID]) async throws -> [OverridePreset] {
+        await intentRequest.fetchIDs(identifiers)
+    }
+
+    func suggestedEntities() async throws -> [OverridePreset] {
+        await intentRequest.fetchAndProcessOverrides()
+    }
+}

+ 171 - 0
FreeAPS/Sources/Shortcuts/Override/OverridePresetsIntentRequest.swift

@@ -0,0 +1,171 @@
+import CoreData
+import Foundation
+
+@available(iOS 16.0, *) final class OverridePresetsIntentRequest: BaseIntentsRequest {
+    enum overridePresetsError: Error {
+        case noTempOverrideFound
+        case noDurationDefined
+        case noActiveOverride
+    }
+
+    func fetchAndProcessOverrides() async -> [OverridePreset] {
+        // Fetch all Override Presets via OverrideStorage
+        let allOverridePresetsIDs = await overrideStorage.fetchForOverridePresets()
+
+        // Since we are fetching on a different background Thread we need to unpack the NSManagedObjectID on the correct Thread first
+        return await coredataContext.perform {
+            do {
+                let overrideObjects = try allOverridePresetsIDs.compactMap { id in
+                    try self.coredataContext.existingObject(with: id) as? OverrideStored
+                }
+
+                return overrideObjects.map { object in
+                    guard let id = object.id,
+                          let name = object.name else { return OverridePreset(id: UUID().uuidString, name: "") }
+                    return OverridePreset(id: id, name: name)
+                }
+
+            } catch {
+                debugPrint(
+                    "\(#file) \(#function) \(DebuggingIdentifiers.failed) error while fetching/ processing the overrides Array: \(error.localizedDescription)"
+                )
+                return [OverridePreset(id: UUID().uuidString, name: "")]
+            }
+        }
+    }
+
+    func fetchIDs(_ uuid: [OverridePreset.ID]) async -> [OverridePreset] {
+        await coredataContext.perform {
+            let fetchRequest: NSFetchRequest<OverrideStored> = OverrideStored.fetchRequest()
+            fetchRequest.predicate = NSPredicate(format: "id IN %@", uuid)
+
+            do {
+                let result = try self.coredataContext.fetch(fetchRequest)
+
+                if result.isEmpty {
+                    debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) No OverrideStored found for ids: \(uuid)")
+                    return [OverridePreset(id: UUID().uuidString, name: "")]
+                }
+
+                return result.map { overrideStored in
+                    OverridePreset(id: overrideStored.id ?? UUID().uuidString, name: overrideStored.name ?? "")
+                }
+            } catch let error as NSError {
+                debugPrint(
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch Override: \(error.localizedDescription)"
+                )
+                return [OverridePreset(id: UUID().uuidString, name: "")]
+            }
+        }
+    }
+
+    private func fetchOverrideID(_ preset: OverridePreset) async -> NSManagedObjectID? {
+        let fetchRequest: NSFetchRequest<OverrideStored> = OverrideStored.fetchRequest()
+        fetchRequest.predicate = NSPredicate(format: "id == %@", preset.id)
+        fetchRequest.fetchLimit = 1
+
+        return await coredataContext.perform {
+            do {
+                return try self.coredataContext.fetch(fetchRequest).first?.objectID
+            } catch {
+                debugPrint(
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch Override: \(error.localizedDescription)"
+                )
+                return nil
+            }
+        }
+    }
+
+    @MainActor func enactOverride(_ preset: OverridePreset) async -> Bool {
+        do {
+            guard let overrideID = await fetchOverrideID(preset),
+                  let overrideObject = try viewContext.existingObject(with: overrideID) as? OverrideStored else { return false }
+
+            overrideObject.enabled = true
+            overrideObject.date = Date()
+            overrideObject.isUploadedToNS = false
+
+            // Disable previous Overrides
+            /// do not create a OverrideRunEntry because we only want that if we cancel a running Override, not when enacting a Preset
+            await disableAllActiveOverrides(except: overrideID, createOverrideRunEntry: true)
+
+            if viewContext.hasChanges {
+                try viewContext.save()
+
+                // Update State variables in OverrideView
+                Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
+
+                return true
+            }
+        } catch let error as NSError {
+            debugPrint(
+                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact Override: \(error.localizedDescription)"
+            )
+        }
+        return false
+    }
+
+    func cancelOverride() async {
+        await disableAllActiveOverrides(createOverrideRunEntry: true)
+    }
+
+    @MainActor func disableAllActiveOverrides(
+        except overrideID: NSManagedObjectID? = nil,
+        createOverrideRunEntry _: Bool
+    ) async {
+        // Get ALL NSManagedObject IDs of ALL active Overrides to cancel every single Override
+        let ids = await overrideStorage.loadLatestOverrideConfigurations(fetchLimit: 0) // 0 = no fetch limit
+
+        await viewContext.perform {
+            do {
+                // Fetch the existing OverrideStored objects from the context
+                let results = try ids.compactMap { id in
+                    try self.viewContext.existingObject(with: id) as? OverrideStored
+                }
+
+                // If there are no results, return early
+                guard !results.isEmpty else { return }
+
+                // Check if we also need to create a corresponding OverrideRunStored entry, i.e. when the User uses the Cancel Button in Override View
+                // Auggie - commented out this if statment, we always need to do this for overrides
+                // if createOverrideRunEntry {
+                // Use the first override to create a new OverrideRunStored entry
+                if let canceledOverride = results.first {
+                    let newOverrideRunStored = OverrideRunStored(context: self.viewContext)
+                    newOverrideRunStored.id = UUID()
+                    newOverrideRunStored.name = canceledOverride.name
+                    newOverrideRunStored.startDate = canceledOverride.date ?? .distantPast
+                    newOverrideRunStored.endDate = Date()
+                    newOverrideRunStored
+                        .target = NSDecimalNumber(
+                            decimal: self.overrideStorage
+                                .calculateTarget(override: canceledOverride)
+                        )
+                    newOverrideRunStored.override = canceledOverride
+                    newOverrideRunStored.isUploadedToNS = false
+                }
+                // }
+
+                // Disable all override except the one with overrideID
+                for overrideToCancel in results {
+                    if overrideToCancel.objectID != overrideID {
+                        overrideToCancel.enabled = false
+                        overrideToCancel.isUploadedToNS = false
+                    }
+                }
+
+                // Save the context if there are changes
+                if self.viewContext.hasChanges {
+                    try self.viewContext.save()
+
+                    // Update State variables in OverrideView
+                    Foundation.NotificationCenter.default.post(name: .didUpdateOverrideConfiguration, object: nil)
+                }
+            } catch {
+                debugPrint(
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to disable active Overrides with error: \(error.localizedDescription)"
+                )
+            }
+        }
+    }
+}

+ 89 - 97
FreeAPS/Sources/Views/TagCloudView.swift

@@ -7,16 +7,14 @@ struct TagCloudView: View {
     var tags: [String]
     var tags: [String]
     var shouldParseToMmolL: Bool
     var shouldParseToMmolL: Bool
 
 
-    @State private var totalHeight
-//          = CGFloat.zero       // << variant for ScrollView/List
-        = CGFloat.infinity // << variant for VStack
+    @State private var totalHeight = CGFloat.infinity // << variant for VStack
+
     var body: some View {
     var body: some View {
         VStack {
         VStack {
             GeometryReader { geometry in
             GeometryReader { geometry in
                 self.generateContent(in: geometry)
                 self.generateContent(in: geometry)
             }
             }
         }
         }
-//        .frame(height: totalHeight)// << variant for ScrollView/List
         .frame(maxHeight: totalHeight) // << variant for VStack
         .frame(maxHeight: totalHeight) // << variant for VStack
     }
     }
 
 
@@ -29,8 +27,7 @@ struct TagCloudView: View {
                 self.item(for: tag, isMmolL: shouldParseToMmolL)
                 self.item(for: tag, isMmolL: shouldParseToMmolL)
                     .padding([.horizontal, .vertical], 2)
                     .padding([.horizontal, .vertical], 2)
                     .alignmentGuide(.leading, computeValue: { d in
                     .alignmentGuide(.leading, computeValue: { d in
-                        if abs(width - d.width) > g.size.width
-                        {
+                        if abs(width - d.width) > g.size.width {
                             width = 0
                             width = 0
                             height -= d.height
                             height -= d.height
                         }
                         }
@@ -53,40 +50,6 @@ struct TagCloudView: View {
         }.background(viewHeightReader($totalHeight))
         }.background(viewHeightReader($totalHeight))
     }
     }
 
 
-//    private func item(for textTag: String) -> some View {
-//        var colorOfTag: Color {
-//            switch textTag {
-//            case textTag where textTag.contains("SMB Delivery Ratio:"):
-//                return .uam
-//            case textTag where textTag.contains("Bolus"):
-//                return .green
-//            case textTag where textTag.contains("TDD:"),
-//                 textTag where textTag.contains("tdd_factor"),
-//                 textTag where textTag.contains("Sigmoid function"),
-//                 textTag where textTag.contains("Logarithmic formula"),
-//                 textTag where textTag.contains("AF:"),
-//                 textTag where textTag.contains("Autosens/Dynamic Limit:"),
-//                 textTag where textTag.contains("Dynamic ISF/CR"),
-//                 textTag where textTag.contains("Basal ratio"),
-//                 textTag where textTag.contains("SMB Ratio"):
-//                return .zt
-//            case textTag where textTag.contains("Middleware:"):
-//                return .red
-//            case textTag where textTag.contains("SMB Ratio"):
-//                return .orange
-//            default:
-//                return .insulin
-//            }
-//        }
-//
-//        return ZStack { Text(textTag)
-//            .padding(.vertical, 2)
-//            .padding(.horizontal, 4)
-//            .font(.subheadline)
-//            .background(colorOfTag.opacity(0.8))
-//            .foregroundColor(Color.white)
-//            .cornerRadius(2) }
-//    }
     private func item(for textTag: String, isMmolL: Bool) -> some View {
     private func item(for textTag: String, isMmolL: Bool) -> some View {
         var colorOfTag: Color {
         var colorOfTag: Color {
             switch textTag {
             switch textTag {
@@ -108,66 +71,14 @@ struct TagCloudView: View {
                 return .red
                 return .red
             case textTag where textTag.contains("SMB Ratio"):
             case textTag where textTag.contains("SMB Ratio"):
                 return .orange
                 return .orange
+            case textTag where textTag.contains("Smoothing: On"):
+                return .white
             default:
             default:
                 return .insulin
                 return .insulin
             }
             }
         }
         }
 
 
-        func formattedTextTag(for tag: String) -> String {
-            // List of glucose-related tags
-            let glucoseTags = ["ISF:", "Target:", "minPredBG", "minGuardBG", "IOBpredBG", "COBpredBG", "UAMpredBG", "Dev:"]
-
-            var updatedTag = tag
-
-            // Apply conversion if necessary
-            for glucoseTag in glucoseTags {
-                if glucoseTag == "ISF:" {
-                    // Handle the special ISF case with the arrow
-                    if let range = updatedTag.range(of: "\(glucoseTag)\\s*\\d+→\\d+", options: .regularExpression) {
-                        let glucoseValueString = updatedTag[range]
-                        let values = glucoseValueString.components(separatedBy: "→")
-
-                        if let firstValue = Double(
-                            values[0]
-                                .components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
-                        ),
-                            let secondValue = Double(
-                                values[1]
-                                    .components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
-                            )
-                        {
-                            let formattedFirstValue = isMmolL ? Double(firstValue.asMmolL) : firstValue
-                            let formattedSecondValue = isMmolL ? Double(secondValue.asMmolL) : secondValue
-
-                            let formattedGlucoseValueString =
-                                "\(glucoseTag) \(formattedFirstValue)→\(formattedSecondValue)"
-                            updatedTag = updatedTag.replacingOccurrences(
-                                of: glucoseValueString,
-                                with: formattedGlucoseValueString
-                            )
-                        }
-                    }
-                } else {
-                    // General case for other glucose tags
-                    if let range = updatedTag.range(of: "\(glucoseTag)\\s*\\d+", options: .regularExpression) {
-                        let glucoseValueString = updatedTag[range]
-                        if let glucoseValue = Double(
-                            glucoseValueString
-                                .components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
-                        ) {
-                            let formattedValue = isMmolL ? Double(glucoseValue.asMmolL) : glucoseValue
-                            updatedTag = updatedTag.replacingOccurrences(
-                                of: glucoseValueString,
-                                with: "\(glucoseTag) \(formattedValue)"
-                            )
-                        }
-                    }
-                }
-            }
-            return updatedTag
-        }
-
-        let formattedTextTag = formattedTextTag(for: textTag)
+        let formattedTextTag = formatGlucoseTags(textTag, isMmolL: isMmolL)
 
 
         return ZStack {
         return ZStack {
             Text(formattedTextTag)
             Text(formattedTextTag)
@@ -175,11 +86,89 @@ struct TagCloudView: View {
                 .padding(.horizontal, 4)
                 .padding(.horizontal, 4)
                 .font(.subheadline)
                 .font(.subheadline)
                 .background(colorOfTag.opacity(0.8))
                 .background(colorOfTag.opacity(0.8))
-                .foregroundColor(Color.white)
+                .foregroundColor(textTag.contains("Smoothing: On") ? Color.black : Color.white)
                 .cornerRadius(2)
                 .cornerRadius(2)
         }
         }
     }
     }
 
 
+    /**
+     Converts glucose-related values in the given `tag` string to mmol/L, including ranges (e.g., `ISF: 54→54`), comparisons (e.g., `maxDelta 37 > 20% of BG 95`), and both positive and negative values (e.g., `Dev: -36`).
+
+     - Parameters:
+       - tag: The string containing glucose-related values to be converted.
+       - isMmolL: A Boolean flag indicating whether to convert values to mmol/L.
+
+     - Returns:
+       A string with glucose values converted to mmol/L.
+
+     - Glucose tags handled: `ISF:`, `Target:`, `minPredBG`, `minGuardBG`, `IOBpredBG`, `COBpredBG`, `UAMpredBG`, `Dev:`, `maxDelta`, `BG`.
+     */
+    private func formatGlucoseTags(_ tag: String, isMmolL: Bool) -> String {
+        // Updated pattern to handle cases like minGuardBG 34, minGuardBG 34<70, "maxDelta 37 > 20% of BG 95", and ensure "Target:" is handled correctly
+        let pattern =
+            "(ISF:\\s*-?\\d+→-?\\d+|Dev:\\s*-?\\d+|Target:\\s*-?\\d+|(?:minPredBG|minGuardBG|IOBpredBG|COBpredBG|UAMpredBG|maxDelta|BG)\\s*-?\\d+(?:<\\d+)?(?:>\\s*\\d+%\\s*of\\s*BG\\s*\\d+)?)"
+
+        let regex = try! NSRegularExpression(pattern: pattern)
+
+        func convertToMmolL(_ value: String) -> String {
+            if let glucoseValue = Double(value.replacingOccurrences(of: "[^\\d.-]", with: "", options: .regularExpression)) {
+                return isMmolL ? glucoseValue.asMmolL.description : value
+            }
+            return value
+        }
+
+        let matches = regex.matches(in: tag, range: NSRange(tag.startIndex..., in: tag))
+        var updatedTag = tag
+
+        for match in matches.reversed() {
+            if let range = Range(match.range, in: tag) {
+                let glucoseValueString = String(tag[range])
+
+                if glucoseValueString.contains("→") {
+                    // Handle ISF case with an arrow (e.g., ISF: 54→54)
+                    let values = glucoseValueString.components(separatedBy: "→")
+                    let firstValue = convertToMmolL(values[0])
+                    let secondValue = convertToMmolL(values[1])
+                    let formattedGlucoseValueString = "\(values[0].components(separatedBy: ":")[0]): \(firstValue)→\(secondValue)"
+                    updatedTag.replaceSubrange(range, with: formattedGlucoseValueString)
+                } else if glucoseValueString.contains("<") {
+                    // Handle range case for minGuardBG like "minGuardBG 34<70"
+                    let values = glucoseValueString.components(separatedBy: "<")
+                    let firstValue = convertToMmolL(values[0])
+                    let secondValue = convertToMmolL(values[1])
+                    let formattedGlucoseValueString = "\(values[0].components(separatedBy: ":")[0]) \(firstValue)<\(secondValue)"
+                    updatedTag.replaceSubrange(range, with: formattedGlucoseValueString)
+                } else if glucoseValueString.contains(">"), glucoseValueString.contains("BG") {
+                    // Handle cases like "maxDelta 37 > 20% of BG 95"
+                    let pattern = "(\\d+) > \\d+% of BG (\\d+)"
+                    let matches = try! NSRegularExpression(pattern: pattern)
+                        .matches(in: glucoseValueString, range: NSRange(glucoseValueString.startIndex..., in: glucoseValueString))
+
+                    if let match = matches.first, match.numberOfRanges == 3 {
+                        let firstValueRange = Range(match.range(at: 1), in: glucoseValueString)!
+                        let secondValueRange = Range(match.range(at: 2), in: glucoseValueString)!
+
+                        let firstValue = convertToMmolL(String(glucoseValueString[firstValueRange]))
+                        let secondValue = convertToMmolL(String(glucoseValueString[secondValueRange]))
+
+                        let formattedGlucoseValueString = glucoseValueString.replacingOccurrences(
+                            of: "\(glucoseValueString[firstValueRange]) > 20% of BG \(glucoseValueString[secondValueRange])",
+                            with: "\(firstValue) > 20% of BG \(secondValue)"
+                        )
+                        updatedTag.replaceSubrange(range, with: formattedGlucoseValueString)
+                    }
+                } else {
+                    // General case for single glucose values like "Target: 100" or "minGuardBG 34"
+                    let parts = glucoseValueString.components(separatedBy: CharacterSet(charactersIn: ": "))
+                    let formattedValue = convertToMmolL(parts.last!.trimmingCharacters(in: .whitespaces))
+                    updatedTag.replaceSubrange(range, with: "\(parts[0]): \(formattedValue)")
+                }
+            }
+        }
+
+        return updatedTag
+    }
+
     private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View {
     private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View {
         GeometryReader { geometry -> Color in
         GeometryReader { geometry -> Color in
             let rect = geometry.frame(in: .local)
             let rect = geometry.frame(in: .local)
@@ -202,7 +191,10 @@ struct TestTagCloudView: View {
             Text("Some other text")
             Text("Some other text")
             Divider()
             Divider()
             Text("Some other cloud")
             Text("Some other cloud")
-            TagCloudView(tags: ["Apple", "Google", "Amazon", "Microsoft", "Oracle", "Facebook"], shouldParseToMmolL: false)
+            TagCloudView(
+                tags: ["Apple", "Google", "Amazon", "Microsoft", "Oracle", "Facebook"],
+                shouldParseToMmolL: false
+            )
         }
         }
     }
     }
 }
 }

+ 0 - 4
ImportError+CoreDataClass.swift

@@ -1,4 +0,0 @@
-import CoreData
-import Foundation
-
-@objc(ImportError) public class ImportError: NSManagedObject {}

+ 0 - 13
ImportError+CoreDataProperties.swift

@@ -1,13 +0,0 @@
-import CoreData
-import Foundation
-
-public extension ImportError {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<ImportError> {
-        NSFetchRequest<ImportError>(entityName: "ImportError")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var error: String?
-}
-
-extension ImportError: Identifiable {}

BolusStored+CoreDataClass.swift → Model/Classes+Properties/BolusStored+CoreDataClass.swift


BolusStored+CoreDataProperties.swift → Model/Classes+Properties/BolusStored+CoreDataProperties.swift


CarbEntryStored+CoreDataClass.swift → Model/Classes+Properties/CarbEntryStored+CoreDataClass.swift


CarbEntryStored+CoreDataProperties.swift → Model/Classes+Properties/CarbEntryStored+CoreDataProperties.swift


Forecast+CoreDataClass.swift → Model/Classes+Properties/Forecast+CoreDataClass.swift


Forecast+CoreDataProperties.swift → Model/Classes+Properties/Forecast+CoreDataProperties.swift


ForecastValue+CoreDataClass.swift → Model/Classes+Properties/ForecastValue+CoreDataClass.swift


ForecastValue+CoreDataProperties.swift → Model/Classes+Properties/ForecastValue+CoreDataProperties.swift


GlucoseStored+CoreDataClass.swift → Model/Classes+Properties/GlucoseStored+CoreDataClass.swift


GlucoseStored+CoreDataProperties.swift → Model/Classes+Properties/GlucoseStored+CoreDataProperties.swift


LoopStatRecord+CoreDataClass.swift → Model/Classes+Properties/LoopStatRecord+CoreDataClass.swift


LoopStatRecord+CoreDataProperties.swift → Model/Classes+Properties/LoopStatRecord+CoreDataProperties.swift


MealPresetStored+CoreDataClass.swift → Model/Classes+Properties/MealPresetStored+CoreDataClass.swift


MealPresetStored+CoreDataProperties.swift → Model/Classes+Properties/MealPresetStored+CoreDataProperties.swift


OpenAPS_Battery+CoreDataClass.swift → Model/Classes+Properties/OpenAPS_Battery+CoreDataClass.swift


OpenAPS_Battery+CoreDataProperties.swift → Model/Classes+Properties/OpenAPS_Battery+CoreDataProperties.swift


OrefDetermination+CoreDataClass.swift → Model/Classes+Properties/OrefDetermination+CoreDataClass.swift


+ 1 - 1
OrefDetermination+CoreDataProperties.swift

@@ -38,7 +38,7 @@ public extension OrefDetermination {
     @NSManaged var timestamp: Date?
     @NSManaged var timestamp: Date?
     @NSManaged var timestampEnacted: Date?
     @NSManaged var timestampEnacted: Date?
     @NSManaged var totalDailyDose: NSDecimalNumber?
     @NSManaged var totalDailyDose: NSDecimalNumber?
-    @NSManaged var forecasts: NSSet?
+    @NSManaged var forecasts: Set<Forecast>?
 }
 }
 
 
 // MARK: Generated accessors for forecasts
 // MARK: Generated accessors for forecasts

OverrideRunStored+CoreDataClass.swift → Model/Classes+Properties/OverrideRunStored+CoreDataClass.swift


OverrideRunStored+CoreDataProperties.swift → Model/Classes+Properties/OverrideRunStored+CoreDataProperties.swift


OverrideStored+CoreDataClass.swift → Model/Classes+Properties/OverrideStored+CoreDataClass.swift


OverrideStored+CoreDataProperties.swift → Model/Classes+Properties/OverrideStored+CoreDataProperties.swift


+ 0 - 0
PumpEventStored+CoreDataClass.swift


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini