ソースを参照

Pull latest dev onto marv-out/migrationScript

Deniz Cengiz 1 年間 前
コミット
d6492c307b
100 ファイル変更148 行追加35474 行削除
  1. 2 1
      .github/ISSUE_TEMPLATE/bug-report.md
  2. 2 1
      .github/ISSUE_TEMPLATE/feature-request.md
  3. 43 10
      .github/workflows/build_trio.yml
  4. 89 19
      .github/workflows/create_certs.yml
  5. 4 5
      .github/workflows/validate_secrets.yml
  6. 1 3
      .gitignore
  7. 3 0
      .gitmodules
  8. 1 1
      CGMBLEKit
  9. 2 1
      Config.xcconfig
  10. 1 0
      DanaKit
  11. 0 4499
      FreeAPS.xcodeproj/project.pbxproj
  12. 0 78
      FreeAPS.xcodeproj/xcshareddata/xcschemes/FreeAPS.xcscheme
  13. 0 415
      FreeAPS.xcodeproj/xcshareddata/xcschemes/Trio.xcscheme
  14. 0 93
      FreeAPS.xcodeproj/xcshareddata/xcschemes/Trio_WatchApp.xcscheme
  15. 0 23
      FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/Contents.json
  16. BIN
      FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/imageLoop 1.png
  17. BIN
      FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/imageLoop 2.png
  18. BIN
      FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/imageLoop 3.png
  19. 0 20
      FreeAPS/Resources/Assets.xcassets/app_icons/trioLoop.appiconset/Contents.json
  20. BIN
      FreeAPS/Resources/Assets.xcassets/app_icons/trioLoop.appiconset/trioLoop watch.png
  21. BIN
      FreeAPS/Resources/Assets.xcassets/app_icons/trioLoop.appiconset/trioLoop.png
  22. 0 20
      FreeAPS/Resources/Base.lproj/InfoPlist.strings
  23. 0 128
      FreeAPS/Resources/Info.plist
  24. 0 20
      FreeAPS/Resources/ar.lproj/InfoPlist.strings
  25. 0 20
      FreeAPS/Resources/ca.lproj/InfoPlist.strings
  26. 0 20
      FreeAPS/Resources/da.lproj/InfoPlist.strings
  27. 0 20
      FreeAPS/Resources/de.lproj/InfoPlist.strings
  28. 0 20
      FreeAPS/Resources/es.lproj/InfoPlist.strings
  29. 0 20
      FreeAPS/Resources/fi.lproj/InfoPlist.strings
  30. 0 20
      FreeAPS/Resources/fr.lproj/InfoPlist.strings
  31. 0 20
      FreeAPS/Resources/he.lproj/InfoPlist.strings
  32. 0 20
      FreeAPS/Resources/hu.lproj/InfoPlist.strings
  33. 0 20
      FreeAPS/Resources/it.lproj/InfoPlist.strings
  34. 0 2
      FreeAPS/Resources/javascript/bundle/autosens.js
  35. 0 1
      FreeAPS/Resources/javascript/bundle/autotune-core.js
  36. 0 2
      FreeAPS/Resources/javascript/bundle/autotune-prep.js
  37. 0 1
      FreeAPS/Resources/javascript/bundle/basal-set-temp.js
  38. 0 1
      FreeAPS/Resources/javascript/bundle/determine-basal.js
  39. 0 2
      FreeAPS/Resources/javascript/bundle/iob.js
  40. 0 2
      FreeAPS/Resources/javascript/bundle/meal.js
  41. 0 2
      FreeAPS/Resources/javascript/bundle/profile.js
  42. 0 60
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  43. 0 54
      FreeAPS/Resources/json/defaults/preferences.json
  44. 0 5
      FreeAPS/Resources/json/defaults/settings/settings.json
  45. 0 20
      FreeAPS/Resources/nb.lproj/InfoPlist.strings
  46. 0 20
      FreeAPS/Resources/nl.lproj/InfoPlist.strings
  47. 0 20
      FreeAPS/Resources/pl.lproj/InfoPlist.strings
  48. 0 20
      FreeAPS/Resources/pt-BR.lproj/InfoPlist.strings
  49. 0 20
      FreeAPS/Resources/pt-PT.lproj/InfoPlist.strings
  50. 0 20
      FreeAPS/Resources/ru.lproj/InfoPlist.strings
  51. 0 20
      FreeAPS/Resources/sk.lproj/InfoPlist.strings
  52. 0 20
      FreeAPS/Resources/sv.lproj/InfoPlist.strings
  53. 0 20
      FreeAPS/Resources/tr.lproj/InfoPlist.strings
  54. 0 20
      FreeAPS/Resources/uk.lproj/InfoPlist.strings
  55. 0 20
      FreeAPS/Resources/vi.lproj/InfoPlist.strings
  56. 0 20
      FreeAPS/Resources/zh-Hans.lproj/InfoPlist.strings
  57. 0 1558
      FreeAPS/Sources/APS/APSManager.swift
  58. 0 126
      FreeAPS/Sources/APS/CGM/AppGroupSource.swift
  59. 0 76
      FreeAPS/Sources/APS/CGM/CGMType.swift
  60. 0 119
      FreeAPS/Sources/APS/CGM/Calibrations/CalibrationService.swift
  61. 0 197
      FreeAPS/Sources/APS/CGM/GlucoseSimulatorSource.swift
  62. 0 247
      FreeAPS/Sources/APS/CGM/PluginSource.swift
  63. 0 716
      FreeAPS/Sources/APS/DeviceDataManager.swift
  64. 0 52
      FreeAPS/Sources/APS/FetchAnnouncementsManager.swift
  65. 0 349
      FreeAPS/Sources/APS/FetchGlucoseManager.swift
  66. 0 49
      FreeAPS/Sources/APS/FetchTreatmentsManager.swift
  67. 0 100
      FreeAPS/Sources/APS/OpenAPS/Constants.swift
  68. 0 1022
      FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
  69. 0 16
      FreeAPS/Sources/APS/OpenAPS/Script.swift
  70. 0 279
      FreeAPS/Sources/APS/PluginManager.swift
  71. 0 80
      FreeAPS/Sources/APS/Storage/AnnouncementsStorage.swift
  72. 0 481
      FreeAPS/Sources/APS/Storage/CarbsStorage.swift
  73. 0 204
      FreeAPS/Sources/APS/Storage/DeterminationStorage.swift
  74. 0 525
      FreeAPS/Sources/APS/Storage/GlucoseStorage.swift
  75. 0 298
      FreeAPS/Sources/APS/Storage/OverrideStorage.swift
  76. 0 535
      FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift
  77. 0 119
      FreeAPS/Sources/APS/Storage/TempTargetsStorage.swift
  78. 0 54
      FreeAPS/Sources/Application/AppDelegate.swift
  79. 0 15
      FreeAPS/Sources/Assemblies/APSAssembly.swift
  80. 0 30
      FreeAPS/Sources/Assemblies/ServiceAssembly.swift
  81. 0 21
      FreeAPS/Sources/Assemblies/StorageAssembly.swift
  82. 0 9
      FreeAPS/Sources/Config/Config.swift
  83. 0 83
      FreeAPS/Sources/Helpers/BuildDetails.swift
  84. 0 52
      FreeAPS/Sources/Helpers/CustomProgressView.swift
  85. 0 20
      FreeAPS/Sources/Helpers/Decimal+Extensions.swift
  86. 0 49
      FreeAPS/Sources/Helpers/Formatters.swift
  87. 0 58
      FreeAPS/Sources/Helpers/HKUnit.swift
  88. 0 99
      FreeAPS/Sources/Helpers/MainChartHelper.swift
  89. 0 25
      FreeAPS/Sources/Helpers/ProgressBar.swift
  90. 0 2229
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  91. 0 1923
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  92. 0 2190
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  93. 0 2235
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  94. 0 2237
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  95. 0 2234
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  96. 0 2235
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  97. 0 2226
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  98. 0 2229
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  99. 0 2184
      FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings
  100. 0 0
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings

+ 2 - 1
.github/ISSUE_TEMPLATE/bug-report.md

@@ -2,7 +2,8 @@
 name: "\U0001F41B Bug report"
 about: Create a report to help us fix things
 title: ''
-labels: ['bug', 'needs-triage']
+labels: ['needs-triage']
+type: "bug"
 assignees: ''
 projects: ['nightscout/2']
 

+ 2 - 1
.github/ISSUE_TEMPLATE/feature-request.md

@@ -2,7 +2,8 @@
 name: "\U0001F4A1 Feature request \U0001F4A1"
 about: Suggest an idea for this project
 title: ''
-labels: ['enhancement', 'needs-triage']
+labels: ['needs-triage']
+types: "feature"
 assignees: ''
 projects: ['nightscout/2']
 

+ 43 - 10
.github/workflows/build_trio.yml

@@ -10,7 +10,7 @@ on:
     - cron: "0 8 * * 3" # Checks for updates at 08:00 UTC every Wednesday
     - cron: "0 6 1 * *" # Builds the app on the 1st of every month at 06:00 UTC
 
-env:  
+env:
   UPSTREAM_REPO: nightscout/Trio
   UPSTREAM_BRANCH: ${{ github.ref_name }} # branch on upstream repository to sync from (replace with specific branch name if needed)
   TARGET_BRANCH: ${{ github.ref_name }} # target branch on fork to be kept in sync, and target branch on upstream to be kept alive (replace with specific branch name if needed)
@@ -18,15 +18,17 @@ env:
   ALIVE_BRANCH_DEV: alive-dev
 
 jobs:
-  validate:
-    name: Validate
-    uses: ./.github/workflows/validate_secrets.yml
+  # Checks if Distribution certificate is present and valid, optionally nukes and
+  # creates new certs if the repository variable ENABLE_NUKE_CERTS == 'true'
+  check_certs:
+    name: Check certificates
+    uses: ./.github/workflows/create_certs.yml
     secrets: inherit
 
   # Checks if GH_PAT holds workflow permissions
   # Checks for existence of alive branch; if non-existent creates it
   check_alive_and_permissions:
-    needs: validate
+    needs: check_certs
     runs-on: ubuntu-latest
     name: Check alive branch and permissions
     permissions:
@@ -96,7 +98,7 @@ jobs:
   # Checks for changes in upstream repository; if changes exist prompts sync for build
   # Performs keepalive to avoid stale fork
   check_latest_from_upstream:
-    needs: [validate, check_alive_and_permissions]
+    needs: [check_certs, check_alive_and_permissions]
     runs-on: ubuntu-latest
     name: Check upstream and keep alive
     outputs:
@@ -181,11 +183,12 @@ jobs:
           echo "Synchronizing your fork of <code>Trio</code> with the upstream repository <code>nightscout/Trio</code> will be skipped." >> $GITHUB_STEP_SUMMARY
           echo "If you want to enable automatic builds and updates for your Trio, please follow the instructions \
               under the following path <code>Trio/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
-  
+
   # Builds Trio
   build:
     name: Build
-    needs: [validate, check_alive_and_permissions, check_latest_from_upstream]
+    needs:
+      [check_certs, check_alive_and_permissions, check_latest_from_upstream]
     runs-on: macos-15
     permissions:
       contents: write
@@ -198,8 +201,8 @@ jobs:
       )
     steps:
       - name: Select Xcode version
-        run: "sudo xcode-select --switch /Applications/Xcode_16.0.app/Contents/Developer"
-      
+        run: "sudo xcode-select --switch /Applications/Xcode_16.2.app/Contents/Developer"
+
       - name: Checkout Repo for syncing
         if: |
           needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
@@ -250,6 +253,36 @@ jobs:
           submodules: recursive
           ref: ${{ env.TARGET_BRANCH }}
 
