mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
116 Commits
Mobi-Rower
...
build-1066
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f7e6e954f | ||
|
|
a540966dcf | ||
|
|
9f3b1af7c6 | ||
|
|
e898513977 | ||
|
|
7dec26350b | ||
|
|
905f624c07 | ||
|
|
6269cfe662 | ||
|
|
10e45b0d0b | ||
|
|
d95455b636 | ||
|
|
469329b6dc | ||
|
|
1b61119ecd | ||
|
|
f1e31a9b83 | ||
|
|
d6852988c4 | ||
|
|
43e27fc70c | ||
|
|
6b18862106 | ||
|
|
5328a94bbb | ||
|
|
be4d6abd14 | ||
|
|
b35bebadae | ||
|
|
89427c2fcc | ||
|
|
c57495897d | ||
|
|
6358a55657 | ||
|
|
371d2145b6 | ||
|
|
55b2372aa2 | ||
|
|
3646f21d22 | ||
|
|
05e90fba20 | ||
|
|
9a5f42b0b2 | ||
|
|
1f002031f3 | ||
|
|
33a41b45a2 | ||
|
|
9ac7eb7110 | ||
|
|
7051bbae6e | ||
|
|
6c17df063a | ||
|
|
89b72c74e0 | ||
|
|
ae78dd951e | ||
|
|
0a9a3f9937 | ||
|
|
468c3d7a27 | ||
|
|
faab1b77a8 | ||
|
|
a23afabb6e | ||
|
|
f5fd16e0e6 | ||
|
|
0962e1a70e | ||
|
|
3d54b82428 | ||
|
|
6a04908812 | ||
|
|
1460c6190e | ||
|
|
32fcb198f8 | ||
|
|
3f04bb2ff0 | ||
|
|
fec16ee496 | ||
|
|
4f0ac7e6ea | ||
|
|
ba79162a28 | ||
|
|
93477f4008 | ||
|
|
6e0b958e56 | ||
|
|
f3f219309a | ||
|
|
860e296ae3 | ||
|
|
505cd193d3 | ||
|
|
f5ed321def | ||
|
|
490039c9a0 | ||
|
|
88eaf9d73c | ||
|
|
05979de55a | ||
|
|
fb3c34f91f | ||
|
|
a5616561d3 | ||
|
|
835a8395bf | ||
|
|
d61a55394b | ||
|
|
e2233d0a86 | ||
|
|
075a0dc368 | ||
|
|
5f18c9f81c | ||
|
|
2f2c084db3 | ||
|
|
68337ee54d | ||
|
|
e69ddb8a26 | ||
|
|
69b227373a | ||
|
|
fbb9524afd | ||
|
|
937f98c77a | ||
|
|
0ec7619408 | ||
|
|
344640fe69 | ||
|
|
26effcd6b2 | ||
|
|
206e6e6fec | ||
|
|
d2ba73b148 | ||
|
|
df8ac18a0f | ||
|
|
cfcf86adcc | ||
|
|
957d2934db | ||
|
|
7c73975c34 | ||
|
|
999ce8022a | ||
|
|
c3fa23159a | ||
|
|
6b674be421 | ||
|
|
7af98b4992 | ||
|
|
8380827494 | ||
|
|
a3a8825d48 | ||
|
|
d8b1dce8de | ||
|
|
6e141bf83e | ||
|
|
f32ad41917 | ||
|
|
e75a5bcb0a | ||
|
|
0a017ca4e6 | ||
|
|
3ceb256272 | ||
|
|
6b8bb96aad | ||
|
|
71cb562b05 | ||
|
|
de77f2aa4e | ||
|
|
316c34213d | ||
|
|
860489616c | ||
|
|
730c16f7b4 | ||
|
|
2bdbeed5f4 | ||
|
|
1946b46665 | ||
|
|
288cb3974b | ||
|
|
345b0d2f74 | ||
|
|
c8355184f2 | ||
|
|
4f7c7fa7c9 | ||
|
|
c75b6ae5f0 | ||
|
|
254067d2b7 | ||
|
|
682f3f612b | ||
|
|
2604031967 | ||
|
|
aacb38aee1 | ||
|
|
6969bb2680 | ||
|
|
e8e17b5338 | ||
|
|
45f0240c3a | ||
|
|
88ee55d4b6 | ||
|
|
e20ec07926 | ||
|
|
cd56f632da | ||
|
|
b5463efa44 | ||
|
|
2f41754da9 | ||
|
|
3e82ac83ab |
@@ -474,6 +474,7 @@
|
||||
87BE6FDE272D2A3E00C35795 /* moc_horizongr7bike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87BE6FDD272D2A3E00C35795 /* moc_horizongr7bike.cpp */; };
|
||||
87BF116D298E28CA00B5B6E7 /* pelotonbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87BF116C298E28CA00B5B6E7 /* pelotonbike.cpp */; };
|
||||
87BF116F298E28EC00B5B6E7 /* moc_pelotonbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87BF116E298E28EC00B5B6E7 /* moc_pelotonbike.cpp */; };
|
||||
87BFEA2F2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87BFEA2E2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm */; };
|
||||
87C424262BC1294000503687 /* moc_treadmillErgTable.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87C424252BC1294000503687 /* moc_treadmillErgTable.cpp */; };
|
||||
87C481FA26DFA7C3006211AD /* eliterizer.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87C481F926DFA7C3006211AD /* eliterizer.cpp */; };
|
||||
87C481FC26DFA7D1006211AD /* moc_eliterizer.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87C481FB26DFA7D1006211AD /* moc_eliterizer.cpp */; };
|
||||
@@ -525,6 +526,12 @@
|
||||
87D5DC4228230496008CCDE7 /* moc_truetreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D5DC4128230496008CCDE7 /* moc_truetreadmill.cpp */; };
|
||||
87D91F9A2800B9970026D43C /* proformwifibike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D91F992800B9970026D43C /* proformwifibike.cpp */; };
|
||||
87D91F9C2800B9B90026D43C /* moc_proformwifibike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87D91F9B2800B9B90026D43C /* moc_proformwifibike.cpp */; };
|
||||
87DA62A42D2305E4008ADA0F /* moc_characteristicnotifier0002.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA62A22D2305E4008ADA0F /* moc_characteristicnotifier0002.cpp */; };
|
||||
87DA62A52D2305E4008ADA0F /* moc_characteristicwriteprocessor0003.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA62A32D2305E4008ADA0F /* moc_characteristicwriteprocessor0003.cpp */; };
|
||||
87DA62AA2D2305F5008ADA0F /* characteristicwriteprocessor0003.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA62A92D2305F5008ADA0F /* characteristicwriteprocessor0003.cpp */; };
|
||||
87DA62AB2D2305F5008ADA0F /* characteristicnotifier0002.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA62A72D2305F5008ADA0F /* characteristicnotifier0002.cpp */; };
|
||||
87DA62AF2D2426F2008ADA0F /* characteristicnotifier0004.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA62AD2D2426F2008ADA0F /* characteristicnotifier0004.cpp */; };
|
||||
87DA62B02D2426F2008ADA0F /* moc_characteristicnotifier0004.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA62AE2D2426F2008ADA0F /* moc_characteristicnotifier0004.cpp */; };
|
||||
87DA76502848F98200A71B64 /* libQt5TextToSpeech.a in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 87DA764F2848F8F200A71B64 /* libQt5TextToSpeech.a */; };
|
||||
87DA8465284933D200B550E9 /* fakeelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA8464284933D200B550E9 /* fakeelliptical.cpp */; };
|
||||
87DA8467284933DE00B550E9 /* moc_fakeelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DA8466284933DE00B550E9 /* moc_fakeelliptical.cpp */; };
|
||||
@@ -1480,6 +1487,8 @@
|
||||
87BF116B298E28CA00B5B6E7 /* pelotonbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pelotonbike.h; path = ../src/devices/pelotonbike/pelotonbike.h; sourceTree = "<group>"; };
|
||||
87BF116C298E28CA00B5B6E7 /* pelotonbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pelotonbike.cpp; path = ../src/devices/pelotonbike/pelotonbike.cpp; sourceTree = "<group>"; };
|
||||
87BF116E298E28EC00B5B6E7 /* moc_pelotonbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_pelotonbike.cpp; sourceTree = "<group>"; };
|
||||
87BFEA2D2CEDDEEE00BDD759 /* ios_echelonconnectsport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ios_echelonconnectsport.h; path = ../src/ios/ios_echelonconnectsport.h; sourceTree = SOURCE_ROOT; };
|
||||
87BFEA2E2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_echelonconnectsport.mm; path = ../src/ios/ios_echelonconnectsport.mm; sourceTree = SOURCE_ROOT; };
|
||||
87C424252BC1294000503687 /* moc_treadmillErgTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_treadmillErgTable.cpp; sourceTree = "<group>"; };
|
||||
87C481F826DFA7C3006211AD /* eliterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = eliterizer.h; path = ../src/devices/eliterizer/eliterizer.h; sourceTree = "<group>"; };
|
||||
87C481F926DFA7C3006211AD /* eliterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = eliterizer.cpp; path = ../src/devices/eliterizer/eliterizer.cpp; sourceTree = "<group>"; };
|
||||
@@ -1557,6 +1566,12 @@
|
||||
87D91F982800B9970026D43C /* proformwifibike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proformwifibike.h; path = ../src/devices/proformwifibike/proformwifibike.h; sourceTree = "<group>"; };
|
||||
87D91F992800B9970026D43C /* proformwifibike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = proformwifibike.cpp; path = ../src/devices/proformwifibike/proformwifibike.cpp; sourceTree = "<group>"; };
|
||||
87D91F9B2800B9B90026D43C /* moc_proformwifibike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_proformwifibike.cpp; sourceTree = "<group>"; };
|
||||
87DA62A22D2305E4008ADA0F /* moc_characteristicnotifier0002.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_characteristicnotifier0002.cpp; sourceTree = "<group>"; };
|
||||
87DA62A32D2305E4008ADA0F /* moc_characteristicwriteprocessor0003.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_characteristicwriteprocessor0003.cpp; sourceTree = "<group>"; };
|
||||
87DA62A72D2305F5008ADA0F /* characteristicnotifier0002.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicnotifier0002.cpp; path = ../src/characteristics/characteristicnotifier0002.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87DA62A92D2305F5008ADA0F /* characteristicwriteprocessor0003.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicwriteprocessor0003.cpp; path = ../src/characteristics/characteristicwriteprocessor0003.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87DA62AD2D2426F2008ADA0F /* characteristicnotifier0004.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicnotifier0004.cpp; path = ../src/characteristics/characteristicnotifier0004.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87DA62AE2D2426F2008ADA0F /* moc_characteristicnotifier0004.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_characteristicnotifier0004.cpp; sourceTree = "<group>"; };
|
||||
87DA764F2848F8F200A71B64 /* libQt5TextToSpeech.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libQt5TextToSpeech.a; path = ../../Qt/5.15.2/ios/lib/libQt5TextToSpeech.a; sourceTree = "<group>"; };
|
||||
87DA8463284933D200B550E9 /* fakeelliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fakeelliptical.h; path = ../src/devices/fakeelliptical/fakeelliptical.h; sourceTree = "<group>"; };
|
||||
87DA8464284933D200B550E9 /* fakeelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fakeelliptical.cpp; path = ../src/devices/fakeelliptical/fakeelliptical.cpp; sourceTree = "<group>"; };
|
||||
@@ -2199,6 +2214,8 @@
|
||||
2EB56BE3C2D93CDAB0C52E67 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
87BFEA2D2CEDDEEE00BDD759 /* ios_echelonconnectsport.h */,
|
||||
87BFEA2E2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm */,
|
||||
8767CA522DA3C1FD0003001F /* elitesquarecontroller.h */,
|
||||
8767CA532DA3C1FD0003001F /* elitesquarecontroller.cpp */,
|
||||
8767CA542DA3C1FD0003001F /* moc_elitesquarecontroller.cpp */,
|
||||
@@ -2859,6 +2876,19 @@
|
||||
name = AppleWatchToIpad;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8798FDCA2D6F338200CF8EE8 /* Recovered References */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
87DA62A92D2305F5008ADA0F /* characteristicwriteprocessor0003.cpp */,
|
||||
87DA62A72D2305F5008ADA0F /* characteristicnotifier0002.cpp */,
|
||||
87DA62A22D2305E4008ADA0F /* moc_characteristicnotifier0002.cpp */,
|
||||
87DA62A32D2305E4008ADA0F /* moc_characteristicwriteprocessor0003.cpp */,
|
||||
87DA62AD2D2426F2008ADA0F /* characteristicnotifier0004.cpp */,
|
||||
87DA62AE2D2426F2008ADA0F /* moc_characteristicnotifier0004.cpp */,
|
||||
);
|
||||
name = "Recovered References";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
87DF60DE337FB58864343E39 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -3277,6 +3307,7 @@
|
||||
AF39DD055C3EF8226FBE929D /* Frameworks */,
|
||||
858FCAB0EB1F29CF8B07677C /* Bundle Data */,
|
||||
FE0A091FDBFB3E9C31B7A1BD /* Products */,
|
||||
8798FDCA2D6F338200CF8EE8 /* Recovered References */,
|
||||
);
|
||||
name = qdomyoszwift;
|
||||
sourceTree = "<group>";
|
||||
@@ -3495,6 +3526,8 @@
|
||||
873824BC27E64707004F1B46 /* moc_resolver.cpp in Compile Sources */,
|
||||
87FA11AD27C5ECE4008AC5D1 /* moc_ultrasportbike.cpp in Compile Sources */,
|
||||
871235BF26B297670012D0F2 /* kingsmithr1protreadmill.cpp in Compile Sources */,
|
||||
87DA62AA2D2305F5008ADA0F /* characteristicwriteprocessor0003.cpp in Compile Sources */,
|
||||
87DA62AB2D2305F5008ADA0F /* characteristicnotifier0002.cpp in Compile Sources */,
|
||||
8772B7F72CB55E98004AB8E9 /* deerruntreadmill.cpp in Compile Sources */,
|
||||
20A50533946A39CBD2C89104 /* bluetoothdevice.cpp in Compile Sources */,
|
||||
87C5F0D126285E7E0067A1B5 /* moc_stagesbike.cpp in Compile Sources */,
|
||||
@@ -3745,6 +3778,7 @@
|
||||
873824BB27E64707004F1B46 /* moc_prober_p.cpp in Compile Sources */,
|
||||
877758B32C98627300BB1697 /* moc_sportstechelliptical.cpp in Compile Sources */,
|
||||
8742C2B227C92C30007D3FA0 /* wahookickrsnapbike.cpp in Compile Sources */,
|
||||
87BFEA2F2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm in Compile Sources */,
|
||||
87EB918327EE5FE7002535E1 /* moc_inappstore.cpp in Compile Sources */,
|
||||
87CF516B293C87B000A7CABC /* moc_characteristicwriteprocessore005.cpp in Compile Sources */,
|
||||
8780D949264FB8B800192D41 /* moc_smartspin2k.cpp in Compile Sources */,
|
||||
@@ -3842,6 +3876,8 @@
|
||||
1FBBC7C86C436CAAAFD37E56 /* moc_domyostreadmill.cpp in Compile Sources */,
|
||||
876BFCA027BE35D8001D7645 /* moc_proformelliptical.cpp in Compile Sources */,
|
||||
873824BD27E64707004F1B46 /* moc_resolver_p.cpp in Compile Sources */,
|
||||
87DA62A42D2305E4008ADA0F /* moc_characteristicnotifier0002.cpp in Compile Sources */,
|
||||
87DA62A52D2305E4008ADA0F /* moc_characteristicwriteprocessor0003.cpp in Compile Sources */,
|
||||
876ED21A25C3E9010065F3DC /* moc_material.cpp in Compile Sources */,
|
||||
87DC27EA2D9BDB53007A1B9D /* echelonstairclimber.cpp in Compile Sources */,
|
||||
87DC27EB2D9BDB53007A1B9D /* stairclimber.cpp in Compile Sources */,
|
||||
@@ -3885,6 +3921,8 @@
|
||||
873824B227E64706004F1B46 /* moc_hostname.cpp in Compile Sources */,
|
||||
873824EB27E647A8004F1B46 /* prober.cpp in Compile Sources */,
|
||||
875CA9462D0C740000667EE6 /* moc_kineticinroadbike.cpp in Compile Sources */,
|
||||
87DA62AF2D2426F2008ADA0F /* characteristicnotifier0004.cpp in Compile Sources */,
|
||||
87DA62B02D2426F2008ADA0F /* moc_characteristicnotifier0004.cpp in Compile Sources */,
|
||||
8767EF1E29448D6700810C0F /* characteristicwriteprocessor.cpp in Compile Sources */,
|
||||
87B617F425F260150094A1CB /* moc_screencapture.cpp in Compile Sources */,
|
||||
87B617ED25F25FED0094A1CB /* fitshowtreadmill.cpp in Compile Sources */,
|
||||
@@ -4295,7 +4333,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1059;
|
||||
CURRENT_PROJECT_VERSION = 1066;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
|
||||
@@ -4489,7 +4527,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1059;
|
||||
CURRENT_PROJECT_VERSION = 1066;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4719,7 +4757,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1059;
|
||||
CURRENT_PROJECT_VERSION = 1066;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -4815,7 +4853,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1059;
|
||||
CURRENT_PROJECT_VERSION = 1066;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -4907,7 +4945,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1059;
|
||||
CURRENT_PROJECT_VERSION = 1066;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -5023,7 +5061,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1059;
|
||||
CURRENT_PROJECT_VERSION = 1066;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
|
||||
188
helpers/dircon-parser.py
Normal file
188
helpers/dircon-parser.py
Normal file
@@ -0,0 +1,188 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional, Tuple
|
||||
import re
|
||||
|
||||
@dataclass
|
||||
class HubRidingData:
|
||||
power: Optional[int] = None
|
||||
cadence: Optional[int] = None
|
||||
speed_x100: Optional[int] = None
|
||||
hr: Optional[int] = None
|
||||
unknown1: Optional[int] = None
|
||||
unknown2: Optional[int] = None
|
||||
|
||||
def __str__(self):
|
||||
return (f"Power={self.power}W Cadence={self.cadence}rpm "
|
||||
f"Speed={self.speed_x100/100 if self.speed_x100 else 0:.1f}km/h "
|
||||
f"HR={self.hr}bpm Unknown1={self.unknown1} Unknown2={self.unknown2}")
|
||||
|
||||
def parse_protobuf_varint(data: bytes, offset: int = 0) -> Tuple[int, int]:
|
||||
result = 0
|
||||
shift = 0
|
||||
while offset < len(data):
|
||||
byte = data[offset]
|
||||
result |= (byte & 0x7F) << shift
|
||||
offset += 1
|
||||
if not (byte & 0x80):
|
||||
break
|
||||
shift += 7
|
||||
return result, offset
|
||||
|
||||
def parse_hub_riding_data(data: bytes) -> Optional[HubRidingData]:
|
||||
try:
|
||||
riding_data = HubRidingData()
|
||||
offset = 0
|
||||
while offset < len(data):
|
||||
key, new_offset = parse_protobuf_varint(data, offset)
|
||||
wire_type = key & 0x07
|
||||
field_number = key >> 3
|
||||
offset = new_offset
|
||||
|
||||
if wire_type == 0:
|
||||
value, offset = parse_protobuf_varint(data, offset)
|
||||
if field_number == 1:
|
||||
riding_data.power = value
|
||||
elif field_number == 2:
|
||||
riding_data.cadence = value
|
||||
elif field_number == 3:
|
||||
riding_data.speed_x100 = value
|
||||
elif field_number == 4:
|
||||
riding_data.hr = value
|
||||
elif field_number == 5:
|
||||
riding_data.unknown1 = value
|
||||
elif field_number == 6:
|
||||
riding_data.unknown2 = value
|
||||
return riding_data
|
||||
except Exception as e:
|
||||
print(f"Error parsing protobuf: {e}")
|
||||
return None
|
||||
|
||||
@dataclass
|
||||
class DirconPacket:
|
||||
message_version: int = 1
|
||||
identifier: int = 0xFF
|
||||
sequence_number: int = 0
|
||||
response_code: int = 0
|
||||
length: int = 0
|
||||
uuid: int = 0
|
||||
uuids: List[int] = None
|
||||
additional_data: bytes = b''
|
||||
is_request: bool = False
|
||||
riding_data: Optional[HubRidingData] = None
|
||||
|
||||
def __str__(self):
|
||||
uuids_str = ','.join(f'{u:04x}' for u in (self.uuids or []))
|
||||
base_str = (f"vers={self.message_version} Id={self.identifier} sn={self.sequence_number} "
|
||||
f"resp={self.response_code} len={self.length} req?={self.is_request} "
|
||||
f"uuid={self.uuid:04x} dat={self.additional_data.hex()} uuids=[{uuids_str}]")
|
||||
if self.riding_data:
|
||||
base_str += f"\nRiding Data: {self.riding_data}"
|
||||
return base_str
|
||||
|
||||
def parse_dircon_packet(data: bytes, offset: int = 0) -> Tuple[Optional[DirconPacket], int]:
|
||||
if len(data) - offset < 6:
|
||||
return None, 0
|
||||
|
||||
packet = DirconPacket()
|
||||
packet.message_version = data[offset]
|
||||
packet.identifier = data[offset + 1]
|
||||
packet.sequence_number = data[offset + 2]
|
||||
packet.response_code = data[offset + 3]
|
||||
packet.length = (data[offset + 4] << 8) | data[offset + 5]
|
||||
|
||||
total_length = 6 + packet.length
|
||||
if len(data) - offset < total_length:
|
||||
return None, 0
|
||||
|
||||
curr_offset = offset + 6
|
||||
|
||||
if packet.identifier == 0x01: # DPKT_MSGID_DISCOVER_SERVICES
|
||||
if packet.length == 0:
|
||||
packet.is_request = True
|
||||
elif packet.length % 16 == 0:
|
||||
packet.uuids = []
|
||||
while curr_offset + 16 <= offset + total_length:
|
||||
uuid = (data[curr_offset + 2] << 8) | data[curr_offset + 3]
|
||||
packet.uuids.append(uuid)
|
||||
curr_offset += 16
|
||||
|
||||
elif packet.identifier == 0x02: # DPKT_MSGID_DISCOVER_CHARACTERISTICS
|
||||
if packet.length >= 16:
|
||||
packet.uuid = (data[curr_offset + 2] << 8) | data[curr_offset + 3]
|
||||
if packet.length == 16:
|
||||
packet.is_request = True
|
||||
elif (packet.length - 16) % 17 == 0:
|
||||
curr_offset += 16
|
||||
packet.uuids = []
|
||||
packet.additional_data = b''
|
||||
while curr_offset + 17 <= offset + total_length:
|
||||
uuid = (data[curr_offset + 2] << 8) | data[curr_offset + 3]
|
||||
packet.uuids.append(uuid)
|
||||
packet.additional_data += bytes([data[curr_offset + 16]])
|
||||
curr_offset += 17
|
||||
|
||||
elif packet.identifier in [0x03, 0x04, 0x05, 0x06]: # READ/WRITE/NOTIFY characteristics
|
||||
if packet.length >= 16:
|
||||
packet.uuid = (data[curr_offset + 2] << 8) | data[curr_offset + 3]
|
||||
if packet.length > 16:
|
||||
packet.additional_data = data[curr_offset + 16:offset + total_length]
|
||||
if packet.uuid == 0x0002:
|
||||
packet.riding_data = parse_hub_riding_data(packet.additional_data)
|
||||
if packet.identifier != 0x06:
|
||||
packet.is_request = True
|
||||
|
||||
return packet, total_length
|
||||
|
||||
def extract_bytes_from_c_array(content: str) -> List[Tuple[str, bytes]]:
|
||||
packets = []
|
||||
pattern = r'static const unsigned char (\w+)\[\d+\] = \{([^}]+)\};'
|
||||
matches = re.finditer(pattern, content)
|
||||
|
||||
for match in matches:
|
||||
name = match.group(1)
|
||||
hex_str = match.group(2)
|
||||
|
||||
hex_values = []
|
||||
for line in hex_str.split('\n'):
|
||||
line = line.split('//')[0]
|
||||
values = re.findall(r'0x[0-9a-fA-F]{2}', line)
|
||||
hex_values.extend(values)
|
||||
|
||||
byte_data = bytes([int(x, 16) for x in hex_values])
|
||||
packets.append((name, byte_data))
|
||||
|
||||
return packets
|
||||
|
||||
def get_tcp_payload(data: bytes) -> bytes:
|
||||
ip_header_start = 14 # Skip Ethernet header
|
||||
ip_header_len = (data[ip_header_start] & 0x0F) * 4
|
||||
tcp_header_start = ip_header_start + ip_header_len
|
||||
tcp_header_len = ((data[tcp_header_start + 12] >> 4) & 0x0F) * 4
|
||||
payload_start = tcp_header_start + tcp_header_len
|
||||
return data[payload_start:]
|
||||
|
||||
def parse_file(filename: str):
|
||||
with open(filename, 'r') as f:
|
||||
content = f.read()
|
||||
packets = extract_bytes_from_c_array(content)
|
||||
|
||||
for name, data in packets:
|
||||
print(f"\nPacket {name}:")
|
||||
payload = get_tcp_payload(data)
|
||||
print(f"Dircon payload: {payload.hex()}")
|
||||
|
||||
offset = 0
|
||||
while offset < len(payload):
|
||||
packet, consumed = parse_dircon_packet(payload, offset)
|
||||
if packet is None or consumed == 0:
|
||||
break
|
||||
print(f"Frame: {packet}")
|
||||
offset += consumed
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python script.py <input_file>")
|
||||
sys.exit(1)
|
||||
|
||||
parse_file(sys.argv[1])
|
||||
23
src/characteristics/characteristicnotifier0002.cpp
Normal file
23
src/characteristics/characteristicnotifier0002.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "characteristicnotifier0002.h"
|
||||
#include "bike.h"
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
|
||||
CharacteristicNotifier0002::CharacteristicNotifier0002(bluetoothdevice *bike, QObject *parent)
|
||||
: CharacteristicNotifier(0x0002, parent) {
|
||||
Bike = bike;
|
||||
answerList = QList<QByteArray>(); // Initialize empty list
|
||||
}
|
||||
|
||||
void CharacteristicNotifier0002::addAnswer(const QByteArray &newAnswer) {
|
||||
answerList.append(newAnswer);
|
||||
}
|
||||
|
||||
int CharacteristicNotifier0002::notify(QByteArray &value) {
|
||||
if(!answerList.isEmpty()) {
|
||||
value.append(answerList.first()); // Get first item
|
||||
answerList.removeFirst(); // Remove it from list
|
||||
return CN_OK;
|
||||
}
|
||||
return CN_INVALID;
|
||||
}
|
||||
19
src/characteristics/characteristicnotifier0002.h
Normal file
19
src/characteristics/characteristicnotifier0002.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef CHARACTERISTICNOTIFIER0002_H
|
||||
#define CHARACTERISTICNOTIFIER0002_H
|
||||
|
||||
#include "bluetoothdevice.h"
|
||||
#include "characteristicnotifier.h"
|
||||
#include <QList>
|
||||
|
||||
class CharacteristicNotifier0002 : public CharacteristicNotifier {
|
||||
Q_OBJECT
|
||||
bluetoothdevice* Bike = nullptr;
|
||||
QList<QByteArray> answerList;
|
||||
|
||||
public:
|
||||
explicit CharacteristicNotifier0002(bluetoothdevice *bike, QObject *parent = nullptr);
|
||||
int notify(QByteArray &value) override;
|
||||
void addAnswer(const QByteArray &newAnswer);
|
||||
};
|
||||
|
||||
#endif // CHARACTERISTICNOTIFIER0002_H
|
||||
23
src/characteristics/characteristicnotifier0004.cpp
Normal file
23
src/characteristics/characteristicnotifier0004.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "characteristicnotifier0004.h"
|
||||
#include "bike.h"
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
|
||||
CharacteristicNotifier0004::CharacteristicNotifier0004(bluetoothdevice *bike, QObject *parent)
|
||||
: CharacteristicNotifier(0x0004, parent) {
|
||||
Bike = bike;
|
||||
answerList = QList<QByteArray>();
|
||||
}
|
||||
|
||||
void CharacteristicNotifier0004::addAnswer(const QByteArray &newAnswer) {
|
||||
answerList.append(newAnswer);
|
||||
}
|
||||
|
||||
int CharacteristicNotifier0004::notify(QByteArray &value) {
|
||||
if(!answerList.isEmpty()) {
|
||||
value.append(answerList.first());
|
||||
answerList.removeFirst();
|
||||
return CN_OK;
|
||||
}
|
||||
return CN_INVALID;
|
||||
}
|
||||
19
src/characteristics/characteristicnotifier0004.h
Normal file
19
src/characteristics/characteristicnotifier0004.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef CHARACTERISTICNOTIFIER0004_H
|
||||
#define CHARACTERISTICNOTIFIER0004_H
|
||||
|
||||
#include "bluetoothdevice.h"
|
||||
#include "characteristicnotifier.h"
|
||||
#include <QList>
|
||||
|
||||
class CharacteristicNotifier0004 : public CharacteristicNotifier {
|
||||
Q_OBJECT
|
||||
bluetoothdevice* Bike = nullptr;
|
||||
QList<QByteArray> answerList;
|
||||
|
||||
public:
|
||||
explicit CharacteristicNotifier0004(bluetoothdevice *bike, QObject *parent = nullptr);
|
||||
int notify(QByteArray &value) override;
|
||||
void addAnswer(const QByteArray &newAnswer);
|
||||
};
|
||||
|
||||
#endif // CHARACTERISTICNOTIFIER0004_H
|
||||
316
src/characteristics/characteristicwriteprocessor0003.cpp
Normal file
316
src/characteristics/characteristicwriteprocessor0003.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
#include "characteristicwriteprocessor0003.h"
|
||||
#include <QDebug>
|
||||
#include "bike.h"
|
||||
|
||||
CharacteristicWriteProcessor0003::CharacteristicWriteProcessor0003(double bikeResistanceGain,
|
||||
int8_t bikeResistanceOffset,
|
||||
bluetoothdevice *bike,
|
||||
CharacteristicNotifier0002 *notifier0002,
|
||||
CharacteristicNotifier0004 *notifier0004,
|
||||
QObject *parent)
|
||||
: CharacteristicWriteProcessor(bikeResistanceGain, bikeResistanceOffset, bike, parent), notifier0002(notifier0002), notifier0004(notifier0004) {
|
||||
}
|
||||
|
||||
CharacteristicWriteProcessor0003::VarintResult CharacteristicWriteProcessor0003::decodeVarint(const QByteArray& bytes, int startIndex) {
|
||||
qint64 result = 0;
|
||||
int shift = 0;
|
||||
int bytesRead = 0;
|
||||
|
||||
for (int i = startIndex; i < bytes.size(); i++) {
|
||||
quint8 byte = static_cast<quint8>(bytes.at(i));
|
||||
result |= static_cast<qint64>(byte & 0x7F) << shift;
|
||||
bytesRead++;
|
||||
|
||||
if ((byte & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
|
||||
return {result, bytesRead};
|
||||
}
|
||||
|
||||
double CharacteristicWriteProcessor0003::currentGear() {
|
||||
if(zwiftGearReceived)
|
||||
return currentZwiftGear;
|
||||
else
|
||||
return ((bike*)Bike)->gears();
|
||||
}
|
||||
|
||||
qint32 CharacteristicWriteProcessor0003::decodeSInt(const QByteArray& bytes) {
|
||||
if (static_cast<quint8>(bytes.at(0)) != 0x22) {
|
||||
qFatal("Invalid field header");
|
||||
}
|
||||
|
||||
int length = static_cast<quint8>(bytes.at(1));
|
||||
|
||||
if (static_cast<quint8>(bytes.at(2)) != 0x10) {
|
||||
qFatal("Invalid inner header");
|
||||
}
|
||||
|
||||
VarintResult varint = decodeVarint(bytes, 3);
|
||||
|
||||
qint32 decoded = (varint.value >> 1) ^ -(varint.value & 1);
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
void CharacteristicWriteProcessor0003::handleZwiftGear(const QByteArray &array) {
|
||||
uint8_t g = 0;
|
||||
if (array.size() >= 2) {
|
||||
if ((uint8_t)array[0] == (uint8_t)0xCC && (uint8_t)array[1] == (uint8_t)0x3A) g = 1;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xFC && (uint8_t)array[1] == (uint8_t)0x43) g = 2;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xAC && (uint8_t)array[1] == (uint8_t)0x4D) g = 3;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xDC && (uint8_t)array[1] == (uint8_t)0x56) g = 4;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x8C && (uint8_t)array[1] == (uint8_t)0x60) g = 5;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xE8 && (uint8_t)array[1] == (uint8_t)0x6B) g = 6;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xC4 && (uint8_t)array[1] == (uint8_t)0x77) g = 7;
|
||||
else if (array.size() >= 3) {
|
||||
if ((uint8_t)array[0] == (uint8_t)0xA0 && (uint8_t)array[1] == (uint8_t)0x83 && (uint8_t)array[2] == (uint8_t)0x01) g = 8;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xA8 && (uint8_t)array[1] == (uint8_t)0x91 && (uint8_t)array[2] == (uint8_t)0x01) g = 9;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xB0 && (uint8_t)array[1] == (uint8_t)0x9F && (uint8_t)array[2] == (uint8_t)0x01) g = 10;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xB8 && (uint8_t)array[1] == (uint8_t)0xAD && (uint8_t)array[2] == (uint8_t)0x01) g = 11;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xC0 && (uint8_t)array[1] == (uint8_t)0xBB && (uint8_t)array[2] == (uint8_t)0x01) g = 12;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xF3 && (uint8_t)array[1] == (uint8_t)0xCB && (uint8_t)array[2] == (uint8_t)0x01) g = 13;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xA8 && (uint8_t)array[1] == (uint8_t)0xDC && (uint8_t)array[2] == (uint8_t)0x01) g = 14;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xDC && (uint8_t)array[1] == (uint8_t)0xEC && (uint8_t)array[2] == (uint8_t)0x01) g = 15;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x90 && (uint8_t)array[1] == (uint8_t)0xFD && (uint8_t)array[2] == (uint8_t)0x01) g = 16;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xD4 && (uint8_t)array[1] == (uint8_t)0x90 && (uint8_t)array[2] == (uint8_t)0x02) g = 17;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x98 && (uint8_t)array[1] == (uint8_t)0xA4 && (uint8_t)array[2] == (uint8_t)0x02) g = 18;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xDC && (uint8_t)array[1] == (uint8_t)0xB7 && (uint8_t)array[2] == (uint8_t)0x02) g = 19;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x9F && (uint8_t)array[1] == (uint8_t)0xCB && (uint8_t)array[2] == (uint8_t)0x02) g = 20;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xD8 && (uint8_t)array[1] == (uint8_t)0xE2 && (uint8_t)array[2] == (uint8_t)0x02) g = 21;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x90 && (uint8_t)array[1] == (uint8_t)0xFA && (uint8_t)array[2] == (uint8_t)0x02) g = 22;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xC8 && (uint8_t)array[1] == (uint8_t)0x91 && (uint8_t)array[2] == (uint8_t)0x03) g = 23;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xF3 && (uint8_t)array[1] == (uint8_t)0xAC && (uint8_t)array[2] == (uint8_t)0x03) g = 24;
|
||||
else { return; }
|
||||
}
|
||||
else { return; }
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
if(settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool()) {
|
||||
int actGear = ((bike*)Bike)->gears();
|
||||
if (g < actGear) {
|
||||
for (int i = 0; i < actGear - g; i++) {
|
||||
((bike*)Bike)->gearDown();
|
||||
}
|
||||
} else if (g > actGear) {
|
||||
for (int i = 0; i < g - actGear; i++) {
|
||||
((bike*)Bike)->gearUp();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (g < currentZwiftGear) {
|
||||
for (int i = 0; i < currentZwiftGear - g; ++i) {
|
||||
((bike*)Bike)->gearDown();
|
||||
}
|
||||
} else if (g > currentZwiftGear) {
|
||||
for (int i = 0; i < g - currentZwiftGear; ++i) {
|
||||
((bike*)Bike)->gearUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
currentZwiftGear = g;
|
||||
zwiftGearReceived = true;
|
||||
}
|
||||
|
||||
QByteArray CharacteristicWriteProcessor0003::encodeHubRidingData(
|
||||
uint32_t power,
|
||||
uint32_t cadence,
|
||||
uint32_t speedX100,
|
||||
uint32_t hr,
|
||||
uint32_t unknown1,
|
||||
uint32_t unknown2
|
||||
) {
|
||||
QByteArray buffer;
|
||||
buffer.append(char(0x03));
|
||||
|
||||
auto encodeVarInt32 = [](QByteArray& buf, uint32_t value) {
|
||||
do {
|
||||
uint8_t byte = value & 0x7F;
|
||||
value >>= 7;
|
||||
if (value) byte |= 0x80;
|
||||
buf.append(char(byte));
|
||||
} while (value);
|
||||
};
|
||||
|
||||
encodeVarInt32(buffer, (1 << 3) | 0);
|
||||
encodeVarInt32(buffer, power);
|
||||
|
||||
encodeVarInt32(buffer, (2 << 3) | 0);
|
||||
encodeVarInt32(buffer, cadence);
|
||||
|
||||
encodeVarInt32(buffer, (3 << 3) | 0);
|
||||
encodeVarInt32(buffer, speedX100);
|
||||
|
||||
encodeVarInt32(buffer, (4 << 3) | 0);
|
||||
encodeVarInt32(buffer, hr);
|
||||
|
||||
encodeVarInt32(buffer, (5 << 3) | 0);
|
||||
encodeVarInt32(buffer, unknown1);
|
||||
|
||||
encodeVarInt32(buffer, (6 << 3) | 0);
|
||||
encodeVarInt32(buffer, unknown2);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static uint32_t lastUnknown1 = 836; // Starting value from logs
|
||||
static uint32_t baseValue = 19000; // Base value from original code
|
||||
|
||||
uint32_t CharacteristicWriteProcessor0003::calculateUnknown1(uint16_t power) {
|
||||
// Increment by a value between 400-800 based on current power
|
||||
uint32_t increment = 400 + (power * 2);
|
||||
if (increment > 800) increment = 800;
|
||||
|
||||
// Adjust based on power changes
|
||||
if (power > 0) {
|
||||
lastUnknown1 += increment;
|
||||
} else {
|
||||
// For zero power, larger increments
|
||||
lastUnknown1 += 600;
|
||||
}
|
||||
|
||||
// Keep within observed range (800-24000)
|
||||
if (lastUnknown1 > 24000) lastUnknown1 = baseValue;
|
||||
|
||||
return lastUnknown1;
|
||||
}
|
||||
|
||||
int CharacteristicWriteProcessor0003::writeProcess(quint16 uuid, const QByteArray &data, QByteArray &reply) {
|
||||
static const QByteArray expectedHexArray = QByteArray::fromHex("52696465 4F6E02");
|
||||
static const QByteArray expectedHexArray2 = QByteArray::fromHex("410805");
|
||||
static const QByteArray expectedHexArray3 = QByteArray::fromHex("00088804");
|
||||
static const QByteArray expectedHexArray4 = QByteArray::fromHex("042A0A10 C0BB0120");
|
||||
static const QByteArray expectedHexArray4b = QByteArray::fromHex("042A0A10 A0830120");
|
||||
static const QByteArray expectedHexArray5 = QByteArray::fromHex("0422");
|
||||
static const QByteArray expectedHexArray6 = QByteArray::fromHex("042A0410");
|
||||
static const QByteArray expectedHexArray7 = QByteArray::fromHex("042A0310");
|
||||
static const QByteArray expectedHexArray8 = QByteArray::fromHex("0418");
|
||||
static const QByteArray expectedHexArray9 = QByteArray::fromHex("042a0810");
|
||||
static const QByteArray expectedHexArray10 = QByteArray::fromHex("000800");
|
||||
|
||||
QByteArray receivedData = data;
|
||||
|
||||
if (receivedData.startsWith(expectedHexArray)) {
|
||||
qDebug() << "Zwift Play Processor: Initial connection request";
|
||||
reply = QByteArray::fromHex("2a08031211220f4154582030342c2053545820303400");
|
||||
notifier0002->addAnswer(reply);
|
||||
reply = QByteArray::fromHex("2a0803120d220b524944455f4f4e28322900");
|
||||
notifier0002->addAnswer(reply);
|
||||
reply = QByteArray::fromHex("526964654f6e0200");
|
||||
notifier0004->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray2)) {
|
||||
qDebug() << "Zwift Play Processor: Device info request";
|
||||
reply = QByteArray::fromHex("3c080012320a3008800412040500050"
|
||||
"11a0b4b49434b5220434f524500320f"
|
||||
"3430323431383030393834000000003a01314204080110140");
|
||||
notifier0004->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray3)) {
|
||||
qDebug() << "Zwift Play Processor: Status request";
|
||||
reply = QByteArray::fromHex("3c0888041206 0a0440c0bb01");
|
||||
notifier0004->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray4) || receivedData.startsWith(expectedHexArray4b)) {
|
||||
qDebug() << "Zwift Play Ask 4";
|
||||
|
||||
reply = QByteArray::fromHex("0308001000185920002800309bed01");
|
||||
notifier0002->addAnswer(reply);
|
||||
|
||||
reply = QByteArray::fromHex("2a08031227222567"
|
||||
"61705f706172616d735f6368616e6765"
|
||||
"2832293a2037322c2037322c20302c20"
|
||||
"36303000");
|
||||
notifier0002->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray5)) {
|
||||
qDebug() << "Zwift Play Processor: Slope change request";
|
||||
double slopefloat = decodeSInt(receivedData.mid(1));
|
||||
QByteArray slope(2, 0);
|
||||
slope[0] = quint8(qint16(slopefloat) & 0xFF);
|
||||
slope[1] = quint8((qint16(slopefloat) >> 8) & 0x00FF);
|
||||
|
||||
emit ftmsCharacteristicChanged(QLowEnergyCharacteristic(),
|
||||
QByteArray::fromHex("116901") + slope + QByteArray::fromHex("3228"));
|
||||
|
||||
changeSlope(slopefloat, 0 /* TODO */, 0 /* TODO */);
|
||||
|
||||
reply = encodeHubRidingData(
|
||||
Bike->wattsMetric().value(),
|
||||
Bike->currentCadence().value(),
|
||||
0,
|
||||
Bike->wattsMetric().value(),
|
||||
calculateUnknown1(Bike->wattsMetric().value()),
|
||||
0
|
||||
);
|
||||
notifier0002->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray6)) {
|
||||
qDebug() << "Zwift Play Ask 6";
|
||||
|
||||
reply = QByteArray::fromHex("3c0888041206 0a0440c0bb01");
|
||||
reply[9] = receivedData[4];
|
||||
reply[10] = receivedData[5];
|
||||
reply[11] = receivedData[6];
|
||||
handleZwiftGear(receivedData.mid(4));
|
||||
notifier0004->addAnswer(reply);
|
||||
|
||||
reply = QByteArray::fromHex("03080010001827e7 20002896143093ed01");
|
||||
notifier0002->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray7)) {
|
||||
qDebug() << "Zwift Play Ask 7";
|
||||
|
||||
reply = QByteArray::fromHex("03080010001827e7 2000 28 00 3093ed01");
|
||||
notifier0002->addAnswer(reply);
|
||||
|
||||
reply = QByteArray::fromHex("3c088804120503408c60");
|
||||
reply[8] = receivedData[4];
|
||||
reply[9] = receivedData[5];
|
||||
handleZwiftGear(receivedData.mid(4));
|
||||
notifier0004->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray8)) {
|
||||
qDebug() << "Zwift Play Processor: Power request";
|
||||
VarintResult Power = decodeVarint(receivedData, 2);
|
||||
QByteArray power(2, 0);
|
||||
power[0] = quint8(qint16(Power.value) & 0xFF);
|
||||
power[1] = quint8((qint16(Power.value) >> 8) & 0x00FF);
|
||||
|
||||
emit ftmsCharacteristicChanged(QLowEnergyCharacteristic(),
|
||||
QByteArray::fromHex("05") + power);
|
||||
|
||||
reply = encodeHubRidingData(
|
||||
Bike->wattsMetric().value(),
|
||||
Bike->currentCadence().value(),
|
||||
0,
|
||||
Bike->wattsMetric().value(),
|
||||
calculateUnknown1(Bike->wattsMetric().value()),
|
||||
0
|
||||
);
|
||||
notifier0002->addAnswer(reply);
|
||||
|
||||
changePower(Power.value);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray9)) {
|
||||
qDebug() << "Zwift Play Ask 9";
|
||||
|
||||
reply = QByteArray::fromHex("050a08400058b60560fc26");
|
||||
notifier0004->addAnswer(reply);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray10)) {
|
||||
qDebug() << "Zwift Play Ask 10";
|
||||
|
||||
reply = QByteArray::fromHex("3c0800122408800412040004000c1a00320f42412d4534333732443932374244453a00420408011053");
|
||||
notifier0004->addAnswer(reply);
|
||||
}
|
||||
else {
|
||||
qDebug() << "Zwift Play Processor: Unhandled request:" << receivedData.toHex();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
45
src/characteristics/characteristicwriteprocessor0003.h
Normal file
45
src/characteristics/characteristicwriteprocessor0003.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef CHARACTERISTICWRITEPROCESSOR0003_H
|
||||
#define CHARACTERISTICWRITEPROCESSOR0003_H
|
||||
|
||||
#include "characteristicnotifier0002.h"
|
||||
#include "characteristicnotifier0004.h"
|
||||
#include "characteristicwriteprocessor.h"
|
||||
|
||||
class CharacteristicWriteProcessor0003 : public CharacteristicWriteProcessor {
|
||||
Q_OBJECT
|
||||
CharacteristicNotifier0002 *notifier0002 = nullptr;
|
||||
CharacteristicNotifier0004 *notifier0004 = nullptr;
|
||||
|
||||
public:
|
||||
explicit CharacteristicWriteProcessor0003(double bikeResistanceGain, int8_t bikeResistanceOffset,
|
||||
bluetoothdevice *bike, CharacteristicNotifier0002 *notifier0002,
|
||||
CharacteristicNotifier0004 *notifier0004,
|
||||
QObject *parent = nullptr);
|
||||
int writeProcess(quint16 uuid, const QByteArray &data, QByteArray &out) override;
|
||||
static QByteArray encodeHubRidingData(uint32_t power,
|
||||
uint32_t cadence,
|
||||
uint32_t speedX100,
|
||||
uint32_t hr,
|
||||
uint32_t unknown1,
|
||||
uint32_t unknown2);
|
||||
static uint32_t calculateUnknown1(uint16_t power);
|
||||
void handleZwiftGear(const QByteArray &array);
|
||||
double currentGear();
|
||||
|
||||
|
||||
private:
|
||||
struct VarintResult {
|
||||
qint64 value;
|
||||
int bytesRead;
|
||||
};
|
||||
|
||||
VarintResult decodeVarint(const QByteArray& bytes, int startIndex);
|
||||
qint32 decodeSInt(const QByteArray& bytes);
|
||||
int currentZwiftGear = 8;
|
||||
bool zwiftGearReceived = false;
|
||||
|
||||
signals:
|
||||
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
};
|
||||
|
||||
#endif // CHARACTERISTICWRITEPROCESSOR0003_H
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "devices/dircon/dirconmanager.h"
|
||||
#include "devices/bike.h"
|
||||
#include <QNetworkInterface>
|
||||
#include <QSettings>
|
||||
#include <chrono>
|
||||
@@ -14,7 +15,8 @@ using namespace std::chrono_literals;
|
||||
OP(CYCLING_POWER, 0x1818, WAHOO_KICKR, P1, P2, P3) \
|
||||
OP(CYCLING_SPEED_AND_CADENCE, 0x1816, WAHOO_KICKR, P1, P2, P3) \
|
||||
OP(RUNNING_SPEED_AND_CADENCE, 0x1814, WAHOO_TREADMILL, P1, P2, P3) \
|
||||
OP(HEART_RATE, 0x180D, WAHOO_BLUEHR, P1, P2, P3)
|
||||
OP(HEART_RATE, 0x180D, WAHOO_BLUEHR, P1, P2, P3) \
|
||||
OP(ZWIFT_PLAY_EMULATOR, ZWIFT_PLAY_ENUM_VALUE, WAHOO_KICKR, P1, P2, P3)
|
||||
|
||||
#define DM_MACHINE_OP(OP, P1, P2, P3) \
|
||||
OP(WAHOO_KICKR, "Wahoo KICKR $uuid_hex$", DM_MACHINE_TYPE_TREADMILL | DM_MACHINE_TYPE_BIKE, P1, P2, P3) \
|
||||
@@ -22,6 +24,7 @@ using namespace std::chrono_literals;
|
||||
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_0003() writeP0003
|
||||
#define DP_PROCESS_WRITE_2AD9() writeP2AD9
|
||||
#define DP_PROCESS_WRITE_2AD9T() writeP2AD9
|
||||
#define DP_PROCESS_WRITE_E005() writePE005
|
||||
@@ -71,7 +74,17 @@ using namespace std::chrono_literals;
|
||||
P3) \
|
||||
OP(RUNNING_SPEED_AND_CADENCE, 0x2A53, DPKT_CHAR_PROP_FLAG_NOTIFY, DM_BT("\x00"), DP_PROCESS_WRITE_NULL, P1, P2, \
|
||||
P3) \
|
||||
OP(HEART_RATE, 0x2A37, DPKT_CHAR_PROP_FLAG_NOTIFY, DM_BT("\x00"), DP_PROCESS_WRITE_NULL, P1, P2, P3)
|
||||
OP(HEART_RATE, 0x2A37, DPKT_CHAR_PROP_FLAG_NOTIFY, DM_BT("\x00"), DP_PROCESS_WRITE_NULL, P1, P2, P3) \
|
||||
OP(ZWIFT_PLAY_EMULATOR, 0x0003, \
|
||||
DPKT_CHAR_PROP_FLAG_WRITE, \
|
||||
DM_BT("\x00"), DP_PROCESS_WRITE_0003, P1, P2, P3) \
|
||||
OP(ZWIFT_PLAY_EMULATOR, 0x0002, \
|
||||
DPKT_CHAR_PROP_FLAG_NOTIFY, \
|
||||
DM_BT("\x00"), DP_PROCESS_WRITE_NULL, P1, P2, P3) \
|
||||
OP(ZWIFT_PLAY_EMULATOR, 0x0004, \
|
||||
/* CHECK THE INDICATE*/ \
|
||||
DPKT_CHAR_PROP_FLAG_INDICATE, \
|
||||
DM_BT("\x02\x00"), DP_PROCESS_WRITE_NULL, P1, P2, P3)
|
||||
|
||||
#define DM_MACHINE_ENUM_OP(DESC, NAME, TYPE, P1, P2, P3) DM_MACHINE_##DESC,
|
||||
|
||||
@@ -150,7 +163,8 @@ DirconManager::DirconManager(bluetoothdevice *Bike, int8_t bikeResistanceOffset,
|
||||
QSettings settings;
|
||||
DirconProcessorService *service;
|
||||
QList<DirconProcessorService *> services, proc_services;
|
||||
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
|
||||
bluetoothdevice::BLUETOOTH_TYPE dt = Bike->deviceType();
|
||||
bt = Bike;
|
||||
uint8_t type = dt == bluetoothdevice::TREADMILL || dt == bluetoothdevice::ELLIPTICAL ? DM_MACHINE_TYPE_TREADMILL
|
||||
: DM_MACHINE_TYPE_BIKE;
|
||||
qDebug() << "Building Dircom Manager";
|
||||
@@ -160,6 +174,7 @@ DirconManager::DirconManager(bluetoothdevice *Bike, int8_t bikeResistanceOffset,
|
||||
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);
|
||||
writeP0003 = new CharacteristicWriteProcessor0003(bikeResistanceGain, bikeResistanceOffset, Bike, notif0002, notif0004, 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,
|
||||
@@ -167,13 +182,17 @@ DirconManager::DirconManager(bluetoothdevice *Bike, int8_t bikeResistanceOffset,
|
||||
connect(writePE005, SIGNAL(changeInclination(double, double)), this, SIGNAL(changeInclination(double, double)));
|
||||
connect(writePE005, SIGNAL(ftmsCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)), this,
|
||||
SIGNAL(ftmsCharacteristicChanged(QLowEnergyCharacteristic, QByteArray)));
|
||||
connect(writeP0003, SIGNAL(changeInclination(double, double)), this, SIGNAL(changeInclination(double, double)));
|
||||
connect(writeP0003, 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)
|
||||
if (settings.value(QZSettings::race_mode, QZSettings::default_race_mode).toBool())
|
||||
bikeTimer.start(100ms);
|
||||
else
|
||||
bikeTimer.start(1s);
|
||||
// 50ms always because even if the zwift_play_emulator is disabled, zwift, seeing the 0003 characateristic, it needs high speed metrics.
|
||||
//if (settings.value(QZSettings::race_mode, QZSettings::default_race_mode).toBool() || settings.value(QZSettings::zwift_play_emulator, QZSettings::default_zwift_play_emulator).toBool())
|
||||
bikeTimer.start(50ms);
|
||||
/*else
|
||||
bikeTimer.start(1s);*/
|
||||
}
|
||||
|
||||
#define DM_CHAR_NOTIF_NOTIF1_OP(UUID, P1, P2, P3) \
|
||||
@@ -188,3 +207,11 @@ void DirconManager::bikeProvider() {
|
||||
DM_CHAR_NOTIF_OP(DM_CHAR_NOTIF_NOTIF1_OP, 0, 0, 0)
|
||||
foreach (DirconProcessor *processor, processors) { DM_CHAR_NOTIF_OP(DM_CHAR_NOTIF_NOTIF2_OP, processor, 0, 0) }
|
||||
}
|
||||
|
||||
double DirconManager::currentGear() {
|
||||
if(writeP0003)
|
||||
return writeP0003->currentGear();
|
||||
else if(bt && bt->deviceType() == bluetoothdevice::BIKE)
|
||||
return ((bike*)bt)->gears();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "characteristics/characteristicnotifier2ad9.h"
|
||||
#include "characteristics/characteristicwriteprocessor2ad9.h"
|
||||
#include "characteristics/characteristicwriteprocessore005.h"
|
||||
#include "characteristics/characteristicwriteprocessor0003.h"
|
||||
#include "devices/dircon/dirconpacket.h"
|
||||
#include "devices/dircon/dirconprocessor.h"
|
||||
#include <QObject>
|
||||
@@ -20,7 +21,8 @@
|
||||
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(2AD9, P1, P2, P3) \
|
||||
OP(0002, P1, P2, P3) OP(0004, P1, P2, P3)
|
||||
|
||||
#define DM_CHAR_NOTIF_DEFINE_OP(UUID, P1, P2, P3) CharacteristicNotifier##UUID *notif##UUID = 0;
|
||||
|
||||
@@ -28,12 +30,15 @@ class DirconManager : public QObject {
|
||||
Q_OBJECT
|
||||
QTimer bikeTimer;
|
||||
CharacteristicWriteProcessor2AD9 *writeP2AD9 = 0;
|
||||
CharacteristicWriteProcessor0003 *writeP0003 = 0;
|
||||
CharacteristicWriteProcessorE005 *writePE005 = 0;
|
||||
DM_CHAR_NOTIF_OP(DM_CHAR_NOTIF_DEFINE_OP, 0, 0, 0)
|
||||
QList<DirconProcessor *> processors;
|
||||
static QString getMacAddress();
|
||||
bluetoothdevice* bt = 0;
|
||||
|
||||
public:
|
||||
double currentGear();
|
||||
explicit DirconManager(bluetoothdevice *t, int8_t bikeResistanceOffset = 4, double bikeResistanceGain = 1.0,
|
||||
QObject *parent = nullptr);
|
||||
private slots:
|
||||
|
||||
@@ -171,22 +171,40 @@ QByteArray DirconPacket::encode(int last_seq_number) {
|
||||
this->Length = this->uuids.size() * 16;
|
||||
byteout.append((char)(this->Length >> 8)).append((char)(this->Length));
|
||||
foreach (u, this->uuids) {
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
if(u >= 1 && u <= 4) {
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes_zwift_play, 16);
|
||||
} else {
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this->Identifier == DPKT_MSGID_DISCOVER_CHARACTERISTICS && !this->isRequest) {
|
||||
this->Length = 16 + this->uuids.size() * 17;
|
||||
byteout.append((char)(this->Length >> 8)).append((char)(this->Length));
|
||||
u = this->uuid;
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
foreach (u, this->uuids) {
|
||||
if(u >= 1 && u <= 4) {
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes_zwift_play, 16);
|
||||
} else {
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
}
|
||||
foreach (u, this->uuids) {
|
||||
if(u >= 1 && u <= 4) {
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes_zwift_play, 16);
|
||||
} else {
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
}
|
||||
byteout.append(this->additional_data.at(i++));
|
||||
}
|
||||
} else if (((this->Identifier == DPKT_MSGID_READ_CHARACTERISTIC ||
|
||||
@@ -196,9 +214,15 @@ QByteArray DirconPacket::encode(int last_seq_number) {
|
||||
this->Length = 16;
|
||||
byteout.append((char)(this->Length >> 8)).append((char)(this->Length));
|
||||
u = this->uuid;
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
if(u >= 1 && u <= 4) {
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes_zwift_play, 16);
|
||||
} else {
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
}
|
||||
} else if (this->Identifier == DPKT_MSGID_WRITE_CHARACTERISTIC ||
|
||||
this->Identifier == DPKT_MSGID_UNSOLICITED_CHARACTERISTIC_NOTIFICATION ||
|
||||
(this->Identifier == DPKT_MSGID_READ_CHARACTERISTIC && !this->isRequest) ||
|
||||
@@ -206,9 +230,15 @@ QByteArray DirconPacket::encode(int last_seq_number) {
|
||||
this->Length = 16 + this->additional_data.size();
|
||||
byteout.append((char)(this->Length >> 8)).append((char)(this->Length));
|
||||
u = this->uuid;
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
if(u >= 1 && u <= 4) {
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes_zwift_play[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes_zwift_play, 16);
|
||||
} else {
|
||||
this->uuid_bytes[DPKT_POS_SH8] = (quint8)(u >> 8);
|
||||
this->uuid_bytes[DPKT_POS_SH0] = (quint8)(u);
|
||||
byteout.append((char *)this->uuid_bytes, 16);
|
||||
}
|
||||
byteout.append(this->additional_data);
|
||||
}
|
||||
return byteout;
|
||||
|
||||
@@ -4,10 +4,20 @@
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
#define ZWIFT_PLAY_ENUM_VALUE 0x0001
|
||||
#define ZWIFT_PLAY_UUID_STRING "00000001-19CA-4651-86E5-FA29DCDD09D1"
|
||||
#define ZWIFT_PLAY_CHAR1_ENUM_VALUE 0x0002
|
||||
#define ZWIFT_PLAY_CHAR2_ENUM_VALUE 0x0003
|
||||
#define ZWIFT_PLAY_CHAR3_ENUM_VALUE 0x0004
|
||||
#define ZWIFT_PLAY_CHAR1_UUID_STRING "00000002-19CA-4651-86E5-FA29DCDD09D1"
|
||||
#define ZWIFT_PLAY_CHAR2_UUID_STRING "00000003-19CA-4651-86E5-FA29DCDD09D1"
|
||||
#define ZWIFT_PLAY_CHAR3_UUID_STRING "00000004-19CA-4651-86E5-FA29DCDD09D1"
|
||||
|
||||
#define DPKT_MESSAGE_HEADER_LENGTH 6
|
||||
#define DPKT_CHAR_PROP_FLAG_READ 0x01
|
||||
#define DPKT_CHAR_PROP_FLAG_WRITE 0x02
|
||||
#define DPKT_CHAR_PROP_FLAG_NOTIFY 0x04
|
||||
#define DPKT_CHAR_PROP_FLAG_INDICATE 0x08
|
||||
#define DPKT_MSGID_ERROR 0xFF
|
||||
#define DPKT_MSGID_DISCOVER_SERVICES 0x01
|
||||
#define DPKT_MSGID_DISCOVER_CHARACTERISTICS 0x02
|
||||
@@ -51,6 +61,8 @@ class DirconPacket {
|
||||
private:
|
||||
quint8 uuid_bytes[16] = {0x00, 0x00, 0x18, 0x26, 0x00, 0x00, 0x10, 0x00,
|
||||
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
|
||||
quint8 uuid_bytes_zwift_play[16] = {0x00, 0x00, 0x00, 0x04, 0x19, 0xCA, 0x46, 0x51,
|
||||
0x86, 0xE5, 0xFA, 0x29, 0xDC, 0xDD, 0x09, 0xD1};
|
||||
bool checkIsRequest(int last_seq_number);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,16 @@ DirconProcessor::DirconProcessor(const QList<DirconProcessorService *> &my_servi
|
||||
|
||||
DirconProcessor::~DirconProcessor() {}
|
||||
|
||||
QString DirconProcessor::convertUUIDFromUINT16ToString (quint16 uuid) {
|
||||
if(uuid == ZWIFT_PLAY_CHAR1_ENUM_VALUE)
|
||||
return ZWIFT_PLAY_CHAR1_UUID_STRING;
|
||||
if(uuid == ZWIFT_PLAY_CHAR2_ENUM_VALUE)
|
||||
return ZWIFT_PLAY_CHAR2_UUID_STRING;
|
||||
if(uuid == ZWIFT_PLAY_CHAR3_ENUM_VALUE)
|
||||
return ZWIFT_PLAY_CHAR3_UUID_STRING;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool DirconProcessor::initServer() {
|
||||
qDebug() << "Initializing dircon tcp server for" << serverName;
|
||||
if (!server) {
|
||||
@@ -55,10 +65,16 @@ void DirconProcessor::initAdvertising() {
|
||||
mdnsService.addAttribute(QByteArrayLiteral("serial-number"), serialN.toUtf8());
|
||||
QString ble_uuids;
|
||||
int i = 0;
|
||||
foreach (DirconProcessorService *service, services)
|
||||
ble_uuids += QString(QStringLiteral(DP_BASE_UUID))
|
||||
.replace("u", QString(QStringLiteral("%1")).arg(service->uuid, 4, 16, QLatin1Char('0'))) +
|
||||
((i++ < services.size() - 1) ? QStringLiteral(",") : QStringLiteral(""));
|
||||
foreach (DirconProcessorService *service, services) {
|
||||
if(service->uuid == ZWIFT_PLAY_ENUM_VALUE) {
|
||||
ble_uuids += ZWIFT_PLAY_UUID_STRING +
|
||||
((i++ < services.size() - 1) ? QStringLiteral(",") : QStringLiteral(""));
|
||||
} else {
|
||||
ble_uuids += QString(QStringLiteral(DP_BASE_UUID))
|
||||
.replace("u", QString(QStringLiteral("%1")).arg(service->uuid, 4, 16, QLatin1Char('0'))) +
|
||||
((i++ < services.size() - 1) ? QStringLiteral(",") : QStringLiteral(""));
|
||||
}
|
||||
}
|
||||
mdnsService.addAttribute(QByteArrayLiteral("ble-service-uuids"), ble_uuids.toUtf8());
|
||||
mdnsService.setPort(serverPort);
|
||||
mdnsProvider->update(mdnsService);
|
||||
@@ -209,7 +225,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 || !settings.value(QZSettings::wahoo_rgt_dircon, QZSettings::default_wahoo_rgt_dircon).toBool()) {
|
||||
/*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)
|
||||
|
||||
@@ -84,6 +84,7 @@ class DirconProcessor : public QObject {
|
||||
bool initServer();
|
||||
void initAdvertising();
|
||||
DirconPacket processPacket(DirconProcessorClient *client, const DirconPacket &pkt);
|
||||
QString convertUUIDFromUINT16ToString (quint16 uuid);
|
||||
|
||||
public:
|
||||
~DirconProcessor();
|
||||
|
||||
@@ -37,6 +37,7 @@ echelonconnectsport::echelonconnectsport(bool noWriteResistance, bool noHeartSer
|
||||
|
||||
void echelonconnectsport::writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
|
||||
bool wait_for_response) {
|
||||
#ifndef Q_OS_IOS
|
||||
QEventLoop loop;
|
||||
QTimer timeout;
|
||||
|
||||
@@ -60,20 +61,29 @@ void echelonconnectsport::writeCharacteristic(uint8_t *data, uint8_t data_len, c
|
||||
qDebug() << QStringLiteral("gattWriteCharacteristic is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (writeBuffer) {
|
||||
delete writeBuffer;
|
||||
}
|
||||
writeBuffer = new QByteArray((const char *)data, data_len);
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
iOS_echelonConnectSport->echelonConnectSport_WriteCharacteristic((unsigned char*)writeBuffer->data(), data_len);
|
||||
#endif
|
||||
#else
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, *writeBuffer);
|
||||
#endif
|
||||
|
||||
if (!disable_log) {
|
||||
qDebug() << QStringLiteral(" >> ") + writeBuffer->toHex(' ') +
|
||||
QStringLiteral(" // ") + info;
|
||||
}
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
loop.exec();
|
||||
#endif
|
||||
}
|
||||
|
||||
void echelonconnectsport::forceResistance(resistance_t requestResistance) {
|
||||
@@ -105,17 +115,23 @@ void echelonconnectsport::sendPoll() {
|
||||
}
|
||||
|
||||
void echelonconnectsport::update() {
|
||||
#ifndef Q_OS_IOS
|
||||
if (m_control->state() == QLowEnergyController::UnconnectedState) {
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (initRequest) {
|
||||
initRequest = false;
|
||||
btinit();
|
||||
} else if (bluetoothDevice.isValid() && m_control->state() == QLowEnergyController::DiscoveredState &&
|
||||
} else if (
|
||||
#ifndef Q_OS_IOS
|
||||
bluetoothDevice.isValid() && m_control->state() == QLowEnergyController::DiscoveredState &&
|
||||
gattCommunicationChannelService && gattWriteCharacteristic.isValid() &&
|
||||
gattNotify1Characteristic.isValid() && gattNotify2Characteristic.isValid() && initDone) {
|
||||
gattNotify1Characteristic.isValid() && gattNotify2Characteristic.isValid() &&
|
||||
#endif
|
||||
initDone) {
|
||||
update_metrics(true, watts());
|
||||
|
||||
// sending poll every 2 seconds
|
||||
@@ -318,9 +334,11 @@ void echelonconnectsport::characteristicChanged(const QLowEnergyCharacteristic &
|
||||
qDebug() << QStringLiteral("Last CrankEventTime: ") + QString::number(LastCrankEventTime);
|
||||
qDebug() << QStringLiteral("Current Watt: ") + QString::number(watts());
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
if (m_control->error() != QLowEnergyController::NoError) {
|
||||
qDebug() << QStringLiteral("QLowEnergyController ERROR!!") << m_control->errorString();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QTime echelonconnectsport::GetElapsedFromPacket(const QByteArray &packet) {
|
||||
@@ -366,6 +384,7 @@ void echelonconnectsport::btinit() {
|
||||
}
|
||||
|
||||
void echelonconnectsport::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
#ifndef Q_OS_IOS
|
||||
QBluetoothUuid _gattWriteCharacteristicId(QStringLiteral("0bf669f2-45f2-11e7-9598-0800200c9a66"));
|
||||
QBluetoothUuid _gattNotify1CharacteristicId(QStringLiteral("0bf669f3-45f2-11e7-9598-0800200c9a66"));
|
||||
QBluetoothUuid _gattNotify2CharacteristicId(QStringLiteral("0bf669f4-45f2-11e7-9598-0800200c9a66"));
|
||||
@@ -393,7 +412,7 @@ void echelonconnectsport::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
this, &echelonconnectsport::errorService);
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::descriptorWritten, this,
|
||||
&echelonconnectsport::descriptorWritten);
|
||||
|
||||
#endif
|
||||
// ******************************************* virtual bike init *************************************
|
||||
if (!firstStateChanged && !this->hasVirtualDevice()
|
||||
#ifdef Q_OS_IOS
|
||||
@@ -438,7 +457,7 @@ void echelonconnectsport::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
}
|
||||
firstStateChanged = 1;
|
||||
// ********************************************************************************************************
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
QByteArray descriptor;
|
||||
descriptor.append((char)0x01);
|
||||
descriptor.append((char)0x00);
|
||||
@@ -447,6 +466,7 @@ void echelonconnectsport::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
gattCommunicationChannelService->writeDescriptor(
|
||||
gattNotify2Characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), descriptor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void echelonconnectsport::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue) {
|
||||
@@ -464,7 +484,7 @@ void echelonconnectsport::characteristicWritten(const QLowEnergyCharacteristic &
|
||||
|
||||
void echelonconnectsport::serviceScanDone(void) {
|
||||
qDebug() << QStringLiteral("serviceScanDone");
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
QBluetoothUuid _gattCommunicationChannelServiceId(QStringLiteral("0bf669f1-45f2-11e7-9598-0800200c9a66"));
|
||||
|
||||
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
|
||||
@@ -477,6 +497,7 @@ void echelonconnectsport::serviceScanDone(void) {
|
||||
homeform::singleton()->setToastRequested("Bluetooth Service Error! Restart the bike!");
|
||||
m_control->disconnectFromDevice();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void echelonconnectsport::errorService(QLowEnergyService::ServiceError err) {
|
||||
@@ -494,6 +515,14 @@ void echelonconnectsport::error(QLowEnergyController::Error err) {
|
||||
void echelonconnectsport::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
qDebug() << QStringLiteral("Found new device: ") + device.name() + QStringLiteral(" (") +
|
||||
device.address().toString() + ')';
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
iOS_echelonConnectSport = new lockscreen();
|
||||
iOS_echelonConnectSport->echelonConnectSport(device.name().toStdString().c_str(), this);
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
if (device.name().startsWith(QStringLiteral("ECH"))) {
|
||||
bluetoothDevice = device;
|
||||
|
||||
@@ -531,6 +560,10 @@ void echelonconnectsport::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
}
|
||||
|
||||
bool echelonconnectsport::connected() {
|
||||
#ifdef Q_OS_IOS
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (!m_control) {
|
||||
return false;
|
||||
}
|
||||
@@ -657,10 +690,16 @@ uint16_t echelonconnectsport::wattsFromResistance(double resistance) {
|
||||
|
||||
void echelonconnectsport::controllerStateChanged(QLowEnergyController::ControllerState state) {
|
||||
qDebug() << QStringLiteral("controllerStateChanged") << state;
|
||||
if (state == QLowEnergyController::UnconnectedState && m_control) {
|
||||
if (state == QLowEnergyController::UnconnectedState
|
||||
#ifndef Q_OS_IOS
|
||||
&& m_control
|
||||
#endif
|
||||
) {
|
||||
lastResistanceBeforeDisconnection = Resistance.value();
|
||||
qDebug() << QStringLiteral("trying to connect back again...");
|
||||
initDone = false;
|
||||
#ifndef Q_OS_IOS
|
||||
m_control->connectToDevice();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +60,12 @@ class echelonconnectsport : public bike {
|
||||
|
||||
QTimer *refresh;
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
QLowEnergyService *gattCommunicationChannelService = nullptr;
|
||||
QLowEnergyCharacteristic gattWriteCharacteristic;
|
||||
QLowEnergyCharacteristic gattNotify1Characteristic;
|
||||
QLowEnergyCharacteristic gattNotify2Characteristic;
|
||||
#endif
|
||||
|
||||
int8_t bikeResistanceOffset = 4;
|
||||
double bikeResistanceGain = 1.0;
|
||||
@@ -82,6 +84,7 @@ class echelonconnectsport : public bike {
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
lockscreen* iOS_echelonConnectSport = nullptr;
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
@@ -89,14 +92,14 @@ class echelonconnectsport : public bike {
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void stateChanged(QLowEnergyService::ServiceState state);
|
||||
void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue);
|
||||
void controllerStateChanged(QLowEnergyController::ControllerState state);
|
||||
|
||||
private slots:
|
||||
|
||||
void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue);
|
||||
void stateChanged(QLowEnergyService::ServiceState state);
|
||||
void controllerStateChanged(QLowEnergyController::ControllerState state);
|
||||
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void serviceScanDone(void);
|
||||
|
||||
@@ -48,10 +48,13 @@ void fakebike::update() {
|
||||
|
||||
if (requestPower != -1) {
|
||||
// bepo70: don't know if this conversion is really needed, i would do it anyway.
|
||||
m_watt = (double)requestPower;
|
||||
Cadence = requestPower;
|
||||
m_watt = (double)requestPower * (1.0 + (((double)rand() / RAND_MAX) * 0.4 - 0.2));
|
||||
if(requestPower)
|
||||
Cadence = 50 + (static_cast<double>(rand()) / RAND_MAX) * 50;
|
||||
else
|
||||
Cadence = 0;
|
||||
emit debug(QStringLiteral("writing power ") + QString::number(requestPower));
|
||||
requestPower = -1;
|
||||
//requestPower = -1;
|
||||
// bepo70: Disregard the current inclination for calculating speed. When the video
|
||||
// has a high inclination you have to give many power to get the desired playback speed,
|
||||
// if inclination is very low little more power gives a quite high speed jump.
|
||||
|
||||
@@ -271,6 +271,9 @@ void ftmsbike::forceResistance(resistance_t requestResistance) {
|
||||
}
|
||||
|
||||
void ftmsbike::update() {
|
||||
|
||||
QSettings settings;
|
||||
|
||||
if (m_control->state() == QLowEnergyController::UnconnectedState) {
|
||||
emit disconnected();
|
||||
return;
|
||||
@@ -280,6 +283,11 @@ void ftmsbike::update() {
|
||||
zwiftPlayInit();
|
||||
if(ICSE)
|
||||
requestResistance = 1; // to force the engine to send every second a target inclination
|
||||
|
||||
// when we are emulating the zwift protocol, zwift doesn't senf the start simulation frames, so we have to send them
|
||||
if(settings.value(QZSettings::zwift_play_emulator, QZSettings::default_zwift_play_emulator).toBool())
|
||||
init();
|
||||
|
||||
initRequest = false;
|
||||
} else if (bluetoothDevice.isValid() &&
|
||||
m_control->state() == QLowEnergyController::DiscoveredState //&&
|
||||
@@ -303,8 +311,7 @@ void ftmsbike::update() {
|
||||
forceResistance(currentResistance().value());
|
||||
}
|
||||
|
||||
auto virtualBike = this->VirtualBike();
|
||||
QSettings settings;
|
||||
auto virtualBike = this->VirtualBike();
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
|
||||
if (requestResistance != -1 || lastGearValue != gears()) {
|
||||
@@ -392,7 +399,8 @@ void ftmsbike::update() {
|
||||
|
||||
lastGearValue = gears();
|
||||
|
||||
if (requestPower != -1) {
|
||||
// if a classic request of power from zwift or any other platform is coming, will be transfereed on the ftmsCharacteristicChanged applying the gear mod too
|
||||
if (requestPower != -1 && (!virtualBike || !virtualBike->ftmsDeviceConnected() || (zwiftPlayService != nullptr && gears_zwift_ratio))) {
|
||||
qDebug() << QStringLiteral("writing power") << requestPower;
|
||||
init();
|
||||
forcePower(requestPower);
|
||||
|
||||
@@ -36,14 +36,14 @@ import Foundation
|
||||
return fullData
|
||||
}
|
||||
|
||||
public static func ridingData(power: UInt32, cadence: UInt32, speed: Double, HR: UInt32) throws -> Data {
|
||||
public static func ridingData(power: UInt32, cadence: UInt32, speed: Double, HR: UInt32, unkown1: UInt32, unkown2: UInt32) throws -> Data {
|
||||
var physical = BLEReceiver_Zwift_HubRidingData()
|
||||
physical.cadence = cadence
|
||||
physical.power = power
|
||||
physical.speedX100 = UInt32(speed * 100.0)
|
||||
physical.hr = HR
|
||||
physical.unknown1 = 2864
|
||||
physical.unknown2 = 25714
|
||||
physical.unknown1 = unkown1
|
||||
physical.unknown2 = unkown2
|
||||
|
||||
let data = try physical.serializedData()
|
||||
var fullData = Data([0x03])
|
||||
|
||||
@@ -4579,9 +4579,12 @@ QString homeform::startIcon() {
|
||||
void homeform::updateGearsValue() {
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
bool zwift_play_emulator = settings.value(QZSettings::zwift_play_emulator, QZSettings::default_zwift_play_emulator).toBool();
|
||||
double gear = ((bike *)bluetoothManager->device())->gears();
|
||||
double maxGearDefault = ((bike *)bluetoothManager->device())->defaultMaxGears();
|
||||
double maxGear = ((bike *)bluetoothManager->device())->maxGears();
|
||||
if(zwift_play_emulator)
|
||||
gear = ((bike *)bluetoothManager->device())->VirtualBike()->currentGear();
|
||||
if (settings.value(QZSettings::gears_gain, QZSettings::default_gears_gain).toDouble() == 1.0 || gears_zwift_ratio || maxGear < maxGearDefault) {
|
||||
this->gears->setValue(QString::number(gear));
|
||||
this->gears->setSecondLine(wheelCircumference::gearsInfo(gear));
|
||||
|
||||
23
src/ios/ios_echelonconnectsport.h
Normal file
23
src/ios/ios_echelonconnectsport.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef IOSECHELONCONNECTSPORT_H
|
||||
#define IOSECHELONCONNECTSPORT_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import "echelonconnectsport.h"
|
||||
|
||||
@interface ios_echelonconnectsport : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate>
|
||||
|
||||
@property (strong, nonatomic) CBCentralManager *centralManager;
|
||||
@property (strong, nonatomic) CBPeripheral *connectedPeripheral;
|
||||
@property (strong, nonatomic) NSString *targetDeviceName;
|
||||
@property echelonconnectsport *qtDevice;
|
||||
@property (strong, nonatomic) CBCharacteristic *gattWriteCharacteristic;
|
||||
@property (strong, nonatomic) CBCharacteristic *gattNotify1Characteristic;
|
||||
@property (strong, nonatomic) CBCharacteristic *gattNotify2Characteristic;
|
||||
|
||||
- (instancetype)init:(NSString *)deviceName qtDevice:(void*)qtDevice;
|
||||
- (void)writeCharacteristc:(uint8_t *)data length:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
|
||||
#endif // IOSECHELONCONNECTSPORT_H
|
||||
112
src/ios/ios_echelonconnectsport.mm
Normal file
112
src/ios/ios_echelonconnectsport.mm
Normal file
@@ -0,0 +1,112 @@
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import "ios_echelonconnectsport.h"
|
||||
|
||||
@implementation ios_echelonconnectsport
|
||||
|
||||
- (instancetype)init:(NSString *)deviceName qtDevice:(void*)qtDevice {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_targetDeviceName = [deviceName copy];
|
||||
qDebug() << _targetDeviceName;
|
||||
_qtDevice = (echelonconnectsport*)qtDevice;
|
||||
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
|
||||
if (central.state == CBManagerStatePoweredOn) {
|
||||
qDebug() << "centralManagerDidUpdateState" << central.state;
|
||||
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
|
||||
if (peripheral && _targetDeviceName && peripheral.name) {
|
||||
qDebug() << _targetDeviceName;
|
||||
if ([peripheral.name isEqualToString:_targetDeviceName]) {
|
||||
self.connectedPeripheral = peripheral;
|
||||
[self.centralManager stopScan];
|
||||
[self.centralManager connectPeripheral:peripheral options:nil];
|
||||
qDebug() << "didDiscoverPeripheral";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
|
||||
peripheral.delegate = self;
|
||||
[peripheral discoverServices:nil];
|
||||
}
|
||||
|
||||
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
|
||||
qDebug() << "Peripheral disconnected:" << peripheral << error;
|
||||
if ([peripheral.name isEqualToString:self.targetDeviceName]) {
|
||||
_qtDevice->controllerStateChanged(QLowEnergyController::UnconnectedState);
|
||||
[self.centralManager connectPeripheral:peripheral options:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
|
||||
for (CBService *service in peripheral.services) {
|
||||
[peripheral discoverCharacteristics:nil forService:service];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
|
||||
if (characteristic.value) {
|
||||
qDebug() << "didUpdateValueForCharacteristic" << characteristic;
|
||||
// Extract the data from characteristic.value and process it as needed
|
||||
NSData *receivedData = characteristic.value;
|
||||
NSLog(@"UUID: %@ Received data: %@", characteristic.UUID, receivedData);
|
||||
// Your processing logic here
|
||||
//[self.qtDevice characteristicChanged:characteristic.UUID data:receivedData];
|
||||
QLowEnergyCharacteristic c;
|
||||
QByteArray b;
|
||||
const uint8_t* d = (const uint8_t*)[receivedData bytes];
|
||||
for(int i=0; i<receivedData.length; i++) {
|
||||
b.append(d[i]);
|
||||
}
|
||||
self.qtDevice->characteristicChanged(c, b);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
|
||||
CBUUID *write = [CBUUID UUIDWithString:@"0bf669f2-45f2-11e7-9598-0800200c9a66"];
|
||||
CBUUID *uuid1 = [CBUUID UUIDWithString:@"0bf669f3-45f2-11e7-9598-0800200c9a66"];
|
||||
CBUUID *uuid2 = [CBUUID UUIDWithString:@"0bf669f4-45f2-11e7-9598-0800200c9a66"];
|
||||
|
||||
for (CBCharacteristic *characteristic in service.characteristics) {
|
||||
if ([characteristic.UUID isEqual:uuid1]) {
|
||||
self.gattNotify1Characteristic = characteristic;
|
||||
[peripheral setNotifyValue:YES forCharacteristic:self.gattNotify1Characteristic];
|
||||
} else if ([characteristic.UUID isEqual:uuid2]) {
|
||||
self.gattNotify2Characteristic = characteristic;
|
||||
[peripheral setNotifyValue:YES forCharacteristic:self.gattNotify2Characteristic];
|
||||
} else if ([characteristic.UUID isEqual:write]) {
|
||||
self.gattWriteCharacteristic = characteristic;
|
||||
}
|
||||
}
|
||||
|
||||
// Verifica se entrambe le caratteristiche sono state trovate
|
||||
if (self.gattNotify1Characteristic && self.gattNotify2Characteristic && self.gattWriteCharacteristic) {
|
||||
self.qtDevice->stateChanged(QLowEnergyService::ServiceDiscovered);
|
||||
self.qtDevice->descriptorWritten(QLowEnergyDescriptor(), QByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeCharacteristc:(uint8_t *)data length:(NSUInteger)length {
|
||||
if (self.connectedPeripheral.state != CBPeripheralStateConnected) {
|
||||
qDebug() << "Cannot send. Peripheral is not connected.";
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *dataToSend = [NSData dataWithBytes:data length:length];
|
||||
|
||||
qDebug() << "writeCharacteristc" << dataToSend;
|
||||
|
||||
if (self.gattWriteCharacteristic) {
|
||||
[self.connectedPeripheral writeValue:dataToSend forCharacteristic:self.gattWriteCharacteristic type:CBCharacteristicWriteWithResponse];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -73,6 +73,10 @@ class lockscreen {
|
||||
// Elite Aria Fan
|
||||
void eliteAriaFan();
|
||||
void eliteAriaFan_fanSpeedRequest(unsigned char speed);
|
||||
|
||||
// Echelon Connect Sport
|
||||
void echelonConnectSport(const char* Name, void* deviceClass);
|
||||
void echelonConnectSport_WriteCharacteristic(unsigned char* qdata, unsigned char length);
|
||||
|
||||
// Zwift API
|
||||
void zwift_api_decodemessage_player(const char* data, int len);
|
||||
|
||||
@@ -10,17 +10,18 @@
|
||||
#include <QDebug>
|
||||
#include "ios/AdbClient.h"
|
||||
#include "ios/ios_eliteariafan.h"
|
||||
#include "ios/ios_echelonconnectsport.h"
|
||||
|
||||
@class virtualbike_ios_swift;
|
||||
@class virtualbike_zwift;
|
||||
@class virtualrower;
|
||||
@class virtualrower_zwift;
|
||||
@class virtualtreadmill_zwift;
|
||||
@class healthkit;
|
||||
|
||||
static healthkit* h = 0;
|
||||
static virtualbike_ios_swift* _virtualbike = nil;
|
||||
static virtualbike_zwift* _virtualbike_zwift = nil;
|
||||
static virtualrower* _virtualrower = nil;
|
||||
static virtualrower_zwift* _virtualrower = nil;
|
||||
static virtualtreadmill_zwift* _virtualtreadmill_zwift = nil;
|
||||
|
||||
static GarminConnect* Garmin = 0;
|
||||
@@ -28,6 +29,7 @@ static GarminConnect* Garmin = 0;
|
||||
static AdbClient *_adb = 0;
|
||||
|
||||
static ios_eliteariafan* ios_eliteAriaFan = nil;
|
||||
static ios_echelonconnectsport* ios_echelonConnectSport = nil;
|
||||
|
||||
static zwift_protobuf_layer* zwiftProtobufLayer = nil;
|
||||
|
||||
@@ -113,7 +115,7 @@ void lockscreen::virtualbike_zwift_ios(bool disable_hr, bool garmin_bluetooth_co
|
||||
|
||||
void lockscreen::virtualrower_ios()
|
||||
{
|
||||
_virtualrower = [[virtualrower alloc] init];
|
||||
_virtualrower = [[virtualrower_zwift alloc] init];
|
||||
}
|
||||
|
||||
double lockscreen::virtualbike_getCurrentSlope()
|
||||
@@ -326,6 +328,17 @@ void lockscreen::eliteAriaFan_fanSpeedRequest(unsigned char speed) {
|
||||
}
|
||||
}
|
||||
|
||||
void lockscreen::echelonConnectSport(const char* Name, void* deviceClass) {
|
||||
NSString *deviceName = [NSString stringWithCString:Name encoding:NSASCIIStringEncoding];
|
||||
ios_echelonConnectSport = [[ios_echelonconnectsport alloc] init:deviceName qtDevice:deviceClass];
|
||||
}
|
||||
|
||||
void lockscreen::echelonConnectSport_WriteCharacteristic(unsigned char* qdata, unsigned char length) {
|
||||
if(ios_echelonConnectSport) {
|
||||
[ios_echelonConnectSport writeCharacteristc:qdata length:length ];
|
||||
}
|
||||
}
|
||||
|
||||
void lockscreen::zwift_api_decodemessage_player(const char* data, int len) {
|
||||
NSData *d = [NSData dataWithBytes:data length:len];
|
||||
[zwiftProtobufLayer getPlayerStateWithValue:d];
|
||||
|
||||
@@ -379,7 +379,27 @@ class BLEPeripheralManagerZwift: NSObject, CBPeripheralManagerDelegate {
|
||||
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
|
||||
func calculateUnknown1(power: UInt16) -> UInt32 {
|
||||
var lastUnknown1: UInt32 = 836
|
||||
let baseValue: UInt32 = 19000
|
||||
|
||||
var increment = 400 + (UInt32(power) * 2)
|
||||
increment = min(increment, 800)
|
||||
|
||||
if power > 0 {
|
||||
lastUnknown1 += increment
|
||||
} else {
|
||||
lastUnknown1 += 600
|
||||
}
|
||||
|
||||
if lastUnknown1 > 24000 {
|
||||
lastUnknown1 = baseValue
|
||||
}
|
||||
|
||||
return lastUnknown1
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
|
||||
if let value = requests.first?.value {
|
||||
let hexString = value.map { String(format: "%02x", $0) }.joined(separator: " ")
|
||||
let debugMessage = "virtualbike_zwift didReceiveWrite: " + String(describing: requests.first!.characteristic) + " " + hexString + " " + ((requests.first!.characteristic == self.FitnessMachineControlPointCharacteristic) ? "FTMS" : "NOFTMS") + " " + String(describing: self.FitnessMachineControlPointCharacteristic) + " " + self.FitnessMachineControlPointCharacteristic.uuid.uuidString + " " + requests.first!.characteristic.uuid.uuidString
|
||||
@@ -493,7 +513,7 @@ class BLEPeripheralManagerZwift: NSObject, CBPeripheralManagerDelegate {
|
||||
// 04 22 02 10 1a TODO
|
||||
var r = receivedBytes
|
||||
r.remove(at: 0)
|
||||
var slopefloat = decodeSInt(r)
|
||||
let slopefloat = decodeSInt(r)
|
||||
print("slopefloat \(slopefloat)")
|
||||
var slope: [UInt8] = [ 0x00, 0x00 ]
|
||||
self.CurrentSlope = Double(slopefloat)
|
||||
@@ -501,10 +521,13 @@ class BLEPeripheralManagerZwift: NSObject, CBPeripheralManagerDelegate {
|
||||
slope[1] = UInt8((Int16(self.CurrentSlope) >> 8) & 0x00FF)
|
||||
LastFTMSMessageReceived = Data([0x11, 0x69, 0x01, slope[0], slope[1], 0x32, 0x28])
|
||||
|
||||
var response: [UInt8] = [ 0x3c, 0x08, 0x88, 0x04, 0x12, 0x06, 0x0a, 0x04, 0x40, 0xc0, 0xbb, 0x01 ]
|
||||
var responseData = Data(bytes: &response, count: 12)
|
||||
|
||||
updateQueue.append((ZwiftPlayIndicateCharacteristic, responseData))
|
||||
do {
|
||||
let response = try ZwiftHubBike.ridingData(power: UInt32(self.CurrentWatt), cadence: UInt32(self.CurrentCadence / 2), speed: 0, HR: UInt32(self.heartRate), unkown1: self.calculateUnknown1(power: self.CurrentWatt), unkown2: 0)
|
||||
|
||||
updateQueue.append((ZwiftPlayIndicateCharacteristic, response))
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
let receivedBytes6 = [UInt8](receivedData.prefix(expectedHexArray6.count))
|
||||
|
||||
@@ -561,14 +584,13 @@ class BLEPeripheralManagerZwift: NSObject, CBPeripheralManagerDelegate {
|
||||
self.PowerRequested = (Double)(power);
|
||||
LastFTMSMessageReceived = Data([0x05, UInt8(UInt16(power) & 0xff), UInt8(((UInt16(power) & 0xff00) >> 8) & 0x00ff)])
|
||||
|
||||
var response: [UInt8] = [ 0x03, 0x08, 0x82, 0x01, 0x10, 0x22, 0x18, 0x10, 0x20, 0x00, 0x28, 0x98, 0x52, 0x30, 0x86, 0xed, 0x01 ]
|
||||
response[2] = receivedData[2]
|
||||
if(receivedData.count == 4) {
|
||||
response[3] = receivedData[3]
|
||||
}
|
||||
let responseData = Data(bytes: &response, count: 17)
|
||||
|
||||
updateQueue.append((ZwiftPlayReadCharacteristic, responseData))
|
||||
do {
|
||||
let response = try ZwiftHubBike.ridingData(power: UInt32(power), cadence: UInt32(self.CurrentCadence / 2), speed: Double(self.NormalizeSpeed) / 100.0, HR: UInt32(self.heartRate), unkown1: self.calculateUnknown1(power: self.CurrentWatt), unkown2: 0)
|
||||
|
||||
updateQueue.append((ZwiftPlayIndicateCharacteristic, response))
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -719,8 +741,12 @@ class BLEPeripheralManagerZwift: NSObject, CBPeripheralManagerDelegate {
|
||||
|
||||
func startSendingDataToSubscribers() {
|
||||
if self.notificationTimer == nil {
|
||||
self.notificationTimer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.updateSubscribers), userInfo: nil, repeats: true)
|
||||
var t : Double = 0.2
|
||||
if(zwift_play_emulator) {
|
||||
t = 0.01
|
||||
}
|
||||
self.notificationTimer = Timer.scheduledTimer(timeInterval: t, target: self, selector: #selector(self.updateSubscribers), userInfo: nil, repeats: true)
|
||||
}
|
||||
}
|
||||
|
||||
func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
|
||||
@@ -855,22 +881,17 @@ class BLEPeripheralManagerZwift: NSObject, CBPeripheralManagerDelegate {
|
||||
}
|
||||
} else if(zwift_play_emulator) {
|
||||
if(!sendUpdates()) {
|
||||
/*
|
||||
do {
|
||||
let ZwiftPlayData = try ZwiftHubBike.ridingData(power: UInt32(self.CurrentWatt), cadence: UInt32(self.CurrentCadence), speed: Double(self.NormalizeSpeed), HR: UInt32(self.heartRate))
|
||||
let ok = self.peripheralManager.updateValue(ZwiftPlayData, for: self.ZwiftPlayReadCharacteristic, onSubscribedCentrals: nil)
|
||||
let response = try ZwiftHubBike.ridingData(power: UInt32(self.CurrentWatt), cadence: UInt32(self.CurrentCadence / 2), speed: 0, HR: UInt32(self.heartRate), unkown1: self.calculateUnknown1(power: self.CurrentWatt), unkown2: 0)
|
||||
|
||||
let ok = self.peripheralManager.updateValue(response, for: self.ZwiftPlayReadCharacteristic, onSubscribedCentrals: nil)
|
||||
if(ok) {
|
||||
self.serviceToggle = self.serviceToggle + 1
|
||||
}
|
||||
|
||||
} catch {
|
||||
self.serviceToggle = self.serviceToggle + 1
|
||||
}*/
|
||||
let ZwiftPlayArray : [UInt8] = [ 0x03, 0x08, 0x00, 0x10, 0x00, 0x18, 0xe7, 0x02, 0x20, 0x00, 0x28, 0x00, 0x30, 0x9b, 0xed, 0x01 ]
|
||||
let ZwiftPlayData = Data(bytes: ZwiftPlayArray, count: 16)
|
||||
let ok = self.peripheralManager.updateValue(ZwiftPlayData, for: self.ZwiftPlayReadCharacteristic, onSubscribedCentrals: nil)
|
||||
if(ok) {
|
||||
self.serviceToggle = self.serviceToggle + 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.serviceToggle = self.serviceToggle + 1
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import CoreBluetooth
|
||||
|
||||
let rowerUuid = CBUUID(string: "0x2AD1");
|
||||
|
||||
@objc public class virtualrower: NSObject {
|
||||
@objc public class virtualrower_zwift: NSObject {
|
||||
private var peripheralManager: rowerBLEPeripheralManagerZwift!
|
||||
|
||||
@objc public override init() {
|
||||
|
||||
@@ -667,7 +667,7 @@ void peloton::login_onfinish(QNetworkReply *reply) {
|
||||
int status = json[QStringLiteral("status")].toInt();
|
||||
|
||||
if (log_request) {
|
||||
qDebug() << QStringLiteral("login_onfinish") << document;
|
||||
qDebug() << QStringLiteral("login_onfinish") << document << payload;
|
||||
} else {
|
||||
qDebug() << QStringLiteral("login_onfinish");
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ DEFINES += QT_DEPRECATED_WARNINGS IO_UNDER_QT SMTP_BUILD NOMINMAX
|
||||
# include(../qtzeroconf/qtzeroconf.pri)
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/characteristics/characteristicnotifier0002.cpp \
|
||||
$$PWD/characteristics/characteristicnotifier0004.cpp \
|
||||
$$PWD/characteristics/characteristicwriteprocessor0003.cpp \
|
||||
$$PWD/devices/antbike/antbike.cpp \
|
||||
$$PWD/devices/crossrope/crossrope.cpp \
|
||||
$$PWD/devices/cycleopsphantombike/cycleopsphantombike.cpp \
|
||||
@@ -339,6 +342,9 @@ INCLUDEPATH += fit-sdk/ devices/
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/EventHandler.h \
|
||||
$$PWD/characteristics/characteristicnotifier0002.h \
|
||||
$$PWD/characteristics/characteristicnotifier0004.h \
|
||||
$$PWD/characteristics/characteristicwriteprocessor0003.h \
|
||||
$$PWD/OAuth2.h \
|
||||
$$PWD/devices/antbike/antbike.h \
|
||||
$$PWD/devices/crossrope/crossrope.h \
|
||||
|
||||
@@ -5135,7 +5135,7 @@ import Qt.labs.platform 1.1
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
/*
|
||||
IndicatorOnlySwitch {
|
||||
text: qsTr("Show Gears to Zwift Only")
|
||||
spacing: 0
|
||||
@@ -5162,7 +5162,7 @@ import Qt.labs.platform 1.1
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
@@ -10933,6 +10933,7 @@ import Qt.labs.platform 1.1
|
||||
settings: settings
|
||||
accordionContent: ColumnLayout {
|
||||
spacing: 0
|
||||
/*
|
||||
IndicatorOnlySwitch {
|
||||
id: wahooRGTDirconDelegate
|
||||
text: qsTr("MyWhoosh Compatibility")
|
||||
@@ -10946,7 +10947,7 @@ import Qt.labs.platform 1.1
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
onClicked: { settings.wahoo_rgt_dircon = checked; window.settings_restart_to_apply = true; }
|
||||
}
|
||||
}*/
|
||||
|
||||
Label {
|
||||
text: qsTr("Enables the compatibility of the Wahoo KICKR protocol to Wahoo RGT app. Leave the RGT compatibility disabled in order to use Zwift.")
|
||||
|
||||
@@ -48,7 +48,10 @@ virtualbike::virtualbike(bluetoothdevice *t, bool noWriteResistance, bool noHear
|
||||
notif2A63 = new CharacteristicNotifier2A63(Bike, this);
|
||||
notif2A37 = new CharacteristicNotifier2A37(Bike, this);
|
||||
notif2A5B = new CharacteristicNotifier2A5B(Bike, this);
|
||||
notif0002 = new CharacteristicNotifier0002(Bike, this);
|
||||
notif0004 = new CharacteristicNotifier0004(Bike, this);
|
||||
writeP2AD9 = new CharacteristicWriteProcessor2AD9(bikeResistanceGain, bikeResistanceOffset, Bike, notif2AD9, this);
|
||||
writeP0003 = new CharacteristicWriteProcessor0003(bikeResistanceGain, bikeResistanceOffset, Bike, notif0002, notif0004, this);
|
||||
connect(writeP2AD9, SIGNAL(changeInclination(double, double)), this, SIGNAL(changeInclination(double, double)));
|
||||
Q_UNUSED(noWriteResistance)
|
||||
|
||||
@@ -509,8 +512,8 @@ virtualbike::virtualbike(bluetoothdevice *t, bool noWriteResistance, bool noHear
|
||||
|
||||
//! [Provide Heartbeat]
|
||||
QObject::connect(&bikeTimer, &QTimer::timeout, this, &virtualbike::bikeProvider);
|
||||
if (settings.value(QZSettings::race_mode, QZSettings::default_race_mode).toBool())
|
||||
bikeTimer.start(100ms);
|
||||
if (settings.value(QZSettings::race_mode, QZSettings::default_race_mode).toBool() || zwift_play_emulator)
|
||||
bikeTimer.start(50ms);
|
||||
else
|
||||
bikeTimer.start(1s);
|
||||
|
||||
@@ -991,7 +994,10 @@ void virtualbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
if (targetCharacteristic.isValid()) {
|
||||
characteristicChanged(targetCharacteristic, QByteArray::fromHex("116901") + slope + QByteArray::fromHex("3228"));
|
||||
|
||||
QByteArray response = QByteArray::fromHex("3c0888041206 0a0440c0bb01");
|
||||
QByteArray response = CharacteristicWriteProcessor0003::encodeHubRidingData(Bike->wattsMetric().value(), Bike->currentCadence().value(), 0,
|
||||
Bike->wattsMetric().value(),
|
||||
CharacteristicWriteProcessor0003::calculateUnknown1(Bike->wattsMetric().value()),
|
||||
0);
|
||||
writeCharacteristic(serviceZwiftPlayBike, zwiftPlayIndicate, response);
|
||||
} else {
|
||||
qDebug() << "ERROR! Zwift Play Ask 5 without answer!";
|
||||
@@ -1004,7 +1010,7 @@ void virtualbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
response[9] = receivedData[4];
|
||||
response[10] = receivedData[5];
|
||||
response[11] = receivedData[6];
|
||||
handleZwiftGear(receivedData.mid(4));
|
||||
writeP0003->handleZwiftGear(receivedData.mid(4));
|
||||
writeCharacteristic(serviceZwiftPlayBike, zwiftPlayIndicate, response);
|
||||
|
||||
response = QByteArray::fromHex("03080010001827e7 20002896143093ed01");
|
||||
@@ -1019,7 +1025,7 @@ void virtualbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
response = QByteArray::fromHex("3c088804120503408c60");
|
||||
response[9] = receivedData[4];
|
||||
response[10] = receivedData[5];
|
||||
handleZwiftGear(receivedData.mid(4));
|
||||
writeP0003->handleZwiftGear(receivedData.mid(4));
|
||||
writeCharacteristic(serviceZwiftPlayBike, zwiftPlayIndicate, response);
|
||||
}
|
||||
else if (receivedData.startsWith(expectedHexArray8)) {
|
||||
@@ -1043,9 +1049,11 @@ void virtualbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
if (targetCharacteristic.isValid()) {
|
||||
characteristicChanged(targetCharacteristic, QByteArray::fromHex("05") + power);
|
||||
|
||||
QByteArray response = QByteArray::fromHex("030882011022181020002898523086ed01");
|
||||
response[2] = (uint8_t)Bike->wattsMetric().value();
|
||||
writeCharacteristic(serviceZwiftPlayBike, zwiftPlayRead, response);
|
||||
QByteArray response = CharacteristicWriteProcessor0003::encodeHubRidingData(Bike->wattsMetric().value(), Bike->currentCadence().value(), 0,
|
||||
Bike->wattsMetric().value(),
|
||||
CharacteristicWriteProcessor0003::calculateUnknown1(Bike->wattsMetric().value()),
|
||||
0);
|
||||
writeCharacteristic(serviceZwiftPlayBike, zwiftPlayIndicate, response);
|
||||
} else {
|
||||
qDebug() << "ERROR! Zwift Play Ask 8 without answer!";
|
||||
}
|
||||
@@ -1135,52 +1143,6 @@ void virtualbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
}
|
||||
}
|
||||
|
||||
void virtualbike::handleZwiftGear(const QByteArray &array)
|
||||
{
|
||||
uint8_t g = 0;
|
||||
if (array.size() >= 2) {
|
||||
if ((uint8_t)array[0] == (uint8_t)0xCC && (uint8_t)array[1] == (uint8_t)0x3A) g = 1;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xFC && (uint8_t)array[1] == (uint8_t)0x43) g = 2;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xAC && (uint8_t)array[1] == (uint8_t)0x4D) g = 3;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xDC && (uint8_t)array[1] == (uint8_t)0x56) g = 4;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x8C && (uint8_t)array[1] == (uint8_t)0x60) g = 5;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xE8 && (uint8_t)array[1] == (uint8_t)0x6B) g = 6;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xC4 && (uint8_t)array[1] == (uint8_t)0x77) g = 7;
|
||||
else if (array.size() >= 3) {
|
||||
if ((uint8_t)array[0] == (uint8_t)0xA0 && (uint8_t)array[1] == (uint8_t)0x83 && (uint8_t)array[2] == (uint8_t)0x01) g = 8;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xA8 && (uint8_t)array[1] == (uint8_t)0x91 && (uint8_t)array[2] == (uint8_t)0x01) g = 9;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xB0 && (uint8_t)array[1] == (uint8_t)0x9F && (uint8_t)array[2] == (uint8_t)0x01) g = 10;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xB8 && (uint8_t)array[1] == (uint8_t)0xAD && (uint8_t)array[2] == (uint8_t)0x01) g = 11;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xC0 && (uint8_t)array[1] == (uint8_t)0xBB && (uint8_t)array[2] == (uint8_t)0x01) g = 12;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xF3 && (uint8_t)array[1] == (uint8_t)0xCB && (uint8_t)array[2] == (uint8_t)0x01) g = 13;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xA8 && (uint8_t)array[1] == (uint8_t)0xDC && (uint8_t)array[2] == (uint8_t)0x01) g = 14;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xDC && (uint8_t)array[1] == (uint8_t)0xEC && (uint8_t)array[2] == (uint8_t)0x01) g = 15;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x90 && (uint8_t)array[1] == (uint8_t)0xFD && (uint8_t)array[2] == (uint8_t)0x01) g = 16;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xD4 && (uint8_t)array[1] == (uint8_t)0x90 && (uint8_t)array[2] == (uint8_t)0x02) g = 17;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x98 && (uint8_t)array[1] == (uint8_t)0xA4 && (uint8_t)array[2] == (uint8_t)0x02) g = 18;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xDC && (uint8_t)array[1] == (uint8_t)0xB7 && (uint8_t)array[2] == (uint8_t)0x02) g = 19;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x9F && (uint8_t)array[1] == (uint8_t)0xCB && (uint8_t)array[2] == (uint8_t)0x02) g = 20;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xD8 && (uint8_t)array[1] == (uint8_t)0xE2 && (uint8_t)array[2] == (uint8_t)0x02) g = 21;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0x90 && (uint8_t)array[1] == (uint8_t)0xFA && (uint8_t)array[2] == (uint8_t)0x02) g = 22;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xC8 && (uint8_t)array[1] == (uint8_t)0x91 && (uint8_t)array[2] == (uint8_t)0x03) g = 23;
|
||||
else if ((uint8_t)array[0] == (uint8_t)0xF3 && (uint8_t)array[1] == (uint8_t)0xAC && (uint8_t)array[2] == (uint8_t)0x03) g = 24;
|
||||
else { return; }
|
||||
}
|
||||
else { return; }
|
||||
}
|
||||
|
||||
if (g < CurrentZwiftGear) {
|
||||
for (int i = 0; i < CurrentZwiftGear - g; ++i) {
|
||||
((bike*)Bike)->gearDown();
|
||||
}
|
||||
} else if (g > CurrentZwiftGear) {
|
||||
for (int i = 0; i < g - CurrentZwiftGear; ++i) {
|
||||
((bike*)Bike)->gearUp();
|
||||
}
|
||||
}
|
||||
CurrentZwiftGear = g;
|
||||
}
|
||||
|
||||
int virtualbike::iFit_pelotonToBikeResistance(int pelotonResistance) {
|
||||
if (pelotonResistance <= 10) {
|
||||
return 2;
|
||||
@@ -1470,9 +1432,11 @@ void virtualbike::bikeProvider() {
|
||||
if(zwift_play_emulator) {
|
||||
QLowEnergyCharacteristic characteristic1 =
|
||||
serviceZwiftPlayBike->characteristic(QBluetoothUuid(QStringLiteral("00000002-19ca-4651-86e5-fa29dcdd09d1")));
|
||||
const uint8_t v[] = {0x03, 0x08, 0x00, 0x10, 0x00, 0x18, 0xe7, 0x02, 0x20, 0x00, 0x28, 0x00, 0x30, 0x9b, 0xed, 0x01};
|
||||
value = QByteArray::fromRawData((char*)v, 16);
|
||||
writeCharacteristic(serviceZwiftPlayBike, characteristic1, value);
|
||||
QByteArray response = CharacteristicWriteProcessor0003::encodeHubRidingData(Bike->wattsMetric().value(), Bike->currentCadence().value(), 0,
|
||||
Bike->wattsMetric().value(),
|
||||
CharacteristicWriteProcessor0003::calculateUnknown1(Bike->wattsMetric().value()),
|
||||
0);
|
||||
writeCharacteristic(serviceZwiftPlayBike, characteristic1, response);
|
||||
} else if (watt_bike_emulator) {
|
||||
QLowEnergyCharacteristic characteristic1 =
|
||||
serviceWattAtomBike->characteristic(QBluetoothUuid(QStringLiteral("b4cc1224-bc02-4cae-adb9-1217ad2860d1")));
|
||||
|
||||
@@ -42,6 +42,12 @@ class virtualbike : public virtualdevice {
|
||||
else
|
||||
return lastDirconFTMSFrameReceived;
|
||||
}
|
||||
double currentGear() {
|
||||
if(dirconManager) {
|
||||
return dirconManager->currentGear();
|
||||
}
|
||||
return writeP0003->currentGear();
|
||||
}
|
||||
|
||||
private:
|
||||
QLowEnergyController *leController = nullptr;
|
||||
@@ -64,11 +70,14 @@ class virtualbike : public virtualdevice {
|
||||
QTimer bikeTimer;
|
||||
bluetoothdevice *Bike;
|
||||
CharacteristicWriteProcessor2AD9 *writeP2AD9 = 0;
|
||||
CharacteristicWriteProcessor0003 *writeP0003 = 0;
|
||||
CharacteristicNotifier2AD2 *notif2AD2 = 0;
|
||||
CharacteristicNotifier2AD9 *notif2AD9 = 0;
|
||||
CharacteristicNotifier2A63 *notif2A63 = 0;
|
||||
CharacteristicNotifier2A37 *notif2A37 = 0;
|
||||
CharacteristicNotifier2A5B *notif2A5B = 0;
|
||||
CharacteristicNotifier0002 *notif0002 = 0;
|
||||
CharacteristicNotifier0004 *notif0004 = 0;
|
||||
|
||||
qint64 lastFTMSFrameReceived = 0;
|
||||
qint64 lastDirconFTMSFrameReceived = 0;
|
||||
@@ -77,7 +86,6 @@ class virtualbike : public virtualdevice {
|
||||
int8_t bikeResistanceOffset = 4;
|
||||
double bikeResistanceGain = 1.0;
|
||||
DirconManager *dirconManager = 0;
|
||||
uint8_t CurrentZwiftGear = 8;
|
||||
int iFit_pelotonToBikeResistance(int pelotonResistance);
|
||||
int iFit_resistanceToIfit(int ifitResistance);
|
||||
qint64 iFit_timer = 0;
|
||||
@@ -86,8 +94,6 @@ class virtualbike : public virtualdevice {
|
||||
resistance_t iFit_LastResistanceRequested = 0;
|
||||
bool iFit_Stop = false;
|
||||
|
||||
void handleZwiftGear(const QByteArray &array);
|
||||
|
||||
bool echelonInitDone = false;
|
||||
void echelonWriteResistance();
|
||||
void echelonWriteStatus();
|
||||
|
||||
Reference in New Issue
Block a user