Selaa lähdekoodia

iAPS 1.1.2 (#1)

* Put back DASH strings deleted in Loop3 branch

* Reverts oref0 commit. Scott Leibrand.
Revert "fix(lib/iob): Move value checks up to index.js"

* Adjust default AF

* Update the calculation of basal insulin for Apple Health with the same formula as TDD (#615)

* Allow to delete Carbs when connexion with NS is impossible - Alert the user of the issue.

* synchronise upload readings toggle in dexcom settings with FAX settings

* Update the calculation of basal insulin for Apple Health with the same formula as TDD

* Default 0.05 U for TDD in oref0

* Meal presets

Concurrency fixes

* add a optional filter of BG EXPERIMENTAL + correction of mininmalDose  (#616)

* add a optional filter of BG - Based on the https://en.wikipedia.org/wiki/Savitzky–Golay_filter developed by xDrip4IOS.

* add unsmoothed BG visualisation in the graph (for testing the smooth algorithm).

* add storage of BG in SVG for GlucoseSimulatorSource

* For AH, same minimalDose than TDD (0.05)

* Round carb equivalents

* Equivalent round fix

* Meal Presets.
Only enable save preset when entered macros are not already save as a preset.
Change colour of Delete Preset and Fat and Protein to be more clear.
Clean up of SwiftUI code.

* Normal font weight makes is more clear in light mode.

* Clean out test files and group

* Add source strings of smoothing

* Crowdin updates

* Remove oref0 test variable

* Add portion button and confirm deletion of preset. Add "-" portion

* New colours

* Simplify Temp Targets

* Bump version

* Improve layout of Meal presets.
Added a light blue colour.

* Use Half Basal Target as a variable in new simplified Temp Targets

* Localization

Co-authored-by: Pierre L <pn.lagarde@gmail.com>
Jon B Mårtensson 3 vuotta sitten
vanhempi
commit
12cb00f0fd
69 muutettua tiedostoa jossa 1288 lisäystä ja 478 poistoa
  1. 0 4
      BGaverages+CoreDataClass.swift
  2. 0 15
      BGaverages+CoreDataProperties.swift
  3. 0 4
      BGmedian+CoreDataClass.swift
  4. 0 15
      BGmedian+CoreDataProperties.swift
  5. 0 4
      Carbohydrates+CoreDataClass.swift
  6. 0 12
      Carbohydrates+CoreDataProperties.swift
  7. 2 2
      Config.xcconfig
  8. 6 0
      Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents
  9. 1 1
      Dependencies/G7SensorKit/nl.lproj/Localizable.strings
  10. 33 33
      Dependencies/G7SensorKit/tr.lproj/Localizable.strings
  11. 4 0
      FreeAPS.xcodeproj/project.pbxproj
  12. 0 61
      FreeAPS.xcworkspace/contents.xcworkspacedata
  13. 38 0
      FreeAPS/Resources/Assets.xcassets/Colors/minus.colorset/Contents.json
  14. 1 1
      FreeAPS/Resources/javascript/bundle/determine-basal.js
  15. 1 1
      FreeAPS/Resources/javascript/bundle/profile.js
  16. 29 33
      FreeAPS/Sources/APS/APSManager.swift
  17. 2 2
      FreeAPS/Sources/APS/CGM/GlucoseSimulatorSource.swift
  18. 15 0
      FreeAPS/Sources/APS/FetchGlucoseManager.swift
  19. 6 0
      FreeAPS/Sources/Application/FreeAPSApp.swift
  20. 1 0
      FreeAPS/Sources/Helpers/Color+Extensions.swift
  21. 6 2
      FreeAPS/Sources/Helpers/CoreDataStack.swift
  22. 172 0
      FreeAPS/Sources/Helpers/SavitzkyGolayFilter.swift
  23. 27 1
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  24. 27 1
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  25. 27 1
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  26. 28 1
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  27. 61 1
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  28. 27 1
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  29. 27 1
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  30. 27 1
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  31. 27 1
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  32. 27 1
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  33. 28 2
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  34. 25 5
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  35. 27 1
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  36. 27 1
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  37. 27 1
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  38. 27 1
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  39. 27 1
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  40. 63 1
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  41. 27 1
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  42. 27 1
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  43. 27 1
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  44. 11 0
      FreeAPS/Sources/Models/BloodGlucose.swift
  45. 5 0
      FreeAPS/Sources/Models/FreeAPSSettings.swift
  46. 2 4
      FreeAPS/Sources/Models/Preferences.swift
  47. 21 0
      FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift
  48. 154 32
      FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
  49. 11 18
      FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift
  50. 50 12
      FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift
  51. 3 0
      FreeAPS/Sources/Modules/CGM/CGMStateModel.swift
  52. 4 0
      FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift
  53. 3 0
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  54. 48 0
      FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift
  55. 2 1
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  56. 1 61
      FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift
  57. 19 2
      FreeAPS/Sources/Services/HealthKit/HealthKitManager.swift
  58. 0 4
      HbA1c+CoreDataClass.swift
  59. 0 15
      HbA1c+CoreDataProperties.swift
  60. 0 4
      InsulinDistribution+CoreDataClass.swift
  61. 0 14
      InsulinDistribution+CoreDataProperties.swift
  62. 0 4
      LoopStatRecord+CoreDataClass.swift
  63. 0 13
      LoopStatRecord+CoreDataProperties.swift
  64. 0 4
      Oref0Suggestion+CoreDataClass.swift
  65. 0 43
      Oref0Suggestion+CoreDataProperties.swift
  66. 0 4
      Readings+CoreDataClass.swift
  67. 0 11
      Readings+CoreDataProperties.swift
  68. 0 4
      TDD+CoreDataClass.swift
  69. 0 12
      TDD+CoreDataProperties.swift

+ 0 - 4
BGaverages+CoreDataClass.swift

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

+ 0 - 15
BGaverages+CoreDataProperties.swift

@@ -1,15 +0,0 @@
-import CoreData
-import Foundation
-
-public extension BGaverages {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<BGaverages> {
-        NSFetchRequest<BGaverages>(entityName: "BGaverages")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var average: NSDecimalNumber?
-    @NSManaged var average_1: NSDecimalNumber?
-    @NSManaged var average_7: NSDecimalNumber?
-    @NSManaged var average_30: NSDecimalNumber?
-    @NSManaged var average_90: NSDecimalNumber?
-}

+ 0 - 4
BGmedian+CoreDataClass.swift

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

+ 0 - 15
BGmedian+CoreDataProperties.swift

@@ -1,15 +0,0 @@
-import CoreData
-import Foundation
-
-public extension BGmedian {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<BGmedian> {
-        NSFetchRequest<BGmedian>(entityName: "BGmedian")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var median: NSDecimalNumber?
-    @NSManaged var median_1: NSDecimalNumber?
-    @NSManaged var median_30: NSDecimalNumber?
-    @NSManaged var median_90: NSDecimalNumber?
-    @NSManaged var median_7: NSDecimalNumber?
-}

+ 0 - 4
Carbohydrates+CoreDataClass.swift

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

+ 0 - 12
Carbohydrates+CoreDataProperties.swift

@@ -1,12 +0,0 @@
-import CoreData
-import Foundation
-
-public extension Carbohydrates {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<Carbohydrates> {
-        NSFetchRequest<Carbohydrates>(entityName: "Carbohydrates")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var carbs: NSDecimalNumber?
-    @NSManaged var enteredBy: String?
-}

+ 2 - 2
Config.xcconfig

@@ -1,5 +1,5 @@
-APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 1.0.9
+APP_DISPLAY_NAME = iAPS
+APP_VERSION = 1.1.2
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 DEVELOPER_TEAM = ##TEAM_ID##

+ 6 - 0
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -46,6 +46,12 @@
         <relationship name="computedInsulinDistribution" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="InsulinDistribution" inverseName="insulin" inverseEntity="InsulinDistribution"/>
         <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
     </entity>
+    <entity name="Presets" representedClassName="Presets" syncable="YES" codeGenerationType="class">
+        <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="dish" optional="YES" attributeType="String"/>
+        <attribute name="fat" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="protein" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
     <entity name="Readings" representedClassName="Readings" syncable="YES" codeGenerationType="class">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>

+ 1 - 1
Dependencies/G7SensorKit/nl.lproj/Localizable.strings

@@ -90,7 +90,7 @@
 "%@/min" = "%@/min";
 
 /* G7 Progress bar label when searching for sensor */
-"Searching for sensor" = "Senor aan het zoeken";
+"Searching for sensor" = "Sensor aan het zoeken";
 
 /* G7 Progress bar label when sensor expired */
 "Sensor expired" = "Sensor verlopen";

+ 33 - 33
Dependencies/G7SensorKit/tr.lproj/Localizable.strings

@@ -2,7 +2,7 @@
 "Dexcom G7" = "Dexcom G7";
 
 /* Descriptive text on G7StartupView */
-"FreeAPS X can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "FreeAPS X can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management.";
+"FreeAPS X can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "FreeAPS X, G7 CGM verilerini okuyabilir ancak yine de eşleştirme, kalibrasyon ve diğer sensör yönetimi için Dexcom G7 Uygulamasını kullanmanız gerekir.";
 
 /* Button title for starting setup */
 "Continue" = "Devam et";
@@ -11,43 +11,43 @@
 "Cancel" = "Vazgeç";
 
 /* Error description for unreliable state */
-"Glucose data is unavailable" = "Glucose data is unavailable";
+"Glucose data is unavailable" = "Glikoz verileri kullanılamıyor";
 
 /* The description of sensor algorithm state when sensor is ok. */
-"Sensor is OK" = "Sensor is OK";
+"Sensor is OK" = "Sensör TAMAM";
 
 /* The description of sensor algorithm state when sensor is stopped." */
-"Sensor is stopped" = "Sensor is stopped";
+"Sensor is stopped" = "Sensör durduruldu";
 
 /* The description of sensor algorithm state when sensor is warming up. */
-"Sensor is warming up" = "Sensor is warming up";
+"Sensor is warming up" = "Sensör ısınıyor";
 
 /* The description of sensor algorithm state when sensor is expired. */
-"Sensor expired" = "Sensor expired";
+"Sensor expired" = "Sensör süresi doldu";
 
 /* The description of sensor algorithm state when sensor failed. */
-"Sensor failed" = "Sensor failed";
+"Sensor failed" = "Sensör arızalı";
 
 /* The description of sensor algorithm state when raw value is unknown. (1: missing data details) */
-"Sensor is in unknown state %1$d" = "Sensor is in unknown state %1$d";
+"Sensor is in unknown state %1$d" = "Sensör bilinmeyen durumda %1$d";
 
 /* title for g7 settings row showing sensor start time */
-"Sensor Start" = "Sensor Start";
+"Sensor Start" = "Sensörü Başlat";
 
 /* title for g7 settings row showing sensor expiration time */
-"Sensor Expiration" = "Sensor Expiration";
+"Sensor Expiration" = "Sensör Süre Sonu";
 
 /* title for g7 settings row showing sensor grace period end time */
-"Grace Period End" = "Grace Period End";
+"Grace Period End" = "Yetkisiz Kullanım Sonu";
 
 /* Field label */
 "Glucose" = "Glikoz";
 
-"Last Reading" = "Last Reading";
+"Last Reading" = "Son Okuma Değeri";
 
 "Time" = "Saat";
 
-"Trend" = "Trend";
+"Trend" = "Eğilim";
 
 "Bluetooth" = "Bluetooth";
 
@@ -55,7 +55,7 @@
 "Name" = "İsim";
 
 /* title for g7 settings connection status when scanning */
-"Scanning" = "Scanning";
+"Scanning" = "Taranıyor";
 
 /* title for g7 settings connection status when connected */
 "Connected" = "Bağlandı";
@@ -64,16 +64,16 @@
 "Connecting" = "Bağlanıyor";
 
 /* title for g7 settings row showing sensor last connect time */
-"Last Connect" = "Last Connect";
+"Last Connect" = "Son Bağlantı";
 
 /* Configuration */
 "Configuration" = "Yapılandırma";
 
 /* title for g7 config settings to upload readings */
-"Upload Readings" = "Upload Readings";
+"Upload Readings" = "Okumaları Yükle";
 
 /* Button */
-"Scan for new sensor" = "Scan for new sensor";
+"Scan for new sensor" = "Yeni sensör için tara";
 
 /* Button label for removing CGM */
 "Delete CGM" = "CGM'i Sil";
@@ -81,49 +81,49 @@
 /* No glucose value representation (3 dashes for mg/dL) */
 "– – –" = "– – –";
 /* String displayed instead of a glucose value below the CGM range */
-"LOW" = "LOW";
+"LOW" = "DÜŞÜK";
 
 /* String displayed instead of a glucose value above the CGM range */
-"HIGH" = "HIGH";
+"HIGH" = "YÜKSEK";
 
 /* Format string for glucose trend per minute. (1: glucose value and unit) */
-"%@/min" = "%@/min";
+"%@/min" = "%@/dak";
 
 /* G7 Progress bar label when searching for sensor */
-"Searching for sensor" = "Searching for sensor";
+"Searching for sensor" = "Sensör aranıyor";
 
 /* G7 Progress bar label when sensor expired */
-"Sensor expired" = "Sensor expired";
+"Sensor expired" = "Sensör süresi doldu";
 
 /* G7 Progress bar label when sensor in warmup */
-"Warmup completes" = "Warmup completes";
+"Warmup completes" = "Isınma tamamlandı";
 
 /* G7 Progress bar label when sensor in warmup */
-"Warmup completes" = "Warmup completes";
+"Warmup completes" = "Isınma tamamlandı";
 
 /* G7 Progress bar label when sensor failed */
-"Sensor failed" = "Sensor failed";
+"Sensor failed" = "Sensör arızalı";
 
 /* G7 Progress bar label when sensor lifetime progress showing */
-"Sensor expires" = "Sensor expires";
+"Sensor expires" = "Sensör süresi doluyor";
 
 /* G7 Progress bar label when sensor grace period progress showing */
-"Grace period remaining" = "Grace period remaining";
+"Grace period remaining" = "Kalan ek süre";
 
 /* G7 Status highlight text for searching for sensor */
-"Searching for\nSensor" = "Searching for\nSensor";
+"Searching for\nSensor" = "Sensör\nAranıyor";
 
 /* G7 Status highlight text for sensor expired */
-"Sensor\nExpired" = "Sensor\nExpired";
+"Sensor\nExpired" = "Sensör\nSüresi Doldu";
 
 /* G7 Status highlight text for signal loss */
-"Sensor\nFailed" = "Sensor\nFailed";
+"Sensor\nFailed" = "Sensör\nArızalı";
 
 /* G7 Status highlight text for signal loss */
-"Signal\nLoss" = "Signal\nLoss";
+"Signal\nLoss" = "Sinyal\nKaybı";
 
 /*G7 Status highlight text for sensor error */
-"Sensor\nIssue" = "Sensor\nIssue";
+"Sensor\nIssue" = "Sensör\nSorunu";
 
 /* G7 Status highlight text for sensor warmup */
-"Sensor\nWarmup" = "Sensor\nWarmup";
+"Sensor\nWarmup" = "Sensör\nIsınıyor";

+ 4 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -308,6 +308,7 @@
 		CE79502F29980E5800FA576E /* ShareClientUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CE79502D29980E4D00FA576E /* ShareClientUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		CE82E02528E867BA00473A9C /* AlertStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE82E02428E867BA00473A9C /* AlertStorage.swift */; };
 		CE82E02728E869DF00473A9C /* AlertEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE82E02628E869DF00473A9C /* AlertEntry.swift */; };
+		CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA4F62229BE10F70011ADF7 /* SavitzkyGolayFilter.swift */; };
 		CEB434DC28B8F5B900B70274 /* MKRingProgressView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEB434DB28B8F5B900B70274 /* MKRingProgressView.framework */; };
 		CEB434DD28B8F5B900B70274 /* MKRingProgressView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEB434DB28B8F5B900B70274 /* MKRingProgressView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		CEB434DF28B8F5C400B70274 /* OmniBLE.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEB434DE28B8F5C400B70274 /* OmniBLE.framework */; };
@@ -766,6 +767,7 @@
 		CE79502D29980E4D00FA576E /* ShareClientUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClientUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		CE82E02428E867BA00473A9C /* AlertStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertStorage.swift; sourceTree = "<group>"; };
 		CE82E02628E869DF00473A9C /* AlertEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertEntry.swift; sourceTree = "<group>"; };
+		CEA4F62229BE10F70011ADF7 /* SavitzkyGolayFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavitzkyGolayFilter.swift; sourceTree = "<group>"; };
 		CEB434DB28B8F5B900B70274 /* MKRingProgressView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MKRingProgressView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		CEB434DE28B8F5C400B70274 /* OmniBLE.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OmniBLE.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		CEB434E228B8F9DB00B70274 /* BluetoothStateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothStateManager.swift; sourceTree = "<group>"; };
@@ -1451,6 +1453,7 @@
 				CEB434E428B8FF5D00B70274 /* UIColor.swift */,
 				FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */,
 				FEFFA7A12929FE49007B8193 /* UIDevice+Extensions.swift */,
+				CEA4F62229BE10F70011ADF7 /* SavitzkyGolayFilter.swift */,
 			);
 			path = Helpers;
 			sourceTree = "<group>";
