From 82cd4c5a996e0aa68f6eed0b23ca65f83605f325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20B=20M=C3=A5rtensson?= <53905247+Jon-b-m@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:48:54 +0200 Subject: [PATCH] Release 5.0.0 (#852) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Release 5.0.0 1. Hypo treatments (#801) Activate a hypo treatment override preset when selecting Hypo Treatment in the meals View. The activated override: 45 min, SMBs off, 90%, 6.5 mmol/l target. This will also skip the Bolus View. Make the necessary updates to update the override/s in Nightscout. Those who never records any hypo treatments in iAPS can disable this option in UI/UX settings. 2. Don't run TDD twice (https://github.com/Artificial-Pancreas/iAPS/commit/dd5cc61c5a3aaec421144eac207ab01ff557d4fe) An add some more printing to console to see time of each oref0 module. 3. Expand iAPS Live Activity (#808). Display Last loop, IOB, COB and eventual glucose prediction. Add support for both open loop mode and closed loop mode. https://github.com/Artificial-Pancreas/iAPS/pull/808 4. Move max carbs (#811). Some users don't know about this new setting. It has now been moved to a more intuitive location. 5. Resolve Calender issue introdced in commit af1acf9b2921aef5adb2c1181cbe154214960eb7 (#821) 6. Resolve issue https://github.com/Artificial-Pancreas/iAPS/issues/804 7. Refactor (https://github.com/Artificial-Pancreas/iAPS/pull/823) 8. Fix the rounding in small TIR chart. Every group together now always end up with 100 %. 9. Hide the COB chart when lacking carb entries. Hide the IOB chart when lacking insulin data. https://github.com/Artificial-Pancreas/iAPS/pull/824 10. Add Glucose drop to Header View Display rotating glucose drop. Current Glucose, loop minutes ago and glucose delta is now in highest contrast. The glucose delta is now an option in UI/UX settings (default is off). I recommend having “Always color glucose value” off in UI/UX settings, as this will make sure you’ll always get the glucose in highest contrast (black/white). https://discord.com/channels/1120154740857245808/1123065808130691124/1277393884053504072 11. Add pump shadows.. 12. Localize Header Clean up. 13. Fix default glucose delta setting * Display + in delta. Round manual glucose values separately. 14. Move infoPanel to be a more integral part of the header View Update animation duration. 15. Add label to the iAPS pill 16. Use original oref0 profile script (#805), by @bastiaanv https://discord.com/channels/1120154740857245808/1123065808130691124/1278662542511575041 17. Edit "Error" to "Status" (#761) 18 Bring back display of before and after the dynamic adjustment in iAPS pill. Bring back correct colour of Insulin 24h. Include CR. 19. New localizations and Crowdin translations. Thank you translators: Aleksandr Van-Zaam, Mirko Trierenberg, Typ1er, Mykola Yroslavadudko, Nicole van Elst - van den Hoek, Hung Nguyen, Salem Allebdi, aboeinas, Abdulrahman Alfantokh, 244877227, Alessandro Fogliani for new translations. We now have some Arabic translations. --- .gitignore | 5 +- Config.xcconfig | 2 +- .../Core_Data.xcdatamodel/contents | 5 +- .../zh-Hans.lproj/Localizable.strings | 4 +- .../CGMBLEKitUI/ar.lproj/Localizable.strings | 4 +- .../ar.lproj/TransmitterManagerSetup.strings | 2 +- .../vi.lproj/TransmitterManagerSetup.strings | 2 +- .../zh-Hans.lproj/Localizable.strings | 2 +- .../ar.lproj/Localizable.strings | 16 +- .../G7SensorKit/ar.lproj/Localizable.strings | 12 +- .../Resources/ar.lproj/Localizable.strings | 46 +- .../Resources/vi.lproj/Localizable.strings | 2 +- .../Resources/ar.lproj/Localizable.strings | 12 +- .../ar.lproj/Localizable.strings | 32 +- .../Resources/ar.lproj/Localizable.strings | 114 ++--- .../Resources/ar.lproj/Localizable.strings | 42 +- .../Resources/vi.lproj/Localizable.strings | 2 +- .../ar.lproj/Localizable.strings | 10 +- .../vi.lproj/Localizable.strings | 4 +- FreeAPS.xcodeproj/project.pbxproj | 4 + .../xcshareddata/swiftpm/Package.resolved | 2 +- .../glucoseDrops.imageset/Contents.json | 26 + .../glucoesDropBlack5_2.png | Bin 0 -> 36502 bytes .../glucoesDropClearNoPointerExtraWhite.png | Bin 0 -> 34090 bytes .../Resources/javascript/bundle/profile.js | 3 +- .../javascript/prepare/determine-basal.js | 5 +- .../javascript/prepare/middleware.js | 9 +- .../Resources/javascript/prepare/profile.js | 46 +- .../defaults/freeaps/freeaps_settings.json | 5 +- FreeAPS/Sources/APS/APSManager.swift | 5 +- FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift | 82 +++- .../Sources/APS/Storage/CoreDataStorage.swift | 12 + .../Sources/APS/Storage/OverrideStorage.swift | 1 + .../Main/ar.lproj/Localizable.strings | 463 +++++++++--------- .../Main/ca.lproj/Localizable.strings | 2 +- .../Main/da.lproj/Localizable.strings | 19 +- .../Main/de.lproj/Localizable.strings | 20 +- .../Main/en.lproj/Localizable.strings | 16 +- .../Main/es.lproj/Localizable.strings | 16 +- .../Main/fi.lproj/Localizable.strings | 16 +- .../Main/fr.lproj/Localizable.strings | 18 +- .../Main/he.lproj/Localizable.strings | 16 +- .../Main/hu.lproj/Localizable.strings | 16 +- .../Main/it.lproj/Localizable.strings | 34 +- .../Main/nb.lproj/Localizable.strings | 16 +- .../Main/nl.lproj/Localizable.strings | 34 +- .../Main/pl.lproj/Localizable.strings | 16 +- .../Main/pt-BR.lproj/Localizable.strings | 16 +- .../Main/pt-PT.lproj/Localizable.strings | 16 +- .../Main/ru.lproj/Localizable.strings | 18 +- .../Main/sk.lproj/Localizable.strings | 16 +- .../Main/sv.lproj/Localizable.strings | 20 +- .../Main/tr.lproj/Localizable.strings | 16 +- .../Main/uk.lproj/Localizable.strings | 18 +- .../Main/vi.lproj/Localizable.strings | 46 +- .../Main/zh-Hans.lproj/Localizable.strings | 16 +- FreeAPS/Sources/Models/Configs.swift | 2 + FreeAPS/Sources/Models/FreeAPSSettings.swift | 10 + .../Modules/AddCarbs/AddCarbsStateModel.swift | 55 ++- .../AddCarbs/View/AddCarbsRootView.swift | 13 +- .../Sources/Modules/Home/HomeStateModel.swift | 11 + .../Home/View/Header/CurrentGlucoseView.swift | 174 ++++--- .../Modules/Home/View/Header/LoopView.swift | 29 +- .../Modules/Home/View/Header/PumpView.swift | 2 + .../Modules/Home/View/HomeRootView.swift | 379 +++++++------- .../Home/View/Previews/ActiveCOBView.swift | 45 +- .../Home/View/Previews/ActiveIOBView.swift | 82 +++- .../Home/View/Previews/LoopsView.swift | 32 +- .../Modules/Home/View/Previews/TIRView.swift | 309 +++++------- .../OverrideProfilesStateModel.swift | 4 +- .../PreferencesEditorStateModel.swift | 2 + .../View/PreferencesEditorRootView.swift | 27 + .../PumpSettingsEditorStateModel.swift | 3 - .../View/PumpSettingsEditorRootView.swift | 4 - .../Settings/View/SettingsRootView.swift | 17 +- .../StatConfig/StatConfigStateModel.swift | 4 + .../StatConfig/View/StatConfigRootView.swift | 2 + .../Services/Calendar/CalendarManager.swift | 18 +- .../LiveActivity/LiveActitiyShared.swift | 5 + .../LiveActivity/LiveActivityBridge.swift | 133 +++-- .../Overrides/OverrideShortcuts.swift | 4 + FreeAPS/Sources/Views/TagCloudView.swift | 2 +- FreeAPS/Sources/Views/ViewModifiers.swift | 56 ++- LiveActivity/LiveActivity.swift | 196 ++++++-- fastlane/Fastfile | 2 +- package.json | 8 + scripts/webpack.config.js | 38 +- 87 files changed, 1972 insertions(+), 1098 deletions(-) create mode 100644 FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/Contents.json create mode 100644 FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/glucoesDropBlack5_2.png create mode 100644 FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/glucoesDropClearNoPointerExtraWhite.png create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 4a72f1f123..aea4123494 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,7 @@ fastlane/FastlaneRunner ConfigOverride.xcconfig -branch.txt \ No newline at end of file +branch.txt +package-lock.json +node_modules +oref0 diff --git a/Config.xcconfig b/Config.xcconfig index 3ad19d0021..4bc8c1e9dc 100644 --- a/Config.xcconfig +++ b/Config.xcconfig @@ -1,5 +1,5 @@ APP_DISPLAY_NAME = iAPS -APP_VERSION = 4.8.0 +APP_VERSION = 5.0.0 APP_BUILD_NUMBER = 1 COPYRIGHT_NOTICE = DEVELOPER_TEAM = ##TEAM_ID## diff --git a/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents b/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents index 0f34a2efc9..8a0bf22421 100644 --- a/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents +++ b/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -160,6 +160,7 @@ + @@ -198,4 +199,4 @@ - \ No newline at end of file + diff --git a/Dependencies/CGMBLEKit/CGMBLEKit/zh-Hans.lproj/Localizable.strings b/Dependencies/CGMBLEKit/CGMBLEKit/zh-Hans.lproj/Localizable.strings index 5a6ae3671a..cfdf4e29b7 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKit/zh-Hans.lproj/Localizable.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKit/zh-Hans.lproj/Localizable.strings @@ -2,7 +2,7 @@ "Dexcom G5" = "Dexcom G 5"; /* CGM display title */ -"Dexcom G6" = "Dexcom G6"; +"Dexcom G6" = "德康 G6"; /* Error description for unreliable state */ "Glucose data is unavailable" = "葡萄糖数据不可用"; @@ -14,7 +14,7 @@ "OK" = "Ok"; /* invlid config error description */ -"Peripheral command was invalid" = "Peripheral command was invalid"; +"Peripheral command was invalid" = "外围命令无效"; /* Timeout error description */ "Peripheral did not respond in time" = "外设没有及时响应"; diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings index 789c5839f8..2fbdf8577b 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings @@ -8,7 +8,7 @@ "Cancel" = "إلغاء"; /* Title describing glucose date */ -"Date" = "Date"; +"Date" = "التاريخ"; /* Button title to delete CGM Title text for the button to remove a CGM from Loop */ @@ -51,7 +51,7 @@ Title text for the button to remove a CGM from Loop */ "Transmitter Age" = "عمر جهاز الإرسال"; /* The title text for the Dexcom G5/G6 transmitter ID config value */ -"Transmitter ID" = "Transmitter ID"; +"Transmitter ID" = "معرف المرسل"; /* Title describing glucose trend */ "Trend" = "إتجاه"; diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings index 1ab79b565f..969c54582c 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings @@ -11,7 +11,7 @@ "Qub-6B-0aB.footerTitle" = "يمكن العثور على معرف جهاز الإرسال مطبوعا على الجزء الخلفي من الجهاز، على جانب الصندوق الذي أتى به، ومن داخل قوائم الإعدادات الخاصة بالمستلم والتطبيق الموبايل."; /* Class = "UITableViewSection"; headerTitle = "Transmitter ID"; ObjectID = "Qub-6B-0aB"; */ -"Qub-6B-0aB.headerTitle" = "Transmitter ID"; +"Qub-6B-0aB.headerTitle" = "معرف المرسل"; /* Class = "UITableViewSection"; footerTitle = "Data can be downloaded over the Internet from Share when the transmitter connection fails."; ObjectID = "k1N-Rg-XDy"; */ "k1N-Rg-XDy.footerTitle" = "يمكن تحميل البيانات عبر الإنترنت من تطبيق Share عند فشل اتصال المرسل."; diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/vi.lproj/TransmitterManagerSetup.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/vi.lproj/TransmitterManagerSetup.strings index 028f87311c..65caa60ed2 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/vi.lproj/TransmitterManagerSetup.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKitUI/vi.lproj/TransmitterManagerSetup.strings @@ -5,7 +5,7 @@ "Dds-49-o7G.title" = "Cài đặt Transmitter"; /* Class = "UILabel"; text = "Detail"; ObjectID = "GOT-KQ-cEh"; */ -"GOT-KQ-cEh.text" = "Chi tiết"; +"GOT-KQ-cEh.text" = "Chi tiết"; /* Class = "UITableViewSection"; footerTitle = "The transmitter ID can be found printed on the back of the device, on the side of the box it came in, and from within the settings menus of the receiver and mobile app."; ObjectID = "Qub-6B-0aB"; */ "Qub-6B-0aB.footerTitle" = "Số ID của Transmitter có thể được tìm thấy trên vỏ hộp hoặc bên hông hộp và trong phần Menu cài đặt cũng như trên ứng dụng của điện thoại."; diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/zh-Hans.lproj/Localizable.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/zh-Hans.lproj/Localizable.strings index 7606ebf3c8..a73353326b 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/zh-Hans.lproj/Localizable.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKitUI/zh-Hans.lproj/Localizable.strings @@ -39,7 +39,7 @@ Title text for the button to remove a CGM from Loop */ "Remote Data Synchronization" = "远程数据同步"; /* Title describing sensor expiration */ -"Sensor Expires" = "Sensor Expires"; +"Sensor Expires" = "传感器已过期"; /* Title describing past sensor expiration */ "Sensor Expired" = "传感器已过期"; diff --git a/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings b/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings index 90c967438f..56920cc985 100644 --- a/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings +++ b/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings @@ -11,10 +11,10 @@ "Bluetooth" = "البلوتوث"; /* Button text to cancel G7 setup */ -"Cancel" = "Cancel"; +"Cancel" = "إلغاء"; /* No comment provided by engineer. */ -"Configuration" = "Configuration"; +"Configuration" = "ضبط"; /* title for g7 settings connection status when connected */ "Connected" = "Connected"; @@ -26,14 +26,14 @@ "Continue" = "Continue"; /* Button label for removing CGM */ -"Delete CGM" = "Delete CGM"; +"Delete CGM" = "حذف المستشعر"; /* Navigation bar title for G7SettingsView Title on WelcomeView */ "Dexcom G7" = "ديكسكوم G7"; /* No comment provided by engineer. */ -"Done" = "Done"; +"Done" = "تمّ"; /* Field label */ "Glucose" = "Glucose"; @@ -60,7 +60,7 @@ "LOW" = "منخفض"; /* title for g7 settings row showing BLE Name */ -"Name" = "Name"; +"Name" = "الاسم"; /* No comment provided by engineer. */ "Scan for new sensor" = "البحث عن مستشعر جديد"; @@ -84,7 +84,7 @@ "Sensor\nIssue" = "مشكلة في المستشعر\n"; /* G7 Status highlight text for sensor warmup */ -"Sensor\nWarmup" = "Sensor\nWarmup"; +"Sensor\nWarmup" = "إحماء\nالمستشعر"; /* title for g7 settings row showing sensor expiration time */ "Sensor Expiration" = "إنتهت صلاحية المستشعر"; @@ -99,13 +99,13 @@ "Sensor failed" = "فشل الحساس"; /* title for g7 settings row showing sensor start time */ -"Sensor Start" = "Start sensor"; +"Sensor Start" = "بدء الحساس"; /* G7 Status highlight text for signal loss */ "Signal\nLoss" = "فقدان الإشارة\n"; /* Field label */ -"Time" = "Time"; +"Time" = "الوقت"; /* Field label */ "Trend" = "إتجاه"; diff --git a/Dependencies/G7SensorKit/ar.lproj/Localizable.strings b/Dependencies/G7SensorKit/ar.lproj/Localizable.strings index a1087c7584..2fa0e844ac 100644 --- a/Dependencies/G7SensorKit/ar.lproj/Localizable.strings +++ b/Dependencies/G7SensorKit/ar.lproj/Localizable.strings @@ -8,7 +8,7 @@ "Continue" = "Continue"; /* Button text to cancel G7 setup */ -"Cancel" = "Cancel"; +"Cancel" = "إلغاء"; /* Error description for unreliable state */ "Glucose data is unavailable" = "قراءات السكر غير متوفرة"; @@ -45,14 +45,14 @@ "Last Reading" = "آخر قراءة"; -"Time" = "Time"; +"Time" = "الوقت"; "Trend" = "إتجاه"; "Bluetooth" = "البلوتوث"; /* title for g7 settings row showing BLE Name */ -"Name" = "Name"; +"Name" = "الاسم"; /* title for g7 settings connection status when scanning */ "Scanning" = "يتم المسح"; @@ -67,7 +67,7 @@ "Last Connect" = "آخر اتصال"; /* Configuration */ -"Configuration" = "Configuration"; +"Configuration" = "ضبط"; /* title for g7 config settings to upload readings */ "Upload Readings" = "رفع القراءات"; @@ -76,7 +76,7 @@ "Scan for new sensor" = "البحث عن مستشعر جديد"; /* Button label for removing CGM */ -"Delete CGM" = "Delete CGM"; +"Delete CGM" = "حذف المستشعر"; /* No glucose value representation (3 dashes for mg/dL) */ "– – –" = "– – –"; @@ -126,4 +126,4 @@ "Sensor\nIssue" = "مشكلة في المستشعر\n"; /* G7 Status highlight text for sensor warmup */ -"Sensor\nWarmup" = "Sensor\nWarmup"; +"Sensor\nWarmup" = "إحماء\nالمستشعر"; diff --git a/Dependencies/MinimedKit/MinimedKit/Resources/ar.lproj/Localizable.strings b/Dependencies/MinimedKit/MinimedKit/Resources/ar.lproj/Localizable.strings index 8a1f932e45..905d18d193 100644 --- a/Dependencies/MinimedKit/MinimedKit/Resources/ar.lproj/Localizable.strings +++ b/Dependencies/MinimedKit/MinimedKit/Resources/ar.lproj/Localizable.strings @@ -1,74 +1,74 @@ /* Communications error for a bolus currently running */ -"A bolus is already in progress" = "A bolus is already in progress"; +"A bolus is already in progress" = "هناك جرعة جارية بالفعل"; /* The description of AlarmClockReminderPumpEvent */ -"AlarmClockReminder" = "AlarmClockReminder"; +"AlarmClockReminder" = "تذكير المنبه"; /* The description of AlarmSensorPumpEvent */ -"AlarmSensor" = "AlarmSensor"; +"AlarmSensor" = "تنبيه الحساس"; /* Describing the battery chemistry as Alkaline */ "Alkaline" = "Alkaline"; /* The format string description of a BasalProfileStartPumpEvent. (1: The index of the profile)(2: The basal rate) */ -"Basal Profile %1$@: %2$@ U/hour" = "Basal Profile %1$@: %2$@ U/hour"; +"Basal Profile %1$@: %2$@ U/hour" = "ملف تعريف القاعدي %1$@: %2$@ U/hour"; /* Pump error code when bolus is in progress */ -"Bolus in progress" = "Bolus in progress"; +"Bolus in progress" = "جرعة قيد التنفيذ"; /* Suggestions for diagnosing a command refused pump error */ "Check that the pump is not suspended or priming, or has a percent temp basal type" = "Check that the pump is not suspended or priming, or has a percent temp basal type"; /* Pump error code returned when command refused */ -"Command refused" = "Command refused"; +"Command refused" = "تم رفض الأمر"; /* No comment provided by engineer. */ -"Comms with another pump detected" = "Comms with another pump detected."; +"Comms with another pump detected" = "تم اكتشاف الاتصال مع مضخة أخرى."; /* Error description */ -"Decoding Error" = "Decoding Error"; +"Decoding Error" = "خطأ في فك التشفير"; /* Error description */ -"Device Error" = "Device Error"; +"Device Error" = "خطأ في الجهاز"; /* Describing the pump history insulin data source */ -"Event History" = "Event History"; +"Event History" = "سجل الأحداث"; /* Format string for failure reason. (1: The operation being performed) (2: The response data) */ -"Invalid response during %1$@: %2$@" = "Invalid response during %1$@: %2$@"; +"Invalid response during %1$@: %2$@" = "استجابة غير صحيحة خلال %1$@: %2$@"; /* Describing the battery chemistry as Lithium */ -"Lithium" = "Lithium"; +"Lithium" = "ليثيوم"; /* Recovery suggestion */ -"Make sure your RileyLink is nearby and powered on" = "Make sure your RileyLink is nearby and powered on"; +"Make sure your RileyLink is nearby and powered on" = "تأكد من أن RileyLink الخاص بك قريب ويعمل"; /* Pump error code describing max setting exceeded */ -"Max setting exceeded" = "Max setting exceeded"; +"Max setting exceeded" = "تم تجاوز الحد الأقصى للإعدادات"; /* Pump title (1: model number) */ -"Minimed %@" = "Minimed %@"; +"Minimed %@" = "تم تصغير %@"; /* Generic title of the minimed pump manager */ -"Minimed 500/700 Series" = "Minimed 500/700 Series"; +"Minimed 500/700 Series" = "الحد الأدنى 500/700 سلسلة"; /* Describing the North America pump region */ -"North America" = "North America"; +"North America" = "أمريكا الشماليّة"; /* No comment provided by engineer. */ -"Pump did not respond" = "Pump did not respond"; +"Pump did not respond" = "المضخة لم تستجب"; /* Error description */ -"Pump Error" = "Pump Error"; +"Pump Error" = "خطأ في المضخة"; /* No comment provided by engineer. */ -"Pump is suspended" = "Pump is suspended"; +"Pump is suspended" = "تم تعليق المضخة"; /* No comment provided by engineer. */ -"Pump responded unexpectedly" = "Pump responded unexpectedly"; +"Pump responded unexpectedly" = "مضخة استجابت بشكل غير متوقع"; /* The format string describing a pump message. (1: The packet type)(2: The message type)(3: The message address)(4: The message data */ -"PumpMessage(%1$@, %2$@, %3$@, %4$@)" = "PumpMessage(%1$@, %2$@, %3$@, %4$@)"; +"PumpMessage(%1$@, %2$@, %3$@, %4$@)" = "رسالة المضخة(%1$@، %2$@، %3$@، %4$@)"; /* Describing the reservoir insulin data source */ "Reservoir" = "الخزان"; @@ -89,7 +89,7 @@ "Unknown pump error code: %1$@" = "Unknown pump error code: %1$@"; /* No comment provided by engineer. */ -"Unknown pump model: %@" = "Unknown pump model: %@"; +"Unknown pump model: %@" = "نَموذج مضخة غير معروف: %@"; /* Format string for an unknown response. (1: The operation being performed) (2: The response data) */ "Unknown response during %1$@: %2$@" = "Unknown response during %1$@: %2$@"; diff --git a/Dependencies/MinimedKit/MinimedKit/Resources/vi.lproj/Localizable.strings b/Dependencies/MinimedKit/MinimedKit/Resources/vi.lproj/Localizable.strings index a5eb87a6b1..ea4dad177a 100644 --- a/Dependencies/MinimedKit/MinimedKit/Resources/vi.lproj/Localizable.strings +++ b/Dependencies/MinimedKit/MinimedKit/Resources/vi.lproj/Localizable.strings @@ -62,7 +62,7 @@ "Pump Error" = "Lỗi bơm"; /* No comment provided by engineer. */ -"Pump is suspended" = "Bơm đang tạm ngưng"; +"Pump is suspended" = "Bơm đã tạm ngưng"; /* No comment provided by engineer. */ "Pump responded unexpectedly" = "Bơm phản ứng bất ngờ"; diff --git a/Dependencies/MinimedKit/MinimedKitUI/Resources/ar.lproj/Localizable.strings b/Dependencies/MinimedKit/MinimedKitUI/Resources/ar.lproj/Localizable.strings index 015607ef1e..d08b6d1aaf 100644 --- a/Dependencies/MinimedKit/MinimedKitUI/Resources/ar.lproj/Localizable.strings +++ b/Dependencies/MinimedKit/MinimedKitUI/Resources/ar.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* Unit format string for an RSSI value in decibles */ -"%@ dB" = "%@ dB"; +"%@ dB" = "%@ ديسيبل"; /* Format string for reservoir volume. (1: The localized volume) */ "%@U" = "%@U"; @@ -47,7 +47,7 @@ "Changing" = "Changing"; /* Progress message for changing pump time. */ -"Changing time…" = "Changing time…"; +"Changing time…" = "تغيير الوقت…"; /* Instructions on selecting battery chemistry type */ "Choose the type of battery you are using in your pump for better alerting about low battery conditions." = "Choose the type of battery you are using in your pump for better alerting about low battery conditions."; @@ -56,7 +56,7 @@ "Configuration" = "المعطيات"; /* Button title to connect to pump during setup */ -"Connect" = "Connect"; +"Connect" = "توصيل"; /* Text for continue button */ "Continue" = "Continue"; @@ -66,7 +66,7 @@ "Delete Pump" = "Delete Pump"; /* Header for devices section of RileyLinkSetupView */ -"Devices" = "Devices"; +"Devices" = "الأجهزة"; /* Description for option to not use MySentry */ "Do not use MySentry" = "Do not use MySentry"; @@ -114,7 +114,7 @@ "Medtronic pump models 523, 723, 554, and 754 have a feature called 'MySentry' that periodically broadcasts the reservoir and pump battery levels. Listening for these broadcasts allows Loop to communicate with the pump less frequently, which can increase pump battery life. However, when using this feature the RileyLink stays awake more of the time and uses more of its own battery. Enabling this may lengthen pump battery life, while disabling it may lengthen RileyLink battery life. This setting is ignored for other pump models." = "Medtronic pump models 523, 723, 554, and 754 have a feature called 'MySentry' that periodically broadcasts the reservoir and pump battery levels. Listening for these broadcasts allows Loop to communicate with the pump less frequently, which can increase pump battery life. However, when using this feature the RileyLink stays awake more of the time and uses more of its own battery. Enabling this may lengthen pump battery life, while disabling it may lengthen RileyLink battery life. This setting is ignored for other pump models."; /* Value string for MySentry config when MySentry is not being used */ -"No" = "No"; +"No" = "لا"; /* Message display when no response from tuning pump */ "No response" = "No response"; @@ -214,7 +214,7 @@ "Use MySentry" = "Use MySentry"; /* Value string for MySentry config when MySentry is being used */ -"Yes" = "Yes"; +"Yes" = "نعم"; /* Button text to confirm pump time sync */ "Yes, Sync to Current Time" = "Yes, Sync to Current Time"; diff --git a/Dependencies/OmniBLE/Localizations/ar.lproj/Localizable.strings b/Dependencies/OmniBLE/Localizations/ar.lproj/Localizable.strings index d578a852ca..7b44c14911 100644 --- a/Dependencies/OmniBLE/Localizations/ar.lproj/Localizable.strings +++ b/Dependencies/OmniBLE/Localizations/ar.lproj/Localizable.strings @@ -75,7 +75,7 @@ "Pod Activated" = "Pod Activated"; /* */ -"Notification Settings" = "Notification Settings"; +"Notification Settings" = "إعدادات الإشعارات"; /* */ "Confidence Reminders" = "Confidence Reminders"; @@ -147,7 +147,7 @@ "minute" = "minute"; /* Unit for plural minutes in pod life remaining */ -"minutes" = "minutes"; +"minutes" = "دقائق"; /* Title of insulin delivery section */ "Insulin Delivery" = "Insulin Delivery"; @@ -165,7 +165,7 @@ "Device Details" = "Device Details"; /* Section header for configuration section */ -"Configuration" = "Configuration"; +"Configuration" = "ضبط"; /* Settings page link description when next lifecycle action is to finish deactivation */ "Finish deactivation" = "Finish deactivation"; @@ -312,7 +312,7 @@ "Resume" = "Resume"; /* */ -"Bolus" = "Bolus"; +"Bolus" = "الجرعة"; /* */ "Cancel Bolus" = "Cancel Bolus"; @@ -345,7 +345,7 @@ "Activity" = "Activity"; /* Section header for configuration section */ -"Configuration" = "Configuration"; +"Configuration" = "ضبط"; /* Title for previous pod page */ "Previous Pod" = "Previous Pod"; @@ -472,7 +472,7 @@ "Paired" = "Paired"; /* Cancel button text in navigation bar on pair pod UI */ -"Cancel" = "Cancel"; +"Cancel" = "إلغاء"; /* Alert title for cancel pairing modal */ "Are you sure you want to cancel Pod setup?" = "Are you sure you want to cancel Pod setup?"; @@ -529,10 +529,10 @@ "The window on the top of the Pod should be colored pink when the cannula is properly inserted into the skin." = "The window on the top of the Pod should be colored pink when the cannula is properly inserted into the skin."; /* Button label for user to answer cannula was properly inserted */ -"Yes" = "Yes"; +"Yes" = "نعم"; /* Button label for user to answer cannula was not properly inserted */ -"No" = "No"; +"No" = "لا"; /* Pod pairing action button text while pairing */ "Pairing..." = "Pairing..."; @@ -553,7 +553,7 @@ "Scheduled Reminder" = "Scheduled Reminder"; /* Label for expiration reminder row */ -"Time" = "Time"; +"Time" = "الوقت"; /* Action button title to continue at Setup Complete */ "Finish Setup" = "Finish Setup"; @@ -603,10 +603,10 @@ /* Description text for critical alerts */ "The reminders above will not sound if your device is in Silent or Do Not Disturb mode.\n\nThere are other critical Pod alerts and alarms that will sound even if your device is set to Silent or Do Not Disturb mode." = "The reminders above will not sound if your device is in Silent or Do Not Disturb mode.\n\nThere are other critical Pod alerts and alarms that will sound even if you device is set to Silent or Do Not Disturb mode."; /* navigation title for notification settings */ -"Notification Settings" = "Notification Settings"; +"Notification Settings" = "إعدادات الإشعارات"; /* Label for scheduled reminder value row */ -"Time" = "Time"; +"Time" = "الوقت"; /* Value text for no expiration reminder */ "No Reminder" = "No Reminder"; @@ -638,7 +638,7 @@ "Confidence reminders will sound when the app automatically adjusts delivery as well as for commands you initiate." = "Confidence reminders will sound when the app automatically adjusts delivery as well as for commands you initiate."; /* Label text for temporary basal rate summary */ -"Rate" = "Rate"; +"Rate" = "معدّل"; /* Insulin unit per hour */ "U/hr" = "U/hr"; @@ -718,7 +718,7 @@ "You will now begin the process of configuring your reminders, filling your Pod with insulin, pairing to your device and placing it on your body." = "You will now begin the process of configuring your reminders, filling your Pod with insulin, pairing to your device and placing it on your body."; /* Cancel button title */ -"Cancel" = "Cancel"; +"Cancel" = "إلغاء"; /* Text for continue button on PodSetupView */ "Continue" = "Continue"; @@ -742,7 +742,7 @@ "Low Reservoir" = "Low Reservoir"; /* */ -"Save" = "Save"; +"Save" = "إحفظ"; /* hr (short for hour) */ "hr" = "hr"; @@ -814,7 +814,7 @@ "Wait for existing temp basal to finish, or suspend to cancel" = "Wait for existing temp basal to finish, or suspend to cancel"; /* DASH Pod time ago since last status */ -"%@ ago" = "%@ ago"; +"%@ ago" = "منذ ‭%@"; /* Title string for SilencePodPreference.enabled */ "Silenced" = "Silenced"; @@ -852,7 +852,7 @@ "Read Pod Status" = "Read Pod Status"; /* Title of done button on OmnipodSettingsView */ -"Done" = "Done"; +"Done" = "تمّ"; /* Title for the pod diagnostic view */ "Pod Diagnostics" = "Pod Diagnostics"; diff --git a/Dependencies/OmniKit/OmniKit/Resources/ar.lproj/Localizable.strings b/Dependencies/OmniKit/OmniKit/Resources/ar.lproj/Localizable.strings index 496f0367fb..cf86c58c03 100644 --- a/Dependencies/OmniKit/OmniKit/Resources/ar.lproj/Localizable.strings +++ b/Dependencies/OmniKit/OmniKit/Resources/ar.lproj/Localizable.strings @@ -1,51 +1,51 @@ /* Description for an inactive alert modifier */ -" (inactive)" = " (inactive)"; +" (inactive)" = "(غير نشط)"; /* Format string for low battery alert body for RileyLink. (1: device name) */ -"\"%1$@\" has a low battery" = "\"%1$@\" has a low battery"; +"\"%1$@\" has a low battery" = "%1$@\" بطارية منخفضة"; /* Unit format string for an RSSI value in decibles */ -"%@ dB" = "%@ dB"; +"%@ dB" = "%@ ديسيبل"; /* Format string for alert content body for lowReservoir pod alert. (1: reminder value) */ "%1$@ insulin or less remaining in Pod. Change Pod soon." = "%1$@ insulin or less remaining in Pod. Change Pod soon."; /* Format string for activation time exceeded Pod state when activation not completed in the time allowed */ -"Activation time exceeded" = "Activation time exceeded"; +"Activation time exceeded" = "تجاوز الوقت المحدد للتفعيل"; /* Description for auto-off */ -"Auto-off" = "Auto-Off"; +"Auto-off" = "إيقاف تلقائي"; /* Description for auto-off alarm */ -"Auto-off alarm" = "Auto-off alarm"; +"Auto-off alarm" = "إيقاف تلقائي للمنبه"; /* Pod state when basal initialized */ -"Basal initialized" = "Basal initialized"; +"Basal initialized" = "تهيئة الانسولين القاعدي"; /* Pod state when running below fifty units */ -"Below 50 units" = "Below 50 units"; +"Below 50 units" = "أقل من 50 وحدة"; /* Pump Event title for UnfinalizedDose with doseType of .bolus */ -"Bolus" = "Bolus"; +"Bolus" = "الجرعة"; /* Error message shown when operation could not be completed due to existing bolus in progress */ -"Bolus in progress" = "Bolus in progress"; +"Bolus in progress" = "جرعة قيد التنفيذ"; /* The format string describing a bolus. (1: The amount delivered)(2: Start time of the dose)(3: duration)(4: scheduled certainty) */ -"Bolus: %1$@U %2$@ %3$@ %4$@" = "Bolus: %1$@U %2$@ %3$@ %4$@"; +"Bolus: %1$@U %2$@ %3$@ %4$@" = "جرعة: %1$@U %2$@ %3$@ %4$@"; /* Delivery status when bolusing */ -"Bolusing" = "Bolusing"; +"Bolusing" = "يضخ"; /* Delivery status when bolusing and temp basal is running */ -"Bolusing with temp basal" = "Bolusing with temp basal"; +"Bolusing with temp basal" = "جرعة مع انسولين قاعدي مؤقت"; /* Pod state when inserting cannula */ -"Cannula inserting" = "Cannula inserting"; +"Cannula inserting" = "إدخال القنية قيد التنفيذ"; /* String describing a dose that was certainly scheduled */ -"Certain" = "Certain"; +"Certain" = "معتمد"; /* Alert content body for podExpireImminent pod alert */ "Change Pod now. Insulin delivery will stop in 1 hour." = "Change Pod now. Insulin delivery will stop in 1 hour."; @@ -54,13 +54,13 @@ "Change Pod now. Pod has been active for 72 hours." = "Change Pod now. Pod has been active for 72 hours."; /* Format string for invalid message error code (1: error code number) */ -"Command error %1$u" = "Command error %1$u"; +"Command error %1$u" = "خطأ في الأمر %1$u"; /* Status highlight that delivery is uncertain. */ "Comms Issue" = "Comms Issue"; /* Error message when command is rejected because an unacknowledged command is pending. */ -"Communication issue: Unacknowledged command pending." = "Communication issue: Unacknowledged command pending."; +"Communication issue: Unacknowledged command pending." = "مشكلة إتصال: أمر غير معترف به معلق."; /* Description for BeepPreference.manualCommands */ "Confidence reminders will sound for commands you initiate, like bolus, cancel bolus, suspend, resume, save notification reminders, etc. When the app automatically adjusts delivery, no confidence reminders are used." = "Confidence reminders will sound for commands you initiate, like bolus, cancel bolus, suspend, resume, save notification reminders, etc. When the app automatically adjusts delivery, no confidence reminders are used."; @@ -81,28 +81,28 @@ "Disabled" = "Disabled"; /* Description for Empty reservoir pod fault */ -"Empty reservoir" = "Empty reservoir"; +"Empty reservoir" = "الخزان فارغ"; /* Error message shown when empty response from pod was received */ -"Empty response from pod" = "Empty response from pod"; +"Empty response from pod" = "إجابة فارغة من المضخة"; /* Pod state error event logged shutting down */ -"Error event logged, shutting down" = "Error event logged, shutting down"; +"Error event logged, shutting down" = "تم تسجيل خطأ ، جاري إيقاف التشغيل"; /* Description for expiration alert */ -"Expiration alert" = "Expiration alert"; +"Expiration alert" = "إنذار انتهاء الصلاحية"; /* Title string for BeepPreference.extended */ "Extended" = "Extended"; /* Delivery status when extended bolus is running */ -"Extended bolus running" = "Extended bolus running"; +"Extended bolus running" = "بولوس مطوّل قيد التنفيذ"; /* Delivery status when extended bolus and temp basal is running */ -"Extended bolus running with temp basal" = "Extended bolus running with temp basal"; +"Extended bolus running with temp basal" = "بولوس مطوّل قيد التنفيذ مع انسولين قاعدي مؤقت"; /* Pod state when fault event has occurred */ -"Fault event occurred" = "Fault event occurred"; +"Fault event occurred" = "حدث خطأ"; /* Status highlight that when pod is deactivating. */ "Finish Deactivation" = "Finish Deactivation"; @@ -111,13 +111,13 @@ "Finish Pairing" = "Finish Pairing"; /* Description for finish setup */ -"Finish setup " = "Finish setup "; +"Finish setup " = "الانتهاء من الإعداد "; /* Description for finish setup reminder */ -"Finish setup reminder" = "Finish setup reminder"; +"Finish setup reminder" = "تذكير إنهاء الإعداد"; /* Pod inititialized */ -"Initialized" = "Initialized"; +"Initialized" = "تهيئة"; /* Pod state when inserting cannula */ "Inserting cannula" = "Inserting cannula"; @@ -132,10 +132,10 @@ "Insulin type not configured" = "Insulin type not configured"; /* The format string for Internal pod fault (1: The fault code value) */ -"Internal pod fault %1$03d" = "Internal pod fault %1$03d"; +"Internal pod fault %1$03d" = "خطأ داخلي في المضخة %1$03d"; /* The format string describing a bolus that was interrupted. (1: The amount delivered)(2: The amount scheduled)(3: Start time of the dose)(4: duration)(5: scheduled certainty) */ -"InterruptedBolus: %1$@ U (%2$@ U scheduled) %3$@ %4$@ %5$@" = "InterruptedBolus: %1$@ U (%2$@ U scheduled) %3$@ %4$@ %5$@"; +"InterruptedBolus: %1$@ U (%2$@ U scheduled) %3$@ %4$@ %5$@" = "البولوس المتعرقل: تم توصيل %1$@ U (من أصل%2$@ U مقرر) %3$@ %4$@ %5$@"; /* Error message for when unexpected address is received (1: received address) (2: expected address) */ "Invalid address 0x%x. Expected 0x%x" = "Invalid address 0x%x. Expected 0x%x"; @@ -153,22 +153,22 @@ "Low Reservoir" = "Low Reservoir"; /* Format string for description for low reservoir advisory (1: reminder units) */ -"Low reservoir advisory (%1$gU)" = "Low reservoir advisory (%1$gU)"; +"Low reservoir advisory (%1$gU)" = "إشعار بأن الخزان منخفض (%1$gU)"; /* Description for low reservoir alarm */ -"Low reservoir advisory alarm" = "Low reservoir advisory alarm"; +"Low reservoir advisory alarm" = "تنبيه بأن الخزان منخفض"; /* Title for RileyLink low battery alert */ -"Low RileyLink Battery" = "Low RileyLink Battery"; +"Low RileyLink Battery" = "بطارية RileyLink منخفضة"; /* Recovery suggestion when no RileyLink is available */ -"Make sure your RileyLink is nearby and powered on" = "Make sure your RileyLink is nearby and powered on"; +"Make sure your RileyLink is nearby and powered on" = "تأكد من أن RileyLink الخاص بك قريب ويعمل"; /* Status highlight when manual temp basal is running. */ "Manual Basal" = "Manual Basal"; /* Pod memory initialized */ -"Memory initialized" = "Memory initialized"; +"Memory initialized" = "تم تهيئة الذاكرة"; /* Recovery suggestion for PodCommsError.tooManyPodsFound */ "Move to a new area away from any other pods and try again." = "Move to a new area away from any other pods and try again."; @@ -178,13 +178,13 @@ "Multiple Command Alert" = "Multiple Command Alert"; /* Pod alert state when no alerts are active */ -"No alerts" = "No alerts"; +"No alerts" = "لا يوجد تنبيهات"; /* Description for BeepPreference.silent */ "No confidence reminders are used." = "No confidence reminders are used."; /* Description for Fault Event Code .noFaults */ -"No faults" = "No faults"; +"No faults" = "لا توجد أخطاء"; /* Status highlight message for emptyReservoir alarm. Status highlight that a pump is out of insulin. */ @@ -200,29 +200,29 @@ "No pods found" = "No pods found"; /* Error message shown when no response from pod was received */ -"No response from pod" = "No response from pod"; +"No response from pod" = "لايوجد رد من الحجيرة"; /* Error message shown when no response from pod was received */ -"No RileyLink available" = "No RileyLink available"; +"No RileyLink available" = "لا يوجد RileyLink متاح"; /* Delivery status when basal is running Pod state when running above fifty units */ "Normal" = "Normal"; /* Description for MessageError notEnoughData */ -"Not enough data" = "Not enough data"; +"Not enough data" = "لا توجد بيانات كافية"; /* Description for Occlusion detected pod fault */ -"Occlusion detected" = "Occlusion detected"; +"Occlusion detected" = "تم اكتشاف خطأ الاستمالة (Occlusion)"; /* Generic title of the omnipod pump manager */ -"Omnipod" = "Omnipod"; +"Omnipod" = "أومنيبود"; /* Pod status after pairing */ "Paired" = "Paired"; /* Pod status when pairing completed */ -"Pairing completed" = "Pairing completed"; +"Pairing completed" = "إكتمل الإقتران"; /* Description for MessageError parsingError. (1: decription of error), (2: hexadecimal data starting at offset) */ "Parsing Error: %1$@ in (%2$@)" = "Parsing Error: %1$@ in (%2$@)"; @@ -231,7 +231,7 @@ "Please bring only original pod in range or deactivate original pod" = "Please bring only original pod in range or deactivate original pod"; /* Recovery suggestion when no response is received from pod */ -"Please bring your pod closer to the RileyLink and try again" = "Please bring your pod closer to the RileyLink and try again"; +"Please bring your pod closer to the RileyLink and try again" = "من فضلك قم بجعل المضخة أقرب إلى RileyLink ثم حاول مرة أخرى"; /* Alert content body for finishSetupReminder pod alert */ "Please finish pairing your pod." = "Please finish pairing your pod."; @@ -240,25 +240,25 @@ "Please pair a new pod" = "Please pair a new pod"; /* Recovery suggestion when pairing signal strength is too high */ -"Please reposition the RileyLink further from the pod" = "Please reposition the RileyLink further from the pod"; +"Please reposition the RileyLink further from the pod" = "يرجى وضع RileyLink أبعد من الحجيرة"; /* Recovery suggestion when pairing signal strength is too low */ -"Please reposition the RileyLink relative to the pod" = "Please reposition the RileyLink relative to the pod"; +"Please reposition the RileyLink relative to the pod" = "يرجى وضع RileyLink اقرب الى المضخة"; /* Error message shown when user cannot pair because pod is already paired */ "Pod already paired" = "Pod already paired"; /* Error message shown when prime is attempted, but pod is already primed */ -"Pod already primed" = "Pod already primed"; +"Pod already primed" = "تمت تهيأت المضخة مسبقاً"; /* Status highlight message for other alarm. */ "Pod Error" = "Pod Error"; /* Description for expiration advisory alarm */ -"Pod expiration advisory alarm" = "Pod expiration advisory alarm"; +"Pod expiration advisory alarm" = "انذار اقتراب وقت انتهاء صلاحية المضخة"; /* The title for pod expiration notification */ -"Pod Expiration Notice" = "Pod Expiration Notice"; +"Pod Expiration Notice" = "إشعار انتهاء صلاحية المضخة"; /* Description for Pod expired pod fault */ "Pod expired" = "Pod expired"; @@ -267,16 +267,16 @@ "Pod expires in %1$@." = "Pod expires in %1$@."; /* Format string for pod fault code */ -"Pod Fault: %1$@" = "Pod Fault: %1$@"; +"Pod Fault: %1$@" = "خطأ مضخة:%1$@"; /* Error message when cannula insertion fails because the pod is in an unexpected state */ "Pod is not in a state ready for cannula insertion." = "Pod is not in a state ready for cannula insertion."; /* Error message when prime fails because the pod is in an unexpected state */ -"Pod is not in a state ready for priming." = "Pod is not in a state ready for priming."; +"Pod is not in a state ready for priming." = "المضخة ليست في حالة جاهزة للتهيئة."; /* Error message action could not be performed because pod is suspended */ -"Pod is suspended" = "Pod is suspended"; +"Pod is suspended" = "تم تعليق المضخة"; /* Status highlight message for occlusion alarm. */ "Pod Occlusion" = "Pod Occlusion"; @@ -288,23 +288,23 @@ "Pod sent ack instead of response" = "Pod sent ack instead of response"; /* Pod state when prime or cannula insertion has not completed in the time allotted */ -"Pod setup window expired" = "Pod setup window expired"; +"Pod setup window expired" = "تم تجاوز الوقت المسموح لإعداد المضخة"; /* Description for pod suspended reminder */ -"Pod suspended reminder" = "Pod suspended reminder"; +"Pod suspended reminder" = "تذكير بأن المضخة معلقة"; /* Format string for poor pod signal strength */ -"Poor signal strength" = "Poor signal strength"; +"Poor signal strength" = "قوة الإشارة ضعيفة"; /* Delivery status when pod is priming Pod status when priming */ -"Priming" = "Priming"; +"Priming" = "قيد التمهيد"; /* Pod state when priming completed */ -"Priming completed" = "Priming completed"; +"Priming completed" = "اكتمل التمهيد"; /* Pod state when ready for basal programming */ -"Ready for basal programming" = "Ready for basal programming"; +"Ready for basal programming" = "جاهز لبرمجة الانسولين القاعدي"; /* Pod state when ready for cannula insertion */ "Ready to insert cannula" = "Ready to insert cannula"; diff --git a/Dependencies/OmniKit/OmniKitUI/Resources/ar.lproj/Localizable.strings b/Dependencies/OmniKit/OmniKitUI/Resources/ar.lproj/Localizable.strings index 8981493221..77e9fe2942 100644 --- a/Dependencies/OmniKit/OmniKitUI/Resources/ar.lproj/Localizable.strings +++ b/Dependencies/OmniKit/OmniKitUI/Resources/ar.lproj/Localizable.strings @@ -2,10 +2,10 @@ "—" = "—"; /* Format string for last status date on pod details screen */ -"%@ ago" = "%@ ago"; +"%@ ago" = "منذ ‭%@"; /* Unit format string for an RSSI value in decibles */ -"%@ dB" = "%@ dB"; +"%@ dB" = "%@ ديسيبل"; /* No comment provided by engineer. */ "%@ has recovered communication with the pod on your body.\n\nInsulin delivery records have been updated and should match what has actually been delivered.\n\nYou may continue to use %@ normally now." = "%@ has recovered communication with the pod on your body.\n\nInsulin delivery records have been updated and should match what has actually been delivered.\n\nYou may continue to use %@ normally now."; @@ -78,13 +78,13 @@ "Adjusting Pump Time..." = "Adjusting Pump Time..."; /* The title of the cell showing alarm status */ -"Alarms" = "Alarms"; +"Alarms" = "التنبيهات"; /* Alert title for cancel pairing modal */ "Are you sure you want to cancel Pod setup?" = "Are you sure you want to cancel Pod setup?"; /* Confirmation message for shutting down a pod */ -"Are you sure you want to shutdown this pod?" = "Are you sure you want to shutdown this pod?"; +"Are you sure you want to shutdown this pod?" = "هل أنت متأكد من أنك تريد إيقاف هذه المضخة؟"; /* No comment provided by engineer. */ "Are you sure you want to skip Omnipod Onboarding?" = "Are you sure you want to skip Omnipod Onboarding?"; @@ -102,7 +102,7 @@ "Attemping to re-establish communication" = "Attemping to re-establish communication"; /* Back button text on DeliveryUncertaintyRecoveryView */ -"Back" = "Back"; +"Back" = "رجوع"; /* The title of the cell showing pod basal status */ "Basal Delivery" = "Basal Delivery"; @@ -129,10 +129,10 @@ "Change Pod now. Insulin delivery will stop in %1$@ or when no more insulin remains." = "Change Pod now. Insulin delivery will stop in %1$@ or when no more insulin remains."; /* The title of the command to change pump time zone */ -"Change Time Zone" = "Change Time Zone"; +"Change Time Zone" = "تغيير المنطقة الزمنية"; /* Progress message for changing pod time. */ -"Changing time…" = "Changing time…"; +"Changing time…" = "تغيير الوقت…"; /* Title for check cannula screen */ "Check Cannula" = "Check Cannula"; @@ -147,7 +147,7 @@ "Checking..." = "Checking..."; /* Title for uncertainty recovered screen */ -"Comms Recovered" = "Comms Recovered"; +"Comms Recovered" = "تم استرداد الاتصال"; /* Text for confidence reminders navigation link */ "Confidence Reminders" = "Confidence Reminders"; @@ -218,7 +218,7 @@ "Device Information" = "Device Information"; /* Header for devices section of RileyLinkSetupView */ -"Devices" = "Devices"; +"Devices" = "الأجهزة"; /* Title text for button to disable bolus beeps */ "Disable Bolus Beeps" = "Disable Bolus Beeps"; @@ -228,7 +228,7 @@ "Discard Pod" = "Discard Pod"; /* No comment provided by engineer. */ -"Done" = "Done"; +"Done" = "تمّ"; /* Title text for button to enable bolus beeps */ "Enable Bolus Beeps" = "Enable Bolus Beeps"; @@ -255,10 +255,10 @@ "Expiration Reminder Default" = "Expiration Reminder Default"; /* The title of the cell showing the pod expiration after expiry */ -"Expired" = "Expired"; +"Expired" = "منتهي"; /* The title of the cell showing the pod expiration */ -"Expires" = "Expires"; +"Expires" = "الانتهاء"; /* Alert title for failing to cancel manual basal error */ "Failed to Cancel Manual Basal" = "Failed to Cancel Manual Basal"; @@ -276,7 +276,7 @@ "Failed to update confidence reminder preference." = "Failed to update confidence reminder preference."; /* Alert title for error when updating expiration reminder */ -"Failed to Update Expiration Reminder" = "Failed to Update Expiration Reminder"; +"Failed to Update Expiration Reminder" = "فشل في تحديث تذكير انتهاء صلاحية"; /* Alert title for error when updating low reservoir reminder */ "Failed to Update Low Reservoir Reminder" = "Failed to Update Low Reservoir Reminder"; @@ -385,7 +385,7 @@ "minute" = "minute"; /* Unit for plural minutes in pod life remaining */ -"minutes" = "minutes"; +"minutes" = "دقائق"; /* Alert title for missing temp basal configuration */ "Missing Config" = "Missing Config"; @@ -399,7 +399,7 @@ "Next" = "Next"; /* Button label for user to answer cannula was not properly inserted */ -"No" = "No"; +"No" = "لا"; /* Text shown in insulin remaining space when no pod is paired */ "No\nDelivery" = "No\nDelivery"; @@ -425,7 +425,7 @@ /* navigation title for notification settings Text for pod details disclosure row */ -"Notification Settings" = "Notification Settings"; +"Notification Settings" = "إعدادات الإشعارات"; /* No comment provided by engineer. */ "Numbers" = "Numbers"; @@ -547,7 +547,7 @@ "Pump Time" = "Pump Time"; /* Label text for basal rate summary */ -"Rate" = "Rate"; +"Rate" = "معدّل"; /* Label describing time remaining view */ "Remaining" = "Remaining"; @@ -593,14 +593,14 @@ /* Title of button to save delivery limit settings Title of button to sync basal profile when no pod paired */ -"Save" = "Save"; +"Save" = "إحفظ"; /* button title for saving low reservoir reminder while saving button title for saving scheduled reminder while saving */ "Saving..." = "Saving..."; /* The detail text of the basal row when pod is running scheduled basal */ -"Schedule" = "Schedule"; +"Schedule" = "الجدول"; /* Title of insulin delivery section */ "Scheduled Basal" = "Scheduled Basal"; @@ -719,7 +719,7 @@ /* Label for expiration reminder row Label for scheduled expiration reminder row Label for scheduled reminder value row */ -"Time" = "Time"; +"Time" = "الوقت"; /* Title for pod sync time action sheet. title for time change detected notice */ @@ -763,7 +763,7 @@ "Wait until insertion is completed." = "Wait until insertion is completed."; /* Button label for user to answer cannula was properly inserted */ -"Yes" = "Yes"; +"Yes" = "نعم"; /* Button title for confirm deactivation option */ "Yes, Deactivate Pod" = "Yes, Deactivate Pod"; diff --git a/Dependencies/OmniKit/OmniKitUI/Resources/vi.lproj/Localizable.strings b/Dependencies/OmniKit/OmniKitUI/Resources/vi.lproj/Localizable.strings index ccfca0225a..685b3eedd5 100644 --- a/Dependencies/OmniKit/OmniKitUI/Resources/vi.lproj/Localizable.strings +++ b/Dependencies/OmniKit/OmniKitUI/Resources/vi.lproj/Localizable.strings @@ -657,7 +657,7 @@ "Suspending insulin delivery..." = "Đang tạm dừng liều insulin..."; /* Title text for the button to delete Omnipod PumpManager */ -"Switch from Omnipod Pumps" = "Chuyển đổ từ bơm Omnipod"; +"Switch from Omnipod Pumps" = "Chuyển đổi từ bơm Omnipod"; /* Label for PumpManager deletion button */ "Switch to other insulin delivery device" = "Chuyển đổi sang bơm khác"; diff --git a/Dependencies/rileylink_ios/RileyLinkKitUI/ar.lproj/Localizable.strings b/Dependencies/rileylink_ios/RileyLinkKitUI/ar.lproj/Localizable.strings index f74714bf35..72c1419928 100644 --- a/Dependencies/rileylink_ios/RileyLinkKitUI/ar.lproj/Localizable.strings +++ b/Dependencies/rileylink_ios/RileyLinkKitUI/ar.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* Unit format string for an RSSI value in decibles */ -"%@ dB" = "%@ dB"; +"%@ dB" = "%@ ديسيبل"; /* Unit format string for an value in percent */ "%@%%" = "%@%%"; @@ -29,7 +29,7 @@ "Connection Monitoring" = "Connection Monitoring"; /* The title of the cell showing BLE connection state */ -"Connection State" = "Connection State"; +"Connection State" = "حالة الإتصال"; /* The title of the cell for connection vibration */ "Connection Vibration" = "Connection Vibration"; @@ -38,7 +38,7 @@ "Device" = "Device"; /* The title of the devices table section in RileyLink settings */ -"Devices" = "Devices"; +"Devices" = "الأجهزة"; /* The disconnected state */ "Disconnected" = "Disconnected"; @@ -50,7 +50,7 @@ "Find Device" = "Find Device"; /* The title of the cell showing firmware version */ -"Firmware" = "Firmware"; +"Firmware" = "البرنامج الثابت"; /* The title of the cell showing current rileylink frequency */ "Frequency" = "Frequency"; @@ -71,7 +71,7 @@ "Low Battery Alert" = "Low Battery Alert"; /* The title of the cell showing device name */ -"Name" = "Name"; +"Name" = "الاسم"; /* Detail text when battery alert disabled. Text indicating LED Mode is off */ diff --git a/Dependencies/rileylink_ios/RileyLinkKitUI/vi.lproj/Localizable.strings b/Dependencies/rileylink_ios/RileyLinkKitUI/vi.lproj/Localizable.strings index 2cff4cd87f..05cebbbca6 100644 --- a/Dependencies/rileylink_ios/RileyLinkKitUI/vi.lproj/Localizable.strings +++ b/Dependencies/rileylink_ios/RileyLinkKitUI/vi.lproj/Localizable.strings @@ -88,10 +88,10 @@ /* The title of the section for orangelink commands The title of the section for rileylink commands */ -"Test Commands" = "Thử nghiệm câu lệnh"; +"Test Commands" = "Kiểm tra câu lệnh"; /* The title of the cell showing Test Vibration */ -"Test Vibration" = "Thử nghiệm độ rung"; +"Test Vibration" = "Kiểm tra độ rung"; /* The title of the command to update diagnostic LEDs */ "Toggle Diagnostic LEDs" = "Cho phép chuẩn đoán LEDs"; diff --git a/FreeAPS.xcodeproj/project.pbxproj b/FreeAPS.xcodeproj/project.pbxproj index fcc7422dcd..344710a6ac 100644 --- a/FreeAPS.xcodeproj/project.pbxproj +++ b/FreeAPS.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ 1982F7DB2BA6509D00EAFADE /* PumpStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1982F7DA2BA6509D00EAFADE /* PumpStorage.swift */; }; 198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; }; 198CED9D2B2E62940073032D /* OverrideStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 198CED9C2B2E62940073032D /* OverrideStorage.swift */; }; + 1994DBE02C696A410076FCF1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; }; + 1994DBE12C696FA10076FCF1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; }; 199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; }; 19A910302A24BF6300C8951B /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A9102F2A24BF6300C8951B /* StatsView.swift */; }; 19A910362A24D6D700C8951B /* Configs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A910352A24D6D700C8951B /* Configs.swift */; }; @@ -2889,6 +2891,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1994DBE02C696A410076FCF1 /* Assets.xcassets in Resources */, + 1994DBE12C696FA10076FCF1 /* Localizable.strings in Resources */, 6B1A8D242B14D91700E76752 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved index 37e3510a58..c6fc96a667 100644 --- a/FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -39,7 +39,7 @@ }, { "package": "SwiftCharts", - "repositoryURL": "https://github.com/ivanschuetz/SwiftCharts.git", + "repositoryURL": "https://github.com/ivanschuetz/SwiftCharts", "state": { "branch": "master", "revision": "c354c1945bb35a1f01b665b22474f6db28cba4a2", diff --git a/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/Contents.json b/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/Contents.json new file mode 100644 index 0000000000..501759a433 --- /dev/null +++ b/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "glucoesDropClearNoPointerExtraWhite.png", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "glucoesDropBlack5_2.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "compression-type" : "lossless", + "template-rendering-intent" : "original" + } +} diff --git a/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/glucoesDropBlack5_2.png b/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/glucoesDropBlack5_2.png new file mode 100644 index 0000000000000000000000000000000000000000..d19eedff4d4aa68893319a9146b99f3fec8add0c GIT binary patch literal 36502 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 Bc$xqJ literal 0 HcmV?d00001 diff --git a/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/glucoesDropClearNoPointerExtraWhite.png b/FreeAPS/Resources/Assets.xcassets/glucoseDrops.imageset/glucoesDropClearNoPointerExtraWhite.png new file mode 100644 index 0000000000000000000000000000000000000000..f48d5712f78a391435c31ff190dbf43a1abac931 GIT binary patch literal 34090 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

o9PE^HWZnGO7Oa3; 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 literal 0 HcmV?d00001 diff --git a/FreeAPS/Resources/javascript/bundle/profile.js b/FreeAPS/Resources/javascript/bundle/profile.js index 03366d75e8..b932cef25f 100644 --- a/FreeAPS/Resources/javascript/bundle/profile.js +++ b/FreeAPS/Resources/javascript/bundle/profile.js @@ -1,2 +1 @@ -/*! For license information please see profile.js.LICENSE.txt */ -var freeaps_profile;(()=>{var t={6261:(t,n)=>{t.exports=function(t){var n=new Date;return n.setHours("00"),n.setMinutes("00"),n.setSeconds("00"),n.getTime()+60*t*1e3}},1836:(t,n,r)=>{var e=r(6486);n.maxDailyBasal=function(t){var n=e.maxBy(t.basals,(function(t){return Number(t.rate)}));return 1e3*Number(n.rate)/1e3},n.maxBasalLookup=function(t){return t.settings.maxBasal},n.basalLookup=function(t,n){var r=n;void 0===n&&(r=new Date);var u=e.sortBy(t,(function(t){return t.i})),i=u[u.length-1].rate;if(0!==i){for(var a=60*r.getHours()+r.getMinutes(),o=0;o=u[o].minutes&&a{var e=r(6261);function u(t,n){var r=new Date,u=t.carbratio;if(void 0===u||void 0===u.schedule);else{var i;if("grams"===u.units||"exchanges"===u.units){i=u.schedule[u.schedule.length-1];for(var a=0;a=e(u.schedule[a].offset)&&r150)return void console.error("Error: carbRatio of "+i+" out of bounds.");break}return"exchanges"===u.units&&(i.ratio=12/i.ratio),i.ratio}console.error("Error: Unsupported carb_ratio units "+u.units)}}u.carbRatioLookup=u,t.exports=u},6014:(t,n,r)=>{var e=r(1836),u=r(4034),i=r(3725),a=r(5364),o=r(6486);function f(){return{max_iob:9,max_daily_safety_multiplier:5,current_basal_safety_multiplier:6,autosens_max:2.5,autosens_min:.5,rewind_resets_autosens:!0,high_temptarget_raises_sensitivity:!1,low_temptarget_lowers_sensitivity:!1,sensitivity_raises_target:!1,resistance_lowers_target:!1,exercise_mode:!1,half_basal_exercise_target:150,maxCOB:120,skip_neutral_temps:!1,unsuspend_if_no_temp:!1,min_5m_carbimpact:8,autotune_isf_adjustmentFraction:1,remainingCarbsFraction:1,remainingCarbsCap:90,enableUAM:!0,A52_risk_enable:!1,enableSMB_with_COB:!0,enableSMB_with_temptarget:!0,enableSMB_always:!0,enableSMB_after_carbs:!0,allowSMB_with_high_temptarget:!0,maxSMBBasalMinutes:90,maxUAMSMBBasalMinutes:90,SMBInterval:3,bolus_increment:.05,maxDelta_bg_threshold:.2,curve:"rapid-acting",useCustomPeakTime:!1,insulinPeakTime:45,carbsReqThreshold:1,offline_hotspot:!1,noisyCGMTargetMultiplier:1.3,suspend_zeros_iob:!0,enableEnliteBgproxy:!1,calc_glucose_noise:!1,target_bg:!1,smb_delivery_ratio:.5,adjustmentFactor:1,useNewFormula:!1,enableDynamicCR:!1,sigmoid:!1,weightPercentage:.65,tddAdjBasal:!1,enableSMB_high_bg:!1,enableSMB_high_bg_target:110}}function c(t,n){var r=n&&n.type?n:{max_iob:9,max_daily_safety_multiplier:5,current_basal_safety_multiplier:6,autosens_max:2.5,autosens_min:.5,rewind_resets_autosens:!0,high_temptarget_raises_sensitivity:!1,low_temptarget_lowers_sensitivity:!1,sensitivity_raises_target:!1,resistance_lowers_target:!1,exercise_mode:!1,half_basal_exercise_target:150,maxCOB:120,skip_neutral_temps:!1,unsuspend_if_no_temp:!1,min_5m_carbimpact:8,autotune_isf_adjustmentFraction:1,remainingCarbsFraction:1,remainingCarbsCap:90,enableUAM:!0,A52_risk_enable:!1,enableSMB_with_COB:!0,enableSMB_with_temptarget:!0,enableSMB_always:!0,enableSMB_after_carbs:!0,allowSMB_with_high_temptarget:!0,maxSMBBasalMinutes:90,maxUAMSMBBasalMinutes:90,SMBInterval:3,bolus_increment:.05,maxDelta_bg_threshold:.2,curve:"rapid-acting",useCustomPeakTime:!1,insulinPeakTime:45,carbsReqThreshold:1,offline_hotspot:!1,noisyCGMTargetMultiplier:1.3,suspend_zeros_iob:!0,enableEnliteBgproxy:!1,calc_glucose_noise:!1,target_bg:!1,smb_delivery_ratio:.5,adjustmentFactor:1,useNewFormula:!1,enableDynamicCR:!1,sigmoid:!1,weightPercentage:.65,tddAdjBasal:!1,enableSMB_high_bg:!1,enableSMB_high_bg_target:110,threshold_setting:65};for(var f in r)t.hasOwnProperty(f)&&(r[f]=t[f]);var c=t.settings;if(!(t.settings.insulin_action_curve>1))return console.error("DIA of",r.dia,"is not supported"),-1;if(r.dia=c.insulin_action_curve,t.model&&(r.model=t.model),r.skip_neutral_temps=t.skip_neutral_temps,r.current_basal=e.basalLookup(t.basals),r.basalprofile=t.basals,o.forEach(r.basalprofile,(function(t){t.rate=+(Math.round(t.rate+"e+3")+"e-3")})),r.max_daily_basal=e.maxDailyBasal(t),r.max_basal=e.maxBasalLookup(t),0===r.current_basal)return console.error("current_basal of",r.current_basal,"is not supported"),-1;if(0===r.max_daily_basal)return console.error("max_daily_basal of",r.max_daily_basal,"is not supported"),-1;if(r.max_basal<.1)return console.error("max_basal of",r.max_basal,"is not supported"),-1;var l=u.bgTargetsLookup(t,r);return r.out_units=t.targets.user_preferred_units,r.min_bg=Math.round(l.min_bg),r.max_bg=Math.round(l.max_bg),r.bg_targets=t.targets,o.forEach(r.bg_targets.targets,(function(t){t.high=Math.round(t.high),t.low=Math.round(t.low),t.min_bg=Math.round(t.min_bg),t.max_bg=Math.round(t.max_bg)})),delete r.bg_targets.raw,r.temptargetSet=l.temptargetSet,r.sens=i.isfLookup(t.isf),r.isfProfile=t.isf,r.sens<5?(console.error("ISF of",r.sens,"is not supported"),-1):(void 0!==t.carbratio?(r.carb_ratio=a.carbRatioLookup(t,r),r.carb_ratios=t.carbratio):console.error("Profile wasn't given carb ratio data, cannot calculate carb_ratio"),r)}c.defaults=f,c.displayedDefaults=function(){var t={max_iob:9,max_daily_safety_multiplier:5,current_basal_safety_multiplier:6,autosens_max:2.5,autosens_min:.5,rewind_resets_autosens:!0,high_temptarget_raises_sensitivity:!1,low_temptarget_lowers_sensitivity:!1,sensitivity_raises_target:!1,resistance_lowers_target:!1,exercise_mode:!1,half_basal_exercise_target:150,maxCOB:120,skip_neutral_temps:!1,unsuspend_if_no_temp:!1,min_5m_carbimpact:8,autotune_isf_adjustmentFraction:1,remainingCarbsFraction:1,remainingCarbsCap:90,enableUAM:!0,A52_risk_enable:!1,enableSMB_with_COB:!0,enableSMB_with_temptarget:!0,enableSMB_always:!0,enableSMB_after_carbs:!0,allowSMB_with_high_temptarget:!0,maxSMBBasalMinutes:90,maxUAMSMBBasalMinutes:90,SMBInterval:3,bolus_increment:.05,maxDelta_bg_threshold:.2,curve:"rapid-acting",useCustomPeakTime:!1,insulinPeakTime:45,carbsReqThreshold:1,offline_hotspot:!1,noisyCGMTargetMultiplier:1.3,suspend_zeros_iob:!0,enableEnliteBgproxy:!1,calc_glucose_noise:!1,target_bg:!1,smb_delivery_ratio:.5,adjustmentFactor:1,useNewFormula:!1,enableDynamicCR:!1,sigmoid:!1,weightPercentage:.65,tddAdjBasal:!1,enableSMB_high_bg:!1,enableSMB_high_bg_target:110,threshold_setting:65},n={};return n.max_iob=t.max_iob,n.max_daily_safety_multiplier=t.max_daily_safety_multiplier,n.current_basal_safety_multiplier=t.current_basal_safety_multiplier,n.autosens_max=t.autosens_max,n.autosens_min=t.autosens_min,n.rewind_resets_autosens=t.rewind_resets_autosens,n.exercise_mode=t.exercise_mode,n.sensitivity_raises_target=t.sensitivity_raises_target,n.unsuspend_if_no_temp=t.unsuspend_if_no_temp,n.enableSMB_with_COB=t.enableSMB_with_COB,n.enableSMB_with_temptarget=t.enableSMB_with_temptarget,n.enableUAM=t.enableUAM,n.curve=t.curve,n.offline_hotspot=t.offline_hotspot,n.bolus_increment=t.bolus_increment,n.smb_delivery_ratio=t.smb_delivery_ratio,n.maxDelta_bg_threshold=t.maxDelta_bg_threshold,n.adjustmentFactor=t.adjustmentFactor,n.useNewFormula=t.useNewFormula,n.enableDynamicCR=t.enableDynamicCR,n.sigmoid=t.sigmoid,n.weightPercentage=t.weightPercentage,n.tddAdjBasal=t.tddAdjBasal,console.error(n),n},t.exports=c},3725:(t,n,r)=>{var e=r(6486),u=null;function i(t,n){var r=n;void 0===n&&(r=new Date);var i=60*r.getHours()+r.getMinutes();if(u&&i>=u.offset&&i=c.offset&&i{var e=r(6261);function u(t,n){return a(i(t,n))}function i(t,n){for(var r=t.targets,u=t.temptargets,i=new Date,a=r.targets[r.targets.length-1],o=0;o=e(r.targets[o].offset)&&i=c&&0===u[o].duration){f=a;break}if(!u[o].targetBottom||!u[o].targetTop){console.error("eventualBG target range invalid: "+u[o].targetBottom+"-"+u[o].targetTop);break}if(i>=c&&i"']/g,J=RegExp(H.source),Y=RegExp(V.source),Q=/<%-([\s\S]+?)%>/g,X=/<%([\s\S]+?)%>/g,tt=/<%=([\s\S]+?)%>/g,nt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,rt=/^\w*$/,et=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,ut=/[\\^$.*+?()[\]{}|]/g,it=RegExp(ut.source),at=/^\s+/,ot=/\s/,ft=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,ct=/\{\n\/\* \[wrapped with (.+)\] \*/,lt=/,? & /,st=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,_t=/[()=,{}\[\]\/\s]/,ht=/\\(\\)?/g,pt=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,gt=/\w*$/,vt=/^[-+]0x[0-9a-f]+$/i,dt=/^0b[01]+$/i,yt=/^\[object .+?Constructor\]$/,bt=/^0o[0-7]+$/i,mt=/^(?:0|[1-9]\d*)$/,wt=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,xt=/($^)/,St=/['\n\r\u2028\u2029\\]/g,Bt="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",jt="\\u2700-\\u27bf",At="a-z\\xdf-\\xf6\\xf8-\\xff",kt="A-Z\\xc0-\\xd6\\xd8-\\xde",It="\\ufe0e\\ufe0f",Mt="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Ct="['’]",Ft="[\\ud800-\\udfff]",Ot="["+Mt+"]",Rt="["+Bt+"]",Et="\\d+",zt="[\\u2700-\\u27bf]",Tt="["+At+"]",Dt="[^\\ud800-\\udfff"+Mt+Et+jt+At+kt+"]",Lt="\\ud83c[\\udffb-\\udfff]",Ut="[^\\ud800-\\udfff]",Wt="(?:\\ud83c[\\udde6-\\uddff]){2}",Pt="[\\ud800-\\udbff][\\udc00-\\udfff]",Nt="["+kt+"]",$t="(?:"+Tt+"|"+Dt+")",qt="(?:"+Nt+"|"+Dt+")",Gt="(?:['’](?:d|ll|m|re|s|t|ve))?",Zt="(?:['’](?:D|LL|M|RE|S|T|VE))?",Kt="(?:"+Rt+"|"+Lt+")"+"?",Ht="[\\ufe0e\\ufe0f]?",Vt=Ht+Kt+("(?:\\u200d(?:"+[Ut,Wt,Pt].join("|")+")"+Ht+Kt+")*"),Jt="(?:"+[zt,Wt,Pt].join("|")+")"+Vt,Yt="(?:"+[Ut+Rt+"?",Rt,Wt,Pt,Ft].join("|")+")",Qt=RegExp(Ct,"g"),Xt=RegExp(Rt,"g"),tn=RegExp(Lt+"(?="+Lt+")|"+Yt+Vt,"g"),nn=RegExp([Nt+"?"+Tt+"+"+Gt+"(?="+[Ot,Nt,"$"].join("|")+")",qt+"+"+Zt+"(?="+[Ot,Nt+$t,"$"].join("|")+")",Nt+"?"+$t+"+"+Gt,Nt+"+"+Zt,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Et,Jt].join("|"),"g"),rn=RegExp("[\\u200d\\ud800-\\udfff"+Bt+It+"]"),en=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,un=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],an=-1,on={};on[T]=on[D]=on[L]=on[U]=on[W]=on[P]=on[N]=on[$]=on[q]=!0,on[y]=on[b]=on[E]=on[m]=on[z]=on[w]=on[x]=on[S]=on[j]=on[A]=on[k]=on[M]=on[C]=on[F]=on[R]=!1;var fn={};fn[y]=fn[b]=fn[E]=fn[z]=fn[m]=fn[w]=fn[T]=fn[D]=fn[L]=fn[U]=fn[W]=fn[j]=fn[A]=fn[k]=fn[M]=fn[C]=fn[F]=fn[O]=fn[P]=fn[N]=fn[$]=fn[q]=!0,fn[x]=fn[S]=fn[R]=!1;var cn={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},ln=parseFloat,sn=parseInt,_n="object"==typeof r.g&&r.g&&r.g.Object===Object&&r.g,hn="object"==typeof self&&self&&self.Object===Object&&self,pn=_n||hn||Function("return this")(),gn=n&&!n.nodeType&&n,vn=gn&&t&&!t.nodeType&&t,dn=vn&&vn.exports===gn,yn=dn&&_n.process,bn=function(){try{var t=vn&&vn.require&&vn.require("util").types;return t||yn&&yn.binding&&yn.binding("util")}catch(t){}}(),mn=bn&&bn.isArrayBuffer,wn=bn&&bn.isDate,xn=bn&&bn.isMap,Sn=bn&&bn.isRegExp,Bn=bn&&bn.isSet,jn=bn&&bn.isTypedArray;function An(t,n,r){switch(r.length){case 0:return t.call(n);case 1:return t.call(n,r[0]);case 2:return t.call(n,r[0],r[1]);case 3:return t.call(n,r[0],r[1],r[2])}return t.apply(n,r)}function kn(t,n,r,e){for(var u=-1,i=null==t?0:t.length;++u-1}function Rn(t,n,r){for(var e=-1,u=null==t?0:t.length;++e-1;);return r}function rr(t,n){for(var r=t.length;r--&&Nn(n,t[r],0)>-1;);return r}function er(t,n){for(var r=t.length,e=0;r--;)t[r]===n&&++e;return e}var ur=Kn({À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"}),ir=Kn({"&":"&","<":"<",">":">",'"':""","'":"'"});function ar(t){return"\\"+cn[t]}function or(t){return rn.test(t)}function fr(t){var n=-1,r=Array(t.size);return t.forEach((function(t,e){r[++n]=[e,t]})),r}function cr(t,n){return function(r){return t(n(r))}}function lr(t,n){for(var r=-1,e=t.length,u=0,i=[];++r",""":'"',"'":"'"});var dr=function t(n){var r,e=(n=null==n?pn:dr.defaults(pn.Object(),n,dr.pick(pn,un))).Array,ot=n.Date,Bt=n.Error,jt=n.Function,At=n.Math,kt=n.Object,It=n.RegExp,Mt=n.String,Ct=n.TypeError,Ft=e.prototype,Ot=jt.prototype,Rt=kt.prototype,Et=n["__core-js_shared__"],zt=Ot.toString,Tt=Rt.hasOwnProperty,Dt=0,Lt=(r=/[^.]+$/.exec(Et&&Et.keys&&Et.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"",Ut=Rt.toString,Wt=zt.call(kt),Pt=pn._,Nt=It("^"+zt.call(Tt).replace(ut,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),$t=dn?n.Buffer:u,qt=n.Symbol,Gt=n.Uint8Array,Zt=$t?$t.allocUnsafe:u,Kt=cr(kt.getPrototypeOf,kt),Ht=kt.create,Vt=Rt.propertyIsEnumerable,Jt=Ft.splice,Yt=qt?qt.isConcatSpreadable:u,tn=qt?qt.iterator:u,rn=qt?qt.toStringTag:u,cn=function(){try{var t=hi(kt,"defineProperty");return t({},"",{}),t}catch(t){}}(),_n=n.clearTimeout!==pn.clearTimeout&&n.clearTimeout,hn=ot&&ot.now!==pn.Date.now&&ot.now,gn=n.setTimeout!==pn.setTimeout&&n.setTimeout,vn=At.ceil,yn=At.floor,bn=kt.getOwnPropertySymbols,Un=$t?$t.isBuffer:u,Kn=n.isFinite,yr=Ft.join,br=cr(kt.keys,kt),mr=At.max,wr=At.min,xr=ot.now,Sr=n.parseInt,Br=At.random,jr=Ft.reverse,Ar=hi(n,"DataView"),kr=hi(n,"Map"),Ir=hi(n,"Promise"),Mr=hi(n,"Set"),Cr=hi(n,"WeakMap"),Fr=hi(kt,"create"),Or=Cr&&new Cr,Rr={},Er=Wi(Ar),zr=Wi(kr),Tr=Wi(Ir),Dr=Wi(Mr),Lr=Wi(Cr),Ur=qt?qt.prototype:u,Wr=Ur?Ur.valueOf:u,Pr=Ur?Ur.toString:u;function Nr(t){if(eo(t)&&!Za(t)&&!(t instanceof Zr)){if(t instanceof Gr)return t;if(Tt.call(t,"__wrapped__"))return Pi(t)}return new Gr(t)}var $r=function(){function t(){}return function(n){if(!ro(n))return{};if(Ht)return Ht(n);t.prototype=n;var r=new t;return t.prototype=u,r}}();function qr(){}function Gr(t,n){this.__wrapped__=t,this.__actions__=[],this.__chain__=!!n,this.__index__=0,this.__values__=u}function Zr(t){this.__wrapped__=t,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=v,this.__views__=[]}function Kr(t){var n=-1,r=null==t?0:t.length;for(this.clear();++n=n?t:n)),t}function le(t,n,r,e,i,a){var o,f=1&n,c=2&n,l=4&n;if(r&&(o=i?r(t,e,i,a):r(t)),o!==u)return o;if(!ro(t))return t;var s=Za(t);if(s){if(o=function(t){var n=t.length,r=new t.constructor(n);n&&"string"==typeof t[0]&&Tt.call(t,"index")&&(r.index=t.index,r.input=t.input);return r}(t),!f)return Fu(t,o)}else{var _=vi(t),h=_==S||_==B;if(Ja(t))return ju(t,f);if(_==k||_==y||h&&!i){if(o=c||h?{}:yi(t),!f)return c?function(t,n){return Ou(t,gi(t),n)}(t,function(t,n){return t&&Ou(n,zo(n),t)}(o,t)):function(t,n){return Ou(t,pi(t),n)}(t,ae(o,t))}else{if(!fn[_])return i?t:{};o=function(t,n,r){var e=t.constructor;switch(n){case E:return Au(t);case m:case w:return new e(+t);case z:return function(t,n){var r=n?Au(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.byteLength)}(t,r);case T:case D:case L:case U:case W:case P:case N:case $:case q:return ku(t,r);case j:return new e;case A:case F:return new e(t);case M:return function(t){var n=new t.constructor(t.source,gt.exec(t));return n.lastIndex=t.lastIndex,n}(t);case C:return new e;case O:return u=t,Wr?kt(Wr.call(u)):{}}var u}(t,_,f)}}a||(a=new Yr);var p=a.get(t);if(p)return p;a.set(t,o),fo(t)?t.forEach((function(e){o.add(le(e,n,r,e,t,a))})):uo(t)&&t.forEach((function(e,u){o.set(u,le(e,n,r,u,t,a))}));var g=s?u:(l?c?ai:ii:c?zo:Eo)(t);return In(g||t,(function(e,u){g&&(e=t[u=e]),ee(o,u,le(e,n,r,u,t,a))})),o}function se(t,n,r){var e=r.length;if(null==t)return!e;for(t=kt(t);e--;){var i=r[e],a=n[i],o=t[i];if(o===u&&!(i in t)||!a(o))return!1}return!0}function _e(t,n,r){if("function"!=typeof t)throw new Ct(i);return Ri((function(){t.apply(u,r)}),n)}function he(t,n,r,e){var u=-1,i=On,a=!0,o=t.length,f=[],c=n.length;if(!o)return f;r&&(n=En(n,Qn(r))),e?(i=Rn,a=!1):n.length>=200&&(i=tr,a=!1,n=new Jr(n));t:for(;++u-1},Hr.prototype.set=function(t,n){var r=this.__data__,e=ue(r,t);return e<0?(++this.size,r.push([t,n])):r[e][1]=n,this},Vr.prototype.clear=function(){this.size=0,this.__data__={hash:new Kr,map:new(kr||Hr),string:new Kr}},Vr.prototype.delete=function(t){var n=si(this,t).delete(t);return this.size-=n?1:0,n},Vr.prototype.get=function(t){return si(this,t).get(t)},Vr.prototype.has=function(t){return si(this,t).has(t)},Vr.prototype.set=function(t,n){var r=si(this,t),e=r.size;return r.set(t,n),this.size+=r.size==e?0:1,this},Jr.prototype.add=Jr.prototype.push=function(t){return this.__data__.set(t,a),this},Jr.prototype.has=function(t){return this.__data__.has(t)},Yr.prototype.clear=function(){this.__data__=new Hr,this.size=0},Yr.prototype.delete=function(t){var n=this.__data__,r=n.delete(t);return this.size=n.size,r},Yr.prototype.get=function(t){return this.__data__.get(t)},Yr.prototype.has=function(t){return this.__data__.has(t)},Yr.prototype.set=function(t,n){var r=this.__data__;if(r instanceof Hr){var e=r.__data__;if(!kr||e.length<199)return e.push([t,n]),this.size=++r.size,this;r=this.__data__=new Vr(e)}return r.set(t,n),this.size=r.size,this};var pe=zu(xe),ge=zu(Se,!0);function ve(t,n){var r=!0;return pe(t,(function(t,e,u){return r=!!n(t,e,u)})),r}function de(t,n,r){for(var e=-1,i=t.length;++e0&&r(o)?n>1?be(o,n-1,r,e,u):zn(u,o):e||(u[u.length]=o)}return u}var me=Tu(),we=Tu(!0);function xe(t,n){return t&&me(t,n,Eo)}function Se(t,n){return t&&we(t,n,Eo)}function Be(t,n){return Fn(n,(function(n){return Xa(t[n])}))}function je(t,n){for(var r=0,e=(n=wu(n,t)).length;null!=t&&rn}function Me(t,n){return null!=t&&Tt.call(t,n)}function Ce(t,n){return null!=t&&n in kt(t)}function Fe(t,n,r){for(var i=r?Rn:On,a=t[0].length,o=t.length,f=o,c=e(o),l=1/0,s=[];f--;){var _=t[f];f&&n&&(_=En(_,Qn(n))),l=wr(_.length,l),c[f]=!r&&(n||a>=120&&_.length>=120)?new Jr(f&&_):u}_=t[0];var h=-1,p=c[0];t:for(;++h=o?f:f*("desc"==r[e]?-1:1)}return t.index-n.index}(t,n,r)}))}function Ke(t,n,r){for(var e=-1,u=n.length,i={};++e-1;)o!==t&&Jt.call(o,f,1),Jt.call(t,f,1);return t}function Ve(t,n){for(var r=t?n.length:0,e=r-1;r--;){var u=n[r];if(r==e||u!==i){var i=u;mi(u)?Jt.call(t,u,1):hu(t,u)}}return t}function Je(t,n){return t+yn(Br()*(n-t+1))}function Ye(t,n){var r="";if(!t||n<1||n>p)return r;do{n%2&&(r+=t),(n=yn(n/2))&&(t+=t)}while(n);return r}function Qe(t,n){return Ei(Ii(t,n,of),t+"")}function Xe(t){return Xr($o(t))}function tu(t,n){var r=$o(t);return Di(r,ce(n,0,r.length))}function nu(t,n,r,e){if(!ro(t))return t;for(var i=-1,a=(n=wu(n,t)).length,o=a-1,f=t;null!=f&&++ii?0:i+n),(r=r>i?i:r)<0&&(r+=i),i=n>r?0:r-n>>>0,n>>>=0;for(var a=e(i);++u>>1,a=t[i];null!==a&&!lo(a)&&(r?a<=n:a=200){var c=n?null:Yu(t);if(c)return sr(c);a=!1,u=tr,f=new Jr}else f=n?[]:o;t:for(;++e=e?t:iu(t,n,r)}var Bu=_n||function(t){return pn.clearTimeout(t)};function ju(t,n){if(n)return t.slice();var r=t.length,e=Zt?Zt(r):new t.constructor(r);return t.copy(e),e}function Au(t){var n=new t.constructor(t.byteLength);return new Gt(n).set(new Gt(t)),n}function ku(t,n){var r=n?Au(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.length)}function Iu(t,n){if(t!==n){var r=t!==u,e=null===t,i=t==t,a=lo(t),o=n!==u,f=null===n,c=n==n,l=lo(n);if(!f&&!l&&!a&&t>n||a&&o&&c&&!f&&!l||e&&o&&c||!r&&c||!i)return 1;if(!e&&!a&&!l&&t1?r[i-1]:u,o=i>2?r[2]:u;for(a=t.length>3&&"function"==typeof a?(i--,a):u,o&&wi(r[0],r[1],o)&&(a=i<3?u:a,i=1),n=kt(n);++e-1?i[a?n[o]:o]:u}}function Pu(t){return ui((function(n){var r=n.length,e=r,a=Gr.prototype.thru;for(t&&n.reverse();e--;){var o=n[e];if("function"!=typeof o)throw new Ct(i);if(a&&!f&&"wrapper"==fi(o))var f=new Gr([],!0)}for(e=f?e:r;++e1&&b.reverse(),h&&lf))return!1;var l=a.get(t),s=a.get(n);if(l&&s)return l==n&&s==t;var _=-1,h=!0,p=2&r?new Jr:u;for(a.set(t,n),a.set(n,t);++_-1&&t%1==0&&t1?"& ":"")+n[e],n=n.join(r>2?", ":" "),t.replace(ft,"{\n/* [wrapped with "+n+"] */\n")}(e,function(t,n){return In(d,(function(r){var e="_."+r[0];n&r[1]&&!On(t,e)&&t.push(e)})),t.sort()}(function(t){var n=t.match(ct);return n?n[1].split(lt):[]}(e),r)))}function Ti(t){var n=0,r=0;return function(){var e=xr(),i=16-(e-r);if(r=e,i>0){if(++n>=800)return arguments[0]}else n=0;return t.apply(u,arguments)}}function Di(t,n){var r=-1,e=t.length,i=e-1;for(n=n===u?e:n;++r1?t[n-1]:u;return r="function"==typeof r?(t.pop(),r):u,oa(t,r)}));function pa(t){var n=Nr(t);return n.__chain__=!0,n}function ga(t,n){return n(t)}var va=ui((function(t){var n=t.length,r=n?t[0]:0,e=this.__wrapped__,i=function(n){return fe(n,t)};return!(n>1||this.__actions__.length)&&e instanceof Zr&&mi(r)?((e=e.slice(r,+r+(n?1:0))).__actions__.push({func:ga,args:[i],thisArg:u}),new Gr(e,this.__chain__).thru((function(t){return n&&!t.length&&t.push(u),t}))):this.thru(i)}));var da=Ru((function(t,n,r){Tt.call(t,r)?++t[r]:oe(t,r,1)}));var ya=Wu(Gi),ba=Wu(Zi);function ma(t,n){return(Za(t)?In:pe)(t,li(n,3))}function wa(t,n){return(Za(t)?Mn:ge)(t,li(n,3))}var xa=Ru((function(t,n,r){Tt.call(t,r)?t[r].push(n):oe(t,r,[n])}));var Sa=Qe((function(t,n,r){var u=-1,i="function"==typeof n,a=Ha(t)?e(t.length):[];return pe(t,(function(t){a[++u]=i?An(n,t,r):Oe(t,n,r)})),a})),Ba=Ru((function(t,n,r){oe(t,r,n)}));function ja(t,n){return(Za(t)?En:Pe)(t,li(n,3))}var Aa=Ru((function(t,n,r){t[r?0:1].push(n)}),(function(){return[[],[]]}));var ka=Qe((function(t,n){if(null==t)return[];var r=n.length;return r>1&&wi(t,n[0],n[1])?n=[]:r>2&&wi(n[0],n[1],n[2])&&(n=[n[0]]),Ze(t,be(n,1),[])})),Ia=hn||function(){return pn.Date.now()};function Ma(t,n,r){return n=r?u:n,n=t&&null==n?t.length:n,Xu(t,s,u,u,u,u,n)}function Ca(t,n){var r;if("function"!=typeof n)throw new Ct(i);return t=vo(t),function(){return--t>0&&(r=n.apply(this,arguments)),t<=1&&(n=u),r}}var Fa=Qe((function(t,n,r){var e=1;if(r.length){var u=lr(r,ci(Fa));e|=c}return Xu(t,e,n,r,u)})),Oa=Qe((function(t,n,r){var e=3;if(r.length){var u=lr(r,ci(Oa));e|=c}return Xu(n,e,t,r,u)}));function Ra(t,n,r){var e,a,o,f,c,l,s=0,_=!1,h=!1,p=!0;if("function"!=typeof t)throw new Ct(i);function g(n){var r=e,i=a;return e=a=u,s=n,f=t.apply(i,r)}function v(t){return s=t,c=Ri(y,n),_?g(t):f}function d(t){var r=t-l;return l===u||r>=n||r<0||h&&t-s>=o}function y(){var t=Ia();if(d(t))return b(t);c=Ri(y,function(t){var r=n-(t-l);return h?wr(r,o-(t-s)):r}(t))}function b(t){return c=u,p&&e?g(t):(e=a=u,f)}function m(){var t=Ia(),r=d(t);if(e=arguments,a=this,l=t,r){if(c===u)return v(l);if(h)return Bu(c),c=Ri(y,n),g(l)}return c===u&&(c=Ri(y,n)),f}return n=bo(n)||0,ro(r)&&(_=!!r.leading,o=(h="maxWait"in r)?mr(bo(r.maxWait)||0,n):o,p="trailing"in r?!!r.trailing:p),m.cancel=function(){c!==u&&Bu(c),s=0,e=l=a=c=u},m.flush=function(){return c===u?f:b(Ia())},m}var Ea=Qe((function(t,n){return _e(t,1,n)})),za=Qe((function(t,n,r){return _e(t,bo(n)||0,r)}));function Ta(t,n){if("function"!=typeof t||null!=n&&"function"!=typeof n)throw new Ct(i);var r=function(){var e=arguments,u=n?n.apply(this,e):e[0],i=r.cache;if(i.has(u))return i.get(u);var a=t.apply(this,e);return r.cache=i.set(u,a)||i,a};return r.cache=new(Ta.Cache||Vr),r}function Da(t){if("function"!=typeof t)throw new Ct(i);return function(){var n=arguments;switch(n.length){case 0:return!t.call(this);case 1:return!t.call(this,n[0]);case 2:return!t.call(this,n[0],n[1]);case 3:return!t.call(this,n[0],n[1],n[2])}return!t.apply(this,n)}}Ta.Cache=Vr;var La=xu((function(t,n){var r=(n=1==n.length&&Za(n[0])?En(n[0],Qn(li())):En(be(n,1),Qn(li()))).length;return Qe((function(e){for(var u=-1,i=wr(e.length,r);++u=n})),Ga=Re(function(){return arguments}())?Re:function(t){return eo(t)&&Tt.call(t,"callee")&&!Vt.call(t,"callee")},Za=e.isArray,Ka=mn?Qn(mn):function(t){return eo(t)&&ke(t)==E};function Ha(t){return null!=t&&no(t.length)&&!Xa(t)}function Va(t){return eo(t)&&Ha(t)}var Ja=Un||mf,Ya=wn?Qn(wn):function(t){return eo(t)&&ke(t)==w};function Qa(t){if(!eo(t))return!1;var n=ke(t);return n==x||"[object DOMException]"==n||"string"==typeof t.message&&"string"==typeof t.name&&!ao(t)}function Xa(t){if(!ro(t))return!1;var n=ke(t);return n==S||n==B||"[object AsyncFunction]"==n||"[object Proxy]"==n}function to(t){return"number"==typeof t&&t==vo(t)}function no(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=p}function ro(t){var n=typeof t;return null!=t&&("object"==n||"function"==n)}function eo(t){return null!=t&&"object"==typeof t}var uo=xn?Qn(xn):function(t){return eo(t)&&vi(t)==j};function io(t){return"number"==typeof t||eo(t)&&ke(t)==A}function ao(t){if(!eo(t)||ke(t)!=k)return!1;var n=Kt(t);if(null===n)return!0;var r=Tt.call(n,"constructor")&&n.constructor;return"function"==typeof r&&r instanceof r&&zt.call(r)==Wt}var oo=Sn?Qn(Sn):function(t){return eo(t)&&ke(t)==M};var fo=Bn?Qn(Bn):function(t){return eo(t)&&vi(t)==C};function co(t){return"string"==typeof t||!Za(t)&&eo(t)&&ke(t)==F}function lo(t){return"symbol"==typeof t||eo(t)&&ke(t)==O}var so=jn?Qn(jn):function(t){return eo(t)&&no(t.length)&&!!on[ke(t)]};var _o=Hu(We),ho=Hu((function(t,n){return t<=n}));function po(t){if(!t)return[];if(Ha(t))return co(t)?pr(t):Fu(t);if(tn&&t[tn])return function(t){for(var n,r=[];!(n=t.next()).done;)r.push(n.value);return r}(t[tn]());var n=vi(t);return(n==j?fr:n==C?sr:$o)(t)}function go(t){return t?(t=bo(t))===h||t===-1/0?17976931348623157e292*(t<0?-1:1):t==t?t:0:0===t?t:0}function vo(t){var n=go(t),r=n%1;return n==n?r?n-r:n:0}function yo(t){return t?ce(vo(t),0,v):0}function bo(t){if("number"==typeof t)return t;if(lo(t))return g;if(ro(t)){var n="function"==typeof t.valueOf?t.valueOf():t;t=ro(n)?n+"":n}if("string"!=typeof t)return 0===t?t:+t;t=Yn(t);var r=dt.test(t);return r||bt.test(t)?sn(t.slice(2),r?2:8):vt.test(t)?g:+t}function mo(t){return Ou(t,zo(t))}function wo(t){return null==t?"":su(t)}var xo=Eu((function(t,n){if(ji(n)||Ha(n))Ou(n,Eo(n),t);else for(var r in n)Tt.call(n,r)&&ee(t,r,n[r])})),So=Eu((function(t,n){Ou(n,zo(n),t)})),Bo=Eu((function(t,n,r,e){Ou(n,zo(n),t,e)})),jo=Eu((function(t,n,r,e){Ou(n,Eo(n),t,e)})),Ao=ui(fe);var ko=Qe((function(t,n){t=kt(t);var r=-1,e=n.length,i=e>2?n[2]:u;for(i&&wi(n[0],n[1],i)&&(e=1);++r1),n})),Ou(t,ai(t),r),e&&(r=le(r,7,ri));for(var u=n.length;u--;)hu(r,n[u]);return r}));var Uo=ui((function(t,n){return null==t?{}:function(t,n){return Ke(t,n,(function(n,r){return Co(t,r)}))}(t,n)}));function Wo(t,n){if(null==t)return{};var r=En(ai(t),(function(t){return[t]}));return n=li(n),Ke(t,r,(function(t,r){return n(t,r[0])}))}var Po=Qu(Eo),No=Qu(zo);function $o(t){return null==t?[]:Xn(t,Eo(t))}var qo=Lu((function(t,n,r){return n=n.toLowerCase(),t+(r?Go(n):n)}));function Go(t){return Xo(wo(t).toLowerCase())}function Zo(t){return(t=wo(t))&&t.replace(wt,ur).replace(Xt,"")}var Ko=Lu((function(t,n,r){return t+(r?"-":"")+n.toLowerCase()})),Ho=Lu((function(t,n,r){return t+(r?" ":"")+n.toLowerCase()})),Vo=Du("toLowerCase");var Jo=Lu((function(t,n,r){return t+(r?"_":"")+n.toLowerCase()}));var Yo=Lu((function(t,n,r){return t+(r?" ":"")+Xo(n)}));var Qo=Lu((function(t,n,r){return t+(r?" ":"")+n.toUpperCase()})),Xo=Du("toUpperCase");function tf(t,n,r){return t=wo(t),(n=r?u:n)===u?function(t){return en.test(t)}(t)?function(t){return t.match(nn)||[]}(t):function(t){return t.match(st)||[]}(t):t.match(n)||[]}var nf=Qe((function(t,n){try{return An(t,u,n)}catch(t){return Qa(t)?t:new Bt(t)}})),rf=ui((function(t,n){return In(n,(function(n){n=Ui(n),oe(t,n,Fa(t[n],t))})),t}));function ef(t){return function(){return t}}var uf=Pu(),af=Pu(!0);function of(t){return t}function ff(t){return De("function"==typeof t?t:le(t,1))}var cf=Qe((function(t,n){return function(r){return Oe(r,t,n)}})),lf=Qe((function(t,n){return function(r){return Oe(t,r,n)}}));function sf(t,n,r){var e=Eo(n),u=Be(n,e);null!=r||ro(n)&&(u.length||!e.length)||(r=n,n=t,t=this,u=Be(n,Eo(n)));var i=!(ro(r)&&"chain"in r&&!r.chain),a=Xa(t);return In(u,(function(r){var e=n[r];t[r]=e,a&&(t.prototype[r]=function(){var n=this.__chain__;if(i||n){var r=t(this.__wrapped__),u=r.__actions__=Fu(this.__actions__);return u.push({func:e,args:arguments,thisArg:t}),r.__chain__=n,r}return e.apply(t,zn([this.value()],arguments))})})),t}function _f(){}var hf=Gu(En),pf=Gu(Cn),gf=Gu(Ln);function vf(t){return xi(t)?Zn(Ui(t)):function(t){return function(n){return je(n,t)}}(t)}var df=Ku(),yf=Ku(!0);function bf(){return[]}function mf(){return!1}var wf=qu((function(t,n){return t+n}),0),xf=Ju("ceil"),Sf=qu((function(t,n){return t/n}),1),Bf=Ju("floor");var jf,Af=qu((function(t,n){return t*n}),1),kf=Ju("round"),If=qu((function(t,n){return t-n}),0);return Nr.after=function(t,n){if("function"!=typeof n)throw new Ct(i);return t=vo(t),function(){if(--t<1)return n.apply(this,arguments)}},Nr.ary=Ma,Nr.assign=xo,Nr.assignIn=So,Nr.assignInWith=Bo,Nr.assignWith=jo,Nr.at=Ao,Nr.before=Ca,Nr.bind=Fa,Nr.bindAll=rf,Nr.bindKey=Oa,Nr.castArray=function(){if(!arguments.length)return[];var t=arguments[0];return Za(t)?t:[t]},Nr.chain=pa,Nr.chunk=function(t,n,r){n=(r?wi(t,n,r):n===u)?1:mr(vo(n),0);var i=null==t?0:t.length;if(!i||n<1)return[];for(var a=0,o=0,f=e(vn(i/n));ai?0:i+r),(e=e===u||e>i?i:vo(e))<0&&(e+=i),e=r>e?0:yo(e);r>>0)?(t=wo(t))&&("string"==typeof n||null!=n&&!oo(n))&&!(n=su(n))&&or(t)?Su(pr(t),0,r):t.split(n,r):[]},Nr.spread=function(t,n){if("function"!=typeof t)throw new Ct(i);return n=null==n?0:mr(vo(n),0),Qe((function(r){var e=r[n],u=Su(r,0,n);return e&&zn(u,e),An(t,this,u)}))},Nr.tail=function(t){var n=null==t?0:t.length;return n?iu(t,1,n):[]},Nr.take=function(t,n,r){return t&&t.length?iu(t,0,(n=r||n===u?1:vo(n))<0?0:n):[]},Nr.takeRight=function(t,n,r){var e=null==t?0:t.length;return e?iu(t,(n=e-(n=r||n===u?1:vo(n)))<0?0:n,e):[]},Nr.takeRightWhile=function(t,n){return t&&t.length?gu(t,li(n,3),!1,!0):[]},Nr.takeWhile=function(t,n){return t&&t.length?gu(t,li(n,3)):[]},Nr.tap=function(t,n){return n(t),t},Nr.throttle=function(t,n,r){var e=!0,u=!0;if("function"!=typeof t)throw new Ct(i);return ro(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),Ra(t,n,{leading:e,maxWait:n,trailing:u})},Nr.thru=ga,Nr.toArray=po,Nr.toPairs=Po,Nr.toPairsIn=No,Nr.toPath=function(t){return Za(t)?En(t,Ui):lo(t)?[t]:Fu(Li(wo(t)))},Nr.toPlainObject=mo,Nr.transform=function(t,n,r){var e=Za(t),u=e||Ja(t)||so(t);if(n=li(n,4),null==r){var i=t&&t.constructor;r=u?e?new i:[]:ro(t)&&Xa(i)?$r(Kt(t)):{}}return(u?In:xe)(t,(function(t,e,u){return n(r,t,e,u)})),r},Nr.unary=function(t){return Ma(t,1)},Nr.union=ea,Nr.unionBy=ua,Nr.unionWith=ia,Nr.uniq=function(t){return t&&t.length?_u(t):[]},Nr.uniqBy=function(t,n){return t&&t.length?_u(t,li(n,2)):[]},Nr.uniqWith=function(t,n){return n="function"==typeof n?n:u,t&&t.length?_u(t,u,n):[]},Nr.unset=function(t,n){return null==t||hu(t,n)},Nr.unzip=aa,Nr.unzipWith=oa,Nr.update=function(t,n,r){return null==t?t:pu(t,n,mu(r))},Nr.updateWith=function(t,n,r,e){return e="function"==typeof e?e:u,null==t?t:pu(t,n,mu(r),e)},Nr.values=$o,Nr.valuesIn=function(t){return null==t?[]:Xn(t,zo(t))},Nr.without=fa,Nr.words=tf,Nr.wrap=function(t,n){return Ua(mu(n),t)},Nr.xor=ca,Nr.xorBy=la,Nr.xorWith=sa,Nr.zip=_a,Nr.zipObject=function(t,n){return yu(t||[],n||[],ee)},Nr.zipObjectDeep=function(t,n){return yu(t||[],n||[],nu)},Nr.zipWith=ha,Nr.entries=Po,Nr.entriesIn=No,Nr.extend=So,Nr.extendWith=Bo,sf(Nr,Nr),Nr.add=wf,Nr.attempt=nf,Nr.camelCase=qo,Nr.capitalize=Go,Nr.ceil=xf,Nr.clamp=function(t,n,r){return r===u&&(r=n,n=u),r!==u&&(r=(r=bo(r))==r?r:0),n!==u&&(n=(n=bo(n))==n?n:0),ce(bo(t),n,r)},Nr.clone=function(t){return le(t,4)},Nr.cloneDeep=function(t){return le(t,5)},Nr.cloneDeepWith=function(t,n){return le(t,5,n="function"==typeof n?n:u)},Nr.cloneWith=function(t,n){return le(t,4,n="function"==typeof n?n:u)},Nr.conformsTo=function(t,n){return null==n||se(t,n,Eo(n))},Nr.deburr=Zo,Nr.defaultTo=function(t,n){return null==t||t!=t?n:t},Nr.divide=Sf,Nr.endsWith=function(t,n,r){t=wo(t),n=su(n);var e=t.length,i=r=r===u?e:ce(vo(r),0,e);return(r-=n.length)>=0&&t.slice(r,i)==n},Nr.eq=Na,Nr.escape=function(t){return(t=wo(t))&&Y.test(t)?t.replace(V,ir):t},Nr.escapeRegExp=function(t){return(t=wo(t))&&it.test(t)?t.replace(ut,"\\$&"):t},Nr.every=function(t,n,r){var e=Za(t)?Cn:ve;return r&&wi(t,n,r)&&(n=u),e(t,li(n,3))},Nr.find=ya,Nr.findIndex=Gi,Nr.findKey=function(t,n){return Wn(t,li(n,3),xe)},Nr.findLast=ba,Nr.findLastIndex=Zi,Nr.findLastKey=function(t,n){return Wn(t,li(n,3),Se)},Nr.floor=Bf,Nr.forEach=ma,Nr.forEachRight=wa,Nr.forIn=function(t,n){return null==t?t:me(t,li(n,3),zo)},Nr.forInRight=function(t,n){return null==t?t:we(t,li(n,3),zo)},Nr.forOwn=function(t,n){return t&&xe(t,li(n,3))},Nr.forOwnRight=function(t,n){return t&&Se(t,li(n,3))},Nr.get=Mo,Nr.gt=$a,Nr.gte=qa,Nr.has=function(t,n){return null!=t&&di(t,n,Me)},Nr.hasIn=Co,Nr.head=Hi,Nr.identity=of,Nr.includes=function(t,n,r,e){t=Ha(t)?t:$o(t),r=r&&!e?vo(r):0;var u=t.length;return r<0&&(r=mr(u+r,0)),co(t)?r<=u&&t.indexOf(n,r)>-1:!!u&&Nn(t,n,r)>-1},Nr.indexOf=function(t,n,r){var e=null==t?0:t.length;if(!e)return-1;var u=null==r?0:vo(r);return u<0&&(u=mr(e+u,0)),Nn(t,n,u)},Nr.inRange=function(t,n,r){return n=go(n),r===u?(r=n,n=0):r=go(r),function(t,n,r){return t>=wr(n,r)&&t=-9007199254740991&&t<=p},Nr.isSet=fo,Nr.isString=co,Nr.isSymbol=lo,Nr.isTypedArray=so,Nr.isUndefined=function(t){return t===u},Nr.isWeakMap=function(t){return eo(t)&&vi(t)==R},Nr.isWeakSet=function(t){return eo(t)&&"[object WeakSet]"==ke(t)},Nr.join=function(t,n){return null==t?"":yr.call(t,n)},Nr.kebabCase=Ko,Nr.last=Qi,Nr.lastIndexOf=function(t,n,r){var e=null==t?0:t.length;if(!e)return-1;var i=e;return r!==u&&(i=(i=vo(r))<0?mr(e+i,0):wr(i,e-1)),n==n?function(t,n,r){for(var e=r+1;e--;)if(t[e]===n)return e;return e}(t,n,i):Pn(t,qn,i,!0)},Nr.lowerCase=Ho,Nr.lowerFirst=Vo,Nr.lt=_o,Nr.lte=ho,Nr.max=function(t){return t&&t.length?de(t,of,Ie):u},Nr.maxBy=function(t,n){return t&&t.length?de(t,li(n,2),Ie):u},Nr.mean=function(t){return Gn(t,of)},Nr.meanBy=function(t,n){return Gn(t,li(n,2))},Nr.min=function(t){return t&&t.length?de(t,of,We):u},Nr.minBy=function(t,n){return t&&t.length?de(t,li(n,2),We):u},Nr.stubArray=bf,Nr.stubFalse=mf,Nr.stubObject=function(){return{}},Nr.stubString=function(){return""},Nr.stubTrue=function(){return!0},Nr.multiply=Af,Nr.nth=function(t,n){return t&&t.length?Ge(t,vo(n)):u},Nr.noConflict=function(){return pn._===this&&(pn._=Pt),this},Nr.noop=_f,Nr.now=Ia,Nr.pad=function(t,n,r){t=wo(t);var e=(n=vo(n))?hr(t):0;if(!n||e>=n)return t;var u=(n-e)/2;return Zu(yn(u),r)+t+Zu(vn(u),r)},Nr.padEnd=function(t,n,r){t=wo(t);var e=(n=vo(n))?hr(t):0;return n&&en){var e=t;t=n,n=e}if(r||t%1||n%1){var i=Br();return wr(t+i*(n-t+ln("1e-"+((i+"").length-1))),n)}return Je(t,n)},Nr.reduce=function(t,n,r){var e=Za(t)?Tn:Hn,u=arguments.length<3;return e(t,li(n,4),r,u,pe)},Nr.reduceRight=function(t,n,r){var e=Za(t)?Dn:Hn,u=arguments.length<3;return e(t,li(n,4),r,u,ge)},Nr.repeat=function(t,n,r){return n=(r?wi(t,n,r):n===u)?1:vo(n),Ye(wo(t),n)},Nr.replace=function(){var t=arguments,n=wo(t[0]);return t.length<3?n:n.replace(t[1],t[2])},Nr.result=function(t,n,r){var e=-1,i=(n=wu(n,t)).length;for(i||(i=1,t=u);++ep)return[];var r=v,e=wr(t,v);n=li(n),t-=v;for(var u=Jn(e,n);++r=a)return t;var f=r-hr(e);if(f<1)return e;var c=o?Su(o,0,f).join(""):t.slice(0,f);if(i===u)return c+e;if(o&&(f+=c.length-f),oo(i)){if(t.slice(f).search(i)){var l,s=c;for(i.global||(i=It(i.source,wo(gt.exec(i))+"g")),i.lastIndex=0;l=i.exec(s);)var _=l.index;c=c.slice(0,_===u?f:_)}}else if(t.indexOf(su(i),f)!=f){var h=c.lastIndexOf(i);h>-1&&(c=c.slice(0,h))}return c+e},Nr.unescape=function(t){return(t=wo(t))&&J.test(t)?t.replace(H,vr):t},Nr.uniqueId=function(t){var n=++Dt;return wo(t)+n},Nr.upperCase=Qo,Nr.upperFirst=Xo,Nr.each=ma,Nr.eachRight=wa,Nr.first=Hi,sf(Nr,(jf={},xe(Nr,(function(t,n){Tt.call(Nr.prototype,n)||(jf[n]=t)})),jf),{chain:!1}),Nr.VERSION="4.17.21",In(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(t){Nr[t].placeholder=Nr})),In(["drop","take"],(function(t,n){Zr.prototype[t]=function(r){r=r===u?1:mr(vo(r),0);var e=this.__filtered__&&!n?new Zr(this):this.clone();return e.__filtered__?e.__takeCount__=wr(r,e.__takeCount__):e.__views__.push({size:wr(r,v),type:t+(e.__dir__<0?"Right":"")}),e},Zr.prototype[t+"Right"]=function(n){return this.reverse()[t](n).reverse()}})),In(["filter","map","takeWhile"],(function(t,n){var r=n+1,e=1==r||3==r;Zr.prototype[t]=function(t){var n=this.clone();return n.__iteratees__.push({iteratee:li(t,3),type:r}),n.__filtered__=n.__filtered__||e,n}})),In(["head","last"],(function(t,n){var r="take"+(n?"Right":"");Zr.prototype[t]=function(){return this[r](1).value()[0]}})),In(["initial","tail"],(function(t,n){var r="drop"+(n?"":"Right");Zr.prototype[t]=function(){return this.__filtered__?new Zr(this):this[r](1)}})),Zr.prototype.compact=function(){return this.filter(of)},Zr.prototype.find=function(t){return this.filter(t).head()},Zr.prototype.findLast=function(t){return this.reverse().find(t)},Zr.prototype.invokeMap=Qe((function(t,n){return"function"==typeof t?new Zr(this):this.map((function(r){return Oe(r,t,n)}))})),Zr.prototype.reject=function(t){return this.filter(Da(li(t)))},Zr.prototype.slice=function(t,n){t=vo(t);var r=this;return r.__filtered__&&(t>0||n<0)?new Zr(r):(t<0?r=r.takeRight(-t):t&&(r=r.drop(t)),n!==u&&(r=(n=vo(n))<0?r.dropRight(-n):r.take(n-t)),r)},Zr.prototype.takeRightWhile=function(t){return this.reverse().takeWhile(t).reverse()},Zr.prototype.toArray=function(){return this.take(v)},xe(Zr.prototype,(function(t,n){var r=/^(?:filter|find|map|reject)|While$/.test(n),e=/^(?:head|last)$/.test(n),i=Nr[e?"take"+("last"==n?"Right":""):n],a=e||/^find/.test(n);i&&(Nr.prototype[n]=function(){var n=this.__wrapped__,o=e?[1]:arguments,f=n instanceof Zr,c=o[0],l=f||Za(n),s=function(t){var n=i.apply(Nr,zn([t],o));return e&&_?n[0]:n};l&&r&&"function"==typeof c&&1!=c.length&&(f=l=!1);var _=this.__chain__,h=!!this.__actions__.length,p=a&&!_,g=f&&!h;if(!a&&l){n=g?n:new Zr(this);var v=t.apply(n,o);return v.__actions__.push({func:ga,args:[s],thisArg:u}),new Gr(v,_)}return p&&g?t.apply(this,o):(v=this.thru(s),p?e?v.value()[0]:v.value():v)})})),In(["pop","push","shift","sort","splice","unshift"],(function(t){var n=Ft[t],r=/^(?:push|sort|unshift)$/.test(t)?"tap":"thru",e=/^(?:pop|shift)$/.test(t);Nr.prototype[t]=function(){var t=arguments;if(e&&!this.__chain__){var u=this.value();return n.apply(Za(u)?u:[],t)}return this[r]((function(r){return n.apply(Za(r)?r:[],t)}))}})),xe(Zr.prototype,(function(t,n){var r=Nr[n];if(r){var e=r.name+"";Tt.call(Rr,e)||(Rr[e]=[]),Rr[e].push({name:n,func:r})}})),Rr[Nu(u,2).name]=[{name:"wrapper",func:u}],Zr.prototype.clone=function(){var t=new Zr(this.__wrapped__);return t.__actions__=Fu(this.__actions__),t.__dir__=this.__dir__,t.__filtered__=this.__filtered__,t.__iteratees__=Fu(this.__iteratees__),t.__takeCount__=this.__takeCount__,t.__views__=Fu(this.__views__),t},Zr.prototype.reverse=function(){if(this.__filtered__){var t=new Zr(this);t.__dir__=-1,t.__filtered__=!0}else(t=this.clone()).__dir__*=-1;return t},Zr.prototype.value=function(){var t=this.__wrapped__.value(),n=this.__dir__,r=Za(t),e=n<0,u=r?t.length:0,i=function(t,n,r){var e=-1,u=r.length;for(;++e=this.__values__.length;return{done:t,value:t?u:this.__values__[this.__index__++]}},Nr.prototype.plant=function(t){for(var n,r=this;r instanceof qr;){var e=Pi(r);e.__index__=0,e.__values__=u,n?i.__wrapped__=e:n=e;var i=e;r=r.__wrapped__}return i.__wrapped__=t,n},Nr.prototype.reverse=function(){var t=this.__wrapped__;if(t instanceof Zr){var n=t;return this.__actions__.length&&(n=new Zr(this)),(n=n.reverse()).__actions__.push({func:ga,args:[ra],thisArg:u}),new Gr(n,this.__chain__)}return this.thru(ra)},Nr.prototype.toJSON=Nr.prototype.valueOf=Nr.prototype.value=function(){return vu(this.__wrapped__,this.__actions__)},Nr.prototype.first=Nr.prototype.head,tn&&(Nr.prototype[tn]=function(){return this}),Nr}();pn._=dr,(e=function(){return dr}.call(n,r,n,t))===u||(t.exports=e)}.call(this)}},n={};function r(e){var u=n[e];if(void 0!==u)return u.exports;var i=n[e]={id:e,loaded:!1,exports:{}};return t[e].call(i.exports,i,i.exports,r),i.loaded=!0,i.exports}r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r.nmd=t=>(t.paths=[],t.children||(t.children=[]),t);var e=r(6014);freeaps_profile=e})(); +var freeaps_profile;(()=>{var t={902:t=>{"use strict";function n(t,n){t.length>0&&(t+="\n");for(var r=n.length,e=0;e{"use strict";t.exports=function(t){var n=new Date;return n.setHours("00"),n.setMinutes("00"),n.setSeconds("00"),n.getTime()+60*t*1e3}},118:(t,n,r)=>{"use strict";var e=r(125);n.maxDailyBasal=function(t){var n=e.maxBy(t.basals,(function(t){return Number(t.rate)}));return 1e3*Number(n.rate)/1e3},n.maxBasalLookup=function(t){return t.settings.maxBasal},n.basalLookup=function(t,n){var r=n;void 0===n&&(r=new Date);var u=e.sortBy(t,(function(t){return t.i})),i=u[u.length-1].rate;if(0!==i){for(var o=60*r.getHours()+r.getMinutes(),a=0;a=u[a].minutes&&o{"use strict";var e=r(53),u=r(902).console_error;function i(t,n,r){var i=new Date,o=n.carbratio;if(void 0===o||void 0===o.schedule);else{var a;if("grams"===o.units||"exchanges"===o.units){a=o.schedule[o.schedule.length-1];for(var f=0;f=e(o.schedule[f].offset)&&i150)return void u(t,"Error: carbRatio of "+a+" out of bounds.");break}return"exchanges"===o.units&&(a.ratio=12/a.ratio),a.ratio}u(t,"Error: Unsupported carb_ratio units "+o.units)}}i.carbRatioLookup=i,t.exports=i},825:(t,n,r)=>{"use strict";var e=r(118),u=r(743),i=r(335),o=r(664),a=r(125),f=r(902),c=f.console_error;f.console_log;function l(){return{max_iob:0,max_daily_safety_multiplier:3,current_basal_safety_multiplier:4,autosens_max:1.2,autosens_min:.7,rewind_resets_autosens:!0,high_temptarget_raises_sensitivity:!1,low_temptarget_lowers_sensitivity:!1,sensitivity_raises_target:!0,resistance_lowers_target:!1,exercise_mode:!1,half_basal_exercise_target:160,maxCOB:120,skip_neutral_temps:!1,unsuspend_if_no_temp:!1,bolussnooze_dia_divisor:2,min_5m_carbimpact:8,autotune_isf_adjustmentFraction:1,remainingCarbsFraction:1,remainingCarbsCap:90,enableUAM:!0,A52_risk_enable:!1,enableSMB_with_COB:!1,enableSMB_with_temptarget:!1,enableSMB_always:!1,enableSMB_after_carbs:!1,enableSMB_high_bg:!1,enableSMB_high_bg_target:110,allowSMB_with_high_temptarget:!1,maxSMBBasalMinutes:30,maxUAMSMBBasalMinutes:30,SMBInterval:3,bolus_increment:.1,maxDelta_bg_threshold:.2,curve:"rapid-acting",useCustomPeakTime:!1,insulinPeakTime:75,carbsReqThreshold:1,offline_hotspot:!1,noisyCGMTargetMultiplier:1.3,suspend_zeros_iob:!0,enableEnliteBgproxy:!1,calc_glucose_noise:!1,target_bg:!1,edison_battery_shutdown_voltage:3050,pi_battery_shutdown_percent:2}}function s(t,n,r){var f=r&&r.type?r:{max_iob:0,max_daily_safety_multiplier:3,current_basal_safety_multiplier:4,autosens_max:1.2,autosens_min:.7,rewind_resets_autosens:!0,high_temptarget_raises_sensitivity:!1,low_temptarget_lowers_sensitivity:!1,sensitivity_raises_target:!0,resistance_lowers_target:!1,exercise_mode:!1,half_basal_exercise_target:160,maxCOB:120,skip_neutral_temps:!1,unsuspend_if_no_temp:!1,bolussnooze_dia_divisor:2,min_5m_carbimpact:8,autotune_isf_adjustmentFraction:1,remainingCarbsFraction:1,remainingCarbsCap:90,enableUAM:!0,A52_risk_enable:!1,enableSMB_with_COB:!1,enableSMB_with_temptarget:!1,enableSMB_always:!1,enableSMB_after_carbs:!1,enableSMB_high_bg:!1,enableSMB_high_bg_target:110,allowSMB_with_high_temptarget:!1,maxSMBBasalMinutes:30,maxUAMSMBBasalMinutes:30,SMBInterval:3,bolus_increment:.1,maxDelta_bg_threshold:.2,curve:"rapid-acting",useCustomPeakTime:!1,insulinPeakTime:75,carbsReqThreshold:1,offline_hotspot:!1,noisyCGMTargetMultiplier:1.3,suspend_zeros_iob:!0,enableEnliteBgproxy:!1,calc_glucose_noise:!1,target_bg:!1,edison_battery_shutdown_voltage:3050,pi_battery_shutdown_percent:2};for(var l in f)n.hasOwnProperty(l)&&(f[l]=n[l]);var s=n.settings;if(!(n.settings.insulin_action_curve>1))return c(t,"DIA of",f.dia,"is not supported"),-1;if(f.dia=s.insulin_action_curve,n.model&&(f.model=n.model),f.skip_neutral_temps=n.skip_neutral_temps,f.current_basal=e.basalLookup(n.basals),f.basalprofile=n.basals,a.forEach(f.basalprofile,(function(t){t.rate=+(Math.round(t.rate+"e+3")+"e-3")})),f.max_daily_basal=e.maxDailyBasal(n),f.max_basal=e.maxBasalLookup(n),0===f.current_basal)return c(t,"current_basal of",f.current_basal,"is not supported"),-1;if(0===f.max_daily_basal)return c(t,"max_daily_basal of",f.max_daily_basal,"is not supported"),-1;if(f.max_basal<.1)return c(t,"max_basal of",f.max_basal,"is not supported"),-1;var _=u.bgTargetsLookup(t,n,f);f.out_units=n.targets.user_preferred_units,f.min_bg=Math.round(_.min_bg),f.max_bg=Math.round(_.max_bg),f.bg_targets=n.targets,a.forEach(f.bg_targets.targets,(function(t){t.high=Math.round(t.high),t.low=Math.round(t.low),t.min_bg=Math.round(t.min_bg),t.max_bg=Math.round(t.max_bg)})),delete f.bg_targets.raw,f.temptargetSet=_.temptargetSet;var h=null;return[f.sens,h]=i.isfLookup(n.isf,void 0,h),f.isfProfile=n.isf,f.sens<5?(c(t,"ISF of",f.sens,"is not supported"),-1):(void 0!==n.carbratio?(f.carb_ratio=o.carbRatioLookup(t,n,f),f.carb_ratios=n.carbratio):c(t,"Profile wasn't given carb ratio data, cannot calculate carb_ratio"),f)}s.defaults=l,s.displayedDefaults=function(t){var n={max_iob:0,max_daily_safety_multiplier:3,current_basal_safety_multiplier:4,autosens_max:1.2,autosens_min:.7,rewind_resets_autosens:!0,high_temptarget_raises_sensitivity:!1,low_temptarget_lowers_sensitivity:!1,sensitivity_raises_target:!0,resistance_lowers_target:!1,exercise_mode:!1,half_basal_exercise_target:160,maxCOB:120,skip_neutral_temps:!1,unsuspend_if_no_temp:!1,bolussnooze_dia_divisor:2,min_5m_carbimpact:8,autotune_isf_adjustmentFraction:1,remainingCarbsFraction:1,remainingCarbsCap:90,enableUAM:!0,A52_risk_enable:!1,enableSMB_with_COB:!1,enableSMB_with_temptarget:!1,enableSMB_always:!1,enableSMB_after_carbs:!1,enableSMB_high_bg:!1,enableSMB_high_bg_target:110,allowSMB_with_high_temptarget:!1,maxSMBBasalMinutes:30,maxUAMSMBBasalMinutes:30,SMBInterval:3,bolus_increment:.1,maxDelta_bg_threshold:.2,curve:"rapid-acting",useCustomPeakTime:!1,insulinPeakTime:75,carbsReqThreshold:1,offline_hotspot:!1,noisyCGMTargetMultiplier:1.3,suspend_zeros_iob:!0,enableEnliteBgproxy:!1,calc_glucose_noise:!1,target_bg:!1,edison_battery_shutdown_voltage:3050,pi_battery_shutdown_percent:2},r={};return r.max_iob=n.max_iob,r.max_daily_safety_multiplier=n.max_daily_safety_multiplier,r.current_basal_safety_multiplier=n.current_basal_safety_multiplier,r.autosens_max=n.autosens_max,r.autosens_min=n.autosens_min,r.rewind_resets_autosens=n.rewind_resets_autosens,r.exercise_mode=n.exercise_mode,r.sensitivity_raises_target=n.sensitivity_raises_target,r.unsuspend_if_no_temp=n.unsuspend_if_no_temp,r.enableSMB_with_COB=n.enableSMB_with_COB,r.enableSMB_with_temptarget=n.enableSMB_with_temptarget,r.enableUAM=n.enableUAM,r.curve=n.curve,r.offline_hotspot=n.offline_hotspot,r.edison_battery_shutdown_voltage=n.edison_battery_shutdown_voltage,r.pi_battery_shutdown_percent=n.pi_battery_shutdown_percent,c(t,r),r},t.exports=s},335:(t,n,r)=>{"use strict";var e=r(125);function u(t,n,r){var u=n;void 0===n&&(u=new Date);var i=60*u.getHours()+u.getMinutes();if(r&&i>=r.offset&&i=c.offset&&i{"use strict";var e=r(53),u=r(902).console_error;function i(t,n,r){return a(o(t,n,r))}function o(t,n,r){for(var i=n.targets,o=n.temptargets,a=new Date,f=i.targets[i.targets.length-1],c=0;c=e(i.targets[c].offset)&&a=s&&0===o[c].duration){l=f;break}if(!o[c].targetBottom||!o[c].targetTop){u(t,"eventualBG target range invalid: "+o[c].targetBottom+"-"+o[c].targetTop);break}if(a>=s&&a<_){l.high=o[c].targetTop,l.low=o[c].targetBottom,l.temptargetSet=!0;break}}return f=l}function a(t){return t.high<20&&(t.high=18*t.high),t.low<20&&(t.low=18*t.low),t.max_bg=Math.max(80,t.high),t.min_bg=Math.max(80,t.low),t.min_bg=Math.min(200,t.min_bg),t.max_bg=Math.min(200,t.max_bg),t}i.bgTargetsLookup=i,i.lookup=o,i.bound_target_range=a,t.exports=i},125:function(t,n,r){var e;t=r.nmd(t),function(){var u,i="Expected a function",o="__lodash_hash_undefined__",a="__lodash_placeholder__",f=16,c=32,l=64,s=128,_=256,h=1/0,p=9007199254740991,v=NaN,g=4294967295,d=[["ary",s],["bind",1],["bindKey",2],["curry",8],["curryRight",f],["flip",512],["partial",c],["partialRight",l],["rearg",_]],y="[object Arguments]",b="[object Array]",m="[object Boolean]",w="[object Date]",x="[object Error]",B="[object Function]",j="[object GeneratorFunction]",k="[object Map]",A="[object Number]",M="[object Object]",S="[object Promise]",O="[object RegExp]",R="[object Set]",C="[object String]",I="[object Symbol]",E="[object WeakMap]",z="[object ArrayBuffer]",T="[object DataView]",L="[object Float32Array]",D="[object Float64Array]",U="[object Int8Array]",W="[object Int16Array]",F="[object Int32Array]",P="[object Uint8Array]",$="[object Uint8ClampedArray]",N="[object Uint16Array]",q="[object Uint32Array]",Z=/\b__p \+= '';/g,G=/\b(__p \+=) '' \+/g,K=/(__e\(.*?\)|\b__t\)) \+\n'';/g,H=/&(?:amp|lt|gt|quot|#39);/g,V=/[&<>"']/g,J=RegExp(H.source),Y=RegExp(V.source),Q=/<%-([\s\S]+?)%>/g,X=/<%([\s\S]+?)%>/g,tt=/<%=([\s\S]+?)%>/g,nt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,rt=/^\w*$/,et=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,ut=/[\\^$.*+?()[\]{}|]/g,it=RegExp(ut.source),ot=/^\s+/,at=/\s/,ft=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,ct=/\{\n\/\* \[wrapped with (.+)\] \*/,lt=/,? & /,st=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,_t=/[()=,{}\[\]\/\s]/,ht=/\\(\\)?/g,pt=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,vt=/\w*$/,gt=/^[-+]0x[0-9a-f]+$/i,dt=/^0b[01]+$/i,yt=/^\[object .+?Constructor\]$/,bt=/^0o[0-7]+$/i,mt=/^(?:0|[1-9]\d*)$/,wt=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,xt=/($^)/,Bt=/['\n\r\u2028\u2029\\]/g,jt="\\ud800-\\udfff",kt="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",At="\\u2700-\\u27bf",Mt="a-z\\xdf-\\xf6\\xf8-\\xff",St="A-Z\\xc0-\\xd6\\xd8-\\xde",Ot="\\ufe0e\\ufe0f",Rt="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Ct="['’]",It="["+jt+"]",Et="["+Rt+"]",zt="["+kt+"]",Tt="\\d+",Lt="["+At+"]",Dt="["+Mt+"]",Ut="[^"+jt+Rt+Tt+At+Mt+St+"]",Wt="\\ud83c[\\udffb-\\udfff]",Ft="[^"+jt+"]",Pt="(?:\\ud83c[\\udde6-\\uddff]){2}",$t="[\\ud800-\\udbff][\\udc00-\\udfff]",Nt="["+St+"]",qt="\\u200d",Zt="(?:"+Dt+"|"+Ut+")",Gt="(?:"+Nt+"|"+Ut+")",Kt="(?:['’](?:d|ll|m|re|s|t|ve))?",Ht="(?:['’](?:D|LL|M|RE|S|T|VE))?",Vt="(?:"+zt+"|"+Wt+")"+"?",Jt="["+Ot+"]?",Yt=Jt+Vt+("(?:"+qt+"(?:"+[Ft,Pt,$t].join("|")+")"+Jt+Vt+")*"),Qt="(?:"+[Lt,Pt,$t].join("|")+")"+Yt,Xt="(?:"+[Ft+zt+"?",zt,Pt,$t,It].join("|")+")",tn=RegExp(Ct,"g"),nn=RegExp(zt,"g"),rn=RegExp(Wt+"(?="+Wt+")|"+Xt+Yt,"g"),en=RegExp([Nt+"?"+Dt+"+"+Kt+"(?="+[Et,Nt,"$"].join("|")+")",Gt+"+"+Ht+"(?="+[Et,Nt+Zt,"$"].join("|")+")",Nt+"?"+Zt+"+"+Kt,Nt+"+"+Ht,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Tt,Qt].join("|"),"g"),un=RegExp("["+qt+jt+kt+Ot+"]"),on=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,an=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],fn=-1,cn={};cn[L]=cn[D]=cn[U]=cn[W]=cn[F]=cn[P]=cn[$]=cn[N]=cn[q]=!0,cn[y]=cn[b]=cn[z]=cn[m]=cn[T]=cn[w]=cn[x]=cn[B]=cn[k]=cn[A]=cn[M]=cn[O]=cn[R]=cn[C]=cn[E]=!1;var ln={};ln[y]=ln[b]=ln[z]=ln[T]=ln[m]=ln[w]=ln[L]=ln[D]=ln[U]=ln[W]=ln[F]=ln[k]=ln[A]=ln[M]=ln[O]=ln[R]=ln[C]=ln[I]=ln[P]=ln[$]=ln[N]=ln[q]=!0,ln[x]=ln[B]=ln[E]=!1;var sn={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},_n=parseFloat,hn=parseInt,pn="object"==typeof r.g&&r.g&&r.g.Object===Object&&r.g,vn="object"==typeof self&&self&&self.Object===Object&&self,gn=pn||vn||Function("return this")(),dn=n&&!n.nodeType&&n,yn=dn&&t&&!t.nodeType&&t,bn=yn&&yn.exports===dn,mn=bn&&pn.process,wn=function(){try{var t=yn&&yn.require&&yn.require("util").types;return t||mn&&mn.binding&&mn.binding("util")}catch(t){}}(),xn=wn&&wn.isArrayBuffer,Bn=wn&&wn.isDate,jn=wn&&wn.isMap,kn=wn&&wn.isRegExp,An=wn&&wn.isSet,Mn=wn&&wn.isTypedArray;function Sn(t,n,r){switch(r.length){case 0:return t.call(n);case 1:return t.call(n,r[0]);case 2:return t.call(n,r[0],r[1]);case 3:return t.call(n,r[0],r[1],r[2])}return t.apply(n,r)}function On(t,n,r,e){for(var u=-1,i=null==t?0:t.length;++u-1}function Tn(t,n,r){for(var e=-1,u=null==t?0:t.length;++e-1;);return r}function ur(t,n){for(var r=t.length;r--&&qn(n,t[r],0)>-1;);return r}var ir=Vn({À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"}),or=Vn({"&":"&","<":"<",">":">",'"':""","'":"'"});function ar(t){return"\\"+sn[t]}function fr(t){return un.test(t)}function cr(t){var n=-1,r=Array(t.size);return t.forEach((function(t,e){r[++n]=[e,t]})),r}function lr(t,n){return function(r){return t(n(r))}}function sr(t,n){for(var r=-1,e=t.length,u=0,i=[];++r",""":'"',"'":"'"});var yr=function t(n){var r,e=(n=null==n?gn:yr.defaults(gn.Object(),n,yr.pick(gn,an))).Array,at=n.Date,jt=n.Error,kt=n.Function,At=n.Math,Mt=n.Object,St=n.RegExp,Ot=n.String,Rt=n.TypeError,Ct=e.prototype,It=kt.prototype,Et=Mt.prototype,zt=n["__core-js_shared__"],Tt=It.toString,Lt=Et.hasOwnProperty,Dt=0,Ut=(r=/[^.]+$/.exec(zt&&zt.keys&&zt.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"",Wt=Et.toString,Ft=Tt.call(Mt),Pt=gn._,$t=St("^"+Tt.call(Lt).replace(ut,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Nt=bn?n.Buffer:u,qt=n.Symbol,Zt=n.Uint8Array,Gt=Nt?Nt.allocUnsafe:u,Kt=lr(Mt.getPrototypeOf,Mt),Ht=Mt.create,Vt=Et.propertyIsEnumerable,Jt=Ct.splice,Yt=qt?qt.isConcatSpreadable:u,Qt=qt?qt.iterator:u,Xt=qt?qt.toStringTag:u,rn=function(){try{var t=_i(Mt,"defineProperty");return t({},"",{}),t}catch(t){}}(),un=n.clearTimeout!==gn.clearTimeout&&n.clearTimeout,sn=at&&at.now!==gn.Date.now&&at.now,pn=n.setTimeout!==gn.setTimeout&&n.setTimeout,vn=At.ceil,dn=At.floor,yn=Mt.getOwnPropertySymbols,mn=Nt?Nt.isBuffer:u,wn=n.isFinite,Pn=Ct.join,Vn=lr(Mt.keys,Mt),br=At.max,mr=At.min,wr=at.now,xr=n.parseInt,Br=At.random,jr=Ct.reverse,kr=_i(n,"DataView"),Ar=_i(n,"Map"),Mr=_i(n,"Promise"),Sr=_i(n,"Set"),Or=_i(n,"WeakMap"),Rr=_i(Mt,"create"),Cr=Or&&new Or,Ir={},Er=Ui(kr),zr=Ui(Ar),Tr=Ui(Mr),Lr=Ui(Sr),Dr=Ui(Or),Ur=qt?qt.prototype:u,Wr=Ur?Ur.valueOf:u,Fr=Ur?Ur.toString:u;function Pr(t){if(ra(t)&&!Zo(t)&&!(t instanceof Zr)){if(t instanceof qr)return t;if(Lt.call(t,"__wrapped__"))return Wi(t)}return new qr(t)}var $r=function(){function t(){}return function(n){if(!na(n))return{};if(Ht)return Ht(n);t.prototype=n;var r=new t;return t.prototype=u,r}}();function Nr(){}function qr(t,n){this.__wrapped__=t,this.__actions__=[],this.__chain__=!!n,this.__index__=0,this.__values__=u}function Zr(t){this.__wrapped__=t,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=g,this.__views__=[]}function Gr(t){var n=-1,r=null==t?0:t.length;for(this.clear();++n=n?t:n)),t}function ce(t,n,r,e,i,o){var a,f=1&n,c=2&n,l=4&n;if(r&&(a=i?r(t,e,i,o):r(t)),a!==u)return a;if(!na(t))return t;var s=Zo(t);if(s){if(a=function(t){var n=t.length,r=new t.constructor(n);n&&"string"==typeof t[0]&&Lt.call(t,"index")&&(r.index=t.index,r.input=t.input);return r}(t),!f)return Ru(t,a)}else{var _=vi(t),h=_==B||_==j;if(Vo(t))return ju(t,f);if(_==M||_==y||h&&!i){if(a=c||h?{}:di(t),!f)return c?function(t,n){return Cu(t,pi(t),n)}(t,function(t,n){return t&&Cu(n,Ea(n),t)}(a,t)):function(t,n){return Cu(t,hi(t),n)}(t,ie(a,t))}else{if(!ln[_])return i?t:{};a=function(t,n,r){var e=t.constructor;switch(n){case z:return ku(t);case m:case w:return new e(+t);case T:return function(t,n){var r=n?ku(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.byteLength)}(t,r);case L:case D:case U:case W:case F:case P:case $:case N:case q:return Au(t,r);case k:return new e;case A:case C:return new e(t);case O:return function(t){var n=new t.constructor(t.source,vt.exec(t));return n.lastIndex=t.lastIndex,n}(t);case R:return new e;case I:return u=t,Wr?Mt(Wr.call(u)):{}}var u}(t,_,f)}}o||(o=new Jr);var p=o.get(t);if(p)return p;o.set(t,a),aa(t)?t.forEach((function(e){a.add(ce(e,n,r,e,t,o))})):ea(t)&&t.forEach((function(e,u){a.set(u,ce(e,n,r,u,t,o))}));var v=s?u:(l?c?ii:ui:c?Ea:Ia)(t);return Rn(v||t,(function(e,u){v&&(e=t[u=e]),re(a,u,ce(e,n,r,u,t,o))})),a}function le(t,n,r){var e=r.length;if(null==t)return!e;for(t=Mt(t);e--;){var i=r[e],o=n[i],a=t[i];if(a===u&&!(i in t)||!o(a))return!1}return!0}function se(t,n,r){if("function"!=typeof t)throw new Rt(i);return Ci((function(){t.apply(u,r)}),n)}function _e(t,n,r,e){var u=-1,i=zn,o=!0,a=t.length,f=[],c=n.length;if(!a)return f;r&&(n=Ln(n,tr(r))),e?(i=Tn,o=!1):n.length>=200&&(i=rr,o=!1,n=new Vr(n));t:for(;++u-1},Kr.prototype.set=function(t,n){var r=this.__data__,e=ee(r,t);return e<0?(++this.size,r.push([t,n])):r[e][1]=n,this},Hr.prototype.clear=function(){this.size=0,this.__data__={hash:new Gr,map:new(Ar||Kr),string:new Gr}},Hr.prototype.delete=function(t){var n=li(this,t).delete(t);return this.size-=n?1:0,n},Hr.prototype.get=function(t){return li(this,t).get(t)},Hr.prototype.has=function(t){return li(this,t).has(t)},Hr.prototype.set=function(t,n){var r=li(this,t),e=r.size;return r.set(t,n),this.size+=r.size==e?0:1,this},Vr.prototype.add=Vr.prototype.push=function(t){return this.__data__.set(t,o),this},Vr.prototype.has=function(t){return this.__data__.has(t)},Jr.prototype.clear=function(){this.__data__=new Kr,this.size=0},Jr.prototype.delete=function(t){var n=this.__data__,r=n.delete(t);return this.size=n.size,r},Jr.prototype.get=function(t){return this.__data__.get(t)},Jr.prototype.has=function(t){return this.__data__.has(t)},Jr.prototype.set=function(t,n){var r=this.__data__;if(r instanceof Kr){var e=r.__data__;if(!Ar||e.length<199)return e.push([t,n]),this.size=++r.size,this;r=this.__data__=new Hr(e)}return r.set(t,n),this.size=r.size,this};var he=zu(we),pe=zu(xe,!0);function ve(t,n){var r=!0;return he(t,(function(t,e,u){return r=!!n(t,e,u)})),r}function ge(t,n,r){for(var e=-1,i=t.length;++e0&&r(a)?n>1?ye(a,n-1,r,e,u):Dn(u,a):e||(u[u.length]=a)}return u}var be=Tu(),me=Tu(!0);function we(t,n){return t&&be(t,n,Ia)}function xe(t,n){return t&&me(t,n,Ia)}function Be(t,n){return En(n,(function(n){return Qo(t[n])}))}function je(t,n){for(var r=0,e=(n=mu(n,t)).length;null!=t&&rn}function Se(t,n){return null!=t&&Lt.call(t,n)}function Oe(t,n){return null!=t&&n in Mt(t)}function Re(t,n,r){for(var i=r?Tn:zn,o=t[0].length,a=t.length,f=a,c=e(a),l=1/0,s=[];f--;){var _=t[f];f&&n&&(_=Ln(_,tr(n))),l=mr(_.length,l),c[f]=!r&&(n||o>=120&&_.length>=120)?new Vr(f&&_):u}_=t[0];var h=-1,p=c[0];t:for(;++h=a?f:f*("desc"==r[e]?-1:1)}return t.index-n.index}(t,n,r)}))}function Ge(t,n,r){for(var e=-1,u=n.length,i={};++e-1;)a!==t&&Jt.call(a,f,1),Jt.call(t,f,1);return t}function He(t,n){for(var r=t?n.length:0,e=r-1;r--;){var u=n[r];if(r==e||u!==i){var i=u;bi(u)?Jt.call(t,u,1):_u(t,u)}}return t}function Ve(t,n){return t+dn(Br()*(n-t+1))}function Je(t,n){var r="";if(!t||n<1||n>p)return r;do{n%2&&(r+=t),(n=dn(n/2))&&(t+=t)}while(n);return r}function Ye(t,n){return Ii(Mi(t,n,uf),t+"")}function Qe(t){return Qr(Pa(t))}function Xe(t,n){var r=Pa(t);return Ti(r,fe(n,0,r.length))}function tu(t,n,r,e){if(!na(t))return t;for(var i=-1,o=(n=mu(n,t)).length,a=o-1,f=t;null!=f&&++ii?0:i+n),(r=r>i?i:r)<0&&(r+=i),i=n>r?0:r-n>>>0,n>>>=0;for(var o=e(i);++u>>1,o=t[i];null!==o&&!ca(o)&&(r?o<=n:o=200){var c=n?null:Ju(t);if(c)return _r(c);o=!1,u=rr,f=new Vr}else f=n?[]:a;t:for(;++e=e?t:uu(t,n,r)}var Bu=un||function(t){return gn.clearTimeout(t)};function ju(t,n){if(n)return t.slice();var r=t.length,e=Gt?Gt(r):new t.constructor(r);return t.copy(e),e}function ku(t){var n=new t.constructor(t.byteLength);return new Zt(n).set(new Zt(t)),n}function Au(t,n){var r=n?ku(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.length)}function Mu(t,n){if(t!==n){var r=t!==u,e=null===t,i=t==t,o=ca(t),a=n!==u,f=null===n,c=n==n,l=ca(n);if(!f&&!l&&!o&&t>n||o&&a&&c&&!f&&!l||e&&a&&c||!r&&c||!i)return 1;if(!e&&!o&&!l&&t1?r[i-1]:u,a=i>2?r[2]:u;for(o=t.length>3&&"function"==typeof o?(i--,o):u,a&&mi(r[0],r[1],a)&&(o=i<3?u:o,i=1),n=Mt(n);++e-1?i[o?n[a]:a]:u}}function Fu(t){return ei((function(n){var r=n.length,e=r,o=qr.prototype.thru;for(t&&n.reverse();e--;){var a=n[e];if("function"!=typeof a)throw new Rt(i);if(o&&!f&&"wrapper"==ai(a))var f=new qr([],!0)}for(e=f?e:r;++e1&&m.reverse(),h&&lf))return!1;var l=o.get(t),s=o.get(n);if(l&&s)return l==n&&s==t;var _=-1,h=!0,p=2&r?new Vr:u;for(o.set(t,n),o.set(n,t);++_-1&&t%1==0&&t1?"& ":"")+n[e],n=n.join(r>2?", ":" "),t.replace(ft,"{\n/* [wrapped with "+n+"] */\n")}(e,function(t,n){return Rn(d,(function(r){var e="_."+r[0];n&r[1]&&!zn(t,e)&&t.push(e)})),t.sort()}(function(t){var n=t.match(ct);return n?n[1].split(lt):[]}(e),r)))}function zi(t){var n=0,r=0;return function(){var e=wr(),i=16-(e-r);if(r=e,i>0){if(++n>=800)return arguments[0]}else n=0;return t.apply(u,arguments)}}function Ti(t,n){var r=-1,e=t.length,i=e-1;for(n=n===u?e:n;++r1?t[n-1]:u;return r="function"==typeof r?(t.pop(),r):u,io(t,r)}));function _o(t){var n=Pr(t);return n.__chain__=!0,n}function ho(t,n){return n(t)}var po=ei((function(t){var n=t.length,r=n?t[0]:0,e=this.__wrapped__,i=function(n){return ae(n,t)};return!(n>1||this.__actions__.length)&&e instanceof Zr&&bi(r)?((e=e.slice(r,+r+(n?1:0))).__actions__.push({func:ho,args:[i],thisArg:u}),new qr(e,this.__chain__).thru((function(t){return n&&!t.length&&t.push(u),t}))):this.thru(i)}));var vo=Iu((function(t,n,r){Lt.call(t,r)?++t[r]:oe(t,r,1)}));var go=Wu(Ni),yo=Wu(qi);function bo(t,n){return(Zo(t)?Rn:he)(t,ci(n,3))}function mo(t,n){return(Zo(t)?Cn:pe)(t,ci(n,3))}var wo=Iu((function(t,n,r){Lt.call(t,r)?t[r].push(n):oe(t,r,[n])}));var xo=Ye((function(t,n,r){var u=-1,i="function"==typeof n,o=Ko(t)?e(t.length):[];return he(t,(function(t){o[++u]=i?Sn(n,t,r):Ce(t,n,r)})),o})),Bo=Iu((function(t,n,r){oe(t,r,n)}));function jo(t,n){return(Zo(t)?Ln:Fe)(t,ci(n,3))}var ko=Iu((function(t,n,r){t[r?0:1].push(n)}),(function(){return[[],[]]}));var Ao=Ye((function(t,n){if(null==t)return[];var r=n.length;return r>1&&mi(t,n[0],n[1])?n=[]:r>2&&mi(n[0],n[1],n[2])&&(n=[n[0]]),Ze(t,ye(n,1),[])})),Mo=sn||function(){return gn.Date.now()};function So(t,n,r){return n=r?u:n,n=t&&null==n?t.length:n,Qu(t,s,u,u,u,u,n)}function Oo(t,n){var r;if("function"!=typeof n)throw new Rt(i);return t=va(t),function(){return--t>0&&(r=n.apply(this,arguments)),t<=1&&(n=u),r}}var Ro=Ye((function(t,n,r){var e=1;if(r.length){var u=sr(r,fi(Ro));e|=c}return Qu(t,e,n,r,u)})),Co=Ye((function(t,n,r){var e=3;if(r.length){var u=sr(r,fi(Co));e|=c}return Qu(n,e,t,r,u)}));function Io(t,n,r){var e,o,a,f,c,l,s=0,_=!1,h=!1,p=!0;if("function"!=typeof t)throw new Rt(i);function v(n){var r=e,i=o;return e=o=u,s=n,f=t.apply(i,r)}function g(t){var r=t-l;return l===u||r>=n||r<0||h&&t-s>=a}function d(){var t=Mo();if(g(t))return y(t);c=Ci(d,function(t){var r=n-(t-l);return h?mr(r,a-(t-s)):r}(t))}function y(t){return c=u,p&&e?v(t):(e=o=u,f)}function b(){var t=Mo(),r=g(t);if(e=arguments,o=this,l=t,r){if(c===u)return function(t){return s=t,c=Ci(d,n),_?v(t):f}(l);if(h)return Bu(c),c=Ci(d,n),v(l)}return c===u&&(c=Ci(d,n)),f}return n=da(n)||0,na(r)&&(_=!!r.leading,a=(h="maxWait"in r)?br(da(r.maxWait)||0,n):a,p="trailing"in r?!!r.trailing:p),b.cancel=function(){c!==u&&Bu(c),s=0,e=l=o=c=u},b.flush=function(){return c===u?f:y(Mo())},b}var Eo=Ye((function(t,n){return se(t,1,n)})),zo=Ye((function(t,n,r){return se(t,da(n)||0,r)}));function To(t,n){if("function"!=typeof t||null!=n&&"function"!=typeof n)throw new Rt(i);var r=function(){var e=arguments,u=n?n.apply(this,e):e[0],i=r.cache;if(i.has(u))return i.get(u);var o=t.apply(this,e);return r.cache=i.set(u,o)||i,o};return r.cache=new(To.Cache||Hr),r}function Lo(t){if("function"!=typeof t)throw new Rt(i);return function(){var n=arguments;switch(n.length){case 0:return!t.call(this);case 1:return!t.call(this,n[0]);case 2:return!t.call(this,n[0],n[1]);case 3:return!t.call(this,n[0],n[1],n[2])}return!t.apply(this,n)}}To.Cache=Hr;var Do=wu((function(t,n){var r=(n=1==n.length&&Zo(n[0])?Ln(n[0],tr(ci())):Ln(ye(n,1),tr(ci()))).length;return Ye((function(e){for(var u=-1,i=mr(e.length,r);++u=n})),qo=Ie(function(){return arguments}())?Ie:function(t){return ra(t)&&Lt.call(t,"callee")&&!Vt.call(t,"callee")},Zo=e.isArray,Go=xn?tr(xn):function(t){return ra(t)&&Ae(t)==z};function Ko(t){return null!=t&&ta(t.length)&&!Qo(t)}function Ho(t){return ra(t)&&Ko(t)}var Vo=mn||yf,Jo=Bn?tr(Bn):function(t){return ra(t)&&Ae(t)==w};function Yo(t){if(!ra(t))return!1;var n=Ae(t);return n==x||"[object DOMException]"==n||"string"==typeof t.message&&"string"==typeof t.name&&!ia(t)}function Qo(t){if(!na(t))return!1;var n=Ae(t);return n==B||n==j||"[object AsyncFunction]"==n||"[object Proxy]"==n}function Xo(t){return"number"==typeof t&&t==va(t)}function ta(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=p}function na(t){var n=typeof t;return null!=t&&("object"==n||"function"==n)}function ra(t){return null!=t&&"object"==typeof t}var ea=jn?tr(jn):function(t){return ra(t)&&vi(t)==k};function ua(t){return"number"==typeof t||ra(t)&&Ae(t)==A}function ia(t){if(!ra(t)||Ae(t)!=M)return!1;var n=Kt(t);if(null===n)return!0;var r=Lt.call(n,"constructor")&&n.constructor;return"function"==typeof r&&r instanceof r&&Tt.call(r)==Ft}var oa=kn?tr(kn):function(t){return ra(t)&&Ae(t)==O};var aa=An?tr(An):function(t){return ra(t)&&vi(t)==R};function fa(t){return"string"==typeof t||!Zo(t)&&ra(t)&&Ae(t)==C}function ca(t){return"symbol"==typeof t||ra(t)&&Ae(t)==I}var la=Mn?tr(Mn):function(t){return ra(t)&&ta(t.length)&&!!cn[Ae(t)]};var sa=Ku(We),_a=Ku((function(t,n){return t<=n}));function ha(t){if(!t)return[];if(Ko(t))return fa(t)?vr(t):Ru(t);if(Qt&&t[Qt])return function(t){for(var n,r=[];!(n=t.next()).done;)r.push(n.value);return r}(t[Qt]());var n=vi(t);return(n==k?cr:n==R?_r:Pa)(t)}function pa(t){return t?(t=da(t))===h||t===-1/0?17976931348623157e292*(t<0?-1:1):t==t?t:0:0===t?t:0}function va(t){var n=pa(t),r=n%1;return n==n?r?n-r:n:0}function ga(t){return t?fe(va(t),0,g):0}function da(t){if("number"==typeof t)return t;if(ca(t))return v;if(na(t)){var n="function"==typeof t.valueOf?t.valueOf():t;t=na(n)?n+"":n}if("string"!=typeof t)return 0===t?t:+t;t=Xn(t);var r=dt.test(t);return r||bt.test(t)?hn(t.slice(2),r?2:8):gt.test(t)?v:+t}function ya(t){return Cu(t,Ea(t))}function ba(t){return null==t?"":lu(t)}var ma=Eu((function(t,n){if(ji(n)||Ko(n))Cu(n,Ia(n),t);else for(var r in n)Lt.call(n,r)&&re(t,r,n[r])})),wa=Eu((function(t,n){Cu(n,Ea(n),t)})),xa=Eu((function(t,n,r,e){Cu(n,Ea(n),t,e)})),Ba=Eu((function(t,n,r,e){Cu(n,Ia(n),t,e)})),ja=ei(ae);var ka=Ye((function(t,n){t=Mt(t);var r=-1,e=n.length,i=e>2?n[2]:u;for(i&&mi(n[0],n[1],i)&&(e=1);++r1),n})),Cu(t,ii(t),r),e&&(r=ce(r,7,ni));for(var u=n.length;u--;)_u(r,n[u]);return r}));var Da=ei((function(t,n){return null==t?{}:function(t,n){return Ge(t,n,(function(n,r){return Sa(t,r)}))}(t,n)}));function Ua(t,n){if(null==t)return{};var r=Ln(ii(t),(function(t){return[t]}));return n=ci(n),Ge(t,r,(function(t,r){return n(t,r[0])}))}var Wa=Yu(Ia),Fa=Yu(Ea);function Pa(t){return null==t?[]:nr(t,Ia(t))}var $a=Du((function(t,n,r){return n=n.toLowerCase(),t+(r?Na(n):n)}));function Na(t){return Ya(ba(t).toLowerCase())}function qa(t){return(t=ba(t))&&t.replace(wt,ir).replace(nn,"")}var Za=Du((function(t,n,r){return t+(r?"-":"")+n.toLowerCase()})),Ga=Du((function(t,n,r){return t+(r?" ":"")+n.toLowerCase()})),Ka=Lu("toLowerCase");var Ha=Du((function(t,n,r){return t+(r?"_":"")+n.toLowerCase()}));var Va=Du((function(t,n,r){return t+(r?" ":"")+Ya(n)}));var Ja=Du((function(t,n,r){return t+(r?" ":"")+n.toUpperCase()})),Ya=Lu("toUpperCase");function Qa(t,n,r){return t=ba(t),(n=r?u:n)===u?function(t){return on.test(t)}(t)?function(t){return t.match(en)||[]}(t):function(t){return t.match(st)||[]}(t):t.match(n)||[]}var Xa=Ye((function(t,n){try{return Sn(t,u,n)}catch(t){return Yo(t)?t:new jt(t)}})),tf=ei((function(t,n){return Rn(n,(function(n){n=Di(n),oe(t,n,Ro(t[n],t))})),t}));function nf(t){return function(){return t}}var rf=Fu(),ef=Fu(!0);function uf(t){return t}function of(t){return Le("function"==typeof t?t:ce(t,1))}var af=Ye((function(t,n){return function(r){return Ce(r,t,n)}})),ff=Ye((function(t,n){return function(r){return Ce(t,r,n)}}));function cf(t,n,r){var e=Ia(n),u=Be(n,e);null!=r||na(n)&&(u.length||!e.length)||(r=n,n=t,t=this,u=Be(n,Ia(n)));var i=!(na(r)&&"chain"in r&&!r.chain),o=Qo(t);return Rn(u,(function(r){var e=n[r];t[r]=e,o&&(t.prototype[r]=function(){var n=this.__chain__;if(i||n){var r=t(this.__wrapped__);return(r.__actions__=Ru(this.__actions__)).push({func:e,args:arguments,thisArg:t}),r.__chain__=n,r}return e.apply(t,Dn([this.value()],arguments))})})),t}function lf(){}var sf=qu(Ln),_f=qu(In),hf=qu(Fn);function pf(t){return wi(t)?Hn(Di(t)):function(t){return function(n){return je(n,t)}}(t)}var vf=Gu(),gf=Gu(!0);function df(){return[]}function yf(){return!1}var bf=Nu((function(t,n){return t+n}),0),mf=Vu("ceil"),wf=Nu((function(t,n){return t/n}),1),xf=Vu("floor");var Bf,jf=Nu((function(t,n){return t*n}),1),kf=Vu("round"),Af=Nu((function(t,n){return t-n}),0);return Pr.after=function(t,n){if("function"!=typeof n)throw new Rt(i);return t=va(t),function(){if(--t<1)return n.apply(this,arguments)}},Pr.ary=So,Pr.assign=ma,Pr.assignIn=wa,Pr.assignInWith=xa,Pr.assignWith=Ba,Pr.at=ja,Pr.before=Oo,Pr.bind=Ro,Pr.bindAll=tf,Pr.bindKey=Co,Pr.castArray=function(){if(!arguments.length)return[];var t=arguments[0];return Zo(t)?t:[t]},Pr.chain=_o,Pr.chunk=function(t,n,r){n=(r?mi(t,n,r):n===u)?1:br(va(n),0);var i=null==t?0:t.length;if(!i||n<1)return[];for(var o=0,a=0,f=e(vn(i/n));oi?0:i+r),(e=e===u||e>i?i:va(e))<0&&(e+=i),e=r>e?0:ga(e);r>>0)?(t=ba(t))&&("string"==typeof n||null!=n&&!oa(n))&&!(n=lu(n))&&fr(t)?xu(vr(t),0,r):t.split(n,r):[]},Pr.spread=function(t,n){if("function"!=typeof t)throw new Rt(i);return n=null==n?0:br(va(n),0),Ye((function(r){var e=r[n],u=xu(r,0,n);return e&&Dn(u,e),Sn(t,this,u)}))},Pr.tail=function(t){var n=null==t?0:t.length;return n?uu(t,1,n):[]},Pr.take=function(t,n,r){return t&&t.length?uu(t,0,(n=r||n===u?1:va(n))<0?0:n):[]},Pr.takeRight=function(t,n,r){var e=null==t?0:t.length;return e?uu(t,(n=e-(n=r||n===u?1:va(n)))<0?0:n,e):[]},Pr.takeRightWhile=function(t,n){return t&&t.length?pu(t,ci(n,3),!1,!0):[]},Pr.takeWhile=function(t,n){return t&&t.length?pu(t,ci(n,3)):[]},Pr.tap=function(t,n){return n(t),t},Pr.throttle=function(t,n,r){var e=!0,u=!0;if("function"!=typeof t)throw new Rt(i);return na(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),Io(t,n,{leading:e,maxWait:n,trailing:u})},Pr.thru=ho,Pr.toArray=ha,Pr.toPairs=Wa,Pr.toPairsIn=Fa,Pr.toPath=function(t){return Zo(t)?Ln(t,Di):ca(t)?[t]:Ru(Li(ba(t)))},Pr.toPlainObject=ya,Pr.transform=function(t,n,r){var e=Zo(t),u=e||Vo(t)||la(t);if(n=ci(n,4),null==r){var i=t&&t.constructor;r=u?e?new i:[]:na(t)&&Qo(i)?$r(Kt(t)):{}}return(u?Rn:we)(t,(function(t,e,u){return n(r,t,e,u)})),r},Pr.unary=function(t){return So(t,1)},Pr.union=no,Pr.unionBy=ro,Pr.unionWith=eo,Pr.uniq=function(t){return t&&t.length?su(t):[]},Pr.uniqBy=function(t,n){return t&&t.length?su(t,ci(n,2)):[]},Pr.uniqWith=function(t,n){return n="function"==typeof n?n:u,t&&t.length?su(t,u,n):[]},Pr.unset=function(t,n){return null==t||_u(t,n)},Pr.unzip=uo,Pr.unzipWith=io,Pr.update=function(t,n,r){return null==t?t:hu(t,n,bu(r))},Pr.updateWith=function(t,n,r,e){return e="function"==typeof e?e:u,null==t?t:hu(t,n,bu(r),e)},Pr.values=Pa,Pr.valuesIn=function(t){return null==t?[]:nr(t,Ea(t))},Pr.without=oo,Pr.words=Qa,Pr.wrap=function(t,n){return Uo(bu(n),t)},Pr.xor=ao,Pr.xorBy=fo,Pr.xorWith=co,Pr.zip=lo,Pr.zipObject=function(t,n){return du(t||[],n||[],re)},Pr.zipObjectDeep=function(t,n){return du(t||[],n||[],tu)},Pr.zipWith=so,Pr.entries=Wa,Pr.entriesIn=Fa,Pr.extend=wa,Pr.extendWith=xa,cf(Pr,Pr),Pr.add=bf,Pr.attempt=Xa,Pr.camelCase=$a,Pr.capitalize=Na,Pr.ceil=mf,Pr.clamp=function(t,n,r){return r===u&&(r=n,n=u),r!==u&&(r=(r=da(r))==r?r:0),n!==u&&(n=(n=da(n))==n?n:0),fe(da(t),n,r)},Pr.clone=function(t){return ce(t,4)},Pr.cloneDeep=function(t){return ce(t,5)},Pr.cloneDeepWith=function(t,n){return ce(t,5,n="function"==typeof n?n:u)},Pr.cloneWith=function(t,n){return ce(t,4,n="function"==typeof n?n:u)},Pr.conformsTo=function(t,n){return null==n||le(t,n,Ia(n))},Pr.deburr=qa,Pr.defaultTo=function(t,n){return null==t||t!=t?n:t},Pr.divide=wf,Pr.endsWith=function(t,n,r){t=ba(t),n=lu(n);var e=t.length,i=r=r===u?e:fe(va(r),0,e);return(r-=n.length)>=0&&t.slice(r,i)==n},Pr.eq=Po,Pr.escape=function(t){return(t=ba(t))&&Y.test(t)?t.replace(V,or):t},Pr.escapeRegExp=function(t){return(t=ba(t))&&it.test(t)?t.replace(ut,"\\$&"):t},Pr.every=function(t,n,r){var e=Zo(t)?In:ve;return r&&mi(t,n,r)&&(n=u),e(t,ci(n,3))},Pr.find=go,Pr.findIndex=Ni,Pr.findKey=function(t,n){return $n(t,ci(n,3),we)},Pr.findLast=yo,Pr.findLastIndex=qi,Pr.findLastKey=function(t,n){return $n(t,ci(n,3),xe)},Pr.floor=xf,Pr.forEach=bo,Pr.forEachRight=mo,Pr.forIn=function(t,n){return null==t?t:be(t,ci(n,3),Ea)},Pr.forInRight=function(t,n){return null==t?t:me(t,ci(n,3),Ea)},Pr.forOwn=function(t,n){return t&&we(t,ci(n,3))},Pr.forOwnRight=function(t,n){return t&&xe(t,ci(n,3))},Pr.get=Ma,Pr.gt=$o,Pr.gte=No,Pr.has=function(t,n){return null!=t&&gi(t,n,Se)},Pr.hasIn=Sa,Pr.head=Gi,Pr.identity=uf,Pr.includes=function(t,n,r,e){t=Ko(t)?t:Pa(t),r=r&&!e?va(r):0;var u=t.length;return r<0&&(r=br(u+r,0)),fa(t)?r<=u&&t.indexOf(n,r)>-1:!!u&&qn(t,n,r)>-1},Pr.indexOf=function(t,n,r){var e=null==t?0:t.length;if(!e)return-1;var u=null==r?0:va(r);return u<0&&(u=br(e+u,0)),qn(t,n,u)},Pr.inRange=function(t,n,r){return n=pa(n),r===u?(r=n,n=0):r=pa(r),function(t,n,r){return t>=mr(n,r)&&t=-9007199254740991&&t<=p},Pr.isSet=aa,Pr.isString=fa,Pr.isSymbol=ca,Pr.isTypedArray=la,Pr.isUndefined=function(t){return t===u},Pr.isWeakMap=function(t){return ra(t)&&vi(t)==E},Pr.isWeakSet=function(t){return ra(t)&&"[object WeakSet]"==Ae(t)},Pr.join=function(t,n){return null==t?"":Pn.call(t,n)},Pr.kebabCase=Za,Pr.last=Ji,Pr.lastIndexOf=function(t,n,r){var e=null==t?0:t.length;if(!e)return-1;var i=e;return r!==u&&(i=(i=va(r))<0?br(e+i,0):mr(i,e-1)),n==n?function(t,n,r){for(var e=r+1;e--;)if(t[e]===n)return e;return e}(t,n,i):Nn(t,Gn,i,!0)},Pr.lowerCase=Ga,Pr.lowerFirst=Ka,Pr.lt=sa,Pr.lte=_a,Pr.max=function(t){return t&&t.length?ge(t,uf,Me):u},Pr.maxBy=function(t,n){return t&&t.length?ge(t,ci(n,2),Me):u},Pr.mean=function(t){return Kn(t,uf)},Pr.meanBy=function(t,n){return Kn(t,ci(n,2))},Pr.min=function(t){return t&&t.length?ge(t,uf,We):u},Pr.minBy=function(t,n){return t&&t.length?ge(t,ci(n,2),We):u},Pr.stubArray=df,Pr.stubFalse=yf,Pr.stubObject=function(){return{}},Pr.stubString=function(){return""},Pr.stubTrue=function(){return!0},Pr.multiply=jf,Pr.nth=function(t,n){return t&&t.length?qe(t,va(n)):u},Pr.noConflict=function(){return gn._===this&&(gn._=Pt),this},Pr.noop=lf,Pr.now=Mo,Pr.pad=function(t,n,r){t=ba(t);var e=(n=va(n))?pr(t):0;if(!n||e>=n)return t;var u=(n-e)/2;return Zu(dn(u),r)+t+Zu(vn(u),r)},Pr.padEnd=function(t,n,r){t=ba(t);var e=(n=va(n))?pr(t):0;return n&&en){var e=t;t=n,n=e}if(r||t%1||n%1){var i=Br();return mr(t+i*(n-t+_n("1e-"+((i+"").length-1))),n)}return Ve(t,n)},Pr.reduce=function(t,n,r){var e=Zo(t)?Un:Jn,u=arguments.length<3;return e(t,ci(n,4),r,u,he)},Pr.reduceRight=function(t,n,r){var e=Zo(t)?Wn:Jn,u=arguments.length<3;return e(t,ci(n,4),r,u,pe)},Pr.repeat=function(t,n,r){return n=(r?mi(t,n,r):n===u)?1:va(n),Je(ba(t),n)},Pr.replace=function(){var t=arguments,n=ba(t[0]);return t.length<3?n:n.replace(t[1],t[2])},Pr.result=function(t,n,r){var e=-1,i=(n=mu(n,t)).length;for(i||(i=1,t=u);++ep)return[];var r=g,e=mr(t,g);n=ci(n),t-=g;for(var u=Qn(e,n);++r=o)return t;var f=r-pr(e);if(f<1)return e;var c=a?xu(a,0,f).join(""):t.slice(0,f);if(i===u)return c+e;if(a&&(f+=c.length-f),oa(i)){if(t.slice(f).search(i)){var l,s=c;for(i.global||(i=St(i.source,ba(vt.exec(i))+"g")),i.lastIndex=0;l=i.exec(s);)var _=l.index;c=c.slice(0,_===u?f:_)}}else if(t.indexOf(lu(i),f)!=f){var h=c.lastIndexOf(i);h>-1&&(c=c.slice(0,h))}return c+e},Pr.unescape=function(t){return(t=ba(t))&&J.test(t)?t.replace(H,dr):t},Pr.uniqueId=function(t){var n=++Dt;return ba(t)+n},Pr.upperCase=Ja,Pr.upperFirst=Ya,Pr.each=bo,Pr.eachRight=mo,Pr.first=Gi,cf(Pr,(Bf={},we(Pr,(function(t,n){Lt.call(Pr.prototype,n)||(Bf[n]=t)})),Bf),{chain:!1}),Pr.VERSION="4.17.21",Rn(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(t){Pr[t].placeholder=Pr})),Rn(["drop","take"],(function(t,n){Zr.prototype[t]=function(r){r=r===u?1:br(va(r),0);var e=this.__filtered__&&!n?new Zr(this):this.clone();return e.__filtered__?e.__takeCount__=mr(r,e.__takeCount__):e.__views__.push({size:mr(r,g),type:t+(e.__dir__<0?"Right":"")}),e},Zr.prototype[t+"Right"]=function(n){return this.reverse()[t](n).reverse()}})),Rn(["filter","map","takeWhile"],(function(t,n){var r=n+1,e=1==r||3==r;Zr.prototype[t]=function(t){var n=this.clone();return n.__iteratees__.push({iteratee:ci(t,3),type:r}),n.__filtered__=n.__filtered__||e,n}})),Rn(["head","last"],(function(t,n){var r="take"+(n?"Right":"");Zr.prototype[t]=function(){return this[r](1).value()[0]}})),Rn(["initial","tail"],(function(t,n){var r="drop"+(n?"":"Right");Zr.prototype[t]=function(){return this.__filtered__?new Zr(this):this[r](1)}})),Zr.prototype.compact=function(){return this.filter(uf)},Zr.prototype.find=function(t){return this.filter(t).head()},Zr.prototype.findLast=function(t){return this.reverse().find(t)},Zr.prototype.invokeMap=Ye((function(t,n){return"function"==typeof t?new Zr(this):this.map((function(r){return Ce(r,t,n)}))})),Zr.prototype.reject=function(t){return this.filter(Lo(ci(t)))},Zr.prototype.slice=function(t,n){t=va(t);var r=this;return r.__filtered__&&(t>0||n<0)?new Zr(r):(t<0?r=r.takeRight(-t):t&&(r=r.drop(t)),n!==u&&(r=(n=va(n))<0?r.dropRight(-n):r.take(n-t)),r)},Zr.prototype.takeRightWhile=function(t){return this.reverse().takeWhile(t).reverse()},Zr.prototype.toArray=function(){return this.take(g)},we(Zr.prototype,(function(t,n){var r=/^(?:filter|find|map|reject)|While$/.test(n),e=/^(?:head|last)$/.test(n),i=Pr[e?"take"+("last"==n?"Right":""):n],o=e||/^find/.test(n);i&&(Pr.prototype[n]=function(){var n=this.__wrapped__,a=e?[1]:arguments,f=n instanceof Zr,c=a[0],l=f||Zo(n),s=function(t){var n=i.apply(Pr,Dn([t],a));return e&&_?n[0]:n};l&&r&&"function"==typeof c&&1!=c.length&&(f=l=!1);var _=this.__chain__,h=!!this.__actions__.length,p=o&&!_,v=f&&!h;if(!o&&l){n=v?n:new Zr(this);var g=t.apply(n,a);return g.__actions__.push({func:ho,args:[s],thisArg:u}),new qr(g,_)}return p&&v?t.apply(this,a):(g=this.thru(s),p?e?g.value()[0]:g.value():g)})})),Rn(["pop","push","shift","sort","splice","unshift"],(function(t){var n=Ct[t],r=/^(?:push|sort|unshift)$/.test(t)?"tap":"thru",e=/^(?:pop|shift)$/.test(t);Pr.prototype[t]=function(){var t=arguments;if(e&&!this.__chain__){var u=this.value();return n.apply(Zo(u)?u:[],t)}return this[r]((function(r){return n.apply(Zo(r)?r:[],t)}))}})),we(Zr.prototype,(function(t,n){var r=Pr[n];if(r){var e=r.name+"";Lt.call(Ir,e)||(Ir[e]=[]),Ir[e].push({name:n,func:r})}})),Ir[Pu(u,2).name]=[{name:"wrapper",func:u}],Zr.prototype.clone=function(){var t=new Zr(this.__wrapped__);return t.__actions__=Ru(this.__actions__),t.__dir__=this.__dir__,t.__filtered__=this.__filtered__,t.__iteratees__=Ru(this.__iteratees__),t.__takeCount__=this.__takeCount__,t.__views__=Ru(this.__views__),t},Zr.prototype.reverse=function(){if(this.__filtered__){var t=new Zr(this);t.__dir__=-1,t.__filtered__=!0}else(t=this.clone()).__dir__*=-1;return t},Zr.prototype.value=function(){var t=this.__wrapped__.value(),n=this.__dir__,r=Zo(t),e=n<0,u=r?t.length:0,i=function(t,n,r){var e=-1,u=r.length;for(;++e=this.__values__.length;return{done:t,value:t?u:this.__values__[this.__index__++]}},Pr.prototype.plant=function(t){for(var n,r=this;r instanceof Nr;){var e=Wi(r);e.__index__=0,e.__values__=u,n?i.__wrapped__=e:n=e;var i=e;r=r.__wrapped__}return i.__wrapped__=t,n},Pr.prototype.reverse=function(){var t=this.__wrapped__;if(t instanceof Zr){var n=t;return this.__actions__.length&&(n=new Zr(this)),(n=n.reverse()).__actions__.push({func:ho,args:[to],thisArg:u}),new qr(n,this.__chain__)}return this.thru(to)},Pr.prototype.toJSON=Pr.prototype.valueOf=Pr.prototype.value=function(){return vu(this.__wrapped__,this.__actions__)},Pr.prototype.first=Pr.prototype.head,Qt&&(Pr.prototype[Qt]=function(){return this}),Pr}();gn._=yr,(e=function(){return yr}.call(n,r,n,t))===u||(t.exports=e)}.call(this)}},n={};function r(e){var u=n[e];if(void 0!==u)return u.exports;var i=n[e]={id:e,loaded:!1,exports:{}};return t[e].call(i.exports,i,i.exports,r),i.loaded=!0,i.exports}r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r.nmd=t=>(t.paths=[],t.children||(t.children=[]),t);var e=r(825);freeaps_profile=e})(); \ No newline at end of file diff --git a/FreeAPS/Resources/javascript/prepare/determine-basal.js b/FreeAPS/Resources/javascript/prepare/determine-basal.js index 6c4fc4663c..ddd9f04aa5 100644 --- a/FreeAPS/Resources/javascript/prepare/determine-basal.js +++ b/FreeAPS/Resources/javascript/prepare/determine-basal.js @@ -28,9 +28,9 @@ function generate(iob, currenttemp, glucose, profile, autosens = null, meal = nu // ISF and CR if (dynamicVariables.isfAndCr) { profile.sense /= factor; - profile.carb_ratio /= factor; + profile.carb_ratio = round(profile.carb_ratio / factor, 1); } else { - if (dynamicVariables.cr) { profile.carb_ratio /= factor; } + if (dynamicVariables.cr) { profile.carb_ratio = round(profile.carb_ratio / factor, 1); } if (dynamicVariables.isf) { profile.sens /= factor; } } console.log("Override Active, " + dynamicVariables.overridePercentage + "%"); @@ -99,6 +99,7 @@ function generate(iob, currenttemp, glucose, profile, autosens = null, meal = nu function dynisf(profile, autosens_data, dynamicVariables, glucose) { console.log("Starting dynamic ISF layer."); var dynISFenabled = true; + // One of two exercise settings (they share the same purpose). var exerciseSetting = false; if (profile.highTemptargetRaisesSensitivity || profile.exerciseMode || dynamicVariables.isEnabled) { diff --git a/FreeAPS/Resources/javascript/prepare/middleware.js b/FreeAPS/Resources/javascript/prepare/middleware.js index 20c5da89a3..21851fef05 100644 --- a/FreeAPS/Resources/javascript/prepare/middleware.js +++ b/FreeAPS/Resources/javascript/prepare/middleware.js @@ -12,14 +12,19 @@ function generate(iob, currenttemp, glucose, profile, autosens = null, meal = nu if (profile.tddAdjBasal && dynamicVariables.average_total_data != 0) { profile.tdd_factor = Math.round( (dynamicVariables.weightedAverage / dynamicVariables.average_total_data) * 100) / 100; - const adjusted = Math.min(Math.max(profile.autosens_min, profile.tdd_factor), profile.autosens_max); - if (profile.tdd_factor != adjusted) { console.log("Dynamic basal adjustment limited by your autosens_min/max settings to: " + adjusted); profile.tdd_factor = adjusted; } } + + if (profile.out_units == 'mmol/L') { + profile.old_isf = Math.round(profile.sens * 0.0555 * 10) / 10; + } else { + profile.old_isf = profile.sens; + } + profile.old_cr = profile.carb_ratio; if (profile.useNewFormula && profile.temptargetSet && (profile.high_temptarget_raises_sensitivity || profile.exercise_mode || dynamicVariables.isEnabled) && profile.min_bg >= 118) { profile.useNewFormula = false; diff --git a/FreeAPS/Resources/javascript/prepare/profile.js b/FreeAPS/Resources/javascript/prepare/profile.js index 572792dc0b..fd3f6e4b99 100644 --- a/FreeAPS/Resources/javascript/prepare/profile.js +++ b/FreeAPS/Resources/javascript/prepare/profile.js @@ -84,7 +84,9 @@ function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data var tdd_factor = { }; var set_basal = false; var basal_rate = { }; - + var old_isf = { }; + var old_cr = { }; + var inputs = { }; //add all preferences to the inputs for (var pref in preferences) { @@ -112,6 +114,9 @@ function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data inputs.tddFactor = tdd_factor; inputs.set_basal = set_basal; inputs.basal_rate = basal_rate; + inputs.old_isf = old_isf; + inputs.old_cr = old_cr; + if (autotune_data) { if (autotune_data.basalprofile) { inputs.basals = autotune_data.basalprofile; } @@ -120,5 +125,42 @@ function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data if (autotune_data.carb_ratio) { inputs.carbratio.schedule[0].ratio = autotune_data.carb_ratio; } } } - return freeaps_profile(inputs); + + // merge oref0 defaults with iAPS ones + const defaults = Object.assign( + {}, + freeaps_profile.defaults(), + { + type: 'iAPS', // attribute to override defaults + // +++++ iAPS settings + // smb_delivery_ratio: included in the current oref0 PR (https://github.com/openaps/oref0/pull/1465/files) + smb_delivery_ratio: 0.5, + adjustmentFactor: 1, + useNewFormula: false, + enableDynamicCR: false, + sigmoid: false, + weightPercentage: 0.65, + tddAdjBasal: false, + // threshold_setting: temporary fix to test thomasvargiu/iAPS#original-oref0 branch before build. + // We can remove it after merged and after build the new original bundles + // because it's included in the current oref0 PR (https://github.com/openaps/oref0/pull/1465/files) + // currently (2024-08-09) this settings probably doesn't work in the current iAPS main/dev branch + threshold_setting: 60 + } + ) + + var logs = { err: '', stdout: '', return_val: 0 }; + var profile = freeaps_profile(logs, inputs, defaults); + if (logs.err.length > 0) { + console.error(logs.err); + } + if (logs.stdout.length > 0) { + console.error(logs.stdout); + } + + if (typeof profile !== 'object') { + return; + } + + return profile; } diff --git a/FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json b/FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json index b3e135569a..55ed5f35b5 100644 --- a/FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json +++ b/FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json @@ -40,7 +40,7 @@ "yGridLines" : true, "oneDimensionalGraph" : false, "rulerMarks" : false, - "maxCarbs" : 1000, + "maxCarbs" : 200, "displayFatAndProteinOnWatch" : false, "confirmBolusFaster" : false, "onlyAutotuneBasals" : false, @@ -62,5 +62,6 @@ "minimumSMB": 0.3, "useInsulinBars": false, "uploadVersion": true, - "birthDate": Date.distantPast + "birthDate": Date.distantPast, + "displayDelta": false } diff --git a/FreeAPS/Sources/APS/APSManager.swift b/FreeAPS/Sources/APS/APSManager.swift index 23cd284681..6fc27ad5b7 100644 --- a/FreeAPS/Sources/APS/APSManager.swift +++ b/FreeAPS/Sources/APS/APSManager.swift @@ -51,7 +51,7 @@ enum APSError: LocalizedError { case let .invalidPumpState(message): return "Error: Invalid Pump State: \(message)" case let .bolusInProgress(message): - return "\(NSLocalizedString("Error: Pump is Busy.", comment: "Pump Error")) \(NSLocalizedString(message, comment: "Pump Error Message"))" + return "\(NSLocalizedString("Pump is Busy.", comment: "Pump Error")) \(NSLocalizedString(message, comment: "Pump Error Message"))" case let .glucoseError(message): return "Error: Invalid glucose: \(message)" case let .apsError(message): @@ -829,7 +829,8 @@ final class BaseAPSManager: APSManager, Injectable { let saveLastLoop = LastLoop(context: self.coredataContext) saveLastLoop.iob = (enacted.iob ?? 0) as NSDecimalNumber saveLastLoop.cob = (enacted.cob ?? 0) as NSDecimalNumber - saveLastLoop.timestamp = (enacted.timestamp ?? .distantPast) as Date + saveLastLoop.timestamp = received ? enacted.timestamp : CoreDataStorage().fetchLastLoop()? + .timestamp ?? .distantPast try? self.coredataContext.save() } diff --git a/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift b/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift index 3747862418..d1d006d56c 100644 --- a/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift +++ b/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift @@ -21,6 +21,10 @@ final class OpenAPS { func determineBasal(currentTemp: TempBasal, clock: Date = Date()) -> Future { Future { promise in self.processQueue.async { + let start = Date.now + + var now = Date.now + debug(.openAPS, "Start determineBasal") // clock self.storage.save(clock, as: Monitor.clock) @@ -36,7 +40,14 @@ final class OpenAPS { // To do: remove this struct. let dynamicVariables = self.loadFileFromStorage(name: Monitor.dynamicVariables) - var now = Date.now + let tdd = CoreDataStorage().fetchInsulinDistribution().first + + print( + "Time for Loading files \(-1 * now.timeIntervalSinceNow) seconds" + ) + + now = Date.now + let meal = self.meal( pumphistory: pumpHistory, profile: profile, @@ -45,7 +56,9 @@ final class OpenAPS { carbs: carbs, glucose: glucose ) - print("Time for Determine Basal: step after meal module \(-1 * now.timeIntervalSinceNow) seconds") + print( + "Time for Meal module \(-1 * now.timeIntervalSinceNow) seconds, total: \(-1 * start.timeIntervalSinceNow)" + ) self.storage.save(meal, as: Monitor.meal) @@ -59,7 +72,9 @@ final class OpenAPS { autosens: autosens.isEmpty ? .null : autosens ) self.storage.save(iob, as: Monitor.iob) - print("Time for Determine Basal: step after IOB module \(-1 * now.timeIntervalSinceNow) seconds") + print( + "Time for IOB module \(-1 * now.timeIntervalSinceNow) seconds, total: \(-1 * start.timeIntervalSinceNow)" + ) // determine-basal let reservoir = self.loadFileFromStorage(name: Monitor.reservoir) @@ -77,6 +92,7 @@ final class OpenAPS { dynamicVariables: dynamicVariables ) + now = Date.now // The OpenAPS JS algorithm layer let suggested = self.determineBasal( glucose: glucose, @@ -90,10 +106,15 @@ final class OpenAPS { dynamicVariables: dynamicVariables ) + print( + "Time for Determine Basal module \(-1 * now.timeIntervalSinceNow) seconds, total: \(-1 * start.timeIntervalSinceNow)" + ) + debug(.openAPS, "SUGGESTED: \(suggested)") // Update Suggestion if var suggestion = Suggestion(from: suggested) { + now = Date.now // Process any eventual middleware basal rate if let newSuggestion = self.overrideBasal(alteredProfile: alteredProfile, oref0Suggestion: suggestion) { suggestion = newSuggestion @@ -103,13 +124,18 @@ final class OpenAPS { reason: suggestion.reason, suggestion: suggestion, preferences: preferencesData, - profile: alteredProfile + profile: alteredProfile, + tdd: tdd ) // Update time suggestion.timestamp = suggestion.deliverAt ?? clock // Save self.storage.save(suggestion, as: Enact.suggested) + print( + "Time for updating and saving reasons: \(-1 * now.timeIntervalSinceNow) seconds, total: \(-1 * start.timeIntervalSinceNow)" + ) + promise(.success(suggestion)) } else { promise(.success(nil)) @@ -209,7 +235,7 @@ final class OpenAPS { let freeaps = self.loadFileFromStorage(name: FreeAPS.settings) let preferencesData = Preferences(from: preferences) let tdd = self.tdd(preferencesData: preferencesData) - if let insulin = tdd, (insulin.basal + insulin.bolus) > 0 { + if let insulin = tdd, insulin.hours > 0 { CoreDataStorage().saveTDD(insulin) } let dynamicVariables = self.dynamicVariables(preferencesData) @@ -260,21 +286,24 @@ final class OpenAPS { private func reasons( reason: String, suggestion: Suggestion, - preferences: Preferences?, - profile: RawJSON + preferences _: Preferences?, + profile: RawJSON, + tdd: InsulinDistribution? ) -> String { var reasonString = reason let startIndex = reasonString.startIndex - let tdd = tdd(preferencesData: preferences) // Autosens.ratio / Dynamic Ratios if let isf = suggestion.sensitivityRatio { // TDD var tddString = "" - if let total = tdd { - let round = round(Double((total.bolus + total.basal) * 10)) / 10 - let bolus = Int(total.bolus * 100 / ((total.bolus + total.basal) != 0 ? total.bolus + total.basal : 1)) - tddString = ", TDD: \(round) U, \(bolus) % Bolus, " + if let tdd = tdd { + let total = ((tdd.bolus ?? 0) as Decimal) + ((tdd.tempBasal ?? 0) as Decimal) + let round = round(Double(total * 10)) / 10 + + let bolus = Int(((tdd.bolus ?? 0) as Decimal) * 100 / (total != 0 ? total : 1)) + + tddString = ", Insulin 24h: \(round) U, \(bolus) % Bolus, " } else { tddString = ", " } @@ -292,9 +321,9 @@ final class OpenAPS { insertedResons += ", AF: \(value)" } if let dynamicCR = readJSON(json: profile, variable: "enableDynamicCR"), Bool(dynamicCR) ?? false { - insertedResons += ", Dynamic ISF/CR: On/On" + insertedResons += ", Dynamic ISF/CR is: On/On" } else { - insertedResons += ", Dynamic ISF/CR: On/Off" + insertedResons += ", Dynamic ISF/CR is: On/Off" } if let tddFactor = readMiddleware(json: profile, variable: "tdd_factor"), tddFactor.count > 1 { insertedResons += ", Basal Adjustment: \(tddFactor.suffix(max(tddFactor.count - 6, 0)))" @@ -306,6 +335,20 @@ final class OpenAPS { } else { reasonString.insert(contentsOf: "Autosens Ratio: \(isf)" + tddString, at: startIndex) } + + // Include ISF before eventual adjustment + if let old = readMiddleware(json: profile, variable: "old_isf"), + let new = readReason(reason: reason, variable: "ISF"), let oldISF = trimmedIsEqual(string: old, decimal: new) + { + reasonString = reasonString.replacingOccurrences(of: "ISF:", with: "ISF: \(oldISF) →") + } + + // Include CR before eventual adjustment + if let old = readMiddleware(json: profile, variable: "old_cr"), + let new = readReason(reason: reason, variable: "CR"), let oldCR = trimmedIsEqual(string: old, decimal: new) + { + reasonString = reasonString.replacingOccurrences(of: "CR:", with: "CR: \(oldCR) →") + } } // Display either Target or Override (where target is included). @@ -365,6 +408,7 @@ final class OpenAPS { saveSuggestion.cob = cob as NSDecimalNumber saveSuggestion.target = target as NSDecimalNumber saveSuggestion.minPredBG = minPredBG as NSDecimalNumber + saveSuggestion.eventualBG = Decimal(suggestion.eventualBG ?? 100) as NSDecimalNumber saveSuggestion.date = Date.now try? coredataContext.save() @@ -376,6 +420,16 @@ final class OpenAPS { return reasonString } + private func trimmedIsEqual(string: String, decimal: Decimal) -> String? { + let old = string.replacingOccurrences(of: ": ", with: "").replacingOccurrences(of: "f", with: "") + let new = "\(decimal)" + + guard old != new else { + return nil + } + return old + } + private func overrideBasal(alteredProfile: RawJSON, oref0Suggestion: Suggestion) -> Suggestion? { guard let changeRate = readJSON(json: alteredProfile, variable: "set_basal"), Bool(changeRate) ?? false, let basal_rate_is = readJSON(json: alteredProfile, variable: "basal_rate") else { return nil } diff --git a/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift b/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift index 9877adc0a2..fe564b3e90 100644 --- a/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift +++ b/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift @@ -334,4 +334,16 @@ final class CoreDataStorage { } return presetsArray.first?.name ?? "default" } + + func fetchLastLoop() -> LastLoop? { + var lastLoop = [LastLoop]() + coredataContext.performAndWait { + let requestLastLoop = LastLoop.fetchRequest() as NSFetchRequest + let sortLoops = NSSortDescriptor(key: "timestamp", ascending: false) + requestLastLoop.sortDescriptors = [sortLoops] + requestLastLoop.fetchLimit = 1 + try? lastLoop = coredataContext.fetch(requestLastLoop) + } + return lastLoop.first + } } diff --git a/FreeAPS/Sources/APS/Storage/OverrideStorage.swift b/FreeAPS/Sources/APS/Storage/OverrideStorage.swift index badd89a15c..4da0393356 100644 --- a/FreeAPS/Sources/APS/Storage/OverrideStorage.swift +++ b/FreeAPS/Sources/APS/Storage/OverrideStorage.swift @@ -100,6 +100,7 @@ final class OverrideStorage { save.smbIsOff = preset.smbIsOff save.smbMinutes = preset.smbMinutes save.uamMinutes = preset.uamMinutes + save.maxIOB = preset.maxIOB save.target = preset.target try? coredataContext.save() } diff --git a/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings index 2de9eb192d..a6a118eade 100644 --- a/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings @@ -4,242 +4,246 @@ */ /* -------------------------------- */ /* Bolus screen when adding insulin */ -"Add insulin without actually bolusing" = "Add insulin without actually bolusing"; +"Add insulin without actually bolusing" = "إضافة الأنسولين دون تنفيذ الجرعة"; /* Add insulin from source outside of pump */ -"Add %@ without bolusing" = "Add %@ without bolusing"; +"Add %@ without bolusing" = "إضافة %@ دون تنفيذ الجرعة"; -"Bolus" = "Bolus"; +"Bolus" = "الجرعة"; -"Close" = "Close"; +"Close" = "إغلاق"; /* Continue after added carbs without bolus */ -"Continue without bolus" = "Continue without bolus"; +"Continue without bolus" = "متابعة دون ضخ جرعة"; /* Continue after added meal without bolus */ -"Save Meal without bolus" = "Save Meal without bolus"; +"Save Meal without bolus" = "إضافة وجبة دون ضخ جرعة"; /* Predictions and Meal summary part of the Bolus View. */ "Status" = "الحالة"; /* Alert when adding large amount without bolusing */ -"\nAmount is more than your Max Bolus setting! \nAre you sure you want to add " = "\nAmount is more than your Max Bolus setting! \nAre you sure you want to add "; +"\nAmount is more than your Max Bolus setting! \nAre you sure you want to add " = "\nالكمية أكثر من الحد الأقصى المحدد للجرعة! +\nهل أنت متيقِّن أنك تريد إضافة "; /* Header */ -"Enact Bolus" = "Enact Bolus"; +"Enact Bolus" = "ضخ الجرعة"; /* Button */ -"Enact bolus" = "Enact bolus"; +"Enact bolus" = "ضخ الجرعة"; /* */ -"Insulin recommended" = "Insulin recommended"; +"Insulin recommended" = "الإنسولين الموصى به"; /* */ -"Insulin required" = "Insulin required"; +"Insulin required" = "الإنسولين المطلوب"; /* Bolus screen */ -"Recommendation" = "Recommendation"; +"Recommendation" = "المقترحات"; /* Button */ -"Clear" = "Clear"; +"Clear" = "مسح"; /* Button */ -"Done" = "Done"; +"Done" = "تمّ"; /* Bolus View footer */ -"Last loop %@ minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run" = "Last loop %@ minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run"; +"Last loop %@ minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run" = "آخر دورة قبل %@ دقيقة. أكمل أو ألغِ هذه الوجبة/الجرعة للسماح بتشغيل الدورة التالية"; /* Calender Option */ -"Display Emojis as Labels" = "Display Emojis as Labels"; +"Display Emojis as Labels" = "عرض الرموز التعبيرية كتسميات"; /* Calender Option */ -"Display IOB and COB" = "Display IOB and COB"; +"Display IOB and COB" = "عرض IOB و COB"; /* Apple Watch App setting */ -"Confirm Bolus Faster" = "Confirm Bolus Faster"; +"Confirm Bolus Faster" = "تفعيل خاصية تأكيد البولوس بشكل اسرع"; /* Setting Section */ -"UI/UX" = "UI/UX"; +"UI/UX" = "اعدادات واجهة/تجربة المستخدم"; /* */ -"Wait please" = "Wait please"; +"Wait please" = "انتظر من فضلك"; /* */ -"Agree and continue" = "Agree and Continue"; +"Agree and continue" = "الموافقة والمتابعة"; /* */ -"Log external insulin" = "Log external insulin"; +"Log external insulin" = "سجل الأنسولين الخارجي"; /* Bolus progress view */ -"of" = "of"; +"of" = "من"; /* Remote Bolus Alert, Part 1 */ -"A Remote Bolus " = "A Remote Bolus "; +"A Remote Bolus " = "جرعة عن بعد "; /* Remote Bolus Alert, Part 2 */ -"was delivered" = "was delivered"; +"was delivered" = "تم تسليمها"; /* Remote Bolus Alert, Part 3 */ " minutes ago, triggered remotely from Nightscout, by a caregiver or a parent. Do you still want to bolus?\n\nPredicted eventual glucose, if you don't bolus, is: " = " minutes ago, triggered remotely from Nightscout, by a caregiver or a parent. Do you still want to bolus?\n\nPredicted eventual glucose, if you don't bolus, is: "; /* Remote Bolus Alert, Title */ -"A Remote Bolus Was Just Delivered!" = "A Remote Bolus Was Just Delivered!"; +"A Remote Bolus Was Just Delivered!" = "تم تسليم الجرعة عن بعد للتو!"; /* Headline in enacted pop up (at: at what time) */ -"Enacted at" = "Enacted at"; +"Enacted at" = "تم تفيعله"; /* Headline in suggested pop up (at: at what time) */ -"Suggested at" = "Suggested at"; +"Suggested at" = "تم اقتراحه"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ -"Meal Summary" = "Meal Summary"; +"Meal Summary" = "موجز الوجبة"; /* Bolus View Meal Edit Meal Button */ -"Edit Meal" = "Edit Meal"; +"Edit Meal" = "تعديل الوجبة"; /* Bolus View Meal Add Meal Button */ -"Add Meal" = "Add Meal"; +"Add Meal" = "إضافة وجبة"; /* Bolus View Bolus Summary Header */ -"Bolus Summary" = "Bolus Summary"; +"Bolus Summary" = "موجز الجرعات"; /* For the Bolus View pop-up */ -"Calculations" = "Calculations"; +"Calculations" = "حساب"; /* For the Bolus View pop-up */ -"Fatty Meal" = "Fatty Meal"; +"Fatty Meal" = "وجبة دهنية"; + +/* Bolus option */ +"Hypo Treatment" = "علاج إنخفاض"; /* For the Bolus View pop-up */ -"Full Bolus" = "Full Bolus"; +"Full Bolus" = "جرعة كاملة"; /* For the Bolus View pop-up */ -"Fraction" = "Fraction"; +"Fraction" = "جزء"; /* For the Bolus View pop-up */ -"Fatty Meal Factor" = "Fatty Meal Factor"; +"Fatty Meal Factor" = "عامل الوجبة الدهنية"; /* Footer */ -"Carbs and previous insulin are included in the eventual glucose prediction." = "Carbs and previous insulin are included in the eventual glucose prediction."; +"Carbs and previous insulin are included in the eventual glucose prediction." = "الكربونات والأنسولين السابقة مدرجة في التنبؤ بالجلوكوز في نهاية المطاف."; /* For the Bolus View pop-up */ -"Result" = "Result"; +"Result" = "النتيجة"; /* For the Bolus View pop-up */ -"Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@"; +"Your entered amount was limited by your max Bolus setting of %d%@" = "تم تحديد المبلغ الذي أدخلته من خلال إعدادتك القصوى Bols %d%@"; /* Bolus View Continue Button */ "Continue" = "Continue"; /* Home title */ -"Home" = "Home"; +"Home" = "الرئيسية"; /* Looping in progress */ "looping" = "looping"; /* min ago since last loop */ -"min ago" = "min ago"; +"min ago" = "قبل دقيقة"; /* Status Title */ -"No suggestion" = "No suggestion"; +"No suggestion" = "لا اقتراحات"; /* Replace pod text in Header */ -"Replace pod" = "Replace pod"; +"Replace pod" = "استبدال البود"; /* Add carbs screen */ -"Add Carbs" = "Add Carbs"; +"Add Carbs" = "إضافة كربونات"; /* Add carbs header and button in Watch app. You can skip the last " " space. It's just for differentiation */ -"Add Carbs " = "Add Carbs "; +"Add Carbs " = "إضافة كربونات "; /* */ -"Amount Carbs" = "Amount Carbs"; +"Amount Carbs" = "كمية الكاربوهيدرات"; /* Grams unit */ -"grams" = "grams"; +"grams" = "جرام"; /* */ -"Carbs required" = "Carbs required"; +"Carbs required" = "الكاربوهيدرات المطلوبة"; /* Saved Food Presets */ -"Saved Food" = "Saved Food"; +"Saved Food" = "الوجبات المحفوظة"; /* */ -"Are you sure?" = "Are you sure?"; +"Are you sure?" = "هل أنت متأكد؟"; /* Bottom target temp */ -"Bottom target" = "Bottom target"; +"Bottom target" = "الهدف السفلي"; /* Cancel preset name */ -"Cancel" = "Cancel"; +"Cancel" = "إلغاء"; /* */ -"Cancel Temp Target" = "Cancel Temp Target"; +"Cancel Temp Target" = "إلغاء الهدف المؤقت"; /* Custom temp target */ -"Custom" = "Custom"; +"Custom" = "مخصص"; /* */ -"Date" = "Date"; +"Date" = "التاريخ"; /* */ -"Delete" = "Delete"; +"Delete" = "حذف"; /* Delete preset temp target */ -"Delete preset \"%@\"" = "Delete preset \"%@\""; +"Delete preset \"%@\"" = "حذف الإعداد المسبق \"%@\""; /* Duration of target temp or temp basal */ -"Duration" = "Duration"; +"Duration" = "المدة"; /* */ -"Enact Temp Target" = "Enact Temp Target"; +"Enact Temp Target" = "تفعيل الهدف المؤقت"; /* */ -"Target" = "Target"; +"Target" = "الهدف"; /* */ -"Basal Insulin and Sensitivity ratio" = "Basal Insulin and Sensitivity ratio"; +"Basal Insulin and Sensitivity ratio" = "نسبة الإنسولين الأساسي والحساسية"; /* */ -"A lower 'Half Basal Target' setting will reduce the basal and raise the ISF earlier, at a lower target glucose." = "A lower 'Half Basal Target' setting will reduce the basal and raise the ISF earlier, at a lower target glucose."; +"A lower 'Half Basal Target' setting will reduce the basal and raise the ISF earlier, at a lower target glucose." = "إعداد 'نصف الهدف الأساسي' سوف يقلل من القاعدي و يرفع من (حساسية الانسولين) في وقت أسبق، عند مستوى جلكوز مستهدف أقل."; /* */ -" Your setting: " = " Your setting: "; +" Your setting: " = " الإعدادات الخاصة بك: "; /* */ "mg/dl. Autosens.max limits the max endpoint" = "mg/dl. Autosens.max limits the max endpoint"; /* */ -"Enter preset name" = "Enter preset name"; +"Enter preset name" = "أدخل اسم التعيين المسبق"; /* Preset name */ -"Name" = "Name"; +"Name" = "الاسم"; /* minutes of target temp */ -"minutes" = "minutes"; +"minutes" = "دقائق"; /* */ -"Presets" = "Presets"; +"Presets" = "التعيينات المسبقة"; /* Save preset name */ -"Save" = "Save"; +"Save" = "إحفظ"; /* */ -"Save as Preset" = "Save as Preset"; +"Save as Preset" = "حفظ كتعيين مسبق"; /* Delete Meal Preset */ -"Delete Preset" = "Delete Preset"; +"Delete Preset" = "حذف التعيين المسبق"; /* Confirm Deletion */ -"Delete preset '%@'?" = "Delete preset '%@'?"; +"Delete preset '%@'?" = "حذف التعيين المسبق '%@'؟"; /* Button */ -"No" = "No"; +"No" = "لا"; /* Button */ -"Yes" = "Yes"; +"Yes" = "نعم"; /* + Button */ "[ +1 ]" = "[ +1 ]"; @@ -248,119 +252,119 @@ "[ -1 ]" = "[ -1 ]"; /* Upper temp target limit */ -"Top target" = "Top target"; +"Top target" = "الحد الاعلى من الهدف"; /* Temp target set for ... minutes */ -"for" = "for"; +"for" = "لـ"; /* Temp target set for ... minutes */ -"min" = "min"; +"min" = "د"; /* */ -"Autotune" = "Autotune"; +"Autotune" = "ضبط الانسولين التلقائي"; /* */ -"Basal profile" = "Basal profile"; +"Basal profile" = "ملف الانسولين القاعدي"; /* */ -"Carb ratio" = "Carb ratio"; +"Carb ratio" = "معامل الكارب"; /* */ -"Delete autotune data" = "Delete autotune data"; +"Delete autotune data" = "حذف بيانات ضبط الانسولين التلقائي"; /* */ -"Run now" = "Run now"; +"Run now" = "نفذ الأن"; /* */ -"Last run" = "Last run"; +"Last run" = "آخر تنفيذ"; /* */ -"Sensitivity" = "Sensitivity"; +"Sensitivity" = "حساسية"; /* */ "Use Autotune" = "use Autotune"; /* Add profile basal */ -"Add" = "Add"; +"Add" = "أضف"; /* */ -"Basal Profile" = "Basal Profile"; +"Basal Profile" = "ملف الانسولين القاعدي"; /* Rate basal profile */ -"Rate" = "Rate"; +"Rate" = "معدّل"; /* */ -"Save on Pump" = "Save on Pump"; +"Save on Pump" = "الحفظ في المضخة"; /* */ -"Schedule" = "Schedule"; +"Schedule" = "الجدول"; /* */ -"starts at" = "starts at"; +"starts at" = "يبدأ عند"; /* Time basal profile */ -"Time" = "Time"; +"Time" = "الوقت"; /* */ -"Calculated Ratio" = "Calculated Ratio"; +"Calculated Ratio" = "النسبة المحسوبة"; /* Carb Ratios header */ -"Carb Ratios" = "Carb Ratios"; +"Carb Ratios" = "معاملات الكارب"; /* */ -"Ratio" = "Ratio"; +"Ratio" = "النسبة"; /* */ -"Autosens" = "Autosens"; +"Autosens" = "حساسية الانسولين التلقائية"; /* */ -"Calculated Sensitivity" = "Calculated Sensitivity"; +"Calculated Sensitivity" = "الحساسية المحسوبة"; /* */ -"Insulin Sensitivities" = "Insulin Sensitivities"; +"Insulin Sensitivities" = "حساسية الأنسولين"; /* */ -"Sensitivity Ratio" = "Sensitivity Ratio"; +"Sensitivity Ratio" = "معدل الحساسية"; /* */ -"Dismiss" = "Dismiss"; +"Dismiss" = "تجاهل"; /* */ -"Important message" = "Important message"; +"Important message" = "رسالة مهمة"; /* */ -"Amount" = "Amount"; +"Amount" = "الكمية"; /* */ -"Cancel Temp Basal" = "Cancel Temp Basal"; +"Cancel Temp Basal" = "إلغاء الضخ المؤقت للإنسولين القاعدي"; /* Enact Enact a temp Basal or a temp target */ -"Enact" = "Enact"; +"Enact" = "فعل"; /* Start a temp target or a profile override */ -"Start" = "Start"; +"Start" = "إبدأ"; /* */ -"Manual Temp Basal" = "Manual Temp Basal"; +"Manual Temp Basal" = "إنسولين قاعدي يدوي مؤقت"; /* Allow uploads to different services */ -"Allow uploads" = "Allow uploads"; +"Allow uploads" = "السماح بالتحميل"; /* API secret in NS */ -"API secret" = "API secret"; +"API secret" = "سر API"; /* Connect to NS */ -"Connect" = "Connect"; +"Connect" = "توصيل"; /* Connected to NS */ -"Connected!" = "Connected!"; +"Connected!" = "متصل!"; /* Connecting to NS */ -"Connecting..." = "Connecting..."; +"Connecting..." = "الاتصال جارِ..."; /* */ -"Invalid URL" = "Invalid URL"; +"Invalid URL" = "عنوان URL غير صحيح"; /* */ "Local glucose source" = "Local glucose source"; @@ -372,7 +376,7 @@ Enact a temp Basal or a temp target */ "Port" = "Port"; /* */ -"URL" = "URL"; +"URL" = "عنوان الرابط"; /**/ "Use local glucose server" = "Use local glucose server"; @@ -381,7 +385,7 @@ Enact a temp Basal or a temp target */ "This enables uploading of statistics.json to Nightscout, which can be used by the Community Statistics and Demographics Project.\n\nParticipation in Community Statistics is opt-in, and requires separate registration at:\n" = "This enables uploading of statistics.json to Nightscout, which can be used by the Community Statistics and Demographics Project.\n\nParticipation in Community Statistics is opt-in, and requires separate registration at:\n"; /* */ -"Edit settings json" = "Edit settings json"; +"Edit settings json" = "تحرير إعدادات json"; /* */ "Glucose units" = "Glucose units"; @@ -476,6 +480,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pump Settings"; @@ -507,163 +514,163 @@ Enact a temp Basal or a temp target */ " g" = " g"; /* The short unit display string for grams */ -"g" = "g"; +"g" = "ج"; /* when 0 U/hr */ -"0 U/hr" = "0 U/hr"; +"0 U/hr" = "0 وحدة/ساعة"; /* abbreviation for days */ -"d" = "d"; +"d" = "يوم"; /* abbreviation for hours */ -"h" = "h"; +"h" = "س"; /* abbreviation for minutes */ -"m" = "m"; +"m" = "د"; /* */ -"Closed loop" = "Closed loop"; +"Closed loop" = "حلقة مغلقة"; /* */ -"Configuration" = "Configuration"; +"Configuration" = "ضبط"; /* */ -"Devices" = "Devices"; +"Devices" = "الأجهزة"; /* */ -"Pump" = "Pump"; +"Pump" = "المضخة"; /* */ -"Watch" = "Watch"; +"Watch" = "ساعه"; /* */ -"Watch Configuration" = "Watch Configuration"; +"Watch Configuration" = "اعدادات الساعة"; /* */ -"Apple Watch" = "Apple Watch"; +"Apple Watch" = "ساعة أبل"; /* */ -"Display on Watch" = "Display on Watch"; +"Display on Watch" = "عرض على الساعة"; /* */ -"Garmin Watch" = "Garmin Watch"; +"Garmin Watch" = "ساعة جارمين"; /* */ -"Add devices" = "Add devices"; +"Add devices" = "إضافة أجهزة"; /* */ -"Glucose Target" = "Glucose Target"; +"Glucose Target" = "هدف الجلوكوز"; /* */ -"Heart Rate" = "Heart Rate"; +"Heart Rate" = "معدل ضربات القلب"; /* */ -"Steps" = "Steps"; +"Steps" = "الخطوات"; /* */ -"ISF" = "ISF"; +"ISF" = "عامل حساسية الأنسولين"; /* */ "The app Garmin Connect must be installed to use for iAPS.\n Go to App Store to download it" = "The app Garmin Connect must be installed to use for iAPS.\n Go to App Store to download it"; /* */ -"Garmin is not available" = "Garmin is not available"; +"Garmin is not available" = "جارمين غير متاح"; /* */ -"Services" = "Services"; +"Services" = "الخدمات"; /* */ -"Settings" = "Settings"; +"Settings" = "الإعدادات"; /* Recommendation for a Manual Bolus */ -"Recommended Bolus Percentage" = "Recommended Bolus Percentage"; +"Recommended Bolus Percentage" = "النسبة المئوية للبولوس الموصى بها"; /* 2 log files to share */ -"Share logs" = "Share logs"; +"Share logs" = "شارك السجلات"; /* Upper target */ -"High target" = "High target"; +"High target" = "هدف مرتفع"; /* Lower target */ -"Low target" = "Low target"; +"Low target" = "هدف منخفض"; /* When bolusing */ -"Bolusing" = "Bolusing"; +"Bolusing" = "يضخ"; /* */ -"Pump suspended" = "Pump suspended"; +"Pump suspended" = "المضخة معلقة"; /* */ -"Middleware" = "Middleware"; +"Middleware" = "الوسطاء"; /* Header */ -"History" = "History"; +"History" = "السّجل"; /* Nightscout option */ -"Upload" = "Upload"; +"Upload" = "رفع"; /* Nightscout option */ -"Allow Uploads" = "Allow Uploads"; +"Allow Uploads" = "السماح بالرفع"; /* Type of CGM or glucose source */ -"Type" = "Type"; +"Type" = "النّوع"; /* CGM */ -"CGM" = "CGM"; +"CGM" = "مستشعر الجلوكوز"; /* CGM Transmitter ID */ -"Transmitter ID" = "Transmitter ID"; +"Transmitter ID" = "معرف المرسل"; /* Other CGM setting */ -"Other" = "Other"; +"Other" = "أخرى"; /* Whatch app alert */ -"Set temp targets presets on iPhone first" = "Set temp targets presets on iPhone first"; +"Set temp targets presets on iPhone first" = "تعيين أهداف المؤقتة مسبقاً على الايفون أولاً"; /* Updating Watch app */ -"Updating..." = "Updating..."; +"Updating..." = "جارٍ التحديث..."; /* Header for Temp targets in Watch app */ -"Temp Targets" = "Temp Targets"; +"Temp Targets" = "الأهداف المؤقتة"; /* Delete carbs from data table and Nightscout */ -"Delete Carbs?" = "Delete Carbs?"; +"Delete Carbs?" = "حذف النشويات؟"; /* Delete insulin from pump history and Nightscout */ -"Delete Insulin?" = "Delete Insulin?"; +"Delete Insulin?" = "حذف الإنسولين؟"; /* Treatments list */ -"Treatments" = "Treatments"; +"Treatments" = "العلاجات"; /* " min" in Treatments list */ -" min" = " min"; +" min" = " د"; /* */ -"Unable to change anything" = "Unable to change anything"; +"Unable to change anything" = "غير قادر على تغيير أي شيء"; /* Calendar and Libre transmitter settings --------------- */ /* */ -"Configure Libre Transmitter" = "Configure Libre Transmitter"; +"Configure Libre Transmitter" = "ضبط مرسل ليبري"; /* */ -"Calibrations" = "Calibrations"; +"Calibrations" = "المعايرة"; /* */ -"Create Events in Calendar" = "Create Events in Calendar"; +"Create Events in Calendar" = "إنشاء أحداث في التقويم"; /* */ -"Calendar" = "Calendar"; +"Calendar" = "التقويم"; /* Automatic delivered treatments */ -"Automatic" = "Automatic"; +"Automatic" = "تلقائي"; /* External insulin treatments */ "External" = "External"; /* */ -"Other" = "Other"; +"Other" = "أخرى"; /* */ "Libre Transmitter" = "Libre Transmitter"; @@ -810,154 +817,154 @@ Enact a temp Basal or a temp target */ "Detected sensor seems not to be a libre 1 sensor!" = "Detected sensor seems not to be a libre 1 sensor!"; /* Detected sensor is invalid: %@ */ -"Detected sensor is invalid: %@" = "Detected sensor is invalid: %@"; +"Detected sensor is invalid: %@" = "المستشعر المكتشف غير صالح: %@"; /* Low Battery */ -"Low battery" = "Low battery"; +"Low battery" = "البطارية منخفضة"; /* */ -"Invalid sensor" = "Invalid sensor"; +"Invalid sensor" = "مستشعر غير صالح"; /* */ -"Sensor change" = "Sensor change"; +"Sensor change" = "تغيير جهاز الاستشعار"; /* */ -"Sensor expires soon" = "Sensor expires soon"; +"Sensor expires soon" = "ستنتهي صلاحية المستشعر قريبا"; /* 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" = "البطارية منخفضه %@، إشحن جهازك %@ في أقرب وقت ممكن"; /* Extracting calibrationdata from sensor */ -"Extracting calibrationdata from sensor" = "Extracting calibrationdata from sensor"; +"Extracting calibrationdata from sensor" = "جاري إستخراج بيانات المعايرة من المستشعر"; /* Sensor Ending Soon */ -"Sensor Ending Soon" = "Sensor Ending Soon"; +"Sensor Ending Soon" = "المستشعر سينتهي قريباً"; /* 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 %@" = "المستشعر الحالي سينتهي قريباً! عمر المستشعر المتبقي %@"; /* */ -"Libre Bluetooth" = "Libre Bluetooth"; +"Libre Bluetooth" = "بلوتوث ليبري"; /* */ -"Snooze Alerts" = "Snooze Alerts"; +"Snooze Alerts" = "إغفاء التنبيهات"; /* */ -"Last measurement" = "Last measurement"; +"Last measurement" = "القياس الأخير"; /* */ "Sensor Footer checksum" = "Sensor Footer checksum"; /* */ -"Last Blood Sugar prediction" = "Last Blood Sugar prediction"; +"Last Blood Sugar prediction" = "آخر تنبؤ بمستوى الجلوكوز في الدم"; /* */ -"CurrentBG" = "CurrentBG"; +"CurrentBG" = "معدل الجلوكوز الحالي"; /* */ -"Sensor Info" = "Sensor Info"; +"Sensor Info" = "معلومات المستشعر"; /* */ -"Sensor Age" = "Sensor Age"; +"Sensor Age" = "عمر المستشعر"; /* */ -"Sensor Age Left" = "Sensor Age Left"; +"Sensor Age Left" = "عمر المستشعر المتبقي"; /* */ -"Sensor Endtime" = "Sensor Endtime"; +"Sensor Endtime" = "وقت انتهاء المستشعر"; /* */ -"Sensor State" = "Sensor State"; +"Sensor State" = "حالة المستشعر"; /* */ -"Sensor Serial" = "Sensor Serial"; +"Sensor Serial" = "الرقم التسلسلي للمستشعر"; /* */ -"Transmitter Info" = "Transmitter Info"; +"Transmitter Info" = "معلومات المُرسل"; /* */ -"Hardware" = "Hardware"; +"Hardware" = "العتاد"; /* */ -"Firmware" = "Firmware"; +"Firmware" = "البرنامج الثابت"; /* */ -"Connection State" = "Connection State"; +"Connection State" = "حالة الإتصال"; /* */ -"Transmitter Type" = "Transmitter Type"; +"Transmitter Type" = "نوع جهاز الإرسال"; /* */ -"Sensor Type" = "Sensor Type"; +"Sensor Type" = "نوع المستشعر"; /* */ -"Factory Calibration Parameters" = "Factory Calibration Parameters"; +"Factory Calibration Parameters" = "عوامل معايرة المصنع"; /* */ -"Valid for footer" = "Valid for footer"; +"Valid for footer" = "صالح لتذييل الصفحة"; /* */ -"Edit calibrations" = "Edit calibrations"; +"Edit calibrations" = "تعديل المعايرات"; /* */ "edit calibration clicked" = "edit calibration clicked"; /* */ -"Delete CGM" = "Delete CGM"; +"Delete CGM" = "حذف المستشعر"; /* */ -"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?" = "هل أنت متأكد من أنك تريد إزالة هذا المستشعر من لووب؟"; /* */ -"There is no undo" = "There is no undo"; +"There is no undo" = "لا يوجد تراجع!"; /* */ -"Advanced" = "Advanced"; +"Advanced" = "متقدم"; /* */ -"Alarms" = "Alarms"; +"Alarms" = "التنبيهات"; /* */ -"Glucose Settings" = "Glucose Settings"; +"Glucose Settings" = "إعدادات الجلوكوز"; /* */ -"Notifications" = "Notifications"; +"Notifications" = "الإشعارات"; /* */ -"Export logs" = "Export logs"; +"Export logs" = "تصدير السجلات"; /* */ -"Export not available" = "Export not available"; +"Export not available" = "التصدير غير متوفر"; /* */ -"Log export requires ios 15" = "Log export requires ios 15"; +"Log export requires ios 15" = "تصدير السجل يتطلب iOS 15"; /* */ -"Got it!" = "Got it!"; +"Got it!" = "فهمت ذلك!"; /* */ -"Saved to %@" = "Saved to %@"; +"Saved to %@" = "تم الحفظ في %@"; /* */ -"No logs available" = "No logs available"; +"No logs available" = "لا توجد سجلات متاحة"; /* */ -"Glucose Notification visibility" = "Glucose Notification visibility"; +"Glucose Notification visibility" = "وضوح إشعار مستوى الجلوكوز"; /* */ -"Always Notify Glucose" = "Always Notify Glucose"; +"Always Notify Glucose" = "الاشعار بمستوى الجلوكوز دائمًا"; /* */ -"Notify per reading" = "Notify per reading"; +"Notify per reading" = "إشعار لكل قراءة"; /* */ -"Value" = "Value"; +"Value" = "القيمة"; /* */ -"Adds Phone Battery" = "Adds Phone Battery"; +"Adds Phone Battery" = "إضافة بطارية الهاتف"; /* */ -"Adds Transmitter Battery" = "Adds Transmitter Battery"; +"Adds Transmitter Battery" = "إضافة بطارية جهاز الإرسال"; /* */ "Also vibrate" = "Also vibrate"; @@ -1104,16 +1111,16 @@ Enact a temp Basal or a temp target */ "Sensor not found" = "Sensor not found"; /* */ -"Also play alert sound" = "Also play alert sound"; +"Also play alert sound" = "تشغيل التنبيه الصوتي أيضا"; /* */ -"Notification Settings" = "Notification Settings"; +"Notification Settings" = "إعدادات الإشعارات"; /* */ -"Found devices: %d" = "Found devices: %d"; +"Found devices: %d" = "الأجهزة التي تم العثور عليها: %d"; /* */ -"Backfill options" = "Backfill options"; +"Backfill options" = "خيارات الإملاء العكسية"; /* */ "Backfilling from trend is currently not well supported by Loop" = "Backfilling from trend is currently not well supported by Loop"; @@ -1243,7 +1250,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1269,7 +1276,7 @@ Enact a temp Basal or a temp target */ "Save as your Normal Basal Rates" = "Save as your Normal Basal Rates"; /* */ -"Save on Pump" = "Save on Pump"; +"Save on Pump" = "الحفظ في المضخة"; /* Debug option view Pump History */ "Pump History" = "Pump History"; @@ -1299,7 +1306,7 @@ Enact a temp Basal or a temp target */ "Enacted announcements" = "Enacted announcements"; /* Debug option view Autotune */ -"Autotune" = "Autotune"; +"Autotune" = "ضبط الانسولين التلقائي"; /* Debug option view Target presets */ "Target presets" = "Target presets"; @@ -1467,7 +1474,7 @@ Enact a temp Basal or a temp target */ "Cancel Profile Override" = "Cancel Profile Override"; /* Alert */ -"Cancel Temp Target" = "Cancel Temp Target"; +"Cancel Temp Target" = "إلغاء الهدف المؤقت"; /* Alert */ "Return to Normal?" = "Return to Normal?"; @@ -1530,7 +1537,7 @@ Enact a temp Basal or a temp target */ "Save and continue" = "Save and continue"; /* */ -"Save as Preset" = "Save as Preset"; +"Save as Preset" = "حفظ كتعيين مسبق"; /* */ "Predictions" = "Predictions"; @@ -1636,7 +1643,7 @@ Enact a temp Basal or a temp target */ "Saving..." = "Saving..."; /* button title for saving low reservoir reminder */ -"Save" = "Save"; +"Save" = "إحفظ"; /* Alert title for error when updating confidence reminder preference */ "Failed to update confidence reminder preference." = "Failed to update confidence reminder preference."; @@ -1689,7 +1696,7 @@ Enact a temp Basal or a temp target */ "Change Pod now. Insulin delivery will stop in %1$@ or when no more insulin remains." = "Change Pod now. Insulin delivery will stop in %1$@ or when no more insulin remains."; /* Label text for temporary basal rate summary */ -"Rate" = "Rate"; +"Rate" = "معدّل"; /* Summary string for temporary basal rate configuration page */ "%1$@ for %2$@" = "%1$@ for %2$@"; @@ -1872,7 +1879,7 @@ Enact a temp Basal or a temp target */ "Interval" = "Interval"; /* Median loop interval */ -"Duration" = "Duration"; +"Duration" = "المدة"; /* "Display SD */ "Display SD instead of CV" = "Display SD instead of CV"; @@ -1953,7 +1960,7 @@ Enact a temp Basal or a temp target */ "Man" = "Man"; /* Sex dropdown menu option */ -"Other" = "Other"; +"Other" = "أخرى"; /* Sex dropdown menu option */ "Secret" = "Secret"; @@ -2515,6 +2522,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings index f41e0bf139..73a5692ddb 100644 --- a/FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings @@ -50,7 +50,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Home title */ "Home" = "Home"; diff --git a/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings index 25a2d8e26f..2ee3fa6954 100644 --- a/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Anbefalet"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Fejl ved"; +"Status at" = "Fejl ved"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Måltidsoversigt"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fedt Måltid"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Fuld Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Maks Kulhydrater"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pumpe Indstillinger"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,15 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +<<<<<<< HEAD +======= +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + +>>>>>>> dev /* Setting title */ "Bolus Calculator" = "Bolus Lommeregner"; diff --git a/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings index 6945c414ce..2452c2a48e 100644 --- a/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Vorgeschlagen um"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Fehler um"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Mahlzeitenübersicht"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fettige Mahlzeit"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Behandlung"; + /* For the Bolus View pop-up */ "Full Bolus" = "Voller Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Kohlenhydrate"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Höchstmenge an Kohlenhydraten (g) pro Eintrag"; + /* */ "Pump Settings" = "Pumpeneinstellungen"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Die neue Empfehlung kann nicht angewendet werden, da zurzeit ein Bolus verabreicht wird. Warten Sie bitte die nächste Schleife ab."; /* Pump Error */ -"Error: Pump is Busy." = "Fehler: Die Pumpe ist beschäftigt."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "Geburtsdatum"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nWenn Sie \"Freigeben und Sichern\" aktivieren, werden täglich Sicherungen Ihrer Einstellungen und Statistiken in der Online-Datenbank erstellt.\n\nStellen Sie daher sicher, dass Sie Ihr Wiederherstellungs-Token unten kopieren und speichern. Das Wiederherstellungs-Token wird benötigt, um Ihre Einstellungen auf ein anderes Telefon zu importieren, wenn Sie die Onboarding view verwenden."; /* Section title if sharing off */ "Share Bare Minimum" = "Nur das absolute Minimum teilen"; @@ -1971,7 +1977,7 @@ Enact a temp Basal or a temp target */ "Just iAPS version number" = "Nur iAPS Versionsnummer"; /* Share info text */ -"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token."; +"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Alle Informationen, die Sie freigeben möchten, werden anonym hochgeladen. Um doppelte Uploads zu vermeiden, werden die Daten mit einer eindeutigen, zufälligen Zeichenfolge identifiziert, die auf Ihrem Telefon gespeichert ist."; /* Token section title */ "Your recovery token" = "Ihr Wiederherstellungs-Token"; @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Beim Scrollen nie das kleine Glukosediagramm anzeigen"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Hypo Behandlung deaktivieren"; + +/* UI/UX option */ +"Display Glucose Delta" = "Glukose-Delta anzeigen"; + /* Setting title */ "Bolus Calculator" = "Bolus-Rechner"; diff --git a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings index 391ee8e0e3..d37191360d 100644 --- a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ - "Error at" = "Error at"; + "Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pump Settings"; @@ -1255,7 +1261,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ @@ -2533,6 +2539,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings index c0064fd928..b04354872d 100644 --- a/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Sugerido a las"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error en"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Resumen de la comida"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Comida Grasienta"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Bolo Completo"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Carbohidratos máximos"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Límites de la bomba"; @@ -1244,7 +1250,7 @@ Solamente puedes emparejar una app con el sensor vía bluetooth. A continuación "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "No se ha ejecutado el cálculo recomendado para el nuevo ciclo porque hay un bolo en progreso. Espere al siguiente ciclo del lazo"; /* Pump Error */ -"Error: Pump is Busy." = "Error: bomba ocupada."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2632,6 +2638,12 @@ Un límite Autosens.máx > 1,5 no es recomendable cuando se utiliza la función /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Calculador de bolo"; diff --git a/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings index cd03eaf01e..b743542340 100644 --- a/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Ehdotettu klo"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Virhe klo"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Aterian yhteenveto"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Rasvainen ruoka"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Täysi bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pump Settings"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings index 6f645af641..b8f5be3999 100644 --- a/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggéré à"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Erreur à"; +"Status at" = "État de"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Résumé du repas"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Repas gras"; +/* Bolus option */ +"Hypo Treatment" = "Traitement d'hypoglycémie"; + /* For the Bolus View pop-up */ "Full Bolus" = "Bolus entier"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Glucides max"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Quantité maximale de glucides (g) que vous pouvez ajouter à chaque entrée"; + /* */ "Pump Settings" = "Paramètres de la pompe"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Impossible d'appliquer la nouvelle recommandation, car l'administration d'un bolus est en cours. Attendez la prochaine boucle."; /* Pump Error */ -"Error: Pump is Busy." = "Erreur: La pompe est occupée."; +"Pump is Busy." = "La pompe est occupée."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "Date de naissance"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nSi vous activez \"Partage et Sauvegarde\", des sauvegardes quotidiennes de vos paramètres et statistiques seront effectuées dans la base de données en ligne.\n\nAssurez-vous de copier et d'enregistrer votre jeton de récupération ci-dessous. Le jeton de récupération est requis pour importer vos paramètres sur un autre téléphone lorsque vous utilisez le tutoriel de mise en marche."; /* Section title if sharing off */ "Share Bare Minimum" = "Partager le minimum"; @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Ne jamais afficher le petit graphique de glucose lors du défilement"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Désactiver les traitements d'hypoglycémie"; + +/* UI/UX option */ +"Display Glucose Delta" = "Afficher l'écart de glycémie"; + /* Setting title */ "Bolus Calculator" = "Calculateur de Bolus"; diff --git a/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings index e055b06a60..768904eeb1 100644 --- a/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pump Settings"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings index 4152c9430a..ea5777a9e2 100644 --- a/FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Ajánlva"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Hiba"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Étel összegzése"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Zsíros étel"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Teljes bólus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Maximum szénhidrát"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pumpa beállítások"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Soha ne jelenítse meg a kis glükózdiagramot görgetés közben"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings index 5ec32b4c9c..71682db831 100644 --- a/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggerito alle"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Errore a"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Riepilogo Pasto"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Pasto Grasso"; +/* Bolus option */ +"Hypo Treatment" = "Trattamento ipoglicemia"; + /* For the Bolus View pop-up */ "Full Bolus" = "Bolo Completo"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Carboidrati massimi"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Quantità massima di carboidrati (g) che è possibile aggiungere alla volta"; + /* */ "Pump Settings" = "Microinfusore"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Non è possibile attuare un nuovo ciclo loop, perché un bolo è in corso. Attendere il ciclo loop successivo"; /* Pump Error */ -"Error: Pump is Busy." = "Errore: la pompa sta già lavorando."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1935,16 +1941,16 @@ Enact a temp Basal or a temp target */ "Sharing" = "Condivisione"; /* Share and Backup page header */ -"Share and Backup" = "Share and Backup"; +"Share and Backup" = "Condivisione e backup"; /* Section 1 title */ -"Upload settings and statistics" = "Upload settings and statistics"; +"Upload settings and statistics" = "Carica le impostazioni e le statistiche"; /* On-off toggle */ -"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; +"Share and Backup all of your Settings and Statistics" = "Condividi e salva tutte le tue impostazioni e statistiche"; /* Title of dropdown menu */ -"Sex" = "Sex"; +"Sex" = "Genere"; /* Sex dropdown menu option */ "Woman" = "Donna"; @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "Data di nascita"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nSe abiliti \"Condividi e Backup\" i backup giornalieri delle tue impostazioni e delle tue statistiche saranno effettuati sul database online.\n\nAssicurati di copiare e salvare il tuo token di recupero qui sotto. Il token di recupero è necessario per importare le tue impostazioni su un altro telefono quando si utilizza la vista onboarding."; /* Section title if sharing off */ "Share Bare Minimum" = "Condividi Minimo Nullo"; @@ -1971,19 +1977,19 @@ Enact a temp Basal or a temp target */ "Just iAPS version number" = "Solo il numero di versione iAPS"; /* Share info text */ -"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token."; +"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Ogni bit di informazione che si sceglie di condividere viene caricato in forma anonima. Per evitare duplicati caricamenti, i dati vengono identificati con una stringa casuale univoca salvata sul telefono, il token di recupero."; /* Token section title */ -"Your recovery token" = "Your recovery token"; +"Your recovery token" = "Il tuo token di recupero"; /* Token display button */ "Tap to display" = "Tocca per visualizzare"; /* Token copy button */ -"Long press to copy" = "Long press to copy"; +"Long press to copy" = "Premi a lungo per copiare"; /* Link to statistics button */ -"View Personal Statistics" = "View Personal Statistics"; +"View Personal Statistics" = "Mostra Statistiche Personali"; /* */ "Your identifier" = "Il tuo identificativo"; @@ -2516,6 +2522,12 @@ Per gli utenti più giovani si consiglia di iniziare con un dosaggio ancora più /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Non visualizzare mai il grafico piccolo del glucosio durante lo scorrimento"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disabilita trattamenti Ipoglicemie"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Calcolatore Bolo"; diff --git a/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings index e3f454b286..e1e28545a9 100644 --- a/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Foreslått kl."; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Feil kl."; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Oppsummering av måltid"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fett måltid"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Maksimalt antall karbohydrater"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pumpeinnstillinger"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Boluskalkulator"; diff --git a/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings index b60f7ab111..79aef53abc 100644 --- a/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Voorgesteld op"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Foutmelding op"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Maaltijd overzicht"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Vette maaltijd"; +/* Bolus option */ +"Hypo Treatment" = "Hypobehandeling"; + /* For the Bolus View pop-up */ "Full Bolus" = "Volledige bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max koolhydraten"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximale hoeveelheid koolhydraten (g) die je elke invoer kunt toevoegen"; + /* */ "Pump Settings" = "Pomp instellingen"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Kan het nieuwe loopcyclus advies niet uitvoeren, omdat er een bolus bezig is. Wacht op de volgende cyclus"; /* Pump Error */ -"Error: Pump is Busy." = "Fout: Pomp is bezig."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1935,16 +1941,16 @@ Enact a temp Basal or a temp target */ "Sharing" = "Delen van statistieken"; /* Share and Backup page header */ -"Share and Backup" = "Share and Backup"; +"Share and Backup" = "Delen en back-up"; /* Section 1 title */ -"Upload settings and statistics" = "Upload settings and statistics"; +"Upload settings and statistics" = "Upload instellingen en statistieken"; /* On-off toggle */ -"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; +"Share and Backup all of your Settings and Statistics" = "Deel en back-up al uw instellingen en statistieken"; /* Title of dropdown menu */ -"Sex" = "Sex"; +"Sex" = "Geslacht"; /* Sex dropdown menu option */ "Woman" = "Vrouw"; @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "Geboortedatum"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nAls u \"Delen en back-uppen\" inschakelt, worden er dagelijks back-ups van uw instellingen en statistieken gemaakt in een online database.\n\nZorg ervoor dat u uw hersteltoken hieronder kopieert en opslaat. Het hersteltoken is nodig om uw instellingen te importeren naar een andere telefoon wanneer u de onboarding-weergave gebruikt."; /* Section title if sharing off */ "Share Bare Minimum" = "Deel alleen het minimale"; @@ -1971,19 +1977,19 @@ Enact a temp Basal or a temp target */ "Just iAPS version number" = "Alleen iAPS versienummer"; /* Share info text */ -"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token."; +"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Elk stukje informatie dat je wilt delen, wordt anoniem geüpload. Om dubbele uploads te voorkomen, worden de gegevens geïdentificeerd met een unieke willekeurige tekenreeks die op je telefoon is opgeslagen, het hersteltoken."; /* Token section title */ -"Your recovery token" = "Your recovery token"; +"Your recovery token" = "Je herstelcode"; /* Token display button */ "Tap to display" = "Tik om weer te geven"; /* Token copy button */ -"Long press to copy" = "Long press to copy"; +"Long press to copy" = "Lang ingedrukt houden om te kopiëren"; /* Link to statistics button */ -"View Personal Statistics" = "View Personal Statistics"; +"View Personal Statistics" = "Persoonlijke statistieken bekijken"; /* */ "Your identifier" = "Jouw identificatie"; @@ -2519,6 +2525,12 @@ Eenvoudig gezegd, de Dynamische Carb Ratio past de koolhydraatverhouding aan op /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Laat de kleine glucosegrafiek nooit zien tijdens het scrollen"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Hypobehandelingen uitschakelen"; + +/* UI/UX option */ +"Display Glucose Delta" = "Glucose delta weergeven"; + /* Setting title */ "Bolus Calculator" = "Bolus calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings index 7d64893723..ca19bbd88b 100644 --- a/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -478,6 +481,9 @@ Połączono z Nightscout!"; /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pump Settings"; @@ -1245,7 +1251,7 @@ Połączono z Nightscout!"; "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2517,6 +2523,12 @@ Połączono z Nightscout!"; /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings index ced78bc51d..ab970c0880 100644 --- a/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Ajustes Bomba"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings index a00bbc4143..975d77bb16 100644 --- a/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Ajustes Bomba"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings index 04258d9310..29f779a168 100644 --- a/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Предложено в"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Ошибка в"; +"Status at" = "Статус в"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Итог питания"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Жирная пища"; +/* Bolus option */ +"Hypo Treatment" = "Гипогликемия"; + /* For the Bolus View pop-up */ "Full Bolus" = "Полный болюс"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Максимум углеводов"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Максимум углеводов (г), который можно внести в запись"; + /* */ "Pump Settings" = "Настройки помпы"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Не удается выполнить рекомендацию по новому циклу петли, так как вводится болюс. Подождите новый цикл петли"; /* Pump Error */ -"Error: Pump is Busy." = "Ошибка: Помпа занята."; +"Pump is Busy." = "Помпа занята."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "День рождения"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nЕсли включить \"Общий доступ и резервное копирование\", то ежедневные резервные копии ваших настроек и статистики будут сохраняться в онлайн-базе данных.\n\nОбязательно скопируйте и сохраните свой токен восстановления, указанный ниже. Токен восстановления необходим для импорта настроек на другой телефон при использовании встроенного режима просмотра."; /* Section title if sharing off */ "Share Bare Minimum" = "Минимально делиться"; @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Не отображать график уровня глюкозы при прокрутке"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Отключить гипогликемии"; + +/* UI/UX option */ +"Display Glucose Delta" = "Отображать дельту глюкозы"; + /* Setting title */ "Bolus Calculator" = "Калькулятор болюса"; diff --git a/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings index 14fc01b649..07c354e57d 100644 --- a/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Navrhnuté o"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Chyba o"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Sumár jedla"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Mastné jedlo"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Celková dávka"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Maximum sacharidov"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Nastavenia pumpy"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Kalkulátor bolusu"; diff --git a/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings index 2d91a3d5f8..f78b6dc1df 100644 --- a/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Föreslaget vid"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Fel vid"; +"Status at" = "Status kl"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Måltidssammanfattning"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fet mat"; +/* Bolus option */ +"Hypo Treatment" = "Behandling av lågt blodsocker"; + /* For the Bolus View pop-up */ "Full Bolus" = "Total mängd"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max antal kolhydrater"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximal antal kolhydrater (g) du kan lägga till varje måltid"; + /* */ "Pump Settings" = "Pumpinställningar"; @@ -1255,7 +1261,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Kan inte utföra ny loop-rekommendation just nu, pga en pågående bolus. Vänta till nästa loop"; /* Pump Error */ -"Error: Pump is Busy." = "Fel: pump är upptagen."; +"Pump is Busy." = "Pump är upptagen."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2365,7 +2371,7 @@ Enact a temp Basal or a temp target */ "Minimum duration in minutes for new SMB since last SMB or manual bolus" = "Minsta tid i minuter för ny automatisk bolus sedan senaste bolus eller automatisk bolus"; /* Headline "Bolus Increment" */ -"Bolus Increment" = "Minsta bolusmångd"; +"Bolus Increment" = "Minsta bolusmängd"; /* "Bolus Increment" */ "Smallest enacted SMB amount. Minimum amount for Omnipod pumps is 0.05 U, whereas for Medtronic pumps it differs for various models, from 0.025 U to 0.10 U. Please check the minimum bolus amount which can be delivered by your pump. The default value is 0.1." = "Minsta mängd för Omnipod-poddar är 0,05 enheter, medan det för Medtronic-pumpar skiljer sig åt för olika modeller, från 0.25 IE till 0,10 IE. Kontrollera den minsta bolusmängd som kan ges av din pump för denna inställning. Standardvärdet är 0,1 enheter."; @@ -2522,11 +2528,17 @@ Enact a temp Basal or a temp target */ "Horizontal Scroll View Visible hours" = "Antal synliga timmar i hemfönstret"; /* UI/UX option */ -"Display Time Interval Setting Button" = "Visa knapp för tidsintervell"; +"Display Time Interval Setting Button" = "Visa knapp för tidsintervall"; /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Dölj det lilla glukosdiagrammet när du scrollar"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Visa inte behandling av lågt blodsocker"; + +/* UI/UX option */ +"Display Glucose Delta" = "Visa exakt glukosförändring"; + /* Setting title */ "Bolus Calculator" = "Boluskalkylator"; diff --git a/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings index a771697a70..b2521fa3b5 100644 --- a/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "Pompa Ayarları"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings index a711c77358..8428dad542 100644 --- a/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Запропоновано на"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Помилка в"; +"Status at" = "Статус в"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Резюме харчування"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Жирна їжа"; +/* Bolus option */ +"Hypo Treatment" = "Гіпотерапія"; + /* For the Bolus View pop-up */ "Full Bolus" = "Повний болюс"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Максимум вуглеводів"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Максимальна кількість вуглеводів (г), яку можна додати для кожного запису"; + /* */ "Pump Settings" = "Налаштування Помпи"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Неможливо виконати рекомендацію щодо нового циклу петлі, оскільки триває болюс. Зачекайте наступного циклу петлі"; /* Pump Error */ -"Error: Pump is Busy." = "Помилка: Помпа зайнята."; +"Pump is Busy." = "Помпа зайнята"; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "Дата народження"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nЯкщо ви ввімкнете «Поділитися та створити резервну копію», щоденні резервні копії ваших налаштувань і статистики будуть створені в онлайн-базі даних.\n\nОбов’язково скопіюйте та збережіть токен відновлення нижче. Токен відновлення потрібен для імпорту ваших налаштувань на інший телефон під час перегляду адаптації."; /* Section title if sharing off */ "Share Bare Minimum" = "Мінімум розподілу"; @@ -2515,6 +2521,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Ніколи не відображайте маленьку діаграму рівня глюкози під час прокручування"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Вимкнути гіпотерапію"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Калькулятор Болюса"; diff --git a/FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings index b69fb59dc6..99fec2fa5c 100644 --- a/FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Đề xuất ở"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Lỗi ở"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Tóm tắt bữa ăn"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Bữa ăn nhiều chất béo"; +/* Bolus option */ +"Hypo Treatment" = "Chống hạ đường huyết"; + /* For the Bolus View pop-up */ "Full Bolus" = "Liều bolus đầy đủ"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Khối lượng carbs tối đa"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Lượng carbs tối đa (g) bạn có thể thêm vào mỗi mục nhập"; + /* */ "Pump Settings" = "Cấu hình bơm"; @@ -510,7 +516,7 @@ Enact a temp Basal or a temp target */ "g" = "g"; /* when 0 U/hr */ -"0 U/hr" = "U/hr"; +"0 U/hr" = "0 U/hr"; /* abbreviation for days */ "d" = "d"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Không thể thực hiện đề xuất chu kỳ vòng lặp mới vì đang tiến hành một đợt Bolus. Đợi chu kỳ vòng lặp tiếp theo"; /* Pump Error */ -"Error: Pump is Busy." = "Lỗi: Máy bơm đang bận."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -1556,7 +1562,7 @@ Enact a temp Basal or a temp target */ "\n\nTap 'Add' to continue with selected amount." = "\n\nNhấn 'Thêm' để tiếp tục với số lượng đã chọn."; /* */ -"Eventual Glucose" = "Glucose hiện tại"; +"Eventual Glucose" = "Eventual Glucose"; /* */ "Please wait" = "Vui lòng chờ"; @@ -1586,25 +1592,25 @@ Enact a temp Basal or a temp target */ "Hide" = "Ẩn"; /* Bolus pop-up / Alert string. Make translations concise! */ -"Eventual Glucose > Target Glucose, but glucose is predicted to drop down to " = "Glucose hiện tại lớn hơn Glucose mục tiêu, nhưng glucose được dự đoán trước tiên sẽ giảm xuống "; +"Eventual Glucose > Target Glucose, but glucose is predicted to drop down to " = "Eventual Glucose > Target Glucose, nhưng glucose được dự đoán trước tiên sẽ giảm xuống "; /* Bolus pop-up / Alert string. Make translations concise! */ "which is below your Threshold (" = "nằm dưới Ngưỡng của bạn ("; /* Bolus pop-up / Alert string. Make translations concise! */ -"Eventual Glucose > Target Glucose, but glucose is climbing slower than expected. Expected: " = "Glucose hiện tại lớn hơn Glucose mục tiêu, nhưng Glucose tăng chậm hơn dự kiến. Hy vọng: "; +"Eventual Glucose > Target Glucose, but glucose is climbing slower than expected. Expected: " = "Eventual Glucose > Target Glucose, nhưng Glucose tăng chậm hơn dự kiến. Hy vọng: "; //* Bolus pop-up / Alert string. Make translations concise! */ ". Climbing: " = ". Đang tăng lên: "; /* Bolus pop-up / Alert string. Make translations concise! */ -"Eventual Glucose > Target Glucose, but glucose is falling faster than expected. Expected: " = "Glucose hiện tại lớn hơn Glucose mục tiêu, nhưng glucose đang giảm nhanh hơn dự kiến. Hy vọng: "; +"Eventual Glucose > Target Glucose, but glucose is falling faster than expected. Expected: " = "Eventual Glucose > Target Glucose, nhưng glucose đang giảm nhanh hơn dự kiến. Hy vọng: "; /* Bolus pop-up / Alert string. Make translations concise! */ ". Falling: " = ". Đang giảm xuống: "; /* Bolus pop-up / Alert string. Make translations concise! */ -"Eventual Glucose > Target Glucose, but glucose is changing faster than expected. Expected: " = "Glucose hiện tại lớn hơn Glucose mục tiêu, nhưng glucose đang giảm nhanh hơn dự kiến. Hy vọng: "; +"Eventual Glucose > Target Glucose, but glucose is changing faster than expected. Expected: " = "Eventual Glucose > Target Glucose, nhưng glucose đang giảm nhanh hơn dự kiến. Hy vọng: "; /* Bolus pop-up / Alert string. Make translations concise! */ ". Changing: " = ". Đang thay đổi: "; @@ -1935,16 +1941,16 @@ Enact a temp Basal or a temp target */ "Sharing" = "Chia sẻ"; /* Share and Backup page header */ -"Share and Backup" = "Share and Backup"; +"Share and Backup" = "Chia sẻ và sao lưu"; /* Section 1 title */ -"Upload settings and statistics" = "Upload settings and statistics"; +"Upload settings and statistics" = "Tải lên cài đặt và số liệu thống kê"; /* On-off toggle */ -"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; +"Share and Backup all of your Settings and Statistics" = "Chia sẻ và sao lưu tất cả các thiết lập và số liệu thống kê của bạn"; /* Title of dropdown menu */ -"Sex" = "Sex"; +"Sex" = "Giới tính"; /* Sex dropdown menu option */ "Woman" = "Nữ"; @@ -1962,7 +1968,7 @@ Enact a temp Basal or a temp target */ "Birth Date" = "Ngày sinh"; /* Share and Backup info text */ -"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view."; +"\nIf you enable \"Share and Backup\" daily backups of your settings and statistics will be made to online database.\n\nMake sure to copy and save your recovery token below. The recovery token is required to import your settings to another phone when using the onboarding view." = "\nNếu bạn bật \"Chia sẻ và Sao lưu\", các bản sao lưu hàng ngày của cài đặt và số liệu thống kê của bạn sẽ được thực hiện vào cơ sở dữ liệu trực tuyến.\n\nHãy đảm bảo sao chép và lưu mã thông báo khôi phục của bạn bên dưới. Mã thông báo khôi phục là bắt buộc để nhập cài đặt của bạn vào điện thoại khác khi sử dụng chế độ xem tích hợp."; /* Section title if sharing off */ "Share Bare Minimum" = "Chia sẻ ở mức tối thiểu"; @@ -1971,19 +1977,19 @@ Enact a temp Basal or a temp target */ "Just iAPS version number" = "Chỉ phiên bản iAPS"; /* Share info text */ -"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token."; +"Every bit of information you choose to share is uploaded anonymously. To prevent duplicate uploads, the data is identified with a unique random string saved on your phone, the recovery token." = "Mọi thông tin bạn chọn chia sẻ đều được tải lên ẩn danh. Để ngăn chặn việc tải lên trùng lặp, dữ liệu được xác định bằng một chuỗi ngẫu nhiên duy nhất được lưu trên điện thoại của bạn, mã thông báo khôi phục."; /* Token section title */ -"Your recovery token" = "Your recovery token"; +"Your recovery token" = "Mã thông báo khôi phục của bạn"; /* Token display button */ "Tap to display" = "Chạm để hiển thị"; /* Token copy button */ -"Long press to copy" = "Long press to copy"; +"Long press to copy" = "Nhấn và giữ để sao chép"; /* Link to statistics button */ -"View Personal Statistics" = "View Personal Statistics"; +"View Personal Statistics" = "Xem Thống kê cá nhân"; /* */ "Your identifier" = "Mã định danh của bạn"; @@ -2516,6 +2522,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Không bao giờ hiển thị biểu đồ glucose nhỏ khi cuộn"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Vô hiệu hóa điều trị hạ đường huyết"; + +/* UI/UX option */ +"Display Glucose Delta" = "Hiển thị Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Tính toán Bolus"; diff --git a/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings index c2bc5e9730..e3bd18e11c 100644 --- a/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings @@ -92,7 +92,7 @@ "Suggested at" = "Suggested at"; /* Headline in enacted pop up (at: at what time) */ -"Error at" = "Error at"; +"Status at" = "Status at"; /* Bolus View Meal Summary Header */ "Meal Summary" = "Meal Summary"; @@ -112,6 +112,9 @@ /* For the Bolus View pop-up */ "Fatty Meal" = "Fatty Meal"; +/* Bolus option */ +"Hypo Treatment" = "Hypo Treatment"; + /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; @@ -476,6 +479,9 @@ Enact a temp Basal or a temp target */ /* Max setting */ "Max Carbs" = "Max Carbs"; +/* Max carbs description */ +"Maximum amount of carbs (g) you can add each entry" = "Maximum amount of carbs (g) you can add each entry"; + /* */ "Pump Settings" = "泵设置"; @@ -1243,7 +1249,7 @@ Enact a temp Basal or a temp target */ "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle" = "Can't enact the new loop cycle recommendation, because a Bolus is in progress. Wait for next loop cycle"; /* Pump Error */ -"Error: Pump is Busy." = "Error: Pump is Busy."; +"Pump is Busy." = "Pump is Busy."; /* -------------- Developer settings ---------------------- */ /* Debug options */ @@ -2517,6 +2523,12 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; +/* UI/UX option */ +"Disable Hypo Treatments" = "Disable Hypo Treatments"; + +/* UI/UX option */ +"Display Glucose Delta" = "Display Glucose Delta"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Models/Configs.swift b/FreeAPS/Sources/Models/Configs.swift index e9c0760965..80b7ab8060 100644 --- a/FreeAPS/Sources/Models/Configs.swift +++ b/FreeAPS/Sources/Models/Configs.swift @@ -40,6 +40,7 @@ extension Font { static let previewSmall = Font.custom("PreviewSmallFont", size: 14) static let previewNormal = Font.custom("PreviewNormalFont", size: 16) static let previewHeadline = Font.custom("PreviewHeadlineFont", size: 18) + static let previewExtraBig = Font.custom("PreviewHeadlineFont", size: 20) static let extraSmall = Font.custom("ExtraSmallFont", size: 12) static let suggestionHeadline = Font.custom("SuggestionHeadlineFont", fixedSize: 20) @@ -48,6 +49,7 @@ extension Font { static let suggestionSmallParts = Font.custom("SuggestionSmallPartsFont", fixedSize: 16) static let glucoseFont = Font.custom("SuggestionSmallPartsFont", size: 45) + static let glucoseFontMdDl = Font.custom("SuggestionSmallPartsFont", size: 40) static let glucoseSmallFont = Font.custom("SuggestionSmallPartsFont", size: 24) static let bolusProgressStopFont = Font.custom("BolusProgressStop", fixedSize: 24) diff --git a/FreeAPS/Sources/Models/FreeAPSSettings.swift b/FreeAPS/Sources/Models/FreeAPSSettings.swift index b722f1f908..968a47dc82 100644 --- a/FreeAPS/Sources/Models/FreeAPSSettings.swift +++ b/FreeAPS/Sources/Models/FreeAPSSettings.swift @@ -68,6 +68,8 @@ struct FreeAPSSettings: JSON, Equatable { var birthDate = Date.distantPast // var sex: Sex = .secret var sexSetting: Int = 3 + var disableHypoTreatment: Bool = false + var displayDelta: Bool = false } extension FreeAPSSettings: Decodable { @@ -345,6 +347,14 @@ extension FreeAPSSettings: Decodable { settings.sexSetting = sexSetting } + if let disableHypoTreatment = try? container.decode(Bool.self, forKey: .disableHypoTreatment) { + settings.disableHypoTreatment = disableHypoTreatment + } + + if let displayDelta = try? container.decode(Bool.self, forKey: .displayDelta) { + settings.displayDelta = displayDelta + } + self = settings } } diff --git a/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift b/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift index b9aa0b11a2..09a2999754 100644 --- a/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift +++ b/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift @@ -7,6 +7,8 @@ extension AddCarbs { @Injected() var carbsStorage: CarbsStorage! @Injected() var apsManager: APSManager! @Injected() var settings: SettingsManager! + @Injected() var nightscoutManager: NightscoutManager! + @Published var carbs: Decimal = 0 @Published var date = Date() @Published var protein: Decimal = 0 @@ -21,16 +23,20 @@ extension AddCarbs { @Published var id_: String = "" @Published var summary: String = "" @Published var skipBolus: Bool = false + @Published var hypoTreatment: Bool = false + @Published var disableHypoTreatment: Bool = false let now = Date.now let coredataContext = CoreDataStack.shared.persistentContainer.viewContext + let coredataContextBackground = CoreDataStack.shared.persistentContainer.newBackgroundContext() override func subscribe() { carbsRequired = provider.suggestion?.carbsReq maxCarbs = settings.settings.maxCarbs skipBolus = settingsManager.settings.skipBolusScreenAfterCarbs useFPUconversion = settingsManager.settings.useFPUconversion + disableHypoTreatment = settingsManager.settings.disableHypoTreatment } func add(_ continue_: Bool, fetch: Bool) { @@ -54,7 +60,9 @@ extension AddCarbs { )] carbsStorage.storeCarbs(carbsToStore) - if skipBolus, !continue_, !fetch { + if hypoTreatment { hypo() } + + if (skipBolus && !continue_ && !fetch) || hypoTreatment { apsManager.determineBasalSync() showModal(for: nil) } else if carbs > 0 { @@ -207,5 +215,50 @@ extension AddCarbs { print("meals 1: ID: " + (save.id ?? "").description + " FPU ID: " + (save.fpuID ?? "").description) } } + + private func hypo() { + let os = OverrideStorage() + + // Cancel any eventual Other Override already active + if let activeOveride = os.fetchLatestOverride().first { + let presetName = os.isPresetName() + // Is the Override a Preset? + if let preset = presetName { + if let duration = os.cancelProfile() { + // Update in Nightscout + nightscoutManager.editOverride(preset, duration, activeOveride.date ?? Date.now) + } + } else if activeOveride.isPreset { // Because hard coded Hypo treatment isn't actually a preset + if let duration = os.cancelProfile() { + nightscoutManager.editOverride("📉", duration, activeOveride.date ?? Date.now) + } + } else { + let nsString = activeOveride.percentage.formatted() != "100" ? activeOveride.percentage + .formatted() + " %" : "Custom" + if let duration = os.cancelProfile() { + nightscoutManager.editOverride(nsString, duration, activeOveride.date ?? Date.now) + } + } + } + + // Enable New Override + let override = OverridePresets(context: coredataContextBackground) + override.percentage = 90 + override.smbIsOff = true + override.duration = 45 + override.name = "📉" + override.advancedSettings = true + override.target = 117 + override.date = Date.now + override.indefinite = false + + os.overrideFromPreset(override, UUID().uuidString) + // Upload to Nightscout + nightscoutManager.uploadOverride( + "📉", + Double(45), + override.date ?? Date.now + ) + } } } diff --git a/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift b/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift index d3ebed4bfc..dc124685c8 100644 --- a/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift +++ b/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift @@ -75,7 +75,6 @@ extension AddCarbs { // Time HStack { - let now = Date.now Text("Time") Spacer() if !pushed { @@ -111,6 +110,11 @@ extension AddCarbs { .popover(isPresented: $isPromptPresented) { presetPopover } + + // Optional Hypo Treatment + if state.carbs > 0, !state.disableHypoTreatment { + Toggle("Hypo Treatment", isOn: $state.hypoTreatment) + } } Section { @@ -118,7 +122,11 @@ extension AddCarbs { button.toggle() if button { state.add(override, fetch: editMode) } } - label: { Text(((state.skipBolus && !override && !editMode) || state.carbs <= 0) ? "Save" : "Continue") } + label: { + Text( + ((state.skipBolus && !override && !editMode) || state.carbs <= 0 || state.hypoTreatment) ? "Save" : + "Continue" + ) } .disabled(empty) .frame(maxWidth: .infinity, alignment: .center) }.listRowBackground(!empty ? Color(.systemBlue) : Color(.systemGray4)) @@ -128,6 +136,7 @@ extension AddCarbs { mealPresets } } + .compactSectionSpacing() .dynamicTypeSize(...DynamicTypeSize.xxLarge) .onAppear { configureView { diff --git a/FreeAPS/Sources/Modules/Home/HomeStateModel.swift b/FreeAPS/Sources/Modules/Home/HomeStateModel.swift index adb27961c9..e05837b441 100644 --- a/FreeAPS/Sources/Modules/Home/HomeStateModel.swift +++ b/FreeAPS/Sources/Modules/Home/HomeStateModel.swift @@ -81,6 +81,8 @@ extension Home { @Published var maxBolusValue: Decimal = 1 @Published var useInsulinBars: Bool = false @Published var iobData: [IOBData] = [] + @Published var carbData: Decimal = 0 + @Published var iobs: Decimal = 0 @Published var neg: Int = 0 @Published var tddChange: Decimal = 0 @Published var tddAverage: Decimal = 0 @@ -89,6 +91,7 @@ extension Home { @Published var tdd3DaysAgo: Decimal = 0 @Published var tddActualAverage: Decimal = 0 @Published var skipGlucoseChart: Bool = false + @Published var displayDelta: Bool = false let coredataContext = CoreDataStack.shared.persistentContainer.viewContext @@ -141,6 +144,7 @@ extension Home { maxBolus = settingsManager.pumpSettings.maxBolus useInsulinBars = settingsManager.settings.useInsulinBars skipGlucoseChart = settingsManager.settings.skipGlucoseChart + displayDelta = settingsManager.settings.displayDelta broadcaster.register(GlucoseObserver.self, observer: self) broadcaster.register(SuggestionObserver.self, observer: self) @@ -274,6 +278,10 @@ extension Home { // Update in Nightscout nightscoutManager.editOverride(preset, duration, activeOveride.date ?? Date.now) } + } else if activeOveride.isPreset { // Because hard coded Hypo treatment isn't actually a preset + if let duration = os.cancelProfile() { + nightscoutManager.editOverride("📉", duration, activeOveride.date ?? Date.now) + } } else { let nsString = activeOveride.percentage.formatted() != "100" ? activeOveride.percentage .formatted() + " %" : "Custom" @@ -490,6 +498,8 @@ extension Home { guard let self = self else { return } if let data = self.provider.reasons() { self.iobData = data + self.carbData = data.map(\.cob).reduce(0, +) + self.iobs = data.map(\.iob).reduce(0, +) neg = data.filter({ $0.iob < 0 }).count * 5 let tdds = CoreDataStorage().fetchTDD(interval: DateFilter().tenDays) let yesterday = (tdds.first(where: { @@ -592,6 +602,7 @@ extension Home.StateModel: maxBolus = settingsManager.pumpSettings.maxBolus useInsulinBars = settingsManager.settings.useInsulinBars skipGlucoseChart = settingsManager.settings.skipGlucoseChart + displayDelta = settingsManager.settings.displayDelta setupGlucose() setupOverrideHistory() setupData() diff --git a/FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift b/FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift index 2208217a69..0ca6f0a066 100644 --- a/FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift @@ -2,19 +2,13 @@ import SwiftUI struct CurrentGlucoseView: View { @Binding var recentGlucose: BloodGlucose? - @Binding var timerDate: Date @Binding var delta: Int? @Binding var units: GlucoseUnits @Binding var alarm: GlucoseAlarm? @Binding var lowGlucose: Decimal @Binding var highGlucose: Decimal @Binding var alwaysUseColors: Bool - - @State private var rotationDegrees: Double = 0.0 - - enum Config { - static let size: CGFloat = 100 - } + @Binding var displayDelta: Bool @Environment(\.colorScheme) var colorScheme @Environment(\.sizeCategory) private var fontSize @@ -26,17 +20,38 @@ struct CurrentGlucoseView: View { if units == .mmolL { formatter.minimumFractionDigits = 1 formatter.maximumFractionDigits = 1 + formatter.roundingMode = .halfUp } - formatter.roundingMode = .halfUp return formatter } + private var manualGlucoseFormatter: NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 0 + if units == .mmolL { + formatter.minimumFractionDigits = 1 + formatter.maximumFractionDigits = 1 + formatter.roundingMode = .ceiling + } + return formatter + } + + private var decimalString: String { + let formatter = NumberFormatter() + return formatter.decimalSeparator + } + private var deltaFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .decimal + if units == .mmolL { + formatter.minimumIntegerDigits = 0 + formatter.decimalSeparator = "." + } formatter.maximumFractionDigits = 1 - formatter.positivePrefix = " +" - formatter.negativePrefix = " -" + formatter.positivePrefix = "+" + formatter.negativePrefix = "-" return formatter } @@ -55,79 +70,120 @@ struct CurrentGlucoseView: View { } var body: some View { + glucoseView + .dynamicTypeSize(DynamicTypeSize.medium ... DynamicTypeSize.xLarge) + } + + var glucoseView: some View { ZStack { - VStack { - let offset: CGFloat = fontSize < .large ? 82 : (fontSize >= .large && fontSize < .extraExtraLarge) ? 87 : 92 - ZStack { - Text( - (recentGlucose?.glucose ?? 100) == 400 ? "HIGH" : recentGlucose?.glucose - .map { - glucoseFormatter - .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! } - ?? "--" - ) - .font(.glucoseFont) - .foregroundColor(alwaysUseColors ? colorOfGlucose : alarm == nil ? .primary : .loopRed) - .frame(maxWidth: .infinity, alignment: .center) - - HStack(spacing: 10) { - image - .font(.system(size: 25)) - VStack { - Text( - delta - .map { - deltaFormatter - .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! - } ?? "--" - ) - HStack { - let minutesAgo = -1 * (recentGlucose?.dateString.timeIntervalSinceNow ?? 0) / 60 - let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? "" - Text( - minutesAgo <= 1 ? "" : ( - text + " " + - NSLocalizedString("min", comment: "Short form for minutes") + " " - ) - ) - }.offset(x: 7, y: 0) - } - .font(.extraSmall).foregroundStyle(.secondary) - }.frame(maxWidth: .infinity, alignment: .center) - .offset(x: offset, y: 0) + if let recent = recentGlucose { + if displayDelta, let deltaInt = delta, !(units == .mmolL && abs(deltaInt) <= 1) { deltaView(deltaInt) } + VStack(spacing: 15) { + let formatter = recent.type == GlucoseType.manual.rawValue ? manualGlucoseFormatter : glucoseFormatter + if let string = recent.glucose.map({ + formatter + .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber) ?? "" }) + { + glucoseText(string).asAny() + .background { glucoseDrop } + let minutesAgo = -1 * recent.dateString.timeIntervalSinceNow / 60 + let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? "" + Text( + minutesAgo <= 1 ? "Now" : + (text + " " + NSLocalizedString("min", comment: "Short form for minutes") + " ") + ) + .font(.caption) + .foregroundStyle(.secondary) + .offset(x: 2, y: fontSize >= .extraLarge ? -3 : 0) + } } - .dynamicTypeSize(DynamicTypeSize.medium ... DynamicTypeSize.xLarge) } } } - var image: some View { + private func deltaView(_ deltaInt: Int) -> some View { + ZStack { + let offset: CGFloat = 4 + let deltaConverted = units == .mmolL ? deltaInt.asMmolL : Decimal(deltaInt) + + Text(deltaFormatter.string(from: deltaConverted as NSNumber) ?? "") + .font(.caption) + .offset(x: -(offset / 2)) + .background { directionDrop } + .offset(x: offset, y: 10) + } + .dynamicTypeSize(DynamicTypeSize.medium ... DynamicTypeSize.large) + .frame(maxHeight: .infinity, alignment: .center).offset(x: 120, y: -7) + } + + private var adjustments: (degree: Double, x: CGFloat, y: CGFloat) { + let yOffset: CGFloat = 17 guard let direction = recentGlucose?.direction else { - return Image(systemName: "arrow.left.and.right") + return (90, 0, yOffset) } switch direction { case .doubleUp, .singleUp, .tripleUp: - return Image(systemName: "arrow.up") + return (0, 0, yOffset) case .fortyFiveUp: - return Image(systemName: "arrow.up.right") + return (45, 0, yOffset) case .flat: - return Image(systemName: "arrow.forward") + return (90, 0, yOffset) case .fortyFiveDown: - return Image(systemName: "arrow.down.forward") + return (135, 0, yOffset) case .doubleDown, .singleDown, .tripleDown: - return Image(systemName: "arrow.down") + return (180, 0, yOffset) case .none, .notComputable, .rateOutOfRange: - return Image(systemName: "arrow.left.and.right") + return (90, 0, yOffset) + } + } + + private func glucoseText(_ string: String) -> any View { + ZStack { + let decimal = string.components(separatedBy: decimalString) + if decimal.count > 1 { + HStack(spacing: 0) { + Text(decimal[0]).font(.glucoseFont) + Text(decimalString).font(.system(size: 28).weight(.semibold)).baselineOffset(-10) + Text(decimal[1]).font(.system(size: 28)).baselineOffset(-10) + } + .tracking(-1) + .offset(x: 0, y: 14) + .foregroundColor(alwaysUseColors ? colorOfGlucose : alarm == nil ? .primary : .loopRed) + } else { + Text(string) + .font(.glucoseFontMdDl.width(.condensed)) // .tracking(-2) + .foregroundColor(alwaysUseColors ? colorOfGlucose : alarm == nil ? .primary : .loopRed) + .offset(x: 0, y: 16) + } } } - var colorOfGlucose: Color { + private var glucoseDrop: some View { + let adjust = adjustments + let degree = adjustments.degree + return Image("glucoseDrops") + .resizable() + .frame(width: 140, height: 140).rotationEffect(.degrees(degree)) + .animation(.bouncy(duration: 1, extraBounce: 0.2), value: degree) + .offset(x: adjust.x, y: adjust.y) + .shadow(radius: 3) + } + + private var directionDrop: some View { + let degree = adjustments.degree + return Image("glucoseDrops") + .resizable() + .frame(width: 40, height: 40).rotationEffect(.degrees(degree)) + .animation(.bouncy(duration: 1, extraBounce: 0.2), value: degree) + } + + private var colorOfGlucose: Color { let whichGlucose = recentGlucose?.glucose ?? 0 guard lowGlucose < highGlucose else { return .primary } diff --git a/FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift b/FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift index bece30b613..6ed1ceaa09 100644 --- a/FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift @@ -25,32 +25,39 @@ struct LoopView: View { @Environment(\.sizeCategory) private var fontSize var body: some View { - VStack { - let multiplyForLargeFonts = fontSize > .extraLarge ? 1.2 : 1 + VStack(spacing: 2) { + let multiplyForLargeFonts = fontSize > .extraLarge ? 1.1 : 1 + + HStack(spacing: 0) { + Text("i").font(.system(size: 10, design: .rounded)).offset(y: 0.35) + Text("APS").font(.system(size: 12, design: .rounded)) + }.foregroundStyle(.secondary.opacity(0.5)) + LoopEllipse(stroke: color) - .frame(width: minutesAgo > 9 ? 70 * multiplyForLargeFonts : 60 * multiplyForLargeFonts, height: 27) + .frame(width: minutesAgo > 9 ? 60 * multiplyForLargeFonts : 60 * multiplyForLargeFonts, height: 27) .overlay { - let textColor: Color = .secondary HStack { ZStack { if closedLoop { if !isLooping, actualSuggestion?.timestamp != nil { - if minutesAgo > 1440 { - Text("--").font(.loopFont).foregroundColor(textColor).padding(.leading, 5) + if minutesAgo > 999 { + Text("--").font(.caption).padding(.leading, 5).foregroundColor(.secondary) } else { - let timeString = "\(minutesAgo) " + - NSLocalizedString("min", comment: "Minutes ago since last loop") - Text(timeString).font(.loopFont).foregroundColor(textColor) + let timeString = NSLocalizedString("m", comment: "Minutes ago since last loop") + HStack(spacing: 0) { + Text("\(minutesAgo) ") + Text(timeString).foregroundColor(.secondary) + }.font(.caption) } } if isLooping { ProgressView() } } else if !isLooping { - Text("Open").font(.loopFont) + Text("Open").font(.caption) } } - } + }.dynamicTypeSize(...DynamicTypeSize.xLarge) } } } diff --git a/FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift b/FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift index 0d8d268d76..a34c89bad0 100644 --- a/FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift @@ -72,6 +72,7 @@ struct PumpView: View { .frame(maxHeight: 15) .foregroundColor(batteryColor) .offset(x: 0, y: -4) + .shadow(radius: 2) .overlay { if let timeZone = timeZone, timeZone.secondsFromGMT() != TimeZone.current.secondsFromGMT() { ClockOffset(mdtPump: true) @@ -85,6 +86,7 @@ struct PumpView: View { .frame(width: IAPSconfig.iconSize * 1.15, height: IAPSconfig.iconSize * 1.6) .foregroundColor(colorScheme == .dark ? .secondary : .white) .offset(x: 0, y: -5) + .shadow(radius: 2) .overlay { if let timeZone = timeZone, timeZone.secondsFromGMT() != TimeZone.current.secondsFromGMT() { ClockOffset(mdtPump: false) diff --git a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift index 353ac5b28d..96a10fd0e5 100644 --- a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift +++ b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift @@ -14,13 +14,10 @@ extension Home { @State var showCancelAlert = false @State var showCancelTTAlert = false @State var triggerUpdate = false - @State var scrollOffset = CGFloat.zero @State var display = false - - @Namespace var scrollSpace - - let scrollAmount: CGFloat = 290 + @State var displayGlucose = false let buttonFont = Font.custom("TimeButtonFont", size: 14) + let viewPadding: CGFloat = 5 @Environment(\.managedObjectContext) var moc @Environment(\.colorScheme) var colorScheme @@ -94,13 +91,13 @@ extension Home { var glucoseView: some View { CurrentGlucoseView( recentGlucose: $state.recentGlucose, - timerDate: $state.timerDate, delta: $state.glucoseDelta, units: $state.units, alarm: $state.alarm, lowGlucose: $state.lowGlucose, highGlucose: $state.highGlucose, - alwaysUseColors: $state.alwaysUseColors + alwaysUseColors: $state.alwaysUseColors, + displayDelta: $state.displayDelta ) .onTapGesture { if state.alarm == nil { @@ -134,6 +131,7 @@ extension Home { state.setupPump = true } } + .offset(y: 1) } var loopView: some View { @@ -153,11 +151,12 @@ extension Home { impactHeavy.impactOccurred() state.runLoop() } + .offset(y: 10) } - var tempBasalString: String? { + var tempBasalString: String { guard let tempRate = state.tempRate else { - return nil + return "?" + NSLocalizedString(" U/hr", comment: "Unit per hour with space") } let rateString = numberFormatter.string(from: tempRate as NSNumber) ?? "0" var manualBasalString = "" @@ -185,7 +184,7 @@ extension Home { if state.pumpSuspended { Text("Pump suspended") .font(.extraSmall).bold().foregroundColor(.loopGray) - } else if let tempBasalString = tempBasalString { + } else { Text(tempBasalString) .font(.statusFont).bold() .foregroundColor(.insulin) @@ -207,28 +206,32 @@ extension Home { } ZStack { - if let eventualBG = state.eventualBG { - HStack { - Text("⇢").font(.statusFont).foregroundStyle(.secondary) + HStack { + Text("⇢").font(.statusFont).foregroundStyle(.secondary) - // Image(systemName: "arrow.forward") + if let eventualBG = state.eventualBG { Text( fetchedTargetFormatter.string( from: (state.units == .mmolL ? eventualBG.asMmolL : Decimal(eventualBG)) as NSNumber - )! + ) ?? "" ).font(.statusFont).foregroundColor(colorScheme == .dark ? .white : .black) - Text(state.units.rawValue).font(.system(size: 12)).foregroundStyle(.secondary) + } else { + Text("?").font(.statusFont).foregroundStyle(.secondary) } - .frame(maxWidth: .infinity, alignment: .trailing) - .padding(.trailing, 8) + Text(state.units.rawValue).font(.system(size: 12)).foregroundStyle(.secondary) } + .frame(maxWidth: .infinity, alignment: .trailing) + .padding(.trailing, 8) } - }.dynamicTypeSize(...DynamicTypeSize.xxLarge) + } + .dynamicTypeSize(...DynamicTypeSize.xxLarge) } var infoPanel: some View { - info - .frame(minHeight: 35, maxHeight: 35) + info.frame(height: 26) + .background { + InfoPanelBackground(colorScheme: colorScheme) + } } var mainChart: some View { @@ -278,102 +281,105 @@ extension Home { .frame(height: 50 + geo.safeAreaInsets.bottom) let isOverride = fetchedPercent.first?.enabled ?? false let isTarget = (state.tempTarget != nil) - HStack { - Button { state.showModal(for: .addCarbs(editMode: false, override: false)) } - label: { - ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) { - Image(systemName: "fork.knife") - .renderingMode(.template) - .font(.custom("Buttons", size: 24)) - .foregroundColor(colorScheme == .dark ? .loopYellow : .orange) - .padding(8) - .foregroundColor(.loopYellow) - if let carbsReq = state.carbsRequired { - Text(numberFormatter.string(from: carbsReq as NSNumber)!) - .font(.caption) - .foregroundColor(.white) - .padding(4) - .background(Capsule().fill(Color.red)) + VStack { + Divider() + HStack { + Button { state.showModal(for: .addCarbs(editMode: false, override: false)) } + label: { + ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) { + Image(systemName: "fork.knife") + .renderingMode(.template) + .font(.custom("Buttons", size: 24)) + .foregroundColor(colorScheme == .dark ? .loopYellow : .orange) + .padding(8) + .foregroundColor(.loopYellow) + if let carbsReq = state.carbsRequired { + Text(numberFormatter.string(from: carbsReq as NSNumber)!) + .font(.caption) + .foregroundColor(.white) + .padding(4) + .background(Capsule().fill(Color.red)) + } } + }.buttonStyle(.borderless) + Spacer() + Button { + state.showModal(for: .bolus( + waitForSuggestion: state.useCalc ? true : false, + fetch: false + )) } - }.buttonStyle(.borderless) - Spacer() - Button { - state.showModal(for: .bolus( - waitForSuggestion: state.useCalc ? true : false, - fetch: false - )) - } - label: { - Image(systemName: "syringe") - .renderingMode(.template) - .font(.custom("Buttons", size: 24)) - } - .buttonStyle(.borderless) - .foregroundColor(.insulin) - Spacer() - if state.allowManualTemp { - Button { state.showModal(for: .manualTempBasal) } label: { - Image("bolus1") + Image(systemName: "syringe") .renderingMode(.template) - .resizable() - .frame(width: IAPSconfig.buttonSize, height: IAPSconfig.buttonSize, alignment: .bottom) + .font(.custom("Buttons", size: 24)) } + .buttonStyle(.borderless) .foregroundColor(.insulin) Spacer() - } - ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) { - Image(systemName: isOverride ? "person.fill" : "person") - .symbolRenderingMode(.palette) - .font(.custom("Buttons", size: 28)) - .foregroundStyle(.purple) - .padding(8) - .background(isOverride ? .purple.opacity(0.15) : .clear) - .clipShape(RoundedRectangle(cornerRadius: 10)) - } - .onTapGesture { - if isOverride { - showCancelAlert.toggle() - } else { + if state.allowManualTemp { + Button { state.showModal(for: .manualTempBasal) } + label: { + Image("bolus1") + .renderingMode(.template) + .resizable() + .frame(width: IAPSconfig.buttonSize, height: IAPSconfig.buttonSize, alignment: .bottom) + } + .foregroundColor(.insulin) + Spacer() + } + ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) { + Image(systemName: isOverride ? "person.fill" : "person") + .symbolRenderingMode(.palette) + .font(.custom("Buttons", size: 28)) + .foregroundStyle(.purple) + .padding(8) + .background(isOverride ? .purple.opacity(0.15) : .clear) + .clipShape(RoundedRectangle(cornerRadius: 10)) + } + .onTapGesture { + if isOverride { + showCancelAlert.toggle() + } else { + state.showModal(for: .overrideProfilesConfig) + } + } + .onLongPressGesture { state.showModal(for: .overrideProfilesConfig) } - } - .onLongPressGesture { - state.showModal(for: .overrideProfilesConfig) - } - if state.useTargetButton { - Spacer() - Image(systemName: "target") - .renderingMode(.template) - .font(.custom("Buttons", size: 24)) - .padding(8) - .foregroundColor(.loopGreen) - .background(isTarget ? .green.opacity(0.15) : .clear) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .onTapGesture { - if isTarget { - showCancelTTAlert.toggle() - } else { + if state.useTargetButton { + Spacer() + Image(systemName: "target") + .renderingMode(.template) + .font(.custom("Buttons", size: 24)) + .padding(8) + .foregroundColor(.loopGreen) + .background(isTarget ? .green.opacity(0.15) : .clear) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .onTapGesture { + if isTarget { + showCancelTTAlert.toggle() + } else { + state.showModal(for: .addTempTarget) + } + } + .onLongPressGesture { state.showModal(for: .addTempTarget) } - } - .onLongPressGesture { - state.showModal(for: .addTempTarget) - } - } - Spacer() - Button { state.showModal(for: .settings) } - label: { - Image(systemName: "gear") - .renderingMode(.template) - .font(.custom("Buttons", size: 24)) + } + Spacer() + Button { state.showModal(for: .settings) } + label: { + Image(systemName: "gear") + .renderingMode(.template) + .font(.custom("Buttons", size: 24)) + } + .buttonStyle(.borderless) + .foregroundColor(.gray) } - .buttonStyle(.borderless) - .foregroundColor(.gray) + .padding(.horizontal, state.allowManualTemp ? 5 : 24) + .padding(.bottom, geo.safeAreaInsets.bottom) } - .padding(.horizontal, state.allowManualTemp ? 5 : 24) - .padding(.bottom, geo.safeAreaInsets.bottom) } .dynamicTypeSize(...DynamicTypeSize.xxLarge) .confirmationDialog("Cancel Profile Override", isPresented: $showCancelAlert) { @@ -390,15 +396,12 @@ extension Home { } var chart: some View { - let ratio = state.timeSettings ? 1.61 : 1.44 - let ratio2 = state.timeSettings ? 1.65 : 1.51 + let ratio = state.timeSettings ? 1.73 : 1.56 + let ratio2 = state.timeSettings ? 1.77 : 1.63 - return addColouredBackground() + return addColouredBackground().shadow(radius: 3, y: 3) .overlay { - VStack(spacing: 0) { - infoPanel - mainChart - } + mainChart } .frame(minHeight: UIScreen.main.bounds.height / (fontSize < .extraExtraLarge ? ratio : ratio2)) } @@ -406,13 +409,15 @@ extension Home { var carbsAndInsulinView: some View { HStack { if let settings = state.settingsManager { - let opacity: CGFloat = colorScheme == .dark ? 0.2 : 0.6 + // A temporary ugly(?) workaround for displaying last real IOB and COB computation + let opacity: CGFloat = colorScheme == .dark ? 0.2 : 0.65 let materialOpacity: CGFloat = colorScheme == .dark ? 0.25 : 0.10 + // Carbs on Board HStack { let substance = Double(state.suggestion?.cob ?? 0) let max = max(Double(settings.preferences.maxCOB), 1) let fraction: Double = 1 - (substance / max) - let fill = CGFloat(min(Swift.max(fraction, 0.05), substance > 0 ? 0.92 : 0.97)) + let fill = CGFloat(min(Swift.max(fraction, 0.05), substance > 0 ? 0.92 : 1)) TestTube( opacity: opacity, amount: fill, @@ -422,17 +427,22 @@ extension Home { .frame(width: 12, height: 38) .offset(x: 0, y: -5) HStack(spacing: 0) { - Text( - numberFormatter.string(from: (state.suggestion?.cob ?? 0) as NSNumber) ?? "0" - ).font(.statusFont).bold() + if let loop = state.suggestion, let cob = loop.cob { + Text(numberFormatter.string(from: cob as NSNumber) ?? "0") + .font(.statusFont).bold() + // Display last loop, unless very old + } else { + Text("?").font(.statusFont).bold() + } Text(NSLocalizedString(" g", comment: "gram of carbs")).font(.statusFont).foregroundStyle(.secondary) }.offset(x: 0, y: 5) } + // Insulin on Board HStack { let substance = Double(state.suggestion?.iob ?? 0) let max = max(Double(settings.preferences.maxIOB), 1) - let fraction: Double = 1 - (substance / max) - let fill = CGFloat(min(Swift.max(fraction, 0.05), substance > 0 ? 0.92 : 0.98)) + let fraction: Double = 1 - abs(substance) / max + let fill = CGFloat(min(Swift.max(fraction, 0.05), 1)) TestTube( opacity: opacity, amount: fill, @@ -442,14 +452,19 @@ extension Home { .frame(width: 12, height: 38) .offset(x: 0, y: -5) HStack(spacing: 0) { - Text( - numberFormatter.string(from: (state.suggestion?.iob ?? 0) as NSNumber) ?? "0" - ).font(.statusFont).bold() + if let loop = state.suggestion, let iob = loop.iob { + Text( + numberFormatter.string(from: iob as NSNumber) ?? "0" + ).font(.statusFont).bold() + } else { + Text("?").font(.statusFont).bold() + } Text(NSLocalizedString(" U", comment: "Insulin unit")).font(.statusFont).foregroundStyle(.secondary) }.offset(x: 0, y: 5) } } } + .offset(y: 5) } var preview: some View { @@ -468,7 +483,7 @@ extension Home { var activeIOBView: some View { addBackground() - .frame(minHeight: 430) + .frame(minHeight: 405) .overlay { ActiveIOBView( data: $state.iobData, @@ -488,7 +503,7 @@ extension Home { var activeCOBView: some View { addBackground() - .frame(minHeight: 230) + .frame(minHeight: 190) .overlay { ActiveCOBView(data: $state.iobData) } @@ -499,7 +514,7 @@ extension Home { var loopPreview: some View { addBackground() - .frame(minHeight: 190) + .frame(minHeight: 160) .overlay { LoopsView(loopStatistics: $state.loopStatistics) } @@ -528,7 +543,7 @@ extension Home { Text(name).font(.statusFont).foregroundStyle(.secondary) } } - } + } else { Text("📉") } // Hypo Treatment is not actually a preset } else if override.percentage != 100 { Text(override.percentage.formatted() + " %").font(.statusFont).foregroundStyle(.secondary) } else if override.smbIsOff, !override.smbIsAlwaysOff { @@ -574,47 +589,46 @@ extension Home { } } - @ViewBuilder private func headerView(_ geo: GeometryProxy) -> some View { + @ViewBuilder private func headerView(_ geo: GeometryProxy, extra: CGFloat) -> some View { + let scrolling: Bool = extra > 0 + let height: CGFloat = scrolling ? 170 : 170 addHeaderBackground() .frame( - maxHeight: fontSize < .extraExtraLarge ? 125 + geo.safeAreaInsets.top : 135 + geo - .safeAreaInsets.top + height: fontSize < .extraExtraLarge ? height + geo.safeAreaInsets.top + extra : height + 10 + geo + .safeAreaInsets.top + extra ) + .clipShape(Rectangle()) .overlay { VStack { ZStack { - glucoseView.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top).padding(.top, 10) + glucoseView.frame(maxHeight: .infinity, alignment: .center).offset(y: -5) + loopView.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding(.leading, 32).padding(.top, 20) HStack { carbsAndInsulinView .frame(maxHeight: .infinity, alignment: .bottom) Spacer() - loopView.frame(maxHeight: .infinity, alignment: .bottom).padding(.bottom, 3) - .offset(x: -2, y: 0) // To do: Remove all offsets, if possible. - Spacer() pumpView .frame(maxHeight: .infinity, alignment: .bottom) - .padding(.bottom, 2) } .dynamicTypeSize(...DynamicTypeSize.xLarge) .padding(.horizontal, 10) } - }.padding(.top, geo.safeAreaInsets.top).padding(.bottom, 10) + // Small glucose View, past 24 hours. + if displayGlucose { glucoseHeaderView() } + + if !scrolling { + infoPanel + } + }.padding(.top, geo.safeAreaInsets.top) } - .clipShape(Rectangle()) } @ViewBuilder private func glucoseHeaderView() -> some View { - addHeaderBackground() - .frame(maxHeight: 90) - .overlay { - VStack { - ZStack { - glucosePreview.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) - .dynamicTypeSize(...DynamicTypeSize.medium) - } - } - } - .clipShape(Rectangle()) + ZStack { + glucosePreview + .dynamicTypeSize(...DynamicTypeSize.medium) + } } var glucosePreview: some View { @@ -636,7 +650,7 @@ extension Home { (($0.glucose ?? 0) > veryHigh || Decimal($0.glucose ?? 0) < low) ? Color(.red) : Decimal($0.glucose ?? 0) > high ? Color(.yellow) : Color(.darkGreen) ) - .symbolSize(7) + .symbolSize(5) } .chartXAxis { AxisMarks(values: .stride(by: .hour, count: 2)) { _ in @@ -656,11 +670,10 @@ extension Home { .chartXScale( domain: Date.now.addingTimeInterval(-1.days.timeInterval) ... Date.now ) - .frame(maxHeight: 70) + .frame(height: 70) .padding(.leading, 30) .padding(.trailing, 32) - .padding(.top, 10) - .padding(.bottom, 10) + .padding(.top, 15) } var timeSetting: some View { @@ -682,40 +695,44 @@ extension Home { var body: some View { GeometryReader { geo in VStack(spacing: 0) { - headerView(geo) - if !state.skipGlucoseChart, scrollOffset > scrollAmount { - glucoseHeaderView() - .transition(.move(edge: .top)) - } - + // Header View + headerView(geo, extra: (displayGlucose && !state.skipGlucoseChart) ? 59 : 0) ScrollView { - ScrollViewReader { _ in - LazyVStack { - chart - if state.timeSettings { timeSetting } - preview.padding(.top, state.timeSettings ? 5 : 15) - loopPreview.padding(.top, 15) - if state.iobData.count > 5 { - activeCOBView.padding(.top, 15) - activeIOBView.padding(.top, 15) - } + VStack { + // Main Chart + chart + // Adjust hours visible (X-Axis) + if state.timeSettings, !displayGlucose { timeSetting } + // TIR Chart + preview.padding(.top, (state.timeSettings && !displayGlucose) ? 5 : 15) + // Loops Chart + loopPreview.padding(.vertical, 15) + // COB Chart + if state.carbData > 0 { + activeCOBView } - .background(GeometryReader { geo in - let offset = -geo.frame(in: .named(scrollSpace)).minY + // IOB Chart + if state.iobs > 0 { + activeIOBView.padding(.top, state.carbData > 0 ? viewPadding : 0) + } + }.background { + // Track vertical scroll + GeometryReader { proxy in + let scrollPosition = proxy.frame(in: .named("HomeScrollView")).minY + let yThreshold: CGFloat = state.timeSettings ? -500 : -560 Color.clear - .preference( - key: ScrollViewOffsetPreferenceKey.self, - value: offset - ) - }) - } - } - .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in - scrollOffset = value - if !state.skipGlucoseChart, scrollOffset > scrollAmount { - display.toggle() + .onChange(of: scrollPosition) { y in + if y < yThreshold, state.iobs > 0 || state.carbData > 0, !state.skipGlucoseChart { + withAnimation(.easeOut(duration: 0.3)) { displayGlucose = true } + } else { + withAnimation(.easeOut(duration: 0.4)) { displayGlucose = false } + } + } + } } - } + + }.coordinateSpace(name: "HomeScrollView") + // Buttons buttonPanel(geo) } .background( @@ -775,7 +792,7 @@ extension Home { Text("No sugestion found").font(.suggestionHeadline).foregroundColor(.white) } if let errorMessage = state.errorMessage, let date = state.errorDate { - Text(NSLocalizedString("Error at", comment: "") + " " + dateFormatter.string(from: date)) + Text(NSLocalizedString("Status at", comment: "") + " " + dateFormatter.string(from: date)) .foregroundColor(.white) .font(.suggestionError) .padding(.bottom, 4) diff --git a/FreeAPS/Sources/Modules/Home/View/Previews/ActiveCOBView.swift b/FreeAPS/Sources/Modules/Home/View/Previews/ActiveCOBView.swift index db17ddcd4b..71f69ccae5 100644 --- a/FreeAPS/Sources/Modules/Home/View/Previews/ActiveCOBView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Previews/ActiveCOBView.swift @@ -14,10 +14,9 @@ struct ActiveCOBView: View { var body: some View { VStack { - Text("Active Carbohydrates").font(.previewHeadline).padding(.top, 20) - cobView().frame(maxHeight: 130).padding(.vertical, 10).padding(.horizontal, 20) - .padding(.bottom, 10) - }.dynamicTypeSize(...DynamicTypeSize.medium) + Text("Active Carbohydrates").font(.previewHeadline).padding(.top, 20).padding(.bottom, 15) + cobView().frame(maxHeight: 130).padding(.bottom, 10).padding(.horizontal, 20) + }.dynamicTypeSize(...DynamicTypeSize.xLarge) } @ViewBuilder private func cobView() -> some View { @@ -27,25 +26,25 @@ struct ActiveCOBView: View { AreaMark( x: .value("Time", $0.date), y: .value("COB", $0.cob) - ).foregroundStyle(Color(.loopYellow)) - } - .chartYAxis { - AxisMarks(values: .automatic(desiredCount: 3)) - } - .chartXAxis { - AxisMarks(values: .stride(by: .hour, count: 2)) { _ in - AxisValueLabel( - format: .dateTime.hour(.defaultDigits(amPM: .omitted)) - .locale(Locale(identifier: "sv")) // Force 24h - ) - AxisGridLine() + ).foregroundStyle(Color(.loopYellow).gradient).opacity(0.8) } + .chartYAxis { + AxisMarks(values: .automatic(desiredCount: 3)) } - } - .chartYScale( - domain: 0 ... maximum - ) - .chartXScale( - domain: Date.now.addingTimeInterval(-1.days.timeInterval) ... Date.now - ) + .chartXAxis { + AxisMarks(values: .stride(by: .hour, count: 2)) { _ in + AxisValueLabel( + format: .dateTime.hour(.defaultDigits(amPM: .omitted)) + .locale(Locale(identifier: "sv")) // 24h format + ) + AxisGridLine() + } + } + .chartYScale( + domain: 0 ... maximum + ) + .chartXScale( + domain: Date.now.addingTimeInterval(-1.days.timeInterval) ... Date.now + ) + .chartLegend(.hidden) } } diff --git a/FreeAPS/Sources/Modules/Home/View/Previews/ActiveIOBView.swift b/FreeAPS/Sources/Modules/Home/View/Previews/ActiveIOBView.swift index 892d15b6c6..8ed713c4f1 100644 --- a/FreeAPS/Sources/Modules/Home/View/Previews/ActiveIOBView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Previews/ActiveIOBView.swift @@ -29,23 +29,69 @@ struct ActiveIOBView: View { var body: some View { VStack { - Text("Active Insulin").font(.previewHeadline).padding(.top, 20) + Text("Active Insulin").font(.previewHeadline).padding(.top, 20).padding(.bottom, 15) iobView().frame(maxHeight: 130).padding(.horizontal, 20) - sumView().frame(maxHeight: 250).padding(.vertical, 30) + sumView().frame(maxHeight: 250).padding(.top, 20).padding(.bottom, 10) }.dynamicTypeSize(...DynamicTypeSize.xLarge) } @ViewBuilder private func iobView() -> some View { - let minimum = data.map(\.iob).min() ?? 0 + // Data + let negIOBData = negIOBdata(data) + // Domain + let minimum = min(data.map(\.iob).min() ?? 0, negIOBData.map(\.iob).min() ?? 0) let minimumRange = min(0, minimum * 1.3) let maximum = (data.map(\.iob).max() ?? 0) * 1.1 - Chart(data) { - AreaMark( - x: .value("Time", $0.date), - y: .value("IOB", $0.iob) - ).foregroundStyle(Color(.insulin)) + Chart { + ForEach(data) { item in + LineMark( + x: .value("Time", item.date), + y: .value("IOB", item.iob) + ).foregroundStyle(by: .value("Time", "Line IOB > 0")) + .lineStyle(StrokeStyle(lineWidth: 0.8)) + + AreaMark( + x: .value("Time", item.date), + y: .value("IOB", item.iob) + ).foregroundStyle(by: .value("Time", "IOB > 0")) + } + ForEach(negIOBData) { item in + AreaMark( + x: .value("Time", item.date), + yStart: .value("IOB", 0), + yEnd: .value("IOB", item.iob) + ).foregroundStyle(by: .value("Time", "IOB < 0")) + } } + .chartForegroundStyleScale( + [ + "IOB > 0": LinearGradient( + gradient: Gradient(colors: [ + Color.insulin.opacity(1), + Color.insulin.opacity(0.4) + ]), + startPoint: .top, + endPoint: .bottom + ), + "IOB < 0": LinearGradient( + gradient: Gradient(colors: [ + Color.red.opacity(1), + Color.red.opacity(1) + ]), + startPoint: .bottom, + endPoint: .top + ), + "Line IOB > 0": LinearGradient( + gradient: Gradient(colors: [ + Color.insulin.opacity(1), + Color.insulin.opacity(1) + ]), + startPoint: .top, + endPoint: .bottom + ) + ] + ) .chartXAxis { AxisMarks(values: .stride(by: .hour, count: 2)) { _ in AxisValueLabel( @@ -64,6 +110,7 @@ struct ActiveIOBView: View { .chartXScale( domain: Date.now.addingTimeInterval(-1.days.timeInterval) ... Date.now ) + .chartLegend(.hidden) } @ViewBuilder private func sumView() -> some View { @@ -93,7 +140,7 @@ struct ActiveIOBView: View { color: Color(.clear) ), BolusSummary( - variable: NSLocalizedString("Average Insulin past 24h", comment: ""), + variable: NSLocalizedString("Average Insulin 10 days", comment: ""), formula: NSLocalizedString(" U", comment: ""), insulin: tddActualAverage, color: .secondary @@ -158,4 +205,21 @@ struct ActiveIOBView: View { } return data } + + private func negIOBdata(_ data: [IOBData]) -> [IOBData] { + var array = [IOBData]() + var previous = data.first + for item in data { + if item.iob < 0 { + if previous?.iob ?? 0 >= 0 { + array.append(IOBData(date: previous?.date ?? .distantPast, iob: 0, cob: 0)) + } + array.append(IOBData(date: item.date, iob: item.iob, cob: 0)) + } else if previous?.iob ?? 0 < 0 { + array.append(IOBData(date: item.date, iob: 0, cob: 0)) + } + previous = item + } + return array + } } diff --git a/FreeAPS/Sources/Modules/Home/View/Previews/LoopsView.swift b/FreeAPS/Sources/Modules/Home/View/Previews/LoopsView.swift index efa6629a4f..29793c03a9 100644 --- a/FreeAPS/Sources/Modules/Home/View/Previews/LoopsView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Previews/LoopsView.swift @@ -12,7 +12,7 @@ struct LoopsView: View { let readings = loopStatistics.1 let percentage = loopStatistics.2 - Text(NSLocalizedString("Loops", comment: "") + " / " + NSLocalizedString("Readings", comment: "")) + Text(NSLocalizedString("Loops", comment: "")) .padding(.bottom, 10).font(.previewHeadline) loopChart(percentage: percentage) @@ -32,19 +32,45 @@ struct LoopsView: View { Text("\(loops)") }.font(.loopFont) } - .padding(.top, 20) - .padding(.bottom, 15) .dynamicTypeSize(...DynamicTypeSize.xLarge) } func loopChart(percentage: Double) -> some View { VStack { Chart { + // Background chart 100 % + if percentage < 100 { + BarMark( + xStart: .value("LoopPercentage", percentage - 4), + xEnd: .value("Full Bar", 100) + ) + .foregroundStyle( + Color(.gray).opacity(0.3) + ) + .clipShape( + UnevenRoundedRectangle( + topLeadingRadius: 0, + bottomLeadingRadius: 0, + bottomTrailingRadius: 4, + topTrailingRadius: 4 + ) + ) + } + + // Loops per readings chart BarMark( x: .value("LoopPercentage", percentage) ) .foregroundStyle( percentage >= 90 ? Color(.darkGreen) : percentage >= 75 ? .orange : .red + ).opacity(1) + .clipShape( + UnevenRoundedRectangle( + topLeadingRadius: 4, + bottomLeadingRadius: 4, + bottomTrailingRadius: 4, + topTrailingRadius: 4 + ) ) .clipShape( UnevenRoundedRectangle( diff --git a/FreeAPS/Sources/Modules/Home/View/Previews/TIRView.swift b/FreeAPS/Sources/Modules/Home/View/Previews/TIRView.swift index af2ddeb8d4..6078bb9e95 100644 --- a/FreeAPS/Sources/Modules/Home/View/Previews/TIRView.swift +++ b/FreeAPS/Sources/Modules/Home/View/Previews/TIRView.swift @@ -17,10 +17,134 @@ struct PreviewChart: View { } var body: some View { - let fetched = previewTir() + VStack { + let padding: CGFloat = 40 + // Prepare the chart data + let data = prepareData() + HStack { + Text("Today") + }.padding(.bottom, 15).font(.previewHeadline) + + HStack { + Chart(data) { item in + BarMark( + x: .value("TIR", item.type), + y: .value("Percentage", item.percentage), + width: .fixed(65) + ) + .foregroundStyle(by: .value("Group", item.group)) + .clipShape( + UnevenRoundedRectangle( + topLeadingRadius: (item.last || item.percentage == 100) ? 4 : 0, + bottomLeadingRadius: (item.first || item.percentage == 100) ? 4 : 0, + bottomTrailingRadius: (item.first || item.percentage == 100) ? 4 : 0, + topTrailingRadius: (item.last || item.percentage == 100) ? 4 : 0 + ) + ) + } + .chartForegroundStyleScale([ + NSLocalizedString( + "Low", + comment: "" + ): .red, + NSLocalizedString("In Range", comment: ""): .darkGreen, + NSLocalizedString( + "High", + comment: "" + ): .yellow, + NSLocalizedString( + "Very High", + comment: "" + ): .red, + NSLocalizedString( + "Very Low", + comment: "" + ): .darkRed, + "Separator": colorScheme == .dark ? .black : .white + ]) + .chartXAxis(.hidden) + .chartYAxis(.hidden) + .chartLegend(.hidden) + .padding(.bottom, 15) + .padding(.leading, padding) + .frame(maxWidth: (UIScreen.main.bounds.width / 5) + padding) + + sumView(data).offset(x: 0, y: -7) + } + + }.frame(maxHeight: 180) + .padding(.top, 20) + .dynamicTypeSize(...DynamicTypeSize.xLarge) + } + + @ViewBuilder private func sumView(_ data: [TIRinPercent]) -> some View { + let entries = data.reversed().filter { $0.group != "Separator" } + let padding: CGFloat = entries.count == 5 ? 4 : 35 / CGFloat(entries.count) + Grid { + ForEach(entries) { entry in + if entry.group != "Separator" { + GridRow(alignment: .firstTextBaseline) { + if entry.percentage != 0 { + HStack { + Text((tirFormatter.string(for: entry.percentage) ?? "") + "%") + Text(entry.group) + }.font( + entry.group == NSLocalizedString("In Range", comment: "") ? .previewHeadline : .previewSmall + ) + .foregroundStyle( + entry + .group == NSLocalizedString("In Range", comment: "") ? .primary : .secondary + ) + .padding( + .bottom, + (entries.count > 1 && entry.group != entries[entries.count - 1].group) ? padding : 0 + ) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + } + } + } + .dynamicTypeSize(...DynamicTypeSize.medium) + } + + private func previewTir() -> [(double: Double, string: String)] { + let hypoLimit = Int(lowLimit) + let hyperLimit = Int(highLimit) + let glucose = readings + let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) }) + let totalReadings = justGlucoseArray.count + let hyperArray = glucose.filter({ $0.glucose >= hyperLimit }) + let hyperReadings = hyperArray.compactMap({ each in each.glucose as Int16 }).count + var hyperPercentage = round(Double(hyperReadings) / Double(totalReadings) * 100) + let hypoArray = glucose.filter({ $0.glucose <= hypoLimit }) + let hypoReadings = hypoArray.compactMap({ each in each.glucose as Int16 }).count + var hypoPercentage = round(Double(hypoReadings) / Double(totalReadings) * 100) + let veryHighArray = glucose.filter({ $0.glucose > 197 }) + let veryHighReadings = veryHighArray.compactMap({ each in each.glucose as Int16 }).count + let veryHighPercentage = round(Double(veryHighReadings) / Double(totalReadings) * 100) + let veryLowArray = glucose.filter({ $0.glucose < 60 }) + let veryLowReadings = veryLowArray.compactMap({ each in each.glucose as Int16 }).count + let veryLowPercentage = round(Double(veryLowReadings) / Double(totalReadings) * 100) - let separator: Decimal = 3 + hypoPercentage -= veryLowPercentage + hyperPercentage -= veryHighPercentage + + let tir = round(100 - (hypoPercentage + hyperPercentage + veryHighPercentage + veryLowPercentage)) + var array: [(double: Double, string: String)] = [] + array.append((double: hypoPercentage, string: "Low")) + array.append((double: tir, string: "NormaL")) + array.append((double: hyperPercentage, string: "High")) + array.append((double: veryHighPercentage, string: "Very High")) + array.append((double: veryLowPercentage, string: "Very Low")) + + return array + } + + private func prepareData() -> [TIRinPercent] { + let fetched = previewTir() + let separator: Double = 2 var data: [TIRinPercent] = [ TIRinPercent( type: "TIR", @@ -28,7 +152,7 @@ struct PreviewChart: View { "Very Low", comment: "" ), - percentage: fetched[4].decimal, + percentage: fetched[4].double, id: UUID(), offset: -5, first: true, @@ -49,7 +173,7 @@ struct PreviewChart: View { "Low", comment: "" ), - percentage: fetched[0].decimal, + percentage: fetched[0].double, id: UUID(), offset: -10, first: false, @@ -67,7 +191,7 @@ struct PreviewChart: View { TIRinPercent( type: "TIR", group: NSLocalizedString("In Range", comment: ""), - percentage: fetched[1].decimal, + percentage: fetched[1].double, id: UUID(), offset: 0, first: false, @@ -88,7 +212,7 @@ struct PreviewChart: View { "High", comment: "" ), - percentage: fetched[2].decimal, + percentage: fetched[2].double, id: UUID(), offset: 10, first: false, @@ -109,7 +233,7 @@ struct PreviewChart: View { "Very High", comment: "" ), - percentage: fetched[3].decimal, + percentage: fetched[3].double, id: UUID(), offset: 5, first: false, @@ -117,175 +241,6 @@ struct PreviewChart: View { ) ] - // Preapre the data array - data = prepareData(data_: data) - - return VStack { - Text("Time In Range").padding(.bottom, 10).font(.previewHeadline) - - Chart(data) { item in - BarMark( - x: .value("TIR", item.type), - y: .value("Percentage", item.percentage), - width: .fixed(60) - ) - .foregroundStyle(by: .value("Group", item.group)) - .clipShape( - UnevenRoundedRectangle( - topLeadingRadius: (item.last || item.percentage == 100) ? 4 : 0, - bottomLeadingRadius: (item.first || item.percentage == 100) ? 4 : 0, - bottomTrailingRadius: (item.first || item.percentage == 100) ? 4 : 0, - topTrailingRadius: (item.last || item.percentage == 100) ? 4 : 0 - ) - ) - .annotation(position: .trailing) { - if item.group == NSLocalizedString("In Range", comment: ""), item.percentage > 0 { - HStack { - if item.percentage < 1 { - Text("< 1%") - } else { - Text((tirFormatter.string(from: item.percentage as NSNumber) ?? "") + "%") - } - Text(item.group) - }.font(.previewNormal) - .padding(.leading, 10) - } else if item.group == NSLocalizedString( - "Low", - comment: "" - ), item.percentage > 0.0 { - HStack { - if item.percentage < 1 { - Text("< 1%") - } else { - Text((tirFormatter.string(from: item.percentage as NSNumber) ?? "") + "%") - } - Text(item.group) - } - .offset(x: 0, y: item.offset) - .font(.loopFont) - .padding(.leading, 10) - } else if item.group == NSLocalizedString( - "High", - comment: "" - ), item.percentage > 0 { - HStack { - if item.percentage < 1 { - Text("< 1%") - } else { - Text((tirFormatter.string(from: item.percentage as NSNumber) ?? "") + "%") - } - Text(item.group) - } - .offset(x: 0, y: item.offset) - .font(.loopFont) - .padding(.leading, 10) - } else if item.group == NSLocalizedString( - "Very High", - comment: "" - ), item.percentage > 0 { - HStack { - if item.percentage < 1 { - Text("< 1%") - } else { - Text((tirFormatter.string(from: item.percentage as NSNumber) ?? "") + "%") - } - Text(item.group) - } - .offset(x: 0, y: item.offset) - .font(.loopFont) - .padding(.leading, 10) - } else if item.group == NSLocalizedString( - "Very Low", - comment: "" - ), item.percentage > 0 { - HStack { - if item.percentage < 1 { - Text("< 1%") - } else { - Text((tirFormatter.string(from: item.percentage as NSNumber) ?? "") + "%") - } - Text(item.group) - } - .offset(x: 0, y: item.offset) - .font(.loopFont) - .padding(.leading, 10) - } - } - } - .chartForegroundStyleScale([ - NSLocalizedString( - "Low", - comment: "" - ): .red, - NSLocalizedString("In Range", comment: ""): .darkGreen, - NSLocalizedString( - "High", - comment: "" - ): .yellow, - NSLocalizedString( - "Very High", - comment: "" - ): .red, - NSLocalizedString( - "Very Low", - comment: "" - ): .darkRed, - "Separator": colorScheme == .dark ? .black : .white - ]) - .chartXAxis(.hidden) - .chartYAxis(.hidden) - .chartLegend(.hidden) - .padding(.bottom, 15) - .frame(maxWidth: UIScreen.main.bounds.width / 5) - .offset(x: -UIScreen.main.bounds.width / 5, y: 0) - }.frame(maxHeight: 200) - .padding(.top, 20) - .dynamicTypeSize(...DynamicTypeSize.xLarge) - } - - private func previewTir() -> [(decimal: Decimal, string: String)] { - let hypoLimit = Int(lowLimit) - let hyperLimit = Int(highLimit) - - let glucose = readings - - let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) }) - let totalReadings = justGlucoseArray.count - - let hyperArray = glucose.filter({ $0.glucose >= hyperLimit }) - let hyperReadings = hyperArray.compactMap({ each in each.glucose as Int16 }).count - var hyperPercentage = Double(hyperReadings) / Double(totalReadings) * 100 - - let hypoArray = glucose.filter({ $0.glucose <= hypoLimit }) - let hypoReadings = hypoArray.compactMap({ each in each.glucose as Int16 }).count - var hypoPercentage = Double(hypoReadings) / Double(totalReadings) * 100 - - let veryHighArray = glucose.filter({ $0.glucose > 197 }) - let veryHighReadings = veryHighArray.compactMap({ each in each.glucose as Int16 }).count - let veryHighPercentage = Double(veryHighReadings) / Double(totalReadings) * 100 - - let veryLowArray = glucose.filter({ $0.glucose < 60 }) - let veryLowReadings = veryLowArray.compactMap({ each in each.glucose as Int16 }).count - let veryLowPercentage = Double(veryLowReadings) / Double(totalReadings) * 100 - - hypoPercentage -= veryLowPercentage - hyperPercentage -= veryHighPercentage - - let tir = 100 - (hypoPercentage + hyperPercentage + veryHighPercentage + veryLowPercentage) - - var array: [(decimal: Decimal, string: String)] = [] - array.append((decimal: Decimal(hypoPercentage), string: "Low")) - array.append((decimal: Decimal(tir), string: "NormaL")) - array.append((decimal: Decimal(hyperPercentage), string: "High")) - array.append((decimal: Decimal(veryHighPercentage), string: "Very High")) - array.append((decimal: Decimal(veryLowPercentage), string: "Very Low")) - - return array - } - - private func prepareData(data_: [TIRinPercent]) -> [TIRinPercent] { - var data = data_ - // Remove separators when needed for index in 0 ..< data.count - 2 { if index < data.count - 1 { @@ -333,7 +288,7 @@ struct PreviewChart: View { struct TIRinPercent: Identifiable { let type: String let group: String - let percentage: Decimal + let percentage: Double let id: UUID let offset: CGFloat var first: Bool diff --git a/FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift b/FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift index e98db2291a..fb9fcf4990 100644 --- a/FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift +++ b/FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift @@ -57,7 +57,7 @@ extension OverrideProfilesConfig { ns.editOverride(preset, duration, last?.date ?? Date.now) } else if let duration = OverrideStorage().cancelProfile() { let nsString = active.percentage.formatted() != "100" ? active.percentage - .formatted() + " %" : "Custom" + .formatted() + " %" : active.isPreset ? "📉" : "Custom" ns.editOverride(nsString, duration, last?.date ?? Date.now) } } @@ -162,7 +162,7 @@ extension OverrideProfilesConfig { let lastPreset = OverrideStorage().isPresetName() if let alreadyActive = last, alreadyActive.enabled, let duration = OverrideStorage().cancelProfile() { ns.editOverride( - (last?.isPreset ?? false) ? (lastPreset ?? "Override") : "Custom", + (last?.isPreset ?? false) ? (lastPreset ?? "📉") : "Custom", duration, alreadyActive.date ?? Date.now ) diff --git a/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift b/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift index bf418665a9..7cdff99272 100644 --- a/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift +++ b/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift @@ -9,11 +9,13 @@ extension PreferencesEditor { @Published var sections: [FieldSection] = [] @Published var useAlternativeBolusCalc: Bool = false @Published var units: GlucoseUnits = .mmolL + @Published var maxCarbs: Decimal = 200 override func subscribe() { preferences = provider.preferences units = settingsManager.settings.units + subscribeSetting(\.maxCarbs, on: $maxCarbs) { maxCarbs = $0 } subscribeSetting(\.units, on: $unitsIndex.map { $0 == 0 ? GlucoseUnits.mgdL : .mmolL }) { unitsIndex = $0 == .mgdL ? 0 : 1 } didSet: { [weak self] _ in diff --git a/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift b/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift index 0b25db6fad..b97005b2b3 100644 --- a/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift +++ b/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift @@ -71,6 +71,11 @@ extension PreferencesEditor { } } } + + // Exceptions. Below a FreeAPS setting added. + if field.displayName == NSLocalizedString("Max COB", comment: "Max COB") { + maxCarbs + } } } } @@ -112,5 +117,27 @@ extension PreferencesEditor { ) } } + + var maxCarbs: some View { + HStack { + ZStack { + Button("", action: { + infoButtonPressed = InfoText( + description: NSLocalizedString( + "Maximum amount of carbs (g) you can add each entry", + comment: "Max carbs description" + ), + oref0Variable: NSLocalizedString("Max Carbs", comment: "Max setting") + ) + }) + Text("Max Carbs") + } + DecimalTextField( + "0", + value: self.$state.maxCarbs, + formatter: formatter + ) + } + } } } diff --git a/FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift b/FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift index 1fb2a84c87..ecd77e0f42 100644 --- a/FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift +++ b/FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift @@ -5,8 +5,6 @@ extension PumpSettingsEditor { @Published var maxBasal: Decimal = 0.0 @Published var maxBolus: Decimal = 0.0 @Published var dia: Decimal = 0.0 - @Published var maxCarbs: Decimal = 1000 - @Published var syncInProgress = false override func subscribe() { @@ -14,7 +12,6 @@ extension PumpSettingsEditor { maxBasal = settings.maxBasal maxBolus = settings.maxBolus dia = settings.insulinActionCurve - subscribeSetting(\.maxCarbs, on: $maxCarbs) { maxCarbs = $0 } } func save() { diff --git a/FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift b/FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift index 4611097c4c..5bf0884898 100644 --- a/FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift +++ b/FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift @@ -23,10 +23,6 @@ extension PumpSettingsEditor { Text("Max Bolus") DecimalTextField("U", value: $state.maxBolus, formatter: formatter) } - HStack { - Text("Max Carbs") - DecimalTextField("g", value: $state.maxCarbs, formatter: formatter) - } } Section(header: Text("Duration of Insulin Action")) { diff --git a/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift b/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift index ff19e36bd3..b069218fb6 100644 --- a/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift +++ b/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift @@ -94,14 +94,15 @@ extension Settings { .frame(maxWidth: .infinity, alignment: .trailing) .buttonStyle(.borderedProminent) } - /* - HStack { - Text("Delete All NS Overrides") - Button("Delete") { state.deleteOverrides() } - .frame(maxWidth: .infinity, alignment: .trailing) - .buttonStyle(.borderedProminent) - .tint(.red) - }*/ + + // Test code + HStack { + Text("Delete All NS Overrides") + Button("Delete") { state.deleteOverrides() } + .frame(maxWidth: .infinity, alignment: .trailing) + .buttonStyle(.borderedProminent) + .tint(.red) + } HStack { Toggle("Ignore flat CGM readings", isOn: $state.disableCGMError) diff --git a/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift b/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift index c8f15b3aa6..5ed218ea1f 100644 --- a/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift +++ b/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift @@ -18,6 +18,8 @@ extension StatConfig { @Published var minimumSMB: Decimal = 0.3 @Published var useInsulinBars: Bool = false @Published var skipGlucoseChart: Bool = false + @Published var disableHypoTreatment: Bool = false + @Published var displayDelta: Bool = false var units: GlucoseUnits = .mmolL @@ -37,6 +39,8 @@ extension StatConfig { subscribeSetting(\.skipBolusScreenAfterCarbs, on: $skipBolusScreenAfterCarbs) { skipBolusScreenAfterCarbs = $0 } subscribeSetting(\.oneDimensionalGraph, on: $oneDimensionalGraph) { oneDimensionalGraph = $0 } subscribeSetting(\.useInsulinBars, on: $useInsulinBars) { useInsulinBars = $0 } + subscribeSetting(\.disableHypoTreatment, on: $disableHypoTreatment) { disableHypoTreatment = $0 } + subscribeSetting(\.displayDelta, on: $displayDelta) { displayDelta = $0 } subscribeSetting(\.low, on: $low, initial: { let value = max(min($0, 90), 40) diff --git a/FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift b/FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift index c68125b5d7..eaf09bafa2 100644 --- a/FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift +++ b/FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift @@ -63,6 +63,7 @@ extension StatConfig { Section { Toggle("Never display the small glucose chart when scrolling", isOn: $state.skipGlucoseChart) Toggle("Always Color Glucose Value (green, yellow etc)", isOn: $state.alwaysUseColors) + Toggle("Display Glucose Delta", isOn: $state.displayDelta) } header: { Text("Header settings") } footer: { Text("Normally glucose is colored red only when over or under your notification limits for high/low") } @@ -86,6 +87,7 @@ extension StatConfig { Section { Toggle("Skip Bolus screen after carbs", isOn: $state.skipBolusScreenAfterCarbs) Toggle("Display and allow Fat and Protein entries", isOn: $state.useFPUconversion) + Toggle("Disable Hypo Treatments", isOn: $state.disableHypoTreatment) } header: { Text("Add Meal View settings ") } } .dynamicTypeSize(...DynamicTypeSize.xxLarge) diff --git a/FreeAPS/Sources/Services/Calendar/CalendarManager.swift b/FreeAPS/Sources/Services/Calendar/CalendarManager.swift index bfe0afea46..52ff1240a5 100644 --- a/FreeAPS/Sources/Services/Calendar/CalendarManager.swift +++ b/FreeAPS/Sources/Services/Calendar/CalendarManager.swift @@ -106,16 +106,10 @@ final class BaseCalendarManager: CalendarManager, Injectable { // Latest Loop data (from CoreData) var freshLoop: Double = 20 - var lastLoop = [LastLoop]() - if displeyCOBandIOB || displayEmojis { - coredataContext.performAndWait { - let requestLastLoop = LastLoop.fetchRequest() as NSFetchRequest - let sortLoops = NSSortDescriptor(key: "timestamp", ascending: false) - requestLastLoop.sortDescriptors = [sortLoops] - requestLastLoop.fetchLimit = 1 - try? lastLoop = coredataContext.fetch(requestLastLoop) - } - freshLoop = -1 * (lastLoop.first?.timestamp ?? .distantPast).timeIntervalSinceNow.minutes + var lastLoop: LastLoop? + if displeyCOBandIOB || displayEmojis, let recentLoop = CoreDataStorage().fetchLastLoop() { + lastLoop = recentLoop + freshLoop = -1 * (recentLoop.timestamp ?? .distantPast).timeIntervalSinceNow.minutes } var glucoseIcon = "🟢" @@ -137,8 +131,8 @@ final class BaseCalendarManager: CalendarManager, Injectable { .string(from: Double(settingsManager.settings.units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! } ?? "--" - let iobText = iobFormatter.string(from: (lastLoop.first?.iob ?? 0) as NSNumber) ?? "" - let cobText = cobFormatter.string(from: (lastLoop.first?.cob ?? 0) as NSNumber) ?? "" + let iobText = lastLoop != nil ? (iobFormatter.string(from: (lastLoop?.iob ?? 0) as NSNumber) ?? "") : "" + let cobText = lastLoop != nil ? (cobFormatter.string(from: (lastLoop?.cob ?? 0) as NSNumber) ?? "") : "" var glucoseDisplayText = displayEmojis ? glucoseIcon + " " : "" glucoseDisplayText += glucoseText + " " + directionText + " " + deltaText diff --git a/FreeAPS/Sources/Services/LiveActivity/LiveActitiyShared.swift b/FreeAPS/Sources/Services/LiveActivity/LiveActitiyShared.swift index 5d9444d7b9..f1cd242460 100644 --- a/FreeAPS/Sources/Services/LiveActivity/LiveActitiyShared.swift +++ b/FreeAPS/Sources/Services/LiveActivity/LiveActitiyShared.swift @@ -7,6 +7,11 @@ struct LiveActivityAttributes: ActivityAttributes { let direction: String? let change: String let date: Date + let iob: String + let cob: String + let loopDate: Date + let eventual: String + let mmol: Bool } let startDate: Date diff --git a/FreeAPS/Sources/Services/LiveActivity/LiveActivityBridge.swift b/FreeAPS/Sources/Services/LiveActivity/LiveActivityBridge.swift index 3c4b4c5a1a..7ab223735a 100644 --- a/FreeAPS/Sources/Services/LiveActivity/LiveActivityBridge.swift +++ b/FreeAPS/Sources/Services/LiveActivity/LiveActivityBridge.swift @@ -18,23 +18,47 @@ extension LiveActivityAttributes.ContentState { formatter.roundingMode = .halfUp return formatter - .string(from: mmol ? value.asMmolL as NSNumber : NSNumber(value: value))! + .string(from: mmol ? value.asMmolL as NSNumber : NSNumber(value: value)) ?? "" } - init?(new bg: BloodGlucose, prev: BloodGlucose?, mmol: Bool) { - guard let glucose = bg.glucose else { - return nil - } - - let formattedBG = Self.formatGlucose(glucose, mmol: mmol, forceSign: false) + static func formatter(_ string: NSNumber) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 1 + return formatter.string(from: string) ?? "" + } - let trendString = bg.direction?.symbol + static func carbFormatter(_ string: NSNumber) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 0 + return formatter.string(from: string) ?? "" + } - let change = prev?.glucose.map({ - Self.formatGlucose(glucose - $0, mmol: mmol, forceSign: true) - }) ?? "" + init?(new bg: Readings?, prev: Readings?, mmol: Bool, suggestion: Suggestion, loopDate: Date) { + guard let glucose = bg?.glucose else { + return nil + } - self.init(bg: formattedBG, direction: trendString, change: change, date: bg.dateString) + let formattedBG = Self.formatGlucose(Int(glucose), mmol: mmol, forceSign: false) + let trendString = bg?.direction + let change = Self.formatGlucose(Int((bg?.glucose ?? 0) - (prev?.glucose ?? 0)), mmol: mmol, forceSign: true) + let cobString = Self.carbFormatter((suggestion.cob ?? 0) as NSNumber) + let iobString = Self.formatter((suggestion.iob ?? 0) as NSNumber) + let eventual = Self.formatGlucose(suggestion.eventualBG ?? 100, mmol: mmol, forceSign: false) + let mmol = mmol + + self.init( + bg: formattedBG, + direction: trendString, + change: change, + date: bg?.date ?? Date.now, + iob: iobString, + cob: cobString, + loopDate: loopDate, + eventual: eventual, + mmol: mmol + ) } } @@ -60,7 +84,7 @@ extension LiveActivityAttributes.ContentState { @available(iOS 16.2, *) final class LiveActivityBridge: Injectable, ObservableObject { @Injected() private var settingsManager: SettingsManager! - @Injected() private var glucoseStorage: GlucoseStorage! + @Injected() private var storage: FileStorage! @Injected() private var broadcaster: Broadcaster! private let activityAuthorizationInfo = ActivityAuthorizationInfo() @@ -71,13 +95,16 @@ extension LiveActivityAttributes.ContentState { } private var currentActivity: ActiveActivity? - private var latestGlucose: BloodGlucose? + private var latestGlucose: Readings? + private var loopDate: Date? + private var suggestion: Suggestion? init(resolver: Resolver) { systemEnabled = activityAuthorizationInfo.areActivitiesEnabled injectServices(resolver) - broadcaster.register(GlucoseObserver.self, observer: self) + broadcaster.register(SuggestionObserver.self, observer: self) + broadcaster.register(EnactedSuggestionObserver.self, observer: self) Foundation.NotificationCenter.default.addObserver( forName: UIApplication.didEnterBackgroundNotification, @@ -110,15 +137,16 @@ extension LiveActivityAttributes.ContentState { } } - /// creates and tries to present a new activity update from the current GlucoseStorage values if live activities are enabled in settings + /// creates and tries to present a new activity update from the current Suggestion values if live activities are enabled in settings /// Ends existing live activities if live activities are not enabled in settings private func forceActivityUpdate() { // just before app resigns active, show a new activity // only do this if there is no current activity or the current activity is older than 1h if settings.useLiveActivity { - if currentActivity?.needsRecreation() ?? true + if currentActivity?.needsRecreation() ?? true, + let suggestion = storage.retrieveFile(OpenAPS.Enact.suggested, as: Suggestion.self) { - glucoseDidUpdate(glucoseStorage.recent()) + suggestionDidUpdate(suggestion) } } else { Task { @@ -144,7 +172,7 @@ extension LiveActivityAttributes.ContentState { } else { let content = ActivityContent( state: state, - staleDate: min(state.date, Date.now).addingTimeInterval(TimeInterval(6 * 60)) + staleDate: min(state.date, Date.now).addingTimeInterval(TimeInterval(8 * 60)) ) await currentActivity.activity.update(content) @@ -152,10 +180,18 @@ extension LiveActivityAttributes.ContentState { } else { do { // always push a non-stale content as the first update - // pushing a stale content as the frst content results in the activity not being shown at all + // pushing a stale content as the first content results in the activity not being shown at all // we want it shown though even if it is iniially stale, as we expect new BG readings to become available soon, which should then be displayed let nonStale = ActivityContent( - state: LiveActivityAttributes.ContentState(bg: "--", direction: nil, change: "--", date: Date.now), + state: LiveActivityAttributes.ContentState( + bg: "--", + direction: nil, + change: "--", + date: Date.now, + iob: "--", + cob: "--", + loopDate: Date.now, eventual: "--", mmol: false + ), staleDate: Date.now.addingTimeInterval(60) ) @@ -190,8 +226,8 @@ extension LiveActivityAttributes.ContentState { } @available(iOS 16.2, *) -extension LiveActivityBridge: GlucoseObserver { - func glucoseDidUpdate(_ glucose: [BloodGlucose]) { +extension LiveActivityBridge: SuggestionObserver, EnactedSuggestionObserver { + func enactedSuggestionDidUpdate(_ suggestion: Suggestion) { guard settings.useLiveActivity else { if currentActivity != nil { Task { @@ -200,21 +236,50 @@ extension LiveActivityBridge: GlucoseObserver { } return } - - // backfill latest glucose if contained in this update - if glucose.count > 1 { - latestGlucose = glucose[glucose.count - 2] + defer { self.suggestion = suggestion } + + let cd = CoreDataStorage() + let glucose = cd.fetchGlucose(interval: DateFilter().twoHours) + let prev = glucose.count > 1 ? glucose[1] : glucose.first + + guard let content = LiveActivityAttributes.ContentState( + new: glucose.first, + prev: prev, + mmol: settings.units == .mmolL, + suggestion: suggestion, + loopDate: (suggestion.recieved ?? false) ? (suggestion.timestamp ?? .distantPast) : + (cd.fetchLastLoop()?.timestamp ?? .distantPast) + ) else { + return } - defer { - self.latestGlucose = glucose.last + + Task { + await self.pushUpdate(content) } + } - guard let bg = glucose.last, let content = LiveActivityAttributes.ContentState( - new: bg, - prev: latestGlucose, - mmol: settings.units == .mmolL + func suggestionDidUpdate(_ suggestion: Suggestion) { + guard settings.useLiveActivity else { + if currentActivity != nil { + Task { + await self.endActivity() + } + } + return + } + defer { self.suggestion = suggestion } + + let cd = CoreDataStorage() + let glucose = cd.fetchGlucose(interval: DateFilter().twoHours) + let prev = glucose.count > 1 ? glucose[1] : glucose.first + + guard let content = LiveActivityAttributes.ContentState( + new: glucose.first, + prev: prev, + mmol: settings.units == .mmolL, + suggestion: suggestion, + loopDate: settings.closedLoop ? (cd.fetchLastLoop()?.timestamp ?? .distantPast) : suggestion.timestamp ?? .distantPast ) else { - // no bg or value, can't update the live activity return } diff --git a/FreeAPS/Sources/Shortcuts/Overrides/OverrideShortcuts.swift b/FreeAPS/Sources/Shortcuts/Overrides/OverrideShortcuts.swift index 3ac741696f..08be5e0864 100644 --- a/FreeAPS/Sources/Shortcuts/Overrides/OverrideShortcuts.swift +++ b/FreeAPS/Sources/Shortcuts/Overrides/OverrideShortcuts.swift @@ -219,6 +219,10 @@ enum OverrideIntentError: Error { // Update in Nightscout nightscoutManager.editOverride(preset, duration, activeOveride.date ?? Date.now) } + } else if activeOveride.isPreset { + if let duration = overrideStorage.cancelProfile() { + nightscoutManager.editOverride("📉", duration, activeOveride.date ?? Date.now) + } } else { let nsString = activeOveride.percentage.formatted() != "100" ? activeOveride.percentage .formatted() + " %" : "Custom" diff --git a/FreeAPS/Sources/Views/TagCloudView.swift b/FreeAPS/Sources/Views/TagCloudView.swift index f10d7edfe6..847cfbbdef 100644 --- a/FreeAPS/Sources/Views/TagCloudView.swift +++ b/FreeAPS/Sources/Views/TagCloudView.swift @@ -58,7 +58,7 @@ struct TagCloudView: View { case textTag where textTag.contains("SMB Delivery Ratio:"): return .uam case textTag where textTag.contains("Bolus"), - textTag where textTag.contains("TDD:"): + textTag where textTag.contains("Insulin 24h:"): return .purple case textTag where textTag.contains("tdd_factor"), textTag where textTag.contains("Sigmoid function"), diff --git a/FreeAPS/Sources/Views/ViewModifiers.swift b/FreeAPS/Sources/Views/ViewModifiers.swift index 5e2d71ae00..25862ffe0f 100644 --- a/FreeAPS/Sources/Views/ViewModifiers.swift +++ b/FreeAPS/Sources/Views/ViewModifiers.swift @@ -49,24 +49,20 @@ struct CompactSectionSpacing: ViewModifier { } } -struct ScrollTargetLayoutModifier: ViewModifier { - func body(content: Content) -> some View { - if #available(iOS 17, *) { - return content - .scrollTargetLayout() - } else { - return content } - } -} - -struct ScrollPositionModifier: ViewModifier { - @Binding var id: Int? - func body(content: Content) -> some View { - if #available(iOS 17, *) { - return content - .scrollPosition(id: $id) +struct InfoPanelBackground: View { + let colorScheme: ColorScheme + var body: some View { + if #available(iOS 17.0, *) { + Rectangle() + .stroke(.gray, lineWidth: 2) + .fill(colorScheme == .light ? .white : .black) + .frame(height: 24) } else { - return content } + Rectangle() + .strokeBorder(.gray, lineWidth: 2) + .background(Rectangle().fill(colorScheme == .light ? .white : .black)) + .frame(height: 24) + } } } @@ -167,7 +163,7 @@ struct LoopEllipse: View { .stroke(stroke, lineWidth: colorScheme == .light ? 2 : 1) .background( RoundedRectangle(cornerRadius: 15) - .fill(Color.white).opacity(colorScheme == .light ? 0.2 : 0.08) + .fill(colorScheme == .light ? .white : .black) ) } } @@ -205,6 +201,22 @@ struct ClockOffset: View { } } +struct TooOldValue: View { + var body: some View { + ZStack { + Image(systemName: "cicle.fill") + .resizable() + .frame(maxHeight: 20) + .symbolRenderingMode(.palette) + .foregroundStyle(Color(.warning).opacity(0.5)) + .offset(x: 5, y: -13) + .overlay { + Text("Old").font(.caption) + } + } + } +} + struct ChartBackground: ViewModifier { @Environment(\.colorScheme) var colorScheme @@ -345,14 +357,6 @@ extension View { modifier(CompactSectionSpacing()) } - func scrollTargetLayoutiOS17() -> some View { - modifier(ScrollTargetLayoutModifier()) - } - - func scrollPositioniOS17(id: Binding) -> some View { - modifier(ScrollPositionModifier(id: id)) - } - func asAny() -> AnyView { .init(self) } } diff --git a/LiveActivity/LiveActivity.swift b/LiveActivity/LiveActivity.swift index 85c7fb14e9..45939d191f 100644 --- a/LiveActivity/LiveActivity.swift +++ b/LiveActivity/LiveActivity.swift @@ -10,19 +10,27 @@ private enum Size { struct LiveActivity: Widget { private let dateFormatter: DateFormatter = { - var f = DateFormatter() - f.dateStyle = .none - f.timeStyle = .short - return f + var formatter = DateFormatter() + formatter.dateStyle = .none + formatter.timeStyle = .short + return formatter }() + private let minuteFormatter: NumberFormatter = { + var formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 0 + return formatter + }() + + @Environment(\.dynamicTypeSize) private var fontSize + @ViewBuilder private func changeLabel(context: ActivityViewContext) -> some View { if !context.state.change.isEmpty { - if context.isStale { - Text(context.state.change).foregroundStyle(.primary.opacity(0.5)) - .strikethrough(pattern: .solid, color: .red.opacity(0.6)) - } else { + if !context.isStale { Text(context.state.change) + } else { + Text("old").foregroundStyle(.secondary) } } else { Text("--") @@ -30,16 +38,8 @@ struct LiveActivity: Widget { } private func updatedLabel(context: ActivityViewContext) -> Text { - let text = Text("Updated: \(dateFormatter.string(from: context.state.date))") - if context.isStale { - if #available(iOSApplicationExtension 17.0, *) { - return text.bold().foregroundStyle(.red) - } else { - return text.bold().foregroundColor(.red) - } - } else { - return text - } + let text = Text("\(dateFormatter.string(from: context.state.loopDate))") + return text } private func bgAndTrend(context: ActivityViewContext, size: Size) -> (some View, Int) { @@ -77,7 +77,7 @@ struct LiveActivity: Widget { let stack = HStack(spacing: spacing) { Text(bgText) - .strikethrough(context.isStale, pattern: .solid, color: .red.opacity(0.6)) + if let direction = directionText { let text = Text(direction) switch size { @@ -92,28 +92,86 @@ struct LiveActivity: Widget { text.scaleEffect(x: 0.8, y: 0.8, anchor: .leading).padding(.trailing, -3) case .expanded: - text.scaleEffect(x: 0.7, y: 0.7, anchor: .leading).padding(.trailing, -5) + text.scaleEffect(x: 0.7, y: 0.7, anchor: .center).padding(.trailing, -5) } } } - .foregroundStyle(context.isStale ? Color.primary.opacity(0.5) : Color.primary) + .foregroundStyle(context.isStale ? .secondary : Color.primary) return (stack, characters) } + private func iob(context: ActivityViewContext, size _: Size) -> some View { + HStack(spacing: 0) { + Text(context.state.iob) + Text(" U") + } + .foregroundStyle(.insulin) + } + + private func cob(context: ActivityViewContext, size _: Size) -> some View { + HStack(spacing: 0) { + Text(context.state.cob) + Text(" g") + } + .foregroundStyle(.loopYellow) + } + + private func loop(context: ActivityViewContext, size: CGFloat) -> some View { + let timeAgo = abs(context.state.loopDate.timeIntervalSinceNow) / 60 + let color: Color = timeAgo > 8 ? .loopYellow : timeAgo > 12 ? .loopRed : .loopGreen + return LoopActivity(stroke: color, compact: size == 12).frame(width: size) + } + + private var emptyText: some View { + Text(" ").font(.caption).offset(x: 0, y: -5) + } + var body: some WidgetConfiguration { ActivityConfiguration(for: LiveActivityAttributes.self) { context in // Lock screen/banner UI goes here - HStack(spacing: 3) { - bgAndTrend(context: context, size: .expanded).0.font(.title) - Spacer() - VStack(alignment: .trailing, spacing: 5) { - changeLabel(context: context).font(.title3) + VStack(spacing: 2) { + ZStack { updatedLabel(context: context).font(.caption).foregroundStyle(.primary.opacity(0.7)) + .frame(maxWidth: .infinity, alignment: .trailing) + } + HStack { + VStack { + loop(context: context, size: 22) + emptyText + }.offset(x: 0, y: 2) + Spacer() + VStack { + bgAndTrend(context: context, size: .expanded).0.font(.title) + changeLabel(context: context).font(.caption).foregroundStyle(.primary.opacity(0.7)).offset(x: -12, y: -5) + } + Spacer() + VStack { + iob(context: context, size: .expanded).font(.title) + emptyText + } + Spacer() + VStack { + cob(context: context, size: .expanded).font(.title) + emptyText + } } + HStack { + Spacer() + Text(NSLocalizedString("Eventual Glucose", comment: "")) + Spacer() + Text(context.state.eventual) + Text(context.state.mmol ? NSLocalizedString( + "mmol/L", + comment: "The short unit display string for millimoles of glucose per liter" + ) : NSLocalizedString( + "mg/dL", + comment: "The short unit display string for milligrams of glucose per decilter" + )).foregroundStyle(.secondary) + }.padding(.top, 10) } .privacySensitive() - .padding(.all, 15) + .padding(.vertical, 10).padding(.horizontal, 15) // Semantic BackgroundStyle and Color values work here. They adapt to the given interface style (light mode, dark mode) // Semantic UIColors do NOT (as of iOS 17.1.1). Like UIColor.systemBackgroundColor (it does not adapt to changes of the interface style) // The colorScheme environment varaible that is usually used to detect dark mode does NOT work here (it reports false values) @@ -127,12 +185,25 @@ struct LiveActivity: Widget { DynamicIslandExpandedRegion(.leading) { bgAndTrend(context: context, size: .expanded).0.font(.title2).padding(.leading, 5) } + + DynamicIslandExpandedRegion(.center) { + HStack { + iob(context: context, size: .expanded).font(.title2).padding(.leading, 5) + cob(context: context, size: .expanded).font(.title2).padding(.horizontal, 10) + } + } + DynamicIslandExpandedRegion(.trailing) { - changeLabel(context: context).font(.title2).padding(.trailing, 5) + loop(context: context, size: 23) } DynamicIslandExpandedRegion(.bottom) { Group { - updatedLabel(context: context).font(.caption).foregroundStyle(Color.secondary) + ZStack { + changeLabel(context: context).font(.title2).padding(.trailing, 5) + .frame(maxWidth: .infinity, alignment: .leading) + updatedLabel(context: context).font(.caption).foregroundStyle(Color.secondary) + .frame(maxWidth: .infinity, alignment: .trailing) + } } .frame( maxHeight: .infinity, @@ -140,7 +211,10 @@ struct LiveActivity: Widget { ) } } compactLeading: { - bgAndTrend(context: context, size: .compact).0.padding(.leading, 4) + HStack { + loop(context: context, size: 12) + bgAndTrend(context: context, size: .compact).0.padding(.leading, 4) + } } compactTrailing: { changeLabel(context: context).padding(.trailing, 4) } minimal: { @@ -157,7 +231,7 @@ struct LiveActivity: Widget { } } .widgetURL(URL(string: "freeaps-x://")) - .keylineTint(Color.purple) + // .keylineTint(Color.purple) .contentMargins(.horizontal, 0, for: .minimal) .contentMargins(.trailing, 0, for: .compactLeading) .contentMargins(.leading, 0, for: .compactTrailing) @@ -176,24 +250,64 @@ private extension LiveActivityAttributes.ContentState { // Use mmol/l notation with decimal point as well for the same reason, it uses up to 4 characters, while mg/dl uses up to 3 static var testWide: LiveActivityAttributes.ContentState { - LiveActivityAttributes.ContentState(bg: "00.0", direction: "→", change: "+0.0", date: Date()) + LiveActivityAttributes.ContentState( + bg: "00.0", + direction: "→", + change: "+0.0", + date: Date(), + iob: "1.2", + cob: "20", + loopDate: Date.now, eventual: "100", mmol: false + ) } static var testVeryWide: LiveActivityAttributes.ContentState { - LiveActivityAttributes.ContentState(bg: "00.0", direction: "↑↑", change: "+0.0", date: Date()) + LiveActivityAttributes.ContentState( + bg: "00.0", + direction: "↑↑", + change: "+0.0", + date: Date(), + iob: "1.2", + cob: "20", + loopDate: Date.now, eventual: "100", mmol: false + ) } static var testSuperWide: LiveActivityAttributes.ContentState { - LiveActivityAttributes.ContentState(bg: "00.0", direction: "↑↑↑", change: "+0.0", date: Date()) + LiveActivityAttributes.ContentState( + bg: "00.0", + direction: "↑↑↑", + change: "+0.0", + date: Date(), + iob: "1.2", + cob: "20", + loopDate: Date.now, eventual: "100", mmol: false + ) } // 2 characters for BG, 1 character for change is the minimum that will be shown static var testNarrow: LiveActivityAttributes.ContentState { - LiveActivityAttributes.ContentState(bg: "00", direction: "↑", change: "+0", date: Date()) + LiveActivityAttributes.ContentState( + bg: "00", + direction: "↑", + change: "+0", + date: Date(), + iob: "1.2", + cob: "20", + loopDate: Date.now, eventual: "100", mmol: false + ) } static var testMedium: LiveActivityAttributes.ContentState { - LiveActivityAttributes.ContentState(bg: "000", direction: "↗︎", change: "+00", date: Date()) + LiveActivityAttributes.ContentState( + bg: "000", + direction: "↗︎", + change: "+00", + date: Date(), + iob: "1.2", + cob: "20", + loopDate: Date.now, eventual: "100", mmol: false + ) } } @@ -207,3 +321,13 @@ private extension LiveActivityAttributes.ContentState { LiveActivityAttributes.ContentState.testMedium LiveActivityAttributes.ContentState.testNarrow } + +struct LoopActivity: View { + @Environment(\.colorScheme) var colorScheme + let stroke: Color + let compact: Bool + var body: some View { + Circle() + .stroke(stroke, lineWidth: compact ? 1.5 : 3) + } +} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index aa3541aaca..5e6b47734d 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -22,7 +22,7 @@ FASTLANE_KEY = ENV["FASTLANE_KEY"] DEVICE_NAME = ENV["DEVICE_NAME"] DEVICE_ID = ENV["DEVICE_ID"] ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120" -APP_IDENTIFIER = "ru.artpancreas.#{TEAMID}.FreeAPS" +APP_IDENTIFIER = "com.jon.#{TEAMID}.aps" platform :ios do desc "Build iAPS" diff --git a/package.json b/package.json new file mode 100644 index 0000000000..587d4a457d --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "build": "webpack --config ./scripts/webpack.config.js" + }, + "devDependencies": { + "webpack-cli": "^5.1.4" + } +} diff --git a/scripts/webpack.config.js b/scripts/webpack.config.js index 7f070b5635..69968af4c7 100644 --- a/scripts/webpack.config.js +++ b/scripts/webpack.config.js @@ -1,31 +1,43 @@ const path = require('path'); const TerserPlugin = require("terser-webpack-plugin"); +const libPath = process.env['OREF0_DIST_PATH'] || './lib' + module.exports = { mode: 'production', entry: { - iob: './lib/iob/index.js', - meal: './lib/meal/index.js', - "determineBasal": './lib/determine-basal/determine-basal.js', - "glucoseGetLast": './lib/glucose-get-last.js', - "basalSetTemp": './lib/basal-set-temp.js', - autosens: './lib/determine-basal/autosens.js', - profile: './lib/profile/index.js', - "autotunePrep": './lib/autotune-prep/index.js', - "autotuneCore": './lib/autotune/index.js' + iob: path.resolve(libPath, 'iob/index.js'), + meal: path.resolve(libPath, 'meal/index.js'), + determineBasal: path.resolve(libPath, 'determine-basal/determine-basal.js'), + glucoseGetLast: path.resolve(libPath, 'glucose-get-last.js'), + basalSetTemp: path.resolve(libPath, 'basal-set-temp.js'), + autosens: path.resolve(libPath, 'determine-basal/autosens.js'), + profile: path.resolve(libPath, 'profile/index.js'), + autotunePrep: path.resolve(libPath, 'autotune-prep/index.js'), + autotuneCore: path.resolve(libPath, 'autotune/index.js') }, output: { - path: path.resolve(__dirname, 'dist'), + path: path.resolve(__dirname, '..', 'FreeAPS', 'Resources', 'javascript', 'bundle'), filename: (pathData) => { return pathData.chunk.name.replace(/[A-Z]/g, function(match) { return '-' + match.toLowerCase(); }) + '.js'; }, - libraryTarget: 'var', - library: 'freeaps_[name]' + library: { + type: 'var', + name: 'freeaps_[name]' + } }, optimization: { minimize: true, - minimizer: [new TerserPlugin()], + minimizer: [new TerserPlugin({ + extractComments: false, + parallel: true, + terserOptions: { + format: { + comments: false, + }, + }, + })], }, };