Explorar o código

Merge branch 'Crowdin' into XPM

Jon B.M %!s(int64=4) %!d(string=hai) anos
pai
achega
b95659a858
Modificáronse 38 ficheiros con 630 adicións e 326 borrados
  1. 2 0
      .gitignore
  2. 7 0
      Config.xcconfig
  3. 17 7
      Dependencies/rileylink_ios/OmniKit/PumpManager/OmnipodPumpManager.swift
  4. 1 1
      Dependencies/rileylink_ios/OmniKit/PumpManager/PodCommsSession.swift
  5. 17 17
      Dependencies/rileylink_ios/OmniKitUI/tr.lproj/Localizable.strings
  6. 9 9
      Dependencies/rileylink_ios/RileyLinkKitUI/tr.lproj/Localizable.strings
  7. 9 3
      FreeAPS.xcodeproj/project.pbxproj
  8. 2 2
      FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved
  9. 0 1
      FreeAPS/Resources/Config.xcconfig
  10. 3 3
      FreeAPS/Resources/Info.plist
  11. 5 5
      FreeAPS/Resources/tr.lproj/InfoPlist.strings
  12. 1 1
      FreeAPS/Sources/APS/APSManager.swift
  13. 5 0
      FreeAPS/Sources/APS/CGM/CGMType.swift
  14. 180 0
      FreeAPS/Sources/APS/CGM/GlucoseSimulatorSource.swift
  15. 3 0
      FreeAPS/Sources/APS/FetchGlucoseManager.swift
  16. 8 3
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  17. 8 3
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  18. 8 3
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  19. 49 44
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  20. 5 0
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  21. 8 3
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  22. 8 3
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  23. 8 3
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  24. 8 3
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  25. 8 3
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  26. 8 3
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  27. 8 3
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  28. 8 3
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  29. 8 3
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  30. 8 3
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  31. 17 12
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  32. 175 170
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  33. 8 3
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  34. 8 3
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  35. 0 4
      FreeAPS/Sources/Models/Suggestion.swift
  36. 1 1
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  37. 1 0
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  38. 1 1
      FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift

+ 2 - 0
.gitignore

@@ -77,3 +77,5 @@ fastlane/Preview.html
 fastlane/screenshots
 fastlane/test_output
 fastlane/FastlaneRunner
+
+ConfigOverride.xcconfig

+ 7 - 0
Config.xcconfig

@@ -0,0 +1,7 @@
+APP_DISPLAY_NAME = FreeAPS X
+BUILD_VERSION = 0.2.3
+DEVELOPER_TEAM = ##TEAM_ID##
+BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS
+APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroups
+
+#include? "ConfigOverride.xcconfig"

+ 17 - 7
Dependencies/rileylink_ios/OmniKit/PumpManager/OmnipodPumpManager.swift