+      # Customize Trio: Use patches or download and apply patches from GitHub
+      - name: Customize Trio
+        run: |
+
+          # Trio workspace patches
+          # -applies any patches located in the Trio/patches/ directory
+          if $(ls ./patches/* &> /dev/null); then
+          git apply ./patches/* --allow-empty -v --whitespace=fix
+          fi
+
+          # Download and apply Trio patches from GitHub:
+          # Template for customizing Trio code (as opposed to submodule code)
+          # Remove the "#" sign from the beginning of the line below to activate
+          #   and then replace the alphanumeric string with your SHA, this SHA is NOT valid
+          #curl https://github.com/nightscout/Trio/commit/d206432b024279ef710df462b20bd464cd9682d4.patch | git apply -v --whitespace=fix
+
+          # Download and apply Submodule patches from GitHub:
+          # Template for customizing submodules (you must edit the submodule name)
+          # This example is for G7SensorKit showing you can apply multiple commits, in the proper order
+          # Remove the "#" sign from the beginning of the lines below to activate
+          # This example applies 3 commits from the scan-fix folder; valid only when these are not already in Trio
+          #curl https://github.com/loopandlearn/G7SensorKit/commit/ba44beb3d1491c453f4f438443c3f8ba29146ab3.patch | git apply --directory=G7SensorKit -v --whitespace=fix
+          #curl https://github.com/loopandlearn/G7SensorKit/commit/d86ac8e9cd523d1267587dd70c96597125eef7ab.patch | git apply --directory=G7SensorKit -v --whitespace=fix
+          #curl https://github.com/loopandlearn/G7SensorKit/commit/205054e7537723c2aec58d807634b4853f687244.patch | git apply --directory=G7SensorKit -v --whitespace=fix
+
+          # Add patches for additional customization by following the templates above,
+          # and make sure to specify the submodule by setting "--directory=(submodule_name)".
+          # Several patches may be added per submodule.
+          # Adding comments (#) is strongly recommended to easily tell the individual patches apart.
+
       # Patch Fastlane Match to not print tables
       - name: Patch Match Tables
         run: |

+ 89 - 19
.github/workflows/create_certs.yml

@@ -1,7 +1,16 @@
 name: 3. Create Certificates
 run-name: Create Certificates (${{ github.ref_name }})
-on:
-  workflow_dispatch:
+
+on: [workflow_call, workflow_dispatch]
+
+env:
+  TEAMID: ${{ secrets.TEAMID }}
+  GH_PAT: ${{ secrets.GH_PAT }}
+  GH_TOKEN: ${{ secrets.GH_PAT }}
+  MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
+  FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
+  FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
+  FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
 
 jobs:
   validate:
@@ -9,12 +18,14 @@ jobs:
     uses: ./.github/workflows/validate_secrets.yml
     secrets: inherit
 
-  certificates:
-    name: Create Certificates
+  create_certs:
+    name: Certificates
     needs: validate
     runs-on: macos-15
-    steps:
+    outputs:
+      new_certificate_needed: ${{ steps.set_output.outputs.new_certificate_needed }}
 
+    steps:
       # Checks-out the repo
       - name: Checkout Repo
         uses: actions/checkout@v4
@@ -34,17 +45,76 @@ jobs:
       - name: Install Project Dependencies
         run: bundle install
 
-      # Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)
-      - name: Sync clock
-        run: sudo sntp -sS time.windows.com
-
-      # Create or update certificates for app
-      - name: Create Certificates
-        run: bundle exec fastlane certs
-        env:
-          TEAMID: ${{ secrets.TEAMID }}
-          GH_PAT: ${{ secrets.GH_PAT }}
-          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
-          FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
-          FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
-          FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
+      # Create or update Distribution certificate and provisioning profiles
+      - name: Check and create or update Distribution certificate and profiles if needed
+        run: |
+          echo "Running Fastlane certs lane..."
+          bundle exec fastlane certs || true # ignore and continue on errors without annotating an exit code
+
+      - name: Check Distribution certificate and launch Nuke certificates if needed
+        run: bundle exec fastlane check_and_renew_certificates
+        id: check_certs
+
+      - name: Set output and annotations based on Fastlane result
+        id: set_output
+        run: |
+          CERT_STATUS_FILE="${{ github.workspace }}/fastlane/new_certificate_needed.txt"
+          ENABLE_NUKE_CERTS=${{ vars.ENABLE_NUKE_CERTS }}
+
+          if [ -f "$CERT_STATUS_FILE" ]; then
+            CERT_STATUS=$(cat "$CERT_STATUS_FILE" | tr -d '\n' | tr -d '\r') # Read file content and strip newlines
+            echo "new_certificate_needed: $CERT_STATUS"
+            echo "new_certificate_needed=$CERT_STATUS" >> $GITHUB_OUTPUT
+          else
+            echo "Certificate status file not found. Defaulting to false."
+            echo "new_certificate_needed=false" >> $GITHUB_OUTPUT
+          fi
+
+          # Check if ENABLE_NUKE_CERTS is not set to true when certs are valid
+          if [ "$CERT_STATUS" != "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
+            echo "::notice::🔔 Automated renewal of certificates is disabled because the repository variable ENABLE_NUKE_CERTS is not set to 'true'."
+          fi
+
+          # Check if ENABLE_NUKE_CERTS is not set to true when certs are not valid
+          if [ "$CERT_STATUS" = "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
+            echo "::error::❌ No valid distribution certificate found. Automated renewal of certificates was skipped because the repository variable ENABLE_NUKE_CERTS is not set to 'true'."
+            exit 1
+          fi
+
+          # Check if vars.FORCE_NUKE_CERTS is not set to true
+          if [ vars.FORCE_NUKE_CERTS = "true" ]; then
+            echo "::warning::‼️ Nuking of certificates was forced because the repository variable FORCE_NUKE_CERTS is set to 'true'."
+          fi
+
+  # Nuke Certs if needed, and if the repository variable ENABLE_NUKE_CERTS is set to 'true', or if FORCE_NUKE_CERTS is set to 'true', which will always force certs to be nuked
+  nuke_certs:
+    name: Nuke certificates
+    needs: [validate, create_certs]
+    runs-on: macos-14
+    if: ${{ (needs.create_certs.outputs.new_certificate_needed == 'true' && vars.ENABLE_NUKE_CERTS == 'true') || vars.FORCE_NUKE_CERTS == 'true' }}
+    steps:
+      - name: Output from step id 'check_certs'
+        run: echo "new_certificate_needed=${{ needs.create_certs.outputs.new_certificate_needed }}"
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Install dependencies
+        run: bundle install
+
+      - name: Run Fastlane nuke_certs
+        run: |
+          set -e # Set error immediately after this step if error occurs
+          bundle exec fastlane nuke_certs
+
+      - name: Recreate Distribution certificate after nuking
+        run: |
+          set -e # Set error immediately after this step if error occurs
+          bundle exec fastlane certs
+
+      - name: Add success annotations for nuke and certificate recreation
+        if: ${{ success() }}
+        run: |
+          echo "::warning::⚠️ All Distribution certificates and TestFlight profiles have been revoked and recreated."
+          echo "::warning::❗️ If you have other apps being distributed by GitHub Actions / Fastlane / TestFlight that does not renew certificates automatically, please run the '3. Create Certificates' workflow for each of these apps to allow these apps to be built."
+          echo "::warning::✅ But don't worry about your existing TestFlight builds, they will keep working!"

+ 4 - 5
.github/workflows/validate_secrets.yml

@@ -178,16 +178,15 @@ jobs:
           elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
             failed=true
             echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that you copied it correctly from the API Key file (*.p8) you downloaded and try again."
-          elif ! bundle exec fastlane validate_secrets 2>&1 | tee fastlane.log; then
+          elif ! (bundle exec fastlane validate_secrets 2>&1 || true) | tee fastlane.log; then # ignore "fastlane validate_secrets" errors and continue on errors without annotating an exit code
             if grep -q "bad decrypt" fastlane.log; then
               failed=true
               echo "::error::Unable to decrypt the Match-Secrets repository using the MATCH_PASSWORD secret. Verify that it is set correctly and try again."
             elif grep -q -e "required agreement" -e "license agreement" fastlane.log; then
               failed=true
-              echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to propagate and try again."
-            elif ! grep -q -e "No code signing identity found" -e "Could not install WWDR certificate" fastlane.log; then
-              failed=true
-              echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY secrets are set correctly and try again."
+              echo "::error::❗️ Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to take effect and try again."
+            elif grep -q "Your certificate .* is not valid" fastlane.log; then
+              echo "::notice::Your Distribution certificate is invalid or expired. Automated renewal of the certificate will be attempted."
             fi
           fi
 

+ 1 - 3
.gitignore

@@ -79,6 +79,4 @@ fastlane/screenshots
 fastlane/test_output
 fastlane/FastlaneRunner
 
-ConfigOverride.xcconfig
-
-branch.txt
+ConfigOverride.xcconfig

+ 3 - 0
.gitmodules

@@ -38,3 +38,6 @@
 	path = TidepoolService
 	url = https://github.com/loopandlearn/TidepoolService.git
 	branch = trio
+[submodule "DanaKit"]
+	path = DanaKit
+	url = https://github.com/loopandlearn/DanaKit

+ 1 - 1
CGMBLEKit

@@ -1 +1 @@
-Subproject commit b786e8b5531cb08c259103c472dcd6a6752728f8
+Subproject commit cd8f6faec67b30231987b79daf0117dfcbb54741

+ 2 - 1
Config.xcconfig

@@ -7,6 +7,7 @@ BUNDLE_IDENTIFIER = org.nightscout.$(DEVELOPMENT_TEAM).trio
 APP_ICON = trioBlack
 APP_URL_SCHEME = Trio
 
+// Optional overrides
 #include? "../../ConfigOverride.xcconfig"
+#include? "../ConfigOverride.xcconfig"
 #include? "ConfigOverride.xcconfig"
-#include? "../../ConfigOverride.xcconfig"

+ 1 - 0
DanaKit

@@ -0,0 +1 @@
+Subproject commit 200937c3c985de4cb05604cf1d7af2a307dfcaf3

ファイルの差分が大きいため隠しています
+ 0 - 4499
FreeAPS.xcodeproj/project.pbxproj


+ 0 - 78
FreeAPS.xcodeproj/xcshareddata/xcschemes/FreeAPS.xcscheme

@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1600"
-   version = "1.7">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES"
-      buildArchitectures = "Automatic">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "388E595725AD948C0019842D"
-               BuildableName = "FreeAPS.app"
-               BlueprintName = "FreeAPS"
-               ReferencedContainer = "container:FreeAPS.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      shouldAutocreateTestPlan = "YES">
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "388E595725AD948C0019842D"
-            BuildableName = "FreeAPS.app"
-            BlueprintName = "FreeAPS"
-            ReferencedContainer = "container:FreeAPS.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "388E595725AD948C0019842D"
-            BuildableName = "FreeAPS.app"
-            BlueprintName = "FreeAPS"
-            ReferencedContainer = "container:FreeAPS.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 0 - 415
FreeAPS.xcodeproj/xcshareddata/xcschemes/Trio.xcscheme

@@ -1,415 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1240"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "NO"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "43D8FDCA1C728FDF0073BE78"
-               BuildableName = "LoopKit.framework"
-               BlueprintName = "LoopKit"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "A9E6758022713F4700E25293"
-               BuildableName = "LoopKit.framework"
-               BlueprintName = "LoopKit-watchOS"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "43BA7153201E484D0058961E"
-               BuildableName = "LoopKitUI.framework"
-               BlueprintName = "LoopKitUI"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "892A5D33222F03CB008961AB"
-               BuildableName = "LoopTestingKit.framework"
-               BlueprintName = "LoopTestingKit"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "89D2047121CC7BD7001238CC"
-               BuildableName = "MockKit.framework"
-               BlueprintName = "MockKit"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "89D2048E21CC7C12001238CC"
-               BuildableName = "MockKitUI.framework"
-               BlueprintName = "MockKitUI"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C1E34B5A29C7AD01009A50A5"
-               BuildableName = "MinimedKitPlugin.loopplugin"
-               BlueprintName = "MinimedKitPlugin"
-               ReferencedContainer = "container:MinimedKit/MinimedKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C124021629C7D93D00B32844"
-               BuildableName = "OmniKitPlugin.loopplugin"
-               BlueprintName = "OmniKitPlugin"
-               ReferencedContainer = "container:OmniKit/OmniKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "B4D40D3D23A428BC00D7ECB5"
-               BuildableName = "CGMBLEKitG5Plugin.loopplugin"
-               BlueprintName = "CGMBLEKitG5Plugin"
-               ReferencedContainer = "container:CGMBLEKit/CGMBLEKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "B4D40D2D23A3E91800D7ECB5"
-               BuildableName = "CGMBLEKitG6Plugin.loopplugin"
-               BlueprintName = "CGMBLEKitG6Plugin"
-               ReferencedContainer = "container:CGMBLEKit/CGMBLEKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "B40BF25D23ABD47400A43CEE"
-               BuildableName = "ShareClientPlugin.loopplugin"
-               BlueprintName = "ShareClientPlugin"
-               ReferencedContainer = "container:dexcom-share-client-swift/ShareClient.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C17F511C291EACCD00555EB5"
-               BuildableName = "G7SensorPlugin.loopplugin"
-               BlueprintName = "G7SensorPlugin"
-               ReferencedContainer = "container:G7SensorKit/G7SensorKit.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C1BDBAE72A4397E200A787D1"
-               BuildableName = "LibreDemoPlugin.loopplugin"
-               BlueprintName = "LibreDemoPlugin"
-               ReferencedContainer = "container:LibreTransmitter/LibreTransmitter.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "B40BF25D23ABD47400A43CEE"
-               BuildableName = "LibreTransmitterPlugin.loopplugin"
-               BlueprintName = "LibreTransmitterPlugin"
-               ReferencedContainer = "container:LibreTransmitter/LibreTransmitter.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C187C196279086A8006E3557"
-               BuildableName = "OmniBLEPlugin.loopplugin"
-               BlueprintName = "OmniBLEPlugin"
-               ReferencedContainer = "container:OmniBLE/OmniBLE.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "A94AE4E3235A89B5005CA320"
-               BuildableName = "TidepoolServiceKitPlugin.loopplugin"
-               BlueprintName = "TidepoolServiceKitPlugin"
-               ReferencedContainer = "container:TidepoolService/TidepoolService.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "388E595725AD948C0019842D"
-               BuildableName = "FreeAPS.app"
-               BlueprintName = "FreeAPS"
-               ReferencedContainer = "container:FreeAPS.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "38FCF3EC25E9028E0078B0D1"
-               BuildableName = "FreeAPSTests.xctest"
-               BlueprintName = "FreeAPSTests"
-               ReferencedContainer = "container:FreeAPS.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "43CABDFC1C3506F100005705"
-               BuildableName = "CGMBLEKitTests.xctest"
-               BlueprintName = "CGMBLEKitTests"
-               ReferencedContainer = "container:CGMBLEKit/CGMBLEKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C17F50CD291EAC3800555EB5"
-               BuildableName = "G7SensorKitTests.xctest"
-               BlueprintName = "G7SensorKitTests"
-               ReferencedContainer = "container:G7SensorKit/G7SensorKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "43D8FDD41C728FDF0073BE78"
-               BuildableName = "LoopKitTests.xctest"
-               BlueprintName = "LoopKitTests"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "B4CEE2DF257129780093111B"
-               BuildableName = "MockKitTests.xctest"
-               BlueprintName = "MockKitTests"
-               ReferencedContainer = "container:LoopKit/LoopKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C13CC34029C7B73A007F25DE"
-               BuildableName = "MinimedKitTests.xctest"
-               BlueprintName = "MinimedKitTests"
-               ReferencedContainer = "container:MinimedKit/MinimedKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "84752E8A26ED0FFE009FD801"
-               BuildableName = "OmniBLETests.xctest"
-               BlueprintName = "OmniBLETests"
-               ReferencedContainer = "container:OmniBLE/OmniBLE.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "C12ED9C929C7DBA900435701"
-               BuildableName = "OmniKitTests.xctest"
-               BlueprintName = "OmniKitTests"
-               ReferencedContainer = "container:OmniKit/OmniKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "431CE7761F98564200255374"
-               BuildableName = "RileyLinkBLEKitTests.xctest"
-               BlueprintName = "RileyLinkBLEKitTests"
-               ReferencedContainer = "container:RileyLinkKit/RileyLinkKit.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      enableThreadSanitizer = "YES"
-      enableUBSanitizer = "YES"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "388E595725AD948C0019842D"
-            BuildableName = "FreeAPS.app"
-            BlueprintName = "FreeAPS"
-            ReferencedContainer = "container:FreeAPS.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-      <CommandLineArguments>
-         <CommandLineArgument
-            argument = "-com.apple.CoreData.ConcurrencyDebug 1"
-            isEnabled = "YES">
-         </CommandLineArgument>
-         <CommandLineArgument
-            argument = "-com.apple.CoreData.SQLDebug 1"
-            isEnabled = "NO">
-         </CommandLineArgument>
-         <CommandLineArgument
-            argument = "-com.apple.CoreData.MigrationDebug 1"
-            isEnabled = "NO">
-         </CommandLineArgument>
-      </CommandLineArguments>
-      <EnvironmentVariables>
-         <EnvironmentVariable
-            key = "CG_NUMERICS_SHOW_BACKTRACE"
-            value = ""
-            isEnabled = "YES">
-         </EnvironmentVariable>
-      </EnvironmentVariables>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "388E595725AD948C0019842D"
-            BuildableName = "FreeAPS.app"
-            BlueprintName = "FreeAPS"
-            ReferencedContainer = "container:FreeAPS.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 0 - 93
FreeAPS.xcodeproj/xcshareddata/xcschemes/Trio_WatchApp.xcscheme

@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1320"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "38E8751B27554D5500975559"
-               BuildableName = "FreeAPSWatch.app"
-               BlueprintName = "FreeAPSWatch"
-               ReferencedContainer = "container:FreeAPS.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "388E595725AD948C0019842D"
-               BuildableName = "FreeAPS.app"
-               BlueprintName = "FreeAPS"
-               ReferencedContainer = "container:FreeAPS.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES"
-      notificationPayloadFile = "FreeAPSWatch WatchKit Extension/PushNotificationPayload.apns">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "38E8751B27554D5500975559"
-            BuildableName = "FreeAPSWatch.app"
-            BlueprintName = "FreeAPSWatch"
-            ReferencedContainer = "container:FreeAPS.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "38E8751B27554D5500975559"
-            BuildableName = "FreeAPSWatch.app"
-            BlueprintName = "FreeAPSWatch"
-            ReferencedContainer = "container:FreeAPS.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 0 - 23
FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/Contents.json

@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "imageLoop 1.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "filename" : "imageLoop 2.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "imageLoop 3.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/imageLoop 1.png


BIN
FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/imageLoop 2.png


BIN
FreeAPS/Resources/Assets.xcassets/app_icon_images/trioLoop.imageset/imageLoop 3.png


+ 0 - 20
FreeAPS/Resources/Assets.xcassets/app_icons/trioLoop.appiconset/Contents.json

@@ -1,20 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "trioLoop.png",
-      "idiom" : "universal",
-      "platform" : "ios",
-      "size" : "1024x1024"
-    },
-    {
-      "filename" : "trioLoop watch.png",
-      "idiom" : "universal",
-      "platform" : "watchos",
-      "size" : "1024x1024"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
FreeAPS/Resources/Assets.xcassets/app_icons/trioLoop.appiconset/trioLoop watch.png


BIN
FreeAPS/Resources/Assets.xcassets/app_icons/trioLoop.appiconset/trioLoop.png


+ 0 - 20
FreeAPS/Resources/Base.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 128
FreeAPS/Resources/Info.plist

@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>AppGroupID</key>
-	<string>$(APP_GROUP_ID)</string>
-	<key>BGTaskSchedulerPermittedIdentifiers</key>
-	<array>
-		<string>$(PRODUCT_BUNDLE_IDENTIFIER).background-task.critical-event-log</string>
-		<string>com.openiaps.cleanup</string>
-	</array>
-	<key>CBBundleDisplayName</key>
-	<string>$(APP_DISPLAY_NAME)</string>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleDisplayName</key>
-	<string>$(APP_DISPLAY_NAME)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(APP_DISPLAY_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
-	<key>CFBundleShortVersionString</key>
-	<string>$(MARKETING_VERSION)</string>
-	<key>CFBundleURLTypes</key>
-	<array>
-		<dict>
-			<key>CFBundleTypeRole</key>
-			<string>None</string>
-			<key>CFBundleURLName</key>
-			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>$(APP_URL_SCHEME)</string>
-			</array>
-		</dict>
-	</array>
-	<key>CFBundleVersion</key>
-	<string>$(CURRENT_PROJECT_VERSION)</string>
-	<key>ITSAppUsesNonExemptEncryption</key>
-	<false/>
-	<key>LSApplicationCategoryType</key>
-	<string></string>
-	<key>LSApplicationQueriesSchemes</key>
-	<array>
-		<string>gcm-ciq</string>
-		<string>dexcomg7</string>
-		<string>xdripswift</string>
-		<string>dexcomg6</string>
-		<string>dexcomcgm</string>
-		<string>diabox</string>
-		<string>spikeapp</string>
-		<string>libredirect</string>
-	</array>
-	<key>LSRequiresIPhoneOS</key>
-	<true/>
-	<key>LSSupportsOpeningDocumentsInPlace</key>
-	<true/>
-	<key>NFCReaderUsageDescription</key>
-	<string>NFC is used to scan Libre sensors.</string>
-	<key>NSAppTransportSecurity</key>
-	<dict>
-		<key>NSAllowsArbitraryLoads</key>
-		<true/>
-	</dict>
-	<key>NSBluetoothAlwaysUsageDescription</key>
-	<string>Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices</string>
-	<key>NSBluetoothPeripheralUsageDescription</key>
-	<string>Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices</string>
-	<key>NSCalendarsFullAccessUsageDescription</key>
-	<string>To create events with BG reading values, so that they can be viewed on Apple Watch and CarPlay</string>
-	<key>NSCalendarsUsageDescription</key>
-	<string>Calendar is used to create a new glucose events.</string>
-	<key>NSFaceIDUsageDescription</key>
-	<string>For authorized acces to bolus</string>
-	<key>NSHealthShareUsageDescription</key>
-	<string>Health App is used to store blood glucose, carbs and insulin</string>
-	<key>NSHealthUpdateUsageDescription</key>
-	<string>Health App is used to store blood glucose, carbs and insulin</string>
-	<key>NSHumanReadableCopyright</key>
-	<string>$(COPYRIGHT_NOTICE)</string>
-	<key>NSSupportsLiveActivities</key>
-	<true/>
-	<key>TeamID</key>
-	<string>$(DEVELOPMENT_TEAM)</string>
-	<key>UIApplicationSceneManifest</key>
-	<dict>
-		<key>UIApplicationSupportsMultipleScenes</key>
-		<false/>
-	</dict>
-	<key>UIApplicationSupportsIndirectInputEvents</key>
-	<true/>
-	<key>UIBackgroundModes</key>
-	<array>
-		<string>bluetooth-central</string>
-		<string>bluetooth-peripheral</string>
-		<string>fetch</string>
-		<string>processing</string>
-	</array>
-	<key>UIFileSharingEnabled</key>
-	<true/>
-	<key>UILaunchScreen</key>
-	<dict/>
-	<key>UIRequiredDeviceCapabilities</key>
-	<array>
-		<string>armv7</string>
-	</array>
-	<key>UIRequiresFullScreen</key>
-	<false/>
-	<key>UISupportedInterfaceOrientations</key>
-	<array>
-		<string>UIInterfaceOrientationPortrait</string>
-		<string>UIInterfaceOrientationPortraitUpsideDown</string>
-	</array>
-	<key>UISupportedInterfaceOrientations~ipad</key>
-	<array>
-		<string>UIInterfaceOrientationPortrait</string>
-		<string>UIInterfaceOrientationPortraitUpsideDown</string>
-		<string>UIInterfaceOrientationLandscapeLeft</string>
-		<string>UIInterfaceOrientationLandscapeRight</string>
-	</array>
-</dict>
-</plist>

+ 0 - 20
FreeAPS/Resources/ar.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";

+ 0 - 20
FreeAPS/Resources/ca.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/da.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC bruges til at scanne Libre sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autoriseret adgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender bruges til at oprette en ny glucose begivenheder.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";

+ 0 - 20
FreeAPS/Resources/de.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wird zum Scannen von Libre Sensoren benutzt.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Autorisierung für Bolusabgabe";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "BZ-Werte werden im Kalender als temporärer Termin erstellt.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";

+ 0 - 20
FreeAPS/Resources/es.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/fi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/fr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC est utilisé pour scanner les capteurs Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pour les accès autorisés au bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Le calendrier est utilisé pour créer un nouvel événement de glycémie.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";

+ 0 - 20
FreeAPS/Resources/he.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/hu.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/it.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC è usato per scansionare i sensori di Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Il bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Il Bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Per accesso autorizzato al bolo";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Il calendario è usato per creare nuovi eventi di glicemia.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";

ファイルの差分が大きいため隠しています
+ 0 - 2
FreeAPS/Resources/javascript/bundle/autosens.js


ファイルの差分が大きいため隠しています
+ 0 - 1
FreeAPS/Resources/javascript/bundle/autotune-core.js


ファイルの差分が大きいため隠しています
+ 0 - 2
FreeAPS/Resources/javascript/bundle/autotune-prep.js


ファイルの差分が大きいため隠しています
+ 0 - 1
FreeAPS/Resources/javascript/bundle/basal-set-temp.js


ファイルの差分が大きいため隠しています
+ 0 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


ファイルの差分が大きいため隠しています
+ 0 - 2
FreeAPS/Resources/javascript/bundle/iob.js


ファイルの差分が大きいため隠しています
+ 0 - 2
FreeAPS/Resources/javascript/bundle/meal.js


ファイルの差分が大きいため隠しています
+ 0 - 2
FreeAPS/Resources/javascript/bundle/profile.js


+ 0 - 60
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -1,60 +0,0 @@
-{
-  "units" : "mg/dL",
-  "closedLoop" : false,
-  "allowAnnouncements" : false,
-  "useAutotune" : false,
-  "onlyAutotuneBasals" : false,
-  "isUploadEnabled" : false,
-  "isDownloadEnabled" : false,
-  "useLocalGlucoseSource" : false,
-  "localGlucosePort" : 8080,
-  "debugOptions" : false,
-  "displayHR" : false,
-  "cgm" : "none",
-  "cgmManagerTypeByIdentifier":"",
-  "uploadGlucose" : true,
-  "useCalendar" : false,
-  "displayCalendarEmojis" : false,
-  "displayCalendarIOBandCOB" : false,
-  "glucoseBadge" : false,
-  "glucoseNotificationsAlways" : false,
-  "useAlarmSound" : false,
-  "addSourceInfoToGlucoseNotifications" : false,
-  "lowGlucose" : 72,
-  "highGlucose" : 270,
-  "carbsRequiredThreshold" : 10,
-  "showCarbsRequiredBadge" : true,
-  "useFPUconversion" : true,
-  "totalInsulinDisplayType": "totalDailyDose",
-  "individualAdjustmentFactor" : 0.5,
-  "timeCap" : 8,
-  "minuteInterval" : 30,
-  "delay" : 60,
-  "useAppleHealth" : false,
-  "smoothGlucose" : false,
-  "displayOnWatch" : "BGTarget",
-  "overrideHbA1cUnit" : false,
-  "high" : 180,
-  "low" : 70,
-  "hours" : 6,
-  "glucoseColorScheme" : "staticColor",
-  "xGridLines" : true,
-  "yGridLines" : true,
-  "oneDimensionalGraph" : false,
-  "rulerMarks" : true,
-  "forecastDisplayType": "cone",
-  "maxCarbs": 250,
-  "maxFat": 250,
-  "maxProtein": 250,
-  "displayFatAndProteinOnWatch": false,
-  "confirmBolusFaster": false,
-  "overrideFactor": 0.8,
-  "fattyMeals": false,
-  "fattyMealFactor": 0.7,
-  "sweetMeals": false,
-  "sweetMealFactor": 2,
-  "lockScreenView": "simple",
-  "useCalendar": false,
-  "displayCalendarIOBandCOB": false,
-  "displayCalendarEmojis": false
-}

+ 0 - 54
FreeAPS/Resources/json/defaults/preferences.json

@@ -1,54 +0,0 @@
-{
-  "max_iob" : 0,
-  "max_daily_safety_multiplier" : 3,
-  "current_basal_safety_multiplier" : 4,
-  "autosens_max" : 1.2,
-  "autosens_min" : 0.7,
-  "smb_delivery_ratio" : 0.5,
-  "rewind_resets_autosens" : true,
-  "high_temptarget_raises_sensitivity" : false,
-  "low_temptarget_lowers_sensitivity" : false,
-  "sensitivity_raises_target" : false,
-  "resistance_lowers_target" : false,
-  "adv_target_adjustments" : false,
-  "exercise_mode" : false,
-  "half_basal_exercise_target" : 160,
-  "maxCOB" : 120,
-  "wide_bg_target_range" : false,
-  "skip_neutral_temps" : false,
-  "unsuspend_if_no_temp" : false,
-  "min_5m_carbimpact" : 8,
-  "autotune_isf_adjustmentFraction" : 1,
-  "remainingCarbsFraction" : 1,
-  "remainingCarbsCap" : 90,
-  "enableUAM" : false,
-  "A52_risk_enable" : false,
-  "enableSMB_with_COB" : false,
-  "enableSMB_with_temptarget" : false,
-  "enableSMB_always" : false,
-  "enableSMB_after_carbs" : false,
-  "allowSMB_with_high_temptarget" : false,
-  "maxSMBBasalMinutes" : 30,
-  "maxUAMSMBBasalMinutes" : 30,
-  "SMBInterval" : 3,
-  "bolus_increment" : 0.1,
-  "curve" : "rapid-acting",
-  "useCustomPeakTime" : false,
-  "insulinPeakTime" : 75,
-  "carbsReqThreshold" : 1,
-  "noisyCGMTargetMultiplier" : 1.3,
-  "suspend_zeros_iob" : false,
-  "maxDelta_bg_threshold" : 0.2,
-  "adjustmentFactor" : 0.8,
-  "adjustmentFactorSigmoid" : 0.5,
-  "sigmoid" : false,
-  "enableDynamicCR" : false,
-  "useNewFormula" : false,
-  "useWeightedAverage" : false,
-  "weightPercentage" : 0.65,
-  "tddAdjBasal" : false,
-  "enableSMB_high_bg" : false,
-  "enableSMB_high_bg_target" : 110,
-  "threshold_setting" : 60,
-  "updateInterval" : 20
-}

+ 0 - 5
FreeAPS/Resources/json/defaults/settings/settings.json

@@ -1,5 +0,0 @@
-{
-    "insulin_action_curve": 6,
-    "maxBolus": 10,
-    "maxBasal": 2
-}

+ 0 - 20
FreeAPS/Resources/nb.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC brukes til å skanne Libre-sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor-enheter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autorisert tilgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender brukes til å opprette nye blodsukker-oppføringer.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";

+ 0 - 20
FreeAPS/Resources/nl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wordt gebruikt voor het scannen van Libre sensoren.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Voor geautoriseerde toegang tot bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Agenda wordt gebruikt om nieuwe glucose gebeurtenissen aan te maken.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";

+ 0 - 20
FreeAPS/Resources/pl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/pt-BR.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/pt-PT.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/ru.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC используется для сканирования сенсоров Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованного болюса";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Календарь используется для создания новых событий о глюкозе.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";

+ 0 - 20
FreeAPS/Resources/sk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC sa používa na skenovanie snímačov Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pre autorizovaný prístup k bolusu";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendár slúži na vytvorenie novej udalosti s glukózou.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";

+ 0 - 20
FreeAPS/Resources/sv.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC används för att skanna Libre-sensorn vid uppstart.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth används för att kommunicera med pump och kontinuerlig glukosmätare";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth används för att kommunicera med insulinpumpen och kontinuerliga glukosmätare";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "För auktoriserad åtkomst till bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendern används för att skapa kalenderhändelser för glukosvärden.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin samt kolhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin och kolhydrater.";

+ 0 - 20
FreeAPS/Resources/tr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC Libre sensörlerini taramak için kullanılır.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Bolus'a yetkili erişim için";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Takvim, yeni bir glikoz olayı oluşturmak için kullanılır.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
FreeAPS/Resources/uk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC використовується для сканування сенсорів Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованого болюсу";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Для створення нових подій глюкози використовується календар.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";

+ 0 - 20
FreeAPS/Resources/vi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC được sử dụng để quét các cảm biến Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Được cấp quyền truy cập vào bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Lịch \nCalendar được sử dụng để tạo ra một sự kiện glucose mới.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";

+ 0 - 20
FreeAPS/Resources/zh-Hans.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC 用于扫描 Libre 传感器。";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "用于输注胰岛素授权";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "日历用于创建一个新的葡萄糖事件。";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

ファイルの差分が大きいため隠しています
+ 0 - 1558
FreeAPS/Sources/APS/APSManager.swift


+ 0 - 126
FreeAPS/Sources/APS/CGM/AppGroupSource.swift

@@ -1,126 +0,0 @@
-import Combine
-import Foundation
-import LoopKit
-import LoopKitUI
-
-public extension GlucoseTrend {
-    var direction: String {
-        switch self {
-        case .upUpUp:
-            return "DoubleUp"
-        case .upUp:
-            return "SingleUp"
-        case .up:
-            return "FortyFiveUp"
-        case .flat:
-            return "Flat"
-        case .down:
-            return "FortyFiveDown"
-        case .downDown:
-            return "SingleDown"
-        case .downDownDown:
-            return "DoubleDown"
-        }
-    }
-}
-
-struct AppGroupSource: GlucoseSource {
-    var cgmManager: CGMManagerUI?
-    var glucoseManager: FetchGlucoseManager?
-    let from: String
-    var cgmType: CGMType
-
-    func fetch(_ heartbeat: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
-        guard let suiteName = Bundle.main.appGroupSuiteName,
-              let sharedDefaults = UserDefaults(suiteName: suiteName)
-        else {
-            return Just([]).eraseToAnyPublisher()
-        }
-
-        return Just(fetchLastBGs(60, sharedDefaults, heartbeat)).eraseToAnyPublisher()
-    }
-
-    func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        fetch(nil)
-    }
-
-    private func fetchLastBGs(_ count: Int, _ sharedDefaults: UserDefaults, _ heartbeat: DispatchTimer?) -> [BloodGlucose] {
-        guard let sharedData = sharedDefaults.data(forKey: "latestReadings") else {
-            return []
-        }
-
-        HeartBeatManager.shared.checkCGMBluetoothTransmitter(sharedUserDefaults: sharedDefaults, heartbeat: heartbeat)
-        debug(.deviceManager, "APPGROUP : START FETCH LAST BG ")
-        let decoded = try? JSONSerialization.jsonObject(with: sharedData, options: [])
-        guard let sgvs = decoded as? [AnyObject] else {
-            return []
-        }
-
-        var results: [BloodGlucose] = []
-
-        for sgv in sgvs.prefix(count) {
-            guard
-                let glucose = sgv["Value"] as? Int,
-                let timestamp = sgv["DT"] as? String,
-                let date = parseDate(timestamp)
-            else { continue }
-
-            var direction: String?
-
-            // Dexcom changed the format of trend in 2021 so we accept both String/Int types
-            if let directionString = sgv["direction"] as? String {
-                direction = directionString
-            } else if let intTrend = sgv["trend"] as? Int {
-                direction = GlucoseTrend(rawValue: intTrend)?.direction
-            } else if let intTrend = sgv["Trend"] as? Int {
-                direction = GlucoseTrend(rawValue: intTrend)?.direction
-            } else if let stringTrend = sgv["trend"] as? String, let intTrend = Int(stringTrend) {
-                direction = GlucoseTrend(rawValue: intTrend)?.direction
-            }
-
-            guard let direction = direction else { continue }
-
-            if let from = sgv["from"] as? String {
-                guard from == self.from else { continue }
-            }
-
-            results.append(
-                BloodGlucose(
-                    sgv: glucose,
-                    direction: BloodGlucose.Direction(rawValue: direction),
-                    date: Decimal(Int(date.timeIntervalSince1970 * 1000)),
-                    dateString: date,
-                    unfiltered: Decimal(glucose),
-                    filtered: nil,
-                    noise: nil,
-                    glucose: glucose,
-                    type: "sgv"
-                )
-            )
-        }
-        return results
-    }
-
-    private func parseDate(_ timestamp: String) -> Date? {
-        // timestamp looks like "/Date(1462404576000)/"
-        guard let re = try? NSRegularExpression(pattern: "\\((.*)\\)"),
-              let match = re.firstMatch(in: timestamp, range: NSMakeRange(0, timestamp.count))
-        else {
-            return nil
-        }
-
-        let matchRange = match.range(at: 1)
-        let epoch = Double((timestamp as NSString).substring(with: matchRange))! / 1000
-        return Date(timeIntervalSince1970: epoch)
-    }
-
-    func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? "Not set"))"]
-    }
-}
-
-public extension Bundle {
-    var appGroupSuiteName: String? {
-        object(forInfoDictionaryKey: "AppGroupID") as? String
-    }
-}

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

@@ -1,76 +0,0 @@
-import Foundation
-
-enum CGMType: String, JSON, CaseIterable, Identifiable {
-    var id: String { rawValue }
-    case none
-    case nightscout
-    case xdrip
-    case simulator
-    case enlite
-    case plugin
-
-    var displayName: String {
-        switch self {
-        case .none:
-            return "None"
-        case .nightscout:
-            return "Nightscout"
-        case .xdrip:
-            return "xDrip4iOS"
-        case .simulator:
-            return NSLocalizedString("Glucose Simulator", comment: "Glucose Simulator CGM type")
-        case .enlite:
-            return "Medtronic Enlite"
-        case .plugin:
-            return "plugin CGM"
-        }
-    }
-
-    var appURL: URL? {
-        switch self {
-        case .enlite,
-             .nightscout,
-             .none:
-            return nil
-        case .xdrip:
-            return URL(string: "xdripswift://")!
-        case .simulator:
-            return nil
-        case .plugin:
-            return nil
-        }
-    }
-
-    var externalLink: URL? {
-        switch self {
-        case .xdrip:
-            return URL(string: "https://xdrip4ios.readthedocs.io/")!
-        default: return nil
-        }
-    }
-
-    var subtitle: String {
-        switch self {
-        case .none:
-            return NSLocalizedString("None", comment: "No CGM choiced")
-        case .nightscout:
-            return NSLocalizedString("Online or internal server", comment: "Online or internal server")
-        case .xdrip:
-            return NSLocalizedString(
-                "Using shared app group with external CGM app xDrip4iOS",
-                comment: "Shared app group xDrip4iOS"
-            )
-        case .simulator:
-            return NSLocalizedString("Simple simulator", comment: "Simple simulator")
-        case .enlite:
-            return NSLocalizedString("Minilink transmitter", comment: "Minilink transmitter")
-        case .plugin:
-            return NSLocalizedString("Plugin CGM", comment: "Plugin CGM")
-        }
-    }
-}
-
-enum GlucoseDataError: Error {
-    case noData
-    case unreliableData
-}

+ 0 - 119
FreeAPS/Sources/APS/CGM/Calibrations/CalibrationService.swift

@@ -1,119 +0,0 @@
-import Foundation
-import LibreTransmitter
-import Swinject
-
-struct Calibration: JSON, Hashable, Identifiable {
-    let x: Double
-    let y: Double
-    var date = Date()
-
-    static let zero = Calibration(x: 0, y: 0)
-
-    var id = UUID()
-}
-
-protocol CalibrationService {
-    var slope: Double { get }
-    var intercept: Double { get }
-    var calibrations: [Calibration] { get }
-
-    func addCalibration(_ calibration: Calibration)
-    func removeCalibration(_ calibration: Calibration)
-    func removeAllCalibrations()
-    func removeLast()
-
-    func calibrate(value: Int) -> Double
-}
-
-final class BaseCalibrationService: CalibrationService, Injectable {
-    private enum Config {
-        static let minSlope = 0.8
-        static let maxSlope = 1.25
-        static let minIntercept = -100.0
-        static let maxIntercept = 100.0
-        static let maxValue = 500.0
-        static let minValue = 0.0
-    }
-
-    @Injected() var storage: FileStorage!
-    @Injected() var notificationCenter: NotificationCenter!
-    private var lifetime = Lifetime()
-
-    private(set) var calibrations: [Calibration] = [] {
-        didSet {
-            storage.save(calibrations, as: OpenAPS.FreeAPS.calibrations)
-        }
-    }
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-        calibrations = storage.retrieve(OpenAPS.FreeAPS.calibrations, as: [Calibration].self) ?? []
-        subscribe()
-    }
-
-    private func subscribe() {
-//        notificationCenter.publisher(for: .newSensorDetected)
-//            .sink { [weak self] _ in
-//                self?.removeAllCalibrations()
-//            }
-//            .store(in: &lifetime)
-    }
-
-    var slope: Double {
-        guard calibrations.count >= 2 else {
-            return 1
-        }
-
-        let xs = calibrations.map(\.x)
-        let ys = calibrations.map(\.y)
-        let sum1 = average(multiply(xs, ys)) - average(xs) * average(ys)
-        let sum2 = average(multiply(xs, xs)) - pow(average(xs), 2)
-        let slope = sum1 / sum2
-
-        return min(max(slope, Config.minSlope), Config.maxSlope)
-    }
-
-    var intercept: Double {
-        guard calibrations.count >= 1 else {
-            return 0
-        }
-        let xs = calibrations.map(\.x)
-        let ys = calibrations.map(\.y)
-
-        let intercept = average(ys) - slope * average(xs)
-
-        return min(max(intercept, Config.minIntercept), Config.maxIntercept)
-    }
-
-    func calibrate(value: Int) -> Double {
-        linearRegression(value)
-    }
-
-    func addCalibration(_ calibration: Calibration) {
-        calibrations.append(calibration)
-    }
-
-    func removeCalibration(_ calibration: Calibration) {
-        calibrations.removeAll { $0 == calibration }
-    }
-
-    func removeAllCalibrations() {
-        calibrations.removeAll()
-    }
-
-    func removeLast() {
-        calibrations.removeLast()
-    }
-
-    private func average(_ input: [Double]) -> Double {
-        input.reduce(0, +) / Double(input.count)
-    }
-
-    private func multiply(_ a: [Double], _ b: [Double]) -> [Double] {
-        zip(a, b).map(*)
-    }
-
-    private func linearRegression(_ x: Int) -> Double {
-        (intercept + slope * Double(x)).clamped(Config.minValue ... Config.maxValue)
-    }
-}

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

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

+ 0 - 247
FreeAPS/Sources/APS/CGM/PluginSource.swift

@@ -1,247 +0,0 @@
-import CGMBLEKit
-import Combine
-import Foundation
-import G7SensorKit
-import LibreTransmitter
-import LoopKit
-import LoopKitUI
-
-final class PluginSource: GlucoseSource {
-    private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
-    private let glucoseStorage: GlucoseStorage!
-    var glucoseManager: FetchGlucoseManager?
-
-    var cgmManager: CGMManagerUI?
-
-    var cgmHasValidSensorSession: Bool = false
-
-    private var promise: Future<[BloodGlucose], Error>.Promise?
-
-    init(glucoseStorage: GlucoseStorage, glucoseManager: FetchGlucoseManager) {
-        self.glucoseStorage = glucoseStorage
-        self.glucoseManager = glucoseManager
-
-        cgmManager = glucoseManager.cgmManager
-        cgmManager?.delegateQueue = processQueue
-        cgmManager?.cgmManagerDelegate = self
-    }
-
-    /// Function that fetches blood glucose data
-    /// This function combines two data fetching mechanisms (`callBLEFetch` and `fetchIfNeeded`) into a single publisher.
-    /// It returns the first non-empty result from either of the sources within a 5-minute timeout period.
-    /// If no valid data is fetched within the timeout, it returns an empty array.
-    ///
-    /// - Parameter timer: An optional `DispatchTimer` (not used in the function but can be used to trigger fetch logic).
-    /// - Returns: An `AnyPublisher` that emits an array of `BloodGlucose` values or an empty array if an error occurs or the timeout is reached.
-    func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
-        Publishers.Merge(
-            callBLEFetch(),
-            fetchIfNeeded()
-        )
-        .filter { !$0.isEmpty }
-        .first()
-        .timeout(60 * 5, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    func callBLEFetch() -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { [weak self] promise in
-            self?.promise = promise
-        }
-        .timeout(60 * 5, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { [weak self] promise in
-            guard let self = self else { return }
-            self.processQueue.async {
-                guard let cgmManager = self.cgmManager else { return }
-                cgmManager.fetchNewDataIfNeeded { result in
-                    promise(self.readCGMResult(readingResult: result))
-                }
-            }
-        }
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    deinit {
-        // dexcomManager.transmitter.stopScanning()
-    }
-}
-
-extension PluginSource: CGMManagerDelegate {
-    func deviceManager(
-        _: LoopKit.DeviceManager,
-        logEventForDeviceIdentifier deviceIdentifier: String?,
-        type _: LoopKit.DeviceLogEntryType,
-        message: String,
-        completion _: ((Error?) -> Void)?
-    ) {
-        debug(.deviceManager, "device Manager for \(String(describing: deviceIdentifier)) : \(message)")
-    }
-
-    func issueAlert(_: LoopKit.Alert) {}
-
-    func retractAlert(identifier _: LoopKit.Alert.Identifier) {}
-
-    func doesIssuedAlertExist(identifier _: LoopKit.Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {}
-
-    func lookupAllUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
-    ) {}
-
-    func lookupAllUnacknowledgedUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
-    ) {}
-
-    func recordRetractedAlert(_: LoopKit.Alert, at _: Date) {}
-
-    func cgmManagerWantsDeletion(_ manager: CGMManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        debug(.deviceManager, " CGM Manager with identifier \(manager.pluginIdentifier) wants deletion")
-        // TODO:
-        glucoseManager?.cgmGlucoseSourceType = .none
-    }
-
-    func cgmManager(_: CGMManager, hasNew readingResult: CGMReadingResult) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        promise?(readCGMResult(readingResult: readingResult))
-        debug(.deviceManager, "CGM PLUGIN - Direct return done")
-    }
-
-    func cgmManager(_: LoopKit.CGMManager, hasNew events: [LoopKit.PersistedCgmEvent]) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        // TODO: Events in APS ?
-        // currently only display in log the date of the event
-        events.forEach { event in
-            debug(.deviceManager, "events from CGM at \(event.date)")
-
-            if event.type == .sensorStart {
-                self.glucoseManager?.removeCalibrations()
-            }
-        }
-    }
-
-    func startDateToFilterNewData(for _: CGMManager) -> Date? {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        return glucoseStorage.lastGlucoseDate()
-    }
-
-    func cgmManagerDidUpdateState(_ cgmManager: CGMManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-
-        guard let fetchGlucoseManager = glucoseManager else {
-            debug(
-                .deviceManager,
-                "Could not gracefully unwrap FetchGlucoseManager upon observing LoopKit's cgmManagerDidUpdateState"
-            )
-            return
-        }
-        // Adjust app-specific NS Upload setting value when CGM setting is changed
-        fetchGlucoseManager.settingsManager.settings.uploadGlucose = cgmManager.shouldSyncToRemoteService
-
-        fetchGlucoseManager.updateGlucoseSource(
-            cgmGlucoseSourceType: fetchGlucoseManager.settingsManager.settings.cgm,
-            cgmGlucosePluginId: fetchGlucoseManager.settingsManager.settings.cgmPluginIdentifier,
-            newManager: cgmManager as? CGMManagerUI
-        )
-    }
-
-    func credentialStoragePrefix(for _: CGMManager) -> String {
-        // return string unique to this instance of the CGMManager
-        UUID().uuidString
-    }
-
-    func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
-        debug(.deviceManager, "DEBUG DID UPDATE STATE")
-        processQueue.async {
-            if self.cgmHasValidSensorSession != status.hasValidSensorSession {
-                self.cgmHasValidSensorSession = status.hasValidSensorSession
-            }
-        }
-    }
-
-    private func readCGMResult(readingResult: CGMReadingResult) -> Result<[BloodGlucose], Error> {
-        debug(.deviceManager, "PLUGIN CGM - Process CGM Reading Result launched with \(readingResult)")
-
-        if glucoseManager?.glucoseSource == nil {
-            debug(
-                .deviceManager,
-                "No glucose source available."
-            )
-        }
-
-        switch readingResult {
-        case let .newData(values):
-
-            var sensorActivatedAt: Date?
-            var sensorStartDate: Date?
-            var sensorTransmitterID: String?
-
-            /// SAGE
-            if let cgmTransmitterManager = cgmManager as? LibreTransmitterManagerV3 {
-                let sensorInfo = cgmTransmitterManager.sensorInfoObservable
-                sensorActivatedAt = sensorInfo.activatedAt
-                sensorStartDate = sensorInfo.activatedAt
-                sensorTransmitterID = sensorInfo.sensorSerial
-            } else if let cgmTransmitterManager = cgmManager as? G5CGMManager {
-                let latestReading = cgmTransmitterManager.latestReading
-                sensorActivatedAt = latestReading?.activationDate
-                sensorStartDate = latestReading?.sessionStartDate
-                sensorTransmitterID = latestReading?.transmitterID
-            } else if let cgmTransmitterManager = cgmManager as? G6CGMManager {
-                let latestReading = cgmTransmitterManager.latestReading
-                sensorActivatedAt = latestReading?.activationDate
-                sensorStartDate = latestReading?.sessionStartDate
-                sensorTransmitterID = latestReading?.transmitterID
-            } else if let cgmTransmitterManager = cgmManager as? G7CGMManager {
-                sensorActivatedAt = cgmTransmitterManager.sensorActivatedAt
-                sensorStartDate = cgmTransmitterManager.sensorActivatedAt
-                sensorTransmitterID = cgmTransmitterManager.sensorName
-            }
-
-            let bloodGlucose = values.compactMap { newGlucoseSample -> BloodGlucose? in
-                let quantity = newGlucoseSample.quantity
-
-                let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
-                return BloodGlucose(
-                    _id: UUID().uuidString,
-                    sgv: value,
-                    direction: .init(trendType: newGlucoseSample.trend),
-                    date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),
-                    dateString: newGlucoseSample.date,
-                    unfiltered: Decimal(value),
-                    filtered: nil,
-                    noise: nil,
-                    glucose: value,
-                    type: "sgv",
-                    activationDate: sensorActivatedAt,
-                    sessionStartDate: sensorStartDate,
-                    transmitterID: sensorTransmitterID
-                )
-            }
-            return .success(bloodGlucose)
-        case .unreliableData:
-            // loopManager.receivedUnreliableCGMReading()
-            return .failure(GlucoseDataError.unreliableData)
-        case .noData:
-            return .failure(GlucoseDataError.noData)
-        case let .error(error):
-            return .failure(error)
-        }
-    }
-}
-
-extension PluginSource {
-    func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Plugin CGM source"]
-    }
-}

+ 0 - 716
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -1,716 +0,0 @@
-import Algorithms
-import Combine
-import CoreData
-import Foundation
-import LoopKit
-import LoopKitUI
-import MinimedKit
-import MockKit
-import OmniBLE
-import OmniKit
-import ShareClient
-import SwiftDate
-import Swinject
-import UserNotifications
-
-protocol DeviceDataManager: GlucoseSource {
-    var pumpManager: PumpManagerUI? { get set }
-    var bluetoothManager: BluetoothStateManager { get }
-    var loopInProgress: Bool { get set }
-    var pumpDisplayState: CurrentValueSubject<PumpDisplayState?, Never> { get }
-    var recommendsLoop: PassthroughSubject<Void, Never> { get }
-    var bolusTrigger: PassthroughSubject<Bool, Never> { get }
-    var manualTempBasal: PassthroughSubject<Bool, Never> { get }
-    var errorSubject: PassthroughSubject<Error, Never> { get }
-    var pumpName: CurrentValueSubject<String, Never> { get }
-    var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
-
-    func heartbeat(date: Date)
-    func createBolusProgressReporter() -> DoseProgressReporter?
-    var alertHistoryStorage: AlertHistoryStorage! { get }
-}
-
-private let staticPumpManagers: [PumpManagerUI.Type] = [
-    MinimedPumpManager.self,
-    OmnipodPumpManager.self,
-    OmniBLEPumpManager.self,
-    MockPumpManager.self
-]
-
-private let staticPumpManagersByIdentifier: [String: PumpManagerUI.Type] = [
-    MinimedPumpManager.pluginIdentifier: MinimedPumpManager.self,
-    OmnipodPumpManager.pluginIdentifier: OmnipodPumpManager.self,
-    OmniBLEPumpManager.pluginIdentifier: OmniBLEPumpManager.self,
-    MockPumpManager.pluginIdentifier: MockPumpManager.self
-]
-
-private let accessLock = NSRecursiveLock(label: "BaseDeviceDataManager.accessLock")
-
-final class BaseDeviceDataManager: DeviceDataManager, Injectable {
-    private let processQueue = DispatchQueue.markedQueue(label: "BaseDeviceDataManager.processQueue")
-    @Injected() private var pumpHistoryStorage: PumpHistoryStorage!
-    @Injected() var alertHistoryStorage: AlertHistoryStorage!
-    @Injected() private var storage: FileStorage!
-    @Injected() private var broadcaster: Broadcaster!
-    @Injected() private var glucoseStorage: GlucoseStorage!
-    @Injected() private var settingsManager: SettingsManager!
-    @Injected() private var bluetoothProvider: BluetoothStateManager!
-
-    @Persisted(key: "BaseDeviceDataManager.lastEventDate") var lastEventDate: Date? = nil
-    @SyncAccess(lock: accessLock) @Persisted(key: "BaseDeviceDataManager.lastHeartBeatTime") var lastHeartBeatTime: Date =
-        .distantPast
-
-    let recommendsLoop = PassthroughSubject<Void, Never>()
-    let bolusTrigger = PassthroughSubject<Bool, Never>()
-    let errorSubject = PassthroughSubject<Error, Never>()
-    let pumpNewStatus = PassthroughSubject<Void, Never>()
-    let manualTempBasal = PassthroughSubject<Bool, Never>()
-
-    private let router = FreeAPSApp.resolver.resolve(Router.self)!
-    @SyncAccess private var pumpUpdateCancellable: AnyCancellable?
-    private var pumpUpdatePromise: Future<Bool, Never>.Promise?
-    @SyncAccess var loopInProgress: Bool = false
-    private let privateContext = CoreDataStack.shared.newTaskContext()
-
-    var pumpManager: PumpManagerUI? {
-        didSet {
-            pumpManager?.pumpManagerDelegate = self
-            pumpManager?.delegateQueue = processQueue
-            rawPumpManager = pumpManager?.rawValue
-            UserDefaults.standard.clearLegacyPumpManagerRawValue()
-            if let pumpManager = pumpManager {
-                pumpDisplayState.value = PumpDisplayState(name: pumpManager.localizedTitle, image: pumpManager.smallImage)
-                pumpName.send(pumpManager.localizedTitle)
-
-                var modifiedPreferences = settingsManager.preferences
-                let bolusIncrement = Decimal(
-                    pumpManager.supportedBolusVolumes.first ??
-                        Double(
-                            settingsManager.preferences
-                                .bolusIncrement
-                        )
-                )
-                modifiedPreferences
-                    .bolusIncrement = bolusIncrement != 0.025 ? bolusIncrement : 0.1
-                storage.save(modifiedPreferences, as: OpenAPS.Settings.preferences)
-
-                if let omnipod = pumpManager as? OmnipodPumpManager {
-                    guard let endTime = omnipod.state.podState?.expiresAt else {
-                        pumpExpiresAtDate.send(nil)
-                        return
-                    }
-                    pumpExpiresAtDate.send(endTime)
-                }
-                if let omnipodBLE = pumpManager as? OmniBLEPumpManager {
-                    guard let endTime = omnipodBLE.state.podState?.expiresAt else {
-                        pumpExpiresAtDate.send(nil)
-                        return
-                    }
-                    pumpExpiresAtDate.send(endTime)
-                }
-                if let simulatorPump = pumpManager as? MockPumpManager {
-                    pumpDisplayState.value = PumpDisplayState(name: simulatorPump.localizedTitle, image: simulatorPump.smallImage)
-                    pumpName.send(simulatorPump.localizedTitle)
-                    storage.save(Decimal(simulatorPump.pumpReservoirCapacity), as: OpenAPS.Monitor.reservoir)
-                    DispatchQueue.main.async {
-                        self.broadcaster.notify(PumpReservoirObserver.self, on: .main) {
-                            $0.pumpReservoirDidChange(Decimal(simulatorPump.state.reservoirUnitsRemaining))
-                        }
-                    }
-                    let batteryPercent = Int((simulatorPump.state.pumpBatteryChargeRemaining ?? 1) * 100)
-                    let battery = Battery(
-                        percent: batteryPercent,
-                        voltage: nil,
-                        string: batteryPercent >= 10 ? .normal : .low,
-                        display: simulatorPump.state.pumpBatteryChargeRemaining != nil
-                    )
-                    Task {
-                        await self.privateContext.perform {
-                            let saveBatteryToCoreData = OpenAPS_Battery(context: self.privateContext)
-                            saveBatteryToCoreData.id = UUID()
-                            saveBatteryToCoreData.date = Date()
-                            saveBatteryToCoreData.percent = Int16(batteryPercent)
-                            saveBatteryToCoreData.voltage = nil
-                            saveBatteryToCoreData.status = batteryPercent >= 10 ? BatteryState.normal.rawValue : BatteryState
-                                .low.rawValue
-                            saveBatteryToCoreData.display = simulatorPump.state.pumpBatteryChargeRemaining != nil
-
-                            do {
-                                guard self.privateContext.hasChanges else { return }
-                                try self.privateContext.save()
-                            } catch {
-                                print(error.localizedDescription)
-                            }
-                        }
-                    }
-                    DispatchQueue.main.async {
-                        self.broadcaster.notify(PumpBatteryObserver.self, on: .main) {
-                            $0.pumpBatteryDidChange(battery)
-                        }
-                    }
-                }
-            } else {
-                pumpDisplayState.value = nil
-                pumpExpiresAtDate.send(nil)
-                pumpName.send("")
-                // Reset bolusIncrement setting to default value, which is 0.1 U
-                var modifiedPreferences = settingsManager.preferences
-                modifiedPreferences.bolusIncrement = 0.1
-                storage.save(modifiedPreferences, as: OpenAPS.Settings.preferences)
-                // Remove OpenAPS_Battery entries
-                Task {
-                    await self.privateContext.perform {
-                        let fetchRequest: NSFetchRequest<OpenAPS_Battery> = OpenAPS_Battery.fetchRequest()
-
-                        do {
-                            let batteryEntries = try self.privateContext.fetch(fetchRequest)
-
-                            for entry in batteryEntries {
-                                self.privateContext.delete(entry)
-                            }
-
-                            guard self.privateContext.hasChanges else { return }
-                            try self.privateContext.save()
-
-                        } catch {
-                            print("Failed to delete OpenAPS_Battery entries: \(error.localizedDescription)")
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @PersistedProperty(key: "PumpManagerState") var rawPumpManager: PumpManager.RawValue?
-
-    var bluetoothManager: BluetoothStateManager { bluetoothProvider }
-
-    var hasBLEHeartbeat: Bool {
-        (pumpManager as? MockPumpManager) == nil
-    }
-
-    let pumpDisplayState = CurrentValueSubject<PumpDisplayState?, Never>(nil)
-    let pumpExpiresAtDate = CurrentValueSubject<Date?, Never>(nil)
-    let pumpName = CurrentValueSubject<String, Never>("Pump")
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-        setupPumpManager()
-        UIDevice.current.isBatteryMonitoringEnabled = true
-        broadcaster.register(AlertObserver.self, observer: self)
-    }
-
-    func setupPumpManager() {
-        if let pumpManagerRawValue = rawPumpManager ?? UserDefaults.standard.legacyPumpManagerRawValue {
-            pumpManager = pumpManagerFromRawValue(pumpManagerRawValue)
-        } else {
-            pumpManager = nil
-        }
-    }
-
-    func createBolusProgressReporter() -> DoseProgressReporter? {
-        pumpManager?.createBolusProgressReporter(reportingOn: processQueue)
-    }
-
-    func heartbeat(date: Date) {
-        guard pumpUpdateCancellable == nil else {
-            warning(.deviceManager, "Pump updating already in progress. Skip updating.")
-            return
-        }
-
-        guard !loopInProgress else {
-            warning(.deviceManager, "Loop in progress. Skip updating.")
-            return
-        }
-
-        func update(_: Future<Bool, Never>.Promise?) {}
-
-        processQueue.safeSync {
-            lastHeartBeatTime = date
-            updatePumpData()
-        }
-    }
-
-    private func updatePumpData() {
-        guard let pumpManager = pumpManager else {
-            debug(.deviceManager, "Pump is not set, skip updating")
-            updateUpdateFinished(false)
-            return
-        }
-
-        debug(.deviceManager, "Start updating the pump data")
-        processQueue.safeSync {
-            pumpManager.ensureCurrentPumpData { _ in
-                debug(.deviceManager, "Pump data updated.")
-                self.updateUpdateFinished(true)
-            }
-        }
-    }
-
-    private func updateUpdateFinished(_ recommendsLoop: Bool) {
-        pumpUpdateCancellable = nil
-        pumpUpdatePromise = nil
-        if !recommendsLoop {
-            warning(.deviceManager, "Loop recommendation time out or got error. Trying to loop right now.")
-        }
-        self.recommendsLoop.send()
-    }
-
-    private func pumpManagerFromRawValue(_ rawValue: [String: Any]) -> PumpManagerUI? {
-        guard let rawState = rawValue["state"] as? PumpManager.RawStateValue,
-              let Manager = pumpManagerTypeFromRawValue(rawValue)
-        else {
-            return nil
-        }
-
-        return Manager.init(rawState: rawState) as? PumpManagerUI
-    }
-
-    private func pumpManagerTypeFromRawValue(_ rawValue: [String: Any]) -> PumpManager.Type? {
-        guard let managerIdentifier = rawValue["managerIdentifier"] as? String else {
-            return nil
-        }
-
-        return staticPumpManagersByIdentifier[managerIdentifier]
-    }
-
-    // MARK: - GlucoseSource
-
-    @Persisted(key: "BaseDeviceDataManager.lastFetchGlucoseDate") private var lastFetchGlucoseDate: Date = .distantPast
-
-    var glucoseManager: FetchGlucoseManager?
-    var cgmManager: CGMManagerUI?
-    var cgmType: CGMType = .enlite
-
-    func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        fetch(nil)
-    }
-
-    func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
-        guard let medtronic = pumpManager as? MinimedPumpManager else {
-            warning(.deviceManager, "Fetch minilink glucose failed: Pump is not Medtronic")
-            return Just([]).eraseToAnyPublisher()
-        }
-
-        guard lastFetchGlucoseDate.addingTimeInterval(5.minutes.timeInterval) < Date() else {
-            return Just([]).eraseToAnyPublisher()
-        }
-
-        medtronic.cgmManagerDelegate = self
-
-        return Future<[BloodGlucose], Error> { promise in
-            self.processQueue.async {
-                medtronic.fetchNewDataIfNeeded { result in
-                    switch result {
-                    case .noData:
-                        debug(.deviceManager, "Minilink glucose is empty")
-                        promise(.success([]))
-                    case .unreliableData:
-                        debug(.deviceManager, "Unreliable data received")
-                        promise(.success([]))
-                    case let .newData(glucose):
-                        let directions: [BloodGlucose.Direction?] = [nil]
-                            + glucose.windows(ofCount: 2).map { window -> BloodGlucose.Direction? in
-                                let pair = Array(window)
-                                guard pair.count == 2 else { return nil }
-                                let firstValue = Int(pair[0].quantity.doubleValue(for: .milligramsPerDeciliter))
-                                let secondValue = Int(pair[1].quantity.doubleValue(for: .milligramsPerDeciliter))
-                                return .init(trend: secondValue - firstValue)
-                            }
-
-                        let results = glucose.enumerated().map { index, sample -> BloodGlucose in
-                            let value = Int(sample.quantity.doubleValue(for: .milligramsPerDeciliter))
-                            return BloodGlucose(
-                                _id: sample.syncIdentifier,
-                                sgv: value,
-                                direction: directions[index],
-                                date: Decimal(Int(sample.date.timeIntervalSince1970 * 1000)),
-                                dateString: sample.date,
-                                unfiltered: Decimal(value),
-                                filtered: nil,
-                                noise: nil,
-                                glucose: value,
-                                type: "sgv"
-                            )
-                        }
-                        if let lastDate = results.last?.dateString {
-                            self.lastFetchGlucoseDate = lastDate
-                        }
-
-                        promise(.success(results))
-                    case let .error(error):
-                        warning(.deviceManager, "Fetch minilink glucose failed", error: error)
-                        promise(.failure(error))
-                    }
-                }
-            }
-        }
-        .timeout(60 * 3, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-}
-
-// MARK: - PumpManagerDelegate
-
-extension BaseDeviceDataManager: PumpManagerDelegate {
-    var automaticDosingEnabled: Bool {
-        settingsManager.settings.closedLoop // Take if close or open loop
-    }
-
-    func pumpManager(
-        _: LoopKit.PumpManager,
-        didRequestBasalRateScheduleChange _: LoopKit.BasalRateSchedule,
-        completion _: @escaping (Error?) -> Void
-    ) {
-        debug(.deviceManager, "pumpManagerBasalRateChange")
-    }
-
-    func pumpManagerPumpWasReplaced(_: PumpManager) {
-        debug(.deviceManager, "pumpManagerPumpWasReplaced")
-    }
-
-    var detectedSystemTimeOffset: TimeInterval {
-        // trustedTimeChecker.detectedSystemTimeOffset
-        0
-    }
-
-    func pumpManager(_: PumpManager, didAdjustPumpClockBy adjustment: TimeInterval) {
-        debug(.deviceManager, "didAdjustPumpClockBy \(adjustment)")
-    }
-
-    func pumpManagerDidUpdateState(_ pumpManager: PumpManager) {
-        rawPumpManager = pumpManager.rawValue
-        if self.pumpManager == nil, let newPumpManager = pumpManager as? PumpManagerUI {
-            self.pumpManager = newPumpManager
-        }
-        pumpName.send(pumpManager.localizedTitle)
-    }
-
-    /// heartbeat with pump occurs some issues in the backgroundtask - so never used
-    func pumpManagerBLEHeartbeatDidFire(_: PumpManager) {
-        debug(.deviceManager, "Pump Heartbeat: do nothing. Pump connection is OK")
-    }
-
-    func pumpManagerMustProvideBLEHeartbeat(_: PumpManager) -> Bool {
-        true
-    }
-
-    func pumpManager(_ pumpManager: PumpManager, didUpdate status: PumpManagerStatus, oldStatus: PumpManagerStatus) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        debug(.deviceManager, "New pump status Bolus: \(status.bolusState)")
-        debug(.deviceManager, "New pump status Basal: \(String(describing: status.basalDeliveryState))")
-
-        if case .inProgress = status.bolusState {
-            bolusTrigger.send(true)
-        } else {
-            bolusTrigger.send(false)
-        }
-
-        if status.insulinType != oldStatus.insulinType {
-            settingsManager.updateInsulinCurve(status.insulinType)
-        }
-
-        broadcaster.notify(PumpTimeZoneObserver.self, on: processQueue) {
-            $0.pumpTimeZoneDidChange(status.timeZone)
-        }
-
-        if let omnipod = pumpManager as? OmnipodPumpManager {
-            let reservoirVal = omnipod.state.podState?.lastInsulinMeasurements?.reservoirLevel ?? 0xDEAD_BEEF
-            // TODO: find the value Pod.maximumReservoirReading
-            let reservoir = Decimal(reservoirVal) > 50.0 ? 0xDEAD_BEEF : reservoirVal
-
-            storage.save(Decimal(reservoir), as: OpenAPS.Monitor.reservoir)
-            broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
-                $0.pumpReservoirDidChange(Decimal(reservoir))
-            }
-
-            if let tempBasal = omnipod.state.podState?.unfinalizedTempBasal, !tempBasal.isFinished(),
-               !tempBasal.automatic
-            {
-                // the manual basal temp is launch - block every thing
-                debug(.deviceManager, "manual temp basal")
-                manualTempBasal.send(true)
-            } else {
-                // no more manual Temp Basal !
-                manualTempBasal.send(false)
-            }
-
-            guard let endTime = omnipod.state.podState?.expiresAt else {
-                pumpExpiresAtDate.send(nil)
-                return
-            }
-            pumpExpiresAtDate.send(endTime)
-
-            if let startTime = omnipod.state.podState?.activatedAt {
-                storage.save(startTime, as: OpenAPS.Monitor.podAge)
-            }
-        }
-
-        if let omnipodBLE = pumpManager as? OmniBLEPumpManager {
-            let reservoirVal = omnipodBLE.state.podState?.lastInsulinMeasurements?.reservoirLevel ?? 0xDEAD_BEEF
-            // TODO: find the value Pod.maximumReservoirReading
-            let reservoir = Decimal(reservoirVal) > 50.0 ? 0xDEAD_BEEF : reservoirVal
-
-            storage.save(Decimal(reservoir), as: OpenAPS.Monitor.reservoir)
-            broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
-                $0.pumpReservoirDidChange(Decimal(reservoir))
-            }
-
-            // manual temp basal on
-            if let tempBasal = omnipodBLE.state.podState?.unfinalizedTempBasal, !tempBasal.isFinished(),
-               !tempBasal.automatic
-            {
-                // the manual basal temp is launch - block every thing
-                debug(.deviceManager, "manual temp basal")
-                manualTempBasal.send(true)
-            } else {
-                // no more manual Temp Basal !
-                manualTempBasal.send(false)
-            }
-
-            guard let endTime = omnipodBLE.state.podState?.expiresAt else {
-                pumpExpiresAtDate.send(nil)
-                return
-            }
-            pumpExpiresAtDate.send(endTime)
-
-            if let startTime = omnipodBLE.state.podState?.activatedAt {
-                storage.save(startTime, as: OpenAPS.Monitor.podAge)
-            }
-        }
-
-        if let simulatorPump = pumpManager as? MockPumpManager {
-            broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
-                $0.pumpReservoirDidChange(Decimal(simulatorPump.state.reservoirUnitsRemaining))
-            }
-        }
-    }
-
-    func pumpManagerWillDeactivate(_: PumpManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        pumpManager = nil
-        broadcaster.notify(PumpDeactivatedObserver.self, on: processQueue) {
-            $0.pumpDeactivatedDidChange()
-        }
-    }
-
-    func pumpManager(_: PumpManager, didUpdatePumpRecordsBasalProfileStartEvents _: Bool) {}
-
-    func pumpManager(_: PumpManager, didError error: PumpManagerError) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        debug(.deviceManager, "error: \(error.localizedDescription), reason: \(String(describing: error.failureReason))")
-        errorSubject.send(error)
-    }
-
-    func pumpManager(
-        _: PumpManager,
-        hasNewPumpEvents events: [NewPumpEvent],
-        lastReconciliation _: Date?,
-        replacePendingEvents _: Bool,
-        completion: @escaping (_ error: Error?) -> Void
-    ) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-
-        // filter buggy TBRs > maxBasal from MDT
-        let events = events.filter {
-            // type is optional...
-            guard let type = $0.type, type == .tempBasal else { return true }
-            return $0.dose?.unitsPerHour ?? 0 <= Double(settingsManager.pumpSettings.maxBasal)
-        }
-        pumpHistoryStorage.storePumpEvents(events)
-        lastEventDate = events.last?.date
-        completion(nil)
-    }
-
-    func pumpManager(
-        _: PumpManager,
-        didReadReservoirValue units: Double,
-        at date: Date,
-        completion: @escaping (Result<
-            (newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool),
-            Error
-        >) -> Void
-    ) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        debug(.deviceManager, "Reservoir Value \(units), at: \(date)")
-        storage.save(Decimal(units), as: OpenAPS.Monitor.reservoir)
-        broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
-            $0.pumpReservoirDidChange(Decimal(units))
-        }
-
-        completion(.success((
-            newValue: Reservoir(startDate: Date(), unitVolume: units),
-            lastValue: nil,
-            areStoredValuesContinuous: true
-        )))
-    }
-
-    func pumpManagerRecommendsLoop(_: PumpManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        debug(.deviceManager, "Pump recommends loop")
-        guard let promise = pumpUpdatePromise else {
-            warning(.deviceManager, "We do not waiting for loop recommendation at this time.")
-            return
-        }
-        promise(.success(true))
-    }
-
-    func startDateToFilterNewPumpEvents(for _: PumpManager) -> Date {
-        lastEventDate?.addingTimeInterval(-15.minutes.timeInterval) ?? Date().addingTimeInterval(-2.hours.timeInterval)
-    }
-}
-
-// MARK: - DeviceManagerDelegate
-
-extension BaseDeviceDataManager: DeviceManagerDelegate {
-    func issueAlert(_ alert: Alert) {
-        alertHistoryStorage.storeAlert(
-            AlertEntry(
-                alertIdentifier: alert.identifier.alertIdentifier,
-                primitiveInterruptionLevel: alert.interruptionLevel.storedValue as? Decimal,
-                issuedDate: Date(),
-                managerIdentifier: alert.identifier.managerIdentifier,
-                triggerType: alert.trigger.storedType,
-                triggerInterval: alert.trigger.storedInterval as? Decimal,
-                contentTitle: alert.foregroundContent?.title,
-                contentBody: alert.foregroundContent?.body
-            )
-        )
-    }
-
-    func retractAlert(identifier: Alert.Identifier) {
-        alertHistoryStorage.deleteAlert(identifier: identifier.alertIdentifier)
-    }
-
-    func doesIssuedAlertExist(identifier _: Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {
-        debug(.deviceManager, "doesIssueAlertExist")
-    }
-
-    func lookupAllUnretracted(managerIdentifier _: String, completion _: @escaping (Result<[PersistedAlert], Error>) -> Void) {
-        debug(.deviceManager, "lookupAllUnretracted")
-    }
-
-    func lookupAllUnacknowledgedUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[PersistedAlert], Error>) -> Void
-    ) {}
-
-    func recordRetractedAlert(_: Alert, at _: Date) {}
-
-    func removeNotificationRequests(for _: DeviceManager, identifiers: [String]) {
-        DispatchQueue.main.async {
-            UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
-        }
-    }
-
-    func deviceManager(
-        _: DeviceManager,
-        logEventForDeviceIdentifier _: String?,
-        type _: DeviceLogEntryType,
-        message: String,
-        completion _: ((Error?) -> Void)?
-    ) {
-        debug(.deviceManager, "Device message: \(message)")
-    }
-}
-
-// MARK: - CGMManagerDelegate
-
-extension BaseDeviceDataManager: CGMManagerDelegate {
-    func startDateToFilterNewData(for _: CGMManager) -> Date? {
-        glucoseStorage.syncDate().addingTimeInterval(-10.minutes.timeInterval) // additional time to calculate directions
-    }
-
-    func cgmManager(_: CGMManager, hasNew _: CGMReadingResult) {}
-
-    func cgmManager(_: LoopKit.CGMManager, hasNew _: [LoopKit.PersistedCgmEvent]) {}
-
-    func cgmManagerWantsDeletion(_: CGMManager) {}
-
-    func cgmManagerDidUpdateState(_: CGMManager) {}
-
-    func credentialStoragePrefix(for _: CGMManager) -> String { "BaseDeviceDataManager" }
-
-    func cgmManager(_: CGMManager, didUpdate _: CGMManagerStatus) {}
-}
-
-// MARK: - AlertPresenter
-
-extension BaseDeviceDataManager: AlertObserver {
-    func AlertDidUpdate(_ alerts: [AlertEntry]) {
-        alerts.forEach { alert in
-            if alert.acknowledgedDate == nil {
-                ackAlert(alert: alert)
-            }
-        }
-    }
-
-    private func ackAlert(alert: AlertEntry) {
-        let typeMessage: MessageType
-        let alertUp = alert.alertIdentifier.uppercased()
-        if alertUp.contains("FAULT") || alertUp.contains("ERROR") {
-            typeMessage = .errorPump
-        } else {
-            typeMessage = .warning
-        }
-
-        let messageCont = MessageContent(content: alert.contentBody ?? "Unknown", type: typeMessage)
-        let alertIssueDate = alert.issuedDate
-
-        processQueue.async {
-            // if not alert in OmniPod/BLE, the acknowledgeAlert didn't do callbacks- Hack to manage this case
-            if let omnipodBLE = self.pumpManager as? OmniBLEPumpManager {
-                if omnipodBLE.state.activeAlerts.isEmpty {
-                    // force to ack alert in the alertStorage
-                    self.alertHistoryStorage.ackAlert(alertIssueDate, nil)
-                }
-            }
-
-            if let omniPod = self.pumpManager as? OmnipodPumpManager {
-                if omniPod.state.activeAlerts.isEmpty {
-                    // force to ack alert in the alertStorage
-                    self.alertHistoryStorage.ackAlert(alertIssueDate, nil)
-                }
-            }
-
-            self.pumpManager?.acknowledgeAlert(alertIdentifier: alert.alertIdentifier) { error in
-                self.router.alertMessage.send(messageCont)
-                if let error = error {
-                    self.alertHistoryStorage.ackAlert(alertIssueDate, error.localizedDescription)
-                    debug(.deviceManager, "acknowledge not succeeded with error \(error.localizedDescription)")
-                } else {
-                    self.alertHistoryStorage.ackAlert(alertIssueDate, nil)
-                }
-            }
-
-            self.broadcaster.notify(pumpNotificationObserver.self, on: self.processQueue) {
-                $0.pumpNotification(alert: alert)
-            }
-        }
-    }
-}
-
-// extension BaseDeviceDataManager: AlertPresenter {
-//    func issueAlert(_: Alert) {}
-//    func retractAlert(identifier _: Alert.Identifier) {}
-// }
-
-// MARK: Others
-
-protocol PumpReservoirObserver {
-    func pumpReservoirDidChange(_ reservoir: Decimal)
-}
-
-protocol PumpBatteryObserver {
-    func pumpBatteryDidChange(_ battery: Battery)
-}
-
-protocol PumpTimeZoneObserver {
-    func pumpTimeZoneDidChange(_ timezone: TimeZone)
-}
-
-protocol PumpDeactivatedObserver {
-    func pumpDeactivatedDidChange()
-}

+ 0 - 52
FreeAPS/Sources/APS/FetchAnnouncementsManager.swift

@@ -1,52 +0,0 @@
-import Combine
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol FetchAnnouncementsManager {}
-
-final class BaseFetchAnnouncementsManager: FetchAnnouncementsManager, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseFetchAnnouncementsManager.processQueue")
-    @Injected() var announcementsStorage: AnnouncementsStorage!
-    @Injected() var nightscoutManager: NightscoutManager!
-    @Injected() var apsManager: APSManager!
-    @Injected() var settingsManager: SettingsManager!
-
-    private var lifetime = Lifetime()
-    private let timer = DispatchTimer(timeInterval: 5.minutes.timeInterval)
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-        subscribe()
-    }
-
-    private func subscribe() {
-        timer.publisher
-            .receive(on: processQueue)
-            .flatMap { _ -> AnyPublisher<[Announcement], Never> in
-                guard self.settingsManager.settings.allowAnnouncements else {
-                    return Just([]).eraseToAnyPublisher()
-                }
-                debug(.nightscout, "FetchAnnouncementsManager heartbeat")
-                debug(.nightscout, "Start fetching announcements")
-                return self.nightscoutManager.fetchAnnouncements()
-            }
-            .sink { announcements in
-                guard let last = announcements.filter({ $0.createdAt > self.announcementsStorage.syncDate() })
-                    .sorted(by: { $0.createdAt < $1.createdAt })
-                    .last
-                else { return }
-
-                self.announcementsStorage.storeAnnouncements([last], enacted: false)
-                if self.settingsManager.settings.allowAnnouncements, let recent = self.announcementsStorage.recent(),
-                   recent.action != nil
-                {
-                    debug(.nightscout, "New announcements found")
-                    self.apsManager.enactAnnouncement(recent)
-                }
-            }
-            .store(in: &lifetime)
-        timer.fire()
-        timer.resume()
-    }
-}

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

@@ -1,349 +0,0 @@
-import Combine
-import Foundation
-import HealthKit
-import LoopKit
-import LoopKitUI
-import SwiftDate
-import Swinject
-import UIKit
-
-protocol FetchGlucoseManager: SourceInfoProvider {
-    func updateGlucoseStore(newBloodGlucose: [BloodGlucose])
-    func refreshCGM()
-    func updateGlucoseSource(cgmGlucoseSourceType: CGMType, cgmGlucosePluginId: String, newManager: CGMManagerUI?)
-    func deleteGlucoseSource()
-    func removeCalibrations()
-    var glucoseSource: GlucoseSource! { get }
-    var cgmManager: CGMManagerUI? { get }
-    var cgmGlucoseSourceType: CGMType { get set }
-    var cgmGlucosePluginId: String { get }
-    var settingsManager: SettingsManager! { get }
-    var shouldSyncToRemoteService: Bool { get }
-}
-
-extension FetchGlucoseManager {
-    func updateGlucoseSource(cgmGlucoseSourceType: CGMType, cgmGlucosePluginId: String) {
-        updateGlucoseSource(cgmGlucoseSourceType: cgmGlucoseSourceType, cgmGlucosePluginId: cgmGlucosePluginId, newManager: nil)
-    }
-}
-
-final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseGlucoseManager.processQueue")
-
-    @Injected() var glucoseStorage: GlucoseStorage!
-    @Injected() var nightscoutManager: NightscoutManager!
-    @Injected() var tidepoolService: TidepoolManager!
-    @Injected() var apsManager: APSManager!
-    @Injected() var settingsManager: SettingsManager!
-    @Injected() var healthKitManager: HealthKitManager!
-    @Injected() var deviceDataManager: DeviceDataManager!
-    @Injected() var pluginCGMManager: PluginManager!
-    @Injected() var calibrationService: CalibrationService!
-
-    private var lifetime = Lifetime()
-    private let timer = DispatchTimer(timeInterval: 1.minutes.timeInterval)
-    var cgmGlucoseSourceType: CGMType = .none
-    var cgmGlucosePluginId: String = ""
-    var cgmManager: CGMManagerUI? {
-        didSet {
-            rawCGMManager = cgmManager?.rawValue
-            UserDefaults.standard.clearLegacyCGMManagerRawValue()
-        }
-    }
-
-    @PersistedProperty(key: "CGMManagerState") var rawCGMManager: CGMManager.RawValue?
-
-    private lazy var simulatorSource = GlucoseSimulatorSource()
-
-    private let context = CoreDataStack.shared.newTaskContext()
-
-    var shouldSyncToRemoteService: Bool {
-        guard let cgmManager = cgmManager else {
-            return true
-        }
-        return cgmManager.shouldSyncToRemoteService
-    }
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-        // init at the start of the app
-        cgmGlucoseSourceType = settingsManager.settings.cgm
-        cgmGlucosePluginId = settingsManager.settings.cgmPluginIdentifier
-        // load cgmManager
-        updateGlucoseSource(
-            cgmGlucoseSourceType: settingsManager.settings.cgm,
-            cgmGlucosePluginId: settingsManager.settings.cgmPluginIdentifier
-        )
-        subscribe()
-    }
-
-    var glucoseSource: GlucoseSource!
-
-    func removeCalibrations() {
-        calibrationService.removeAllCalibrations()
-    }
-
-    func deleteGlucoseSource() {
-        cgmManager = nil
-        updateGlucoseSource(
-            cgmGlucoseSourceType: CGMType.none,
-            cgmGlucosePluginId: ""
-        )
-    }
-
-    func saveConfigManager() {
-        guard let cgmM = cgmManager else {
-            return
-        }
-        // save the config in rawCGMManager
-        rawCGMManager = cgmM.rawValue
-
-        // sync with upload glucose
-        settingsManager.settings.uploadGlucose = cgmM.shouldSyncToRemoteService
-    }
-
-    private func updateManagerUnits(_ manager: CGMManagerUI?) {
-        let units = settingsManager.settings.units
-        let managerName = cgmManager.map { "\(type(of: $0))" } ?? "nil"
-        let loopkitUnits: HKUnit = units == .mgdL ? .milligramsPerDeciliter : .millimolesPerLiter
-        print("manager: \(managerName) is changing units to: \(loopkitUnits.description) ")
-        manager?.unitDidChange(to: loopkitUnits)
-    }
-
-    func updateGlucoseSource(cgmGlucoseSourceType: CGMType, cgmGlucosePluginId: String, newManager: CGMManagerUI?) {
-        // if changed, remove all calibrations
-        if self.cgmGlucoseSourceType != cgmGlucoseSourceType || self.cgmGlucosePluginId != cgmGlucosePluginId {
-            removeCalibrations()
-            cgmManager = nil
-            glucoseSource = nil
-        }
-
-        self.cgmGlucoseSourceType = cgmGlucoseSourceType
-        self.cgmGlucosePluginId = cgmGlucosePluginId
-
-        // if not plugin, manager is not changed and stay with the "old" value if the user come back to previous cgmtype
-        // if plugin, if the same pluginID, no change required because the manager is available
-        // if plugin, if not the same pluginID, need to reset the cgmManager
-        // if plugin and newManager provides, update cgmManager
-        debug(.apsManager, "plugin : \(String(describing: cgmManager?.pluginIdentifier))")
-        if let manager = newManager
-        {
-            cgmManager = manager
-            removeCalibrations()
-        } else if self.cgmGlucoseSourceType == .plugin, cgmManager == nil, let rawCGMManager = rawCGMManager {
-            cgmManager = cgmManagerFromRawValue(rawCGMManager)
-            updateManagerUnits(cgmManager)
-
-        } else {
-            saveConfigManager()
-        }
-
-        if glucoseSource == nil {
-            switch self.cgmGlucoseSourceType {
-            case .none:
-                glucoseSource = nil
-            case .xdrip:
-                glucoseSource = AppGroupSource(from: "xDrip", cgmType: .xdrip)
-            case .nightscout:
-                glucoseSource = nightscoutManager
-            case .simulator:
-                glucoseSource = simulatorSource
-            case .enlite:
-                glucoseSource = deviceDataManager
-            case .plugin:
-                glucoseSource = PluginSource(glucoseStorage: glucoseStorage, glucoseManager: self)
-            }
-        }
-    }
-
-    /// Upload cgmManager from raw value
-    func cgmManagerFromRawValue(_ rawValue: [String: Any]) -> CGMManagerUI? {
-        guard let rawState = rawValue["state"] as? CGMManager.RawStateValue,
-              let Manager = pluginCGMManager.getCGMManagerTypeByIdentifier(cgmGlucosePluginId)
-        else {
-            return nil
-        }
-        return Manager.init(rawState: rawState)
-    }
-
-    /// function called when a callback is fired by CGM BLE - no more used
-    public func updateGlucoseStore(newBloodGlucose: [BloodGlucose]) {
-        let syncDate = glucoseStorage.syncDate()
-        debug(.deviceManager, "CGM BLE FETCHGLUCOSE  : SyncDate is \(syncDate)")
-        glucoseStoreAndHeartDecision(syncDate: syncDate, glucose: newBloodGlucose)
-    }
-
-    /// function to try to force the refresh of the CGM - generally provide by the pump heartbeat
-    public func refreshCGM() {
-        debug(.deviceManager, "refreshCGM by pump")
-
-        Publishers.CombineLatest(
-            Just(glucoseStorage.syncDate()),
-            glucoseSource.fetchIfNeeded()
-        )
-        .eraseToAnyPublisher()
-        .receive(on: processQueue)
-        .sink { syncDate, glucose in
-            debug(.nightscout, "refreshCGM FETCHGLUCOSE : SyncDate is \(syncDate)")
-            self.glucoseStoreAndHeartDecision(syncDate: syncDate, glucose: glucose)
-        }
-        .store(in: &lifetime)
-    }
-
-    private func fetchGlucose() -> [GlucoseStored]? {
-        CoreDataStack.shared.fetchEntities(
-            ofType: GlucoseStored.self,
-            onContext: context,
-            predicate: NSPredicate.predicateFor30MinAgo,
-            key: "date",
-            ascending: false,
-            fetchLimit: 6
-        )
-    }
-
-    private func processGlucose() -> [BloodGlucose] {
-        context.performAndWait {
-            guard let results = fetchGlucose() else { return [] }
-            return results.map { result in
-                BloodGlucose(
-                    sgv: Int(result.glucose),
-                    direction: BloodGlucose.Direction(from: result.direction ?? ""),
-                    date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
-                    dateString: result.date ?? Date(),
-                    unfiltered: Decimal(result.glucose),
-                    filtered: Decimal(result.glucose),
-                    noise: nil,
-                    glucose: Int(result.glucose)
-                )
-            }
-        }
-    }
-
-    private func glucoseStoreAndHeartDecision(syncDate: Date, glucose: [BloodGlucose]) {
-        // calibration add if required only for sensor
-        let newGlucose = overcalibrate(entries: glucose)
-
-        var filteredByDate: [BloodGlucose] = []
-        var filtered: [BloodGlucose] = []
-
-        // start background time extension
-        var backGroundFetchBGTaskID: UIBackgroundTaskIdentifier?
-        backGroundFetchBGTaskID = UIApplication.shared.beginBackgroundTask(withName: "save BG starting") {
-            guard let bg = backGroundFetchBGTaskID else { return }
-            UIApplication.shared.endBackgroundTask(bg)
-            backGroundFetchBGTaskID = .invalid
-        }
-
-        guard newGlucose.isNotEmpty else {
-            if let backgroundTask = backGroundFetchBGTaskID {
-                UIApplication.shared.endBackgroundTask(backgroundTask)
-                backGroundFetchBGTaskID = .invalid
-            }
-            return
-        }
-
-        filteredByDate = newGlucose.filter { $0.dateString > syncDate }
-        filtered = glucoseStorage.filterTooFrequentGlucose(filteredByDate, at: syncDate)
-
-        guard filtered.isNotEmpty else {
-            // end of the Background tasks
-            if let backgroundTask = backGroundFetchBGTaskID {
-                UIApplication.shared.endBackgroundTask(backgroundTask)
-                backGroundFetchBGTaskID = .invalid
-            }
-            return
-        }
-        debug(.deviceManager, "New glucose found")
-
-        // filter the data if it is the case
-        if settingsManager.settings.smoothGlucose {
-            // limited to 30 min of old glucose data
-            let oldGlucoseValues = processGlucose()
-
-            var smoothedValues = oldGlucoseValues + filtered
-            // smooth with 3 repeats
-            for _ in 1 ... 3 {
-                smoothedValues.smoothSavitzkyGolayQuaDratic(withFilterWidth: 3)
-            }
-            // find the new values only
-            filtered = smoothedValues.filter { $0.dateString > syncDate }
-        }
-
-        glucoseStorage.storeGlucose(filtered)
-
-        deviceDataManager.heartbeat(date: Date())
-
-        // End of the Background tasks
-        if let backgroundTask = backGroundFetchBGTaskID {
-            UIApplication.shared.endBackgroundTask(backgroundTask)
-            backGroundFetchBGTaskID = .invalid
-        }
-    }
-
-    /// The function used to start the timer sync - Function of the variable defined in config
-    private func subscribe() {
-        timer.publisher
-            .receive(on: processQueue)
-            .flatMap { [self] _ -> AnyPublisher<[BloodGlucose], Never> in
-                debug(.nightscout, "FetchGlucoseManager timer heartbeat")
-                if let glucoseSource = self.glucoseSource {
-                    return glucoseSource.fetch(self.timer).eraseToAnyPublisher()
-                } else {
-                    return Empty(completeImmediately: false).eraseToAnyPublisher()
-                }
-            }
-            .sink { glucose in
-                debug(.nightscout, "FetchGlucoseManager callback sensor")
-                Publishers.CombineLatest(
-                    Just(glucose),
-                    Just(self.glucoseStorage.syncDate())
-                )
-                .eraseToAnyPublisher()
-                .sink { newGlucose, syncDate in
-                    self.glucoseStoreAndHeartDecision(
-                        syncDate: syncDate,
-                        glucose: newGlucose
-                    )
-                }
-                .store(in: &self.lifetime)
-            }
-            .store(in: &lifetime)
-        timer.fire()
-        timer.resume()
-    }
-
-    func sourceInfo() -> [String: Any]? {
-        glucoseSource.sourceInfo()
-    }
-
-    private func overcalibrate(entries: [BloodGlucose]) -> [BloodGlucose] {
-        // overcalibrate
-        var overcalibration: ((Int) -> (Double))?
-
-        if let cal = calibrationService {
-            overcalibration = cal.calibrate
-        }
-
-        if let overcalibration = overcalibration {
-            return entries.map { entry in
-                var entry = entry
-                entry.glucose = Int(overcalibration(entry.glucose!))
-                entry.sgv = Int(overcalibration(entry.sgv!))
-                return entry
-            }
-        } else {
-            return entries
-        }
-    }
-}
-
-extension CGMManager {
-    typealias RawValue = [String: Any]
-
-    var rawValue: [String: Any] {
-        [
-            "managerIdentifier": pluginIdentifier,
-            "state": rawState
-        ]
-    }
-}

+ 0 - 49
FreeAPS/Sources/APS/FetchTreatmentsManager.swift

@@ -1,49 +0,0 @@
-import Combine
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol FetchTreatmentsManager {}
-
-final class BaseFetchTreatmentsManager: FetchTreatmentsManager, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseFetchTreatmentsManager.processQueue")
-    @Injected() var nightscoutManager: NightscoutManager!
-    @Injected() var tempTargetsStorage: TempTargetsStorage!
-    @Injected() var carbsStorage: CarbsStorage!
-
-    private var lifetime = Lifetime()
-    private let timer = DispatchTimer(timeInterval: 1.minutes.timeInterval)
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-        subscribe()
-    }
-
-    private func subscribe() {
-        timer.publisher
-            .receive(on: processQueue)
-            .sink { [weak self] _ in
-                guard let self = self else { return }
-                debug(.nightscout, "FetchTreatmentsManager heartbeat")
-                debug(.nightscout, "Start fetching carbs and temptargets")
-
-                Task {
-                    async let carbs = self.nightscoutManager.fetchCarbs()
-                    async let tempTargets = self.nightscoutManager.fetchTempTargets()
-
-                    let filteredCarbs = await carbs.filter { !($0.enteredBy?.contains(CarbsEntry.manual) ?? false) }
-                    if filteredCarbs.isNotEmpty {
-                        await self.carbsStorage.storeCarbs(filteredCarbs, areFetchedFromRemote: true)
-                    }
-
-                    let filteredTargets = await tempTargets.filter { !($0.enteredBy?.contains(TempTarget.manual) ?? false) }
-                    if filteredTargets.isNotEmpty {
-                        self.tempTargetsStorage.storeTempTargets(filteredTargets)
-                    }
-                }
-            }
-            .store(in: &lifetime)
-        timer.fire()
-        timer.resume()
-    }
-}

+ 0 - 100
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -1,100 +0,0 @@
-extension OpenAPS {
-    enum Bundle {
-        static let iob = "bundle/iob.js"
-        static let meal = "bundle/meal.js"
-        static let autotunePrep = "bundle/autotune-prep.js"
-        static let autotuneCore = "bundle/autotune-core.js"
-        static let getLastGlucose = "bundle/glucose-get-last.js"
-        static let basalSetTemp = "bundle/basal-set-temp.js"
-        static let determineBasal = "bundle/determine-basal.js"
-        static let autosens = "bundle/autosens.js"
-        static let profile = "bundle/profile.js"
-    }
-
-    enum Prepare {
-        static let iob = "prepare/iob.js"
-        static let meal = "prepare/meal.js"
-        static let autotunePrep = "prepare/autotune-prep.js"
-        static let autotuneCore = "prepare/autotune-core.js"
-        static let determineBasal = "prepare/determine-basal.js"
-        static let autosens = "prepare/autosens.js"
-        static let profile = "prepare/profile.js"
-        static let log = "prepare/log.js"
-    }
-
-    enum Middleware {
-        static let determineBasal = "middleware/determine_basal.js"
-    }
-
-    enum Settings {
-        static let preferences = "preferences.json"
-        static let autotune = "settings/autotune.json"
-        static let autosense = "settings/autosense.json"
-        static let profile = "settings/profile.json"
-        static let pumpProfile = "settings/pumpprofile.json"
-        static let settings = "settings/settings.json"
-        static let bgTargets = "settings/bg_targets.json"
-        static let insulinSensitivities = "settings/insulin_sensitivities.json"
-        static let basalProfile = "settings/basal_profile.json"
-        static let carbRatios = "settings/carb_ratios.json"
-        static let tempTargets = "settings/temptargets.json"
-        static let model = "settings/model.json"
-    }
-
-    enum Monitor {
-        static let pumpHistory = "monitor/pumphistory-24h-zoned.json"
-        static let reservoir = "monitor/reservoir.json"
-        static let carbHistory = "monitor/carbhistory.json"
-        static let clock = "monitor/clock-zoned.json"
-        static let status = "monitor/status.json"
-        static let tempBasal = "monitor/temp_basal.json"
-        static let meal = "monitor/meal.json"
-        static let glucose = "monitor/glucose.json"
-        static let iob = "monitor/iob.json"
-        static let cgmState = "monitor/cgm-state.json"
-        static let podAge = "monitor/pod-age.json"
-        static let oref2_variables = "monitor/oref2_variables.json"
-        static let alertHistory = "monitor/alerthistory.json"
-        static let statistics = "monitor/statistics.json"
-    }
-
-    enum Enact {
-        static let suggested = "enact/suggested.json"
-        static let enacted = "enact/enacted.json"
-    }
-
-    enum Upload {
-        static let nsStatus = "upload/ns-status.json"
-        static let latestTreatments = "upload/latest-treatments.json"
-        static let recentPumphistory = "upload/recent-pumphistory.json"
-    }
-
-    enum Function {
-        static let freeaps = "freeaps"
-        static let generate = "generate"
-        static let tempBasalFunctions = "tempBasalFunctions"
-        static let exportDefaults = "exportDefaults"
-    }
-
-    enum Nightscout {
-        static let uploadedPumphistory = "upload/uploaded-pumphistory.json"
-        static let uploadedCarbs = "upload/uploaded-carbs.json"
-        static let uploadedTempTargets = "upload/uploaded-temptargets.json"
-        static let uploadedGlucose = "upload/uploaded-glucose.json"
-        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 uploadedPreferences = "upload/uploaded-preferences.json"
-        static let uploadedSettings = "upload/uploaded-settings.json"
-        static let uploadedManualGlucose = "upload/uploaded-manual-readings.json"
-        static let uploadedNotes = "upload/uploaded-notes.json"
-    }
-
-    enum FreeAPS {
-        static let settings = "freeaps/freeaps_settings.json"
-        static let announcements = "freeaps/announcements.json"
-        static let announcementsEnacted = "freeaps/announcements_enacted.json"
-        static let tempTargetsPresets = "freeaps/temptargets_presets.json"
-        static let calibrations = "freeaps/calibrations.json"
-    }
-}

ファイルの差分が大きいため隠しています
+ 0 - 1022
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift


+ 0 - 16
FreeAPS/Sources/APS/OpenAPS/Script.swift

@@ -1,16 +0,0 @@
-import Foundation
-
-struct Script {
-    let name: String
-    let body: String
-
-    init(name: String) {
-        self.name = name
-        body = try! String(contentsOf: Bundle.main.url(forResource: "javascript/\(name)", withExtension: "")!)
-    }
-
-    init(name: String, body: String) {
-        self.name = name
-        self.body = body
-    }
-}

+ 0 - 279
FreeAPS/Sources/APS/PluginManager.swift

@@ -1,279 +0,0 @@
-import Foundation
-import LoopKit
-import LoopKitUI
-import Swinject
-
-protocol PluginManager {
-    var availablePumpManagers: [PumpManagerDescriptor] { get }
-    var availableCGMManagers: [CGMManagerDescriptor] { get }
-    var availableServices: [ServiceDescriptor] { get }
-    func getPumpManagerTypeByIdentifier(_ identifier: String) -> PumpManagerUI.Type?
-    func getCGMManagerTypeByIdentifier(_ identifier: String) -> CGMManagerUI.Type?
-    func getServiceTypeByIdentifier(_ identifier: String) -> ServiceUI.Type?
-}
-
-class BasePluginManager: Injectable, PluginManager {
-    let pluginBundles: [Bundle]
-
-    init(resolver: Resolver) {
-        let pluginsURL: URL? = Bundle.main.privateFrameworksURL
-        var bundles = [Bundle]()
-
-        if let pluginsURL = pluginsURL {
-            do {
-                for pluginURL in try FileManager.default.contentsOfDirectory(at: pluginsURL, includingPropertiesForKeys: nil)
-                    .filter({ $0.path.hasSuffix(".framework") })
-                {
-                    if let bundle = Bundle(url: pluginURL) {
-                        if let bname = bundle.object(forInfoDictionaryKey: "CFBundleName") as? String {
-                            debug(.deviceManager, "bundle name: \(bname)")
-                        }
-                        if let bcgm = bundle.object(forInfoDictionaryKey: "com.loopkit.Loop.CGMManagerIdentifier") as? String {
-                            debug(.deviceManager, "bundle is CGM: \(bcgm)")
-                        }
-
-                        if bundle.isLoopPlugin {
-                            debug(.deviceManager, "Found loop plugin:\(pluginURL.absoluteString)")
-                            bundles.append(bundle)
-                        }
-                    }
-                }
-            } catch {
-                debug(.deviceManager, "Error loading plugin: \(error)")
-            }
-        }
-        pluginBundles = bundles
-        injectServices(resolver)
-    }
-
-    func getPumpManagerTypeByIdentifier(_ identifier: String) -> PumpManagerUI.Type? {
-        for bundle in pluginBundles {
-            if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerIdentifier.rawValue) as? String,
-               name == identifier
-            {
-                do {
-                    try bundle.loadAndReturnError()
-
-                    if let principalClass = bundle.principalClass as? NSObject.Type {
-                        if let plugin = principalClass.init() as? PumpManagerUIPlugin {
-                            return plugin.pumpManagerType
-                        } else {
-                            fatalError("PrincipalClass does not conform to PumpManagerUIPlugin")
-                        }
-
-                    } else {
-                        fatalError("PrincipalClass not found")
-                    }
-                } catch {
-                    debug(.deviceManager, "Error loading plugin: \(error)")
-                }
-            }
-        }
-        return nil
-    }
-
-    var availablePumpManagers: [PumpManagerDescriptor] {
-        pluginBundles.compactMap({ (bundle) -> PumpManagerDescriptor? in
-            guard let title = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerDisplayName.rawValue) as? String,
-                  let identifier = bundle
-                  .object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerIdentifier.rawValue) as? String
-            else {
-                return nil
-            }
-
-            return PumpManagerDescriptor(identifier: identifier, localizedTitle: title)
-        })
-    }
-
-    func getCGMManagerTypeByIdentifier(_ identifier: String) -> CGMManagerUI.Type? {
-        for bundle in pluginBundles {
-            if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerIdentifier.rawValue) as? String,
-               name == identifier
-            {
-                do {
-                    try bundle.loadAndReturnError()
-
-                    if let principalClass = bundle.principalClass as? NSObject.Type {
-                        if let plugin = principalClass.init() as? CGMManagerUIPlugin {
-                            return plugin.cgmManagerType
-                        } else {
-                            fatalError("PrincipalClass does not conform to CGMManagerUIPlugin")
-                        }
-
-                    } else {
-                        fatalError("PrincipalClass not found")
-                    }
-                } catch {
-                    debug(.deviceManager, "Error loading plugin: \(error)")
-                }
-            }
-        }
-        return nil
-    }
-
-    var availableCGMManagers: [CGMManagerDescriptor] {
-        pluginBundles.compactMap({ (bundle) -> CGMManagerDescriptor? in
-            guard let title = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerDisplayName.rawValue) as? String,
-                  let identifier = bundle
-                  .object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerIdentifier.rawValue) as? String
-            else {
-                return nil
-            }
-
-            return CGMManagerDescriptor(identifier: identifier, localizedTitle: title)
-        })
-    }
-
-    func getServiceTypeByIdentifier(_ identifier: String) -> ServiceUI.Type? {
-        for bundle in pluginBundles {
-            if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.serviceIdentifier.rawValue) as? String,
-               name == identifier
-            {
-                do {
-                    try bundle.loadAndReturnError()
-
-                    if let principalClass = bundle.principalClass as? NSObject.Type {
-                        if let plugin = principalClass.init() as? ServiceUIPlugin {
-                            return plugin.serviceType
-                        } else {
-                            fatalError("PrincipalClass does not conform to ServiceUIPlugin")
-                        }
-
-                    } else {
-                        fatalError("PrincipalClass not found")
-                    }
-                } catch {
-                    debug(.deviceManager, "Error loading plugin: \(error)")
-                }
-            }
-        }
-        return nil
-    }
-
-    var availableServices: [ServiceDescriptor] {
-        pluginBundles.compactMap({ (bundle) -> ServiceDescriptor? in
-            guard let title = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.serviceDisplayName.rawValue) as? String,
-                  let identifier = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.serviceIdentifier.rawValue) as? String
-            else {
-                return nil
-            }
-
-            return ServiceDescriptor(identifier: identifier, localizedTitle: title)
-        })
-    }
-
-    func getStatefulPluginTypeByIdentifier(_ identifier: String) -> StatefulPluggable.Type? {
-        for bundle in pluginBundles {
-            if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.statefulPluginIdentifier.rawValue) as? String,
-               name == identifier
-            {
-                do {
-                    try bundle.loadAndReturnError()
-
-                    if let principalClass = bundle.principalClass as? NSObject.Type {
-                        if let plugin = principalClass.init() as? StatefulPlugin {
-                            return plugin.pluginType
-                        } else {
-                            fatalError("PrincipalClass does not conform to StatefulPlugin")
-                        }
-
-                    } else {
-                        fatalError("PrincipalClass not found")
-                    }
-                } catch {
-                    debug(.deviceManager, "Error loading plugin: \(error)")
-                }
-            }
-        }
-        return nil
-    }
-
-    var availableStatefulPluginIdentifiers: [String] {
-        pluginBundles.compactMap({ (bundle) -> String? in
-            bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.statefulPluginIdentifier.rawValue) as? String
-        })
-    }
-
-    func getOnboardingTypeByIdentifier(_ identifier: String) -> OnboardingUI.Type? {
-        for bundle in pluginBundles {
-            if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.onboardingIdentifier.rawValue) as? String,
-               name == identifier
-            {
-                do {
-                    try bundle.loadAndReturnError()
-
-                    if let principalClass = bundle.principalClass as? NSObject.Type {
-                        if let plugin = principalClass.init() as? OnboardingUIPlugin {
-                            return plugin.onboardingType
-                        } else {
-                            fatalError("PrincipalClass does not conform to OnboardingUIPlugin")
-                        }
-
-                    } else {
-                        fatalError("PrincipalClass not found")
-                    }
-                } catch {
-                    debug(.deviceManager, "Error loading plugin: \(error)")
-                }
-            }
-        }
-        return nil
-    }
-
-    var availableOnboardingIdentifiers: [String] {
-        pluginBundles.compactMap({ (bundle) -> String? in
-            bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.onboardingIdentifier.rawValue) as? String
-        })
-    }
-
-    func getSupportUITypeByIdentifier(_ identifier: String) -> SupportUI.Type? {
-        for bundle in pluginBundles {
-            if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.supportIdentifier.rawValue) as? String,
-               name == identifier
-            {
-                do {
-                    try bundle.loadAndReturnError()
-
-                    if let principalClass = bundle.principalClass as? NSObject.Type {
-                        if let plugin = principalClass.init() as? SupportUIPlugin {
-                            return type(of: plugin.support)
-                        } else {
-                            fatalError("PrincipalClass does not conform to SupportUIPlugin")
-                        }
-
-                    } else {
-                        fatalError("PrincipalClass not found")
-                    }
-                } catch {
-                    debug(.deviceManager, "Error loading plugin: \(error)")
-                }
-            }
-        }
-        return nil
-    }
-}
-
-extension Bundle {
-    var isPumpManagerPlugin: Bool {
-        object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerIdentifier.rawValue) as? String != nil }
-
-    var isCGMManagerPlugin: Bool {
-        object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerIdentifier.rawValue) as? String != nil }
-
-    var isStatefulPlugin: Bool {
-        object(forInfoDictionaryKey: LoopPluginBundleKey.statefulPluginIdentifier.rawValue) as? String != nil }
-
-    var isServicePlugin: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.serviceIdentifier.rawValue) as? String != nil }
-    var isOnboardingPlugin: Bool {
-        object(forInfoDictionaryKey: LoopPluginBundleKey.onboardingIdentifier.rawValue) as? String != nil }
-
-    var isSupportPlugin: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.supportIdentifier.rawValue) as? String != nil }
-
-    var isLoopPlugin: Bool {
-        isPumpManagerPlugin || isCGMManagerPlugin || isStatefulPlugin || isServicePlugin || isOnboardingPlugin || isSupportPlugin
-    }
-
-    var isLoopExtension: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.extensionIdentifier.rawValue) as? String != nil }
-
-    var isSimulator: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.pluginIsSimulator.rawValue) as? Bool == true }
-}

+ 0 - 80
FreeAPS/Sources/APS/Storage/AnnouncementsStorage.swift

@@ -1,80 +0,0 @@
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol AnnouncementsStorage {
-    func storeAnnouncements(_ announcements: [Announcement], enacted: Bool)
-    func syncDate() -> Date
-    func recent() -> Announcement?
-    func validate() -> [Announcement]
-}
-
-final class BaseAnnouncementsStorage: AnnouncementsStorage, Injectable {
-    enum Config {
-        static let recentInterval = 10.minutes.timeInterval
-    }
-
-    private let processQueue = DispatchQueue(label: "BaseAnnouncementsStorage.processQueue")
-    @Injected() private var storage: FileStorage!
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    func storeAnnouncements(_ announcements: [Announcement], enacted: Bool) {
-        processQueue.sync {
-            let file = enacted ? OpenAPS.FreeAPS.announcementsEnacted : OpenAPS.FreeAPS.announcements
-            self.storage.transaction { storage in
-                storage.append(announcements, to: file, uniqBy: \.createdAt)
-                let uniqEvents = storage.retrieve(file, as: [Announcement].self)?
-                    .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
-                    .sorted { $0.createdAt > $1.createdAt } ?? []
-                storage.save(Array(uniqEvents), as: file)
-            }
-        }
-    }
-
-    func syncDate() -> Date {
-        guard let events = storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self),
-              let recentEnacted = events.filter({ $0.enteredBy == Announcement.remote }).first
-        else {
-            return Date().addingTimeInterval(-Config.recentInterval)
-        }
-        return recentEnacted.createdAt.addingTimeInterval(Config.recentInterval)
-    }
-
-    func recent() -> Announcement? {
-        guard let events = storage.retrieve(OpenAPS.FreeAPS.announcements, as: [Announcement].self)
-        else {
-            return nil
-        }
-        guard let recent = events
-            .filter({
-                $0.enteredBy == Announcement.remote && $0.createdAt.addingTimeInterval(Config.recentInterval) > Date()
-            })
-            .first
-        else {
-            return nil
-        }
-        guard let enactedEvents = storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self)
-        else {
-            return recent
-        }
-
-        guard enactedEvents.first(where: { $0.createdAt == recent.createdAt }) == nil
-        else {
-            return nil
-        }
-        return recent
-    }
-
-    func validate() -> [Announcement] {
-        guard let enactedEvents = storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self)?.reversed()
-        else {
-            return []
-        }
-        let validate = enactedEvents
-            .filter({ $0.enteredBy == Announcement.remote })
-        return validate
-    }
-}

+ 0 - 481
FreeAPS/Sources/APS/Storage/CarbsStorage.swift

@@ -1,481 +0,0 @@
-import Combine
-import CoreData
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol CarbsObserver {
-    func carbsDidUpdate(_ carbs: [CarbsEntry])
-}
-
-protocol CarbsStorage {
-    var updatePublisher: AnyPublisher<Void, Never> { get }
-    func storeCarbs(_ carbs: [CarbsEntry], areFetchedFromRemote: Bool) async
-    func deleteCarbs(_ treatmentObjectID: NSManagedObjectID) async
-    func syncDate() -> Date
-    func recent() -> [CarbsEntry]
-    func getCarbsNotYetUploadedToNightscout() async -> [NightscoutTreatment]
-    func getFPUsNotYetUploadedToNightscout() async -> [NightscoutTreatment]
-    func deleteCarbs(at uniqueID: String, fpuID: String, complex: Bool)
-    func getCarbsNotYetUploadedToHealth() async -> [CarbsEntry]
-    func getCarbsNotYetUploadedToTidepool() async -> [CarbsEntry]
-}
-
-final class BaseCarbsStorage: CarbsStorage, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseCarbsStorage.processQueue")
-    @Injected() private var storage: FileStorage!
-    @Injected() private var broadcaster: Broadcaster!
-    @Injected() private var settings: SettingsManager!
-
-    let coredataContext = CoreDataStack.shared.newTaskContext()
-
-    private let updateSubject = PassthroughSubject<Void, Never>()
-
-    var updatePublisher: AnyPublisher<Void, Never> {
-        updateSubject.eraseToAnyPublisher()
-    }
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    func storeCarbs(_ entries: [CarbsEntry], areFetchedFromRemote: Bool) async {
-        var entriesToStore = entries
-
-        if areFetchedFromRemote {
-            entriesToStore = await filterRemoteEntries(entries: entriesToStore)
-        }
-
-        await saveCarbsToCoreData(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)
-
-        await saveCarbEquivalents(entries: entriesToStore, areFetchedFromRemote: areFetchedFromRemote)
-    }
-
-    private func filterRemoteEntries(entries: [CarbsEntry]) async -> [CarbsEntry] {
-        // Fetch only the date property from Core Data
-        guard let existing24hCarbEntries = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: CarbEntryStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.predicateForOneDayAgo,
-            key: "date",
-            ascending: false,
-            batchSize: 50,
-            propertiesToFetch: ["date", "objectID"]
-        ) as? [[String: Any]] else {
-            return entries
-        }
-
-        // Extract dates into a set for efficient lookup
-        // Since we are not dealing with NSManagedObjects directly it is safe to pass properties between threads
-        let existingTimestamps = Set(existing24hCarbEntries.compactMap { $0["date"] as? Date })
-
-        // Remove all entries that have a matching date in existingTimestamps
-        var filteredEntries = entries
-        filteredEntries.removeAll { entry in
-            let entryDate = entry.actualDate ?? entry.createdAt
-            return existingTimestamps.contains(entryDate)
-        }
-
-        return filteredEntries
-    }
-
-    /**
-     Calculates the duration for processing FPUs (fat and protein units) based on the FPUs and the time cap.
-
-     - The function uses predefined rules to determine the duration based on the number of FPUs.
-     - Ensures that the duration does not exceed the time cap.
-
-     - Parameters:
-       - fpus: The number of FPUs calculated from fat and protein.
-       - timeCap: The maximum allowed duration.
-
-     - Returns: The computed duration in hours.
-     */
-    private func calculateComputedDuration(fpus: Decimal, timeCap: Int) -> Int {
-        switch fpus {
-        case ..<2:
-            return 3
-        case 2 ..< 3:
-            return 4
-        case 3 ..< 4:
-            return 5
-        default:
-            return timeCap
-        }
-    }
-
-    /**
-     Processes fat and protein entries to generate future carb equivalents, ensuring each equivalent is at least 1.0 grams.
-
-     - The function calculates the equivalent carb dosage size and adjusts the interval to ensure each equivalent is at least 1.0 grams.
-     - Creates future carb entries based on the adjusted carb equivalent size and interval.
-
-     - Parameters:
-       - entries: An array of `CarbsEntry` objects representing the carbohydrate entries to be processed.
-       - fat: The amount of fat in the last entry.
-       - protein: The amount of protein in the last entry.
-       - createdAt: The creation date of the last entry.
-
-     - Returns: A tuple containing the array of future carb entries and the total carb equivalents.
-     */
-    private func processFPU(
-        entries: [CarbsEntry],
-        fat: Decimal,
-        protein: Decimal,
-        createdAt: Date,
-        actualDate: Date?
-    ) -> ([CarbsEntry], Decimal) {
-        let interval = settings.settings.minuteInterval
-        let timeCap = settings.settings.timeCap
-        let adjustment = settings.settings.individualAdjustmentFactor
-        let delay = settings.settings.delay
-
-        let kcal = protein * 4 + fat * 9
-        let carbEquivalents = (kcal / 10) * adjustment
-        let fpus = carbEquivalents / 10
-        var computedDuration = calculateComputedDuration(fpus: fpus, timeCap: timeCap)
-
-        var carbEquivalentSize: Decimal = carbEquivalents / Decimal(computedDuration)
-        carbEquivalentSize /= Decimal(60 / interval)
-
-        if carbEquivalentSize < 1.0 {
-            carbEquivalentSize = 1.0
-            computedDuration = Int(carbEquivalents / carbEquivalentSize)
-        }
-
-        let roundedEquivalent: Double = round(Double(carbEquivalentSize * 10)) / 10
-        carbEquivalentSize = Decimal(roundedEquivalent)
-        var numberOfEquivalents = carbEquivalents / carbEquivalentSize
-
-        var useDate = actualDate ?? createdAt
-        let fpuID = entries.first?.fpuID ?? UUID().uuidString
-        var futureCarbArray = [CarbsEntry]()
-        var firstIndex = true
-
-        while carbEquivalents > 0, numberOfEquivalents > 0 {
-            useDate = firstIndex ? useDate.addingTimeInterval(delay.minutes.timeInterval) : useDate
-                .addingTimeInterval(interval.minutes.timeInterval)
-            firstIndex = false
-
-            let eachCarbEntry = CarbsEntry(
-                id: UUID().uuidString,
-                createdAt: createdAt,
-                actualDate: useDate,
-                carbs: carbEquivalentSize,
-                fat: 0,
-                protein: 0,
-                note: nil,
-                enteredBy: CarbsEntry.manual,
-                isFPU: true,
-                fpuID: fpuID
-            )
-            futureCarbArray.append(eachCarbEntry)
-            numberOfEquivalents -= 1
-        }
-
-        return (futureCarbArray, carbEquivalents)
-    }
-
-    private func saveCarbEquivalents(entries: [CarbsEntry], areFetchedFromRemote: Bool) async {
-        guard let lastEntry = entries.last else { return }
-
-        if let fat = lastEntry.fat, let protein = lastEntry.protein, fat > 0 || protein > 0 {
-            let (futureCarbEquivalents, carbEquivalentCount) = processFPU(
-                entries: entries,
-                fat: fat,
-                protein: protein,
-                createdAt: lastEntry.createdAt,
-                actualDate: lastEntry.actualDate
-            )
-
-            if carbEquivalentCount > 0 {
-                await saveFPUToCoreDataAsBatchInsert(entries: futureCarbEquivalents, areFetchedFromRemote: areFetchedFromRemote)
-            }
-        }
-    }
-
-    private func saveCarbsToCoreData(entries: [CarbsEntry], areFetchedFromRemote: Bool) async {
-        guard let entry = entries.last, entry.carbs != 0 else { return }
-
-        await coredataContext.perform {
-            let newItem = CarbEntryStored(context: self.coredataContext)
-            newItem.date = entry.actualDate ?? entry.createdAt
-            newItem.carbs = Double(truncating: NSDecimalNumber(decimal: entry.carbs))
-            newItem.fat = Double(truncating: NSDecimalNumber(decimal: entry.fat ?? 0))
-            newItem.protein = Double(truncating: NSDecimalNumber(decimal: entry.protein ?? 0))
-            newItem.note = entry.note
-            newItem.id = UUID()
-            newItem.isFPU = false
-            newItem.isUploadedToNS = areFetchedFromRemote ? true : false
-            newItem.isUploadedToHealth = false
-            newItem.isUploadedToTidepool = false
-
-            if entry.fat != nil, entry.protein != nil, let fpuId = entry.fpuID {
-                newItem.fpuID = UUID(uuidString: fpuId)
-            }
-
-            do {
-                guard self.coredataContext.hasChanges else { return }
-                try self.coredataContext.save()
-            } catch {
-                print(error.localizedDescription)
-            }
-        }
-    }
-
-    private func saveFPUToCoreDataAsBatchInsert(entries: [CarbsEntry], areFetchedFromRemote: Bool) async {
-        let commonFPUID = UUID(
-            uuidString: entries.first?.fpuID ?? UUID()
-                .uuidString
-        ) // all fpus should only get ONE id per batch insert to be able to delete them referencing the fpuID
-        var entrySlice = ArraySlice(entries) // convert to ArraySlice
-        let batchInsert = NSBatchInsertRequest(entity: CarbEntryStored.entity()) { (managedObject: NSManagedObject) -> Bool in
-            guard let carbEntry = managedObject as? CarbEntryStored, let entry = entrySlice.popFirst(),
-                  let entryId = entry.id
-            else {
-                return true // return true to stop
-            }
-            carbEntry.date = entry.actualDate
-            carbEntry.carbs = Double(truncating: NSDecimalNumber(decimal: entry.carbs))
-            carbEntry.id = UUID.init(uuidString: entryId)
-            carbEntry.fpuID = commonFPUID
-            carbEntry.isFPU = true
-            carbEntry.isUploadedToNS = areFetchedFromRemote ? true : false
-            // do NOT set Health and Tidepool flags to ensure they will NOT be uploaded
-            return false // return false to continue
-        }
-        await coredataContext.perform {
-            do {
-                try self.coredataContext.execute(batchInsert)
-                debugPrint("Carbs Storage: \(DebuggingIdentifiers.succeeded) saved fpus to core data")
-
-                // Notify subscriber in Home State Model to update the FPU Array
-                self.updateSubject.send(())
-            } catch {
-                debugPrint("Carbs Storage: \(DebuggingIdentifiers.failed) error while saving fpus to core data")
-            }
-        }
-    }
-
-    func syncDate() -> Date {
-        Date().addingTimeInterval(-1.days.timeInterval)
-    }
-
-    func recent() -> [CarbsEntry] {
-        storage.retrieve(OpenAPS.Monitor.carbHistory, as: [CarbsEntry].self)?.reversed() ?? []
-    }
-
-    func deleteCarbs(_ treatmentObjectID: NSManagedObjectID) async {
-        let taskContext = CoreDataStack.shared.newTaskContext()
-        taskContext.name = "deleteContext"
-        taskContext.transactionAuthor = "deleteCarbs"
-
-        var carbEntry: CarbEntryStored?
-
-        await taskContext.perform {
-            do {
-                carbEntry = try taskContext.existingObject(with: treatmentObjectID) as? CarbEntryStored
-                guard let carbEntry = carbEntry else {
-                    debugPrint("Carb entry for batch delete not found. \(DebuggingIdentifiers.failed)")
-                    return
-                }
-
-                if carbEntry.isFPU, let fpuID = carbEntry.fpuID {
-                    // fetch request for all carb entries with the same id
-                    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = CarbEntryStored.fetchRequest()
-                    fetchRequest.predicate = NSPredicate(format: "fpuID == %@", fpuID as CVarArg)
-
-                    // NSBatchDeleteRequest
-                    let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
-                    deleteRequest.resultType = .resultTypeCount
-
-                    // execute the batch delete request
-                    let result = try taskContext.execute(deleteRequest) as? NSBatchDeleteResult
-                    debugPrint("\(DebuggingIdentifiers.succeeded) Deleted \(result?.result ?? 0) items with FpuID \(fpuID)")
-
-                    // Notifiy subscribers of the batch delete
-                    self.updateSubject.send(())
-                } else {
-                    taskContext.delete(carbEntry)
-
-                    guard taskContext.hasChanges else { return }
-                    try taskContext.save()
-
-                    debugPrint(
-                        "Data Table State: \(#function) \(DebuggingIdentifiers.succeeded) deleted carb entry from core data"
-                    )
-                }
-
-            } catch {
-                debugPrint("\(DebuggingIdentifiers.failed) Error deleting carb entry: \(error.localizedDescription)")
-            }
-        }
-    }
-
-    func deleteCarbs(at uniqueID: String, fpuID: String, complex: Bool) {
-        processQueue.sync {
-            var allValues = storage.retrieve(OpenAPS.Monitor.carbHistory, as: [CarbsEntry].self) ?? []
-
-            if fpuID != "" {
-                if allValues.firstIndex(where: { $0.fpuID == fpuID }) == nil {
-                    debug(.default, "Didn't find any carb equivalents to delete. ID to search for: " + fpuID.description)
-                } else {
-                    allValues.removeAll(where: { $0.fpuID == fpuID })
-                    storage.save(allValues, as: OpenAPS.Monitor.carbHistory)
-                    broadcaster.notify(CarbsObserver.self, on: processQueue) {
-                        $0.carbsDidUpdate(allValues)
-                    }
-                }
-            }
-
-            if fpuID == "" || complex {
-                if allValues.firstIndex(where: { $0.id == uniqueID }) == nil {
-                    debug(.default, "Didn't find any carb entries to delete. ID to search for: " + uniqueID.description)
-                } else {
-                    allValues.removeAll(where: { $0.id == uniqueID })
-                    storage.save(allValues, as: OpenAPS.Monitor.carbHistory)
-                    broadcaster.notify(CarbsObserver.self, on: processQueue) {
-                        $0.carbsDidUpdate(allValues)
-                    }
-                }
-            }
-        }
-    }
-
-    func getCarbsNotYetUploadedToNightscout() async -> [NightscoutTreatment] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: CarbEntryStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.carbsNotYetUploadedToNightscout,
-            key: "date",
-            ascending: false
-        )
-
-        return await coredataContext.perform {
-            guard let carbEntries = results as? [CarbEntryStored] else {
-                return []
-            }
-
-            return carbEntries.map { result in
-                NightscoutTreatment(
-                    duration: nil,
-                    rawDuration: nil,
-                    rawRate: nil,
-                    absolute: nil,
-                    rate: nil,
-                    eventType: .nsCarbCorrection,
-                    createdAt: result.date,
-                    enteredBy: CarbsEntry.manual,
-                    bolus: nil,
-                    insulin: nil,
-                    notes: result.note,
-                    carbs: Decimal(result.carbs),
-                    fat: Decimal(result.fat),
-                    protein: Decimal(result.protein),
-                    foodType: result.note,
-                    targetTop: nil,
-                    targetBottom: nil,
-                    id: result.id?.uuidString
-                )
-            }
-        }
-    }
-
-    func getFPUsNotYetUploadedToNightscout() async -> [NightscoutTreatment] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: CarbEntryStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.fpusNotYetUploadedToNightscout,
-            key: "date",
-            ascending: false
-        )
-
-        return await coredataContext.perform {
-            guard let fpuEntries = results as? [CarbEntryStored] else { return [] }
-
-            return fpuEntries.map { result in
-                NightscoutTreatment(
-                    duration: nil,
-                    rawDuration: nil,
-                    rawRate: nil,
-                    absolute: nil,
-                    rate: nil,
-                    eventType: .nsCarbCorrection,
-                    createdAt: result.date,
-                    enteredBy: CarbsEntry.manual,
-                    bolus: nil,
-                    insulin: nil,
-                    carbs: Decimal(result.carbs),
-                    fat: Decimal(result.fat),
-                    protein: Decimal(result.protein),
-                    foodType: result.note,
-                    targetTop: nil,
-                    targetBottom: nil,
-                    id: result.fpuID?.uuidString
-                )
-            }
-        }
-    }
-
-    func getCarbsNotYetUploadedToHealth() async -> [CarbsEntry] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: CarbEntryStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.carbsNotYetUploadedToHealth,
-            key: "date",
-            ascending: false
-        )
-
-        guard let carbEntries = results as? [CarbEntryStored] else {
-            return []
-        }
-
-        return await coredataContext.perform {
-            return carbEntries.map { result in
-                CarbsEntry(
-                    id: result.id?.uuidString,
-                    createdAt: result.date ?? Date(),
-                    actualDate: result.date,
-                    carbs: Decimal(result.carbs),
-                    fat: Decimal(result.fat),
-                    protein: Decimal(result.protein),
-                    note: result.note,
-                    enteredBy: CarbsEntry.manual,
-                    isFPU: result.isFPU,
-                    fpuID: result.fpuID?.uuidString
-                )
-            }
-        }
-    }
-
-    func getCarbsNotYetUploadedToTidepool() async -> [CarbsEntry] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: CarbEntryStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.carbsNotYetUploadedToTidepool,
-            key: "date",
-            ascending: false
-        )
-
-        guard let carbEntries = results as? [CarbEntryStored] else {
-            return []
-        }
-
-        return await coredataContext.perform {
-            return carbEntries.map { result in
-                CarbsEntry(
-                    id: result.id?.uuidString,
-                    createdAt: result.date ?? Date(),
-                    actualDate: result.date,
-                    carbs: Decimal(result.carbs),
-                    fat: nil,
-                    protein: nil,
-                    note: result.note,
-                    enteredBy: CarbsEntry.manual,
-                    isFPU: nil,
-                    fpuID: nil
-                )
-            }
-        }
-    }
-}

