Просмотр исходного кода

Merge branch 'alpha' into update_browser_build_directions

marionbarker 1 год назад
Родитель
Сommit
9b177a22c2

+ 2 - 0
.github/CODEOWNERS

@@ -0,0 +1,2 @@
+*    @dnzxy @bjornoleh @MikePlante1 @aug0211 @AndreasStokholm @Sjoerd-Bo3 @t1dude
+*.js @dnzxy @bjornoleh @MikePlante1 @aug0211 @AndreasStokholm @Sjoerd-Bo3 @t1dude @jeremystorring

+ 20 - 19
.gitmodules

@@ -1,39 +1,40 @@
 [submodule "LoopKit"]
 [submodule "LoopKit"]
 	path = LoopKit
 	path = LoopKit
-	url = https://github.com/LoopKit/LoopKit.git
-	branch = dev
+	url = https://github.com/loopandlearn/LoopKit.git
+	branch = trio
 [submodule "CGMBLEKit"]
 [submodule "CGMBLEKit"]
 	path = CGMBLEKit
 	path = CGMBLEKit
-	url = https://github.com/LoopKit/CGMBLEKit.git
-	branch = dev
+	url = https://github.com/loopandlearn/CGMBLEKit.git
+	branch = trio
 [submodule "dexcom-share-client-swift"]
 [submodule "dexcom-share-client-swift"]
 	path = dexcom-share-client-swift
 	path = dexcom-share-client-swift
-	url = https://github.com/LoopKit/dexcom-share-client-swift.git
-	branch = dev
+	url = https://github.com/loopandlearn/dexcom-share-client-swift.git
+	branch = trio
 [submodule "RileyLinkKit"]
 [submodule "RileyLinkKit"]
 	path = RileyLinkKit
 	path = RileyLinkKit
-	url = https://github.com/LoopKit/RileyLinkKit
-	branch = dev
+	url = https://github.com/loopandlearn/RileyLinkKit
+	branch = trio
 [submodule "OmniBLE"]
 [submodule "OmniBLE"]
 	path = OmniBLE
 	path = OmniBLE
-	url = https://github.com/LoopKit/OmniBLE.git
-	branch = dev
+	url = https://github.com/loopandlearn/OmniBLE.git
+	branch = trio
 [submodule "G7SensorKit"]
 [submodule "G7SensorKit"]
 	path = G7SensorKit
 	path = G7SensorKit
-	url = https://github.com/LoopKit/G7SensorKit.git
-	branch = main
+	url = https://github.com/loopandlearn/G7SensorKit.git
+	branch = trio
 [submodule "OmniKit"]
 [submodule "OmniKit"]
 	path = OmniKit
 	path = OmniKit
-	url = https://github.com/LoopKit/OmniKit.git
-	branch = main
+	url = https://github.com/loopandlearn/OmniKit.git
+	branch = trio
 [submodule "MinimedKit"]
 [submodule "MinimedKit"]
 	path = MinimedKit
 	path = MinimedKit
-	url = https://github.com/LoopKit/MinimedKit.git
-	branch = main
+	url = https://github.com/loopandlearn/MinimedKit.git
+	branch = trio
 [submodule "LibreTransmitter"]
 [submodule "LibreTransmitter"]
 	path = LibreTransmitter
 	path = LibreTransmitter
-	url = https://github.com/LoopKit/LibreTransmitter.git
-	branch = main
+	url = https://github.com/loopandlearn/LibreTransmitter.git
+	branch = trio
 [submodule "TidepoolService"]
 [submodule "TidepoolService"]
 	path = TidepoolService
 	path = TidepoolService
-	url = https://github.com/LoopKit/TidepoolService.git
+	url = https://github.com/loopandlearn/TidepoolService.git
+	branch = trio

+ 30 - 22
FreeAPS/Sources/APS/CGM/PluginSource.swift

@@ -24,7 +24,26 @@ final class PluginSource: GlucoseSource {
         cgmManager?.cgmManagerDelegate = self
         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> {
     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
         Future<[BloodGlucose], Error> { [weak self] promise in
             self?.promise = promise
             self?.promise = promise
         }
         }
@@ -35,17 +54,15 @@ final class PluginSource: GlucoseSource {
     }
     }
 
 
     func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
     func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { _ in
+        Future<[BloodGlucose], Error> { [weak self] promise in
+            guard let self = self else { return }
             self.processQueue.async {
             self.processQueue.async {
                 guard let cgmManager = self.cgmManager else { return }
                 guard let cgmManager = self.cgmManager else { return }
                 cgmManager.fetchNewDataIfNeeded { result in
                 cgmManager.fetchNewDataIfNeeded { result in
-                    self.processCGMReadingResult(cgmManager, readingResult: result) {
-                        // nothing to do
-                    }
+                    promise(self.readCGMResult(readingResult: result))
                 }
                 }
             }
             }
         }
         }