@@ -399,7 +399,7 @@ extension OmnipodPumpManager {
     private func pumpStatusHighlight(for state: OmnipodPumpManagerState) -> PumpManagerStatus.PumpStatusHighlight? {
         guard let podState = state.podState else {
             return PumpManagerStatus.PumpStatusHighlight(
-                localizedMessage: NSLocalizedString("No Pod", comment: "Status highlight that when no pod is paired."),
+                localizedMessage: LocalizedString("No Pod", comment: "Status highlight that when no pod is paired."),
                 imageName: "exclamationmark.circle.fill",
                 state: .warning)
         }
@@ -414,7 +414,7 @@ extension OmnipodPumpManager {
         if let reservoir = podState.lastInsulinMeasurements, let level = reservoir.reservoirLevel {
             if level <= 0 {
                 return PumpManagerStatus.PumpStatusHighlight(
-                    localizedMessage: NSLocalizedString("No Insulin", comment: "Status highlight that a pump is out of insulin."),
+                    localizedMessage: LocalizedString("No Insulin", comment: "Status highlight that a pump is out of insulin."),
                     imageName: "exclamationmark.circle.fill",
                     state: .critical)
             }
@@ -422,7 +422,7 @@ extension OmnipodPumpManager {
         
         if case .suspended = podState.suspendState {
             return PumpManagerStatus.PumpStatusHighlight(
-                localizedMessage: NSLocalizedString("Insulin Suspended", comment: "Status highlight that insulin delivery was suspended."),
+                localizedMessage: LocalizedString("Insulin Suspended", comment: "Status highlight that insulin delivery was suspended."),
                 imageName: "pause.circle.fill",
                 state: .warning)
         }
@@ -505,9 +505,9 @@ extension OmnipodPumpManager {
 
         let timeUntilExpiration = formatter.string(from: timeBetweenNoticeAndExpiration) ?? ""
 
-        content.title = NSLocalizedString("Pod Expiration Notice", comment: "The title for pod expiration notification")
+        content.title = LocalizedString("Pod Expiration Notice", comment: "The title for pod expiration notification")
 
-        content.body = String(format: NSLocalizedString("Time to replace your pod! Your pod will expire in %1$@", comment: "The format string for pod expiration notification body (1: time until expiration)"), timeUntilExpiration)
+        content.body = String(format: LocalizedString("Time to replace your pod! Your pod will expire in %1$@", comment: "The format string for pod expiration notification body (1: time until expiration)"), timeUntilExpiration)
         content.sound = UNNotificationSound.default
         content.categoryIdentifier = LoopNotificationCategory.pumpExpired.rawValue
         content.threadIdentifier = LoopNotificationCategory.pumpExpired.rawValue
@@ -1425,18 +1425,28 @@ extension OmnipodPumpManager: PumpManager {
                 state.bolusEngageState = .engaging
             })
 
-            // If pod suspended, resume basal before bolusing to match existing Medtronic PumpManager behavior
+            // Initialize to true to match existing Medtronic PumpManager behavior for any
+            // manual boluses or to false to never auto resume a suspended pod for any bolus.
+            let autoResumeOnManualBolus = true
+
             if case .some(.suspended) = self.state.podState?.suspendState {
+                // Pod suspended, only auto resume for a manual bolus if autoResumeOnManualBolus is true
+                if automatic || autoResumeOnManualBolus == false {
+                    self.log.error("enactBolus: returning pod suspended error for %@ bolus", automatic ? "automatic" : "manual")
+                    completion(.failure(PumpManagerError.deviceState(PodCommsError.podSuspended)))
+                    return
+                }
                 do {
                     let scheduleOffset = self.state.timeZone.scheduleOffset(forDate: Date())
                     let beep = self.confirmationBeeps
                     let podStatus = try session.resumeBasal(schedule: self.state.basalSchedule, scheduleOffset: scheduleOffset, acknowledgementBeep: beep, completionBeep: beep)
+                    try session.cancelSuspendAlerts()
                     guard podStatus.deliveryStatus.bolusing == false else {
                         completion(.failure(PumpManagerError.deviceState(PodCommsError.unfinalizedBolus)))
                         return
                     }
                 } catch let error {
-                    self.log.error("enactBolus: error resuming suspended pod: %s", String(describing: error))
+                    self.log.error("enactBolus: error resuming suspended pod: %@", String(describing: error))
                     completion(.failure(PumpManagerError.communication(error as? LocalizedError)))
                     return
                 }

+ 1 - 1
Dependencies/rileylink_ios/OmniKit/PumpManager/PodCommsSession.swift

@@ -123,7 +123,7 @@ extension PodCommsError: LocalizedError {
         case .nonceResyncFailed:
             return nil
         case .podSuspended:
-            return nil
+            return LocalizedString("Resume delivery", comment: "Recovery suggestion when pod is suspended")
         case .podFault:
             return nil
         case .commsError:

+ 17 - 17
Dependencies/rileylink_ios/OmniKitUI/tr.lproj/Localizable.strings

@@ -1,11 +1,11 @@
 /* Accessibility format string for (1: localized volume)(2: time) */
-"%1$@ units remaining at %2$@" = "%1$@ units remaining at %2$@";
+"%1$@ units remaining at %2$@" = "%1$@ ünite %2$@'de kaldı";
 
 /* The format string for displaying an offset from a time zone: (1: GMT)(2: -)(3: 4:00) */
 "%1$@%2$@%3$@" = "%1$@%2$@%3$@";
 
 /* Format string providing instructions for replacing pod due to a fault. (1: The fault description) */
-"%1$@. Insulin delivery has stopped. Please deactivate and remove pod." = "%1$@. Insulin delivery has stopped. Please deactivate and remove pod.";
+"%1$@. Insulin delivery has stopped. Please deactivate and remove pod." = "%1$@. İnsülin iletimi durduruldu. Lütfen pod'u devre dışı bırakın ve çıkartın.";
 
 /* Format string for delivered insulin. (1: The localized amount)
    Format string for insulin remaining in reservoir. (1: The localized amount) */
@@ -15,7 +15,7 @@
 "%@ U (Finished)" = "%@ Ü (Tamamlandı)";
 
 /* Format string for bolus progress. (1: The delivered amount) (2: The programmed amount) (3: the percent progress) */
-"%@ U of %@ U (%@)" = "%1$@ U of %2$@ U (%3$@)";
+"%@ U of %@ U (%@)" = "%2$@ Ü den %1$@ Ü (%3$@)";
 
 /* Format string for temp basal rate. (1: The localized amount) */
 "%@ U/hour" = "%@ Ü/Sa";
@@ -33,10 +33,10 @@
 "%@U" = "%@Ü";
 
 /* The title of the cell showing the pod activated at time */
-"Active Time" = "Active Time";
+"Active Time" = "Aktif Süre";
 
 /* The title of the cell showing the Pod Active Clock */
-"Pod Active Clock" = "Pod Active Clock";
+"Pod Active Clock" = "Pod Aktif Saati";
 
 /* The title of the cell showing alarm status */
 "Alarms" = "Alarmlar";
@@ -45,7 +45,7 @@
 "Are you sure you want to shutdown this pod?" = "Bu pod'u iptal etmek istediğinizden emin misiniz?";
 
 /* Confirmation message for removing Omnipod PumpManager */
-"Are you sure you want to stop using Omnipod?" = "Are you sure you want to stop using Omnipod?";
+"Are you sure you want to stop using Omnipod?" = "Omnipod'u kullanmayı bırakmak istediğinizden emin misiniz?";
 
 /* The title text for the address assigned to the pod */
 "Assigned Address" = "Adress Atandı";
@@ -94,28 +94,28 @@
 "Device Information" = "Cihaz Bilgileri";
 
 /* Title text for button to disable bolus beeps */
-"Disable Bolus Beeps" = "Disable Bolus Beeps";
+"Disable Bolus Beeps" = "Bolus Uyarılarını Devre Dışı Bırak";
 
 /* Title text for button to enable bolus beeps */
-"Enable Bolus Beeps" = "Enable Bolus Beeps";
+"Enable Bolus Beeps" = "Bolus Uyarılarını Etkinleştir";
 
 /* The alert title for disable bolus beeps error */
-"Error disabling bolus beeps" = "Error disabling bolus beeps";
+"Error disabling bolus beeps" = "Bolus uyarılarını devre dışı bırakılırken hata oluştu";
 
 /* The alert title for enable bolus beeps error */
-"Error enabling bolus beeps" = "Error enabling bolus beeps";
+"Error enabling bolus beeps" = "Bolus uyarılarını etkinleştirirken hata oluştu";
 
 /* The alert title for enable Confirmation Beeps */
-"Enable Confirmation Beeps" = "Enable Confirmation Beeps";
+"Enable Confirmation Beeps" = "Onay Uyarılarını Etkinleştir";
 
 /* The alert title for Disable Confirmation Beep */
-"Disable Confirmation Beeps" = "Disable Confirmation Beeps";
+"Disable Confirmation Beeps" = "Onay Uyarılarını Devre Dışı Bırak";
 
 /* The alert title for enable Automatic Bolus Beeps */
-"Enable Automatic Bolus Beeps" = "Enable Automatic Bolus Beeps";
+"Enable Automatic Bolus Beeps" = "Otomatik Bolus Uyarılarını Etkinleştir";
 
 /* The alert title for Disable Automatic Bolus Beeps */
-"Disable Automatic Bolus Beeps" = "Disable Automatic Bolus Beeps";
+"Disable Automatic Bolus Beeps" = "Otomatik Bolus Uyarılarını Devre Dışı Bırak";
 
 /* The alert title for a resume error */
 "Error Resuming" = "Devam ederken hata oluştu";
@@ -139,13 +139,13 @@
 "Finish pod setup" = "Pod kurulumu bitir";
 
 /* Accessibility format string for (1: localized volume)(2: time) */
-"Greater than %1$@ units remaining at %2$@" = "Greater than %1$@ units remaining at %2$@";
+"Greater than %1$@ units remaining at %2$@" = "%2$@ 'te %1$@ üniteden fazla kaldı";
 
 /* Instructions when deactivating pod that has been paired, but not attached. */
-"Incompletely set up pod must be deactivated before pairing with a new one. Please deactivate and discard pod." = "Incompletely set up pod must be deactivated before pairing with a new one. Please deactivate and discard pod.";
+"Incompletely set up pod must be deactivated before pairing with a new one. Please deactivate and discard pod." = "Eksik ayarlanmış pod, yenisiyle eşleştirmeden önce devre dışı bırakılmalıdır. Lütfen pod'u devre dışı bırakın ve atın.";
 
 /* Instructions when deactivating pod that has been paired and possibly attached. */
-"Incompletely set up pod must be deactivated before pairing with a new one. Please deactivate and remove pod." = "Incompletely set up pod must be deactivated before pairing with a new one. Please deactivate and remove pod.";
+"Incompletely set up pod must be deactivated before pairing with a new one. Please deactivate and remove pod." = "Eksik ayarlanmış pod, yenisiyle eşleştirmeden önce devre dışı bırakılmalıdır. Lütfen pod'u devre dışı bırakın ve çıkartın.";
 
 /* Button title to insert cannula during setup */
 "Insert Cannula" = "Kanül Yerleştir";

+ 9 - 9
Dependencies/rileylink_ios/RileyLinkKitUI/tr.lproj/Localizable.strings

@@ -41,7 +41,7 @@
 "Connection Monitoring" = "Bağlantı İzleme";
 
 /* The title of the cell showing uptime */
-"Uptime" = "Uptime";
+"Uptime" = "Çalışma süresi";
 
 /* The title of the cell showing battery level */
 "Battery level" = "Pil seviyesi";
@@ -62,22 +62,22 @@
 "OFF" = "KAPA";
 
 /* The title of the command to update diagnostic LEDs */
-"Diagnostic LEDs" = "Diagnostic LEDs";
+"Diagnostic LEDs" = "Sistem kontrol LEDleri";
 
 /* The title of the command to fetch RileyLink statistics */
 "Get RileyLink Statistics" = "RileyLink İstatistiklerini Al";
 
 /* The title of the command to invert BLE connection LED logic */
-"Invert LED Logic" = "Invert LED Logic";
+"Invert LED Logic" = "LED Mantığını Ters Çevir";
 
 /* The header of the cells showing test commands */
-"Test Commands" = "Test Commands";
+"Test Commands" = "Komutları Dene";
 
 /* The title of the cell showing Lighten Yellow LED */
-"Lighten Yellow LED" = "Lighten Yellow LED";
+"Lighten Yellow LED" = "Sarı LED'i Aç";
 
 /* The title of the cell showing Lighten Yellow LED */
-"Lighten Red LED" = "Lighten Red LED";
+"Lighten Red LED" = "Kırmızı LED'i Aç";
 
 /* The title of the cell showing Test Vibration */
 "Test Vibration" = "Titreşimi Dene";
@@ -92,13 +92,13 @@
 "Connection Vibration" = "Bağlanıldığında Titreş";
 
 /* Detail text when battery alert disabled. */
-"Off" = "Off";
+"Off" = "Kapa";
 
 /* Text indicating LED Mode is on */
-"On" = "On";
+"On" = "";
 
 /* Text indicating LED Mode is off */
-"Off" = "Off";
+"Off" = "Kapa";
 
 /* Text indicating LED Mode is auto */
 "Auto" = "Oto";

+ 9 - 3
FreeAPS.xcodeproj/project.pbxproj

@@ -243,6 +243,7 @@
 		E00EEC0627368630002FF094 /* UIAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00EEC0027368630002FF094 /* UIAssembly.swift */; };
 		E00EEC0727368630002FF094 /* APSAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00EEC0127368630002FF094 /* APSAssembly.swift */; };
 		E00EEC0827368630002FF094 /* NetworkAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00EEC0227368630002FF094 /* NetworkAssembly.swift */; };
+		E013D872273AC6FE0014109C /* GlucoseSimulatorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E013D871273AC6FE0014109C /* GlucoseSimulatorSource.swift */; };
 		E13B7DAB2A435F57066AF02E /* TargetsEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36F58DDD71F0E795464FA3F0 /* TargetsEditorStateModel.swift */; };
 		E25073BC86C11C3D6A42F5AC /* CalibrationsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47DFCE895C930F784EF11843 /* CalibrationsStateModel.swift */; };
 		E39E418C56A5A46B61D960EE /* ConfigEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */; };
