├── .ruby-version ├── .watchmanconfig ├── src ├── declarations.d.ts ├── assets │ ├── google_fit.png │ ├── health_kit.png │ ├── launcher.png │ ├── launcher_screen.png │ ├── ios_icons │ │ └── iOS │ │ │ └── AppIcon.appiconset │ │ │ ├── launcher-20.png │ │ │ ├── launcher-29.png │ │ │ ├── launcher-40.png │ │ │ ├── launcher-512.png │ │ │ ├── launcher-76.png │ │ │ ├── launcher-1024.png │ │ │ ├── launcher-20@2x.png │ │ │ ├── launcher-20@3x.png │ │ │ ├── launcher-29@2x.png │ │ │ ├── launcher-29@3x.png │ │ │ ├── launcher-40@2x.png │ │ │ ├── launcher-40@3x.png │ │ │ ├── launcher-60@2x.png │ │ │ ├── launcher-60@3x.png │ │ │ ├── launcher-76@2x.png │ │ │ ├── launcher-83.5@2x.png │ │ │ └── Contents.json │ ├── home_connected_icon.svg │ ├── vitals_logo.svg │ ├── launcher.svg │ └── logo_title.svg ├── globals.d.ts ├── api │ ├── auth.ts │ └── central.ts ├── progress.d.ts ├── hooks │ ├── bluetoothHooks.tsx │ ├── auth.ts │ ├── layout.ts │ └── common.ts ├── health │ ├── custom │ │ └── phone.ts │ └── manufacterMap.ts ├── components │ ├── buttons.tsx │ ├── appbar.tsx │ ├── footer.tsx │ ├── qrcodeMask.tsx │ ├── utils.tsx │ ├── typography.tsx │ └── insightDrawer.tsx ├── styles.ts ├── contexts │ ├── auth.tsx │ ├── ui.tsx │ └── config.tsx ├── utils.ts ├── models.ts ├── App.tsx ├── screens │ ├── providers.tsx │ └── welcome.tsx └── types.ts ├── app.json ├── .bundle └── config ├── media ├── home.png ├── ios.gif ├── login.png ├── scan.png ├── android.gif ├── numeric.png ├── qrcode.png ├── insight.jpeg ├── modelid_1.png ├── modelid_2.png ├── permission.png ├── authorization.png ├── google_fit_sync.png ├── scan_simulated.png ├── sync_options.jpeg ├── creds_generator_1.png ├── creds_generator_2.png ├── device_connect_1.png ├── device_connect_2.png ├── device_connect_3.png ├── device_connect_4.png ├── edit_dashboard_1.png ├── edit_dashboard_2.png ├── simulated_result.png ├── Google_Fit.json ├── HealthDevice.json └── Apple_HealthKit.json ├── android ├── app │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ └── drawable │ │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── ic_launcher-web.png │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── cpm │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── AndroidManifest.xml │ │ └── debug │ │ │ └── AndroidManifest.xml │ ├── .classpath │ ├── proguard-rules.pro │ ├── build_defs.bzl │ └── BUCK ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .settings │ └── org.eclipse.buildship.core.prefs ├── settings.gradle ├── .project ├── gradle.properties ├── build.gradle └── gradlew.bat ├── ios ├── cpm │ ├── Images.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── launcher-1024.png │ │ │ ├── launcher-20.png │ │ │ ├── launcher-29.png │ │ │ ├── launcher-40.png │ │ │ ├── launcher-76.png │ │ │ ├── launcher-20@2x.png │ │ │ ├── launcher-20@3x.png │ │ │ ├── launcher-29@2x.png │ │ │ ├── launcher-29@3x.png │ │ │ ├── launcher-40@2x.png │ │ │ ├── launcher-40@3x.png │ │ │ ├── launcher-60@2x.png │ │ │ ├── launcher-60@3x.png │ │ │ ├── launcher-76@2x.png │ │ │ ├── launcher-83.5@2x.png │ │ │ └── Contents.json │ │ ├── SplashIcon.imageset │ │ │ ├── launcher_screen-1.png │ │ │ ├── launcher_screen-2.png │ │ │ ├── launcher_screen.png │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ ├── LaunchImage-1792x828.png │ │ │ ├── LaunchImage-320x480.png │ │ │ ├── LaunchImage-640x1136.png │ │ │ ├── LaunchImage-640x960.png │ │ │ ├── LaunchImage-750x1334.png │ │ │ ├── LaunchImage-828x1792.png │ │ │ ├── LaunchImage-1125x2436.png │ │ │ ├── LaunchImage-1242x2208.png │ │ │ ├── LaunchImage-1242x2688.png │ │ │ ├── LaunchImage-2208x1242.png │ │ │ ├── LaunchImage-2436x1125.png │ │ │ ├── LaunchImage-2688x1242.png │ │ │ ├── LaunchImage-640x1136-1.png │ │ │ ├── LaunchImage-640x960-1.png │ │ │ └── Contents.json │ ├── main.m │ ├── cpmDebug.entitlements │ ├── cpm.entitlements │ ├── AppDelegate.h │ ├── cpmRelease.entitlements │ ├── Info.plist │ ├── AppDelegate.mm │ └── LaunchScreen.storyboard ├── cpm-Bridging-Header.h ├── cpm.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── cpm.xcscheme ├── BLE.swift ├── Charts.swift ├── cpm.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── .xcode.env ├── cpmTests │ ├── Info.plist │ └── cpmTests.m ├── Podfile └── LaunchScreen.xib ├── .buckconfig ├── creds-generator ├── .gitignore ├── server │ ├── dist │ │ └── public │ │ │ ├── assets │ │ │ ├── favicon.gif │ │ │ └── background.jpg │ │ │ ├── fonts │ │ │ ├── poppins │ │ │ │ ├── Poppins-Bold.ttf │ │ │ │ ├── Poppins-Thin.ttf │ │ │ │ ├── Poppins-Black.ttf │ │ │ │ ├── Poppins-Italic.ttf │ │ │ │ ├── Poppins-Light.ttf │ │ │ │ ├── Poppins-Medium.ttf │ │ │ │ ├── Poppins-ExtraBold.ttf │ │ │ │ ├── Poppins-Regular.ttf │ │ │ │ ├── Poppins-SemiBold.ttf │ │ │ │ ├── Poppins-BlackItalic.ttf │ │ │ │ ├── Poppins-BoldItalic.ttf │ │ │ │ ├── Poppins-ExtraLight.ttf │ │ │ │ ├── Poppins-LightItalic.ttf │ │ │ │ ├── Poppins-ThinItalic.ttf │ │ │ │ ├── Poppins-MediumItalic.ttf │ │ │ │ ├── Poppins-SemiBoldItalic.ttf │ │ │ │ ├── Poppins-ExtraBoldItalic.ttf │ │ │ │ └── Poppins-ExtraLightItalic.ttf │ │ │ ├── font-awesome-4.7.0 │ │ │ │ ├── less │ │ │ │ │ ├── screen-reader.less │ │ │ │ │ ├── fixed-width.less │ │ │ │ │ ├── larger.less │ │ │ │ │ ├── list.less │ │ │ │ │ ├── core.less │ │ │ │ │ ├── stacked.less │ │ │ │ │ ├── font-awesome.less │ │ │ │ │ ├── bordered-pulled.less │ │ │ │ │ ├── rotated-flipped.less │ │ │ │ │ ├── path.less │ │ │ │ │ ├── animated.less │ │ │ │ │ └── mixins.less │ │ │ │ ├── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ │ ├── scss │ │ │ │ │ ├── _fixed-width.scss │ │ │ │ │ ├── _screen-reader.scss │ │ │ │ │ ├── _larger.scss │ │ │ │ │ ├── _list.scss │ │ │ │ │ ├── _core.scss │ │ │ │ │ ├── font-awesome.scss │ │ │ │ │ ├── _stacked.scss │ │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ │ ├── _path.scss │ │ │ │ │ ├── _animated.scss │ │ │ │ │ └── _mixins.scss │ │ │ │ └── HELP-US-OUT.txt │ │ │ └── Linearicons-Free-v1.0.0 │ │ │ │ └── WebFont │ │ │ │ ├── Linearicons-Free.eot │ │ │ │ ├── Linearicons-Free.ttf │ │ │ │ ├── Linearicons-Free.woff │ │ │ │ └── Linearicons-Free.woff2 │ │ │ └── index.html │ └── src │ │ └── index.ts └── package.json ├── .prettierrc.js ├── Gemfile ├── wdyr.js ├── appcenter-pre-build.sh ├── pre-packager.js ├── createReleaseFile.sh ├── release-ios.sh ├── index.js ├── .eslintrc.json ├── .azuredevops ├── node-build.yml └── resign.yml ├── podinstall.js ├── docs ├── insight.md ├── walkthrough.md ├── data_format.md ├── google_fit.md └── simulation.md ├── metro.config.js ├── appcenter-post-build.sh ├── tsconfig.json ├── ExportOptions.plist ├── babel.config.js ├── LICENSE ├── .gitignore ├── bumpVersion.sh ├── .flowconfig ├── patches └── react-native+0.69.1.patch ├── Gemfile.lock └── package.json /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.5 -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cpm", 3 | "displayName": "cpm" 4 | } -------------------------------------------------------------------------------- /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "3" 3 | BUNDLE_JOBS: "4" 4 | -------------------------------------------------------------------------------- /media/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/home.png -------------------------------------------------------------------------------- /media/ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/ios.gif -------------------------------------------------------------------------------- /media/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/login.png -------------------------------------------------------------------------------- /media/scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/scan.png -------------------------------------------------------------------------------- /media/android.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/android.gif -------------------------------------------------------------------------------- /media/numeric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/numeric.png -------------------------------------------------------------------------------- /media/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/qrcode.png -------------------------------------------------------------------------------- /media/insight.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/insight.jpeg -------------------------------------------------------------------------------- /media/modelid_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/modelid_1.png -------------------------------------------------------------------------------- /media/modelid_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/modelid_2.png -------------------------------------------------------------------------------- /media/permission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/permission.png -------------------------------------------------------------------------------- /media/authorization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/authorization.png -------------------------------------------------------------------------------- /media/google_fit_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/google_fit_sync.png -------------------------------------------------------------------------------- /media/scan_simulated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/scan_simulated.png -------------------------------------------------------------------------------- /media/sync_options.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/sync_options.jpeg -------------------------------------------------------------------------------- /src/assets/google_fit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/google_fit.png -------------------------------------------------------------------------------- /src/assets/health_kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/health_kit.png -------------------------------------------------------------------------------- /src/assets/launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/launcher.png -------------------------------------------------------------------------------- /android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /media/creds_generator_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/creds_generator_1.png -------------------------------------------------------------------------------- /media/creds_generator_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/creds_generator_2.png -------------------------------------------------------------------------------- /media/device_connect_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/device_connect_1.png -------------------------------------------------------------------------------- /media/device_connect_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/device_connect_2.png -------------------------------------------------------------------------------- /media/device_connect_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/device_connect_3.png -------------------------------------------------------------------------------- /media/device_connect_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/device_connect_4.png -------------------------------------------------------------------------------- /media/edit_dashboard_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/edit_dashboard_1.png -------------------------------------------------------------------------------- /media/edit_dashboard_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/edit_dashboard_2.png -------------------------------------------------------------------------------- /media/simulated_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/media/simulated_result.png -------------------------------------------------------------------------------- /src/assets/launcher_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/launcher_screen.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | IoT Central CPM 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ios/cpm-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /ios/cpm.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /creds-generator/.gitignore: -------------------------------------------------------------------------------- 1 | # node 2 | node_modules/ 3 | server/**/*.js 4 | server/**/db.json 5 | server/**/*.d.ts 6 | 7 | # vscode 8 | .vscode 9 | .deployment 10 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/assets/favicon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/assets/favicon.gif -------------------------------------------------------------------------------- /src/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import {SvgProps} from 'react-native-svg'; 3 | const content: React.FC; 4 | export default content; 5 | } 6 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/assets/background.jpg -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | }; 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-1024.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-20.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-29.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-40.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-76.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-20.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-29.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-40.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-512.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-76.png -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 3 | ruby '~> 2.7.5' 4 | gem 'cocoapods', '~> 1.11', '>= 1.11.2' -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-20@2x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-20@3x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-29@2x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-29@3x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-40@2x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-40@3x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-60@2x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-60@3x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-76@2x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-1024.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-20@2x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-20@3x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-29@2x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-29@3x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-40@2x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-40@3x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-60@2x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-60@3x.png -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-76@2x.png -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Bold.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Thin.ttf -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/AppIcon.appiconset/launcher-83.5@2x.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/SplashIcon.imageset/launcher_screen-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/SplashIcon.imageset/launcher_screen-1.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/SplashIcon.imageset/launcher_screen-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/SplashIcon.imageset/launcher_screen-2.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/SplashIcon.imageset/launcher_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/SplashIcon.imageset/launcher_screen.png -------------------------------------------------------------------------------- /src/api/auth.ts: -------------------------------------------------------------------------------- 1 | export async function login( 2 | username: string, 3 | password: string, 4 | ): Promise { 5 | //TODO: return authentication code 6 | return password; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/src/assets/ios_icons/iOS/AppIcon.appiconset/launcher-83.5@2x.png -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Black.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Italic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Light.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Medium.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraBold.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-Regular.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-SemiBold.ttf -------------------------------------------------------------------------------- /ios/BLE.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BLE.swift 3 | // cpm 4 | // 5 | // Created by Luca Druda on 25/02/2020. 6 | // Copyright © 2020 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /src/progress.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-native-progress/CircleSnail' { 2 | import React from 'react'; 3 | export default class CircleSnail extends React.Component {} 4 | } 5 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-BlackItalic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-BoldItalic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraLight.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-LightItalic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-ThinItalic.ttf -------------------------------------------------------------------------------- /ios/Charts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Charts.swift 3 | // cpm 4 | // 5 | // Created by Luca Druda on 02/03/2020. 6 | // Copyright © 2020 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1792x828.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1792x828.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-320x480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-320x480.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x1136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x1136.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x960.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x960.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-750x1334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-750x1334.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-828x1792.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-828x1792.png -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-MediumItalic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1125x2436.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1125x2436.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1242x2208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1242x2208.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1242x2688.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-1242x2688.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-2208x1242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-2208x1242.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-2436x1125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-2436x1125.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-2688x1242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-2688x1242.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x1136-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x1136-1.png -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x960-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/ios/cpm/Images.xcassets/LaunchImage.launchimage/LaunchImage-640x960-1.png -------------------------------------------------------------------------------- /wdyr.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | if (__DEV__) { 3 | console.log(`Tracing active`); 4 | const whyDidYouRender = require('@welldone-software/why-did-you-render'); 5 | whyDidYouRender(React); 6 | } -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/poppins/Poppins-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /appcenter-pre-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ "$APP_CENTER_CURRENT_PLATFORM" == "android" ] 3 | then 4 | cd android 5 | ./gradlew increment 6 | cd .. 7 | else 8 | #iOS 9 | cd ./ios 10 | pod install 11 | fi 12 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.eot -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.ttf -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.woff -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iot-for-all/iotc-cpm-sample/HEAD/creds-generator/server/dist/public/fonts/Linearicons-Free-v1.0.0/WebFont/Linearicons-Free.woff2 -------------------------------------------------------------------------------- /pre-packager.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const path = './env.json'; 4 | try { 5 | if (!fs.existsSync(path)) { 6 | fs.writeFileSync(path, '{}'); 7 | } 8 | } 9 | catch (err) { 10 | console.error(err); 11 | } 12 | -------------------------------------------------------------------------------- /ios/cpm/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /createReleaseFile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURRENT_TAG=$(git tag -l --sort=-v:refname | sed -n 2p) 4 | echo "Current Tag: $CURRENT_TAG" 5 | 6 | CHANGES=$(git log --pretty=format:"- %s" ...$CURRENT_TAG) 7 | 8 | echo -e "**$BUILD_BUILDNUMBER**\n\n$CHANGES" > CHANGELOG.md -------------------------------------------------------------------------------- /release-ios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | xcodebuild -scheme cpm -workspace ios/cpm.xcworkspace -destination generic/platform=iOS build 4 | 5 | xcodebuild -workspace ios/cpm.xcworkspace -scheme cpm -sdk iphoneos -configuration Enterprise archive -archivePath $PWD/ios/build/cpm.xcarchive -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 13 16:03:47 CEST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip 7 | -------------------------------------------------------------------------------- /ios/cpm.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/cpm.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | import './wdyr'; 5 | 6 | import 'react-native-gesture-handler'; 7 | import {AppRegistry} from 'react-native'; 8 | import App from './src/App'; 9 | import {name as appName} from './app.json'; 10 | import 'react-native-get-random-values'; 11 | 12 | AppRegistry.registerComponent(appName, () => App); 13 | -------------------------------------------------------------------------------- /ios/cpm/cpmDebug.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.healthkit 6 | 7 | com.apple.developer.healthkit.access 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/HELP-US-OUT.txt: -------------------------------------------------------------------------------- 1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, 2 | Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, 3 | comprehensive icon sets or copy and paste your own. 4 | 5 | Please. Check it out. 6 | 7 | -Dave Gandy 8 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "jsx": true, 5 | "useJSXTextNode": true, 6 | "ecmaVersion": 6, 7 | "sourceType": "module", 8 | "ecmaFeatures": { 9 | "jsx": true 10 | } 11 | }, 12 | "plugins": [ 13 | "react-hooks" 14 | ], 15 | "rules": { 16 | "react-hooks/rules-of-hooks": "error", 17 | "react-hooks/exhaustive-deps": "error" 18 | } 19 | } -------------------------------------------------------------------------------- /ios/cpm/cpm.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.healthkit 6 | 7 | com.apple.developer.healthkit.access 8 | 9 | com.apple.developer.healthkit.background-delivery 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) 5 | connection.project.dir= 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=/usr/lib/jvm/java-16-openjdk-amd64 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /ios/cpm/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /ios/cpm/cpmRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.healthkit 6 | 7 | com.apple.developer.healthkit.access 8 | 9 | health-records 10 | 11 | com.apple.developer.healthkit.background-delivery 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | # NODE_BINARY variable contains the PATH to the node executable. 6 | # 7 | # Customize the NODE_BINARY variable here. 8 | # For example, to use nvm with brew, add the following line 9 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 10 | export NODE_BINARY=$(command -v node) -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.azuredevops/node-build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: NodeTool@0 3 | inputs: 4 | versionSpec: "16.14.0" 5 | displayName: "Install Node" 6 | 7 | - script: npm install 8 | workingDirectory: . 9 | displayName: "Install node dependencies" 10 | 11 | - script: npm version prerelease --preid "$(Build.BuildNumber)" --no-git-tag-version 12 | workingDirectory: . 13 | displayName: "Increment version in package.json" 14 | 15 | - task: ShellScript@2 16 | inputs: 17 | scriptPath: "$(System.DefaultWorkingDirectory)/bumpVersion.sh" -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/SplashIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "launcher_screen.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "launcher_screen-1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "launcher_screen-2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /src/hooks/bluetoothHooks.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect, useState, useContext} from 'react'; 2 | import {ConfigContext} from '../contexts/config'; 3 | import {SimulatedHealthManager} from '../health/simulated'; 4 | 5 | export function useSimulated(): boolean { 6 | const {state} = useContext(ConfigContext); 7 | const [simulated, setSimulated] = useState( 8 | state.healthManager instanceof SimulatedHealthManager, 9 | ); 10 | 11 | useEffect(() => { 12 | setSimulated(state.healthManager instanceof SimulatedHealthManager); 13 | }, [state.healthManager]); 14 | 15 | return simulated; 16 | } 17 | -------------------------------------------------------------------------------- /podinstall.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const exec = require('child_process').exec; 3 | const path = require('path'); 4 | 5 | switch (process.platform) { 6 | case 'darwin': 7 | exec('bundle install && pod install', { cwd: path.join(__dirname, 'ios') }, (err, stdout, stderr) => { 8 | if (err) { 9 | console.error(err); 10 | } 11 | if (stderr) { 12 | console.error(stderr); 13 | } 14 | if (stdout) { 15 | console.log(stdout); 16 | } 17 | }); 18 | break; 19 | default: 20 | break; 21 | } -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /docs/insight.md: -------------------------------------------------------------------------------- 1 | # Data Insight 2 | 3 | The insight screen shows a preview of telemetry data streamed from bluetooth device (either real or simulated) or from one of the platform provider (Apple Health, Google Fit). 4 | 5 | Insight | Sync Options 6 | :-------------------------:|:-------------------------: 7 | ![](../media/insight.jpeg) | ![](../media/sync_options.jpeg) 8 | 9 | Data shown can be controlled using side menu that can be activated by tapping on "Sync options" or on the 3-dots icon on top-right in header bar. 10 | 11 | Unselecting an item also stops telemetry delivery to IoT Central, if connected, for it. 12 | 13 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'cpm' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/react-native-gradle-plugin') 5 | if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") { 6 | include(":ReactAndroid") 7 | project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid') 8 | include(":ReactAndroid:hermes-engine") 9 | project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine') 10 | } 11 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require('metro-config'); 2 | 3 | module.exports = (async () => { 4 | const { 5 | resolver: { sourceExts, assetExts }, 6 | } = await getDefaultConfig(); 7 | return { 8 | transformer: { 9 | getTransformOptions: async () => ({ 10 | transform: { 11 | experimentalImportSupport: false, 12 | inlineRequires: true, 13 | }, 14 | }), 15 | babelTransformerPath: require.resolve('react-native-svg-transformer'), 16 | }, 17 | resolver: { 18 | assetExts: assetExts.filter(ext => ext !== 'svg'), 19 | sourceExts: [...sourceExts, 'svg'], 20 | }, 21 | }; 22 | })(); -------------------------------------------------------------------------------- /appcenter-post-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ "$APPCENTER_BRANCH" != "master" ]; then 3 | echo "Not on master. Exiting..." 4 | exit 1 5 | fi 6 | 7 | if [ "$AGENT_JOBSTATUS" == "Succeeded" ]; then 8 | cd android 9 | ./gradlew -q increment 10 | VERSION=`./gradlew -q printVersion | tail -n 1` 11 | echo $VERSION 12 | git config user.email "$GIT_EMAIL" 13 | git config user.name "$GIT_USER" 14 | git add ./gradle.properties 15 | git commit -m "Android Release v$VERSION" --author="$GIT_USER <$GIT_EMAIL>" 16 | git remote set-url --push origin https://$GIT_TOKEN@github.com/$GIT_USER/cpm-poc.git 17 | git push origin master 18 | cd .. 19 | fi -------------------------------------------------------------------------------- /android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /src/hooks/auth.ts: -------------------------------------------------------------------------------- 1 | import {IUser, AuthContext} from '../contexts/auth'; 2 | import {useContext} from 'react'; 3 | 4 | type UserDispatch = (value: IUser | null) => Promise; 5 | 6 | export function useUser(): [IUser | null, UserDispatch] { 7 | const {state, dispatch} = useContext(AuthContext); 8 | 9 | const setUser: UserDispatch = async function (value: IUser | null) { 10 | if (value == null) { 11 | dispatch({ 12 | type: 'LOGOUT', 13 | payload: {user: null, initialized: true}, 14 | }); 15 | } else { 16 | dispatch({ 17 | type: 'LOGIN', 18 | payload: {user: value, initialized: true}, 19 | }); 20 | } 21 | }; 22 | 23 | return [state.user, setUser]; 24 | } 25 | -------------------------------------------------------------------------------- /src/api/central.ts: -------------------------------------------------------------------------------- 1 | import {IIoTCClient} from 'react-native-azure-iotcentral-client'; 2 | import {bleToIoTCName} from '../utils'; 3 | 4 | export async function getCredentialsFromNumericCode( 5 | numeric: string, 6 | ): Promise { 7 | return ( 8 | await fetch( 9 | `https://cpm-cred-server.azurewebsites.net/numeric?numeric=${numeric}`, 10 | ) 11 | ).text(); 12 | } 13 | 14 | export async function sendTelemetryData( 15 | centralClient: IIoTCClient, 16 | normalize: boolean, 17 | itemData: {itemId: string; value: any; itemName: string}, 18 | ): Promise { 19 | const itemKey = normalize ? bleToIoTCName(itemData.itemId) : itemData.itemId; 20 | await centralClient.sendTelemetry({[itemKey]: itemData.value}); 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "sourceMap": true, 8 | "jsx": "react-native", 9 | "lib": [ 10 | "es6" 11 | ], 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "noEmit": true, 15 | "strict": true, 16 | "noUnusedLocals": true, 17 | "module": "commonjs", 18 | "target": "es2017", 19 | "skipLibCheck": true, 20 | "baseUrl": "./src" 21 | }, 22 | "exclude": [ 23 | "node_modules", 24 | "src/sensors/health/**/*.ts*", 25 | "babel.config.js", 26 | "metro.config.js", 27 | "jest.config.js", 28 | "creds-generator" 29 | ] 30 | } -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /ExportOptions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | destination 6 | export 7 | method 8 | enterprise 9 | provisioningProfiles 10 | 11 | com.microsoft.CPM-df 12 | ff754a3e-0e2a-4204-b08f-2db96c8d18a8 13 | 14 | signingCertificate 15 | iPhone Distribution 16 | signingStyle 17 | manual 18 | teamID 19 | 9KBH5RKYEW 20 | compileBitcode 21 | 22 | uploadSymbols 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: [ 4 | ["module-resolver", { 5 | "root": ["./src"], 6 | "extensions": [ 7 | '.ios.ts', 8 | '.android.ts', 9 | '.ts', 10 | '.ios.tsx', 11 | '.android.tsx', 12 | '.tsx', 13 | '.jsx', 14 | '.js', 15 | '.json', 16 | ], 17 | "alias": { 18 | "tools": "./src/tools", 19 | "hooks": "./src/hooks", 20 | "properties": "./src/properties", 21 | "contexts": "./src/contexts", 22 | "sensors": "./src/sensors", 23 | "types": "./src/types", 24 | "components": "./src/components" 25 | } 26 | }], 27 | 'react-native-reanimated/plugin' 28 | ] 29 | }; 30 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /ios/cpmTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | cpm 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1617023572750 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/home_connected_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 23 15:19:15 GMT 2020 2 | FLIPPER_VERSION=0.125.0 3 | # Use this property to specify which architecture you want to build. 4 | # You can also override it from the CLI using 5 | # ./gradlew -PreactNativeArchitectures=x86_64 6 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 7 | # Use this property to enable support to the new architecture. 8 | # This will allow you to use TurboModules and the Fabric render in 9 | # your application. You should enable this flag either if you want 10 | # to write custom TurboModules/Fabric components OR use libraries that 11 | # are providing them. 12 | newArchEnabled=false 13 | android.enableJetifier=true 14 | VERSION_NAME=1.1.5 15 | android.useAndroidX=true 16 | org.gradle.daemon=true 17 | org.gradle.configureondemand=true 18 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 19 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 20 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/health/custom/phone.ts: -------------------------------------------------------------------------------- 1 | import {BleDevice} from '../ble'; 2 | import {IHealthDevice} from '../../models'; 3 | import {Characteristic} from 'react-native-ble-plx'; 4 | import {ManufacturerMap} from '../manufacterMap'; 5 | 6 | export class PhonePeripheralDevice extends BleDevice implements IHealthDevice { 7 | getValue(characteristic: Characteristic) { 8 | if (characteristic.value == null) { 9 | return null; 10 | } 11 | const val = Buffer.from(characteristic.value, 'base64'); 12 | 13 | if ( 14 | characteristic.uuid.toLowerCase() === 15 | '00002a1c-0000-1000-8000-00805f9b34fb' 16 | ) { 17 | if (val.length === 5) { 18 | return 0; 19 | } 20 | } 21 | const intval = val.readInt8(); 22 | return intval; 23 | } 24 | } 25 | 26 | ManufacturerMap.addManufacturer( 27 | 'phone', 28 | [ 29 | '0000180f-0000-1000-8000-00805f9b34fb', 30 | '00001809-0000-1000-8000-00805f9b34fb', 31 | ], 32 | PhonePeripheralDevice, 33 | ); 34 | -------------------------------------------------------------------------------- /src/health/manufacterMap.ts: -------------------------------------------------------------------------------- 1 | export class ManufacturerMap { 2 | static mMap: { 3 | [ManufacturerId: string]: { 4 | ids: string[]; 5 | deviceCtr: any; 6 | }; 7 | } = {}; 8 | 9 | static addManufacturer(name: string, ids: string[], deviceCtr: any): void { 10 | if (!this.mMap[name]) { 11 | this.mMap[name] = { 12 | ids, 13 | deviceCtr, 14 | }; 15 | } 16 | } 17 | 18 | static getManufacturerConstructor(name: string) { 19 | if (!this.mMap[name]) { 20 | return null; 21 | } 22 | return this.mMap[name].deviceCtr; 23 | } 24 | 25 | static getManufacturers() { 26 | return Object.keys(this.mMap); 27 | } 28 | 29 | static getManufacturerIds(name: string): string[] | null { 30 | if (this.mMap[name]) { 31 | return this.mMap[name].ids; 32 | } 33 | return null; 34 | } 35 | 36 | static removeManufacturer(name: string) { 37 | if (this.mMap[name]) { 38 | delete this.mMap[name]; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/buttons.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button, useTheme} from 'react-native-paper'; 3 | import {StyleProp, ViewStyle} from 'react-native'; 4 | import DefaultStyles from '../styles'; 5 | 6 | export type CPMButtonProps = { 7 | children: React.ReactNode; 8 | onPress?: () => void; 9 | mode?: 'text' | 'outlined' | 'contained'; 10 | style?: StyleProp; 11 | }; 12 | 13 | export function CPMButton(props: CPMButtonProps) { 14 | const {mode, children, style, onPress} = props; 15 | const theme = useTheme(); 16 | const textColor = theme.colors.text; 17 | 18 | return ( 19 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/assets/vitals_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 iot-for-all 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /media/Google_Fit.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "@id": "dtmi:cpm:GoogleFit_5r4;1", 4 | "@type": "Interface", 5 | "contents": [ 6 | { 7 | "@id": "dtmi:cpm:GoogleFit_5r4:steps;1", 8 | "@type": "Telemetry", 9 | "displayName": { 10 | "en": "Steps" 11 | }, 12 | "name": "steps", 13 | "schema": "double" 14 | }, 15 | { 16 | "@id": "dtmi:cpm:GoogleFit_5r4:BloodPressureSystolic;1", 17 | "@type": "Telemetry", 18 | "displayName": { 19 | "en": "Blood Pressure Systolic" 20 | }, 21 | "name": "BloodPressureSystolic", 22 | "schema": "double" 23 | }, 24 | { 25 | "@id": "dtmi:cpm:GoogleFit_5r4:BloodPressureDiastolic;1", 26 | "@type": "Telemetry", 27 | "displayName": { 28 | "en": "Blood Pressure Diastolic" 29 | }, 30 | "name": "BloodPressureDiastolic", 31 | "schema": "double" 32 | } 33 | ], 34 | "displayName": { 35 | "en": "Google Fit" 36 | }, 37 | "@context": [ 38 | "dtmi:iotcentral:context;2", 39 | "dtmi:dtdl:context;2" 40 | ] 41 | } 42 | ] -------------------------------------------------------------------------------- /src/assets/launcher.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/styles.ts: -------------------------------------------------------------------------------- 1 | import {TextStyle, ViewStyle, Platform} from 'react-native'; 2 | 3 | const DefaultStyles: { 4 | header: TextStyle; 5 | centerFragment: ViewStyle; 6 | centeredButton: ViewStyle; 7 | elevated: ViewStyle; 8 | itemName: TextStyle; 9 | itemDetail: TextStyle; 10 | } = { 11 | header: { 12 | fontWeight: 'bold', 13 | fontSize: 24, 14 | }, 15 | centerFragment: { 16 | flex: 1, 17 | justifyContent: 'center', 18 | alignItems: 'center', 19 | }, 20 | centeredButton: { 21 | width: 230, 22 | height: 50, 23 | marginVertical: 20, 24 | justifyContent: 'center', 25 | }, 26 | elevated: { 27 | ...Platform.select({ 28 | ios: { 29 | shadowColor: "'rgba(0, 0, 0, 0.14)'", 30 | shadowOffset: { 31 | width: 0, 32 | height: 3, 33 | }, 34 | shadowOpacity: 0.25, 35 | shadowRadius: 3.84, 36 | elevation: 5, 37 | }, 38 | android: { 39 | elevation: 2, 40 | }, 41 | }), 42 | }, 43 | itemName: { 44 | fontWeight: 'bold', 45 | fontSize: 16, 46 | }, 47 | itemDetail: { 48 | fontSize: 14, 49 | }, 50 | }; 51 | export default DefaultStyles; 52 | -------------------------------------------------------------------------------- /src/hooks/layout.ts: -------------------------------------------------------------------------------- 1 | import {useState, useEffect, useCallback} from 'react'; 2 | import {Dimensions, ScaledSize} from 'react-native'; 3 | 4 | type Orientation = 'portrait' | 'landscape'; 5 | function getOrientation(width: number, height: number): Orientation { 6 | if (width > height) { 7 | return 'landscape'; 8 | } 9 | return 'portrait'; 10 | } 11 | 12 | export function useScreenDimensions() { 13 | const [screenData, setScreenData] = useState(Dimensions.get('screen')); 14 | const [orientation, setOrientation] = useState( 15 | getOrientation(screenData.width, screenData.height), 16 | ); 17 | 18 | const onChange = useCallback( 19 | (result: {window: ScaledSize; screen: ScaledSize}) => { 20 | setScreenData(result.screen); 21 | setOrientation(getOrientation(result.screen.width, result.screen.height)); 22 | }, 23 | [], 24 | ); 25 | 26 | useEffect(() => { 27 | const dimsub = Dimensions.addEventListener('change', onChange); 28 | return () => { 29 | // @ts-ignore 30 | // typings for 0.65.x not available yet 31 | dimsub?.remove(); 32 | }; 33 | }, [orientation, onChange]); 34 | return {screen: screenData, orientation}; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/appbar.tsx: -------------------------------------------------------------------------------- 1 | import React, {useContext, memo} from 'react'; 2 | import {Appbar} from 'react-native-paper'; 3 | import {Platform} from 'react-native'; 4 | import {UIContext} from '../contexts/ui'; 5 | 6 | export interface IAppBarProps { 7 | title: string; 8 | hasPrevious?: boolean; 9 | goBack: (count?: number) => void; 10 | } 11 | 12 | const ApplicationBar = memo(function (props: IAppBarProps) { 13 | const {title, hasPrevious, goBack} = props; 14 | const {state: uiState} = useContext(UIContext); 15 | let leftIcon = 'menu'; 16 | let leftAction = uiState.headersActions?.left; 17 | let rightAction = uiState.headersActions?.right; 18 | if (hasPrevious) { 19 | leftIcon = 'arrow-left'; 20 | leftAction = function () { 21 | goBack(title === 'Insight' ? 2 : 1); 22 | }; 23 | } 24 | const rightIcon = Platform.select({ 25 | android: 'dots-vertical', 26 | ios: 'dots-horizontal', 27 | }) as string; 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | }); 37 | 38 | export default ApplicationBar; 39 | -------------------------------------------------------------------------------- /src/contexts/auth.tsx: -------------------------------------------------------------------------------- 1 | import React, {useReducer} from 'react'; 2 | 3 | export interface IUser { 4 | id: string; 5 | } 6 | 7 | export interface IAuthState { 8 | user: IUser | null; 9 | initialized: boolean; 10 | } 11 | 12 | export interface IAuthAction { 13 | type: 'LOGIN' | 'LOGOUT'; 14 | payload: IAuthState; 15 | } 16 | 17 | export type IAuthContext = { 18 | state: IAuthState; 19 | dispatch: React.Dispatch; 20 | }; 21 | 22 | export const authReducer = (state: IAuthState, action: IAuthAction) => { 23 | switch (action.type) { 24 | case 'LOGIN': 25 | case 'LOGOUT': 26 | return {...state, ...action.payload}; 27 | default: 28 | return state; 29 | } 30 | }; 31 | 32 | const initialState: IAuthState = { 33 | user: null, 34 | initialized: false, 35 | }; 36 | 37 | export const AuthContext = React.createContext({ 38 | state: initialState, 39 | dispatch: () => {}, 40 | }); 41 | const {Provider} = AuthContext; 42 | 43 | const AuthProvider: React.FC<{children: React.ReactNode}> = ({children}) => { 44 | const [state, dispatch] = useReducer(authReducer, initialState); 45 | 46 | return {children}; 47 | }; 48 | 49 | export default AuthProvider; 50 | -------------------------------------------------------------------------------- /creds-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "creds-generator", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server/dist/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "watch::client": "./node_modules/.bin/tsc -w -p client/tsconfig.json", 9 | "watch::server": "./node_modules/.bin/tsc -w -p server/tsconfig.json", 10 | "build::client": "./node_modules/.bin/tsc -p client/tsconfig.json", 11 | "build::server": "./node_modules/.bin/tsc -p server/tsconfig.json", 12 | "build": "npm run build::client && npm run build::server", 13 | "start": "node server/dist/index.js", 14 | "prepare": "npm run build", 15 | "live-server": "five-server" 16 | }, 17 | "author": "", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "@types/crypto-js": "^3.1.47", 21 | "@types/express": "^4.17.13", 22 | "@types/qrcode": "^1.4.2", 23 | "@types/react": "^16.14.21", 24 | "@types/react-dom": "^16.9.14", 25 | "csstype": "^2.6.19", 26 | "five-server": "^0.1.3", 27 | "typescript": "^3.9.10" 28 | }, 29 | "dependencies": { 30 | "body-parser": "^1.19.1", 31 | "crypto-js": "^4.1.1", 32 | "express": "^4.17.2", 33 | "qrcode": "^1.5.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | java_pid* 33 | android/app/.project 34 | *.hprof 35 | 36 | # node.js 37 | # 38 | node_modules/ 39 | npm-debug.log 40 | yarn-error.log 41 | .npmrc 42 | 43 | # BUCK 44 | buck-out/ 45 | \.buckd/ 46 | *.keystore 47 | debug.keystore 48 | 49 | # fastlane 50 | # 51 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 52 | # screenshots whenever they are needed. 53 | # For more information about the recommended setup visit: 54 | # https://docs.fastlane.tools/best-practices/source-control/ 55 | 56 | **/fastlane/report.xml 57 | **/fastlane/Preview.html 58 | **/fastlane/screenshots 59 | **/fastlane/test_output 60 | 61 | # Bundle artifact 62 | *.jsbundle 63 | 64 | # Ruby / CocoaPods 65 | /ios/Pods/ 66 | /vendor/bundle/ 67 | 68 | # vscode 69 | .vscode 70 | 71 | # envs 72 | env.json -------------------------------------------------------------------------------- /src/contexts/ui.tsx: -------------------------------------------------------------------------------- 1 | import React, {useReducer} from 'react'; 2 | 3 | interface HeadersActions { 4 | left?(): void; 5 | right?(): void; 6 | } 7 | 8 | export interface IUIState { 9 | headersActions: HeadersActions | null; 10 | } 11 | 12 | type IHeaderAction = { 13 | type: 'SET'; 14 | payload: HeadersActions; 15 | }; 16 | 17 | type IUIAction = IHeaderAction; 18 | 19 | export type IUIContext = { 20 | state: IUIState; 21 | dispatch: React.Dispatch; 22 | }; 23 | 24 | export const uiReducer = (state: IUIState, action: IUIAction) => { 25 | switch (action.type) { 26 | case 'SET': 27 | return { 28 | ...state, 29 | headersActions: {...state.headersActions, ...action.payload}, 30 | }; 31 | default: 32 | return state; 33 | } 34 | }; 35 | 36 | const initialState: IUIState = { 37 | headersActions: null, 38 | }; 39 | 40 | export const UIContext = React.createContext({ 41 | state: initialState, 42 | dispatch: () => {}, 43 | }); 44 | const {Provider} = UIContext; 45 | 46 | const UIProvider: React.FC<{children: React.ReactNode}> = ({children}) => { 47 | const [state, dispatch] = useReducer(uiReducer, initialState); 48 | return {children}; 49 | }; 50 | 51 | export default UIProvider; 52 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/cpm/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.cpm; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.ReactRootView; 6 | 7 | public class MainActivity extends ReactActivity { 8 | 9 | /** 10 | * Returns the name of the main component registered from JavaScript. This is used to schedule 11 | * rendering of the component. 12 | */ 13 | @Override 14 | protected String getMainComponentName() { 15 | return "cpm"; 16 | } 17 | 18 | /** 19 | * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and 20 | * you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer 21 | * (Paper). 22 | */ 23 | @Override 24 | protected ReactActivityDelegate createReactActivityDelegate() { 25 | return new MainActivityDelegate(this, getMainComponentName()); 26 | } 27 | public static class MainActivityDelegate extends ReactActivityDelegate { 28 | public MainActivityDelegate(ReactActivity activity, String mainComponentName) { 29 | super(activity, mainComponentName); 30 | } 31 | @Override 32 | protected ReactRootView createRootView() { 33 | ReactRootView reactRootView = new ReactRootView(getContext()); 34 | return reactRootView; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bumpVersion.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ARGS_LENGTH=$# 3 | 4 | while test $ARGS_LENGTH -gt 0; do 5 | case "$1" in 6 | -m|--marketing-version) 7 | shift 8 | CUSTOM_MARKETING="$1" 9 | shift 10 | ;; 11 | -b|--bundle-version) 12 | shift 13 | CUSTOM_BUNDLE="$1" 14 | shift 15 | ;; 16 | *) 17 | break 18 | ;; 19 | esac 20 | done 21 | 22 | if [ -z $CUSTOM_BUNDLE ]; then 23 | BUILD_NAME="$BUILD_BUILDNUMBER" 24 | else 25 | BUILD_NAME="$CUSTOM_BUNDLE" 26 | fi 27 | 28 | echo "Build Name: $BUILD_NAME" 29 | echo "Version Name: $CUSTOM_MARKETING" 30 | 31 | 32 | if [ "$AGENT_OS" == "Darwin" ] # iOS build 33 | then 34 | # iOS 35 | cd ios 36 | CUR_VER_STRING=`agvtool what-marketing-version -terse1` 37 | IFS='.' read -ra CUR_VER_NUMB <<< "$CUR_VER_STRING" 38 | echo "Current Marketing Version: ${CUR_VER_NUMB[0]}.${CUR_VER_NUMB[1]}.${CUR_VER_NUMB[2]}" 39 | 40 | agvtool new-version -all $BUILD_NAME 41 | # if [ "$BUILD_SOURCEBRANCHNAME" == "master" ]; then 42 | # if [ -z "$CUSTOM_MARKETING" ]; then 43 | # NEW_MINOR=$(( ${CUR_VER_NUMB[2]} + 1 )) 44 | # agvtool new-marketing-version "${CUR_VER_NUMB[0]}.${CUR_VER_NUMB[1]}.${NEW_MINOR}" 45 | # else 46 | # agvtool new-marketing-version "$CUSTOM_MARKETING" 47 | # fi 48 | # fi 49 | fi -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.cpm", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.cpm", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 40 | 41 | 42 | 43 |
44 | 45 |
46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Walkthrough 2 | 3 | ## 1. Authorization 4 | ![authorization](../media/authorization.png) 5 | ![qrcode](../media/qrcode.png) 6 | 7 | Scan QR Code to connect to a device in IoT Central. 8 | 9 | To skip this, tap on "Use simulation". The application will still read data from BLE devices or health providers and show it on insight chart but it will not generate telemetry data for IoT Central. 10 | 11 | ## 3. Home 12 | ![home](../media/home.png) 13 | 14 | 15 | Select one of the options. 16 | - __Pair device__: allows to connect to a real or simulated BLE device. 17 | - __Sync with platform__: allows to read data from health platforms (Apple Health when running on iOS, Google Fit when on Android). 18 | 19 | ## 4. Scan devices 20 | ![scan](../media/scan.png) 21 | ![scan_simulated](../media/scan_simulated.png) 22 | ![simulated_result](../media/simulated_result.png) 23 | 24 | 25 | Scans for nearby devices. By selecting "Use simulated device" you can skip connecting a real device and use one of the simulated ones which generates sample data every 5 seconds. Simulated devices are mapped to models in the CPM application template (see [docs](https://docs.microsoft.com/en-us/azure/iot-central/healthcare/tutorial-continuous-patient-monitoring)). 26 | 27 | ## 5. Insight 28 | ![insight](../media/insight.jpeg) 29 | 30 | Data from BLE device or platforms are shown in the chart. Legend is at bottom. 31 | Detail message is hardcoded. The developer can change that text accordingly to read data. 32 | 33 | More details at [Insight.](./insight.md) -------------------------------------------------------------------------------- /docs/data_format.md: -------------------------------------------------------------------------------- 1 | # Data Format 2 | By default, the application only converts raw data to standard integers or floating point number using usual conversion from bytes. 3 | 4 | For non-standard data formats or to add data pre-processing features you can add your own custom device implementation. 5 | 6 | To enable a quick integration with custom devices, this sample is organized to support inheritance: it is sufficient to extend the BleDevice class and add the custom class to the ManufacturerMap. 7 | 8 | The _getValue_ function must be implemented. It accepts a ble characteristic instance and returns a number. 9 | 10 | health/custom/mydevice.ts 11 | ```ts 12 | class MyDevice extends BleDevice implements IHealthDevice { 13 | getValue(characteristic: Characteristic) { 14 | if (characteristic.value == null) { 15 | return null; 16 | } 17 | const val = Buffer.from(characteristic.value, 'base64'); 18 | 19 | const intval = val.readInt8(); 20 | return intval; 21 | } 22 | } 23 | 24 | ManufacturerMap.addManufacturer('MyDevice', ['0000180f-0000-1000-8000-00805f9b34fb', '00001809-0000-1000-8000-00805f9b34fb'], MyDevice); 25 | ``` 26 | 27 | The BleManager will create specific device instances based on available services. A list of service ids must be provided to the Manufacturers Map in order to create the right device instance. 28 | 29 | However multiple devices can provide the same service with different data format. To overcome this issue just create one custom virtual device and put logic into the _getValue_ function. 30 | 31 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 12 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/components/footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, StyleSheet, ViewStyle, Platform} from 'react-native'; 3 | import {IconButton} from 'react-native-paper'; 4 | import {useState} from 'react'; 5 | import {CPMText} from './typography'; 6 | 7 | const defaultTitle = 'Learn more about this technology'; 8 | export interface IFooterProps { 9 | text: string; 10 | title?: string; 11 | style?: ViewStyle; 12 | textColor?: string; 13 | } 14 | export function Footer(props: IFooterProps) { 15 | const [expanded, setExpanded] = useState(false); 16 | return ( 17 | 18 | { 21 | setExpanded(current => !current); 22 | }} 23 | size={20} 24 | color={props.textColor} 25 | /> 26 | {expanded ? ( 27 | 33 | {props.text} 34 | 35 | ) : ( 36 | 37 | {props.title ? props.title : defaultTitle} 38 | 39 | )} 40 | 41 | ); 42 | } 43 | 44 | const style = StyleSheet.create({ 45 | container: { 46 | justifyContent: 'center', 47 | alignItems: 'center', 48 | marginBottom: Platform.select({ 49 | android: 10, 50 | ios: 30, 51 | }), 52 | marginHorizontal: 30, 53 | }, 54 | }); 55 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import {Dimensions, Platform, PixelRatio} from 'react-native'; 2 | 3 | const {width: SCREEN_WIDTH} = Dimensions.get('window'); 4 | 5 | // based on iphone 5s's scale 6 | const scale = SCREEN_WIDTH / 350; 7 | 8 | export function normalize(size: number) { 9 | const newSize = size * scale; 10 | if (Platform.OS === 'ios') { 11 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 1; 12 | } else { 13 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2; 14 | } 15 | } 16 | 17 | export function camelToName(text: string): string { 18 | return ( 19 | text 20 | // insert a space before all caps 21 | .replace(/([A-Z])/g, ' $1') 22 | // uppercase the first character 23 | .replace(/^./, function (str) { 24 | return str.toUpperCase(); 25 | }) 26 | ); 27 | } 28 | 29 | export function snakeToName(text: string): string { 30 | return ( 31 | text 32 | .toLowerCase() 33 | // insert a space for every underscore 34 | .replace(/([_])/g, ' ') 35 | // uppercase the first character 36 | .replace(/^./, function (str) { 37 | return str.toUpperCase(); 38 | }) 39 | ); 40 | } 41 | 42 | export function dottedToName(text: string): string { 43 | return ( 44 | text 45 | .toLowerCase() 46 | // insert a space for every underscore 47 | .replace(/([.])/g, ' ') 48 | // uppercase the first character 49 | .replace(/^./, function (str) { 50 | return str.toUpperCase(); 51 | }) 52 | ); 53 | } 54 | 55 | export function bleToIoTCName(text: string): string { 56 | return text.replace(/[-]/g, '').replace(/^./, function (str) { 57 | return `ble${str}`; 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; Flow doesn't support platforms 12 | .*/Libraries/Utilities/LoadingView.js 13 | 14 | 15 | .*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$ 16 | 17 | [untyped] 18 | .*/node_modules/@react-native-community/cli/.*/.* 19 | 20 | [include] 21 | 22 | [libs] 23 | node_modules/react-native/interface.js 24 | node_modules/react-native/flow/ 25 | 26 | [options] 27 | emoji=true 28 | 29 | exact_by_default=true 30 | 31 | format.bracket_spacing=false 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | [lints] 48 | sketchy-null-number=warn 49 | sketchy-null-mixed=warn 50 | sketchy-number=warn 51 | untyped-type-import=warn 52 | nonstrict-import=warn 53 | deprecated-type=warn 54 | unsafe-getters-setters=warn 55 | unnecessary-invariant=warn 56 | signature-verification-failure=warn 57 | 58 | [strict] 59 | deprecated-type 60 | nonstrict-import 61 | sketchy-null 62 | unclear-type 63 | unsafe-getters-setters 64 | untyped-import 65 | untyped-type-import 66 | 67 | [version] 68 | ^0.176.3 -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '12.4' 5 | install! 'cocoapods', :deterministic_uuids => false 6 | 7 | permissions_path = '../node_modules/react-native-permissions/ios' 8 | pod 'Permission-Camera', :path => "#{permissions_path}/Camera" 9 | pod 'RNCharts', :path => '../node_modules/react-native-charts-wrapper' 10 | 11 | target 'cpm' do 12 | config = use_native_modules! 13 | 14 | # Flags change depending on the env values. 15 | flags = get_default_flags() 16 | 17 | use_react_native!( 18 | :path => config[:reactNativePath], 19 | # to enable hermes on iOS, change `false` to `true` and then install pods 20 | :hermes_enabled => flags[:hermes_enabled], 21 | :fabric_enabled => flags[:fabric_enabled], 22 | # An absolute path to your application root. 23 | :app_path => "#{Pod::Config.instance.installation_root}/.." 24 | ) 25 | 26 | target 'cpmTests' do 27 | inherit! :complete 28 | # Pods for testing 29 | end 30 | 31 | # Enables Flipper. 32 | # 33 | # Note that if you have use_frameworks! enabled, Flipper will not work and 34 | # you should disable the next line. 35 | use_flipper!() 36 | 37 | post_install do |installer| 38 | react_native_post_install(installer) 39 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 40 | # Error when archiving. Workaround. 41 | # Opened issue: https://github.com/facebook/react-native/issues/30335 42 | installer.pods_project.targets.each do |target| 43 | if target.name == "React-Core.common-AccessibilityResources" 44 | target.remove_from_project 45 | end 46 | end 47 | # End of workaround 48 | end 49 | end -------------------------------------------------------------------------------- /src/models.ts: -------------------------------------------------------------------------------- 1 | export type DataAvailableCallback = ( 2 | itemId: string, 3 | value: any, 4 | itemName?: string, 5 | ) => void; 6 | export interface IHealthManager { 7 | startScan(onDeviceFound: (device: IHealthDevice) => void): void; 8 | stopScan(): void; 9 | connect(deviceId: string): Promise; 10 | } 11 | 12 | export type DeviceType = 'real' | 'simulated' | 'platform'; 13 | export interface IHealthDevice { 14 | name: string; 15 | id: string; 16 | paired: boolean; 17 | connected: boolean; 18 | type: DeviceType; 19 | fetch(): Promise; 20 | disconnect(): Promise; 21 | items?: IHealthItem[]; 22 | addListener( 23 | eventType: string, 24 | listener: (...args: any[]) => any, 25 | context?: any, 26 | ): void; 27 | removeListener(eventType: string, listener: (...args: any[]) => any): void; 28 | } 29 | 30 | export interface IHealthItem { 31 | id: string; 32 | name?: string; 33 | parentId?: string; 34 | value: any | undefined; 35 | enabled: boolean; 36 | enable(status: boolean): Promise; 37 | getData?(): any; 38 | } 39 | 40 | export type ItemData = { 41 | itemId: string; 42 | itemName?: string; 43 | value: any; 44 | }; 45 | 46 | export type MonitoredDevice = IHealthDevice & { 47 | monitorIds: { 48 | [itemId: string]: number; // the value is the id for the setInterval loop. Used to clear it 49 | }; 50 | }; 51 | 52 | const bluetoothHealthServices = [ 53 | '180d', 54 | '1810', 55 | '181f', 56 | '1808', 57 | '1809', 58 | '180d', 59 | '183a', 60 | '181d', 61 | ]; 62 | 63 | export function isHealthService(serviceUUID: string) { 64 | if ( 65 | bluetoothHealthServices.indexOf(serviceUUID.substring(4, 8).toLowerCase()) > 66 | -1 67 | ) { 68 | return true; 69 | } 70 | return false; 71 | } 72 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = '31.0.0' 6 | minSdkVersion = 21 7 | compileSdkVersion = 31 8 | targetSdkVersion = 31 9 | if (System.properties['os.arch'] == "aarch64") { 10 | // For M1 Users we need to use the NDK 24 which added support for aarch64 11 | ndkVersion = "24.0.8215888" 12 | } else { 13 | // Otherwise we default to the side-by-side NDK version from AGP. 14 | ndkVersion = "21.4.7075529" 15 | } 16 | } 17 | repositories { 18 | google() 19 | mavenCentral() 20 | } 21 | dependencies { 22 | classpath('com.android.tools.build:gradle:7.1.2') 23 | classpath("com.facebook.react:react-native-gradle-plugin") 24 | classpath("de.undercouch:gradle-download-task:5.0.1") 25 | 26 | // NOTE: Do not place your application dependencies here; they belong 27 | // in the individual module build.gradle files 28 | } 29 | } 30 | 31 | allprojects { 32 | repositories { 33 | mavenCentral() 34 | mavenLocal() 35 | maven { 36 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 37 | url("$rootDir/../node_modules/react-native/android") 38 | } 39 | maven { 40 | // Android JSC is installed from npm 41 | url("$rootDir/../node_modules/jsc-android/dist") 42 | } 43 | mavenCentral { 44 | // We don't want to fetch react-native from Maven Central as there are 45 | // older versions over there. 46 | content { 47 | excludeGroup "com.facebook.react" 48 | } 49 | } 50 | google() 51 | maven { url 'https://www.jitpack.io' } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/components/qrcodeMask.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View} from 'react-native'; 3 | import {useScreenDimensions} from '../hooks/layout'; 4 | export default function QRCodeMask() { 5 | const {screen} = useScreenDimensions(); 6 | 7 | const markerWidth = Math.floor(screen.width / 2); 8 | const sectorWidth = Math.floor(markerWidth / 5); 9 | return ( 10 | 17 | 29 | 41 | 53 | 65 | 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /creds-generator/server/dist/public/fonts/font-awesome-4.7.0/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /media/HealthDevice.json: -------------------------------------------------------------------------------- 1 | { 2 | "@id": "urn:lucacpm:HealthDevice:1", 3 | "@type": "CapabilityModel", 4 | "implements": [ 5 | { 6 | "@id": "urn:lucacpm:HealthDevice:m0bszgacs:1", 7 | "@type": "InterfaceInstance", 8 | "displayName": { 9 | "en": "Telemetry" 10 | }, 11 | "name": "telemetry", 12 | "schema": { 13 | "@id": "urn:lucacpm:HealthDevice:telemetry:2", 14 | "@type": "Interface", 15 | "displayName": { 16 | "en": "Telemetry" 17 | }, 18 | "contents": [ 19 | { 20 | "@id": "urn:lucacpm:HealthDevice:temperature:1", 21 | "@type": [ 22 | "Telemetry", 23 | "SemanticType/Temperature" 24 | ], 25 | "displayName": { 26 | "en": "Temperature" 27 | }, 28 | "name": "ble00002A1C00001000800000805f9b34fb", 29 | "schema": "double" 30 | }, 31 | { 32 | "@id": "urn:lucacpm:HealthDevice:heartrate:1", 33 | "@type": "Telemetry", 34 | "displayName": { 35 | "en": "Heart Rate" 36 | }, 37 | "name": "ble00002A3700001000800000805f9b34fb", 38 | "displayUnit": { 39 | "en": "BPM" 40 | }, 41 | "schema": "double" 42 | }, 43 | { 44 | "@id": "urn:lucacpm:HealthDevice:pressure:1", 45 | "@type": [ 46 | "Telemetry", 47 | "SemanticType/Pressure" 48 | ], 49 | "displayName": { 50 | "en": "Blood Pressure" 51 | }, 52 | "name": "ble00002A3500001000800000805f9b34fb", 53 | "schema": "double" 54 | } 55 | ] 56 | } 57 | } 58 | ], 59 | "displayName": { 60 | "en": "HealthDevice" 61 | }, 62 | "@context": [ 63 | "http://azureiot.com/v1/contexts/IoTModel.json" 64 | ] 65 | } -------------------------------------------------------------------------------- /patches/react-native+0.69.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/react-native/index.js b/node_modules/react-native/index.js 2 | index d59ba34..1bc8c9d 100644 3 | --- a/node_modules/react-native/index.js 4 | +++ b/node_modules/react-native/index.js 5 | @@ -435,32 +435,16 @@ module.exports = { 6 | }, 7 | // Deprecated Prop Types 8 | get ColorPropType(): $FlowFixMe { 9 | - invariant( 10 | - false, 11 | - 'ColorPropType has been removed from React Native. Migrate to ' + 12 | - "ColorPropType exported from 'deprecated-react-native-prop-types'.", 13 | - ); 14 | + return require('deprecated-react-native-prop-types').ColorPropType; 15 | }, 16 | get EdgeInsetsPropType(): $FlowFixMe { 17 | - invariant( 18 | - false, 19 | - 'EdgeInsetsPropType has been removed from React Native. Migrate to ' + 20 | - "EdgeInsetsPropType exported from 'deprecated-react-native-prop-types'.", 21 | - ); 22 | + return require('deprecated-react-native-prop-types').EdgeInsetsPropType; 23 | }, 24 | get PointPropType(): $FlowFixMe { 25 | - invariant( 26 | - false, 27 | - 'PointPropType has been removed from React Native. Migrate to ' + 28 | - "PointPropType exported from 'deprecated-react-native-prop-types'.", 29 | - ); 30 | + return require('deprecated-react-native-prop-types').PointPropType; 31 | }, 32 | get ViewPropTypes(): $FlowFixMe { 33 | - invariant( 34 | - false, 35 | - 'ViewPropTypes has been removed from React Native. Migrate to ' + 36 | - "ViewPropTypes exported from 'deprecated-react-native-prop-types'.", 37 | - ); 38 | + return require('deprecated-react-native-prop-types').ViewPropTypes; 39 | }, 40 | }; 41 | 42 | diff --git a/node_modules/react-native/scripts/.packager.env b/node_modules/react-native/scripts/.packager.env 43 | new file mode 100644 44 | index 0000000..361f5fb 45 | --- /dev/null 46 | +++ b/node_modules/react-native/scripts/.packager.env 47 | @@ -0,0 +1 @@ 48 | +export RCT_METRO_PORT=8081 49 | -------------------------------------------------------------------------------- /src/components/utils.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, processColor} from 'react-native'; 3 | import {ActivityIndicator, Portal, Dialog, Button} from 'react-native-paper'; 4 | import {Detail, Headline} from './typography'; 5 | import DefaultStyles from '../styles'; 6 | import GetConnected from '../assets/home_connected_icon.svg'; 7 | import {ReactDispatch} from '../types'; 8 | 9 | export function Loading() { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export function ErrorDialog(props: { 18 | visible: boolean; 19 | setVisible: ReactDispatch; 20 | title: string; 21 | text: string; 22 | }) { 23 | const {visible, setVisible, text, title} = props; 24 | return ( 25 | 26 | setVisible(false)}> 27 | {title} 28 | 29 | {text} 30 | 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | } 38 | 39 | export function GetConnectedHeader() { 40 | const header = 'Get connected'; 41 | const sub = 'Pair a device or sync your data'; 42 | return ( 43 | 44 | 45 | 46 | {header} 47 | {sub} 48 | 49 | 50 | ); 51 | } 52 | 53 | export function getRandomColor() { 54 | return processColor( 55 | `rgb(${Math.floor(Math.random() * 256)},${Math.floor( 56 | Math.random() * 256, 57 | )},${Math.floor(Math.random() * 256)})`, 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /ios/cpmTests/cpmTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface cpmTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation cpmTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /ios/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {enableScreens} from 'react-native-screens'; 3 | import { 4 | Provider as PaperProvider, 5 | DefaultTheme, 6 | configureFonts, 7 | } from 'react-native-paper'; 8 | import Welcome from './screens/welcome'; 9 | import ConfigProvider, {ConfigContext} from './contexts/config'; 10 | import Registration from './screens/registration'; 11 | import Main from './screens/main'; 12 | import {StatusBar} from 'react-native'; 13 | import {NavigationContainer} from '@react-navigation/native'; 14 | import AuthProvider from './contexts/auth'; 15 | import UIProvider from './contexts/ui'; 16 | import {useContext} from 'react'; 17 | 18 | enableScreens(); 19 | 20 | const fontConfig: {} = { 21 | default: { 22 | regular: { 23 | fontFamily: 'Roboto', 24 | fontWeight: 'normal', 25 | fontSize: 14, 26 | }, 27 | medium: { 28 | fontFamily: 'sans-serif-medium', 29 | fontWeight: 'normal', 30 | }, 31 | light: { 32 | fontFamily: 'sans-serif-light', 33 | fontWeight: 'normal', 34 | }, 35 | thin: { 36 | fontFamily: 'sans-serif-thin', 37 | fontWeight: 'normal', 38 | }, 39 | }, 40 | }; 41 | 42 | const theme = { 43 | ...DefaultTheme, 44 | roundness: 2, 45 | colors: { 46 | ...DefaultTheme.colors, 47 | accent: '#75FBFD', 48 | primary: '#3783c5', 49 | background: 'transparent', 50 | text: '#1F529D', 51 | placeholder: '#1F529D', 52 | }, 53 | fonts: configureFonts(fontConfig), 54 | }; 55 | 56 | export default function App() { 57 | return ( 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ); 71 | } 72 | 73 | const Root = React.memo(() => { 74 | const {state} = useContext(ConfigContext); 75 | if (state.centralClient !== undefined) { 76 | return
; 77 | } else if (state.initialized) { 78 | return ; 79 | } 80 | return ; 81 | }); 82 | -------------------------------------------------------------------------------- /src/components/typography.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Text} from 'react-native-paper'; 3 | import {TextProps} from 'react-native'; 4 | import {normalize} from '../utils'; 5 | 6 | interface Props extends TextProps { 7 | children?: any; 8 | theme?: any; 9 | } 10 | 11 | export function Headline(props: Props) { 12 | const {children, style, ...textProps} = props; 13 | return ( 14 | 18 | {props.children} 19 | 20 | ); 21 | } 22 | 23 | export function CPMText(props: Props) { 24 | const {children, style, ...textProps} = props; 25 | return ( 26 | 30 | {props.children} 31 | 32 | ); 33 | } 34 | 35 | export function Name(props: Props) { 36 | const {children, style, ...textProps} = props; 37 | return ( 38 | 50 | {props.children} 51 | 52 | ); 53 | } 54 | 55 | export function Item(props: Props) { 56 | const {children, style, ...textProps} = props; 57 | return ( 58 | 65 | {props.children} 66 | 67 | ); 68 | } 69 | 70 | export function Detail(props: Props) { 71 | const {children, style, ...textProps} = props; 72 | 73 | return ( 74 | 78 | {props.children} 79 | 80 | ); 81 | } 82 | 83 | export function Action(props: Props) { 84 | const {children, style, ...textProps} = props; 85 | 86 | return ( 87 | 91 | {props.children} 92 | 93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /src/hooks/common.ts: -------------------------------------------------------------------------------- 1 | import {useRef, useEffect, useState} from 'react'; 2 | import {useIsFocused, useNavigation} from '@react-navigation/native'; 3 | import {NavigationProperty} from 'types'; 4 | 5 | export type EnvironmentVariables = { 6 | GoogleFit?: boolean; 7 | AppleHealth?: boolean; 8 | } & {[x: string]: any}; 9 | 10 | export function usePrevious(value: T) { 11 | // The ref object is a generic container whose current property is mutable ... 12 | // ... and can hold any value, similar to an instance property on a class 13 | const ref = useRef(); 14 | 15 | // Store current value in ref 16 | useEffect(() => { 17 | ref.current = value; 18 | }, [value]); // Only re-run if value changes 19 | 20 | // Return previous value (happens before update in useEffect above) 21 | return ref.current; 22 | } 23 | 24 | export function useEnv() { 25 | const [variables, setVariables] = useState({ 26 | GoogleFit: true, 27 | AppleHealth: true, 28 | }); 29 | 30 | const readEnv = async function () { 31 | try { 32 | const envvars = (await import('../../env.json')).default; 33 | console.log(`Envvars: ${JSON.stringify(envvars)}`); 34 | setVariables(vars => ({...vars, ...envvars})); 35 | } catch (e) { 36 | setVariables(vars => ({ 37 | ...vars, 38 | ...{ 39 | GoogleFit: true, 40 | AppleHealth: true, 41 | }, 42 | })); 43 | } 44 | }; 45 | useEffect(() => { 46 | readEnv(); 47 | }, []); 48 | 49 | return variables; 50 | } 51 | 52 | /** 53 | * Get a timer returning 'true' when triggers. 54 | * @param seconds Number of seconds before timer triggers. 55 | */ 56 | export function useTimer(seconds: number) { 57 | const [done, setDone] = useState(false); 58 | useEffect(() => { 59 | setTimeout(() => { 60 | setDone(true); 61 | }, seconds); 62 | }, [seconds]); 63 | return done; 64 | } 65 | 66 | export function useHeaderTitle(title: string): void { 67 | const isFocused = useIsFocused(); 68 | const navigation = useNavigation(); 69 | const counter = useRef(0); 70 | 71 | useEffect(() => { 72 | if (isFocused) { 73 | counter.current++; 74 | navigation.setParams({title}); 75 | } 76 | }, [navigation, title, isFocused]); 77 | } 78 | -------------------------------------------------------------------------------- /src/contexts/config.tsx: -------------------------------------------------------------------------------- 1 | import React, {useReducer} from 'react'; 2 | import {IIoTCClient} from 'react-native-azure-iotcentral-client'; 3 | import {IHealthManager, IHealthDevice} from '../models'; 4 | import {ChartUpdateCallback} from '../types'; 5 | 6 | export interface IConfigState { 7 | initialized: boolean; 8 | device: IHealthDevice | null; 9 | centralClient?: IIoTCClient | null; 10 | healthManager: IHealthManager | null; 11 | insightUpdate: ChartUpdateCallback | null; 12 | } 13 | 14 | type CommonAction = { 15 | type: 'INIT'; 16 | payload: boolean; 17 | }; 18 | 19 | type IIoTCAction = { 20 | type: 'CONNECT' | 'DISCONNECT'; 21 | payload: IIoTCClient | null; 22 | }; 23 | type IHealthAction = { 24 | type: 'ACTIVATE' | 'UNACTIVATE'; 25 | payload: IHealthManager | null; 26 | }; 27 | 28 | type IDeviceAction = { 29 | type: 'HEALTH_CONNECT' | 'HEALTH_DISCONNECT'; 30 | payload: IHealthDevice | null; 31 | }; 32 | 33 | type IConfigAction = IDeviceAction | IHealthAction | IIoTCAction | CommonAction; 34 | 35 | export type IConfigContext = { 36 | state: IConfigState; 37 | dispatch: React.Dispatch; 38 | }; 39 | 40 | export const configReducer = (state: IConfigState, action: IConfigAction) => { 41 | switch (action.type) { 42 | case 'INIT': 43 | return {...state, initialized: action.payload}; 44 | case 'CONNECT': 45 | case 'DISCONNECT': 46 | return {...state, centralClient: action.payload}; 47 | case 'HEALTH_CONNECT': 48 | case 'HEALTH_DISCONNECT': 49 | return {...state, device: action.payload}; 50 | case 'ACTIVATE': 51 | case 'UNACTIVATE': 52 | return {...state, healthManager: action.payload}; 53 | default: 54 | return state; 55 | } 56 | }; 57 | 58 | const initialState: IConfigState = { 59 | initialized: false, 60 | device: null, 61 | centralClient: undefined, 62 | healthManager: null, 63 | insightUpdate: null, 64 | }; 65 | 66 | export const ConfigContext = React.createContext({ 67 | state: initialState, 68 | dispatch: () => {}, 69 | }); 70 | const {Provider} = ConfigContext; 71 | 72 | const ConfigProvider: React.FC<{children: React.ReactNode}> = ({children}) => { 73 | const [state, dispatch] = useReducer(configReducer, initialState); 74 | return {children}; 75 | }; 76 | 77 | export default ConfigProvider; 78 | -------------------------------------------------------------------------------- /src/screens/providers.tsx: -------------------------------------------------------------------------------- 1 | import React, {useContext, useEffect} from 'react'; 2 | import {ConfigContext} from '../contexts/config'; 3 | import {Loading} from '../components/utils'; 4 | import {Platform, View} from 'react-native'; 5 | import {AppleHealthManager} from '../health/appleHealth'; 6 | import {IHealthManager} from '../models'; 7 | import {GoogleFitManager} from '../health/googleFit'; 8 | import {Headline} from '../components/typography'; 9 | import {useNavigation} from '@react-navigation/native'; 10 | import {NavigationProperty, Screens} from '../types'; 11 | import {DATA_AVAILABLE_EVENT} from '../health/ble'; 12 | import {sendTelemetryData} from '../api/central'; 13 | 14 | const Manager = Platform.select< 15 | typeof AppleHealthManager | typeof GoogleFitManager 16 | >({ 17 | android: GoogleFitManager, 18 | ios: AppleHealthManager, 19 | }) as typeof AppleHealthManager | typeof GoogleFitManager; 20 | 21 | export default function Providers() { 22 | const {state, dispatch} = useContext(ConfigContext); 23 | const {navigate} = useNavigation(); 24 | 25 | useEffect(() => { 26 | const initManager = async () => { 27 | if (state.healthManager) { 28 | state.healthManager.startScan(async device => { 29 | const dev = await (state.healthManager as IHealthManager).connect(''); 30 | navigate(Screens.INSIGHT_SCREEN); 31 | await dev.fetch(); 32 | 33 | if (state.centralClient) { 34 | dev.addListener( 35 | DATA_AVAILABLE_EVENT, 36 | sendTelemetryData.bind(null, state.centralClient, false), 37 | ); 38 | } 39 | dispatch({ 40 | type: 'HEALTH_CONNECT', 41 | payload: dev, 42 | }); 43 | }); 44 | } else { 45 | const payload = new Manager(); 46 | dispatch({ 47 | type: 'ACTIVATE', 48 | payload, 49 | }); 50 | } 51 | }; 52 | initManager(); 53 | }, [state.healthManager, dispatch, navigate, state.centralClient]); 54 | 55 | if (!state.healthManager) { 56 | return ( 57 | 58 | 59 | 62 | Loading provider data ... 63 | 64 | 65 | ); 66 | } 67 | 68 | return null; 69 | } 70 | -------------------------------------------------------------------------------- /ios/cpm/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | $(PRODUCT_NAME) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSBluetoothAlwaysUsageDescription 39 | CPM would like to use bluetooth to search for nearby devices 40 | NSCameraUsageDescription 41 | CPM would like to access your camera to scan QR codes. 42 | NSHealthShareUsageDescription 43 | Read and understand health data. 44 | NSHealthUpdateUsageDescription 45 | Share workout data with other apps. 46 | UIAppFonts 47 | 48 | Ionicons.ttf 49 | MaterialIcons.ttf 50 | MaterialCommunityIcons.ttf 51 | 52 | UILaunchStoryboardName 53 | LaunchScreen 54 | UIMainStoryboardFile 55 | LaunchScreen 56 | UIRequiredDeviceCapabilities 57 | 58 | armv7 59 | 60 | UISupportedInterfaceOrientations 61 | 62 | UIInterfaceOrientationPortrait 63 | UIInterfaceOrientationLandscapeLeft 64 | UIInterfaceOrientationLandscapeRight 65 | 66 | UIUserInterfaceStyle 67 | Light 68 | UIViewControllerBasedStatusBarAppearance 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /media/Apple_HealthKit.json: -------------------------------------------------------------------------------- 1 | { 2 | "@id": "urn:cpm:AppleHealthKit_5rt:1", 3 | "@type": "CapabilityModel", 4 | "implements": [ 5 | { 6 | "@id": "urn:cpm:AppleHealthKit_5rt:480u2qoun:1", 7 | "@type": "InterfaceInstance", 8 | "displayName": { 9 | "en": "Interface" 10 | }, 11 | "name": "health_telemetry", 12 | "schema": { 13 | "@id": "urn:cpm:HealthData:1", 14 | "@type": "Interface", 15 | "displayName": { 16 | "en": "Interface" 17 | }, 18 | "contents": [ 19 | { 20 | "@id": "urn:cpm:HealthData:BodyTemperature:1", 21 | "@type": [ 22 | "Telemetry", 23 | "SemanticType/Temperature" 24 | ], 25 | "displayName": { 26 | "en": "Body Temperature" 27 | }, 28 | "name": "BodyTemperature", 29 | "schema": "double" 30 | }, 31 | { 32 | "@id": "urn:cpm:HealthData:HeartRate:1", 33 | "@type": "Telemetry", 34 | "displayName": { 35 | "en": "Heart Rate" 36 | }, 37 | "name": "HeartRate", 38 | "schema": "integer" 39 | }, 40 | { 41 | "@id": "urn:cpm:HealthData:BloodPressureDiastolic:1", 42 | "@type": [ 43 | "Telemetry", 44 | "SemanticType/Pressure" 45 | ], 46 | "displayName": { 47 | "en": "Blood Pressure Diastolic" 48 | }, 49 | "name": "BloodPressureDiastolic", 50 | "schema": "double" 51 | }, 52 | { 53 | "@id": "urn:cpm:HealthData:BloodPressureSystolic:1", 54 | "@type": [ 55 | "Telemetry", 56 | "SemanticType/Pressure" 57 | ], 58 | "displayName": { 59 | "en": "Blood Pressure Systolic" 60 | }, 61 | "name": "BloodPressureSystolic", 62 | "schema": "double" 63 | }, 64 | { 65 | "@id": "urn:cpm:HealthData:StepCount:1", 66 | "@type": "Telemetry", 67 | "displayName": { 68 | "en": "StepCount" 69 | }, 70 | "name": "StepCount", 71 | "schema": "integer" 72 | } 73 | ] 74 | } 75 | } 76 | ], 77 | "displayName": { 78 | "en": "Apple HealthKit" 79 | }, 80 | "@context": [ 81 | "http://azureiot.com/v1/contexts/IoTModel.json" 82 | ] 83 | } -------------------------------------------------------------------------------- /docs/google_fit.md: -------------------------------------------------------------------------------- 1 | # Google Fit 2 | 3 | ## 1. Add Item 4 | 5 | To enable telemetries, add an entry to the _GOOGLE_ITEMS_ enum in [_googleFit.ts_](../src/health/googleFit.ts#L27). 6 | 7 | ```ts 8 | enum GOOGLE_ITEMS { 9 | STEPS = 'steps', 10 | BLOOD_PRESSURE_SYSTOLIC = 'bloodPressureSystolic', 11 | BLOOD_PRESSURE_DIASTOLIC = 'bloodPressureDiastolic', 12 | ... 13 | } 14 | ``` 15 | 16 | ## 2. Add types 17 | 18 | To leverage type definitions, add result types to [_types.ts_](../src/types.ts). 19 | Follow specs from the official documentation of the [_react-native-google-fit_](https://github.com/StasDoskalenko/react-native-google-fit#usage) library for available items and result types. 20 | 21 | ## 3. Add data handlers 22 | 23 | Each fit scope requires a different function to be called in order to fetch samples. 24 | Then add handling code for the specific item by adding a case in [_googleFit.ts_](../src/health/googleFit.ts#L183). 25 | 26 | ```ts 27 | case GOOGLE_ITEMS.STEPS: 28 | results = (await GoogleFit.getDailyStepCountSamples({ 29 | startDate: startDate.toISOString(), 30 | endDate: new Date().toISOString(), 31 | })) as GoogleFitStepResult[]; 32 | results.forEach(result => { 33 | ... 34 | ... 35 | this.eventEmitter.emit(DATA_AVAILABLE_EVENT, { 36 | itemId: item.id, 37 | value: item.value ?? 0, 38 | itemName: item.name, 39 | }); 40 | break; 41 | ... 42 | ... 43 | ``` 44 | 45 | ## 4. Add items to IoT Central model (optional) 46 | 47 | Every item added to the _GOOGLE_ITEMS_ enum will be send to IoT Central using the item Id as telemetry name. 48 | 49 | ```ts 50 | enum GOOGLE_ITEMS { 51 | STEPS = 'steps', 52 | BLOOD_PRESSURE_SYSTOLIC = 'bloodPressureSystolic', 53 | BLOOD_PRESSURE_DIASTOLIC = 'bloodPressureDiastolic', 54 | } 55 | ``` 56 | 57 | ```json 58 | ... 59 | { 60 | "@id": "dtmi:cpm:GoogleFit_5r4:steps;1", 61 | "@type": "Telemetry", 62 | "displayName": { 63 | "en": "Steps" 64 | }, 65 | "name": "steps", 66 | "schema": "double" 67 | }, 68 | { 69 | "@id": "dtmi:cpm:GoogleFit_5r4:BloodPressureSystolic;1", 70 | "@type": "Telemetry", 71 | "displayName": { 72 | "en": "Blood Pressure Systolic" 73 | }, 74 | "name": "BloodPressureSystolic", 75 | "schema": "double" 76 | }, 77 | { 78 | "@id": "dtmi:cpm:GoogleFit_5r4:BloodPressureDiastolic;1", 79 | "@type": "Telemetry", 80 | "displayName": { 81 | "en": "Blood Pressure Diastolic" 82 | }, 83 | "name": "BloodPressureDiastolic", 84 | "schema": "double" 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /src/screens/welcome.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, StyleSheet} from 'react-native'; 3 | import {Text} from 'react-native-paper'; 4 | import LogoTitle from '../assets/logo_title.svg'; 5 | import LinearGradient from 'react-native-linear-gradient'; 6 | import ProgressCircleSnail from 'react-native-progress/CircleSnail'; 7 | import {useScreenDimensions} from 'hooks/layout'; 8 | import {useEffect} from 'react'; 9 | import {useContext} from 'react'; 10 | import {ConfigContext} from 'contexts/config'; 11 | 12 | const title = 'Welcome to connected care.'; 13 | 14 | const theme = { 15 | colors: {placeholder: 'white', text: 'white'}, 16 | }; 17 | 18 | export default React.memo(() => { 19 | const {screen} = useScreenDimensions(); 20 | const {dispatch} = useContext(ConfigContext); 21 | 22 | useEffect(() => { 23 | setTimeout(() => { 24 | dispatch({ 25 | type: 'INIT', 26 | payload: true, 27 | }); 28 | }, 4000); 29 | }, [dispatch]); 30 | 31 | return ( 32 | 33 | 34 | 35 | 36 | {title} 37 | 38 | {/* 39 | {subTitle} 40 | */} 41 | 42 | 51 | 52 | ); 53 | }); 54 | 55 | const style = StyleSheet.create({ 56 | container: { 57 | alignItems: 'center', 58 | flex: 1, 59 | justifyContent: 'center', 60 | }, 61 | logo: { 62 | alignItems: 'center', 63 | marginBottom: 50, 64 | }, 65 | logoSub: { 66 | fontSize: 20, 67 | fontWeight: 'bold', 68 | color: 'white', 69 | }, 70 | logoDetails: { 71 | fontSize: 15, 72 | color: 'white', 73 | }, 74 | form: { 75 | width: '80%', 76 | flex: 2, 77 | alignItems: 'center', 78 | }, 79 | forgot: { 80 | textAlign: 'right', 81 | marginTop: 6, 82 | }, 83 | text: { 84 | color: 'white', 85 | width: '100%', 86 | backgroundColor: 'transparent', 87 | }, 88 | button: { 89 | width: 120, 90 | marginVertical: 50, 91 | backgroundColor: 'white', 92 | }, 93 | progress: { 94 | marginVertical: 5, 95 | }, 96 | }); 97 | -------------------------------------------------------------------------------- /creds-generator/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import Express from 'express'; 2 | import * as path from 'path'; 3 | import bodyParser from 'body-parser'; 4 | import QRCode from 'qrcode'; 5 | import CryptoJS from 'crypto-js' 6 | import * as fs from 'fs'; 7 | 8 | 9 | export enum CredentialTypes { 10 | QRCODE = 1, 11 | NUMERIC = 2, 12 | ALL = 3 13 | } 14 | export type Credentials = { 15 | deviceId: string, 16 | modelId: string, 17 | patientId: string, 18 | deviceKey: string, 19 | scopeId: string, 20 | types: CredentialTypes 21 | } 22 | 23 | 24 | const app = Express(); 25 | const port = process.env.PORT || 3700; 26 | const dbPath = path.join(__dirname, '../db.json'); 27 | if (!fs.existsSync(dbPath)) { 28 | fs.writeFileSync(dbPath, '{}'); 29 | } 30 | let db = JSON.parse(fs.readFileSync(dbPath).toString()); 31 | // define a route handler for the default home page 32 | 33 | app.use(bodyParser.urlencoded({ extended: true })); 34 | app.use(bodyParser.json()); 35 | app.use(Express.static(path.join(__dirname, 'public'))); 36 | 37 | app.get('/', (req, res) => { 38 | res.sendFile(path.join(__dirname, './public/index.html')); 39 | }); 40 | app.post('/creds', async (req, res) => { 41 | const creds: Credentials = req.body; 42 | if (!creds['scopeId']) { 43 | res.status(500).send('Invalid body'); 44 | return; 45 | } 46 | let response: { qrCode?: string, numeric?: number } = {}; 47 | let credType = +creds.types; 48 | const { types, ...credentials } = creds; 49 | let cr = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(JSON.stringify(credentials))); 50 | const codeStr = creds.patientId ? CryptoJS.AES.encrypt(cr, creds.patientId).toString() : cr; 51 | if (credType == CredentialTypes.ALL.valueOf() || credType == CredentialTypes.QRCODE.valueOf()) { 52 | response['qrCode'] = await QRCode.toDataURL(codeStr); 53 | } 54 | if (credType == CredentialTypes.ALL.valueOf() || credType == CredentialTypes.NUMERIC.valueOf()) { 55 | const numCode = Math.floor(Math.random() * 100000); 56 | db[numCode] = codeStr; 57 | fs.writeFileSync(dbPath, JSON.stringify(db)); 58 | response['numeric'] = numCode; 59 | } 60 | 61 | res.send(response); 62 | }); 63 | app.get('/numeric', (req, res) => { 64 | let numeric = req.query.numeric as string; 65 | if (db[numeric]) { 66 | res.send(db[numeric]); 67 | } 68 | else { 69 | res.send(-1); 70 | } 71 | }); 72 | 73 | // start the Express server 74 | app.listen(port, () => { 75 | console.log(`server started at http://localhost:${port}`); 76 | }); -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import {NativeStackNavigationProp} from '@react-navigation/native-stack'; 2 | import {DrawerNavigationProp} from '@react-navigation/drawer'; 3 | import { 4 | LineData, 5 | LineValue, 6 | LineDatasetConfig, 7 | } from 'react-native-charts-wrapper'; 8 | import {ItemData} from './models'; 9 | 10 | export const Screens = { 11 | DEVICES_SCREEN: 'Devices', 12 | INSIGHT_SCREEN: 'Insight', 13 | HOME_SCREEN: 'Home', 14 | PROVIDERS_SCREEN: 'Health Provider', 15 | }; 16 | 17 | export const DrawerScreens = { 18 | MAIN: 'Main', 19 | }; 20 | // Type for getting the values of an object (lookup) 21 | export type valueof = T[keyof T]; 22 | 23 | export type ReactDispatch = React.Dispatch>; 24 | export type ContextDispatch = React.Dispatch; 25 | 26 | /** 27 | * Parameters available for all routes 28 | */ 29 | export type NavigationParams = { 30 | title?: string; 31 | backTitle?: string; 32 | titleColor?: string; 33 | headerLeft?: any; 34 | icon?: { 35 | name: string; 36 | type: string; 37 | }; 38 | previousScreen?: string; 39 | }; 40 | 41 | /** 42 | * Defines type of screens 43 | */ 44 | export type NavigationScreens = { 45 | [k in valueof]: NavigationParams | undefined; 46 | }; 47 | 48 | export type DrawerNavigationScreens = { 49 | [k in valueof]: NavigationParams | undefined; 50 | }; 51 | 52 | export type ScreenNames = typeof Screens[keyof typeof Screens]; 53 | export type DrawerScreenNames = 54 | typeof DrawerScreens[keyof typeof DrawerScreens]; 55 | /** 56 | * Defines type of navigator properties 57 | */ 58 | export type NavigationProperty = NativeStackNavigationProp< 59 | NavigationScreens, 60 | ScreenNames 61 | >; 62 | 63 | export type DrawerProperty = DrawerNavigationProp< 64 | DrawerNavigationScreens, 65 | DrawerScreenNames 66 | >; 67 | 68 | /** 69 | * Chart typings 70 | */ 71 | export interface ExtendedLineData extends LineData { 72 | dataSets: { 73 | itemId: string; 74 | values?: Array; 75 | label?: string; 76 | config?: LineDatasetConfig; 77 | }[]; 78 | } 79 | 80 | export type ChartUpdateCallback = (itemdata: ItemData) => void; 81 | 82 | /** 83 | * Health typings 84 | */ 85 | 86 | export const HealthRealTimeData = { 87 | Walking: 'Walking', 88 | StairClimbing: 'StairClimbing', 89 | Running: 'Running', 90 | Cycling: 'Cycling', 91 | Workout: 'Workout', 92 | } as const; 93 | 94 | export type GoogleFitStepResult = { 95 | source: string; 96 | steps: { 97 | date: string; 98 | value: number; 99 | }[]; 100 | }; 101 | 102 | export type GoogleFitBloodPressureResult = { 103 | systolic: number; 104 | diastolic: number; 105 | endDate: string; 106 | startDate: string; 107 | day: string; 108 | }; 109 | -------------------------------------------------------------------------------- /src/assets/ios_icons/iOS/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "20x20", 5 | "idiom": "iphone", 6 | "filename": "launcher-20@2x.png", 7 | "scale": "2x" 8 | }, 9 | { 10 | "size": "20x20", 11 | "idiom": "iphone", 12 | "filename": "launcher-20@3x.png", 13 | "scale": "3x" 14 | }, 15 | { 16 | "size": "20x20", 17 | "idiom": "ipad", 18 | "filename": "launcher-20.png", 19 | "scale": "1x" 20 | }, 21 | { 22 | "size": "20x20", 23 | "idiom": "ipad", 24 | "filename": "launcher-20@2x.png", 25 | "scale": "2x" 26 | }, 27 | { 28 | "size": "29x29", 29 | "idiom": "iphone", 30 | "filename": "launcher-29@2x.png", 31 | "scale": "2x" 32 | }, 33 | { 34 | "size": "29x29", 35 | "idiom": "iphone", 36 | "filename": "launcher-29@3x.png", 37 | "scale": "3x" 38 | }, 39 | { 40 | "size": "40x40", 41 | "idiom": "iphone", 42 | "filename": "launcher-40@2x.png", 43 | "scale": "2x" 44 | }, 45 | { 46 | "size": "40x40", 47 | "idiom": "iphone", 48 | "filename": "launcher-40@3x.png", 49 | "scale": "3x" 50 | }, 51 | { 52 | "size": "60x60", 53 | "idiom": "iphone", 54 | "filename": "launcher-60@2x.png", 55 | "scale": "2x" 56 | }, 57 | { 58 | "size": "60x60", 59 | "idiom": "iphone", 60 | "filename": "launcher-60@3x.png", 61 | "scale": "3x" 62 | }, 63 | { 64 | "size": "29x29", 65 | "idiom": "ipad", 66 | "filename": "launcher-29.png", 67 | "scale": "1x" 68 | }, 69 | { 70 | "size": "29x29", 71 | "idiom": "ipad", 72 | "filename": "launcher-29@2x.png", 73 | "scale": "2x" 74 | }, 75 | { 76 | "size": "40x40", 77 | "idiom": "ipad", 78 | "filename": "launcher-40.png", 79 | "scale": "1x" 80 | }, 81 | { 82 | "size": "40x40", 83 | "idiom": "ipad", 84 | "filename": "launcher-40@2x.png", 85 | "scale": "2x" 86 | }, 87 | { 88 | "size": "76x76", 89 | "idiom": "ipad", 90 | "filename": "launcher-76.png", 91 | "scale": "1x" 92 | }, 93 | { 94 | "size": "76x76", 95 | "idiom": "ipad", 96 | "filename": "launcher-76@2x.png", 97 | "scale": "2x" 98 | }, 99 | { 100 | "size": "83.5x83.5", 101 | "idiom": "ipad", 102 | "filename": "launcher-83.5@2x.png", 103 | "scale": "2x" 104 | }, 105 | { 106 | "size": "1024x1024", 107 | "idiom": "ios-marketing", 108 | "filename": "launcher-1024.png", 109 | "scale": "1x" 110 | } 111 | ], 112 | "info": { 113 | "version": 1, 114 | "author": "xcode" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem http://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto execute 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto execute 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :execute 65 | @rem Setup the command line 66 | 67 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 68 | 69 | @rem Execute Gradle 70 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 71 | 72 | :end 73 | @rem End local scope for the variables with windows NT shell 74 | if "%ERRORLEVEL%"=="0" goto mainEnd 75 | 76 | :fail 77 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 78 | rem the _cmd.exe /c_ return code! 79 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 80 | exit /b 1 81 | 82 | :mainEnd 83 | if "%OS%"=="Windows_NT" endlocal 84 | 85 | :omega 86 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.5) 5 | rexml 6 | activesupport (6.1.6.1) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 1.6, < 2) 9 | minitest (>= 5.1) 10 | tzinfo (~> 2.0) 11 | zeitwerk (~> 2.3) 12 | addressable (2.8.0) 13 | public_suffix (>= 2.0.2, < 5.0) 14 | algoliasearch (1.27.5) 15 | httpclient (~> 2.8, >= 2.8.3) 16 | json (>= 1.5.1) 17 | atomos (0.1.3) 18 | claide (1.1.0) 19 | cocoapods (1.11.3) 20 | addressable (~> 2.8) 21 | claide (>= 1.0.2, < 2.0) 22 | cocoapods-core (= 1.11.3) 23 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 24 | cocoapods-downloader (>= 1.4.0, < 2.0) 25 | cocoapods-plugins (>= 1.0.0, < 2.0) 26 | cocoapods-search (>= 1.0.0, < 2.0) 27 | cocoapods-trunk (>= 1.4.0, < 2.0) 28 | cocoapods-try (>= 1.1.0, < 2.0) 29 | colored2 (~> 3.1) 30 | escape (~> 0.0.4) 31 | fourflusher (>= 2.3.0, < 3.0) 32 | gh_inspector (~> 1.0) 33 | molinillo (~> 0.8.0) 34 | nap (~> 1.0) 35 | ruby-macho (>= 1.0, < 3.0) 36 | xcodeproj (>= 1.21.0, < 2.0) 37 | cocoapods-core (1.11.3) 38 | activesupport (>= 5.0, < 7) 39 | addressable (~> 2.8) 40 | algoliasearch (~> 1.0) 41 | concurrent-ruby (~> 1.1) 42 | fuzzy_match (~> 2.0.4) 43 | nap (~> 1.0) 44 | netrc (~> 0.11) 45 | public_suffix (~> 4.0) 46 | typhoeus (~> 1.0) 47 | cocoapods-deintegrate (1.0.5) 48 | cocoapods-downloader (1.6.3) 49 | cocoapods-plugins (1.0.0) 50 | nap 51 | cocoapods-search (1.0.1) 52 | cocoapods-trunk (1.6.0) 53 | nap (>= 0.8, < 2.0) 54 | netrc (~> 0.11) 55 | cocoapods-try (1.2.0) 56 | colored2 (3.1.2) 57 | concurrent-ruby (1.1.10) 58 | escape (0.0.4) 59 | ethon (0.15.0) 60 | ffi (>= 1.15.0) 61 | ffi (1.15.5) 62 | fourflusher (2.3.1) 63 | fuzzy_match (2.0.4) 64 | gh_inspector (1.1.3) 65 | httpclient (2.8.3) 66 | i18n (1.12.0) 67 | concurrent-ruby (~> 1.0) 68 | json (2.6.2) 69 | minitest (5.16.2) 70 | molinillo (0.8.0) 71 | nanaimo (0.3.0) 72 | nap (1.1.0) 73 | netrc (0.11.0) 74 | public_suffix (4.0.7) 75 | rexml (3.2.5) 76 | ruby-macho (2.5.1) 77 | typhoeus (1.4.0) 78 | ethon (>= 0.9.0) 79 | tzinfo (2.0.5) 80 | concurrent-ruby (~> 1.0) 81 | xcodeproj (1.22.0) 82 | CFPropertyList (>= 2.3.3, < 4.0) 83 | atomos (~> 0.1.3) 84 | claide (>= 1.0.2, < 2.0) 85 | colored2 (~> 3.1) 86 | nanaimo (~> 0.3.0) 87 | rexml (~> 3.2.4) 88 | zeitwerk (2.6.0) 89 | 90 | PLATFORMS 91 | ruby 92 | 93 | DEPENDENCIES 94 | cocoapods (~> 1.11, >= 1.11.2) 95 | 96 | RUBY VERSION 97 | ruby 2.7.6p219 98 | 99 | BUNDLED WITH 100 | 2.1.4 101 | -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "launcher-20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "launcher-20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "launcher-29@2x.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "launcher-29@3x.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "launcher-40@2x.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "launcher-40@3x.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "launcher-60@2x.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "launcher-60@3x.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "launcher-20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "launcher-20@2x.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "launcher-29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "launcher-29@2x.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "launcher-40.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "launcher-40@2x.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "launcher-76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "launcher-76@2x.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "launcher-83.5@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "launcher-1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /docs/simulation.md: -------------------------------------------------------------------------------- 1 | # Simulation 2 | This application comes with a simulation feature that allows a complete experience without a physical BLE device or a real connection to IoT Central. Due to their limitations, this is a good option when testing on Android or iOS simulators. 3 | 4 | ## Simulate BLE device 5 | Two types of simulated devices are available in the application: "Smart Knee Brace" and "Smart Vitals Patch". This models reflect default models in the Azure IoT Central CPM application template (read more on Continuous Patient Monitoring [here](https://docs.microsoft.com/en-gb/azure/iot-central/healthcare/overview-iot-central-healthcare#what-is-continuous-patient-monitoring-template)). 6 | 7 | Both Knee Brace and Vitals Patch simulated devices send out all available telemetry items, however the CPM application template only shows some of them by default (Device Temperature and Battery Level). 8 | You can enable/disable items to be shown in IoT Central from the "Device templates" tab. 9 | 10 | ![edit_dashboard_1](../media/edit_dashboard_1.png) 11 | ![edit_dashboard_2](../media/edit_dashboard_2.png) 12 | 13 | Either add/remove items from the available tile as shown above or create new tiles. 14 | More details available in [official documentation.](https://docs.microsoft.com/en-gb/azure/iot-central/core/howto-set-up-template). 15 | 16 | 17 | ### Limitations 18 | The current version of the application doesn't support device re-mapping after switching between different simulated device models. 19 | This means that if a simulated device has been associated through credentials with a device in IoT Central and the user goes back selecting another simulated device of different type, a new device will not be created in IoT Central and data will not be available. 20 | We suggest you restart the application and generate new credentials to create/associate a different device in IoT Central. 21 | 22 | Possible scenarios: 23 | - Different templates (not working): 24 | 1. User generates credentials for a "Smart Knee Brace" device in Central. 25 | 2. User selects a "Smart Knee Brace" device in the mobile application. 26 | 3. Data starts flowing. 27 | 4. User goes back and selects a simulated "Smart Vitals Patch" device in the mobile application. 28 | 5. Data does not show in IoT Central (besides Device Temperature and Battery Level) 29 | - Same template (working): 30 | 1. User generates credentials for a "Smart Knee Brace" device in Central. 31 | 2. User selects a "Smart Knee Brace" device in the mobile application. 32 | 3. Data starts flowing. 33 | 4. User goes back and selects another simulated "Smart Knee Brace" device in the mobile application. 34 | 5. Data shows in IoT Central in the same device 35 | 36 | 37 | 38 | ## Simulate IoT Central connection 39 | If you don't have an IoT Central application, network is not available or simply want to see data in mobile application but not sending any telemetry out, you can skip connection to IoT Central by selecting "Use simulated code" in either QR code or numeric code screens. 40 | 41 | ![qrcode](../media/qrcode.png) 42 | ![numeric](../media/numeric.png) 43 | 44 | Since mobile application is not connected to any device in IoT Central, you can change device at any time during application run. 45 | 46 | 47 | -------------------------------------------------------------------------------- /ios/cpm/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import 3 | #import 4 | #import 5 | #import 6 | #if RCT_NEW_ARCH_ENABLED 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | static NSString *const kRNConcurrentRoot = @"concurrentRoot"; 15 | @interface AppDelegate () { 16 | RCTTurboModuleManager *_turboModuleManager; 17 | RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; 18 | std::shared_ptr _reactNativeConfig; 19 | facebook::react::ContextContainer::Shared _contextContainer; 20 | } 21 | @end 22 | #endif 23 | @implementation AppDelegate 24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 25 | { 26 | RCTAppSetupPrepareApp(application); 27 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 28 | #if RCT_NEW_ARCH_ENABLED 29 | _contextContainer = std::make_shared(); 30 | _reactNativeConfig = std::make_shared(); 31 | _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); 32 | _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; 33 | bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; 34 | #endif 35 | NSDictionary *initProps = [self prepareInitialProps]; 36 | UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"cpm", initProps); 37 | if (@available(iOS 13.0, *)) { 38 | rootView.backgroundColor = [UIColor systemBackgroundColor]; 39 | } else { 40 | rootView.backgroundColor = [UIColor whiteColor]; 41 | } 42 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 43 | UIViewController *rootViewController = [UIViewController new]; 44 | rootViewController.view = rootView; 45 | self.window.rootViewController = rootViewController; 46 | [self.window makeKeyAndVisible]; 47 | return YES; 48 | } 49 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 50 | /// 51 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 52 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 53 | /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. 54 | - (BOOL)concurrentRootEnabled 55 | { 56 | // Switch this bool to turn on and off the concurrent root 57 | return true; 58 | } 59 | - (NSDictionary *)prepareInitialProps 60 | { 61 | NSMutableDictionary *initProps = [NSMutableDictionary new]; 62 | 63 | return initProps; 64 | } 65 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 66 | { 67 | #if DEBUG 68 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 69 | #else 70 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 71 | #endif 72 | } 73 | 74 | @end -------------------------------------------------------------------------------- /android/app/src/main/java/com/cpm/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.cpm; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactInstanceManager; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.react.config.ReactFeatureFlags; 11 | import com.facebook.soloader.SoLoader; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.util.List; 14 | import com.facebook.react.bridge.JSIModulePackage; // https://github.com/facebook/react-native/issues/32111 15 | import com.swmansion.reanimated.ReanimatedJSIModulePackage; // https://github.com/facebook/react-native/issues/32111 16 | 17 | public class MainApplication extends Application implements ReactApplication { 18 | 19 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 20 | @Override 21 | public boolean getUseDeveloperSupport() { 22 | return BuildConfig.DEBUG; 23 | } 24 | 25 | @Override 26 | protected List getPackages() { 27 | @SuppressWarnings("UnnecessaryLocalVariable") 28 | List packages = new PackageList(this).getPackages(); 29 | // Packages that cannot be autolinked yet can be added manually here, for 30 | // example: 31 | // packages.add(new MyReactNativePackage()); 32 | return packages; 33 | } 34 | 35 | @Override 36 | protected String getJSMainModuleName() { 37 | return "index"; 38 | } 39 | 40 | @Override 41 | protected JSIModulePackage getJSIModulePackage() { 42 | return new ReanimatedJSIModulePackage(); 43 | } 44 | 45 | }; 46 | 47 | @Override 48 | public ReactNativeHost getReactNativeHost() { 49 | return mReactNativeHost; 50 | } 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | SoLoader.init(this, /* native exopackage */ false); 56 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 57 | } 58 | 59 | /** 60 | * Loads Flipper in React Native templates. Call this in the onCreate method 61 | * with something like initializeFlipper(this, 62 | * getReactNativeHost().getReactInstanceManager()); 63 | * 64 | * @param context 65 | * @param reactInstanceManager 66 | */ 67 | private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 68 | if (BuildConfig.DEBUG) { 69 | try { 70 | /* 71 | * We use reflection here to pick up the class that initializes Flipper, since 72 | * Flipper library is not available in release mode 73 | */ 74 | Class aClass = Class.forName("com.cpm.ReactNativeFlipper"); 75 | aClass.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class).invoke(null, context, 76 | reactInstanceManager); 77 | } catch (ClassNotFoundException e) { 78 | e.printStackTrace(); 79 | } catch (NoSuchMethodException e) { 80 | e.printStackTrace(); 81 | } catch (IllegalAccessException e) { 82 | e.printStackTrace(); 83 | } catch (InvocationTargetException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cpm", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "tsc --noEmit && eslint --ext .js,.jsx,.ts,.tsx ./src", 11 | "format": "prettier --write src/", 12 | "build": "npm run format && npm run lint && tsc", 13 | "podinstall": "node podinstall.js", 14 | "postinstall": "npm run prepackage && patch-package", 15 | "prepackage": "node pre-packager.js" 16 | }, 17 | "dependencies": { 18 | "@react-native-community/masked-view": "^0.1.10", 19 | "@react-navigation/drawer": "^6.1.4", 20 | "@react-navigation/material-bottom-tabs": "^6.0.5", 21 | "@react-navigation/native": "^6.0.10", 22 | "@react-navigation/native-stack": "^6.6.2", 23 | "buffer": "^5.6.0", 24 | "deprecated-react-native-prop-types": "^2.3.0", 25 | "events": "^3.2.0", 26 | "patch-package": "^6.4.7", 27 | "react": "^18.0.0", 28 | "react-native": "^0.69.1", 29 | "react-native-azure-iotcentral-client": "1.1.7", 30 | "react-native-ble-plx": "^2.0.3", 31 | "react-native-camera": "^4.1.1", 32 | "react-native-charts-wrapper": "^0.5.7", 33 | "react-native-gesture-handler": "^2.3.0", 34 | "react-native-get-random-values": "^1.4.0", 35 | "react-native-google-fit": "^0.18.2", 36 | "react-native-health": "^1.12.0", 37 | "react-native-linear-gradient": "^2.5.6", 38 | "react-native-paper": "^4.9.2", 39 | "react-native-permissions": "^3.3.0", 40 | "react-native-progress": "^5.0.0", 41 | "react-native-qrcode-scanner": "^1.3.1", 42 | "react-native-reanimated": "^2.9.1", 43 | "react-native-safe-area-context": "^4.2.5", 44 | "react-native-screens": "^3.13.1", 45 | "react-native-svg": "^12.1.1", 46 | "react-native-vector-icons": "^9.0.0" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.12.9", 50 | "@babel/runtime": "^7.12.5", 51 | "@react-native-community/eslint-config": "^3.0.0", 52 | "@types/react": "^17.0.19", 53 | "@types/react-native": "^0.64.13", 54 | "@types/react-native-charts-wrapper": "^0.5.0", 55 | "@types/react-native-linear-gradient": "^2.4.0", 56 | "@types/react-native-vector-icons": "^6.4.5", 57 | "@typescript-eslint/eslint-plugin": "^4.30.0", 58 | "@typescript-eslint/parser": "^4.30.0", 59 | "@welldone-software/why-did-you-render": "^4.2.5", 60 | "babel-jest": "^26.6.3", 61 | "babel-plugin-module-resolver": "^4.1.0", 62 | "eslint": "^7.32.0", 63 | "eslint-plugin-react": "^7.25.1", 64 | "eslint-plugin-react-hooks": "^4.2.0", 65 | "fs": "^0.0.1-security", 66 | "jest": "^26.6.3", 67 | "metro-react-native-babel-preset": "^0.70.3", 68 | "prettier": "^2.3.2", 69 | "react-native-codegen": "0.0.7", 70 | "react-native-svg-transformer": "^1.0.0", 71 | "react-test-renderer": "^18.0.0", 72 | "typescript": "^4.4.2" 73 | }, 74 | "jest": { 75 | "preset": "react-native", 76 | "transform": { 77 | "^.+\\.js$": "/node_modules/react-native/jest/preprocessor.js", 78 | "\\.(ts|tsx)$": "ts-jest" 79 | }, 80 | "globals": { 81 | "ts-jest": { 82 | "tsConfig": "tsconfig.jest.json" 83 | } 84 | }, 85 | "moduleFileExtensions": [ 86 | "ts", 87 | "tsx", 88 | "js" 89 | ], 90 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$" 91 | }, 92 | "overrides": { 93 | "react-native-google-fit": { 94 | "moment": "2.29.4" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ios/cpm.xcodeproj/xcshareddata/xcschemes/cpm.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /ios/cpm/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "filename" : "LaunchImage-1242x2688.png", 6 | "idiom" : "iphone", 7 | "minimum-system-version" : "12.0", 8 | "orientation" : "portrait", 9 | "scale" : "3x", 10 | "subtype" : "2688h" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "filename" : "LaunchImage-2688x1242.png", 15 | "idiom" : "iphone", 16 | "minimum-system-version" : "12.0", 17 | "orientation" : "landscape", 18 | "scale" : "3x", 19 | "subtype" : "2688h" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "filename" : "LaunchImage-828x1792.png", 24 | "idiom" : "iphone", 25 | "minimum-system-version" : "12.0", 26 | "orientation" : "portrait", 27 | "scale" : "2x", 28 | "subtype" : "1792h" 29 | }, 30 | { 31 | "extent" : "full-screen", 32 | "filename" : "LaunchImage-1792x828.png", 33 | "idiom" : "iphone", 34 | "minimum-system-version" : "12.0", 35 | "orientation" : "landscape", 36 | "scale" : "2x", 37 | "subtype" : "1792h" 38 | }, 39 | { 40 | "extent" : "full-screen", 41 | "filename" : "LaunchImage-1125x2436.png", 42 | "idiom" : "iphone", 43 | "minimum-system-version" : "11.0", 44 | "orientation" : "portrait", 45 | "scale" : "3x", 46 | "subtype" : "2436h" 47 | }, 48 | { 49 | "extent" : "full-screen", 50 | "filename" : "LaunchImage-2436x1125.png", 51 | "idiom" : "iphone", 52 | "minimum-system-version" : "11.0", 53 | "orientation" : "landscape", 54 | "scale" : "3x", 55 | "subtype" : "2436h" 56 | }, 57 | { 58 | "extent" : "full-screen", 59 | "filename" : "LaunchImage-1242x2208.png", 60 | "idiom" : "iphone", 61 | "minimum-system-version" : "8.0", 62 | "orientation" : "portrait", 63 | "scale" : "3x", 64 | "subtype" : "736h" 65 | }, 66 | { 67 | "extent" : "full-screen", 68 | "filename" : "LaunchImage-2208x1242.png", 69 | "idiom" : "iphone", 70 | "minimum-system-version" : "8.0", 71 | "orientation" : "landscape", 72 | "scale" : "3x", 73 | "subtype" : "736h" 74 | }, 75 | { 76 | "extent" : "full-screen", 77 | "filename" : "LaunchImage-750x1334.png", 78 | "idiom" : "iphone", 79 | "minimum-system-version" : "8.0", 80 | "orientation" : "portrait", 81 | "scale" : "2x", 82 | "subtype" : "667h" 83 | }, 84 | { 85 | "extent" : "full-screen", 86 | "filename" : "LaunchImage-640x960.png", 87 | "idiom" : "iphone", 88 | "minimum-system-version" : "7.0", 89 | "orientation" : "portrait", 90 | "scale" : "2x" 91 | }, 92 | { 93 | "extent" : "full-screen", 94 | "filename" : "LaunchImage-640x1136.png", 95 | "idiom" : "iphone", 96 | "minimum-system-version" : "7.0", 97 | "orientation" : "portrait", 98 | "scale" : "2x", 99 | "subtype" : "retina4" 100 | }, 101 | { 102 | "extent" : "full-screen", 103 | "filename" : "LaunchImage-320x480.png", 104 | "idiom" : "iphone", 105 | "orientation" : "portrait", 106 | "scale" : "1x" 107 | }, 108 | { 109 | "extent" : "full-screen", 110 | "filename" : "LaunchImage-640x960-1.png", 111 | "idiom" : "iphone", 112 | "orientation" : "portrait", 113 | "scale" : "2x" 114 | }, 115 | { 116 | "extent" : "full-screen", 117 | "filename" : "LaunchImage-640x1136-1.png", 118 | "idiom" : "iphone", 119 | "orientation" : "portrait", 120 | "scale" : "2x", 121 | "subtype" : "retina4" 122 | } 123 | ], 124 | "info" : { 125 | "author" : "xcode", 126 | "version" : 1 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/components/insightDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React, {useContext} from 'react'; 2 | import {SafeAreaView, View, StyleSheet, Image} from 'react-native'; 3 | import {Name, Headline, Detail, Item} from './typography'; 4 | import {Switch, IconButton, Divider} from 'react-native-paper'; 5 | import {ConfigContext} from '../contexts/config'; 6 | import {ScrollView} from 'react-native-gesture-handler'; 7 | import {AppleHealthManager} from '../health/appleHealth'; 8 | import {GoogleFitManager} from '../health/googleFit'; 9 | import {Screens} from 'types'; 10 | interface DrawerProps { 11 | sourceSide: 'left' | 'right'; 12 | currentScreen: string; 13 | close(): void; 14 | } 15 | 16 | /** 17 | * This navigator doesn't actually navigate to any screen. 18 | * It is used to have a drawer for chart management by levereging on what react-navigation already offers (gestures, styles...). 19 | * @param props 20 | */ 21 | export default function InsightDrawer(props: DrawerProps) { 22 | const {state, dispatch} = useContext(ConfigContext); 23 | const {currentScreen} = props; 24 | let icon: any = 'bluetooth'; 25 | 26 | if (state.healthManager) { 27 | if (state.healthManager instanceof AppleHealthManager) { 28 | icon = ({size}: {size: number}) => ( 29 | 33 | ); 34 | } else if (state.healthManager instanceof GoogleFitManager) { 35 | icon = ({size}: {size: number}) => ( 36 | 40 | ); 41 | } 42 | } 43 | 44 | if ( 45 | !state.device || 46 | !state.device.items || 47 | currentScreen !== Screens.INSIGHT_SCREEN 48 | ) { 49 | return null; 50 | } 51 | return ( 52 | 53 | 54 | 59 | 60 | 61 | Sync options 62 | { 64 | props.close(); 65 | }} 66 | icon="chevron-left" 67 | style={{marginLeft: 40, marginTop: -5}} 68 | /> 69 | 70 | Which kind of device data would you like to show? 71 | 72 | 73 | {state.device.name} 74 | 75 | 76 | {state.device.items.map((item, index) => ( 77 | 78 | {item.name} 79 | {/* pass extra parameter to the ref in order to process and enable only valid ids */} 80 | { 84 | await item.enable(current); 85 | // dispatch is needed to update state of device items 86 | dispatch({ 87 | type: 'HEALTH_CONNECT', 88 | payload: state.device, 89 | }); 90 | }} 91 | /> 92 | 93 | ))} 94 | 95 | 96 | ); 97 | } 98 | 99 | const style = StyleSheet.create({ 100 | container: { 101 | flex: 1, 102 | marginLeft: 20, 103 | }, 104 | header: { 105 | marginTop: 30, 106 | flexDirection: 'row', 107 | }, 108 | itemContainer: { 109 | flexDirection: 'row', 110 | alignItems: 'center', 111 | justifyContent: 'space-between', 112 | padding: 20, 113 | }, 114 | }); 115 | -------------------------------------------------------------------------------- /ios/cpm/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.azuredevops/resign.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | vmImage: 'macOS-12' 3 | 4 | name: $(Date:yyyyMMdd)$(Rev:.r) 5 | jobs: 6 | - job: iOS_Release 7 | timeoutInMinutes: 120 8 | variables: 9 | rootPath: '$(System.DefaultWorkingDirectory)' 10 | agentName: $[ dependencies.Setup.outputs['passOutput.AgentName'] ] 11 | outputPath: '$(rootPath)/output' 12 | exportPath: '$(rootPath)/output/ios_unsigned' 13 | zipToSign: '$(exportPath)/cpm.zip' 14 | signedExtractedPath: '$(exportPath)/signed' 15 | steps: 16 | - task: DownloadBuildArtifacts@0 17 | inputs: 18 | buildType: 'specific' # Options: current, specific 19 | project: 'One' # Required when buildType == Specific 20 | pipeline: '216276' # Required when buildType == Specific 21 | #specificBuildWithTriggering: false # Optional 22 | buildVersionToDownload: 'specific' # Required when buildType == Specific. Options: latest, latestFromBranch, specific 23 | allowPartiallySucceededBuilds: true # Optional 24 | #branchName: 'refs/heads/master' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch 25 | buildId: '55245310' # Required when buildType == Specific && BuildVersionToDownload == Specific 26 | #tags: # Optional 27 | downloadType: 'single' # Choose whether to download a single artifact or all artifacts of a specific build. Options: single, specific 28 | artifactName: 'ios_unsigned' # Required when downloadType == Single 29 | #itemPattern: '**' # Optional 30 | downloadPath: '$(outputPath)' 31 | #cleanDestinationFolder: false # Optional 32 | #parallelizationLimit: '8' # Optional 33 | #extractTars: false # Optional 34 | 35 | # - task: DeleteFiles@1 36 | # inputs: 37 | # SourceFolder: '$(archivePath)' 38 | # Contents: 'ExportOptions.plist' 39 | #RemoveSourceFolder: # Optional 40 | - task: DownloadSecureFile@1 41 | displayName: 'Download distribution profile from Azure secure files storage' 42 | inputs: 43 | secureFile: '$(Distribution_Provisioning_Profile)' 44 | retryCount: 5 45 | 46 | - task: CopyFiles@2 47 | displayName: 'Copy distribution provisioning profile' 48 | inputs: 49 | SourceFolder: '$(Agent.TempDirectory)' 50 | Contents: '$(Distribution_Provisioning_Profile)' 51 | TargetFolder: '$(exportPath)' 52 | flattenFolders: true 53 | 54 | - task: CopyFiles@2 55 | displayName: 'Copy ExportOptions' 56 | inputs: 57 | SourceFolder: '$(rootPath)' 58 | Contents: 'ExportOptions.plist' 59 | TargetFolder: '$(exportPath)' 60 | flattenFolders: true 61 | 62 | - task: ArchiveFiles@2 63 | displayName: 'Create zip for signing' 64 | inputs: 65 | rootFolderOrFile: '$(exportPath)' 66 | includeRootFolder: false 67 | archiveFile: '$(zipToSign)' 68 | 69 | - task: UseDotNet@2 70 | displayName: 'Use .NET Core sdk(temporary)' 71 | inputs: 72 | packageType: sdk 73 | version: 2.1.x 74 | installationPath: $(Agent.ToolsDirectory)/dotnet 75 | 76 | - task: EsrpCodeSigning@1 77 | displayName: 'Sign build' 78 | inputs: 79 | ConnectedServiceName: 'IoT PnP PaaD' 80 | FolderPath: '$(exportPath)' 81 | Pattern: '*.zip' 82 | signConfigType: 'inlineSignParams' 83 | inlineOperation: | 84 | [ 85 | { 86 | "KeyCode" : "CP-464234-Apple", 87 | "OperationCode" : "iOSAppSign", 88 | "Parameters" : {}, 89 | "ToolName" : "sign", 90 | "ToolVersion" : "1.0" 91 | } 92 | ] 93 | SessionTimeout: '60' 94 | MaxConcurrency: '50' 95 | MaxRetryAttempts: '5' 96 | 97 | - task: ExtractFiles@1 98 | displayName: 'Extract signed build' 99 | inputs: 100 | archiveFilePatterns: '$(zipToSign)' 101 | destinationFolder: '$(signedExtractedPath)' 102 | 103 | - task: PublishBuildArtifacts@1 104 | displayName: 'Publish Signed Ipa' 105 | inputs: 106 | PathtoPublish: '$(signedExtractedPath)' 107 | ArtifactName: 'ios_signed' 108 | publishLocation: 'Container' -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /src/assets/logo_title.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | --------------------------------------------------------------------------------