+ 0 - 204
FreeAPS/Sources/APS/Storage/DeterminationStorage.swift

@@ -1,204 +0,0 @@
-import Combine
-import CoreData
-import Foundation
-import Swinject
-
-protocol DeterminationStorage {
-    func fetchLastDeterminationObjectID(predicate: NSPredicate) async -> [NSManagedObjectID]
-    func getForecastIDs(for determinationID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID]
-    func getForecastValueIDs(for forecastID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID]
-    func fetchForecastObjects(
-        for data: (id: UUID, forecastID: NSManagedObjectID, forecastValueIDs: [NSManagedObjectID]),
-        in context: NSManagedObjectContext
-    ) async -> (UUID, Forecast?, [ForecastValue])
-    func getOrefDeterminationNotYetUploadedToNightscout(_ determinationIds: [NSManagedObjectID]) async -> Determination?
-}
-
-final class BaseDeterminationStorage: DeterminationStorage, Injectable {
-    private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
-    private let backgroundContext = CoreDataStack.shared.newTaskContext()
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    func fetchLastDeterminationObjectID(predicate: NSPredicate) async -> [NSManagedObjectID] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OrefDetermination.self,
-            onContext: backgroundContext,
-            predicate: predicate,
-            key: "deliverAt",
-            ascending: false,
-            fetchLimit: 1
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OrefDetermination] else { return [] }
-
-            return fetchedResults.map(\.objectID)
-        }
-    }
-
-    func getForecastIDs(for determinationID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID] {
-        await context.perform {
-            do {
-                guard let determination = try context.existingObject(with: determinationID) as? OrefDetermination,
-                      let forecastSet = determination.forecasts
-                else {
-                    return []
-                }
-                let forecasts = Array(forecastSet)
-                return forecasts.map(\.objectID) as [NSManagedObjectID]
-            } catch {
-                debugPrint(
-                    "Failed \(DebuggingIdentifiers.failed) to fetch Forecast IDs for OrefDetermination with ID \(determinationID): \(error.localizedDescription)"
-                )
-                return []
-            }
-        }
-    }
-
-    func getForecastValueIDs(for forecastID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID] {
-        await context.perform {
-            do {
-                guard let forecast = try context.existingObject(with: forecastID) as? Forecast,
-                      let forecastValueSet = forecast.forecastValues
-                else {
-                    return []
-                }
-                let forecastValues = forecastValueSet.sorted(by: { $0.index < $1.index })
-                return forecastValues.map(\.objectID)
-            } catch {
-                debugPrint(
-                    "Failed \(DebuggingIdentifiers.failed) to fetch Forecast Value IDs with ID \(forecastID): \(error.localizedDescription)"
-                )
-                return []
-            }
-        }
-    }
-
-    // Fetch forecast value IDs for a given data set
-    func fetchForecastObjects(
-        for data: (id: UUID, forecastID: NSManagedObjectID, forecastValueIDs: [NSManagedObjectID]),
-        in context: NSManagedObjectContext
-    ) async -> (UUID, Forecast?, [ForecastValue]) {
-        return await context.perform {
-            var forecast: Forecast?
-            var forecastValues: [ForecastValue] = []
-
-            do {
-                // Fetch the forecast object
-                forecast = try context.existingObject(with: data.forecastID) as? Forecast
-
-                // Fetch the first 3h of forecast values
-                for forecastValueID in data.forecastValueIDs.prefix(36) {
-                    if let forecastValue = try context.existingObject(with: forecastValueID) as? ForecastValue {
-                        forecastValues.append(forecastValue)
-                    }
-                }
-            } catch {
-                debugPrint(
-                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch forecast Values with error: \(error.localizedDescription)"
-                )
-            }
-            return (data.id, forecast, forecastValues)
-        }
-    }
-
-    // Convert NSDecimalNumber to Decimal
-    func decimal(from nsDecimalNumber: NSDecimalNumber?) -> Decimal {
-        nsDecimalNumber?.decimalValue ?? 0.0
-    }
-
-    // Convert NSSet to array of Ints for Predictions
-    func parseForecastValues(ofType type: String, from determinationID: NSManagedObjectID) async -> [Int]? {
-        let forecastIDs = await getForecastIDs(for: determinationID, in: backgroundContext)
-
-        var forecastValuesList: [Int] = []
-
-        for forecastID in forecastIDs {
-            await backgroundContext.perform {
-                if let forecast = try? self.backgroundContext.existingObject(with: forecastID) as? Forecast {
-                    // Filter the forecast based on the type
-                    if forecast.type == type {
-                        let forecastValueIDs = forecast.forecastValues?.sorted(by: { $0.index < $1.index }).map(\.objectID) ?? []
-
-                        for forecastValueID in forecastValueIDs {
-                            if let forecastValue = try? self.backgroundContext
-                                .existingObject(with: forecastValueID) as? ForecastValue
-                            {
-                                let forecastValueInt = Int(forecastValue.value)
-                                forecastValuesList.append(forecastValueInt)
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        return forecastValuesList.isEmpty ? nil : forecastValuesList
-    }
-
-    func getOrefDeterminationNotYetUploadedToNightscout(_ determinationIds: [NSManagedObjectID]) async -> Determination? {
-        var result: Determination?
-
-        guard let determinationId = determinationIds.first else {
-            return nil
-        }
-
-        let predictions = Predictions(
-            iob: await parseForecastValues(ofType: "iob", from: determinationId),
-            zt: await parseForecastValues(ofType: "zt", from: determinationId),
-            cob: await parseForecastValues(ofType: "cob", from: determinationId),
-            uam: await parseForecastValues(ofType: "uam", from: determinationId)
-        )
-
-        return await backgroundContext.perform {
-            do {
-                let orefDetermination = try self.backgroundContext.existingObject(with: determinationId) as? OrefDetermination
-
-                // Check if the fetched object is of the expected type
-                if let orefDetermination = orefDetermination {
-                    result = Determination(
-                        id: orefDetermination.id ?? UUID(),
-                        reason: orefDetermination.reason ?? "",
-                        units: orefDetermination.smbToDeliver as Decimal?,
-                        insulinReq: self.decimal(from: orefDetermination.insulinReq),
-                        eventualBG: orefDetermination.eventualBG as? Int,
-                        sensitivityRatio: self.decimal(from: orefDetermination.sensitivityRatio),
-                        rate: self.decimal(from: orefDetermination.rate),
-                        duration: self.decimal(from: orefDetermination.duration),
-                        iob: self.decimal(from: orefDetermination.iob),
-                        cob: orefDetermination.cob != 0 ? Decimal(orefDetermination.cob) : nil,
-                        predictions: predictions,
-                        deliverAt: orefDetermination.deliverAt,
-                        carbsReq: orefDetermination.carbsRequired != 0 ? Decimal(orefDetermination.carbsRequired) : nil,
-                        temp: TempType(rawValue: orefDetermination.temp ?? "absolute"),
-                        bg: self.decimal(from: orefDetermination.glucose),
-                        reservoir: self.decimal(from: orefDetermination.reservoir),
-                        isf: self.decimal(from: orefDetermination.insulinSensitivity),
-                        timestamp: orefDetermination.timestamp,
-                        tdd: self.decimal(from: orefDetermination.totalDailyDose),
-                        insulin: nil,
-                        current_target: self.decimal(from: orefDetermination.currentTarget),
-                        insulinForManualBolus: self.decimal(from: orefDetermination.insulinForManualBolus),
-                        manualBolusErrorString: self.decimal(from: orefDetermination.manualBolusErrorString),
-                        minDelta: self.decimal(from: orefDetermination.minDelta),
-                        expectedDelta: self.decimal(from: orefDetermination.expectedDelta),
-                        minGuardBG: nil,
-                        minPredBG: nil,
-                        threshold: self.decimal(from: orefDetermination.threshold),
-                        carbRatio: self.decimal(from: orefDetermination.carbRatio),
-                        received: orefDetermination.enacted // this is actually part of NS...
-                    )
-                }
-            } catch {
-                debugPrint(
-                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch managed object with error: \(error.localizedDescription)"
-                )
-            }
-
-            return result
-        }
-    }
-}

+ 0 - 525
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -1,525 +0,0 @@
-import AVFAudio
-import Combine
-import CoreData
-import Foundation
-import LoopKit
-import SwiftDate
-import SwiftUI
-import Swinject
-
-protocol GlucoseStorage {
-    var updatePublisher: AnyPublisher<Void, Never> { get }
-    func storeGlucose(_ glucose: [BloodGlucose])
-    func isGlucoseDataFresh(_ glucoseDate: Date?) -> Bool
-    func syncDate() -> Date
-    func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at: Date) -> [BloodGlucose]
-    func lastGlucoseDate() -> Date
-    func isGlucoseFresh() -> Bool
-    func getGlucoseNotYetUploadedToNightscout() async -> [BloodGlucose]
-    func getCGMStateNotYetUploadedToNightscout() async -> [NightscoutTreatment]
-    func getManualGlucoseNotYetUploadedToNightscout() async -> [NightscoutTreatment]
-    func getGlucoseNotYetUploadedToHealth() async -> [BloodGlucose]
-    func getManualGlucoseNotYetUploadedToHealth() async -> [BloodGlucose]
-    func getGlucoseNotYetUploadedToTidepool() async -> [StoredGlucoseSample]
-    func getManualGlucoseNotYetUploadedToTidepool() async -> [StoredGlucoseSample]
-    var alarm: GlucoseAlarm? { get }
-    func deleteGlucose(_ treatmentObjectID: NSManagedObjectID) async
-}
-
-final class BaseGlucoseStorage: GlucoseStorage, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseGlucoseStorage.processQueue")
-    @Injected() private var storage: FileStorage!
-    @Injected() private var broadcaster: Broadcaster!
-    @Injected() private var settingsManager: SettingsManager!
-
-    let coredataContext = CoreDataStack.shared.newTaskContext()
-
-    private let updateSubject = PassthroughSubject<Void, Never>()
-
-    var updatePublisher: AnyPublisher<Void, Never> {
-        updateSubject.eraseToAnyPublisher()
-    }
-
-    private enum Config {
-        static let filterTime: TimeInterval = 3.5 * 60
-    }
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    private var glucoseFormatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 0
-        if settingsManager.settings.units == .mmolL {
-            formatter.maximumFractionDigits = 1
-        }
-        formatter.decimalSeparator = "."
-        return formatter
-    }
-
-    func storeGlucose(_ glucose: [BloodGlucose]) {
-        processQueue.sync {
-            self.coredataContext.perform {
-                let datesToCheck: Set<Date?> = Set(glucose.compactMap { $0.dateString as Date? })
-                let fetchRequest: NSFetchRequest<NSFetchRequestResult> = GlucoseStored.fetchRequest()
-                fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
-                    NSPredicate(format: "date IN %@", datesToCheck),
-                    NSPredicate.predicateForOneDayAgo
-                ])
-                fetchRequest.propertiesToFetch = ["date"]
-                fetchRequest.resultType = .dictionaryResultType
-
-                var existingDates = Set<Date>()
-                do {
-                    let results = try self.coredataContext.fetch(fetchRequest) as? [NSDictionary]
-                    existingDates = Set(results?.compactMap({ $0["date"] as? Date }) ?? [])
-                } catch {
-                    debugPrint("Failed to fetch existing glucose dates: \(error)")
-                }
-
-                var filteredGlucose = glucose.filter { !existingDates.contains($0.dateString) }
-
-                // prepare batch insert
-                let batchInsert = NSBatchInsertRequest(
-                    entity: GlucoseStored.entity(),
-                    managedObjectHandler: { (managedObject: NSManagedObject) -> Bool in
-                        guard let glucoseEntry = managedObject as? GlucoseStored, !filteredGlucose.isEmpty else {
-                            return true // Stop if there are no more items
-                        }
-                        let entry = filteredGlucose.removeFirst()
-                        glucoseEntry.id = UUID()
-                        glucoseEntry.glucose = Int16(entry.glucose ?? 0)
-                        glucoseEntry.date = entry.dateString
-                        glucoseEntry.direction = entry.direction?.rawValue
-                        glucoseEntry.isUploadedToNS = false /// the value is not uploaded to NS (yet)
-                        glucoseEntry.isUploadedToHealth = false /// the value is not uploaded to Health (yet)
-                        glucoseEntry.isUploadedToTidepool = false /// the value is not uploaded to Tidepool (yet)
-                        return false // Continue processing
-                    }
-                )
-
-                // process batch insert
-                do {
-                    try self.coredataContext.execute(batchInsert)
-
-                    // Notify subscribers that there is a new glucose value
-                    // We need to do this because the due to the batch insert there is no ManagedObjectContext notification
-                    self.updateSubject.send(())
-                } catch {
-                    debugPrint(
-                        "Glucose Storage: \(#function) \(DebuggingIdentifiers.failed) failed to execute batch insert: \(error)"
-                    )
-                }
-
-                debug(.deviceManager, "start storage cgmState")
-                self.storage.transaction { storage in
-                    let file = OpenAPS.Monitor.cgmState
-                    var treatments = storage.retrieve(file, as: [NightscoutTreatment].self) ?? []
-                    var updated = false
-                    for x in glucose {
-                        debug(.deviceManager, "storeGlucose \(x)")
-                        guard let sessionStartDate = x.sessionStartDate else {
-                            continue
-                        }
-                        if let lastTreatment = treatments.last,
-                           let createdAt = lastTreatment.createdAt,
-                           // When a new Dexcom sensor is started, it produces multiple consecutive
-                           // startDates. Disambiguate them by only allowing a session start per minute.
-                           abs(createdAt.timeIntervalSince(sessionStartDate)) < TimeInterval(60)
-                        {
-                            continue
-                        }
-                        var notes = ""
-                        if let t = x.transmitterID {
-                            notes = t
-                        }
-                        if let a = x.activationDate {
-                            notes = "\(notes) activated on \(a)"
-                        }
-                        let treatment = NightscoutTreatment(
-                            duration: nil,
-                            rawDuration: nil,
-                            rawRate: nil,
-                            absolute: nil,
-                            rate: nil,
-                            eventType: .nsSensorChange,
-                            createdAt: sessionStartDate,
-                            enteredBy: NightscoutTreatment.local,
-                            bolus: nil,
-                            insulin: nil,
-                            notes: notes,
-                            carbs: nil,
-                            fat: nil,
-                            protein: nil,
-                            targetTop: nil,
-                            targetBottom: nil
-                        )
-                        debug(.deviceManager, "CGM sensor change \(treatment)")
-                        treatments.append(treatment)
-                        updated = true
-                    }
-                    if updated {
-                        // We have to keep quite a bit of history as sensors start only every 10 days.
-                        storage.save(
-                            treatments.filter
-                                { $0.createdAt != nil && $0.createdAt!.addingTimeInterval(30.days.timeInterval) > Date() },
-                            as: file
-                        )
-                    }
-                }
-            }
-        }
-    }
-
-    func isGlucoseDataFresh(_ glucoseDate: Date?) -> Bool {
-        guard let glucoseDate = glucoseDate else { return false }
-        return glucoseDate > Date().addingTimeInterval(-6 * 60)
-    }
-
-    func syncDate() -> Date {
-        let fr = GlucoseStored.fetchRequest()
-        fr.predicate = NSPredicate.predicateForOneDayAgo
-        fr.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: false)]
-        fr.fetchLimit = 1
-
-        var date: Date?
-        coredataContext.performAndWait {
-            do {
-                let results = try self.coredataContext.fetch(fr)
-                date = results.first?.date
-            } catch let error as NSError {
-                print("Fetch error: \(DebuggingIdentifiers.failed) \(error.localizedDescription), \(error.userInfo)")
-            }
-        }
-
-        return date ?? .distantPast
-    }
-
-    func lastGlucoseDate() -> Date {
-        let fr = GlucoseStored.fetchRequest()
-        fr.predicate = NSPredicate.predicateForOneDayAgo
-        fr.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: false)]
-        fr.fetchLimit = 1
-
-        var date: Date?
-        coredataContext.performAndWait {
-            do {
-                let results = try self.coredataContext.fetch(fr)
-                date = results.first?.date
-            } catch let error as NSError {
-                print("Fetch error: \(DebuggingIdentifiers.failed) \(error.localizedDescription), \(error.userInfo)")
-            }
-        }
-
-        return date ?? .distantPast
-    }
-
-    func isGlucoseFresh() -> Bool {
-        Date().timeIntervalSince(lastGlucoseDate()) <= Config.filterTime
-    }
-
-    func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at date: Date) -> [BloodGlucose] {
-        var lastDate = date
-        var filtered: [BloodGlucose] = []
-        let sorted = glucose.sorted { $0.date < $1.date }
-
-        for entry in sorted {
-            guard entry.dateString.addingTimeInterval(-Config.filterTime) > lastDate else {
-                continue
-            }
-            filtered.append(entry)
-            lastDate = entry.dateString
-        }
-
-        return filtered
-    }
-
-    func fetchLatestGlucose() -> GlucoseStored? {
-        let predicate = NSPredicate.predicateFor20MinAgo
-        return CoreDataStack.shared.fetchEntities(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: predicate,
-            key: "date",
-            ascending: false,
-            fetchLimit: 1
-        ).first
-    }
-
-    // Fetch glucose that is not uploaded to Nightscout yet
-    /// - Returns: Array of BloodGlucose to ensure the correct format for the NS Upload
-    func getGlucoseNotYetUploadedToNightscout() async -> [BloodGlucose] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.glucoseNotYetUploadedToNightscout,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        return await coredataContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-            return fetchedResults.map { result in
-                BloodGlucose(
-                    _id: result.id?.uuidString ?? UUID().uuidString,
-                    sgv: Int(result.glucose),
-                    direction: BloodGlucose.Direction(from: result.direction ?? ""),
-                    date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
-                    dateString: result.date ?? Date(),
-                    unfiltered: Decimal(result.glucose),
-                    filtered: Decimal(result.glucose),
-                    noise: nil,
-                    glucose: Int(result.glucose)
-                )
-            }
-        }
-    }
-
-    // Fetch manual glucose that is not uploaded to Nightscout yet
-    /// - Returns: Array of NightscoutTreatment to ensure the correct format for the NS Upload
-    func getManualGlucoseNotYetUploadedToNightscout() async -> [NightscoutTreatment] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.manualGlucoseNotYetUploadedToNightscout,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-        return await coredataContext.perform {
-            return fetchedResults.map { result in
-                NightscoutTreatment(
-                    duration: nil,
-                    rawDuration: nil,
-                    rawRate: nil,
-                    absolute: nil,
-                    rate: nil,
-                    eventType: .capillaryGlucose,
-                    createdAt: result.date,
-                    enteredBy: CarbsEntry.manual,
-                    bolus: nil,
-                    insulin: nil,
-                    notes: "Trio User",
-                    carbs: nil,
-                    fat: nil,
-                    protein: nil,
-                    foodType: nil,
-                    targetTop: nil,
-                    targetBottom: nil,
-                    glucoseType: "Manual",
-                    glucose: self.settingsManager.settings
-                        .units == .mgdL ? (self.glucoseFormatter.string(from: Int(result.glucose) as NSNumber) ?? "")
-                        : (self.glucoseFormatter.string(from: Decimal(result.glucose).asMmolL as NSNumber) ?? ""),
-                    units: self.settingsManager.settings.units == .mmolL ? "mmol" : "mg/dl",
-                    id: result.id?.uuidString
-                )
-            }
-        }
-    }
-
-    func getCGMStateNotYetUploadedToNightscout() async -> [NightscoutTreatment] {
-        async let alreadyUploaded: [NightscoutTreatment] = storage
-            .retrieveAsync(OpenAPS.Nightscout.uploadedCGMState, as: [NightscoutTreatment].self) ?? []
-        async let allValues: [NightscoutTreatment] = storage
-            .retrieveAsync(OpenAPS.Monitor.cgmState, as: [NightscoutTreatment].self) ?? []
-
-        let (alreadyUploadedValues, allValuesSet) = await (alreadyUploaded, allValues)
-        return Array(Set(allValuesSet).subtracting(Set(alreadyUploadedValues)))
-    }
-
-    // Fetch glucose that is not uploaded to Nightscout yet
-    /// - Returns: Array of BloodGlucose to ensure the correct format for the NS Upload
-    func getGlucoseNotYetUploadedToHealth() async -> [BloodGlucose] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.glucoseNotYetUploadedToHealth,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-        return await coredataContext.perform {
-            return fetchedResults.map { result in
-                BloodGlucose(
-                    _id: result.id?.uuidString ?? UUID().uuidString,
-                    sgv: Int(result.glucose),
-                    direction: BloodGlucose.Direction(from: result.direction ?? ""),
-                    date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
-                    dateString: result.date ?? Date(),
-                    unfiltered: Decimal(result.glucose),
-                    filtered: Decimal(result.glucose),
-                    noise: nil,
-                    glucose: Int(result.glucose)
-                )
-            }
-        }
-    }
-
-    // Fetch manual glucose that is not uploaded to Nightscout yet
-    /// - Returns: Array of NightscoutTreatment to ensure the correct format for the NS Upload
-    func getManualGlucoseNotYetUploadedToHealth() async -> [BloodGlucose] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.manualGlucoseNotYetUploadedToHealth,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-        return await coredataContext.perform {
-            return fetchedResults.map { result in
-                BloodGlucose(
-                    _id: result.id?.uuidString ?? UUID().uuidString,
-                    sgv: Int(result.glucose),
-                    direction: BloodGlucose.Direction(from: result.direction ?? ""),
-                    date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
-                    dateString: result.date ?? Date(),
-                    unfiltered: Decimal(result.glucose),
-                    filtered: Decimal(result.glucose),
-                    noise: nil,
-                    glucose: Int(result.glucose)
-                )
-            }
-        }
-    }
-
-    // Fetch glucose that is not uploaded to Tidepool yet
-    /// - Returns: Array of StoredGlucoseSample to ensure the correct format for Tidepool upload
-    func getGlucoseNotYetUploadedToTidepool() async -> [StoredGlucoseSample] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.glucoseNotYetUploadedToTidepool,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-        return await coredataContext.perform {
-            return fetchedResults.map { result in
-                BloodGlucose(
-                    _id: result.id?.uuidString ?? UUID().uuidString,
-                    sgv: Int(result.glucose),
-                    direction: BloodGlucose.Direction(from: result.direction ?? ""),
-                    date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
-                    dateString: result.date ?? Date(),
-                    unfiltered: Decimal(result.glucose),
-                    filtered: Decimal(result.glucose),
-                    noise: nil,
-                    glucose: Int(result.glucose)
-                )
-            }
-            .map { $0.convertStoredGlucoseSample(isManualGlucose: false) }
-        }
-    }
-
-    // Fetch manual glucose that is not uploaded to Tidepool yet
-    /// - Returns: Array of StoredGlucoseSample to ensure the correct format for the Tidepool upload
-    func getManualGlucoseNotYetUploadedToTidepool() async -> [StoredGlucoseSample] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: GlucoseStored.self,
-            onContext: coredataContext,
-            predicate: NSPredicate.manualGlucoseNotYetUploadedToTidepool,
-            key: "date",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
-
-        return await coredataContext.perform {
-            return fetchedResults.map { result in
-                BloodGlucose(
-                    _id: result.id?.uuidString ?? UUID().uuidString,
-                    sgv: Int(result.glucose),
-                    direction: BloodGlucose.Direction(from: result.direction ?? ""),
-                    date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
-                    dateString: result.date ?? Date(),
-                    unfiltered: Decimal(result.glucose),
-                    filtered: Decimal(result.glucose),
-                    noise: nil,
-                    glucose: Int(result.glucose)
-                )
-            }.map { $0.convertStoredGlucoseSample(isManualGlucose: true) }
-        }
-    }
-
-    func deleteGlucose(_ treatmentObjectID: NSManagedObjectID) async {
-        let taskContext = CoreDataStack.shared.newTaskContext()
-        taskContext.name = "deleteContext"
-        taskContext.transactionAuthor = "deleteGlucose"
-
-        await taskContext.perform {
-            do {
-                let result = try taskContext.existingObject(with: treatmentObjectID) as? GlucoseStored
-
-                guard let glucoseToDelete = result else {
-                    debugPrint("Data Table State: \(#function) \(DebuggingIdentifiers.failed) glucose not found in core data")
-                    return
-                }
-
-                taskContext.delete(glucoseToDelete)
-
-                guard taskContext.hasChanges else { return }
-                try taskContext.save()
-                debugPrint("\(#file) \(#function) \(DebuggingIdentifiers.succeeded) deleted glucose from core data")
-            } catch {
-                debugPrint(
-                    "\(#file) \(#function) \(DebuggingIdentifiers.failed) error while deleting glucose from core data: \(error.localizedDescription)"
-                )
-            }
-        }
-    }
-
-    var alarm: GlucoseAlarm? {
-        /// glucose can not be older than 20 minutes due to the predicate in the fetch request
-        coredataContext.performAndWait {
-            guard let glucose = fetchLatestGlucose() else { return nil }
-
-            let glucoseValue = glucose.glucose
-
-            if Decimal(glucoseValue) <= settingsManager.settings.lowGlucose {
-                return .low
-            }
-
-            if Decimal(glucoseValue) >= settingsManager.settings.highGlucose {
-                return .high
-            }
-
-            return nil
-        }
-    }
-}
-
-protocol GlucoseObserver {
-    func glucoseDidUpdate(_ glucose: [BloodGlucose])
-}
-
-enum GlucoseAlarm {
-    case high
-    case low
-
-    var displayName: String {
-        switch self {
-        case .high:
-            return NSLocalizedString("LOWALERT!", comment: "LOWALERT!")
-        case .low:
-            return NSLocalizedString("HIGHALERT!", comment: "HIGHALERT!")
-        }
-    }
-}

