Explorar o código

Pull in latest 'Core-Data-Sync-Trio'

polscm32 aka Marvout hai 1 ano
pai
achega
1a57ec8edf
Modificáronse 43 ficheiros con 583 adicións e 894 borrados
  1. 8 36
      FreeAPS.xcodeproj/project.pbxproj
  2. 0 2
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  3. 3 40
      FreeAPS/Sources/APS/APSManager.swift
  4. 5 126
      FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
  5. 0 17
      FreeAPS/Sources/Models/Autotune.swift
  6. 0 7
      FreeAPS/Sources/Models/DecimalPickerSettings.swift
  7. 0 10
      FreeAPS/Sources/Models/FreeAPSSettings.swift
  8. 0 6
      FreeAPS/Sources/Models/Preferences.swift
  9. 1 1
      FreeAPS/Sources/Modules/Adjustments/View/Overrides/OverrideHelpView.swift
  10. 1 1
      FreeAPS/Sources/Modules/Adjustments/View/TempTargets/TempTargetHelpView.swift
  11. 0 3
      FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift
  12. 0 27
      FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift
  13. 0 10
      FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigDataFlow.swift
  14. 0 15
      FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigProvider.swift
  15. 0 113
      FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigStateModel.swift
  16. 0 198
      FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift
  17. 0 1
      FreeAPS/Sources/Modules/CarbRatioEditor/CarbRatioEditorDataFlow.swift
  18. 0 4
      FreeAPS/Sources/Modules/CarbRatioEditor/CarbRatioEditorProvider.swift
  19. 0 3
      FreeAPS/Sources/Modules/CarbRatioEditor/CarbRatioEditorStateModel.swift
  20. 0 11
      FreeAPS/Sources/Modules/CarbRatioEditor/View/CarbRatioEditorRootView.swift
  21. 1 1
      FreeAPS/Sources/Modules/ContactImage/View/ContactImageHelpView.swift
  22. 1 2
      FreeAPS/Sources/Modules/Home/HomeDataFlow.swift
  23. 4 9
      FreeAPS/Sources/Modules/Home/HomeProvider.swift
  24. 2 5
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  25. 1 2
      FreeAPS/Sources/Modules/Home/View/Chart/ChartLegendView.swift
  26. 248 0
      FreeAPS/Sources/Modules/Home/View/Header/LoopStatusHelpView.swift
  27. 254 0
      FreeAPS/Sources/Modules/Home/View/Header/LoopStatusView.swift
  28. 7 170
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  29. 0 1
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorDataFlow.swift
  30. 0 4
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorProvider.swift
  31. 0 3
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift
  32. 0 15
      FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift
  33. 1 1
      FreeAPS/Sources/Modules/PumpConfig/PumpConfigDataFlow.swift
  34. 4 3
      FreeAPS/Sources/Modules/PumpConfig/PumpConfigProvider.swift
  35. 15 11
      FreeAPS/Sources/Modules/PumpConfig/PumpConfigStateModel.swift
  36. 0 2
      FreeAPS/Sources/Modules/Settings/SettingItems.swift
  37. 0 2
      FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift
  38. 0 8
      FreeAPS/Sources/Modules/Settings/View/Subviews/FeatureSettingsView.swift
  39. 1 1
      FreeAPS/Sources/Modules/Treatments/View/PopupView.swift
  40. 0 3
      FreeAPS/Sources/Router/Screen.swift
  41. 1 2
      FreeAPS/Sources/Views/SettingInputHintView.swift
  42. 0 2
      FreeAPS/Sources/Views/SettingInputSection.swift
  43. 25 16
      FreeAPS/Sources/Views/TagCloudView.swift

+ 8 - 36
FreeAPS.xcodeproj/project.pbxproj

@@ -64,7 +64,6 @@
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
 		23888883D4EA091C88480FF2 /* TreatmentsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19984D62EFC0035A9E9644D /* TreatmentsProvider.swift */; };
-		3083261C4B268E353F36CD0B /* AutotuneConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DCCCCE633F5E98E41B0CD3C /* AutotuneConfigDataFlow.swift */; };
 		3171D2818C7C72CD1584BB5E /* GlucoseNotificationSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC2C6489D29ECCCAD78E0721 /* GlucoseNotificationSettingsStateModel.swift */; };
 		320D030F724170A637F06D50 /* (null) in Sources */ = {isa = PBXBuildFile; };
 		3811DE0B25C9D32F00A708ED /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE0725C9D32E00A708ED /* BaseView.swift */; };
@@ -141,7 +140,6 @@
 		389A572026079BAA00BC102F /* Interpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 389A571F26079BAA00BC102F /* Interpolation.swift */; };
 		389ECDFE2601061500D86C4F /* View+Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 389ECDFD2601061500D86C4F /* View+Snapshot.swift */; };
 		389ECE052601144100D86C4F /* ConcurrentMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 389ECE042601144100D86C4F /* ConcurrentMap.swift */; };
-		38A00B1F25FC00F7006BC0B0 /* Autotune.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A00B1E25FC00F7006BC0B0 /* Autotune.swift */; };
 		38A00B2325FC2B55006BC0B0 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A00B2225FC2B55006BC0B0 /* LRUCache.swift */; };
 		38A0363B25ECF07E00FCBB52 /* GlucoseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A0363A25ECF07E00FCBB52 /* GlucoseStorage.swift */; };
 		38A0364225ED069400FCBB52 /* TempBasal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A0364125ED069400FCBB52 /* TempBasal.swift */; };
@@ -305,12 +303,10 @@
 		7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A401509D21F7F35D4E109EDA /* DataTableDataFlow.swift */; };
 		8194B80890CDD6A3C13B0FEE /* SnoozeStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26904AACA8D9C15D229D675 /* SnoozeStateModel.swift */; };
 		88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB3BAE7494CB771ABAC7B8B /* ISFEditorRootView.swift */; };
-		891DECF7BC20968D7F566161 /* AutotuneConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EF98E22A39CD656A230704 /* AutotuneConfigProvider.swift */; };
 		8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */; };
 		9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */; };
 		9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */; };
 		98641AF4F92123DA668AB931 /* CarbRatioEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */; };
-		A05235B9112E677ED03B6E8E /* AutotuneConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CF5ACEE1F0859670E71B2C0 /* AutotuneConfigRootView.swift */; };
 		A33352ED40476125EBAC6EE0 /* CarbRatioEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */; };
 		AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */; };
 		B7C465E9472624D8A2BE2A6A /* (null) in Sources */ = {isa = PBXBuildFile; };
@@ -418,7 +414,6 @@
 		CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */; };
 		D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */; };
 		D6DEC113821A7F1056C4AA1E /* NightscoutConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */; };
-		D76333C9256787610B3B4875 /* AutotuneConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D295A3F870E826BE371C0BB5 /* AutotuneConfigStateModel.swift */; };
 		DBA5254DBB2586C98F61220C /* ISFEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F137F126D9F8DEB799F26 /* ISFEditorProvider.swift */; };
 		DD07CA142CE80B73002D45A9 /* TimeInRangeChartStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD07CA132CE80B73002D45A9 /* TimeInRangeChartStyle.swift */; };
 		DD09D47B2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD09D47A2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift */; };
@@ -454,6 +449,7 @@
 		DD1745522C55CA5D00211FAC /* UnitsLimitsSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745512C55CA5D00211FAC /* UnitsLimitsSettingsStateModel.swift */; };
 		DD1745552C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */; };
 		DD1DB7CC2BECCA1F0048B367 /* BuildDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */; };
+		DD1E53592D273F26008F32A4 /* LoopStatusHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1E53582D273F20008F32A4 /* LoopStatusHelpView.swift */; };
 		DD21FCB52C6952AD00AF2C25 /* DecimalPickerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */; };
 		DD2CC85C2D25DA1000445446 /* GlucoseTargetsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2CC85B2D25D9CE00445446 /* GlucoseTargetsView.swift */; };
 		DD32CF982CC82463003686D6 /* TrioRemoteControl+Bolus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD32CF972CC82460003686D6 /* TrioRemoteControl+Bolus.swift */; };
@@ -484,6 +480,7 @@
 		DD9ECB722CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB6F2CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift */; };
 		DD9ECB742CA9A0C300AA7C45 /* RemoteControlConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB732CA9A0C300AA7C45 /* RemoteControlConfig.swift */; };
 		DDA6E2502D22187500C2988C /* ChartLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6E24F2D22187500C2988C /* ChartLegendView.swift */; };
+		DDA6E2852D2361F800C2988C /* LoopStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6E2842D2361F800C2988C /* LoopStatusView.swift */; };
 		DDA6E3202D258E0500C2988C /* OverrideHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6E31F2D258E0500C2988C /* OverrideHelpView.swift */; };
 		DDA6E3222D25901100C2988C /* TempTargetHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6E3212D25901100C2988C /* TempTargetHelpView.swift */; };
 		DDA6E3572D25988500C2988C /* ContactImageHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6E3562D25988500C2988C /* ContactImageHelpView.swift */; };
@@ -840,7 +837,6 @@
 		389A571F26079BAA00BC102F /* Interpolation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interpolation.swift; sourceTree = "<group>"; };
 		389ECDFD2601061500D86C4F /* View+Snapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Snapshot.swift"; sourceTree = "<group>"; };
 		389ECE042601144100D86C4F /* ConcurrentMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentMap.swift; sourceTree = "<group>"; };
-		38A00B1E25FC00F7006BC0B0 /* Autotune.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Autotune.swift; sourceTree = "<group>"; };
 		38A00B2225FC2B55006BC0B0 /* LRUCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = "<group>"; };
 		38A0363A25ECF07E00FCBB52 /* GlucoseStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseStorage.swift; sourceTree = "<group>"; };
 		38A0364125ED069400FCBB52 /* TempBasal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempBasal.swift; sourceTree = "<group>"; };
@@ -1004,8 +1000,6 @@
 		7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorDataFlow.swift; sourceTree = "<group>"; };
 		8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigRootView.swift; sourceTree = "<group>"; };
 		881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableRootView.swift; sourceTree = "<group>"; };
-		8CF5ACEE1F0859670E71B2C0 /* AutotuneConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AutotuneConfigRootView.swift; sourceTree = "<group>"; };
-		8DCCCCE633F5E98E41B0CD3C /* AutotuneConfigDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AutotuneConfigDataFlow.swift; sourceTree = "<group>"; };
 		920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorRootView.swift; sourceTree = "<group>"; };
 		9455FA2D92E77A6C4AFED8A3 /* DataTableStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableStateModel.swift; sourceTree = "<group>"; };
 		96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalDataFlow.swift; sourceTree = "<group>"; };
@@ -1017,7 +1011,6 @@
 		AAFF91130F2FCCC7EBBA11AD /* BasalProfileEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorStateModel.swift; sourceTree = "<group>"; };
 		AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpConfigDataFlow.swift; sourceTree = "<group>"; };
 		B5822B15939E719628E9FF7C /* SnoozeRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeRootView.swift; sourceTree = "<group>"; };
-		B5EF98E22A39CD656A230704 /* AutotuneConfigProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AutotuneConfigProvider.swift; sourceTree = "<group>"; };
 		B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMDataFlow.swift; sourceTree = "<group>"; };
 		B9CAAEFB2AE70836000F68BC /* branch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = branch.txt; sourceTree = SOURCE_ROOT; };
 		BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorDataFlow.swift; sourceTree = "<group>"; };
@@ -1124,7 +1117,6 @@
 		CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationsTests.swift; sourceTree = "<group>"; };
 		CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalStateModel.swift; sourceTree = "<group>"; };
 		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
-		D295A3F870E826BE371C0BB5 /* AutotuneConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AutotuneConfigStateModel.swift; sourceTree = "<group>"; };
 		DC2C6489D29ECCCAD78E0721 /* GlucoseNotificationSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsStateModel.swift; sourceTree = "<group>"; };
 		DD07CA132CE80B73002D45A9 /* TimeInRangeChartStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInRangeChartStyle.swift; sourceTree = "<group>"; };
 		DD09D47A2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsDataFlow.swift; sourceTree = "<group>"; };
@@ -1160,6 +1152,7 @@
 		DD1745512C55CA5D00211FAC /* UnitsLimitsSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsLimitsSettingsStateModel.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>"; };
+		DD1E53582D273F20008F32A4 /* LoopStatusHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopStatusHelpView.swift; sourceTree = "<group>"; };
 		DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalPickerSettings.swift; sourceTree = "<group>"; };
 		DD2CC85B2D25D9CE00445446 /* GlucoseTargetsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseTargetsView.swift; sourceTree = "<group>"; };
 		DD32CF972CC82460003686D6 /* TrioRemoteControl+Bolus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TrioRemoteControl+Bolus.swift"; sourceTree = "<group>"; };
@@ -1190,6 +1183,7 @@
 		DD9ECB6F2CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteControlConfigDataFlow.swift; sourceTree = "<group>"; };
 		DD9ECB732CA9A0C300AA7C45 /* RemoteControlConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteControlConfig.swift; sourceTree = "<group>"; };
 		DDA6E24F2D22187500C2988C /* ChartLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartLegendView.swift; sourceTree = "<group>"; };
+		DDA6E2842D2361F800C2988C /* LoopStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopStatusView.swift; sourceTree = "<group>"; };
 		DDA6E31F2D258E0500C2988C /* OverrideHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideHelpView.swift; sourceTree = "<group>"; };
 		DDA6E3212D25901100C2988C /* TempTargetHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargetHelpView.swift; sourceTree = "<group>"; };
 		DDA6E3562D25988500C2988C /* ContactImageHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageHelpView.swift; sourceTree = "<group>"; };
@@ -1559,7 +1553,6 @@
 				DDD163032C4C67B400CD525A /* Adjustments */,
 				DD1745382C55BF8B00211FAC /* AlgorithmAdvancedSettings */,
 				DD1745422C55C5C400211FAC /* AutosensSettings */,
