Compare commits

...

334 Commits

Author SHA1 Message Date
Roberto Viola
215b9897dd pip works only locking and unlocking the tablet 2022-12-11 18:40:17 +01:00
Roberto Viola
bb7dc3f4e9 Domyos EL 520 Minimum Resistance #1069 2022-12-10 14:30:00 +01:00
Roberto Viola
e9c383ce8b ios project updated 2022-12-10 10:49:59 +01:00
Roberto Viola
395973b0ae Bowflex T6 treadmill #1064 2022-12-10 09:44:16 +01:00
Roberto Viola
8cdbc8463a Open proform wifi bike doesn't reconnect automatically #1062 2022-12-10 09:15:41 +01:00
Roberto Viola
9f1d6a163f The control of RGT app not working with Wahoo Direct Connect for RGT setting (#1059)
* fundaments building :)

* Update project.pbxproj

* dircon wahoo protocol managed for RFT

* The control of RGT app not working with Wahoo Direct Connect for RGT setting #1055

* The control of RGT app not working with Wahoo Direct Connect for RGT setting #1055

* cleaning the code

* The control of RGT app not working with Wahoo Direct Connect for RGT setting #1055
2022-12-10 09:08:21 +01:00
Roberto Viola
44b304bcf7 adding close button on android since the notification icon will keep qz alive 2022-12-09 11:21:57 +01:00
Roberto Viola
b0bea930e0 trying to fix swag bag on android 2022-12-09 11:21:32 +01:00
Roberto Viola
6ce71c69f6 incline doesn't work #1063 2022-12-09 07:05:11 +01:00
Roberto Viola
1bf665ae98 incline doesn't work (Issue #1063) 2022-12-08 17:31:03 +01:00
Roberto Viola
a3e6cbd416 removing qt5-default 2022-12-07 14:40:22 +01:00
Roberto Viola
c2ef787a74 qt5-default missing
https://askubuntu.com/questions/1335184/qt5-default-not-in-ubuntu-21-04
2022-12-07 14:37:48 +01:00
Roberto Viola
c00f2a81ca Bowflex T6 treadmill #1064 2022-12-07 14:28:09 +01:00
Roberto Viola
b4164cd10f Kingsmith walkingpad X21, app not responding #1031 2022-12-07 08:06:27 +01:00
Roberto Viola
f95a0510c7 echelon stride doesn't provide metrics #1056 2022-12-05 09:11:34 +01:00
Roberto Viola
b6c5099e07 Kingsmith walkingpad X21, app not responding #1031 2022-12-05 08:50:52 +01:00
Roberto Viola
bc26307c63 proform wifi bike doesn't reconnect automatically #1062
https://github.com/cagnulein/qdomyos-zwift/issues/1062#issuecomment-1336437407
2022-12-04 16:42:23 +01:00
Roberto Viola
ee0b7993de new iOS requirement 2022-12-02 20:24:50 +01:00
Roberto Viola
93a6a352a1 Tacx Flow #1060 2022-12-02 08:18:48 +01:00
Roberto Viola
1e98454070 version 2.12.9 2022-12-01 17:44:48 +01:00
Roberto Viola
a90d3f227f Wahoo Dircon on Treadmill doesn't flash the inclination icon #1061 2022-12-01 17:44:14 +01:00
Roberto Viola
0ad5ca8651 Background operation // Permanent notification option #711 2022-12-01 16:31:23 +01:00
Roberto Viola
8daf02afb6 Background operation // Permanent notification option #711 2022-12-01 16:06:07 +01:00
Roberto Viola
422bf4144f Tacx Flow #1060 2022-12-01 14:23:49 +01:00
Roberto Viola
57455ee7d6 Back from the same gpx path (START - END - START - END all the time) #987 2022-12-01 09:19:57 +01:00
Roberto Viola
032636b3b6 Background operation // Permanent notification option #711 2022-12-01 08:54:57 +01:00
Roberto Viola
536067d731 Background operation permanent notification option (#1058) under settings 2022-11-30 14:40:54 +01:00
Roberto Viola
9aaa1a0d18 Background operation permanent notification option (#1058)
* first try

* Update NotificationClient.java

* Update NotificationClient.java

* adding Foreground Service

* fixing build errors

* build error fixed

* fixing

* Update ForegroundService.java

* fixing click on the notification
2022-11-30 10:00:05 +01:00
Roberto Viola
17db301d5d fixing forcing resistance,speed and incline for nordictrack adb devices 2022-11-29 17:03:15 +01:00
Roberto Viola
0e4945f15b Back from the same gpx path (START - END - START - END all the time) #987 2022-11-29 11:36:10 +01:00
Roberto Viola
94ba539de4 erg mode for proform wifi bike improved 2022-11-28 10:58:57 +01:00
Roberto Viola
9c9d540585 Domyos Elliptical EL 520 #1046 2022-11-28 04:26:32 +01:00
Roberto Viola
9f6e54dfc4 nordictreadmill can go with negative inclination 2022-11-28 03:10:05 +01:00
Roberto Viola
3b1d24381e Revert "Proform Treadmill on Windows error #1054"
This reverts commit 563e7708f0.
2022-11-27 19:00:20 +01:00
Roberto Viola
563e7708f0 Proform Treadmill on Windows error #1054 2022-11-27 13:45:34 +01:00
Roberto Viola
5127420097 Ability to change target power by tiles for bikes #1053 2022-11-26 08:46:05 +01:00
Roberto Viola
9202958a39 fixing ERG mode for proformwifibike 2022-11-25 08:21:53 +01:00
Roberto Viola
c2ca7a06fe Auto stop when a GPX ride has completed #905 2022-11-22 15:32:28 +01:00
Roberto Viola
0a852644bc Auto stop when a GPX ride has completed #905 2022-11-22 13:47:44 +01:00
Roberto Viola
fd32d29c87 Care Fitness CV-351 : #1042 2022-11-21 16:50:49 +01:00
Roberto Viola
c4c1091147 XT800 treadmill #1051 2022-11-21 15:57:19 +01:00
Roberto Viola
fd15b1ac4f Add ProForm Cadence LT Treadmill to QZ to connect to Peloton [REQ] #1047 2022-11-21 14:32:08 +01:00
Roberto Viola
7b53ee0dc9 build issue fixed 2022-11-21 14:03:35 +01:00
Roberto Viola
59fc9864ec fixing decimal error on inclination on horizontreadmill #1050 2022-11-21 13:50:12 +01:00
Roberto Viola
c59e8a0079 Care Fitness CV-351 : (Issue #1042) 2022-11-21 13:35:03 +01:00
Roberto Viola
96ff845abe inclination offset and gain for treadmill used also on train program 2022-11-21 08:42:05 +01:00
Roberto Viola
62f504bdc7 Power Sensor Profile with 16bit UUID on iOS #1044 2022-11-20 07:52:14 +01:00
Roberto Viola
8113bccb77 Power Sensor Profile with 16bit UUID on iOS #1044 2022-11-19 16:11:28 +01:00
Roberto Viola
ab14cbfa49 Care Fitness CV-351 : #1042 2022-11-19 15:19:09 +01:00
Roberto Viola
ee252a313d Care Fitness CV-351 : #1042 2022-11-18 14:24:04 +01:00
Roberto Viola
293ff2eda1 Virtual Dircon Bike device for treadmill #1043 2022-11-18 14:16:47 +01:00
Roberto Viola
f084b88312 Life Fitness Treadmill FTMS #1018 2022-11-18 12:04:32 +01:00
Roberto Viola
bc9c36624a Stages bike peloton resistance null sometimes #1040 2022-11-18 08:49:55 +01:00
Roberto Viola
b49af71c2e Life Fitness Treadmill FTMS #1018
removing forcing commands since this treadmill is not able to do so
2022-11-18 08:40:19 +01:00
Roberto Viola
118684ebff Carefitness CV-351 #1041 2022-11-18 08:23:51 +01:00
Roberto Viola
9e8c8322e3 Life Fitness Treadmill FTMS #1018 2022-11-17 15:21:24 +01:00
Roberto Viola
cce52fcdfa Update SwagBagView.qml 2022-11-16 15:12:52 +01:00
Roberto Viola
e29b7f1356 swag bag edited in order to be accepted from the app store 2022-11-16 14:16:00 +01:00
Roberto Viola
036f0c4fc9 Blank Screen/No Metrics after Class #857 2022-11-15 14:00:25 +01:00
Roberto Viola
2ff9cdeba6 Apple Watch/Health integration - indoor walk for treadmill instead of indoor cycle #924 2022-11-15 09:27:07 +01:00
Roberto Viola
1b7e61e77a Life Fitness Treadmill FTMS #1018 2022-11-15 08:17:14 +01:00
Roberto Viola
5affe4b687 Add Virtual Pod mode #45 2022-11-14 16:16:46 +01:00
Roberto Viola
5f6d3cc7f3 Footpod (Stryd) support with manual incline #1038 2022-11-14 15:33:14 +01:00
Roberto Viola
c651c682c3 inCondi ET660i support #1007 2022-11-14 08:42:43 +01:00
Roberto Viola
6163b967f7 NordiTrack T 7.0 treadmill #1030
fixing start
2022-11-14 08:33:24 +01:00
Roberto Viola
0c1c25823c Life Fitness Treadmill FTMS #1018 2022-11-14 08:16:12 +01:00
Roberto Viola
30046c8177 Merge pull request #1035 from cagnulein/ftmsCRR
FTMS CRR implemented
2022-11-12 18:30:17 +01:00
Roberto Viola
c893180e4d added QZ_EnableDiscoveryCharsAndDescripttors to domyostreadmill and fitmetria 2022-11-12 09:37:51 +01:00
Roberto Viola
5d49fa8174 ios project updated 2022-11-11 19:59:46 +01:00
Roberto Viola
e0f8c30a94 build issue fixed 2022-11-11 19:58:50 +01:00
Roberto Viola
a4b7eb7ebb fixed build issue 2022-11-11 19:57:12 +01:00
Roberto Viola
ee3067baa0 qmdnsengine doesn't work anymore with iOS 16 or Ventura #1011 2022-11-11 19:54:39 +01:00
Roberto Viola
73a1d8dbb6 fixing qml error 2022-11-11 16:04:37 +01:00
Roberto Viola
63f778c32d added also the wind resistance 2022-11-11 15:37:54 +01:00
Roberto Viola
9d50363a9c removing the gain from the CRR formula 2022-11-11 15:19:17 +01:00
Roberto Viola
7dc112b6f5 implemented 2022-11-11 15:08:16 +01:00
Roberto Viola
3b1a478822 Life Fitness Treadmill FTMS #1018 2022-11-11 09:09:59 +01:00
Roberto Viola
e114745d5f Pace (Treadmill Only) on 3d maps #1020
pin updated
2022-11-10 12:00:01 +01:00
Roberto Viola
a18818d5fe Pace (Treadmill Only) on 3d maps #1020 2022-11-10 10:08:46 +01:00
Roberto Viola
c5fdf4a9bf Life Fitness Treadmill FTMS #1018 2022-11-10 04:39:49 +01:00
Roberto Viola
f45610343b reverting all the iOS mdns madness. Let's wait an answer from Apple 2022-11-10 04:19:45 +01:00
Roberto Viola
f45dd3bfed adding some protection to the domyostreadmill 2022-11-09 20:58:05 +01:00
Roberto Viola
5d0f0138a5 peloton power zone metrics fail #1033 2022-11-09 16:47:36 +01:00
Roberto Viola
827285ede0 JK fitness 577 bike #1032 2022-11-09 09:57:03 +01:00
Roberto Viola
df759a14a1 Android version 2.11.89 2022-11-09 08:10:41 +01:00
Roberto Viola
2b38d4fbe3 JK fitness 577 bike #1032 2022-11-09 08:09:54 +01:00
Roberto Viola
2dccd4016a qmdnsengine doesn't work anymore with iOS 16 or Ventura #1011 2022-11-08 14:59:00 +01:00
Roberto Viola
06ccf9136b Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-11-08 13:37:12 +01:00
Roberto Viola
66e3420abc JK fitness 577 bike #1032 2022-11-08 13:37:09 +01:00
Bepo7012
650fc81c6d Test Videos with higher fps Rates (#1024)
* Test Videos with higher fps Rates

* Set the speed limit higher, no longer a Problem

* iOS project updated

Co-authored-by: Roberto Viola <cagnulein@gmail.com>
2022-11-08 12:14:47 +01:00
Roberto Viola
dff786dd2a NordiTrack T 7.0 treadmill #1030 2022-11-08 11:57:08 +01:00
Roberto Viola
78bc2b2d97 Life Fitness Treadmill FTMS #1018 2022-11-08 08:03:50 +01:00
Roberto Viola
bd79b30eda fixing integer cast for inclination for the proform wifi bike 2022-11-07 14:55:31 +01:00
Roberto Viola
2a6a4e3db8 Disable TTS prefixes for frequent repetitions #986 2022-11-07 14:23:14 +01:00
Roberto Viola
8af33b0845 Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-11-06 07:28:57 +01:00
Roberto Viola
cdb09471b1 iOS project updated 2022-11-06 07:28:47 +01:00
Andrea Gelmini
f5c7b1d891 Fix typos (#1025) 2022-11-05 07:05:53 +01:00
David Mason
7908420622 #962 Bluetooth details to settings for all devices (#963)
* #962 factored out the setting of the last bluetooth device settings to a method

* #962 call setLastBluetoothDevice for every device

* #962 fixed typos

* #962 whitespace adjustments

* #962 whitespace adjustments

* #962 updated qmdnsengine submodule to HEAD

Co-authored-by: Roberto Viola <Cagnulein@gmail.com>
2022-11-04 14:01:58 +01:00
Roberto Viola
e0ba6e6f08 Octane Fitness Q37xi Support #1023 2022-11-04 12:12:26 +01:00
Roberto Viola
c9cb0e5728 QZ / ZWIFT / Treadmill / Smartwatch: Different distance value #1022 2022-11-04 08:36:55 +01:00
Roberto Viola
43c263bd22 GPX Issues on Treadmill #973 2022-11-03 14:54:31 +01:00
Roberto Viola
1f38aee4d0 GPX Issues on Treadmill #973
cesium metrics on the bottom right and with a rounded green box around
2022-11-03 12:25:52 +01:00
Roberto Viola
424d98cb80 using watt gain and watt offset even for target_watt for proform bikes 2022-11-03 12:24:42 +01:00
Roberto Viola
8869913c4e Differences in the GPX starting point #988
new pin point in the 3d map
2022-11-03 10:04:52 +01:00
Roberto Viola
69d9b23b6e Fitshow FTMS bike doesn't send metrics #1019 2022-11-03 08:20:23 +01:00
Roberto Viola
c9b215ff44 Life Fitness Treadmill FTMS #1018 2022-11-03 08:06:03 +01:00
Roberto Viola
ee8c66d3e8 bluetooth discovery fitfan improvement 2022-11-02 16:01:11 +01:00
Roberto Viola
c2589f7f26 AVG Watts per Lap #1009 2022-11-02 15:03:48 +01:00
Roberto Viola
8dd3ff6340 Preset resistance tiles #493 2022-11-02 14:36:43 +01:00
Roberto Viola
82e075798c inCondi ET660i support #1007 2022-11-02 08:42:55 +01:00
Roberto Viola
732e84d2e3 new raspberry install #1014 2022-10-31 11:05:59 +01:00
Roberto Viola
c2d0176b67 ios dircon permission fixed #1011 2022-10-28 23:46:43 +02:00
Roberto Viola
01ef11496d proformwifibike: send only 0.5 inclination 2022-10-28 14:53:48 +02:00
Roberto Viola
0c768a4943 optimizing bluetooth traffic over fitmetria 2022-10-28 14:43:43 +02:00
Roberto Viola
9e63e31954 erg mode added to proformwifiibike 2022-10-28 11:49:27 +02:00
Roberto Viola
c7efa0e5ad fixing a problem with the speed based on power setting enabled
if the wattage was evaluated after the speed in the bike module, the speed was always 0
2022-10-28 09:57:13 +02:00
Roberto Viola
0b9de9509c Differences in the GPX starting point (Issue #988) 2022-10-28 09:17:47 +02:00
Roberto Viola
92dbbcaf5a preparing version 2.11.84 2022-10-28 08:36:35 +02:00
Roberto Viola
d8951ab83e Differences in the GPX starting point #988
chartjs color typo
2022-10-28 08:35:45 +02:00
Roberto Viola
a4bd3cfd6e iOS version 2.11.83 2022-10-26 18:26:09 +02:00
Roberto Viola
83637aa626 adding ECH-UK as another name of echelon stride 2022-10-26 17:40:19 +02:00
Roberto Viola
f65ecd3df7 fixing speed gain and offset to the calculateSpeedFromPower function 2022-10-26 15:13:22 +02:00
Roberto Viola
7e11c740ac Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-10-26 14:26:12 +02:00
Roberto Viola
7b4102964e Apple Watch Miles and Km conversion #1006 2022-10-26 14:26:05 +02:00
Roberto Viola
c706b2db71 Merge pull request #1008 from Bepo7012/master
Added Debug for Video Jump Issues
2022-10-26 12:04:00 +02:00
Bepo7012
70f5b627d1 Added Debug for Video Jump Issues 2022-10-26 11:19:15 +02:00
Roberto Viola
547bd51673 build issues 2022-10-26 10:23:51 +02:00
Roberto Viola
b42f684bbf inCondi ET660i support #1007 2022-10-26 09:46:29 +02:00
Roberto Viola
8434cebdc7 version 2.11.83 2022-10-26 08:28:11 +02:00
Roberto Viola
9f7886af58 Differences in the GPX starting point #988 2022-10-26 08:27:38 +02:00
Roberto Viola
82f3cf227d Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-10-25 16:15:44 +02:00
Roberto Viola
8708bb76cc android version 2.11.82 2022-10-25 16:12:11 +02:00
Roberto Viola
3a23d6798a Merge pull request #1005 from Bepo7012/master
Fix for INF Calculation in cagnulein#973
2022-10-25 15:46:21 +02:00
Bepo7012
9d3457b53c Update trainprogram.cpp
Fix for INF Calculation in cagnulein#973
2022-10-25 15:42:42 +02:00
Roberto Viola
e0f5c90d93 Spirit XT385 not able to get working, Gets discovered but can't see Qdomyos in Zwift Discovery. #833 2022-10-25 14:24:45 +02:00
Roberto Viola
46d2d0c346 Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-10-25 11:52:36 +02:00
Roberto Viola
dc2876f80a Differences in the GPX starting point #988 2022-10-25 11:52:29 +02:00
Roberto Viola
73767d68fe Merge pull request #999 from drmason789/iconcept_virtualdevice_setup_998
#998 only attempt to set up virtual device on first update
2022-10-25 11:44:19 +02:00
Roberto Viola
ee840abd5e Merge pull request #1003 from drmason789/kingsmith_searchingStop_removal_1000 2022-10-25 06:55:14 +02:00
David Mason
31d401a1df #1000 removed searchStopped check from virtual device setup 2022-10-24 21:25:40 +01:00
David Mason
05a0184f03 #998 only attempt to set up virtual device on first update 2022-10-24 20:54:50 +01:00
Roberto Viola
1707f7c8ff yesoul target resistance fixed on zwift 2022-10-24 21:22:19 +02:00
Roberto Viola
c0b4ea071e fixing typo 2022-10-24 16:46:27 +02:00
Roberto Viola
7a2528d007 Merge pull request #997 from cagnulein/preset_tiles
Preset resistance tiles (#493)
2022-10-24 15:31:02 +02:00
Roberto Viola
1a37a4c3b2 Preset resistance tiles (#493) 2022-10-24 15:27:18 +02:00
Roberto Viola
ed1568c583 Spirit XT385 not able to get working, Gets discovered but can't see Qdomyos in Zwift Discovery. #833 2022-10-24 08:45:43 +02:00
Roberto Viola
69d95ee27d Spirit XT385 not able to get working, Gets discovered but can't see Qdomyos in Zwift Discovery. #833 2022-10-22 13:07:53 +02:00
Roberto Viola
1cfb556e6c Merge pull request #992 from jwatral/ant_plus_speed_channel_controller_fix_167
fixed speed value conversion in SpeedChannelController #167
2022-10-20 08:30:02 +02:00
Roberto Viola
20a0b9e968 helper label added 2022-10-20 08:29:15 +02:00
Jaroslaw Watral
57dd826033 removed cadence reference usage in ChannelService 2022-10-20 00:09:54 +02:00
Jaroslaw Watral
fe7e259db3 fixed speed value conversion in SpeedChannelController 2022-10-20 00:01:30 +02:00
Roberto Viola
88ba5a5239 Differences in the GPX starting point #988 2022-10-19 10:24:42 +02:00
Roberto Viola
bbd586895a keeping useful modification 2022-10-18 21:40:08 +02:00
Roberto Viola
ae1d25208e Revert "Differences in the GPX starting point #988"
This reverts commit 2b19896498.
2022-10-18 21:39:11 +02:00
Roberto Viola
2b19896498 Differences in the GPX starting point #988 2022-10-18 14:33:48 +02:00
Roberto Viola
ea6c5c1190 version 2.11.75 2022-10-18 08:03:27 +02:00
Roberto Viola
50bb52c15c gpx without timestamp fixed 2022-10-17 23:19:31 +02:00
Roberto Viola
be4454563b version 2.11.74 for android 2022-10-17 16:54:42 +02:00
Roberto Viola
0ac29a4470 Disable TTS prefixes for frequent repetitions #986 2022-10-17 14:41:29 +02:00
Roberto Viola
136c807f16 Android 2 Android Wahoo Kickr direct no data #983 2022-10-17 11:58:10 +02:00
Roberto Viola
c74e3545d4 Android 2 Android Wahoo Kickr direct no data #983 2022-10-17 10:59:15 +02:00
Roberto Viola
a05345264c adding wifi permissions on android in order to collect the ip address of the wifi for dircon 2022-10-16 20:22:04 +02:00
Roberto Viola
92c10f3042 GPX Issues on Treadmill #973 2022-10-16 14:24:31 +02:00
Roberto Viola
76935090a4 Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-10-16 10:43:09 +02:00
Roberto Viola
2b0accb933 Merach MRK-S02-0B8D Resistance Change #984 2022-10-16 10:43:05 +02:00
Brad Stoney
26e2146051 Update 30_usage.md (#985)
Updated -qml default and added -noqml
2022-10-16 10:39:01 +02:00
Roberto Viola
f552f27224 Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-10-16 09:36:28 +02:00
Roberto Viola
05deaa76fc GPX Issues on Treadmill (Issue #973 2022-10-16 09:36:24 +02:00
Roberto Viola
8d79fb3f35 android version 2.11.70 2022-10-14 08:03:38 +02:00
Roberto Viola
ec748b695e calling watts() instead of m_watt on speed based on power formula 2022-10-14 08:02:42 +02:00
Roberto Viola
8618384bb6 Pro FlexCycle #979 2022-10-13 14:46:43 +02:00
Roberto Viola
11e1117611 adding debug on ftmsbike 2022-10-13 13:38:40 +02:00
Roberto Viola
ba08f656fe adding a setting for zwift compatibility #476 2022-10-13 13:33:58 +02:00
Roberto Viola
af875a7295 Wahoo Direct Connect #476 RGT compatibility fixed
Tested with RGT and Zwift on iOS
2022-10-13 11:49:12 +02:00
Bepo7012
56e38e650a Keep Video and Gpx Position synchronized (Addition to #838) (#945)
* Speed up debugging on Windows

* Added new way for Video Rate

* Tried another Variant

* Fixed double double Conversion

* Splitted Videopos Rate and Frame rate

* Fixed new calculation

* Very easy possible solution

* Simple try

* Not really better, another try

* Adjust both Endpoints for calculation

* Framerate set

* Collecting Data

* Use average Rate

* Adjusted Videoframe Distance

* Fixed correction

* use weighted mean rate

* Played with factor weighting

* Found logic Error in Video Rate

* Factor weighting and try reduce flickering

* Fixed weighting of Rates

* New Idea

* Bad  Idea, changed Factor weighting again

* Code cleanup before Pull Request

* added Time difference for testing spec. Video

* Test should not be in Pull Request

* reverted debug on main.cpp

* ios version 2.11.54

* Fixed rewinding Video

* Variable Weighting Videorate/Framerate

* Added changes again for windows debug

* Different Aproach again

* Fixed wrong speed calc

* Add adjustment to rate based on time difference

* try higher or lower rate by 10% first

* Adjusted the Rate Base

* Removed the Rate % adoption

* video length added

* Implemented Referencepoint at End of Video

* Corrected Video start Time

* Init Videotimestamp with Traintimestamp at Start

* Fixed comparence

* Added trainProgram::TotalGPXSecs

* Corrected return

* Time to finish

* Removed Test and Debug changes in main.cpp

* Debug Message removed for next Windows Build

* Setting Video to correct Pos if length differs

* Don't do anything if Video isn't displayed

* fixed visible

* fixed visible comparison

* added videoSeekPosition in order to change the position of the video in realtime

* Implemented Video Adjustment on start

* Correct Videotimestamp every time if needed

* As usual, fixed Typo

* Reversed Video Timestamp correction

* Implemented case that Video is shorter then gpx

* Approach should be smoother

* Corrected shorter Video

* Fixed Video starting point if hidden/redisplayed

* Reverted Debug changes in main.cpp

* adding acceleration in the calculation of the speed based on wattage

* fixing negative speed values

* Windows debugging again

* added Debug Info to calculateSpeedFromPower

* Moved Watt calc up to have it for Speed calc

* Corrected Start for shorter Videos

* Try to use smaller Frame for average Video Speed

* Use Fullrate if Diff is to high

* Changed high difference rate

* adjusted factor

* Add 1 % of Timediff to Totalrate

* Not successfull, removed again

* rolling resistance setting added

* Hopefully the last approach

* Corrected Target

* Remarked some Things to get Debug Data

* use only 90 % of rate

* Only adjust factor one time per gpx Cycle

* Try Percent adoption

* fixing changeTimestamp callback with milliseconds precision

* Removed % in-/decrease

* Removed call to changeTimestamp

* removed avgInclination for bikes #838

* Test for Debug

* fixing distance error on trxappgateusbbike when speed was calculated over wattage

* tried fake bike

* Use a 2 sec Window

* Added changed Header definition

* try to fix gaps in gps

* Added some Debug Info

* Changed Params for Fake

* Removed 2 seconds frame

* Try Speed without average

* Debug Output Trainprogram

* Add more Debug for Trainprogram

* Removed Trainprogram Debug

* Tried avg. Speed -3/+3 secs

* Fix Bug in avgSpeedCalculation

* try to limit the rate change

* Fixed limiting

* Fixed again

* Fixed init of Limitation

* Removed Limitation, not working as hoped

* Check speed Deviation between 90 and 110 %

* Search for speed in 6 Secs/clearify Property-Names

* Fixed Typo

* Fixed Bug in gpx average Speed calculation

* Changed Filerrange from +/- 15 %

* Fixed possible segmentation Fault

* improving precision in the debuglog

* fixing elapsed time #838

* changeTimestamp again when next point reached

* fixing wrong ratiodistance

* qzsettings updated

* Save currentStepDistance before set to 0

* currentStop is already incremented, use -2

* use Distance from prev.Row too

* currentStep -1 distance and -2 time

* Don't use Point 1

* just a try

* Should be correct now

* changed distance

* fixing merge issues

* fixing another merge issue

* updating ios project

* fixing circuit gpx for video

* caching the odometer for all the scheduler event and removing the changetimestamp not time based

* Calc RatioDistance with Timediff between Points

* do this only if the point get hits first

* Calc distanceRow to gpx Diff

* only use currentStepDistance added since last

* Ratio Time has to be summed

* Redesign the process

* fixed typo

* Forgot one change

* fixed crash

* hopefully fixed now

* only add ratioDistance one time

* Added debug Info

* Filter changingTimestamp if Variance to high

* added Debug Info

* fixed Variance calculation

* fixed typo

* Store last Tick/TimeStamp Ratio for next cycle

* moved ratioDistance in correct scope

* Add ratioDistance, not calculated Ratio

* Back to Ratio again, better Results

* add ms not s

* changed Variance Range to -25/+25%

* Apply filtering Speed to trainingrows

* header definition added

* Fixed typo

* fixed array access

* accessing row data

* fixed bug when timetotickratio was 0

* logic Error

* fix bug in wma calculation

* added more detail to debug

* Calculate new distance into temporary array before

* fixed typo

* removed filter in changing timestamp

* Removed some routines no longer needed

* Remove peak Filter ompletely, no longer needed

* fixed crash at end of program

* fixing ios build and applySpeedFilter only on gpx workout

* adding running cadence from apple watch to ipad

* Fix for iOS Crash?

* fixing treadmill force speed setting with applyspeedfilter on

* adding speedlimit to bike

* Update trainprogram.cpp

Max.Speed for Bikes (=2 * avg current Trainprogram Speed) implemented

* Update trainprogram.cpp

changed to max speed to get a max replay factor of 1.9

* fixing speed limit

* removing bad check on the speedlimit

* adding a new method to know if the video is visible and reset the speed limit

* Fixed Issues on video hiding/redisplaying

 Also makes the first display a little nicer

* Removed no longer needed filterSeconds

The Option in the setting can also be removed, it's no longer needed

* Try to calculate the Speedlimit +1sec

* video_playback_window setting removed

* preparing for the merge

* removed unuseful coment

Co-authored-by: Roberto Viola <cagnulein@gmail.com>
Co-authored-by: Bernhard Ponemayr <bernhard.ponemayr@voglauer.com>
2022-10-13 10:56:12 +02:00
Roberto Viola
3b75f4fa94 Zwift Workouts Over Cadence #980 2022-10-13 10:48:23 +02:00
Roberto Viola
7e18946909 Update settings.qml 2022-10-12 18:16:34 +02:00
Roberto Viola
6edde653b6 Average Speed in the Email doesn't match #977 2022-10-12 09:54:24 +02:00
Roberto Viola
caedc73901 Bike Support Merach MRK-S02-0B8D #978 2022-10-12 09:46:40 +02:00
Roberto Viola
19e552f4cd Pro FlexCycle #979 2022-10-12 08:18:08 +02:00
Roberto Viola
6dc46aa689 license request every 30s 2022-10-11 17:18:27 +02:00
Roberto Viola
2cce91beea fixed typo 2022-10-11 17:03:32 +02:00
Roberto Viola
4824b65590 GPX Issues on Treadmill #973
speed on treadmill fixed
2022-10-11 16:59:15 +02:00
Roberto Viola
f327d13252 fix typo 2022-10-10 20:45:56 +02:00
Roberto Viola
e1a4bbb19a fixin avg inclination for treadmill 2022-10-10 20:31:02 +02:00
Roberto Viola
e11d4610a9 fixing build error on iOS 2022-10-10 20:17:11 +02:00
Roberto Viola
68d1eb678f GPX Issues on Treadmill #973 2022-10-10 16:26:50 +02:00
Roberto Viola
95d1f1fd8e Merge pull request #976 from nzbrian/master
Adding ESLinker YPOO Mini Change treadmill
2022-10-10 10:05:36 +02:00
Brian Bannister
ceaa5f0fa2 Adding ESLinker YPOO Mini Change treadmill 2022-10-10 18:22:53 +13:00
Roberto Viola
efede4bee0 GPX Issues on Treadmill #973 2022-10-09 08:26:46 +02:00
Roberto Viola
18787431a6 fixed crash for nautilus treadmill 2022-10-08 19:55:17 +02:00
Roberto Viola
e30e3c85e3 GPX Issues on Treadmill #973
map icon showed only when speed forcing for treadmill is disabled
2022-10-08 15:19:05 +02:00
Roberto Viola
e5724b43db fixed qzsettings usage 2022-10-08 03:02:45 +02:00
Roberto Viola
2428b4c230 GPX Issues on Treadmill #973 2022-10-07 14:30:34 +02:00
Roberto Viola
cf111ab23c Merge pull request #974 from cagnulein/cherrypickingfromvideobranch
Cherry-picking from video branch
2022-10-07 13:59:38 +02:00
Roberto Viola
3c37f1d77b fixing another merge issue 2022-10-07 12:23:59 +02:00
Roberto Viola
e0a79b51d3 updating ios project 2022-10-07 12:23:33 +02:00
Roberto Viola
5de8e7f1d8 rolling resistance setting added 2022-10-07 12:22:13 +02:00
Roberto Viola
45ddd78624 fixing negative speed values 2022-10-07 12:19:16 +02:00
Roberto Viola
b47d605999 adding acceleration in the calculation of the speed based on wattage 2022-10-07 12:18:40 +02:00
Roberto Viola
b2ea235729 using qzsettings in kcal on fitshowtreadmill 2022-10-06 12:23:45 +02:00
Roberto Viola
875764ac5c fixing kcal and distance for fitshowtreadmills 2022-10-06 08:46:29 +02:00
David Mason
832fd25edb #948 Set string values in cpp file to lower output size (#972)
* #948 copied settings.qml to qzsettings.h

* #948 removed non-settings content

* #948 adjusted tabs

* #948 converted to C++

* #948 move defaults to next line

* #948 fixed invalid bool value from settings.qml

* #948 converted settings.value

* #948 added settings keys and defaults that are not in settings.qml. Fixed some build errors.

* #948 comment fixes

* #948 fixed comments

* #948 include qzsettings.h

* #948 added missing settings keys that are not in settings.qml

* #948 include qzsettings.h

* #948 fixed comment

* #948 changed settings key name to match the one from settings.qml

* #948 converted a QLatin1String to QStringLiteral in qzsettings.h

* #948 added placeholders for comment/documentation

* #948 added function to qDebug log the settings from the QZSettings constants

* #948 some missing usage of new constants

* #948 removed superfluous )

* #948 restored some defaults reading the settings that were not constants. Added some docs on the settings keys.

* #948 some removal of the commented out old defaults for QSettings.value. having checked they match the default obtained from settings.qml

* #948 updates based on feedback about defaults that differ from settings.qml

* #948 removed most of the remaining commented-out defaults having checked them individually against the constants the were replaced with.

* #948 removed more of the remaining commented-out defaults having checked them individually against the constants the were replaced with.

* #948 another use of settings constant

* #948 added missing keys, write settings to debug log from qzsettings in main.cpp. Sort the qdebug dump from qzsettings by key.

* #948 removed unused variable and settings read.

* #948 removed another commented, previously hardcoded default.

* #948 filled in some @briefs, deleted others where deemed to be self explanatory, or  very device specific. A few remain, for more consideration.

* #948 processed most remaining commented out hardcoded default settings values

* #948
- updates for PR review feedback
- factored out setting of last bluetooth device settings

* #948 restored original user_email defaults, and made the constant for it a QLatin1String (though not actually used)

* #948 restored defaults in homeform::sortTiles

* #948 filled in some more @briefs

* #948 added new nordictrack device setting

* Update proformbike.cpp

* removing the qdebugallsettings from main

* #948 set string values in cpp file to lower output size

Co-authored-by: Roberto Viola <Cagnulein@gmail.com>
2022-10-06 08:45:51 +02:00
Roberto Viola
c526d4e675 trying const char instead of qstring for qzsettings (#971) 2022-10-05 18:20:31 +02:00
Roberto Viola
bb8e0f0417 NordicTrack GX 2.7 U #964 2022-10-05 17:27:20 +02:00
Roberto Viola
9cbf72d594 Schwinn 510T #970 2022-10-05 16:28:13 +02:00
Roberto Viola
df65f0d951 android build fixed 2022-10-05 16:26:08 +02:00
Roberto Viola
2325635b28 NordicTrack GX 2.7 U #964 2022-10-05 14:48:40 +02:00
David Mason
1d9c24ba91 Use of named constants for settings keys and default values (#950)
* #948 copied settings.qml to qzsettings.h

* #948 removed non-settings content

* #948 adjusted tabs

* #948 converted to C++

* #948 move defaults to next line

* #948 fixed invalid bool value from settings.qml

* #948 converted settings.value

* #948 added settings keys and defaults that are not in settings.qml. Fixed some build errors.

* #948 comment fixes

* #948 fixed comments

* #948 include qzsettings.h

* #948 added missing settings keys that are not in settings.qml

* #948 include qzsettings.h

* #948 fixed comment

* #948 changed settings key name to match the one from settings.qml

* #948 converted a QLatin1String to QStringLiteral in qzsettings.h

* #948 added placeholders for comment/documentation

* #948 added function to qDebug log the settings from the QZSettings constants

* #948 some missing usage of new constants

* #948 removed superfluous )

* #948 restored some defaults reading the settings that were not constants. Added some docs on the settings keys.

* #948 some removal of the commented out old defaults for QSettings.value. having checked they match the default obtained from settings.qml

* #948 updates based on feedback about defaults that differ from settings.qml

* #948 removed most of the remaining commented-out defaults having checked them individually against the constants the were replaced with.

* #948 removed more of the remaining commented-out defaults having checked them individually against the constants the were replaced with.

* #948 another use of settings constant

* #948 added missing keys, write settings to debug log from qzsettings in main.cpp. Sort the qdebug dump from qzsettings by key.

* #948 removed unused variable and settings read.

* #948 removed another commented, previously hardcoded default.

* #948 filled in some @briefs, deleted others where deemed to be self explanatory, or  very device specific. A few remain, for more consideration.

* #948 processed most remaining commented out hardcoded default settings values

* #948
- updates for PR review feedback
- factored out setting of last bluetooth device settings

* #948 restored original user_email defaults, and made the constant for it a QLatin1String (though not actually used)

* #948 restored defaults in homeform::sortTiles

* #948 filled in some more @briefs

* #948 added new nordictrack device setting

* Update proformbike.cpp

* removing the qdebugallsettings from main

Co-authored-by: Roberto Viola <Cagnulein@gmail.com>
2022-10-05 14:13:10 +02:00
Roberto Viola
39ba7402d7 ftmsCharacteristicChanged missing on the virtualtreadmill 2022-10-05 08:56:04 +02:00
Roberto Viola
4e29df685a Open Train Program Update Suggestions[REQ] #968 2022-10-05 08:48:12 +02:00
Roberto Viola
d1f5afce4c Include elevation gain in summary email #966 2022-10-05 07:59:13 +02:00
Roberto Viola
641783d90a improving precision in the qdebug in the gpx.cpp 2022-10-04 16:04:24 +02:00
Roberto Viola
7f6cdf82cf Video again, just to show and see #838
float to double conversion for the lat and lon in the gpx file
2022-10-04 15:55:38 +02:00
Roberto Viola
6e414f8e02 Include elevation gain in summary email #966 2022-10-04 14:05:31 +02:00
Roberto Viola
5f41e9a08d NordicTrack GX 2.7 U #964 2022-10-04 12:03:23 +02:00
Roberto Viola
115cbc0fa1 fix desync from ipad to iphone #957 2022-10-03 15:26:14 +02:00
Roberto Viola
a702d7e4c3 android version 2.11.63 2022-10-03 09:01:37 +02:00
Roberto Viola
cbe3f7de78 starting NordicTrack GX 2.7 U #964 2022-10-03 08:10:26 +02:00
Roberto Viola
0803236439 Should NordicTrack Commercial X11i treadmill work with Android app? #961 2022-10-03 07:31:31 +02:00
Roberto Viola
5c8c8881e0 build issue fixed #957 2022-10-02 08:56:18 +02:00
Roberto Viola
1bbca34ca5 writing distance and kcal to iphone #957 2022-10-02 07:22:49 +02:00
Roberto Viola
37e98fa0a6 android version 2.11.62 2022-10-02 06:45:48 +02:00
Roberto Viola
9498aa5501 bidirectional Apple Watch communication to IPad #957 2022-10-01 17:24:59 +02:00
Roberto Viola
e659322a0b removing assert for wrong HR devices 2022-10-01 15:21:06 +02:00
Roberto Viola
b74a5b3f8b also the iphone should connect to the ipad to collecting distance and kcal #958 2022-10-01 07:13:20 +02:00
Roberto Viola
6e2541d1b7 Merge pull request #960 from drmason789/ios_peloton_workaround_default_959 2022-10-01 06:37:02 +02:00
David Mason
647717cd73 #959 changed default for reading ios_peloton_workaround setting to true 2022-10-01 00:22:47 +01:00
Roberto Viola
56a09fad21 Merge pull request #958 from cagnulein/AppleWatchFromIpad
Apple Watch to iPad
2022-09-30 22:31:46 +02:00
Roberto Viola
b156396d1c it's working! (at least the hr) 2022-09-30 22:27:56 +02:00
Roberto Viola
ae1f099703 not tested code, but the idea should works 2022-09-30 16:21:48 +02:00
Roberto Viola
07d62ed9ad trying the bonjour approach 2022-09-30 15:53:41 +02:00
Roberto Viola
a8cf95380c adding the multicast entitlement 2022-09-29 13:55:10 +02:00
Roberto Viola
48f2d479a3 first test for Apple Watch on iPad 2022-09-29 12:15:20 +02:00
Roberto Viola
67515b94c9 adding issuehunt account 2022-09-29 10:09:03 +02:00
Roberto Viola
f1b05c5975 #923 horizon profiles can't accept letters 2022-09-28 17:27:43 +02:00
Roberto Viola
2e1d3b03c7 iOS version 2.11.62 2022-09-28 16:54:31 +02:00
Roberto Viola
79e3be71ca Merge pull request #951 from cagnulein/horizontreadmill7_4
Horizon treadmill 7.4
2022-09-28 16:52:16 +02:00
Roberto Viola
82717a6775 Revert "trying to fix some strange init situation #923"
This reverts commit f4b9be340f.
2022-09-28 15:09:41 +02:00
Roberto Viola
1f0582e0d0 license retry 2022-09-28 14:58:29 +02:00
Roberto Viola
f4b9be340f trying to fix some strange init situation #923 2022-09-28 09:52:57 +02:00
Roberto Viola
43d6e6c233 removed the - 500 meter offset from fit file (strava seems to doesn't consider it)
https://www.strava.com/activities/7873313493
2022-09-27 09:39:02 +02:00
Roberto Viola
2e608cae22 Resistance control BH Fitness i.CARBON BIKE DUAL H8705LW with the DualKit FTMS Extension #912 2022-09-26 14:28:56 +02:00
Roberto Viola
0d8dd957ad fixing build error on iOS 2022-09-26 11:32:36 +02:00
Roberto Viola
810391f03b Apple Watch/Health integration - indoor walk for treadmill instead of indoor cycle #924 2022-09-26 11:14:01 +02:00
Roberto Viola
386484d03b Resistance control BH Fitness i.CARBON BIKE DUAL H8705LW with the DualKit FTMS Extension #912 2022-09-26 10:54:14 +02:00
Roberto Viola
2c2436a430 Resistance control BH Fitness i.CARBON BIKE DUAL H8705LW with the DualKit FTMS Extension #912 2022-09-26 09:58:58 +02:00
Roberto Viola
06c178cbff fixing profiles 2022-09-26 08:30:57 +02:00
Roberto Viola
ce22fef00d fixed wrong index and segmentation fault 2022-09-25 09:15:04 +02:00
Roberto Viola
4c49918a97 fixing wrong index #923 2022-09-24 20:24:09 +02:00
Roberto Viola
88344438a7 fixing decimal point on distance on apple watch 2022-09-22 17:25:28 +02:00
Roberto Viola
09d8881b43 profiles for horizon in the settings added 2022-09-22 14:22:29 +02:00
Roberto Viola
83fe4e19ec fixing pid heart (double issue) #427 2022-09-22 12:06:29 +02:00
Roberto Viola
5733889e8a CRC function for updating the profile messages 2022-09-22 08:59:10 +02:00
Roberto Viola
5895f5cc7c Merge branch 'master' into horizontreadmill7_4 2022-09-22 08:08:41 +02:00
Roberto Viola
94830044f6 disabling qml too if the no-gui selector is enabled 2022-09-21 08:10:09 +02:00
Roberto Viola
3200cf2b28 PID function a little pushy #427 2022-09-20 14:34:51 +02:00
Roberto Viola
38cb8f4ccd Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-09-20 11:00:35 +02:00
Roberto Viola
73a20b9980 Avoidance of compiler warning #936 2022-09-20 11:00:33 +02:00
Roberto Viola
57b06c2143 Merge pull request #937 from cagnulein/Speed-Sensor
Speed Sensor #935
2022-09-20 10:22:30 +02:00
Roberto Viola
583ab09dc4 fixing battery cscbike issue 2022-09-20 10:18:33 +02:00
Roberto Viola
67a5f8612f fixing memory overflow 2022-09-20 09:43:40 +02:00
Roberto Viola
14c91a308b Profiles seem to overwrite each other #947 2022-09-20 08:57:49 +02:00
Roberto Viola
8995cf866c disabling HR from FTMS rower is now possible 2022-09-20 08:44:35 +02:00
Roberto Viola
3808619f3e reverting tunturi fix 2022-09-18 19:40:18 +02:00
Roberto Viola
3206a28199 trying to fix communication error on TUNTURI_2 #920 2022-09-18 18:56:43 +02:00
Roberto Viola
216e6874b2 Tunturi E60 Performance combatibly (Discussion #920)Tunturi E60 Performance combatibly (Discussion #920) 2022-09-18 16:06:56 +02:00
Roberto Viola
28aaf7fbff Bodytone DS25 2022-09-18 15:41:06 +02:00
Roberto Viola
55d4467886 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-16 16:17:41 +02:00
Roberto Viola
f1887b7ea1 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-16 14:54:58 +02:00
Roberto Viola
a8bc82c2d0 2.11.53 2022-09-16 14:21:21 +02:00
Roberto Viola
3b2dcaef43 Yesoul C1H #944 2022-09-16 13:58:22 +02:00
Roberto Viola
cd217d4f38 Connecting TUNTURI Fitcycle 50i #932 2022-09-16 11:31:31 +02:00
Roberto Viola
c5e113bd20 bluetooth device setting to max width 2022-09-16 10:10:18 +02:00
Roberto Viola
d63eafe0b3 fixed build issue #838 2022-09-16 09:59:29 +02:00
Roberto Viola
ea69aa2251 adding the videotimestamp #838 2022-09-16 09:52:46 +02:00
Roberto Viola
e697842df7 Merge pull request #941 from cagnulein/quit_button
Quit option #939
2022-09-15 08:41:28 +02:00
Roberto Viola
909bc2dbc5 use "nickname" setting to profiles name #923 2022-09-14 15:14:13 +02:00
Roberto Viola
d152c5d9c6 Quit option #939 2022-09-14 10:47:16 +02:00
Roberto Viola
dad642e82d trying to remove profiles #923 2022-09-14 10:32:27 +02:00
Roberto Viola
c3d757a872 Connecting TUNTURI Fitcycle 50i #932 2022-09-12 15:16:57 +02:00
Roberto Viola
a1cd69d68f Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-12 14:48:23 +02:00
Roberto Viola
cd6edddea0 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-12 14:04:24 +02:00
Roberto Viola
22a8352963 Speed Sensor #935 2022-09-12 11:11:04 +02:00
Roberto Viola
997b04ee54 Horizon GR7 Spin Bike #477
bike without the 2AD2 characteristic
2022-09-12 10:30:15 +02:00
Roberto Viola
835b1c3956 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-12 10:16:57 +02:00
Roberto Viola
097e23d20b Support for Whipr kayak please #653 2022-09-12 08:15:42 +02:00
Roberto Viola
51e55a81e7 Video again, just to show and see #838 2022-09-10 21:01:44 +02:00
Roberto Viola
95eeae56cc Connecting TUNTURI Fitcycle 50i #932 2022-09-10 19:06:04 +02:00
Roberto Viola
90278bd6b2 ios 2.11.49 2022-09-10 17:21:37 +02:00
Roberto Viola
32c0019f89 fixing ftms rower bug in the kcal metric 2022-09-10 17:14:48 +02:00
Roberto Viola
f8df2c580b Open Video again, just to show and see #838 2022-09-10 13:10:40 +02:00
Roberto Viola
38dce24e26 Kickr Rollr #934 2022-09-10 13:09:21 +02:00
Roberto Viola
5bfa16ace1 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-09 14:43:21 +02:00
Roberto Viola
7b0819dce2 Horizon GR7 Spin Bike #477 2022-09-09 13:56:21 +02:00
Roberto Viola
c349e3d6e4 HR fixed for wifi based devices 2022-09-09 09:38:14 +02:00
Roberto Viola
e6a3ce0d3c Video again, just to show and see (Issue #838) 2022-09-09 09:26:36 +02:00
Roberto Viola
85f8d4454f Merge pull request #928 from cagnulein/VideoWorkoutFooter
Video Workout footer
2022-09-08 20:21:36 +02:00
Roberto Viola
3ea6780fe7 version 2.11.46 2022-09-08 20:17:07 +02:00
Roberto Viola
8c2598f2c6 Video again, just to show and see #838 2022-09-08 20:16:06 +02:00
Roberto Viola
6c987ef4bf Merge pull request #930 from drmason789/discovery_927 2022-09-07 21:46:39 +02:00
David Mason
970915996a #927 log a qDebug when stopDiscovery is called when the discovery agent is null 2022-09-07 20:42:32 +01:00
David Mason
7159812dbc #927 route starting and stopping of bluetooth discovery through new functions startDiscovery() and stopDiscovery() 2022-09-07 20:17:51 +01:00
Roberto Viola
515ec1605b faketreadmill HR fixed 2022-09-07 21:14:52 +02:00
Roberto Viola
00802d1374 qz immediately crashes (Issue #929) 2022-09-07 21:03:00 +02:00
Roberto Viola
428f07dc6f Video again, just to show and see (Issue #838) 2022-09-07 20:55:45 +02:00
Roberto Viola
dcfb1f9588 Video again, just to show and see #838 2022-09-07 16:52:24 +02:00
Roberto Viola
5736cc6a97 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-07 16:48:59 +02:00
Roberto Viola
146ebd156c Video again, just to show and see #838 2022-09-07 15:18:37 +02:00
Roberto Viola
0ad2c37dcb Video again, just to show and see #838 2022-09-07 12:09:08 +02:00
Roberto Viola
253244abda typo fixed! 2022-09-07 08:34:32 +02:00
Roberto Viola
0ac3d3e944 version 2.11.43 2022-09-06 15:52:35 +02:00
Roberto Viola
c6b1398aa9 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-06 15:45:55 +02:00
Roberto Viola
4ba6862c61 Cant enter colon for times in iOS app under pace options #926 #838 2022-09-06 15:36:18 +02:00
Roberto Viola
9066c4e02a typo fixed 2022-09-06 12:26:42 +02:00
Roberto Viola
9349996989 adding some debug to changeTimestamp 2022-09-06 09:25:31 +02:00
Roberto Viola
da5157970a Merge branch 'master' of https://github.com/cagnulein/qdomyos-zwift 2022-09-06 09:18:40 +02:00
Roberto Viola
28efbcdd9b Apple Watch/Health integration - indoor walk for treadmill instead of indoor cycle #924 2022-09-06 09:18:29 +02:00
Roberto Viola
10200c1e29 Horizon 7.4 Treadmill starts on its own after app launch #923 2022-09-06 08:45:41 +02:00
Roberto Viola
e37e0853d1 Video again, just to show and see #838 2022-09-06 08:36:15 +02:00
Roberto Viola
4b464ba041 sole f80 treadmill HR builtin handled 2022-09-05 15:56:31 +02:00
Roberto Viola
ff7863a25c Horizon 7.4 Treadmill starts on its own after app launch #923
pause handled
2022-09-05 15:46:44 +02:00
Roberto Viola
72af1701a2 Apple Watch/Health integration - indoor walk for treadmill instead of indoor cycle (Issue #924) 2022-09-05 11:36:23 +02:00
Roberto Viola
487ff2e8e9 Apple Watch/Health integration - indoor walk for treadmill instead of indoor cycle (Issue #924) 2022-09-05 11:23:15 +02:00
Roberto Viola
6cda7cd02c Apple Watch/Health integration - indoor walk for treadmill instead of indoor cycle (Issue #924) 2022-09-05 11:05:01 +02:00
Roberto Viola
77b34e2f41 start event added for horizon treadmill 7.4 2022-09-03 16:26:42 +02:00
Roberto Viola
0e27c87480 Horizon 7.4 Treadmill starts on its own after app launch (Issue #923) 2022-09-03 11:04:50 +02:00
Roberto Viola
fb1bac5e5e "Virtual Treadmill" #917 2022-09-02 09:16:41 +02:00
Roberto Viola
27ea164da1 Spirit Xt485 Treadmill Connection (Log file attached) #918 2022-09-02 08:43:57 +02:00
Roberto Viola
2ec9abc4ad Video again, just to show and see #838
trying a different videorate approach
2022-09-02 08:03:55 +02:00
Roberto Viola
bb1d903e07 Debug log using qdomyos on a proform Tour de France 10.0 bike #877 2022-08-31 14:04:46 +02:00
Roberto Viola
9ccb1ef5d5 Debug log using qdomyos on a proform Tour de France 10.0 bike #877 2022-08-31 12:08:32 +02:00
Roberto Viola
065ee078a2 Merge pull request #913 from cagnulein/android_api_31 2022-08-30 15:27:16 +02:00
Roberto Viola
8b876ac17f footer added and it's working! 2022-08-03 17:56:18 +02:00
220 changed files with 17785 additions and 4852 deletions

2
.github/FUNDING.yml vendored
View File

@@ -7,6 +7,6 @@ ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
issuehunt: cagnulein
otechie: # Replace with a single Otechie username
custom: ['https://www.buymeacoffee.com/cagnulein'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -142,7 +142,7 @@ jobs:
ref: "zwift"
- name: Install packages required to run QZ inside workflow
run: sudo apt update -y && sudo apt-get install -y qt5-default qtquickcontrols2-5-dev 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
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
- name: Install Qt
uses: jurplel/install-qt-action@v2

1
.gitignore vendored
View File

@@ -45,3 +45,4 @@ template-examples/train-program-saver/debug.js
*build-*
!build-qdomyos-zwift-Qt_*_for_iOS-Debug # Needed for Apple Watch
src/inner_templates/googlemaps/cesium-key.js
*.autosave

View File

@@ -167,6 +167,8 @@
872261F0289EA887006A6F75 /* moc_nordictrackelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872261EF289EA887006A6F75 /* moc_nordictrackelliptical.cpp */; };
8727A47727849EA600019B5D /* paferstreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8727A47627849EA600019B5D /* paferstreadmill.cpp */; };
8727A47927849EB200019B5D /* moc_paferstreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8727A47827849EB200019B5D /* moc_paferstreadmill.cpp */; };
872A20DA28C5EC380037774D /* faketreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872A20D928C5EC380037774D /* faketreadmill.cpp */; };
872A20DC28C5F5CE0037774D /* moc_faketreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872A20DB28C5F5CE0037774D /* moc_faketreadmill.cpp */; };
872BAB4E261750EE006A59AB /* libQt5Charts.a in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 872BAB4D261750EE006A59AB /* libQt5Charts.a */; };
872BAB50261751FB006A59AB /* libqtchartsqml2.a in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 872BAB4F261751FB006A59AB /* libqtchartsqml2.a */; };
873063BE259DF20000DA0F44 /* heartratebelt.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 873063BC259DF20000DA0F44 /* heartratebelt.cpp */; };
@@ -259,6 +261,7 @@
8762D5132601F89500F6F049 /* scanrecordresult.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8762D5112601F89500F6F049 /* scanrecordresult.cpp */; };
87646C2027B5064600F82131 /* bhfitnesselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87646C1E27B5064500F82131 /* bhfitnesselliptical.cpp */; };
87646C2227B5065100F82131 /* moc_bhfitnesselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87646C2127B5065100F82131 /* moc_bhfitnesselliptical.cpp */; };
8767EF1E29448D6700810C0F /* characteristicwriteprocessor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */; };
8768D1FB285081FE00F58E3A /* nordictrackifitadbtreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8768D1F9285081FE00F58E3A /* nordictrackifitadbtreadmill.cpp */; };
8768D1FD2850820B00F58E3A /* moc_nordictrackifitadbtreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8768D1FC2850820B00F58E3A /* moc_nordictrackifitadbtreadmill.cpp */; };
876BFC9C27BE35C5001D7645 /* proformelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 876BFC9827BE35C4001D7645 /* proformelliptical.cpp */; };
@@ -314,6 +317,10 @@
87900DC8268B673C000CB351 /* moc_renphobike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87900DC7268B673C000CB351 /* moc_renphobike.cpp */; };
8790FDDF277B0ABA00247550 /* nautilustreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8790FDDD277B0ABA00247550 /* nautilustreadmill.cpp */; };
8790FDE1277B0AC600247550 /* moc_nautilustreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8790FDE0277B0AC600247550 /* moc_nautilustreadmill.cpp */; };
87917A7328E768D200F8D9AC /* Browser.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87917A6E28E768D200F8D9AC /* Browser.swift */; };
87917A7528E768D200F8D9AC /* Connection.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87917A7028E768D200F8D9AC /* Connection.swift */; };
87917A7628E768D200F8D9AC /* Server.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87917A7128E768D200F8D9AC /* Server.swift */; };
87917A7728E768D200F8D9AC /* Client.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87917A7228E768D200F8D9AC /* Client.swift */; };
8791A8AA25C8603F003B50B2 /* moc_inspirebike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8791A8A925C8603F003B50B2 /* moc_inspirebike.cpp */; };
8791A8AB25C861BD003B50B2 /* inspirebike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8791A8A825C8602A003B50B2 /* inspirebike.cpp */; };
87958F1927628D4500124B24 /* elitesterzosmart.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87958F1827628D4500124B24 /* elitesterzosmart.cpp */; };
@@ -389,6 +396,10 @@
87CC3B9E25A08812001EC5A8 /* moc_elliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87CC3B9C25A08812001EC5A8 /* moc_elliptical.cpp */; };
87CC3BA325A0885F001EC5A8 /* domyoselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87CC3B9F25A0885D001EC5A8 /* domyoselliptical.cpp */; };
87CC3BA425A0885F001EC5A8 /* elliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87CC3BA025A0885D001EC5A8 /* elliptical.cpp */; };
87CF5169293C879800A7CABC /* characteristicwriteprocessore005.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87CF5167293C879700A7CABC /* characteristicwriteprocessore005.cpp */; };
87CF516B293C87B000A7CABC /* moc_characteristicwriteprocessore005.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87CF516A293C87AF00A7CABC /* moc_characteristicwriteprocessore005.cpp */; };
87D10552290996EA00B3935B /* mepanelbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D10550290996EA00B3935B /* mepanelbike.cpp */; };
87D105542909971100B3935B /* moc_mepanelbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D105532909971100B3935B /* moc_mepanelbike.cpp */; };
87D2699F25F535200076AA48 /* m3ibike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D2699A25F535160076AA48 /* m3ibike.cpp */; };
87D269A025F535200076AA48 /* skandikawiribike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D2699D25F535180076AA48 /* skandikawiribike.cpp */; };
87D269A325F535340076AA48 /* moc_skandikawiribike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D269A125F535300076AA48 /* moc_skandikawiribike.cpp */; };
@@ -414,6 +425,8 @@
87DF68BF25E2675100FCDA46 /* moc_schwinnic4bike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DF68BC25E2675100FCDA46 /* moc_schwinnic4bike.cpp */; };
87E0761D277A081A00FDA0F9 /* technogymmyruntreadmillrfcomm.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E0761B277A081900FDA0F9 /* technogymmyruntreadmillrfcomm.cpp */; };
87E0761F277A082300FDA0F9 /* moc_technogymmyruntreadmillrfcomm.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E0761E277A082300FDA0F9 /* moc_technogymmyruntreadmillrfcomm.cpp */; };
87E2F85D291ED308002BDC65 /* lifefitnesstreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E2F85C291ED308002BDC65 /* lifefitnesstreadmill.cpp */; };
87E2F85F291ED315002BDC65 /* moc_lifefitnesstreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E2F85E291ED315002BDC65 /* moc_lifefitnesstreadmill.cpp */; };
87E34C2B2886F95400CEDE4B /* octanetreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E34C2A2886F95400CEDE4B /* octanetreadmill.cpp */; };
87E34C2D2886F99A00CEDE4B /* moc_octanetreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E34C2C2886F99900CEDE4B /* moc_octanetreadmill.cpp */; };
87E5D2C625E69F3100BDBE6C /* horizontreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E5D2C525E69F3100BDBE6C /* horizontreadmill.cpp */; };
@@ -433,7 +446,10 @@
87EFB57025BD704A0039DD5A /* moc_proformtreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EFB56F25BD704A0039DD5A /* moc_proformtreadmill.cpp */; };
87EFE45927A518F5006EA1C3 /* nautiluselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EFE45827A518F5006EA1C3 /* nautiluselliptical.cpp */; };
87EFE45B27A51901006EA1C3 /* moc_nautiluselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EFE45A27A51900006EA1C3 /* moc_nautiluselliptical.cpp */; };
87F02E4029178524000DB52C /* octaneelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87F02E3E29178523000DB52C /* octaneelliptical.cpp */; };
87F02E4229178545000DB52C /* moc_octaneelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87F02E4129178545000DB52C /* moc_octaneelliptical.cpp */; };
87F1179E26A5FBDE00541B3A /* libqtwebview_darwin.a in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 87420DF7269D7CE1000C5EC6 /* libqtwebview_darwin.a */; };
87F527BE28EEB5AA00A9F8D5 /* qzsettings.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87F527BC28EEB5AA00A9F8D5 /* qzsettings.cpp */; };
87F93427278E0EC00088B596 /* domyosrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87F93426278E0EC00088B596 /* domyosrower.cpp */; };
87F93429278E0ECF0088B596 /* moc_domyosrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87F93428278E0ECF0088B596 /* moc_domyosrower.cpp */; };
87FA11AB27C5ECD1008AC5D1 /* ultrasportbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FA11AA27C5ECD1008AC5D1 /* ultrasportbike.cpp */; };
@@ -833,6 +849,9 @@
8727A47527849EA600019B5D /* paferstreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = paferstreadmill.h; path = ../src/paferstreadmill.h; sourceTree = "<group>"; };
8727A47627849EA600019B5D /* paferstreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = paferstreadmill.cpp; path = ../src/paferstreadmill.cpp; sourceTree = "<group>"; };
8727A47827849EB200019B5D /* moc_paferstreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_paferstreadmill.cpp; sourceTree = "<group>"; };
872A20D828C5EC380037774D /* faketreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = faketreadmill.h; path = ../src/faketreadmill.h; sourceTree = "<group>"; };
872A20D928C5EC380037774D /* faketreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = faketreadmill.cpp; path = ../src/faketreadmill.cpp; sourceTree = "<group>"; };
872A20DB28C5F5CE0037774D /* moc_faketreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_faketreadmill.cpp; sourceTree = "<group>"; };
872BAB4D261750EE006A59AB /* libQt5Charts.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQt5Charts.a; path = ../../Qt/5.15.2/ios/lib/libQt5Charts.a; sourceTree = "<group>"; };
872BAB4F261751FB006A59AB /* libqtchartsqml2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libqtchartsqml2.a; path = ../../Qt/5.15.2/ios/qml/QtCharts/libqtchartsqml2.a; sourceTree = "<group>"; };
873063BC259DF20000DA0F44 /* heartratebelt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = heartratebelt.cpp; path = ../src/heartratebelt.cpp; sourceTree = "<group>"; };
@@ -972,6 +991,7 @@
87646C1E27B5064500F82131 /* bhfitnesselliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bhfitnesselliptical.cpp; path = ../src/bhfitnesselliptical.cpp; sourceTree = "<group>"; };
87646C1F27B5064500F82131 /* bhfitnesselliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bhfitnesselliptical.h; path = ../src/bhfitnesselliptical.h; sourceTree = "<group>"; };
87646C2127B5065100F82131 /* moc_bhfitnesselliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_bhfitnesselliptical.cpp; sourceTree = "<group>"; };
8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicwriteprocessor.cpp; path = ../src/characteristicwriteprocessor.cpp; sourceTree = "<group>"; };
8768D1F9285081FE00F58E3A /* nordictrackifitadbtreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nordictrackifitadbtreadmill.cpp; path = ../src/nordictrackifitadbtreadmill.cpp; sourceTree = "<group>"; };
8768D1FA285081FE00F58E3A /* nordictrackifitadbtreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nordictrackifitadbtreadmill.h; path = ../src/nordictrackifitadbtreadmill.h; sourceTree = "<group>"; };
8768D1FC2850820B00F58E3A /* moc_nordictrackifitadbtreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_nordictrackifitadbtreadmill.cpp; sourceTree = "<group>"; };
@@ -1059,6 +1079,10 @@
8790FDDD277B0ABA00247550 /* nautilustreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nautilustreadmill.cpp; path = ../src/nautilustreadmill.cpp; sourceTree = "<group>"; };
8790FDDE277B0ABA00247550 /* nautilustreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nautilustreadmill.h; path = ../src/nautilustreadmill.h; sourceTree = "<group>"; };
8790FDE0277B0AC600247550 /* moc_nautilustreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_nautilustreadmill.cpp; sourceTree = "<group>"; };
87917A6E28E768D200F8D9AC /* Browser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Browser.swift; path = ../src/ios/AppleWatchToIpad/Browser.swift; sourceTree = "<group>"; };
87917A7028E768D200F8D9AC /* Connection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Connection.swift; path = ../src/ios/AppleWatchToIpad/Connection.swift; sourceTree = "<group>"; };
87917A7128E768D200F8D9AC /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Server.swift; path = ../src/ios/AppleWatchToIpad/Server.swift; sourceTree = "<group>"; };
87917A7228E768D200F8D9AC /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Client.swift; path = ../src/ios/AppleWatchToIpad/Client.swift; sourceTree = "<group>"; };
8791A8A725C8602A003B50B2 /* inspirebike.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = inspirebike.h; path = ../src/inspirebike.h; sourceTree = "<group>"; };
8791A8A825C8602A003B50B2 /* inspirebike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = inspirebike.cpp; path = ../src/inspirebike.cpp; sourceTree = "<group>"; };
8791A8A925C8603F003B50B2 /* moc_inspirebike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_inspirebike.cpp; sourceTree = "<group>"; };
@@ -1173,6 +1197,12 @@
87CC3BA025A0885D001EC5A8 /* elliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = elliptical.cpp; path = ../src/elliptical.cpp; sourceTree = "<group>"; };
87CC3BA125A0885E001EC5A8 /* elliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = elliptical.h; path = ../src/elliptical.h; sourceTree = "<group>"; };
87CC3BA225A0885E001EC5A8 /* domyoselliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domyoselliptical.h; path = ../src/domyoselliptical.h; sourceTree = "<group>"; };
87CF5167293C879700A7CABC /* characteristicwriteprocessore005.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicwriteprocessore005.cpp; path = ../src/characteristicwriteprocessore005.cpp; sourceTree = "<group>"; };
87CF5168293C879700A7CABC /* characteristicwriteprocessore005.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = characteristicwriteprocessore005.h; path = ../src/characteristicwriteprocessore005.h; sourceTree = "<group>"; };
87CF516A293C87AF00A7CABC /* moc_characteristicwriteprocessore005.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_characteristicwriteprocessore005.cpp; sourceTree = "<group>"; };
87D10550290996EA00B3935B /* mepanelbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mepanelbike.cpp; path = ../src/mepanelbike.cpp; sourceTree = "<group>"; };
87D10551290996EA00B3935B /* mepanelbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mepanelbike.h; path = ../src/mepanelbike.h; sourceTree = "<group>"; };
87D105532909971100B3935B /* moc_mepanelbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_mepanelbike.cpp; sourceTree = "<group>"; };
87D2699925F535160076AA48 /* m3ibike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = m3ibike.h; path = ../src/m3ibike.h; sourceTree = "<group>"; };
87D2699A25F535160076AA48 /* m3ibike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = m3ibike.cpp; path = ../src/m3ibike.cpp; sourceTree = "<group>"; };
87D2699C25F535170076AA48 /* skandikawiribike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = skandikawiribike.h; path = ../src/skandikawiribike.h; sourceTree = "<group>"; };
@@ -1209,6 +1239,9 @@
87E0761B277A081900FDA0F9 /* technogymmyruntreadmillrfcomm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = technogymmyruntreadmillrfcomm.cpp; path = ../src/technogymmyruntreadmillrfcomm.cpp; sourceTree = "<group>"; };
87E0761C277A081900FDA0F9 /* technogymmyruntreadmillrfcomm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = technogymmyruntreadmillrfcomm.h; path = ../src/technogymmyruntreadmillrfcomm.h; sourceTree = "<group>"; };
87E0761E277A082300FDA0F9 /* moc_technogymmyruntreadmillrfcomm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_technogymmyruntreadmillrfcomm.cpp; sourceTree = "<group>"; };
87E2F85B291ED308002BDC65 /* lifefitnesstreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lifefitnesstreadmill.h; path = ../src/lifefitnesstreadmill.h; sourceTree = "<group>"; };
87E2F85C291ED308002BDC65 /* lifefitnesstreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lifefitnesstreadmill.cpp; path = ../src/lifefitnesstreadmill.cpp; sourceTree = "<group>"; };
87E2F85E291ED315002BDC65 /* moc_lifefitnesstreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_lifefitnesstreadmill.cpp; sourceTree = "<group>"; };
87E34C292886F95300CEDE4B /* octanetreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = octanetreadmill.h; path = ../src/octanetreadmill.h; sourceTree = "<group>"; };
87E34C2A2886F95400CEDE4B /* octanetreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = octanetreadmill.cpp; path = ../src/octanetreadmill.cpp; sourceTree = "<group>"; };
87E34C2C2886F99900CEDE4B /* moc_octanetreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_octanetreadmill.cpp; sourceTree = "<group>"; };
@@ -1237,6 +1270,11 @@
87EFE45727A518F5006EA1C3 /* nautiluselliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nautiluselliptical.h; path = ../src/nautiluselliptical.h; sourceTree = "<group>"; };
87EFE45827A518F5006EA1C3 /* nautiluselliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nautiluselliptical.cpp; path = ../src/nautiluselliptical.cpp; sourceTree = "<group>"; };
87EFE45A27A51900006EA1C3 /* moc_nautiluselliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_nautiluselliptical.cpp; sourceTree = "<group>"; };
87F02E3E29178523000DB52C /* octaneelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = octaneelliptical.cpp; path = ../src/octaneelliptical.cpp; sourceTree = "<group>"; };
87F02E3F29178524000DB52C /* octaneelliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = octaneelliptical.h; path = ../src/octaneelliptical.h; sourceTree = "<group>"; };
87F02E4129178545000DB52C /* moc_octaneelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_octaneelliptical.cpp; sourceTree = "<group>"; };
87F527BC28EEB5AA00A9F8D5 /* qzsettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = qzsettings.cpp; path = ../src/qzsettings.cpp; sourceTree = "<group>"; };
87F527BD28EEB5AA00A9F8D5 /* qzsettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qzsettings.h; path = ../src/qzsettings.h; sourceTree = "<group>"; };
87F93425278E0EC00088B596 /* domyosrower.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = domyosrower.h; path = ../src/domyosrower.h; sourceTree = "<group>"; };
87F93426278E0EC00088B596 /* domyosrower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = domyosrower.cpp; path = ../src/domyosrower.cpp; sourceTree = "<group>"; };
87F93428278E0ECF0088B596 /* moc_domyosrower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_domyosrower.cpp; sourceTree = "<group>"; };
@@ -1593,6 +1631,7 @@
0FF051564C679F373AD93E32 /* ios */ = {
isa = PBXGroup;
children = (
87917A6D28E768B000F8D9AC /* AppleWatchToIpad */,
8754D24B27F786F0003D7054 /* virtualrower.swift */,
2F0E70F726316F3600E11F3A /* virtualbike_zwift.swift */,
871E4CD025A6FB5A00E18D6D /* BLEPeripheralManager.swift */,
@@ -1615,6 +1654,11 @@
25B08E2869634E9BCBA333A2 /* Generated Sources */ = {
isa = PBXGroup;
children = (
87CF516A293C87AF00A7CABC /* moc_characteristicwriteprocessore005.cpp */,
87E2F85E291ED315002BDC65 /* moc_lifefitnesstreadmill.cpp */,
87F02E4129178545000DB52C /* moc_octaneelliptical.cpp */,
87D105532909971100B3935B /* moc_mepanelbike.cpp */,
872A20DB28C5F5CE0037774D /* moc_faketreadmill.cpp */,
878C9E6A28B77E9800669129 /* moc_nordictrackifitadbbike.cpp */,
872261EF289EA887006A6F75 /* moc_nordictrackelliptical.cpp */,
879E5AA9289C05A500FEA38A /* moc_proformwifitreadmill.cpp */,
@@ -1767,6 +1811,19 @@
2EB56BE3C2D93CDAB0C52E67 /* Sources */ = {
isa = PBXGroup;
children = (
8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */,
87CF5167293C879700A7CABC /* characteristicwriteprocessore005.cpp */,
87CF5168293C879700A7CABC /* characteristicwriteprocessore005.h */,
87E2F85C291ED308002BDC65 /* lifefitnesstreadmill.cpp */,
87E2F85B291ED308002BDC65 /* lifefitnesstreadmill.h */,
87F02E3E29178523000DB52C /* octaneelliptical.cpp */,
87F02E3F29178524000DB52C /* octaneelliptical.h */,
87D10550290996EA00B3935B /* mepanelbike.cpp */,
87D10551290996EA00B3935B /* mepanelbike.h */,
87F527BC28EEB5AA00A9F8D5 /* qzsettings.cpp */,
87F527BD28EEB5AA00A9F8D5 /* qzsettings.h */,
872A20D928C5EC380037774D /* faketreadmill.cpp */,
872A20D828C5EC380037774D /* faketreadmill.h */,
878C9E6828B77E7B00669129 /* nordictrackifitadbbike.cpp */,
878C9E6728B77E7B00669129 /* nordictrackifitadbbike.h */,
872261EC289EA873006A6F75 /* nordictrackelliptical.cpp */,
@@ -2154,6 +2211,17 @@
path = "Preview Content";
sourceTree = "<group>";
};
87917A6D28E768B000F8D9AC /* AppleWatchToIpad */ = {
isa = PBXGroup;
children = (
87917A6E28E768D200F8D9AC /* Browser.swift */,
87917A7228E768D200F8D9AC /* Client.swift */,
87917A7028E768D200F8D9AC /* Connection.swift */,
87917A7128E768D200F8D9AC /* Server.swift */,
);
name = AppleWatchToIpad;
sourceTree = "<group>";
};
87DF60DE337FB58864343E39 /* Resources */ = {
isa = PBXGroup;
children = (
@@ -2768,6 +2836,7 @@
8738249127E646E3004F1B46 /* dirconpacket.cpp in Compile Sources */,
87C5F0BB26285E5F0067A1B5 /* mimetext.cpp in Compile Sources */,
8732C17F27353464006DF424 /* iconceptbike.cpp in Compile Sources */,
87CF5169293C879800A7CABC /* characteristicwriteprocessore005.cpp in Compile Sources */,
873824C027E64707004F1B46 /* moc_dirconmanager.cpp in Compile Sources */,
2F0E70F826316F3600E11F3A /* virtualbike_zwift.swift in Compile Sources */,
8772A0E825E43AE70080718C /* moc_trxappgateusbbike.cpp in Compile Sources */,
@@ -2814,6 +2883,9 @@
C6B3CD471768392E18F85819 /* fit_accumulated_field.cpp in Compile Sources */,
3D7395B0A17915A06361C7F3 /* fit_accumulator.cpp in Compile Sources */,
2A61806454201575EDB3F94F /* fit_buffer_encode.cpp in Compile Sources */,
87F02E4229178545000DB52C /* moc_octaneelliptical.cpp in Compile Sources */,
87E2F85D291ED308002BDC65 /* lifefitnesstreadmill.cpp in Compile Sources */,
87917A7328E768D200F8D9AC /* Browser.swift in Compile Sources */,
873CD20B27EF8D8A000131BC /* inapptransaction.cpp in Compile Sources */,
873824EF27E647A9004F1B46 /* query.cpp in Compile Sources */,
876F45FF279350D9003CDA5A /* moc_concept2skierg.cpp in Compile Sources */,
@@ -2827,6 +2899,7 @@
87368825259C602800C71C7E /* watchAppStart.swift in Compile Sources */,
876ED21925C3E9000065F3DC /* moc_ftmsbike.cpp in Compile Sources */,
87C5F0BD26285E5F0067A1B5 /* chronobike.cpp in Compile Sources */,
872A20DA28C5EC380037774D /* faketreadmill.cpp in Compile Sources */,
87900DC8268B673C000CB351 /* moc_renphobike.cpp in Compile Sources */,
87CC3B9E25A08812001EC5A8 /* moc_elliptical.cpp in Compile Sources */,
876BFC9C27BE35C5001D7645 /* proformelliptical.cpp in Compile Sources */,
@@ -2839,6 +2912,7 @@
87CC3B9D25A08812001EC5A8 /* moc_domyoselliptical.cpp in Compile Sources */,
87900DC6268B672E000CB351 /* renphobike.cpp in Compile Sources */,
879F16462847E55C00CE4945 /* proformellipticaltrainer.cpp in Compile Sources */,
87917A7728E768D200F8D9AC /* Client.swift in Compile Sources */,
873824B927E64707004F1B46 /* moc_provider.cpp in Compile Sources */,
8727A47727849EA600019B5D /* paferstreadmill.cpp in Compile Sources */,
DF1FD9718B75FA591A7E3D80 /* fit_decode.cpp in Compile Sources */,
@@ -2920,6 +2994,7 @@
87C5F0B726285E5F0067A1B5 /* mimecontentformatter.cpp in Compile Sources */,
23191C28CB29474279752FD3 /* fit_protocol_validator.cpp in Compile Sources */,
275D55B5D956B2E5F1B7E46E /* fit_unicode.cpp in Compile Sources */,
87F527BE28EEB5AA00A9F8D5 /* qzsettings.cpp in Compile Sources */,
8703BAEB273C67A90058E206 /* pafersbike.cpp in Compile Sources */,
87061399286D8D6500D2446E /* moc_wobjectdefs.cpp in Compile Sources */,
873824BA27E64707004F1B46 /* moc_server_p.cpp in Compile Sources */,
@@ -2939,6 +3014,7 @@
87A3BC222656429600D302E3 /* rower.cpp in Compile Sources */,
C719682D8D421AF6B2DAAEA9 /* main.cpp in Compile Sources */,
87BB1774269E983200F46A1C /* moc_webserverinfosender.cpp in Compile Sources */,
87E2F85F291ED315002BDC65 /* moc_lifefitnesstreadmill.cpp in Compile Sources */,
876BFCA127BE35D8001D7645 /* moc_bowflext216treadmill.cpp in Compile Sources */,
25FCD41CCCAF49293B9369E8 /* qfit.cpp in Compile Sources */,
87ADD2BD27634C2100B7A0AB /* moc_technogymmyruntreadmill.cpp in Compile Sources */,
@@ -2961,6 +3037,7 @@
873824BB27E64707004F1B46 /* moc_prober_p.cpp in Compile Sources */,
8742C2B227C92C30007D3FA0 /* wahookickrsnapbike.cpp in Compile Sources */,
87EB918327EE5FE7002535E1 /* moc_inappstore.cpp in Compile Sources */,
87CF516B293C87B000A7CABC /* moc_characteristicwriteprocessore005.cpp in Compile Sources */,
8780D949264FB8B800192D41 /* moc_smartspin2k.cpp in Compile Sources */,
8768D1FD2850820B00F58E3A /* moc_nordictrackifitadbtreadmill.cpp in Compile Sources */,
87D5DC402823047D008CCDE7 /* truetreadmill.cpp in Compile Sources */,
@@ -2996,9 +3073,11 @@
87AE0CB227760DCB00E547E9 /* virtualtreadmill_zwift.swift in Compile Sources */,
87062643259480A200D06586 /* AppDelegate.swift in Compile Sources */,
873824F027E647A9004F1B46 /* server.cpp in Compile Sources */,
872A20DC28C5F5CE0037774D /* moc_faketreadmill.cpp in Compile Sources */,
873CD20927EF8D8A000131BC /* inapppurchasebackend.cpp in Compile Sources */,
87E0761F277A082300FDA0F9 /* moc_technogymmyruntreadmillrfcomm.cpp in Compile Sources */,
87433F2127D8B722003D1672 /* simplecrypt.cpp in Compile Sources */,
87917A7628E768D200F8D9AC /* Server.swift in Compile Sources */,
2B42755BF45173E11E2110CB /* FitFieldDefinition.mm in Compile Sources */,
873824AE27E64706004F1B46 /* moc_browser.cpp in Compile Sources */,
8738249727E646E3004F1B46 /* characteristicnotifier2a53.cpp in Compile Sources */,
@@ -3015,10 +3094,12 @@
873824E527E647A8004F1B46 /* message.cpp in Compile Sources */,
210F6A0A7E2FA7CDD3CA0084 /* qdomyoszwift_qml_plugin_import.cpp in Compile Sources */,
87062644259480A600D06586 /* APIFetcher.swift in Compile Sources */,
87F02E4029178524000DB52C /* octaneelliptical.cpp in Compile Sources */,
878531682711A3EC004B153D /* moc_activiotreadmill.cpp in Compile Sources */,
87C5F0D626285E7E0067A1B5 /* moc_emailaddress.cpp in Compile Sources */,
39FAA19B9285AB16AE3A39BA /* qrc_icons.cpp in Compile Sources */,
87D2699F25F535200076AA48 /* m3ibike.cpp in Compile Sources */,
87917A7528E768D200F8D9AC /* Connection.swift in Compile Sources */,
87FFA13727BBE3FF00924E4E /* solebike.cpp in Compile Sources */,
7352E0F0EE5366AC809B9D64 /* qrc_qml.cpp in Compile Sources */,
873824E727E647A8004F1B46 /* record.cpp in Compile Sources */,
@@ -3032,6 +3113,7 @@
873824BD27E64707004F1B46 /* moc_resolver_p.cpp in Compile Sources */,
876ED21A25C3E9010065F3DC /* moc_material.cpp in Compile Sources */,
87A4B76125AF27CB0027EF3C /* metric.cpp in Compile Sources */,
87D10552290996EA00B3935B /* mepanelbike.cpp in Compile Sources */,
9D9484EED654597C394345DE /* moc_echelonconnectsport.cpp in Compile Sources */,
87DED80627D1273900BE4FBB /* filedownloader.cpp in Compile Sources */,
7DEEAF0C3D671FBFD84ACFCE /* moc_homeform.cpp in Compile Sources */,
@@ -3043,6 +3125,7 @@
E62DA5FF2436135448C94671 /* moc_toorxtreadmill.cpp in Compile Sources */,
87586A4325B8341B00A243C4 /* moc_proformbike.cpp in Compile Sources */,
87CC3BA325A0885F001EC5A8 /* domyoselliptical.cpp in Compile Sources */,
87D105542909971100B3935B /* moc_mepanelbike.cpp in Compile Sources */,
87B617F325F260150094A1CB /* moc_snodebike.cpp in Compile Sources */,
87DA8467284933DE00B550E9 /* moc_fakeelliptical.cpp in Compile Sources */,
87C5F0D726285E7E0067A1B5 /* moc_mimefile.cpp in Compile Sources */,
@@ -3061,6 +3144,7 @@
879E5AA8289C057E00FEA38A /* proformwifitreadmill.cpp in Compile Sources */,
873824B227E64706004F1B46 /* moc_hostname.cpp in Compile Sources */,
873824EB27E647A8004F1B46 /* prober.cpp in Compile Sources */,
8767EF1E29448D6700810C0F /* characteristicwriteprocessor.cpp in Compile Sources */,
87B617F425F260150094A1CB /* moc_screencapture.cpp in Compile Sources */,
87B617ED25F25FED0094A1CB /* fitshowtreadmill.cpp in Compile Sources */,
87A0C4BC262329A600121A76 /* cscbike.cpp in Compile Sources */,
@@ -3452,7 +3536,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2.11.24;
CURRENT_PROJECT_VERSION = 2.12.10;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@@ -3481,6 +3565,7 @@
../src/purchasing/qmltypes,
../src/purchasing/ios,
../../Qt/5.15.2/ios/include/QtTextToSpeech,
../../Qt/5.15.2/ios/include/QtMultimedia,
);
LIBRARY_SEARCH_PATHS = (
/Users/cagnulein/Qt/5.15.2/ios/plugins/platforms,
@@ -3526,7 +3611,7 @@
/Users/cagnulein/Qt/5.15.2/ios/plugins/playlistformats,
/Users/cagnulein/Qt/5.15.2/ios/plugins/audio,
);
MARKETING_VERSION = 2.11;
MARKETING_VERSION = 2.12;
OTHER_CFLAGS = (
"-pipe",
"-g",
@@ -3619,7 +3704,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2.11.24;
CURRENT_PROJECT_VERSION = 2.12.10;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = NO;
@@ -3650,6 +3735,7 @@
../src/purchasing/qmltypes,
../src/purchasing/ios,
../../Qt/5.15.2/ios/include/QtTextToSpeech,
../../Qt/5.15.2/ios/include/QtMultimedia,
);
LIBRARY_SEARCH_PATHS = (
/Users/cagnulein/Qt/5.15.2/ios/plugins/platforms,
@@ -3695,7 +3781,7 @@
/Users/cagnulein/Qt/5.15.2/ios/plugins/playlistformats,
/Users/cagnulein/Qt/5.15.2/ios/plugins/audio,
);
MARKETING_VERSION = 2.11;
MARKETING_VERSION = 2.12;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = (
"-pipe",
@@ -3822,7 +3908,7 @@
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2.11.24;
CURRENT_PROJECT_VERSION = 2.12.10;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -3843,7 +3929,7 @@
IBSC_MODULE = watchkit_Extension;
INFOPLIST_FILE = watchkit/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MARKETING_VERSION = 2.11;
MARKETING_VERSION = 2.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
@@ -3914,7 +4000,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2.11.24;
CURRENT_PROJECT_VERSION = 2.12.10;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = YES;
@@ -3931,7 +4017,7 @@
IBSC_MODULE = watchkit_Extension;
INFOPLIST_FILE = watchkit/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MARKETING_VERSION = 2.11;
MARKETING_VERSION = 2.12;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
@@ -4001,7 +4087,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2.11.24;
CURRENT_PROJECT_VERSION = 2.12.10;
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
ENABLE_BITCODE = YES;
ENABLE_PREVIEWS = YES;
@@ -4020,9 +4106,29 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
../src,
.,
../../Qt/5.15.2/ios/mkspecs/common/uikit,
"../src/fit-sdk",
../../Qt/5.15.2/ios/include,
../../Qt/5.15.2/ios/include/QtWidgets,
../../Qt/5.15.2/ios/include/QtQuick,
../../Qt/5.15.2/ios/include/QtGui,
../../Qt/5.15.2/ios/include/QtBluetooth,
../../Qt/5.15.2/ios/include/QtXml,
../../Qt/5.15.2/ios/include/QtPositioning,
../../Qt/5.15.2/ios/include/QtQmlModels,
../../Qt/5.15.2/ios/include/QtQml,
../../Qt/5.15.2/ios/include/QtNetwork,
../../Qt/5.15.2/ios/include/QtCore,
.,
"../../Qt/5.15.2/ios/mkspecs/macx-ios-clang",
../../Qt/5.15.2/ios/include/QtMultimedia,
);
INFOPLIST_FILE = "watchkit Extension/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 2.11;
MARKETING_VERSION = 2.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
@@ -4091,7 +4197,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2.11.24;
CURRENT_PROJECT_VERSION = 2.12.10;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
ENABLE_BITCODE = YES;
@@ -4106,9 +4212,29 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
../src,
.,
../../Qt/5.15.2/ios/mkspecs/common/uikit,
"../src/fit-sdk",
../../Qt/5.15.2/ios/include,
../../Qt/5.15.2/ios/include/QtWidgets,
../../Qt/5.15.2/ios/include/QtQuick,
../../Qt/5.15.2/ios/include/QtGui,
../../Qt/5.15.2/ios/include/QtBluetooth,
../../Qt/5.15.2/ios/include/QtXml,
../../Qt/5.15.2/ios/include/QtPositioning,
../../Qt/5.15.2/ios/include/QtQmlModels,
../../Qt/5.15.2/ios/include/QtQml,
../../Qt/5.15.2/ios/include/QtNetwork,
../../Qt/5.15.2/ios/include/QtCore,
.,
"../../Qt/5.15.2/ios/mkspecs/macx-ios-clang",
../../Qt/5.15.2/ios/include/QtMultimedia,
);
INFOPLIST_FILE = "watchkit Extension/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 2.11;
MARKETING_VERSION = 2.12;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";

View File

@@ -17,16 +17,33 @@ class MainController: WKInterfaceController {
@IBOutlet weak var distanceLabel: WKInterfaceLabel!
@IBOutlet weak var heartRateLabel: WKInterfaceLabel!
@IBOutlet weak var startButton: WKInterfaceButton!
@IBOutlet weak var cmbSports: WKInterfacePicker!
static var start: Bool! = false
let pedometer = CMPedometer()
var sport: Int = 0
override func awake(withContext context: Any?) {
super.awake(withContext: context)
let sports: [WKPickerItem] = [WKPickerItem(),WKPickerItem(),WKPickerItem(),WKPickerItem(),WKPickerItem()]
sports[0].title = "Bike"
sports[1].title = "Run"
sports[2].title = "Walk"
sports[3].title = "Elliptical"
sports[4].title = "Rowing"
cmbSports.setItems(sports)
sport = UserDefaults.standard.value(forKey: "sport") as? Int ?? 0
cmbSports.setSelectedItemIndex(sport)
// Configure interface objects here.
print("AWAKE")
}
@IBAction func changeSport(_ value: Int) {
self.sport = value
UserDefaults.standard.set(value, forKey: "sport")
UserDefaults.standard.synchronize()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
@@ -57,6 +74,7 @@ extension MainController {
MainController.start = true
startButton.setTitle("Stop")
WorkoutTracking.authorizeHealthKit()
WorkoutTracking.shared.setSport(sport)
WorkoutTracking.shared.startWorkOut()
WorkoutTracking.shared.delegate = self
@@ -86,8 +104,12 @@ extension MainController: WorkoutTrackingDelegate {
"\(heartRate)" as AnyObject])
WorkoutTracking.distance = WatchKitConnection.distance
WorkoutTracking.kcal = WatchKitConnection.kcal
self.distanceLabel.setText("Distance \(Double(WorkoutTracking.distance))")
if Locale.current.measurementSystem != "Metric" {
self.distanceLabel.setText("Distance \(String(format:"%.2f", WorkoutTracking.distance))")
} else {
self.distanceLabel.setText("Distance \(String(format:"%.2f", WorkoutTracking.distance * 1.60934))")
}
self.caloriesLabel.setText("KCal \(Int(WorkoutTracking.kcal))")
//WorkoutTracking.cadenceSteps = pedometer.
}
@@ -105,3 +127,11 @@ extension MainController: WatchKitConnectionDelegate {
userNameLabel.setText(userName)
}
}
extension Locale
{
var measurementSystem : String?
{
return (self as NSLocale).object(forKey: NSLocale.Key.measurementSystem) as? String
}
}

View File

@@ -31,6 +31,7 @@ class WorkoutTracking: NSObject {
public static var cadenceTimeStamp = NSDate().timeIntervalSince1970
public static var cadenceLastSteps = Double()
public static var cadenceSteps = 0
var sport: Int = 0
let healthStore = HKHealthStore()
let configuration = HKWorkoutConfiguration()
var workoutSession: HKWorkoutSession!
@@ -101,8 +102,23 @@ extension WorkoutTracking {
}
}
func setSport(_ sport: Int) {
self.sport = sport
}
private func configWorkout() {
configuration.activityType = .cycling
var activityType = HKWorkoutActivityType.cycling
if self.sport == 1 {
activityType = HKWorkoutActivityType.running
} else if self.sport == 2 {
activityType = HKWorkoutActivityType.walking
} else if self.sport == 3 {
activityType = HKWorkoutActivityType.elliptical
} else if self.sport == 4 {
activityType = HKWorkoutActivityType.rowing
}
configuration.activityType = activityType
configuration.locationType = .indoor
do {
@@ -134,6 +150,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
HKSampleType.quantityType(forIdentifier: .stepCount)!,
HKSampleType.quantityType(forIdentifier: .heartRate)!,
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKSampleType.workoutType()
])
@@ -159,6 +176,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
if let error = error {
print(error)
}
if self.sport > 0 {
self.workoutBuilder.dataSource?.enableCollection(for: HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!, predicate: nil)
}
}
}
@@ -183,29 +204,72 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
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
if(sport == 0) {
guard let quantityTypeDistance = HKQuantityType.quantityType(
forIdentifier: .distanceCycling) else {
return
}
let sampleDistance = HKCumulativeQuantitySeriesSample(type: quantityTypeDistance,
quantity: quantityMiles,
start: workoutSession.startDate!,
end: Date())
workoutBuilder.add([sampleDistance]) {(success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.finishWorkout{ (workout, error) in
if let error = error {
print(error)
}
workout?.setValue(quantityMiles, forKey: "totalDistance")
}
}
}
} else {
guard let quantityTypeDistance = HKQuantityType.quantityType(
forIdentifier: .distanceWalkingRunning) else {
return
}
let sampleDistance = HKCumulativeQuantitySeriesSample(type: quantityTypeDistance,
quantity: quantityMiles,
start: workoutSession.startDate!,
end: Date())
workoutBuilder.add([sampleDistance]) {(success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.finishWorkout{ (workout, error) in
if let error = error {
print(error)
}
workout?.setValue(quantityMiles, forKey: "totalDistance")
}
}
}
}
workoutBuilder.finishWorkout{ (success, error) in }
}
func fetchStepCounts() {

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="19529" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Tpn-rd-UUX">
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="20037" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Tpn-rd-UUX">
<device id="watch38"/>
<dependencies>
<deployment identifier="watchOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="19514"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="20006"/>
</dependencies>
<scenes>
<!--Main-->
@@ -22,9 +22,15 @@
<label width="136" alignment="left" text="Step Counts" id="HpA-e9-6YV"/>
<label width="136" alignment="left" text="Calories" id="Szi-Jp-J3S"/>
<label width="136" alignment="left" text="Distance" id="eRf-NJ-6If"/>
<picker height="100" alignment="left" id="OTR-HF-vYb">
<connections>
<action selector="changeSport:" destination="Tpn-rd-UUX" id="3vY-lq-IhZ"/>
</connections>
</picker>
</items>
<connections>
<outlet property="caloriesLabel" destination="Szi-Jp-J3S" id="trd-YS-bJy"/>
<outlet property="cmbSports" destination="OTR-HF-vYb" id="Ws5-w9-ZT8"/>
<outlet property="distanceLabel" destination="eRf-NJ-6If" id="ZE2-OB-jqN"/>
<outlet property="heartRateLabel" destination="Nda-m1-XRw" id="1la-8R-3jG"/>
<outlet property="startButton" destination="vZg-X8-uY5" id="pJc-09-kfV"/>

View File

@@ -25,13 +25,13 @@ $ sudo ./qdomyos-zwift
You will need to (at a minimum) to install the xcode Command Line Tools (CLI) thanks to @richardwait
https://developer.apple.com/download/more/?=xcode
Download and install http://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-mac-x64-5.12.9.dmg and simply run the qdomyos-zwift relase for MacOs
Download and install http://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-mac-x64-5.12.9.dmg and simply run the qdomyos-zwift release for MacOs
## On Raspberry Pi Zero W
![raspi](../docs/img/raspi-bike.jpg)
This guide will walk you through steps to setup an autonomous, headless raspberry brigde.
This guide will walk you through steps to setup an autonomous, headless raspberry bridge.
### Initial System Preparation

View File

@@ -26,5 +26,5 @@ You can have ae true 4k video stream while you ride ("extreme quality" setting)
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.
You can adjust resistance using arrows [up and down](img/21_zwift-resistance-buttons.jpg) rom the riding screen.

View File

@@ -24,8 +24,9 @@ This is the list of settings available in the application. These settings needs
| **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 |
| -qml | Boolean | True | Enables the QML interface |
| -noqml | Boolean | False | Enables the NativeQT interface |
| -miles | Boolean | False | Switches to Imperial Units System |
| -no-console | Boolean | False | Not in use |
| -test-resistance | Boolean | False | |
| -no-log | Boolean | False | Disable Logging |
@@ -42,10 +43,10 @@ This is the list of settings available in the application. These settings needs
| -service-changed | Boolean | False | |
| -bike-wheel-revs | Boolean | False | |
| -run-cadence-sensor | Boolean | False | |
| -nordictrack-10-treadmill | Boolean | False | Enable NordicTrack compatibility mode |
| -nordictrack-10-treadmill | Boolean | False | Enable NordicTrack compatibility mode |
| -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 |
| -poll-device-time | Int | 200 (ms) | Frequency to refresh information 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 |

View File

@@ -0,0 +1,272 @@
<?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="Cross Trainer Data"
type="org.bluetooth.characteristic.cross_trainer_data" uuid="2ACE"
last-modified="2017-02-14" approved="Yes">
<InformativeText>
<Summary>The Cross Trainer Data characteristic is used to send
training-related data to the Client from a cross trainer
(Server).</Summary>
</InformativeText>
<Value>
<Field name="Flags">
<Requirement>Mandatory</Requirement>
<Format>24bit</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 Speed 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="Step Count present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C4" />
</Enumerations>
</Bit>
<Bit index="4" size="1" name="Stride Count present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C5" />
</Enumerations>
</Bit>
<Bit index="5" size="1" name="Elevation Gain present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C6" />
</Enumerations>
</Bit>
<Bit index="6" size="1"
name="Inclination and Ramp Angle Setting 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="Instantaneous Power present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C9" />
</Enumerations>
</Bit>
<Bit index="9" size="1" name="Average Power present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C10" />
</Enumerations>
</Bit>
<Bit index="10" size="1" name="Expended Energy present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C11" />
</Enumerations>
</Bit>
<Bit index="11" size="1" name="Heart Rate present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C12" />
</Enumerations>
</Bit>
<Bit index="12" size="1"
name="Metabolic Equivalent present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C13" />
</Enumerations>
</Bit>
<Bit index="13" size="1" name="Elapsed Time present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C14" />
</Enumerations>
</Bit>
<Bit index="14" size="1" name="Remaining Time present">
<Enumerations>
<Enumeration key="0" value="False" />
<Enumeration key="1" value="True" requires="C15" />
</Enumerations>
</Bit>
<Bit index="15" size="1" name="Movement Direction">
<Enumerations>
<Enumeration key="0" value="Forward" />
<Enumeration key="1" value="Backward" />
</Enumerations>
</Bit>
<ReservedForFutureUse index="16" size="8" />
</BitField>
</Field>
<Field name="Instantaneous Speed">
<InformativeText>Kilometer per hour with a resolution of
0.01</InformativeText>
<Requirement>C1</Requirement>
<Format>uint16</Format>
<Unit>org.bluetooth.unit.velocity.kilometre_per_hour</Unit>
<DecimalExponent>-2</DecimalExponent>
</Field>
<Field name="Average Speed">
<InformativeText>Kilometer per hour with a resolution of
0.01</InformativeText>
<Requirement>C2</Requirement>
<Format>uint16</Format>
<Unit>org.bluetooth.unit.velocity.kilometre_per_hour</Unit>
<DecimalExponent>-2</DecimalExponent>
</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="Step Per Minute">
<InformativeText>Step/minute with a resolution of
1</InformativeText>
<Requirement>C4</Requirement>
<Format>uint16</Format>
<Unit>org.bluetooth.unit.step_per_minute</Unit>
</Field>
<Field name="Average Step Rate">
<InformativeText>Step/minute with a resolution of
1</InformativeText>
<Requirement>C4</Requirement>
<Format>uint16</Format>
<Unit>org.bluetooth.unit.step_per_minute</Unit>
</Field>
<Field name="Stride Count">
<InformativeText>Unitless with a resolution of
0.1</InformativeText>
<Requirement>C5</Requirement>
<Format>uint16</Format>
<DecimalExponent>-1</DecimalExponent>
<Unit>org.bluetooth.unit.unitless</Unit>
</Field>
<Field name="Positive Elevation Gain">
<InformativeText>Meters with a resolution of
1</InformativeText>
<Requirement>C6</Requirement>
<Format>uint16</Format>
<Unit>org.bluetooth.unit.length.metre</Unit>
</Field>
<Field name="Negative Elevation Gain">
<InformativeText>Meters with a resolution of
1</InformativeText>
<Requirement>C6</Requirement>
<Format>uint16</Format>
<Unit>org.bluetooth.unit.length.metre</Unit>
</Field>
<Field name="Inclination">
<InformativeText>Percent with a resolution of
0.1</InformativeText>
<Requirement>C7</Requirement>
<Format>sint16</Format>
<Unit>org.bluetooth.unit.percentage</Unit>
<DecimalExponent>-1</DecimalExponent>
</Field>
<Field name="Ramp Angle Setting">
<InformativeText>Degree with a resolution of
0.1</InformativeText>
<Requirement>C7</Requirement>
<Format>sint16</Format>
<Unit>org.bluetooth.unit.plane_angle.degree</Unit>
<DecimalExponent>-1</DecimalExponent>
</Field>
<Field name="Resistance Level">
<InformativeText>Unitless with a resolution of
0.1</InformativeText>
<Requirement>C8</Requirement>
<Format>sint16</Format>
<DecimalExponent>-1</DecimalExponent>
<Unit>org.bluetooth.unit.unitless</Unit>
</Field>
<Field name="Instantaneous Power">
<InformativeText>Watts with a resolution of
1</InformativeText>
<Requirement>C9</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>C10</Requirement>
<Format>sint16</Format>
<Unit>org.bluetooth.unit.power.watt</Unit>
</Field>
<Field name="Total Energy">
<InformativeText>Kilo Calorie with a resolution of
1</InformativeText>
<Requirement>C11</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>C11</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>C11</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>C12</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>C13</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>C14</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>C15</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

@@ -1018,7 +1018,7 @@ public class QtBluetoothLE {
/*
* Already executed in GattCallback so executed by the HandlerThread. No need to
* post it to the Hander.
* post it to the Handler.
*/
private void scheduleMtuExchange() {
ReadWriteJob newJob = new ReadWriteJob();

View File

@@ -385,11 +385,11 @@ QT_USE_NAMESPACE
}
[uuids addObject:nsUuid];
// With the latest CoreBluetooth, we can synchronously retrive peripherals:
// With the latest CoreBluetooth, we can synchronously retrieve peripherals:
QT_BT_MAC_AUTORELEASEPOOL;
NSArray *const peripherals = [manager retrievePeripheralsWithIdentifiers:uuids];
if (!peripherals || peripherals.count != 1) {
qCWarning(QT_BT_OSX) << "failed to retrive a peripheral";
qCWarning(QT_BT_OSX) << "failed to retrieve a peripheral";
if (notifier)
emit notifier->CBManagerError(QLowEnergyController::UnknownRemoteDeviceError);
return;
@@ -648,7 +648,7 @@ QT_USE_NAMESPACE
servicesToDiscoverDetails.removeAll(serviceUuid);
const NSUInteger nHandles = qt_countGATTEntries(service);
Q_ASSERT_X(nHandles, Q_FUNC_INFO, "unexpected number of GATT entires");
Q_ASSERT_X(nHandles, Q_FUNC_INFO, "unexpected number of GATT entries");
const QLowEnergyHandle maxHandle = std::numeric_limits<QLowEnergyHandle>::max();
if (nHandles >= maxHandle || lastValidHandle > maxHandle - nHandles) {
@@ -1058,7 +1058,7 @@ QT_USE_NAMESPACE
// TODO: test that we NEVER have the same characteristic twice in array!
// At the moment I just protect against this by iterating in a reverse
// order (at least avoiding a potential inifite loop with '-indexOfObject:').
// order (at least avoiding a potential infinite loop with '-indexOfObject:').
NSArray *const cs = service.characteristics;
if (cs.count == 1)
return nil;
@@ -1091,7 +1091,7 @@ QT_USE_NAMESPACE
// TODO: test that we NEVER have the same characteristic twice in array!
// At the moment I just protect against this by iterating in a reverse
// order (at least avoiding a potential inifite loop with '-indexOfObject:').
// order (at least avoiding a potential infinite loop with '-indexOfObject:').
NSArray *const cs = service.characteristics;
if (cs.count == 1)
return nil;
@@ -1597,8 +1597,8 @@ QT_USE_NAMESPACE
} else {
// This is (probably) the result of update notification.
// It's very possible we can have an invalid handle here (0) -
// if something esle is wrong (we subscribed for a notification),
// disconnected (but other application is connected) and still receiveing
// if something else is wrong (we subscribed for a notification),
// disconnected (but other application is connected) and still receiving
// updated values ...
// TODO: this must be properly tested.
if (!chHandle) {

View File

@@ -61,6 +61,12 @@ ColumnLayout {
id: filterField
onTextChanged: updateFilter()
}
Button {
anchors.left: mainRect.right
anchors.leftMargin: 5
text: "←"
onClicked: folderModel.folder = folderModel.parentFolder
}
}
ListView {
@@ -78,6 +84,8 @@ ColumnLayout {
folder: "file://" + rootItem.getWritableAppDir() + 'gpx'
showDotAndDotDot: false
showDirs: true
sortField: "Name"
showDirsFirst: true
}
model: folderModel
delegate: Component {
@@ -96,7 +104,7 @@ ColumnLayout {
clip: true
Text {
id: fileTextBox
color: Material.color(Material.Grey)
color: (!folderModel.isFolder(index)?Material.color(Material.Grey):Material.color(Material.Orange))
font.pixelSize: Qt.application.font.pixelSize * 1.6
text: fileName.substring(0, fileName.length-4)
NumberAnimation on x {
@@ -124,9 +132,11 @@ ColumnLayout {
console.log('onclicked ' + index+ " count "+list.count);
if (index == list.currentIndex) {
let fileUrl = folderModel.get(list.currentIndex, 'fileUrl') || folderModel.get(list.currentIndex, 'fileURL');
if (fileUrl) {
if (fileUrl && !folderModel.isFolder(list.currentIndex)) {
trainprogram_open_clicked(fileUrl);
popup.open()
} else {
folderModel.folder = fileURL
}
}
else {

View File

@@ -5,6 +5,7 @@ import QtGraphicalEffects 1.12
import QtQuick.Window 2.12
import Qt.labs.settings 1.0
import Qt.labs.platform 1.1
import QtMultimedia 5.15
HomeForm{
objectName: "home"
@@ -15,6 +16,7 @@ HomeForm{
signal peloton_abort_workout;
signal plus_clicked(string name)
signal minus_clicked(string name)
signal largeButton_clicked(string name)
Settings {
id: settings
@@ -79,172 +81,244 @@ HomeForm{
Component.onCompleted: { console.log("completed"); }
GridView {
anchors.horizontalCenter: parent.horizontalCenter
anchors.fill: parent
cellWidth: 175 * settings.ui_zoom / 100
cellHeight: 130 * settings.ui_zoom / 100
focus: true
model: appModel
leftMargin: { if(OS_VERSION === "Android") (Screen.width % cellWidth) / 2; else (parent.width % cellWidth) / 2; }
anchors.topMargin: (!window.lockTiles ? rootItem.topBarHeight + 30 : 0)
id: gridView
objectName: "gridview"
onMovementEnded: { headerToolbar.visible = (contentY == 0) || window.lockTiles; }
Screen.orientationUpdateMask: Qt.LandscapeOrientation | Qt.PortraitOrientation
Screen.onPrimaryOrientationChanged:{
if(OS_VERSION === "Android")
gridView.leftMargin = (Screen.width % cellWidth) / 2;
else
gridView.leftMargin = (parent.width % cellWidth) / 2;
GridView {
anchors.horizontalCenter: parent.horizontalCenter
anchors.fill: parent
cellWidth: 175 * settings.ui_zoom / 100
cellHeight: 130 * settings.ui_zoom / 100
focus: true
model: appModel
leftMargin: { if(OS_VERSION === "Android") (Screen.width % cellWidth) / 2; else (parent.width % cellWidth) / 2; }
anchors.topMargin: (!window.lockTiles ? rootItem.topBarHeight + 30 : 0)
id: gridView
objectName: "gridview"
onMovementEnded: { headerToolbar.visible = (contentY == 0) || window.lockTiles; }
Screen.orientationUpdateMask: Qt.LandscapeOrientation | Qt.PortraitOrientation
Screen.onPrimaryOrientationChanged:{
if(OS_VERSION === "Android")
gridView.leftMargin = (Screen.width % cellWidth) / 2;
else
gridView.leftMargin = (parent.width % cellWidth) / 2;
}
// highlight: Rectangle {
// width: 150
// height: 150
// color: "lightsteelblue"
// }
delegate: Item {
id: id1
width: 170 * settings.ui_zoom / 100
height: 125 * settings.ui_zoom / 100
visible: visibleItem
Component.onCompleted: console.log("completed " + objectName)
Behavior on x {
enabled: id1.state != "active"
NumberAnimation { duration: 400; easing.type: Easing.OutBack }
}
Behavior on y {
enabled: id1.state != "active"
NumberAnimation { duration: 400; easing.type: Easing.OutBack }
}
SequentialAnimation on rotation {
NumberAnimation { to: 2; duration: 60 }
NumberAnimation { to: -2; duration: 120 }
NumberAnimation { to: 0; duration: 60 }
running: loc.currentId !== -1 && id1.state !== "active" && window.lockTiles
loops: Animation.Infinite; alwaysRunToEnd: true
}
states: State {
name: "active"; when: loc.currentId === gridId && window.lockTiles
PropertyChanges { target: id1; x: loc.mouseX - gridView.x - width/2; y: loc.mouseY - gridView.y - height/2; scale: 0.5; z: 10 }
}
transitions: Transition { NumberAnimation { property: "scale"; duration: 200} }
Rectangle {
width: 168 * settings.ui_zoom / 100
height: 123 * settings.ui_zoom / 100
radius: 3
border.width: 1
border.color: "purple"
color: Material.backgroundColor
id: rect
}
DropShadow {
anchors.fill: rect
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: Material.color(Material.Purple)
source: rect
}
Timer {
id: toggleIconTimer
interval: 500; running: true; repeat: true
onTriggered: { if(identificator === "inclination" && rootItem.autoInclinationEnabled()) myIcon.visible = !myIcon.visible; else myIcon.visible = true; }
}
Image {
id: myIcon
x: 5
anchors {
bottom: id1.bottom
}
width: 48 * settings.ui_zoom / 100
height: 48 * settings.ui_zoom / 100
source: icon
visible: !largeButton
}
Text {
objectName: "value"
id: myValue
color: valueFontColor
y: 0
anchors {
horizontalCenter: parent.horizontalCenter
}
text: value
horizontalAlignment: Text.AlignHCenter
font.pointSize: valueFontSize * settings.ui_zoom / 100
font.bold: true
visible: !largeButton
}
Text {
objectName: "secondLine"
id: secondLineText
color: "white"
y: myValue.bottom
anchors {
top: myValue.bottom
horizontalCenter: parent.horizontalCenter
}
text: secondLine
horizontalAlignment: Text.AlignHCenter
font.pointSize: 12 * settings.ui_zoom / 100
font.bold: false
visible: !largeButton
}
Text {
id: myText
anchors {
top: myIcon.top
}
font.bold: true
font.pointSize: labelFontSize
color: "white"
text: name
anchors.left: parent.left
anchors.leftMargin: 55 * settings.ui_zoom / 100
anchors.topMargin: 20 * settings.ui_zoom / 100
visible: !largeButton
}
RoundButton {
objectName: minusName
autoRepeat: true
text: "-"
onClicked: minus_clicked(objectName)
visible: writable && !largeButton
anchors.top: myValue.top
anchors.left: parent.left
anchors.leftMargin: 2
width: 48 * settings.ui_zoom / 100
height: 48 * settings.ui_zoom / 100
}
RoundButton {
autoRepeat: true
objectName: plusName
text: "+"
onClicked: plus_clicked(objectName)
visible: writable && !largeButton
anchors.top: myValue.top
anchors.right: parent.right
anchors.rightMargin: 2
width: 48 * settings.ui_zoom / 100
height: 48 * settings.ui_zoom / 100
}
RoundButton {
autoRepeat: true
objectName: identificator
text: largeButtonLabel
onClicked: largeButton_clicked(objectName)
visible: largeButton
anchors.fill: rect
background: Rectangle {
color: largeButtonColor
radius: 20
}
font.pointSize: 16 * settings.ui_zoom / 100
//width: 48 * settings.ui_zoom / 100
//height: 48 * settings.ui_zoom / 100
}
/*MouseArea {
anchors.fill: parent
onClicked: parent.GridView.view.currentIndex = index
}*/
}
}
// highlight: Rectangle {
// width: 150
// height: 150
// color: "lightsteelblue"
// }
delegate: Item {
id: id1
width: 170 * settings.ui_zoom / 100
height: 125 * settings.ui_zoom / 100
visible: visibleItem
Component.onCompleted: console.log("completed " + objectName)
Behavior on x {
enabled: id1.state != "active"
NumberAnimation { duration: 400; easing.type: Easing.OutBack }
}
Behavior on y {
enabled: id1.state != "active"
NumberAnimation { duration: 400; easing.type: Easing.OutBack }
}
SequentialAnimation on rotation {
NumberAnimation { to: 2; duration: 60 }
NumberAnimation { to: -2; duration: 120 }
NumberAnimation { to: 0; duration: 60 }
running: loc.currentId !== -1 && id1.state !== "active" && window.lockTiles
loops: Animation.Infinite; alwaysRunToEnd: true
}
states: State {
name: "active"; when: loc.currentId === gridId && window.lockTiles
PropertyChanges { target: id1; x: loc.mouseX - gridView.x - width/2; y: loc.mouseY - gridView.y - height/2; scale: 0.5; z: 10 }
}
transitions: Transition { NumberAnimation { property: "scale"; duration: 200} }
footer:
Rectangle {
width: 168 * settings.ui_zoom / 100
height: 123 * settings.ui_zoom / 100
radius: 3
border.width: 1
border.color: "purple"
color: Material.backgroundColor
id: rect
}
objectName: "footerrectangle"
visible: rootItem.videoVisible
anchors.top: gridView.bottom
width: parent.width
height: parent.height / 2
DropShadow {
anchors.fill: rect
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: Material.color(Material.Purple)
source: rect
}
Timer {
id: pauseTimer
interval: 1000; running: true; repeat: true
onTriggered: { if(visible == true) { (rootItem.currentSpeed > 0 ?
videoPlaybackHalf.play() :
videoPlaybackHalf.pause()) } }
}
Timer {
id: toggleIconTimer
interval: 500; running: true; repeat: true
onTriggered: { if(identificator === "inclination" && rootItem.autoInclinationEnabled()) myIcon.visible = !myIcon.visible; else myIcon.visible = true; }
}
onVisibleChanged: {
if(visible === true) {
console.log("mediaPlayer onCompleted: " + rootItem.videoPath)
console.log("videoRate: " + rootItem.videoRate)
videoPlaybackHalf.source = rootItem.videoPath
//videoPlaybackHalf.playbackRate = rootItem.videoRate
Image {
id: myIcon
x: 5
anchors {
bottom: id1.bottom
}
width: 48 * settings.ui_zoom / 100
height: 48 * settings.ui_zoom / 100
source: icon
}
Text {
objectName: "value"
id: myValue
color: valueFontColor
y: 0
anchors {
horizontalCenter: parent.horizontalCenter
}
text: value
horizontalAlignment: Text.AlignHCenter
font.pointSize: valueFontSize * settings.ui_zoom / 100
font.bold: true
}
Text {
objectName: "secondLine"
id: secondLineText
color: "white"
y: myValue.bottom
anchors {
top: myValue.bottom
horizontalCenter: parent.horizontalCenter
}
text: secondLine
horizontalAlignment: Text.AlignHCenter
font.pointSize: 12 * settings.ui_zoom / 100
font.bold: false
}
Text {
id: myText
anchors {
top: myIcon.top
}
font.bold: true
font.pointSize: labelFontSize
color: "white"
text: name
anchors.left: parent.left
anchors.leftMargin: 55 * settings.ui_zoom / 100
anchors.topMargin: 20 * settings.ui_zoom / 100
}
RoundButton {
objectName: minusName
autoRepeat: true
text: "-"
onClicked: minus_clicked(objectName)
visible: writable
anchors.top: myValue.top
anchors.left: parent.left
anchors.leftMargin: 2
width: 48 * settings.ui_zoom / 100
height: 48 * settings.ui_zoom / 100
}
RoundButton {
autoRepeat: true
objectName: plusName
text: "+"
onClicked: plus_clicked(objectName)
visible: writable
anchors.top: myValue.top
anchors.right: parent.right
anchors.rightMargin: 2
width: 48 * settings.ui_zoom / 100
height: 48 * settings.ui_zoom / 100
}
videoPlaybackHalf.seek(rootItem.videoPosition)
videoPlaybackHalf.play()
videoPlaybackHalf.muted = true
} else {
videoPlaybackHalf.stop()
}
/*MouseArea {
anchors.fill: parent
onClicked: parent.GridView.view.currentIndex = index
}*/
}
}
}
MediaPlayer {
id: videoPlaybackHalf
objectName: "videoplaybackhalf"
autoPlay: false
playbackRate: rootItem.videoRate
onError: {
if (videoPlaybackHalf.NoError !== error) {
console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString)
}
}
}
VideoOutput {
id:videoPlayer
anchors.fill: parent
source: videoPlaybackHalf
}
}
MouseArea {
property int currentId: -1 // Original position in model

View File

@@ -63,21 +63,35 @@ Item {
color: "white"
font.pointSize: 22
wrapMode: TextArea.Wrap
text: qsTr("Hi! Do you know that QZ is just an Open Souce Indie App?<br><br>No Big Companies are running this!<br>The \"Swag Bag\" is a way to support the ongoing development, maintenance and support of QZ Fitness!<br><br>Thanks to Rungap App to give me the idea of the name!")
text: qsTr("Hi! Do you know that QZ is just an Open Source Indie App?<br><br>No Big Companies are running this!<br>The \"Swag Bag\" is a way to support the ongoing development, maintenance and support of QZ Fitness!")
}
Column {
//anchors.top: description.bottom + 10
anchors.top: description.bottom
//anchors.bottom: restoreButton.top
anchors.right: parent.right
anchors.left: parent.left
id: itemSwagBag
SwagBagItem {
product: productUnlockVowels
width: parent.width
}
}
Text {
anchors {
top: itemSwagBag.bottom
horizontalCenter: parent.horizontalCenter
}
padding: 5
id: appleDescription
width: parent.width
color: "white"
font.pointSize: 8
wrapMode: TextArea.Wrap
text: qsTr("<html><style type='text/css'></style>Swag bag feature:<br>• an auto-renewable subscription<br>• 1 month ($1.99)<br>• Your subscription will be charged to your iTunes account at confirmation of purchase and will automatically renew (at the duration selected) unless auto-renew is turned off at least 24 hours before the end of the current period.<br>• Current subscription may not be cancelled during the active subscription period; however, you can manage your subscription and/or turn off auto-renewal by visiting your iTunes Account Settings after purchase.<br>• Privacy policy: <a href='https://robertoviola.cloud/privacy-policy-qdomyos-zwift/'>https://robertoviola.cloud/privacy-policy-qdomyos-zwift/</a><br>• Licensed Application end user license agreement: <a href='https://www.apple.com/legal/internet-services/itunes/dev/stdeula/'>https://www.apple.com/legal/internet-services/itunes/dev/stdeula/</a><br></html>")
onLinkActivated: Qt.openUrlExternally(link)
}
/*Button {
id: restoreButton

View File

@@ -59,6 +59,12 @@ ColumnLayout {
id: filterField
onTextChanged: updateFilter()
}
Button {
anchors.left: mainRect.right
anchors.leftMargin: 5
text: "←"
onClicked: folderModel.folder = folderModel.parentFolder
}
}
ListView {
@@ -74,8 +80,10 @@ ColumnLayout {
id: folderModel
nameFilters: ["*.xml", "*.zwo"]
folder: "file://" + rootItem.getWritableAppDir() + 'training'
showDotAndDotDot: false
showDotAndDotDot: false
showDirs: true
sortField: "Name"
showDirsFirst: true
}
model: folderModel
delegate: Component {
@@ -83,7 +91,7 @@ ColumnLayout {
property alias textColor: fileTextBox.color
width: parent.width
height: 40
color: Material.backgroundColor
color: Material.backgroundColor
z: 1
Item {
id: root
@@ -94,7 +102,7 @@ ColumnLayout {
clip: true
Text {
id: fileTextBox
color: Material.color(Material.Grey)
color: (!folderModel.isFolder(index)?Material.color(Material.Grey):Material.color(Material.Orange))
font.pixelSize: Qt.application.font.pixelSize * 1.6
text: fileName.substring(0, fileName.length-4)
NumberAnimation on x {
@@ -122,10 +130,12 @@ ColumnLayout {
console.log('onclicked ' + index+ " count "+list.count);
if (index == list.currentIndex) {
let fileUrl = folderModel.get(list.currentIndex, 'fileUrl') || folderModel.get(list.currentIndex, 'fileURL');
if (fileUrl) {
if (fileUrl && !folderModel.isFolder(list.currentIndex)) {
trainprogram_open_clicked(fileUrl);
popup.open()
}
} else {
folderModel.folder = fileURL
}
}
else {
if (list.currentItem)

View File

@@ -1,6 +1,5 @@
#include "activiotreadmill.h"
#include "activiotreadmill.h"
#include "ios/lockscreen.h"
#include "keepawakehelper.h"
#include "virtualtreadmill.h"
@@ -34,7 +33,7 @@ activiotreadmill::activiotreadmill(uint32_t pollDeviceTime, bool noConsole, bool
refresh->start(pollDeviceTime);
}
void activiotreadmill::writeCharacteristic(const QLowEnergyCharacteristic characteristc, uint8_t *data,
void activiotreadmill::writeCharacteristic(const QLowEnergyCharacteristic characteristic, uint8_t *data,
uint8_t data_len, const QString &info, bool disable_log,
bool wait_for_response) {
QEventLoop loop;
@@ -55,7 +54,7 @@ void activiotreadmill::writeCharacteristic(const QLowEnergyCharacteristic charac
return;
}
gattCommunicationChannelService->writeCharacteristic(characteristc, QByteArray((const char *)data, data_len));
gattCommunicationChannelService->writeCharacteristic(characteristic, QByteArray((const char *)data, data_len));
if (!disable_log) {
emit debug(QStringLiteral(" >> ") + QByteArray((const char *)data, data_len).toHex(' ') +
@@ -75,7 +74,7 @@ void activiotreadmill::forceSpeed(double requestSpeed) {
writeSpeed[1] = (requestSpeed * 10);
writeSpeed[5] += writeSpeed[1];
if(!settings.value(QStringLiteral("fitfiu_mc_v460"), false).toBool())
if(!settings.value(QZSettings::fitfiu_mc_v460, QZSettings::default_fitfiu_mc_v460).toBool())
writeSpeed[6] = writeSpeed[1] + 1;
else {
switch(writeSpeed[1] & 0x0F) {
@@ -169,8 +168,8 @@ void activiotreadmill::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && !virtualTreadMill && !virtualBike) {
bool virtual_device_enabled = settings.value("virtual_device_enabled", true).toBool();
bool virtual_device_force_bike = settings.value("virtual_device_force_bike", false).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike = settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike).toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
@@ -191,7 +190,7 @@ void activiotreadmill::update() {
// debug("Domyos Treadmill RSSI " + QString::number(bluetoothDevice.rssi()));
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
{
if (requestSpeed != -1) {
@@ -283,7 +282,7 @@ void activiotreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
Q_UNUSED(characteristic);
QByteArray value = newValue;
@@ -298,13 +297,13 @@ void activiotreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
double speed = GetSpeedFromPacket(value);
double incline = 1.0; // "fitfiu_mc_v460" has 1.0 fixed inclination
if(!settings.value(QStringLiteral("fitfiu_mc_v460"), false).toBool())
if(!settings.value(QZSettings::fitfiu_mc_v460, QZSettings::default_fitfiu_mc_v460).toBool())
incline = GetInclinationFromPacket(value);
// double kcal = GetKcalFromPacket(value);
// double distance = GetDistanceFromPacket(value);
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -331,10 +330,10 @@ void activiotreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
}
if (!firstCharacteristicChanged) {
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in

View File

@@ -53,7 +53,7 @@ class activiotreadmill : public treadmill {
void forceSpeed(double requestSpeed);
void forceIncline(double requestIncline);
void btinit(bool startTape);
void writeCharacteristic(const QLowEnergyCharacteristic characteristc, uint8_t *data, uint8_t data_len,
void writeCharacteristic(const QLowEnergyCharacteristic characteristic, uint8_t *data, uint8_t data_len,
const QString &info, bool disable_log = false, bool wait_for_response = false);
void startDiscover();
bool noConsole = false;

View File

@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.11.40" android:versionCode="381" android:installLocation="auto">
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.12.10" android:versionCode="440" android:installLocation="auto">
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
@@ -10,7 +10,7 @@
<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" android:usesCleartextTraffic="true">
<activity android:exported="true" 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">
<activity android:exported="true" android:resizeableActivity="true" android:supportsPictureInPicture="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="qdomyos-zwift" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -69,6 +69,10 @@
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
<!-- extract android style -->
</activity>
<service
android:name=".ForegroundService"
android:enabled="true"
android:exported="true"></service>
<service android:name=".ChannelService"></service>
<activity android:name="org.cagnulen.qdomyoszwift.MyActivity" />
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
@@ -76,10 +80,14 @@
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<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_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
</manifest>

View File

@@ -19,6 +19,8 @@ apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation "com.android.billingclient:billing:4.0.0"
implementation 'com.android.support:appcompat-v7:28.0.0'
androidTestImplementation "com.android.support:support-annotations:28.0.0"
}
android {

View File

@@ -47,7 +47,7 @@ public class ChannelService extends Service {
private AntChannelProvider mAntChannelProvider = null;
private boolean mAllowAddChannel = false;
HeartChannelController heartChannelController = null;
HeartChannelController heartChannelController = null;
PowerChannelController powerChannelController = null;
SpeedChannelController speedChannelController = null;
@@ -116,17 +116,14 @@ public class ChannelService extends Service {
if (null != powerChannelController) {
powerChannelController.cadence = cadence;
}
if (null != speedChannelController) {
speedChannelController.cadence = cadence;
}
}
int getHeart() {
if (null != heartChannelController) {
return heartChannelController.heart;
}
return 0;
}
int getHeart() {
if (null != heartChannelController) {
return heartChannelController.heart;
}
return 0;
}
/**
* Closes all channels currently added.
@@ -134,28 +131,28 @@ public class ChannelService extends Service {
void clearAllChannels() {
closeAllChannels();
}
}
}
public void openAllChannels() throws ChannelNotAvailableException {
if(Ant.heartRequest)
heartChannelController = new HeartChannelController(acquireChannel());
if (Ant.heartRequest)
heartChannelController = new HeartChannelController(acquireChannel());
if(Ant.speedRequest) {
powerChannelController = new PowerChannelController(acquireChannel());
speedChannelController = new SpeedChannelController(acquireChannel());
}
if (Ant.speedRequest) {
powerChannelController = new PowerChannelController(acquireChannel());
speedChannelController = new SpeedChannelController(acquireChannel());
}
}
private void closeAllChannels() {
if(heartChannelController != null)
heartChannelController.close();
if(powerChannelController != null)
powerChannelController.close();
if(speedChannelController != null)
speedChannelController.close();
heartChannelController = null;
powerChannelController = null;
speedChannelController = null;
if (heartChannelController != null)
heartChannelController.close();
if (powerChannelController != null)
powerChannelController.close();
if (speedChannelController != null)
speedChannelController.close();
heartChannelController = null;
powerChannelController = null;
speedChannelController = null;
}
AntChannel acquireChannel() throws ChannelNotAvailableException {
@@ -171,19 +168,18 @@ public class ChannelService extends Service {
* acquireChannel(context, PredefinedNetwork,
* requiredCapabilities, desiredCapabilities).
*/
if(Ant.garminKey == false)
mAntChannel = mAntChannelProvider.acquireChannel(this, PredefinedNetwork.ANT_PLUS_1);
else
{
NetworkKey mNK = new NetworkKey(new byte[] { (byte)0xb9, (byte)0xa5, (byte)0x21, (byte)0xfb,
(byte)0xbd, (byte)0x72, (byte)0xc3, (byte)0x45 });
Log.v(TAG, mNK.toString());
mAntChannel = mAntChannelProvider.acquireChannelOnPrivateNetwork(this, mNK);
}
} catch (RemoteException e) {
Log.v(TAG, "ACP Remote Ex");
} catch (UnsupportedFeatureException e) {
Log.v(TAG, "ACP UnsupportedFeature Ex");
if (Ant.garminKey == false)
mAntChannel = mAntChannelProvider.acquireChannel(this, PredefinedNetwork.ANT_PLUS_1);
else {
NetworkKey mNK = new NetworkKey(new byte[]{(byte) 0xb9, (byte) 0xa5, (byte) 0x21, (byte) 0xfb,
(byte) 0xbd, (byte) 0x72, (byte) 0xc3, (byte) 0x45});
Log.v(TAG, mNK.toString());
mAntChannel = mAntChannelProvider.acquireChannelOnPrivateNetwork(this, mNK);
}
} catch (RemoteException e) {
Log.v(TAG, "ACP Remote Ex");
} catch (UnsupportedFeatureException e) {
Log.v(TAG, "ACP UnsupportedFeature Ex");
}
}
return mAntChannel;

View File

@@ -0,0 +1,57 @@
package org.cagnulen.qdomyoszwift;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
public class ForegroundService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("QZ is Running")
.setContentText(input)
.setSmallIcon(R.drawable.icon)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}

View File

@@ -0,0 +1,28 @@
package org.cagnulen.qdomyoszwift;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.app.PendingIntent;
import android.graphics.Color;
import android.graphics.BitmapFactory;
import android.app.NotificationChannel;
public class NotificationClient
{
private static NotificationManager m_notificationManager;
private static Notification.Builder m_builder;
public NotificationClient() {}
public static void notify(Context context, String message) {
Intent serviceIntent = new Intent(context, ForegroundService.class);
serviceIntent.putExtra("inputExtra", "QZ is Running");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
}
}

54
src/android/src/PiP.java Normal file
View File

@@ -0,0 +1,54 @@
package org.cagnulen.qdomyoszwift;
import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.TextView;
import android.widget.Toast;
import android.util.Log;
import android.content.Intent;
import android.app.PictureInPictureParams;
import android.util.Rational;
import android.provider.Settings;
import android.view.Display;
import android.graphics.Point;
public class PiP {
public static void enterPiP(Activity a) {
Display d = a.getWindowManager()
.getDefaultDisplay();
Point p = new Point();
d.getSize(p);
int width = p.x;
int height = p.y;
Rational ratio
= new Rational(width, height);
PictureInPictureParams.Builder
pip_Builder
= new PictureInPictureParams
.Builder();
pip_Builder.setAspectRatio(ratio).build();
Log.v("QZ", "Pip");
a.enterPictureInPictureMode(pip_Builder.build());
}
}

View File

@@ -42,16 +42,12 @@ public class SpeedChannelController {
private static final String TAG = SpeedChannelController.class.getSimpleName();
public static final int SPEED_SENSOR_ID = 0x9e3d4b65;
private static Random randGen = new Random();
private AntChannel mAntChannel;
private ChannelEventCallback mChannelEventCallback = new ChannelEventCallback();
private boolean mIsOpen;
double speed = 0.0;
double cadence = 0.0;
public SpeedChannelController(AntChannel antChannel) {
mAntChannel = antChannel;
@@ -111,7 +107,7 @@ public class SpeedChannelController {
String logString = "Remote service communication failed.";
Log.e(TAG, logString);
}
}
void channelError(String error, AntCommandFailedException e) {
StringBuilder logString;
@@ -174,7 +170,6 @@ public class SpeedChannelController {
int rev;
double remWay;
double wheel = 0.1;
long unixTime = 0;
@Override
public void onChannelDeath() {
@@ -207,11 +202,15 @@ public class SpeedChannelController {
// Switching on event code to handle the different types of channel events
switch (code) {
case TX:
if(speed > 0)
{
revCounts++;
unixTime += (long)(1024.0 / (((double)(speed)) / 60.0));
}
long unixTime = System.currentTimeMillis() / 1000L;
if (lastTime != 0) {
way = speed * (unixTime - lastTime) / 3.6 + remWay;
rev = (int) (way / wheel + 0.5);
remWay = way - rev * wheel;
revCounts += rev;
}
lastTime = unixTime;
ucPageChange += 0x20;
ucPageChange &= 0xF0;
@@ -228,14 +227,12 @@ public class SpeedChannelController {
payload[1] = (byte) (halfunixTime & 0xFF);
payload[2] = (byte) ((halfunixTime >> 8) & 0xFF);
payload[3] = (byte) ((halfunixTime >> 16) & 0xFF);
}
else if (ucExtMesgType == 2) {
} else if (ucExtMesgType == 2) {
payload[0] = (byte) ((byte) 0x02 | (byte) (ucPageChange & (byte) 0x80));
payload[1] = (byte) 0xFF;
payload[2] = (byte) ((SPEED_SENSOR_ID >> 16) & 0xFF);
payload[3] = (byte) ((SPEED_SENSOR_ID >> 24) & 0xFF);
}
else if (ucExtMesgType == 3) {
} else if (ucExtMesgType == 3) {
payload[0] = (byte) ((byte) 0x03 | (byte) (ucPageChange & (byte) 0x80));
payload[1] = (byte) 0x01;
payload[2] = (byte) 0x01;

View File

@@ -181,6 +181,20 @@ public class InAppPurchase implements PurchasesUpdatedListener
purchaseFailed(purchaseRequestCode, FAILUREREASON_ERROR, e.getMessage());
}
purchaseSucceeded(purchaseRequestCode, purchase.getSignature(), purchase.getOriginalJson(), purchase.getPurchaseToken(), purchase.getOrderId(), purchase.getPurchaseTime());
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams,
new AcknowledgePurchaseResponseListener()
{
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult)
{
Log.d(TAG, "Purchase acknowledged ");
}
}
);
}
}

View File

@@ -29,6 +29,9 @@ bhfitnesselliptical::bhfitnesselliptical(bool noWriteResistance, bool noHeartSer
initDone = false;
connect(refresh, &QTimer::timeout, this, &bhfitnesselliptical::update);
refresh->start(200ms);
// this bike doesn't send resistance, so I have to use the default value
Resistance = default_resistance;
}
void bhfitnesselliptical::writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
@@ -55,11 +58,15 @@ void bhfitnesselliptical::writeCharacteristic(uint8_t *data, uint8_t data_len, c
void bhfitnesselliptical::forceResistance(resistance_t requestResistance) {
uint8_t write[] = {FTMS_SET_TARGET_RESISTANCE_LEVEL, 0x00};
uint8_t write[] = {FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS, 0x00, 0x00, 0x00, 0x00, 0x21, 0x22};
write[1] = ((uint16_t)requestResistance) & 0xFF;
write[3] = ((int16_t)(requestResistance - default_resistance) * 33) & 0xFF;
write[4] = ((int16_t)(requestResistance - default_resistance) * 33) >> 8;
writeCharacteristic(write, sizeof(write), QStringLiteral("forceResistance ") + QString::number(requestResistance));
// this bike doesn't send resistance, so I have to use the value forced
Resistance = requestResistance;
}
void bhfitnesselliptical::update() {
@@ -69,6 +76,7 @@ void bhfitnesselliptical::update() {
}
if (initRequest) {
initRequest = false;
} else if (bluetoothDevice.isValid() &&
m_control->state() == QLowEnergyController::DiscoveredState //&&
@@ -85,16 +93,15 @@ void bhfitnesselliptical::update() {
}
if (requestResistance != -1) {
if (requestResistance > 100) {
requestResistance = 100;
} // TODO, use the bluetooth value
else if (requestResistance == 0) {
requestResistance = 1;
if (requestResistance > max_resistance) {
requestResistance = max_resistance;
}
if (requestResistance != currentResistance().value()) {
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
forceResistance(requestResistance);
if (((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike)) {
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
forceResistance(requestResistance);
}
}
requestResistance = -1;
}
@@ -124,8 +131,9 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
bool disable_hr_frommachinery = settings.value(QStringLiteral("heart_ignore_builtin"), false).toBool();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
bool disable_hr_frommachinery =
settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
@@ -168,14 +176,17 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
index += 2;
if (!Flags.moreData) {
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
// this elliptical doesn't send speed so i have to calculate this based on cadence
/*
Speed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;*/
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(
watts(), Inclination.value(), Speed.value(),
fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0),
0 /* not useful for elliptical*/);
}
index += 2;
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
@@ -191,7 +202,7 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
}
if (Flags.instantCadence) {
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
@@ -199,7 +210,7 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
2.0;
// this elliptical doesn't send speed so i have to calculate this based on cadence
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = Cadence.value() / 10.0;
}
}
@@ -238,7 +249,7 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
}
if (Flags.instantPower) {
if (settings.value(QStringLiteral("power_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
@@ -267,8 +278,8 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
} else {
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() *
3.5) /
((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
@@ -278,7 +289,7 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -328,10 +339,10 @@ void bhfitnesselliptical::characteristicChanged(const QLowEnergyCharacteristic &
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
/*
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualTreadmill_setCadence(currentCrankRevolutions(), lastCrankEventTime());
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround,
QZSettings::default_ios_peloton_workaround).toBool(); if (ios_peloton_workaround && cadence && h &&
firstStateChanged) { h->virtualTreadmill_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualTreadmill_setHeartRate((uint8_t)metrics_override_heartrate());
}
*/
@@ -427,7 +438,7 @@ void bhfitnesselliptical::stateChanged(QLowEnergyService::ServiceState state) {
}
// ******************************************* virtual bike init *************************************
if (!firstStateChanged && !virtualTreadmill
if (!firstStateChanged
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
&& !h
@@ -435,25 +446,53 @@ void bhfitnesselliptical::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
if (virtual_device_enabled) {
emit debug(QStringLiteral("creating virtual treadmill interface..."));
virtualTreadmill = new virtualtreadmill(this, noHeartService);
// connect(virtualTreadmill,&virtualTreadmill::debug ,this,&bhfitnesselliptical::debug);
connect(virtualTreadmill, &virtualtreadmill::changeInclination, this,
&bhfitnesselliptical::changeInclination);
if (!virtualTreadmill && !virtualBike) {
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike =
settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike)
.toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
virtualTreadmill = new virtualtreadmill(this, noHeartService);
connect(virtualTreadmill, &virtualtreadmill::debug, this, &bhfitnesselliptical::debug);
connect(virtualTreadmill, &virtualtreadmill::changeInclination, this,
&bhfitnesselliptical::changeInclinationRequested);
} else {
debug("creating virtual bike interface...");
virtualBike = new virtualbike(this);
connect(virtualBike, &virtualbike::changeInclination, this,
&bhfitnesselliptical::changeInclinationRequested);
connect(virtualBike, &virtualbike::changeInclination, this,
&bhfitnesselliptical::changeInclination);
connect(virtualBike, &virtualbike::ftmsCharacteristicChanged, this,
&bhfitnesselliptical::ftmsCharacteristicChanged);
}
}
}
}
firstStateChanged = 1;
// ********************************************************************************************************
}
void bhfitnesselliptical::changeInclinationRequested(double grade, double percentage) {
if (percentage < 0)
percentage = 0;
changeInclination(grade, percentage);
}
void bhfitnesselliptical::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic,
const QByteArray &newValue) {
QByteArray b = newValue;
if (gattWriteCharControlPointId.isValid()) {
qDebug() << "routing FTMS packet to the bike from virtualTreadmill" << characteristic.uuid()
<< newValue.toHex(' ');
qDebug() << "routing FTMS packet to the bike from virtualBike" << characteristic.uuid() << newValue.toHex(' ');
// handling reading current resistance
if (b.at(0) == 0x11) {
int16_t slope = (((uint8_t)b.at(3)) + (b.at(4) << 8));
Resistance = (slope / 33) + default_resistance;
}
gattFTMSService->writeCharacteristic(gattWriteCharControlPointId, b);
}

View File

@@ -27,6 +27,7 @@
#include <QString>
#include "elliptical.h"
#include "virtualbike.h"
#include "virtualtreadmill.h"
#ifdef Q_OS_IOS
@@ -52,6 +53,7 @@ class bhfitnesselliptical : public elliptical {
QTimer *refresh;
virtualtreadmill *virtualTreadmill = nullptr;
virtualbike *virtualBike = nullptr;
QList<QLowEnergyService *> gattCommunicationChannelService;
QLowEnergyCharacteristic gattWriteCharControlPointId;
@@ -63,6 +65,8 @@ class bhfitnesselliptical : public elliptical {
uint8_t firstStateChanged = 0;
uint8_t bikeResistanceOffset = 4;
double bikeResistanceGain = 1.0;
const uint8_t max_resistance = 72; // 24;
const uint8_t default_resistance = 6;
bool initDone = false;
bool initRequest = false;
@@ -96,6 +100,7 @@ class bhfitnesselliptical : public elliptical {
void update();
void error(QLowEnergyController::Error err);
void errorService(QLowEnergyService::ServiceError);
void changeInclinationRequested(double grade, double percentage);
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
};

View File

@@ -39,12 +39,12 @@ void bike::changePower(int32_t power) {
RequestedPower = power;
requestPower = power; // used by some bikes that have ERG mode builtin
QSettings settings;
bool force_resistance = settings.value(QStringLiteral("virtualbike_forceresistance"), true).toBool();
// bool erg_mode = settings.value(QStringLiteral("zwift_erg"), false).toBool(); //Not used anywhere in code
double erg_filter_upper = settings.value(QStringLiteral("zwift_erg_filter"), 0.0).toDouble();
double erg_filter_lower = settings.value(QStringLiteral("zwift_erg_filter_down"), 0.0).toDouble();
double zwift_erg_resistance_up = settings.value(QStringLiteral("zwift_erg_resistance_up"), 999.0).toDouble();
double zwift_erg_resistance_down = settings.value(QStringLiteral("zwift_erg_resistance_down"), 0.0).toDouble();
bool force_resistance = settings.value(QZSettings::virtualbike_forceresistance, QZSettings::default_virtualbike_forceresistance).toBool();
// bool erg_mode = settings.value(QZSettings::zwift_erg, QZSettings::default_zwift_erg).toBool(); //Not used anywhere in code
double erg_filter_upper = settings.value(QZSettings::zwift_erg_filter, QZSettings::default_zwift_erg_filter).toDouble();
double erg_filter_lower = settings.value(QZSettings::zwift_erg_filter_down, QZSettings::default_zwift_erg_filter_down).toDouble();
double zwift_erg_resistance_up = settings.value(QZSettings::zwift_erg_resistance_up, QZSettings::default_zwift_erg_resistance_up).toDouble();
double zwift_erg_resistance_down = settings.value(QZSettings::zwift_erg_resistance_down, QZSettings::default_zwift_erg_resistance_down).toDouble();
double deltaDown = wattsMetric().value() - ((double)power);
double deltaUp = ((double)power) - wattsMetric().value();
@@ -162,7 +162,7 @@ uint8_t bike::metrics_override_heartrate() {
QSettings settings;
QString setting =
settings.value(QStringLiteral("peloton_heartrate_metric"), QStringLiteral("Heart Rate")).toString();
settings.value(QZSettings::peloton_heartrate_metric, QZSettings::default_peloton_heartrate_metric).toString();
if (!setting.compare(QStringLiteral("Heart Rate"))) {
return qRound(currentHeart().value());
} else if (!setting.compare(QStringLiteral("Speed"))) {

View File

@@ -32,6 +32,8 @@ class bike : public bluetoothdevice {
uint8_t metrics_override_heartrate();
void setGears(int8_t d);
int8_t gears();
void setSpeedLimit(double speed) {m_speedLimit = speed;}
double speedLimit() {return m_speedLimit;}
/**
@@ -80,6 +82,8 @@ class bike : public bluetoothdevice {
metric m_pelotonResistance;
metric m_steeringAngle;
double m_speedLimit = 0;
};
#endif // BIKE_H

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,8 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qloggingcategory.h>
#include "qzsettings.h"
#include "activiotreadmill.h"
#include "bhfitnesselliptical.h"
#include "bluetoothdevice.h"
@@ -37,6 +39,7 @@
#include "eslinkertreadmill.h"
#include "fakebike.h"
#include "fakeelliptical.h"
#include "faketreadmill.h"
#include "fitmetria_fanfit.h"
#include "fitplusbike.h"
@@ -52,15 +55,18 @@
#include "keepbike.h"
#include "kingsmithr1protreadmill.h"
#include "kingsmithr2treadmill.h"
#include "lifefitnesstreadmill.h"
#include "m3ibike.h"
#include "mcfbike.h"
#include "mepanelbike.h"
#include "nautilusbike.h"
#include "nautiluselliptical.h"
#include "nautilustreadmill.h"
#include "nordictrackelliptical.h"
#include "nordictrackifitadbtreadmill.h"
#include "nordictrackifitadbbike.h"
#include "nordictrackifitadbtreadmill.h"
#include "npecablebike.h"
#include "octaneelliptical.h"
#include "octanetreadmill.h"
#include "pafersbike.h"
#include "paferstreadmill.h"
@@ -151,6 +157,7 @@ class bluetooth : public QObject, public SignalHandler {
nordictrackelliptical *nordictrackElliptical = nullptr;
nordictrackifitadbtreadmill *nordictrackifitadbTreadmill = nullptr;
nordictrackifitadbbike *nordictrackifitadbBike = nullptr;
octaneelliptical *octaneElliptical = nullptr;
octanetreadmill *octaneTreadmill = nullptr;
proformrower *proformRower = nullptr;
proformbike *proformBike = nullptr;
@@ -173,6 +180,7 @@ class bluetooth : public QObject, public SignalHandler {
snodebike *snodeBike = nullptr;
eslinkertreadmill *eslinkerTreadmill = nullptr;
m3ibike *m3iBike = nullptr;
mepanelbike *mepanelBike = nullptr;
skandikawiribike *skandikaWiriBike = nullptr;
cscbike *cscBike = nullptr;
mcfbike *mcfBike = nullptr;
@@ -187,6 +195,7 @@ class bluetooth : public QObject, public SignalHandler {
ftmsrower *ftmsRower = nullptr;
smartrowrower *smartrowRower = nullptr;
echelonstride *echelonStride = nullptr;
lifefitnesstreadmill *lifefitnessTreadmill = nullptr;
keepbike *keepBike = nullptr;
kingsmithr1protreadmill *kingsmithR1ProTreadmill = nullptr;
kingsmithr2treadmill *kingsmithR2Treadmill = nullptr;
@@ -209,6 +218,7 @@ class bluetooth : public QObject, public SignalHandler {
elitesterzosmart *eliteSterzoSmart = nullptr;
fakebike *fakeBike = nullptr;
fakeelliptical *fakeElliptical = nullptr;
faketreadmill *fakeTreadmill = nullptr;
QList<fitmetria_fanfit *> fitmetriaFanfit;
QString filterDevice = QLatin1String("");
@@ -222,6 +232,16 @@ class bluetooth : public QObject, public SignalHandler {
double bikeResistanceGain = 1.0;
bool forceHeartBeltOffForTimeout = false;
/**
* @brief Start the Bluetooth discovery agent.
*/
void startDiscovery();
/**
* @brief Stop the Bluetooth discovery agent.
*/
void stopDiscovery();
bool handleSignal(int signal) override;
void stateFileUpdate();
void stateFileRead();
@@ -231,12 +251,18 @@ class bluetooth : public QObject, public SignalHandler {
bool powerSensorAvaiable();
bool eliteRizerAvaiable();
bool eliteSterzoSmartAvaiable();
bool fitmetriaFanfitAvaiable();
bool fitmetria_fanfit_isconnected(QString name);
#ifdef Q_OS_WIN
QTimer discoveryTimeout;
#endif
/**
* @brief Store the name and other info in the settings.
* @param b The bluetooth device info.
*/
void setLastBluetoothDevice(const QBluetoothDeviceInfo &b);
signals:
void deviceConnected(QBluetoothDeviceInfo b);
void deviceFound(QString name);

View File

@@ -8,7 +8,11 @@ bluetoothdevice::bluetoothdevice() {}
bluetoothdevice::BLUETOOTH_TYPE bluetoothdevice::deviceType() { return bluetoothdevice::UNKNOWN; }
void bluetoothdevice::start() { requestStart = 1; }
void bluetoothdevice::stop() { requestStop = 1; }
void bluetoothdevice::stop(bool pause) {
requestStop = 1;
if (pause)
requestPause = 1;
}
metric bluetoothdevice::currentHeart() { return Heart; }
metric bluetoothdevice::currentSpeed() { return Speed; }
metric bluetoothdevice::currentInclination() { return Inclination; }
@@ -38,7 +42,7 @@ void bluetoothdevice::offsetElapsedTime(int offset) { elapsed += offset; }
QTime bluetoothdevice::currentPace() {
QSettings settings;
bool miles = settings.value(QStringLiteral("miles_unit"), false).toBool();
bool miles = settings.value(QZSettings::miles_unit, QZSettings::default_miles_unit).toBool();
double unit_conversion = 1.0;
if (miles) {
unit_conversion = 0.621371;
@@ -55,7 +59,7 @@ QTime bluetoothdevice::currentPace() {
QTime bluetoothdevice::averagePace() {
QSettings settings;
bool miles = settings.value(QStringLiteral("miles_unit"), false).toBool();
bool miles = settings.value(QZSettings::miles_unit, QZSettings::default_miles_unit).toBool();
double unit_conversion = 1.0;
if (miles) {
unit_conversion = 0.621371;
@@ -72,7 +76,7 @@ QTime bluetoothdevice::averagePace() {
QTime bluetoothdevice::maxPace() {
QSettings settings;
bool miles = settings.value(QStringLiteral("miles_unit"), false).toBool();
bool miles = settings.value(QZSettings::miles_unit, QZSettings::default_miles_unit).toBool();
double unit_conversion = 1.0;
if (miles) {
unit_conversion = 0.621371;
@@ -135,17 +139,19 @@ void bluetoothdevice::update_metrics(bool watt_calc, const double watts) {
QDateTime current = QDateTime::currentDateTime();
double deltaTime = (((double)_lastTimeUpdate.msecsTo(current)) / ((double)1000.0));
QSettings settings;
bool power_as_bike = settings.value(QStringLiteral("power_sensor_as_bike"), false).toBool();
bool power_as_treadmill = settings.value(QStringLiteral("power_sensor_as_treadmill"), false).toBool();
bool power_as_bike =
settings.value(QZSettings::power_sensor_as_bike, QZSettings::default_power_sensor_as_bike).toBool();
bool power_as_treadmill =
settings.value(QZSettings::power_sensor_as_treadmill, QZSettings::default_power_sensor_as_treadmill).toBool();
if (settings.value(QStringLiteral("power_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")) == false &&
!power_as_bike && !power_as_treadmill)
watt_calc = false;
if (!_firstUpdate && !paused) {
if (currentSpeed().value() > 0.0 || settings.value(QStringLiteral("continuous_moving"), true).toBool()) {
if (currentSpeed().value() > 0.0 || settings.value(QZSettings::continuous_moving, true).toBool()) {
elapsed += deltaTime;
}
@@ -157,18 +163,21 @@ void bluetoothdevice::update_metrics(bool watt_calc, const double watts) {
if (watt_calc) {
m_watt = watts;
}
WattKg = m_watt.value() / settings.value(QStringLiteral("weight"), 75.0).toFloat();
WattKg = m_watt.value() / settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
} else if (m_watt.value() > 0) {
m_watt = 0;
if (watt_calc) {
m_watt = 0;
}
WattKg = 0;
}
} else if (paused && settings.value(QStringLiteral("instant_power_on_pause"), false).toBool()) {
} else if (paused && settings.value(QZSettings::instant_power_on_pause, QZSettings::default_instant_power_on_pause)
.toBool()) {
// useful for FTP test
if (watt_calc) {
m_watt = watts;
}
WattKg = m_watt.value() / settings.value(QStringLiteral("weight"), 75.0).toFloat();
WattKg = m_watt.value() / settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
} else if (m_watt.value() > 0) {
m_watt = 0;
@@ -267,7 +276,7 @@ uint8_t bluetoothdevice::metrics_override_heartrate() {
QSettings settings;
QString setting =
settings.value(QStringLiteral("peloton_heartrate_metric"), QStringLiteral("Heart Rate")).toString();
settings.value(QZSettings::peloton_heartrate_metric, QZSettings::default_peloton_heartrate_metric).toString();
if (!setting.compare(QStringLiteral("Heart Rate"))) {
return currentHeart().value();
} else if (!setting.compare(QStringLiteral("Speed"))) {

View File

@@ -3,6 +3,7 @@
#include "definitions.h"
#include "metric.h"
#include "qzsettings.h"
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo>
@@ -21,16 +22,12 @@
#include <QtBluetooth/qlowenergyservice.h>
#include <QtBluetooth/qlowenergyservicedata.h>
#if defined(Q_OS_IOS)
#define SAME_BLUETOOTH_DEVICE(d1, d2) (d1.deviceUuid() == d2.deviceUuid())
#else
#define SAME_BLUETOOTH_DEVICE(d1, d2) (d1.address() == d2.address())
#endif
/**
* @brief The MetersByInclination class represents a section of track at a specific inclination.
*/
@@ -164,7 +161,6 @@ class bluetoothdevice : public QObject {
*/
virtual QGeoCoordinate currentCordinate();
/**
* @brief nextInclination300Meters The next 300m of track sections: length and inclination
* @return A list of MetersByInclination objects
@@ -293,12 +289,14 @@ class bluetoothdevice : public QObject {
metric currentMETS() { return METS; }
/**
* @brief currentHeartZone Gets a metric object to get or set the current heart zone. Units: depends on implementation.
* @brief currentHeartZone Gets a metric object to get or set the current heart zone. Units: depends on
* implementation.
*/
metric currentHeartZone() { return HeartZone; }
/**
* @brief currentPowerZone Gets a metric object to get or set the current power zome. Units: depends on implementation.
* @brief currentPowerZone Gets a metric object to get or set the current power zome. Units: depends on
* implementation.
* @return
*/
metric currentPowerZone() { return PowerZone; }
@@ -357,7 +355,7 @@ class bluetoothdevice : public QObject {
public Q_SLOTS:
virtual void start();
virtual void stop();
virtual void stop(bool pause);
virtual void heartRate(uint8_t heart);
virtual void cadenceSensor(uint8_t cadence);
virtual void powerSensor(uint16_t power);
@@ -392,7 +390,7 @@ class bluetoothdevice : public QObject {
metric elapsed;
/**
* @brief moving The time spent moving int he current session. Units: seconds
* @brief moving The time spent moving in the current session. Units: seconds
*/
metric moving; // moving time
@@ -406,7 +404,7 @@ class bluetoothdevice : public QObject {
* e.g. the product of bike flywheel speed and simulated wheel size, or
* the belt speed of a treadmill.
*/
metric Speed;
metric Speed;
/**
* @brief Distance The simulated distance travelled. Units: km
@@ -416,19 +414,19 @@ class bluetoothdevice : public QObject {
*/
metric Distance;
/**
* @brief FanSpeed The currently requested fan speed. Units: revolutions per second
*/
uint8_t FanSpeed = 0;
/**
* @brief Heart rate. Unit: beats per minute
* @brief Heart rate. Unit: beats per minute
*/
metric Heart;
int8_t requestStart = -1;
int8_t requestStop = -1;
int8_t requestPause = -1;
int8_t requestIncreaseFan = -1;
int8_t requestDecreaseFan = -1;
double requestFanSpeed = -1;

View File

@@ -93,7 +93,8 @@ void bowflext216treadmill::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && !virtualTreadMill) {
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
if (virtual_device_enabled) {
emit debug(QStringLiteral("creating virtual treadmill interface..."));
virtualTreadMill = new virtualtreadmill(this, noHeartService);
@@ -103,7 +104,7 @@ void bowflext216treadmill::update() {
}
// ********************************************************************************************************
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
// updating the treadmill console every second
// it seems that stops the communication
@@ -124,7 +125,7 @@ void bowflext216treadmill::update() {
requestSpeed = -1;
}
if (requestInclination != -100) {
if(requestInclination < 0)
if (requestInclination < 0)
requestInclination = 0;
if (requestInclination != currentInclination().value() && requestInclination >= 0 &&
requestInclination <= 15) {
@@ -167,7 +168,7 @@ void bowflext216treadmill::characteristicChanged(const QLowEnergyCharacteristic
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
Q_UNUSED(characteristic);
QByteArray value = newValue;
@@ -188,13 +189,16 @@ void bowflext216treadmill::characteristicChanged(const QLowEnergyCharacteristic
if ((newValue.length() != 20))
return;
if (bowflex_t6 == true && newValue.at(1) != 0x00)
return;
double speed = GetSpeedFromPacket(value);
double incline = GetInclinationFromPacket(value);
// double kcal = GetKcalFromPacket(value);
// double distance = GetDistanceFromPacket(value);
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -225,10 +229,11 @@ void bowflext216treadmill::characteristicChanged(const QLowEnergyCharacteristic
}
if (!firstCharacteristicChanged) {
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) +
1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
@@ -249,9 +254,15 @@ void bowflext216treadmill::characteristicChanged(const QLowEnergyCharacteristic
}
double bowflext216treadmill::GetSpeedFromPacket(const QByteArray &packet) {
uint16_t convertedData = (packet.at(7) << 8) | packet.at(6);
double data = (double)convertedData / 100.0f;
return data * 1.60934;
if(bowflex_t6 == false) {
uint16_t convertedData = (packet.at(7) << 8) | packet.at(6);
double data = (double)convertedData / 100.0f;
return data * 1.60934;
} else {
uint16_t convertedData = (packet.at(13) << 8) | packet.at(12);
double data = (double)convertedData / 100.0f;
return data;
}
}
double bowflext216treadmill::GetKcalFromPacket(const QByteArray &packet) {
@@ -360,6 +371,16 @@ void bowflext216treadmill::serviceScanDone(void) {
QBluetoothUuid _gattCommunicationChannelServiceId(QStringLiteral("edff9e80-cad7-11e5-ab63-0002a5d5c51b"));
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
if (gattCommunicationChannelService == nullptr) {
qDebug() << "trying with the BOWFLEX T6 treadmill";
bowflex_t6 = true;
QBluetoothUuid _gattCommunicationChannelServiceId(QStringLiteral("15B7BF49-1693-481E-B877-69D33CE6BAFA"));
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
if (gattCommunicationChannelService == nullptr) {
qDebug() << "WRONG SERVICE";
return;
}
}
connect(gattCommunicationChannelService, &QLowEnergyService::stateChanged, this,
&bowflext216treadmill::stateChanged);
gattCommunicationChannelService->discoverDetails();

View File

@@ -79,6 +79,8 @@ class bowflext216treadmill : public treadmill {
bool initDone = false;
bool initRequest = false;
bool bowflex_t6 = false;
Q_SIGNALS:
void disconnected();
void debug(QString string);

View File

@@ -95,7 +95,7 @@ void bowflextreadmill::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && !virtualTreadMill) {
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
if (virtual_device_enabled) {
emit debug(QStringLiteral("creating virtual treadmill interface..."));
virtualTreadMill = new virtualtreadmill(this, noHeartService);
@@ -105,7 +105,7 @@ void bowflextreadmill::update() {
}
// ********************************************************************************************************
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
// updating the treadmill console every second
// it seems that stops the communication
@@ -166,7 +166,7 @@ void bowflextreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
Q_UNUSED(characteristic);
QByteArray value = newValue;
@@ -183,7 +183,7 @@ void bowflextreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
// double distance = GetDistanceFromPacket(value);
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -214,10 +214,10 @@ void bowflextreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
}
if (!firstCharacteristicChanged) {
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in

View File

@@ -4,7 +4,7 @@
CharacteristicNotifier2A5B::CharacteristicNotifier2A5B(bluetoothdevice *Bike, QObject *parent)
: CharacteristicNotifier(0x2a5b, parent), Bike(Bike) {
QSettings settings;
bike_wheel_revs = settings.value(QStringLiteral("bike_wheel_revs"), false).toBool();
bike_wheel_revs = settings.value(QZSettings::bike_wheel_revs, QZSettings::default_bike_wheel_revs).toBool();
}
int CharacteristicNotifier2A5B::notify(QByteArray &value) {

View File

@@ -8,8 +8,8 @@ CharacteristicNotifier2ACD::CharacteristicNotifier2ACD(bluetoothdevice *Bike, QO
int CharacteristicNotifier2ACD::notify(QByteArray &value) {
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
if (dt == bluetoothdevice::TREADMILL || dt == bluetoothdevice::ELLIPTICAL) {
value.append(0x08); // Inclination avaiable
value.append((char)0x01); // heart rate avaiable
value.append(0x08); // Inclination available
value.append((char)0x01); // heart rate available
uint16_t normalizeSpeed = (uint16_t)qRound(Bike->currentSpeed().value() * 100);
char a = (normalizeSpeed >> 8) & 0XFF;

View File

@@ -34,7 +34,7 @@ int CharacteristicNotifier2AD2::notify(QByteArray &value) {
return CN_OK;
} else if (dt == bluetoothdevice::TREADMILL || dt == bluetoothdevice::ELLIPTICAL) {
QSettings settings;
bool double_cadence = settings.value(QStringLiteral("powr_sensor_running_cadence_double"), false).toBool();
bool double_cadence = settings.value(QZSettings::powr_sensor_running_cadence_double, QZSettings::default_powr_sensor_running_cadence_double).toBool();
double cadence_multiplier = 2.0;
if (double_cadence)
cadence_multiplier = 1.0;

View File

@@ -0,0 +1,87 @@
#include "characteristicwriteprocessor.h"
#include <QSettings>
CharacteristicWriteProcessor::CharacteristicWriteProcessor(double bikeResistanceGain, uint8_t bikeResistanceOffset,
bluetoothdevice *bike, QObject *parent)
: QObject(parent), bikeResistanceOffset(bikeResistanceOffset), bikeResistanceGain(bikeResistanceGain), Bike(bike) {}
void CharacteristicWriteProcessor::changePower(uint16_t power) { Bike->changePower(power); }
void CharacteristicWriteProcessor::changeSlope(int16_t iresistance, uint8_t crr, uint8_t cw) {
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
QSettings settings;
bool force_resistance =
settings.value(QZSettings::virtualbike_forceresistance, QZSettings::default_virtualbike_forceresistance)
.toBool();
bool erg_mode = settings.value(QZSettings::zwift_erg, QZSettings::default_zwift_erg).toBool();
bool zwift_negative_inclination_x2 =
settings.value(QZSettings::zwift_negative_inclination_x2, QZSettings::default_zwift_negative_inclination_x2)
.toBool();
double offset =
settings.value(QZSettings::zwift_inclination_offset, QZSettings::default_zwift_inclination_offset).toDouble();
double gain =
settings.value(QZSettings::zwift_inclination_gain, QZSettings::default_zwift_inclination_gain).toDouble();
double CRRGain = settings.value(QZSettings::CRRGain, QZSettings::default_CRRGain).toDouble();
double CWGain = settings.value(QZSettings::CWGain, QZSettings::default_CWGain).toDouble();
qDebug() << QStringLiteral("new requested resistance zwift erg grade ") + QString::number(iresistance) +
QStringLiteral(" enabled ") + force_resistance;
double resistance = ((double)iresistance * 1.5) / 100.0;
qDebug() << QStringLiteral("calculated erg grade ") + QString::number(resistance);
double grade = ((iresistance / 100.0) * gain) + offset;
/*
Surface Road Crr MTB Crr Gravel Crr (Namebrand) Zwift Gravel Crr
Pavement .004 .01 .008 .008
Sand .004 .01 .008 .008
Brick .0055 .01 .008 .008
Wood .0065 .01 .008 .008
Cobbles .0065 .01 .008 .008
Ice/Snow .0075 .014 .018 .018
Dirt .025 .014 .016 .018
Grass .042
*/
const double fCRR = crr / 10000.0;
const double CRR_offset = ((crr - 40) * 0.05) * CRRGain;
const double fCW = cw / 100.0;
const double CW_offset = ((crr - 40) * 0.05) * CWGain;
qDebug() << "changeSlope CRR = " << fCRR << CRR_offset << "CW = " << fCW;
if (dt == bluetoothdevice::BIKE) {
// if the bike doesn't have the inclination by hardware, i'm simulating inclination with the value received
// form Zwift
if (!((bike *)Bike)->inclinationAvailableByHardware())
Bike->setInclination(grade + CRR_offset + CW_offset);
if (iresistance >= 0 || !zwift_negative_inclination_x2)
emit changeInclination(grade, ((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * gain) + offset);
else
emit changeInclination((((iresistance / 100.0) * 2.0) * gain) + offset,
(((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * 2.0) * gain) + offset);
if (force_resistance && !erg_mode) {
// same on the training program
Bike->changeResistance((resistance_t)(round(resistance * bikeResistanceGain)) + bikeResistanceOffset + 1 +
CRR_offset + CW_offset); // resistance start from 1
}
} else if (dt == bluetoothdevice::TREADMILL) {
emit changeInclination(((iresistance / 100.0) * gain) + offset,
((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * gain) + offset);
} else if (dt == bluetoothdevice::ELLIPTICAL) {
emit changeInclination(((iresistance / 100.0) * gain) + offset,
((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * gain) + offset);
if (!((elliptical *)Bike)->inclinationAvailableByHardware()) {
if (force_resistance && !erg_mode) {
// same on the training program
Bike->changeResistance((resistance_t)(round(resistance * bikeResistanceGain)) + bikeResistanceOffset +
1 + CRR_offset + CW_offset); // resistance start from 1
}
}
}
emit slopeChanged();
}

View File

@@ -1,7 +1,13 @@
#ifndef CHARACTERISTICWRITEPROCESSOR_H
#define CHARACTERISTICWRITEPROCESSOR_H
#include "bike.h"
#include "bluetoothdevice.h"
#include "elliptical.h"
#include "treadmill.h"
#include <QObject>
#include <QSettings>
#include <QtMath>
#define CP_INVALID -1
#define CP_OK 0
@@ -9,9 +15,18 @@
class CharacteristicWriteProcessor : public QObject {
Q_OBJECT
public:
explicit CharacteristicWriteProcessor(QObject *parent = nullptr) : QObject(parent) {}
uint8_t bikeResistanceOffset = 4;
double bikeResistanceGain = 1.0;
bluetoothdevice *Bike;
explicit CharacteristicWriteProcessor(double bikeResistanceGain, uint8_t bikeResistanceOffset,
bluetoothdevice *bike, QObject *parent = nullptr);
virtual int writeProcess(quint16 uuid, const QByteArray &data, QByteArray &out) = 0;
virtual void changePower(uint16_t power);
virtual void changeSlope(int16_t iresistance, uint8_t crr, uint8_t cw);
signals:
void changeInclination(double grade, double percentage);
void slopeChanged();
};
#endif // CHARACTERISTICWRITEPROCESSOR_H

View File

@@ -9,16 +9,17 @@ CharacteristicWriteProcessor2AD9::CharacteristicWriteProcessor2AD9(double bikeRe
uint8_t bikeResistanceOffset, bluetoothdevice *bike,
CharacteristicNotifier2AD9 *notifier,
QObject *parent)
: CharacteristicWriteProcessor(parent), bikeResistanceOffset(bikeResistanceOffset),
bikeResistanceGain(bikeResistanceGain), Bike(bike), notifier(notifier) {}
: CharacteristicWriteProcessor(bikeResistanceGain, bikeResistanceOffset, bike, parent), notifier(notifier) {}
int CharacteristicWriteProcessor2AD9::writeProcess(quint16 uuid, const QByteArray &data, QByteArray &reply) {
if (data.size()) {
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
if (dt == bluetoothdevice::BIKE) {
QSettings settings;
bool force_resistance = settings.value(QStringLiteral("virtualbike_forceresistance"), true).toBool();
bool erg_mode = settings.value(QStringLiteral("zwift_erg"), false).toBool();
bool force_resistance =
settings.value(QZSettings::virtualbike_forceresistance, QZSettings::default_virtualbike_forceresistance)
.toBool();
bool erg_mode = settings.value(QZSettings::zwift_erg, QZSettings::default_zwift_erg).toBool();
char cmd = data.at(0);
emit ftmsCharacteristicChanged(QLowEnergyCharacteristic(), data);
if (cmd == FTMS_SET_TARGET_RESISTANCE_LEVEL) {
@@ -43,7 +44,9 @@ int CharacteristicWriteProcessor2AD9::writeProcess(quint16 uuid, const QByteArra
reply.append((quint8)FTMS_SUCCESS);
int16_t iresistance = (((uint8_t)data.at(3)) + (data.at(4) << 8));
changeSlope(iresistance);
uint8_t crr = data.at(5);
uint8_t cw = data.at(6);
changeSlope(iresistance, crr, cw);
} else if (cmd == FTMS_SET_TARGET_POWER) // erg mode
{
@@ -114,66 +117,18 @@ int CharacteristicWriteProcessor2AD9::writeProcess(quint16 uuid, const QByteArra
{
qDebug() << QStringLiteral("indoor bike simulation parameters");
int16_t iresistance = (((uint8_t)data.at(3)) + (data.at(4) << 8));
changeSlope(iresistance);
uint8_t crr = data.at(5);
uint8_t cw = data.at(6);
changeSlope(iresistance, crr, cw);
}
reply.append((quint8)FTMS_RESPONSE_CODE);
reply.append((quint8)data.at(0));
reply.append((quint8)FTMS_SUCCESS);
}
if(notifier) {
if (notifier) {
notifier->answer = reply;
}
return CP_OK;
} else
return CP_INVALID;
}
void CharacteristicWriteProcessor2AD9::changePower(uint16_t power) { Bike->changePower(power); }
void CharacteristicWriteProcessor2AD9::changeSlope(int16_t iresistance) {
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
if (dt == bluetoothdevice::BIKE) {
QSettings settings;
bool force_resistance = settings.value(QStringLiteral("virtualbike_forceresistance"), true).toBool();
bool erg_mode = settings.value(QStringLiteral("zwift_erg"), false).toBool();
bool zwift_negative_inclination_x2 =
settings.value(QStringLiteral("zwift_negative_inclination_x2"), false).toBool();
double offset = settings.value(QStringLiteral("zwift_inclination_offset"), 0.0).toDouble();
double gain = settings.value(QStringLiteral("zwift_inclination_gain"), 1.0).toDouble();
qDebug() << QStringLiteral("new requested resistance zwift erg grade ") + QString::number(iresistance) +
QStringLiteral(" enabled ") + force_resistance;
double resistance = ((double)iresistance * 1.5) / 100.0;
qDebug() << QStringLiteral("calculated erg grade ") + QString::number(resistance);
double grade = ((iresistance / 100.0) * gain) + offset;
// if the bike doesn't have the inclination by hardware, i'm simulating inclination with the value received form Zwift
if(!((bike*)Bike)->inclinationAvailableByHardware())
Bike->setInclination(grade);
if (iresistance >= 0 || !zwift_negative_inclination_x2)
emit changeInclination(grade,
((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * gain) + offset);
else
emit changeInclination((((iresistance / 100.0) * 2.0) * gain) + offset,
(((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * 2.0) * gain) + offset);
if (force_resistance && !erg_mode) {
// same on the training program
Bike->changeResistance((resistance_t)(round(resistance * bikeResistanceGain)) + bikeResistanceOffset +
1); // resistance start from 1
}
} else if (dt == bluetoothdevice::TREADMILL || dt == bluetoothdevice::ELLIPTICAL) {
QSettings settings;
double offset = settings.value(QStringLiteral("zwift_inclination_offset"), 0.0).toDouble();
double gain = settings.value(QStringLiteral("zwift_inclination_gain"), 1.0).toDouble();
qDebug() << QStringLiteral("new requested resistance zwift erg grade ") + QString::number(iresistance);
double resistance = ((double)iresistance * 1.5) / 100.0;
qDebug() << QStringLiteral("calculated erg grade ") + QString::number(resistance);
emit changeInclination(((iresistance / 100.0) * gain) + offset,
((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * gain) + offset);
}
emit slopeChanged();
}

View File

@@ -1,26 +1,19 @@
#ifndef CHARACTERISTICWRITEPROCESSOR2AD9_H
#define CHARACTERISTICWRITEPROCESSOR2AD9_H
#include "bluetoothdevice.h"
#include "characteristicwriteprocessor.h"
#include "characteristicnotifier2ad9.h"
#include "characteristicwriteprocessor.h"
class CharacteristicWriteProcessor2AD9 : public CharacteristicWriteProcessor {
Q_OBJECT
uint8_t bikeResistanceOffset = 4;
double bikeResistanceGain = 1.0;
bluetoothdevice *Bike;
CharacteristicNotifier2AD9 *notifier = nullptr;
public:
explicit CharacteristicWriteProcessor2AD9(double bikeResistanceGain, uint8_t bikeResistanceOffset,
bluetoothdevice *bike, CharacteristicNotifier2AD9 *notifier, QObject *parent = nullptr);
bluetoothdevice *bike, CharacteristicNotifier2AD9 *notifier,
QObject *parent = nullptr);
virtual int writeProcess(quint16 uuid, const QByteArray &data, QByteArray &out);
void changeSlope(int16_t slope);
void changePower(uint16_t power);
signals:
void changeInclination(double grade, double percentage);
void slopeChanged();
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
};

View File

@@ -0,0 +1,49 @@
#include "characteristicwriteprocessore005.h"
#include "elliptical.h"
#include "ftmsbike.h"
#include "treadmill.h"
#include "wahookickrsnapbike.h"
#include <QtMath>
CharacteristicWriteProcessorE005::CharacteristicWriteProcessorE005(double bikeResistanceGain,
uint8_t bikeResistanceOffset, bluetoothdevice *bike,
// CharacteristicNotifier2AD9 *notifier,
QObject *parent)
: CharacteristicWriteProcessor(bikeResistanceGain, bikeResistanceOffset, bike, parent) {}
int CharacteristicWriteProcessorE005::writeProcess(quint16 uuid, const QByteArray &data, QByteArray &reply) {
if (data.size()) {
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
if (dt == bluetoothdevice::BIKE) {
char cmd = data.at(0);
emit ftmsCharacteristicChanged(QLowEnergyCharacteristic(), data);
if (cmd == wahookickrsnapbike::_setSimMode && data.count() >= 7) {
weight = ((double)((uint16_t)data.at(1)) + (((uint16_t)data.at(2)) >> 8)) / 100.0;
rrc = ((double)((uint16_t)data.at(3)) + (((uint16_t)data.at(4)) >> 8)) / 1000.0;
wrc = ((double)((uint16_t)data.at(5)) + (((uint16_t)data.at(6)) >> 8)) / 1000.0;
qDebug() << "weigth" << weight << "rrc" << rrc << "wrc" << wrc;
} else if (cmd == wahookickrsnapbike::_setSimGrade && data.count() >= 3) {
uint16_t grade;
double fgrade;
grade = (uint16_t)((uint8_t)data.at(1)) + (((uint16_t)((uint8_t)data.at(2))) << 8);
fgrade = (((((double)grade) / 65535.0) * 2) - 1.0) * 100.0;
qDebug() << "grade" << grade << "fgrade" << fgrade;
changeSlope(fgrade * 100.0, rrc, wrc);
} else if (cmd == wahookickrsnapbike::_setErgMode && data.count() >= 3) {
uint16_t watts;
watts = (uint16_t)((uint8_t)data.at(1)) + (((uint16_t)((uint8_t)data.at(2))) << 8);
qDebug() << "erg mode" << watts;
changePower(watts);
}
} else if (dt == bluetoothdevice::TREADMILL || dt == bluetoothdevice::ELLIPTICAL) {
}
reply.append((quint8)FTMS_RESPONSE_CODE);
reply.append((quint8)data.at(0));
reply.append((quint8)FTMS_SUCCESS);
/*if (notifier) {
notifier->answer = reply;
}*/
return CP_OK;
} else
return CP_INVALID;
}

View File

@@ -0,0 +1,24 @@
#ifndef CHARACTERISTICWRITEPROCESSORE005_H
#define CHARACTERISTICWRITEPROCESSORE005_H
#include "bluetoothdevice.h"
#include "characteristicnotifier2ad9.h"
#include "characteristicwriteprocessor.h"
class CharacteristicWriteProcessorE005 : public CharacteristicWriteProcessor {
Q_OBJECT
public:
explicit CharacteristicWriteProcessorE005(double bikeResistanceGain, uint8_t bikeResistanceOffset,
bluetoothdevice *bike, // CharacteristicNotifier2AD9 *notifier,
QObject *parent = nullptr);
virtual int writeProcess(quint16 uuid, const QByteArray &data, QByteArray &out);
private:
double weight, rrc, wrc;
signals:
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
};
#endif // CHARACTERISTICWRITEPROCESSORE005_H

View File

@@ -129,7 +129,7 @@ void chronobike::characteristicChanged(const QLowEnergyCharacteristic &character
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
@@ -138,23 +138,26 @@ void chronobike::characteristicChanged(const QLowEnergyCharacteristic &character
if (newValue.length() != 19)
return;
if (settings.value(QStringLiteral("power_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = (uint16_t)((uint8_t)newValue.at(17)) + ((uint16_t)((uint8_t)newValue.at(18)) << 8);
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((uint8_t)newValue.at(8)) / 2;
}
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = ((double)((uint16_t)((uint8_t)newValue.at(6)) + ((uint16_t)((uint8_t)newValue.at(7)) << 8))) / 100.0;
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(
watts(), Inclination.value(), Speed.value(),
fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in kg
@@ -176,8 +179,8 @@ void chronobike::characteristicChanged(const QLowEnergyCharacteristic &character
(cr - (m_watt.value() * 132.0 / (ac * pow(Cadence.value(), 2.0) + bc * Cadence.value() + cc)))) -
br) /
(2.0 * ar)) *
settings.value(QStringLiteral("peloton_gain"), 1.0).toDouble()) +
settings.value(QStringLiteral("peloton_offset"), 0.0).toDouble();
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
Resistance = m_pelotonResistance;
emit resistanceRead(Resistance.value());
@@ -189,7 +192,7 @@ void chronobike::characteristicChanged(const QLowEnergyCharacteristic &character
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -210,8 +213,9 @@ void chronobike::characteristicChanged(const QLowEnergyCharacteristic &character
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", false).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -268,11 +272,14 @@ void chronobike::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", false).toBool();
bool cadence =
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();

View File

@@ -131,7 +131,7 @@ void concept2skierg::characteristicChanged(const QLowEnergyCharacteristic &chara
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
qDebug() << QStringLiteral(" << ") << characteristic.uuid() << " " << newValue.toHex(' ');
@@ -180,7 +180,7 @@ void concept2skierg::characteristicChanged(const QLowEnergyCharacteristic &chara
uint8_t heart_rate = newValue.at(7);
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -248,8 +248,8 @@ void concept2skierg::characteristicChanged(const QLowEnergyCharacteristic &chara
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
@@ -365,11 +365,11 @@ void concept2skierg::stateChanged(QLowEnergyService::ServiceState state) {
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";

View File

@@ -58,11 +58,11 @@ data_len));
void cscbike::update() {
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
if (!noVirtualDevice) {
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool()) {
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
Heart = (uint8_t)KeepAwakeHelper::heart();
debug("Current Heart: " + QString::number(Heart.value()));
}
@@ -82,16 +82,16 @@ void cscbike::update() {
}
if (Heart.value() > 0) {
int avgP = ((settings.value(QStringLiteral("power_hr_pwr1"), 200).toDouble() *
settings.value(QStringLiteral("power_hr_hr2"), 170).toDouble()) -
(settings.value(QStringLiteral("power_hr_pwr2"), 230).toDouble() *
settings.value(QStringLiteral("power_hr_hr1"), 150).toDouble())) /
(settings.value(QStringLiteral("power_hr_hr2"), 170).toDouble() -
settings.value(QStringLiteral("power_hr_hr1"), 150).toDouble()) +
(Heart.value() * ((settings.value(QStringLiteral("power_hr_pwr1"), 200).toDouble() -
settings.value(QStringLiteral("power_hr_pwr2"), 230).toDouble()) /
(settings.value(QStringLiteral("power_hr_hr1"), 150).toDouble() -
settings.value(QStringLiteral("power_hr_hr2"), 170).toDouble())));
int avgP = ((settings.value(QZSettings::power_hr_pwr1, QZSettings::default_power_hr_pwr1).toDouble() *
settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble()) -
(settings.value(QZSettings::power_hr_pwr2, QZSettings::default_power_hr_pwr2).toDouble() *
settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble())) /
(settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble() -
settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble()) +
(Heart.value() * ((settings.value(QZSettings::power_hr_pwr1, QZSettings::default_power_hr_pwr1).toDouble() -
settings.value(QZSettings::power_hr_pwr2, QZSettings::default_power_hr_pwr2).toDouble()) /
(settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble() -
settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble())));
if (avgP < 50) {
avgP = 50;
}
@@ -158,38 +158,67 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
Q_UNUSED(characteristic);
QSettings settings;
// QString heartRateBeltName = //unused QString
// settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
// settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
uint16_t _LastCrankEventTime = 0;
double _CrankRevs = 0;
uint16_t _LastWheelEventTime = 0;
double _WheelRevs = 0;
uint8_t battery = 0;
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
if (characteristic.uuid() == QBluetoothUuid((quint16)0x2A19)) {
battery = newValue.at(0);
qDebug() << QStringLiteral("battery: ") << battery;
return;
}
if (characteristic.uuid() != QBluetoothUuid((quint16)0x2A5B)) {
return;
}
lastPacket = newValue;
bool CrankPresent = (newValue.at(0) & 0x02) == 0x02;
bool WheelPresent = (newValue.at(0) & 0x01) == 0x01;
qDebug() << QStringLiteral("CrankPresent: ") << CrankPresent;
qDebug() << QStringLiteral("WheelPresent: ") << WheelPresent;
uint8_t index = 1;
if (newValue.at(0) == 0x02) {
CrankRevs = (((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
} else {
CrankRevs =
if (WheelPresent) {
_WheelRevs =
(((uint32_t)((uint8_t)newValue.at(index + 3)) << 24) | ((uint32_t)((uint8_t)newValue.at(index + 2)) << 16) |
((uint32_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint32_t)((uint8_t)newValue.at(index)));
}
if (newValue.at(0) == 0x01) {
emit debug(QStringLiteral("Current Wheel Revs: ") + QString::number(_WheelRevs));
index += 4;
} else {
_LastWheelEventTime =
(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
emit debug(QStringLiteral("Current Wheel Event Time: ") + QString::number(_LastWheelEventTime));
index += 2;
}
LastCrankEventTime = (((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
if (CrankPresent) {
_CrankRevs = (((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
emit debug(QStringLiteral("Current Crank Revs: ") + QString::number(_CrankRevs));
index += 2;
_LastCrankEventTime =
(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)));
emit debug(QStringLiteral("Current Crank Event Time: ") + QString::number(_LastCrankEventTime));
}
if ((!CrankPresent || _CrankRevs == 0) && WheelPresent) {
CrankRevs = _WheelRevs;
LastCrankEventTime = _LastWheelEventTime;
} else {
CrankRevs = _CrankRevs;
LastCrankEventTime = _LastCrankEventTime;
}
int16_t deltaT = LastCrankEventTime - oldLastCrankEventTime;
if (deltaT < 0) {
if (newValue.at(0) == 0x01) {
deltaT = LastCrankEventTime + 1024 - oldLastCrankEventTime;
} else {
deltaT = LastCrankEventTime + 65535 - oldLastCrankEventTime;
}
deltaT = LastCrankEventTime + 65535 - oldLastCrankEventTime;
}
if (CrankRevs != oldCrankRevs && deltaT) {
@@ -206,10 +235,10 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
oldLastCrankEventTime = LastCrankEventTime;
oldCrankRevs = CrankRevs;
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
Speed = Cadence.value() * settings.value(QStringLiteral("cadence_sensor_speed_ratio"), 0.33).toDouble();
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = Cadence.value() * settings.value(QZSettings::cadence_sensor_speed_ratio, QZSettings::default_cadence_sensor_speed_ratio).toDouble();
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
@@ -232,8 +261,8 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
(ac * pow(Cadence.value(), 2.0) + bc * Cadence.value() + cc)))) -
br) /
(2.0 * ar)) *
settings.value(QStringLiteral("peloton_gain"), 1.0).toDouble()) +
settings.value(QStringLiteral("peloton_offset"), 0.0).toDouble();
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
Resistance = m_pelotonResistance;
} else {
m_pelotonResistance = 0;
@@ -243,7 +272,7 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts()) + 1.19) * settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in kg
@@ -260,8 +289,8 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
if (!noVirtualDevice) {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -360,11 +389,11 @@ void cscbike::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();

View File

@@ -14,13 +14,15 @@
OP(HEART_RATE, 0x180D, WAHOO_BLUEHR, P1, P2, P3)
#define DM_MACHINE_OP(OP, P1, P2, P3) \
OP(WAHOO_KICKR, "Wahoo KICKR $uuid_hex$", DM_MACHINE_TYPE_BIKE, P1, P2, P3) \
OP(WAHOO_KICKR, "Wahoo KICKR $uuid_hex$", DM_MACHINE_TYPE_TREADMILL | DM_MACHINE_TYPE_BIKE, P1, P2, P3) \
OP(WAHOO_BLUEHR, "Wahoo HRM", DM_MACHINE_TYPE_BIKE | DM_MACHINE_TYPE_TREADMILL, P1, P2, P3) \
OP(WAHOO_RPM_SPEED, "Wahoo SPEED $uuid_hex$", DM_MACHINE_TYPE_BIKE, P1, P2, P3) \
OP(WAHOO_TREADMILL, "Wahoo TREAD $uuid_hex$", DM_MACHINE_TYPE_TREADMILL, P1, P2, P3)
#define DP_PROCESS_WRITE_2AD9() writeP2AD9
#define DP_PROCESS_WRITE_2AD9T() writeP2AD9
#define DP_PROCESS_WRITE_E005() writePE005
#define DP_PROCESS_WRITE_E005T() writePE005
#define DP_PROCESS_WRITE_2A55() 0
#define DP_PROCESS_WRITE_2A55T() 0
#define DP_PROCESS_WRITE_NULL() 0
@@ -33,6 +35,7 @@
OP(FITNESS_MACHINE_CYCLE, 0x2AD6, DPKT_CHAR_PROP_FLAG_READ, DM_BT("\x0A\x00\x96\x00\x0A\x00"), \
DP_PROCESS_WRITE_NULL, P1, P2, P3) \
OP(FITNESS_MACHINE_CYCLE, 0x2AD9, DPKT_CHAR_PROP_FLAG_WRITE, DM_BT("\x00"), DP_PROCESS_WRITE_2AD9, P1, P2, P3) \
OP(FITNESS_MACHINE_CYCLE, 0xE005, DPKT_CHAR_PROP_FLAG_WRITE, DM_BT("\x00"), DP_PROCESS_WRITE_E005, P1, P2, P3) \
OP(FITNESS_MACHINE_CYCLE, 0x2AD2, DPKT_CHAR_PROP_FLAG_NOTIFY, DM_BT("\x00"), DP_PROCESS_WRITE_NULL, P1, P2, P3) \
OP(FITNESS_MACHINE_CYCLE, 0x2AD3, DPKT_CHAR_PROP_FLAG_READ, DM_BT("\x00\x01"), DP_PROCESS_WRITE_NULL, P1, P2, P3) \
OP(FITNESS_MACHINE_TREADMILL, 0x2ACC, DPKT_CHAR_PROP_FLAG_READ, DM_BT("\x08\x14\x00\x00\x00\x00\x00\x00"), \
@@ -101,9 +104,10 @@ enum { DM_SERV_OP(DM_SERV_ENUMI_OP, 0, 0, 0) DM_SERV_I_NUM };
} \
if (P2.size()) { \
DirconProcessor *processor = new DirconProcessor( \
P2, QString(QStringLiteral(NAME)) \
.replace(QStringLiteral("$uuid_hex$"), \
QString(QStringLiteral("%1")).arg(DM_MACHINE_##DESC, 4, 10, QLatin1Char('0'))), \
P2, \
QString(QStringLiteral(NAME)) \
.replace(QStringLiteral("$uuid_hex$"), \
QString(QStringLiteral("%1")).arg(DM_MACHINE_##DESC, 4, 10, QLatin1Char('0'))), \
server_base_port + DM_MACHINE_##DESC, QString(QStringLiteral("%1")).arg(DM_MACHINE_##DESC), mac, \
this); \
QString servdesc; \
@@ -146,14 +150,19 @@ DirconManager::DirconManager(bluetoothdevice *Bike, uint8_t bikeResistanceOffset
uint8_t type = dt == bluetoothdevice::TREADMILL || dt == bluetoothdevice::ELLIPTICAL ? DM_MACHINE_TYPE_TREADMILL
: DM_MACHINE_TYPE_BIKE;
qDebug() << "Building Dircom Manager";
uint16_t server_base_port = settings.value(QStringLiteral("dircon_server_base_port"), 4810).toUInt();
bool bike_wheel_revs = settings.value(QStringLiteral("bike_wheel_revs"), false).toBool();
uint16_t server_base_port =
settings.value(QZSettings::dircon_server_base_port, QZSettings::default_dircon_server_base_port).toUInt();
bool bike_wheel_revs = settings.value(QZSettings::bike_wheel_revs, QZSettings::default_bike_wheel_revs).toBool();
DM_CHAR_NOTIF_OP(DM_CHAR_NOTIF_BUILD_OP, Bike, 0, 0)
writeP2AD9 = new CharacteristicWriteProcessor2AD9(bikeResistanceGain, bikeResistanceOffset, Bike, notif2AD9, this);
writePE005 = new CharacteristicWriteProcessorE005(bikeResistanceGain, bikeResistanceOffset, Bike, this);
DM_CHAR_OP(DM_CHAR_INIT_OP, services, service, 0)
connect(writeP2AD9, SIGNAL(changeInclination(double, double)), this, SIGNAL(changeInclination(double, double)));
connect(writeP2AD9, SIGNAL(ftmsCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)), this,
SIGNAL(ftmsCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)));
connect(writePE005, SIGNAL(changeInclination(double, double)), this, SIGNAL(changeInclination(double, double)));
connect(writePE005, SIGNAL(ftmsCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)), this,
SIGNAL(ftmsCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)));
QObject::connect(&bikeTimer, &QTimer::timeout, this, &DirconManager::bikeProvider);
QString mac = getMacAddress();
DM_MACHINE_OP(DM_MACHINE_INIT_OP, services, proc_services, type)

View File

@@ -6,18 +6,21 @@
#include "characteristicnotifier2a53.h"
#include "characteristicnotifier2a5b.h"
#include "characteristicnotifier2a63.h"
#include "characteristicnotifier2acd.h"
#include "characteristicnotifier2acc.h"
#include "characteristicnotifier2acd.h"
#include "characteristicnotifier2ad2.h"
#include "characteristicnotifier2ad9.h"
#include "characteristicwriteprocessor2ad9.h"
#include "characteristicwriteprocessore005.h"
#include "dirconpacket.h"
#include "dirconprocessor.h"
#include <QObject>
#define DM_CHAR_NOTIF_OP(OP, P1, P2, P3) \
OP(2AD2, P1, P2, P3) \
OP(2A63, P1, P2, P3) OP(2A37, P1, P2, P3) OP(2A5B, P1, P2, P3) OP(2A53, P1, P2, P3) OP(2ACD, P1, P2, P3) OP(2ACC, P1, P2, P3) OP(2AD9, P1, P2, P3)
OP(2A63, P1, P2, P3) \
OP(2A37, P1, P2, P3) OP(2A5B, P1, P2, P3) OP(2A53, P1, P2, P3) OP(2ACD, P1, P2, P3) OP(2ACC, P1, P2, P3) \
OP(2AD9, P1, P2, P3)
#define DM_CHAR_NOTIF_DEFINE_OP(UUID, P1, P2, P3) CharacteristicNotifier##UUID *notif##UUID = 0;
@@ -25,6 +28,7 @@ class DirconManager : public QObject {
Q_OBJECT
QTimer bikeTimer;
CharacteristicWriteProcessor2AD9 *writeP2AD9 = 0;
CharacteristicWriteProcessorE005 *writePE005 = 0;
DM_CHAR_NOTIF_OP(DM_CHAR_NOTIF_DEFINE_OP, 0, 0, 0)
QList<DirconProcessor *> processors;
static QString getMacAddress();

View File

@@ -1,5 +1,8 @@
#include "dirconprocessor.h"
#include "dirconpacket.h"
#include "qzsettings.h"
#include <QSettings>
#include <QHostInfo>
DirconProcessor::DirconProcessor(const QList<DirconProcessorService *> &my_services, const QString &serv_name,
quint16 serv_port, const QString &serv_sn, const QString &my_mac, QObject *parent)
@@ -198,6 +201,7 @@ bool DirconProcessor::sendCharacteristicNotification(quint16 uuid, const QByteAr
DirconPacket pkt;
QTcpSocket *socket;
DirconProcessorClient *client;
QSettings settings;
bool rv = true, rvs;
pkt.additional_data = data;
pkt.Identifier = DPKT_MSGID_UNSOLICITED_CHARACTERISTIC_NOTIFICATION;
@@ -205,7 +209,7 @@ bool DirconProcessor::sendCharacteristicNotification(quint16 uuid, const QByteAr
pkt.uuid = uuid;
for (QHash<QTcpSocket *, DirconProcessorClient *>::iterator i = clientsMap.begin(); i != clientsMap.end(); ++i) {
client = i.value();
/*if (client->char_notify.indexOf(uuid) >= 0)*/ {
if (client->char_notify.indexOf(uuid) >= 0 || !settings.value(QZSettings::wahoo_rgt_dircon, QZSettings::default_wahoo_rgt_dircon).toBool()) {
socket = i.key();
rvs = socket->write(pkt.encode(0)) < 0;
if (rvs)

View File

@@ -18,7 +18,6 @@ class DirconProcessorCharacteristic : public QObject {
DirconProcessorCharacteristic(quint16 my_uuid, quint8 my_type, const QByteArray &data,
CharacteristicWriteProcessor *w, QObject *parent = nullptr)
: QObject(parent), read_values(data), uuid(my_uuid), type(my_type), writeP(w) {}
DirconProcessorCharacteristic(const DirconProcessorCharacteristic &cp) { this->operator=(cp); }
~DirconProcessorCharacteristic() {}
DirconProcessorCharacteristic &operator=(const DirconProcessorCharacteristic &cp) {
read_values = cp.read_values;

View File

@@ -74,8 +74,8 @@ void domyosbike::updateDisplay(uint16_t elapsed) {
uint16_t multiplier = 1;
QSettings settings;
bool distance = settings.value(QStringLiteral("domyos_treadmill_distance_display"), true).toBool();
bool domyos_bike_display_calories = settings.value(QStringLiteral("domyos_bike_display_calories"), true).toBool();
bool distance = settings.value(QZSettings::domyos_treadmill_distance_display, QZSettings::default_domyos_treadmill_distance_display).toBool();
bool domyos_bike_display_calories = settings.value(QZSettings::domyos_bike_display_calories, QZSettings::default_domyos_bike_display_calories).toBool();
if (domyos_bike_display_calories) {
multiplier = 10;
@@ -101,7 +101,7 @@ void domyosbike::updateDisplay(uint16_t elapsed) {
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00};
display[3] = (elapsed / 60) & 0xFF; // high byte for elapsed time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elasped time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elapsed time (in seconds)
if (currentSpeed().value() < 10.0) {
@@ -190,11 +190,11 @@ void domyosbike::update() {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();
@@ -278,15 +278,15 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
QByteArray value = newValue;
qDebug() << QStringLiteral(" << ") + QString::number(value.length()) + QStringLiteral(" ") + value.toHex(' ');
// for the init packets, the lenght is always less than 20
// for the display and status packets, the lenght is always grater then 20 and there are 2 cases:
// - intense run: it always send more than 20 bytes in one packets, so the lenght will be always != 20
// - t900: it splits packets with lenght grater than 20 in two distinct packets, so the first one it has lenght of
// for the init packets, the length is always less than 20
// for the display and status packets, the length is always grater then 20 and there are 2 cases:
// - intense run: it always send more than 20 bytes in one packets, so the length will be always != 20
// - t900: it splits packets with length grater than 20 in two distinct packets, so the first one it has length of
// 20,
// and the second one with the remained byte
// so this simply condition will match all the cases, excluding the 20byte packet of the T900.
@@ -307,7 +307,7 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
startBytes3.append(0xf0);
startBytes3.append(0xdd);
// on some treadmills, the 26bytes has splitted in 2 packets
// on some treadmills, the 26bytes has split in 2 packets
if ((lastPacket.length() == 20 && lastPacket.startsWith(startBytes) && value.length() == 6) ||
(lastPacket.length() == 20 && lastPacket.startsWith(startBytes2) && value.length() == 7) ||
(lastPacket.length() == 20 && lastPacket.startsWith(startBytes3) && value.length() == 7)) {
@@ -347,8 +347,8 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
double distance = GetDistanceFromPacket(value);
double ucadence = ((uint8_t)value.at(9));
double cadenceFilter = settings.value(QStringLiteral("domyos_bike_cadence_filter"), 0).toDouble();
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
double cadenceFilter = settings.value(QZSettings::domyos_bike_cadence_filter, QZSettings::default_domyos_bike_cadence_filter).toDouble();
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
if (cadenceFilter == 0 || cadenceFilter > ucadence) {
@@ -367,10 +367,10 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
emit resistanceRead(Resistance.value());
m_pelotonResistance = (Resistance.value() * 100) / max_resistance;
bool disable_hr_frommachinery = settings.value(QStringLiteral("heart_ignore_builtin"), false).toBool();
bool disable_hr_frommachinery = settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -401,8 +401,8 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -424,10 +424,10 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
qDebug() << "QLowEnergyController ERROR!!" << m_control->errorString();
}
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = speed;
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
KCal = kcal;
Distance = distance;

View File

@@ -87,7 +87,7 @@ void domyoselliptical::updateDisplay(uint16_t elapsed) {
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00};
display[3] = (elapsed / 60) & 0xFF; // high byte for elapsed time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elasped time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elapsed time (in seconds)
display[7] = ((uint8_t)((uint16_t)(currentSpeed().value()) >> 8)) & 0xFF;
display[8] = (uint8_t)(currentSpeed().value()) & 0xFF;
@@ -171,8 +171,8 @@ void domyoselliptical::update() {
// ******************************************* virtual bike init *************************************
QSettings settings;
if (!firstVirtual && searchStopped && !virtualTreadmill && !virtualBike) {
bool virtual_device_enabled = settings.value("virtual_device_enabled", true).toBool();
bool virtual_device_force_bike = settings.value("virtual_device_force_bike", false).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike = settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike).toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
@@ -204,7 +204,7 @@ void domyoselliptical::update() {
if (requestResistance != -1) {
if (requestResistance > 15) {
requestResistance = 15;
} else if (requestResistance == 0) {
} else if (requestResistance <= 0) {
requestResistance = 1;
}
@@ -264,7 +264,7 @@ void domyoselliptical::characteristicChanged(const QLowEnergyCharacteristic &cha
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
@@ -287,12 +287,12 @@ void domyoselliptical::characteristicChanged(const QLowEnergyCharacteristic &cha
inclination and speed status return;*/
double speed =
GetSpeedFromPacket(newValue) * settings.value(QStringLiteral("domyos_elliptical_speed_ratio"), 1.0).toDouble();
GetSpeedFromPacket(newValue) * settings.value(QZSettings::domyos_elliptical_speed_ratio, QZSettings::default_domyos_elliptical_speed_ratio).toDouble();
double kcal = GetKcalFromPacket(newValue);
double distance = GetDistanceFromPacket(newValue) *
settings.value(QStringLiteral("domyos_elliptical_speed_ratio"), 1.0).toDouble();
settings.value(QZSettings::domyos_elliptical_speed_ratio, QZSettings::default_domyos_elliptical_speed_ratio).toDouble();
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((uint8_t)newValue.at(9));
@@ -311,7 +311,7 @@ void domyoselliptical::characteristicChanged(const QLowEnergyCharacteristic &cha
}
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -582,19 +582,29 @@ uint16_t domyoselliptical::watts() {
// calc Watts ref. https://alancouzens.com/blog/Run_Power.html
uint16_t watts = 0;
double weight = settings.value(QStringLiteral("weight"), 75.0).toFloat();
double weight = settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
if (currentSpeed().value() > 0) {
double pace = 60 / currentSpeed().value();
double VO2R = 210.0 / pace;
double VO2A = (VO2R * weight) / 1000.0;
double hwatts = 75 * VO2A;
double vwatts = ((9.8 * weight) * (currentInclination().value() / 100.0));
double inc_res_ratio;
if(settings.value(QZSettings::domyos_elliptical_inclination, QZSettings::default_domyos_elliptical_inclination).toBool())
inc_res_ratio = currentInclination().value() / 100.0;
else
inc_res_ratio = currentResistance().value() / 100.0;
double vwatts = ((9.8 * weight) * (inc_res_ratio));
watts = hwatts + vwatts;
}
return watts;
}
bool domyoselliptical::inclinationAvailableByHardware() {
QSettings settings;
return settings.value(QZSettings::domyos_elliptical_inclination, QZSettings::default_domyos_elliptical_inclination).toBool();
}
void domyoselliptical::controllerStateChanged(QLowEnergyController::ControllerState state) {
qDebug() << QStringLiteral("controllerStateChanged") << state;
if (state == QLowEnergyController::UnconnectedState && m_control) {

View File

@@ -37,6 +37,7 @@ class domyoselliptical : public elliptical {
uint8_t bikeResistanceOffset = 4, double bikeResistanceGain = 1.0);
~domyoselliptical();
bool connected();
bool inclinationAvailableByHardware();
void *VirtualTreadmill();
void *VirtualDevice();

View File

@@ -87,7 +87,7 @@ void domyosrower::updateDisplay(uint16_t elapsed) {
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00};
display[3] = (elapsed / 60) & 0xFF; // high byte for elapsed time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elasped time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elapsed time (in seconds)
display[7] = ((uint8_t)((uint16_t)(currentSpeed().value()) >> 8)) & 0xFF;
display[8] = (uint8_t)(currentSpeed().value()) & 0xFF;
@@ -171,8 +171,8 @@ void domyosrower::update() {
// ******************************************* virtual bike init *************************************
QSettings settings;
if (!firstVirtual && searchStopped && !virtualTreadmill && !virtualBike) {
bool virtual_device_enabled = settings.value("virtual_device_enabled", true).toBool();
bool virtual_device_force_bike = settings.value("virtual_device_force_bike", false).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike = settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike).toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
@@ -263,7 +263,7 @@ void domyosrower::characteristicChanged(const QLowEnergyCharacteristic &characte
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
@@ -286,12 +286,12 @@ void domyosrower::characteristicChanged(const QLowEnergyCharacteristic &characte
inclination and speed status return;*/
double speed =
GetSpeedFromPacket(newValue) * settings.value(QStringLiteral("domyos_elliptical_speed_ratio"), 1.0).toDouble();
GetSpeedFromPacket(newValue) * settings.value(QZSettings::domyos_elliptical_speed_ratio, QZSettings::default_domyos_elliptical_speed_ratio).toDouble();
double kcal = GetKcalFromPacket(newValue);
double distance = GetDistanceFromPacket(newValue) *
settings.value(QStringLiteral("domyos_elliptical_speed_ratio"), 1.0).toDouble();
settings.value(QZSettings::domyos_elliptical_speed_ratio, QZSettings::default_domyos_elliptical_speed_ratio).toDouble();
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((uint8_t)newValue.at(9));
@@ -310,7 +310,7 @@ void domyosrower::characteristicChanged(const QLowEnergyCharacteristic &characte
}
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -579,7 +579,7 @@ uint16_t domyosrower::watts() {
// calc Watts ref. https://alancouzens.com/blog/Run_Power.html
uint16_t watts = 0;
double weight = settings.value(QStringLiteral("weight"), 75.0).toFloat();
double weight = settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
if (currentSpeed().value() > 0) {
double pace = 60 / currentSpeed().value();

View File

@@ -47,8 +47,15 @@ QBluetoothUuid _gattCommunicationChannelServiceId(QStringLiteral("49535343-fe7d-
QBluetoothUuid _gattWriteCharacteristicId(QStringLiteral("49535343-8841-43f4-a8d4-ecbe34729bb3"));
QBluetoothUuid _gattNotifyCharacteristicId(QStringLiteral("49535343-1e4d-4bd9-ba61-23c647249616"));
#ifdef Q_OS_IOS
extern quint8 QZ_EnableDiscoveryCharsAndDescripttors;
#endif
domyostreadmill::domyostreadmill(uint32_t pollDeviceTime, bool noConsole, bool noHeartService, double forceInitSpeed,
double forceInitInclination) {
#ifdef Q_OS_IOS
QZ_EnableDiscoveryCharsAndDescripttors = true;
#endif
m_watt.setType(metric::METRIC_WATT);
Speed.setType(metric::METRIC_SPEED);
this->noConsole = noConsole;
@@ -83,23 +90,28 @@ void domyostreadmill::writeCharacteristic(uint8_t *data, uint8_t data_len, const
if (gattCommunicationChannelService->state() != QLowEnergyService::ServiceState::ServiceDiscovered ||
m_control->state() == QLowEnergyController::UnconnectedState) {
emit debug(QStringLiteral("writeCharacteristic error because the connection is closed"));
qDebug() << QStringLiteral("writeCharacteristic error because the connection is closed");
return;
}
if (!gattWriteCharacteristic.isValid()) {
qDebug() << QStringLiteral("gattWriteCharacteristic is invalid");
return;
}
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic,
QByteArray((const char *)data, data_len));
if (!disable_log) {
emit debug(QStringLiteral(" >> ") + QByteArray((const char *)data, data_len).toHex(' ') +
QStringLiteral(" // ") + info);
qDebug() << QStringLiteral(" >> ") + QByteArray((const char *)data, data_len).toHex(' ') <<
QStringLiteral(" // ") + info;
}
loop.exec();
if (timeout.isActive() == false) {
emit debug(QStringLiteral(" exit for timeout"));
qDebug() << QStringLiteral(" exit for timeout");
}
}
@@ -108,17 +120,17 @@ void domyostreadmill::updateDisplay(uint16_t elapsed) {
0x01, 0x00, 0x05, 0x01, 0x01, 0x00, 0x0c, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00};
QSettings settings;
bool distance = settings.value(QStringLiteral("domyos_treadmill_distance_display"), true).toBool();
bool domyos_treadmill_display_invert = settings.value(QStringLiteral("domyos_treadmill_display_invert"), false).toBool();
bool distance = settings.value(QZSettings::domyos_treadmill_distance_display, QZSettings::default_domyos_treadmill_distance_display).toBool();
bool domyos_treadmill_display_invert = settings.value(QZSettings::domyos_treadmill_display_invert, QZSettings::default_domyos_treadmill_display_invert).toBool();
if (elapsed > 5999) // 99:59
{
display[3] = ((elapsed / 60) / 60) & 0xFF; // high byte for elapsed time (in seconds)
display[4] = ((elapsed / 60) % 60) & 0xFF; // low byte for elasped time (in seconds)
display[4] = ((elapsed / 60) % 60) & 0xFF; // low byte for elapsed time (in seconds)
} else {
display[3] = (elapsed / 60) & 0xFF; // high byte for elapsed time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elasped time (in seconds)
display[4] = (elapsed % 60 & 0xFF); // low byte for elapsed time (in seconds)
}
if (distance) {
@@ -255,8 +267,8 @@ void domyostreadmill::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && searchStopped && !virtualTreadMill && !virtualBike) {
bool virtual_device_enabled = settings.value("virtual_device_enabled", true).toBool();
bool virtual_device_force_bike = settings.value("virtual_device_force_bike", false).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike = settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike).toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
@@ -277,7 +289,7 @@ void domyostreadmill::update() {
// debug("Domyos Treadmill RSSI " + QString::number(bluetoothDevice.rssi()));
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
// updating the treadmill console every second
if (sec1Update++ >= (1000 / refresh->interval())) {
@@ -302,7 +314,7 @@ void domyostreadmill::update() {
double inc = Inclination.value();
if (requestInclination != -100) {
// only 0.5 steps ara avaiable
// only 0.5 steps ara available
requestInclination = qRound(requestInclination * 2.0) / 2.0;
inc = requestInclination;
requestInclination = -100;
@@ -314,7 +326,7 @@ void domyostreadmill::update() {
if (requestInclination != -100) {
if(requestInclination < 0)
requestInclination = 0;
// only 0.5 steps ara avaiable
// only 0.5 steps ara available
requestInclination = qRound(requestInclination * 2.0) / 2.0;
if (requestInclination != currentInclination().value() && requestInclination >= 0 &&
requestInclination <= 15) {
@@ -376,17 +388,17 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
bool domyos_treadmill_buttons = settings.value(QStringLiteral("domyos_treadmill_buttons"), false).toBool();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
bool domyos_treadmill_buttons = settings.value(QZSettings::domyos_treadmill_buttons, QZSettings::default_domyos_treadmill_buttons).toBool();
Q_UNUSED(characteristic);
QByteArray value = newValue;
emit debug(QStringLiteral(" << ") + QString::number(value.length()) + QStringLiteral(" ") + value.toHex(' '));
// for the init packets, the lenght is always less than 20
// for the display and status packets, the lenght is always grater then 20 and there are 2 cases:
// - intense run: it always send more than 20 bytes in one packets, so the lenght will be always != 20
// - t900: it splits packets with lenght grater than 20 in two distinct packets, so the first one it has lenght of
// for the init packets, the length is always less than 20
// for the display and status packets, the length is always grater then 20 and there are 2 cases:
// - intense run: it always send more than 20 bytes in one packets, so the length will be always != 20
// - t900: it splits packets with length grater than 20 in two distinct packets, so the first one it has length of
// 20,
// and the second one with the remained byte
// so this simply condition will match all the cases, excluding the 20byte packet of the T900.
@@ -404,7 +416,7 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
startBytes2.append(0xf0);
startBytes2.append(0xdb);
// on some treadmills, the 26bytes has splitted in 2 packets
// on some treadmills, the 26bytes has split in 2 packets
if ((lastPacket.length() == 20 && lastPacket.startsWith(startBytes) && value.length() == 6) ||
(lastPacket.length() == 20 && lastPacket.startsWith(startBytes2) && value.length() == 7)) {
@@ -526,10 +538,10 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
double incline = GetInclinationFromPacket(value);
double kcal = GetKcalFromPacket(value);
double distance = GetDistanceFromPacket(value);
bool disable_hr_frommachinery = settings.value(QStringLiteral("heart_ignore_builtin"), false).toBool();
bool disable_hr_frommachinery = settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -557,7 +569,7 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
if (settings.value(QStringLiteral("power_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
{
@@ -571,10 +583,10 @@ void domyostreadmill::characteristicChanged(const QLowEnergyCharacteristic &char
FanSpeed = value.at(23);
if (!firstCharacteristicChanged) {
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in

View File

@@ -189,7 +189,8 @@ double echelonconnectsport::bikeResistanceToPeloton(double resistance) {
if (p < 0) {
p = 0;
}
return (p * settings.value(QStringLiteral("peloton_gain"), 1.0).toDouble()) + settings.value(QStringLiteral("peloton_offset"), 0.0).toDouble();
return (p * settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
}
void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &characteristic,
@@ -198,7 +199,7 @@ void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
qDebug() << " << " + newValue.toHex(' ');
@@ -223,19 +224,22 @@ void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &
double distance = GetDistanceFromPacket(newValue);
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((uint8_t)newValue.at(10));
}
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = 0.37497622 * ((double)Cadence.value());
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(
watts(), Inclination.value(), Speed.value(),
fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in kg
@@ -251,7 +255,7 @@ void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
#ifdef Q_OS_ANDROID
if (settings.value(QStringLiteral("ant_heart"), false).toBool()) {
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
Heart = (uint8_t)KeepAwakeHelper::heart();
} else
#endif
@@ -272,8 +276,9 @@ void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -380,11 +385,14 @@ void echelonconnectsport::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence =
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();
@@ -595,7 +603,9 @@ uint16_t echelonconnectsport::wattsFromResistance(double resistance) {
}
double *watts_of_level;
QSettings settings;
if (!settings.value("echelon_watttable", "Echelon").toString().compare("mgarcea"))
if (!settings.value(QZSettings::echelon_watttable, QZSettings::default_echelon_watttable)
.toString()
.compare("mgarcea"))
watts_of_level = wattTable_mgarcea[level];
else
watts_of_level = wattTable[level];

View File

@@ -191,7 +191,7 @@ void echelonrower::characteristicChanged(const QLowEnergyCharacteristic &charact
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
qDebug() << QStringLiteral(" << ") + newvalue.toHex(' ');
@@ -221,7 +221,7 @@ void echelonrower::characteristicChanged(const QLowEnergyCharacteristic &charact
// double distance = GetDistanceFromPacket(newValue);
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((uint8_t)lastPacket.at(11));
@@ -234,7 +234,7 @@ void echelonrower::characteristicChanged(const QLowEnergyCharacteristic &charact
Cadence.value(); // this is just to fill the tile, but it's quite useless since the machinery doesn't report it
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts()) + 1.19) * settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in kg
@@ -250,7 +250,7 @@ void echelonrower::characteristicChanged(const QLowEnergyCharacteristic &charact
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
#ifdef Q_OS_ANDROID
if (settings.value(QStringLiteral("ant_heart"), false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -271,9 +271,9 @@ void echelonrower::characteristicChanged(const QLowEnergyCharacteristic &charact
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool virtual_device_rower = settings.value("virtual_device_rower", false).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
bool virtual_device_rower = settings.value(QZSettings::virtual_device_rower, QZSettings::default_virtual_device_rower).toBool();
if (ios_peloton_workaround && cadence && !virtual_device_rower && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -375,12 +375,12 @@ void echelonrower::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_rower = settings.value("virtual_device_rower", false).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_rower = settings.value(QZSettings::virtual_device_rower, QZSettings::default_virtual_device_rower).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && !virtual_device_rower) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();

View File

@@ -132,8 +132,8 @@ void echelonstride::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && !virtualTreadMill && !virtualBike) {
bool virtual_device_enabled = settings.value("virtual_device_enabled", true).toBool();
bool virtual_device_force_bike = settings.value("virtual_device_force_bike", false).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike = settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike).toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
@@ -152,7 +152,7 @@ void echelonstride::update() {
}
// ********************************************************************************************************
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
// updating the treadmill console every second
if (sec1Update++ >= (2000 / refresh->interval())) {
@@ -184,6 +184,13 @@ void echelonstride::update() {
}
uint8_t initData3[] = {0xf0, 0xb0, 0x01, 0x01, 0xa2};
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("start"), false, true);
uint8_t initData4[] = {0xf0, 0xd0, 0x01, 0x00, 0xc1};
writeCharacteristic(initData4, sizeof(initData4), QStringLiteral("start"), false, false);
uint8_t initData5[] = {0xf0, 0xd0, 0x01, 0x11, 0xd2};
writeCharacteristic(initData5, sizeof(initData5), QStringLiteral("start"), false, false);
lastStart = QDateTime::currentMSecsSinceEpoch();
requestStart = -1;
emit tapeStarted();
@@ -222,7 +229,7 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
Q_UNUSED(characteristic);
QByteArray value = newValue;
@@ -232,6 +239,8 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
if (((unsigned char)newValue.at(0)) == 0xf0 && ((unsigned char)newValue.at(1)) == 0xd3) {
writeCharacteristic((uint8_t*)newValue.constData(), newValue.length(), "reply to d3", false, false);
double miles = 1.60934;
// this line on iOS sometimes gives strange overflow values
@@ -255,9 +264,13 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
qDebug() << QStringLiteral("Current Speed: ") + QString::number(Speed.value());
return;
} else if (((unsigned char)newValue.at(0)) == 0xf0 && ((unsigned char)newValue.at(1)) == 0xd2) {
writeCharacteristic((uint8_t*)newValue.constData(), newValue.length(), "reply to d2", false, false);
Inclination = (uint8_t)newValue.at(3);
qDebug() << QStringLiteral("Current Inclination: ") + QString::number(Inclination.value());
return;
} else if (((unsigned char)newValue.at(0)) == 0xf0 && ((unsigned char)newValue.at(1)) == 0xd0) {
writeCharacteristic((uint8_t*)newValue.constData(), newValue.length(), "reply to d0", false, false);
return;
}
/*if (newValue.length() != 21)
@@ -267,10 +280,10 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
return;*/
if (!firstCharacteristicChanged) {
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
@@ -281,7 +294,7 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
if((uint8_t)newValue.at(1) == 0xD1 && newValue.length() > 11)
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -309,7 +322,7 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
qDebug() << QStringLiteral("Current Heart: ") + QString::number(Heart.value());
qDebug() << QStringLiteral("Current Calculate Distance: ") + QString::number(Distance.value());
qDebug() << QStringLiteral("Current Watt: ") +
QString::number(watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
QString::number(watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
if (m_control->error() != QLowEnergyController::NoError)
qDebug() << QStringLiteral("QLowEnergyController ERROR!!") << m_control->errorString();
@@ -319,15 +332,12 @@ void echelonstride::characteristicChanged(const QLowEnergyCharacteristic &charac
}
void echelonstride::btinit() {
uint8_t initData0[] = {0xf0, 0xa4, 0x00, 0x94};
uint8_t initData1[] = {0xf0, 0xa1, 0x00, 0x91};
uint8_t initData2[] = {0xf0, 0xa3, 0x00, 0x93};
// uint8_t initData4[] = { 0xf0, 0x60, 0x00, 0x50 }; // get sleep command
// useless i guess
// writeCharacteristic(initData4, sizeof(initData4), "get sleep", false, true);
writeCharacteristic(initData0, sizeof(initData0), QStringLiteral("init"), false, true);
// in the snoof log it repeats this frame 4 times, i will have to analyze the response to understand if 4 times are
// enough
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, true);
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, true);
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, true);

View File

@@ -34,7 +34,7 @@ void eliterizer::changeInclinationRequested(double grade, double percentage) {
Q_UNUSED(grade);
uint8_t incline[] = {0x0a, 0x00, 0x00};
QSettings settings;
double gain = settings.value(QStringLiteral("elite_rizer_gain"), 1.0).toDouble();
double gain = settings.value(QZSettings::elite_rizer_gain, QZSettings::default_elite_rizer_gain).toDouble();
percentage = percentage * gain;
incline[1] = ((int16_t)(percentage * 10.0)) & 0xff;

View File

@@ -10,7 +10,7 @@ void elliptical::update_metrics(bool watt_calc, const double watts) {
double deltaTime = (((double)_lastTimeUpdate.msecsTo(current)) / ((double)1000.0));
QSettings settings;
if (!_firstUpdate && !paused) {
if (currentSpeed().value() > 0.0 || settings.value(QStringLiteral("continuous_moving"), true).toBool()) {
if (currentSpeed().value() > 0.0 || settings.value(QZSettings::continuous_moving, true).toBool()) {
elapsed += deltaTime;
}
if (currentSpeed().value() > 0.0) {
@@ -20,7 +20,7 @@ void elliptical::update_metrics(bool watt_calc, const double watts) {
}
m_jouls += (m_watt.value() * deltaTime);
WeightLoss = metric::calculateWeightLoss(KCal.value());
WattKg = m_watt.value() / settings.value(QStringLiteral("weight"), 75.0).toFloat();
WattKg = m_watt.value() / settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
} else if (m_watt.value() > 0) {
m_watt = 0;
WattKg = 0;
@@ -40,7 +40,7 @@ void elliptical::update_metrics(bool watt_calc, const double watts) {
uint16_t elliptical::watts() {
QSettings settings;
double weight = settings.value(QStringLiteral("weight"), 75.0).toFloat();
double weight = settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
// calc Watts ref. https://alancouzens.com/blog/Run_Power.html
uint16_t watts = 0;
@@ -133,3 +133,4 @@ metric elliptical::lastRequestedCadence() { return RequestedCadence; }
metric elliptical::pelotonResistance() { return m_pelotonResistance; }
metric elliptical::lastRequestedPelotonResistance() { return RequestedPelotonResistance; }
metric elliptical::lastRequestedResistance() { return RequestedResistance; }
bool elliptical::inclinationAvailableByHardware() { return true; }

View File

@@ -22,6 +22,7 @@ class elliptical : public bluetoothdevice {
virtual bool connected();
metric pelotonResistance();
virtual int pelotonToEllipticalResistance(int pelotonResistance);
virtual bool inclinationAvailableByHardware();
bluetoothdevice::BLUETOOTH_TYPE deviceType();
void clearStats();
void setPaused(bool p);

View File

@@ -60,7 +60,7 @@ void eslinkertreadmill::writeCharacteristic(uint8_t *data, uint8_t data_len, con
}
void eslinkertreadmill::updateDisplay(uint16_t elapsed) {
if (treadmill_type == RHYTHM_FUN) {
if (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE) {
// trying to force a fixed value to keep the connection on
uint8_t display[] = {0xa9, 0xa0, 0x03, 0x02, 0x23, 0x00, 0x2b};
@@ -111,7 +111,7 @@ void eslinkertreadmill::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && !virtualTreadMill) {
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
if (virtual_device_enabled) {
emit debug(QStringLiteral("creating virtual treadmill interface..."));
virtualTreadMill = new virtualtreadmill(this, noHeartService);
@@ -121,7 +121,7 @@ void eslinkertreadmill::update() {
}
// ********************************************************************************************************
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
// updating the treadmill console every second
// it seems that stops the communication
@@ -129,7 +129,7 @@ void eslinkertreadmill::update() {
updateDisplay(elapsed.value());
}
if (treadmill_type == TYPE::RHYTHM_FUN) {
if (treadmill_type == TYPE::RHYTHM_FUN || treadmill_type == TYPE::YPOO_MINI_CHANGE) { //
if (requestSpeed != -1) {
if (requestSpeed != currentSpeed().value() && requestSpeed >= 0 && requestSpeed <= 22) {
emit debug(QStringLiteral("writing speed ") + QString::number(requestSpeed));
@@ -202,7 +202,7 @@ void eslinkertreadmill::update() {
if (lastSpeed == 0.0) {
lastSpeed = 0.5;
}
if (treadmill_type == TYPE::RHYTHM_FUN)
if (treadmill_type == TYPE::RHYTHM_FUN || treadmill_type == TYPE::YPOO_MINI_CHANGE)
btinit(true);
requestSpeed = 1.0;
requestStart = -1;
@@ -226,7 +226,7 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
Q_UNUSED(characteristic);
QByteArray value = newValue;
@@ -299,7 +299,7 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
} else if (newValue.length() == 3 && newValue.at(0) == 2 && newValue.at(1) == 1) {
uint8_t heart = newValue.at(2);
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -311,17 +311,17 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
}
}
if ((newValue.length() != 17 && treadmill_type == RHYTHM_FUN))
if ((newValue.length() != 17 && (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE)))
return;
if (treadmill_type == RHYTHM_FUN) {
if (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE) {
double speed = GetSpeedFromPacket(value);
double incline = GetInclinationFromPacket(value);
double kcal = GetKcalFromPacket(value);
// double distance = GetDistanceFromPacket(value);
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -353,10 +353,10 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
}
if (!firstCharacteristicChanged) {
if (watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()))
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(settings.value(QStringLiteral("weight"), 75.0).toFloat())) + 1.19) *
settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
@@ -379,12 +379,23 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
double eslinkertreadmill::GetSpeedFromPacket(const QByteArray &packet) {
uint8_t convertedData = (uint8_t)packet.at(14);
double data = (double)convertedData / 10.0f;
if (treadmill_type == YPOO_MINI_CHANGE && data < 1.0) {
data = 0.0;
}
return data;
}
double eslinkertreadmill::GetKcalFromPacket(const QByteArray &packet) {
uint16_t convertedData = (packet.at(7) << 8) | packet.at(8);
return (double)convertedData;
double data;
if (treadmill_type == YPOO_MINI_CHANGE) {
uint16_t convertedData = (((uint8_t)packet.at(5)) << 8) | (uint8_t)packet.at(6);;
data = (double)convertedData / 100.0f;
}
else {
uint16_t convertedData = (packet.at(7) << 8) | packet.at(8);
data = (double)convertedData;
}
return data;
}
double eslinkertreadmill::GetDistanceFromPacket(const QByteArray &packet) {
@@ -394,9 +405,11 @@ double eslinkertreadmill::GetDistanceFromPacket(const QByteArray &packet) {
}
double eslinkertreadmill::GetInclinationFromPacket(const QByteArray &packet) {
uint16_t convertedData = packet.at(11);
double data = convertedData;
double data = 0.0;
if (treadmill_type != YPOO_MINI_CHANGE) {
uint16_t convertedData = packet.at(11);
data = convertedData;
}
return data;
}
@@ -418,7 +431,7 @@ void eslinkertreadmill::btinit(bool startTape) {
uint8_t initData2_CADENZA[] = {0x08, 0x01, 0x01};
if (treadmill_type == RHYTHM_FUN) {
if (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE) {
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, false);
writeCharacteristic(initData2, sizeof(initData2), QStringLiteral("init"), false, true);
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("init"), false, true);
@@ -542,9 +555,12 @@ void eslinkertreadmill::deviceDiscovered(const QBluetoothDeviceInfo &device) {
});
QSettings settings;
bool eslinker_cadenza = settings.value(QStringLiteral("eslinker_cadenza"), true).toBool();
bool eslinker_cadenza = settings.value(QZSettings::eslinker_cadenza, QZSettings::default_eslinker_cadenza).toBool();
bool eslinker_ypoo = settings.value(QZSettings::eslinker_ypoo, QZSettings::default_eslinker_ypoo).toBool();
if (eslinker_cadenza) {
treadmill_type = CADENZA_FITNESS_T45;
} else if (eslinker_ypoo) {
treadmill_type = YPOO_MINI_CHANGE;
} else
treadmill_type = RHYTHM_FUN;

View File

@@ -68,6 +68,7 @@ class eslinkertreadmill : public treadmill {
typedef enum TYPE {
RHYTHM_FUN = 0,
CADENZA_FITNESS_T45 = 1, // it has the same protocol of RHYTHM_FUN but without the header and the footer
YPOO_MINI_CHANGE = 2, // Similar to RHYTHM_FUN but has no ascension
} TYPE;
volatile TYPE treadmill_type = RHYTHM_FUN;

View File

@@ -31,7 +31,20 @@ fakebike::fakebike(bool noWriteResistance, bool noHeartService, bool noVirtualDe
void fakebike::update() {
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
/*
static int updcou = 0;
updcou++;
double w = 250.0;
if (updcou > 20000 )
updcou = 0;
else if (updcou > 12000)
w = 300;
else if (updcou > 6000)
w = 150;
Speed = metric::calculateSpeedFromPower(w, Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), speedLimit());*/
update_metrics(true, watts());
@@ -47,11 +60,11 @@ void fakebike::update() {
#endif
#endif
) {
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();
@@ -73,7 +86,7 @@ void fakebike::update() {
if (!noVirtualDevice) {
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool()) {
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
Heart = (uint8_t)KeepAwakeHelper::heart();
debug("Current Heart: " + QString::number(Heart.value()));
}
@@ -93,8 +106,8 @@ void fakebike::update() {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -104,39 +117,7 @@ void fakebike::update() {
}
if (Heart.value()) {
/*
* If VO2 max is unknown, the following formulas would apply:
Women:
CB = T * (0.4472*H - 0.1263*W + 0.074*A - 20.4022) / 4.184
Men:
CB = T * (0.6309*H + 0.1988*W + 0.2017*A - 55.0969) / 4.184
Where:
CB is the number of calories burned;
T is the duration of exercise in minutes;
H is your average heart rate in beats per minute;
W is your weight in kilograms; and
A is your age in years.
*/
QString sex = settings.value(QStringLiteral("sex"), "Male").toString();
double weight = settings.value(QStringLiteral("weight"), 75.0).toFloat();
double age = settings.value(QStringLiteral("age"), 35).toDouble();
double T = elapsed.value() / 60;
double H = Heart.average();
double W = weight;
double A = age;
if (sex.toLower().contains("female")) {
KCal = T * ((0.4472 * H) - (0.1263 * W) + (0.074 * A) - 20.4022) / 4.184;
} else {
KCal = T * ((0.6309 * H) + (0.1988 * W) + (0.2017 * A) - 55.0969) / 4.184;
}
KCal = metric::calculateKCalfromHR(Heart.average(), elapsed.value());
}
if (requestResistance != -1 && requestResistance != currentResistance().value()) {

View File

@@ -31,7 +31,7 @@ fakeelliptical::fakeelliptical(bool noWriteResistance, bool noHeartService, bool
void fakeelliptical::update() {
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
update_metrics(true, watts());
@@ -53,11 +53,11 @@ void fakeelliptical::update() {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();
@@ -69,7 +69,8 @@ void fakeelliptical::update() {
emit debug(QStringLiteral("creating virtual bike interface..."));
virtualBike = new virtualbike(this, noWriteResistance, noHeartService);
connect(virtualBike, &virtualbike::changeInclination, this, &fakeelliptical::changeInclinationRequested);
connect(virtualBike, &virtualbike::ftmsCharacteristicChanged, this, &fakeelliptical::ftmsCharacteristicChanged);
connect(virtualBike, &virtualbike::ftmsCharacteristicChanged, this,
&fakeelliptical::ftmsCharacteristicChanged);
}
}
if (!firstStateChanged)
@@ -79,7 +80,7 @@ void fakeelliptical::update() {
if (!noVirtualDevice) {
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool()) {
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
Heart = (uint8_t)KeepAwakeHelper::heart();
debug("Current Heart: " + QString::number(Heart.value()));
}
@@ -99,8 +100,8 @@ void fakeelliptical::update() {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -109,12 +110,17 @@ void fakeelliptical::update() {
#endif
}
if (Heart.value()) {
KCal = metric::calculateKCalfromHR(Heart.average(), elapsed.value());
}
if (requestResistance != -1 && requestResistance != currentResistance().value()) {
Resistance = requestResistance;
}
}
void fakeelliptical::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
void fakeelliptical::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic,
const QByteArray &newValue) {
QByteArray b = newValue;
qDebug() << "routing FTMS packet to the bike from virtualbike" << characteristic.uuid() << newValue.toHex(' ');
}

137
src/faketreadmill.cpp Normal file
View File

@@ -0,0 +1,137 @@
#include "faketreadmill.h"
#include "ios/lockscreen.h"
#include "virtualbike.h"
#include <QBluetoothLocalDevice>
#include <QDateTime>
#include <QFile>
#include <QMetaEnum>
#include <QSettings>
#include <QThread>
#include <math.h>
#ifdef Q_OS_ANDROID
#include <QLowEnergyConnectionParameters>
#endif
#include "keepawakehelper.h"
#include <chrono>
using namespace std::chrono_literals;
faketreadmill::faketreadmill(bool noWriteResistance, bool noHeartService, bool noVirtualDevice) {
m_watt.setType(metric::METRIC_WATT);
Speed.setType(metric::METRIC_SPEED);
refresh = new QTimer(this);
this->noWriteResistance = noWriteResistance;
this->noHeartService = noHeartService;
this->noVirtualDevice = noVirtualDevice;
initDone = false;
connect(refresh, &QTimer::timeout, this, &faketreadmill::update);
refresh->start(200ms);
}
void faketreadmill::update() {
QSettings settings;
QString heartRateBeltName =
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
if (requestSpeed != -1) {
Speed = requestSpeed;
emit debug(QStringLiteral("writing speed ") + QString::number(requestSpeed));
requestSpeed = -1;
}
if (requestInclination != -100) {
Inclination = requestInclination;
emit debug(QStringLiteral("writing incline ") + QString::number(requestInclination));
requestInclination = -100;
}
Distance += ((Speed.value() / (double)3600.0) /
((double)1000.0 / (double)(lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime()))));
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
// ******************************************* virtual treadmill init *************************************
if (!firstStateChanged && !virtualTreadmill && !virtualBike) {
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
bool virtual_device_force_bike =
settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike)
.toBool();
if (virtual_device_enabled) {
if (!virtual_device_force_bike) {
debug("creating virtual treadmill interface...");
virtualTreadmill = new virtualtreadmill(this, noHeartService);
connect(virtualTreadmill, &virtualtreadmill::debug, this, &faketreadmill::debug);
connect(virtualTreadmill, &virtualtreadmill::changeInclination, this,
&faketreadmill::changeInclinationRequested);
} else {
debug("creating virtual bike interface...");
virtualBike = new virtualbike(this);
connect(virtualBike, &virtualbike::changeInclination, this, &faketreadmill::changeInclinationRequested);
}
}
if (!firstStateChanged)
emit connectedAndDiscovered();
firstStateChanged = 1;
}
// ********************************************************************************************************
if (!noVirtualDevice) {
#ifdef Q_OS_ANDROID
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
Heart = (uint8_t)KeepAwakeHelper::heart();
debug("Current Heart: " + QString::number(Heart.value()));
}
#endif
if (heartRateBeltName.startsWith(QStringLiteral("Disabled"))) {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
lockscreen h;
long appleWatchHeartRate = h.heartRate();
h.setKcal(KCal.value());
h.setDistance(Distance.value());
Heart = appleWatchHeartRate;
debug("Current Heart from Apple Watch: " + QString::number(appleWatchHeartRate));
#endif
#endif
}
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence =
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
}
#endif
#endif
}
if (Heart.value()) {
KCal = metric::calculateKCalfromHR(Heart.average(), elapsed.value());
}
}
void faketreadmill::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic,
const QByteArray &newValue) {
QByteArray b = newValue;
qDebug() << "routing FTMS packet to the bike from virtualbike" << characteristic.uuid() << newValue.toHex(' ');
}
void faketreadmill::changeInclinationRequested(double grade, double percentage) {
if (percentage < 0)
percentage = 0;
changeInclination(grade, percentage);
}
bool faketreadmill::connected() { return true; }
void *faketreadmill::VirtualBike() { return virtualBike; }
void *faketreadmill::VirtualTreadmill() { return virtualTreadmill; }
void *faketreadmill::VirtualDevice() { return VirtualTreadmill(); }

80
src/faketreadmill.h Normal file
View File

@@ -0,0 +1,80 @@
#ifndef FAKETREADMILL_H
#define FAKETREADMILL_H
#include <QBluetoothDeviceDiscoveryAgent>
#include <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
#include <QtBluetooth/qlowenergycharacteristic.h>
#include <QtBluetooth/qlowenergycharacteristicdata.h>
#include <QtBluetooth/qlowenergycontroller.h>
#include <QtBluetooth/qlowenergydescriptordata.h>
#include <QtBluetooth/qlowenergyservice.h>
#include <QtBluetooth/qlowenergyservicedata.h>
#include <QtCore/qbytearray.h>
#ifndef Q_OS_ANDROID
#include <QtCore/qcoreapplication.h>
#else
#include <QtGui/qguiapplication.h>
#endif
#include <QtCore/qlist.h>
#include <QtCore/qmutex.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qtimer.h>
#include <QDateTime>
#include <QObject>
#include <QString>
#include "treadmill.h"
#include "virtualbike.h"
#include "virtualtreadmill.h"
#ifdef Q_OS_IOS
#include "ios/lockscreen.h"
#endif
class faketreadmill : public treadmill {
Q_OBJECT
public:
faketreadmill(bool noWriteResistance, bool noHeartService, bool noVirtualDevice);
bool connected();
void *VirtualBike();
void *VirtualTreadmill();
void *VirtualDevice();
private:
QTimer *refresh;
virtualbike *virtualBike = nullptr;
virtualtreadmill *virtualTreadmill = nullptr;
uint8_t sec1Update = 0;
QByteArray lastPacket;
QDateTime lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
QDateTime lastGoodCadence = QDateTime::currentDateTime();
uint8_t firstStateChanged = 0;
bool initDone = false;
bool initRequest = false;
bool noWriteResistance = false;
bool noHeartService = false;
bool noVirtualDevice = false;
#ifdef Q_OS_IOS
lockscreen *h = 0;
#endif
signals:
void disconnected();
void debug(QString string);
private slots:
void changeInclinationRequested(double grade, double percentage);
void update();
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
};
#endif // FAKETREADMILL_H

View File

@@ -148,7 +148,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the accelerometer sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in accel_x and accel_y and accel_z
// Comment: Each time in the array describes the time at which the accelerometer sample with the corresponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in accel_x and accel_y and accel_z
///////////////////////////////////////////////////////////////////////
FIT_UINT16 GetSampleTimeOffset(FIT_UINT8 index) const
{
@@ -158,7 +158,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the accelerometer sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in accel_x and accel_y and accel_z
// Comment: Each time in the array describes the time at which the accelerometer sample with the corresponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in accel_x and accel_y and accel_z
///////////////////////////////////////////////////////////////////////
void SetSampleTimeOffset(FIT_UINT8 index, FIT_UINT16 sampleTimeOffset)
{

View File

@@ -140,7 +140,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the barometer sample with the corrosponding index was taken. The samples may span across seconds. Array size must match the number of samples in baro_cal
// Comment: Each time in the array describes the time at which the barometer sample with the corresponding index was taken. The samples may span across seconds. Array size must match the number of samples in baro_cal
///////////////////////////////////////////////////////////////////////
FIT_UINT16 GetSampleTimeOffset(FIT_UINT8 index) const
{
@@ -150,7 +150,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the barometer sample with the corrosponding index was taken. The samples may span across seconds. Array size must match the number of samples in baro_cal
// Comment: Each time in the array describes the time at which the barometer sample with the corresponding index was taken. The samples may span across seconds. Array size must match the number of samples in baro_cal
///////////////////////////////////////////////////////////////////////
void SetSampleTimeOffset(FIT_UINT8 index, FIT_UINT16 sampleTimeOffset)
{

View File

@@ -145,7 +145,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the gyro sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in gyro_x and gyro_y and gyro_z
// Comment: Each time in the array describes the time at which the gyro sample with the corresponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in gyro_x and gyro_y and gyro_z
///////////////////////////////////////////////////////////////////////
FIT_UINT16 GetSampleTimeOffset(FIT_UINT8 index) const
{
@@ -155,7 +155,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the gyro sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in gyro_x and gyro_y and gyro_z
// Comment: Each time in the array describes the time at which the gyro sample with the corresponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in gyro_x and gyro_y and gyro_z
///////////////////////////////////////////////////////////////////////
void SetSampleTimeOffset(FIT_UINT8 index, FIT_UINT16 sampleTimeOffset)
{

View File

@@ -145,7 +145,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the compass sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in cmps_x and cmps_y and cmps_z
// Comment: Each time in the array describes the time at which the compass sample with the corresponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in cmps_x and cmps_y and cmps_z
///////////////////////////////////////////////////////////////////////
FIT_UINT16 GetSampleTimeOffset(FIT_UINT8 index) const
{
@@ -155,7 +155,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set sample_time_offset field
// Units: ms
// Comment: Each time in the array describes the time at which the compass sample with the corrosponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in cmps_x and cmps_y and cmps_z
// Comment: Each time in the array describes the time at which the compass sample with the corresponding index was taken. Limited to 30 samples in each message. The samples may span across seconds. Array size must match the number of samples in cmps_x and cmps_y and cmps_z
///////////////////////////////////////////////////////////////////////
void SetSampleTimeOffset(FIT_UINT8 index, FIT_UINT16 sampleTimeOffset)
{

View File

@@ -145,7 +145,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns time_offset field
// Units: ms
// Comment: Offset of PID reading [i] from start_timestamp+start_timestamp_ms. Readings may span accross seconds.
// Comment: Offset of PID reading [i] from start_timestamp+start_timestamp_ms. Readings may span across seconds.
///////////////////////////////////////////////////////////////////////
FIT_UINT16 GetTimeOffset(FIT_UINT8 index) const
{
@@ -155,7 +155,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set time_offset field
// Units: ms
// Comment: Offset of PID reading [i] from start_timestamp+start_timestamp_ms. Readings may span accross seconds.
// Comment: Offset of PID reading [i] from start_timestamp+start_timestamp_ms. Readings may span across seconds.
///////////////////////////////////////////////////////////////////////
void SetTimeOffset(FIT_UINT8 index, FIT_UINT16 timeOffset)
{

View File

@@ -1865,7 +1865,7 @@ typedef FIT_UINT32Z FIT_CONNECTIVITY_CAPABILITIES;
#define FIT_CONNECTIVITY_CAPABILITIES_CONNECT_IQ_WIDGET_DOWNLOAD ((FIT_CONNECTIVITY_CAPABILITIES)0x00020000)
#define FIT_CONNECTIVITY_CAPABILITIES_CONNECT_IQ_WATCH_FACE_DOWNLOAD ((FIT_CONNECTIVITY_CAPABILITIES)0x00040000)
#define FIT_CONNECTIVITY_CAPABILITIES_CONNECT_IQ_DATA_FIELD_DOWNLOAD ((FIT_CONNECTIVITY_CAPABILITIES)0x00080000)
#define FIT_CONNECTIVITY_CAPABILITIES_CONNECT_IQ_APP_MANAGMENT ((FIT_CONNECTIVITY_CAPABILITIES)0x00100000) // Device supports delete and reorder of apps via GCM
#define FIT_CONNECTIVITY_CAPABILITIES_CONNECT_IQ_APP_MANAGEMENT ((FIT_CONNECTIVITY_CAPABILITIES)0x00100000) // Device supports delete and reorder of apps via GCM
#define FIT_CONNECTIVITY_CAPABILITIES_SWING_SENSOR ((FIT_CONNECTIVITY_CAPABILITIES)0x00200000)
#define FIT_CONNECTIVITY_CAPABILITIES_SWING_SENSOR_REMOTE ((FIT_CONNECTIVITY_CAPABILITIES)0x00400000)
#define FIT_CONNECTIVITY_CAPABILITIES_INCIDENT_DETECTION ((FIT_CONNECTIVITY_CAPABILITIES)0x00800000) // Device supports incident detection
@@ -2297,7 +2297,7 @@ typedef FIT_ENUM FIT_EXD_DATA_UNITS;
#define FIT_EXD_DATA_UNITS_FEET_PER_HOUR ((FIT_EXD_DATA_UNITS)4)
#define FIT_EXD_DATA_UNITS_METERS_PER_HOUR ((FIT_EXD_DATA_UNITS)5)
#define FIT_EXD_DATA_UNITS_DEGREES_CELSIUS ((FIT_EXD_DATA_UNITS)6)
#define FIT_EXD_DATA_UNITS_DEGREES_FARENHEIT ((FIT_EXD_DATA_UNITS)7)
#define FIT_EXD_DATA_UNITS_DEGREES_FAHRENHEIT ((FIT_EXD_DATA_UNITS)7)
#define FIT_EXD_DATA_UNITS_ZONE ((FIT_EXD_DATA_UNITS)8)
#define FIT_EXD_DATA_UNITS_GEAR ((FIT_EXD_DATA_UNITS)9)
#define FIT_EXD_DATA_UNITS_RPM ((FIT_EXD_DATA_UNITS)10)
@@ -2394,7 +2394,7 @@ typedef FIT_ENUM FIT_EXD_DESCRIPTORS;
#define FIT_EXD_DESCRIPTORS_INVALID FIT_ENUM_INVALID
#define FIT_EXD_DESCRIPTORS_BIKE_LIGHT_BATTERY_STATUS ((FIT_EXD_DESCRIPTORS)0)
#define FIT_EXD_DESCRIPTORS_BEAM_ANGLE_STATUS ((FIT_EXD_DESCRIPTORS)1)
#define FIT_EXD_DESCRIPTORS_BATERY_LEVEL ((FIT_EXD_DESCRIPTORS)2)
#define FIT_EXD_DESCRIPTORS_BATTERY_LEVEL ((FIT_EXD_DESCRIPTORS)2)
#define FIT_EXD_DESCRIPTORS_LIGHT_NETWORK_MODE ((FIT_EXD_DESCRIPTORS)3)
#define FIT_EXD_DESCRIPTORS_NUMBER_LIGHTS_CONNECTED ((FIT_EXD_DESCRIPTORS)4)
#define FIT_EXD_DESCRIPTORS_CADENCE ((FIT_EXD_DESCRIPTORS)5)

View File

@@ -1361,7 +1361,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns cadence256 field
// Units: rpm
// Comment: Log cadence and fractional cadence for backwards compatability
// Comment: Log cadence and fractional cadence for backwards compatibility
///////////////////////////////////////////////////////////////////////
FIT_FLOAT32 GetCadence256(void) const
{
@@ -1371,7 +1371,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set cadence256 field
// Units: rpm
// Comment: Log cadence and fractional cadence for backwards compatability
// Comment: Log cadence and fractional cadence for backwards compatibility
///////////////////////////////////////////////////////////////////////
void SetCadence256(FIT_FLOAT32 cadence256)
{

View File

@@ -223,7 +223,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns timestamp field
// Units: s
// Comment: Sesson end time.
// Comment: Session end time.
///////////////////////////////////////////////////////////////////////
FIT_DATE_TIME GetTimestamp(void) const
{
@@ -233,7 +233,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set timestamp field
// Units: s
// Comment: Sesson end time.
// Comment: Session end time.
///////////////////////////////////////////////////////////////////////
void SetTimestamp(FIT_DATE_TIME timestamp)
{

View File

@@ -134,7 +134,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Returns repetitions field
// Comment: # of repitions of the movement
// Comment: # of repetitions of the movement
///////////////////////////////////////////////////////////////////////
FIT_UINT16 GetRepetitions(void) const
{
@@ -143,7 +143,7 @@ public:
///////////////////////////////////////////////////////////////////////
// Set repetitions field
// Comment: # of repitions of the movement
// Comment: # of repetitions of the movement
///////////////////////////////////////////////////////////////////////
void SetRepetitions(FIT_UINT16 repetitions)
{

View File

@@ -9,7 +9,14 @@
using namespace std::chrono_literals;
#ifdef Q_OS_IOS
extern quint8 QZ_EnableDiscoveryCharsAndDescripttors;
#endif
fitmetria_fanfit::fitmetria_fanfit(bluetoothdevice *parentDevice) {
#ifdef Q_OS_IOS
QZ_EnableDiscoveryCharsAndDescripttors = true;
#endif
this->parentDevice = parentDevice;
}
@@ -40,14 +47,14 @@ void fitmetria_fanfit::fanSpeedRequest(uint8_t speed) {
QSettings settings;
if (speed > 102)
speed = 102;
double max = settings.value(QStringLiteral("fitmetria_fanfit_max"), 100).toDouble();
double min = settings.value(QStringLiteral("fitmetria_fanfit_min"), 0).toDouble();
double max = settings.value(QZSettings::fitmetria_fanfit_max, QZSettings::default_fitmetria_fanfit_max).toDouble();
double min = settings.value(QZSettings::fitmetria_fanfit_min, QZSettings::default_fitmetria_fanfit_min).toDouble();
uint16_t speed16 = (uint16_t)((double)speed * ((max * 10.0) - (min * 10.0)) / 100.0 + (min * 10.0));
const uint8_t brightness = 5;
const uint8_t leds_max = 15;
uint8_t leds[leds_max] = {30,30,30,15,15,15,13,24,24,8,27,27,27,26,26};
uint8_t leds[leds_max] = {30, 30, 30, 15, 15, 15, 13, 24, 24, 8, 27, 27, 27, 26, 26};
// 0~5~30~30~30~15~15~15~13~24~24~8~27~27~27~26~26
// 0 - fans peed
// 1 - brightness
@@ -88,52 +95,85 @@ void fitmetria_fanfit::fanSpeedRequest(uint8_t speed) {
myCollorDict['Yellow'] = 31
*/
QString s = QString::number((speed16)) +
"~" + QString::number(brightness);
QString s = QString::number((speed16)) + "~" + QString::number(brightness);
if(!settings.value(QStringLiteral("fitmetria_fanfit_mode"), QStringLiteral("Heart"))
.toString()
.compare(QStringLiteral("Power")) && parentDevice) {
if (!settings.value(QZSettings::fitmetria_fanfit_mode, QZSettings::default_fitmetria_fanfit_mode)
.toString()
.compare(QStringLiteral("Power")) &&
parentDevice) {
double ftp = parentDevice->currentPowerZone().value();
if(ftp < 1.3) memset(&leds[1], 0, leds_max - 1);
else if(ftp < 1.6) memset(&leds[2], 0, leds_max - 2);
else if(ftp < 2) memset(&leds[3], 0, leds_max - 3);
else if(ftp < 2.4) memset(&leds[4], 0, leds_max - 4);
else if(ftp < 3) memset(&leds[5], 0, leds_max - 5);
else if(ftp < 3.3) memset(&leds[6], 0, leds_max - 6);
else if(ftp < 3.6) memset(&leds[7], 0, leds_max - 7);
else if(ftp < 4) memset(&leds[8], 0, leds_max - 8);
else if(ftp < 5) memset(&leds[9], 0, leds_max - 9);
else if(ftp < 5.6) memset(&leds[10], 0, leds_max - 10);
else if(ftp < 6) memset(&leds[11], 0, leds_max - 11);
else if(ftp < 6.6) memset(&leds[12], 0, leds_max - 12);
else if(ftp < 7) memset(&leds[13], 0, leds_max - 13);
else if(ftp < 7.1) memset(&leds[14], 0, leds_max - 14);
} else if(!settings.value(QStringLiteral("fitmetria_fanfit_mode"), QStringLiteral("Heart"))
.toString()
.compare(QStringLiteral("Heart")) && parentDevice) {
if (ftp < 1.3)
memset(&leds[1], 0, leds_max - 1);
else if (ftp < 1.6)
memset(&leds[2], 0, leds_max - 2);
else if (ftp < 2)
memset(&leds[3], 0, leds_max - 3);
else if (ftp < 2.4)
memset(&leds[4], 0, leds_max - 4);
else if (ftp < 3)
memset(&leds[5], 0, leds_max - 5);
else if (ftp < 3.3)
memset(&leds[6], 0, leds_max - 6);
else if (ftp < 3.6)
memset(&leds[7], 0, leds_max - 7);
else if (ftp < 4)
memset(&leds[8], 0, leds_max - 8);
else if (ftp < 5)
memset(&leds[9], 0, leds_max - 9);
else if (ftp < 5.6)
memset(&leds[10], 0, leds_max - 10);
else if (ftp < 6)
memset(&leds[11], 0, leds_max - 11);
else if (ftp < 6.6)
memset(&leds[12], 0, leds_max - 12);
else if (ftp < 7)
memset(&leds[13], 0, leds_max - 13);
else if (ftp < 7.1)
memset(&leds[14], 0, leds_max - 14);
} else if (!settings.value(QZSettings::fitmetria_fanfit_mode, QZSettings::default_fitmetria_fanfit_mode)
.toString()
.compare(QStringLiteral("Heart")) &&
parentDevice) {
double ftp = parentDevice->currentHeartZone().value();
if(ftp < 1.3) memset(&leds[1], 0, leds_max - 1);
else if(ftp < 1.6) memset(&leds[2], 0, leds_max - 2);
else if(ftp < 1.9) memset(&leds[3], 0, leds_max - 3);
else if(ftp < 2) memset(&leds[4], 0, leds_max - 4);
else if(ftp < 2.3) memset(&leds[5], 0, leds_max - 5);
else if(ftp < 2.6) memset(&leds[6], 0, leds_max - 6);
else if(ftp < 2.9) memset(&leds[7], 0, leds_max - 7);
else if(ftp < 3.3) memset(&leds[8], 0, leds_max - 8);
else if(ftp < 3.6) memset(&leds[9], 0, leds_max - 9);
else if(ftp < 4) memset(&leds[10], 0, leds_max - 10);
else if(ftp < 4.3) memset(&leds[11], 0, leds_max - 11);
else if(ftp < 4.9) memset(&leds[12], 0, leds_max - 12);
else if(ftp < 5) memset(&leds[13], 0, leds_max - 13);
else if(ftp < 5.1) memset(&leds[14], 0, leds_max - 14);
if (ftp < 1.3)
memset(&leds[1], 0, leds_max - 1);
else if (ftp < 1.6)
memset(&leds[2], 0, leds_max - 2);
else if (ftp < 1.9)
memset(&leds[3], 0, leds_max - 3);
else if (ftp < 2)
memset(&leds[4], 0, leds_max - 4);
else if (ftp < 2.3)
memset(&leds[5], 0, leds_max - 5);
else if (ftp < 2.6)
memset(&leds[6], 0, leds_max - 6);
else if (ftp < 2.9)
memset(&leds[7], 0, leds_max - 7);
else if (ftp < 3.3)
memset(&leds[8], 0, leds_max - 8);
else if (ftp < 3.6)
memset(&leds[9], 0, leds_max - 9);
else if (ftp < 4)
memset(&leds[10], 0, leds_max - 10);
else if (ftp < 4.3)
memset(&leds[11], 0, leds_max - 11);
else if (ftp < 4.9)
memset(&leds[12], 0, leds_max - 12);
else if (ftp < 5)
memset(&leds[13], 0, leds_max - 13);
else if (ftp < 5.1)
memset(&leds[14], 0, leds_max - 14);
} else {
memset(&leds, 0, leds_max);
}
for(int i=0;i<leds_max;i++)
for (int i = 0; i < leds_max; i++)
s += "~" + QString::number(leds[i]);
writeCharacteristic((uint8_t *)s.toLocal8Bit().data(), s.length(), QStringLiteral("forcing fan ") + s, false, true);
// only if the string differs i update the value over bluetooth
if (lastValueSent != s)
writeCharacteristic((uint8_t *)s.toLocal8Bit().data(), s.length(), QStringLiteral("forcing fan ") + s, false,
true);
lastValueSent = s;
}
void fitmetria_fanfit::writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
@@ -176,7 +216,8 @@ void fitmetria_fanfit::writeCharacteristic(uint8_t *data, uint8_t data_len, cons
QStringLiteral(" // ") + info;
}
loop.exec();
// not necessary, since the communication is one way only. also it could lead to crashes
// loop.exec();
}
void fitmetria_fanfit::stateChanged(QLowEnergyService::ServiceState state) {
@@ -236,6 +277,8 @@ void fitmetria_fanfit::serviceScanDone(void) {
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
connect(gattCommunicationChannelService, &QLowEnergyService::stateChanged, this, &fitmetria_fanfit::stateChanged);
gattCommunicationChannelService->discoverDetails();
lastValueSent = "";
}
void fitmetria_fanfit::errorService(QLowEnergyService::ServiceError err) {

View File

@@ -1,7 +1,6 @@
#ifndef FITMETRIA_FANFIT_H
#define FITMETRIA_FANFIT_H
#include <QBluetoothDeviceDiscoveryAgent>
#include <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
@@ -36,13 +35,14 @@ class fitmetria_fanfit : public bluetoothdevice {
private:
QLowEnergyService *gattCommunicationChannelService = nullptr;
//QLowEnergyCharacteristic gattNotifyCharacteristic;
// QLowEnergyCharacteristic gattNotifyCharacteristic;
QLowEnergyCharacteristic gattWriteCharacteristic;
void writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
bool wait_for_response = false);
bluetoothdevice *parentDevice = nullptr;
QString lastValueSent = "";
signals:
void disconnected();

View File

@@ -71,8 +71,8 @@ void fitplusbike::writeCharacteristic(uint8_t *data, uint8_t data_len, const QSt
void fitplusbike::forceResistance(resistance_t requestResistance) {
QSettings settings;
bool virtufit_etappe = settings.value(QStringLiteral("virtufit_etappe"), false).toBool();
if (virtufit_etappe) {
bool virtufit_etappe = settings.value(QZSettings::virtufit_etappe, QZSettings::default_virtufit_etappe).toBool();
if (virtufit_etappe || merach_MRK) {
if (requestResistance == 1) {
uint8_t res[] = {0x02, 0x44, 0x05, 0x01, 0xf9, 0xb9, 0x03};
writeCharacteristic(res, sizeof(res), "force resistance", false, true);
@@ -163,23 +163,25 @@ void fitplusbike::update() {
gattNotify1Characteristic.isValid() && initDone) {
QSettings settings;
update_metrics(true, watts());
bool virtufit_etappe = settings.value(QStringLiteral("virtufit_etappe"), false).toBool();
bool virtufit_etappe =
settings.value(QZSettings::virtufit_etappe, QZSettings::default_virtufit_etappe).toBool();
if (virtufit_etappe) {
if (virtufit_etappe || merach_MRK) {
} else {
if (Heart.value() > 0) {
int avgP = ((settings.value(QStringLiteral("power_hr_pwr1"), 200).toDouble() *
settings.value(QStringLiteral("power_hr_hr2"), 170).toDouble()) -
(settings.value(QStringLiteral("power_hr_pwr2"), 230).toDouble() *
settings.value(QStringLiteral("power_hr_hr1"), 150).toDouble())) /
(settings.value(QStringLiteral("power_hr_hr2"), 170).toDouble() -
settings.value(QStringLiteral("power_hr_hr1"), 150).toDouble()) +
(Heart.value() * ((settings.value(QStringLiteral("power_hr_pwr1"), 200).toDouble() -
settings.value(QStringLiteral("power_hr_pwr2"), 230).toDouble()) /
(settings.value(QStringLiteral("power_hr_hr1"), 150).toDouble() -
settings.value(QStringLiteral("power_hr_hr2"), 170).toDouble())));
int avgP = ((settings.value(QZSettings::power_hr_pwr1, QZSettings::default_power_hr_pwr1).toDouble() *
settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble()) -
(settings.value(QZSettings::power_hr_pwr2, QZSettings::default_power_hr_pwr2).toDouble() *
settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble())) /
(settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble() -
settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble()) +
(Heart.value() *
((settings.value(QZSettings::power_hr_pwr1, QZSettings::default_power_hr_pwr1).toDouble() -
settings.value(QZSettings::power_hr_pwr2, QZSettings::default_power_hr_pwr2).toDouble()) /
(settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble() -
settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble())));
if (avgP < 50) {
avgP = 50;
}
@@ -248,31 +250,52 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
qDebug() << QStringLiteral(" << ") + newValue.toHex(' ');
lastPacket = newValue;
bool virtufit_etappe = settings.value(QStringLiteral("virtufit_etappe"), false).toBool();
if (virtufit_etappe) {
bool virtufit_etappe = settings.value(QZSettings::virtufit_etappe, QZSettings::default_virtufit_etappe).toBool();
if (virtufit_etappe || merach_MRK) {
if (newValue.length() != 15 && newValue.length() != 13)
return;
if (newValue.length() == 15) {
Resistance = newValue.at(5);
m_pelotonResistance = (100 * Resistance.value()) / max_resistance;
if(merach_MRK) {
// if we change this, also change the wattsFromResistance function. We can create a standard function in order to
// have all the costants in one place (I WANT MORE TIME!!!)
double ac = 0.01243107769;
double bc = 1.145964912;
double cc = -23.50977444;
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
double ar = 0.1469553975;
double br = -5.841344538;
double cr = 97.62165482;
m_pelotonResistance =
(((sqrt(pow(br, 2.0) -
4.0 * ar *
(cr - (m_watt.value() * 132.0 / (ac * pow(Cadence.value(), 2.0) + bc * Cadence.value() + cc)))) -
br) /
(2.0 * ar)) *
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
} else {
m_pelotonResistance = (100 * Resistance.value()) / max_resistance;
}
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
Cadence = ((uint8_t)newValue.at(6));
m_watt = (double)((((uint8_t)newValue.at(4)) << 8) | ((uint8_t)newValue.at(3))) / 10.0;
/*if (!settings.value(QStringLiteral("speed_power_based"), false).toBool())
/*if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool())
Speed = (double)((((uint8_t)newValue.at(4)) << 10) | ((uint8_t)newValue.at(9))) / 100.0;
else*/
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
} else if (newValue.length() == 13) {
@@ -291,19 +314,20 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
Resistance = 1;
m_pelotonResistance = 1;
emit resistanceRead(Resistance.value());
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
Cadence = ((uint8_t)newValue.at(8));
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool())
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool())
Speed = (double)((((uint8_t)newValue.at(7)) << 8) | ((uint8_t)newValue.at(6))) / 10.0;
else
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in kg
@@ -319,7 +343,7 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -340,8 +364,9 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -364,9 +389,12 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
void fitplusbike::btinit() {
QSettings settings;
bool virtufit_etappe = settings.value(QStringLiteral("virtufit_etappe"), false).toBool();
bool virtufit_etappe = settings.value(QZSettings::virtufit_etappe, QZSettings::default_virtufit_etappe).toBool();
if (virtufit_etappe) {
if (merach_MRK) {
uint8_t initData1[] = {0xaa, 0x01, 0x00, 0x01, 0x55};
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, true);
} else if (virtufit_etappe) {
uint8_t initData1[] = {0x02, 0x42, 0x42, 0x03};
uint8_t initData2[] = {0x02, 0x41, 0x02, 0x43, 0x03};
uint8_t initData3[] = {0x02, 0x41, 0x03, 0x42, 0x03};
@@ -448,11 +476,14 @@ void fitplusbike::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence =
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();
@@ -518,6 +549,10 @@ void fitplusbike::deviceDiscovered(const QBluetoothDeviceInfo &device) {
{
bluetoothDevice = device;
if (device.name().startsWith(QStringLiteral("MRK-"))) {
merach_MRK = true;
}
m_control = QLowEnergyController::createCentral(bluetoothDevice, this);
connect(m_control, &QLowEnergyController::serviceDiscovered, this, &fitplusbike::serviceDiscovered);
connect(m_control, &QLowEnergyController::discoveryFinished, this, &fitplusbike::serviceScanDone);

View File

@@ -75,6 +75,8 @@ class fitplusbike : public bike {
bool noWriteResistance = false;
bool noHeartService = false;
bool merach_MRK = false;
#ifdef Q_OS_IOS
lockscreen *h = 0;
#endif

View File

@@ -33,8 +33,8 @@ fitshowtreadmill::fitshowtreadmill(uint32_t pollDeviceTime, bool noConsole, bool
refresh = new QTimer(this);
initDone = false;
QSettings settings;
anyrun = settings.value(QStringLiteral("fitshow_anyrun"), false).toBool();
truetimer = settings.value(QStringLiteral("fitshow_truetimer"), false).toBool();
anyrun = settings.value(QZSettings::fitshow_anyrun, QZSettings::default_fitshow_anyrun).toBool();
truetimer = settings.value(QZSettings::fitshow_truetimer, QZSettings::default_fitshow_truetimer).toBool();
connect(refresh, &QTimer::timeout, this, &fitshowtreadmill::update);
refresh->start(pollDeviceTime);
}
@@ -156,7 +156,8 @@ void fitshowtreadmill::update() {
QSettings settings;
// ******************************************* virtual treadmill init *************************************
if (!firstInit && searchStopped && !virtualTreadMill) {
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
if (virtual_device_enabled) {
emit debug(QStringLiteral("creating virtual treadmill interface..."));
virtualTreadMill = new virtualtreadmill(this, noHeartService);
@@ -171,7 +172,7 @@ void fitshowtreadmill::update() {
emit debug(QStringLiteral("fitshow Treadmill RSSI ") + QString::number(bluetoothDevice.rssi()));
update_metrics(true, watts(settings.value(QStringLiteral("weight"), 75.0).toFloat()));
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
if (requestSpeed != -1) {
if (requestSpeed != currentSpeed().value()) {
@@ -295,7 +296,7 @@ void fitshowtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
Q_UNUSED(characteristic);
QByteArray value = newValue;
@@ -425,9 +426,21 @@ void fitshowtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
}
if (!firstCharacteristicChanged) {
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
KCal +=
((((0.048 * ((double)watts(
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) +
1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 /
((double)lastTimeCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
// kg * 3.5) / 200 ) / 60
DistanceCalculated +=
((speed / 3600.0) /
(1000.0 / (lastTimeCharacteristicChanged.msecsTo(QDateTime::currentDateTime()))));
lastTimeCharacteristicChanged = QDateTime::currentDateTime();
}
emit debug(QStringLiteral("Current elapsed from treadmill: ") + QString::number(seconds_elapsed));
@@ -436,7 +449,8 @@ void fitshowtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
emit debug(QStringLiteral("Current heart: ") + QString::number(heart));
emit debug(QStringLiteral("Current Distance: ") + QString::number(distance));
emit debug(QStringLiteral("Current Distance Calculated: ") + QString::number(DistanceCalculated));
emit debug(QStringLiteral("Current KCal: ") + QString::number(kcal));
emit debug(QStringLiteral("Current KCal from the Machine: ") + QString::number(kcal));
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
emit debug(QStringLiteral("Current step countl: ") + QString::number(step_count));
if (m_control->error() != QLowEnergyController::NoError) {
@@ -450,21 +464,20 @@ void fitshowtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
lastStop = 0;
}
Speed = speed;
if (Speed.value() != speed) {
Speed = speed;
emit speedChanged(speed);
}
Inclination = incline;
if (Inclination.value() != incline) {
Inclination = incline;
emit inclinationChanged(0, incline);
}
KCal = kcal;
if (truetimer)
elapsed = seconds_elapsed;
Distance = distance;
Distance = DistanceCalculated;
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
@@ -487,7 +500,6 @@ void fitshowtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
lastInclination = incline;
}
lastTimeCharacteristicChanged = QDateTime::currentDateTime();
firstCharacteristicChanged = false;
if (par != FITSHOW_STATUS_RUNNING) {
sendSportData();
@@ -563,12 +575,12 @@ void fitshowtreadmill::characteristicChanged(const QLowEnergyCharacteristic &cha
emit debug(QStringLiteral("Current elapsed from treadmill: ") + QString::number(seconds_elapsed));
emit debug(QStringLiteral("Current step countl: ") + QString::number(step_count));
emit debug(QStringLiteral("Current KCal: ") + QString::number(kcal));
emit debug(QStringLiteral("Current Distance: ") + QString::number(distance));
KCal = kcal;
emit debug(QStringLiteral("Current KCal from the machine: ") + QString::number(kcal));
emit debug(QStringLiteral("Current Distance from the machine: ") + QString::number(distance));
// KCal = kcal;
if (truetimer)
elapsed = seconds_elapsed;
Distance = distance;
// Distance = distance;
}
}
}
@@ -601,8 +613,8 @@ void fitshowtreadmill::btinit(bool startTape) {
0x00 // mode-dependent value (u16le)
}; // to verify
QSettings settings;
int user_id = settings.value(QStringLiteral("fitshow_user_id"), 0x13AA).toInt();
uint8_t weight = (uint8_t)(settings.value(QStringLiteral("weight"), 75.0).toFloat() + 0.5);
int user_id = settings.value(QZSettings::fitshow_user_id, QZSettings::default_fitshow_user_id).toInt();
uint8_t weight = (uint8_t)(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() + 0.5);
uint8_t initUserData[] = {FITSHOW_SYS_CONTROL, FITSHOW_CONTROL_USER, 0, 0, 0, 0, 0, 0};
initUserData[2] = (user_id >> 0) & 0xFF;
initUserData[3] = (user_id >> 8) & 0xFF;

View File

@@ -182,12 +182,12 @@ void flywheelbike::decodeReceivedData(QByteArray buffer) {
void flywheelbike::updateStats() {
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
// calculate the acculamator every time on the current data, in order to avoid holes in peloton or strava
// calculate the accumulator every time on the current data, in order to avoid holes in peloton or strava
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() * 3.5) /
((((0.048 * ((double)watts()) + 1.19) * settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in kg
@@ -217,8 +217,8 @@ void flywheelbike::updateStats() {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", false).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -245,7 +245,7 @@ void flywheelbike::characteristicChanged(const QLowEnergyCharacteristic &charact
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
Q_UNUSED(characteristic);
QSettings settings;
// QString heartRateBeltName = settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled"))
// QString heartRateBeltName = settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name)
// .toString(); // NOTE: clazy-unused-non-trivial-variable
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
@@ -263,7 +263,7 @@ void flywheelbike::characteristicChanged(const QLowEnergyCharacteristic &charact
// double distance = GetDistanceFromPacket(newValue); //Note: clang-analyzer-deadcode.DeadStores
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
#endif
@@ -272,7 +272,7 @@ void flywheelbike::characteristicChanged(const QLowEnergyCharacteristic &charact
uint16_t speed = ((parsedData->speed >> 8) & 0xFF);
speed += ((parsedData->speed & 0xFF) << 8);
if (zero_fix_filter < settings.value(QStringLiteral("flywheel_filter"), 2).toUInt() &&
if (zero_fix_filter < settings.value(QZSettings::flywheel_filter, QZSettings::default_flywheel_filter).toUInt() &&
(parsedData->cadence == 0 || speed == 0 || power == 0)) {
qDebug() << QStringLiteral("filtering crappy values");
zero_fix_filter++;
@@ -281,16 +281,16 @@ void flywheelbike::characteristicChanged(const QLowEnergyCharacteristic &charact
Resistance = parsedData->brake_level;
emit resistanceRead(Resistance.value());
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = parsedData->cadence;
}
m_watts = power;
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = ((double)speed) / 10.0;
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
// https://www.facebook.com/groups/149984563348738/permalink/174268944253633/?comment_id=174366620910532&reply_comment_id=174666314213896
@@ -366,11 +366,11 @@ void flywheelbike::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", false).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();

View File

@@ -161,192 +161,389 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
bool disable_hr_frommachinery = settings.value(QStringLiteral("heart_ignore_builtin"), false).toBool();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
bool disable_hr_frommachinery =
settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
bool heart = false;
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
if (characteristic.uuid() != QBluetoothUuid((quint16)0x2AD2)) {
return;
}
qDebug() << characteristic.uuid() << QStringLiteral(" << ") << newValue.toHex(' ');
lastPacket = newValue;
union flags {
struct {
uint16_t moreData : 1;
uint16_t avgSpeed : 1;
uint16_t instantCadence : 1;
uint16_t avgCadence : 1;
uint16_t totDistance : 1;
uint16_t resistanceLvl : 1;
uint16_t instantPower : 1;
uint16_t avgPower : 1;
uint16_t expEnergy : 1;
uint16_t heartRate : 1;
uint16_t metabolic : 1;
uint16_t elapsedTime : 1;
uint16_t remainingTime : 1;
uint16_t spare : 3;
if (characteristic.uuid() == QBluetoothUuid((quint16)0x2AD2)) {
union flags {
struct {
uint16_t moreData : 1;
uint16_t avgSpeed : 1;
uint16_t instantCadence : 1;
uint16_t avgCadence : 1;
uint16_t totDistance : 1;
uint16_t resistanceLvl : 1;
uint16_t instantPower : 1;
uint16_t avgPower : 1;
uint16_t expEnergy : 1;
uint16_t heartRate : 1;
uint16_t metabolic : 1;
uint16_t elapsedTime : 1;
uint16_t remainingTime : 1;
uint16_t spare : 3;
};
uint16_t word_flags;
};
uint16_t word_flags;
};
flags Flags;
int index = 0;
Flags.word_flags = (newValue.at(1) << 8) | newValue.at(0);
index += 2;
flags Flags;
int index = 0;
Flags.word_flags = (newValue.at(1) << 8) | newValue.at(0);
index += 2;
if (!Flags.moreData) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
} else {
Speed = metric::calculateSpeedFromPower(
watts(), Inclination.value(), Speed.value(),
fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
index += 2;
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
}
if (!Flags.moreData) {
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
Speed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
if (Flags.avgSpeed) {
double avgSpeed;
avgSpeed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
index += 2;
emit debug(QStringLiteral("Current Average Speed: ") + QString::number(avgSpeed));
}
if (Flags.instantCadence) {
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
2.0;
}
index += 2;
emit debug(QStringLiteral("Current Cadence: ") + QString::number(Cadence.value()));
}
if (Flags.avgCadence) {
double avgCadence;
avgCadence = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
2.0;
index += 2;
emit debug(QStringLiteral("Current Average Cadence: ") + QString::number(avgCadence));
}
if (Flags.totDistance) {
Distance = ((double)((((uint32_t)((uint8_t)newValue.at(index + 2)) << 16) |
(uint32_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint32_t)((uint8_t)newValue.at(index)))) /
1000.0;
index += 3;
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
}
index += 2;
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
}
if (Flags.avgSpeed) {
double avgSpeed;
avgSpeed =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
index += 2;
emit debug(QStringLiteral("Current Average Speed: ") + QString::number(avgSpeed));
}
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
if (Flags.instantCadence) {
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
2.0;
}
index += 2;
emit debug(QStringLiteral("Current Cadence: ") + QString::number(Cadence.value()));
}
if (Flags.avgCadence) {
double avgCadence;
avgCadence =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)))) /
2.0;
index += 2;
emit debug(QStringLiteral("Current Average Cadence: ") + QString::number(avgCadence));
}
if (Flags.totDistance) {
Distance = ((double)((((uint32_t)((uint8_t)newValue.at(index + 2)) << 16) |
(uint32_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint32_t)((uint8_t)newValue.at(index)))) /
1000.0;
index += 3;
} else {
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
}
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
if (Flags.resistanceLvl) {
Resistance =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
emit resistanceRead(Resistance.value());
index += 2;
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
} else {
double ac = 0.01243107769;
double bc = 1.145964912;
double cc = -23.50977444;
double ar = 0.1469553975;
double br = -5.841344538;
double cr = 97.62165482;
if (Cadence.value() && m_watt.value()) {
m_pelotonResistance =
(((sqrt(pow(br, 2.0) - 4.0 * ar *
(cr - (m_watt.value() * 132.0 /
(ac * pow(Cadence.value(), 2.0) + bc * Cadence.value() + cc)))) -
br) /
(2.0 * ar)) *
settings.value(QStringLiteral("peloton_gain"), 1.0).toDouble()) +
settings.value(QStringLiteral("peloton_offset"), 0.0).toDouble();
Resistance = m_pelotonResistance;
if (Flags.resistanceLvl) {
Resistance = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
emit resistanceRead(Resistance.value());
index += 2;
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
} else {
double ac = 0.01243107769;
double bc = 1.145964912;
double cc = -23.50977444;
double ar = 0.1469553975;
double br = -5.841344538;
double cr = 97.62165482;
if (Cadence.value() && m_watt.value()) {
m_pelotonResistance =
(((sqrt(pow(br, 2.0) - 4.0 * ar *
(cr - (m_watt.value() * 132.0 /
(ac * pow(Cadence.value(), 2.0) + bc * Cadence.value() + cc)))) -
br) /
(2.0 * ar)) *
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
Resistance = m_pelotonResistance;
emit resistanceRead(Resistance.value());
}
}
}
if (Flags.instantPower) {
if (settings.value(QStringLiteral("power_sensor_name"), QStringLiteral("Disabled"))
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
}
if (Flags.instantPower) {
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
}
if (Flags.avgPower) {
double avgPower;
avgPower =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Average Watt: ") + QString::number(avgPower));
}
if (Flags.avgPower && newValue.length() > index + 1) {
double avgPower;
avgPower = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Average Watt: ") + QString::number(avgPower));
}
if (Flags.expEnergy && newValue.length() > index + 1) {
KCal = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
index += 2;
if (Flags.expEnergy && newValue.length() > index + 1) {
KCal = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
// energy per hour
index += 2;
// energy per hour
index += 2;
// energy per minute
index += 1;
} else {
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() *
3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
// kg * 3.5) / 200 ) / 60
}
// energy per minute
index += 1;
} else {
if (watts())
KCal += ((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 /
((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
// kg * 3.5) / 200 ) / 60
}
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
{
if (Flags.heartRate && !disable_hr_frommachinery && newValue.length() > index) {
Heart = ((double)((newValue.at(index))));
// index += 1; // NOTE: clang-analyzer-deadcode.DeadStores
emit debug(QStringLiteral("Current Heart: ") + QString::number(Heart.value()));
} else {
Flags.heartRate = false;
{
if (Flags.heartRate && !disable_hr_frommachinery && newValue.length() > index) {
Heart = ((double)((newValue.at(index))));
// index += 1; // NOTE: clang-analyzer-deadcode.DeadStores
emit debug(QStringLiteral("Current Heart: ") + QString::number(Heart.value()));
} else {
Flags.heartRate = false;
}
heart = Flags.heartRate;
}
}
if (Flags.metabolic) {
// todo
}
if (Flags.metabolic) {
// todo
}
if (Flags.elapsedTime) {
// todo
}
if (Flags.elapsedTime) {
// todo
}
if (Flags.remainingTime) {
// todo
if (Flags.remainingTime) {
// todo
}
} else if (characteristic.uuid() == QBluetoothUuid((quint16)0x2ACE)) {
union flags {
struct {
uint32_t moreData : 1;
uint32_t avgSpeed : 1;
uint32_t totDistance : 1;
uint32_t stepCount : 1;
uint32_t strideCount : 1;
uint32_t elevationGain : 1;
uint32_t rampAngle : 1;
uint32_t resistanceLvl : 1;
uint32_t instantPower : 1;
uint32_t avgPower : 1;
uint32_t expEnergy : 1;
uint32_t heartRate : 1;
uint32_t metabolicEq : 1;
uint32_t elapsedTime : 1;
uint32_t remainingTime : 1;
uint32_t movementDirection : 1;
uint32_t spare : 8;
};
uint32_t word_flags;
};
flags Flags;
int index = 0;
Flags.word_flags = (newValue.at(2) << 16) | (newValue.at(1) << 8) | newValue.at(0);
index += 3;
if (!Flags.moreData) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
} else {
Speed = metric::calculateSpeedFromPower(
watts(), Inclination.value(), Speed.value(),
fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
index += 2;
}
if (Flags.avgSpeed) {
double avgSpeed;
avgSpeed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
index += 2;
emit debug(QStringLiteral("Current Average Speed: ") + QString::number(avgSpeed));
}
if (Flags.totDistance) {
Distance = ((double)((((uint32_t)((uint8_t)newValue.at(index + 2)) << 16) |
(uint32_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint32_t)((uint8_t)newValue.at(index)))) /
1000.0;
index += 3;
} else {
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
}
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
if (Flags.stepCount) {
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
Cadence = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
}
emit debug(QStringLiteral("Current Cadence: ") + QString::number(Cadence.value()));
index += 2;
index += 2;
}
if (Flags.strideCount) {
index += 2;
}
if (Flags.elevationGain) {
index += 2;
index += 2;
}
if (Flags.rampAngle) {
index += 2;
index += 2;
}
if (Flags.resistanceLvl) {
Resistance = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
emit resistanceRead(Resistance.value());
index += 2;
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
} else {
double ac = 0.01243107769;
double bc = 1.145964912;
double cc = -23.50977444;
double ar = 0.1469553975;
double br = -5.841344538;
double cr = 97.62165482;
if (Cadence.value() && m_watt.value()) {
m_pelotonResistance =
(((sqrt(pow(br, 2.0) - 4.0 * ar *
(cr - (m_watt.value() * 132.0 /
(ac * pow(Cadence.value(), 2.0) + bc * Cadence.value() + cc)))) -
br) /
(2.0 * ar)) *
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
Resistance = m_pelotonResistance;
emit resistanceRead(Resistance.value());
}
}
if (Flags.instantPower) {
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
index += 2;
}
if (Flags.avgPower && newValue.length() > index + 1) {
double avgPower;
avgPower = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
emit debug(QStringLiteral("Current Average Watt: ") + QString::number(avgPower));
index += 2;
}
if (Flags.expEnergy && newValue.length() > index + 1) {
KCal = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
// energy per hour
index += 2;
// energy per minute
index += 1;
} else {
if (watts())
KCal += ((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 /
((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
// kg * 3.5) / 200 ) / 60
}
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
#ifdef Q_OS_ANDROID
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
{
if (Flags.heartRate && !disable_hr_frommachinery && newValue.length() > index) {
Heart = ((double)((newValue.at(index))));
// index += 1; // NOTE: clang-analyzer-deadcode.DeadStores
emit debug(QStringLiteral("Current Heart: ") + QString::number(Heart.value()));
} else {
Flags.heartRate = false;
}
heart = Flags.heartRate;
}
if (Flags.metabolicEq) {
// todo
}
if (Flags.elapsedTime) {
// todo
}
if (Flags.remainingTime) {
// todo
}
} else {
return;
}
if (Cadence.value() > 0) {
@@ -357,7 +554,7 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
if (heartRateBeltName.startsWith(QStringLiteral("Disabled")) &&
(!Flags.heartRate || Heart.value() == 0 || disable_hr_frommachinery)) {
(!heart || Heart.value() == 0 || disable_hr_frommachinery)) {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
lockscreen h;
@@ -372,8 +569,9 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -390,6 +588,7 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
}
void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
QSettings settings;
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceState>();
emit debug(QStringLiteral("BTLE stateChanged ") + QString::fromLocal8Bit(metaEnum.valueToKey(state)));
@@ -422,6 +621,16 @@ void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
qDebug() << s->serviceUuid() << QStringLiteral("connected!");
if (settings.value(QZSettings::hammer_racer_s, QZSettings::default_hammer_racer_s).toBool()) {
QBluetoothUuid ftmsService((quint16)0x1826);
if (s->serviceUuid() != ftmsService) {
qDebug() << QStringLiteral("hammer racer bike wants to be subscribed only to FTMS service in order "
"to send metrics")
<< s->serviceUuid();
continue;
}
}
auto characteristics_list = s->characteristics();
for (const QLowEnergyCharacteristic &c : qAsConst(characteristics_list)) {
qDebug() << QStringLiteral("char uuid") << c.uuid() << QStringLiteral("handle") << c.handle();
@@ -474,6 +683,11 @@ void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
}
}
if (gattFTMSService && gattWriteCharControlPointId.isValid() &&
settings.value(QZSettings::hammer_racer_s, QZSettings::default_hammer_racer_s).toBool()) {
init();
}
// ******************************************* virtual bike init *************************************
if (!firstStateChanged && !virtualBike
#ifdef Q_OS_IOS
@@ -483,11 +697,14 @@ void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled =
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence =
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround =
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();
@@ -561,11 +778,15 @@ void ftmsbike::serviceScanDone(void) {
initRequest = false;
auto services_list = m_control->services();
QBluetoothUuid ftmsService((quint16)0x1826);
bool JK_fitness_577 = bluetoothDevice.name().toUpper().startsWith("DHZ-");
for (const QBluetoothUuid &s : qAsConst(services_list)) {
gattCommunicationChannelService.append(m_control->createServiceObject(s));
connect(gattCommunicationChannelService.constLast(), &QLowEnergyService::stateChanged, this,
&ftmsbike::stateChanged);
gattCommunicationChannelService.constLast()->discoverDetails();
if ((JK_fitness_577 && s == ftmsService) || !JK_fitness_577) {
gattCommunicationChannelService.append(m_control->createServiceObject(s));
connect(gattCommunicationChannelService.constLast(), &QLowEnergyService::stateChanged, this,
&ftmsbike::stateChanged);
gattCommunicationChannelService.constLast()->discoverDetails();
}
}
}
@@ -581,14 +802,16 @@ void ftmsbike::error(QLowEnergyController::Error err) {
m_control->errorString());
}
resistance_t ftmsbike::pelotonToBikeResistance(int pelotonResistance) { return (pelotonResistance * max_resistance) / 100; }
resistance_t ftmsbike::pelotonToBikeResistance(int pelotonResistance) {
return (pelotonResistance * max_resistance) / 100;
}
void ftmsbike::deviceDiscovered(const QBluetoothDeviceInfo &device) {
emit debug(QStringLiteral("Found new device: ") + device.name() + QStringLiteral(" (") +
device.address().toString() + ')');
{
bluetoothDevice = device;
if(bluetoothDevice.name().toUpper().startsWith("SUITO")) {
if (bluetoothDevice.name().toUpper().startsWith("SUITO")) {
qDebug() << QStringLiteral("SUITO found");
max_resistance = 16;
}

View File

@@ -137,8 +137,9 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
Q_UNUSED(characteristic);
QSettings settings;
bool disable_hr_frommachinery = settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
qDebug() << QStringLiteral(" << ") << characteristic.uuid() << " " << newValue.toHex(' ');
@@ -172,19 +173,22 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
flags Flags;
int index = 0;
double cadence_divider = 2.0;
if (WHIPR)
cadence_divider = 1.0;
Flags.word_flags = (newValue.at(1) << 8) | newValue.at(0);
index += 2;
if (!Flags.moreData) {
Cadence = ((uint8_t)newValue.at(index)) / 2;
Cadence = ((uint8_t)newValue.at(index)) / cadence_divider;
StrokesCount =
(((uint16_t)((uint8_t)newValue.at(index + 2)) << 8) | (uint16_t)((uint8_t)newValue.at(index + 1)));
index += 3;
/*
* the concept 2 sends the pace in 2 frames, so this condition will create a bugus speed
* the concept 2 sends the pace in 2 frames, so this condition will create a bogus speed
if (!Flags.instantPace) {
// eredited by echelon rower, probably we need to change this
Speed = (0.37497622 * ((double)Cadence.value())) / 2.0;
@@ -196,7 +200,7 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
if (Flags.avgStroke) {
double avgStroke;
avgStroke = ((double)(uint16_t)((uint8_t)newValue.at(index))) / 2.0;
avgStroke = ((double)(uint16_t)((uint8_t)newValue.at(index))) / cadence_divider;
index += 1;
emit debug(QStringLiteral("Current Average Stroke: ") + QString::number(avgStroke));
}
@@ -240,7 +244,7 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
double watt =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
index += 2;
if(!filterWattNull || watt != 0) {
if (!filterWattNull || watt != 0) {
m_watt = watt;
}
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
@@ -263,8 +267,7 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
}
if (Flags.expEnergy) {
if (Flags.expEnergy && index + 1 < newValue.length()) {
KCal = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
index += 2;
@@ -276,7 +279,7 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
} else {
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() *
((((0.048 * ((double)watts()) + 1.19) * settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() *
3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
@@ -287,12 +290,12 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
{
if (Flags.heartRate) {
if (Flags.heartRate && !disable_hr_frommachinery) {
if (index < newValue.length()) {
Heart = ((double)((newValue.at(index))));
// index += 1; //NOTE: clang-analyzer-deadcode.DeadStores
@@ -341,8 +344,8 @@ void ftmsrower::characteristicChanged(const QLowEnergyCharacteristic &characteri
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
@@ -454,11 +457,11 @@ void ftmsrower::stateChanged(QLowEnergyService::ServiceState state) {
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
@@ -540,8 +543,9 @@ void ftmsrower::deviceDiscovered(const QBluetoothDeviceInfo &device) {
{
bluetoothDevice = device;
if(device.name().trimmed().toUpper().startsWith("WHIPR")) {
if (device.name().trimmed().toUpper().startsWith("WHIPR")) {
filterWattNull = true;
WHIPR = true;
qDebug() << "WHIPR found! filtering null wattage";
}

View File

@@ -68,6 +68,7 @@ class ftmsrower : public rower {
bool noHeartService = false;
bool filterWattNull = false;
bool WHIPR = false;
#ifdef Q_OS_IOS
lockscreen *h = 0;

View File

@@ -9,27 +9,29 @@ gpx::gpx(QObject *parent) : QObject(parent) {}
QList<gpx_altitude_point_for_treadmill> gpx::open(const QString &gpx) {
QSettings settings;
bool treadmill_force_speed = settings.value(QStringLiteral("treadmill_force_speed"), false).toBool();
const double meter_limit_for_auto_loop = 300;
bool treadmill_force_speed =
settings.value(QZSettings::treadmill_force_speed, QZSettings::default_treadmill_force_speed).toBool();
bool gpx_loop = settings.value(QZSettings::gpx_loop, QZSettings::default_gpx_loop).toBool();
QFile input(gpx);
input.open(QIODevice::ReadOnly);
QDomDocument doc;
doc.setContent(&input);
QDomNodeList metadata = doc.elementsByTagName(QStringLiteral("metadata"));
if(metadata.size()) {
if (metadata.size()) {
QDomNodeList list = metadata.at(0).childNodes();
for(int i=0; i<list.count(); i++) {
if(list.at(i).nodeName().toLower() == "video") {
for (int i = 0; i < list.count(); i++) {
if (list.at(i).nodeName().toLower() == "video") {
QString video = list.at(i).toElement().firstChild().nodeValue();
if(!video.isEmpty()) {
if (!video.isEmpty()) {
videoUrl = video;
qDebug() << "gpx::videoUrl " << videoUrl;
break;
}
}
}
}
QDomNodeList points = doc.elementsByTagName(QStringLiteral("trkpt"));
for (int i = 0; i < points.size(); i++) {
QDomNode point = points.item(i);
@@ -41,13 +43,21 @@ QList<gpx_altitude_point_for_treadmill> gpx::open(const QString &gpx) {
gpx_point g;
// 2020-10-10T10:54:45
g.time = QDateTime::fromString(time.text(), Qt::ISODate);
g.p.setAltitude(ele.text().toFloat());
g.p.setLatitude(lat.toFloat());
g.p.setLongitude(lon.toFloat());
g.p.setAltitude(ele.text().toDouble());
g.p.setLatitude(lat.toDouble());
g.p.setLongitude(lon.toDouble());
this->points.append(g);
}
const uint8_t secondsInclination = 60;
if (gpx_loop && this->points.size() > 2 &&
this->points.first().p.distanceTo(this->points.last().p) >= meter_limit_for_auto_loop) {
for (int i =
this->points.size() - 2 /* -2 because otherwise the first point will be the same as the last point */;
i >= 0; i--) {
this->points.append(this->points.at(i));
}
}
QList<gpx_altitude_point_for_treadmill> inclinationList;
if (this->points.isEmpty()) {
@@ -57,19 +67,31 @@ QList<gpx_altitude_point_for_treadmill> gpx::open(const QString &gpx) {
gpx_point pP = this->points.constFirst();
if (treadmill_force_speed) {
// starting point
gpx_altitude_point_for_treadmill g;
g.distance = 0;
g.inclination = 0;
g.elevation = pP.p.altitude();
g.latitude = pP.p.latitude();
g.longitude = pP.p.longitude();
g.seconds = 0;
inclinationList.append(g);
for (int32_t i = 1; i < this->points.count(); i++) {
qint64 dT = qAbs(pP.time.secsTo(this->points.at(i).time));
if (dT < secondsInclination) {
continue;
}
double distance = this->points.at(i).p.distanceTo(pP.p);
double elevation = this->points.at(i).p.altitude() - pP.p.altitude();
if (distance == 0 || dT == 0) {
continue;
}
pP = this->points[i];
gpx_altitude_point_for_treadmill g;
g.seconds = dT;
g.seconds = this->points.constFirst().time.secsTo(pP.time);
g.distance = distance / 1000.0;
g.speed = (distance / 1000.0) * (3600 / dT);
g.inclination = (elevation / distance) * 100;
@@ -85,11 +107,24 @@ QList<gpx_altitude_point_for_treadmill> gpx::open(const QString &gpx) {
if (!isnan(this->points.constFirst().p.latitude()) && !isnan(this->points.constFirst().p.longitude()) &&
QGeoCoordinate(this->points.first().p.latitude(), this->points.first().p.longitude())
.distanceTo(QGeoCoordinate(this->points.constLast().p.latitude(),
this->points.constLast().p.longitude())) < 300) {
this->points.constLast().p.longitude())) < meter_limit_for_auto_loop) {
// to create the circuit
this->points.append(this->points.constFirst());
this->points.last().time = this->points.at(this->points.count() - 2).time;
}
// starting point
gpx_altitude_point_for_treadmill g;
g.distance = 0;
g.inclination = 0;
g.elevation = pP.p.altitude();
g.latitude = pP.p.latitude();
g.longitude = pP.p.longitude();
g.seconds = 0;
/*qDebug() << qSetRealNumberPrecision(10) << i << g.distance << g.inclination << g.elevation << g.latitude
<< g.longitude << totDistance << pP.time;*/
inclinationList.append(g);
for (int32_t i = 1; i < this->points.count(); i++) {
double distance = this->points.at(i).p.distanceTo(pP.p);
double elevation = this->points.at(i).p.altitude() - pP.p.altitude();
@@ -108,7 +143,8 @@ QList<gpx_altitude_point_for_treadmill> gpx::open(const QString &gpx) {
g.latitude = pP.p.latitude();
g.longitude = pP.p.longitude();
g.seconds = this->points.constFirst().time.secsTo(pP.time);
// qDebug() << i << g.distance << g.inclination << g.elevation << g.latitude << g.longitude << totDistance;
/*qDebug() << qSetRealNumberPrecision(10) << i << g.distance << g.inclination << g.elevation << g.latitude
<< g.longitude << totDistance << pP.time;*/
inclinationList.append(g);
}
}

View File

@@ -10,7 +10,7 @@
class gpx_altitude_point_for_treadmill {
public:
uint32_t seconds = 0;
uint64_t seconds = 0;
float inclination = 0;
float elevation = 0;
float speed = 0;

View File

@@ -50,7 +50,10 @@ void heartratebelt::stateChanged(QLowEnergyService::ServiceState state) {
gattNotifyCharacteristic =
gattCommunicationChannelService->characteristic(QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement));
Q_ASSERT(gattNotifyCharacteristic.isValid());
if(!gattNotifyCharacteristic.isValid()) {
qDebug() << "gattNotifyCharacteristic not valid for HR";
return;
}
// establish hook into notifications
connect(gattCommunicationChannelService, &QLowEnergyService::characteristicChanged, this,
@@ -111,8 +114,8 @@ void heartratebelt::error(QLowEnergyController::Error err) {
void heartratebelt::deviceDiscovered(const QBluetoothDeviceInfo &device) {
QSettings settings;
// QString heartRateBeltName = settings.value(QStringLiteral("heart_rate_belt_name"),
// QStringLiteral("Disabled")).toString();//NOTE: clazy-unsed-non-trivial-variable
// QString heartRateBeltName = settings.value(QZSettings::heart_rate_belt_name),
// QStringLiteral("Disabled")).toString();//NOTE: clazy-unused-non-trivial-variable
emit debug(QStringLiteral("Found new device: ") + device.name() + QStringLiteral(" (") +
device.address().toString() + ')');
// if(device.name().startsWith(heartRateBeltName))

View File

@@ -19,7 +19,7 @@ homefitnessbuddy::homefitnessbuddy(bluetooth *bl, QObject *parent) : QObject(par
retry.setInterval(10s);
connect(&retry, &QTimer::timeout, this, &homefitnessbuddy::startEngine);
if (!settings.value(QStringLiteral("peloton_username"), QStringLiteral("username"))
if (!settings.value(QZSettings::peloton_username, QZSettings::default_peloton_username)
.toString()
.compare(QStringLiteral("username"))) {
qDebug() << QStringLiteral("invalid peloton credentials");
@@ -192,7 +192,7 @@ void homefitnessbuddy::search_workout_onfinish(QNetworkReply *reply) {
QSettings settings;
// NOTE: clazy-unused-non-trivial-variable
// QString difficulty = settings.value(QStringLiteral("peloton_difficulty"), QStringLiteral("lower")).toString();
// QString difficulty = settings.value(QZSettings::peloton_difficulty, QZSettings::default_peloton_difficulty).toString();
trainrows.clear();
trainrows = zwiftworkout::load(payload);

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@
#include <QChart>
#include <QColor>
#include <QGraphicsScene>
#include <QMediaPlayer>
#include <QNetworkReply>
#include <QOAuth2AuthorizationCodeFlow>
#include <QQmlApplicationEngine>
@@ -34,14 +35,19 @@ class DataObject : public QObject {
Q_PROPERTY(int labelFontSize READ labelFontSize WRITE setLabelFontSize NOTIFY labelFontSizeChanged)
Q_PROPERTY(bool writable READ writable NOTIFY writableChanged)
Q_PROPERTY(bool visibleItem READ visibleItem NOTIFY visibleChanged)
Q_PROPERTY(bool largeButton READ largeButton NOTIFY largeButtonChanged)
Q_PROPERTY(QString largeButtonColor READ largeButtonColor NOTIFY largeButtonColorChanged)
Q_PROPERTY(QString largeButtonLabel READ largeButtonLabel NOTIFY largeButtonLabelChanged)
Q_PROPERTY(QString plusName READ plusName NOTIFY plusNameChanged)
Q_PROPERTY(QString minusName READ minusName NOTIFY minusNameChanged)
Q_PROPERTY(QString identificator READ identificator)
Q_PROPERTY(QString identificator READ identificator NOTIFY identificatorChanged)
public:
DataObject(const QString &name, const QString &icon, const QString &value, bool writable, const QString &id,
int valueFontSize, int labelFontSize, const QString &valueFontColor = QStringLiteral("white"),
const QString &secondLine = QLatin1String(""), const int gridId = 0);
const QString &secondLine = QLatin1String(""), const int gridId = 0, const bool largeButton = false,
const QString largeButtonLabel = QLatin1String(""),
const QString largeButtonColor = QZSettings::default_tile_preset_resistance_1_color);
void setName(const QString &value);
void setValue(const QString &value);
void setSecondLine(const QString &value);
@@ -63,6 +69,9 @@ class DataObject : public QObject {
QString plusName() { return m_id + QStringLiteral("_plus"); }
QString minusName() { return m_id + QStringLiteral("_minus"); }
QString identificator() { return m_id; }
bool largeButton() { return m_largeButton; }
QString largeButtonLabel() { return m_largeButtonLabel; }
QString largeButtonColor() { return m_largeButtonColor; }
QString m_id;
QString m_name;
@@ -75,6 +84,9 @@ class DataObject : public QObject {
int m_labelFontSize;
bool m_writable;
bool m_visible = true;
bool m_largeButton = false;
QString m_largeButtonLabel = QLatin1String("");
QString m_largeButtonColor = QZSettings::default_tile_preset_resistance_1_color;
signals:
void valueChanged(QString value);
@@ -89,6 +101,10 @@ class DataObject : public QObject {
void visibleChanged(bool value);
void plusNameChanged(QString value);
void minusNameChanged(QString value);
void identificatorChanged(QString value);
void largeButtonChanged(bool value);
void largeButtonLabelChanged(QString value);
void largeButtonColorChanged(QString value);
};
class homeform : public QObject {
@@ -116,8 +132,9 @@ class homeform : public QObject {
Q_PROPERTY(bool licensePopupVisible READ licensePopupVisible NOTIFY licensePopupVisibleChanged WRITE
setLicensePopupVisible)
Q_PROPERTY(bool mapsVisible READ mapsVisible NOTIFY mapsVisibleChanged WRITE setMapsVisible)
Q_PROPERTY(bool videoIconVisible READ videoIconVisible NOTIFY videoIconVisibleChanged WRITE setVideoIconVisible)
Q_PROPERTY(bool videoVisible READ videoVisible NOTIFY videoVisibleChanged WRITE setVideoVisible)
Q_PROPERTY(QUrl videoPath READ videoPath)
Q_PROPERTY(QUrl videoPath READ videoPath NOTIFY videoPathChanged)
Q_PROPERTY(int videoPosition READ videoPosition NOTIFY videoPositionChanged WRITE setVideoPosition)
Q_PROPERTY(double videoRate READ videoRate NOTIFY videoRateChanged WRITE setVideoRate)
Q_PROPERTY(double currentSpeed READ currentSpeed NOTIFY currentSpeedChanged)
@@ -185,7 +202,7 @@ class homeform : public QObject {
QLinearGradient backgroundGradient;
double maxWatt = wattMaxChart();
QSettings settings;
double ftpSetting = settings.value(QStringLiteral("ftp"), 200.0).toDouble();
double ftpSetting = settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble();
/*backgroundGradient.setStart(QPointF(0, 0));
backgroundGradient.setFinalStop(QPointF(0, 1));
backgroundGradient.setColorAt((maxWatt - (ftpSetting * 0.55)) / maxWatt, QColor("white"));
@@ -228,14 +245,14 @@ class homeform : public QObject {
/*backgroundGradient.setStart(QPointF(0, 0));
backgroundGradient.setFinalStop(QPointF(0, 1));
backgroundGradient.setColorAt((220 - (maxHeartRate *
settings.value("heart_rate_zone1", 70.0).toDouble() / 100)) / 220, QColor("lightsteelblue"));
backgroundGradient.setColorAt((220 - (maxHeartRate *
settings.value("heart_rate_zone2", 80.0).toDouble() / 100)) / 220, QColor("green"));
backgroundGradient.setColorAt((220 - (maxHeartRate *
settings.value("heart_rate_zone3", 90.0).toDouble() / 100)) / 220, QColor("yellow"));
backgroundGradient.setColorAt((220 - (maxHeartRate * settings.value("heart_rate_zone4",
100.0).toDouble() / 100)) / 220, QColor("orange")); backgroundGradient.setColorAt(0.0,
QColor("red"));*/
settings.value(QZSettings::heart_rate_zone1, QZSettings::default_heart_rate_zone1).toDouble() /
100)) / 220, QColor("lightsteelblue")); backgroundGradient.setColorAt((220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone2, QZSettings::default_heart_rate_zone2).toDouble() /
100)) / 220, QColor("green")); backgroundGradient.setColorAt((220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone3, QZSettings::default_heart_rate_zone3).toDouble() /
100)) / 220, QColor("yellow")); backgroundGradient.setColorAt((220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone4, QZSettings::default_heart_rate_zone4).toDouble() /
100)) / 220, QColor("orange")); backgroundGradient.setColorAt(0.0, QColor("red")); */
// backgroundGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
// chart->setBackgroundBrush(backgroundGradient);
@@ -244,23 +261,31 @@ class homeform : public QObject {
plotAreaGradient.setStart(QPointF(0, 0));
plotAreaGradient.setFinalStop(QPointF(0, 1));
plotAreaGradient.setColorAt(
(220 -
(maxHeartRate * settings.value(QStringLiteral("heart_rate_zone1"), 70.0).toDouble() / 100)) /
(220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone1, QZSettings::default_heart_rate_zone1)
.toDouble() /
100)) /
160,
QColor(QStringLiteral("lightsteelblue")));
plotAreaGradient.setColorAt(
(220 -
(maxHeartRate * settings.value(QStringLiteral("heart_rate_zone2"), 80.0).toDouble() / 100)) /
(220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone2, QZSettings::default_heart_rate_zone2)
.toDouble() /
100)) /
160,
QColor(QStringLiteral("green")));
plotAreaGradient.setColorAt(
(220 -
(maxHeartRate * settings.value(QStringLiteral("heart_rate_zone3"), 90.0).toDouble() / 100)) /
(220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone3, QZSettings::default_heart_rate_zone3)
.toDouble() /
100)) /
160,
QColor(QStringLiteral("yellow")));
plotAreaGradient.setColorAt(
(220 -
(maxHeartRate * settings.value(QStringLiteral("heart_rate_zone4"), 100.0).toDouble() / 100)) /
(220 - (maxHeartRate *
settings.value(QZSettings::heart_rate_zone4, QZSettings::default_heart_rate_zone4)
.toDouble() /
100)) /
160,
QColor(QStringLiteral("orange")));
plotAreaGradient.setColorAt(0.0, QColor(QStringLiteral("red")));
@@ -287,7 +312,9 @@ class homeform : public QObject {
Q_INVOKABLE bool autoInclinationEnabled() {
QSettings settings;
bool virtual_bike = settings.value("virtual_device_force_bike", false).toBool();
bool virtual_bike =
settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike)
.toBool();
return bluetoothManager && bluetoothManager->device() &&
bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL && !virtual_bike &&
bluetoothManager->device()->VirtualDevice() &&
@@ -338,7 +365,8 @@ class homeform : public QObject {
bool generalPopupVisible();
bool licensePopupVisible();
bool mapsVisible();
bool videoVisible();
bool videoIconVisible();
bool videoVisible() { return m_VideoVisible; }
int videoPosition();
double videoRate();
double currentSpeed() {
@@ -361,8 +389,13 @@ class homeform : public QObject {
}
}
void setLicensePopupVisible(bool value);
void setVideoVisible(bool value);
void setVideoPosition(int position);
void setVideoIconVisible(bool value);
void setVideoVisible(bool value) {
m_VideoVisible = value;
emit videoVisibleChanged(m_VideoVisible);
}
void setVideoPosition(int position); // on startup
void videoSeekPosition(int ms); // in realtime
void setVideoRate(double rate);
void setMapsVisible(bool value);
void setGeneralPopupVisible(bool value);
@@ -380,10 +413,10 @@ class homeform : public QObject {
QSettings settings;
if (bluetoothManager && bluetoothManager->device() &&
bluetoothManager->device()->wattsMetric().max() >
(settings.value(QStringLiteral("ftp"), 200.0).toDouble() * 2)) {
(settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble() * 2)) {
return bluetoothManager->device()->wattsMetric().max();
} else {
return settings.value(QStringLiteral("ftp"), 200.0).toDouble() * 2;
return settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble() * 2;
}
}
@@ -487,6 +520,7 @@ class homeform : public QObject {
bool m_generalPopupVisible = false;
bool m_LicensePopupVisible = false;
bool m_MapsVisible = false;
bool m_VideoIconVisible = false;
bool m_VideoVisible = false;
int m_VideoPosition = 0;
double m_VideoRate = 1;
@@ -531,6 +565,7 @@ class homeform : public QObject {
DataObject *resistance;
DataObject *watt;
DataObject *avgWatt;
DataObject *avgWattLap;
DataObject *heart;
DataObject *fan;
DataObject *jouls;
@@ -563,6 +598,21 @@ class homeform : public QObject {
DataObject *instantaneousStrideLengthCM;
DataObject *groundContactMS;
DataObject *verticalOscillationMM;
DataObject *preset_resistance_1;
DataObject *preset_resistance_2;
DataObject *preset_resistance_3;
DataObject *preset_resistance_4;
DataObject *preset_resistance_5;
DataObject *preset_speed_1;
DataObject *preset_speed_2;
DataObject *preset_speed_3;
DataObject *preset_speed_4;
DataObject *preset_speed_5;
DataObject *preset_inclination_1;
DataObject *preset_inclination_2;
DataObject *preset_inclination_3;
DataObject *preset_inclination_4;
DataObject *preset_inclination_5;
QTimer *timer;
QTimer *backupTimer;
@@ -575,7 +625,6 @@ class homeform : public QObject {
const QUrl &clientIdentifierSharedKey);
bool strava_upload_file(const QByteArray &data, const QString &remotename);
const QString cryptoKeySettingsProfilesTag = QStringLiteral("cryptoKeySettingsProfiles");
quint64 cryptoKeySettingsProfiles();
int16_t fanOverride = 0;
@@ -592,10 +641,13 @@ class homeform : public QObject {
#if defined(Q_OS_WIN) || (defined(Q_OS_MAC) && !defined(Q_OS_IOS))
QTimer tLicense;
QNetworkAccessManager *mgr = nullptr;
void licenseRequest();
#endif
QGeoPath gpx_preview;
PathController pathController;
bool videoMustBeReset = true;
public slots:
void aboutToQuit();
@@ -611,6 +663,7 @@ class homeform : public QObject {
void Lap();
void Minus(const QString &);
void Plus(const QString &);
void LargeButton(const QString &);
void volumeDown();
void volumeUp();
void keyMediaPrevious();
@@ -675,8 +728,10 @@ class homeform : public QObject {
void changePelotonProvider(QString value);
void generalPopupVisibleChanged(bool value);
void licensePopupVisibleChanged(bool value);
void videoIconVisibleChanged(bool value);
void videoVisibleChanged(bool value);
void videoPositionChanged(int value);
void videoPathChanged(QUrl value);
void videoRateChanged(double value);
void currentSpeedChanged(double value);
void mapsVisibleChanged(bool value);

View File

@@ -35,15 +35,31 @@ void horizongr7bike::writeCharacteristic(uint8_t *data, uint8_t data_len, const
bool wait_for_response) {
QEventLoop loop;
QTimer timeout;
if (wait_for_response) {
connect(gattFTMSService, &QLowEnergyService::characteristicChanged, &loop, &QEventLoop::quit);
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
} else {
connect(gattFTMSService, &QLowEnergyService::characteristicWritten, &loop, &QEventLoop::quit);
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
}
gattFTMSService->writeCharacteristic(gattWriteCharControlPointId, QByteArray((const char *)data, data_len));
if (gattFTMSService) {
if (wait_for_response) {
connect(gattFTMSService, &QLowEnergyService::characteristicChanged, &loop, &QEventLoop::quit);
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
} else {
connect(gattFTMSService, &QLowEnergyService::characteristicWritten, &loop, &QEventLoop::quit);
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
}
gattFTMSService->writeCharacteristic(gattWriteCharControlPointId, QByteArray((const char *)data, data_len));
} else if (customService && customWriteChar.isValid()) {
if (wait_for_response) {
connect(customService, &QLowEnergyService::characteristicChanged, &loop, &QEventLoop::quit);
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
} else {
connect(customService, &QLowEnergyService::characteristicWritten, &loop, &QEventLoop::quit);
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
}
customService->writeCharacteristic(customWriteChar, QByteArray((const char *)data, data_len));
} else {
qDebug() << "writeCharacteristic error!";
return;
}
if (!disable_log) {
emit debug(QStringLiteral(" >> ") + QByteArray((const char *)data, data_len).toHex(' ') +
@@ -128,18 +144,10 @@ void horizongr7bike::characteristicChanged(const QLowEnergyCharacteristic &chara
Q_UNUSED(characteristic);
QSettings settings;
QString heartRateBeltName =
settings.value(QStringLiteral("heart_rate_belt_name"), QStringLiteral("Disabled")).toString();
bool disable_hr_frommachinery = settings.value(QStringLiteral("heart_ignore_builtin"), false).toBool();
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
bool disable_hr_frommachinery = settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
static bool firstPacket = false;
emit debug(QStringLiteral(" << ") + newValue.toHex(' '));
if (characteristic.uuid() != QBluetoothUuid((quint16)0x2AD2)) {
return;
}
lastPacket = newValue;
union flags {
struct {
uint16_t moreData : 1;
@@ -162,150 +170,206 @@ void horizongr7bike::characteristicChanged(const QLowEnergyCharacteristic &chara
};
flags Flags;
int index = 0;
Flags.word_flags = (newValue.at(1) << 8) | newValue.at(0);
index += 2;
Flags.heartRate = 0;
if (!Flags.moreData) {
if (!settings.value(QStringLiteral("speed_power_based"), false).toBool()) {
Speed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
} else {
Speed = metric::calculateSpeedFromPower(m_watt.value(), Inclination.value());
}
index += 2;
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
}
emit debug(QStringLiteral(" << ") + characteristic.uuid().toString() + " " + newValue.toHex(' '));
if (Flags.avgSpeed) {
double avgSpeed;
avgSpeed =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
index += 2;
emit debug(QStringLiteral("Current Average Speed: ") + QString::number(avgSpeed));
}
if (Flags.instantCadence) {
if (settings.value(QStringLiteral("cadence_sensor_name"), QStringLiteral("Disabled"))
.toString()
.startsWith(QStringLiteral("Disabled"))) {
// this bike sent a cadence 1/10 of the real one
Cadence = (((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
2.0) *
settings.value(QStringLiteral("horizon_gr7_cadence_multiplier"), 1.0).toDouble();
}
index += 2;
if (gattFTMSService == nullptr && characteristic.uuid() == QBluetoothUuid((quint16)0xfff4) &&
newValue.length() == 13) {
Cadence = newValue.at(11);
emit debug(QStringLiteral("Current Cadence: ") + QString::number(Cadence.value()));
}
if (Flags.avgCadence) {
double avgCadence;
avgCadence =
(((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index)))) /
2.0) *
settings.value(QStringLiteral("horizon_gr7_cadence_multiplier"), 1.0).toDouble();
index += 2;
emit debug(QStringLiteral("Current Average Cadence: ") + QString::number(avgCadence));
}
if (Flags.totDistance) {
// this bike sent the distance but it doesn't send the avg cadence, so the parsing is wrong.
// Let's calculate the distance by software
/*Distance = ((double)((((uint32_t)((uint8_t)newValue.at(index + 2)) << 16) |
(uint32_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint32_t)((uint8_t)newValue.at(index)))) /
1000.0;*/
if (firstPacket)
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
index += 3;
} else {
if (firstPacket)
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
}
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
if (Flags.resistanceLvl) {
Resistance =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
emit resistanceRead(Resistance.value());
m_pelotonResistance = bikeResistanceToPeloton(Resistance.value());
index += 2;
Resistance = newValue.at(12);
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
}
if (Flags.instantPower) {
if (settings.value(QStringLiteral("power_sensor_name"), QStringLiteral("Disabled"))
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
}
if (Heart.value() > 0) {
int avgP = ((settings.value(QZSettings::power_hr_pwr1, QZSettings::default_power_hr_pwr1).toDouble() *
settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble()) -
(settings.value(QZSettings::power_hr_pwr2, QZSettings::default_power_hr_pwr2).toDouble() *
settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble())) /
(settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble() -
settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble()) +
(Heart.value() * ((settings.value(QZSettings::power_hr_pwr1, QZSettings::default_power_hr_pwr1).toDouble() -
settings.value(QZSettings::power_hr_pwr2, QZSettings::default_power_hr_pwr2).toDouble()) /
(settings.value(QZSettings::power_hr_hr1, QZSettings::default_power_hr_hr1).toDouble() -
settings.value(QZSettings::power_hr_hr2, QZSettings::default_power_hr_hr2).toDouble())));
if (avgP < 50) {
avgP = 50;
}
m_watt = avgP;
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
}
if (Flags.avgPower) {
double avgPower;
avgPower =
((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Average Watt: ") + QString::number(avgPower));
}
if (Flags.expEnergy && newValue.length() > index + 1) {
KCal = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) | (uint16_t)((uint8_t)newValue.at(index))));
index += 2;
// energy per hour
index += 2;
// energy per minute
index += 1;
} else {
if (watts())
KCal +=
((((0.048 * ((double)watts()) + 1.19) * settings.value(QStringLiteral("weight"), 75.0).toFloat() *
((((0.048 * ((double)watts()) + 1.19) * settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() *
3.5) /
200.0) /
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
// kg * 3.5) / 200 ) / 60
}
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = Cadence.value() * settings.value(QZSettings::cadence_sensor_speed_ratio, QZSettings::default_cadence_sensor_speed_ratio).toDouble();
} else {
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
return;
} else if (characteristic.uuid() == QBluetoothUuid((quint16)0x2AD2)) {
lastPacket = newValue;
int index = 0;
Flags.word_flags = (newValue.at(1) << 8) | newValue.at(0);
index += 2;
if (!Flags.moreData) {
if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool()) {
Speed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
} else {
Speed = metric::calculateSpeedFromPower(watts(), Inclination.value(), Speed.value(),fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
}
index += 2;
emit debug(QStringLiteral("Current Speed: ") + QString::number(Speed.value()));
}
if (Flags.avgSpeed) {
double avgSpeed;
avgSpeed = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
100.0;
index += 2;
emit debug(QStringLiteral("Current Average Speed: ") + QString::number(avgSpeed));
}
if (Flags.instantCadence) {
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled"))) {
// this bike sent a cadence 1/10 of the real one
Cadence = (((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
2.0) *
settings.value(QZSettings::horizon_gr7_cadence_multiplier, QZSettings::default_horizon_gr7_cadence_multiplier).toDouble();
}
index += 2;
emit debug(QStringLiteral("Current Cadence: ") + QString::number(Cadence.value()));
}
if (Flags.avgCadence) {
double avgCadence;
avgCadence = (((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index)))) /
2.0) *
settings.value(QZSettings::horizon_gr7_cadence_multiplier, QZSettings::default_horizon_gr7_cadence_multiplier).toDouble();
index += 2;
emit debug(QStringLiteral("Current Average Cadence: ") + QString::number(avgCadence));
}
if (Flags.totDistance) {
// this bike sent the distance but it doesn't send the avg cadence, so the parsing is wrong.
// Let's calculate the distance by software
/*Distance = ((double)((((uint32_t)((uint8_t)newValue.at(index + 2)) << 16) |
(uint32_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint32_t)((uint8_t)newValue.at(index)))) /
1000.0;*/
if (firstPacket)
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
index += 3;
} else {
if (firstPacket)
Distance += ((Speed.value() / 3600000.0) *
((double)lastRefreshCharacteristicChanged.msecsTo(QDateTime::currentDateTime())));
}
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
if (Flags.resistanceLvl) {
Resistance = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
emit resistanceRead(Resistance.value());
m_pelotonResistance = bikeResistanceToPeloton(Resistance.value());
index += 2;
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
}
if (Flags.instantPower) {
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
.toString()
.startsWith(QStringLiteral("Disabled")))
m_watt = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
}
if (Flags.avgPower) {
double avgPower;
avgPower = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
emit debug(QStringLiteral("Current Average Watt: ") + QString::number(avgPower));
}
if (Flags.expEnergy && newValue.length() > index + 1) {
KCal = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
(uint16_t)((uint8_t)newValue.at(index))));
index += 2;
// energy per hour
index += 2;
// energy per minute
index += 1;
} else {
if (watts())
KCal += ((((0.048 * ((double)watts()) + 1.19) *
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
200.0) /
(60000.0 /
((double)lastRefreshCharacteristicChanged.msecsTo(
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
// kg * 3.5) / 200 ) / 60
}
emit debug(QStringLiteral("Current KCal: ") + QString::number(KCal.value()));
#ifdef Q_OS_ANDROID
if (settings.value("ant_heart", false).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
Heart = (uint8_t)KeepAwakeHelper::heart();
else
#endif
{
if (Flags.heartRate && !disable_hr_frommachinery && newValue.length() > index) {
Heart = ((double)((newValue.at(index))));
// index += 1; // NOTE: clang-analyzer-deadcode.DeadStores
emit debug(QStringLiteral("Current Heart: ") + QString::number(Heart.value()));
} else {
Flags.heartRate = false;
{
if (Flags.heartRate && !disable_hr_frommachinery && newValue.length() > index) {
Heart = ((double)((newValue.at(index))));
// index += 1; // NOTE: clang-analyzer-deadcode.DeadStores
emit debug(QStringLiteral("Current Heart: ") + QString::number(Heart.value()));
} else {
Flags.heartRate = false;
}
}
}
if (Flags.metabolic) {
// todo
}
if (Flags.metabolic) {
// todo
}
if (Flags.elapsedTime) {
// todo
}
if (Flags.elapsedTime) {
// todo
}
if (Flags.remainingTime) {
// todo
if (Flags.remainingTime) {
// todo
}
}
if (Cadence.value() > 0) {
@@ -331,8 +395,8 @@ void horizongr7bike::characteristicChanged(const QLowEnergyCharacteristic &chara
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
@@ -350,6 +414,13 @@ void horizongr7bike::characteristicChanged(const QLowEnergyCharacteristic &chara
firstPacket = true;
}
void horizongr7bike::btinit() {
if (gattFTMSService == nullptr) {
uint8_t write[] = {0x55, 0xaa, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0a};
writeCharacteristic(write, sizeof(write), QStringLiteral("init"));
}
}
void horizongr7bike::stateChanged(QLowEnergyService::ServiceState state) {
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceState>();
emit debug(QStringLiteral("BTLE stateChanged ") + QString::fromLocal8Bit(metaEnum.valueToKey(state)));
@@ -421,15 +492,22 @@ void horizongr7bike::stateChanged(QLowEnergyService::ServiceState state) {
}
QBluetoothUuid _gattWriteCharControlPointId((quint16)0x2AD9);
QBluetoothUuid _customWriteChar((quint16)0xFFF3);
if (c.properties() & QLowEnergyCharacteristic::Write && c.uuid() == _gattWriteCharControlPointId) {
qDebug() << QStringLiteral("FTMS service and Control Point found");
gattWriteCharControlPointId = c;
gattFTMSService = s;
} else if (c.properties() & QLowEnergyCharacteristic::Write && c.uuid() == _customWriteChar) {
qDebug() << QStringLiteral("Custom Service found");
customWriteChar = c;
customService = s;
}
}
}
}
btinit();
// ******************************************* virtual bike init *************************************
if (!firstStateChanged && !virtualBike
#ifdef Q_OS_IOS
@@ -439,11 +517,11 @@ void horizongr7bike::stateChanged(QLowEnergyService::ServiceState state) {
#endif
) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
bool cadence = settings.value("bike_cadence_sensor", false).toBool();
bool ios_peloton_workaround = settings.value("ios_peloton_workaround", true).toBool();
bool cadence = settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
bool ios_peloton_workaround = settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
if (ios_peloton_workaround && cadence) {
qDebug() << "ios_peloton_workaround activated!";
h = new lockscreen();

View File

@@ -47,6 +47,7 @@ class horizongr7bike : public bike {
void writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
bool wait_for_response = false);
void startDiscover();
void btinit();
uint16_t watts();
void forceResistance(resistance_t requestResistance);
@@ -55,7 +56,10 @@ class horizongr7bike : public bike {
QList<QLowEnergyService *> gattCommunicationChannelService;
QLowEnergyCharacteristic gattWriteCharControlPointId;
QLowEnergyService *gattFTMSService;
QLowEnergyService *gattFTMSService = nullptr;
QLowEnergyService* customService = nullptr;
QLowEnergyCharacteristic customWriteChar;
double bikeResistanceToPeloton(double resistance);
const resistance_t max_resistance = 12;

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,8 @@ class horizontreadmill : public treadmill {
bool connected();
void forceSpeed(double requestSpeed);
void forceIncline(double requestIncline);
double minStepInclination();
double minStepSpeed();
void *VirtualTreadmill();
void *VirtualDevice();
@@ -78,6 +80,8 @@ class horizontreadmill : public treadmill {
double lastInclination = 0;
int64_t lastStart = 0;
int64_t lastStop = 0;
bool horizonPaused = false;
double lastHorizonForceSpeed = 0;
bool initDone = false;
bool initRequest = false;
@@ -87,7 +91,71 @@ class horizontreadmill : public treadmill {
int32_t customRecv = 0;
int32_t messageID = 0;
int GenerateCRC_CCITT(uint8_t *PUPtr8, int PU16_Count);
void testProfileCRC();
void updateProfileCRC();
int GenerateCRC_CCITT(uint8_t *PUPtr8, int PU16_Count, int crcStart = 65535);
bool checkIfForceSpeedNeeding(double requestSpeed);
// profiles
uint8_t initData7[20] = {0x55, 0xaa, 0x02, 0x00, 0x01, 0x16, 0xdb, 0x02, 0xed, 0xc2,
0x00, 0x47, 0x75, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00};
uint8_t initData8[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x05, 0xc2, 0x07};
uint8_t initData10[20] = {0x01, 0x01, 0x00, 0xd3, 0x8a, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData11[20] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData12[20] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData13[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData14[1] = {0x30};
uint8_t initData7_1[20] = {0x55, 0xaa, 0x03, 0x00, 0x01, 0x16, 0xdb, 0x02, 0xae, 0x2a,
0x01, 0x41, 0x69, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9_1[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x06, 0xc4, 0x07};
uint8_t initData10_1[20] = {0x09, 0x1c, 0x00, 0x9f, 0xef, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData7_2[20] = {0x55, 0xaa, 0x04, 0x00, 0x01, 0x16, 0xdb, 0x02, 0xae, 0x2a,
0x01, 0x41, 0x69, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9_2[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x06, 0xc4, 0x07};
uint8_t initData10_2[20] = {0x09, 0x1c, 0x00, 0x9f, 0xef, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData7_3[20] = {0x55, 0xaa, 0x05, 0x00, 0x01, 0x16, 0xdb, 0x02, 0xa9, 0xe7,
0x02, 0x4d, 0x65, 0x67, 0x68, 0x61, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9_3[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x06, 0xc5, 0x07};
uint8_t initData10_3[20] = {0x0b, 0x0f, 0x00, 0x4b, 0x40, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData7_4[20] = {0x55, 0xaa, 0x06, 0x00, 0x01, 0x16, 0xdb, 0x02, 0xbc, 0x76,
0x03, 0x44, 0x61, 0x72, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9_4[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0xca, 0x07};
uint8_t initData10_4[20] = {0x05, 0x1c, 0x00, 0x07, 0x25, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData7_5[20] = {0x55, 0xaa, 0x07, 0x00, 0x01, 0x16, 0xdb, 0x02, 0x7d, 0xeb,
0x04, 0x41, 0x68, 0x6f, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00};
uint8_t initData9_5[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x04, 0xcc, 0x07};
uint8_t initData10_5[20] = {0x01, 0x08, 0x00, 0xc2, 0x0f, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
uint8_t initData7_6[20] = {0x55, 0xaa, 0x08, 0x00, 0x01, 0x16, 0xdb, 0x02, 0x03, 0x0d,
0x05, 0x55, 0x73, 0x65, 0x72, 0x20, 0x35, 0x00, 0x00, 0x00};
uint8_t initData9_6[20] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x05, 0xc2, 0x07};
uint8_t initData10_6[20] = {0x01, 0x01, 0x00, 0x8e, 0x6a, 0x0c, 0x00, 0x01, 0x01, 0x02,
0x23, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30};
// profiles end
#ifdef Q_OS_IOS
lockscreen *h = 0;

View File

@@ -78,15 +78,17 @@ void iconceptbike::update() {
if (initDone) {
// ******************************************* virtual treadmill init *************************************
if (!virtualBike) {
if (!firstStateChanged && !virtualBike) {
QSettings settings;
bool virtual_device_enabled = settings.value(QStringLiteral("virtual_device_enabled"), true).toBool();
bool virtual_device_enabled = settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
if (virtual_device_enabled) {
emit debug(QStringLiteral("creating virtual treadmill interface..."));
emit debug(QStringLiteral("creating virtual bike interface..."));
virtualBike = new virtualbike(this, true);
connect(virtualBike, &virtualbike::changeInclination, this, &iconceptbike::changeInclination);
}
}
firstStateChanged = 1;
// ********************************************************************************************************
if (requestResistance != -1) {

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