Skip to content

Commit

Permalink
Feature/weightscale_bluetooth (#6)
Browse files Browse the repository at this point in the history
# Connect to weight scale, Save and display recorded measurements

## ♻️ Current situation & Problem
Currently, the application is unable to connect to the weight scale,
record weight measurements, and save them to cloud storage.

## ⚙️ Release Notes 
- Application now automatically connects to nearest device advertising
the Weight Scale Service as defined in the Bluetooth LE protocol.
- If the device is new, the application will prompt the user to pair the
device.
- New measurements are managed by the MeasurementManager class.
- When a new weight measurement is recorded, a sheet will appear
prompting the user to confirm it, and either save or discard the new
measurement. This will appear over any tab of the home view, wherever
the user happens to be at the time.
- If the user selects save, the measurement is converted to an Apple
HealthKit HKQuantitySample, then uploaded to cloud storage (Firestore)
as a FHIR Observation via HealthkitOnFHIR.
- Also edited the Github action workflow for building and testing to fix
a bug preventing previous PR's from passing. This related to the test
runs not being signed into a Google Firebase account.


## 📚 Documentation
More thorough in-line documentation will be included along with testing
in the next PR.

## ✅ Testing
UI Tests will be implemented in the next PR.

### Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md):
- [X] I agree to follow the [Code of
Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md).

---------

Co-authored-by: Paul Schmiedmayer <[email protected]>
  • Loading branch information
nriedman and PSchmiedmayer authored May 22, 2024
1 parent 15b47f6 commit 9e3715d
Show file tree
Hide file tree
Showing 20 changed files with 982 additions and 49 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ jobs:
runsonlabels: '["macOS", "self-hosted"]'
setupSimulators: true
setupfirebaseemulator: true
customcommand: "npm install --previx ./functions && firebase emulators:exec --import=./firebase 'fastlane test'"
fastlanelane: test
firebaseemulatorimport: ./firebase
secrets:
GOOGLE_APPLICATION_CREDENTIALS_BASE64: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_BASE64 }}
uploadcoveragereport:
name: Upload Coverage Report
needs: buildandtest
Expand Down
114 changes: 113 additions & 1 deletion ENGAGEHF.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "84060c25a97a757bf3045128a2563021282283c70a9cb6009941a41298deef92",
"originHash" : "7a1ae4cd835f2e84578822a40fc673f29fc7ab26c0416a0079ce8402bbb3da0b",
"pins" : [
{
"identity" : "abseil-cpp-binary",
Expand All @@ -10,13 +10,22 @@
"version" : "1.2024011601.1"
}
},
{
"identity" : "antlr4",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antlr/antlr4",
"state" : {
"revision" : "7ed420ff2c78d62883875c442d75f32e73bc86c8",
"version" : "4.13.1"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "c218c2054299b15ae577e818bbba16084d3eabe6",
"version" : "10.18.2"
"revision" : "076b241a625e25eac22f8849be256dfb960fcdfe",
"version" : "10.19.1"
}
},
{
Expand All @@ -33,8 +42,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
"state" : {
"revision" : "7892a123f7e8d0fe62f9f03728b17bbd4f94df5c",
"version" : "1.8.1"
"revision" : "c9c3df6ab812de32bae61fc0cd1bf6d45170ebf0",
"version" : "1.8.2"
}
},
{
Expand All @@ -51,17 +60,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "888f0b6026e2441a69e3ee2ad5293c7a92031e62",
"version" : "10.23.1"
"revision" : "9d17b500cd98d9a7009751ad62f802e152e97021",
"version" : "10.26.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "c7a5917ebe48d69f421aadf154ef3969c8b7f12d",
"version" : "10.23.1"
"revision" : "16244d177c4e989f87b25e9db1012b382cfedc55",
"version" : "10.25.0"
}
},
{
Expand All @@ -78,8 +87,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "26c898aed8bed13b8a63057ee26500abbbcb8d55",
"version" : "7.13.1"
"revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6",
"version" : "7.13.3"
}
},
{
Expand All @@ -96,17 +105,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "9534039303015a84837090d20fa21cae6e5eadb6",
"version" : "3.3.2"
"revision" : "0382ca27f22fb3494cf657d8dc356dc282cd1193",
"version" : "3.4.1"
}
},
{
"identity" : "healthkitonfhir",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordBDHG/HealthKitOnFHIR.git",
"state" : {
"revision" : "d6ceecf11800d73fed0c6ce33717f3dc71a44bd7",
"version" : "0.2.7"
"revision" : "418929f315f37e6d9c8f30f40030bc65b9cc47c9",
"version" : "0.2.8"
}
},
{
Expand All @@ -123,8 +132,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "43aaef65e0c665daadf848761d560e446d350d3d",
"version" : "1.22.4"
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
Expand Down Expand Up @@ -159,17 +168,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordBDHG/ResearchKitOnFHIR",
"state" : {
"revision" : "7cd02fe3eee061b8cfbb32d272715af8838b978e",
"version" : "1.2.0"
"revision" : "cdb24dd5607d5a63aaf3a3597c98122189cb548e",
"version" : "1.3.0"
}
},
{
"identity" : "sourcekitten",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/SourceKitten.git",
"state" : {
"revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56",
"version" : "0.34.1"
"revision" : "fd4df99170f5e9d7cf9aa8312aa8506e0e7a44e7",
"version" : "0.35.0"
}
},
{
Expand All @@ -186,8 +195,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziAccount.git",
"state" : {
"revision" : "cb9441e5fe9ca31a17be2507d03817a080e63e9d",
"version" : "1.2.2"
"revision" : "2de07209430fe7b13c44790eab948b30482fcb9d",
"version" : "1.2.4"
}
},
{
"identity" : "spezibluetooth",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziBluetooth.git",
"state" : {
"revision" : "8e94fc71720ef3fcf7f5d9dba9eef603c5151d7a",
"version" : "1.3.0"
}
},
{
Expand All @@ -199,6 +217,15 @@
"version" : "1.0.0"
}
},
{
"identity" : "spezifileformats",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziFileFormats",
"state" : {
"revision" : "017ea997f4f6128b15e8b8d4aac979cb7a822e74",
"version" : "1.2.0"
}
},
{
"identity" : "spezifirebase",
"kind" : "remoteSourceControl",
Expand All @@ -211,7 +238,7 @@
{
"identity" : "spezifoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziFoundation.git",
"location" : "https://github.com/StanfordSpezi/SpeziFoundation",
"state" : {
"revision" : "01af5b91a54f30ddd121258e81aff2ddc2a99ff9",
"version" : "1.0.4"
Expand Down Expand Up @@ -240,8 +267,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziOnboarding",
"state" : {
"revision" : "4971a82e94996ce0c3d8ecf64fdeec874a1f20d6",
"version" : "1.1.1"
"revision" : "8d6dda3501720a1952573439b21a503cbecd9e0f",
"version" : "1.2.0"
}
},
{
Expand Down Expand Up @@ -274,10 +301,19 @@
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.2.3"
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
Expand All @@ -289,6 +325,15 @@
"version" : "1.1.0"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "359c461e5561d22c6334828806cc25d759ca7aa6",
"version" : "2.65.0"
}
},
{
"identity" : "swift-package-list",
"kind" : "remoteSourceControl",
Expand All @@ -312,17 +357,26 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
"version" : "509.0.2"
"revision" : "303e5c5c36d6a558407d364878df131c3546fad8",
"version" : "510.0.2"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "f9266c85189c2751589a50ea5aec72799797e471",
"version" : "1.3.0"
}
},
{
"identity" : "swiftlint",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/SwiftLint.git",
"state" : {
"revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee",
"version" : "0.54.0"
"revision" : "b515723b16eba33f15c4677ee65f3fef2ce8c255",
"version" : "0.55.1"
}
},
{
Expand Down Expand Up @@ -375,8 +429,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git",
"state" : {
"revision" : "8a835d918245ca22f36663dd3862138805d7f707",
"version" : "5.1.0"
"revision" : "9234124cff5e22e178988c18d8b95a8ae8007f76",
"version" : "5.1.2"
}
}
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// This source file is part of the ENGAGE-HF project based on the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import BluetoothServices
import ByteCoding
import Foundation
import NIOCore


