Compare commits
3 Commits
ifit-virtu
...
chart
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b73003316d | ||
|
|
2bbbd0b9bf | ||
|
|
1f03a5e10b |
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -32,10 +32,5 @@ If applicable, add screenshots to help explain your problem.
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Append a debug log**
|
||||
|
||||
Follow this guide https://github.com/cagnulein/qdomyos-zwift/wiki/How-do-i-get-the-debug-log-in-case-something-doesn't-work%3F
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[REQ]"
|
||||
labels: enhancement
|
||||
assignees: cagnulein
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
43
.github/workflows/main.yml
vendored
@@ -2,19 +2,13 @@
|
||||
|
||||
name: CI
|
||||
|
||||
env:
|
||||
DISPLAY: ':99'
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master, github-workflow-playground ]
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: "0 */12 * * *"
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
@@ -39,39 +33,28 @@ jobs:
|
||||
# path: '${{ github.workspace }}/output/android/'
|
||||
# key: ${{ runner.os }}-QtCache-Android
|
||||
|
||||
- name: Xvfb install and run
|
||||
run: |
|
||||
sudo apt-get install -y xvfb
|
||||
Xvfb -ac ${{ env.DISPLAY }} -screen 0 1280x780x24 &
|
||||
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: bluetiger9/SmtpClient-for-Qt
|
||||
path: "src/smtpclient/"
|
||||
|
||||
- name: Install packages required to run QZ inside workflow
|
||||
run: sudo apt update -y && sudo apt-get install -y qt5-default libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-default libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
|
||||
# Runs a set of commands using the runners shell
|
||||
- name: Install Qt Linux Desktop
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: '5.12.9'
|
||||
host: 'linux'
|
||||
target: 'desktop'
|
||||
modules: 'qtcharts debug_info qtnetworkauth'
|
||||
dir: '${{ github.workspace }}/output/linux-desktop/'
|
||||
# cached: ${{ steps.cache-qt-linux-desktop.outputs.cache-hit }}
|
||||
|
||||
- name: Compile Linux Desktop
|
||||
run: cd src; qmake; make -j8
|
||||
run: cd src; qmake; make -j4
|
||||
|
||||
- name: Archive linux-desktop binary
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linux-desktop-binary
|
||||
path: src/qdomyos-zwift
|
||||
|
||||
# - name: Test Peloton API
|
||||
# if: github.event_name == 'push' || github.event_name == 'schedule'
|
||||
# run: cd /home/runner/work/qdomyos-zwift/qdomyos-zwift/src/; ./qdomyos-zwift -test-peloton -peloton-username ${{ secrets.peloton_username }} -peloton-password ${{ secrets.peloton_password }}
|
||||
# timeout-minutes: 2
|
||||
|
||||
# - name: Test Home Fitness Buddy API
|
||||
# run: cd /home/runner/work/qdomyos-zwift/qdomyos-zwift/src/; ./qdomyos-zwift -test-hfb
|
||||
# timeout-minutes: 2
|
||||
path: src/qdomyos-zwift
|
||||
|
||||
# - uses: actions/checkout@v2
|
||||
# with:
|
||||
|
||||
19
.gitignore
vendored
@@ -13,23 +13,4 @@ src/qdomyos-zwift
|
||||
src/ui_charts.h
|
||||
src/ui_mainwindow.h
|
||||
|
||||
src/build/*
|
||||
src/build/*
|
||||
|
||||
src/debug-*
|
||||
|
||||
*.swo
|
||||
*.swp
|
||||
|
||||
template-examples/youtube-viewer/node_modules/*
|
||||
template-examples/youtube-viewer/*.json
|
||||
template-examples/youtube-viewer/.eslintrc.js
|
||||
template-examples/youtube-viewer/.jshintrc
|
||||
template-examples/youtube-viewer/debug.js
|
||||
|
||||
|
||||
template-examples/train-program-saver/node_modules/*
|
||||
template-examples/train-program-saver/*.json
|
||||
template-examples/train-program-saver/.eslintrc.js
|
||||
template-examples/train-program-saver/.jshintrc
|
||||
template-examples/train-program-saver/debug.js
|
||||
|
||||
3
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
||||
[submodule "android_openssl"]
|
||||
path = android_openssl
|
||||
url = https://github.com/KDAB/android_openssl.git
|
||||
[submodule "src/smtpclient"]
|
||||
path = src/smtpclient
|
||||
url = https://github.com/bluetiger9/SmtpClient-for-Qt.git
|
||||
|
||||
34
README.md
@@ -1,8 +1,6 @@
|
||||
# qdomyos-zwift
|
||||
Zwift bridge for Treadmills and Bike!
|
||||
|
||||
## QZ is not affiliated with or endorsed by any subscription service or maker of exercise equipment.
|
||||
|
||||
[<img src="docs/img/google_play.png">](https://play.google.com/store/apps/details?id=org.cagnulen.qdomyoszwift&fbclid=IwAR3CVoYb0scvGf7gb0Y20VFh5Na5fDWwe7VACk-2c45Tm0x5s8sXpIGhGyw)
|
||||
[<img src="docs/img/app_store.png">](https://apps.apple.com/app/id1543684531?fbclid=IwAR10H6y3mEgwkTlGJON3e8voYOh2wt3kLFOpFzoIXaYZ_N0y0pDvKxHMUaM)
|
||||
<a href="https://www.buymeacoffee.com/cagnulein" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
|
||||
@@ -41,9 +39,35 @@ Read the [installation procedure](docs/10_Installation.md)
|
||||
|
||||
### Tested on
|
||||
|
||||
You can run the app on [Macintosh or Linux devices](docs/10_Installation.md). IOS and Android are also supported.
|
||||
- Raspberry PI 0W and Domyos Intense Run
|
||||
|
||||
QDomyos-Zwift works on every [FTMS-compatible application](docs/20_supported_devices_and_applications.md), and virtually any [bluetooth enabled device](docs/20_supported_devices_and_applications.md).
|
||||
- MacBook Air 2011 and Domyos Intense Run
|
||||
|
||||
- Raspberry 3b+ and Domyos T900C
|
||||
|
||||
- Raspberry 3b+ and Toorx TRX Route Key
|
||||
|
||||
- Android Pixel 2 and Echelon Connect Sport
|
||||
|
||||
- Raspberry Pi 0W and SportsTech ESX500
|
||||
|
||||
### Your machine is not compatible?
|
||||
|
||||
Open an issue and follow these steps!
|
||||
|
||||
1. first of all you need an android device (phone or tablet)
|
||||
2. you need to become developer on your phone https://wccftech.com/how-to/how-to-enable-developer-options-on-android-10-tutorial/
|
||||
3. Go to Settings
|
||||
4. Go into developer options
|
||||
5. Enable the option Enable Bluetooth HCI snoop log
|
||||
6. restart your phone
|
||||
7. open your machine app and play with it collecting inclination and speed
|
||||
8. Disable the option Enable Bluetooth HCI snoop log
|
||||
9. in Developer Options: Bug report->Full report
|
||||
10. wait a random amount of time (10-20 seconds)
|
||||
11. A notification will appear at the top of the device. Click on it, share, email it to yourself
|
||||
12. You'll get a zip file with the entire report. In the FS/Data/Log/bt directory of the zipfile is the file you want.
|
||||
13. attach the log file in a new issue with a short description of the steps you did in the app when you used it
|
||||
|
||||
### No gui version
|
||||
|
||||
@@ -57,8 +81,6 @@ https://github.com/ProH4Ck/treadmill-bridge
|
||||
|
||||
https://www.livestrong.com/article/422012-what-is-10-degrees-in-incline-on-a-treadmill/
|
||||
|
||||
Icons used in this documentation comes from [flaticon.com](https://www.flaticon.com)
|
||||
|
||||
### Blog
|
||||
|
||||
https://robertoviola.cloud
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.6.18" android:versionCode="128" android:installLocation="auto">
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.location" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
|
||||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
<application android:hardwareAccelerated="true" android:debuggable="false" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="qdomyos-zwift" android:extractNativeLibs="true" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="qdomyos-zwift" android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<!-- Application arguments -->
|
||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||
<!-- Application arguments -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="qdomyos-zwift"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Used to specify custom system library path to run with local system libs -->
|
||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||
<!-- Messages maps -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
||||
are done populating your window with content. -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="true"/>
|
||||
<!-- Background running -->
|
||||
<!-- auto screen scale factor -->
|
||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="full"/>
|
||||
<!-- auto screen scale factor -->
|
||||
<!-- extract android style -->
|
||||
<!-- available android:values :
|
||||
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||
* full - useful QWidget & Quick Controls 1 apps
|
||||
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
||||
* none - useful for apps that don't use any of the above Qt modules
|
||||
-->
|
||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||
<!-- extract android style -->
|
||||
<meta-data android:name="com.amazon.input.cursor" android:value="pointer"/>
|
||||
</activity>
|
||||
<service android:name=".ChannelService"></service>
|
||||
<activity android:name="org.cagnulen.qdomyoszwift.MyActivity" />
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
</manifest>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="ipad12_9rounded" orientation="landscape" layout="splitview2_3" appearance="light"/>
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
|
||||
@@ -13,32 +13,32 @@
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="981" height="1024"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
|
||||
<rect key="frame" x="0.0" y="1004" width="981" height="0.0"/>
|
||||
<rect key="frame" x="0.0" y="876" width="414" height="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="launcher" translatesAutoresizingMaskIntoConstraints="NO" id="VVq-0c-S8O">
|
||||
<rect key="frame" x="20" y="114" width="941" height="776"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="frame" x="20" y="348" width="374" height="201"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
||||
<color key="backgroundColor" red="0.10980264100000001" green="0.11007446799999999" blue="0.1176523939" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
|
||||
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="SfN-ll-jLj"/>
|
||||
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" constant="20" symbolic="YES" id="SfN-ll-jLj"/>
|
||||
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="50.625" y="373.75"/>
|
||||
<point key="canvasLocation" x="52.173913043478265" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
|
||||
@@ -59,19 +59,10 @@ extension MainController {
|
||||
}
|
||||
|
||||
extension MainController: WorkoutTrackingDelegate {
|
||||
func didReceiveHealthKitDistanceCycling(_ distanceCycling: Double) {
|
||||
|
||||
}
|
||||
func didReceiveHealthKitActiveEnergyBurned(_ activeEnergyBurned: Double) {
|
||||
|
||||
}
|
||||
|
||||
func didReceiveHealthKitHeartRate(_ heartRate: Double) {
|
||||
heartRateLabel.setText("\(heartRate) BPM")
|
||||
WatchKitConnection.shared.sendMessage(message: ["heartRate":
|
||||
"\(heartRate)" as AnyObject])
|
||||
WorkoutTracking.distance = WatchKitConnection.distance
|
||||
WorkoutTracking.kcal = WatchKitConnection.kcal
|
||||
}
|
||||
|
||||
func didReceiveHealthKitStepCounts(_ stepCounts: Double) {
|
||||
|
||||
@@ -21,8 +21,6 @@ protocol WatchKitConnectionProtocol {
|
||||
|
||||
class WatchKitConnection: NSObject {
|
||||
static let shared = WatchKitConnection()
|
||||
public static var distance = 0.0
|
||||
public static var kcal = 0.0
|
||||
weak var delegate: WatchKitConnectionDelegate?
|
||||
|
||||
private override init() {
|
||||
@@ -61,10 +59,6 @@ extension WatchKitConnection: WatchKitConnectionProtocol {
|
||||
{
|
||||
validReachableSession?.sendMessage(message, replyHandler: { (result) in
|
||||
print(result)
|
||||
let dDistance = Double(result["distance"] as! Double)
|
||||
WatchKitConnection.distance = dDistance
|
||||
let dKcal = Double(result["kcal"] as! Double)
|
||||
WatchKitConnection.kcal = dKcal
|
||||
}, errorHandler: { (error) in
|
||||
print(error)
|
||||
})
|
||||
|
||||
455
build-qdomyos-zwift-Qt_5_15_2_for_iOS-Debug/watchkit Extension/WatchWorkoutTracking.swift
Executable file → Normal file
@@ -1,256 +1,199 @@
|
||||
//
|
||||
// WatchWorkoutTracking.swift
|
||||
// ElecDemo WatchKit Extension
|
||||
//
|
||||
// Created by NhatHM on 8/12/19.
|
||||
// Copyright © 2019 GST.PID. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import HealthKit
|
||||
|
||||
protocol WorkoutTrackingDelegate: class {
|
||||
func didReceiveHealthKitHeartRate(_ heartRate: Double)
|
||||
func didReceiveHealthKitStepCounts(_ stepCounts: Double)
|
||||
func didReceiveHealthKitDistanceCycling(_ distanceCycling: Double)
|
||||
func didReceiveHealthKitActiveEnergyBurned(_ activeEnergyBurned: Double)
|
||||
}
|
||||
|
||||
protocol WorkoutTrackingProtocol {
|
||||
static func authorizeHealthKit()
|
||||
func startWorkOut()
|
||||
func stopWorkOut()
|
||||
func fetchStepCounts()
|
||||
}
|
||||
|
||||
class WorkoutTracking: NSObject {
|
||||
static let shared = WorkoutTracking()
|
||||
public static var distance = Double()
|
||||
public static var kcal = Double()
|
||||
let healthStore = HKHealthStore()
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
var workoutSession: HKWorkoutSession!
|
||||
var workoutBuilder: HKLiveWorkoutBuilder!
|
||||
|
||||
weak var delegate: WorkoutTrackingDelegate?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking {
|
||||
private func handleSendStatisticsData(_ statistics: HKStatistics) {
|
||||
switch statistics.quantityType {
|
||||
case HKQuantityType.quantityType(forIdentifier: .distanceCycling):
|
||||
let distanceUnit = HKUnit.mile()
|
||||
let value = statistics.mostRecentQuantity()?.doubleValue(for: distanceUnit)
|
||||
let roundedValue = Double( round( 1 * value! ) / 1 )
|
||||
delegate?.didReceiveHealthKitDistanceCycling(roundedValue)
|
||||
|
||||
case HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned):
|
||||
let energyUnit = HKUnit.kilocalorie()
|
||||
let value = statistics.mostRecentQuantity()?.doubleValue(for: energyUnit)
|
||||
let roundedValue = Double( round( 1 * value! ) / 1 )
|
||||
delegate?.didReceiveHealthKitActiveEnergyBurned(roundedValue)
|
||||
|
||||
case HKQuantityType.quantityType(forIdentifier: .heartRate):
|
||||
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
|
||||
let value = statistics.mostRecentQuantity()?.doubleValue(for: heartRateUnit)
|
||||
let roundedValue = Double( round( 1 * value! ) / 1 )
|
||||
delegate?.didReceiveHealthKitHeartRate(roundedValue)
|
||||
|
||||
case HKQuantityType.quantityType(forIdentifier: .stepCount):
|
||||
guard let stepCounts = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
let startOfDay = Calendar.current.startOfDay(for: Date())
|
||||
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: Date(), options: .strictStartDate)
|
||||
|
||||
let query = HKStatisticsQuery(quantityType: stepCounts, quantitySamplePredicate: predicate, options: .cumulativeSum) { [weak self] (_, result, error) in
|
||||
guard let weakSelf = self else {
|
||||
return
|
||||
}
|
||||
var resultCount = 0.0
|
||||
guard let result = result else {
|
||||
print("Failed to fetch steps rate")
|
||||
return
|
||||
}
|
||||
|
||||
if let sum = result.sumQuantity() {
|
||||
resultCount = sum.doubleValue(for: HKUnit.count())
|
||||
weakSelf.delegate?.didReceiveHealthKitStepCounts(resultCount)
|
||||
} else {
|
||||
print("Failed to fetch steps rate 2")
|
||||
}
|
||||
}
|
||||
healthStore.execute(query)
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private func configWorkout() {
|
||||
configuration.activityType = .cycling
|
||||
configuration.locationType = .indoor
|
||||
|
||||
do {
|
||||
workoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
|
||||
workoutBuilder = workoutSession?.associatedWorkoutBuilder()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
workoutSession.delegate = self
|
||||
workoutBuilder.delegate = self
|
||||
|
||||
workoutBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
static func authorizeHealthKit() {
|
||||
if HKHealthStore.isHealthDataAvailable() {
|
||||
let infoToRead = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
/*HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,*/
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
|
||||
let infoToShare = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
|
||||
HKHealthStore().requestAuthorization(toShare: infoToShare, read: infoToRead) { (success, error) in
|
||||
if success {
|
||||
print("Authorization healthkit success")
|
||||
} else if let error = error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("HealthKit not avaiable")
|
||||
}
|
||||
}
|
||||
|
||||
func startWorkOut() {
|
||||
print("Start workout")
|
||||
configWorkout()
|
||||
workoutSession.startActivity(with: Date())
|
||||
workoutBuilder.beginCollection(withStart: Date()) { (success, error) in
|
||||
print(success)
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopWorkOut() {
|
||||
print("Stop workout")
|
||||
workoutSession.stopActivity(with: Date())
|
||||
workoutSession.end()
|
||||
|
||||
guard let quantityType = HKQuantityType.quantityType(
|
||||
forIdentifier: .activeEnergyBurned) else {
|
||||
return
|
||||
}
|
||||
|
||||
let unit = HKUnit.kilocalorie()
|
||||
let totalEnergyBurned = WorkoutTracking.kcal
|
||||
let quantity = HKQuantity(unit: unit,
|
||||
doubleValue: totalEnergyBurned)
|
||||
|
||||
let sample = HKCumulativeQuantitySeriesSample(type: quantityType,
|
||||
quantity: quantity,
|
||||
start: workoutSession.startDate!,
|
||||
end: Date())
|
||||
|
||||
workoutBuilder.add([sample]) {(success, error) in}
|
||||
|
||||
guard let quantityTypeDistance = HKQuantityType.quantityType(
|
||||
forIdentifier: .distanceCycling) else {
|
||||
return
|
||||
}
|
||||
|
||||
let unitDistance = HKUnit.mile()
|
||||
let miles = WorkoutTracking.distance
|
||||
let quantityMiles = HKQuantity(unit: unitDistance,
|
||||
doubleValue: miles)
|
||||
|
||||
let sampleDistance = HKCumulativeQuantitySeriesSample(type: quantityTypeDistance,
|
||||
quantity: quantityMiles,
|
||||
start: workoutSession.startDate!,
|
||||
end: Date())
|
||||
|
||||
workoutBuilder.add([sample]) {(success, error) in}
|
||||
workoutBuilder.add([sampleDistance]) {(success, error) in}
|
||||
|
||||
workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
|
||||
}
|
||||
workoutBuilder.finishWorkout{ (success, error) in }
|
||||
}
|
||||
|
||||
func fetchStepCounts() {
|
||||
guard let stepCounts = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
let startOfDay = Calendar.current.startOfDay(for: Date())
|
||||
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: Date(), options: .strictStartDate)
|
||||
|
||||
let query = HKStatisticsQuery(quantityType: stepCounts, quantitySamplePredicate: predicate, options: .cumulativeSum) { [weak self] (_, result, error) in
|
||||
guard let weakSelf = self else {
|
||||
return
|
||||
}
|
||||
var resultCount = 0.0
|
||||
guard let result = result else {
|
||||
print("Failed to fetch steps rate")
|
||||
return
|
||||
}
|
||||
|
||||
if let sum = result.sumQuantity() {
|
||||
resultCount = sum.doubleValue(for: HKUnit.count())
|
||||
weakSelf.delegate?.didReceiveHealthKitStepCounts(resultCount)
|
||||
} else {
|
||||
print("Failed to fetch steps rate 2")
|
||||
}
|
||||
}
|
||||
healthStore.execute(query)
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking: HKLiveWorkoutBuilderDelegate {
|
||||
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
|
||||
print("GET DATA: \(Date())")
|
||||
for type in collectedTypes {
|
||||
guard let quantityType = type as? HKQuantityType else {
|
||||
return
|
||||
}
|
||||
|
||||
if let statistics = workoutBuilder.statistics(for: quantityType) {
|
||||
handleSendStatisticsData(statistics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking: HKWorkoutSessionDelegate {
|
||||
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
|
||||
|
||||
}
|
||||
|
||||
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
|
||||
|
||||
}
|
||||
}
|
||||
//
|
||||
// WatchWorkoutTracking.swift
|
||||
// ElecDemo WatchKit Extension
|
||||
//
|
||||
// Created by NhatHM on 8/12/19.
|
||||
// Copyright © 2019 GST.PID. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import HealthKit
|
||||
|
||||
protocol WorkoutTrackingDelegate: class {
|
||||
func didReceiveHealthKitHeartRate(_ heartRate: Double)
|
||||
func didReceiveHealthKitStepCounts(_ stepCounts: Double)
|
||||
}
|
||||
|
||||
protocol WorkoutTrackingProtocol {
|
||||
static func authorizeHealthKit()
|
||||
func startWorkOut()
|
||||
func stopWorkOut()
|
||||
func fetchStepCounts()
|
||||
}
|
||||
|
||||
class WorkoutTracking: NSObject {
|
||||
static let shared = WorkoutTracking()
|
||||
let healthStore = HKHealthStore()
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
var workoutSession: HKWorkoutSession!
|
||||
var workoutBuilder: HKLiveWorkoutBuilder!
|
||||
|
||||
weak var delegate: WorkoutTrackingDelegate?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking {
|
||||
private func handleSendStatisticsData(_ statistics: HKStatistics) {
|
||||
switch statistics.quantityType {
|
||||
case HKQuantityType.quantityType(forIdentifier: .heartRate):
|
||||
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
|
||||
let value = statistics.mostRecentQuantity()?.doubleValue(for: heartRateUnit)
|
||||
let roundedValue = Double( round( 1 * value! ) / 1 )
|
||||
delegate?.didReceiveHealthKitHeartRate(roundedValue)
|
||||
|
||||
case HKQuantityType.quantityType(forIdentifier: .stepCount):
|
||||
guard let stepCounts = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
let startOfDay = Calendar.current.startOfDay(for: Date())
|
||||
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: Date(), options: .strictStartDate)
|
||||
|
||||
let query = HKStatisticsQuery(quantityType: stepCounts, quantitySamplePredicate: predicate, options: .cumulativeSum) { [weak self] (_, result, error) in
|
||||
guard let weakSelf = self else {
|
||||
return
|
||||
}
|
||||
var resultCount = 0.0
|
||||
guard let result = result else {
|
||||
print("Failed to fetch steps rate")
|
||||
return
|
||||
}
|
||||
|
||||
if let sum = result.sumQuantity() {
|
||||
resultCount = sum.doubleValue(for: HKUnit.count())
|
||||
weakSelf.delegate?.didReceiveHealthKitStepCounts(resultCount)
|
||||
} else {
|
||||
print("Failed to fetch steps rate 2")
|
||||
}
|
||||
}
|
||||
healthStore.execute(query)
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private func configWorkout() {
|
||||
configuration.activityType = .cycling
|
||||
configuration.locationType = .indoor
|
||||
|
||||
do {
|
||||
workoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
|
||||
workoutBuilder = workoutSession?.associatedWorkoutBuilder()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
workoutSession.delegate = self
|
||||
workoutBuilder.delegate = self
|
||||
|
||||
workoutBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
static func authorizeHealthKit() {
|
||||
if HKHealthStore.isHealthDataAvailable() {
|
||||
let infoToRead = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
|
||||
let infoToShare = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
|
||||
HKHealthStore().requestAuthorization(toShare: infoToShare, read: infoToRead) { (success, error) in
|
||||
if success {
|
||||
print("Authorization healthkit success")
|
||||
} else if let error = error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("HealthKit not avaiable")
|
||||
}
|
||||
}
|
||||
|
||||
func startWorkOut() {
|
||||
print("Start workout")
|
||||
configWorkout()
|
||||
workoutSession.startActivity(with: Date())
|
||||
workoutBuilder.beginCollection(withStart: Date()) { (success, error) in
|
||||
print(success)
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopWorkOut() {
|
||||
print("Stop workout")
|
||||
workoutSession.stopActivity(with: Date())
|
||||
workoutSession.end()
|
||||
workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func fetchStepCounts() {
|
||||
guard let stepCounts = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
let startOfDay = Calendar.current.startOfDay(for: Date())
|
||||
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: Date(), options: .strictStartDate)
|
||||
|
||||
let query = HKStatisticsQuery(quantityType: stepCounts, quantitySamplePredicate: predicate, options: .cumulativeSum) { [weak self] (_, result, error) in
|
||||
guard let weakSelf = self else {
|
||||
return
|
||||
}
|
||||
var resultCount = 0.0
|
||||
guard let result = result else {
|
||||
print("Failed to fetch steps rate")
|
||||
return
|
||||
}
|
||||
|
||||
if let sum = result.sumQuantity() {
|
||||
resultCount = sum.doubleValue(for: HKUnit.count())
|
||||
weakSelf.delegate?.didReceiveHealthKitStepCounts(resultCount)
|
||||
} else {
|
||||
print("Failed to fetch steps rate 2")
|
||||
}
|
||||
}
|
||||
healthStore.execute(query)
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking: HKLiveWorkoutBuilderDelegate {
|
||||
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
|
||||
print("GET DATA: \(Date())")
|
||||
for type in collectedTypes {
|
||||
guard let quantityType = type as? HKQuantityType else {
|
||||
return
|
||||
}
|
||||
|
||||
if let statistics = workoutBuilder.statistics(for: quantityType) {
|
||||
handleSendStatisticsData(statistics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking: HKWorkoutSessionDelegate {
|
||||
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
|
||||
|
||||
}
|
||||
|
||||
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,6 @@
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "33x33",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
@@ -47,13 +40,6 @@
|
||||
"size" : "44x44",
|
||||
"subtype" : "40mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "46x46",
|
||||
"subtype" : "41mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
@@ -61,13 +47,6 @@
|
||||
"size" : "50x50",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "51x51",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
@@ -89,13 +68,6 @@
|
||||
"size" : "108x108",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "117x117",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch-marketing",
|
||||
"scale" : "1x",
|
||||
|
||||
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -103,7 +103,7 @@
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "ItunesArtwork-1.png",
|
||||
"filename" : "iTunesArtwork@2x.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
@@ -138,13 +138,6 @@
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "33x33",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "80x80-1.png",
|
||||
"idiom" : "watch",
|
||||
@@ -161,13 +154,6 @@
|
||||
"size" : "44x44",
|
||||
"subtype" : "40mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "46x46",
|
||||
"subtype" : "41mm"
|
||||
},
|
||||
{
|
||||
"filename" : "50@2x-1.png",
|
||||
"idiom" : "watch",
|
||||
@@ -176,13 +162,6 @@
|
||||
"size" : "50x50",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "51x51",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "86@2x-1.png",
|
||||
"idiom" : "watch",
|
||||
@@ -207,13 +186,6 @@
|
||||
"size" : "108x108",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "117x117",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "1024@1x.png",
|
||||
"idiom" : "watch-marketing",
|
||||
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
@@ -1,25 +1,17 @@
|
||||
# Installation
|
||||
|
||||
QDomyos-Zwift can be installed from source on MacOs, Linux, Android and IOS.
|
||||
|
||||
Once you've installed QDomyos-Zwift, you can access the [operation guide](30_usage.md) for more information.
|
||||
|
||||
## On a Linux System (from source)
|
||||
|
||||
```buildoutcfg
|
||||
$ sudo apt update && sudo apt upgrade # this is very important on raspberry pi: you need the bluetooth firmware updated!
|
||||
$ sudo sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev
|
||||
$ sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-default libqt5networkauth5-dev
|
||||
$ git clone https://github.com/cagnulein/qdomyos-zwift.git
|
||||
$ cd qdomyos-zwift
|
||||
$ git submodule update --init src/smtpclient/
|
||||
$ git submodule update --init src/qmdnsengine/
|
||||
$ cd src
|
||||
$ qmake
|
||||
$ make -j4
|
||||
$ sudo ./qdomyos-zwift
|
||||
```
|
||||
|
||||
|
||||
## MacOs installation
|
||||
|
||||
You will need to (at a minimum) to install the xcode Command Line Tools (CLI) thanks to @richardwait
|
||||
@@ -102,14 +94,11 @@ This operation takes a moment to complete.
|
||||
|
||||
#### Install qdomyos-zwift from sources
|
||||
|
||||
`sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev`
|
||||
`sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-default libqt5networkauth5-dev`
|
||||
|
||||
`git clone https://github.com/cagnulein/qdomyos-zwift.git`
|
||||
`cd qdomyos-zwift`
|
||||
`git submodule update --init src/smtpclient/`
|
||||
`git submodule update --init src/qmdnsengine/`
|
||||
`cd src`
|
||||
`qmake`
|
||||
`cd src`
|
||||
`qmake`
|
||||
`make`
|
||||
|
||||
Please note :
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
# Supported applications
|
||||
|
||||
QDomyos-Zwift should support every application able to read a [FTMS](specs/FTMS_v1.0.pdf) bluetooth signal.
|
||||
|
||||
This list is not exhaustive. Please report any application known to be working with QDomyos-Zwift !
|
||||
|
||||
|Application|Sport|Platform|Speed|RPM|Power|HRM |Resistance Control|
|
||||
|-----------|-----|--------|------------|---|-----|-----|----------------|
|
||||
|[Zwift](21_applications_detail.md#zwift)| |  | Yes|Yes|Yes| Yes, no FTMS support | Yes |
|
||||
|[BKool](21_applications_detail.md#bkool) ||  |Yes|Yes|Yes|Yes| Yes |
|
||||
|[Fulgaz](21_applications_detail.md#fulgaz)||  |Yes|Yes|Yes|Yes, no FTMS support (see note)|Yes (see note) |
|
||||
|
||||
# Supported devices
|
||||
This list is not exhaustive.
|
||||
Try the qdomyos app with your fitness appliance and report how it is going.
|
||||
If it's not working, you can [ask for your device to be supported](#ask-for-device-support)
|
||||
|
||||
## Supported bikes
|
||||
|
||||
|Manufacturer|Model|Speed|RPM|Power|HRM|Resistence Control|
|
||||
|------------|-----|------------|---|-----|---|------------------|
|
||||
|[Echelon](22_devices_detail.md#echelon)|Connect Sport|Yes|Yes|Yes|Yes|N/A|
|
||||
|[Sportstech](22_devices_detail.md#sportstech)|ESX500|Yes|Yes|Yes|Yes|Yes|
|
||||
|
||||
## Supported treadmills
|
||||
|
||||
|Manufacturer|Model|Speed|HRM|Inclinaison Control| Speed control|
|
||||
|------------|-----|------------|---|-------------------|--------------|
|
||||
|Domyos|Intense Run|Yes|Yes|Yes|Yes|
|
||||
|Domyos|T900c|Yes|Yes|Yes|Yes|
|
||||
|Toorx|TRX Route Key|Yes|Yes|Yes|Yes|
|
||||
|
||||
|
||||
# Ask for device support
|
||||
|
||||
You can ask for supporting a device by opening an issue and following these steps.
|
||||
We need to "spy" the bluetooth activity from your fitness device and it's application, in order to guess how they communicate.
|
||||
|
||||
An android device is required for this operation.
|
||||
|
||||
## Android device
|
||||
|
||||
1. first of all you need an android device (phone or tablet)
|
||||
2. you need to become developer on your phone https://wccftech.com/how-to/how-to-enable-developer-options-on-android-10-tutorial/
|
||||
3. Go to Settings
|
||||
4. Go into developer options
|
||||
5. Enable the option Enable Bluetooth HCI snoop log
|
||||
6. restart your phone
|
||||
7. open your machine app and play with it collecting inclination and speed
|
||||
8. Disable the option Enable Bluetooth HCI snoop log
|
||||
9. in Developer Options: Bug report->Full report
|
||||
10. wait a random amount of time (10-20 seconds)
|
||||
11. A notification will appear at the top of the device. Click on it, share, email it to yourself
|
||||
12. You'll get a zip file with the entire report. In the FS/Data/Log/bt directory of the zipfile is the file you want.
|
||||
13. attach the log file in a new issue with a short description of the steps you did in the app when you used it
|
||||
|
||||
## Android Device (oppo based OS : oppo, ColorOS, RealMe, ...)
|
||||
1. Dial *#800# on the phone app. A special menu should appear.
|
||||
2. Go to Bluetooth and press "Start capture" (green button)
|
||||
3. open your machine app and play with it collecting inclination and speed
|
||||
4. Go back to the special menu by dialing *#800# on the phone app. Stop the bluetooth capture.
|
||||
5. A new debug directory is stored onto `/oppo_log/` with a time stamp.
|
||||
6. Fetch the CFA file stored in the `btsnoop_hci` folder.
|
||||
@@ -1,30 +0,0 @@
|
||||
# Applications supported
|
||||
|
||||
This list is not exhaustive !
|
||||
Open a discussion to tell us if a missing application is supported.
|
||||
|
||||
## BKool
|
||||
Everything is working out of the box (read FTMS data).
|
||||
|
||||
|
||||
## Fulgaz
|
||||
|
||||
### HRM Support
|
||||
The application do not read the FTMS value. It is required to start the application with `-heart-service` or `bike_heartrate_service: true` in settings.
|
||||
The HRM captor will not be shown by default, you need to go in the `Application Settings > Advanced > Disable Bluetooth Filter`.
|
||||
|
||||
### Resistance management
|
||||
Fulgaz is known to be very severe by default in resistance adjustment. It is advised to adjust [application settings](https://tempocyclist.com/2020/04/29/fulgaz-resistance-too-hard/).
|
||||
The resistance is automatically adjusted with the slope. However, you can override it using
|
||||
|
||||
### Additional notes
|
||||
You can have ae true 4k video stream while you ride ("extreme quality" setting) however it requires about 10 gb per hour.
|
||||
|
||||
|
||||
## Zwift
|
||||
### HRM support
|
||||
The application do not read the FTMS value. It is required to start the application with `-heart-service` or `bike_heartrate_service: true` in settings.
|
||||
|
||||
### Resistance management
|
||||
You can adjust resistence using arrows [up and down](img/21_zwift-resistance-buttons.jpg) rom the riding screen.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Devices detail
|
||||
|
||||
## Echelon
|
||||
|
||||
|
||||
## Sportstech
|
||||
|
||||
### ESX 500
|
||||
#### HRM
|
||||
The [cardio belt provided](https://www.sports-tech.uk.com/chest-strap-sportstech-uncoded) with the bike is also supported, the bike reads the value and forwards it in its bluetooth signal.
|
||||
The cardio captors are reported to not be accurate.
|
||||
#### Resistance
|
||||
The resistance is adjusted after a few seconds delay (time for the engine to adapt magnetic resistance).
|
||||
@@ -1,52 +0,0 @@
|
||||
# QDomyos-Zwift operation guide
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
The QDomyos-Zwift can be started in two modes : QML or NativeQT.
|
||||
The main difference is the configuration management : you can change settings within the application with QML, where you need to specify settings at startup in NativeQT.
|
||||
|
||||
**Note:** Android and IOS are always running in QML mode.
|
||||
|
||||
On MacOS and Linux, you start QDomyos-Zwift in NativeQT mode (where settings are defined in commandline switches).
|
||||
|
||||
You can start the application in QML mode with the command-line switch -qml.
|
||||
|
||||
## Configuration in QML mode
|
||||
Please refer to this article for more information under [QML Operations](https://robertoviola.cloud/qdomyos-zwift-guide/) with several useful information.
|
||||
|
||||
|
||||
## Configuration in NativeQT mode
|
||||
|
||||
This is the list of settings available in the application. These settings needs to be appended to the binary command line.
|
||||
*Example :* `sudo ./qdomyos-zwift -no-gui` for disabling any graphical interface.
|
||||
|
||||
| **Option** | **Type** | **Default** | **Function** |
|
||||
|:------------------------|:---------|:------------|:-----------------------------------------------------------------------------|
|
||||
| -no-gui | Boolean | False | Disable GUI |
|
||||
| -qml | Boolean | False | Enables the QML interface |
|
||||
| -miles | Boolean | False | Swithes to Imperial Units System |
|
||||
| -no-console | Boolean | False | Not in use |
|
||||
| -test-resistance | Boolean | False | |
|
||||
| -no-log | Boolean | False | Disable Logging |
|
||||
| -no-write-resistance | Boolean | False | Disable resistance instructions from QZ to your fitness equipment |
|
||||
| -no-heart-service | Boolean | False | Do not simulate external HR monitor, use only FTMS |
|
||||
| -heart-service | Boolean | True | Simulate HR service (required for applications not reading FTMS) |
|
||||
| -only-virtualbike | Boolean | False | |
|
||||
| -only-virtualtreadmill | Boolean | False | |
|
||||
| -no-reconnection | Boolean | False | QZ will not try to reconnect your fitness equipement if enabled |
|
||||
| -bluetooth-relaxed | Boolean | False | In case of deconnections from QZ to your fitness equipement |
|
||||
| -bike-cadence-sensor | Boolean | False | |
|
||||
| -bike-power-sensor | Boolean | False | |
|
||||
| -battery-service | Boolean | False | |
|
||||
| -service-changed | Boolean | False | |
|
||||
| -bike-wheel-revs | Boolean | False | |
|
||||
| -run-cadence-sensor | Boolean | False | |
|
||||
| -train | String | | Force training program |
|
||||
| -name | String | | Force bluetooth device name (if QZ struggles finding your fitness equipment) |
|
||||
| -poll-device-time | Int | 200 (ms) | Frequency to refresh informations from QZ to Fitness equipment |
|
||||
| -bike-resistance-gain | Int | | Adjust resistance from the fitness application |
|
||||
| -bike-resistance-offset | Int | | Set another resistance point than default |
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 491 B |
|
Before Width: | Height: | Size: 427 B |
|
Before Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 380 B |
|
Before Width: | Height: | Size: 377 B |
|
Before Width: | Height: | Size: 369 B |
|
Before Width: | Height: | Size: 166 KiB |
@@ -1,220 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--Copyright 2017 Bluetooth SIG, Inc. All rights reserved.-->
|
||||
<Characteristic xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/characteristic.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
name="Rower Data" type="org.bluetooth.characteristic.rower_data"
|
||||
uuid="2AD1" last-modified="2017-02-14" approved="No">
|
||||
<InformativeText>
|
||||
<Summary>The Rower Data characteristic is used to send
|
||||
training-related data to the Client from a rower
|
||||
(Server).</Summary>
|
||||
</InformativeText>
|
||||
<Value>
|
||||
<Field name="Flags">
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Format>16bit</Format>
|
||||
<BitField>
|
||||
<Bit index="0" size="1" name="More Data">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" requires="C1" />
|
||||
<Enumeration key="1" value="True" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="1" size="1" name="Average Stroke present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C2" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="2" size="1" name="Total Distance Present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C3" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="3" size="1" name="Instantaneous Pace present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C4" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="4" size="1" name="Average Pace Present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C5" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="5" size="1" name="Instantaneous Power present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C6" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="6" size="1" name="Average Power present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C7" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="7" size="1" name="Resistance Level present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C8" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="8" size="1" name="Expended Energy present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C9" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="9" size="1" name="Heart Rate present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C10" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="10" size="1"
|
||||
name="Metabolic Equivalent present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C11" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="11" size="1" name="Elapsed Time present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C12" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="12" size="1" name="Remaining Time present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C13" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<ReservedForFutureUse index="13" size="3" />
|
||||
</BitField>
|
||||
</Field>
|
||||
<Field name="Stroke Rate">
|
||||
<InformativeText>stroke/minute with a resolution of
|
||||
0.5</InformativeText>
|
||||
<Requirement>C1</Requirement>
|
||||
<Format>uint8</Format>
|
||||
<BinaryExponent>-1</BinaryExponent>
|
||||
<Unit>org.bluetooth.unit.stroke_per_minute</Unit>
|
||||
</Field>
|
||||
<Field name="Stroke Count">
|
||||
<InformativeText>Unitless with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C1</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.unitless</Unit>
|
||||
</Field>
|
||||
<Field name="Average Stroke Rate">
|
||||
<InformativeText>1/minute with a resolution of
|
||||
0.5</InformativeText>
|
||||
<Requirement>C2</Requirement>
|
||||
<Format>uint8</Format>
|
||||
<BinaryExponent>-1</BinaryExponent>
|
||||
<Unit>org.bluetooth.unit.stroke_per_minute</Unit>
|
||||
</Field>
|
||||
<Field name="Total Distance">
|
||||
<InformativeText>Meters with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C3</Requirement>
|
||||
<Format>uint24</Format>
|
||||
<Unit>org.bluetooth.unit.length.metre</Unit>
|
||||
</Field>
|
||||
<Field name="Instantaneous Pace">
|
||||
<InformativeText>Second with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C4</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.time.second</Unit>
|
||||
</Field>
|
||||
<Field name="Average Pace">
|
||||
<InformativeText>Second with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C5</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.time.second</Unit>
|
||||
</Field>
|
||||
<Field name="Instantaneous Power">
|
||||
<InformativeText>Watts with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C6</Requirement>
|
||||
<Format>sint16</Format>
|
||||
<Unit>org.bluetooth.unit.power.watt</Unit>
|
||||
</Field>
|
||||
<Field name="Average Power">
|
||||
<InformativeText>Watts with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C7</Requirement>
|
||||
<Format>sint16</Format>
|
||||
<Unit>org.bluetooth.unit.power.watt</Unit>
|
||||
</Field>
|
||||
<Field name="Resistance Level">
|
||||
<InformativeText>Unitless with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C8</Requirement>
|
||||
<Format>sint16</Format>
|
||||
<Unit>org.bluetooth.unit.unitless</Unit>
|
||||
</Field>
|
||||
<Field name="Total Energy">
|
||||
<InformativeText>Kilo Calorie with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C9</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.energy.kilogram_calorie</Unit>
|
||||
</Field>
|
||||
<Field name="Energy Per Hour">
|
||||
<InformativeText>Kilo Calorie with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C9</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.energy.kilogram_calorie</Unit>
|
||||
</Field>
|
||||
<Field name="Energy Per Minute">
|
||||
<InformativeText>Kilo Calorie with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C9</Requirement>
|
||||
<Format>uint8</Format>
|
||||
<Unit>org.bluetooth.unit.energy.kilogram_calorie</Unit>
|
||||
</Field>
|
||||
<Field name="Heart Rate">
|
||||
<InformativeText>Beats per minute with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C10</Requirement>
|
||||
<Format>uint8</Format>
|
||||
<Unit>org.bluetooth.unit.period.beats_per_minute</Unit>
|
||||
</Field>
|
||||
<Field name="Metabolic Equivalent">
|
||||
<InformativeText>Metabolic Equivalent with a resolution of
|
||||
0.1</InformativeText>
|
||||
<Requirement>C11</Requirement>
|
||||
<Format>uint8</Format>
|
||||
<DecimalExponent>-1</DecimalExponent>
|
||||
<Unit>org.bluetooth.unit.metabolic_equivalent</Unit>
|
||||
</Field>
|
||||
<Field name="Elapsed Time">
|
||||
<InformativeText>Second with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C12</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.time.second</Unit>
|
||||
</Field>
|
||||
<Field name="Remaining Time">
|
||||
<InformativeText>Second with a resolution of
|
||||
1</InformativeText>
|
||||
<Requirement>C13</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.time.second</Unit>
|
||||
</Field>
|
||||
</Value>
|
||||
<Note>The fields in the above table, reading from top to bottom,
|
||||
are shown in the order of LSO to MSO, where LSO = Least
|
||||
Significant Octet and MSO = Most Significant Octet. The Least
|
||||
Significant Octet represents the eight bits numbered 0 to
|
||||
7.</Note>
|
||||
</Characteristic>
|
||||
@@ -1,87 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--Copyright 2011 Bluetooth SIG, Inc. All rights reserved.-->
|
||||
<Characteristic xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/characteristic.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
name="RSC Measurement"
|
||||
type="org.bluetooth.characteristic.rsc_measurement" uuid="2A53"
|
||||
last-modified="2012-09-29" approved="Yes">
|
||||
<InformativeText>
|
||||
<Summary>The RSC Measurement characteristic (RSC refers to
|
||||
Running Speed and Cadence) is a variable length structure
|
||||
containing a Flags field, an Instantaneous Speed field and an
|
||||
Instantaneous Cadence field and, based on the contents of the
|
||||
Flags field, may contain a Stride Length field and a Total
|
||||
Distance field.</Summary>
|
||||
</InformativeText>
|
||||
<Value>
|
||||
<Field name="Flags">
|
||||
<InformativeText>These flags define which data fields are
|
||||
present in the Characteristic value.</InformativeText>
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Format>8bit</Format>
|
||||
<BitField>
|
||||
<Bit index="0" size="1"
|
||||
name="Instantaneous Stride Length Present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C1" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="1" size="1" name="Total Distance Present">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="False" />
|
||||
<Enumeration key="1" value="True" requires="C2" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<Bit index="2" size="1"
|
||||
name="Walking or Running Status bits">
|
||||
<Enumerations>
|
||||
<Enumeration key="0" value="Walking" />
|
||||
<Enumeration key="1" value="Running" />
|
||||
</Enumerations>
|
||||
</Bit>
|
||||
<ReservedForFutureUse index="3" size="5" />
|
||||
</BitField>
|
||||
</Field>
|
||||
<Field name="Instantaneous Speed">
|
||||
<InformativeText>Unit is in m/s with a resolution of 1/256
|
||||
s</InformativeText>
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.velocity.metres_per_second</Unit>
|
||||
</Field>
|
||||
<Field name="Instantaneous Cadence">
|
||||
<InformativeText>Unit is in 1/minute (or RPM) with a
|
||||
resolutions of 1 1/min (or 1 RPM)</InformativeText>
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Format>uint8</Format>
|
||||
<Unit>
|
||||
org.bluetooth.unit.angular_velocity.revolution_per_minute</Unit>
|
||||
</Field>
|
||||
<Field name="Instantaneous Stride Length">
|
||||
<InformativeText>
|
||||
<p>C1: Field exists if the key of bit 0 of the Flags field
|
||||
is set to 1.</p>
|
||||
<p>- Unit is in meter with a resolution of 1/100 m (or
|
||||
centimeter).</p>
|
||||
</InformativeText>
|
||||
<Requirement>C1</Requirement>
|
||||
<Format>uint16</Format>
|
||||
<Unit>org.bluetooth.unit.length.meter</Unit>
|
||||
</Field>
|
||||
<Field name="Total Distance">
|
||||
<InformativeText>
|
||||
<p>C2: Field exists if the key of bit 1 of the Flags field
|
||||
is set to 1.</p>
|
||||
<p>- Unit is in meter with a resolution of 1/10 m (or
|
||||
decimeter).</p>
|
||||
</InformativeText>
|
||||
<Requirement>C2</Requirement>
|
||||
<Format>uint32</Format>
|
||||
<Unit>org.bluetooth.unit.length.meter</Unit>
|
||||
</Field>
|
||||
</Value>
|
||||
<Note>The fields in the above table are in the order of LSO to
|
||||
MSO. Where LSO = Least Significant Octet and MSO = Most
|
||||
Significant Octet.</Note>
|
||||
</Characteristic>
|
||||
@@ -1,161 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2011 Bluetooth SIG, Inc. All rights reserved. -->
|
||||
<Service xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/service.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
name="Running Speed and Cadence"
|
||||
type="org.bluetooth.service.running_speed_and_cadence" uuid="1814"
|
||||
last-modified="2012-3-26" approved="Yes">
|
||||
<InformativeText>
|
||||
<Abstract>This service exposes speed, cadence and other data
|
||||
from a Running Speed and Cadence Sensor intended for fitness
|
||||
applications.</Abstract>
|
||||
<Summary>The Running Speed and Cadence (RSC) Service exposes
|
||||
speed, cadence and other data related to fitness applications
|
||||
such as the stride length and the total distance the user has
|
||||
traveled while using the Speed and Cadence Sensor
|
||||
(Server).</Summary>
|
||||
</InformativeText>
|
||||
<Dependencies>
|
||||
<Dependency>This service has no dependencies on other
|
||||
GATT-based services.</Dependency>
|
||||
</Dependencies>
|
||||
<GATTRequirements>
|
||||
<Requirement subProcedure="Write Characteristic Values">
|
||||
C1</Requirement>
|
||||
<Requirement subProcedure="Notifications">
|
||||
Mandatory</Requirement>
|
||||
<Requirement subProcedure="Indications">C1</Requirement>
|
||||
<Requirement subProcedure="Read Characteristic Descriptors">
|
||||
Mandatory</Requirement>
|
||||
<Requirement subProcedure="Write Characteristic Descriptors">
|
||||
Mandatory</Requirement>
|
||||
</GATTRequirements>
|
||||
<Note>C1: Mandatory if the SC Control Point characteristic is
|
||||
supported, otherwise excluded for this service.</Note>
|
||||
<Transports>
|
||||
<Classic>true</Classic>
|
||||
<LowEnergy>true</LowEnergy>
|
||||
</Transports>
|
||||
<ErrorCodes>
|
||||
<ErrorCode name="Procedure Already in Progress" code="0x80">A
|
||||
SC Control Point request cannot be serviced because a
|
||||
previously triggered SC Control Point operation is still in
|
||||
progress.</ErrorCode>
|
||||
<ErrorCode name="Client Characteristic Configuration descriptor improperly configured"
|
||||
code="0x81">The Client Characteristic Configuration descriptor
|
||||
is not configured according to the requirements of the
|
||||
service.</ErrorCode>
|
||||
</ErrorCodes>
|
||||
<Characteristics>
|
||||
<Characteristic name="RSC Measurement"
|
||||
type="org.bluetooth.characteristic.rsc_measurement">
|
||||
<InformativeText>The RSC Measurement characteristic is used
|
||||
to send speed and cadence measurements.</InformativeText>
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Properties>
|
||||
<Read>Excluded</Read>
|
||||
<Write>Excluded</Write>
|
||||
<WriteWithoutResponse>Excluded</WriteWithoutResponse>
|
||||
<SignedWrite>Excluded</SignedWrite>
|
||||
<ReliableWrite>Excluded</ReliableWrite>
|
||||
<Notify>Mandatory</Notify>
|
||||
<Indicate>Excluded</Indicate>
|
||||
<WritableAuxiliaries>Excluded</WritableAuxiliaries>
|
||||
<Broadcast>Excluded</Broadcast>
|
||||
</Properties>
|
||||
<Descriptors>
|
||||
<Descriptor name="Client Characteristic Configuration"
|
||||
type="org.bluetooth.descriptor.gatt.client_characteristic_configuration">
|
||||
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Properties>
|
||||
<Read>Mandatory</Read>
|
||||
<Write>Mandatory</Write>
|
||||
</Properties>
|
||||
</Descriptor>
|
||||
</Descriptors>
|
||||
</Characteristic>
|
||||
<Characteristic name="RSC Feature"
|
||||
type="org.bluetooth.characteristic.rsc_feature">
|
||||
<InformativeText>The RSC Feature characteristic is used to
|
||||
describe the supported features of the Server. Reserved for
|
||||
Future Use (RFU) bits in the SC Feature characteristic value
|
||||
are set to 0.</InformativeText>
|
||||
<Requirement>Mandatory</Requirement>
|
||||
<Properties>
|
||||
<Read>Mandatory</Read>
|
||||
<Write>Excluded</Write>
|
||||
<WriteWithoutResponse>Excluded</WriteWithoutResponse>
|
||||
<SignedWrite>Excluded</SignedWrite>
|
||||
<ReliableWrite>Excluded</ReliableWrite>
|
||||
<Notify>Excluded</Notify>
|
||||
<Indicate>Excluded</Indicate>
|
||||
<WritableAuxiliaries>Excluded</WritableAuxiliaries>
|
||||
<Broadcast>Excluded</Broadcast>
|
||||
</Properties>
|
||||
</Characteristic>
|
||||
<Characteristic name="Sensor Location"
|
||||
type="org.bluetooth.characteristic.sensor_location">
|
||||
<InformativeText>
|
||||
<p>The Sensor Location characteristic of the device is used
|
||||
to describe the physical location of the Server when
|
||||
correctly fitted.</p>
|
||||
<p>
|
||||
<b>C1:</b>Mandatory if the Multiple Sensor Location feature
|
||||
is supported, otherwise optional.</p>
|
||||
</InformativeText>
|
||||
<Requirement>C1</Requirement>
|
||||
<Properties>
|
||||
<Read>Mandatory</Read>
|
||||
<Write>Excluded</Write>
|
||||
<WriteWithoutResponse>Excluded</WriteWithoutResponse>
|
||||
<SignedWrite>Excluded</SignedWrite>
|
||||
<ReliableWrite>Excluded</ReliableWrite>
|
||||
<Notify>Excluded</Notify>
|
||||
<Indicate>Excluded</Indicate>
|
||||
<WritableAuxiliaries>Excluded</WritableAuxiliaries>
|
||||
<Broadcast>Excluded</Broadcast>
|
||||
</Properties>
|
||||
</Characteristic>
|
||||
<Characteristic name="SC Control Point"
|
||||
type="org.bluetooth.characteristic.sc_control_point">
|
||||
<InformativeText>
|
||||
<p>If the SC Control Point is supported, profiles utilizing
|
||||
this service are required to ensure that the Client
|
||||
configures the SC Control Point characteristic for
|
||||
indications (i.e. via the Client Characteristic
|
||||
Configuration descriptor) at the first connection.</p>
|
||||
<p>Support for this characteristic is mandatory if the
|
||||
Server supports Calibration Procedure, Total Distance or
|
||||
Multiple Sensor Locations features, otherwise it is
|
||||
excluded.</p>
|
||||
<p>
|
||||
<b>C2:</b>Mandatory if at least one SC Control Point
|
||||
procedure is supported, otherwise excluded.</p>
|
||||
</InformativeText>
|
||||
<Requirement>C2</Requirement>
|
||||
<Properties>
|
||||
<Read>Excluded</Read>
|
||||
<Write>Mandatory</Write>
|
||||
<WriteWithoutResponse>Excluded</WriteWithoutResponse>
|
||||
<SignedWrite>Excluded</SignedWrite>
|
||||
<ReliableWrite>Excluded</ReliableWrite>
|
||||
<Notify>Excluded</Notify>
|
||||
<Indicate>Mandatory</Indicate>
|
||||
<WritableAuxiliaries>Excluded</WritableAuxiliaries>
|
||||
<Broadcast>Excluded</Broadcast>
|
||||
</Properties>
|
||||
<Descriptors>
|
||||
<Descriptor name="Client Characteristic Configuration"
|
||||
type="org.bluetooth.descriptor.gatt.client_characteristic_configuration">
|
||||
|
||||
<Requirement>if_characteristic_supported</Requirement>
|
||||
<Properties>
|
||||
<Read>Mandatory</Read>
|
||||
<Write>Mandatory</Write>
|
||||
</Properties>
|
||||
</Descriptor>
|
||||
</Descriptors>
|
||||
</Characteristic>
|
||||
</Characteristics>
|
||||
</Service>
|
||||
|
Before Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 287 KiB |
|
Before Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 249 KiB |
|
Before Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 297 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 417 KiB |
|
Before Width: | Height: | Size: 310 KiB |
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qabstracthttpserver_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserver_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverliterals_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverrequest_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverresponder_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverresponse_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverrouter_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverrouterrule_p.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qabstracthttpserver.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverfutureresponse.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserver.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverviewtraits.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverfutureresponse.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverrequest.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverresponder.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverresponse.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverrouter.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverrouterrule.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "qhttpserverrouterviewtraits.h"
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef QT_QTHTTPSERVER_MODULE_H
|
||||
#define QT_QTHTTPSERVER_MODULE_H
|
||||
#include <QtHttpServer/QtHttpServerDepends>
|
||||
#include "qthttpserverglobal.h"
|
||||
#include "qabstracthttpserver.h"
|
||||
#include "qhttpserver.h"
|
||||
#include "qhttpserverfutureresponse.h"
|
||||
#include "qhttpserverrequest.h"
|
||||
#include "qhttpserverresponder.h"
|
||||
#include "qhttpserverresponse.h"
|
||||
#include "qhttpserverrouter.h"
|
||||
#include "qhttpserverrouterrule.h"
|
||||
#include "qhttpserverrouterviewtraits.h"
|
||||
#include "qhttpserverviewtraits.h"
|
||||
#include "qthttpserverversion.h"
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
/* This file was generated by qmake with the info from <root>/src/httpserver/httpserver.pro. */
|
||||
#ifdef __cplusplus /* create empty PCH in C mode */
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <QtCore/QtCore>
|
||||
#include <QtWebSockets/QtWebSockets>
|
||||
#include <QtSslServer/QtSslServer>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#endif
|
||||