-        .timeout(60, scheduler: processQueue, options: nil, customError: nil)
         .replaceError(with: [])
         .replaceError(with: [])
         .replaceEmpty(with: [])
         .replaceEmpty(with: [])
         .eraseToAnyPublisher()
         .eraseToAnyPublisher()
@@ -92,11 +109,10 @@ extension PluginSource: CGMManagerDelegate {
         glucoseManager?.cgmGlucoseSourceType = .none
         glucoseManager?.cgmGlucoseSourceType = .none
     }
     }
 
 
-    func cgmManager(_ manager: CGMManager, hasNew readingResult: CGMReadingResult) {
+    func cgmManager(_: CGMManager, hasNew readingResult: CGMReadingResult) {
         dispatchPrecondition(condition: .onQueue(processQueue))
         dispatchPrecondition(condition: .onQueue(processQueue))
-        processCGMReadingResult(manager, readingResult: readingResult) {
-            debug(.deviceManager, "CGM PLUGIN - Direct return done")
-        }
+        promise?(readCGMResult(readingResult: readingResult))
+        debug(.deviceManager, "CGM PLUGIN - Direct return done")
     }
     }
 
 
     func cgmManager(_: LoopKit.CGMManager, hasNew events: [LoopKit.PersistedCgmEvent]) {
     func cgmManager(_: LoopKit.CGMManager, hasNew events: [LoopKit.PersistedCgmEvent]) {
@@ -140,11 +156,7 @@ extension PluginSource: CGMManagerDelegate {
         }
         }
     }
     }
 
 
-    private func processCGMReadingResult(
-        _: CGMManager,
-        readingResult: CGMReadingResult,
-        completion: @escaping () -> Void
-    ) {
+    private func readCGMResult(readingResult: CGMReadingResult) -> Result<[BloodGlucose], Error> {
         debug(.deviceManager, "PLUGIN CGM - Process CGM Reading Result launched with \(readingResult)")
         debug(.deviceManager, "PLUGIN CGM - Process CGM Reading Result launched with \(readingResult)")
         switch readingResult {
         switch readingResult {
         case let .newData(values):
         case let .newData(values):
@@ -177,18 +189,14 @@ extension PluginSource: CGMManagerDelegate {
                     transmitterID: sensorTransmitterID
                     transmitterID: sensorTransmitterID
                 )
                 )
             }
             }
-            promise?(.success(bloodGlucose))
-            completion()
+            return .success(bloodGlucose)
         case .unreliableData:
         case .unreliableData:
             // loopManager.receivedUnreliableCGMReading()
             // loopManager.receivedUnreliableCGMReading()
-            promise?(.failure(GlucoseDataError.unreliableData))
-            completion()
+            return .failure(GlucoseDataError.unreliableData)
         case .noData:
         case .noData:
-            promise?(.failure(GlucoseDataError.noData))
-            completion()
+            return .failure(GlucoseDataError.noData)
         case let .error(error):
         case let .error(error):
-            promise?(.failure(error))
-            completion()
+            return .failure(error)
         }
         }
     }
     }
 }
 }

+ 12 - 12
FreeAPS/Sources/Modules/Stat/View/ChartsView.swift

@@ -114,7 +114,7 @@ struct ChartsView: View {
                 type: NSLocalizedString(
                 type: NSLocalizedString(
                     "Low",
                     "Low",
                     comment: ""
                     comment: ""
-                ) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
+                ) + " (<\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
                 percent: fetched[0].decimal
                 percent: fetched[0].decimal
             ),
             ),
             .init(type: NSLocalizedString("In Range", comment: ""), percent: fetched[1].decimal),
             .init(type: NSLocalizedString("In Range", comment: ""), percent: fetched[1].decimal),
@@ -122,7 +122,7 @@ struct ChartsView: View {
                 type: NSLocalizedString(
                 type: NSLocalizedString(
                     "High",
                     "High",
                     comment: ""
                     comment: ""
-                ) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
+                ) + " (>\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
                 percent: fetched[2].decimal
                 percent: fetched[2].decimal
             )
             )
         ]
         ]