+ 0 - 298
FreeAPS/Sources/APS/Storage/OverrideStorage.swift

@@ -1,298 +0,0 @@
-import CoreData
-import Foundation
-import Swinject
-
-protocol OverrideStorage {
-    func fetchLastCreatedOverride() async -> [NSManagedObjectID]
-    func loadLatestOverrideConfigurations(fetchLimit: Int) async -> [NSManagedObjectID]
-    func fetchForOverridePresets() async -> [NSManagedObjectID]
-    func calculateTarget(override: OverrideStored) -> Decimal
-    func storeOverride(override: Override) async
-    func copyRunningOverride(_ override: OverrideStored) async -> NSManagedObjectID
-    func deleteOverridePreset(_ objectID: NSManagedObjectID) async
-    func getOverridesNotYetUploadedToNightscout() async -> [NightscoutExercise]
-    func getOverrideRunsNotYetUploadedToNightscout() async -> [NightscoutExercise]
-    func getPresetOverridesForNightscout() async -> [NightscoutPresetOverride]
-}
-
-final class BaseOverrideStorage: OverrideStorage, Injectable {
-    @Injected() private var settingsManager: SettingsManager!
-
-    private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
-    private let backgroundContext = CoreDataStack.shared.newTaskContext()
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    private var dateFormatter: DateFormatter {
-        let dateFormatter = DateFormatter()
-        dateFormatter.dateStyle = .short
-        dateFormatter.timeStyle = .short
-        dateFormatter.locale = Locale.current
-        return dateFormatter
-    }
-
-    func fetchLastCreatedOverride() async -> [NSManagedObjectID] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OverrideStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate(
-                format: "date >= %@",
-                Date.oneDayAgo as NSDate
-            ),
-            key: "date",
-            ascending: false,
-            fetchLimit: 1
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
-
-            return fetchedResults.map(\.objectID)
-        }
-    }
-
-    func loadLatestOverrideConfigurations(fetchLimit: Int) async -> [NSManagedObjectID] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OverrideStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverride,
-            key: "orderPosition",
-            ascending: true,
-            fetchLimit: fetchLimit
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
-
-            return fetchedResults.map(\.objectID)
-        }
-    }
-
-    /// Returns the NSManagedObjectID of the Override Presets
-    func fetchForOverridePresets() async -> [NSManagedObjectID] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OverrideStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate.allOverridePresets,
-            key: "orderPosition",
-            ascending: true
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
-
-            return fetchedResults.map(\.objectID)
-        }
-    }
-
-    @MainActor func calculateTarget(override: OverrideStored) -> Decimal {
-        guard let overrideTarget = override.target, overrideTarget != 0 else {
-            return 100 // default
-        }
-        return overrideTarget.decimalValue
-    }
-
-    func storeOverride(override: Override) async {
-        var presetCount = -1
-        if override.isPreset {
-            let presets = await fetchForOverridePresets()
-            presetCount = presets.count
-        }
-
-        await backgroundContext.perform {
-            let newOverride = OverrideStored(context: self.backgroundContext)
-
-            // override key meta data
-            if !override.name.isEmpty {
-                newOverride.name = override.name
-            } else {
-                let formattedDate = self.dateFormatter.string(from: Date())
-                newOverride.name = "Override \(formattedDate)"
-            }
-            newOverride.id = UUID().uuidString
-            newOverride.date = override.date
-            newOverride.isPreset = override.isPreset
-            newOverride.isUploadedToNS = false
-
-            // Assign orderPosition if it's a preset and presetCount is valid
-            if override.isPreset, presetCount > -1 {
-                newOverride.orderPosition = Int16(presetCount + 1) // Ensure type matches Core Data model
-            }
-
-            // override metrics
-            newOverride.duration = override.duration as NSDecimalNumber
-            newOverride.indefinite = override.indefinite
-            newOverride.percentage = override.percentage
-            newOverride.enabled = override.enabled
-            newOverride.smbIsOff = override.smbIsOff
-            if override.overrideTarget {
-                newOverride.target = (
-                    self.settingsManager.settings.units == .mmolL ? override.target.asMgdL : override.target
-                ) as NSDecimalNumber
-            } else {
-                newOverride.target = 0
-            }
-            if override.advancedSettings {
-                newOverride.advancedSettings = true
-
-                if !override.isfAndCr {
-                    newOverride.isfAndCr = false
-                    newOverride.isf = override.isf
-                    newOverride.cr = override.cr
-                } else {
-                    newOverride.isfAndCr = true
-                }
-
-                if override.smbIsAlwaysOff {
-                    newOverride.smbIsAlwaysOff = true
-                    newOverride.start = override.start as NSDecimalNumber
-                    newOverride.end = override.end as NSDecimalNumber
-                } else {
-                    newOverride.smbIsAlwaysOff = false
-                }
-
-                newOverride.smbMinutes = override.smbMinutes as NSDecimalNumber
-                newOverride.uamMinutes = override.uamMinutes as NSDecimalNumber
-            }
-
-            do {
-                guard self.backgroundContext.hasChanges else { return }
-                try self.backgroundContext.save()
-            } catch let error as NSError {
-                debugPrint(
-                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to save Override Preset to Core Data with error: \(error.userInfo)"
-                )
-            }
-        }
-    }
-
-    // Copy the current Override if it is a RUNNING Preset
-    /// otherwise we would edit the Preset
-    @MainActor func copyRunningOverride(_ override: OverrideStored) async -> NSManagedObjectID {
-        let newOverride = OverrideStored(context: viewContext)
-        newOverride.duration = override.duration
-        newOverride.indefinite = override.indefinite
-        newOverride.percentage = override.percentage
-        newOverride.smbIsOff = override.smbIsOff
-        newOverride.name = override.name
-        newOverride.isPreset = false // no Preset
-        newOverride.date = override.date
-        newOverride.enabled = override.enabled
-        newOverride.target = override.target
-        newOverride.advancedSettings = override.advancedSettings
-        newOverride.isfAndCr = override.isfAndCr
-        newOverride.isf = override.isf
-        newOverride.cr = override.cr
-        newOverride.smbIsAlwaysOff = override.smbIsAlwaysOff
-        newOverride.start = override.start
-        newOverride.end = override.end
-        newOverride.smbMinutes = override.smbMinutes
-        newOverride.uamMinutes = override.uamMinutes
-        newOverride.isUploadedToNS = true // set to true to avoid getting duplicate entries on NS
-
-        await viewContext.perform {
-            do {
-                guard self.viewContext.hasChanges else { return }
-                try self.viewContext.save()
-            } catch let error as NSError {
-                debugPrint(
-                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to copy Override with error: \(error.userInfo)"
-                )
-            }
-        }
-
-        return newOverride.objectID
-    }
-
-    /// marked as MainActor to be able to publish changes from the background
-    /// - Parameter: NSManagedObjectID to be able to transfer the object safely from one thread to another thread
-    @MainActor func deleteOverridePreset(_ objectID: NSManagedObjectID) async {
-        await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
-    }
-
-    func getOverridesNotYetUploadedToNightscout() async -> [NightscoutExercise] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OverrideStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout,
-            key: "date",
-            ascending: false
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedOverrides = results as? [OverrideStored] else { return [] }
-
-            return fetchedOverrides.map { override in
-                let duration = override.indefinite ? 1440 : override.duration ?? 0 // 1440 min = 1 day
-                return NightscoutExercise(
-                    duration: Int(truncating: duration),
-                    eventType: OverrideStored.EventType.nsExercise,
-                    createdAt: override.date ?? Date(),
-                    enteredBy: NightscoutExercise.local,
-                    notes: override.name ?? "Custom Override",
-                    id: UUID(uuidString: override.id ?? UUID().uuidString)
-                )
-            }
-        }
-    }
-
-    func getOverrideRunsNotYetUploadedToNightscout() async -> [NightscoutExercise] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OverrideRunStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate(
-                format: "startDate >= %@ AND isUploadedToNS == %@",
-                Date.oneDayAgo as NSDate,
-                false as NSNumber
-            ),
-            key: "startDate",
-            ascending: false
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedOverrideRuns = results as? [OverrideRunStored] else { return [] }
-
-            return fetchedOverrideRuns.map { overrideRun in
-                var durationInMinutes = (overrideRun.endDate?.timeIntervalSince(overrideRun.startDate ?? Date()) ?? 1) / 60
-                durationInMinutes = durationInMinutes < 1 ? 1 : durationInMinutes
-                return NightscoutExercise(
-                    duration: Int(durationInMinutes),
-                    eventType: OverrideStored.EventType.nsExercise,
-                    createdAt: (overrideRun.startDate ?? overrideRun.override?.date) ?? Date(),
-                    enteredBy: NightscoutExercise.local,
-                    notes: overrideRun.name ?? "Custom Override",
-                    id: overrideRun.id
-                )
-            }
-        }
-    }
-
-    func getPresetOverridesForNightscout() async -> [NightscoutPresetOverride] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: OverrideStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate.allOverridePresets,
-            key: "orderPosition",
-            ascending: true
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
-
-            return fetchedResults.map { overrideStored in
-                let duration = overrideStored.duration as? Decimal != 0 ? overrideStored.duration as? Decimal : nil
-                let percentage = overrideStored.percentage != 0 ? overrideStored.percentage : nil
-                let target = (overrideStored.target as? Decimal) != 0 ? overrideStored.target as? Decimal : nil
-
-                return NightscoutPresetOverride(
-                    name: overrideStored.name ?? "",
-                    duration: duration,
-                    percentage: percentage,
-                    target: target
-                )
-            }
-        }
-    }
-}

