From c5454af039915a26a902fe602875c935290a863c 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: Thu, 1 Aug 2024 20:38:13 +0200 Subject: [PATCH] V4.8.0 Omnipod updates ported by @itsmojo: LoopKit/OmniBLE#125 & LoopKit/OmniKit#36, fix bolusState & basalDeliveryState use !isFinished() again LoopKit/OmniKit#36, update OmniKit setStateWithResult() to match OmniBLE version LoopKit/OmniBLE#126 & LoopKit/OmniKit#37, fix incorrect pod suspended message on bolus after an error Remove no longer needed APSManager updateStatus() to avoid extra get pod status commands Nightscout Glucose backfill resolved: #775 Resolves several old issues with import/upload to Nightscout and to the statistics database. Prepare for the configuration profiles and the onboarding Views coming in v4.9, for instance backup of your CoreData. f0d1d52 Make Oref0 error string in pop-up more accessible: 41d2b44 Localization of the Sharing strings, by @Mirko-tri Crowdin translations from translators: Hung Nguyen Phuteleco, @Mirko-tri, Abdulrahman Alfantokh, @vanzaam, Mykola Yroslavadudko and Typ1er. --- Config.xcconfig | 2 +- .../Core_Data.xcdatamodel/contents | 16 +- .../CGMBLEKit/ar.lproj/Localizable.strings | 8 +- .../CGMBLEKitUI/ar.lproj/Localizable.strings | 6 +- .../ar.lproj/TransmitterManagerSetup.strings | 4 +- .../ar.lproj/Localizable.strings | 50 +-- .../G7SensorKit/ar.lproj/Localizable.strings | 68 +-- .../Resources/vi.lproj/Localizable.strings | 7 +- .../vi.lproj/Localizable.strings | 28 +- .../PumpManager/OmniBLEPumpManager.swift | 26 +- .../OmniBLE/PumpManager/PodCommsSession.swift | 6 + .../PumpManager/OmnipodPumpManager.swift | 50 ++- .../PumpManager/OmnipodPumpManagerState.swift | 2 + .../OmniKit/PumpManager/PodCommsSession.swift | 6 + FreeAPS.xcodeproj/project.pbxproj | 74 +-- FreeAPS/Sources/APS/APSManager.swift | 18 - FreeAPS/Sources/APS/OpenAPS/Constants.swift | 5 + .../Sources/APS/Storage/CoreDataStorage.swift | 128 ++++++ .../Main/ar.lproj/Localizable.strings | 55 ++- .../Main/da.lproj/Localizable.strings | 53 ++- .../Main/de.lproj/Localizable.strings | 55 ++- .../Main/en.lproj/Localizable.strings | 50 ++- .../Main/es.lproj/Localizable.strings | 53 ++- .../Main/fi.lproj/Localizable.strings | 53 ++- .../Main/fr.lproj/Localizable.strings | 135 +++--- .../Main/he.lproj/Localizable.strings | 53 ++- .../Main/hu.lproj/Localizable.strings | 53 ++- .../Main/it.lproj/Localizable.strings | 53 ++- .../Main/nb.lproj/Localizable.strings | 53 ++- .../Main/nl.lproj/Localizable.strings | 59 ++- .../Main/pl.lproj/Localizable.strings | 53 ++- .../Main/pt-BR.lproj/Localizable.strings | 53 ++- .../Main/pt-PT.lproj/Localizable.strings | 53 ++- .../Main/ru.lproj/Localizable.strings | 53 ++- .../Main/sk.lproj/Localizable.strings | 53 ++- .../Main/sv.lproj/Localizable.strings | 53 ++- .../Main/tr.lproj/Localizable.strings | 53 ++- .../Main/uk.lproj/Localizable.strings | 53 ++- .../Main/vi.lproj/Localizable.strings | 103 +++-- .../Main/zh-Hans.lproj/Localizable.strings | 53 ++- FreeAPS/Sources/Models/BloodGlucose.swift | 72 ++- FreeAPS/Sources/Models/DatabaseModels.swift | 100 +++++ .../Models/NightscoutPreferences.swift | 7 - .../Sources/Models/NightscoutSettings.swift | 7 - .../Sources/Models/NightscoutStatistics.swift | 7 - FreeAPS/Sources/Models/NightscoutStatus.swift | 1 + .../DataTable/DataTableStateModel.swift | 5 +- .../Modules/Home/View/HomeRootView.swift | 2 +- .../Sharing/View/SharingRootView.swift | 4 +- .../Sources/Services/Network/Database.swift | 422 ++++++++++++++++++ .../Services/Network/NightscoutManager.swift | 408 ++++++++++++----- .../Services/Storage/FileStorage.swift | 12 +- 52 files changed, 2193 insertions(+), 663 deletions(-) create mode 100644 FreeAPS/Sources/Models/DatabaseModels.swift delete mode 100644 FreeAPS/Sources/Models/NightscoutPreferences.swift delete mode 100644 FreeAPS/Sources/Models/NightscoutSettings.swift delete mode 100644 FreeAPS/Sources/Models/NightscoutStatistics.swift create mode 100644 FreeAPS/Sources/Services/Network/Database.swift diff --git a/Config.xcconfig b/Config.xcconfig index 75a089bb5a..3ad19d0021 100644 --- a/Config.xcconfig +++ b/Config.xcconfig @@ -1,5 +1,5 @@ APP_DISPLAY_NAME = iAPS -APP_VERSION = 4.6.0 +APP_VERSION = 4.8.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 4e30b53910..0f34a2efc9 100644 --- a/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents +++ b/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents @@ -1,5 +1,10 @@ - + + + + + + @@ -71,6 +76,10 @@ + + + + @@ -126,6 +135,11 @@ + + + + + diff --git a/Dependencies/CGMBLEKit/CGMBLEKit/ar.lproj/Localizable.strings b/Dependencies/CGMBLEKit/CGMBLEKit/ar.lproj/Localizable.strings index 603776c285..09777b4c0c 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKit/ar.lproj/Localizable.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKit/ar.lproj/Localizable.strings @@ -5,7 +5,7 @@ "Dexcom G6" = "ديكسكوم G6"; /* Error description for unreliable state */ -"Glucose data is unavailable" = "Glucose data is unavailable"; +"Glucose data is unavailable" = "قراءات السكر غير متوفرة"; /* Describes a low battery */ "Low Battery" = "البطارية منخفضة"; @@ -26,13 +26,13 @@ "Sensor calibration is OK" = "معايرة المستشعر مضبوطة"; /* The description of sensor calibration state when raw value is unknown. (1: missing data details) */ -"Sensor is in unknown state %1$d" = "Sensor is in unknown state %1$d"; +"Sensor is in unknown state %1$d" = "المستشعر في حالة غير معروفة %1$d"; /* The description of sensor calibration state when sensor sensor is stopped. */ -"Sensor is stopped" = "Sensor is stopped"; +"Sensor is stopped" = "المستشعر متوقف"; /* The description of sensor calibration state when sensor sensor is warming up. */ -"Sensor is warming up" = "Sensor is warming up"; +"Sensor is warming up" = "المستشعر تحت الإحماء"; /* The description of sensor calibration state when sensor needs calibration. */ "Sensor needs calibration" = "المستشعر بحاجة الى معايرة"; diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings index 19c2951fec..789c5839f8 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* Format string for glucose trend per minute. (1: glucose value and unit) */ -"%@/min" = "%@/min"; +"%@/min" = "%@ دقيقة"; /* Confirmation message for deleting a CGM */ "Are you sure you want to delete this CGM?" = "هل أنت متأكد أنك تريد حذف هذا CGM؟"; @@ -54,7 +54,7 @@ Title text for the button to remove a CGM from Loop */ "Transmitter ID" = "Transmitter ID"; /* Title describing glucose trend */ -"Trend" = "Trend"; +"Trend" = "إتجاه"; /* The title text for the upload glucose switch cell */ -"Upload Readings" = "Upload Readings"; +"Upload Readings" = "رفع القراءات"; diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings index b3b8878728..1ab79b565f 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings +++ b/Dependencies/CGMBLEKit/CGMBLEKitUI/ar.lproj/TransmitterManagerSetup.strings @@ -17,7 +17,7 @@ "k1N-Rg-XDy.footerTitle" = "يمكن تحميل البيانات عبر الإنترنت من تطبيق Share عند فشل اتصال المرسل."; /* Class = "UITableViewSection"; headerTitle = "Dexcom Share"; ObjectID = "k1N-Rg-XDy"; */ -"k1N-Rg-XDy.headerTitle" = "Dexcom Share"; +"k1N-Rg-XDy.headerTitle" = "ديكسكوم شير"; /* Class = "UITextField"; placeholder = "Enter the 6-digit transmitter ID"; ObjectID = "nKX-TW-GhD"; */ -"nKX-TW-GhD.placeholder" = "Enter the 6-digit transmitter ID"; +"nKX-TW-GhD.placeholder" = "أدخل معرف جهاز الإرسال المكون من 6 أرقام"; diff --git a/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings b/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings index 9b9a6b9523..90c967438f 100644 --- a/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings +++ b/Dependencies/G7SensorKit/G7SensorKitUI/ar.lproj/Localizable.strings @@ -2,13 +2,13 @@ "– – –" = "– – –"; /* Format string for glucose trend per minute. (1: glucose value and unit) */ -"%@/min" = "%@/min"; +"%@/min" = "%@ دقيقة"; /* No comment provided by engineer. */ "Are you sure you want to delete this CGM?" = "Are you sure you want to delete this CGM?"; /* No comment provided by engineer. */ -"Bluetooth" = "Bluetooth"; +"Bluetooth" = "البلوتوث"; /* Button text to cancel G7 setup */ "Cancel" = "Cancel"; @@ -30,7 +30,7 @@ /* Navigation bar title for G7SettingsView Title on WelcomeView */ -"Dexcom G7" = "Dexcom G7"; +"Dexcom G7" = "ديكسكوم G7"; /* No comment provided by engineer. */ "Done" = "Done"; @@ -39,79 +39,79 @@ "Glucose" = "Glucose"; /* title for g7 settings row showing sensor grace period end time */ -"Grace Period End" = "Grace Period End"; +"Grace Period End" = "نهاية الفترة"; /* G7 Progress bar label when sensor grace period progress showing */ -"Grace period remaining" = "Grace period remaining"; +"Grace period remaining" = "الفترة المتبقية"; /* String displayed instead of a glucose value above the CGM range */ -"HIGH" = "HIGH"; +"HIGH" = "مرتفع"; /* title for g7 settings row showing sensor last connect time */ -"Last Connect" = "Last Connect"; +"Last Connect" = "آخر اتصال"; /* No comment provided by engineer. */ -"Last Reading" = "Last Reading"; +"Last Reading" = "آخر قراءة"; /* Descriptive text on G7StartupView */ -"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management."; +"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "يمكن لـ iAPS قراءة بيانات G7، ولكن لا يزال عليك استخدام تطبيق ديكسكوم G7 للإقران والمعايرة وإدارة المستشعرات الأخرى."; /* String displayed instead of a glucose value below the CGM range */ -"LOW" = "LOW"; +"LOW" = "منخفض"; /* title for g7 settings row showing BLE Name */ "Name" = "Name"; /* No comment provided by engineer. */ -"Scan for new sensor" = "Scan for new sensor"; +"Scan for new sensor" = "البحث عن مستشعر جديد"; /* title for g7 settings connection status when scanning */ -"Scanning" = "Scanning"; +"Scanning" = "يتم المسح"; /* G7 Status highlight text for searching for sensor */ -"Searching for\nSensor" = "Searching for\nSensor"; +"Searching for\nSensor" = "البحث عن مستشعر\n"; /* G7 Progress bar label when searching for sensor */ -"Searching for sensor" = "Searching for sensor"; +"Searching for sensor" = "البحث عن مستشعر"; /* G7 Status highlight text for sensor expired */ -"Sensor\nExpired" = "Sensor\nExpired"; +"Sensor\nExpired" = "إنتهاء صلاحية المستشعر\n"; /* G7 Status highlight text for sensor failed */ -"Sensor\nFailed" = "Sensor\nFailed"; +"Sensor\nFailed" = "فشل جهاز الاستشعار\n"; /* G7 Status highlight text for sensor error */ -"Sensor\nIssue" = "Sensor\nIssue"; +"Sensor\nIssue" = "مشكلة في المستشعر\n"; /* G7 Status highlight text for sensor warmup */ "Sensor\nWarmup" = "Sensor\nWarmup"; /* title for g7 settings row showing sensor expiration time */ -"Sensor Expiration" = "Sensor Expiration"; +"Sensor Expiration" = "إنتهت صلاحية المستشعر"; /* G7 Progress bar label when sensor expired */ -"Sensor expired" = "Sensor expired"; +"Sensor expired" = "إنتهت صلاحية المستشعر"; /* G7 Progress bar label when sensor lifetime progress showing */ -"Sensor expires" = "Sensor expires"; +"Sensor expires" = "إنتهت صلاحية المستشعر"; /* G7 Progress bar label when sensor failed */ -"Sensor failed" = "Sensor failed"; +"Sensor failed" = "فشل الحساس"; /* title for g7 settings row showing sensor start time */ "Sensor Start" = "Start sensor"; /* G7 Status highlight text for signal loss */ -"Signal\nLoss" = "Signal\nLoss"; +"Signal\nLoss" = "فقدان الإشارة\n"; /* Field label */ "Time" = "Time"; /* Field label */ -"Trend" = "Trend"; +"Trend" = "إتجاه"; /* title for g7 config settings to upload readings */ -"Upload Readings" = "Upload Readings"; +"Upload Readings" = "رفع القراءات"; /* G7 Progress bar label when sensor in warmup */ -"Warmup completes" = "Warmup completes"; +"Warmup completes" = "اكتمل الإحماء"; diff --git a/Dependencies/G7SensorKit/ar.lproj/Localizable.strings b/Dependencies/G7SensorKit/ar.lproj/Localizable.strings index cdf9de583c..a1087c7584 100644 --- a/Dependencies/G7SensorKit/ar.lproj/Localizable.strings +++ b/Dependencies/G7SensorKit/ar.lproj/Localizable.strings @@ -1,8 +1,8 @@ /* Title on WelcomeView */ -"Dexcom G7" = "Dexcom G7"; +"Dexcom G7" = "ديكسكوم G7"; /* Descriptive text on G7StartupView */ -"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management."; +"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "يمكن لـ iAPS قراءة بيانات G7، ولكن لا يزال عليك استخدام تطبيق ديكسكوم G7 للإقران والمعايرة وإدارة المستشعرات الأخرى."; /* Button title for starting setup */ "Continue" = "Continue"; @@ -11,51 +11,51 @@ "Cancel" = "Cancel"; /* Error description for unreliable state */ -"Glucose data is unavailable" = "Glucose data is unavailable"; +"Glucose data is unavailable" = "قراءات السكر غير متوفرة"; /* The description of sensor algorithm state when sensor is ok. */ -"Sensor is OK" = "Sensor is OK"; +"Sensor is OK" = "المستشعر يعمل جيداً"; /* The description of sensor algorithm state when sensor is stopped." */ -"Sensor is stopped" = "Sensor is stopped"; +"Sensor is stopped" = "المستشعر متوقف"; /* The description of sensor algorithm state when sensor is warming up. */ -"Sensor is warming up" = "Sensor is warming up"; +"Sensor is warming up" = "المستشعر تحت الإحماء"; /* The description of sensor algorithm state when sensor is expired. */ -"Sensor expired" = "Sensor expired"; +"Sensor expired" = "إنتهت صلاحية المستشعر"; /* The description of sensor algorithm state when sensor failed. */ -"Sensor failed" = "Sensor failed"; +"Sensor failed" = "فشل الحساس"; /* The description of sensor algorithm state when raw value is unknown. (1: missing data details) */ -"Sensor is in unknown state %1$d" = "Sensor is in unknown state %1$d"; +"Sensor is in unknown state %1$d" = "المستشعر في حالة غير معروفة %1$d"; /* title for g7 settings row showing sensor start time */ -"Sensor Start" = "Sensor Start"; +"Sensor Start" = "ابدأ الحساس"; /* title for g7 settings row showing sensor expiration time */ -"Sensor Expiration" = "Sensor Expiration"; +"Sensor Expiration" = "إنتهت صلاحية المستشعر"; /* title for g7 settings row showing sensor grace period end time */ -"Grace Period End" = "Grace Period End"; +"Grace Period End" = "نهاية الفترة"; /* Field label */ "Glucose" = "Glucose"; -"Last Reading" = "Last Reading"; +"Last Reading" = "آخر قراءة"; "Time" = "Time"; -"Trend" = "Trend"; +"Trend" = "إتجاه"; -"Bluetooth" = "Bluetooth"; +"Bluetooth" = "البلوتوث"; /* title for g7 settings row showing BLE Name */ "Name" = "Name"; /* title for g7 settings connection status when scanning */ -"Scanning" = "Scanning"; +"Scanning" = "يتم المسح"; /* title for g7 settings connection status when connected */ "Connected" = "Connected"; @@ -64,16 +64,16 @@ "Connecting" = "Connecting"; /* title for g7 settings row showing sensor last connect time */ -"Last Connect" = "Last Connect"; +"Last Connect" = "آخر اتصال"; /* Configuration */ "Configuration" = "Configuration"; /* title for g7 config settings to upload readings */ -"Upload Readings" = "Upload Readings"; +"Upload Readings" = "رفع القراءات"; /* Button */ -"Scan for new sensor" = "Scan for new sensor"; +"Scan for new sensor" = "البحث عن مستشعر جديد"; /* Button label for removing CGM */ "Delete CGM" = "Delete CGM"; @@ -81,49 +81,49 @@ /* No glucose value representation (3 dashes for mg/dL) */ "– – –" = "– – –"; /* String displayed instead of a glucose value below the CGM range */ -"LOW" = "LOW"; +"LOW" = "منخفض"; /* String displayed instead of a glucose value above the CGM range */ -"HIGH" = "HIGH"; +"HIGH" = "مرتفع"; /* Format string for glucose trend per minute. (1: glucose value and unit) */ -"%@/min" = "%@/min"; +"%@/min" = "%@ دقيقة"; /* G7 Progress bar label when searching for sensor */ -"Searching for sensor" = "Searching for sensor"; +"Searching for sensor" = "البحث عن مستشعر"; /* G7 Progress bar label when sensor expired */ -"Sensor expired" = "Sensor expired"; +"Sensor expired" = "إنتهت صلاحية المستشعر"; /* G7 Progress bar label when sensor in warmup */ -"Warmup completes" = "Warmup completes"; +"Warmup completes" = "اكتمل الإحماء"; /* G7 Progress bar label when sensor in warmup */ -"Warmup completes" = "Warmup completes"; +"Warmup completes" = "اكتمل الإحماء"; /* G7 Progress bar label when sensor failed */ -"Sensor failed" = "Sensor failed"; +"Sensor failed" = "فشل الحساس"; /* G7 Progress bar label when sensor lifetime progress showing */ -"Sensor expires" = "Sensor expires"; +"Sensor expires" = "إنتهت صلاحية المستشعر"; /* G7 Progress bar label when sensor grace period progress showing */ -"Grace period remaining" = "Grace period remaining"; +"Grace period remaining" = "الفترة المتبقية"; /* G7 Status highlight text for searching for sensor */ -"Searching for\nSensor" = "Searching for\nSensor"; +"Searching for\nSensor" = "البحث عن مستشعر\n"; /* G7 Status highlight text for sensor expired */ -"Sensor\nExpired" = "Sensor\nExpired"; +"Sensor\nExpired" = "إنتهاء صلاحية المستشعر\n"; /* G7 Status highlight text for signal loss */ -"Sensor\nFailed" = "Sensor\nFailed"; +"Sensor\nFailed" = "فشل جهاز الاستشعار\n"; /* G7 Status highlight text for signal loss */ -"Signal\nLoss" = "Signal\nLoss"; +"Signal\nLoss" = "فقدان الإشارة\n"; /*G7 Status highlight text for sensor error */ -"Sensor\nIssue" = "Sensor\nIssue"; +"Sensor\nIssue" = "مشكلة في المستشعر\n"; /* G7 Status highlight text for sensor warmup */ "Sensor\nWarmup" = "Sensor\nWarmup"; diff --git a/Dependencies/MinimedKit/MinimedKitUI/Resources/vi.lproj/Localizable.strings b/Dependencies/MinimedKit/MinimedKitUI/Resources/vi.lproj/Localizable.strings index 03b4bdf7e7..e06356aaf7 100644 --- a/Dependencies/MinimedKit/MinimedKitUI/Resources/vi.lproj/Localizable.strings +++ b/Dependencies/MinimedKit/MinimedKitUI/Resources/vi.lproj/Localizable.strings @@ -11,7 +11,7 @@ "%1$@ basal schedule entries\n" = "%1$@ Lịch biểu liều nền\n"; /* The format string describing units of insulin remaining: (1: number of units) */ -"%1$@ Units of insulin remaining\n" = "%1$@ Số unit insulin còn lại\n"; +"%1$@ Units of insulin remaining\n" = "%1$@ Số đơn vị insulin còn lại\n"; /* Accessibility format string for (1: localized volume)(2: time) */ "%1$@ units remaining at %2$@" = "%1$@ units vẫn đang còn lúc %2$@"; @@ -99,7 +99,7 @@ "Insulin Delivery" = "Khối lượng tiêm insulin"; /* Instructions on selecting an insulin data source */ -"Insulin delivery can be determined from the pump by either interpreting the event history or comparing the reservoir volume over time. Reading event history allows for a more accurate status graph and uploading up-to-date treatment data to Nightscout, at the cost of faster pump battery drain and the possibility of a higher radio error rate compared to reading only reservoir volume. If the selected source cannot be used for any reason, the system will attempt to fall back to the other option." = "Việc tiêm insulin có thể được quyết định từ bơm bằng cách kết hợp giải thuật dữ liệu của người sử dụng và so sánh với khối lượngngăn chứa insulin theo thời gian. Việc đọc các dữ liệu cũ sẽ đảm bảo biểu đồ đường huyết luôn được tính chính xác và tải dữ liệu điều trị cập nhật lên Nightscout, nhưng lại tăng việc tiêu hao pin cũng như lỗi giao tiếp tần số radio cao hơn việc chỉ đọc mỗi dữ liệu ngăn chứa insulin. Trong trường hợp nguồn dữ liệu không được lựa chọn vì bất kỳ lý do gì thì phần mềm sẽ quay sang lựa chọn khác."; +"Insulin delivery can be determined from the pump by either interpreting the event history or comparing the reservoir volume over time. Reading event history allows for a more accurate status graph and uploading up-to-date treatment data to Nightscout, at the cost of faster pump battery drain and the possibility of a higher radio error rate compared to reading only reservoir volume. If the selected source cannot be used for any reason, the system will attempt to fall back to the other option." = "Việc tiêm insulin có thể được quyết định từ bơm bằng cách kết hợp giải thuật dữ liệu của người sử dụng và so sánh với khối lượng ngăn chứa insulin theo thời gian. Việc đọc các dữ liệu cũ sẽ đảm bảo biểu đồ đường huyết luôn được tính chính xác và tải dữ liệu điều trị cập nhật lên Nightscout, nhưng lại tăng việc tiêu hao pin cũng như lỗi giao tiếp tần số radio cao hơn việc chỉ đọc mỗi dữ liệu ngăn chứa insulin. Trong trường hợp nguồn dữ liệu không được lựa chọn vì bất kỳ lý do gì thì phần mềm sẽ quay sang lựa chọn khác."; /* Header for insulin remaining on pod settings screen */ "Insulin Remaining" = "Insulin còn lại"; @@ -123,8 +123,7 @@ "No, Keep Pump As Is" = "Không, Giữ nguyên máy bơm"; /* Pump find device instruction */ -"On your pump, go to the Find Device screen and select \"Find Device\".\n\nMain Menu >\nUtilities >\nConnect Devices >\nOther Devices >\nOn >\nFind Device" = "Trên máy bơm của bạn, hãy đi tới \"Find Device\". Chọn Main Menu >\Utilities >\\Connect Devices >\\Other Devices >\\On >\\Find Device. -Bơm sẽ tìm thấy chỉ định"; +"On your pump, go to the Find Device screen and select \"Find Device\".\n\nMain Menu >\nUtilities >\nConnect Devices >\nOther Devices >\nOn >\nFind Device" = "Trên máy bơm của bạn, hãy đi tới \"Find Device\". Chọn Main Menu >\Utilities >\\Connect Devices >\\Other Devices >\\On >\\Find Device"; /* navigation title for pump battery type selection Text for medtronic pump preferred data source */ diff --git a/Dependencies/OmniBLE/Localizations/vi.lproj/Localizable.strings b/Dependencies/OmniBLE/Localizations/vi.lproj/Localizable.strings index eee91e908d..b9271baeeb 100644 --- a/Dependencies/OmniBLE/Localizations/vi.lproj/Localizable.strings +++ b/Dependencies/OmniBLE/Localizations/vi.lproj/Localizable.strings @@ -9,7 +9,7 @@ "Multiple Command Alert" = "Multiple Command Alert"; /* Alert content title for userPodExpiration pod alert */ -"Pod Expiration Reminder" = "Lời Nhắc Pod Hết hạn"; +"Pod Expiration Reminder" = "Nhắc nhở hết hạn"; /* Alert content title for podExpiring pod alert */ "Pod Expired" = "Pod đã hết hạn"; @@ -18,7 +18,7 @@ "Low Reservoir" = "Sắp hết thuốc"; /* Alert content title for suspendInProgress pod alert */ -"Suspend In Progress Reminder" = "Tạm dừng lời nhắc đang tiến hành"; +"Suspend In Progress Reminder" = "Đang tiến hành tạm dừng lời nhắc"; /* Alert content title for suspendEnded pod alert */ "Resume Insulin" = "Tiếp tục lại việc tiêm insulin"; @@ -27,7 +27,7 @@ "Pod Pairing Incomplete" = "Pod ghép nối không thành công"; /* Alert content title for timeOffsetChangeDetected pod alert */ -"Time Change Detected" = "Thay đổi thời gian được phát hiện"; +"Time Change Detected" = "Đã phát hiện thay đổi thời gian"; /* Alert content body for multiCommand pod alert */ "Multiple Command Alert" = "Multiple Command Alert"; @@ -45,19 +45,19 @@ "%1$@ insulin or less remaining in Pod. Change Pod soon." = "%1$@ insulin hoặc ít hơn còn lại trong Pod. Thay Pod ngay."; /* Alert content body for suspendInProgress pod alert */ -"Suspend In Progress Reminder" = "Tạm dừng lời nhắc đang tiến hành"; +"Suspend In Progress Reminder" = "Đang tiến hành tạm dừng lời nhắc"; /* Alert content body for suspendEnded pod alert */ "The insulin suspension period has ended.\n\nYou can resume delivery from the banner on the home screen or from your pump settings screen. You will be reminded again in 15 minutes." = "Thời gian tạm ngưng insulin đã kết thúc.\n\n Bạn có thể phục hồi việc tiêm thuốc từ màn hình chính hoặc từ màn hình cài đặt bơm. Sẽ có thông báo nhắc trong vòng 15 phút."; /* Alert content body for finishSetupReminder pod alert */ -"Please finish pairing your pod." = "Đề nghị hoàn thành ghép đôi pod."; +"Please finish pairing your pod." = "Vui lòng hoàn tất việc ghép nối pod của bạn."; /* Alert content body for timeOffsetChangeDetected pod alert */ "The time on your pump is different from the current time. You can review the pump time and and sync to current time in settings." = "Thời gian trên bơm khác so với thời gian thực tế. Bạn có thể xem thời gian trên bơm và sync thời gian hiện hành trong cài đặt."; /* Alert notification body for suspendEnded pod alert user notification */ -"Suspension time is up. Open the app and resume." = "Thời gian tạm dừng hết. Mở ứng dụng và tiếp tục lại."; +"Suspension time is up. Open the app and resume." = "Đã hết thời gian tạm dừng. Mở ứng dụng và tiếp tục."; /* Action button default text for PodAlerts */ "Ok" = "Ok"; @@ -237,7 +237,7 @@ "No Pod" = "Không pod"; /* Status highlight message for emptyReservoir alarm. */ -"No Insulin" = "Hết thuốc"; +"No Insulin" = "Hết Insulin"; /* Status highlight message for podExpired alarm. */ "Pod Expired" = "Pod đã hết hạn"; @@ -249,7 +249,7 @@ "Pod Error" = "Lỗi Pod"; /* Status highlight that a pump is out of insulin. */ -"No Insulin" = "Hết thuốc"; +"No Insulin" = "Hết Insulin"; /* Status highlight that insulin delivery was suspended. */ "Insulin Suspended" = "Insulin Đã tạm ngưng"; @@ -273,7 +273,7 @@ "Checking..." = "Đang kiểm tra..."; /* */ -"Check cannula insertion finished" = "Kiểm tra việc gắn cannula hoàn tất"; +"Check cannula insertion finished" = "Kiểm tra việc gắn cannula đã hoàn tất"; /* */ "Get pod status" = "Lấy thông tin pod"; @@ -538,7 +538,7 @@ "Pairing..." = "Đang ghép đôi..."; /* Pod pairing action button text while priming */ -"Priming..." = "Priming..."; +"Priming..." = "Đang chuẩn bị..."; /* */ "Deactivating..." = "Đang hủy kích hoạt..."; @@ -706,7 +706,7 @@ "Active Time" = "Thời gian Hoạt động"; /* description label for last status date pod details row */ -"Last Status" = "Trạng thái cuối"; +"Last Status" = "Trạng thái gần nhất"; /* description label for pod fault details */ "Pod Fault Details" = "Thông tin lỗi Pod"; @@ -838,12 +838,12 @@ "Previous Pod Details" = "Thông tin của Pod trước đó"; /* Text for pump manager details navigation link */ -"Pump Manager Details" = "Chi tiết về Trình quản lý Máy bơm"; +"Pump Manager Details" = "Chi tiết về Trình quản lý Bơm"; /* button title when retrieving pump manager details */ -"Retrieving Pump Manager Details..." = "Đang truy xuất thông tin Trình quản lý máy bơm..."; +"Retrieving Pump Manager Details..." = "Đang truy xuất thông tin Trình quản lý Bơm..."; /* button title to refresh pump manager details */ -"Refresh Pump Manager Details" = "Làm mới Chi tiết về Trình quản lý Máy bơm"; +"Refresh Pump Manager Details" = "Làm mới Chi tiết về Trình quản lý Bơm"; /* Section header for diagnostic section */ "Diagnostics" = "Chẩn đoán"; diff --git a/Dependencies/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift b/Dependencies/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift index 2ca87e4b11..93099843d2 100644 --- a/Dependencies/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift +++ b/Dependencies/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift @@ -154,12 +154,12 @@ public class OmniBLEPumpManager: DeviceManager { oldValue = state let oldStatusEvaluationDate = state.lastStatusChange let oldHighlight = buildPumpStatusHighlight(for: oldValue, andDate: oldStatusEvaluationDate) - oldStatus = status(for: oldValue) + oldStatus = status(for: oldValue, at: oldStatusEvaluationDate) returnType = changes(&state) let newStatusEvaluationDate = Date() - let newStatus = status(for: state) + let newStatus = status(for: state, at: newStatusEvaluationDate) let newHighlight = buildPumpStatusHighlight(for: state, andDate: newStatusEvaluationDate) if oldStatus != newStatus || oldHighlight != newHighlight { @@ -302,13 +302,13 @@ extension OmniBLEPumpManager { podStateObservers.removeElement(observer) } - private func status(for state: OmniBLEPumpManagerState) -> PumpManagerStatus { + private func status(for state: OmniBLEPumpManagerState, at date: Date = Date()) -> PumpManagerStatus { return PumpManagerStatus( timeZone: state.timeZone, device: device(for: state), pumpBatteryChargeRemaining: nil, - basalDeliveryState: basalDeliveryState(for: state), - bolusState: bolusState(for: state), + basalDeliveryState: basalDeliveryState(for: state, at: date), + bolusState: bolusState(for: state, at: date), insulinType: state.insulinType, deliveryIsUncertain: state.podState?.needsCommsRecovery == true ) @@ -340,7 +340,7 @@ extension OmniBLEPumpManager { } } - private func basalDeliveryState(for state: OmniBLEPumpManagerState) -> PumpManagerStatus.BasalDeliveryState { + private func basalDeliveryState(for state: OmniBLEPumpManagerState, at date: Date = Date()) -> PumpManagerStatus.BasalDeliveryState { guard let podState = state.podState else { return .active(.distantPast) } @@ -367,7 +367,7 @@ extension OmniBLEPumpManager { case .disengaging: return .cancelingTempBasal case .stable: - if let tempBasal = podState.unfinalizedTempBasal { + if let tempBasal = podState.unfinalizedTempBasal, !tempBasal.isFinished(at: date) { return .tempBasal(DoseEntry(tempBasal)) } switch podState.suspendState { @@ -379,7 +379,7 @@ extension OmniBLEPumpManager { } } - private func bolusState(for state: OmniBLEPumpManagerState) -> PumpManagerStatus.BolusState { + private func bolusState(for state: OmniBLEPumpManagerState, at date: Date = Date()) -> PumpManagerStatus.BolusState { guard let podState = state.podState else { return .noBolus } @@ -390,7 +390,7 @@ extension OmniBLEPumpManager { case .disengaging: return .canceling case .stable: - if let bolus = podState.unfinalizedBolus { + if let bolus = podState.unfinalizedBolus, !bolus.isFinished(at: date) { return .inProgress(DoseEntry(bolus)) } } @@ -1841,8 +1841,8 @@ extension OmniBLEPumpManager: PumpManager { state.bolusEngageState = .engaging }) - if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended != false { - self.log.error("enactBolus: returning pod suspended error for bolus") + if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended == true { + self.log.error("Not enacting bolus because podState or last status received indicates pod is suspended") completion(.deviceState(PodCommsError.podSuspended)) return } @@ -1982,8 +1982,8 @@ extension OmniBLEPumpManager: PumpManager { return } - if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended != false { - self.log.info("Not enacting temp basal because podState indicates pod is suspended.") + if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended == true { + self.log.info("Not enacting temp basal because podState or last status received indicates pod is suspended") completion(.deviceState(PodCommsError.podSuspended)) return } diff --git a/Dependencies/OmniBLE/OmniBLE/PumpManager/PodCommsSession.swift b/Dependencies/OmniBLE/OmniBLE/PumpManager/PodCommsSession.swift index c31ae0dd28..abc693e252 100644 --- a/Dependencies/OmniBLE/OmniBLE/PumpManager/PodCommsSession.swift +++ b/Dependencies/OmniBLE/OmniBLE/PumpManager/PodCommsSession.swift @@ -552,6 +552,12 @@ public class PodCommsSession { log.default("bolus: pod is still bolusing") return DeliveryCommandResult.certainFailure(error: .unfinalizedBolus) } + // If the pod setup is complete, also confirm that the pod is indeed not suspended + if podState.setupProgress == .completed && statusResponse.deliveryStatus.suspended { + log.default("bolus: pod is suspended") + return DeliveryCommandResult.certainFailure(error: .podSuspended) + } + log.default("bolus: get status response verifies pod is not bolusing and not suspended") } else { log.default("bolus: failed to read pod status to verify there is no bolus running") return DeliveryCommandResult.certainFailure(error: .noResponse) diff --git a/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift b/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift index 0888bcdd5b..5cf0113a4c 100644 --- a/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift +++ b/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift @@ -146,13 +146,25 @@ public class OmnipodPumpManager: RileyLinkPumpManager { private func setStateWithResult(_ changes: (_ state: inout OmnipodPumpManagerState) -> ReturnType) -> ReturnType { var oldValue: OmnipodPumpManagerState! var returnType: ReturnType! + var shouldNotifyStatusUpdate = false + var oldStatus: PumpManagerStatus? + let newValue = lockedState.mutate { (state) in oldValue = state + let oldStatusEvaluationDate = state.lastStatusChange + let oldHighlight = buildPumpStatusHighlight(for: oldValue, andDate: oldStatusEvaluationDate) + oldStatus = status(for: oldValue, at: oldStatusEvaluationDate) + returnType = changes(&state) - } - guard oldValue != newValue else { - return returnType + let newStatusEvaluationDate = Date() + let newStatus = status(for: state, at: newStatusEvaluationDate) + let newHighlight = buildPumpStatusHighlight(for: state, andDate: newStatusEvaluationDate) + + if oldStatus != newStatus || oldHighlight != newHighlight { + shouldNotifyStatusUpdate = true + state.lastStatusChange = newStatusEvaluationDate + } } if oldValue.podState != newValue.podState { @@ -173,25 +185,19 @@ public class OmnipodPumpManager: RileyLinkPumpManager { } } - // Ideally we ensure that oldValue.rawValue != newValue.rawValue, but the types aren't // defined as equatable pumpDelegate.notify { (delegate) in delegate?.pumpManagerDidUpdateState(self) } - let oldStatus = status(for: oldValue) - let newStatus = status(for: newValue) - - let oldHighlight = buildPumpStatusHighlight(for: oldValue) - let newHighlight = buildPumpStatusHighlight(for: newValue) - - if oldStatus != newStatus || oldHighlight != newHighlight { + if let oldStatus = oldStatus, shouldNotifyStatusUpdate { notifyStatusObservers(oldStatus: oldStatus) } return returnType } + private let lockedState: Locked private let statusObservers = WeakSynchronizedSet() @@ -431,13 +437,13 @@ extension OmnipodPumpManager { } } - private func status(for state: OmnipodPumpManagerState) -> PumpManagerStatus { + private func status(for state: OmnipodPumpManagerState, at date: Date = Date()) -> PumpManagerStatus { return PumpManagerStatus( timeZone: state.timeZone, device: device(for: state), pumpBatteryChargeRemaining: nil, - basalDeliveryState: basalDeliveryState(for: state), - bolusState: bolusState(for: state), + basalDeliveryState: basalDeliveryState(for: state, at: date), + bolusState: bolusState(for: state, at: date), insulinType: state.insulinType, deliveryIsUncertain: state.podState?.needsCommsRecovery == true ) @@ -469,7 +475,7 @@ extension OmnipodPumpManager { } } - private func basalDeliveryState(for state: OmnipodPumpManagerState) -> PumpManagerStatus.BasalDeliveryState { + private func basalDeliveryState(for state: OmnipodPumpManagerState, at date: Date = Date()) -> PumpManagerStatus.BasalDeliveryState { guard let podState = state.podState else { return .active(.distantPast) } @@ -496,7 +502,7 @@ extension OmnipodPumpManager { case .disengaging: return .cancelingTempBasal case .stable: - if let tempBasal = podState.unfinalizedTempBasal { + if let tempBasal = podState.unfinalizedTempBasal, !tempBasal.isFinished(at: date) { return .tempBasal(DoseEntry(tempBasal)) } switch podState.suspendState { @@ -508,7 +514,7 @@ extension OmnipodPumpManager { } } - private func bolusState(for state: OmnipodPumpManagerState) -> PumpManagerStatus.BolusState { + private func bolusState(for state: OmnipodPumpManagerState, at date: Date = Date()) -> PumpManagerStatus.BolusState { guard let podState = state.podState else { return .noBolus } @@ -519,7 +525,7 @@ extension OmnipodPumpManager { case .disengaging: return .canceling case .stable: - if let bolus = podState.unfinalizedBolus, !bolus.isFinished() { + if let bolus = podState.unfinalizedBolus, !bolus.isFinished(at: date) { return .inProgress(DoseEntry(bolus)) } } @@ -1856,8 +1862,8 @@ extension OmnipodPumpManager: PumpManager { state.bolusEngageState = .engaging }) - if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended != false { - self.log.error("enactBolus: returning pod suspended error for bolus") + if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended == true { + self.log.error("Not enacting bolus because podState or last status received indicates pod is suspended") completion(.deviceState(PodCommsError.podSuspended)) return } @@ -1995,8 +2001,8 @@ extension OmnipodPumpManager: PumpManager { return } - if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended != false { - self.log.info("Not enacting temp basal because podState indicates pod is suspended.") + if let podState = self.state.podState, podState.isSuspended || podState.lastDeliveryStatusReceived?.suspended == true { + self.log.info("Not enacting temp basal because podState or last status received indicates pod is suspended") completion(.deviceState(PodCommsError.podSuspended)) return } diff --git a/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManagerState.swift b/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManagerState.swift index 3b4913d3a7..034afc25b7 100644 --- a/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManagerState.swift +++ b/Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManagerState.swift @@ -67,6 +67,8 @@ public struct OmnipodPumpManagerState: RawRepresentable, Equatable { internal var tempBasalEngageState: EngageablePumpState = .stable + internal var lastStatusChange: Date = .distantPast + internal var lastPumpDataReportDate: Date? internal var insulinType: InsulinType? diff --git a/Dependencies/OmniKit/OmniKit/PumpManager/PodCommsSession.swift b/Dependencies/OmniKit/OmniKit/PumpManager/PodCommsSession.swift index 9c8298dcde..ae3393d871 100644 --- a/Dependencies/OmniKit/OmniKit/PumpManager/PodCommsSession.swift +++ b/Dependencies/OmniKit/OmniKit/PumpManager/PodCommsSession.swift @@ -527,6 +527,12 @@ public class PodCommsSession { log.default("bolus: pod is still bolusing") return DeliveryCommandResult.certainFailure(error: .unfinalizedBolus) } + // If the pod setup is complete, also confirm that the pod is indeed not suspended + if podState.setupProgress == .completed && statusResponse.deliveryStatus.suspended { + log.default("bolus: pod is suspended") + return DeliveryCommandResult.certainFailure(error: .podSuspended) + } + log.default("bolus: get status response verifies pod is not bolusing and not suspended") } else { log.default("bolus: failed to read pod status to verify there is no bolus running") return DeliveryCommandResult.certainFailure(error: .noResponse) diff --git a/FreeAPS.xcodeproj/project.pbxproj b/FreeAPS.xcodeproj/project.pbxproj index fb4b111bbe..fcc7422dcd 100644 --- a/FreeAPS.xcodeproj/project.pbxproj +++ b/FreeAPS.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ 190F8CF72BC6F70800EDB473 /* IllustrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190F8CF62BC6F70800EDB473 /* IllustrationView.swift */; }; 191A9D162BED00A500028D48 /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191A9D152BED00A500028D48 /* Version.swift */; }; 191A9D182BED24B000028D48 /* ActiveIOBView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191A9D172BED24B000028D48 /* ActiveIOBView.swift */; }; - 191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */; }; 1920BF5D2B9DF53200E861FE /* BolusShortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1920BF5C2B9DF53200E861FE /* BolusShortcut.swift */; }; 19229B962AFBB84800CD91CA /* Predictions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19229B952AFBB84800CD91CA /* Predictions.swift */; }; 192424CB2B7A64E70063CBF0 /* NIghtscoutExercise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 192424CA2B7A64E70063CBF0 /* NIghtscoutExercise.swift */; }; @@ -31,6 +30,8 @@ 1935364028496F7D001E0B16 /* Dynamic structs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Dynamic structs.swift */; }; 193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; }; 194297512B815938006B8A0B /* OverridesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194297502B815938006B8A0B /* OverridesView.swift */; }; + 19493A3B2C5997AD00EC83A7 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19493A3A2C5997AD00EC83A7 /* Database.swift */; }; + 19493A3D2C59987700EC83A7 /* DatabaseModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19493A3C2C59987700EC83A7 /* DatabaseModels.swift */; }; 194C32772B93A9BF0016FB2A /* OverrideShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194C32762B93A9BF0016FB2A /* OverrideShortcuts.swift */; }; 194D7E6E2B974F9F007A38C1 /* LoopsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194D7E6D2B974F9F007A38C1 /* LoopsView.swift */; }; 1956FB212AFF79E200C7B4FF /* CoreDataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1956FB202AFF79E200C7B4FF /* CoreDataStorage.swift */; }; @@ -53,7 +54,6 @@ 19AEF4322B1F5A98006FFE8B /* TIRView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AEF4312B1F5A98006FFE8B /* TIRView.swift */; }; 19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; }; 19C14F442C29807C009A7E07 /* ScrollOffset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C14F432C29807C009A7E07 /* ScrollOffset.swift */; }; - 19C3FB742C3878BE007AB7E6 /* AppShortcuts.strings in Resources */ = {isa = PBXBuildFile; fileRef = 19C3FB462C3878BE007AB7E6 /* AppShortcuts.strings */; }; 19D466A329AA2B80004D5F33 /* FPUConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A229AA2B80004D5F33 /* FPUConfigDataFlow.swift */; }; 19D466A529AA2BD4004D5F33 /* FPUConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */; }; 19D466A729AA2C22004D5F33 /* FPUConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */; }; @@ -451,8 +451,6 @@ F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F90692D2274B9A130037068D /* AppleHealthKitRootView.swift */; }; F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F90692D5274B9A450037068D /* HealthKitStateModel.swift */; }; FA630397F76B582C8D8681A7 /* BasalProfileEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42369F66CF91F30624C0B3A6 /* BasalProfileEditorProvider.swift */; }; - FE41E4D429463C660047FD55 /* NightscoutStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */; }; - FE41E4D629463EE20047FD55 /* NightscoutPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */; }; FE66D16B291F74F8005D6F77 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */; }; FEFA5C0F299F810B00765C17 /* Core_Data.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FEFA5C0D299F810B00765C17 /* Core_Data.xcdatamodeld */; }; FEFA5C11299F814A00765C17 /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFA5C10299F814A00765C17 /* CoreDataStack.swift */; }; @@ -572,7 +570,6 @@ 1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; 191A9D152BED00A500028D48 /* Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = ""; }; 191A9D172BED24B000028D48 /* ActiveIOBView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveIOBView.swift; sourceTree = ""; }; - 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = ""; }; 1920BF5C2B9DF53200E861FE /* BolusShortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusShortcut.swift; sourceTree = ""; }; 192202902BAB567800B95BE8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 19229B952AFBB84800CD91CA /* Predictions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Predictions.swift; sourceTree = ""; }; @@ -599,6 +596,29 @@ 1927C8FB2744612600347C69 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; 1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 192A9A112C0F5361000BBC29 /* MealsShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealsShortcuts.swift; sourceTree = ""; }; + 192E77322B9F40C7004F4822 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77462B9F4CF1004F4822 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77472B9F4CF3004F4822 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77482B9F4CF6004F4822 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AppShortcuts.strings"; sourceTree = ""; }; + 192E77492B9F4CF9004F4822 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E774A2B9F4CFB004F4822 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E774B2B9F4CFE004F4822 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E774C2B9F4D01004F4822 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E774D2B9F4D04004F4822 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E774E2B9F4D07004F4822 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E774F2B9F4D08004F4822 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77502B9F4D0A004F4822 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77512B9F4D0B004F4822 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77522B9F4D0D004F4822 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77532B9F4D0E004F4822 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/AppShortcuts.strings"; sourceTree = ""; }; + 192E77542B9F4D0F004F4822 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/AppShortcuts.strings"; sourceTree = ""; }; + 192E77552B9F4D11004F4822 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77562B9F4D12004F4822 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77572B9F4D14004F4822 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77582B9F4D1A004F4822 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E77592B9F4D1B004F4822 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E775A2B9F4D1C004F4822 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/AppShortcuts.strings; sourceTree = ""; }; + 192E775B2B9F4D1E004F4822 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/AppShortcuts.strings; sourceTree = ""; }; 1935363F28496F7D001E0B16 /* Dynamic structs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dynamic structs.swift"; sourceTree = ""; }; 193F1E392B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; 193F1E3A2B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; @@ -606,6 +626,8 @@ 193F1E3C2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; 193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = ""; }; 194297502B815938006B8A0B /* OverridesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverridesView.swift; sourceTree = ""; }; + 19493A3A2C5997AD00EC83A7 /* Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; }; + 19493A3C2C59987700EC83A7 /* DatabaseModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseModels.swift; sourceTree = ""; }; 194C32762B93A9BF0016FB2A /* OverrideShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideShortcuts.swift; sourceTree = ""; }; 194D7E6D2B974F9F007A38C1 /* LoopsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopsView.swift; sourceTree = ""; }; 1956FB202AFF79E200C7B4FF /* CoreDataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStorage.swift; sourceTree = ""; }; @@ -1053,8 +1075,6 @@ F90692D2274B9A130037068D /* AppleHealthKitRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleHealthKitRootView.swift; sourceTree = ""; }; F90692D5274B9A450037068D /* HealthKitStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthKitStateModel.swift; sourceTree = ""; }; FBB3BAE7494CB771ABAC7B8B /* ISFEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorRootView.swift; sourceTree = ""; }; - FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutStatistics.swift; sourceTree = ""; }; - FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutPreferences.swift; sourceTree = ""; }; FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = ""; }; FEFA5C0E299F810B00765C17 /* Core_Data.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Core_Data.xcdatamodel; sourceTree = ""; }; FEFA5C10299F814A00765C17 /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = ""; }; @@ -1594,6 +1614,7 @@ 3811DE9725C9D88300A708ED /* NightscoutManager.swift */, 38FE826925CC82DB001FF17A /* NetworkService.swift */, 38FE826C25CC8461001FF17A /* NightscoutAPI.swift */, + 19493A3A2C5997AD00EC83A7 /* Database.swift */, ); path = Network; sourceTree = ""; @@ -1855,49 +1876,47 @@ 388E5A5925B6F0250019842D /* Models */ = { isa = PBXGroup; children = ( + CE82E02628E869DF00473A9C /* AlertEntry.swift */, 385CEAC025F2EA52002D6D5B /* Announcement.swift */, 388E5A5F25B6F2310019842D /* Autosens.swift */, 38A00B1E25FC00F7006BC0B0 /* Autotune.swift */, + 19F191E32BE686AE00F6297E /* BareMinimum.swift */, 388358C725EEF6D200E024B2 /* BasalProfileEntry.swift */, 38D0B3B525EBE24900CB6E88 /* Battery.swift */, 382C134A25F14E3700715CE1 /* BGTargets.swift */, 3870FF4225EC13F40088248F /* BloodGlucose.swift */, 38A9260425F012D8009E3739 /* CarbRatios.swift */, 38D0B3D825EC07C400CB6E88 /* CarbsEntry.swift */, + 19D4E4EA29FC6A9F00351451 /* Charts.swift */, + 19A910352A24D6D700C8951B /* Configs.swift */, + F2159A532BA6207F00A0B716 /* ContactTrickEntry.swift */, 3811DF0125CA9FEA00A708ED /* Credentials.swift */, + 19493A3C2C59987700EC83A7 /* DatabaseModels.swift */, + 1935363F28496F7D001E0B16 /* Dynamic structs.swift */, + F270F68C2BAE374C00F6D8DD /* FontTracking.swift */, + F2159A512BA60F7A00A0B716 /* FontWeight.swift */, 38AEE73C25F0200C0013F05B /* FreeAPSSettings.swift */, 383948D925CD64D500E91849 /* Glucose.swift */, + E0D4F80427513ECF00BDF1FE /* HealthKitSample.swift */, + 1967DFBD29D052C200759F30 /* Icons.swift */, 382C133625F13A1E00715CE1 /* InsulinSensitivities.swift */, 38887CCD25F5725200944304 /* IOBEntry.swift */, + 193F6CDC2A512C8F001240FD /* Loops.swift */, + 19012CDB291D2CB900FB8210 /* LoopStats.swift */, + 192424CA2B7A64E70063CBF0 /* NIghtscoutExercise.swift */, 385CEA8125F23DFD002D6D5B /* NightscoutStatus.swift */, 389442CA25F65F7100FA1F27 /* NightscoutTreatment.swift */, 3895E4C525B9E00D00214B37 /* Preferences.swift */, 38A13D3125E28B4B00EAA382 /* PumpHistoryEvent.swift */, 3883583325EEB38000E024B2 /* PumpSettings.swift */, 38E989DC25F5021400C0CED0 /* PumpStatus.swift */, + CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */, 38BF021C25E7E3AF00579895 /* Reservoir.swift */, + 19B0EF2028F6D66200069496 /* Statistics.swift */, 3871F38625ED661C0013ECB5 /* Suggestion.swift */, 38A0364125ED069400FCBB52 /* TempBasal.swift */, 3871F39B25ED892B0013ECB5 /* TempTarget.swift */, 3811DE8E25C9D80400A708ED /* User.swift */, - E0D4F80427513ECF00BDF1FE /* HealthKitSample.swift */, - 1935363F28496F7D001E0B16 /* Dynamic structs.swift */, - CE82E02628E869DF00473A9C /* AlertEntry.swift */, - 19B0EF2028F6D66200069496 /* Statistics.swift */, - 19F191E32BE686AE00F6297E /* BareMinimum.swift */, - 19012CDB291D2CB900FB8210 /* LoopStats.swift */, - FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */, - FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */, - 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */, - 1967DFBD29D052C200759F30 /* Icons.swift */, - 19D4E4EA29FC6A9F00351451 /* Charts.swift */, - 19A910352A24D6D700C8951B /* Configs.swift */, - 193F6CDC2A512C8F001240FD /* Loops.swift */, - CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */, - 192424CA2B7A64E70063CBF0 /* NIghtscoutExercise.swift */, - F2159A512BA60F7A00A0B716 /* FontWeight.swift */, - F2159A532BA6207F00A0B716 /* ContactTrickEntry.swift */, - F270F68C2BAE374C00F6D8DD /* FontTracking.swift */, 191A9D152BED00A500028D48 /* Version.swift */, ); path = Models; @@ -2945,6 +2964,7 @@ 19E1F7EF29D08EBA005C8D20 /* IconConfigRootWiew.swift in Sources */, 1967DFC229D053D300759F30 /* IconImage.swift in Sources */, 382C134B25F14E3700715CE1 /* BGTargets.swift in Sources */, + 19493A3B2C5997AD00EC83A7 /* Database.swift in Sources */, 38AEE75725F0F18E0013F05B /* CarbsStorage.swift in Sources */, 38B4F3CA25E502E200E76A18 /* SwiftNotificationCenter.swift in Sources */, 38AEE75225F022080013F05B /* SettingsManager.swift in Sources */, @@ -2995,7 +3015,6 @@ 191A9D162BED00A500028D48 /* Version.swift in Sources */, 38E44535274E411700EC9A94 /* Disk+Data.swift in Sources */, 3811DE3125C9D49500A708ED /* HomeProvider.swift in Sources */, - FE41E4D629463EE20047FD55 /* NightscoutPreferences.swift in Sources */, E013D872273AC6FE0014109C /* GlucoseSimulatorSource.swift in Sources */, 388E5A5C25B6F0770019842D /* JSON.swift in Sources */, 3811DF0225CA9FEA00A708ED /* Credentials.swift in Sources */, @@ -3057,7 +3076,6 @@ 38A9260525F012D8009E3739 /* CarbRatios.swift in Sources */, 38FCF3D625E8FDF40078B0D1 /* MD5.swift in Sources */, 3871F39C25ED892B0013ECB5 /* TempTarget.swift in Sources */, - 191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */, FEFA5C11299F814A00765C17 /* CoreDataStack.swift in Sources */, 3811DEAB25C9D88300A708ED /* HTTPResponseStatus.swift in Sources */, 3811DE5F25C9D4D500A708ED /* ProgressBar.swift in Sources */, @@ -3193,6 +3211,7 @@ 19E1F7EA29D082ED005C8D20 /* IconConfigProvider.swift in Sources */, 44190F0BBA464D74B857D1FB /* PreferencesEditorRootView.swift in Sources */, E97285ED9B814CD5253C6658 /* AddCarbsDataFlow.swift in Sources */, + 19493A3D2C59987700EC83A7 /* DatabaseModels.swift in Sources */, CE48C86428CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift in Sources */, 38E8755427561E9800975559 /* DataFlow.swift in Sources */, 38E44522274E3DDC00EC9A94 /* NetworkReachabilityManager.swift in Sources */, @@ -3230,7 +3249,6 @@ BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */, F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */, C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */, - FE41E4D429463C660047FD55 /* NightscoutStatistics.swift in Sources */, 38E4453B274E411700EC9A94 /* Disk+VolumeInformation.swift in Sources */, 7BCFACB97C821041BA43A114 /* ManualTempBasalRootView.swift in Sources */, 38E44534274E411700EC9A94 /* Disk+InternalHelpers.swift in Sources */, diff --git a/FreeAPS/Sources/APS/APSManager.swift b/FreeAPS/Sources/APS/APSManager.swift index c8f7c09ff7..23cd284681 100644 --- a/FreeAPS/Sources/APS/APSManager.swift +++ b/FreeAPS/Sources/APS/APSManager.swift @@ -3,9 +3,6 @@ import CoreData import Foundation import LoopKit import LoopKitUI -import OmniBLE -import OmniKit -import RileyLinkKit import SwiftDate import SwiftUI import Swinject @@ -1330,26 +1327,11 @@ final class BaseAPSManager: APSManager, Injectable { bolusReporter?.addObserver(self) } - private func updateStatus() { - debug(.apsManager, "force update status") - guard let pump = pumpManager else { - return - } - - if let omnipod = pump as? OmnipodPumpManager { - omnipod.getPodStatus { _ in } - } - if let omnipodBLE = pump as? OmniBLEPumpManager { - omnipodBLE.getPodStatus { _ in } - } - } - private func clearBolusReporter() { bolusReporter?.removeObserver(self) bolusReporter = nil processQueue.asyncAfter(deadline: .now() + 0.5) { self.bolusProgress.send(nil) - self.updateStatus() } } } diff --git a/FreeAPS/Sources/APS/OpenAPS/Constants.swift b/FreeAPS/Sources/APS/OpenAPS/Constants.swift index 3698b2e230..9cba5a742b 100644 --- a/FreeAPS/Sources/APS/OpenAPS/Constants.swift +++ b/FreeAPS/Sources/APS/OpenAPS/Constants.swift @@ -87,10 +87,15 @@ extension OpenAPS { static let uploadedCGMState = "upload/uploaded-cgm-state.json" static let uploadedPodAge = "upload/uploaded-pod-age.json" static let uploadedProfile = "upload/uploaded-profile.json" + static let uploadedProfileToDatabase = "upload/uploaded-profile_database.json" static let uploadedPreferences = "upload/uploaded-preferences.json" static let uploadedSettings = "upload/uploaded-settings.json" static let uploadedManualGlucose = "upload/uploaded-manual-readings.json" static let notUploadedOverrides = "upload/not-uploaded-overrides.json" + static let uploadedPumpSettings = "upload/uploaded-pump_settings.json" + static let uploadedTempTargetsDatabase = "upload/uploaded-temptargets_database.json" + static let uploadedMealPresets = "upload/uploaded-meal-presets.json" + static let uploadedOverridePresets = "upload/uploaded-override-presets.json" } enum FreeAPS { diff --git a/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift b/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift index 388c72c038..9877adc0a2 100644 --- a/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift +++ b/FreeAPS/Sources/APS/Storage/CoreDataStorage.swift @@ -206,4 +206,132 @@ final class CoreDataStorage { } return presetsArray } + + func fetchOnbarding() -> Bool { + var firstRun = true + coredataContext.performAndWait { + let requestBool = Onboarding.fetchRequest() as NSFetchRequest + let sort = NSSortDescriptor(key: "date", ascending: false) + requestBool.sortDescriptors = [sort] + requestBool.fetchLimit = 1 + try? firstRun = self.coredataContext.fetch(requestBool).first?.firstRun ?? true + } + return firstRun + } + + func saveOnbarding() { + coredataContext.performAndWait { [self] in + let save = Onboarding(context: self.coredataContext) + save.firstRun = false + save.date = Date.now + try? self.coredataContext.save() + } + } + + func startOnbarding() { + coredataContext.performAndWait { [self] in + let save = Onboarding(context: self.coredataContext) + save.firstRun = true + save.date = Date.now + try? self.coredataContext.save() + } + } + + func fetchSettingProfileName() -> String { + fetchActiveProfile() + } + + func fetchSettingProfileNames() -> [Profiles]? { + var presetsArray: [Profiles]? + coredataContext.performAndWait { + let requestProfiles = Profiles.fetchRequest() as NSFetchRequest + let sort = NSSortDescriptor(key: "date", ascending: false) + requestProfiles.sortDescriptors = [sort] + try? presetsArray = self.coredataContext.fetch(requestProfiles) + } + return presetsArray + } + + func fetchUniqueSettingProfileName(_ name: String) -> Bool { + var presetsArray: Profiles? + coredataContext.performAndWait { + let requestProfiles = Profiles.fetchRequest() as NSFetchRequest + let sort = NSSortDescriptor(key: "date", ascending: false) + requestProfiles.sortDescriptors = [sort] + requestProfiles.predicate = NSPredicate( + format: "uploaded == true && name == %@", name as String + ) + try? presetsArray = self.coredataContext.fetch(requestProfiles).first + } + return (presetsArray != nil) + } + + func saveProfileSettingName(name: String) { + coredataContext.perform { [self] in + let save = Profiles(context: self.coredataContext) + save.name = name + save.date = Date.now + try? self.coredataContext.save() + } + } + + func migrateProfileSettingName(name: String) { + coredataContext.perform { [self] in + let save = Profiles(context: self.coredataContext) + save.name = name + save.date = Date.now + save.uploaded = true + try? self.coredataContext.save() + } + } + + func profileSettingUploaded(name: String) { + var profile: String = name + if profile.isEmpty { + profile = "default" + } + + // Avoid duplicates + if !fetchUniqueSettingProfileName(name) { + coredataContext.perform { [self] in + let save = Profiles(context: self.coredataContext) + save.name = profile + save.date = Date.now + save.uploaded = true + try? self.coredataContext.save() + } + } + } + + func activeProfile(name: String) { + coredataContext.perform { [self] in + let save = ActiveProfile(context: self.coredataContext) + save.name = name + save.date = Date.now + save.active = true + try? self.coredataContext.save() + } + } + + func checkIfActiveProfile() -> Bool { + var presetsArray = [ActiveProfile]() + coredataContext.performAndWait { + let requestProfiles = ActiveProfile.fetchRequest() as NSFetchRequest + let sort = NSSortDescriptor(key: "date", ascending: false) + requestProfiles.sortDescriptors = [sort] + try? presetsArray = self.coredataContext.fetch(requestProfiles) + } + return (presetsArray.first?.active ?? false) + } + + func fetchActiveProfile() -> String { + var presetsArray = [ActiveProfile]() + coredataContext.performAndWait { + let requestProfiles = ActiveProfile.fetchRequest() as NSFetchRequest + let sort = NSSortDescriptor(key: "date", ascending: false) + requestProfiles.sortDescriptors = [sort] + try? presetsArray = self.coredataContext.fetch(requestProfiles) + } + return presetsArray.first?.name ?? "default" + } } diff --git a/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings index 6e289e9e40..2de9eb192d 100644 --- a/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Other"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2108,7 +2132,7 @@ Enact a temp Basal or a temp target */ "DeltaContactValue" = "Delta"; /* Contact Image, settings, Trend (data to display) */ -"TrendContactValue" = "Trend"; +"TrendContactValue" = "إتجاه"; /* Contact Image, settings, Last loop time (data to display) */ "LastLoopTimeContactValue" = "Last loop time"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings index f8638ff9ee..25a2d8e26f 100644 --- a/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Glat Glukose Værdi"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Andet"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Vis Tidsinterval Indstillingsknap"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 92feae5c2e..6945c414ce 100644 --- a/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings @@ -47,7 +47,7 @@ "Done" = "Fertig"; /* 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" = "Letzte Loop-Schleife vor %@ Minuten. Berechnung des (Mahlzeiten-)Bolus abschließen oder abbrechen, um den nächsten Schleifendurchlauf zu ermöglichen."; /* Calender Option */ "Display Emojis as Labels" = "Emojis als Etiketten benutzen"; @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Glätten der Glukosewerte"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Datenübermittlung"; + +/* Share and Backup page header */ +"Share and Backup" = "Teilen und Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Einstellungen und Statistiken hochladen"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Teilen und sichern Sie alle Ihre Einstellungen und Statistiken"; + +/* Title of dropdown menu */ +"Sex" = "Geschlecht"; + +/* Sex dropdown menu option */ "Woman" = "Weiblich"; -/* */ +/* Sex dropdown menu option */ "Man" = "Männlich"; -/* */ +/* Sex dropdown menu option */ "Other" = "Sonstiges"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Geheim-Schlüssel"; -/* */ +/* Title of birth date field */ "Birth Date" = "Geburtsdatum"; -/* */ -"Share all of your Statistics" = "Alle meine Statistiken teilen"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Nur das absolute Minimum teilen"; + +/* Minimum share toggle */ "Just iAPS version number" = "Nur iAPS Versionsnummer"; -/* */ -"Share Bare Minimum" = "Nur das absolute Minimum teilen"; +/* 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." = "Alle Informationen, die Sie freigeben möchten, werden anonym hochgeladen. Um doppeltes Hochladen zu verhindern, werden die Daten mit einer eindeutigen, zufälligen Zeichenkette identifiziert, die auf Ihrem Telefon gespeichert wird."; +/* Token section title */ +"Your recovery token" = "Ihr Wiederherstellungs-Token"; -/* */ +/* Token display button */ "Tap to display" = "Tippen, um anzuzeigen"; +/* Token copy button */ +"Long press to copy" = "Zum Kopieren lange drücken"; + +/* Link to statistics button */ +"View Personal Statistics" = "Persönliche Statistiken anzeigen"; + /* */ "Your identifier" = "Ihr Identifikator"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Anzeigeintervall Einstellungs-Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Beim Scrollen nie das kleine Glukosediagramm 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 4303cfb5ae..391ee8e0e3 100644 --- a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings @@ -1948,36 +1948,60 @@ Enact a temp Basal or a temp target */ /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; + +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Other"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; diff --git a/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings index c20b936d16..c0064fd928 100644 --- a/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings @@ -1941,37 +1941,61 @@ Ajustes predeterminados:\n /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Suavizar valores de glucosa"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Compartir"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Mujer"; -/* */ +/* Sex dropdown menu option */ "Man" = "Hombre"; -/* */ +/* Sex dropdown menu option */ "Other" = "Otro"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Anónimo"; -/* */ +/* Title of birth date field */ "Birth Date" = "Fecha de nacimiento"; -/* */ -"Share all of your Statistics" = "Comparte todas las estadísticas"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Compartir lo mínimo"; + +/* Minimum share toggle */ "Just iAPS version number" = "Sólo el número de versión iAPS"; -/* */ -"Share Bare Minimum" = "Compartir lo mínimo"; +/* 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." = "Todo registro de información que elija compartir se hace de forma anónima. Para evitar subidas de información duplicadas, los datos se identifican con un identificador único aleatorio guardado en su teléfono."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Pulsa para mostrar"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Su identificador"; @@ -2605,6 +2629,9 @@ Un límite Autosens.máx > 1,5 no es recomendable cuando se utiliza la función /* UI/UX option */ "Display Time Interval Setting Button" = "Botón de intervalo de horas visibles"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 4ce18c8a7e..cd03eaf01e 100644 --- a/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Other"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 b9ce82b1b3..6f645af641 100644 --- a/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Valeur Glycémie lisse"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Partage"; + +/* Share and Backup page header */ +"Share and Backup" = "Partage et sauvegarde"; + +/* Section 1 title */ +"Upload settings and statistics" = "Téléverser vos paramètres et statistiques"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Partager et sauvegarder tous vos paramètres et statistiques"; + +/* Title of dropdown menu */ +"Sex" = "Sexe biologique"; + +/* Sex dropdown menu option */ "Woman" = "Femme"; -/* */ +/* Sex dropdown menu option */ "Man" = "Homme"; -/* */ +/* Sex dropdown menu option */ "Other" = "Autre"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Confidentiel"; -/* */ +/* Title of birth date field */ "Birth Date" = "Date de naissance"; -/* */ -"Share all of your Statistics" = "Partagez vos statistiques"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Partager le minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Juste le numéro de version iAPS"; -/* */ -"Share Bare Minimum" = "Partager le minimum"; +/* 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." = "Toutes les informations que vous choisissez de partager sont téléchargées de façon anonyme. Pour éviter les doublons, vos données sont identifiées avec une chaîne aléatoire unique enregistrée sur votre téléphone : le jeton de récupération."; -/* */ -"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." = "Chaque parcelle d'information que vous choisissez de partager et téléverser anonymement. Pour éviter les doublons, les données sont identifiées avec une chaine unique aléatoire enregistrée sur votre téléphone."; +/* Token section title */ +"Your recovery token" = "Votre jeton de récupération"; -/* */ +/* Token display button */ "Tap to display" = "Appuyer pour afficher"; +/* Token copy button */ +"Long press to copy" = "Appuyez longuement pour copier"; + +/* Link to statistics button */ +"View Personal Statistics" = "Consulter les statistiques personnelles"; + /* */ "Your identifier" = "Votre identifiant"; @@ -1997,103 +2021,103 @@ Enact a temp Basal or a temp target */ "Access to contacts - unknown state" = "Accès aux contacts - état inconnu"; /* Contact Image, settings, short description of the feature */ -"A Contact Image can be used to get live updates from iAPS to your Apple Watch Contact complication and/or your iPhone Contact widget." = "A Contact Image can be used to get live updates from iAPS to your Apple Watch Contact complication and/or your iPhone Contact widget."; +"A Contact Image can be used to get live updates from iAPS to your Apple Watch Contact complication and/or your iPhone Contact widget." = "Une image de contact peut être utilisée pour obtenir des mises à jour en direct d'iAPS vers votre complication de contact Apple Watch et/ou votre widget de contact pour iPhone."; /* Contact Image, settings, main header */ -"Contact Image" = "Contact Image"; +"Contact Image" = "Image de contact"; /* Contact Image, settings, label for the layout picker */ -"Layout" = "Layout"; +"Layout" = "Affichage"; /* Contact Image, settings, single layout name */ -"Single" = "Single"; +"Single" = "Simple"; /* Contact Image, settings, split layout name */ -"Split" = "Split"; +"Split" = "Double"; /* Contact Image, settings, outer ring - don't show */ -"DontShowRing" = "Don't show"; +"DontShowRing" = "Ne pas afficher"; /* Contact Image, settings, outer ring - Loop status */ -"LoopStatusRing" = "Loop status"; +"LoopStatusRing" = "État de la boucle"; /* Contact Image, settings, outer ring - IOB */ -"IOBRing" = "IOB"; +"IOBRing" = "Insuline active (IOB)"; /* Contact Image, settings, outer ring - COB */ -"COBRing" = "COB"; +"COBRing" = "Glucides actifs (COB)"; /* Contact Image, settings, outer ring - IOB+COB */ -"IOB+COBRing" = "IOB+COB"; +"IOB+COBRing" = "Insuline active + Glucides actifs"; /* Contact Image, settings, label for the primary value picker */ -"Primary" = "Primary"; +"Primary" = "Principal"; /* Contact Image, settings, label for the top value picker */ -"Top" = "Top"; +"Top" = "En haut"; /* Contact Image, settings, label for the bottom value picker */ -"Bottom" = "Bottom"; +"Bottom" = "En bas"; /* Contact Image, settings, header for the outer ring section */ -"Ring" = "Ring"; +"Ring" = "Anneau"; /* Contact Image, settings, label for the outer ring picker */ -"Outer" = "Outer"; +"Outer" = "Anneau extérieur"; /* Contact Image, settings, label for the ring width picker */ -"Width" = "Width"; +"Width" = "Largeur"; /* Contact Image, settings, label for the ring gap picker */ -"Gap" = "Gap"; +"Gap" = "Espacement"; /* Contact Image, settings, header for the font section */ -"Font" = "Font"; +"Font" = "Police de caractères"; /* Contact Image, settings, label for the font size picker */ -"Size" = "Size"; +"Size" = "Taille"; /* Contact Image, settings, label for the secondary font size picker */ -"Secondary size" = "Secondary size"; +"Secondary size" = "Taille secondaire"; /* Contact Image, settings, label for the font tracking picker (tracking - horizontal spacing between letters) */ -"Tracking" = "Tracking"; +"Tracking" = "Espacement"; /* Contact Image, settings, label for the font weight picker */ -"Weight" = "Weight"; +"Weight" = "Poids"; /* Contact Image, settings, label for the dark-mode switcher */ -"Dark mode" = "Dark mode"; +"Dark mode" = "Mode sombre"; /* Contact Image, settings, tighter font tracking */ -"TighterFontTracking" = "Tighter"; +"TighterFontTracking" = "Très serré"; /* Contact Image, settings, tight font tracking */ -"TightFontTracking" = "Tight"; +"TightFontTracking" = "Serré"; /* Contact Image, settings, normal font tracking */ "NormalFontTracking" = "Normal"; /* Contact Image, settings, wide font tracking */ -"WideFontTracking" = "Wide"; +"WideFontTracking" = "Large"; /* Contact Image, settings, light font weight */ -"LightFontWeight" = "Light"; +"LightFontWeight" = "Léger"; /* Contact Image, settings, regular font weight */ -"RegularFontWeight" = "Regular"; +"RegularFontWeight" = "Régulier"; /* Contact Image, settings, medium font weight */ -"MediumFontWeight" = "Medium"; +"MediumFontWeight" = "Moyen"; /* Contact Image, settings, semibold font weight */ -"SemiboldFontWeight" = "Semibold"; +"SemiboldFontWeight" = "Semi-gras"; /* Contact Image, settings, bold font weight */ -"BoldFontWeight" = "Bold"; +"BoldFontWeight" = "Gras"; /* Contact Image, settings, black font weight (black is "very bold") */ -"BlackFontWeight" = "Black"; +"BlackFontWeight" = "Noir"; /* Contact Image, settings, don't display any value */ "NoneContactValue" = "Aucun"; @@ -2102,25 +2126,25 @@ Enact a temp Basal or a temp target */ "GlucoseContactValue" = "Glycémie"; /* Contact Image, settings, Eventual BG (data to display) */ -"EventualBGContactValue" = "Eventual BG"; +"EventualBGContactValue" = "Taux de glucose éventuel"; /* Contact Image, settings, Delta (data to display) */ -"DeltaContactValue" = "Delta"; +"DeltaContactValue" = "Écart"; /* Contact Image, settings, Trend (data to display) */ "TrendContactValue" = "Tendance"; /* Contact Image, settings, Last loop time (data to display) */ -"LastLoopTimeContactValue" = "Last loop time"; +"LastLoopTimeContactValue" = "Heure de la dernière boucle"; /* Contact Image, settings, COB (data to display) */ -"COBContactValue" = "COB"; +"COBContactValue" = "Glucides actifs (COB)"; /* Contact Image, settings, IOB (data to display) */ -"IOBContactValue" = "IOB"; +"IOBContactValue" = "Insuline active (IOB)"; /* Contact Image, settings, loop status (data to display) */ -"LoopStatusContactValue" = "Loop status"; +"LoopStatusContactValue" = "État de la boucle"; /* --------------------------------------------------------------------------------------------------------------- Infotexts from openaps.docs and androidaps.docs @@ -2182,10 +2206,10 @@ Enact a temp Basal or a temp target */ "Defaults to false, so that iAPS will set temps whenever it can, so it will be easier to see if the system is working, even when you are offline. This means iAPS will set a “neutral” temp (same as your default basal) if no adjustments are needed. This is an old setting for OpenAPS to have the options to minimise sounds and notifications from the 'rig', that may wake you up during the night." = "La valeur par défaut est faux, de sorte que iAPS définisse une valeur basal dès qu'il le peut, donc il sera plus facile de voir si le système fonctionne, même lorsque vous êtes hors ligne. Cela signifie qu'OpenAPS définira une valeur basale « neutre » (identique à votre basal par défaut) si aucun ajustement n'est nécessaire. Ceci est un ancien paramètre d'OpenAPS afin de minimiser les sons et les notifications de la 'rig', qui pouvaient vous réveiller pendant la nuit. "; /* Headline "Unsuspend If No Temp” */ -"Unsuspend If No Temp" = "Annuler la suspension si aucune température"; +"Unsuspend If No Temp" = "Annuler la suspension à la fin du temporaire zéro"; /* "Unsuspend If No Temp” */ -"Many people occasionally forget to resume / unsuspend their pump after reconnecting it. If you’re one of them, and you are willing to reliably set a zero temp basal whenever suspending and disconnecting your pump, this feature has your back. If enabled, it will automatically resume / unsuspend the pump if you forget to do so before your zero temp expires. As long as the zero temp is still running, it will leave the pump suspended." = "De nombreuses personnes oublient parfois de reprendre / désuspendre leur pompe après la reconnexion. Si vous êtes l'un d'eux, et que vous êtes définissez un basal temporaire à zéro chaque fois que vous suspendez ou débranchez votre pompe, cette fonction peut être pertinente. Si cette option est activée, elle reprendra automatiquement le basal en cours si vous oubliez de reprendre votre pompe. Tant que la valeur temporaire de basal est de zéro, elle laissera la pompe suspendue."; +"Many people occasionally forget to resume / unsuspend their pump after reconnecting it. If you’re one of them, and you are willing to reliably set a zero temp basal whenever suspending and disconnecting your pump, this feature has your back. If enabled, it will automatically resume / unsuspend the pump if you forget to do so before your zero temp expires. As long as the zero temp is still running, it will leave the pump suspended." = "De nombreuses personnes oublient parfois de reprendre / désuspendre leur pompe après la reconnexion. Si vous êtes l'un d'eux, et que vous êtes prêt à définir de manière fiable un basal temporaire zéro à chaque fois que vous suspendez et débranchez votre pompe, cette fonctionnalité peut être pertinente. Si cette option est activée, elle reprendra automatiquement le fonctionnement de votre pompe si vous oubliez de le faire avant l'expiration du basal temporaire à zéro que vous avez programmé. Tant que le basal temporaire à zéro est toujours en cours d'exécution, la pompe restera suspendue."; /* Headline "Enable UAM" */ "Enable UAM" = "Activer UAM"; @@ -2469,7 +2493,7 @@ Enact a temp Basal or a temp target */ "Statistics settings " = "Paramètres des statistiques "; /* UI/UX title */ -"Override HbA1c Unit" = "Outrepasser l'unité HbA1c"; +"Override HbA1c Unit" = "Convertir l'unité HbA1c"; /* UI/UX option */ "In case you're using both profiles and temp targets" = "Si vous utilisez à la fois des profils et des cibles temporaires"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Afficher le bouton de réglage des heures visibles"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Ne jamais afficher le petit graphique de glucose lors du défilement"; + /* 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 c48456d437..e055b06a60 100644 --- a/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Other"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 7a73c21a55..4152c9430a 100644 --- a/FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Megosztás"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Nő"; -/* */ +/* Sex dropdown menu option */ "Man" = "Férfi"; -/* */ +/* Sex dropdown menu option */ "Other" = "Egyéb"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Titok"; -/* */ +/* Title of birth date field */ "Birth Date" = "Születési dátum"; -/* */ -"Share all of your Statistics" = "Összes statisztika megosztása"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Lehető legkevesebb adat megosztása"; + +/* Minimum share toggle */ "Just iAPS version number" = "Csak az iAPS verzió száma"; -/* */ -"Share Bare Minimum" = "Lehető legkevesebb adat megosztása"; +/* 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." = "Minden egyes megosztani kívánt információ névtelenül kerül feltöltésre. A duplikált feltöltések elkerülése érdekében az adatokat egy egyedi, véletlenszerű karakterlánc azonosítja, amelyet a telefonján tárolunk."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Érintsd meg a megjelenítéshez"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Az ön azonosítója"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* 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"; + /* 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 bf8dee5a10..5ec32b4c9c 100644 --- a/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Valori Glicemici Levigati"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Condivisione"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Donna"; -/* */ +/* Sex dropdown menu option */ "Man" = "Uomo"; -/* */ +/* Sex dropdown menu option */ "Other" = "Altro"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Segreto"; -/* */ +/* Title of birth date field */ "Birth Date" = "Data di nascita"; -/* */ -"Share all of your Statistics" = "Condividi tutte le tue statistiche"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Condividi Minimo Nullo"; + +/* Minimum share toggle */ "Just iAPS version number" = "Solo il numero di versione iAPS"; -/* */ -"Share Bare Minimum" = "Condividi Minimo Nullo"; +/* 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." = "Ogni bit di informazioni che si sceglie di condividere viene caricato in modo anonimo. Per evitare duplicati di caricamenti, i dati sono identificati con una stringa casuale univoca salvata sul telefono."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tocca per visualizzare"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Il tuo identificativo"; @@ -2489,6 +2513,9 @@ Per gli utenti più giovani si consiglia di iniziare con un dosaggio ancora più /* UI/UX option */ "Display Time Interval Setting Button" = "Mostra Pulsante Impostazione nell'intervallo di tempo"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Non visualizzare mai il grafico piccolo del glucosio durante lo scorrimento"; + /* 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 99b5feff9a..e3f454b286 100644 --- a/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Utjevning av blodsukkerverdi"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Annet"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Vis knapp for tidsintervall"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 ca07bd3a48..b60f7ab111 100644 --- a/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings @@ -260,7 +260,7 @@ "Autotune" = "Autotune"; /* */ -"Basal profile" = "Basaal profiel"; +"Basal profile" = "Basaalprofiel"; /* */ "Carb ratio" = "Koolhydraat verhouding"; @@ -284,7 +284,7 @@ "Add" = "Toevoegen"; /* */ -"Basal Profile" = "Basaal profiel"; +"Basal Profile" = "Basaalprofiel"; /* Rate basal profile */ "Rate" = "Waarde"; @@ -363,7 +363,7 @@ Enact a temp Basal or a temp target */ "Invalid URL" = "Ongeldige URL"; /* */ -"Local glucose source" = "Lokale glucose bron"; +"Local glucose source" = "Lokale glucosebron"; /* Header */ "Nightscout Config" = "Nightscout instellingen"; @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Vloeiende glucosewaarde"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Delen van statistieken"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Vrouw"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Anders"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Geheim"; -/* */ +/* Title of birth date field */ "Birth Date" = "Geboortedatum"; -/* */ -"Share all of your Statistics" = "Deel al je statistieken"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Deel alleen het minimale"; + +/* Minimum share toggle */ "Just iAPS version number" = "Alleen iAPS versienummer"; -/* */ -"Share Bare Minimum" = "Deel alleen het minimale"; +/* 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." = "Alle informatie die 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 apparaat is opgeslagen."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tik om weer te geven"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Jouw identificatie"; @@ -2492,6 +2516,9 @@ Eenvoudig gezegd, de Dynamische Carb Ratio past de koolhydraatverhouding aan op /* UI/UX option */ "Display Time Interval Setting Button" = "Knop voor tijdinterval weergeven"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Laat de kleine glucosegrafiek nooit zien tijdens het scrollen"; + /* 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 cb7d4e4518..7d64893723 100644 --- a/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings @@ -1932,37 +1932,61 @@ Połączono z Nightscout!"; /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Other"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2490,6 +2514,9 @@ Połączono z Nightscout!"; /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 6b3b8144ba..ced78bc51d 100644 --- a/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Outros"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 bfc774aa82..a00bbc4143 100644 --- a/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Outro"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 1cd9008798..04258d9310 100644 --- a/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Сглаживать значения глюкозы"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Делиться статистикой"; + +/* Share and Backup page header */ +"Share and Backup" = "Общий доступ и резервирование"; + +/* Section 1 title */ +"Upload settings and statistics" = "Выгружать настройки и статистику"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Делиться и резервно копировать все настройки и статистику"; + +/* Title of dropdown menu */ +"Sex" = "Пол"; + +/* Sex dropdown menu option */ "Woman" = "Женский"; -/* */ +/* Sex dropdown menu option */ "Man" = "Мужской"; -/* */ +/* Sex dropdown menu option */ "Other" = "Прочее"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Скрыть"; -/* */ +/* Title of birth date field */ "Birth Date" = "День рождения"; -/* */ -"Share all of your Statistics" = "Делиться всей Вашей статистикой"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Минимально делиться"; + +/* Minimum share toggle */ "Just iAPS version number" = "Только номер версии iAPS"; -/* */ -"Share Bare Minimum" = "Минимально делиться"; +/* 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." = "Любая информация, которой вы хотите поделиться, загружается анонимно. Чтобы предотвратить повторную загрузку, данные идентифицируются с помощью уникальной случайной строки, сохраненной на вашем телефоне."; +/* Token section title */ +"Your recovery token" = "Ваш токен восстановления"; -/* */ +/* Token display button */ "Tap to display" = "Нажать для отображения"; +/* Token copy button */ +"Long press to copy" = "Длительно нажмите, чтобы скопировать"; + +/* Link to statistics button */ +"View Personal Statistics" = "Просмотр личной статистики"; + /* */ "Your identifier" = "Ваш идентификатор"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Отображать кнопку выбора интервала времени"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Не отображать график уровня глюкозы при прокрутке"; + /* 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 f0cf23ff3f..14fc01b649 100644 --- a/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Hladká hodnota glukózy"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Žena"; -/* */ +/* Sex dropdown menu option */ "Man" = "Muž"; -/* */ +/* Sex dropdown menu option */ "Other" = "Iné"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Tajomstvo"; -/* */ +/* Title of birth date field */ "Birth Date" = "Dátum narodenia"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Len číslo verzie iAPS"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Tlačidlo nastavenia časového intervalu zobrazenia"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 04cf5a4453..2d91a3d5f8 100644 --- a/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings @@ -1942,37 +1942,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Utjämna blodsocker från sensor"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Delning"; + +/* Share and Backup page header */ +"Share and Backup" = "Dela och säkerhetskopiera"; + +/* Section 1 title */ +"Upload settings and statistics" = "Ladda upp inställningar och statistik"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Dela och säkerhetskopiera alla dina inställningar och statistik"; + +/* Title of dropdown menu */ +"Sex" = "Kön"; + +/* Sex dropdown menu option */ "Woman" = "Kvinna"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Annan"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Hemligt"; -/* */ +/* Title of birth date field */ "Birth Date" = "Födelsedatum"; -/* */ -"Share all of your Statistics" = "Dela all statistik"; +/* 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." = "\nOm du aktiverar \"Dela och säkerhetskopiera\" kommer dagliga säkerhetskopior av dina inställningar och statistik att göras till en moln-databas.\n\nSe till att kopiera och spara din återställningstoken nedan. Den kommer att behövas om du någon gång behöver importera dina inställningar till en annan telefon."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Dela endast ett minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Endast appversion"; -/* */ -"Share Bare Minimum" = "Dela endast ett minimum"; +/* 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." = "All information du väljer att dela laddas upp anonymt. För att förhindra dubbletter av uppladdningar identifieras datan med en unik slumpmässig sträng som sparas på din telefon."; -/* */ -"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." = "All information du väljer att dela laddas upp anonymt. För att förhindra dubbletter av uppladdningar identifieras datan med en unik slumpmässig sträng som sparas på telefonen."; +/* Token section title */ +"Your recovery token" = "Din återställningsnyckel"; -/* */ +/* Token display button */ "Tap to display" = "Tryck för att visa"; +/* Token copy button */ +"Long press to copy" = "Tryck länge för att kopiera"; + +/* Link to statistics button */ +"View Personal Statistics" = "Visa personlig statistik"; + /* */ "Your identifier" = "Din identifierare"; @@ -2500,6 +2524,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Visa knapp för tidsintervell"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Dölj det lilla glukosdiagrammet när du scrollar"; + /* 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 fdadba26d2..a771697a70 100644 --- a/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "Diğer"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* 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 bdce32793e..a711c77358 100644 --- a/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Гладке Значення Глюкози"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Поширити"; + +/* Share and Backup page header */ +"Share and Backup" = "Поділитися та створити Резервну копію"; + +/* Section 1 title */ +"Upload settings and statistics" = "Звантажити налаштування та статистику"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Поділитись та зробити Резервну копію всіх ваших Налаштувань та Статистики"; + +/* Title of dropdown menu */ +"Sex" = "Стать"; + +/* Sex dropdown menu option */ "Woman" = "Жінка"; -/* */ +/* Sex dropdown menu option */ "Man" = "Чоловік"; -/* */ +/* Sex dropdown menu option */ "Other" = "Інше"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Секретний ключ"; -/* */ +/* Title of birth date field */ "Birth Date" = "Дата народження"; -/* */ -"Share all of your Statistics" = "Поділіться своєю Статистикою"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Мінімум розподілу"; + +/* Minimum share toggle */ "Just iAPS version number" = "Номер версії iAPS"; -/* */ -"Share Bare Minimum" = "Мінімум розподілу"; +/* 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." = "Кожна інформація, якою ви вирішите поділитися, завантажується анонімно. Щоб запобігти повторним завантаженням, дані ідентифікуються за допомогою унікального випадкового рядка, збереженого на вашому телефоні."; +/* Token section title */ +"Your recovery token" = "Ваш токен для відновлення"; -/* */ +/* Token display button */ "Tap to display" = "Натисніть для відображення"; +/* Token copy button */ +"Long press to copy" = "Утримуйте для копіювання"; + +/* Link to statistics button */ +"View Personal Statistics" = "Переглянути Персональну Статистику"; + /* */ "Your identifier" = "Ваш ідентифікатор"; @@ -2488,6 +2512,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Відображення Кнопки Налаштування Інтервалу Часу"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Ніколи не відображайте маленьку діаграму рівня глюкози під час прокручування"; + /* 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 9fa75840ae..b69fb59dc6 100644 --- a/FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings @@ -348,7 +348,7 @@ Enact a temp Basal or a temp target */ "Allow uploads" = "Cho phép tải lên"; /* API secret in NS */ -"API secret" = "Khóa API"; +"API secret" = "API secret"; /* Connect to NS */ "Connect" = "Kết nối"; @@ -414,22 +414,22 @@ Enact a temp Basal or a temp target */ "\nSettings were imported but the Basals couldn't be saved to pump (No pump). Check your basal settings and tap ´Save on Pump´ to sync the new basal settings" = "\n Các cấu hình đã được nhập nhưng liều nền không được lưu vào bơm (không có bơm). Kiểm tra cấu hình liều nền của bạn và nhấn 'Lưu vào bơm' để đồng hóa cấu hình liều nền mới"; /* Import Error Headline */ -"Import Error" = "Cập nhật lỗi"; +"Import Error" = "Nhập lỗi"; /* */ -"Yes, Import" = "Đồng ý, hãy cập nhật"; +"Yes, Import" = "Đồng ý, hãy nhập"; /* */ "Import settings from Nightscout" = "Nhập cấu hình từ Nightscout"; /* */ -"Import settings?" = "Nhập các cấu hình?"; +"Import settings?" = "Nhập các cài đặt?"; /* */ "Import from Nightscout" = "Nhập từ Nightscout"; /* */ -"Settings imported" = "Các cấu hình đã được nhập"; +"Settings imported" = "Các cài đặt đã được nhập"; /* Import Error */ "\nMismatching glucose units in Nightscout and Pump Settings. Import settings aborted." = "\n Đơn vị glucose không khớp trong Cài đặt Nightscout và Pump. Nhập cấu hình bị hủy bỏ."; @@ -513,13 +513,13 @@ Enact a temp Basal or a temp target */ "0 U/hr" = "U/hr"; /* abbreviation for days */ -"d" = "ngày"; +"d" = "d"; /* abbreviation for hours */ -"h" = "giờ"; +"h" = "h"; /* abbreviation for minutes */ -"m" = "phút"; +"m" = "m"; /* */ "Closed loop" = "Vòng lặp kín"; @@ -1194,7 +1194,7 @@ Enact a temp Basal or a temp target */ "Animated Background" = "Nền màn hình động"; /* Sensor day(s) */ -" day(s)" = " ngày(s)"; +" day(s)" = " day(s)"; /* Option to show HR in Watch app*/ "Display HR on Watch" = "Hiển thị trên đồng hồ"; @@ -1724,10 +1724,10 @@ Enact a temp Basal or a temp target */ "Total Delivery" = "Tổng liều"; /* */ -"Add Omnipod Dash" = "Thay Omnipod Dash"; +"Add Omnipod Dash" = "Thêm Omnipod Dash"; /* */ -"Insert Cannula" = "Thay Cannula"; +"Insert Cannula" = "Lắp Cannula"; /* */ "Check Cannula" = "Kiểm tra Cannula"; @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Làm mịn Glucose"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Chia sẻ"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Nữ"; -/* */ +/* Sex dropdown menu option */ "Man" = "Nam"; -/* */ +/* Sex dropdown menu option */ "Other" = "Khác"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Bí mật"; -/* */ +/* Title of birth date field */ "Birth Date" = "Ngày sinh"; -/* */ -"Share all of your Statistics" = "Chia sẻ tất cả số liệu thống kê của bạn"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Chia sẻ ở mức tối thiểu"; + +/* Minimum share toggle */ "Just iAPS version number" = "Chỉ phiên bản iAPS"; -/* */ -"Share Bare Minimum" = "Chia sẻ ở mức tối thiểu"; +/* 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." = "Mọi thông tin bạn chọn chia sẻ đều được tải lên ẩn danh. Để tránh 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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Chạm để hiển thị"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Mã định danh của bạn"; @@ -2075,25 +2099,25 @@ Enact a temp Basal or a temp target */ "NormalFontTracking" = "Normal"; /* Contact Image, settings, wide font tracking */ -"WideFontTracking" = "Rộng"; +"WideFontTracking" = "Wide"; /* Contact Image, settings, light font weight */ -"LightFontWeight" = "Phông nền sáng"; +"LightFontWeight" = "Light"; /* Contact Image, settings, regular font weight */ -"RegularFontWeight" = "Binh thường"; +"RegularFontWeight" = "Regular"; /* Contact Image, settings, medium font weight */ -"MediumFontWeight" = "Trung bình"; +"MediumFontWeight" = "Medium"; /* Contact Image, settings, semibold font weight */ -"SemiboldFontWeight" = "Đậm nhẹ"; +"SemiboldFontWeight" = "Semibold"; /* Contact Image, settings, bold font weight */ -"BoldFontWeight" = "Đậm"; +"BoldFontWeight" = "Bold"; /* Contact Image, settings, black font weight (black is "very bold") */ -"BlackFontWeight" = "Đen"; +"BlackFontWeight" = "Black"; /* Contact Image, settings, don't display any value */ "NoneContactValue" = "Không"; @@ -2131,7 +2155,7 @@ Enact a temp Basal or a temp target */ "Rewind Resets Autosens" = "Rewind sẽ thiết lập lại Autosens"; /* ”Rewind Resets Autosens” */ -"This feature, enabled by default, resets the autosens ratio to neutral when you rewind your pump, on the assumption that this corresponds to a probable site change. Autosens will begin learning sensitivity anew from the time of the rewind, which may take up to 6 hours. If you usually rewind your pump independently of site changes, you may want to consider disabling this feature." = "Tính năng này, được bật theo mặc định, sẽ đặt lại tỷ lệ cảm biến tự động về mức trung tính khi bạn tua lại máy bơm của mình, với giả định rằng điều này tương ứng với một sự thay đổi địa điểm có thể xảy ra. Autosens sẽ bắt đầu học lại độ nhạy kể từ thời điểm tua lại, quá trình này có thể mất tới 6 giờ. Nếu bạn thường tua lại máy bơm của mình một cách độc lập với những thay đổi ở địa điểm, bạn có thể cân nhắc việc tắt tính năng này."; +"This feature, enabled by default, resets the autosens ratio to neutral when you rewind your pump, on the assumption that this corresponds to a probable site change. Autosens will begin learning sensitivity anew from the time of the rewind, which may take up to 6 hours. If you usually rewind your pump independently of site changes, you may want to consider disabling this feature." = "Tính năng này, được bật theo mặc định, sẽ đặt lại tỷ lệ cảm biến tự động về mức trung tính khi bạn tua lại máy bơm của mình, với giả định rằng điều này tương ứng với một sự thay đổi điểm gắn có thể xảy ra. Autosens sẽ bắt đầu học lại độ nhạy kể từ thời điểm tua lại, quá trình này có thể mất tới 6 giờ. Nếu bạn thường tua lại máy bơm của mình một cách độc lập với những thay đổi ở địa điểm, bạn có thể cân nhắc việc tắt tính năng này."; /* Headline "High Temptarget Raises Sensitivity" */ "High Temptarget Raises Sensitivity" = "Mục tiêu tạm thời cao sẽ làm tăng độ nhạy"; @@ -2179,13 +2203,13 @@ Enact a temp Basal or a temp target */ "Skip Neutral Temps" = "Bỏ qua liều tạm thời trung lập"; /* "Skip Neutral Temps" */ -"Defaults to false, so that iAPS will set temps whenever it can, so it will be easier to see if the system is working, even when you are offline. This means iAPS will set a “neutral” temp (same as your default basal) if no adjustments are needed. This is an old setting for OpenAPS to have the options to minimise sounds and notifications from the 'rig', that may wake you up during the night." = "Mặc định là False, do đó iAPS sẽ đặt liều tạm thời bất cứ khi nào có thể, do đó, sẽ dễ dàng hơn để xem hệ thống có hoạt động hay không, ngay cả khi bạn ngoại tuyến. Điều này có nghĩa là OpenAPS sẽ đặt Liều tạm thời “trung tính” (giống như Liều tạm thời cơ bản mặc định của bạn) nếu không cần điều chỉnh. Đây là cài đặt cũ để OpenAPS có các tùy chọn giảm thiểu âm thanh và thông báo từ 'âm mưu' có thể đánh thức bạn vào ban đêm. "; +"Defaults to false, so that iAPS will set temps whenever it can, so it will be easier to see if the system is working, even when you are offline. This means iAPS will set a “neutral” temp (same as your default basal) if no adjustments are needed. This is an old setting for OpenAPS to have the options to minimise sounds and notifications from the 'rig', that may wake you up during the night." = "Mặc định là False, do đó iAPS sẽ đặt liều tạm thời bất cứ khi nào có thể, do đó, sẽ dễ dàng hơn để xem hệ thống có hoạt động hay không, ngay cả khi bạn ngoại tuyến. Điều này có nghĩa là OpenAPS sẽ đặt Liều tạm thời “trung tính” (giống như Liều tạm thời cơ bản mặc định của bạn) nếu không cần điều chỉnh. Đây là cài đặt cũ để OpenAPS có các tùy chọn giảm thiểu âm thanh và thông báo từ 'rig' có thể đánh thức bạn vào ban đêm. "; /* Headline "Unsuspend If No Temp” */ "Unsuspend If No Temp" = "Bỏ tạm dừng nếu không có tạm thời"; /* "Unsuspend If No Temp” */ -"Many people occasionally forget to resume / unsuspend their pump after reconnecting it. If you’re one of them, and you are willing to reliably set a zero temp basal whenever suspending and disconnecting your pump, this feature has your back. If enabled, it will automatically resume / unsuspend the pump if you forget to do so before your zero temp expires. As long as the zero temp is still running, it will leave the pump suspended." = "Nhiều người thỉnh thoảng quên tiếp tục/hủy tạm dừng máy bơm sau khi kết nối lại. Nếu bạn là một trong số họ và sẵn sàng đặt nhiệt độ cơ bản bằng 0 một cách đáng tin cậy bất cứ khi nào tạm dừng và ngắt kết nối máy bơm của mình, thì tính năng này sẽ hỗ trợ bạn. Nếu được bật, nó sẽ tự động tiếp tục/hủy tạm dừng máy bơm nếu bạn quên làm như vậy trước khi hết tạm thời 0. Miễn là tạm thời bằng 0 vẫn đang chạy, nó sẽ khiến máy bơm bị treo."; +"Many people occasionally forget to resume / unsuspend their pump after reconnecting it. If you’re one of them, and you are willing to reliably set a zero temp basal whenever suspending and disconnecting your pump, this feature has your back. If enabled, it will automatically resume / unsuspend the pump if you forget to do so before your zero temp expires. As long as the zero temp is still running, it will leave the pump suspended." = "Nhiều người thỉnh thoảng quên tiếp tục/hủy tạm dừng máy bơm sau khi kết nối lại. Nếu bạn là một trong số họ và sẵn sàng đặt liều tạm thời cơ bản bằng 0 một cách đáng tin cậy bất cứ khi nào tạm dừng và ngắt kết nối máy bơm của mình, thì tính năng này sẽ hỗ trợ bạn. Nếu được bật, nó sẽ tự động tiếp tục/hủy tạm dừng máy bơm nếu bạn quên làm như vậy trước khi hết tạm thời 0. Miễn là tạm thời bằng 0 vẫn đang chạy, nó sẽ khiến máy bơm bị treo."; /* Headline "Enable UAM" */ "Enable UAM" = "Kích hoạt UAM"; @@ -2203,7 +2227,7 @@ Enact a temp Basal or a temp target */ "Enable SMB With Temptarget" = "Kích hoạt SMB với mục tiêu tạm thời"; /* "Enable SMB With Temptarget” */ -"This enables supermicrobolus (SMB) with eating soon / low temp targets. With this feature enabled, any temporary target below 100mg/dL, such as a temp target of 99 (or 80, the typical eating soon target) will enable SMB." = "Điều này cho phép Super Micro Bolus (SMB) đạt được mục tiêu ăn sớm / nhiệt độ thấp. Khi tính năng này được bật, mọi mục tiêu tạm thời dưới 100mg/dL, chẳng hạn như mục tiêu tạm thời là 99 (hoặc 80, mục tiêu ăn sớm thông thường) sẽ kích hoạt SMB."; +"This enables supermicrobolus (SMB) with eating soon / low temp targets. With this feature enabled, any temporary target below 100mg/dL, such as a temp target of 99 (or 80, the typical eating soon target) will enable SMB." = "Điều này cho phép Super Micro Bolus (SMB) đạt được mục tiêu ăn sớm / liều tạm thời thấp. Khi tính năng này được bật, mọi mục tiêu tạm thời dưới 100mg/dL, chẳng hạn như mục tiêu tạm thời là 99 (hoặc 80, mục tiêu ăn sớm thông thường) sẽ kích hoạt SMB."; /* Headline "Enable SMB Always" */ "Enable SMB Always" = "Luôn bật SMB"; @@ -2321,7 +2345,7 @@ Enact a temp Basal or a temp target */ "Max UAM SMB Basal Minutes" = "Số phút cơ bản UAM SMB tối đa"; /* "Max UAM SMB Basal Minutes" */ -"Defaults to start at 30. This is the maximum minutes of basal that can be delivered by UAM as a single SMB when IOB exceeds COB. This gives the ability to make UAM more or less aggressive if you choose. It is recommended that the value is set to start at 30, in line with the default, and if you choose to increase this value, do so in no more than 15 minute increments, keeping a close eye on the effects of the changes. Reducing the value will cause UAM to dose less insulin for each SMB. It is not recommended to set this value higher than 60 mins, as this may affect the ability for the algorithm to safely zero temp. It is also recommended that pushover is used when setting the value to be greater than default, so that alerts are generated for any predicted lows or highs." = "Mặc định bắt đầu từ 30. Đây là số phút cơ bản tối đa mà UAM có thể phân phối dưới dạng một SMB khi IOB vượt quá COB. Điều này mang lại khả năng khiến UAM trở nên hung hãn hơn hoặc ít hơn nếu bạn chọn. Bạn nên đặt giá trị này để bắt đầu ở mức 30, phù hợp với giá trị mặc định và nếu bạn chọn tăng giá trị này, hãy thực hiện với khoảng tăng không quá 15 phút, đồng thời theo dõi chặt chẽ tác động của các thay đổi. Việc giảm giá trị sẽ khiến UAM giảm liều insulin cho mỗi SMB. Không nên đặt giá trị này cao hơn 60 phút vì điều này có thể ảnh hưởng đến khả năng thuật toán về nhiệt độ bằng 0 một cách an toàn. Chúng tôi cũng khuyên bạn nên sử dụng tính năng đẩy khi đặt giá trị lớn hơn giá trị mặc định để cảnh báo được tạo ra cho bất kỳ mức thấp hoặc mức cao được dự đoán nào."; +"Defaults to start at 30. This is the maximum minutes of basal that can be delivered by UAM as a single SMB when IOB exceeds COB. This gives the ability to make UAM more or less aggressive if you choose. It is recommended that the value is set to start at 30, in line with the default, and if you choose to increase this value, do so in no more than 15 minute increments, keeping a close eye on the effects of the changes. Reducing the value will cause UAM to dose less insulin for each SMB. It is not recommended to set this value higher than 60 mins, as this may affect the ability for the algorithm to safely zero temp. It is also recommended that pushover is used when setting the value to be greater than default, so that alerts are generated for any predicted lows or highs." = "Mặc định bắt đầu từ 30. Đây là số phút cơ bản tối đa mà UAM có thể phân phối dưới dạng một SMB khi IOB vượt quá COB. Điều này mang lại khả năng khiến UAM trở nên hung hãn hơn hoặc ít hơn nếu bạn chọn. Bạn nên đặt giá trị này để bắt đầu ở mức 30, phù hợp với giá trị mặc định và nếu bạn chọn tăng giá trị này, hãy thực hiện với khoảng tăng không quá 15 phút, đồng thời theo dõi chặt chẽ tác động của các thay đổi. Việc giảm giá trị sẽ khiến UAM giảm liều insulin cho mỗi SMB. Không nên đặt giá trị này cao hơn 60 phút vì điều này có thể ảnh hưởng đến khả năng thuật toán về liều tạm thời bằng 0 một cách an toàn. Chúng tôi cũng khuyên bạn nên sử dụng tính năng đẩy khi đặt giá trị lớn hơn giá trị mặc định để cảnh báo được tạo ra cho bất kỳ mức thấp hoặc mức cao được dự đoán nào."; /* Headline "SMB Interval" */ "SMB Interval" = "Khoảng thời gian SMB"; @@ -2330,7 +2354,7 @@ Enact a temp Basal or a temp target */ "Minimum duration in minutes for new SMB since last SMB or manual bolus" = "Thời lượng tối thiểu tính bằng phút cho SMB mới kể từ SMB cuối cùng hoặc liều truyền thủ công"; /* Headline "Bolus Increment" */ -"Bolus Increment" = "Tăng liều Bolus nhanh"; +"Bolus Increment" = "Tốc độ Bolus nhanh"; /* "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." = "Số SMB được ban hành nhỏ nhất. Lượng tối thiểu đối với máy bơm Omnipod là 0,05 U, trong khi đối với máy bơm Medtronic, lượng này khác nhau đối với nhiều kiểu máy khác nhau, từ 0,025 U đến 0,10 U. Vui lòng kiểm tra lượng truyền nhanh tối thiểu mà máy bơm của bạn có thể cung cấp. Giá trị mặc định là 0,1."; @@ -2489,6 +2513,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Nút cài đặt khoảng thời gian hiển thị"; +/* 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"; + /* Setting title */ "Bolus Calculator" = "Tính toán Bolus"; @@ -2499,7 +2526,7 @@ Enact a temp Basal or a temp target */ "Live Activity" = "Hoạt động trực tiếp"; /* Notification option */ -"Live activity displays blood glucose live on the lock screen and on the dynamic island (if available)" = "Hiển thị hoạt động trực tiếp đường huyết trên màn hình khóa"; +"Live activity displays blood glucose live on the lock screen and on the dynamic island (if available)" = "Hiển thị hoạt động trực tiếp đường huyết trên màn hình khóa và năng động (nếu có)"; /* Notification option */ "Show Live activity" = "Hiển thị hoạt động trực tiếp"; diff --git a/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings index 851acff552..c2bc5e9730 100644 --- a/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings @@ -1930,37 +1930,61 @@ Enact a temp Basal or a temp target */ /* Smoothing of CGM readings */ "Smooth Glucose Value" = "Smooth Glucose Value"; /* ------------------------------------------- Sharing -------------------------------------------------------*/ -/* */ +/* Setting Title */ + +"Sharing" = "Sharing"; + +/* Share and Backup page header */ +"Share and Backup" = "Share and Backup"; + +/* Section 1 title */ +"Upload settings and statistics" = "Upload settings and statistics"; +/* On-off toggle */ +"Share and Backup all of your Settings and Statistics" = "Share and Backup all of your Settings and Statistics"; + +/* Title of dropdown menu */ +"Sex" = "Sex"; + +/* Sex dropdown menu option */ "Woman" = "Woman"; -/* */ +/* Sex dropdown menu option */ "Man" = "Man"; -/* */ +/* Sex dropdown menu option */ "Other" = "其他"; -/* */ +/* Sex dropdown menu option */ "Secret" = "Secret"; -/* */ +/* Title of birth date field */ "Birth Date" = "Birth Date"; -/* */ -"Share all of your Statistics" = "Share all of your Statistics"; +/* 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."; -/* */ +/* Section title if sharing off */ +"Share Bare Minimum" = "Share Bare Minimum"; + +/* Minimum share toggle */ "Just iAPS version number" = "Just iAPS version number"; -/* */ -"Share Bare Minimum" = "Share Bare Minimum"; +/* 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." = "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."; +/* Token section title */ +"Your recovery token" = "Your recovery token"; -/* */ +/* Token display button */ "Tap to display" = "Tap to display"; +/* Token copy button */ +"Long press to copy" = "Long press to copy"; + +/* Link to statistics button */ +"View Personal Statistics" = "View Personal Statistics"; + /* */ "Your identifier" = "Your identifier"; @@ -2490,6 +2514,9 @@ Enact a temp Basal or a temp target */ /* UI/UX option */ "Display Time Interval Setting Button" = "Display Time Interval Setting Button"; +/* UI/UX option */ +"Never display the small glucose chart when scrolling" = "Never display the small glucose chart when scrolling"; + /* Setting title */ "Bolus Calculator" = "Bolus Calculator"; diff --git a/FreeAPS/Sources/Models/BloodGlucose.swift b/FreeAPS/Sources/Models/BloodGlucose.swift index ad4307f40c..9f37b8ae40 100644 --- a/FreeAPS/Sources/Models/BloodGlucose.swift +++ b/FreeAPS/Sources/Models/BloodGlucose.swift @@ -1,6 +1,6 @@ import Foundation -struct BloodGlucose: JSON, Identifiable, Hashable { +struct BloodGlucose: JSON, Identifiable, Hashable, Codable { enum Direction: String, JSON { case tripleUp = "TripleUp" case doubleUp = "DoubleUp" @@ -16,6 +16,76 @@ struct BloodGlucose: JSON, Identifiable, Hashable { case rateOutOfRange = "RATE OUT OF RANGE" } + enum CodingKeys: String, CodingKey { + case _id + case sgv + case direction + case date + case dateString + case unfiltered + case filtered + case noise + case glucose + case type + case activationDate + case sessionStartDate + case transmitterID + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + _id = try container.decode(String.self, forKey: ._id) + + do { + sgv = try container.decode(Int.self, forKey: .sgv) + } catch { + // The nightscout API returns a double instead of an int + sgv = Int(try container.decode(Double.self, forKey: .sgv)) + } + + direction = try container.decodeIfPresent(Direction.self, forKey: .direction) + date = try container.decode(Decimal.self, forKey: .date) + dateString = try container.decode(Date.self, forKey: .dateString) + unfiltered = try container.decodeIfPresent(Decimal.self, forKey: .unfiltered) + filtered = try container.decodeIfPresent(Decimal.self, forKey: .filtered) + noise = try container.decodeIfPresent(Int.self, forKey: .noise) + glucose = try container.decodeIfPresent(Int.self, forKey: .glucose) + type = try container.decodeIfPresent(String.self, forKey: .type) + activationDate = try container.decodeIfPresent(Date.self, forKey: .activationDate) + sessionStartDate = try container.decodeIfPresent(Date.self, forKey: .sessionStartDate) + transmitterID = try container.decodeIfPresent(String.self, forKey: .transmitterID) + } + + init( + _id: String = UUID().uuidString, + sgv: Int? = nil, + direction: Direction? = nil, + date: Decimal, + dateString: Date, + unfiltered: Decimal? = nil, + filtered: Decimal? = nil, + noise: Int? = nil, + glucose: Int? = nil, + type: String? = nil, + activationDate: Date? = nil, + sessionStartDate: Date? = nil, + transmitterID: String? = nil + ) { + self._id = _id + self.sgv = sgv + self.direction = direction + self.date = date + self.dateString = dateString + self.unfiltered = unfiltered + self.filtered = filtered + self.noise = noise + self.glucose = glucose + self.type = type + self.activationDate = activationDate + self.sessionStartDate = sessionStartDate + self.transmitterID = transmitterID + } + var _id = UUID().uuidString var id: String { _id diff --git a/FreeAPS/Sources/Models/DatabaseModels.swift b/FreeAPS/Sources/Models/DatabaseModels.swift new file mode 100644 index 0000000000..90108ec53e --- /dev/null +++ b/FreeAPS/Sources/Models/DatabaseModels.swift @@ -0,0 +1,100 @@ +import Foundation + +struct DatabasePumpSettings: JSON { + var report = "pumpSettings" + let settings: PumpSettings? + let enteredBy: String + let profile: String? +} + +struct DatabaseTempTargets: JSON { + var report = "tempTargets" + let tempTargets: [TempTarget] + let enteredBy: String + let profile: String? +} + +struct DatabaseProfileStore: JSON { + var report = "profiles" + let units: String + var enteredBy: String + let store: [String: ScheduledNightscoutProfile] + var profile: String +} + +struct NightscoutStatistics: JSON { + var report = "statistics" + let dailystats: Statistics? + let justVersion: BareMinimum? +} + +struct NightscoutPreferences: JSON { + var report = "preferences" + let preferences: Preferences? + let enteredBy: String + let profile: String? +} + +struct NightscoutSettings: JSON { + var report = "settings" + let settings: FreeAPSSettings? + let enteredBy: String + let profile: String? +} + +struct Loaded { + var sens = false + var settings = false + var preferences = false + var targets = false + var carbratios = false + var basalProfiles = false +} + +struct ProfileList: JSON { + var profiles: String +} + +struct MigratedMeals: Codable { + var carbs: Decimal + var dish: String + var fat: Decimal + var protein: Decimal +} + +struct MigratedOverridePresets: Codable { + var advancedSettings: Bool + var cr: Bool + var date: Date + var duration: Decimal + var emoji: String + var end: Decimal + var id: String + var indefininite: Bool + var isf: Bool + var isndAndCr: Bool + var maxIOB: Decimal + var name: String + var overrideMaxIOB: Bool + var percentage: Double + var smbAlwaysOff: Bool + var smbIsOff: Bool + var smbMinutes: Decimal + var start: Decimal + var target: Decimal + var uamMinutes: Decimal +} + +struct MealDatabase: JSON { + var report = "mealPresets" + var profile: String + var presets: [MigratedMeals] + let enteredBy: String +} + +struct OverrideDatabase: JSON { + var report = "overridePresets" + var profile: String + var presets: [MigratedOverridePresets] + let enteredBy: String +} diff --git a/FreeAPS/Sources/Models/NightscoutPreferences.swift b/FreeAPS/Sources/Models/NightscoutPreferences.swift deleted file mode 100644 index f02213772f..0000000000 --- a/FreeAPS/Sources/Models/NightscoutPreferences.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -struct NightscoutPreferences: JSON { - var report = "preferences" - let preferences: Preferences? - let enteredBy: String -} diff --git a/FreeAPS/Sources/Models/NightscoutSettings.swift b/FreeAPS/Sources/Models/NightscoutSettings.swift deleted file mode 100644 index 5a757372a7..0000000000 --- a/FreeAPS/Sources/Models/NightscoutSettings.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -struct NightscoutSettings: JSON { - var report = "settings" - let settings: FreeAPSSettings? - let enteredBy: String -} diff --git a/FreeAPS/Sources/Models/NightscoutStatistics.swift b/FreeAPS/Sources/Models/NightscoutStatistics.swift deleted file mode 100644 index e743c78ba6..0000000000 --- a/FreeAPS/Sources/Models/NightscoutStatistics.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -struct NightscoutStatistics: JSON { - let report = "statistics" - let dailystats: Statistics? - let justVersion: BareMinimum? -} diff --git a/FreeAPS/Sources/Models/NightscoutStatus.swift b/FreeAPS/Sources/Models/NightscoutStatus.swift index 4102dde9d2..4cae2e9088 100644 --- a/FreeAPS/Sources/Models/NightscoutStatus.swift +++ b/FreeAPS/Sources/Models/NightscoutStatus.swift @@ -52,4 +52,5 @@ struct NightscoutProfileStore: JSON { let units: String var enteredBy: String let store: [String: ScheduledNightscoutProfile] + let profile: String? } diff --git a/FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift b/FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift index f3b3138026..626b64fd62 100644 --- a/FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift +++ b/FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift @@ -209,12 +209,9 @@ extension DataTable { let saveToJSON = BloodGlucose( _id: id, - direction: nil, + sgv: Int(glucose), date: Decimal(now.timeIntervalSince1970) * 1000, dateString: now, - unfiltered: nil, - filtered: nil, - noise: nil, glucose: Int(glucose), type: GlucoseType.manual.rawValue ) diff --git a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift index bc38ca3db4..353ac5b28d 100644 --- a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift +++ b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift @@ -780,7 +780,7 @@ extension Home { .font(.suggestionError) .padding(.bottom, 4) .padding(.top, 8) - Text(errorMessage).font(.buttonFont).foregroundColor(.loopRed) + Text(errorMessage).font(.suggestionError).fontWeight(.semibold).foregroundColor(.orange) } else if let suggestion = state.suggestion, (suggestion.bg ?? 100) == 400 { Text("Invalid CGM reading (HIGH).").font(.suggestionError).bold().foregroundColor(.loopRed).padding(.top, 8) Text("SMBs and High Temps Disabled.").font(.suggestionParts).foregroundColor(.white).padding(.bottom, 4) diff --git a/FreeAPS/Sources/Modules/Sharing/View/SharingRootView.swift b/FreeAPS/Sources/Modules/Sharing/View/SharingRootView.swift index 7527ddcefb..e3b4fe812f 100644 --- a/FreeAPS/Sources/Modules/Sharing/View/SharingRootView.swift +++ b/FreeAPS/Sources/Modules/Sharing/View/SharingRootView.swift @@ -68,7 +68,7 @@ extension Sharing { Section { HStack { - Text(display ? state.identfier : "Tap to display") + Text(display ? state.identfier : NSLocalizedString("Tap to display", comment: "Token display button")) } .frame(maxWidth: .infinity, alignment: .center) .onTapGesture { display.toggle() } @@ -81,7 +81,7 @@ extension Sharing { } } } - header: { Text("\nYour recovery token") } + header: { Text("Your recovery token") } footer: { Text((copied && display) ? "" : display ? "Long press to copy" : "") diff --git a/FreeAPS/Sources/Services/Network/Database.swift b/FreeAPS/Sources/Services/Network/Database.swift new file mode 100644 index 0000000000..9af01b06b3 --- /dev/null +++ b/FreeAPS/Sources/Services/Network/Database.swift @@ -0,0 +1,422 @@ +import Combine +import Foundation + +class Database { + init(token: String) { + self.token = token + } + + private enum Config { + static let sharePath = "/upload.php" + static let versionPath = "/vcheck.php" + static let download = "/download.php?token=" + static let profileList = "§ion=profile_list" + static let retryCount = 2 + static let timeout: TimeInterval = 60 + } + + let url: URL = IAPSconfig.statURL + let token: String + + private let service = NetworkService() +} + +extension Database { + func fetchPreferences(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=preferences&profile=" + name + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: Preferences.self, decoder: JSONCoding.decoder) + .eraseToAnyPublisher() + } + + func moveProfiles(token: String, restoreToken: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + restoreToken + "&new_token=" + token + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func fetchProfiles() -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + Config.profileList + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: ProfileList.self, decoder: JSONCoding.decoder) + .eraseToAnyPublisher() + } + + func fetchSettings(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=settings&profile=" + name + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .customISO8601 + + return service.run(request) + .retry(Config.retryCount) + .decode(type: FreeAPSSettings.self, decoder: decoder) + .eraseToAnyPublisher() + } + + func fetchProfile(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=profile&profile=" + name + + var request = URLRequest(url: components.url!) + request.allowsConstrainedNetworkAccess = false + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: NightscoutProfileStore.self, decoder: JSONCoding.decoder) + .eraseToAnyPublisher() + } + + func deleteProfile(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=profiles_delete&profile=" + name + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func fetchPumpSettings(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=pumpSettings&profile=" + name + + var request = URLRequest(url: components.url!) + request.allowsConstrainedNetworkAccess = true + request.timeoutInterval = Config.timeout + + let decoder = JSONDecoder() + + return service.run(request) + .retry(Config.retryCount) + .decode(type: PumpSettings.self, decoder: decoder) + .eraseToAnyPublisher() + } + + func fetchTempTargets(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=tempTargets&profile=" + name + + var request = URLRequest(url: components.url!) + request.allowsConstrainedNetworkAccess = true + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: DatabaseTempTargets.self, decoder: JSONCoding.decoder) + .eraseToAnyPublisher() + } + + func fetchMealPressets(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=mealPresets&profile=" + name + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: MealDatabase.self, decoder: JSONCoding.decoder) + .eraseToAnyPublisher() + } + + func fetchOverridePressets(_ name: String) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.download + token + "§ion=overridePresets&profile=" + name + + var request = URLRequest(url: components.url!) + request.allowsConstrainedNetworkAccess = true + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: OverrideDatabase.self, decoder: JSONCoding.decoder) + .eraseToAnyPublisher() + } + + func uploadSettingsToDatabase(_ profile: NightscoutProfileStore) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(profile) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func uploadStats(_ stats: NightscoutStatistics) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = try! JSONCoding.encoder.encode(stats) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func fetchVersion() -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.versionPath + + var request = URLRequest(url: components.url!) + request.allowsConstrainedNetworkAccess = true + request.timeoutInterval = Config.timeout + + return service.run(request) + .retry(Config.retryCount) + .decode(type: Version.self, decoder: JSONCoding.decoder) + .catch { error -> AnyPublisher in + warning(.nightscout, "Version fetching error: \(error.localizedDescription) \(request)") + return Just(Version(main: "", dev: "")).setFailureType(to: Swift.Error.self).eraseToAnyPublisher() + } + .eraseToAnyPublisher() + } + + func uploadPrefs(_ prefs: NightscoutPreferences) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(prefs) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func uploadSettings(_ settings: NightscoutSettings) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(settings) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func uploadPumpSettings(_ settings: DatabasePumpSettings) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(settings) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func uploadTempTargets(_ targets: DatabaseTempTargets) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(targets) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func uploadMealPresets(_ presets: MealDatabase) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(presets) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + func uploaOverrridePresets(_ presets: OverrideDatabase) -> AnyPublisher { + var components = URLComponents() + components.scheme = url.scheme + components.host = url.host + components.port = url.port + components.path = Config.sharePath + + var request = URLRequest(url: components.url!) + request.timeoutInterval = Config.timeout + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + request.httpBody = try! JSONCoding.encoder.encode(presets) + request.httpMethod = "POST" + + return service.run(request) + .retry(Config.retryCount) + .map { _ in () } + .eraseToAnyPublisher() + } + + private func migrateMealPresets() -> [MigratedMeals] { + let meals = CoreDataStorage().fetchMealPresets() + return meals.map({ item -> MigratedMeals in + MigratedMeals( + carbs: (item.carbs ?? 0) as Decimal, + dish: item.dish ?? "", + fat: (item.fat ?? 0) as Decimal, + protein: (item.protein ?? 0) as Decimal + ) + }) + } + + private func migrateOverridePresets() -> [MigratedOverridePresets] { + let presets = OverrideStorage().fetchProfiles() + return presets.map({ item -> MigratedOverridePresets in + MigratedOverridePresets( + advancedSettings: item.advancedSettings, + cr: item.cr, + date: item.date ?? Date(), + duration: (item.duration ?? 0) as Decimal, + emoji: item.emoji ?? "", + end: (item.end ?? 0) as Decimal, + id: item.id ?? "", + indefininite: item.indefinite, + isf: item.isf, + isndAndCr: item.isfAndCr, + maxIOB: (item.maxIOB ?? 0) as Decimal, + name: item.name ?? "", + overrideMaxIOB: item.overrideMaxIOB, + percentage: item.percentage, + smbAlwaysOff: item.smbIsAlwaysOff, + smbIsOff: item.smbIsOff, + smbMinutes: (item.smbMinutes ?? 0) as Decimal, + start: (item.start ?? 0) as Decimal, + target: (item.target ?? 0) as Decimal, + uamMinutes: (item.uamMinutes ?? 0) as Decimal + ) + + }) + } + + func mealPresetDatabaseUpload(profile: String, token: String) -> MealDatabase { + MealDatabase(profile: profile, presets: migrateMealPresets(), enteredBy: token) + } + + func overridePresetDatabaseUpload(profile: String, token: String) -> OverrideDatabase { + OverrideDatabase(profile: profile, presets: migrateOverridePresets(), enteredBy: token) + } +} diff --git a/FreeAPS/Sources/Services/Network/NightscoutManager.swift b/FreeAPS/Sources/Services/Network/NightscoutManager.swift index 0ea68ef309..7d4bb63890 100644 --- a/FreeAPS/Sources/Services/Network/NightscoutManager.swift +++ b/FreeAPS/Sources/Services/Network/NightscoutManager.swift @@ -19,7 +19,7 @@ protocol NightscoutManager: GlucoseSource { func uploadManualGlucose() func uploadStatistics(dailystat: Statistics) func uploadVersion(json: BareMinimum) - func uploadPreferences(_ preferences: Preferences) + func uploadPreferences(_ preferences: NightscoutPreferences) func uploadProfileAndSettings(_: Bool) func uploadOverride(_ profile: String, _ duration: Double, _ date: Date) func deleteAnnouncements() @@ -62,12 +62,16 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { settingsManager.settings.uploadStats } + private var isVersionUploadEnabled: Bool { + settingsManager.settings.uploadVersion + } + private var isUploadGlucoseEnabled: Bool { settingsManager.settings.uploadGlucose } - private var isVersionUploadEnabled: Bool { - settingsManager.settings.uploadVersion + private var name: String { + CoreDataStorage().fetchSettingProfileName() } private var nightscoutAPI: NightscoutAPI? { @@ -95,6 +99,10 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { } } + private func saveToCoreData(_ name: String) { + CoreDataStorage().profileSettingUploaded(name: name) + } + func sourceInfo() -> [String: Any]? { if let ping = ping { return [GlucoseSourceKey.nightscoutPing.rawValue: ping] @@ -444,6 +452,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { debug(.nightscout, "Statistics uploaded") CoreDataStorage().saveStatUploadCount() UserDefaults.standard.set(false, forKey: IAPSconfig.newVersion) + self.uploadProfileAndSettings(true) case let .failure(error): debug(.nightscout, "Statistics upload failed" + error.localizedDescription) } @@ -477,20 +486,34 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { } } - func uploadPreferences(_ preferences: Preferences) { - let prefs = NightscoutPreferences( - preferences: settingsManager.preferences, enteredBy: getIdentifier() - ) - - let nightscout = NightscoutAPI(url: IAPSconfig.statURL) - + func uploadPreferences(_ preferences: NightscoutPreferences) { + let db = Database(token: preferences.enteredBy) processQueue.async { - nightscout.uploadPrefs(prefs) + db.uploadPrefs(preferences) .sink { completion in switch completion { case .finished: - debug(.nightscout, "Preferences uploaded to database") + debug(.nightscout, "Preferences uploaded to database. Profile: \(preferences.profile ?? "")") self.storage.save(preferences, as: OpenAPS.Nightscout.uploadedPreferences) + self.saveToCoreData(preferences.profile ?? "default") + case let .failure(error): + debug(.nightscout, "Preferences failed to upload to database " + error.localizedDescription) + } + } receiveValue: {} + .store(in: &self.lifetime) + } + } + + func uploadSettings(_ settings: NightscoutSettings) { + let db = Database(token: settings.enteredBy) + processQueue.async { + db.uploadSettings(settings) + .sink { completion in + switch completion { + case .finished: + debug(.nightscout, "Settings uploaded to database. Profile: \(settings.profile ?? "")") + self.storage.save(settings, as: OpenAPS.Nightscout.uploadedSettings) + self.saveToCoreData(settings.profile ?? "default") case let .failure(error): debug(.nightscout, error.localizedDescription) } @@ -499,22 +522,70 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { } } - func uploadSettings(_ settings: FreeAPSSettings) { - let sets = NightscoutSettings( - settings: settingsManager.settings, enteredBy: getIdentifier() - ) + private func uploadPumpSettingsToDatabase(_ settings: PumpSettings, token: String, name: String?) { + let upload = DatabasePumpSettings(settings: settings, enteredBy: token, profile: name) + processQueue.async { + Database(token: token).uploadPumpSettings(upload) + .sink { completion in + switch completion { + case .finished: + debug(.nightscout, "Pump settings uploaded to database. Profile: \(upload.profile ?? "")") + self.storage.save(settings, as: OpenAPS.Nightscout.uploadedPumpSettings) + self.saveToCoreData(name ?? "default") + case let .failure(error): + debug(.nightscout, "Pump settings failed to upload to database " + error.localizedDescription) + } + } receiveValue: {} + .store(in: &self.lifetime) + } + } - let nightscout = NightscoutAPI(url: IAPSconfig.statURL) + private func uploadTempTargetsToDatabase(_ targets: [TempTarget], token: String, name: String?) { + let upload = DatabaseTempTargets(tempTargets: targets, enteredBy: token, profile: name ?? "default") + processQueue.async { + Database(token: token).uploadTempTargets(upload) + .sink { completion in + switch completion { + case .finished: + debug(.nightscout, "Temp targets uploaded to database. Profile: \(upload.profile ?? "")") + self.storage.save(targets, as: OpenAPS.Nightscout.uploadedTempTargetsDatabase) + self.saveToCoreData(name ?? "default") + case let .failure(error): + debug(.nightscout, "Temp targets failed to upload to database " + error.localizedDescription) + } + } receiveValue: {} + .store(in: &self.lifetime) + } + } + private func uploadMealPresetsToDatabase(_ presets: MealDatabase, token: String) { processQueue.async { - nightscout.uploadSettings(sets) + Database(token: token).uploadMealPresets(presets) .sink { completion in switch completion { case .finished: - debug(.nightscout, "Settings uploaded to database") - self.storage.save(settings, as: OpenAPS.Nightscout.uploadedSettings) + debug(.nightscout, "Meal presets uploaded to database. Profile: \(presets.profile)") + self.storage.save(presets, as: OpenAPS.Nightscout.uploadedMealPresets) + self.saveToCoreData(presets.profile) case let .failure(error): - debug(.nightscout, error.localizedDescription) + debug(.nightscout, "Meal presets failed to upload to database " + error.localizedDescription) + } + } receiveValue: {} + .store(in: &self.lifetime) + } + } + + private func uploadOverridePresetsToDatabase(_ presets: OverrideDatabase, token: String) { + processQueue.async { + Database(token: token).uploaOverrridePresets(presets) + .sink { completion in + switch completion { + case .finished: + debug(.nightscout, "Override presets uploaded to database. Profile: \(presets.profile)") + self.storage.save(presets, as: OpenAPS.Nightscout.uploadedOverridePresets) + self.saveToCoreData(presets.profile) + case let .failure(error): + debug(.nightscout, "Override presets failed to upload to database " + error.localizedDescription) } } receiveValue: {} .store(in: &self.lifetime) @@ -626,60 +697,84 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { } func uploadProfileAndSettings(_ force: Bool) { - guard let sensitivities = storage.retrieve(OpenAPS.Settings.insulinSensitivities, as: InsulinSensitivities.self) else { + var loaded = Loaded() + + // Start trying retrieving files + let sensitivities = storage.retrieveFile(OpenAPS.Settings.insulinSensitivities, as: InsulinSensitivities.self) + if sensitivities != nil { + loaded.sens = true + debug(.nightscout, "NightscoutManager uploadProfile: file insulinSensitivities loaded") + } else { debug(.nightscout, "NightscoutManager uploadProfile: error loading insulinSensitivities") - return } - guard let settings = storage.retrieve(OpenAPS.FreeAPS.settings, as: FreeAPSSettings.self) else { + + let settings = storage.retrieveFile(OpenAPS.FreeAPS.settings, as: FreeAPSSettings.self) + if settings != nil { + loaded.settings = true + } else { debug(.nightscout, "NightscoutManager uploadProfile: error loading settings") - return } - guard let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) else { + + let preferences = storage.retrieveFile(OpenAPS.Settings.preferences, as: Preferences.self) + if preferences != nil { + loaded.preferences = true + } else { debug(.nightscout, "NightscoutManager uploadProfile: error loading preferences") - return } - guard let targets = storage.retrieve(OpenAPS.Settings.bgTargets, as: BGTargets.self) else { + + let targets = storage.retrieveFile(OpenAPS.Settings.bgTargets, as: BGTargets.self) + if targets != nil { + loaded.targets = true + } else { debug(.nightscout, "NightscoutManager uploadProfile: error loading bgTargets") - return } - guard let carbRatios = storage.retrieve(OpenAPS.Settings.carbRatios, as: CarbRatios.self) else { + + let carbRatios = storage.retrieveFile(OpenAPS.Settings.carbRatios, as: CarbRatios.self) + if carbRatios != nil { + loaded.carbratios = true + } else { debug(.nightscout, "NightscoutManager uploadProfile: error loading carbRatios") - return } - guard let basalProfile = storage.retrieve(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self) else { + + let basalProfile = storage.retrieveFile(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self) + if basalProfile != nil { + loaded.basalProfiles = true + } else { debug(.nightscout, "NightscoutManager uploadProfile: error loading basalProfile") - return } - let sens = sensitivities.sensitivities.map { item -> NightscoutTimevalue in + let token = getIdentifier() + + let sens = sensitivities?.sensitivities.map { item -> NightscoutTimevalue in NightscoutTimevalue( time: String(item.start.prefix(5)), value: item.sensitivity, timeAsSeconds: item.offset * 60 ) } - let target_low = targets.targets.map { item -> NightscoutTimevalue in + + let target_low = targets?.targets.map { item -> NightscoutTimevalue in NightscoutTimevalue( time: String(item.start.prefix(5)), value: item.low, timeAsSeconds: item.offset * 60 ) } - let target_high = targets.targets.map { item -> NightscoutTimevalue in + let target_high = targets?.targets.map { item -> NightscoutTimevalue in NightscoutTimevalue( time: String(item.start.prefix(5)), value: item.high, timeAsSeconds: item.offset * 60 ) } - let cr = carbRatios.schedule.map { item -> NightscoutTimevalue in + let cr = carbRatios?.schedule.map { item -> NightscoutTimevalue in NightscoutTimevalue( time: String(item.start.prefix(5)), value: item.ratio, timeAsSeconds: item.offset * 60 ) } - let basal = basalProfile.map { item -> NightscoutTimevalue in + let basal = basalProfile?.map { item -> NightscoutTimevalue in NightscoutTimevalue( time: String(item.start.prefix(5)), value: item.rate, @@ -696,8 +791,8 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { } var carbs_hr: Decimal = 0 - if let isf = sensitivities.sensitivities.map(\.sensitivity).first, - let cr = carbRatios.schedule.map(\.ratio).first, + if let isf = sensitivities?.sensitivities.map(\.sensitivity).first, + let cr = carbRatios?.schedule.map(\.ratio).first, isf > 0, cr > 0 { // CarbImpact -> Carbs/hr = CI [mg/dl/5min] * 12 / ISF [mg/dl/U] * CR [g/U] @@ -709,85 +804,180 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { carbs_hr = Decimal(round(Double(carbs_hr) * 10.0)) / 10 } - let ps = ScheduledNightscoutProfile( - dia: settingsManager.pumpSettings.insulinActionCurve, - carbs_hr: Int(carbs_hr), - delay: 0, - timezone: TimeZone.current.identifier, - target_low: target_low, - target_high: target_high, - sens: sens, - basal: basal, - carbratio: cr, - units: nsUnits - ) - let defaultProfile = "default" - - let now = Date() - let p = NightscoutProfileStore( - defaultProfile: defaultProfile, - startDate: now, - mills: Int(now.timeIntervalSince1970) * 1000, - units: nsUnits, - enteredBy: NigtscoutTreatment.local, - store: [defaultProfile: ps] - ) + if loaded.basalProfiles, loaded.carbratios, loaded.carbratios, loaded.sens, loaded.targets { + // Unknown errors, as it shouldn't happen here + guard let glucosetarget_low = target_low else { return } + guard let glucosetarget_high = target_high else { return } + guard let unwrappedSens = sens else { return } + guard let unwrappedBasal = basal else { return } + guard let unwrappedCR = cr else { return } + + let ps = ScheduledNightscoutProfile( + dia: settingsManager.pumpSettings.insulinActionCurve, + carbs_hr: Int(carbs_hr), + delay: 0, + timezone: TimeZone.current.identifier, + target_low: glucosetarget_low, + target_high: glucosetarget_high, + sens: unwrappedSens, + basal: unwrappedBasal, + carbratio: unwrappedCR, + units: nsUnits + ) + let defaultProfile = "default" + + let now = Date() + var p = NightscoutProfileStore( + defaultProfile: "default", + startDate: now, + mills: Int(now.timeIntervalSince1970) * 1000, + units: nsUnits, + enteredBy: NigtscoutTreatment.local, + store: [defaultProfile: ps], + profile: name + ) - let nightscout = NightscoutAPI(url: IAPSconfig.statURL) + let q = NightscoutProfileStore( + defaultProfile: "default", + startDate: now, + mills: Int(now.timeIntervalSince1970) * 1000, + units: nsUnits, + enteredBy: NigtscoutTreatment.local, + store: [defaultProfile: ps], + profile: name + ) + + // UPLOAD Profiles WHEN CHANGED + if let uploadedProfile = storage.retrieveFile(OpenAPS.Nightscout.uploadedProfile, as: NightscoutProfileStore.self), + (uploadedProfile.store["default"]?.rawJSON ?? "").sorted() == ps.rawJSON.sorted(), !force + { + NSLog("NightscoutManager uploadProfile, no profile change") + } else { + if let ns = nightscoutAPI, isUploadEnabled { + processQueue.async { + ns.uploadProfile(q) + .sink { completion in + switch completion { + case .finished: + self.storage.save(p, as: OpenAPS.Nightscout.uploadedProfile) + debug(.nightscout, "Profile uploaded") + case let .failure(error): + debug(.nightscout, error.localizedDescription) + } + } receiveValue: {} + .store(in: &self.lifetime) + } + } + } + + // UPLOAD Profiles to database WHEN CHANGED + if let uploadedProfile = storage.retrieveFile( + OpenAPS.Nightscout.uploadedProfileToDatabase, + as: DatabaseProfileStore.self + ), + (uploadedProfile.store["default"]?.rawJSON ?? "").sorted() == ps.rawJSON.sorted(), !force + { + NSLog("NightscoutManager uploadProfile to database, no profile change") + } else { + if isStatsUploadEnabled { + p.enteredBy = getIdentifier() + processQueue.async { + Database(token: token).uploadSettingsToDatabase(p) + .sink { completion in + switch completion { + case .finished: + debug(.nightscout, "Profiles uploaded to database. Profile: \(p.profile ?? "")") + self.storage.save(p, as: OpenAPS.Nightscout.uploadedProfileToDatabase) + case let .failure(error): + debug(.nightscout, error.localizedDescription) + } + } receiveValue: {} + .store(in: &self.lifetime) + } + } + } + } // UPLOAD PREFERNCES WHEN CHANGED - if let uploadedPreferences = storage.retrieve(OpenAPS.Nightscout.uploadedPreferences, as: Preferences.self), - uploadedPreferences.rawJSON.sorted() == preferences.rawJSON.sorted(), !force + if let uploadedPreferences = storage.retrieveFile(OpenAPS.Nightscout.uploadedPreferences, as: Preferences.self), + let unWrappedPreferences = preferences { - NSLog("NightscoutManager Preferences, preferences unchanged") - } else { uploadPreferences(preferences) } + if uploadedPreferences.rawJSON.sorted() != unWrappedPreferences.rawJSON.sorted() || + force + { + let prefs = NightscoutPreferences(preferences: unWrappedPreferences, enteredBy: token, profile: name) + uploadPreferences(prefs) + } else { + NSLog("NightscoutManager Preferences, preferences unchanged") + } + } else if loaded.preferences { + let prefs = NightscoutPreferences(preferences: preferences, enteredBy: token, profile: name) + uploadPreferences(prefs) + } // UPLOAD FreeAPS Settings WHEN CHANGED if let uploadedSettings = storage.retrieve(OpenAPS.Nightscout.uploadedSettings, as: FreeAPSSettings.self), - uploadedSettings.rawJSON.sorted() == settings.rawJSON.sorted(), !force + let unwrappedSettings = settings, uploadedSettings.rawJSON.sorted() == unwrappedSettings.rawJSON.sorted(), !force { NSLog("NightscoutManager Settings, settings unchanged") - } else { uploadSettings(settings) } + } else { + let sets = NightscoutSettings( + settings: settingsManager.settings, enteredBy: getIdentifier(), profile: name + ) + uploadSettings(sets) + } + + // UPLOAD PumpSettings WHEN CHANGED + if let pumpSettings = storage.retrieveFile(OpenAPS.Settings.settings, as: PumpSettings.self) { + if let uploadedSettings = storage.retrieve(OpenAPS.Nightscout.uploadedPumpSettings, as: PumpSettings.self), + uploadedSettings.rawJSON.sorted() == pumpSettings.rawJSON.sorted(), !force + { + NSLog("PumpSettings unchanged") + } else { uploadPumpSettingsToDatabase(pumpSettings, token: token, name: name) } - // UPLOAD Profiles WHEN CHANGED - if let uploadedProfile = storage.retrieve(OpenAPS.Nightscout.uploadedProfile, as: NightscoutProfileStore.self), - (uploadedProfile.store["default"]?.rawJSON ?? "").sorted() == ps.rawJSON.sorted(), !force - { - NSLog("NightscoutManager uploadProfile, no profile change") } else { - if let ns = nightscoutAPI, isUploadEnabled { - processQueue.async { - ns.uploadProfile(p) - .sink { completion in - switch completion { - case .finished: - self.storage.save(p, as: OpenAPS.Nightscout.uploadedProfile) - debug(.nightscout, "Profile uploaded") - case let .failure(error): - debug(.nightscout, error.localizedDescription) - } - } receiveValue: {} - .store(in: &self.lifetime) - } + debug(.nightscout, "UploadPumpSettings: error opening pump settings") + } + + // UPLOAD Temp Targets WHEN CHANGED + if let tempTargets = storage.retrieveFile(OpenAPS.FreeAPS.tempTargetsPresets, as: [TempTarget].self) { + if let uploadedTempTargets = storage.retrieve( + OpenAPS.Nightscout.uploadedTempTargetsDatabase, + as: [TempTarget].self + ), + uploadedTempTargets.rawJSON.sorted() == tempTargets.rawJSON.sorted(), !force + { + NSLog("Temp targets unchanged") + } else { uploadTempTargetsToDatabase(tempTargets, token: token, name: name) } + + } else { + debug(.nightscout, "UploadPumpSettings: error opening pump settings") + } + + // Upload Meal Presets when needed + let mealPresets = Database(token: token).mealPresetDatabaseUpload(profile: name, token: token) + if !mealPresets.presets.isEmpty { + if let uploadedMealPresets = storage.retrieveFile(OpenAPS.Nightscout.uploadedMealPresets, as: MealDatabase.self), + mealPresets.rawJSON.sorted() == uploadedMealPresets.rawJSON.sorted(), !force + { + NSLog("Meal Presets unchanged") + } else { + uploadMealPresetsToDatabase(mealPresets, token: token) } - if isStatsUploadEnabled { - var q = p - q.enteredBy = getIdentifier() - processQueue.async { - nightscout.uploadSettingsToDatabase(q) - .sink { completion in - switch completion { - case .finished: - debug(.nightscout, "Profiles uploaded to database") - if !self.isUploadEnabled { - self.storage.save(p, as: OpenAPS.Nightscout.uploadedProfile) - } - case let .failure(error): - debug(.nightscout, error.localizedDescription) - } - } receiveValue: {} - .store(in: &self.lifetime) - } + } + + // Upload Override Presets when needed + let overridePresets = Database(token: token).overridePresetDatabaseUpload(profile: name, token: token) + if !overridePresets.presets.isEmpty { + if let uploadedOverridePresets = storage.retrieveFile( + OpenAPS.Nightscout.uploadedOverridePresets, + as: OverrideDatabase.self + ), + overridePresets.rawJSON.sorted() == uploadedOverridePresets.rawJSON.sorted(), !force + { + NSLog("Override Presets unchanged") + } else { + uploadOverridePresetsToDatabase(overridePresets, token: token) } } } @@ -950,6 +1140,10 @@ final class BaseNightscoutManager: NightscoutManager, Injectable { uploadTreatments(carbsStorage.nightscoutTretmentsNotUploaded(), fileToSave: OpenAPS.Nightscout.uploadedCarbs) } + private func loadFileFromStorage(name: String) -> RawJSON { + storage.retrieveRaw(name) ?? OpenAPS.defaults(for: name) + } + private func uploadTempTargets() { uploadTreatments(tempTargetsStorage.nightscoutTretmentsNotUploaded(), fileToSave: OpenAPS.Nightscout.uploadedTempTargets) } diff --git a/FreeAPS/Sources/Services/Storage/FileStorage.swift b/FreeAPS/Sources/Services/Storage/FileStorage.swift index 6332b514f1..bd29d2d532 100644 --- a/FreeAPS/Sources/Services/Storage/FileStorage.swift +++ b/FreeAPS/Sources/Services/Storage/FileStorage.swift @@ -11,11 +11,12 @@ protocol FileStorage { func remove(_ name: String) func rename(_ name: String, to newName: String) func transaction(_ exec: (FileStorage) -> Void) + func retrieveFile(_ name: String, as type: Value.Type) -> Value? func urlFor(file: String) -> URL? } -final class BaseFileStorage: FileStorage { +final class BaseFileStorage: FileStorage, Injectable { private let processQueue = DispatchQueue.markedQueue(label: "BaseFileStorage.processQueue", qos: .utility) func save(_ value: Value, as name: String) { @@ -43,6 +44,15 @@ final class BaseFileStorage: FileStorage { } } + func retrieveFile(_ name: String, as type: Value.Type) -> Value? { + if let loaded = retrieve(name, as: type) { + return loaded + } + let file = retrieveRaw(name) ?? OpenAPS.defaults(for: name) + save(file, as: name) + return retrieve(name, as: type) + } + func append(_ newValue: Value, to name: String) { processQueue.safeSync { try? Disk.append(newValue, to: name, in: .documents, decoder: JSONCoding.decoder, encoder: JSONCoding.encoder)