@@ -142,12 +142,12 @@ struct ChartsView: View {
             NSLocalizedString(
             NSLocalizedString(
                 "Low",
                 "Low",
                 comment: ""
                 comment: ""
-            ) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
+            ) + " (<\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
             NSLocalizedString("In Range", comment: ""): .green,
             NSLocalizedString("In Range", comment: ""): .green,
             NSLocalizedString(
             NSLocalizedString(
                 "High",
                 "High",
                 comment: ""
                 comment: ""
-            ) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
+            ) + " (>\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
         ]).frame(maxHeight: 25)
         ]).frame(maxHeight: 25)
     }
     }
 
 
@@ -161,18 +161,18 @@ struct ChartsView: View {
                 type: NSLocalizedString(
                 type: NSLocalizedString(
                     "Low",
                     "Low",
                     comment: ""
                     comment: ""
-                ) + " ( \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
+                ) + " (< \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
                 percent: fetched[0].decimal
                 percent: fetched[0].decimal
             ),
             ),
             .init(
             .init(
-                type: "> \(low.formatted(.number.precision(.fractionLength(fraction)))) - < \(high.formatted(.number.precision(.fractionLength(fraction))))",
+                type: "\(low.formatted(.number.precision(.fractionLength(fraction)))) - \(high.formatted(.number.precision(.fractionLength(fraction))))",
                 percent: fetched[1].decimal
                 percent: fetched[1].decimal
             ),
             ),
             .init(
             .init(
                 type: NSLocalizedString(
                 type: NSLocalizedString(
                     "High",
                     "High",
                     comment: ""
                     comment: ""
-                ) + " ( \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
+                ) + " (> \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
                 percent: fetched[2].decimal
                 percent: fetched[2].decimal
             )
             )
         ]
         ]
@@ -196,12 +196,12 @@ struct ChartsView: View {
             NSLocalizedString(
             NSLocalizedString(
                 "Low",
                 "Low",
                 comment: ""
                 comment: ""
-            ) + " ( \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
-            "> \(low.formatted(.number.precision(.fractionLength(fraction)))) - < \(high.formatted(.number.precision(.fractionLength(fraction))))": .green,
+            ) + " (< \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
+            "\(low.formatted(.number.precision(.fractionLength(fraction)))) - \(high.formatted(.number.precision(.fractionLength(fraction))))": .green,
             NSLocalizedString(
             NSLocalizedString(
                 "High",
                 "High",
                 comment: ""
                 comment: ""
-            ) + " ( \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
+            ) + " (> \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
         ])
         ])
     }
     }
 
 
@@ -276,11 +276,11 @@ struct ChartsView: View {
         let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) })
         let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) })
         let totalReadings = justGlucoseArray.count
         let totalReadings = justGlucoseArray.count
 
 
-        let hyperArray = glucose.filter({ $0.glucose >= hyperLimit })
+        let hyperArray = glucose.filter({ $0.glucose > hyperLimit })
         let hyperReadings = hyperArray.compactMap({ each in each.glucose as Int16 }).count
         let hyperReadings = hyperArray.compactMap({ each in each.glucose as Int16 }).count
         let hyperPercentage = Double(hyperReadings) / Double(totalReadings) * 100
         let hyperPercentage = Double(hyperReadings) / Double(totalReadings) * 100
 
 
-        let hypoArray = glucose.filter({ $0.glucose <= hypoLimit })
+        let hypoArray = glucose.filter({ $0.glucose < hypoLimit })
         let hypoReadings = hypoArray.compactMap({ each in each.glucose as Int16 }).count
         let hypoReadings = hypoArray.compactMap({ each in each.glucose as Int16 }).count
         let hypoPercentage = Double(hypoReadings) / Double(totalReadings) * 100
         let hypoPercentage = Double(hypoReadings) / Double(totalReadings) * 100
 
 

+ 68 - 0
fastlane/testflight.md

@@ -16,6 +16,7 @@ These instructions allow you to build Trio without having access to a Mac.
 > 
 > 
 > It also creates an alive branch, if you don't already have one. See [Why do I have an alive branch?](#why-do-i-have-an-alive-branch).
 > It also creates an alive branch, if you don't already have one. See [Why do I have an alive branch?](#why-do-i-have-an-alive-branch).
 >
 >