+ 0 - 535
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -1,535 +0,0 @@
-import Combine
-import CoreData
-import Foundation
-import LoopKit
-import SwiftDate
-import Swinject
-
-protocol PumpHistoryObserver {
-    func pumpHistoryDidUpdate(_ events: [PumpHistoryEvent])
-}
-
-protocol PumpHistoryStorage {
-    var updatePublisher: AnyPublisher<Void, Never> { get }
-    func storePumpEvents(_ events: [NewPumpEvent])
-    func storeExternalInsulinEvent(amount: Decimal, timestamp: Date) async
-    func recent() -> [PumpHistoryEvent]
-    func getPumpHistoryNotYetUploadedToNightscout() async -> [NightscoutTreatment]
-    func getPumpHistoryNotYetUploadedToHealth() async -> [PumpHistoryEvent]
-    func getPumpHistoryNotYetUploadedToTidepool() async -> [PumpHistoryEvent]
-    func deleteInsulin(at date: Date)
-}
-
-final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
-    private let processQueue = DispatchQueue(label: "BasePumpHistoryStorage.processQueue")
-    @Injected() private var storage: FileStorage!
-    @Injected() private var broadcaster: Broadcaster!
-    @Injected() private var settings: SettingsManager!
-
-    private let updateSubject = PassthroughSubject<Void, Never>()
-
-    var updatePublisher: AnyPublisher<Void, Never> {
-        updateSubject.eraseToAnyPublisher()
-    }
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    typealias PumpEvent = PumpEventStored.EventType
-    typealias TempType = PumpEventStored.TempType
-
-    private let context = CoreDataStack.shared.newTaskContext()
-
-    private func roundDose(_ dose: Double, toIncrement increment: Double) -> Decimal {
-        let roundedValue = (dose / increment).rounded() * increment
-        return Decimal(roundedValue)
-    }
-
-    func storePumpEvents(_ events: [NewPumpEvent]) {
-        processQueue.async {
-            self.context.perform {
-                for event in events {
-                    // Fetch to filter out duplicates
-                    // TODO: - move this to the Core Data Class
-
-                    let existingEvents: [PumpEventStored] = CoreDataStack.shared.fetchEntities(
-                        ofType: PumpEventStored.self,
-                        onContext: self.context,
-                        predicate: NSPredicate.duplicateInLastHour(event.date),
-                        key: "timestamp",
-                        ascending: false,
-                        batchSize: 50
-                    )
-
-                    switch event.type {
-                    case .bolus:
-
-                        guard let dose = event.dose else { continue }
-                        let amount = self.roundDose(
-                            dose.unitsInDeliverableIncrements,
-                            toIncrement: Double(self.settings.preferences.bolusIncrement)
-                        )
-
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-
-                            if let existingEvent = existingEvents.first(where: { $0.type == EventType.bolus.rawValue }) {
-                                if existingEvent.timestamp == event.date {
-                                    if let existingAmount = existingEvent.bolus?.amount, amount < existingAmount as Decimal {
-                                        // Update existing event with new smaller value
-                                        existingEvent.bolus?.amount = amount as NSDecimalNumber
-                                        existingEvent.bolus?.isSMB = dose.automatic ?? true
-                                        existingEvent.isUploadedToNS = false
-                                        existingEvent.isUploadedToHealth = false
-                                        existingEvent.isUploadedToTidepool = false
-
-                                        print("Updated existing event with smaller value: \(amount)")
-                                    }
-                                }
-                            }
-                            continue
-                        }
-
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
-                        newPumpEvent.type = PumpEvent.bolus.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-
-                        let newBolusEntry = BolusStored(context: self.context)
-                        newBolusEntry.pumpEvent = newPumpEvent
-                        newBolusEntry.amount = NSDecimalNumber(decimal: amount)
-                        newBolusEntry.isExternal = dose.manuallyEntered
-                        newBolusEntry.isSMB = dose.automatic ?? true
-
-                    case .tempBasal:
-                        guard let dose = event.dose else { continue }
-
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-                            continue
-                        }
-
-                        let rate = Decimal(dose.unitsPerHour)
-                        let minutes = (dose.endDate - dose.startDate).timeInterval / 60
-                        let delivered = dose.deliveredUnits
-                        let date = event.date
-
-                        let isCancel = delivered != nil
-                        guard !isCancel else { continue }
-
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = date
-                        newPumpEvent.type = PumpEvent.tempBasal.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-
-                        let newTempBasal = TempBasalStored(context: self.context)
-                        newTempBasal.pumpEvent = newPumpEvent
-                        newTempBasal.duration = Int16(round(minutes))
-                        newTempBasal.rate = rate as NSDecimalNumber
-                        newTempBasal.tempType = TempType.absolute.rawValue
-
-                    case .suspend:
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-                            continue
-                        }
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
-                        newPumpEvent.type = PumpEvent.pumpSuspend.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-
-                    case .resume:
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-                            continue
-                        }
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
-                        newPumpEvent.type = PumpEvent.pumpResume.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-
-                    case .rewind:
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-                            continue
-                        }
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
-                        newPumpEvent.type = PumpEvent.rewind.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-
-                    case .prime:
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-                            continue
-                        }
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
-                        newPumpEvent.type = PumpEvent.prime.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-
-                    case .alarm:
-                        guard existingEvents.isEmpty else {
-                            // Duplicate found, do not store the event
-                            print("Duplicate event found with timestamp: \(event.date)")
-                            continue
-                        }
-                        let newPumpEvent = PumpEventStored(context: self.context)
-                        newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
-                        newPumpEvent.type = PumpEvent.pumpAlarm.rawValue
-                        newPumpEvent.isUploadedToNS = false
-                        newPumpEvent.isUploadedToHealth = false
-                        newPumpEvent.isUploadedToTidepool = false
-                        newPumpEvent.note = event.title
-
-                    default:
-                        continue
-                    }
-                }
-
-                do {
-                    guard self.context.hasChanges else { return }
-                    try self.context.save()
-
-                    self.updateSubject.send(())
-                    debugPrint("\(DebuggingIdentifiers.succeeded) stored pump events in Core Data")
-                } catch let error as NSError {
-                    debugPrint("\(DebuggingIdentifiers.failed) failed to store pump events with error: \(error.userInfo)")
-                }
-            }
-        }
-    }
-
-    func storeExternalInsulinEvent(amount: Decimal, timestamp: Date) async {
-        debug(.default, "External insulin saved")
-        await context.perform {
-            // create pump event
-            let newPumpEvent = PumpEventStored(context: self.context)
-            newPumpEvent.id = UUID().uuidString
-            newPumpEvent.timestamp = timestamp
-            newPumpEvent.type = PumpEvent.bolus.rawValue
-            newPumpEvent.isUploadedToNS = false
-            newPumpEvent.isUploadedToHealth = false
-            newPumpEvent.isUploadedToTidepool = false
-
-            // create bolus entry and specify relationship to pump event
-            let newBolusEntry = BolusStored(context: self.context)
-            newBolusEntry.pumpEvent = newPumpEvent
-            newBolusEntry.amount = amount as NSDecimalNumber
-            newBolusEntry.isExternal = true // we are creating an external dose
-            newBolusEntry.isSMB = false // the dose is manually administered
-
-            do {
-                guard self.context.hasChanges else { return }
-                try self.context.save()
-
-                self.updateSubject.send(())
-            } catch {
-                print(error.localizedDescription)
-            }
-        }
-    }
-
-    func recent() -> [PumpHistoryEvent] {
-        storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self)?.reversed() ?? []
-    }
-
-    func deleteInsulin(at date: Date) {
-        processQueue.sync {
-            var allValues = storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self) ?? []
-            guard let entryIndex = allValues.firstIndex(where: { $0.timestamp == date }) else {
-                return
-            }
-            allValues.remove(at: entryIndex)
-            storage.save(allValues, as: OpenAPS.Monitor.pumpHistory)
-            broadcaster.notify(PumpHistoryObserver.self, on: processQueue) {
-                $0.pumpHistoryDidUpdate(allValues)
-            }
-        }
-    }
-
-    func determineBolusEventType(for event: PumpEventStored) -> PumpEventStored.EventType {
-        if event.bolus!.isSMB {
-            return .smb
-        }
-        if event.bolus!.isExternal {
-            return .isExternal
-        }
-        return PumpEventStored.EventType(rawValue: event.type!) ?? PumpEventStored.EventType.bolus
-    }
-
-    func getPumpHistoryNotYetUploadedToNightscout() async -> [NightscoutTreatment] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: PumpEventStored.self,
-            onContext: context,
-            predicate: NSPredicate.pumpEventsNotYetUploadedToNightscout,
-            key: "timestamp",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        return await context.perform { [self] in
-            guard let fetchedPumpEvents = results as? [PumpEventStored] else { return [] }
-
-            return fetchedPumpEvents.map { event in
-                switch event.type {
-                case PumpEvent.bolus.rawValue:
-                    // eventType determines whether bolus is external, smb or manual (=administered via app by user)
-                    let eventType = determineBolusEventType(for: event)
-                    return NightscoutTreatment(
-                        duration: nil,
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: nil,
-                        rate: nil,
-                        eventType: eventType,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: event.bolus?.amount as Decimal?,
-                        notes: nil,
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil,
-                        id: event.id
-                    )
-                case PumpEvent.tempBasal.rawValue:
-                    return NightscoutTreatment(
-                        duration: Int(event.tempBasal?.duration ?? 0),
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: event.tempBasal?.rate as Decimal?,
-                        rate: event.tempBasal?.rate as Decimal?,
-                        eventType: .nsTempBasal,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: nil,
-                        notes: nil,
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil,
-                        id: event.id
-                    )
-                case PumpEvent.pumpSuspend.rawValue:
-                    return NightscoutTreatment(
-                        duration: nil,
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: nil,
-                        rate: nil,
-                        eventType: .nsNote,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: nil,
-                        notes: PumpEvent.pumpSuspend.rawValue,
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil
-                    )
-                case PumpEvent.pumpResume.rawValue:
-                    return NightscoutTreatment(
-                        duration: nil,
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: nil,
-                        rate: nil,
-                        eventType: .nsNote,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: nil,
-                        notes: PumpEvent.pumpResume.rawValue,
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil
-                    )
-                case PumpEvent.rewind.rawValue:
-                    return NightscoutTreatment(
-                        duration: nil,
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: nil,
-                        rate: nil,
-                        eventType: .nsInsulinChange,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: nil,
-                        notes: nil,
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil
-                    )
-                case PumpEvent.prime.rawValue:
-                    return NightscoutTreatment(
-                        duration: nil,
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: nil,
-                        rate: nil,
-                        eventType: .nsSiteChange,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: nil,
-                        notes: nil,
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil
-                    )
-                case PumpEvent.pumpAlarm.rawValue:
-                    return NightscoutTreatment(
-                        duration: 30, // minutes
-                        rawDuration: nil,
-                        rawRate: nil,
-                        absolute: nil,
-                        rate: nil,
-                        eventType: .nsAnnouncement,
-                        createdAt: event.timestamp,
-                        enteredBy: NightscoutTreatment.local,
-                        bolus: nil,
-                        insulin: nil,
-                        notes: "Alarm \(String(describing: event.note)) \(PumpEvent.pumpAlarm.rawValue)",
-                        carbs: nil,
-                        fat: nil,
-                        protein: nil,
-                        targetTop: nil,
-                        targetBottom: nil
-                    )
-
-                default:
-                    return nil
-                }
-            }.compactMap { $0 }
-        }
-    }
-
-    func getPumpHistoryNotYetUploadedToHealth() async -> [PumpHistoryEvent] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: PumpEventStored.self,
-            onContext: context,
-            predicate: NSPredicate.pumpEventsNotYetUploadedToHealth,
-            key: "timestamp",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedPumpEvents = results as? [PumpEventStored] else { return [] }
-
-        return await context.perform {
-            fetchedPumpEvents.map { event in
-                switch event.type {
-                case PumpEvent.bolus.rawValue:
-                    return PumpHistoryEvent(
-                        id: event.id ?? UUID().uuidString,
-                        type: .bolus,
-                        timestamp: event.timestamp ?? Date(),
-                        amount: event.bolus?.amount as Decimal?
-                    )
-                case PumpEvent.tempBasal.rawValue:
-                    if let id = event.id, let timestamp = event.timestamp, let tempBasal = event.tempBasal,
-                       let tempBasalRate = tempBasal.rate
-                    {
-                        return PumpHistoryEvent(
-                            id: id,
-                            type: .tempBasal,
-                            timestamp: timestamp,
-                            amount: tempBasalRate as Decimal,
-                            duration: Int(tempBasal.duration)
-                        )
-                    } else {
-                        return nil
-                    }
-                default:
-                    return nil
-                }
-            }.compactMap { $0 }
-        }
-    }
-
-    func getPumpHistoryNotYetUploadedToTidepool() async -> [PumpHistoryEvent] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: PumpEventStored.self,
-            onContext: context,
-            predicate: NSPredicate.pumpEventsNotYetUploadedToTidepool,
-            key: "timestamp",
-            ascending: false,
-            fetchLimit: 288
-        )
-
-        guard let fetchedPumpEvents = results as? [PumpEventStored] else { return [] }
-
-        return await context.perform {
-            fetchedPumpEvents.map { event in
-                switch event.type {
-                case PumpEvent.bolus.rawValue:
-                    return PumpHistoryEvent(
-                        id: event.id ?? UUID().uuidString,
-                        type: .bolus,
-                        timestamp: event.timestamp ?? Date(),
-                        amount: event.bolus?.amount as Decimal?,
-                        isSMB: event.bolus?.isSMB ?? true,
-                        isExternal: event.bolus?.isExternal ?? false
-                    )
-                case PumpEvent.tempBasal.rawValue:
-                    if let id = event.id, let timestamp = event.timestamp, let tempBasal = event.tempBasal,
-                       let tempBasalRate = tempBasal.rate
-                    {
-                        return PumpHistoryEvent(
-                            id: id,
-                            type: .tempBasal,
-                            timestamp: timestamp,
-                            amount: tempBasalRate as Decimal,
-                            duration: Int(tempBasal.duration)
-                        )
-                    } else {
-                        return nil
-                    }
-
-                default:
-                    return nil
-                }
-            }.compactMap { $0 }
-        }
-    }
-}