@@ -378,6 +379,7 @@
 		3811DEE725CA063400A708ED /* PersistedProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistedProperty.swift; sourceTree = "<group>"; };
 		3811DF0125CA9FEA00A708ED /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
 		3811DF0F25CAAAE200A708ED /* APSManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APSManager.swift; sourceTree = "<group>"; };
+		3818AA42274BBC1100843DB3 /* ConfigOverride.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigOverride.xcconfig; sourceTree = "<group>"; };
 		38192E03261B82FA0094D973 /* ReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityManager.swift; sourceTree = "<group>"; };
 		38192E06261BA9960094D973 /* FetchTreatmentsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchTreatmentsManager.swift; sourceTree = "<group>"; };
 		38192E0C261BAF980094D973 /* ConvenienceExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvenienceExtensions.swift; sourceTree = "<group>"; };
@@ -560,6 +562,7 @@
 		E00EEC0027368630002FF094 /* UIAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIAssembly.swift; sourceTree = "<group>"; };
 		E00EEC0127368630002FF094 /* APSAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APSAssembly.swift; sourceTree = "<group>"; };
 		E00EEC0227368630002FF094 /* NetworkAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkAssembly.swift; sourceTree = "<group>"; };
+		E013D871273AC6FE0014109C /* GlucoseSimulatorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseSimulatorSource.swift; sourceTree = "<group>"; };
 		E2EBA7C03C26FCC67E16D798 /* LibreConfigProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LibreConfigProvider.swift; sourceTree = "<group>"; };
 		E68CDC1E5C438D1BEAD4CF24 /* LibreConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LibreConfigStateModel.swift; sourceTree = "<group>"; };
 		E9AAB83FB6C3B41EFD1846A0 /* AddTempTargetRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddTempTargetRootView.swift; sourceTree = "<group>"; };
@@ -871,7 +874,6 @@
 				388E596425AD948E0019842D /* Info.plist */,
 				1927C8E82744606D00347C69 /* InfoPlist.strings */,
 				388E595F25AD948E0019842D /* Assets.xcassets */,