+> The [**Optional**](#optional) section provides instructions to modify the default behavior if desired. >
 
 
 ## Introduction
 ## Introduction
 
 
@@ -192,3 +193,70 @@ If a GitHub repository has no activity (no commits are made) in 60 days, then Gi
 The `build_trio.yml` file uses a special branch called `alive` and adds a dummy commit to the `alive` branch at regular intervals. This "trick" keeps the Actions enabled so the automated build works.
 The `build_trio.yml` file uses a special branch called `alive` and adds a dummy commit to the `alive` branch at regular intervals. This "trick" keeps the Actions enabled so the automated build works.
 
 
 The branch `alive` is created automatically for you. Do not delete or rename it! Do not modify `alive` yourself; it is not used for building the app.
 The branch `alive` is created automatically for you. Do not delete or rename it! Do not modify `alive` yourself; it is not used for building the app.
+
+## OPTIONAL
+
+What if you don't want to allow automated updates of the repository or automatic builds?
+
+You can affect the default behavior:
+
+1. [`GH_PAT` `workflow` permission](#gh_pat-workflow-permission)
+1. [Modify scheduled building and synchronization](#modify-scheduled-building-and-synchronization)
+
+### `GH_PAT` `workflow` permission
+
+To enable the scheduled build and sync, the `GH_PAT` must hold the `workflow` permission scopes. This permission serves as the enabler for automatic and scheduled builds with browser build. To verify your token holds this permission, follow these steps.
+
+1. Go to your [FastLane Access Token](https://github.com/settings/tokens)
+2. It should say `repo`, `workflow` next to the `FastLane Access Token` link
+3. If it does not, click on the link to open the token detail view
+4. Click to check the `workflow` box. You will see that the checked boxes for the `repo` scope become disabled (change color to dark gray and are not clickable)
+5. Scroll all the way down to and click the green `Update token` button
+6. Your token now holds both required permissions
+
+If you choose not to have automatic building enabled, be sure the `GH_PAT` has `repo` scope or you won't be able to manually build.
+
+### Modify scheduled building and synchronization
+
+You can modify the automation by creating and using some variables.
+
+To configure the automated build more granularly involves creating up to two environment variables: `SCHEDULED_BUILD` and/or `SCHEDULED_SYNC`. See [How to configure a variable](#how-to-configure-a-variable). 
+
+Note that the weekly and monthly Build Trio actions will continue, but the actions are modified if one or more of these variables is set to false. **A successful Action Log will still appear, even if no automatic activity happens**.
+
+* If you want to manually decide when to update your repository to the latest commit, but you want the monthly builds and keep-alive to continue: set `SCHEDULED_SYNC` to false and either do not create `SCHEDULED_BUILD` or set it to true
+* If you want to only build when an update has been found: set `SCHEDULED_BUILD` to false and either do not create `SCHEDULED_SYNC` or set it to true
+    * **Warning**: if no updates to your default branch are detected within 90 days, your previous TestFlight build may expire requiring a manual build
+
+|`SCHEDULED_SYNC`|`SCHEDULED_BUILD`|Automatic Actions|
+|---|---|---|
+| `true` (or NA) | `true` (or NA) | keep-alive, weekly update check (auto update/build), monthly build with auto update|
+| `true` (or NA) | `false` | keep-alive, weekly update check with auto update, only builds if update detected|
+| `false` | `true` (or NA) | keep-alive, monthly build, no auto update |
+| `false` | `false` | no automatic activity, no keep-alive|
+
+### How to configure a variable
+
+1. Go to the "Settings" tab of your Trio repository.
+2. Click on `Secrets and Variables`.
+3. Click on `Actions`
+4. You will now see a page titled *Actions secrets and variables*. Click on the `Variables` tab
+5. To disable ONLY scheduled building, do the following:
+    - Click on the green `New repository variable` button (upper right)
+    - Type `SCHEDULED_BUILD` in the "Name" field
+    - Type `false` in the "Value" field
+    - Click the green `Add variable` button to save.
+7. To disable scheduled syncing, add a variable:
+    - Click on the green `New repository variable` button (upper right)
+    - - Type `SCHEDULED_SYNC` in the "Name" field
+    - Type `false` in the "Value" field
+    - Click the green `Add variable` button to save
+  
+Your build will run on the following conditions:
+- Default behaviour:
+    - Run weekly, every Wednesday at 08:00 UTC to check for changes; if there are changes, it will update your repository and build
+    - Run monthly, every first of the month at 06:00 UTC, if there are changes, it will update your repository; regardless of changes, it will build
+    - Each time the action runs, it makes a keep-alive commit to the `alive` branch if necessary
+- If you disable any automation (both variables set to `false`), no updates, keep-alive or building happens when Build Trio runs
+- If you disabled just scheduled synchronization (`SCHEDULED_SYNC` set to`false`), it will only run once a month, on the first of the month, no update will happen; keep-alive will run
+- If you disabled just scheduled build (`SCHEDULED_BUILD` set to`false`), it will run once weekly, every Wednesday, to check for changes; if there are changes, it will update and build; keep-alive will run