+ 0 - 119
FreeAPS/Sources/APS/Storage/TempTargetsStorage.swift

@@ -1,119 +0,0 @@
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol TempTargetsObserver {
-    func tempTargetsDidUpdate(_ targets: [TempTarget])
-}
-
-protocol TempTargetsStorage {
-    func storeTempTargets(_ targets: [TempTarget])
-    func syncDate() -> Date
-    func recent() -> [TempTarget]
-    func nightscoutTreatmentsNotUploaded() -> [NightscoutTreatment]
-    func storePresets(_ targets: [TempTarget])
-    func presets() -> [TempTarget]
-    func current() -> TempTarget?
-}
-
-final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseTempTargetsStorage.processQueue")
-    @Injected() private var storage: FileStorage!
-    @Injected() private var broadcaster: Broadcaster!
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    func storeTempTargets(_ targets: [TempTarget]) {
-        storeTempTargets(targets, isPresets: false)
-    }
-
-    private func storeTempTargets(_ targets: [TempTarget], isPresets: Bool) {
-        processQueue.sync {
-            var targets = targets
-            if !isPresets {
-                if current() != nil, let newActive = targets.last(where: {
-                    $0.createdAt.addingTimeInterval(Int($0.duration).minutes.timeInterval) > Date()
-                        && $0.createdAt <= Date()
-                }) {
-                    // cancel current
-                    targets += [TempTarget.cancel(at: newActive.createdAt.addingTimeInterval(-1))]
-                }
-            }
-
-            let file = isPresets ? OpenAPS.FreeAPS.tempTargetsPresets : OpenAPS.Settings.tempTargets
-            var uniqEvents: [TempTarget] = []
-            self.storage.transaction { storage in
-                storage.append(targets, to: file, uniqBy: \.createdAt)
-                uniqEvents = storage.retrieve(file, as: [TempTarget].self)?
-                    .filter {
-                        guard !isPresets else { return true }
-                        return $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date()
-                    }
-                    .sorted { $0.createdAt > $1.createdAt } ?? []
-                storage.save(Array(uniqEvents), as: file)
-            }
-            broadcaster.notify(TempTargetsObserver.self, on: processQueue) {
-                $0.tempTargetsDidUpdate(uniqEvents)
-            }
-        }
-    }
-
-    func syncDate() -> Date {
-        Date().addingTimeInterval(-1.days.timeInterval)
-    }
-
-    func recent() -> [TempTarget] {
-        storage.retrieve(OpenAPS.Settings.tempTargets, as: [TempTarget].self)?.reversed() ?? []
-    }
-
-    func current() -> TempTarget? {
-        guard let last = recent().last else {
-            return nil
-        }
-
-        guard last.createdAt.addingTimeInterval(Int(last.duration).minutes.timeInterval) > Date(), last.createdAt <= Date(),
-              last.duration != 0
-        else {
-            return nil
-        }
-
-        return last
-    }
-
-    func nightscoutTreatmentsNotUploaded() -> [NightscoutTreatment] {
-        let uploaded = storage.retrieve(OpenAPS.Nightscout.uploadedTempTargets, as: [NightscoutTreatment].self) ?? []
-
-        let eventsManual = recent().filter { $0.enteredBy == TempTarget.manual }
-        let treatments = eventsManual.map {
-            NightscoutTreatment(
-                duration: Int($0.duration),
-                rawDuration: nil,
-                rawRate: nil,
-                absolute: nil,
-                rate: nil,
-                eventType: .nsTempTarget,
-                createdAt: $0.createdAt,
-                enteredBy: TempTarget.manual,
-                bolus: nil,
-                insulin: nil,
-                notes: nil,
-                carbs: nil,
-                targetTop: $0.targetTop,
-                targetBottom: $0.targetBottom
-            )
-        }
-        return Array(Set(treatments).subtracting(Set(uploaded)))
-    }
-
-    func storePresets(_ targets: [TempTarget]) {
-        storage.remove(OpenAPS.FreeAPS.tempTargetsPresets)
-
-        storeTempTargets(targets, isPresets: true)
-    }
-
-    func presets() -> [TempTarget] {
-        storage.retrieve(OpenAPS.FreeAPS.tempTargetsPresets, as: [TempTarget].self)?.reversed() ?? []
-    }
-}