-				38F3783A2613555C009DB701 /* Config.xcconfig */,
 			);
 			path = Resources;
 			sourceTree = "<group>";
@@ -935,6 +937,7 @@
 				38569344270B5DFA0002C50D /* CGMType.swift */,
 				386A124E271707F000DDC61C /* DexcomSource.swift */,
 				38569345270B5DFA0002C50D /* GlucoseSource.swift */,
+				E013D871273AC6FE0014109C /* GlucoseSimulatorSource.swift */,
 				38FEF407273B011A00574A46 /* LibreTransmitterSource.swift */,
 				3862CC03273D150600BF832C /* Calibrations */,
 			);
@@ -972,6 +975,8 @@
 		388E594F25AD948C0019842D = {
 			isa = PBXGroup;
 			children = (
+				3818AA42274BBC1100843DB3 /* ConfigOverride.xcconfig */,
+				38F3783A2613555C009DB701 /* Config.xcconfig */,
 				388E595A25AD948C0019842D /* FreeAPS */,
 				38FCF3EE25E9028E0078B0D1 /* FreeAPSTests */,
 				388E595925AD948C0019842D /* Products */,
@@ -1712,6 +1717,7 @@
 				3811DE4225C9D4A100A708ED /* SettingsDataFlow.swift in Sources */,
 				3811DE2525C9D48300A708ED /* MainRootView.swift in Sources */,
 				3811DE3125C9D49500A708ED /* HomeProvider.swift in Sources */,
+				E013D872273AC6FE0014109C /* GlucoseSimulatorSource.swift in Sources */,
 				388E5A5C25B6F0770019842D /* JSON.swift in Sources */,
 				3811DF0225CA9FEA00A708ED /* Credentials.swift in Sources */,
 				389A572026079BAA00BC102F /* Interpolation.swift in Sources */,
@@ -2073,7 +2079,7 @@
 		388E596825AD948E0019842D /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APP_GROUP_ID = "group.com.${DEVELOPMENT_TEAM}.loopkit.LoopGroup";
+				APP_GROUP_ID = "$(APP_GROUP_ID)";
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = FreeAPS/Resources/FreeAPS.entitlements;
@@ -2099,7 +2105,7 @@
 		388E596925AD948E0019842D /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				APP_GROUP_ID = "group.com.${DEVELOPMENT_TEAM}.loopkit.LoopGroup";
+				APP_GROUP_ID = "$(APP_GROUP_ID)";
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = FreeAPS/Resources/FreeAPS.entitlements;

+ 2 - 2
FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -24,8 +24,8 @@
         "repositoryURL": "https://github.com/ivalkou/LibreTransmitterX",
         "state": {
           "branch": null,
-          "revision": "af874b58f03554d92053e06d33fd1b638b721552",
-          "version": "1.0.8"
+          "revision": "9c5d68262eef51365114213ebd4dee91687eeeed",
+          "version": "1.0.12"
         }
       },
       {

+ 0 - 1
FreeAPS/Resources/Config.xcconfig

@@ -1 +0,0 @@
-BUILD_VERSION = 0.2.3

+ 3 - 3
FreeAPS/Resources/Info.plist

@@ -17,7 +17,7 @@
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundleName</key>
-	<string>FreeAPS X</string>
+	<string>$(APP_DISPLAY_NAME)</string>
 	<key>CFBundlePackageType</key>
 	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
 	<key>CFBundleShortVersionString</key>
@@ -60,6 +60,8 @@
 	<string>Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices</string>
 	<key>NSBluetoothPeripheralUsageDescription</key>
 	<string>Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices</string>
+	<key>NSCalendarsUsageDescription</key>
+	<string>Calendar is used to create a new glucose events.</string>
 	<key>NSFaceIDUsageDescription</key>
 	<string>For authorized acces to bolus</string>
 	<key>UIApplicationSceneManifest</key>
@@ -69,8 +71,6 @@
 	</dict>
 	<key>UIApplicationSupportsIndirectInputEvents</key>
 	<true/>
-	<key>NSCalendarsUsageDescription</key>
-	<string>Calendar is used to create a new glucose events.</string>
 	<key>UIBackgroundModes</key>
 	<array>
 		<string>bluetooth-central</string>

+ 5 - 5
FreeAPS/Resources/tr.lproj/InfoPlist.strings

@@ -1,15 +1,15 @@
 
 /* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
+"NFCReaderUsageDescription" = "NFC Libre sensörlerini taramak için kullanılır.";
 
 /* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
+"NSBluetoothAlwaysUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
 
 /* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
+"NSBluetoothPeripheralUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
 
 /* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
+"NSFaceIDUsageDescription" = "Bolus'a yetkili erişim için";
 
 /* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
+"NSCalendarsUsageDescription" = "Takvim, yeni bir glikoz olayı oluşturmak için kullanılır.";

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

@@ -535,7 +535,7 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     private func reportEnacted(suggestion: Suggestion, received: Bool) {
-        if suggestion.deliverAt != nil, suggestion.rate != nil || suggestion.units != nil {
+        if suggestion.deliverAt != nil {
             var enacted = suggestion
             enacted.timestamp = Date()
             enacted.recieved = received

+ 5 - 0
FreeAPS/Sources/APS/CGM/CGMType.swift

@@ -7,6 +7,7 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
     case xdrip
     case dexcomG6
     case dexcomG5
+    case simulator
     case libreTransmitter
 
     var displayName: String {
@@ -19,6 +20,8 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
             return "Dexcom G6"
         case .dexcomG5:
             return "Dexcom G5"
+        case .simulator:
+            return NSLocalizedString("Glucose Simulator", comment: "Glucose Simulator CGM type")
         case .libreTransmitter:
             return NSLocalizedString("Libre Transmitter", comment: "Libre Transmitter type")
         }
@@ -34,6 +37,8 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
             return URL(string: "dexcomg6://")!
         case .dexcomG5:
             return URL(string: "dexcomgcgm://")!
+        case .simulator:
+            return nil
         case .libreTransmitter:
             return URL(string: "freeaps-x://libre-transmitter")!
         }

+ 180 - 0
FreeAPS/Sources/APS/CGM/GlucoseSimulatorSource.swift

@@ -0,0 +1,180 @@
+/// Glucose source - Blood Glucose Simulator
+///
+/// Source publish fake data about glucose's level, creates ascending and descending trends
+///
+/// Enter point of Source is GlucoseSimulatorSource.fetch method. Method is called from FetchGlucoseManager module.
+/// Not more often than a specified period (default - 300 seconds), it returns a Combine-publisher that publishes data on glucose values (global type BloodGlucose). If there is no up-to-date data (or the publication period has not passed yet), then a publisher of type Empty is returned, otherwise it returns a publisher of type Just.
+///
+/// Simulator composition
+/// ===================
+///
+/// class GlucoseSimulatorSource - main class
+/// protocol BloodGlucoseGenerator
+///  - IntelligentGenerator: BloodGlucoseGenerator
+
+// TODO: Every itteration trend make two steps, but must only one
+
+// TODO: Trend's value sticks to max and min Glucose value (in Glucose Generator)
+
+// TODO: Add reaction to insulin
+
+// TODO: Add probability to set trend's target value. Middle values must have more probability, than max and min.
+
+import Combine
+import Foundation
+
+// MARK: - Glucose simulator
+
+final class GlucoseSimulatorSource: GlucoseSource {
+    private enum Config {
+        // min time period to publish data
+        static let workInterval: TimeInterval = 300
+        // default BloodGlucose item at first run
+        // 288 = 1 day * 24 hours * 60 minites * 60 seconds / workInterval
+        static let defaultBGItems = 288
+    }
+
+    @Persisted(key: "GlucoseSimulatorLastGlucose") private var lastGlucose = 100
+
+    @Persisted(key: "GlucoseSimulatorLastFetchDate") private var lastFetchDate: Date! = nil
+
+    init() {
+        if lastFetchDate == nil {
+            var lastDate = Date()
+            for _ in 1 ... Config.defaultBGItems {
+                lastDate = lastDate.addingTimeInterval(-Config.workInterval)
+            }
+            lastFetchDate = lastDate
+        }
+    }
+
+    private lazy var generator: BloodGlucoseGenerator = {
+        IntelligentGenerator(
+            currentGlucose: lastGlucose
+        )
+    }()
+
+    private var canGenerateNewValues: Bool {
+        guard let lastDate = lastFetchDate else { return true }
+        if Calendar.current.dateComponents([.second], from: lastDate, to: Date()).second! >= Int(Config.workInterval) {
+            return true
+        } else {
+            return false
+        }
+    }
+
+    func fetch() -> AnyPublisher<[BloodGlucose], Never> {
+        guard canGenerateNewValues else {
+            return Empty().eraseToAnyPublisher()
+        }
+
+        let glucoses = generator.getBloodGlucoses(
+            startDate: lastFetchDate,
+            finishDate: Date(),
+            withInterval: Config.workInterval
+        )
+
+        if let lastItem = glucoses.last {
+            lastGlucose = lastItem.glucose!
+            lastFetchDate = Date()
+        }
+
+        return Just(glucoses.reversed()).eraseToAnyPublisher()
+    }
+}
+
+// MARK: - Glucose generator
+
+protocol BloodGlucoseGenerator {
+    func getBloodGlucoses(startDate: Date, finishDate: Date, withInterval: TimeInterval) -> [BloodGlucose]
+}
+
+class IntelligentGenerator: BloodGlucoseGenerator {
+    private enum Config {
+        // max and min glucose of trend's target
+        static let maxGlucose = 320
+        static let minGlucose = 45
+    }
+
+    // target glucose of trend
+    @Persisted(key: "GlucoseSimulatorTargetValue") private var trendTargetValue = 100
+    // how many steps left in current trend
+    @Persisted(key: "GlucoseSimulatorTargetSteps") private var trendStepsLeft = 1
+    // direction of last step
+    @Persisted(key: "GlucoseSimulatorDirection") private var trandsStepDirection = BloodGlucose.Direction.flat.rawValue
+    var currentGlucose: Int
+
+    init(currentGlucose: Int) {
+        self.currentGlucose = currentGlucose
+    }
+
+    func getBloodGlucoses(startDate: Date, finishDate: Date, withInterval interval: TimeInterval) -> [BloodGlucose] {
+        var result = [BloodGlucose]()
+
+        var _currentDate = startDate
+        while _currentDate <= finishDate {
+            result.append(getNextBloodGlucose(forDate: _currentDate))
+            _currentDate = _currentDate.addingTimeInterval(interval)
+        }
+
+        return result
+    }
+
+    // get next glucose's value in current trend
+    private func getNextBloodGlucose(forDate date: Date) -> BloodGlucose {
+        let previousGlucose = currentGlucose
+        makeStepInTrend()
+        trandsStepDirection = getDirection(fromGlucose: previousGlucose, toGlucose: currentGlucose).rawValue
+        let glucose = BloodGlucose(
+            _id: UUID().uuidString,
+            sgv: nil,
+            direction: BloodGlucose.Direction(rawValue: trandsStepDirection),
+            date: Decimal(Int(date.timeIntervalSince1970) * 1000),
+            dateString: date,
+            unfiltered: nil,
+            filtered: nil,
+            noise: nil,
+            glucose: currentGlucose,
+            type: nil
+        )
+        return glucose
+    }
+
+    private func setNewRandomTarget() {
+        guard trendTargetValue > 0 else {
+            trendTargetValue = Array(80 ... 110).randomElement()!
+            return
+        }
+        let difference = (Array(-50 ... -20) + Array(20 ... 50)).randomElement()!
+        let _value = trendTargetValue + difference
+        if _value <= Config.minGlucose {
+            trendTargetValue = Config.minGlucose
+        } else if _value >= Config.maxGlucose {
+            trendTargetValue = Config.maxGlucose
+        } else {
+            trendTargetValue = _value
+        }
+    }
+
+    private func setNewRandomSteps() {
+        trendStepsLeft = Array(3 ... 8).randomElement()!
+    }
+
+    private func getDirection(fromGlucose from: Int, toGlucose to: Int) -> BloodGlucose.Direction {
+        BloodGlucose.Direction(trend: to - from)
+    }
+
+    private func generateNewTrend() {
+        setNewRandomTarget()
+        setNewRandomSteps()
+    }
+
+    private func makeStepInTrend() {
+        currentGlucose +=
+            Int(Double((trendTargetValue - currentGlucose) / trendStepsLeft) * [0.3, 0.6, 1, 1.3, 1.6].randomElement()!)
+        trendStepsLeft -= 1
+        if trendStepsLeft == 0 {
+            generateNewTrend()
+        }
+    }
+}

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

@@ -18,6 +18,7 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
 
     private lazy var appGroupSource = AppGroupSource()
     private lazy var dexcomSource = DexcomSource()
+    private lazy var simulatorSource = GlucoseSimulatorSource()
 
     init(resolver: Resolver) {
         injectServices(resolver)
@@ -36,6 +37,8 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
             glucoseSource = dexcomSource
         case .nightscout:
             glucoseSource = nightscoutManager
+        case .simulator:
+            glucoseSource = simulatorSource
         case .libreTransmitter:
             glucoseSource = libreTransmitter
         }

+ 8 - 3
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 49 - 44
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings

@@ -438,7 +438,7 @@ Enact a temp Basal or a temp target */
 "Select the third party transmitter you want to connect to" = "Wählen Sie den externen Sender, den Sie verbinden möchten";
 
 /* State was restored */
-"State was restored" = "State was restored";
+"State was restored" = "Status wurde wieder hergestellt";
 
 /* The short unit display string for millimoles of glucose per liter */
 "mmol/L" = "mmol/L";
@@ -501,10 +501,10 @@ Enact a temp Basal or a temp target */
 "Pair Sensor & connect" = "Sensor koppeln & verbinden";
 
 /* */
-"Phone NFC required!" = "Phone NFC required!";
+"Phone NFC required!" = "NFC ist erforderlich!";
 
 /* */
-"Your phone or app is not enabled for NFC communications, which is needed to pair to libre2 sensors" = "Your phone or app is not enabled for NFC communications, which is needed to pair to libre2 sensors";
+"Your phone or app is not enabled for NFC communications, which is needed to pair to libre2 sensors" = "Ihr Telefon oder Ihre App ist für NFC-Kommunikation nicht aktiviert, was benötigt wird, um eine Verbindung mit Libre2-Sensoren herzustellen";
 
 /* Bluetooth Power Off */
 "Bluetooth Power Off" = "Bluetooth ausschalten";
@@ -519,22 +519,22 @@ Enact a temp Basal or a temp target */
 "Delete CGMManager and start anew. Your libreoopweb credentials will be preserved" = "CGMManager löschen und neu starten. Ihre libreoopweb Zugangsdaten bleiben erhalten";
 
 /* Invalid libre checksum */
-"Invalid libre checksum" = "Invalid libre checksum";
+"Invalid libre checksum" = "Ungültige Libre Datei-Prüfsumme";
 
 /* Libre sensor was incorrectly read, CRCs were not valid */
-"Libre sensor was incorrectly read, CRCs were not valid" = "Libre sensor was incorrectly read, CRCs were not valid";
+"Libre sensor was incorrectly read, CRCs were not valid" = "Libre Sensor wurde unkorrekt ausgelesen, CRCs waren nicht gültig";
 
 /* Glucose */
-"Glucose" = "Glucose";
+"Glucose" = "Blutzucker";
 
 /* LOWALERT! */
-"LOWALERT!" = "LOWALERT!";
+"LOWALERT!" = "Unterzuckerungs Alarm!";
 
 /* HIGHALERT! */
-"HIGHALERT!" = "HIGHALERT!";
+"HIGHALERT!" = "Überzuckerungs Alarm!";
 
 /* (Snoozed)*/
-"(Snoozed)" = "(Snoozed)";
+"(Snoozed)" = "(Schlummern)";
 
 /* Glucose: %@ */
 "Glucose: %@" = "Glucose: %@";
@@ -543,82 +543,82 @@ Enact a temp Basal or a temp target */
 "Transmitter: %@%%" = "Transmitter: %@%%";
 
 /* No Sensor Detected */
-"No Sensor Detected" = "No Sensor Detected";
+"No Sensor Detected" = "Kein Sensor entdeckt";
 
 /* This might be an intermittent problem, but please check that your transmitter is tightly secured over your sensor */
-"This might be an intermittent problem, but please check that your transmitter is tightly secured over your sensor" = "This might be an intermittent problem, but please check that your transmitter is tightly secured over your sensor";
+"This might be an intermittent problem, but please check that your transmitter is tightly secured over your sensor" = "Dies könnte ein vorübergehendes Problem sein, aber bitte überprüfen Sie, ob Ihr Sender fest über Ihren Sensor gesichert ist";
 
 /* New Sensor Detected */
-"New Sensor Detected" = "New Sensor Detected";
+"New Sensor Detected" = "Neuer Sensor wurde erkannt";
 
 /* Please wait up to 30 minutes before glucose readings are available! */
-"Please wait up to 30 minutes before glucose readings are available!" = "Please wait up to 30 minutes before glucose readings are available!";
+"Please wait up to 30 minutes before glucose readings are available!" = "Bitte warte bis zu 30 Minuten, bevor Glukosemessungen verfügbar sind!";
 
 /* Invalid Glucose sample detected, try again later */
-"Invalid Glucose sample detected, try again later" = "Invalid Glucose sample detected, try again later";
+"Invalid Glucose sample detected, try again later" = "Ungültiger Glucose-Wert erkannt, versuchen Sie es später erneut";
 
 /* ensor might have temporarily stopped, fallen off or is too cold or too warm */
-"Sensor might have temporarily stopped, fallen off or is too cold or too warm" = "Sensor might have temporarily stopped, fallen off or is too cold or too warm";
+"Sensor might have temporarily stopped, fallen off or is too cold or too warm" = "Möglicherweise ist der Sensor vorübergehend gestoppt, hat sich gelöst, ist zu kalt oder zu warm";
 
 /* Invalid Sensor Detected */
-"Invalid Sensor Detected" = "Invalid Sensor Detected";
+"Invalid Sensor Detected" = "Fehlerhafter Sensor erkannt";
 
 /* Detected sensor seems not to be a libre 1 sensor! */
-"Detected sensor seems not to be a libre 1 sensor!" = "Detected sensor seems not to be a libre 1 sensor!";
+"Detected sensor seems not to be a libre 1 sensor!" = "Der erkannte Sensor scheint kein Libre 1 Sensor zu sein!";
 
 /* Detected sensor is invalid: %@ */
-"Detected sensor is invalid: %@" = "Detected sensor is invalid: %@";
+"Detected sensor is invalid: %@" = "Erkannter Sensor ist ungültig: %@";
 
 /* Low Battery */
-"Low Battery" = "Low Battery";
+"Low Battery" = "Batterie schwach";
 
 /* Battery is running low %@, consider charging your %@ device as soon as possible */
-"Battery is running low %@, consider charging your %@ device as soon as possible" = "Battery is running low %@, consider charging your %@ device as soon as possible";
+"Battery is running low %@, consider charging your %@ device as soon as possible" = "Batteriestand ist niedrig %@, lade dein %@ Gerät so schnell wie möglich auf";
 
 /* Extracting calibrationdata from sensor */
-"Extracting calibrationdata from sensor" = "Extracting calibrationdata from sensor";
+"Extracting calibrationdata from sensor" = "Kalibrationsdaten aus Sensor extrahieren";
 
 /* Sensor Ending Soon */
-"Sensor Ending Soon" = "Sensor Ending Soon";
+"Sensor Ending Soon" = "Sensorlaufzeit endet bald";
 
 /* Current Sensor is Ending soon! Sensor Life left in %@ */
-"Current Sensor is Ending soon! Sensor Life left in %@" = "Current Sensor is Ending soon! Sensor Life left in %@";
+"Current Sensor is Ending soon! Sensor Life left in %@" = "Der Sensorlaufzeit endet bald! Sensorlaufzeit übrig in %@";
 
 /* */
 "Libre Bluetooth" = "Libre Bluetooth";
 
 /* */
-"Snooze Alerts" = "Snooze Alerts";
+"Snooze Alerts" = "Alarme vorübergehend ausschalten";
 
 /* */
-"Last measurement" = "Last measurement";
+"Last measurement" = "Letzte Messung";
 
 /* */
 "Sensor Footer checksum" = "Sensor Footer checksum";
 
 /* */
-"Last Blood Sugar prediction" = "Last Blood Sugar prediction";
+"Last Blood Sugar prediction" = "Letzte Blutzuckervorhersage";
 
 /* */
-"CurrentBG" = "CurrentBG";
+"CurrentBG" = "AktuellerBG";
 
 /* */
 "Sensor Info" = "Sensor Info";
 
 /* */
-"Sensor Age" = "Sensor Age";
+"Sensor Age" = "Sensoralter";
 
 /* */
-"Sensor Age Left" = "Sensor Age Left";
+"Sensor Age Left" = "Sensorlaufzeit übrig";
 
 /* */
-"Sensor Endtime" = "Sensor Endtime";
+"Sensor Endtime" = "Sensorlaufzeit Ende";
 
 /* */
-"Sensor State" = "Sensor State";
+"Sensor State" = "Sensor Status";
 
 /* */
-"Sensor Serial" = "Sensor Serial";
+"Sensor Serial" = "Sensor Serien Nummer";
 
 /* */
 "Transmitter Info" = "Transmitter Info";
@@ -630,33 +630,33 @@ Enact a temp Basal or a temp target */
 "Firmware" = "Firmware";
 
 /* */
-"Connection State" = "Connection State";
+"Connection State" = "Verbindungsstatus";
 
 /* */
-"Transmitter Type" = "Transmitter Type";
+"Transmitter Type" = "Transmitter-Typ";
 
 /* */
-"Sensor Type" = "Sensor Type";
+"Sensor Type" = "Sensor-Typ";
 
 /* */
-"Factory Calibration Parameters" = "Factory Calibration Parameters";
+"Factory Calibration Parameters" = "Werkskalibrierungsparameter";
 
 /* */
 "Valid for footer" = "Valid for footer";
 
 /* */
-"Edit calibrations" = "Edit calibrations";
+"Edit calibrations" = "Kalibrierung bearbeiten";
 
 /* */
-"edit calibration clicked" = "edit calibration clicked";
+"edit calibration clicked" = "kalibration bearbeiten angeklickt";
 
 /* */
-"Delete CGM" = "Delete CGM";
+"Delete CGM" = "CGM löschen";
 /* */
-"Are you sure you want to remove this cgm from loop?" = "Are you sure you want to remove this cgm from loop?";
+"Are you sure you want to remove this cgm from loop?" = "Sind Sie sicher, dass Sie dieses Cgm aus der Liste löschen wollen?";
 
 /* */
-"There is no undo" = "There is no undo";
+"There is no undo" = "Rücknahme nicht möglich";
 
 /* */
 "Advanced" = "Advanced";
@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS weitere Einstellungen";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 5 - 0
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings

@@ -803,6 +803,11 @@ Enact a temp Basal or a temp target */
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS andre innstillinger";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings

@@ -715,10 +715,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -804,6 +804,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Вибрировать";
 "Additional notification types" = "Дополнительные уведомления";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Разное";
+/* */
+"Misc" = "Разное";
 
 /* */
 "Unit override" = "Переопределение единицы";
@@ -802,6 +802,11 @@ Misc" = "Разное";
 
 "OpenAPS other settings" = "Другие настройки OpenAPS";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 17 - 12
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings

@@ -594,7 +594,7 @@ Enact a temp Basal or a temp target */
 "Last measurement" = "Senaste mätning";
 
 /* */
-"Sensor Footer checksum" = "Sensor Footer checksum";
+"Sensor Footer checksum" = "Kontrollsumma för sensor";
 
 /* */
 "Last Blood Sugar prediction" = "Senaste blodsockerförutsägelse";
@@ -642,7 +642,7 @@ Enact a temp Basal or a temp target */
 "Factory Calibration Parameters" = "Parametrar för fabrikskalibrering";
 
 /* */
-"Valid for footer" = "Valid for footer";
+"Valid for footer" = "Giltig för sidfot";
 
 /* */
 "Edit calibrations" = "Ändra kalibreringar";
@@ -689,7 +689,7 @@ Enact a temp Basal or a temp target */
 "No logs available" = "Inga loggar tillgängliga";
 
 /* */
-"Glucose Notification visibility" = "Glucose Notification visibility";
+"Glucose Notification visibility" = "Blodsockernotiser";
 
 /* */
 "Always Notify Glucose" = "Meddela alltid glukos";
@@ -713,10 +713,10 @@ Also vibrate" = "Vibrera också";
 "Additional notification types" = "Ytterligare notistyper";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Övrigt";
+/* */
+"Misc" = "Övrigt";
 
 /* */
 "Unit override" = "Åsidosätt enhet";
@@ -731,13 +731,13 @@ Misc" = "Övrigt";
 "Schedule %d" = "Schema %d";
 
 /* */
-"tapped save schedules" = "tapped save schedules";
+"tapped save schedules" = "klickat på sparade scheman";
 
 /* */
 "Error" = "Fel";
 
 /* */
-"Some ui element was incorrectly specified" = "Some ui element was incorrectly specified";
+"Some ui element was incorrectly specified" = "Vissa UI-element felaktigt specificerade";
 
 /* */
 "Success" = "Lyckades";
@@ -746,13 +746,13 @@ Misc" = "Övrigt";
 "Schedules were saved successfully!" = "Scheman har sparats!";
 
 /* */
-"High Glucose Alarm active" = "High Glucose Alarm active";
+"High Glucose Alarm active" = "Högt blodsockerlarm aktivt";
 
 /* */
-"Low Glucose Alarm active" = "Low Glucose Alarm active";
+"Low Glucose Alarm active" = "Lågt blodsockerlarm aktivt";
 
 /* */
-"No Glucose Alarm active" = "No Glucose Alarm active";
+"No Glucose Alarm active" = "Inget blodsockerlarm aktivt";
 
 /* */
 "snoozing until %@" = "snoozar till %@";
@@ -764,7 +764,7 @@ Misc" = "Övrigt";
 "nothing to see here" = "inget att se här";
 
 /* */
-"snooze from testview clicked" = "snooze from testview clicked";
+"snooze from testview clicked" = "klickat på snooze från testvy";
 
 /* */
 "will snooze for %@ until %@" = "kommer att snooza i %@ tills %@";
@@ -802,6 +802,11 @@ Misc" = "Övrigt";
 
 "OpenAPS other settings" = "OpenAPS andra inställningar";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glukossimulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 175 - 170
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


+ 8 - 3
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 8 - 3
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings

@@ -713,10 +713,10 @@ Also vibrate" = "Also vibrate";
 "Additional notification types" = "Additional notification types";
 
 /* */
-/* */
+"Low battery" = "Low battery";
 
-"Low battery
-Misc" = "Misc";
+/* */
+"Misc" = "Misc";
 
 /* */
 "Unit override" = "Unit override";
@@ -802,6 +802,11 @@ Misc" = "Misc";
 
 "OpenAPS other settings" = "OpenAPS other settings";
 
+
+/* Glucose Simulator CGM */
+"Glucose Simulator" = "Glucose Simulator";
+
+
 /*
   Infotexts from openaps.docs and androidaps.docs
   FreeAPS X

+ 0 - 4
FreeAPS/Sources/Models/Suggestion.swift

@@ -18,10 +18,6 @@ struct Suggestion: JSON, Equatable {
     let reservoir: Decimal?
     var timestamp: Date?
     var recieved: Bool?
-
-    var isNoTempRequired: Bool {
-        reason.contains("no temp required")
-    }
 }
 
 struct Predictions: JSON, Equatable {

+ 1 - 1
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -238,7 +238,7 @@ extension Home {
             if closedLoop,
                let enactedSuggestion = enactedSuggestion,
                let timestamp = enactedSuggestion.timestamp,
-               enactedSuggestion.deliverAt == suggestion.deliverAt, suggestion.rate != nil || suggestion.units != nil
+               enactedSuggestion.deliverAt == suggestion.deliverAt, enactedSuggestion.recieved == true
             {
                 statusTitle = "Enacted at \(dateFormatter.string(from: timestamp))"
             } else if let suggestedDate = suggestion.deliverAt {

+ 1 - 0
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -13,6 +13,7 @@ struct CurrentGlucoseView: View {
             formatter.minimumFractionDigits = 1
             formatter.maximumFractionDigits = 1
         }
+        formatter.roundingMode = .halfUp
         return formatter
     }
 

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

@@ -78,7 +78,7 @@ struct LoopView: View {
     }
 
     private var actualSuggestion: Suggestion? {
-        if closedLoop, suggestion?.rate != nil || suggestion?.units != nil || suggestion?.isNoTempRequired ?? false {
+        if closedLoop, enactedSuggestion?.recieved == true {
             return enactedSuggestion ?? suggestion
         } else {
             return suggestion