Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alpha #21

Merged
merged 10 commits into from
Jun 17, 2024
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* @dnzxy @bjornoleh @MikePlante1 @aug0211 @AndreasStokholm @Sjoerd-Bo3 @t1dude
*.js @dnzxy @bjornoleh @MikePlante1 @aug0211 @AndreasStokholm @Sjoerd-Bo3 @t1dude @jeremystorring
39 changes: 20 additions & 19 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
[submodule "LoopKit"]
path = LoopKit
url = https://github.com/LoopKit/LoopKit.git
branch = dev
url = https://github.com/loopandlearn/LoopKit.git
branch = trio
[submodule "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"]
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"]
path = RileyLinkKit
url = https://github.com/LoopKit/RileyLinkKit
branch = dev
url = https://github.com/loopandlearn/RileyLinkKit
branch = trio
[submodule "OmniBLE"]
path = OmniBLE
url = https://github.com/LoopKit/OmniBLE.git
branch = dev
url = https://github.com/loopandlearn/OmniBLE.git
branch = trio
[submodule "G7SensorKit"]
path = G7SensorKit
url = https://github.com/LoopKit/G7SensorKit.git
branch = main
url = https://github.com/loopandlearn/G7SensorKit.git
branch = trio
[submodule "OmniKit"]
path = OmniKit
url = https://github.com/LoopKit/OmniKit.git
branch = main
url = https://github.com/loopandlearn/OmniKit.git
branch = trio
[submodule "MinimedKit"]
path = MinimedKit
url = https://github.com/LoopKit/MinimedKit.git
branch = main
url = https://github.com/loopandlearn/MinimedKit.git
branch = trio
[submodule "LibreTransmitter"]
path = LibreTransmitter
url = https://github.com/LoopKit/LibreTransmitter.git
branch = main
url = https://github.com/loopandlearn/LibreTransmitter.git
branch = trio
[submodule "TidepoolService"]
path = TidepoolService
url = https://github.com/LoopKit/TidepoolService.git
url = https://github.com/loopandlearn/TidepoolService.git
branch = trio
52 changes: 30 additions & 22 deletions FreeAPS/Sources/APS/CGM/PluginSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,26 @@ final class PluginSource: GlucoseSource {
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
}
Expand All @@ -35,17 +54,15 @@ final class PluginSource: GlucoseSource {
}

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 {
guard let cgmManager = self.cgmManager else { return }
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: [])
.replaceEmpty(with: [])
.eraseToAnyPublisher()
Expand Down Expand Up @@ -92,11 +109,10 @@ extension PluginSource: CGMManagerDelegate {
glucoseManager?.cgmGlucoseSourceType = .none
}

func cgmManager(_ manager: CGMManager, hasNew readingResult: CGMReadingResult) {
func cgmManager(_: CGMManager, hasNew readingResult: CGMReadingResult) {
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]) {
Expand Down Expand Up @@ -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)")
switch readingResult {
case let .newData(values):
Expand Down Expand Up @@ -177,18 +189,14 @@ extension PluginSource: CGMManagerDelegate {
transmitterID: sensorTransmitterID
)
}
promise?(.success(bloodGlucose))
completion()
return .success(bloodGlucose)
case .unreliableData:
// loopManager.receivedUnreliableCGMReading()
promise?(.failure(GlucoseDataError.unreliableData))
completion()
return .failure(GlucoseDataError.unreliableData)
case .noData:
promise?(.failure(GlucoseDataError.noData))
completion()
return .failure(GlucoseDataError.noData)
case let .error(error):
promise?(.failure(error))
completion()
return .failure(error)
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions FreeAPS/Sources/Modules/Stat/View/ChartsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ struct ChartsView: View {
type: NSLocalizedString(
"Low",
comment: ""
) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
) + " (<\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
percent: fetched[0].decimal
),
.init(type: NSLocalizedString("In Range", comment: ""), percent: fetched[1].decimal),
.init(
type: NSLocalizedString(
"High",
comment: ""
) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
) + " (>\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
percent: fetched[2].decimal
)
]
Expand All @@ -142,12 +142,12 @@ struct ChartsView: View {
NSLocalizedString(
"Low",
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(
"High",
comment: ""
) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
) + " (>\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
]).frame(maxHeight: 25)
}

Expand All @@ -161,18 +161,18 @@ struct ChartsView: View {
type: NSLocalizedString(
"Low",
comment: ""
) + " ( \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
) + " (< \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
percent: fetched[0].decimal
),
.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
),
.init(
type: NSLocalizedString(
"High",
comment: ""
) + " ( \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
) + " (> \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
percent: fetched[2].decimal
)
]
Expand All @@ -196,12 +196,12 @@ struct ChartsView: View {
NSLocalizedString(
"Low",
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(
"High",
comment: ""
) + " ( \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
) + " (> \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
])
}

Expand Down Expand Up @@ -276,11 +276,11 @@ struct ChartsView: View {
let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) })
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 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 hypoPercentage = Double(hypoReadings) / Double(totalReadings) * 100

Expand Down
68 changes: 68 additions & 0 deletions fastlane/testflight.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
>
> The [**Optional**](#optional) section provides instructions to modify the default behavior if desired. >

## Introduction

Expand Down Expand Up @@ -181,3 +182,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 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