+ 0 - 54
FreeAPS/Sources/Application/AppDelegate.swift

@@ -1,54 +0,0 @@
-import SwiftUI
-import UIKit
-import UserNotifications
-
-class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject, UNUserNotificationCenterDelegate {
-    func application(
-        _ application: UIApplication,
-        didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?
-    ) -> Bool {
-        UNUserNotificationCenter.current().delegate = self
-        application.registerForRemoteNotifications()
-        return true
-    }
-
-    func application(
-        _: UIApplication,
-        didReceiveRemoteNotification userInfo: [AnyHashable: Any],
-        fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
-    ) {
-        debug(.remoteControl, "Received notification")
-
-        do {
-            let jsonData = try JSONSerialization.data(withJSONObject: userInfo)
-            let pushMessage = try JSONDecoder().decode(PushMessage.self, from: jsonData)
-
-            Task {
-                await TrioRemoteControl.shared.handleRemoteNotification(pushMessage: pushMessage)
-                completionHandler(.newData)
-            }
-        } catch {
-            debug(.remoteControl, "Error decoding push message: \(error.localizedDescription)")
-            completionHandler(.failed)
-        }
-    }
-
-    func application(
-        _: UIApplication,
-        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
-    ) {
-        let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
-        let token = tokenParts.joined()
-
-        Task {
-            await TrioRemoteControl.shared.handleAPNSChanges(deviceToken: token)
-        }
-    }
-
-    func application(
-        _: UIApplication,
-        didFailToRegisterForRemoteNotificationsWithError error: Error
-    ) {
-        debug(.remoteControl, "Failed to register for remote notifications: \(error.localizedDescription)")
-    }
-}