@@ -2271,6 +2274,7 @@
 				38C4D33725E9A1A300D30B77 /* DispatchQueue+Extensions.swift in Sources */,
 				F90692CF274B999A0037068D /* HealthKitDataFlow.swift in Sources */,
 				3862CC2E2743F9F700BF832C /* CalendarManager.swift in Sources */,
+				CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */,
 				38B4F3C325E2A20B00E76A18 /* PumpSetupView.swift in Sources */,
 				38E4453C274E411700EC9A94 /* Disk+Codable.swift in Sources */,
 				382C134B25F14E3700715CE1 /* BGTargets.swift in Sources */,

+ 0 - 61
FreeAPS.xcworkspace/contents.xcworkspacedata

@@ -1,67 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Workspace
    version = "1.0">
-   <Group
-      location = "container:"
-      name = "CoreDataClassesAndProperties">
-      <FileRef
-         location = "group:InsulinDistribution+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:InsulinDistribution+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:Oref0Suggestion+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:Oref0Suggestion+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:HbA1c+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:HbA1c+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:BGmedian+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:BGmedian+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:BGaverages+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:BGaverages+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:Carbohydrates+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:Carbohydrates+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:LoopStatRecord+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:LoopStatRecord+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:Readings+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:Readings+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:TDD+CoreDataClass.swift">
-      </FileRef>
-      <FileRef
-         location = "group:TDD+CoreDataProperties.swift">
-      </FileRef>
-      <FileRef
-         location = "group:FreeAPS/Sources/Helpers/CoreDataStack.swift">
-      </FileRef>
-   </Group>
    <FileRef
       location = "group:FreeAPS.xcodeproj">
    </FileRef>