-				672F63EEAE27400625E14BAD /* AutotuneConfig */,
 				A42F1FEDFFD0DDE00AAD54D3 /* BasalProfileEditor */,
 				3811DE0425C9D32E00A708ED /* Base */,
 				BD7DA9A32AE06DBA00601B20 /* BolusCalculatorConfig */,
@@ -1898,6 +1891,8 @@
 		3833B51F260264B6003021B3 /* Header */ = {
 			isa = PBXGroup;
 			children = (
+				DD1E53582D273F20008F32A4 /* LoopStatusHelpView.swift */,
+				DDA6E2842D2361F800C2988C /* LoopStatusView.swift */,
 				383420D525FFE38C002D46C1 /* LoopView.swift */,
 				38AAF85425FFF846004AF583 /* CurrentGlucoseView.swift */,
 				38DAB27F260CBB7F00F74C1A /* PumpView.swift */,
@@ -2002,7 +1997,6 @@
 				DD940BA92CA7585D000830A5 /* GlucoseColorScheme.swift */,
 				DD6D67E32C9C253500660C9B /* ColorSchemeOption.swift */,
 				388E5A5F25B6F2310019842D /* Autosens.swift */,
-				38A00B1E25FC00F7006BC0B0 /* Autotune.swift */,
 				388358C725EEF6D200E024B2 /* BasalProfileEntry.swift */,
 				38D0B3B525EBE24900CB6E88 /* Battery.swift */,
 				382C134A25F14E3700715CE1 /* BGTargets.swift */,
@@ -2329,14 +2323,6 @@
 			path = View;
 			sourceTree = "<group>";
 		};
-		55DE731ACE8289FAF3819077 /* View */ = {
-			isa = PBXGroup;
-			children = (
-				8CF5ACEE1F0859670E71B2C0 /* AutotuneConfigRootView.swift */,
-			);
-			path = View;
-			sourceTree = "<group>";
-		};
 		5825D1622BD405AE00F36E9B /* Helper */ = {
 			isa = PBXGroup;
 			children = (
@@ -2399,17 +2385,6 @@
 			path = TargetsEditor;
 			sourceTree = "<group>";
 		};
-		672F63EEAE27400625E14BAD /* AutotuneConfig */ = {
-			isa = PBXGroup;
-			children = (
-				8DCCCCE633F5E98E41B0CD3C /* AutotuneConfigDataFlow.swift */,
-				B5EF98E22A39CD656A230704 /* AutotuneConfigProvider.swift */,
-				D295A3F870E826BE371C0BB5 /* AutotuneConfigStateModel.swift */,
-				55DE731ACE8289FAF3819077 /* View */,
-			);
-			path = AutotuneConfig;
-			sourceTree = "<group>";
-		};
 		6B1A8D1C2B14D91600E76752 /* LiveActivity */ = {
 			isa = PBXGroup;
 			children = (
@@ -3553,6 +3528,7 @@
 				DDD1631F2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld in Sources */,
 				DD1745482C55C61D00211FAC /* AutosensSettingsStateModel.swift in Sources */,
 				DD1745462C55C61500211FAC /* AutosensSettingsProvider.swift in Sources */,
+				DDA6E2852D2361F800C2988C /* LoopStatusView.swift in Sources */,
 				DDA6E3202D258E0500C2988C /* OverrideHelpView.swift in Sources */,
 				DDA6E2502D22187500C2988C /* ChartLegendView.swift in Sources */,
 				3811DEAF25C9D88300A708ED /* KeyValueStorage.swift in Sources */,
@@ -3733,6 +3709,7 @@
 				F90692AA274B7AAE0037068D /* HealthKitManager.swift in Sources */,
 				38887CCE25F5725200944304 /* IOBEntry.swift in Sources */,
 				38E98A2425F52C9300C0CED0 /* Logger.swift in Sources */,
+				DD1E53592D273F26008F32A4 /* LoopStatusHelpView.swift in Sources */,
 				CA370FC152BC98B3D1832968 /* BasalProfileEditorRootView.swift in Sources */,
 				195D80BB2AF6980B00D25097 /* DynamicSettingsStateModel.swift in Sources */,
 				E00EEC0327368630002FF094 /* ServiceAssembly.swift in Sources */,
@@ -3805,7 +3782,6 @@
 				DDD163162C4C690300CD525A /* AdjustmentsDataFlow.swift in Sources */,
 				BDF34F932C10D0E100D51995 /* LiveActivityAttributes+Helper.swift in Sources */,
 				E0D4F80527513ECF00BDF1FE /* HealthKitSample.swift in Sources */,
-				38A00B1F25FC00F7006BC0B0 /* Autotune.swift in Sources */,
 				38AAF85525FFF846004AF583 /* CurrentGlucoseView.swift in Sources */,
 				041D1E995A6AE92E9289DC49 /* TreatmentsDataFlow.swift in Sources */,
 				DD32CF9E2CC824C5003686D6 /* TrioRemoteControl+Override.swift in Sources */,
@@ -3836,11 +3812,7 @@
 				38E44534274E411700EC9A94 /* Disk+InternalHelpers.swift in Sources */,
 				38A00B2325FC2B55006BC0B0 /* LRUCache.swift in Sources */,
 				DDD163122C4C689900CD525A /* AdjustmentsStateModel.swift in Sources */,
-				3083261C4B268E353F36CD0B /* AutotuneConfigDataFlow.swift in Sources */,
 				DD1745132C54169400211FAC /* DevicesView.swift in Sources */,
-				891DECF7BC20968D7F566161 /* AutotuneConfigProvider.swift in Sources */,
-				D76333C9256787610B3B4875 /* AutotuneConfigStateModel.swift in Sources */,
-				A05235B9112E677ED03B6E8E /* AutotuneConfigRootView.swift in Sources */,
 				7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */,
 				1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */,
 				19F95FFA29F1102A00314DDC /* StatRootView.swift in Sources */,

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

@@ -1,8 +1,6 @@
 {
   "units" : "mg/dL",
   "closedLoop" : false,
-  "useAutotune" : false,
-  "onlyAutotuneBasals" : false,
   "isUploadEnabled" : false,
   "isDownloadEnabled" : false,
   "useLocalGlucoseSource" : false,

+ 3 - 40
FreeAPS/Sources/APS/APSManager.swift

@@ -8,7 +8,6 @@ import Swinject
 
 protocol APSManager {
     func heartbeat(date: Date)
-    func autotune() async -> Autotune?
     func enactBolus(amount: Double, isSMB: Bool) async
     var pumpManager: PumpManagerUI? { get set }
     var bluetoothManager: BluetoothStateManager? { get }
@@ -21,7 +20,6 @@ protocol APSManager {
     var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
     var isManualTempBasal: Bool { get }
     func enactTempBasal(rate: Double, duration: TimeInterval) async
-    func makeProfiles() async throws -> Bool
     func determineBasal() async -> Bool
     func determineBasalSync() async
     func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination?
@@ -68,7 +66,6 @@ final class BaseAPSManager: APSManager, Injectable {
     @Injected() private var nightscout: NightscoutManager!
     @Injected() private var settingsManager: SettingsManager!
     @Injected() private var broadcaster: Broadcaster!
-    @Persisted(key: "lastAutotuneDate") private var lastAutotuneDate = Date()
     @Persisted(key: "lastLoopStartDate") private var lastLoopStartDate: Date = .distantPast
     @Persisted(key: "lastLoopDate") var lastLoopDate: Date = .distantPast {
         didSet {
@@ -133,7 +130,7 @@ final class BaseAPSManager: APSManager, Injectable {
             let wasParsed = storage.parseOnFileSettingsToMgdL()
             if wasParsed {
                 Task {
-                    try await makeProfiles()
+                    await openAPS.createInitialProfiles()
                 }
             }
         }
@@ -389,11 +386,9 @@ final class BaseAPSManager: APSManager, Injectable {
             let now = Date()
 
             // Start fetching asynchronously
-            let (currentTemp, _, _, _) = try await (
+            let (currentTemp, _) = try await (
                 fetchCurrentTempBasal(date: now),
-                makeProfiles(),
-                autosense(),
-                dailyAutotune()
+                autosense()
             )
 
             // Determine basal using the fetched temp and current time
@@ -431,18 +426,6 @@ final class BaseAPSManager: APSManager, Injectable {
         }
     }
 
-    func makeProfiles() async throws -> Bool {
-        let tunedProfile = await openAPS.makeProfiles(useAutotune: settings.useAutotune)
-        if let basalProfile = tunedProfile?.basalProfile {
-            processQueue.async {
-                self.broadcaster.notify(BasalProfileObserver.self, on: self.processQueue) {
-                    $0.basalProfileDidChange(basalProfile)
-                }
-            }
-        }
-        return tunedProfile != nil
-    }
-
     func roundBolus(amount: Decimal) -> Decimal {
         guard let pump = pumpManager else { return amount }
         let rounded = Decimal(pump.roundToSupportedBolusVolume(units: Double(amount)))
@@ -535,26 +518,6 @@ final class BaseAPSManager: APSManager, Injectable {
         }
     }
 
-    func dailyAutotune() async throws -> Bool {
-        guard settings.useAutotune else {
-            return false
-        }
-
-        let now = Date()
-
-        guard lastAutotuneDate.isBeforeDate(now, granularity: .day) else {
-            return false
-        }
-        lastAutotuneDate = now
-
-        let result = await autotune()
-        return result != nil
-    }
-
-    func autotune() async -> Autotune? {
-        await openAPS.autotune()
-    }
-
     private func fetchCurrentTempBasal(date: Date) async -> TempBasal {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: PumpEventStored.self,

+ 5 - 126
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -462,66 +462,8 @@ final class OpenAPS {
         }
     }
 
-    func autotune(categorizeUamAsBasal: Bool = false, tuneInsulinCurve: Bool = false) async -> Autotune? {
-        debug(.openAPS, "Start autotune")
-
-        // Perform asynchronous calls in parallel
-        async let pumpHistoryObjectIDs = fetchPumpHistoryObjectIDs() ?? []
-        async let carbs = fetchAndProcessCarbs()
-        async let glucose = fetchAndProcessGlucose()
-        async let getProfile = loadFileFromStorageAsync(name: Settings.profile)
-        async let getPumpProfile = loadFileFromStorageAsync(name: Settings.pumpProfile)
-        async let getPreviousAutotune = storage.retrieveAsync(Settings.autotune, as: RawJSON.self)
-
-        // Await the results of asynchronous tasks
-        let (pumpHistoryJSON, carbsAsJSON, glucoseAsJSON, profile, pumpProfile, previousAutotune) = await (
-            parsePumpHistory(await pumpHistoryObjectIDs),
-            carbs,
-            glucose,
-            getProfile,
-            getPumpProfile,
-            getPreviousAutotune
-        )
-
-        // Error need to be handled here because the function is not declared as throws
-        do {
-            // Autotune Prepare
-            let autotunePreppedGlucose = try await autotunePrepare(
-                pumphistory: pumpHistoryJSON,
-                profile: profile,
-                glucose: glucoseAsJSON,
-                pumpprofile: pumpProfile,
-                carbs: carbsAsJSON,
-                categorizeUamAsBasal: categorizeUamAsBasal,
-                tuneInsulinCurve: tuneInsulinCurve
-            )
-
-            debug(.openAPS, "AUTOTUNE PREP: \(autotunePreppedGlucose)")
-
-            // Autotune Run
-            let autotuneResult = try await autotuneRun(
-                autotunePreparedData: autotunePreppedGlucose,
-                previousAutotuneResult: previousAutotune ?? profile,
-                pumpProfile: pumpProfile
-            )
-
-            debug(.openAPS, "AUTOTUNE RESULT: \(autotuneResult)")
-
-            if let autotune = Autotune(from: autotuneResult) {
-                storage.save(autotuneResult, as: Settings.autotune)
-
-                return autotune
-            } else {
-                return nil
-            }
-        } catch {
-            debug(.openAPS, "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to prepare/run Autotune")
-            return nil
-        }
-    }
-
-    func makeProfiles(useAutotune _: Bool) async -> Autotune? {
-        debug(.openAPS, "Start makeProfiles")
+    func createInitialProfiles() async {
+        debug(.openAPS, "Start creating pump profile and user profile")
 
         // Load required settings and profiles asynchronously
         async let getPumpSettings = loadFileFromStorageAsync(name: Settings.settings)
@@ -531,10 +473,9 @@ final class OpenAPS {
         async let getCR = loadFileFromStorageAsync(name: Settings.carbRatios)
         async let getTempTargets = loadFileFromStorageAsync(name: Settings.tempTargets)
         async let getModel = loadFileFromStorageAsync(name: Settings.model)
-        async let getAutotune = loadFileFromStorageAsync(name: Settings.autotune)
         async let getFreeAPS = loadFileFromStorageAsync(name: FreeAPS.settings)
 
-        let (pumpSettings, bgTargets, basalProfile, isf, cr, tempTargets, model, autotune, freeaps) = await (
+        let (pumpSettings, bgTargets, basalProfile, isf, cr, tempTargets, model, freeaps) = await (
             getPumpSettings,
             getBGTargets,
             getBasalProfile,
@@ -542,7 +483,6 @@ final class OpenAPS {
             getCR,
             getTempTargets,
             getModel,
-            getAutotune,
             getFreeAPS
         )
 
@@ -588,27 +528,18 @@ final class OpenAPS {
                 carbRatio: cr,
                 tempTargets: tempTargets,
                 model: model,
-                autotune: autotune.isEmpty ? .null : autotune,
+                autotune: RawJSON.null,
                 freeaps: freeaps
             )
 
             // Save the profiles
             await storage.saveAsync(pumpProfile, as: Settings.pumpProfile)
             await storage.saveAsync(profile, as: Settings.profile)
-
-            // Return the Autotune object, if available
-            if let tunedProfile = Autotune(from: profile) {
-                return tunedProfile
-            } else {
-                return nil
-            }
         } catch {
-            // Handle errors and log failure
             debug(
                 .apsManager,
-                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to execute makeProfiles()"
+                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to create pump profile and normal profile: \(error)"
             )
-            return nil
         }
     }
 
@@ -687,58 +618,6 @@ final class OpenAPS {
         }
     }
 
-    private func autotunePrepare(
-        pumphistory: JSON,
-        profile: JSON,
-        glucose: JSON,
-        pumpprofile: JSON,
-        carbs: JSON,
-        categorizeUamAsBasal: Bool,
-        tuneInsulinCurve: Bool
-    ) async throws -> RawJSON {
-        try await withCheckedThrowingContinuation { continuation in
-            jsWorker.inCommonContext { worker in
-                worker.evaluateBatch(scripts: [
-                    Script(name: Prepare.log),
-                    Script(name: Bundle.autotunePrep),
-                    Script(name: Prepare.autotunePrep)
-                ])
-                let result = worker.call(function: Function.generate, with: [
-                    pumphistory,
-                    profile,
-                    glucose,
-                    pumpprofile,
-                    carbs,
-                    categorizeUamAsBasal,
-                    tuneInsulinCurve
-                ])
-                continuation.resume(returning: result)
-            }
-        }
-    }
-
-    private func autotuneRun(
-        autotunePreparedData: JSON,
-        previousAutotuneResult: JSON,
-        pumpProfile: JSON
-    ) async throws -> RawJSON {
-        try await withCheckedThrowingContinuation { continuation in
-            jsWorker.inCommonContext { worker in
-                worker.evaluateBatch(scripts: [
-                    Script(name: Prepare.log),
-                    Script(name: Bundle.autotuneCore),
-                    Script(name: Prepare.autotuneCore)
-                ])
-                let result = worker.call(function: Function.generate, with: [
-                    autotunePreparedData,
-                    previousAutotuneResult,
-                    pumpProfile
-                ])
-                continuation.resume(returning: result)
-            }
-        }
-    }
-
     private func determineBasal(
         glucose: JSON,
         currentTemp: JSON,

+ 0 - 17
FreeAPS/Sources/Models/Autotune.swift

@@ -1,17 +0,0 @@
-import Foundation
-
-struct Autotune: JSON, Equatable {
-    var createdAt: Date?
-    let basalProfile: [BasalProfileEntry]
-    let sensitivity: Decimal
-    let carbRatio: Decimal
-}
-
-extension Autotune {
-    private enum CodingKeys: String, CodingKey {
-        case createdAt = "created_at"
-        case basalProfile = "basalprofile"
-        case sensitivity = "sens"
-        case carbRatio = "carb_ratio"
-    }
-}

+ 0 - 7
FreeAPS/Sources/Models/DecimalPickerSettings.swift

@@ -78,13 +78,6 @@ struct DecimalPickerSettings {
     )
     var maxCOB = PickerSetting(value: 120, step: 5, min: 0, max: 300, type: PickerSetting.PickerSettingType.gram)
     var min5mCarbimpact = PickerSetting(value: 8, step: 1, min: 1, max: 20, type: PickerSetting.PickerSettingType.glucose)
-    var autotuneISFAdjustmentFraction = PickerSetting(
-        value: 1.0,
-        step: 0.05,
-        min: 0,
-        max: 1,
-        type: PickerSetting.PickerSettingType.factor
-    )
     var remainingCarbsFraction = PickerSetting(
         value: 1.0,
         step: 0.05,

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

@@ -18,7 +18,6 @@ enum BolusShortcutLimit: String, JSON, CaseIterable, Identifiable {
 struct FreeAPSSettings: JSON, Equatable {
     var units: GlucoseUnits = .mgdL
     var closedLoop: Bool = false
-    var useAutotune: Bool = false
     var isUploadEnabled: Bool = false
     var isDownloadEnabled: Bool = false
     var useLocalGlucoseSource: Bool = false
@@ -67,7 +66,6 @@ struct FreeAPSSettings: JSON, Equatable {
     var maxProtein: Decimal = 250
     var displayFatAndProteinOnWatch: Bool = false
     var confirmBolusFaster: Bool = false
-    var onlyAutotuneBasals: Bool = false
     var overrideFactor: Decimal = 0.8
     var fattyMeals: Bool = false
     var fattyMealFactor: Decimal = 0.7
@@ -93,10 +91,6 @@ extension FreeAPSSettings: Decodable {
             settings.closedLoop = closedLoop
         }
 
-        if let useAutotune = try? container.decode(Bool.self, forKey: .useAutotune) {
-            settings.useAutotune = useAutotune
-        }
-
         if let isUploadEnabled = try? container.decode(Bool.self, forKey: .isUploadEnabled) {
             settings.isUploadEnabled = isUploadEnabled
         }
@@ -314,10 +308,6 @@ extension FreeAPSSettings: Decodable {
             settings.confirmBolusFaster = confirmBolusFaster
         }
 
-        if let onlyAutotuneBasals = try? container.decode(Bool.self, forKey: .onlyAutotuneBasals) {
-            settings.onlyAutotuneBasals = onlyAutotuneBasals
-        }
-
         if let displayPresets = try? container.decode(Bool.self, forKey: .displayPresets) {
             settings.displayPresets = displayPresets
         }

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

@@ -20,7 +20,6 @@ struct Preferences: JSON, Equatable {
     var skipNeutralTemps: Bool = false
     var unsuspendIfNoTemp: Bool = false
     var min5mCarbimpact: Decimal = 8
-    var autotuneISFAdjustmentFraction: Decimal = 1.0
     var remainingCarbsFraction: Decimal = 1.0
     var remainingCarbsCap: Decimal = 90
     var enableUAM: Bool = false
@@ -77,7 +76,6 @@ extension Preferences {
         case skipNeutralTemps = "skip_neutral_temps"
         case unsuspendIfNoTemp = "unsuspend_if_no_temp"
         case min5mCarbimpact = "min_5m_carbimpact"
-        case autotuneISFAdjustmentFraction = "autotune_isf_adjustmentFraction"
         case remainingCarbsFraction
         case remainingCarbsCap
         case enableUAM
@@ -202,10 +200,6 @@ extension Preferences: Decodable {
             preferences.min5mCarbimpact = min5mCarbimpact
         }
 
-        if let autotuneISFAdjustmentFraction = try? container.decode(Decimal.self, forKey: .autotuneISFAdjustmentFraction) {
-            preferences.autotuneISFAdjustmentFraction = autotuneISFAdjustmentFraction
-        }
-
         if let remainingCarbsFraction = try? container.decode(Decimal.self, forKey: .remainingCarbsFraction) {
             preferences.remainingCarbsFraction = remainingCarbsFraction
         }

+ 1 - 1
FreeAPS/Sources/Modules/Adjustments/View/Overrides/OverrideHelpView.swift

@@ -38,7 +38,7 @@ struct OverrideHelpView: View {
             .navigationBarTitle("Help", displayMode: .inline)
 
             Button { state.isHelpSheetPresented.toggle() }
-            label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
+            label: { Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center) }
                 .buttonStyle(.bordered)
                 .padding(.top)
         }

+ 1 - 1
FreeAPS/Sources/Modules/Adjustments/View/TempTargets/TempTargetHelpView.swift

@@ -25,7 +25,7 @@ struct TempTargetHelpView: View {
             .navigationBarTitle("Help", displayMode: .inline)
 
             Button { state.isHelpSheetPresented.toggle() }
-            label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
+            label: { Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center) }
                 .buttonStyle(.bordered)
                 .padding(.top)
         }

+ 0 - 3
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift

@@ -18,7 +18,6 @@ extension AlgorithmAdvancedSettings {
         @Published var unsuspendIfNoTemp: Bool = false
         @Published var suspendZerosIOB: Bool = false
         @Published var min5mCarbimpact: Decimal = 8
-        @Published var autotuneISFAdjustmentFraction: Decimal = 1.0
         @Published var remainingCarbsFraction: Decimal = 1.0
         @Published var remainingCarbsCap: Decimal = 90
         @Published var noisyCGMTargetMultiplier: Decimal = 1.3
@@ -42,8 +41,6 @@ extension AlgorithmAdvancedSettings {
             subscribePreferencesSetting(\.suspendZerosIOB, on: $suspendZerosIOB) { suspendZerosIOB = $0 }
             subscribePreferencesSetting(\.suspendZerosIOB, on: $suspendZerosIOB) { suspendZerosIOB = $0 }
             subscribePreferencesSetting(\.min5mCarbimpact, on: $min5mCarbimpact) { min5mCarbimpact = $0 }
-            subscribePreferencesSetting(\.autotuneISFAdjustmentFraction, on: $autotuneISFAdjustmentFraction) {
-                autotuneISFAdjustmentFraction = $0 }
             subscribePreferencesSetting(\.remainingCarbsFraction, on: $remainingCarbsFraction) { remainingCarbsFraction = $0 }
             subscribePreferencesSetting(\.remainingCarbsCap, on: $remainingCarbsCap) { remainingCarbsCap = $0 }
             subscribePreferencesSetting(\.noisyCGMTargetMultiplier, on: $noisyCGMTargetMultiplier) {

+ 0 - 27
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift

@@ -228,33 +228,6 @@ extension AlgorithmAdvancedSettings {
                     }
                 )
 
-                // Commenting out Autotune from Settings Menu until full removal is complete
-                // SettingInputSection(
-                // decimalValue: $state.autotuneISFAdjustmentFraction,
-                // booleanValue: $booleanPlaceholder,
-                // shouldDisplayHint: $shouldDisplayHint,
-                // selectedVerboseHint: Binding(
-                // get: { selectedVerboseHint },
-                // set: {
-                // selectedVerboseHint = $0.map { AnyView($0) }
-                // hintLabel = NSLocalizedString(
-                // "Autotune ISF Adjustment Percent",
-                // comment: "Autotune ISF Adjustment Percent"
-                // )
-                // }
-                // ),
-                // units: state.units,
-                // type: .decimal("autotuneISFAdjustmentFraction"),
-                // label: NSLocalizedString("Autotune ISF Adjustment Percent", comment: "Autotune ISF Adjustment Percent"),
-                // miniHint: "Using Autotune is not advised",
-                // verboseHint: Text(
-                // NSLocalizedString(
-                // "The default of 50% for this value keeps autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 100% allows full adjustment, 0% is no adjustment from pump ISF.",
-                // comment: "Autotune ISF Adjustment Percent"
-                // )
-                // )
-                // )
-
                 SettingInputSection(
                     decimalValue: $state.min5mCarbimpact,
                     booleanValue: $booleanPlaceholder,

+ 0 - 10
FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigDataFlow.swift

@@ -1,10 +0,0 @@
-import Combine
-
-enum AutotuneConfig {
-    enum Config {}
-}
-
-protocol AutotuneConfigProvider: Provider {
-    var autotune: Autotune? { get }
-    func deleteAutotune()
-}

+ 0 - 15
FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigProvider.swift

@@ -1,15 +0,0 @@
-import Combine
-
-extension AutotuneConfig {
-    final class Provider: BaseProvider, AutotuneConfigProvider {
-        @Injected() private var apsManager: APSManager!
-
-        var autotune: Autotune? {
-            storage.retrieve(OpenAPS.Settings.autotune, as: Autotune.self)
-        }
-
-        func deleteAutotune() {
-            storage.remove(OpenAPS.Settings.autotune)
-        }
-    }
-}

+ 0 - 113
FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigStateModel.swift

@@ -1,113 +0,0 @@
-import Combine
-import LoopKit
-import SwiftUI
-
-extension AutotuneConfig {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var apsManager: APSManager!
-        @Injected() private var storage: FileStorage!
-        @Published var useAutotune = false
-        @Published var onlyAutotuneBasals = false
-        @Published var autotune: Autotune?
-        private(set) var units: GlucoseUnits = .mgdL
-        @Published var publishedDate = Date()
-        @Persisted(key: "lastAutotuneDate") private var lastAutotuneDate = Date() {
-            didSet {
-                DispatchQueue.main.async {
-                    self.publishedDate = self.lastAutotuneDate
-                }
-            }
-        }
-
-        override func subscribe() {
-            autotune = provider.autotune
-            units = settingsManager.settings.units
-            useAutotune = settingsManager.settings.useAutotune
-            publishedDate = lastAutotuneDate
-            subscribeSetting(\.onlyAutotuneBasals, on: $onlyAutotuneBasals) { onlyAutotuneBasals = $0 }
-
-            $useAutotune
-                .removeDuplicates()
-                .flatMap { [weak self] use -> AnyPublisher<Bool, Never> in
-                    guard let self = self else {
-                        return Just(false).eraseToAnyPublisher()
-                    }
-                    self.settingsManager.settings.useAutotune = use
-                    return Future { promise in
-                        Task.init(priority: .background) {
-                            do {
-                                _ = try await self.apsManager.makeProfiles()
-                                promise(.success(true))
-
-                            } catch {
-                                promise(.success(false))
-                            }
-                        }
-                    }
-                    .eraseToAnyPublisher()
-                }
-                .cancellable()
-                .store(in: &lifetime)
-        }
-
-        func run() {
-            Task {
-                do {
-                    if let result = await self.apsManager.autotune() {
-                        autotune = result
-                        _ = try await self.apsManager.makeProfiles()
-                        lastAutotuneDate = Date()
-                    }
-                } catch {
-                    debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to run Autotune")
-                }
-            }
-        }
-
-        func delete() async {
-            provider.deleteAutotune()
-            autotune = nil
-            do {
-                _ = try await apsManager.makeProfiles()
-            } catch {
-                return
-            }
-        }
-
-        func replace() {
-            if let autotunedBasals = autotune {
-                let basals = autotunedBasals.basalProfile
-                    .map { basal -> BasalProfileEntry in
-                        BasalProfileEntry(
-                            start: String(basal.start.prefix(5)),
-                            minutes: basal.minutes,
-                            rate: basal.rate
-                        )
-                    }
-                guard let pump = apsManager.pumpManager else {
-                    storage.save(basals, as: OpenAPS.Settings.basalProfile)
-                    debug(.service, "Basals have been replaced with Autotuned Basals by user.")
-                    return
-                }
-                let syncValues = basals.map {
-                    RepeatingScheduleValue(startTime: TimeInterval($0.minutes * 60), value: Double($0.rate))
-                }
-                pump.syncBasalRateSchedule(items: syncValues) { result in
-                    switch result {
-                    case .success:
-                        self.storage.save(basals, as: OpenAPS.Settings.basalProfile)
-                        debug(.service, "Basals saved to pump!")
-                    case .failure:
-                        debug(.service, "Basals couldn't be save to pump")
-                    }
-                }
-            }
-        }
-    }
-}
-
-extension AutotuneConfig.StateModel: SettingsObserver {
-    func settingsDidChange(_: FreeAPSSettings) {
-        units = settingsManager.settings.units
-    }
-}

+ 0 - 198
FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift

@@ -1,198 +0,0 @@
-import SwiftUI
-import Swinject
-
-extension AutotuneConfig {
-    struct RootView: BaseView {
-        let resolver: Resolver
-        @StateObject var state = StateModel()
-
-        @State private var shouldDisplayHint: Bool = false
-        @State var hintDetent = PresentationDetent.large
-        @State var selectedVerboseHint: AnyView?
-        @State var hintLabel: String?
-        @State private var decimalPlaceholder: Decimal = 0.0
-        @State private var booleanPlaceholder: Bool = false
-
-        @State var replaceAlert = false
-
-        @Environment(\.colorScheme) var colorScheme
-        @Environment(AppState.self) var appState
-
-        private var isfFormatter: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 2
-            return formatter
-        }
-
-        private var rateFormatter: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 2
-            return formatter
-        }
-
-        private var dateFormatter: DateFormatter {
-            let formatter = DateFormatter()
-            formatter.dateStyle = .medium
-            formatter.timeStyle = .short
-            return formatter
-        }
-
-        var body: some View {
-            Form {
-                SettingInputSection(
-                    decimalValue: $decimalPlaceholder,
-                    booleanValue: $state.useAutotune,
-                    shouldDisplayHint: $shouldDisplayHint,
-                    selectedVerboseHint: Binding(
-                        get: { selectedVerboseHint },
-                        set: {
-                            selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Use Autotune"
-                        }
-                    ),
-                    units: state.units,
-                    type: .boolean,
-                    label: "Use Autotune",
-                    miniHint: "It is not advised to use Autotune with Trio.",
-                    verboseHint:
-                    VStack(alignment: .leading, spacing: 10) {
-                        Text("It is not advised to use Autotune with Trio").bold()
-                        Text("Autotune is not designed to work with Trio. It is best to keep Autotune off and do not use it.")
-
-                    },
-                    headerText: "Data-driven Adjustments"
-                )
-
-                if state.useAutotune {
-                    SettingInputSection(
-                        decimalValue: $decimalPlaceholder,
-                        booleanValue: $state.onlyAutotuneBasals,
-                        shouldDisplayHint: $shouldDisplayHint,
-                        selectedVerboseHint: Binding(
-                            get: { selectedVerboseHint },
-                            set: {
-                                selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Only Autotune Basal Insulin"
-                            }
-                        ),
-                        units: state.units,
-                        type: .boolean,
-                        label: "Only Autotune Basal Insulin",
-                        miniHint: "Restricts Autotune adjustments to only basal settings.",
-                        verboseHint: Text("Restricts Autotune adjustments to only basal settings.")
-                    )
-                }
-
-                Section(
-                    header: HStack {
-                        Text("Last run")
-                        Spacer()
-                        Text(dateFormatter.string(from: state.publishedDate))
-                    },
-                    content: {
-                        Button {
-                            state.run()
-                        } label: {
-                            Text("Run now")
-                        }
-                        .frame(maxWidth: .infinity, alignment: .center)
-                        .listRowBackground(Color(.systemBlue))
-                        .tint(.white)
-                    }
-                )
-
-                if let autotune = state.autotune {
-                    if !state.onlyAutotuneBasals {
-                        Section {
-                            HStack {
-                                Text("Carb ratio")
-                                Spacer()
-                                Text(isfFormatter.string(from: autotune.carbRatio as NSNumber) ?? "0")
-                                Text("g/U").foregroundColor(.secondary)
-                            }
-                            HStack {
-                                Text("Sensitivity")
-                                Spacer()
-                                if state.units == .mmolL {
-                                    Text(isfFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
-                                } else {
-                                    Text(isfFormatter.string(from: autotune.sensitivity as NSNumber) ?? "0")
-                                }
-                                Text(state.units.rawValue + "/U").foregroundColor(.secondary)
-                            }
-                        }
-                        .listRowBackground(Color.chart)
-                    }
-
-                    Section(header: Text("Basal profile")) {
-                        ForEach(0 ..< autotune.basalProfile.count, id: \.self) { index in
-                            HStack {
-                                Text(autotune.basalProfile[index].start).foregroundColor(.secondary)
-                                Spacer()
-                                Text(rateFormatter.string(from: autotune.basalProfile[index].rate as NSNumber) ?? "0")
-                                Text("U/hr").foregroundColor(.secondary)
-                            }
-                        }
-                        HStack {
-                            Text("Total")
-                                .bold()
-                                .foregroundColor(.primary)
-                            Spacer()
-                            Text(rateFormatter.string(from: autotune.basalProfile.reduce(0) { $0 + $1.rate } as NSNumber) ?? "0")
-                                .foregroundColor(.primary) +
-                                Text(" U/day")
-                                .foregroundColor(.secondary)
-                        }
-                    }
-                    .listRowBackground(Color.chart)
-
-                    Section {
-                        Button {
-                            Task {
-                                await state.delete()
-                            }
-                        } label: {
-                            Text("Delete Autotune Data")
-                        }
-                        .frame(maxWidth: .infinity, alignment: .center)
-                        .listRowBackground(Color(.loopRed))
-                        .tint(.white)
-                    }
-
-                    Section {
-                        Button {
-                            replaceAlert = true
-                        } label: {
-                            Text("Save as Normal Basal Rates")
-                        }
-                        .frame(maxWidth: .infinity, alignment: .center)
-                        .listRowBackground(Color(.systemGray4))
-                        .tint(.white)
-                    }
-                }
-            }
-            .sheet(isPresented: $shouldDisplayHint) {
-                SettingInputHintView(
-                    hintDetent: $hintDetent,
-                    shouldDisplayHint: $shouldDisplayHint,
-                    hintLabel: hintLabel ?? "",
-                    hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
-                )
-            }
-            .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
-            .onAppear(perform: configureView)
-            .navigationTitle("Autotune")
-            .navigationBarTitleDisplayMode(.automatic)
-            .alert(Text("Are you sure?"), isPresented: $replaceAlert) {
-                Button("Yes", action: {
-                    state.replace()
-                    replaceAlert.toggle()
-                })
-                Button("No", action: { replaceAlert.toggle() })
-            }
-        }
-    }
-}

+ 0 - 1
FreeAPS/Sources/Modules/CarbRatioEditor/CarbRatioEditorDataFlow.swift

@@ -27,5 +27,4 @@ enum CarbRatioEditor {
 protocol CarbRatioEditorProvider: Provider {
     var profile: CarbRatios { get }
     func saveProfile(_ profile: CarbRatios)
-    var autotune: Autotune? { get }
 }

+ 0 - 4
FreeAPS/Sources/Modules/CarbRatioEditor/CarbRatioEditorProvider.swift

@@ -11,9 +11,5 @@ extension CarbRatioEditor {
         func saveProfile(_ profile: CarbRatios) {
             storage.save(profile, as: OpenAPS.Settings.carbRatios)
         }
-
-        var autotune: Autotune? {
-            storage.retrieve(OpenAPS.Settings.autotune, as: Autotune.self)
-        }
     }
 }

+ 0 - 3
FreeAPS/Sources/Modules/CarbRatioEditor/CarbRatioEditorStateModel.swift

@@ -5,7 +5,6 @@ extension CarbRatioEditor {
         @Injected() private var nightscout: NightscoutManager!
         @Published var items: [Item] = []
         @Published var initialItems: [Item] = []
-        @Published var autotune: Autotune?
         @Published var shouldDisplaySaving: Bool = false
 
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
@@ -39,8 +38,6 @@ extension CarbRatioEditor {
             }
 
             initialItems = items.map { Item(rateIndex: $0.rateIndex, timeIndex: $0.timeIndex) }
-
-            autotune = provider.autotune
         }
 
         func add() {

+ 0 - 11
FreeAPS/Sources/Modules/CarbRatioEditor/View/CarbRatioEditorRootView.swift

@@ -68,17 +68,6 @@ extension CarbRatioEditor {
 
         var body: some View {
             Form {
-                if let autotune = state.autotune, !state.settingsManager.settings.onlyAutotuneBasals {
-                    Section(header: Text("Autotune")) {
-                        HStack {
-                            Text("Calculated Ratio")
-                            Spacer()
-                            Text(rateFormatter.string(from: autotune.carbRatio as NSNumber) ?? "0")
-                            Text("g/U").foregroundColor(.secondary)
-                        }
-                    }.listRowBackground(Color.chart)
-                }
-
                 if !state.canAdd {
                     Section {
                         VStack(alignment: .leading) {

+ 1 - 1
FreeAPS/Sources/Modules/ContactImage/View/ContactImageHelpView.swift

@@ -62,7 +62,7 @@ struct ContactImageHelpView: View {
             .navigationBarTitle("Help", displayMode: .inline)
 
             Button { state.isHelpSheetPresented.toggle() }
-            label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
+            label: { Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center) }
                 .buttonStyle(.bordered)
                 .padding(.top)
         }

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

@@ -8,8 +8,7 @@ enum Home {
 protocol HomeProvider: Provider {
     func heartbeatNow()
     func pumpSettings() async -> PumpSettings
-    func autotunedBasalProfile() async -> [BasalProfileEntry]
-    func basalProfile() async -> [BasalProfileEntry]
+    func getBasalProfile() async -> [BasalProfileEntry]
     func tempTargets(hours: Int) -> [TempTarget]
     func pumpReservoir() async -> Decimal?
     func tempTarget() -> TempTarget?

+ 4 - 9
FreeAPS/Sources/Modules/Home/HomeProvider.swift

@@ -36,15 +36,10 @@ extension Home {
             await storage.retrieveAsync(OpenAPS.Monitor.reservoir, as: Decimal.self)
         }
 
-        func autotunedBasalProfile() async -> [BasalProfileEntry] {
-            await storage.retrieveAsync(OpenAPS.Settings.profile, as: Autotune.self)?.basalProfile
-                ?? storage.retrieve(OpenAPS.Settings.pumpProfile, as: Autotune.self)?.basalProfile
-                ?? [BasalProfileEntry(start: "00:00", minutes: 0, rate: 1)]
-        }
-
-        func basalProfile() async -> [BasalProfileEntry] {
-            await storage.retrieveAsync(OpenAPS.Settings.pumpProfile, as: Autotune.self)?.basalProfile
-                ?? [BasalProfileEntry(start: "00:00", minutes: 0, rate: 1)]
+        func getBasalProfile() async -> [BasalProfileEntry] {
+            await storage.retrieveAsync(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self)
+                ?? [BasalProfileEntry](from: OpenAPS.defaults(for: OpenAPS.Settings.basalProfile))
+                ?? []
         }
 
         func getBGTargets() async -> BGTargets {

+ 2 - 5
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -23,7 +23,6 @@ extension Home {
         var uploadStats = false
         var recentGlucose: BloodGlucose?
         var maxBasal: Decimal = 2
-        var autotunedBasalProfile: [BasalProfileEntry] = []
         var basalProfile: [BasalProfileEntry] = []
         var bgTargets = BGTargets(from: OpenAPS.defaults(for: OpenAPS.Settings.bgTargets))
             ?? BGTargets(units: .mgdL, userPreferredUnits: .mgdL, targets: [])
@@ -67,7 +66,7 @@ extension Home {
         var timeZone: TimeZone?
         var hours: Int16 = 6
         var totalBolus: Decimal = 0
-        var isStatusPopupPresented: Bool = false
+        var isLoopStatusPresented: Bool = false
         var isLegendPresented: Bool = false
         var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
         var roundedTotalBolus: String = ""
@@ -472,10 +471,8 @@ extension Home {
         }
 
         private func setupBasalProfile() async {
-            let autotunedBasalProfile = await provider.autotunedBasalProfile()
-            let basalProfile = await provider.basalProfile()
+            let basalProfile = await provider.getBasalProfile()
             await MainActor.run {
-                self.autotunedBasalProfile = autotunedBasalProfile
                 self.basalProfile = basalProfile
             }
         }

+ 1 - 2
FreeAPS/Sources/Modules/Home/View/Chart/ChartLegendView.swift

@@ -174,8 +174,7 @@ struct ChartLegendView: View {
                 Button {
                     state.isLegendPresented.toggle()
                 } label: {
-                    Text("Got it!")
-                        .frame(maxWidth: .infinity, alignment: .center)
+                    Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center)
                 }
                 .buttonStyle(.bordered)
                 .padding(.top)

+ 248 - 0
FreeAPS/Sources/Modules/Home/View/Header/LoopStatusHelpView.swift

@@ -0,0 +1,248 @@
+import SwiftUI
+
+struct LoopStatusHelpView: View {
+    @Environment(AppState.self) var appState
+    @Environment(\.colorScheme) var colorScheme
+
+    var state: Home.StateModel
+    var helpSheetDetent: Binding<PresentationDetent>
+    var isHelpSheetPresented: Binding<Bool>
+
+    var body: some View {
+        NavigationStack {
+            VStack(alignment: .leading) {
+                Text(
+                    "The oref algorithm provides recommendations, showing key variables, decisions on temporary basal rates or super-micro-boluses, and a 'reason' field explaining its actions. Find all key terms of this 'reason' explained below:"
+                )
+                .font(.subheadline)
+                .foregroundColor(.secondary)
+                .padding(.top, 50)
+
+                List {
+                    DefinitionRow(
+                        term: "Autosens Ratio",
+                        definition: Text(
+                            "The ratio of how sensitive or resistant to insulin you are in the current loop cycle. Baseline = 1.0, Sensitive < 1.0, Resistant > 1.0"
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "ISF",
+                        definition: Text(
+                            "The first value is your profile Insulin Sensitivity Factor (ISF). The second value, after the arrow, is your adjusted ISF used for the most recent automated dosing calculation."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "COB",
+                        definition: Text(
+                            "Amount of Carbs on Board (COB) used in the most recent automated dosing calculation."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Dev",
+                        definition: Text(
+                            "Abbreviation for 'Deviation'. How much the actual glucose change deviated from the BGI."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "BGI",
+                        definition: Text(
+                            "The degree to which your glucose should be rising or falling based solely on insulin activity."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "CR",
+                        definition: Text(
+                            "The first value is your profile Carb Ratio (CR). The second value, after the arrow, is your adjusted CR used for the most recent automated dosing calculation."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Target",
+                        definition: Text(
+                            "The first value is your target glucose from your settings. The second value, after the arrow, is your adjusted target glucose used for the most recent automated dosing calculation. A second value is shown if you have a temp target, override, or one of the Target Behavior options enabled."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "minPredBG",
+                        definition: Text(
+                            "The lowest forecasted value that Trio has estimated for your future glucose."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "minGuardBG",
+                        definition: Text(
+                            "The lowest forecasted glucose during the remaining duration of insulin action (DIA)."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "IOBpredBG",
+                        definition: Text(
+                            "The forecasted glucose value in 4 hours calculated based on IOB only."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "COBpredBG",
+                        definition: Text(
+                            "The forecasted glucose value in 4 hours calculated based on current IOB and COB."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "UAMpredBG",
+                        definition: Text(
+                            "The forecasted glucose value in 4 hours based on current deviations ramping down to zero at the same rate they have been recently."
+                        ),
+                        color: .insulin
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "TDD",
+                        definition: Text(
+                            "Abbreviation for 'Total Daily Dose'. Last 24 hours of total insulin administered, both basal and bolus."
+                        ),
+                        color: .zt
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Bolus/Basal %",
+                        definition: Text(
+                            "Of the total insulin delivered in the past 24 hours, this indicates what percentage was administered through basals and what was given through bolus."
+                        ),
+                        color: .green
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Dynamic ISF/CR",
+                        definition: Text(
+                            "A display of On/On indicates both Dynamic ISF and CR are enabled. On/Off indicates only Dynamic ISF is enabled. Dynamic CR cannot be enabled when Dynamic ISF is disabled."
+                        ),
+                        color: .zt
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Sigmoid function",
+                        definition: Text("If shown, Sigmoid Dynamic ISF is enabled."),
+                        color: .zt
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Logarithmic formula",
+                        definition: Text("If shown, Logarithmic Dynamic ISF is enabled."),
+                        color: .zt
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "AF",
+                        definition: Text(
+                            "Displays the Adjustment Factor (AF) for either Logathmic or Sigmoid Dynamic ISF in use."
+                        ),
+                        color: .zt
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "SMB Ratio",
+                        definition: Text(
+                            "SMB Delivery Ratio of calculated insulin required that is given as SMB."
+                        ),
+                        color: .zt
+                    ).listRowBackground(Color.gray.opacity(0.1))
+
+                    DefinitionRow(
+                        term: "Smoothing",
+                        definition: Text("Indicates glucose smoothing is enabled."),
+                        color: .gray
+                    ).listRowBackground(Color.gray.opacity(0.1))
+                }
+                .scrollContentBackground(.hidden)
+                .navigationBarTitle("Glossary", displayMode: .inline)
+                .padding(.bottom, 15)
+
+                Button {
+                    isHelpSheetPresented.wrappedValue.toggle()
+                } label: {
+                    Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center)
+                }
+                .buttonStyle(.bordered)
+                .padding(.top)
+            }
+            .padding([.horizontal, .bottom])
+            .listSectionSpacing(10)
+            .ignoresSafeArea(edges: .top)
+            .presentationDetents(
+                [.fraction(0.9), .large],
+                selection: helpSheetDetent
+            )
+        }
+    }
+
+    var legendLinesView: some View {
+        Group {
+            DefinitionRow(
+                term: "IOB (Insulin on Board)",
+                definition: Text(
+                    "Forecasts future glucose readings based on the amount of insulin still active in the body."
+                ),
+                color: .insulin
+            )
+
+            DefinitionRow(
+                term: "ZT (Zero-Temp)",
+                definition: Text(
+                    "Forecasts the worst-case future glucose reading scenario if no carbs are absorbed and insulin delivery is stopped until glucose starts rising."
+                ),
+                color: .zt
+            )
+
+            DefinitionRow(
+                term: "COB (Carbs on Board)",
+                definition: Text(
+                    "Forecasts future glucose reading changes by considering the amount of carbohydrates still being absorbed in the body."
+                ),
+                color: .loopYellow
+            )
+
+            DefinitionRow(
+                term: "UAM (Unannounced Meal)",
+                definition: Text(
+                    "Forecasts future glucose levels and insulin dosing needs for unexpected meals or other causes of glucose reading increases without prior notice."
+                ),
+                color: .uam
+            )
+        }
+    }
+
+    var legendConeOfUncertaintyView: some View {
+        DefinitionRow(
+            term: "Cone of Uncertainty",
+            definition: VStack(alignment: .leading, spacing: 10) {
+                Text(
+                    "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."
+                )
+                Text(
+                    "To modify how the forecast is displayed, go to Settings > Features > User Interface > Forecast Display Type."
+                )
+            },
+            color: Color.blue.opacity(0.5)
+        )
+    }
+}

+ 254 - 0
FreeAPS/Sources/Modules/Home/View/Header/LoopStatusView.swift

@@ -0,0 +1,254 @@
+import SwiftUI
+
+struct LoopStatusView: View {
+    @Environment(\.colorScheme) var colorScheme
+    @Environment(AppState.self) var appState
+
+    var state: Home.StateModel
+
+    @State var sheetDetent = PresentationDetent.fraction(0.8)
+    // Help Sheet
+    @State var isHelpSheetPresented: Bool = false
+    @State var helpSheetDetent = PresentationDetent.fraction(0.9)
+
+    @State private var statusTitle: String = ""
+
+    var body: some View {
+        NavigationStack {
+            VStack(alignment: .leading, spacing: 10) {
+                Text("Current Loop Status").bold().padding(.top, 20)
+
+                Text(statusTitle)
+                    .font(.headline)
+                    .bold()
+                    .padding(.horizontal, 12)
+                    .padding(.vertical, 6)
+                    .foregroundColor(statusBadgeTextColor)
+                    .background(statusBadgeColor)
+                    .clipShape(Capsule())
+
+                if let errorMessage = state.errorMessage, let date = state.errorDate {
+                    Group {
+                        Text("Error During Algorithm Run at \(Formatter.dateFormatter.string(from: date))").font(.headline)
+                        Text(errorMessage).font(.caption)
+                    }.foregroundColor(.loopRed)
+                }
+
+                if let determination = state.determinationsFromPersistence.first {
+                    if determination.glucose == 400 {
+                        Text("Invalid CGM reading (HIGH).")
+                            .bold()
+                            .padding(.top)
+                            .foregroundStyle(Color.loopRed)
+
+                        Text("SMBs and Non-Zero Temp. Basal Rates are disabled.")
+                            .font(.subheadline)
+
+                    } else {
+                        Text("Latest Raw Algorithm Output")
+                            .bold()
+                            .padding(.top)
+
+                        Text(
+                            "Trio is currently using these metrics and values as determined by the oref algorithm:"
+                        )
+                        .font(.subheadline)
+                        .lineLimit(nil)
+                        .multilineTextAlignment(.leading)
+                        .fixedSize(horizontal: false, vertical: true)
+
+                        let tags = !state.isSmoothingEnabled ? determination.reasonParts : determination
+                            .reasonParts + ["Smoothing: On"]
+                        TagCloudView(
+                            tags: tags,
+                            shouldParseToMmolL: state.units == .mmolL
+                        )
+
+                        Text("Current Algorithm Reasoning").bold().padding(.top)
+
+                        Text(
+                            self
+                                .parseReasonConclusion(
+                                    determination.reasonConclusion,
+                                    isMmolL: state.units == .mmolL
+                                )
+                        )
+                        .font(.subheadline)
+                        .lineLimit(nil)
+                        .multilineTextAlignment(.leading)
+                        .fixedSize(horizontal: false, vertical: true)
+                    }
+                } else {
+                    Text("No recent oref algorithm determination.")
+                }
+
+                Spacer()
+
+                Button {
+                    state.isLoopStatusPresented.toggle()
+                } label: {
+                    Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center)
+                }
+                .buttonStyle(.bordered)
+                .padding(.top)
+            }
+            .padding(.vertical)
+            .padding(.horizontal, 20)
+            .presentationDetents(
+                [.fraction(0.8), .large],
+                selection: $sheetDetent
+            )
+            .ignoresSafeArea(edges: .top)
+            .background(appState.trioBackgroundColor(for: colorScheme))
+            .toolbar(content: {
+                ToolbarItem(placement: .topBarTrailing) {
+                    Button(
+                        action: {
+                            isHelpSheetPresented.toggle()
+                        },
+                        label: {
+                            Image(systemName: "questionmark.circle")
+                        }
+                    )
+                }
+            })
+            .onAppear {
+                setStatusTitle()
+            }
+            .sheet(isPresented: $isHelpSheetPresented) {
+                LoopStatusHelpView(state: state, helpSheetDetent: $helpSheetDetent, isHelpSheetPresented: $isHelpSheetPresented)
+            }
+        }
+        .scrollContentBackground(.hidden)
+    }
+
+    private var statusBadgeColor: Color {
+        guard let determination = state.determinationsFromPersistence.first, determination.timestamp != nil
+        else {
+            // previously the .timestamp property was used here because this only gets updated when the reportenacted function in the aps manager gets called
+            return .secondary
+        }
+
+        let delta = state.timerDate.timeIntervalSince(state.lastLoopDate) - 30
+
+        if delta <= 5.minutes.timeInterval {
+            guard determination.timestamp != nil else {
+                return .loopYellow
+            }
+            return .loopGreen
+        } else if delta <= 10.minutes.timeInterval {
+            return .loopYellow
+        } else {
+            return .loopRed
+        }
+    }
+
+    private var statusBadgeTextColor: Color {
+        if statusBadgeColor == .secondary {
+            .black
+        } else {
+            colorScheme == .dark ? Color(red: 25.0 / 255.0, green: 39.0 / 255.0, blue: 53.0 / 255.0, opacity: 1.0) : .white
+        }
+    }
+
+    private func setStatusTitle() {
+        if let determination = state.determinationsFromPersistence.first {
+            statusTitle =
+                "Enacted at \(Formatter.dateFormatter.string(from: determination.deliverAt ?? Date()))"
+        } else {
+            statusTitle = "Not enacted."
+        }
+    }
+
+    // TODO: Consolidate all mmol parsing methods (in TagCloudView, NightscoutManager and HomeRootView) to one central func
+    private func parseReasonConclusion(_ reasonConclusion: String, isMmolL: Bool) -> String {
+        let patterns = [
+            "minGuardBG\\s*-?\\d+\\.?\\d*<-?\\d+\\.?\\d*", // minGuardBG x<y
+            "Eventual BG\\s*-?\\d+\\.?\\d*\\s*>=\\s*-?\\d+\\.?\\d*", // Eventual BG x >= target
+            "Eventual BG\\s*-?\\d+\\.?\\d*\\s*<\\s*-?\\d+\\.?\\d*", // Eventual BG x < target
+            "(\\S+)\\s+(-?\\d+\\.?\\d*)\\s*>\\s*(\\d+)%\\s+of\\s+BG\\s+(-?\\d+\\.?\\d*)" // maxDelta x > y% of BG z
+        ]
+        let pattern = patterns.joined(separator: "|")
+        let regex = try! NSRegularExpression(pattern: pattern)
+
+        func convertToMmolL(_ value: String) -> String {
+            if let glucoseValue = Double(value.replacingOccurrences(of: "[^\\d.-]", with: "", options: .regularExpression)) {
+                let mmolValue = Decimal(glucoseValue).asMmolL
+                return mmolValue.description
+            }
+            return value
+        }
+
+        let matches = regex.matches(
+            in: reasonConclusion,
+            range: NSRange(reasonConclusion.startIndex..., in: reasonConclusion)
+        )
+        var updatedConclusion = reasonConclusion
+
+        for match in matches.reversed() {
+            guard let range = Range(match.range, in: reasonConclusion) else { continue }
+            let matchedString = String(reasonConclusion[range])
+
+            if isMmolL {
+                if matchedString.contains("<"), matchedString.contains("Eventual BG"), !matchedString.contains("=") {
+                    // Handle "Eventual BG x < target" pattern
+                    let parts = matchedString.components(separatedBy: "<")
+                    if parts.count == 2 {
+                        let bgPart = parts[0].replacingOccurrences(of: "Eventual BG", with: "")
+                            .trimmingCharacters(in: .whitespaces)
+                        let targetValue = parts[1].trimmingCharacters(in: .whitespaces)
+                        let formattedBGPart = convertToMmolL(bgPart)
+                        let formattedTargetValue = convertToMmolL(targetValue)
+                        let formattedString = "Eventual BG \(formattedBGPart)<\(formattedTargetValue)"
+                        updatedConclusion.replaceSubrange(range, with: formattedString)
+                    }
+                } else if matchedString.contains("<"), matchedString.contains("minGuardBG") {
+                    // Handle "minGuardBG x<y" pattern
+                    let parts = matchedString.components(separatedBy: "<")
+                    if parts.count == 2 {
+                        let firstValue = parts[0].trimmingCharacters(in: .whitespaces)
+                        let secondValue = parts[1].trimmingCharacters(in: .whitespaces)
+                        let formattedFirstValue = convertToMmolL(firstValue)
+                        let formattedSecondValue = convertToMmolL(secondValue)
+                        let formattedString = "minGuardBG \(formattedFirstValue)<\(formattedSecondValue)"
+                        updatedConclusion.replaceSubrange(range, with: formattedString)
+                    }
+                } else if matchedString.contains(">=") {
+                    // Handle "Eventual BG x >= target" pattern
+                    let parts = matchedString.components(separatedBy: " >= ")
+                    if parts.count == 2 {
+                        let firstValue = parts[0].replacingOccurrences(of: "Eventual BG", with: "")
+                            .trimmingCharacters(in: .whitespaces)
+                        let secondValue = parts[1].trimmingCharacters(in: .whitespaces)
+                        let formattedFirstValue = convertToMmolL(firstValue)
+                        let formattedSecondValue = convertToMmolL(secondValue)
+                        let formattedString = "Eventual BG \(formattedFirstValue) >= \(formattedSecondValue)"
+                        updatedConclusion.replaceSubrange(range, with: formattedString)
+                    }
+                } else if let localMatch = regex.firstMatch(
+                    in: matchedString,
+                    range: NSRange(matchedString.startIndex..., in: matchedString)
+                ) {
+                    // Handle "maxDelta 37 > 20% of BG 95" style
+                    if match.numberOfRanges == 5 {
+                        let metric = String(matchedString[Range(localMatch.range(at: 1), in: matchedString)!])
+                        let firstValue = String(matchedString[Range(localMatch.range(at: 2), in: matchedString)!])
+                        let percentage = String(matchedString[Range(localMatch.range(at: 3), in: matchedString)!])
+                        let bgValue = String(matchedString[Range(localMatch.range(at: 4), in: matchedString)!])
+
+                        let formattedFirstValue = convertToMmolL(firstValue)
+                        let formattedBGValue = convertToMmolL(bgValue)
+
+                        let formattedString = "\(metric) \(formattedFirstValue) > \(percentage)% of BG \(formattedBGValue)"
+                        updatedConclusion.replaceSubrange(range, with: formattedString)
+                    }
+                }
+            } else {
+                // When isMmolL is false, ensure the original value is retained without duplication
+                updatedConclusion.replaceSubrange(range, with: matchedString)
+            }
+        }
+
+        return updatedConclusion.capitalizingFirstLetter()
+    }
+}

+ 7 - 170
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -33,7 +33,6 @@ extension Home {
         @State var isMenuPresented = false
         @State var showTreatments = false
         @State var selectedTab: Int = 0
-        @State private var statusTitle: String = ""
         @State var showPumpSelection: Bool = false
         @State var notificationsDisabled = false
         @State var timeButtons: [TimePicker] = [
@@ -326,8 +325,7 @@ extension Home {
                     manualTempBasal: state.manualTempBasal,
                     determination: state.determinationsFromPersistence
                 ).onTapGesture {
-                    state.isStatusPopupPresented = true
-                    setStatusTitle()
+                    state.isLoopStatusPresented = true
                 }.onLongPressGesture {
                     let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
                     impactHeavy.impactOccurred()
@@ -563,6 +561,8 @@ extension Home {
         }
 
         @ViewBuilder func adjustmentView(geo: GeometryProxy) -> some View {
+//            let background = colorScheme == .dark ? Material.ultraThinMaterial.opacity(0.5) : Color.black.opacity(0.2)
+
             ZStack {
                 /// rectangle as background
                 RoundedRectangle(cornerRadius: 15)
@@ -574,7 +574,7 @@ extension Home {
                                     Color.insulin.opacity(0.1)
                             ) : Color.clear // Use clear and add the Material in the background
                     )
-                    .background(.ultraThinMaterial.opacity(colorScheme == .dark ? 0.35 : 0))
+                    .background(colorScheme == .dark ? Color.chart.opacity(0.25) : Color.black.opacity(0.075))
                     .clipShape(RoundedRectangle(cornerRadius: 15))
                     .frame(height: geo.size.height * 0.08)
                     .shadow(
@@ -854,26 +854,9 @@ extension Home {
             .navigationTitle("Home")
             .navigationBarHidden(true)
             .ignoresSafeArea(.keyboard)
-            .popup(isPresented: state.isStatusPopupPresented, alignment: .top, direction: .top) {
-                popup
-                    .padding()
-                    .background(
-                        RoundedRectangle(cornerRadius: 8, style: .continuous)
-                            .fill(colorScheme == .dark ? Color(
-                                "Chart"
-                            ) : Color(UIColor.darkGray))
-                    )
-                    .onTapGesture {
-                        state.isStatusPopupPresented = false
-                    }
-                    .gesture(
-                        DragGesture(minimumDistance: 10, coordinateSpace: .local)
-                            .onEnded { value in
-                                if value.translation.height < 0 {
-                                    state.isStatusPopupPresented = false
-                                }
-                            }
-                    )
+            .blur(radius: state.isLoopStatusPresented ? 3 : 0)
+            .sheet(isPresented: $state.isLoopStatusPresented) {
+                LoopStatusView(state: state)
             }
             .confirmationDialog("Pump Model", isPresented: $showPumpSelection) {
                 Button("Medtronic") { state.addPump(.minimed) }
@@ -975,152 +958,6 @@ extension Home {
                 }
             }
         }
-
-        // TODO: Consolidate all mmol parsing methods (in TagCloudView, NightscoutManager and HomeRootView) to one central func
-        private func parseReasonConclusion(_ reasonConclusion: String, isMmolL: Bool) -> String {
-            let patterns = [
-                "minGuardBG\\s*-?\\d+\\.?\\d*<-?\\d+\\.?\\d*", // minGuardBG x<y
-                "Eventual BG\\s*-?\\d+\\.?\\d*\\s*>=\\s*-?\\d+\\.?\\d*", // Eventual BG x >= target
-                "Eventual BG\\s*-?\\d+\\.?\\d*\\s*<\\s*-?\\d+\\.?\\d*", // Eventual BG x < target
-                "(\\S+)\\s+(-?\\d+\\.?\\d*)\\s*>\\s*(\\d+)%\\s+of\\s+BG\\s+(-?\\d+\\.?\\d*)" // maxDelta x > y% of BG z
-            ]
-            let pattern = patterns.joined(separator: "|")
-            let regex = try! NSRegularExpression(pattern: pattern)
-
-            func convertToMmolL(_ value: String) -> String {
-                if let glucoseValue = Double(value.replacingOccurrences(of: "[^\\d.-]", with: "", options: .regularExpression)) {
-                    let mmolValue = Decimal(glucoseValue).asMmolL
-                    return mmolValue.description
-                }
-                return value
-            }
-
-            let matches = regex.matches(
-                in: reasonConclusion,
-                range: NSRange(reasonConclusion.startIndex..., in: reasonConclusion)
-            )
-            var updatedConclusion = reasonConclusion
-
-            for match in matches.reversed() {
-                guard let range = Range(match.range, in: reasonConclusion) else { continue }
-                let matchedString = String(reasonConclusion[range])
-
-                if isMmolL {
-                    if matchedString.contains("<"), matchedString.contains("Eventual BG"), !matchedString.contains("=") {
-                        // Handle "Eventual BG x < target" pattern
-                        let parts = matchedString.components(separatedBy: "<")
-                        if parts.count == 2 {
-                            let bgPart = parts[0].replacingOccurrences(of: "Eventual BG", with: "")
-                                .trimmingCharacters(in: .whitespaces)
-                            let targetValue = parts[1].trimmingCharacters(in: .whitespaces)
-                            let formattedBGPart = convertToMmolL(bgPart)
-                            let formattedTargetValue = convertToMmolL(targetValue)
-                            let formattedString = "Eventual BG \(formattedBGPart)<\(formattedTargetValue)"
-                            updatedConclusion.replaceSubrange(range, with: formattedString)
-                        }
-                    } else if matchedString.contains("<"), matchedString.contains("minGuardBG") {
-                        // Handle "minGuardBG x<y" pattern
-                        let parts = matchedString.components(separatedBy: "<")
-                        if parts.count == 2 {
-                            let firstValue = parts[0].trimmingCharacters(in: .whitespaces)
-                            let secondValue = parts[1].trimmingCharacters(in: .whitespaces)
-                            let formattedFirstValue = convertToMmolL(firstValue)
-                            let formattedSecondValue = convertToMmolL(secondValue)
-                            let formattedString = "minGuardBG \(formattedFirstValue)<\(formattedSecondValue)"
-                            updatedConclusion.replaceSubrange(range, with: formattedString)
-                        }
-                    } else if matchedString.contains(">=") {
-                        // Handle "Eventual BG x >= target" pattern
-                        let parts = matchedString.components(separatedBy: " >= ")
-                        if parts.count == 2 {
-                            let firstValue = parts[0].replacingOccurrences(of: "Eventual BG", with: "")
-                                .trimmingCharacters(in: .whitespaces)
-                            let secondValue = parts[1].trimmingCharacters(in: .whitespaces)
-                            let formattedFirstValue = convertToMmolL(firstValue)
-                            let formattedSecondValue = convertToMmolL(secondValue)
-                            let formattedString = "Eventual BG \(formattedFirstValue) >= \(formattedSecondValue)"
-                            updatedConclusion.replaceSubrange(range, with: formattedString)
-                        }
-                    } else if let localMatch = regex.firstMatch(
-                        in: matchedString,
-                        range: NSRange(matchedString.startIndex..., in: matchedString)
-                    ) {
-                        // Handle "maxDelta 37 > 20% of BG 95" style
-                        if match.numberOfRanges == 5 {
-                            let metric = String(matchedString[Range(localMatch.range(at: 1), in: matchedString)!])
-                            let firstValue = String(matchedString[Range(localMatch.range(at: 2), in: matchedString)!])
-                            let percentage = String(matchedString[Range(localMatch.range(at: 3), in: matchedString)!])
-                            let bgValue = String(matchedString[Range(localMatch.range(at: 4), in: matchedString)!])
-
-                            let formattedFirstValue = convertToMmolL(firstValue)
-                            let formattedBGValue = convertToMmolL(bgValue)
-
-                            let formattedString = "\(metric) \(formattedFirstValue) > \(percentage)% of BG \(formattedBGValue)"
-                            updatedConclusion.replaceSubrange(range, with: formattedString)
-                        }
-                    }
-                } else {
-                    // When isMmolL is false, ensure the original value is retained without duplication
-                    updatedConclusion.replaceSubrange(range, with: matchedString)
-                }
-            }
-
-            return updatedConclusion.capitalizingFirstLetter()
-        }
-
-        private var popup: some View {
-            VStack(alignment: .leading, spacing: 4) {
-                Text(statusTitle).font(.headline).foregroundColor(.white)
-                    .padding(.bottom, 4)
-                if let determination = state.determinationsFromPersistence.first {
-                    if determination.glucose == 400 {
-                        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)
-                    } else {
-                        let tags = !state.isSmoothingEnabled ? determination.reasonParts : determination
-                            .reasonParts + ["Smoothing: On"]
-                        TagCloudView(
-                            tags: tags,
-                            shouldParseToMmolL: state.units == .mmolL
-                        )
-                        .animation(.none, value: false)
-
-                        Text(
-                            self
-                                .parseReasonConclusion(
-                                    determination.reasonConclusion,
-                                    isMmolL: state.units == .mmolL
-                                )
-                        ).font(.caption).foregroundColor(.white)
-                    }
-                } else {
-                    Text("No determination found").font(.body).foregroundColor(.white)
-                }
-
-                if let errorMessage = state.errorMessage, let date = state.errorDate {
-                    Text(NSLocalizedString("Error at", comment: "") + " " + Formatter.dateFormatter.string(from: date))
-                        .foregroundColor(.white)
-                        .font(.headline)
-                        .padding(.bottom, 4)
-                        .padding(.top, 8)
-                    Text(errorMessage).font(.caption).foregroundColor(.loopRed)
-                }
-            }
-        }
-
-        private func setStatusTitle() {
-            if let determination = state.determinationsFromPersistence.first {
-                let dateFormatter = DateFormatter()
-                dateFormatter.timeStyle = .short
-                statusTitle = NSLocalizedString("Oref Determination enacted at", comment: "Headline in enacted pop up") +
-                    " " +
-                    dateFormatter
-                    .string(from: determination.deliverAt ?? Date())
-            } else {
-                statusTitle = "No Oref determination"
-                return
-            }
-        }
     }
 }
 

+ 0 - 1
FreeAPS/Sources/Modules/ISFEditor/ISFEditorDataFlow.swift

@@ -27,5 +27,4 @@ enum ISFEditor {
 protocol ISFEditorProvider: Provider {
     var profile: InsulinSensitivities { get }
     func saveProfile(_ profile: InsulinSensitivities)
-    var autotune: Autotune? { get }
 }

+ 0 - 4
FreeAPS/Sources/Modules/ISFEditor/ISFEditorProvider.swift

@@ -34,9 +34,5 @@ extension ISFEditor {
         func saveProfile(_ profile: InsulinSensitivities) {
             storage.save(profile, as: OpenAPS.Settings.insulinSensitivities)
         }
-
-        var autotune: Autotune? {
-            storage.retrieve(OpenAPS.Settings.autotune, as: Autotune.self)
-        }
     }
 }

+ 0 - 3
FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift

@@ -10,7 +10,6 @@ extension ISFEditor {
         var items: [Item] = []
         var initialItems: [Item] = []
         var shouldDisplaySaving: Bool = false
-        var autotune: Autotune?
 
         let context = CoreDataStack.shared.newTaskContext()
 
@@ -49,8 +48,6 @@ extension ISFEditor {
             }
 
             initialItems = items.map { Item(rateIndex: $0.rateIndex, timeIndex: $0.timeIndex) }
-
-            autotune = provider.autotune
         }
 
         func add() {

+ 0 - 15
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -61,21 +61,6 @@ extension ISFEditor {
 
         var body: some View {
             Form {
-                if let autotune = state.autotune, !state.settingsManager.settings.onlyAutotuneBasals {
-                    Section(header: Text("Autotune")) {
-                        HStack {
-                            Text("Calculated Sensitivity")
-                            Spacer()
-                            if state.units == .mgdL {
-                                Text(autotune.sensitivity.description)
-                            } else {
-                                Text(autotune.sensitivity.formattedAsMmolL)
-                            }
-                            Text(state.units.rawValue + "/U").foregroundColor(.secondary)
-                        }
-                    }.listRowBackground(Color.chart)
-                }
-
                 if !state.canAdd {
                     Section {
                         VStack(alignment: .leading) {

+ 1 - 1
FreeAPS/Sources/Modules/PumpConfig/PumpConfigDataFlow.swift

@@ -35,5 +35,5 @@ protocol PumpConfigProvider: Provider {
     func setPumpManager(_: PumpManagerUI)
     var pumpDisplayState: AnyPublisher<PumpDisplayState?, Never> { get }
     func pumpSettings() -> PumpSettings
-    func basalProfile() -> [BasalProfileEntry]
+    func getBasalProfile() async -> [BasalProfileEntry]
 }

+ 4 - 3
FreeAPS/Sources/Modules/PumpConfig/PumpConfigProvider.swift

@@ -14,9 +14,10 @@ extension PumpConfig {
             apsManager.pumpDisplayState.eraseToAnyPublisher()
         }
 
-        func basalProfile() -> [BasalProfileEntry] {
-            storage.retrieve(OpenAPS.Settings.pumpProfile, as: Autotune.self)?.basalProfile
-                ?? [BasalProfileEntry(start: "00:00", minutes: 0, rate: 1)]
+        func getBasalProfile() async -> [BasalProfileEntry] {
+            await storage.retrieveAsync(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self)
+                ?? [BasalProfileEntry](from: OpenAPS.defaults(for: OpenAPS.Settings.basalProfile))
+                ?? []
         }
 
         func pumpSettings() -> PumpSettings {

+ 15 - 11
FreeAPS/Sources/Modules/PumpConfig/PumpConfigStateModel.swift

@@ -23,19 +23,23 @@ extension PumpConfig {
                 .assign(to: \.alertNotAck, on: self)
                 .store(in: &lifetime)
 
-            let basalSchedule = BasalRateSchedule(
-                dailyItems: provider.basalProfile().map {
-                    RepeatingScheduleValue(startTime: $0.minutes.minutes.timeInterval, value: Double($0.rate))
-                }
-            )
+            Task {
+                let basalSchedule = BasalRateSchedule(
+                    dailyItems: await provider.getBasalProfile().map {
+                        RepeatingScheduleValue(startTime: $0.minutes.minutes.timeInterval, value: Double($0.rate))
+                    }
+                )
 
-            let pumpSettings = provider.pumpSettings()
+                let pumpSettings = provider.pumpSettings()
 
-            initialSettings = PumpInitialSettings(
-                maxBolusUnits: Double(pumpSettings.maxBolus),
-                maxBasalRateUnitsPerHour: Double(pumpSettings.maxBasal),
-                basalSchedule: basalSchedule!
-            )
+                await MainActor.run {
+                    initialSettings = PumpInitialSettings(
+                        maxBolusUnits: Double(pumpSettings.maxBolus),
+                        maxBasalRateUnitsPerHour: Double(pumpSettings.maxBasal),
+                        basalSchedule: basalSchedule!
+                    )
+                }
+            }
         }
 
         func addPump(_ type: PumpType) {

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

@@ -136,7 +136,6 @@ enum SettingItems {
                 "Unsuspend If No Temp",
                 "Suspend Zeros IOB",
                 "Min 5m Carbimpact",
-                // "Autotune ISF Adjustment Fraction",
                 "Remaining Carbs Fraction",
                 "Remaining Carbs Cap",
                 "Noisy CGM Target Multiplier"
@@ -212,7 +211,6 @@ enum SettingItems {
             path: ["Features", "User Interface"]
         ),
         SettingItem(title: "App Icons", view: .iconConfig)
-        // SettingItem(title: "Autotune", view: .autotuneConfig)
     ]
 
     static let notificationItems = [

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

@@ -260,8 +260,6 @@ extension Settings {
 //                                .navigationLink(to: .configEditor(file: OpenAPS.Settings.profile), from: self)
 //                            //                            Text("Carbs")
 //                            //                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.carbHistory), from: self)
-//                            Text("Autotune")
-//                                .navigationLink(to: .configEditor(file: OpenAPS.Settings.autotune), from: self)
 //                        }
 //
 //                        Group {

+ 0 - 8
FreeAPS/Sources/Modules/Settings/View/Subviews/FeatureSettingsView.swift

@@ -37,14 +37,6 @@ struct FeatureSettingsView: BaseView {
                 }
             )
             .listRowBackground(Color.chart)
-
-            // Section(
-            // header: Text("Data-Driven Settings Tuning"),
-            // content: {
-            // Text("Autotune").navigationLink(to: .autotuneConfig, from: self)
-            // }
-            // )
-            .listRowBackground(Color.chart)
         }
         .scrollContentBackground(.hidden)
         .background(appState.trioBackgroundColor(for: colorScheme))

+ 1 - 1
FreeAPS/Sources/Modules/Treatments/View/PopupView.swift

@@ -101,7 +101,7 @@ struct PopupView: View {
                 Spacer()
 
                 Button { state.showInfo = false }
-                label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
+                label: { Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center) }
                     .buttonStyle(.bordered)
                     .padding(.top)
             }

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

@@ -17,7 +17,6 @@ enum Screen: Identifiable, Hashable {
     case targetsEditor
     case bolus
     case manualTempBasal
-    case autotuneConfig
     case dataTable
     case cgm
     case cgmDirect
@@ -87,8 +86,6 @@ extension Screen {
             Treatments.RootView(resolver: resolver)
         case .manualTempBasal:
             ManualTempBasal.RootView(resolver: resolver)
-        case .autotuneConfig:
-            AutotuneConfig.RootView(resolver: resolver)
         case .dataTable:
             DataTable.RootView(resolver: resolver)
         case .cgm:

+ 1 - 2
FreeAPS/Sources/Views/SettingInputHintView.swift

@@ -25,8 +25,7 @@ struct SettingInputHintView<HintView: View>: View {
             Button {
                 shouldDisplayHint.toggle()
             } label: {
-                Text("Got it!")
-                    .frame(maxWidth: .infinity, alignment: .center)
+                Text("Got it!").bold().frame(maxWidth: .infinity, minHeight: 30, alignment: .center)
             }
             .buttonStyle(.bordered)
             .padding(.top)

+ 0 - 2
FreeAPS/Sources/Views/SettingInputSection.swift

@@ -135,8 +135,6 @@ struct SettingInputSection<VerboseHint: View>: View {
             return pickerSettingsProvider.settings.maxCOB
         case "min5mCarbimpact":
             return pickerSettingsProvider.settings.min5mCarbimpact
-        case "autotuneISFAdjustmentFraction":
-            return pickerSettingsProvider.settings.autotuneISFAdjustmentFraction
         case "remainingCarbsFraction":
             return pickerSettingsProvider.settings.remainingCarbsFraction
         case "remainingCarbsCap":

+ 25 - 16
FreeAPS/Sources/Views/TagCloudView.swift

@@ -7,7 +7,10 @@ struct TagCloudView: View {
     var tags: [String]
     var shouldParseToMmolL: Bool
 
-    @State private var totalHeight = CGFloat.infinity // << variant for VStack
+    @Environment(\.colorScheme) var colorScheme
+
+    @State private var totalHeight = CGFloat.zero // << variant for ScrollView/List
+//    = CGFloat.infinity // << variant for VStack
 
     var body: some View {
         VStack {
@@ -15,27 +18,28 @@ struct TagCloudView: View {
                 self.generateContent(in: geometry)
             }
         }
-        .frame(maxHeight: totalHeight) // << variant for VStack
+        .frame(height: totalHeight) // << variant for ScrollView/List
+//        .frame(maxHeight: totalHeight) // << variant for VStack
     }
 
-    private func generateContent(in g: GeometryProxy) -> some View {
+    private func generateContent(in geometry: GeometryProxy) -> some View {
         var width = CGFloat.zero
         var height = CGFloat.zero
 
         return ZStack(alignment: .topLeading) {
             ForEach(self.tags, id: \.self) { tag in
-                self.item(for: tag, isMmolL: shouldParseToMmolL)
-                    .padding([.horizontal, .vertical], 2)
-                    .alignmentGuide(.leading, computeValue: { d in
-                        if abs(width - d.width) > g.size.width {
+                self.drawTag(for: tag, isMmolL: shouldParseToMmolL)
+                    .padding([.horizontal, .vertical], 3)
+                    .alignmentGuide(.leading, computeValue: { dimensions in
+                        if abs(width - dimensions.width) > geometry.size.width {
                             width = 0
-                            height -= d.height
+                            height -= dimensions.height
                         }
                         let result = width
                         if tag == self.tags.last! {
                             width = 0 // last item
                         } else {
-                            width -= d.width
+                            width -= dimensions.width
                         }
                         return result
                     })
@@ -50,7 +54,7 @@ struct TagCloudView: View {
         }.background(viewHeightReader($totalHeight))
     }
 
-    private func item(for textTag: String, isMmolL: Bool) -> some View {
+    private func drawTag(for textTag: String, isMmolL: Bool) -> some View {
         var colorOfTag: Color {
             switch textTag {
             case textTag where textTag.contains("SMB Delivery Ratio:"):
@@ -72,7 +76,7 @@ struct TagCloudView: View {
             case textTag where textTag.contains("SMB Ratio"):
                 return .orange
             case textTag where textTag.contains("Smoothing: On"):
-                return .white
+                return .gray
             default:
                 return .insulin
             }
@@ -82,12 +86,17 @@ struct TagCloudView: View {
 
         return ZStack {
             Text(formattedTextTag)
-                .padding(.vertical, 2)
-                .padding(.horizontal, 4)
+                .padding(.horizontal, 10)
+                .padding(.vertical, 5)
                 .font(.subheadline)
-                .background(colorOfTag.opacity(0.8))
-                .foregroundColor(textTag.contains("Smoothing: On") ? Color.black : Color.white)
-                .cornerRadius(2)
+                .fontWeight(.semibold)
+                .background(colorOfTag.opacity(colorScheme == .dark ? 0.15 : 0.25))
+                .foregroundColor(colorOfTag)
+                .clipShape(Capsule())
+                .overlay(
+                    Capsule()
+                        .stroke(colorOfTag.opacity(0.4), lineWidth: 2)
+                )
         }
     }