Procházet zdrojové kódy

Merge branch 'bdb_dev' into bdb_dev

Jon B Mårtensson před 3 roky
rodič
revize
15a5c2c051

+ 32 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -21,6 +21,10 @@
 		19854F492961C3E500941627 /* DurationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19854F482961C3E500941627 /* DurationButton.swift */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
 		19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; };
+		19D466A329AA2B80004D5F33 /* FPUConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A229AA2B80004D5F33 /* FPUConfigDataFlow.swift */; };
+		19D466A529AA2BD4004D5F33 /* FPUConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */; };
+		19D466A729AA2C22004D5F33 /* FPUConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */; };
+		19D466AA29AA3099004D5F33 /* FPUConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */; };
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
 		23888883D4EA091C88480FF2 /* BolusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19984D62EFC0035A9E9644D /* BolusProvider.swift */; };
@@ -487,6 +491,10 @@
 		19B0EF2028F6D66200069496 /* Statistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics.swift; sourceTree = "<group>"; };
 		19C166682756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		19C166692756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
+		19D466A229AA2B80004D5F33 /* FPUConfigDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigDataFlow.swift; sourceTree = "<group>"; };
+		19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigProvider.swift; sourceTree = "<group>"; };
+		19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigStateModel.swift; sourceTree = "<group>"; };
+		19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigRootView.swift; sourceTree = "<group>"; };
 		1CAE81192B118804DCD23034 /* SnoozeProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeProvider.swift; sourceTree = "<group>"; };
 		212E8BFE6D66EE65AA26A114 /* CalibrationsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CalibrationsProvider.swift; sourceTree = "<group>"; };
 		223EC0494F55A91E3EA69EF4 /* BolusStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusStateModel.swift; sourceTree = "<group>"; };
@@ -927,6 +935,25 @@
 			path = Main;
 			sourceTree = "<group>";
 		};
+		19D466A129AA2B0A004D5F33 /* FPUConfig */ = {
+			isa = PBXGroup;
+			children = (
+				19D466A229AA2B80004D5F33 /* FPUConfigDataFlow.swift */,
+				19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */,
+				19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */,
+				19D466A829AA306E004D5F33 /* View */,
+			);
+			path = FPUConfig;
+			sourceTree = "<group>";
+		};
+		19D466A829AA306E004D5F33 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		29B478DF61BF8D270F7D8954 /* Snooze */ = {
 			isa = PBXGroup;
 			children = (
@@ -949,6 +976,7 @@
 		3811DE0325C9D31700A708ED /* Modules */ = {
 			isa = PBXGroup;
 			children = (
+				19D466A129AA2B0A004D5F33 /* FPUConfig */,
 				F90692CD274B99850037068D /* HealthKit */,
 				6DC5D590658EF8B8DF94F9F5 /* AddCarbs */,
 				A9A4C88374496B3C89058A89 /* AddTempTarget */,
@@ -2233,6 +2261,7 @@
 				38E44537274E411700EC9A94 /* Disk+Helpers.swift in Sources */,
 				388E5A6025B6F2310019842D /* Autosens.swift in Sources */,
 				3811DE8F25C9D80400A708ED /* User.swift in Sources */,
+				19D466A329AA2B80004D5F33 /* FPUConfigDataFlow.swift in Sources */,
 				3811DEB225C9D88300A708ED /* KeychainItemAccessibility.swift in Sources */,
 				385CEAC425F2F154002D6D5B /* AnnouncementsStorage.swift in Sources */,
 				38AEE73D25F0200C0013F05B /* FreeAPSSettings.swift in Sources */,
@@ -2271,6 +2300,7 @@
 				38DAB280260CBB7F00F74C1A /* PumpView.swift in Sources */,
 				3811DEB125C9D88300A708ED /* Keychain.swift in Sources */,
 				382C133725F13A1E00715CE1 /* InsulinSensitivities.swift in Sources */,
+				19D466A529AA2BD4004D5F33 /* FPUConfigProvider.swift in Sources */,
 				383948D625CD4D8900E91849 /* FileStorage.swift in Sources */,
 				3811DE4125C9D4A100A708ED /* SettingsRootView.swift in Sources */,
 				38192E04261B82FA0094D973 /* ReachabilityManager.swift in Sources */,
@@ -2289,6 +2319,7 @@
 				3811DF0225CA9FEA00A708ED /* Credentials.swift in Sources */,
 				389A572026079BAA00BC102F /* Interpolation.swift in Sources */,
 				38B4F3C625E5017E00E76A18 /* NotificationCenter.swift in Sources */,
+				19D466A729AA2C22004D5F33 /* FPUConfigStateModel.swift in Sources */,
 				38E44528274E401C00EC9A94 /* Protected.swift in Sources */,
 				3811DEB625C9D88300A708ED /* UnlockManager.swift in Sources */,
 				E00EEC0827368630002FF094 /* NetworkAssembly.swift in Sources */,
@@ -2375,6 +2406,7 @@
 				AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */,
 				53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */,
 				5D16287A969E64D18CE40E44 /* PumpConfigStateModel.swift in Sources */,
+				19D466AA29AA3099004D5F33 /* FPUConfigRootView.swift in Sources */,
 				E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */,
 				448B6FCB252BD4796E2960C0 /* PumpSettingsEditorDataFlow.swift in Sources */,
 				38E44536274E411700EC9A94 /* Disk.swift in Sources */,

+ 2 - 2
FreeAPS/Resources/Base.lproj/InfoPlist.strings

@@ -14,7 +14,7 @@
 "NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
 
 /* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose data";
+"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
 
 /* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose data";
+"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 2 - 2
FreeAPS/Resources/Info.plist

@@ -71,9 +71,9 @@
 	<key>NSFaceIDUsageDescription</key>
 	<string>For authorized acces to bolus</string>
 	<key>NSHealthShareUsageDescription</key>
-	<string>Health App is used to store blood glucose data</string>
+	<string>Health App is used to store blood glucose, carbs and insulin</string>
 	<key>NSHealthUpdateUsageDescription</key>
-	<string>Health App is used to store blood glucose data</string>
+	<string>Health App is used to store blood glucose, carbs and insulin</string>
 	<key>NSHumanReadableCopyright</key>
 	<string>$(COPYRIGHT_NOTICE)</string>
 	<key>UIApplicationSceneManifest</key>

+ 2 - 2
FreeAPS/Resources/ar.lproj/InfoPlist.strings

@@ -14,7 +14,7 @@
 "NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
 
 /* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose data";
+"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";
 
 /* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose data";
+"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";

+ 5 - 1
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -21,5 +21,9 @@
     "carbsRequiredThreshold": 10,
     "useAppleHealth": false,
     "animatedBackground": false,
-    "displayStatistics": false
+    "displayStatistics": false,
+    "useFPUconversion": false
+    "individualAdjustmentFactor": 0.5,
+    "timeCap": 8,
+    "minuteInterval": 60
 }

+ 2 - 2
FreeAPS/Resources/sv.lproj/InfoPlist.strings

@@ -14,7 +14,7 @@
 "NSCalendarsUsageDescription" = "Kalendern används för att skapa kalenderhändelser för glukosvärden.";
 
 /* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden etc.";
+"NSHealthUpdateUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin och kolhydrater.";
 
 /* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden etc.";
+"NSHealthShareUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin och kolhydrater.";

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

@@ -25,6 +25,10 @@ struct FreeAPSSettings: JSON, Equatable {
     var carbsRequiredThreshold: Decimal = 10
     var animatedBackground: Bool = false
     var displayStatistics: Bool = false
+    var useFPUconversion: Bool = false
+    var individualAdjustmentFactor: Decimal = 0.5
+    var timeCap: Decimal = 8
+    var minuteInterval: Int = 60
 }
 
 extension FreeAPSSettings: Decodable {
@@ -97,6 +101,22 @@ extension FreeAPSSettings: Decodable {
             settings.glucoseBadge = glucoseBadge
         }
 
+        if let useFPUconversion = try? container.decode(Bool.self, forKey: .useFPUconversion) {
+            settings.useFPUconversion = useFPUconversion
+        }
+
+        if let individualAdjustmentFactor = try? container.decode(Decimal.self, forKey: .individualAdjustmentFactor) {
+            settings.individualAdjustmentFactor = individualAdjustmentFactor
+        }
+
+        if let timeCap = try? container.decode(Decimal.self, forKey: .timeCap) {
+            settings.timeCap = timeCap
+        }
+
+        if let minuteInterval = try? container.decode(Int.self, forKey: .minuteInterval) {
+            settings.minuteInterval = minuteInterval
+        }
+
         if let glucoseNotificationsAlways = try? container.decode(Bool.self, forKey: .glucoseNotificationsAlways) {
             settings.glucoseNotificationsAlways = glucoseNotificationsAlways
         }

+ 16 - 5
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -4,14 +4,17 @@ extension AddCarbs {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var carbsStorage: CarbsStorage!
         @Injected() var apsManager: APSManager!
+        @Injected() var settings: SettingsManager!
         @Published var carbs: Decimal = 0
         @Published var date = Date()
         @Published var protein: Decimal = 0
         @Published var fat: Decimal = 0
         @Published var carbsRequired: Decimal?
+        @Published var useFPU: Bool = false
 
         override func subscribe() {
             carbsRequired = provider.suggestion?.carbsReq
+            useFPU = settingsManager.settings.useFPUconversion
         }
 
         func add() {
@@ -21,17 +24,25 @@ extension AddCarbs {
             }
 
             // Convert fat and protein to carb equivalents and store as future carbs
-            let fpucarb = (0.4 * protein) + (0.9 * fat)
-            let fpus = ((fat * 9.0) + (protein * 4.0)) / 100.0
+            let fpucarb = 0.4 * protein + 0.9 * fat
+            let fpus = (fat * 9.0 + protein * 4.0) / 100.0
+            // Default is 1 hour (60 minutes)
+            let timeInterval = 60 * settings.settings.minuteInterval
+            // Deffault is 8 hours
+            let timeCap = settings.settings.timeCap
+            let adjustment = settings.settings.individualAdjustmentFactor
             var counter: Decimal = (fpus * 2) - 1.0
+            counter = min(timeCap, counter)
             var roundedCounter: Decimal = 0
             NSDecimalRound(&roundedCounter, &counter, 0, .up)
-            let carbequiv = fpucarb / roundedCounter
-            while counter > 0 {
+            let carbequiv = (fpucarb / roundedCounter) * adjustment
+
+            while counter > 0, counter <= timeCap {
                 let newdate = 1.0 + trunc(Double(truncating: counter as NSNumber))
                 carbsStorage.storeCarbs([
                     CarbsEntry(
-                        id: UUID().uuidString, createdAt: date + (newdate * 3600), carbs: carbequiv, enteredBy: CarbsEntry.manual
+                        id: UUID().uuidString, createdAt: date + (newdate * Double(timeInterval)), carbs: carbequiv, enteredBy: CarbsEntry.manual
+
                     )
                 ])
                 counter -= 1

+ 28 - 25
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -32,32 +32,35 @@ extension AddCarbs {
                             DecimalTextField("0", value: $state.carbs, formatter: formatter, autofocus: true, cleanInput: true)
                             Text("grams").foregroundColor(.secondary)
                         }.padding(.vertical)
-                        // Adding Protein and Fat. Test
-                        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)
-                        }
 
+                        // 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)
+                            }
+                        }
                         DatePicker("Date", selection: $state.date)
                     }
                 }

+ 5 - 0
FreeAPS/Sources/Modules/FPUConfig/FPUConfigDataFlow.swift

@@ -0,0 +1,5 @@
+enum FPUConfig {
+    enum Config {}
+}
+
+protocol FPUConfigProvider {}

+ 3 - 0
FreeAPS/Sources/Modules/FPUConfig/FPUConfigProvider.swift

@@ -0,0 +1,3 @@
+extension FPUConfig {
+    final class Provider: BaseProvider, FPUConfigProvider {}
+}

+ 36 - 0
FreeAPS/Sources/Modules/FPUConfig/FPUConfigStateModel.swift

@@ -0,0 +1,36 @@
+import SwiftUI
+
+extension FPUConfig {
+    final class StateModel: BaseStateModel<Provider> {
+        @Published var useFPUconversion = false
+        @Published var individualAdjustmentFactor: Decimal = 0
+        @Published var timeCap: Decimal = 0
+        @Published var minuteInterval: Decimal = 0
+
+        override func subscribe() {
+            subscribeSetting(\.useFPUconversion, on: $useFPUconversion) { useFPUconversion = $0 }
+            subscribeSetting(\.timeCap, on: $timeCap) { timeCap = $0 }
+
+            subscribeSetting(\.timeCap, on: $timeCap, initial: {
+                let value = max(min($0, 12), 8)
+                timeCap = value
+            }, map: {
+                $0
+            })
+
+            subscribeSetting(\.minuteInterval, on: $minuteInterval.map(Int.init), initial: {
+                let value = max(min($0, 90), 10)
+                minuteInterval = Decimal(value)
+            }, map: {
+                $0
+            })
+
+            subscribeSetting(\.individualAdjustmentFactor, on: $individualAdjustmentFactor, initial: {
+                let value = max(min($0, 1.2), 0.3)
+                individualAdjustmentFactor = value
+            }, map: {
+                $0
+            })
+        }
+    }
+}

+ 59 - 0
FreeAPS/Sources/Modules/FPUConfig/View/FPUConfigRootView.swift

@@ -0,0 +1,59 @@
+import SwiftUI
+import Swinject
+
+extension FPUConfig {
+    struct RootView: BaseView {
+        let resolver: Resolver
+        @StateObject var state = StateModel()
+
+        private var conversionFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 1
+
+            return formatter
+        }
+
+        private var intFormater: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.allowsFloats = false
+            return formatter
+        }
+
+        var body: some View {
+            Form {
+                Section(header: Text("Convert Fat and Protein")) {
+                    Toggle("Enable", isOn: $state.useFPUconversion)
+                }
+
+                Section(header: Text("Optional conversion settings")) {
+                    HStack {
+                        Text("Maximum Time Cap In Hours")
+                        Spacer()
+                        DecimalTextField("8", value: $state.timeCap, formatter: intFormater)
+                    }
+                    HStack {
+                        Text("Interval In Minutes")
+                        Spacer()
+                        DecimalTextField("60", value: $state.minuteInterval, formatter: intFormater)
+                    }
+                    HStack {
+                        Text("Override with a factor of ")
+                        Spacer()
+                        DecimalTextField("0.8", value: $state.individualAdjustmentFactor, formatter: conversionFormatter)
+                    }
+                }
+
+                Section(
+                    footer: Text(
+                        "Allows fat and protein to be converted to future carb equivalents.\n\nDefault settings:\n\nTime Cap: 8 hours\nInterval: 60 min\nFactor: 0.5"
+                    )
+                )
+                    {}
+            }
+            .onAppear(perform: configureView)
+            .navigationBarTitle("Fat and Protein")
+            .navigationBarTitleDisplayMode(.automatic)
+        }
+    }
+}

+ 0 - 3
FreeAPS/Sources/Modules/Settings/SettingsStateModel.swift

@@ -11,11 +11,8 @@ extension Settings {
         @Published var animatedBackground = false
 
         private(set) var buildNumber = ""
-
         private(set) var versionNumber = ""
-
         private(set) var branch = ""
-
         private(set) var copyrightNotice = ""
 
         override func subscribe() {

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

@@ -29,6 +29,7 @@ extension Settings {
                         Text("Apple Health").navigationLink(to: .healthkit, from: self)
                     }
                     Text("Notifications").navigationLink(to: .notificationsConfig, from: self)
+                    Text("Fat And Protein Conversion").navigationLink(to: .fpuConfig, from: self)
                 }
 
                 Section(header: Text("Configuration")) {

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

@@ -25,6 +25,7 @@ enum Screen: Identifiable, Hashable {
     case libreConfig
     case calibrations
     case notificationsConfig
+    case fpuConfig
     case snooze
 
     var id: Int { String(reflecting: self).hashValue }
@@ -82,6 +83,8 @@ extension Screen {
             Calibrations.RootView(resolver: resolver)
         case .notificationsConfig:
             NotificationsConfig.RootView(resolver: resolver)
+        case .fpuConfig:
+            FPUConfig.RootView(resolver: resolver)
         case .snooze:
             Snooze.RootView(resolver: resolver)
         }

+ 8 - 0
fastlane/testflight.md

@@ -70,6 +70,14 @@ _Please note that in default builds of FreeAPS X, the app group is actually iden
 1. Click "Confirm".
 1. Remember to do this for each of the identifiers above.
 
+## Add NFC Tag Reading to FreeeAPS App ID
+1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list) on the apple developer site.
+1. Click on the "FreeeAPS" identifier
+1. Scroll down to "NFC Tag Reading"
+1. Tap the check box to enable NFC Tag Reading.
+1. Click "Save".
+1. Click "Confirm".
+
 ## Create FreeAPS X App in App Store Connect
 
 If you have created a FreeAPS X app in App Store Connect before, you can skip this section as well.