+ 38 - 0
FreeAPS/Resources/Assets.xcassets/Colors/minus.colorset/Contents.json

@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.976",
+          "green" : "0.839",
+          "red" : "0.635"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.976",
+          "green" : "0.839",
+          "red" : "0.635"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
FreeAPS/Resources/javascript/bundle/profile.js


+ 29 - 33
FreeAPS/Sources/APS/APSManager.swift

@@ -974,7 +974,7 @@ final class BaseAPSManager: APSManager, Injectable {
             minimumLoopTime = 0.0
         }
 
-        var glucose: [Readings] = []
+        var glucose = [Readings]()
 
         var firstElementTime = Date()
         var lastElementTime = Date()
@@ -1163,16 +1163,19 @@ final class BaseAPSManager: APSManager, Injectable {
 
         // MARK: Save to Median to CoreData
 
-        let saveMedianToCoreData = BGmedian(context: coredataContext)
-
-        saveMedianToCoreData.date = Date()
-        saveMedianToCoreData.median = median.total as NSDecimalNumber
-        saveMedianToCoreData.median_1 = median.day as NSDecimalNumber
-        saveMedianToCoreData.median_7 = median.week as NSDecimalNumber
-        saveMedianToCoreData.median_30 = median.month as NSDecimalNumber
-        saveMedianToCoreData.median_90 = roundDecimal(Decimal(medianCalculation(array: bgArray_90_)), 1) as NSDecimalNumber
-
         coredataContext.perform {
+            let saveMedianToCoreData = BGmedian(context: self.coredataContext)
+
+            saveMedianToCoreData.date = Date()
+            saveMedianToCoreData.median = median.total as NSDecimalNumber
+            saveMedianToCoreData.median_1 = median.day as NSDecimalNumber
+            saveMedianToCoreData.median_7 = median.week as NSDecimalNumber
+            saveMedianToCoreData.median_30 = median.month as NSDecimalNumber
+            saveMedianToCoreData.median_90 = self.roundDecimal(
+                Decimal(self.medianCalculation(array: bgArray_90_)),
+                1
+            ) as NSDecimalNumber
+
             try? self.coredataContext.save()
         }
 
@@ -1183,17 +1186,15 @@ final class BaseAPSManager: APSManager, Injectable {
             total: roundDecimal(NGSPa1CStatisticValue_total, 1)
         )
 
-        let saveHbA1c = HbA1c(context: coredataContext)
-        saveHbA1c.date = Date()
-        saveHbA1c.hba1c = NGSPa1CStatisticValue_total as NSDecimalNumber
-        saveHbA1c.hba1c_1 = NGSPa1CStatisticValue as NSDecimalNumber
-        saveHbA1c.hba1c_7 = NGSPa1CStatisticValue_7 as NSDecimalNumber
-        saveHbA1c.hba1c_30 = NGSPa1CStatisticValue_30 as NSDecimalNumber
-        saveHbA1c.hba1c_90 = NGSPa1CStatisticValue_90 as NSDecimalNumber
-
-        // MARK: Save to HbA1c to CoreData
-
         coredataContext.perform {
+            let saveHbA1c = HbA1c(context: self.coredataContext)
+            saveHbA1c.date = Date()
+            saveHbA1c.hba1c = NGSPa1CStatisticValue_total as NSDecimalNumber
+            saveHbA1c.hba1c_1 = NGSPa1CStatisticValue as NSDecimalNumber
+            saveHbA1c.hba1c_7 = NGSPa1CStatisticValue_7 as NSDecimalNumber
+            saveHbA1c.hba1c_30 = NGSPa1CStatisticValue_30 as NSDecimalNumber
+            saveHbA1c.hba1c_90 = NGSPa1CStatisticValue_90 as NSDecimalNumber
+
             try? self.coredataContext.save()
         }
 
@@ -1279,24 +1280,19 @@ final class BaseAPSManager: APSManager, Injectable {
             total: roundDecimal(bg_total, 1)
         )
 
-        let saveAverages = BGaverages(context: coredataContext)
-        saveAverages.date = Date()
-        saveAverages.average = bg_total as NSDecimalNumber
-        saveAverages.average_1 = bg_1 as NSDecimalNumber
-        saveAverages.average_7 = bg_7 as NSDecimalNumber
-        saveAverages.average_30 = bg_30 as NSDecimalNumber
-        saveAverages.average_90 = bg_90 as NSDecimalNumber
-
-        // MARK: Save to HbA1c to CoreData
-
         coredataContext.perform {
+            let saveAverages = BGaverages(context: self.coredataContext)
+            saveAverages.date = Date()
+            saveAverages.average = bg_total as NSDecimalNumber
+            saveAverages.average_1 = bg_1 as NSDecimalNumber
+            saveAverages.average_7 = bg_7 as NSDecimalNumber
+            saveAverages.average_30 = bg_30 as NSDecimalNumber
+            saveAverages.average_90 = bg_90 as NSDecimalNumber
+
             try? self.coredataContext.save()
         }
 
         let avg = Averages(Average: avgs, Median: median)
-
-        // MARK: Fetch InsulinDuration from CoreData
-
         var insulinDistribution = [InsulinDistribution]()
 
         var insulin = Ins(

+ 2 - 2
FreeAPS/Sources/APS/CGM/GlucoseSimulatorSource.swift

@@ -136,7 +136,7 @@ class IntelligentGenerator: BloodGlucoseGenerator {
         trandsStepDirection = getDirection(fromGlucose: previousGlucose, toGlucose: currentGlucose).rawValue
         let glucose = BloodGlucose(
             _id: UUID().uuidString,
-            sgv: nil,
+            sgv: currentGlucose,
             direction: BloodGlucose.Direction(rawValue: trandsStepDirection),
             date: Decimal(Int(date.timeIntervalSince1970) * 1000),
             dateString: date,
@@ -183,7 +183,7 @@ class IntelligentGenerator: BloodGlucoseGenerator {
 
     private func makeStepInTrend() {
         currentGlucose +=
-            Int(Double((trendTargetValue - currentGlucose) / trendStepsLeft) * [0.3, 0.6, 1, 1.3, 1.6].randomElement()!)
+            Int(Double((trendTargetValue - currentGlucose) / trendStepsLeft) * [0.3, 0.6, 1, 1.3, 1.6, 2.0].randomElement()!)
         trendStepsLeft -= 1
         if trendStepsLeft == 0 {
             generateNewTrend()

+ 15 - 0
FreeAPS/Sources/APS/FetchGlucoseManager.swift

@@ -130,6 +130,21 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
         }
         debug(.deviceManager, "New glucose found")
 
+        // filter the data if it is the case
+        if settingsManager.settings.smoothGlucose {
+            // limit to 30 minutes of previous BG Data
+            let oldGlucoses = glucoseStorage.recent().filter {
+                $0.dateString.addingTimeInterval(31 * 60) > Date()
+            }
+            var smoothedValues = oldGlucoses + filtered
+            // smooth with 3 repeats
+            for _ in 1 ... 3 {
+                smoothedValues.smoothSavitzkyGolayQuaDratic(withFilterWidth: 3)
+            }
+            // find the new values only
+            filtered = smoothedValues.filter { $0.dateString > syncDate }
+        }
+
         glucoseStorage.storeGlucose(filtered)
 
         deviceDataManager.heartbeat(date: Date())

+ 6 - 0
FreeAPS/Sources/Application/FreeAPSApp.swift

@@ -1,10 +1,14 @@
+import CoreData
 import SwiftUI
 import Swinject
 
 @main struct FreeAPSApp: App {
     @Environment(\.scenePhase) var scenePhase
+
     @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
 
+    @StateObject var dataController = CoreDataStack.shared
+
     // Dependencies Assembler
     // contain all dependencies Assemblies
     // TODO: Remove static key after update "Use Dependencies" logic
@@ -53,6 +57,8 @@ import Swinject
     var body: some Scene {
         WindowGroup {
             Main.RootView(resolver: resolver)
+
+                .environment(\.managedObjectContext, dataController.persistentContainer.viewContext)
         }
         .onChange(of: scenePhase) { newScenePhase in
             debug(.default, "APPLICATION PHASE: \(newScenePhase)")

+ 1 - 0
FreeAPS/Sources/Helpers/Color+Extensions.swift

@@ -61,4 +61,5 @@ extension Color {
     static let darkerBlue = Color("DarkerBlue")
     static let loopPink = Color("LoopPink")
     static let lemon = Color("Lemon")
+    static let minus = Color("minus")
 }

+ 6 - 2
FreeAPS/Sources/Helpers/CoreDataStack.swift

@@ -1,8 +1,8 @@
 import CoreData
 import Foundation
 
-class CoreDataStack {
-    private init() {}
+class CoreDataStack: ObservableObject {
+    init() {}
 
     static let shared = CoreDataStack()
 
@@ -33,4 +33,8 @@ class CoreDataStack {
             }
         }
     }
+
+    func delete(obj: NSManagedObject) {
+        persistentContainer.viewContext.delete(obj)
+    }
 }

+ 172 - 0
FreeAPS/Sources/Helpers/SavitzkyGolayFilter.swift

@@ -0,0 +1,172 @@
+import Foundation
+
+/// allowed values are 0, 1, 2 or 3. It's the index in coefficients
+private var coefficientsRowToUse = 3
+
+/// Savitzky Golay coefficients
+private let coefficients = [
+    [-3.0, 12.0, 17.0, 12.0, -3.0],
+    [-2.0, 3.0, 6.0, 7.0, 6.0, 3.0, -2.0],
+    [-21.0, 14.0, 39.0, 54.0, 59.0, 54.0, 39.0, 14.0, -21.0],
+    [-36.0, 9.0, 44.0, 69.0, 84.0, 89.0, 84.0, 69.0, 44.0, 9.0, -36.0]
+]
+
+/// an array with elements of a type that conforms to Smoothable, can be filtered using  the Savitzky Golay algorithm
+protocol SavitzkyGolaySmoothable {
+    /// value to be smoothed
+    var value: Double { get set }
+}
+
+/// local help class
+private class IsSmoothable: SavitzkyGolaySmoothable {
+    var value: Double = 0.0
+
+    init(withValue value: Double = 0.0) {
+        self.value = value
+    }
+}
+
+extension Array where Element: SavitzkyGolaySmoothable {
+    /// - apply Savitzky Golay filter
+    /// - before applying the filter, the array will be prepended and append with a number of elements equal to the filterwidth, filterWidth default 5. Allowed values are 5, 4, 3, 2. If any other value is assigned, then 5 will be used
+    /// - ...continue with 5 here in the explanation ...
+    /// - for the 5 last elements and 5 first elements, a regression is done. This regression is done used to give values to the 5 prepended and appended values. Which means it's as if we draw a line through the first 5 and 5 last original values, and use this line to give values to the 5 prepended and appended values
+    /// - the 5 prepended and appended values are then used in the filter algorithm, which means we can also filter the original 5 first and last elements
+    /// see also example https://github.com/JohanDegraeve/xdripswift/wiki/Libre-value-smoothing
+    mutating func smoothSavitzkyGolayQuaDratic(withFilterWidth filterWidth: Int = 5) {
+        // filterWidthToUse is the value of filterWidth to use in the algorithm. By default filterWidthToUse = parameter value filterWidth
+        var filterWidthToUse = filterWidth
+
+        // calculate coefficientsRowToUse based on filterWdith
+        switch filterWidth {
+        case 5:
+            coefficientsRowToUse = 3
+
+        case 4:
+            coefficientsRowToUse = 2
+
+        case 3:
+            coefficientsRowToUse = 1
+
+        case 2:
+            coefficientsRowToUse = 0
+
+        default:
+            // invalid filterWidth was given in parameterList, use default value
+            coefficientsRowToUse = 3
+
+            filterWidthToUse = 5
+        }
+
+        // using 5 here in the comments as value for filterWidthToUse
+
+        // the amount of elements must be at least 5. If that's not the case then don't apply any smoothing
+        guard count >= filterWidthToUse else { return }
+
+        // create a new array, to which we will prepend and append 5 elements so that we can do also smoothing for the 5 last and 5 first values of the input array (which is self)
+        // the 5 elements will be estimated by doing linear regression of the first 5 and last 5 elements of the original input array respectively
+        // this is only a temporary array, but it will hold the elements of the original array, those elements will get a new value when doing the smoothing
+        var tempArray = [SavitzkyGolaySmoothable]()
+        for element in self {
+            tempArray.append(element)
+        }
+
+        // now prepend and append with 5 elements, each with a default value 0.0
+        for _ in 0 ..< filterWidthToUse {
+            tempArray.insert(IsSmoothable(), at: 0)
+            tempArray.append(IsSmoothable())
+        }
+
+        // so now we have tempArray, of length size of original array + 2 * 5
+        // the first 5 and the last 5 elements are of type IsSmoothable with value 0
+
+        // - indicesArray is a help array needed for the function linearRegressionCreator
+        // - this will be the first parameter in the call to the linearRegression function, in fact it's an array of IsSmoothable with length = length of tempArray
+        // - we give each IsSmoothable the value of the index, meaning from 0 up to (length of tempArray) - 1
+        // - in fact it's not really smoothable, it's just because we use isSmoothable in function linearRegressionCreator
+        var indicesArray = [SavitzkyGolaySmoothable]()
+        for index in 0 ..< (count + (filterWidthToUse * 2)) {
+            indicesArray.append(IsSmoothable(withValue: Double(index)))
+        }
+
+        /// - this is a piece of code that we will execute two times, once for the firs 5 elements, then for the last 5, so we put it in a closure variable
+        /// - it calculates the regression function (which is nothing else but doing y = intercept + slope*x) for range defined by predictorRange in tempArray. It will be used for the 5 first and 5 last real values, ie the 5 first and 5 last real glucose values
+        /// - then executes the regression for every element in the range defined by targetRange, again in tempArray
+        let doRegression = { (predictorRange: Range<Int>, targetRange: Range<Int>) in
+
+            // calculate the linearRegression function
+            let linearRegression = linearRegressionCreator(indicesArray[predictorRange], tempArray[predictorRange])
+
+            // ready to do the linear regression for the targetRange in tempArray
+            for index in targetRange {
+                tempArray[index].value = linearRegression(indicesArray[index].value)
+            }
+        }
+
+        // now do the regression for the 5 first elements
+        doRegression(filterWidthToUse ..< (filterWidthToUse * 2), 0 ..< filterWidthToUse)
+
+        // now do the regression for the 5 last elements
+        doRegression(
+            (tempArray.count - filterWidthToUse * 2) ..< (tempArray.count - filterWidthToUse),
+            (tempArray.count - filterWidthToUse) ..< tempArray.count
+        )
+
+        // now start filtering
+
+        // initialize array that will hold the resulting filtered values
+        var filteredValues = [Double]()
+
+        // calculate divider
+        let divider = coefficients[coefficientsRowToUse].reduce(0, { x, y in
+            x + y
+        })
+
+        // filter each original value
+        for _ in 0 ..< count {
+            // add a new element to filteredValues, start value is 0.0
+            // this new value will be the last element, so we access it with index filteredValues.count - 1
+            filteredValues.append(0.0)
+
+            // iterate through the coefficients
+            for (index, coefficient) in coefficients[coefficientsRowToUse].enumerated() {
+                filteredValues[filteredValues.count - 1] = filteredValues[filteredValues.count - 1] + coefficient *
+                    tempArray[index + filteredValues.count - 1].value
+            }
+
+            filteredValues[filteredValues.count - 1] = filteredValues[filteredValues.count - 1] / divider
+        }
+
+        // now assign the new values to the original objects
+        for (index, _) in enumerated() {
+            self[index].value = filteredValues[index]
+        }
+    }
+}
+
+/// source https://github.com/raywenderlich/swift-algorithm-club/tree/master/Linear%20Regression
+private func multiply(
+    _ a: ArraySlice<SavitzkyGolaySmoothable>,
+    _ b: ArraySlice<SavitzkyGolaySmoothable>
+) -> ArraySlice<SavitzkyGolaySmoothable> {
+    zip(a, b).map({ IsSmoothable(withValue: $0.value * $1.value) })[0 ..< a.count]
+}
+
+/// source https://github.com/raywenderlich/swift-algorithm-club/tree/master/Linear%20Regression
+private func average(_ input: ArraySlice<SavitzkyGolaySmoothable>) -> Double {
+    (input.reduce(IsSmoothable(), { (x: SavitzkyGolaySmoothable, y: SavitzkyGolaySmoothable) in
+        IsSmoothable(withValue: x.value + y.value) })).value / Double(input.count)
+}
+
+/// source https://github.com/raywenderlich/swift-algorithm-club/tree/master/Linear%20Regression
+private func linearRegressionCreator(
+    _ xs: ArraySlice<SavitzkyGolaySmoothable>,
+    _ ys: ArraySlice<SavitzkyGolaySmoothable>
+) -> (Double) -> Double {
+    let sum1 = average(multiply(ys, xs)) - average(xs) * average(ys)
+    let sum2 = average(multiply(xs, xs)) - pow(average(xs), 2)
+    let slope = sum1 / sum2
+    let intercept = average(ys) - slope * average(xs)
+
+    return { x in intercept + slope * x }
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 28 - 1
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 61 - 1
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 28 - 2
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 25 - 5
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 63 - 1
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 1
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 11 - 0
FreeAPS/Sources/Models/BloodGlucose.swift

@@ -79,3 +79,14 @@ extension Double {
         Decimal(self) / GlucoseUnits.exchangeRate
     }
 }
+
+extension BloodGlucose: SavitzkyGolaySmoothable {
+    var value: Double {
+        get {
+            Double(glucose ?? 0)
+        }
+        set {
+            glucose = Int(newValue)
+        }
+    }
+}

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

@@ -30,6 +30,7 @@ struct FreeAPSSettings: JSON, Equatable {
     var timeCap: Int = 8
     var minuteInterval: Int = 30
     var delay: Int = 60
+    var smoothGlucose: Bool = false
 }
 
 extension FreeAPSSettings: Decodable {
@@ -157,6 +158,10 @@ extension FreeAPSSettings: Decodable {
             settings.displayStatistics = displayStatistics
         }
 
+        if let smoothGlucose = try? container.decode(Bool.self, forKey: .smoothGlucose) {
+            settings.smoothGlucose = smoothGlucose
+        }
+
         self = settings
     }
 }

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

@@ -8,8 +8,8 @@ struct Preferences: JSON {
     var autosensMin: Decimal = 0.7
     var smbDeliveryRatio: Decimal = 0.5
     var rewindResetsAutosens: Bool = true
-    var highTemptargetRaisesSensitivity: Bool = false
-    var lowTemptargetLowersSensitivity: Bool = false
+    var highTemptargetRaisesSensitivity: Bool = true
+    var lowTemptargetLowersSensitivity: Bool = true
     var sensitivityRaisesTarget: Bool = true
     var resistanceLowersTarget: Bool = false
     var advTargetAdjustments: Bool = false
@@ -19,7 +19,6 @@ struct Preferences: JSON {
     var wideBGTargetRange: Bool = false
     var skipNeutralTemps: Bool = false
     var unsuspendIfNoTemp: Bool = false
-    var bolusSnoozeDIADivisor: Decimal = 2
     var min5mCarbimpact: Decimal = 8
     var autotuneISFAdjustmentFraction: Decimal = 1.0
     var remainingCarbsFraction: Decimal = 1.0
@@ -80,7 +79,6 @@ extension Preferences {
         case wideBGTargetRange = "wide_bg_target_range"
         case skipNeutralTemps = "skip_neutral_temps"
         case unsuspendIfNoTemp = "unsuspend_if_no_temp"
-        case bolusSnoozeDIADivisor = "bolussnooze_dia_divisor"
         case min5mCarbimpact = "min_5m_carbimpact"
         case autotuneISFAdjustmentFraction = "autotune_isf_adjustmentFraction"
         case remainingCarbsFraction

+ 21 - 0
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -1,3 +1,4 @@
+import CoreData
 import SwiftUI
 
 extension AddCarbs {
@@ -11,6 +12,11 @@ extension AddCarbs {
         @Published var fat: Decimal = 0
         @Published var carbsRequired: Decimal?
         @Published var useFPU: Bool = false
+        @Published var dish: String = ""
+        @Published var selection: Presets?
+
+        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext // .newBackgroundContext()
+        @Environment(\.managedObjectContext) var moc
 
         override func subscribe() {
             carbsRequired = provider.suggestion?.carbsReq
@@ -51,6 +57,10 @@ extension AddCarbs {
                 var equivalent: Decimal = carbEquivalents / Decimal(computedDuration)
                 // Adjust for interval setting other than 60 minutes
                 equivalent /= Decimal(60 / interval)
+                // Round to 1 fraction digit
+                // equivalent = Decimal(round(Double(equivalent * 10) / 10))
+                let roundedEquivalent: Double = round(Double(equivalent * 10)) / 10
+                equivalent = Decimal(roundedEquivalent)
                 // Number of equivalents
                 var numberOfEquivalents = carbEquivalents / equivalent
                 // Only use delay in first loop
@@ -100,5 +110,16 @@ extension AddCarbs {
                 showModal(for: .bolus(waitForSuggestion: true))
             }
         }
+
+        func deletePreset() {
+            if selection != nil {
+                try? coredataContext.delete(selection!)
+                try? coredataContext.save()
+                carbs = 0
+                fat = 0
+                protein = 0
+            }
+            selection = nil
+        }
     }
 }

+ 154 - 32
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -1,3 +1,4 @@
+import CoreData
 import SwiftUI
 import Swinject
 
@@ -5,11 +6,22 @@ extension AddCarbs {
     struct RootView: BaseView {
         let resolver: Resolver
         @StateObject var state = StateModel()
+        @State var dish: String = ""
+        @State var isPromtPresented = false
+        @State var saved = false
+        @State private var showAlert = false
+
+        @FetchRequest(
+            entity: Presets.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "dish", ascending: true)]
+        ) var carbPresets: FetchedResults<Presets>
+
+        @Environment(\.managedObjectContext) var moc
 
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 0
+            formatter.maximumFractionDigits = 1
             return formatter
         }
 
@@ -33,47 +45,157 @@ extension AddCarbs {
                             Text("grams").foregroundColor(.secondary)
                         }.padding(.vertical)
 
-                        // MARK: Adding Protein and Fat. Test
-
-                        if state.useFPU {
-                            HStack {
-                                Text("Protein").foregroundColor(.loopRed).fontWeight(.thin)
-                                Spacer()
-                                DecimalTextField(
-                                    "0",
-                                    value: $state.protein,
-                                    formatter: formatter,
-                                    autofocus: false,
-                                    cleanInput: true
-                                ).foregroundColor(.loopRed)
-                                Text("grams").foregroundColor(.secondary)
-                            }
-                            HStack {
-                                Text("Fat").foregroundColor(.loopYellow).fontWeight(.thin)
-                                Spacer()
-                                DecimalTextField(
-                                    "0",
-                                    value: $state.fat,
-                                    formatter: formatter,
-                                    autofocus: false,
-                                    cleanInput: true
-                                )
-                                Text("grams").foregroundColor(.secondary)
-                            }
-                        }
+                        if state.useFPU { proteinAndFat() }
                         DatePicker("Date", selection: $state.date)
                     }
                 }
                 Section {
                     Button { state.add() }
-                    label: { Text("Add") }
+                    label: { Text("Save and continue") }
                         .disabled(state.carbs <= 0 && state.fat <= 0 && state.protein <= 0)
                 }
+
+                if state.useFPU {
+                    mealPresets
+                }
             }
             .onAppear(perform: configureView)
-            .navigationTitle("Add Carbs")
-            .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
+
+        var presetPopover: some View {
+            Form {
+                Section(header: Text("Enter Meal Preset Name")) {
+                    TextField("Name Of Dish", text: $dish)
+                    Button {
+                        saved = true
+                        if dish != "", saved {
+                            let preset = Presets(context: moc)
+                            preset.dish = dish
+                            preset.fat = state.fat as NSDecimalNumber
+                            preset.protein = state.protein as NSDecimalNumber
+                            preset.carbs = state.carbs as NSDecimalNumber
+                            try? moc.save()
+                            state.selection = preset
+                            saved = false
+                            isPromtPresented = false
+                        }
+                    }
+                    label: { Text("Save") }
+                    Button {
+                        dish = ""
+                        saved = false
+                        isPromtPresented = false }
+                    label: { Text("Cancel") }
+                }
+            }
+        }
+
+        var mealPresets: some View {
+            Section {
+                VStack {
+                    Picker("Meal Presets", selection: $state.selection) {
+                        Text("Empty").tag(nil as Presets?)
+                        ForEach(carbPresets, id: \.self) { (preset: Presets) in
+                            Text(preset.dish ?? "").tag(preset as Presets?)
+                        }
+                    }
+                    .pickerStyle(.automatic)
+                    ._onBindingChange($state.selection) { _ in
+                        state.carbs = ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
+                        state.fat = ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
+                        state.protein = ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
+                    }
+                }
+                HStack {
+                    Button("Delete Preset") {
+                        showAlert.toggle()
+                    }
+                    .disabled(state.selection == nil)
+                    .accentColor(.orange)
+                    .buttonStyle(BorderlessButtonStyle())
+                    .alert(
+                        "Delete preset '\(state.selection?.dish ?? "")'?",
+                        isPresented: $showAlert,
+                        actions: {
+                            Button("No", role: .cancel) {}
+                            Button("Yes", role: .destructive) {
+                                state.deletePreset()
+                            }
+                        }
+                    )
+                    Button {
+                        if state.carbs != 0 { state.carbs -= ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal }
+                        if state.fat != 0 { state.fat -= ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal }
+                        if state.protein != 0 { state.protein -= ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal }
+                    }
+                    label: { Text("[ -1 ]") }
+                        .disabled(state.selection == nil || (
+                            (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
+                                .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
+                                .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
+                                .protein
+                        ))
+                        .buttonStyle(BorderlessButtonStyle())
+                        .frame(maxWidth: .infinity, alignment: .trailing)
+                        .accentColor(.minus)
+                    Button {
+                        state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
+                        state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
+                        state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal }
+                    label: { Text("[ +1 ]") }
+                        .disabled(state.selection == nil)
+                        .buttonStyle(BorderlessButtonStyle())
+                        .accentColor(.blue)
+                }
+            }
+        }
+
+        @ViewBuilder private func proteinAndFat() -> some View {
+            HStack {
+                Text("Protein").foregroundColor(.red) // .fontWeight(.thin)
+                Spacer()
+                DecimalTextField(
+                    "0",
+                    value: $state.protein,
+                    formatter: formatter,
+                    autofocus: false,
+                    cleanInput: true
+                ).foregroundColor(.loopRed)
+
+                Text("grams").foregroundColor(.secondary)
+            }
+            HStack {
+                Text("Fat").foregroundColor(.orange) // .fontWeight(.thin)
+                Spacer()
+                DecimalTextField(
+                    "0",
+                    value: $state.fat,
+                    formatter: formatter,
+                    autofocus: false,
+                    cleanInput: true
+                )
+                Text("grams").foregroundColor(.secondary)
+            }
+            HStack {
+                Button {
+                    isPromtPresented = true
+                }
+                label: { Text("Save as Preset") }
+            }
+            .frame(maxWidth: .infinity, alignment: .trailing)
+            .disabled(
+                (state.carbs <= 0 && state.fat <= 0 && state.protein <= 0) ||
+                    (
+                        (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
+                            .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
+                            .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
+                            .protein
+                    )
+            )
+            .popover(isPresented: $isPromtPresented) {
+                presetPopover
+            }
+        }
     }
 }

+ 11 - 18
FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift

@@ -11,24 +11,24 @@ extension AddTempTarget {
         @Published var date = Date()
         @Published var newPresetName = ""
         @Published var presets: [TempTarget] = []
+        @Published var percentage = 100.0
+        @Published var maxValue: Decimal = 1.2
+        @Published var halfBasal: Decimal = 160
 
         private(set) var units: GlucoseUnits = .mmolL
 
         override func subscribe() {
             units = settingsManager.settings.units
             presets = storage.presets()
+            maxValue = settingsManager.preferences.autosensMax
+            halfBasal = settingsManager.preferences.halfBasalExerciseTarget
         }
 
         func enact() {
-            var lowTarget = low
-            var highTarget = high
-
-            highTarget = max(highTarget, lowTarget)
-
-            if units == .mmolL {
-                lowTarget = lowTarget.asMgdL
-                highTarget = highTarget.asMgdL
-            }
+            let diff = Double(halfBasal - 100)
+            let multiplier = percentage - (diff * (percentage / 100))
+            let lowTarget = Decimal(diff + multiplier) / (Decimal(percentage) / 100)
+            let highTarget = lowTarget
 
             let entry = TempTarget(
                 name: TempTarget.custom,
@@ -50,15 +50,8 @@ extension AddTempTarget {
         }
 
         func save() {
-            var lowTarget = low
-            var highTarget = high
-
-            highTarget = max(highTarget, lowTarget)
-
-            if units == .mmolL {
-                lowTarget = lowTarget.asMgdL
-                highTarget = highTarget.asMgdL
-            }
+            let lowTarget = Decimal(60 + 40 * (percentage / 100)) / (Decimal(percentage) / 100)
+            let highTarget = lowTarget
 
             let entry = TempTarget(
                 name: newPresetName.isEmpty ? TempTarget.custom : newPresetName,

+ 50 - 12
FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift

@@ -8,6 +8,7 @@ extension AddTempTarget {
         @State private var isPromtPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var removeAlert: Alert?
+        @State private var isEditing = false
 
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -26,19 +27,44 @@ extension AddTempTarget {
                     }
                 }
 
-                Section(header: Text("Custom")) {
-                    HStack {
-                        Text("Bottom target")
-                        Spacer()
-                        DecimalTextField("0", value: $state.low, formatter: formatter, cleanInput: true)
-                        Text(state.units.rawValue).foregroundColor(.secondary)
-                    }
-                    HStack {
-                        Text("Top target")
-                        Spacer()
-                        DecimalTextField("0", value: $state.high, formatter: formatter, cleanInput: true)
-                        Text(state.units.rawValue).foregroundColor(.secondary)
+                Section(
+                    header: Text("Basal Insulin and Sensitivity ratio"),
+                    footer: Text(
+                        NSLocalizedString(
+                            "A lower 'Half Basal Target' setting will reduce the basal and raise the ISF earlier, at a lower target glucose.",
+                            comment: ""
+                        ) +
+                            NSLocalizedString(" Your setting: ", comment: "") + "\(state.halfBasal) " +
+                            NSLocalizedString("mg/dl. Autosens.max limits the max endpoint", comment: "") +
+                            " (\(state.maxValue * 100) %)"
+                    )
+                ) {
+                    VStack {
+                        Slider(
+                            value: $state.percentage,
+                            in: 15 ...
+                                Double(state.maxValue * 100),
+                            step: 1,
+                            onEditingChanged: { editing in
+                                isEditing = editing
+                            }
+                        )
+                        Text("\(state.percentage.formatted(.number)) %")
+                            .foregroundColor(isEditing ? .orange : .blue)
+                            .font(.largeTitle)
+                        Divider()
+                        Text(
+                            NSLocalizedString("Target", comment: "") +
+                                (
+                                    state
+                                        .units == .mmolL ? ": \(computeTarget().asMmolL.formatted(.number)) mmol/L" :
+                                        ": \(computeTarget().formatted(.number)) mg/dl"
+                                )
+                        ).foregroundColor(.secondary).italic()
                     }
+                }
+
+                Section {
                     HStack {
                         Text("Duration")
                         Spacer()
@@ -77,6 +103,18 @@ extension AddTempTarget {
             .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
 
+        func computeTarget() -> Decimal {
+            let ratio = min(Decimal(state.percentage / 100), state.maxValue)
+            let diff = Double(state.halfBasal - 100)
+            let multiplier = state.percentage - (diff * (state.percentage / 100))
+            var target = Decimal(diff + multiplier) / ratio
+
+            if (state.halfBasal + (state.halfBasal + target - 100)) <= 0 {
+                target = (state.halfBasal - 100 + (state.halfBasal - 100) * state.maxValue) / state.maxValue
+            }
+            return target
+        }
+
         private func presetView(for preset: TempTarget) -> some View {
             var low = preset.targetBottom
             var high = preset.targetTop

+ 3 - 0
FreeAPS/Sources/Modules/CGM/CGMStateModel.swift

@@ -14,6 +14,7 @@ extension CGM {
         @Published var cgm: CGMType = .nightscout
         // @Published var transmitterID = ""
         @Published var uploadGlucose = false
+        @Published var smoothGlucose = false
         @Published var createCalendarEvents = false
         @Published var calendarIDs: [String] = []
         @Published var currentCalendarID: String = ""
@@ -39,6 +40,8 @@ extension CGM {
                 }
             })
 
+            subscribeSetting(\.smoothGlucose, on: $smoothGlucose, initial: { smoothGlucose = $0 })
+
             $cgm
                 .removeDuplicates()
                 .sink { [weak self] value in

+ 4 - 0
FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift

@@ -67,6 +67,10 @@ extension CGM {
                     Section(header: Text("Other")) {
                         Toggle("Upload glucose to Nightscout", isOn: $state.uploadGlucose)
                     }
+
+                    Section(header: Text("Experimental")) {
+                        Toggle("Smooth Glucose Value", isOn: $state.smoothGlucose)
+                    }
                 }
 
                 .onAppear(perform: configureView)

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

@@ -53,6 +53,7 @@ extension Home {
         @Published var alarm: GlucoseAlarm?
         @Published var animatedBackground = false
         @Published var manualTempBasal = false
+        @Published var smooth = false
 
         override func subscribe() {
             setupGlucose()
@@ -83,6 +84,7 @@ extension Home {
             manualTempBasal = apsManager.isManualTempBasal
             setStatusTitle()
             setupCurrentTempTarget()
+            smooth = settingsManager.settings.smoothGlucose
 
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(SuggestionObserver.self, observer: self)
@@ -386,6 +388,7 @@ extension Home.StateModel:
         units = settingsManager.settings.units
         animatedBackground = settingsManager.settings.animatedBackground
         manualTempBasal = apsManager.isManualTempBasal
+        smooth = settingsManager.settings.smoothGlucose
         setupGlucose()
         setupStatistics()
     }

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

@@ -50,9 +50,11 @@ struct MainChartView: View {
     @Binding var carbs: [CarbsEntry]
     @Binding var timerDate: Date
     @Binding var units: GlucoseUnits
+    @Binding var smooth: Bool
 
     @State var didAppearTrigger = false
     @State private var glucoseDots: [CGRect] = []
+    @State private var unSmoothedGlucoseDots: [CGRect] = []
     @State private var predictionDots: [PredictionType: [CGRect]] = [:]
     @State private var bolusDots: [DotInfo] = []
     @State private var bolusPath = Path()
@@ -249,6 +251,7 @@ struct MainChartView: View {
                     carbsView(fullSize: fullSize)
                     fpuView(fullSize: fullSize)
                     bolusView(fullSize: fullSize)
+                    if smooth { unSmoothedGlucoseView(fullSize: fullSize) }
                     glucoseView(fullSize: fullSize)
                     predictionsView(fullSize: fullSize)
                 }
@@ -320,6 +323,27 @@ struct MainChartView: View {
         }
     }
 
+    private func unSmoothedGlucoseView(fullSize: CGSize) -> some View {
+        Path { path in
+            var lines: [CGPoint] = []
+            for rect in unSmoothedGlucoseDots {
+                lines.append(CGPoint(x: rect.midX, y: rect.midY))
+                path.addEllipse(in: rect)
+            }
+            path.addLines(lines)
+        }
+        .stroke(Color.loopGray, lineWidth: 0.5)
+        .onChange(of: glucose) { _ in
+            update(fullSize: fullSize)
+        }
+        .onChange(of: didAppearTrigger) { _ in
+            update(fullSize: fullSize)
+        }
+        .onReceive(Foundation.NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
+            update(fullSize: fullSize)
+        }
+    }
+
     private func bolusView(fullSize: CGSize) -> some View {
         ZStack {
             bolusPath
@@ -445,6 +469,7 @@ extension MainChartView {
         calculatePredictionDots(fullSize: fullSize, type: .zt)
         calculatePredictionDots(fullSize: fullSize, type: .uam)
         calculateGlucoseDots(fullSize: fullSize)
+        calculateUnSmoothedGlucoseDots(fullSize: fullSize)
         calculateBolusDots(fullSize: fullSize)
         calculateCarbsDots(fullSize: fullSize)
         calculateFPUsDots(fullSize: fullSize)
@@ -469,6 +494,22 @@ extension MainChartView {
         }
     }
 
+    private func calculateUnSmoothedGlucoseDots(fullSize: CGSize) {
+        calculationQueue.async {
+            let dots = glucose.concurrentMap { value -> CGRect in
+                let position = UnSmoothedGlucoseToCoordinate(value, fullSize: fullSize)
+                return CGRect(x: position.x - 2, y: position.y - 2, width: 4, height: 4)
+            }
+
+            let range = self.getGlucoseYRange(fullSize: fullSize)
+
+            DispatchQueue.main.async {
+                glucoseYGange = range
+                unSmoothedGlucoseDots = dots
+            }
+        }
+    }
+
     private func calculateBolusDots(fullSize: CGSize) {
         calculationQueue.async {
             let dots = boluses.map { value -> DotInfo in
@@ -879,6 +920,13 @@ extension MainChartView {
         return CGPoint(x: x, y: y)
     }
 
+    private func UnSmoothedGlucoseToCoordinate(_ glucoseEntry: BloodGlucose, fullSize: CGSize) -> CGPoint {
+        let x = timeToXCoordinate(glucoseEntry.dateString.timeIntervalSince1970, fullSize: fullSize)
+        let y = glucoseToYCoordinate(glucoseEntry.sgv ?? glucoseEntry.glucose ?? 0, fullSize: fullSize)
+
+        return CGPoint(x: x, y: y)
+    }
+
     private func predictionToCoordinate(_ pred: Int, fullSize: CGSize, index: Int) -> CGPoint {
         guard let deliveredAt = suggestion?.deliverAt else {
             return .zero

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

@@ -566,7 +566,8 @@ extension Home {
                     tempTargets: $state.tempTargets,
                     carbs: $state.carbs,
                     timerDate: $state.timerDate,
-                    units: $state.units
+                    units: $state.units,
+                    smooth: $state.smooth
                 )
             }
             .padding(.bottom)

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 61
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift


+ 19 - 2
FreeAPS/Sources/Services/HealthKit/HealthKitManager.swift

@@ -280,16 +280,33 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
                     .compactMap { item -> InsulinBasal? in
                         let nextElementEventIndex = item.offset + 1
                         guard basalEvents.count > nextElementEventIndex else { return nil }
+
+                        var minimalDose = self.settingsManager.preferences.bolusIncrement
+                        if (minimalDose != 0.05) || (minimalDose != 0.025) {
+                            minimalDose = Decimal(0.05)
+                        }
+
                         let nextBasalEvent = basalEvents[nextElementEventIndex]
                         let secondsOfCurrentBasal = nextBasalEvent.timestamp.timeIntervalSince(item.element.timestamp)
                         let amount = Decimal(secondsOfCurrentBasal / 3600) * (item.element.rate ?? 0)
+                        let incrementsRaw = amount / minimalDose
+
+                        var amountRounded: Decimal
+                        if incrementsRaw >= 1 {
+                            let incrementsRounded = floor(Double(incrementsRaw))
+                            amountRounded = Decimal(round(incrementsRounded * Double(minimalDose) * 100_000.0) / 100_000.0)
+                        } else {
+                            amountRounded = 0
+                        }
+
                         let id = String(item.element.id.dropFirst())
-                        guard amount > 0,
+                        guard amountRounded >= 0,
                               id != ""
                         else { return nil }
+
                         return InsulinBasal(
                             id: id,
-                            amount: amount,
+                            amount: amountRounded,
                             startDelivery: item.element.timestamp,
                             endDelivery: nextBasalEvent.timestamp
                         )

+ 0 - 4
HbA1c+CoreDataClass.swift

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

+ 0 - 15
HbA1c+CoreDataProperties.swift

@@ -1,15 +0,0 @@
-import CoreData
-import Foundation
-
-public extension HbA1c {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<HbA1c> {
-        NSFetchRequest<HbA1c>(entityName: "HbA1c")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var hba1c: NSDecimalNumber?
-    @NSManaged var hba1c_1: NSDecimalNumber?
-    @NSManaged var hba1c_7: NSDecimalNumber?
-    @NSManaged var hba1c_30: NSDecimalNumber?
-    @NSManaged var hba1c_90: NSDecimalNumber?
-}

+ 0 - 4
InsulinDistribution+CoreDataClass.swift

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

+ 0 - 14
InsulinDistribution+CoreDataProperties.swift

@@ -1,14 +0,0 @@
-import CoreData
-import Foundation
-
-public extension InsulinDistribution {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<InsulinDistribution> {
-        NSFetchRequest<InsulinDistribution>(entityName: "InsulinDistribution")
-    }
-
-    @NSManaged var bolus: NSDecimalNumber?
-    @NSManaged var tempBasal: NSDecimalNumber?
-    @NSManaged var scheduledBasal: NSDecimalNumber?
-    @NSManaged var date: Date?
-    @NSManaged var insulin: Oref0Suggestion?
-}

+ 0 - 4
LoopStatRecord+CoreDataClass.swift

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

+ 0 - 13
LoopStatRecord+CoreDataProperties.swift

@@ -1,13 +0,0 @@
-import CoreData
-import Foundation
-
-public extension LoopStatRecord {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<LoopStatRecord> {
-        NSFetchRequest<LoopStatRecord>(entityName: "LoopStatRecord")
-    }
-
-    @NSManaged var duration: Double
-    @NSManaged var end: Date?
-    @NSManaged var start: Date?
-    @NSManaged var loopStatus: String?
-}

+ 0 - 4
Oref0Suggestion+CoreDataClass.swift

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

+ 0 - 43
Oref0Suggestion+CoreDataProperties.swift

@@ -1,43 +0,0 @@
-import CoreData
-import Foundation
-
-public extension Oref0Suggestion {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<Oref0Suggestion> {
-        NSFetchRequest<Oref0Suggestion>(entityName: "Oref0Suggestion")
-    }
-
-    @NSManaged var computedTDD: NSSet?
-    @NSManaged var computedInsulinDistribution: NSSet?
-}
-
-// MARK: Generated accessors for computedTDD
-
-public extension Oref0Suggestion {
-    @objc(addComputedTDDObject:)
-    @NSManaged func addToComputedTDD(_ value: TDD)
-
-    @objc(removeComputedTDDObject:)
-    @NSManaged func removeFromComputedTDD(_ value: TDD)
-
-    @objc(addComputedTDD:)
-    @NSManaged func addToComputedTDD(_ values: NSSet)
-
-    @objc(removeComputedTDD:)
-    @NSManaged func removeFromComputedTDD(_ values: NSSet)
-}
-
-// MARK: Generated accessors for computedInsulinDistribution
-
-public extension Oref0Suggestion {
-    @objc(addComputedInsulinDistributionObject:)
-    @NSManaged func addToComputedInsulinDistribution(_ value: InsulinDistribution)
-
-    @objc(removeComputedInsulinDistributionObject:)
-    @NSManaged func removeFromComputedInsulinDistribution(_ value: InsulinDistribution)
-
-    @objc(addComputedInsulinDistribution:)
-    @NSManaged func addToComputedInsulinDistribution(_ values: NSSet)
-
-    @objc(removeComputedInsulinDistribution:)
-    @NSManaged func removeFromComputedInsulinDistribution(_ values: NSSet)
-}

+ 0 - 4
Readings+CoreDataClass.swift

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

+ 0 - 11
Readings+CoreDataProperties.swift

@@ -1,11 +0,0 @@
-import CoreData
-import Foundation
-
-public extension Readings {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<Readings> {
-        NSFetchRequest<Readings>(entityName: "Readings")
-    }
-
-    @NSManaged var date: Date?
-    @NSManaged var glucose: Int16
-}

+ 0 - 4
TDD+CoreDataClass.swift

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

+ 0 - 12
TDD+CoreDataProperties.swift

@@ -1,12 +0,0 @@
-import CoreData
-import Foundation
-
-public extension TDD {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<TDD> {
-        NSFetchRequest<TDD>(entityName: "TDD")
-    }
-
-    @NSManaged var tdd: NSDecimalNumber?
-    @NSManaged var timestamp: Date?
-    @NSManaged var computed: Oref0Suggestion?
-}