enum WeightUnits: String, Equatable {
case metric = "kg"
case imperial = "lb"
}


struct WeightMeasurement: Equatable {
// Flags
let units: WeightUnits
let timeStampPresent: Bool
let userIDPresent: Bool
let heightBMIPresent: Bool

// Units:
// Kilograms with a resolution of 0.005
// Pounds with a resolution of 0.01
let weight: UInt16

// Only present when corresponding flag is true
let timeStamp: DateTime?
let bmi: UInt16?
let height: UInt16?
let userID: UInt8?
}


extension WeightMeasurement: ByteDecodable {
init?(from byteBuffer: inout NIOCore.ByteBuffer, preferredEndianness endianness: NIOCore.Endianness) {
guard byteBuffer.readableBytes >= 11 else {
return nil
}

// Decode fields as described in the manual
guard let flagBits = UInt8(from: &byteBuffer, preferredEndianness: endianness),
let weight = UInt16(from: &byteBuffer, preferredEndianness: endianness) else {
return nil
}

// Extract flags
let timeStampFlag: Bool = ((flagBits >> 1) & 0b1) != 0
let userIDFlag: Bool = ((flagBits >> 2) & 0b1) != 0
let heightBMIFlag: Bool = ((flagBits >> 3) & 0b1) != 0

// Get the time stamp
if timeStampFlag {
guard let timeStamp = DateTime(from: &byteBuffer, preferredEndianness: endianness) else {
return nil
}

self.timeStamp = timeStamp
} else {
self.timeStamp = nil
}

if userIDFlag {
guard let userID = UInt8(from: &byteBuffer, preferredEndianness: endianness) else {
return nil
}

self.userID = userID
} else {
self.userID = nil
}

if heightBMIFlag {
guard let bmi = UInt16(from: &byteBuffer),
let height = UInt16(from: &byteBuffer) else {
return nil
}

self.bmi = bmi
self.height = height
} else {
self.bmi = nil
self.height = nil
}

self.units = {
if (flagBits & 1) == 1 {
return .imperial
} else {
return .metric
}
}()


self.timeStampPresent = timeStampFlag
self.heightBMIPresent = heightBMIFlag
self.userIDPresent = userIDFlag
self.weight = weight
}
}
Loading

0 comments on commit 9e3715d

Please sign in to comment.