Compare commits

..

3 Commits

Author SHA1 Message Date
Roberto Viola
b73003316d power chart works! 2021-03-29 09:43:30 +02:00
Roberto Viola
2bbbd0b9bf rolling chart with zones. Issue on left margin 2021-03-28 14:26:20 +02:00
Roberto Viola
1f03a5e10b realtime chart works! 2021-03-27 18:08:51 +01:00
551 changed files with 13566 additions and 55116 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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)
})

View 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) {
}
}

View File

@@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -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",

View File

@@ -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 :

View File

@@ -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)|![bike](img/20_bike.png) ![run](img/20_treadmill.png)|![IOS](img/20_apple.png) ![Android](img/20_android.png) ![PC](img/20_windows.png)| Yes|Yes|Yes| Yes, no FTMS support | Yes |
|[BKool](21_applications_detail.md#bkool) |![bike](img/20_bike.png)|![IOS](img/20_apple.png) ![Android](img/20_android.png) ![PC](img/20_windows.png)|Yes|Yes|Yes|Yes| Yes |
|[Fulgaz](21_applications_detail.md#fulgaz)|![bike](img/20_bike.png)|![IOS](img/20_apple.png) ![Android](img/20_android.png) ![PC](img/20_windows.png)|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.

View File

@@ -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.

View File

@@ -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).

View File

@@ -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 |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qabstracthttpserver_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserver_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverliterals_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverrequest_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverresponder_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverresponse_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverrouter_p.h"

View File

@@ -1 +0,0 @@
#include "../../../../../../qthttpserver/src/httpserver/qhttpserverrouterrule_p.h"

View File

@@ -1 +0,0 @@
#include "qabstracthttpserver.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverfutureresponse.h"

View File

@@ -1 +0,0 @@
#include "qhttpserver.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverviewtraits.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverfutureresponse.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverrequest.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverresponder.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverresponse.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverrouter.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverrouterrule.h"

View File

@@ -1 +0,0 @@
#include "qhttpserverrouterviewtraits.h"

View File

@@ -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

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More