+ 0 - 15
FreeAPS/Sources/Assemblies/APSAssembly.swift

@@ -1,15 +0,0 @@
-import Foundation
-import Swinject
-
-final class APSAssembly: Assembly {
-    func assemble(container: Container) {
-        container.register(DeviceDataManager.self) { r in BaseDeviceDataManager(resolver: r) }
-        container.register(APSManager.self) { r in BaseAPSManager(resolver: r) }
-        container.register(FetchGlucoseManager.self) { r in BaseFetchGlucoseManager(resolver: r) }
-        container.register(FetchTreatmentsManager.self) { r in BaseFetchTreatmentsManager(resolver: r) }
-        container.register(FetchAnnouncementsManager.self) { r in BaseFetchAnnouncementsManager(resolver: r) }
-        container.register(BluetoothStateManager.self) { r in BaseBluetoothStateManager(resolver: r) }
-        container.register(PluginManager.self) { r in BasePluginManager(resolver: r) }
-        container.register(CalibrationService.self) { r in BaseCalibrationService(resolver: r) }
-    }
-}

+ 0 - 30
FreeAPS/Sources/Assemblies/ServiceAssembly.swift

@@ -1,30 +0,0 @@
-import Foundation
-import HealthKit
-import Swinject
-
-final class ServiceAssembly: Assembly {
-    func assemble(container: Container) {
-        container.register(NotificationCenter.self) { _ in Foundation.NotificationCenter.default }
-        container.register(Broadcaster.self) { _ in BaseBroadcaster() }
-        container.register(GroupedIssueReporter.self) { _ in
-            let reporter = CollectionIssueReporter()
-            reporter.add(reporters: [
-                SimpleLogReporter()
-            ])
-            reporter.setup()
-            return reporter
-        }
-        container.register(CalendarManager.self) { r in BaseCalendarManager(resolver: r) }
-        container.register(HKHealthStore.self) { _ in HKHealthStore() }
-        container.register(HealthKitManager.self) { r in BaseHealthKitManager(resolver: r) }
-        container.register(UserNotificationsManager.self) { r in BaseUserNotificationsManager(resolver: r) }
-        container.register(WatchManager.self) { r in BaseWatchManager(resolver: r) }
-        container.register(GarminManager.self) { r in BaseGarminManager(resolver: r) }
-
-        if #available(iOS 16.2, *) {
-            container.register(LiveActivityBridge.self) { r in
-                LiveActivityBridge(resolver: r)
-            }
-        }
-    }
-}

+ 0 - 21
FreeAPS/Sources/Assemblies/StorageAssembly.swift

@@ -1,21 +0,0 @@
-import Foundation
-import Swinject
-
-final class StorageAssembly: Assembly {
-    func assemble(container: Container) {
-        container.register(FileManager.self) { _ in
-            Foundation.FileManager.default
-        }
-        container.register(FileStorage.self) { _ in BaseFileStorage() }
-        container.register(PumpHistoryStorage.self) { r in BasePumpHistoryStorage(resolver: r) }
-        container.register(OverrideStorage.self) { r in BaseOverrideStorage(resolver: r) }
-        container.register(DeterminationStorage.self) { r in BaseDeterminationStorage(resolver: r) }
-        container.register(GlucoseStorage.self) { r in BaseGlucoseStorage(resolver: r) }
-        container.register(TempTargetsStorage.self) { r in BaseTempTargetsStorage(resolver: r) }
-        container.register(CarbsStorage.self) { r in BaseCarbsStorage(resolver: r) }
-        container.register(AnnouncementsStorage.self) { r in BaseAnnouncementsStorage(resolver: r) }
-        container.register(SettingsManager.self) { r in BaseSettingsManager(resolver: r) }
-        container.register(Keychain.self) { _ in BaseKeychain() }
-        container.register(AlertHistoryStorage.self) { r in BaseAlertHistoryStorage(resolver: r) }
-    }
-}

+ 0 - 9
FreeAPS/Sources/Config/Config.swift

@@ -1,9 +0,0 @@
-import Foundation
-import SwiftDate
-
-enum Config {
-    static let treatWarningsAsErrors = true
-    static let withSignPosts = false
-    static let loopInterval = 4.minutes.timeInterval
-    static let eхpirationInterval = 10.minutes.timeInterval
-}

+ 0 - 83
FreeAPS/Sources/Helpers/BuildDetails.swift

@@ -1,83 +0,0 @@
-//
-//  BuildDetails.swift
-//  Trio
-//
-//  Created by Jonas Björkert on 2024-05-09.
-//
-import Foundation
-
-class BuildDetails {
-    static var `default` = BuildDetails()
-
-    let dict: [String: Any]
-
-    init() {
-        guard let url = Bundle.main.url(forResource: "BuildDetails", withExtension: "plist"),
-              let data = try? Data(contentsOf: url),
-              let parsed = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any]
-        else {
-            dict = [:]
-            return
-        }
-        dict = parsed
-    }
-
-    var buildDateString: String? {
-        dict["com-trio-build-date"] as? String
-    }
-
-    var branchAndSha: String {
-        let branch = dict["com-trio-branch"] as? String ?? "Unknown"
-        let sha = dict["com-trio-commit-sha"] as? String ?? "Unknown"
-        return "\(branch) \(sha)"
-    }
-
-    // Determine if the build is from TestFlight
-    func isTestFlightBuild() -> Bool {
-        #if targetEnvironment(simulator)
-            return false
-        #else
-            if Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision") != nil {
-                return false
-            }
-            guard let receiptName = Bundle.main.appStoreReceiptURL?.lastPathComponent else {
-                return false
-            }
-            return "sandboxReceipt".caseInsensitiveCompare(receiptName) == .orderedSame
-        #endif
-    }
-
-    // Parse the build date string into a Date object
-    func buildDate() -> Date? {
-        let dateFormatter = DateFormatter()
-        dateFormatter.dateFormat = "EEE MMM d HH:mm:ss 'UTC' yyyy"
-        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
-        dateFormatter.timeZone = TimeZone(identifier: "UTC")
-
-        guard let dateString = buildDateString,
-              let date = dateFormatter.date(from: dateString)
-        else {
-            return nil
-        }
-        return date
-    }
-
-    // Calculate the expiration date based on the build type
-    func calculateExpirationDate() -> Date? {
-        if isTestFlightBuild(), let buildDate = buildDate() {
-            // For TestFlight, add 90 days to the build date
-            return Calendar.current.date(byAdding: .day, value: 90, to: buildDate)!
-        } else {
-            return Bundle.main.profileExpirationDate
-        }
-    }
-
-    // Expiration header based on build type
-    var expirationHeaderString: String {
-        if isTestFlightBuild() {
-            return "Beta (TestFlight) Expires"
-        } else {
-            return "App Expires"
-        }
-    }
-}

+ 0 - 52
FreeAPS/Sources/Helpers/CustomProgressView.swift

@@ -1,52 +0,0 @@
-import SwiftUI
-
-struct CustomProgressView: View {
-    @State var animate = false
-
-    let text: String
-
-    @Environment(\.colorScheme) var colorScheme
-
-    var body: some View {
-        ZStack {
-            Text(text)
-                .font(.system(.body, design: .rounded))
-                .bold()
-                .offset(x: 0, y: -25)
-
-            RoundedRectangle(cornerRadius: 3)
-                .stroke(Color(.systemGray5), lineWidth: 3)
-                .frame(width: 250, height: 3)
-
-            RoundedRectangle(cornerRadius: 3)
-                .stroke(LinearGradient(colors: [
-                    Color(red: 0.7215686275, green: 0.3411764706, blue: 1),
-                    Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569),
-                    Color(red: 0.4862745098, green: 0.5450980392, blue: 0.9529411765),
-                    Color(red: 0.3411764706, green: 0.6666666667, blue: 0.9254901961),
-                    Color(red: 0.262745098, green: 0.7333333333, blue: 0.9137254902)
-                ], startPoint: .leading, endPoint: .trailing), lineWidth: 3)
-                .frame(width: 250, height: 3)
-                .mask(
-                    RoundedRectangle(cornerRadius: 3)
-                        .frame(width: 80, height: 3)
-                        .offset(x: self.animate ? 180 : -180, y: 0)
-                        .animation(
-                            Animation.linear(duration: 2)
-                                .repeatForever(autoreverses: false), value: UUID()
-                        )
-                )
-        }
-        .onAppear {
-            self.animate.toggle()
-        }
-    }
-}
-
-enum ProgressText: String {
-    case updatingIOB = "Updating IOB ..."
-    case updatingCOB = "Updating COB ..."
-    case updatingHistory = "Updating History ..."
-    case updatingTreatments = "Updating Treatments ..."
-    case updatingIOBandCOB = "Updating IOB and COB ..."
-}

+ 0 - 20
FreeAPS/Sources/Helpers/Decimal+Extensions.swift

@@ -1,20 +0,0 @@
-import CoreGraphics
-import Foundation
-
-extension Double {
-    init(_ decimal: Decimal) {
-        self.init(truncating: decimal as NSNumber)
-    }
-}
-
-extension Int {
-    init(_ decimal: Decimal) {
-        self.init(Double(decimal))
-    }
-}
-
-extension CGFloat {
-    init(_ decimal: Decimal) {
-        self.init(Double(decimal))
-    }
-}

+ 0 - 49
FreeAPS/Sources/Helpers/Formatters.swift

@@ -1,49 +0,0 @@
-import Foundation
-import HealthKit
-
-enum Formatters {
-    static func percent(for number: Double) -> String {
-        let formater = NumberFormatter()
-        formater.numberStyle = .percent
-        return formater.string(for: number)!
-    }
-
-    static func timeFor(minutes: Int) -> String {
-        let formater = DateComponentsFormatter()
-        formater.unitsStyle = .abbreviated
-        formater.allowedUnits = [.hour, .minute]
-        return formater.string(from: TimeInterval(minutes * 60))!
-    }
-}
-
-extension Formatter {
-    static let iso8601withFractionalSeconds: ISO8601DateFormatter = {
-        let formatter = ISO8601DateFormatter()
-        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
-        return formatter
-    }()
-
-    static let iso8601: ISO8601DateFormatter = {
-        let formatter = ISO8601DateFormatter()
-        formatter.formatOptions = [.withInternetDateTime]
-        return formatter
-    }()
-}
-
-extension JSONDecoder.DateDecodingStrategy {
-    static let customISO8601 = custom {
-        let container = try $0.singleValueContainer()
-        let string = try container.decode(String.self)
-        if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ?? Formatter.iso8601.date(from: string) {
-            return date
-        }
-        throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
-    }
-}
-
-extension JSONEncoder.DateEncodingStrategy {
-    static let customISO8601 = custom {
-        var container = $1.singleValueContainer()
-        try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0))
-    }
-}

+ 0 - 58
FreeAPS/Sources/Helpers/HKUnit.swift

@@ -1,58 +0,0 @@
-import HealthKit
-
-extension HKUnit {
-    static let milligramsPerDeciliter: HKUnit = {
-        HKUnit.gramUnit(with: .milli).unitDivided(by: .literUnit(with: .deci))
-    }()
-
-    static let millimolesPerLiter: HKUnit = {
-        HKUnit.moleUnit(with: .milli, molarMass: HKUnitMolarMassBloodGlucose).unitDivided(by: .liter())
-    }()
-
-    static let internationalUnitsPerHour: HKUnit = {
-        HKUnit.internationalUnit().unitDivided(by: .hour())
-    }()
-
-    static let gramsPerUnit: HKUnit = {
-        HKUnit.gram().unitDivided(by: .internationalUnit())
-    }()
-
-    var foundationUnit: Unit? {
-        if self == HKUnit.milligramsPerDeciliter {
-            return UnitConcentrationMass.milligramsPerDeciliter
-        }
-
-        if self == HKUnit.millimolesPerLiter {
-            return UnitConcentrationMass.millimolesPerLiter(withGramsPerMole: HKUnitMolarMassBloodGlucose)
-        }
-
-        if self == HKUnit.gram() {
-            return UnitMass.grams
-        }
-
-        return nil
-    }
-
-    /// The smallest value expected to be visible on a chart
-    var chartableIncrement: Double {
-        if self == .milligramsPerDeciliter {
-            return 1
-        } else {
-            return 1 / 25
-        }
-    }
-
-    var localizedShortUnitString: String {
-        if self == HKUnit.millimolesPerLiter {
-            return NSLocalizedString("mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
-        } else if self == .milligramsPerDeciliter {
-            return NSLocalizedString("mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
-        } else if self == .internationalUnit() {
-            return NSLocalizedString("U", comment: "The short unit display string for international units of insulin")
-        } else if self == .gram() {
-            return NSLocalizedString("g", comment: "The short unit display string for grams")
-        } else {
-            return String(describing: self)
-        }
-    }
-}

+ 0 - 99
FreeAPS/Sources/Helpers/MainChartHelper.swift

@@ -1,99 +0,0 @@
-import CoreData
-import Foundation
-
-enum MainChartHelper {
-    // Calculates the glucose value thats the nearest to parameter 'time'
-    /// -Returns: A NSManagedObject of GlucoseStored
-    /// it is thread safe as everything is executed on the main thread
-    static func timeToNearestGlucose(glucoseValues: [GlucoseStored], time: TimeInterval) -> GlucoseStored? {
-        guard !glucoseValues.isEmpty else {
-            return nil
-        }
-
-        var low = 0
-        var high = glucoseValues.count - 1
-        var closestGlucose: GlucoseStored?
-
-        // binary search to find next glucose
-        while low <= high {
-            let mid = low + (high - low) / 2
-            let midTime = glucoseValues[mid].date?.timeIntervalSince1970 ?? 0
-
-            if midTime == time {
-                return glucoseValues[mid]
-            } else if midTime < time {
-                low = mid + 1
-            } else {
-                high = mid - 1
-            }
-
-            // update if necessary
-            if closestGlucose == nil || abs(midTime - time) < abs(closestGlucose!.date?.timeIntervalSince1970 ?? 0 - time) {
-                closestGlucose = glucoseValues[mid]
-            }
-        }
-
-        return closestGlucose
-    }
-
-    enum Config {
-        static let bolusSize: CGFloat = 5
-        static let bolusScale: CGFloat = 1.8
-        static let carbsSize: CGFloat = 5
-        static let maxCarbSize: CGFloat = 30
-        static let carbsScale: CGFloat = 0.3
-        static let fpuSize: CGFloat = 10
-        static let maxGlucose = 270
-        static let minGlucose = 45
-    }
-
-    static var bolusFormatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.minimumIntegerDigits = 0
-        formatter.maximumFractionDigits = 2
-        formatter.decimalSeparator = "."
-        return formatter
-    }
-
-    static var carbsFormatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 0
-        return formatter
-    }
-
-    static func bolusOffset(units: GlucoseUnits) -> Decimal {
-        units == .mgdL ? 30 : 1.66
-    }
-
-    static func calculateDuration(objectID: NSManagedObjectID, context: NSManagedObjectContext) -> TimeInterval? {
-        do {
-            if let override = try context.existingObject(with: objectID) as? OverrideStored,
-               let overrideDuration = override.duration as? Double, overrideDuration != 0
-            {
-                return TimeInterval(overrideDuration * 60) // return seconds
-            }
-        } catch {
-            debugPrint(
-                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to calculate Override Target with error: \(error.localizedDescription)"
-            )
-        }
-        return nil
-    }
-
-    static func calculateTarget(objectID: NSManagedObjectID, context: NSManagedObjectContext) -> Decimal? {
-        do {
-            if let override = try context.existingObject(with: objectID) as? OverrideStored,
-               let overrideTarget = override.target, overrideTarget != 0
-            {
-                return overrideTarget.decimalValue
-            }
-        } catch {
-            debugPrint(
-                "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to calculate Override Target with error: \(error.localizedDescription)"
-            )
-        }
-        return nil
-    }
-}

+ 0 - 25
FreeAPS/Sources/Helpers/ProgressBar.swift

@@ -1,25 +0,0 @@
-import SwiftUI
-
-struct ProgressBar: View {
-    @Binding var value: Float
-
-    var body: some View {
-        GeometryReader { geometry in
-            ZStack(alignment: .leading) {
-                Capsule(style: .circular)
-                    .frame(width: geometry.size.width, height: geometry.size.height)
-                    .opacity(0.3)
-                    .foregroundColor(.secondary)
-
-                Capsule(style: .circular)
-                    .frame(
-                        width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width),
-                        height: geometry.size.height
-                    )
-                    .foregroundColor(.accentColor)
-                    .animation(.linear)
-            }
-        }
-        .frame(height: 20)
-    }
-}

ファイルの差分が大きいため隠しています
+ 0 - 2229
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 1923
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2190
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2235
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2237
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2234
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2235
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2226
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2229
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


ファイルの差分が大きいため隠しています
+ 0 - 2184
FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings


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


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません