mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
741 Commits
cagnulein-
...
build-1062
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4d6abd14 | ||
|
|
b35bebadae | ||
|
|
624234dc92 | ||
|
|
89427c2fcc | ||
|
|
cdda64575a | ||
|
|
483a31d984 | ||
|
|
6d88e7e831 | ||
|
|
bb97e49982 | ||
|
|
4f8090e5bf | ||
|
|
8c18d6f179 | ||
|
|
7d615b4e65 | ||
|
|
6358a55657 | ||
|
|
371d2145b6 | ||
|
|
e264a4c887 | ||
|
|
0991495f51 | ||
|
|
55b2372aa2 | ||
|
|
3646f21d22 | ||
|
|
05e90fba20 | ||
|
|
94c19d70eb | ||
|
|
9a5f42b0b2 | ||
|
|
805639bbfa | ||
|
|
a900ab939d | ||
|
|
34881df30a | ||
|
|
ea9a7170ca | ||
|
|
4e41a8aaac | ||
|
|
b718eb3f4c | ||
|
|
8b03718422 | ||
|
|
e0bb6f3c95 | ||
|
|
4b1649b26a | ||
|
|
20bd6dbdc4 | ||
|
|
2c6088e96c | ||
|
|
33a41b45a2 | ||
|
|
9ac7eb7110 | ||
|
|
db3961443c | ||
|
|
7051bbae6e | ||
|
|
6c17df063a | ||
|
|
83c3cea88f | ||
|
|
850f680e32 | ||
|
|
dcc0b3cbd8 | ||
|
|
fb372cee89 | ||
|
|
916af395b6 | ||
|
|
27b9ef9216 | ||
|
|
c85463b728 | ||
|
|
9561cc0269 | ||
|
|
89b72c74e0 | ||
|
|
ae78dd951e | ||
|
|
0a9a3f9937 | ||
|
|
468c3d7a27 | ||
|
|
faab1b77a8 | ||
|
|
e69c1689ec | ||
|
|
18497e92fd | ||
|
|
f692621565 | ||
|
|
934a1089db | ||
|
|
b0dfeb6e5f | ||
|
|
a23afabb6e | ||
|
|
f5fd16e0e6 | ||
|
|
0962e1a70e | ||
|
|
448702b081 | ||
|
|
3d54b82428 | ||
|
|
8ed9117bee | ||
|
|
6a04908812 | ||
|
|
1460c6190e | ||
|
|
439bb30bbd | ||
|
|
32fcb198f8 | ||
|
|
3f04bb2ff0 | ||
|
|
fec16ee496 | ||
|
|
fc29cd7051 | ||
|
|
51aca98536 | ||
|
|
2ab3f0b442 | ||
|
|
4f0ac7e6ea | ||
|
|
ba79162a28 | ||
|
|
cb9105682e | ||
|
|
93477f4008 | ||
|
|
84d46e3aee | ||
|
|
6e0b958e56 | ||
|
|
9a97dc5221 | ||
|
|
ee810b9e0c | ||
|
|
516a96a4a8 | ||
|
|
506a9c0896 | ||
|
|
71196983ca | ||
|
|
fc600873b4 | ||
|
|
49e588e60e | ||
|
|
f3f219309a | ||
|
|
860e296ae3 | ||
|
|
dc5beebb24 | ||
|
|
505cd193d3 | ||
|
|
58d4e8b456 | ||
|
|
ca89072273 | ||
|
|
ff5d8baa1a | ||
|
|
9ac09db4c3 | ||
|
|
18e845f99f | ||
|
|
e6307dec97 | ||
|
|
d2941d94bc | ||
|
|
ba132a0546 | ||
|
|
d3bbd836f6 | ||
|
|
8777c2df64 | ||
|
|
e123c94e8a | ||
|
|
7b3af6ac90 | ||
|
|
ce522a75b5 | ||
|
|
f5ed321def | ||
|
|
490039c9a0 | ||
|
|
24720e02f0 | ||
|
|
f34430348a | ||
|
|
88eaf9d73c | ||
|
|
05979de55a | ||
|
|
667cdb1520 | ||
|
|
fb3c34f91f | ||
|
|
966bcfadab | ||
|
|
3c13f211f2 | ||
|
|
a5616561d3 | ||
|
|
835a8395bf | ||
|
|
0033d261d3 | ||
|
|
e1ed32af92 | ||
|
|
0d51f4c1d4 | ||
|
|
4c0ffd483e | ||
|
|
d61a55394b | ||
|
|
e2233d0a86 | ||
|
|
075a0dc368 | ||
|
|
5f18c9f81c | ||
|
|
3e4751070f | ||
|
|
2f2c084db3 | ||
|
|
4b6611ae2b | ||
|
|
1a58636cd4 | ||
|
|
11dc65bdf1 | ||
|
|
387a8f0efe | ||
|
|
68337ee54d | ||
|
|
e69ddb8a26 | ||
|
|
0847f9237f | ||
|
|
69b227373a | ||
|
|
fbb9524afd | ||
|
|
937f98c77a | ||
|
|
0ec7619408 | ||
|
|
2f5818ba5a | ||
|
|
d502f983c0 | ||
|
|
344640fe69 | ||
|
|
26effcd6b2 | ||
|
|
af79605268 | ||
|
|
0192da5a92 | ||
|
|
e563a9dc21 | ||
|
|
8f5d969f61 | ||
|
|
3a8fb33dd6 | ||
|
|
206e6e6fec | ||
|
|
0bc2d74dcc | ||
|
|
13da5056be | ||
|
|
ecb2d98ad7 | ||
|
|
e88811c1fd | ||
|
|
56c3ab74cb | ||
|
|
ae280e170a | ||
|
|
d2dfb16033 | ||
|
|
64d99748c7 | ||
|
|
16d90a010b | ||
|
|
32baab9072 | ||
|
|
31dd125263 | ||
|
|
483fd87ee5 | ||
|
|
254786ea5d | ||
|
|
c06a439c0c | ||
|
|
7b76999c0d | ||
|
|
5c0190dffe | ||
|
|
aabd2824d3 | ||
|
|
2f7033cd6d | ||
|
|
756fe823f8 | ||
|
|
ed0d163944 | ||
|
|
b7ee025a6f | ||
|
|
d2ba73b148 | ||
|
|
df8ac18a0f | ||
|
|
23dca8ec93 | ||
|
|
fe05cb613f | ||
|
|
c56c6fe5e4 | ||
|
|
8f7fafa4f2 | ||
|
|
ef9ca0bfc8 | ||
|
|
34635114df | ||
|
|
3e29dd63df | ||
|
|
cfcf86adcc | ||
|
|
c575159616 | ||
|
|
ddebfc7e75 | ||
|
|
957d2934db | ||
|
|
7c73975c34 | ||
|
|
2b51e5982a | ||
|
|
00b616f4f8 | ||
|
|
38274e1056 | ||
|
|
cf1397cb81 | ||
|
|
b23c1b46ab | ||
|
|
4750ee9214 | ||
|
|
05b39acb3e | ||
|
|
26d2a59ad5 | ||
|
|
1ed382faef | ||
|
|
ebbbd4febb | ||
|
|
95014c3863 | ||
|
|
b61f5752d2 | ||
|
|
4b7533d721 | ||
|
|
6519e9ae86 | ||
|
|
999ce8022a | ||
|
|
c3fa23159a | ||
|
|
6b674be421 | ||
|
|
cbc3b9d292 | ||
|
|
1dde627a4c | ||
|
|
06727f23e4 | ||
|
|
1c8279d2fc | ||
|
|
aa0193b41e | ||
|
|
fcf6a8b586 | ||
|
|
574c51bcec | ||
|
|
01fa8602a0 | ||
|
|
b006e8cc2b | ||
|
|
e8486364a3 | ||
|
|
f72b6b04ce | ||
|
|
1152b4d9b2 | ||
|
|
219c4e2491 | ||
|
|
47d78f4464 | ||
|
|
bebfd03ae9 | ||
|
|
0bf98491cb | ||
|
|
63e4c627b3 | ||
|
|
a37e3c8287 | ||
|
|
45ab560d08 | ||
|
|
3d1846cbe8 | ||
|
|
936bbe2372 | ||
|
|
963a3fbb97 | ||
|
|
b4161da81a | ||
|
|
7af98b4992 | ||
|
|
8380827494 | ||
|
|
be65d915e3 | ||
|
|
22fb9df723 | ||
|
|
8709f81b16 | ||
|
|
347ede62b2 | ||
|
|
c4a913d317 | ||
|
|
1706fde7ab | ||
|
|
60e8d37624 | ||
|
|
519bc38fb6 | ||
|
|
375d223f66 | ||
|
|
f16e0649cb | ||
|
|
9e685cde37 | ||
|
|
d32d7cd802 | ||
|
|
8961ca860a | ||
|
|
9a3fa7c82f | ||
|
|
183f36bf40 | ||
|
|
bac91d14c6 | ||
|
|
7455729225 | ||
|
|
223b6b7a0e | ||
|
|
825555a34f | ||
|
|
aeead83510 | ||
|
|
78a8981006 | ||
|
|
e9bb6bc73b | ||
|
|
d2354074f8 | ||
|
|
78ee43cb7d | ||
|
|
952fc914fb | ||
|
|
bc54203cdf | ||
|
|
854846585a | ||
|
|
a3a8825d48 | ||
|
|
309bfb623b | ||
|
|
9a99740701 | ||
|
|
1f299a1ff1 | ||
|
|
8bce3d0541 | ||
|
|
d8b1dce8de | ||
|
|
6e141bf83e | ||
|
|
f32ad41917 | ||
|
|
e75a5bcb0a | ||
|
|
0a017ca4e6 | ||
|
|
06d033d13c | ||
|
|
654b070c7e | ||
|
|
a159c8f072 | ||
|
|
3846d974af | ||
|
|
7e5fcfb881 | ||
|
|
4e9cafcd5e | ||
|
|
15f4fc51ce | ||
|
|
d343c1c98c | ||
|
|
4a17a8474a | ||
|
|
2bb1ff0b09 | ||
|
|
dec5bd6603 | ||
|
|
282f01b55d | ||
|
|
349be00771 | ||
|
|
adc47fd19c | ||
|
|
e876ef97cd | ||
|
|
903409d962 | ||
|
|
9295554195 | ||
|
|
8d6cfe03ac | ||
|
|
70d5051a6f | ||
|
|
124e4ec561 | ||
|
|
036db83321 | ||
|
|
23dfa67fe5 | ||
|
|
79d7a09203 | ||
|
|
96d9fb485b | ||
|
|
68c4d954ef | ||
|
|
ef7bedacb8 | ||
|
|
e7a1373305 | ||
|
|
cfd06df25e | ||
|
|
89bc6d0529 | ||
|
|
0446000270 | ||
|
|
9908e8ca98 | ||
|
|
326f09c903 | ||
|
|
2b52206795 | ||
|
|
4cadcddac1 | ||
|
|
8910b8bf28 | ||
|
|
fc3287758e | ||
|
|
c94a03bb23 | ||
|
|
2c5ba21b99 | ||
|
|
4f00550400 | ||
|
|
dfd622c948 | ||
|
|
3ceb256272 | ||
|
|
a7d66727f3 | ||
|
|
06a5c412bd | ||
|
|
6b8bb96aad | ||
|
|
0a3616ec0e | ||
|
|
b4226306b0 | ||
|
|
4f3353303a | ||
|
|
81b832071a | ||
|
|
d1966df73c | ||
|
|
e194291efb | ||
|
|
af88f6cd0d | ||
|
|
62838da761 | ||
|
|
7236608f59 | ||
|
|
2570f2843c | ||
|
|
a9fe9bebaf | ||
|
|
15a7c3abd0 | ||
|
|
7872950f65 | ||
|
|
4a711368e3 | ||
|
|
fbcc7e4478 | ||
|
|
e23af2e5f5 | ||
|
|
9c6fed4d48 | ||
|
|
26ac25d3ba | ||
|
|
19beae66bb | ||
|
|
037f660825 | ||
|
|
47e719bff0 | ||
|
|
020f30d8df | ||
|
|
7078508ba9 | ||
|
|
eb002332ed | ||
|
|
ea1da07e71 | ||
|
|
e1d32cd747 | ||
|
|
1bb3450512 | ||
|
|
ce1a78156e | ||
|
|
9d95e52d12 | ||
|
|
73c072583a | ||
|
|
253e2b7eab | ||
|
|
650c6de692 | ||
|
|
1ac4e20efb | ||
|
|
f08ea4346e | ||
|
|
c206886639 | ||
|
|
61bf953b1a | ||
|
|
1dcd35e825 | ||
|
|
5a7bb8b103 | ||
|
|
c4be4f068f | ||
|
|
4534c334bc | ||
|
|
9fa6d6d8b1 | ||
|
|
a5b34161c1 | ||
|
|
bf2c6929e1 | ||
|
|
2a451c3120 | ||
|
|
1169714908 | ||
|
|
14de4e4760 | ||
|
|
712f527ce0 | ||
|
|
0631c64ba5 | ||
|
|
85c43db53e | ||
|
|
8394bf3f19 | ||
|
|
bd1f25f016 | ||
|
|
95f340063a | ||
|
|
2be1d82e8d | ||
|
|
501af18298 | ||
|
|
724292bd34 | ||
|
|
cbbdebdf84 | ||
|
|
02c17dcf55 | ||
|
|
71cb562b05 | ||
|
|
23d1f9d8c0 | ||
|
|
f4e0d3596d | ||
|
|
de77f2aa4e | ||
|
|
316c34213d | ||
|
|
3b012bc946 | ||
|
|
33a5a2c80f | ||
|
|
e8b481d517 | ||
|
|
dcfa58b3a9 | ||
|
|
fd4106cf00 | ||
|
|
87dddac5f4 | ||
|
|
5488af7e35 | ||
|
|
0a3bd56f15 | ||
|
|
a5ae8f994b | ||
|
|
860489616c | ||
|
|
730c16f7b4 | ||
|
|
2bdbeed5f4 | ||
|
|
1946b46665 | ||
|
|
6a0b3e7fc4 | ||
|
|
288cb3974b | ||
|
|
345b0d2f74 | ||
|
|
c8355184f2 | ||
|
|
4f7c7fa7c9 | ||
|
|
c75b6ae5f0 | ||
|
|
a7620c38d0 | ||
|
|
0060e316dc | ||
|
|
b71321f301 | ||
|
|
c99ef80d78 | ||
|
|
2adf3fe27b | ||
|
|
ade033eb59 | ||
|
|
42666cf1e9 | ||
|
|
530f11f67c | ||
|
|
0391db60aa | ||
|
|
486c90a112 | ||
|
|
d1767797d7 | ||
|
|
fbe03d23f3 | ||
|
|
361280c131 | ||
|
|
04e0fc6e7c | ||
|
|
ab52eee127 | ||
|
|
94825252f7 | ||
|
|
93f13817be | ||
|
|
739ea4e841 | ||
|
|
fe3ad9ffb4 | ||
|
|
8fce809ee9 | ||
|
|
c156cbff99 | ||
|
|
268be8e0f5 | ||
|
|
5581e1c0e1 | ||
|
|
7fea2d442f | ||
|
|
74276764a6 | ||
|
|
a3e54782bb | ||
|
|
b7bc80b2a3 | ||
|
|
b869a41f3d | ||
|
|
9c7954945f | ||
|
|
13cd666718 | ||
|
|
c3e627e85b | ||
|
|
f23c24ae9b | ||
|
|
d27da35beb | ||
|
|
6457b205e4 | ||
|
|
bea7b61dcc | ||
|
|
2cc8d51a6c | ||
|
|
5410b806bb | ||
|
|
b937d8bd71 | ||
|
|
cd25cfab8e | ||
|
|
229e6ad461 | ||
|
|
977cae1cbd | ||
|
|
c8a9be2ca6 | ||
|
|
c3acf82a9b | ||
|
|
ddfc60bbf5 | ||
|
|
445646fe02 | ||
|
|
3dd3c8fb40 | ||
|
|
fb390b3618 | ||
|
|
ca5fb75f3a | ||
|
|
e881ce5f0f | ||
|
|
8002e47551 | ||
|
|
5b66b5705d | ||
|
|
d1c5521d2a | ||
|
|
74151edfb3 | ||
|
|
00f6747d7d | ||
|
|
0101955ad3 | ||
|
|
f6f9a95f06 | ||
|
|
3d82b89db0 | ||
|
|
8c7b549a45 | ||
|
|
3ad4dc1cfe | ||
|
|
7524314f74 | ||
|
|
94545e8958 | ||
|
|
2c74b2d2e2 | ||
|
|
108c190254 | ||
|
|
466209307e | ||
|
|
acccba59dc | ||
|
|
9325e2f9d1 | ||
|
|
36ebff2667 | ||
|
|
6d0d08b5fb | ||
|
|
e695a1e291 | ||
|
|
133488221b | ||
|
|
b186b672ea | ||
|
|
2badef3daf | ||
|
|
f8700296fb | ||
|
|
0f79fb56c7 | ||
|
|
d8412c95d4 | ||
|
|
469c239eed | ||
|
|
7fad542553 | ||
|
|
d0c0aeab84 | ||
|
|
9fd7123649 | ||
|
|
5b922043ec | ||
|
|
2953589ece | ||
|
|
5836990903 | ||
|
|
acd7e24382 | ||
|
|
71827e0546 | ||
|
|
7e8139e5a5 | ||
|
|
20d2b6ec9e | ||
|
|
be7d0e58a7 | ||
|
|
f20c449279 | ||
|
|
bf059715ec | ||
|
|
98cd3f22a2 | ||
|
|
bad290d104 | ||
|
|
3c55d025ce | ||
|
|
5c775ac5b4 | ||
|
|
9295aa58a7 | ||
|
|
96d68bbd39 | ||
|
|
7ddb6bc1ca | ||
|
|
a0145793a2 | ||
|
|
ecb37d67cc | ||
|
|
969476f368 | ||
|
|
3ae203d7ad | ||
|
|
e979b5aebe | ||
|
|
d568bccc28 | ||
|
|
301429182d | ||
|
|
8df78b9387 | ||
|
|
ba57309bcd | ||
|
|
8d573b3ee6 | ||
|
|
ba43ba8c21 | ||
|
|
47a3c24b03 | ||
|
|
40579fd376 | ||
|
|
bb17c1cc1a | ||
|
|
1cc8862a04 | ||
|
|
ff4606caa4 | ||
|
|
aff12a0462 | ||
|
|
3c5054acbd | ||
|
|
9a854f7810 | ||
|
|
1e731f7cbe | ||
|
|
c0cd6234f3 | ||
|
|
9cc79ab33a | ||
|
|
181de73a13 | ||
|
|
57e03c39f1 | ||
|
|
ff8a89d688 | ||
|
|
36f6fa7feb | ||
|
|
7d7e9cc79d | ||
|
|
2616ebe229 | ||
|
|
2b568c6260 | ||
|
|
f49f539e71 | ||
|
|
1845f3a5ae | ||
|
|
9aa337cd47 | ||
|
|
9b12c5c4bf | ||
|
|
1d12f7e475 | ||
|
|
e463ab9aae | ||
|
|
baa9de9059 | ||
|
|
e3b706f537 | ||
|
|
438bd2c195 | ||
|
|
5b546911ff | ||
|
|
b544e325ce | ||
|
|
c01fad2e29 | ||
|
|
a4d2f53207 | ||
|
|
a8b3fc3129 | ||
|
|
b7dec6d223 | ||
|
|
2d76ea554d | ||
|
|
b0f03dbe0a | ||
|
|
46429e04b4 | ||
|
|
7d479b7d88 | ||
|
|
c86651cdf6 | ||
|
|
63bfdba992 | ||
|
|
6767c42b14 | ||
|
|
70f2f2ecb5 | ||
|
|
691ec420b0 | ||
|
|
278f130906 | ||
|
|
531a88e326 | ||
|
|
d8e3e193b8 | ||
|
|
cda99d6f21 | ||
|
|
3cf3ae9135 | ||
|
|
7333361190 | ||
|
|
f75ebbb47f | ||
|
|
ce35fad608 | ||
|
|
8160d711a1 | ||
|
|
2a595ced57 | ||
|
|
9cb14b91ba | ||
|
|
92e008b3c8 | ||
|
|
f6d8924e18 | ||
|
|
ef955a03bc | ||
|
|
67c12f7342 | ||
|
|
5cfbc6855b | ||
|
|
aa9e9e20fe | ||
|
|
417f9c370d | ||
|
|
3f3bdbb83e | ||
|
|
c1c58a7a4d | ||
|
|
a6a3dd4c28 | ||
|
|
55c3dbe3b6 | ||
|
|
50e0c5aab9 | ||
|
|
fde0566abf | ||
|
|
be83fcbddb | ||
|
|
a7ccc1997a | ||
|
|
f6339a9f70 | ||
|
|
6d15d61f68 | ||
|
|
7b0b81694c | ||
|
|
e9ab643aab | ||
|
|
2e1ef17861 | ||
|
|
2527a3d303 | ||
|
|
62973d0564 | ||
|
|
59c428a14b | ||
|
|
064a4de214 | ||
|
|
998d95f6b2 | ||
|
|
e609d9ea93 | ||
|
|
0d191e6f02 | ||
|
|
3cb3aa4d1c | ||
|
|
f580e98d26 | ||
|
|
e1ad8aab73 | ||
|
|
340cdd9323 | ||
|
|
254067d2b7 | ||
|
|
682f3f612b | ||
|
|
2604031967 | ||
|
|
82ba5debcd | ||
|
|
d9a677b4ca | ||
|
|
cd56463286 | ||
|
|
3faa726fcd | ||
|
|
ef938df79f | ||
|
|
470ca0a98e | ||
|
|
fe9bb7e26a | ||
|
|
a30b1f5298 | ||
|
|
e7196d3033 | ||
|
|
09977ac703 | ||
|
|
ea9ed85bf7 | ||
|
|
1f3d819b24 | ||
|
|
096a025b79 | ||
|
|
0926f0b484 | ||
|
|
b6f6641204 | ||
|
|
65418c6e9a | ||
|
|
6abd0e677b | ||
|
|
ecfae5f416 | ||
|
|
e2f4d4e376 | ||
|
|
fb8de4606a | ||
|
|
09af8f98b3 | ||
|
|
70abcee27d | ||
|
|
d0deb6bee5 | ||
|
|
3494349961 | ||
|
|
5abc9ac9c0 | ||
|
|
d0360ea87b | ||
|
|
d31c5c4d53 | ||
|
|
f2f49464fc | ||
|
|
f6d0e068f6 | ||
|
|
17f699234c | ||
|
|
bf4c07aba4 | ||
|
|
a7fadf55aa | ||
|
|
4912807ae4 | ||
|
|
4a61c34d58 | ||
|
|
109dc901e9 | ||
|
|
9b8b6643f8 | ||
|
|
a26e79dac7 | ||
|
|
291c5d68f2 | ||
|
|
c85e838e33 | ||
|
|
94975e0117 | ||
|
|
eebb9359a6 | ||
|
|
40e6609297 | ||
|
|
af028504eb | ||
|
|
bfe4564659 | ||
|
|
85cdcaa457 | ||
|
|
cd5c10835c | ||
|
|
ce97c92645 | ||
|
|
d27b3b9ba0 | ||
|
|
0f96923006 | ||
|
|
bc7ac73de9 | ||
|
|
8d2fe9dd25 | ||
|
|
2d99106254 | ||
|
|
04d01efd0f | ||
|
|
da10fff966 | ||
|
|
991ee8674a | ||
|
|
7f24950498 | ||
|
|
405fb415d4 | ||
|
|
0aa4c27e02 | ||
|
|
add12366a0 | ||
|
|
2c496f6fe2 | ||
|
|
1381c19723 | ||
|
|
a8ddfc8b70 | ||
|
|
6680ff1cd9 | ||
|
|
863dc6378c | ||
|
|
be0465d094 | ||
|
|
42d2dcef7e | ||
|
|
c70fd8608d | ||
|
|
e217f929e8 | ||
|
|
b95c51a2be | ||
|
|
e3b635d107 | ||
|
|
ae1642d052 | ||
|
|
13e9f49a8b | ||
|
|
13f174fb1e | ||
|
|
3ef9e6304d | ||
|
|
398909b809 | ||
|
|
dc140a3dd5 | ||
|
|
b2fe23c32e | ||
|
|
46f0761f50 | ||
|
|
63258f2029 | ||
|
|
eeaf8fbe19 | ||
|
|
934e6dfa57 | ||
|
|
a9dd04f4fa | ||
|
|
34df54b96c | ||
|
|
e5ebd3c925 | ||
|
|
e99660ce40 | ||
|
|
d5357ed1c3 | ||
|
|
5a357c43e0 | ||
|
|
287ef5bdc7 | ||
|
|
89db56ae58 | ||
|
|
8f8aa888ca | ||
|
|
f9321a7bde | ||
|
|
4c59d2e2cb | ||
|
|
26d346bdf1 | ||
|
|
ba741ada31 | ||
|
|
2e8c4ebf9a | ||
|
|
579e30683a | ||
|
|
b1f893e944 | ||
|
|
415e305415 | ||
|
|
adb6928772 | ||
|
|
dc70bd1513 | ||
|
|
57f6a7d1a5 | ||
|
|
deafbd45d0 | ||
|
|
b2fa338e03 | ||
|
|
4e6a98e789 | ||
|
|
72ca19e3e7 | ||
|
|
2c7dac1837 | ||
|
|
de22d58e75 | ||
|
|
2f6d5415cc | ||
|
|
b8101ffa76 | ||
|
|
281590cf63 | ||
|
|
6759fb9ec0 | ||
|
|
c7dad4f1ad | ||
|
|
d29726632b | ||
|
|
95e5c58a92 | ||
|
|
8c5a3693c8 | ||
|
|
7aa1061b06 | ||
|
|
46bd172d59 | ||
|
|
d4dbaf5c57 | ||
|
|
6b4d47c79d | ||
|
|
1bd32ade9f | ||
|
|
5e1f3abd14 | ||
|
|
5bf7ecab64 | ||
|
|
dd75df0af8 | ||
|
|
72de08f9a3 | ||
|
|
13213edb4f | ||
|
|
872b618ea1 | ||
|
|
78e7fe76c6 | ||
|
|
980245bbfc | ||
|
|
2fd98a0be0 | ||
|
|
700f5debe5 | ||
|
|
06b4604e59 | ||
|
|
a6fd6b71d6 | ||
|
|
da194caf7c | ||
|
|
fa45e1040f | ||
|
|
09f0357763 | ||
|
|
f82e106fc1 | ||
|
|
906431b3a6 | ||
|
|
e82a76492a | ||
|
|
8c75c01017 | ||
|
|
dfabd2b414 | ||
|
|
8199dea809 | ||
|
|
1cb20088b2 | ||
|
|
203a9e5ca5 | ||
|
|
5a70586756 | ||
|
|
478beca96d | ||
|
|
637b57158a | ||
|
|
a2009fa91f | ||
|
|
4ee5fa3b00 | ||
|
|
36dde79dac | ||
|
|
9071d8a000 | ||
|
|
1c815e9d47 | ||
|
|
9b16779293 | ||
|
|
c702477dc8 | ||
|
|
b54df1d299 | ||
|
|
548a254262 | ||
|
|
d7330ad654 | ||
|
|
aacb38aee1 | ||
|
|
6969bb2680 | ||
|
|
e8e17b5338 | ||
|
|
45f0240c3a | ||
|
|
88ee55d4b6 | ||
|
|
e20ec07926 | ||
|
|
cd56f632da | ||
|
|
b5463efa44 | ||
|
|
2f41754da9 | ||
|
|
3e82ac83ab |
365
.github/workflows/main.yml
vendored
365
.github/workflows/main.yml
vendored
@@ -1,3 +1,4 @@
|
||||
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CI
|
||||
@@ -127,6 +128,7 @@ jobs:
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
@@ -140,7 +142,7 @@ jobs:
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.exe output/
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp "C:/mingw64/bin/libwinpthread-1.dll" .
|
||||
@@ -167,7 +169,7 @@ jobs:
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.exe output/
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp "C:/mingw64/bin/libwinpthread-1.dll" .
|
||||
@@ -303,6 +305,7 @@ jobs:
|
||||
# qmake
|
||||
# cd src
|
||||
# echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
# echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
# echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
# echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
# echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
@@ -337,7 +340,7 @@ jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
linux-x86-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
@@ -605,6 +608,7 @@ jobs:
|
||||
export ANDROID_NDK_ROOT="${ANDROID_NDK}"
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
@@ -651,7 +655,7 @@ jobs:
|
||||
|
||||
ios-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: macos-12
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -708,6 +712,7 @@ jobs:
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
@@ -818,6 +823,7 @@ jobs:
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
@@ -832,9 +838,23 @@ jobs:
|
||||
run: .\vcpkg\bootstrap-vcpkg.bat
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Create vcpkg.json
|
||||
working-directory: ${{ runner.workspace }}
|
||||
run: |
|
||||
echo '{
|
||||
"name": "qdomyos-zwift",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
"protobuf",
|
||||
"protobuf-c",
|
||||
"abseil"
|
||||
],
|
||||
"builtin-baseline": "8c2fcacefba009d63672f9d137f192765e632c9f"
|
||||
}' > vcpkg.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
.\vcpkg\vcpkg install protobuf protobuf-c abseil
|
||||
.\vcpkg\vcpkg install --triplet x64-windows --x-install-root=D:\a\qdomyos-zwift\vcpkg\installed
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Build
|
||||
@@ -847,7 +867,7 @@ jobs:
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.exe output/
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp ../../../icons/iOS/iTunesArtwork@2x.png .
|
||||
@@ -875,7 +895,7 @@ jobs:
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.exe output/
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp "C:/mingw64/bin/libwinpthread-1.dll" .
|
||||
@@ -991,6 +1011,7 @@ jobs:
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
@@ -1005,9 +1026,23 @@ jobs:
|
||||
run: .\vcpkg\bootstrap-vcpkg.bat
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Create vcpkg.json
|
||||
working-directory: ${{ runner.workspace }}
|
||||
run: |
|
||||
echo '{
|
||||
"name": "qdomyos-zwift",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
"protobuf",
|
||||
"protobuf-c",
|
||||
"abseil"
|
||||
],
|
||||
"builtin-baseline": "8c2fcacefba009d63672f9d137f192765e632c9f"
|
||||
}' > vcpkg.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
.\vcpkg\vcpkg install protobuf protobuf-c abseil
|
||||
.\vcpkg\vcpkg install --triplet x64-windows --x-install-root=D:\a\qdomyos-zwift\vcpkg\installed
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Build
|
||||
@@ -1023,7 +1058,7 @@ jobs:
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.exe output/
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp ../../../icons/iOS/iTunesArtwork@2x.png .
|
||||
@@ -1052,14 +1087,312 @@ jobs:
|
||||
name: windows-msvc2019-ai-server-binary
|
||||
path: windows-msvc2019-ai-server-binary.zip
|
||||
|
||||
raspberry-pi-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Secrets
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
|
||||
- name: Build for Raspberry Pi
|
||||
uses: docker://arm32v7/debian:bullseye-20241016
|
||||
with:
|
||||
args: >
|
||||
bash -c "
|
||||
set -ex &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 &&
|
||||
export QT_SELECT=qt5 &&
|
||||
export PATH=/usr/lib/qt5/bin:$PATH &&
|
||||
cd /github/workspace &&
|
||||
sed -i '/QtHttpServer/d' qdomyos-zwift.pro &&
|
||||
find src -type f \( -name '*.cpp' -o -name '*.h' \) -exec sed -i 's/#include <QtHttpServer/\/\/#include <QtHttpServer/' {} + &&
|
||||
find src -type f \( -name '*.cpp' -o -name '*.h' \) -exec sed -i 's/QHttpServer/\/\/QHttpServer/' {} + &&
|
||||
cat qdomyos-zwift.pro &&
|
||||
qmake &&
|
||||
make -j$(nproc)
|
||||
"
|
||||
|
||||
- name: Rename binary
|
||||
run: mv src/qdomyos-zwift src/qdomyos-zwift-32bit
|
||||
|
||||
- name: Archive Raspberry Pi binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: raspberry-pi-binary
|
||||
path: src/qdomyos-zwift-32bit
|
||||
|
||||
raspberry-pi-build-and-image-64bit:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:master
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: v0.19.3
|
||||
|
||||
- name: Secrets
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
|
||||
- name: Build for Raspberry Pi 64-bit
|
||||
uses: docker://arm64v8/debian:bullseye-20241016
|
||||
with:
|
||||
args: >
|
||||
bash -c "
|
||||
set -ex &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 &&
|
||||
export QT_SELECT=qt5 &&
|
||||
export PATH=/usr/lib/qt5/bin:$PATH &&
|
||||
cd /github/workspace &&
|
||||
sed -i '/QtHttpServer/d' qdomyos-zwift.pro &&
|
||||
find src -type f \( -name '*.cpp' -o -name '*.h' \) -exec sed -i 's/#include <QtHttpServer/\/\/#include <QtHttpServer/' {} + &&
|
||||
find src -type f \( -name '*.cpp' -o -name '*.h' \) -exec sed -i 's/QHttpServer/\/\/QHttpServer/' {} + &&
|
||||
cat qdomyos-zwift.pro &&
|
||||
qmake &&
|
||||
make -j$(nproc)
|
||||
"
|
||||
|
||||
- name: Rename binary
|
||||
run: mv src/qdomyos-zwift src/qdomyos-zwift-64bit
|
||||
|
||||
window-msvc2022-build:
|
||||
runs-on: windows-latest
|
||||
if: github.event_name == 'schedule'
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {python: true}
|
||||
- {python: false}
|
||||
|
||||
steps:
|
||||
- name: Checkout PR code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: refs/pull/1508/head
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: download python and paddleocr
|
||||
run: |
|
||||
python -VV
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --upgrade setuptools
|
||||
python -m pip install "protobuf<=3.20.2,>=3.1.0"
|
||||
python -m pip install paddlepaddle==2.5.1
|
||||
python -m pip install paddleocr
|
||||
python -m pip install imutils
|
||||
python -m pip install "Pillow<10.0.0"
|
||||
python -m pip install opencv-python
|
||||
python -m pip install numpy
|
||||
python -m pip install pywin32
|
||||
if: matrix.config.python
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: '6.8.2'
|
||||
host: 'windows'
|
||||
modules: 'qtnetworkauth qtcharts qtconnectivity qtspeech qtpositioning qtwebsockets qtlocation qtmultimedia qtwebengine qtwebview qtwebchannel'
|
||||
target: "desktop"
|
||||
arch: win64_msvc2022_64
|
||||
dir: "${{github.workspace}}/qt/"
|
||||
install-deps: "true"
|
||||
cache: 'true'
|
||||
cache-key-prefix: 'install-qt-action-windows'
|
||||
|
||||
- name: Install MSVC compiler
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
# 14.1 is for vs2017, 14.2 is vs2019, following the upstream vcpkg build from Qv2ray-deps repo
|
||||
#toolset: 14.2
|
||||
arch: x64
|
||||
|
||||
# - name: download 3rd party files for qthttpserver
|
||||
# run: |
|
||||
# cp qHttpServerBin/5.15.2/headers/* src/qthttpserver/src/3rdparty/http-parser/
|
||||
|
||||
# - name: Build qthttpserver
|
||||
# run: |
|
||||
# cd src\qthttpserver
|
||||
# qmake
|
||||
# nmake
|
||||
# nmake install
|
||||
# cd ../..
|
||||
|
||||
- name: Secrets
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
cd ..
|
||||
|
||||
- name: Clone vcpkg
|
||||
run: git clone https://github.com/microsoft/vcpkg.git
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Bootstrap vcpkg
|
||||
run: .\vcpkg\bootstrap-vcpkg.bat
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Create vcpkg.json
|
||||
working-directory: ${{ runner.workspace }}
|
||||
run: |
|
||||
echo '{
|
||||
"name": "qdomyos-zwift",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
"protobuf",
|
||||
"protobuf-c",
|
||||
"abseil"
|
||||
],
|
||||
"builtin-baseline": "8c2fcacefba009d63672f9d137f192765e632c9f"
|
||||
}' > vcpkg.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
.\vcpkg\vcpkg install --triplet x64-windows --x-install-root=D:\a\qdomyos-zwift\vcpkg\installed
|
||||
working-directory: ${{ runner.workspace }}
|
||||
|
||||
- name: Build
|
||||
Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\lib\*.* -Destination . -Verbose
|
||||
Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\lib\*.* -Destination src/ -Verbose
|
||||
Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\include\* -Destination src/ -Recurse -Verbose
|
||||
run: |
|
||||
qmake
|
||||
nmake
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp ../../../icons/iOS/iTunesArtwork@2x.png .
|
||||
cp ../../AppxManifest.xml .
|
||||
cp ../../windows/*.py .
|
||||
cp ../../windows/*.bat .
|
||||
cp ../../../windows_openssl/*.* .
|
||||
Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\bin\*.* -Destination . -Verbose
|
||||
mkdir adb
|
||||
mkdir python
|
||||
Copy-Item -Path C:\hostedtoolcache\windows\Python\3.7.9\x64 -Destination python -Recurse
|
||||
cp ../../adb/* adb/
|
||||
cd ..
|
||||
cd appx
|
||||
#../../MSIX-Toolkit/WindowsSDK/10/10.0.20348.0/x64/makeappx.exe pack /d ../output/ /p qz
|
||||
if: matrix.config.python
|
||||
|
||||
|
||||
- name: Build without python
|
||||
# Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\lib\*.* -Destination . -Verbose
|
||||
# Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\lib\*.* -Destination src/ -Verbose
|
||||
# Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\include\* -Destination src/ -Recurse -Verbose
|
||||
run: |
|
||||
qmake
|
||||
nmake
|
||||
cd src/debug
|
||||
mkdir output
|
||||
mkdir appx
|
||||
cp qdomyos-zwift.* output/
|
||||
cd output
|
||||
windeployqt --qmldir ../../ qdomyos-zwift.exe
|
||||
cp "C:/mingw64/bin/libwinpthread-1.dll" .
|
||||
cp "C:/mingw64/bin/libgcc_s_seh-1.dll" .
|
||||
cp "C:/mingw64/bin/libstdc++-6.dll" .
|
||||
cp ../../../icons/iOS/iTunesArtwork@2x.png .
|
||||
cp ../../AppxManifest.xml .
|
||||
cp ../../../windows_openssl/*.* .
|
||||
Copy-Item -Path ${{ runner.workspace }}\vcpkg\installed\x64-windows\bin\*.* -Destination . -Verbose
|
||||
mkdir adb
|
||||
cp ../../adb/* adb/
|
||||
cd ..
|
||||
cd appx
|
||||
#../../MSIX-Toolkit/WindowsSDK/10/10.0.20348.0/x64/makeappx.exe pack /d ../output/ /p qz
|
||||
if: matrix.config.python == false
|
||||
|
||||
- name: patching qt for bluetooth
|
||||
run: cp qt-patches/windows/5.15.2/binary/msvc2019/*.* ${{ github.workspace }}/src/debug/output/
|
||||
|
||||
- name: Zip artifact for deployment
|
||||
run: Compress-Archive src/debug/output windows-msvc2022-binary.zip
|
||||
if: matrix.config.python
|
||||
|
||||
- name: Zip artifact for deployment
|
||||
run: Compress-Archive src/debug/output windows-msvc2022-binary-no-python.zip
|
||||
if: ${{ ! matrix.config.python }}
|
||||
|
||||
- name: Archive windows binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-msvc2022-binary
|
||||
path: windows-msvc2022-binary.zip
|
||||
if: matrix.config.python
|
||||
|
||||
- name: Archive windows binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-msvc2022-binary-no-python
|
||||
path: windows-msvc2022-binary-no-python.zip
|
||||
if: ${{ ! matrix.config.python }}
|
||||
|
||||
upload_to_release:
|
||||
permissions: write-all
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.event_name == 'schedule'
|
||||
needs: [linux-x86-build, window-msvc2019-build, ios-build, window-build, android-build] # Specify the job dependencies
|
||||
needs: [linux-x86-build, window-msvc2019-build, window-msvc2022-build, ios-build, window-build, android-build, raspberry-pi-build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Update nightly release
|
||||
uses: andelf/nightly-release@main
|
||||
env:
|
||||
@@ -1077,11 +1410,23 @@ jobs:
|
||||
of new complex code, the stability of the program may suffer compared to
|
||||
official releases, so **use it with caution**!
|
||||
|
||||
## Windows Builds:
|
||||
- **windows-msvc2019**: Recommended for Windows 10
|
||||
- **windows-msvc2022**: Recommended for Windows 11 (experimental, QT6)
|
||||
- **windows**: MinGW build (alternative version)
|
||||
|
||||
## Other Platforms:
|
||||
- **fdroid-android-trial**: Android build
|
||||
- **raspberry-pi-binary**: Raspberry Pi build
|
||||
|
||||
__Please help us improve QZ by reporting any issues you encounter!__ :wink:
|
||||
files: |
|
||||
windows-msvc2019-binary-no-python/*
|
||||
windows-msvc2019-binary/*
|
||||
windows-msvc2022-binary-no-python/*
|
||||
windows-msvc2022-binary/*
|
||||
windows-msvc2019-ai-server-binary/*
|
||||
windows-binary-no-python/*
|
||||
windows-binary/*
|
||||
fdroid-android-trial/*
|
||||
raspberry-pi-binary/qdomyos-zwift-32bit
|
||||
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(Windows) Launch",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "C://Users//violarob//Downloads//windows-msvc2019-binary-no-python (1)//output/qdomyos-zwift.exe",
|
||||
"symbolSearchPath": "C://Users//violarob//Downloads//windows-msvc2019-binary-no-python (1)//output/qdomyos-zwift.pdb",
|
||||
"sourceFileMap": {
|
||||
"d:/a/qdomyos-zwift/qdomyos-zwift": "c:/work/qdomyos-zwift/",
|
||||
"compiled_source_path": "C://Users//violarob//Downloads//windows-msvc2019-binary-no-python (1)//output/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
22
README.md
22
README.md
@@ -96,34 +96,36 @@ Zwift bridge for Treadmills and Bike!
|
||||
|:---|:---:|:---:|:---:|:---:|---:|
|
||||
|Resistance shifting with bluetooth remote|X||X|||
|
||||
|TTS support|X|X|X|X||
|
||||
|Zwift Play & Click support|X|||||
|
||||
|MQTT integration|X|X|X|X||
|
||||
|OpenSoundControl integration|X|X|X|X||
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
You can install it on multiple platforms.
|
||||
Read the [installation procedure](docs/10_Installation.md)
|
||||
You can install it on multiple platforms.
|
||||
Read the [installation procedure](docs/10_Installation.md)
|
||||
|
||||
|
||||
### Tested on
|
||||
|
||||
You can run the app on [Macintosh or Linux devices](docs/10_Installation.md). IOS and Android are also supported.
|
||||
|
||||
QDomyos-Zwift works on every [FTMS-compatible application](docs/20_supported_devices_and_applications.md), and virtually any [bluetooth enabled device](docs/20_supported_devices_and_applications.md).
|
||||
The QDomyos-Zwift application can run on [Macintosh or Linux devices](docs/10_Installation.md) iOS, and Android.
|
||||
It supports any [FTMS-compatible application](docs/20_supported_devices_and_applications.md) software and most [bluetooth enabled device](docs/20_supported_devices_and_applications.md).
|
||||
|
||||
### No GUI version
|
||||
|
||||
run as
|
||||
|
||||
$ sudo ./qdomyos-zwift -no-gui
|
||||
$ sudo ./qdomyos-zwift -no-gui
|
||||
|
||||
### Reference
|
||||
|
||||
https://github.com/ProH4Ck/treadmill-bridge
|
||||
=> GitHub Repository: [QDomyos-Zwift on GitHub](https://github.com/ProH4Ck/treadmill-bridge)
|
||||
|
||||
https://www.livestrong.com/article/422012-what-is-10-degrees-in-incline-on-a-treadmill/
|
||||
=> Treadmill Incline Reference: [What Is 10 Degrees in Incline on a Treadmill?](https://www.livestrong.com/article/422012-what-is-10-degrees-in-incline-on-a-treadmill/)
|
||||
|
||||
Icons used in this documentation come from [flaticon.com](https://www.flaticon.com)
|
||||
=> Icon Attribution: Icons used in this documentation are from [Flaticon.com](https://www.flaticon.com)
|
||||
|
||||
### Blog
|
||||
|
||||
https://robertoviola.cloud
|
||||
=> Related Blog: [Roberto Viola's Blog](https://robertoviola.cloud)
|
||||
|
||||
@@ -6,21 +6,6 @@
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
E9F0AFC024A6F2D65CE84E08 /* Qt Preprocess */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 6CC3B5D2136C7CD6A5CF5A59 /* Build configuration list for PBXNativeTarget "qdomyoszwift" */;
|
||||
buildPhases = (
|
||||
5E618435888B9D49F8540165 /* Qt Qmake */,
|
||||
7EF0942E79C014DCEC8976BC /* Qt Preprocessors */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Qt Preprocess";
|
||||
productName = "Qt Preprocess";
|
||||
};
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
023642106C14651D2E1F4D5D /* dialogplugin in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = B9CCC4658EA620ABBD832E71 /* dialogplugin */; };
|
||||
0317752B0C295CAB82D37E45 /* virtualtreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 3088C65BF5992B009FFC93B5 /* virtualtreadmill.cpp */; settings = {ATTRIBUTES = (); }; };
|
||||
@@ -143,6 +128,8 @@
|
||||
87083D9626678EFA0072410D /* zwiftworkout.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87083D9526678EFA0072410D /* zwiftworkout.cpp */; };
|
||||
87097D2F275EA9A30020EE6F /* sportsplusbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87097D2D275EA9A20020EE6F /* sportsplusbike.cpp */; };
|
||||
87097D31275EA9AF0020EE6F /* moc_sportsplusbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87097D30275EA9AE0020EE6F /* moc_sportsplusbike.cpp */; };
|
||||
870A5DB32CEF8FB100839641 /* moc_technogymbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 870A5DB22CEF8FB100839641 /* moc_technogymbike.cpp */; };
|
||||
870A5DB52CEF8FD200839641 /* technogymbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 870A5DB42CEF8FD200839641 /* technogymbike.cpp */; };
|
||||
8710706C29C48AEA0094D0F3 /* handleurl.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8710706B29C48AEA0094D0F3 /* handleurl.cpp */; };
|
||||
8710706E29C48AF30094D0F3 /* moc_handleurl.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8710706D29C48AF30094D0F3 /* moc_handleurl.cpp */; };
|
||||
8710707329C4A5E70094D0F3 /* GarminConnect.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8710707229C4A5E70094D0F3 /* GarminConnect.swift */; };
|
||||
@@ -168,6 +155,24 @@
|
||||
871B9FD2265E6A8800DB41F4 /* powerzonepack.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 871B9FD1265E6A8800DB41F4 /* powerzonepack.cpp */; };
|
||||
871B9FD4265E6A9A00DB41F4 /* moc_powerzonepack.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 871B9FD3265E6A9A00DB41F4 /* moc_powerzonepack.cpp */; };
|
||||
871E4CD125A6FB5A00E18D6D /* BLEPeripheralManager.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 871E4CD025A6FB5A00E18D6D /* BLEPeripheralManager.swift */; };
|
||||
872088EB2CE6543C008C2C17 /* moc_mqttpublisher.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088E62CE6543C008C2C17 /* moc_mqttpublisher.cpp */; };
|
||||
872088EC2CE6543C008C2C17 /* moc_qmqttclient.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088E72CE6543C008C2C17 /* moc_qmqttclient.cpp */; };
|
||||
872088ED2CE6543C008C2C17 /* moc_qmqttmessage.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088E92CE6543C008C2C17 /* moc_qmqttmessage.cpp */; };
|
||||
872088EE2CE6543C008C2C17 /* moc_qmqttsubscription.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088EA2CE6543C008C2C17 /* moc_qmqttsubscription.cpp */; };
|
||||
872088EF2CE6543C008C2C17 /* moc_qmqttconnection_p.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088E82CE6543C008C2C17 /* moc_qmqttconnection_p.cpp */; };
|
||||
8720890E2CE65451008C2C17 /* qmqttsubscriptionproperties.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872089072CE65451008C2C17 /* qmqttsubscriptionproperties.cpp */; };
|
||||
8720890F2CE65451008C2C17 /* qmqttconnectionproperties.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088F82CE65451008C2C17 /* qmqttconnectionproperties.cpp */; };
|
||||
872089102CE65451008C2C17 /* qmqttconnection.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088F52CE65451008C2C17 /* qmqttconnection.cpp */; };
|
||||
872089112CE65451008C2C17 /* qmqttsubscription.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872089042CE65451008C2C17 /* qmqttsubscription.cpp */; };
|
||||
872089122CE65451008C2C17 /* qmqttclient.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088F32CE65451008C2C17 /* qmqttclient.cpp */; };
|
||||
872089132CE65451008C2C17 /* qmqtttopicfilter.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872089092CE65451008C2C17 /* qmqtttopicfilter.cpp */; };
|
||||
872089142CE65451008C2C17 /* qmqtttype.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8720890D2CE65451008C2C17 /* qmqtttype.cpp */; };
|
||||
872089152CE65451008C2C17 /* qmqttcontrolpacket.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088FA2CE65451008C2C17 /* qmqttcontrolpacket.cpp */; };
|
||||
872089162CE65451008C2C17 /* qmqttmessage.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088FE2CE65451008C2C17 /* qmqttmessage.cpp */; };
|
||||
872089172CE65451008C2C17 /* qmqtttopicname.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8720890B2CE65451008C2C17 /* qmqtttopicname.cpp */; };
|
||||
872089182CE65451008C2C17 /* qmqttpublishproperties.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872089012CE65451008C2C17 /* qmqttpublishproperties.cpp */; };
|
||||
872089192CE65451008C2C17 /* qmqttauthenticationproperties.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872088F12CE65451008C2C17 /* qmqttauthenticationproperties.cpp */; };
|
||||
8720891B2CE6567D008C2C17 /* mqttpublisher.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8720891A2CE6567D008C2C17 /* mqttpublisher.cpp */; };
|
||||
872261EE289EA873006A6F75 /* nordictrackelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872261EC289EA873006A6F75 /* nordictrackelliptical.cpp */; };
|
||||
872261F0289EA887006A6F75 /* moc_nordictrackelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872261EF289EA887006A6F75 /* moc_nordictrackelliptical.cpp */; };
|
||||
8727A47727849EA600019B5D /* paferstreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8727A47627849EA600019B5D /* paferstreadmill.cpp */; };
|
||||
@@ -283,6 +288,10 @@
|
||||
8754D24C27F786F0003D7054 /* virtualrower.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8754D24B27F786F0003D7054 /* virtualrower.swift */; };
|
||||
87586A4125B8340E00A243C4 /* proformbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87586A4025B8340E00A243C4 /* proformbike.cpp */; };
|
||||
87586A4325B8341B00A243C4 /* moc_proformbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87586A4225B8341B00A243C4 /* moc_proformbike.cpp */; };
|
||||
875CA9462D0C740000667EE6 /* moc_kineticinroadbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 875CA9452D0C740000667EE6 /* moc_kineticinroadbike.cpp */; };
|
||||
875CA9492D0C742500667EE6 /* kineticinroadbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 875CA9482D0C742500667EE6 /* kineticinroadbike.cpp */; };
|
||||
875CA94C2D130F8100667EE6 /* moc_osc.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 875CA94B2D130F8100667EE6 /* moc_osc.cpp */; };
|
||||
875CA9552D130FBC00667EE6 /* osc.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 875CA9502D130FBC00667EE6 /* osc.cpp */; };
|
||||
875F69B926342E8D0009FD78 /* spirittreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 875F69B826342E8D0009FD78 /* spirittreadmill.cpp */; };
|
||||
875F69BB26342E9A0009FD78 /* moc_spirittreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 875F69BA26342E9A0009FD78 /* moc_spirittreadmill.cpp */; };
|
||||
8762D50F2601F7EA00F6F049 /* M3iNS.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8762D50B2601F7EA00F6F049 /* M3iNS.mm */; };
|
||||
@@ -290,6 +299,8 @@
|
||||
8762D5132601F89500F6F049 /* scanrecordresult.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8762D5112601F89500F6F049 /* scanrecordresult.cpp */; };
|
||||
87646C2027B5064600F82131 /* bhfitnesselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87646C1E27B5064500F82131 /* bhfitnesselliptical.cpp */; };
|
||||
87646C2227B5065100F82131 /* moc_bhfitnesselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87646C2127B5065100F82131 /* moc_bhfitnesselliptical.cpp */; };
|
||||
8767CA552DA3C1FD0003001F /* elitesquarecontroller.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767CA532DA3C1FD0003001F /* elitesquarecontroller.cpp */; };
|
||||
8767CA562DA3C1FD0003001F /* moc_elitesquarecontroller.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767CA542DA3C1FD0003001F /* moc_elitesquarecontroller.cpp */; };
|
||||
8767EF1E29448D6700810C0F /* characteristicwriteprocessor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */; };
|
||||
8768C8BA2BBC11C80099DBE1 /* file_sync_client.c in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8768C89C2BBC11C70099DBE1 /* file_sync_client.c */; };
|
||||
8768C8BB2BBC11C80099DBE1 /* protocol.txt in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = 8768C89D2BBC11C70099DBE1 /* protocol.txt */; };
|
||||
@@ -358,6 +369,7 @@
|
||||
8772A0E825E43AE70080718C /* moc_trxappgateusbbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772A0E725E43AE70080718C /* moc_trxappgateusbbike.cpp */; };
|
||||
8772B7F42CB55E80004AB8E9 /* moc_deerruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772B7F32CB55E80004AB8E9 /* moc_deerruntreadmill.cpp */; };
|
||||
8772B7F72CB55E98004AB8E9 /* deerruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772B7F62CB55E98004AB8E9 /* deerruntreadmill.cpp */; };
|
||||
877350F72D1C08E60070CBD8 /* SmartControl.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 877350F62D1C08E50070CBD8 /* SmartControl.cpp */; };
|
||||
8775008329E876F8008E48B7 /* iconceptelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8775008129E876F7008E48B7 /* iconceptelliptical.cpp */; };
|
||||
8775008529E87713008E48B7 /* moc_iconceptelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8775008429E87712008E48B7 /* moc_iconceptelliptical.cpp */; };
|
||||
877758B32C98627300BB1697 /* moc_sportstechelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 877758B22C98627300BB1697 /* moc_sportstechelliptical.cpp */; };
|
||||
@@ -380,6 +392,8 @@
|
||||
878531692711A3EC004B153D /* moc_fakebike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878531672711A3EB004B153D /* moc_fakebike.cpp */; };
|
||||
8785D5432B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8785D5412B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp */; };
|
||||
8785D5442B3DD105005A2EB7 /* moc_zwift_client_auth.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8785D5422B3DD105005A2EB7 /* moc_zwift_client_auth.cpp */; };
|
||||
87873AEE2D09A8AA005F86B4 /* moc_sportsplusrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87873AED2D09A8AA005F86B4 /* moc_sportsplusrower.cpp */; };
|
||||
87873AF12D09A8CE005F86B4 /* sportsplusrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87873AF02D09A8CE005F86B4 /* sportsplusrower.cpp */; };
|
||||
878A331A25AB4FF800BD13E1 /* yesoulbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878A331725AB4FF800BD13E1 /* yesoulbike.cpp */; };
|
||||
878A331D25AB50C300BD13E1 /* moc_yesoulbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878A331B25AB50C200BD13E1 /* moc_yesoulbike.cpp */; };
|
||||
878C9E6928B77E7C00669129 /* nordictrackifitadbbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878C9E6828B77E7B00669129 /* nordictrackifitadbbike.cpp */; };
|
||||
@@ -401,6 +415,7 @@
|
||||
87958F1B27628D5400124B24 /* moc_elitesterzosmart.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87958F1A27628D5400124B24 /* moc_elitesterzosmart.cpp */; };
|
||||
8798C8872733E103003148B3 /* strydrunpowersensor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8798C8862733E103003148B3 /* strydrunpowersensor.cpp */; };
|
||||
8798C8892733E10E003148B3 /* moc_strydrunpowersensor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8798C8882733E10E003148B3 /* moc_strydrunpowersensor.cpp */; };
|
||||
8798FDC52D66075B00CF8EE8 /* OSXBtManagerInternal.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8798FDC32D66075B00CF8EE8 /* OSXBtManagerInternal.mm */; };
|
||||
879A38C8281BD83300F78B2A /* characteristicnotifier2ad9.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 879A38C7281BD83300F78B2A /* characteristicnotifier2ad9.cpp */; };
|
||||
879E5AA8289C057E00FEA38A /* proformwifitreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 879E5AA6289C057E00FEA38A /* proformwifitreadmill.cpp */; };
|
||||
879E5AAA289C05A500FEA38A /* moc_proformwifitreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 879E5AA9289C05A500FEA38A /* moc_proformwifitreadmill.cpp */; };
|
||||
@@ -423,11 +438,17 @@
|
||||
87A18F072660D5C1002D7C96 /* ftmsrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A18F052660D5C0002D7C96 /* ftmsrower.cpp */; };
|
||||
87A18F092660D5D9002D7C96 /* moc_ftmsrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A18F082660D5D9002D7C96 /* moc_ftmsrower.cpp */; };
|
||||
87A2E0222B2B053E00E6168F /* swiftDebug.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A2E0212B2B053E00E6168F /* swiftDebug.mm */; };
|
||||
87A33F1A2D611D8400BFFF29 /* moc_logwriter.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A33F192D611D8400BFFF29 /* moc_logwriter.cpp */; };
|
||||
87A33F1D2D611D9500BFFF29 /* logwriter.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A33F1C2D611D9500BFFF29 /* logwriter.cpp */; };
|
||||
87A3BC222656429600D302E3 /* rower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A3BC1F2656429400D302E3 /* rower.cpp */; };
|
||||
87A3BC232656429600D302E3 /* echelonrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A3BC202656429400D302E3 /* echelonrower.cpp */; };
|
||||
87A3BC26265642A300D302E3 /* moc_rower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A3BC24265642A200D302E3 /* moc_rower.cpp */; };
|
||||
87A3BC27265642A300D302E3 /* moc_echelonrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A3BC25265642A200D302E3 /* moc_echelonrower.cpp */; };
|
||||
87A3DD9B2D3413790060BAEB /* moc_lifespantreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A3DD9A2D3413790060BAEB /* moc_lifespantreadmill.cpp */; };
|
||||
87A3DD9C2D3413790060BAEB /* lifespantreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A3DD992D3413790060BAEB /* lifespantreadmill.cpp */; };
|
||||
87A4B76125AF27CB0027EF3C /* metric.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A4B75F25AF27CB0027EF3C /* metric.cpp */; };
|
||||
87A6825A2CE3AB3100586A2A /* moc_sramAXSController.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A682592CE3AB3100586A2A /* moc_sramAXSController.cpp */; };
|
||||
87A6825D2CE3AB4000586A2A /* sramAXSController.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A6825C2CE3AB4000586A2A /* sramAXSController.cpp */; };
|
||||
87ADD2BB27634C1500B7A0AB /* technogymmyruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87ADD2B927634C1400B7A0AB /* technogymmyruntreadmill.cpp */; };
|
||||
87ADD2BD27634C2100B7A0AB /* moc_technogymmyruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87ADD2BC27634C2100B7A0AB /* moc_technogymmyruntreadmill.cpp */; };
|
||||
87AE0CB227760DCB00E547E9 /* virtualtreadmill_zwift.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87AE0CB127760DCB00E547E9 /* virtualtreadmill_zwift.swift */; };
|
||||
@@ -439,6 +460,8 @@
|
||||
87B617F225F260150094A1CB /* moc_fitshowtreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87B617EF25F260140094A1CB /* moc_fitshowtreadmill.cpp */; };
|
||||
87B617F325F260150094A1CB /* moc_snodebike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87B617F025F260140094A1CB /* moc_snodebike.cpp */; };
|
||||
87B617F425F260150094A1CB /* moc_screencapture.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87B617F125F260150094A1CB /* moc_screencapture.cpp */; };
|
||||
87B871902CE1E837009B06CA /* Zwift hub.pb.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87B8718F2CE1E837009B06CA /* Zwift hub.pb.swift */; };
|
||||
87B871932CE1E94D009B06CA /* zwifthubbike.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87B871922CE1E94D009B06CA /* zwifthubbike.swift */; };
|
||||
87BAC3BF2BA497160003E925 /* PrivacyInfo.xcprivacy in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = 87BAC3BE2BA497160003E925 /* PrivacyInfo.xcprivacy */; };
|
||||
87BAC3C12BA497350003E925 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 87BAC3C02BA497350003E925 /* PrivacyInfo.xcprivacy */; };
|
||||
87BAFE482B8CA7AA00065FCD /* moc_focustreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87BAFE472B8CA7AA00065FCD /* moc_focustreadmill.cpp */; };
|
||||
@@ -451,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 */; };
|
||||
@@ -502,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 */; };
|
||||
@@ -511,6 +541,12 @@
|
||||
87DAE16926E9FF5000B0527E /* moc_shuaa5treadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DAE16626E9FF5000B0527E /* moc_shuaa5treadmill.cpp */; };
|
||||
87DAE16A26E9FF5000B0527E /* moc_kingsmithr2treadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DAE16726E9FF5000B0527E /* moc_kingsmithr2treadmill.cpp */; };
|
||||
87DAE16B26E9FF5000B0527E /* moc_solef80treadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DAE16826E9FF5000B0527E /* moc_solef80treadmill.cpp */; };
|
||||
87DC27EA2D9BDB53007A1B9D /* echelonstairclimber.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DC27E72D9BDB53007A1B9D /* echelonstairclimber.cpp */; };
|
||||
87DC27EB2D9BDB53007A1B9D /* stairclimber.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DC27E92D9BDB53007A1B9D /* stairclimber.cpp */; };
|
||||
87DC27EE2D9BDB8F007A1B9D /* moc_stairclimber.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DC27ED2D9BDB8F007A1B9D /* moc_stairclimber.cpp */; };
|
||||
87DC27EF2D9BDB8F007A1B9D /* moc_echelonstairclimber.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DC27EC2D9BDB8F007A1B9D /* moc_echelonstairclimber.cpp */; };
|
||||
87DC27F32D9BDC43007A1B9D /* moc_moxy5sensor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DC27F02D9BDC43007A1B9D /* moc_moxy5sensor.cpp */; };
|
||||
87DC27F42D9BDC43007A1B9D /* moxy5sensor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DC27F22D9BDC43007A1B9D /* moxy5sensor.cpp */; };
|
||||
87DED80627D1273900BE4FBB /* filedownloader.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DED80427D1273800BE4FBB /* filedownloader.cpp */; };
|
||||
87DED80827D1274600BE4FBB /* moc_filedownloader.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DED80727D1274500BE4FBB /* moc_filedownloader.cpp */; };
|
||||
87DF68B825E2673B00FCDA46 /* eslinkertreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87DF68B625E2673600FCDA46 /* eslinkertreadmill.cpp */; };
|
||||
@@ -528,6 +564,8 @@
|
||||
87E5D2C825E69F4700BDBE6C /* moc_horizontreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E5D2C725E69F4700BDBE6C /* moc_horizontreadmill.cpp */; };
|
||||
87E6A85825B5C88E00371D28 /* moc_flywheelbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E6A85725B5C88E00371D28 /* moc_flywheelbike.cpp */; };
|
||||
87E6A85B25B5C8B900371D28 /* flywheelbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87E6A85925B5C8B900371D28 /* flywheelbike.cpp */; };
|
||||
87EAC3D32D1D8D1D004FE975 /* moc_pitpatbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EAC3D22D1D8D1D004FE975 /* moc_pitpatbike.cpp */; };
|
||||
87EAC3D62D1D8D34004FE975 /* pitpatbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EAC3D52D1D8D34004FE975 /* pitpatbike.cpp */; };
|
||||
87EB917627EE5FB3002535E1 /* nautilusbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EB917427EE5FB3002535E1 /* nautilusbike.cpp */; };
|
||||
87EB918227EE5FE7002535E1 /* moc_inapptransaction.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EB917727EE5FE7002535E1 /* moc_inapptransaction.cpp */; };
|
||||
87EB918327EE5FE7002535E1 /* moc_inappstore.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87EB917827EE5FE7002535E1 /* moc_inappstore.cpp */; };
|
||||
@@ -552,6 +590,10 @@
|
||||
87FA11AB27C5ECD1008AC5D1 /* ultrasportbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FA11AA27C5ECD1008AC5D1 /* ultrasportbike.cpp */; };
|
||||
87FA11AD27C5ECE4008AC5D1 /* moc_ultrasportbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FA11AC27C5ECE4008AC5D1 /* moc_ultrasportbike.cpp */; };
|
||||
87FA94672B6B89FD00B6AB9A /* SwiftUI.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 87FA94662B6B89FD00B6AB9A /* SwiftUI.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
87FC40BC2D2E74F9008BA736 /* cycleopsphantombike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FC40BA2D2E74F9008BA736 /* cycleopsphantombike.cpp */; };
|
||||
87FC40BD2D2E74F9008BA736 /* moc_cycleopsphantombike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FC40BB2D2E74F9008BA736 /* moc_cycleopsphantombike.cpp */; };
|
||||
87FE06812D170D3C00CDAAF6 /* moc_trxappgateusbrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FE06802D170D3C00CDAAF6 /* moc_trxappgateusbrower.cpp */; };
|
||||
87FE06842D170D5600CDAAF6 /* trxappgateusbrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FE06832D170D5600CDAAF6 /* trxappgateusbrower.cpp */; };
|
||||
87FE5BAF2692F3130056EFC8 /* tacxneo2.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FE5BAD2692F3130056EFC8 /* tacxneo2.cpp */; };
|
||||
87FE5BB12692F31E0056EFC8 /* moc_tacxneo2.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FE5BB02692F31E0056EFC8 /* moc_tacxneo2.cpp */; };
|
||||
87FFA13727BBE3FF00924E4E /* solebike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87FFA13527BBE3FE00924E4E /* solebike.cpp */; };
|
||||
@@ -624,13 +666,6 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
876E4E0E2594739400BD5714 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6DB9C3763D02B1415CD9D565 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = E9F0AFC024A6F2D65CE84E08;
|
||||
remoteInfo = "Qt Preprocess";
|
||||
};
|
||||
876E4E1C2594748000BD5714 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6DB9C3763D02B1415CD9D565 /* Project object */;
|
||||
@@ -913,6 +948,8 @@
|
||||
87097D2D275EA9A20020EE6F /* sportsplusbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sportsplusbike.cpp; path = ../src/devices/sportsplusbike/sportsplusbike.cpp; sourceTree = "<group>"; };
|
||||
87097D2E275EA9A20020EE6F /* sportsplusbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sportsplusbike.h; path = ../src/devices/sportsplusbike/sportsplusbike.h; sourceTree = "<group>"; };
|
||||
87097D30275EA9AE0020EE6F /* moc_sportsplusbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_sportsplusbike.cpp; sourceTree = "<group>"; };
|
||||
870A5DB22CEF8FB100839641 /* moc_technogymbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_technogymbike.cpp; sourceTree = "<group>"; };
|
||||
870A5DB42CEF8FD200839641 /* technogymbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = technogymbike.cpp; path = ../src/devices/technogymbike/technogymbike.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8710706A29C48AE90094D0F3 /* handleurl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = handleurl.h; path = ../src/handleurl.h; sourceTree = "<group>"; };
|
||||
8710706B29C48AEA0094D0F3 /* handleurl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = handleurl.cpp; path = ../src/handleurl.cpp; sourceTree = "<group>"; };
|
||||
8710706D29C48AF30094D0F3 /* moc_handleurl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_handleurl.cpp; sourceTree = "<group>"; };
|
||||
@@ -949,6 +986,42 @@
|
||||
871B9FD1265E6A8800DB41F4 /* powerzonepack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = powerzonepack.cpp; path = ../src/powerzonepack.cpp; sourceTree = "<group>"; };
|
||||
871B9FD3265E6A9A00DB41F4 /* moc_powerzonepack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_powerzonepack.cpp; sourceTree = "<group>"; };
|
||||
871E4CD025A6FB5A00E18D6D /* BLEPeripheralManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BLEPeripheralManager.swift; path = ../src/ios/BLEPeripheralManager.swift; sourceTree = "<group>"; };
|
||||
872088E62CE6543C008C2C17 /* moc_mqttpublisher.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_mqttpublisher.cpp; sourceTree = "<group>"; };
|
||||
872088E72CE6543C008C2C17 /* moc_qmqttclient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_qmqttclient.cpp; sourceTree = "<group>"; };
|
||||
872088E82CE6543C008C2C17 /* moc_qmqttconnection_p.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_qmqttconnection_p.cpp; sourceTree = "<group>"; };
|
||||
872088E92CE6543C008C2C17 /* moc_qmqttmessage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_qmqttmessage.cpp; sourceTree = "<group>"; };
|
||||
872088EA2CE6543C008C2C17 /* moc_qmqttsubscription.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_qmqttsubscription.cpp; sourceTree = "<group>"; };
|
||||
872088F02CE65451008C2C17 /* qmqttauthenticationproperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttauthenticationproperties.h; path = ../src/mqtt/qmqttauthenticationproperties.h; sourceTree = SOURCE_ROOT; };
|
||||
872088F12CE65451008C2C17 /* qmqttauthenticationproperties.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttauthenticationproperties.cpp; path = ../src/mqtt/qmqttauthenticationproperties.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872088F22CE65451008C2C17 /* qmqttclient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttclient.h; path = ../src/mqtt/qmqttclient.h; sourceTree = SOURCE_ROOT; };
|
||||
872088F32CE65451008C2C17 /* qmqttclient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttclient.cpp; path = ../src/mqtt/qmqttclient.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872088F42CE65451008C2C17 /* qmqttclient_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttclient_p.h; path = ../src/mqtt/qmqttclient_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872088F52CE65451008C2C17 /* qmqttconnection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttconnection.cpp; path = ../src/mqtt/qmqttconnection.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872088F62CE65451008C2C17 /* qmqttconnection_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttconnection_p.h; path = ../src/mqtt/qmqttconnection_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872088F72CE65451008C2C17 /* qmqttconnectionproperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttconnectionproperties.h; path = ../src/mqtt/qmqttconnectionproperties.h; sourceTree = SOURCE_ROOT; };
|
||||
872088F82CE65451008C2C17 /* qmqttconnectionproperties.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttconnectionproperties.cpp; path = ../src/mqtt/qmqttconnectionproperties.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872088F92CE65451008C2C17 /* qmqttconnectionproperties_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttconnectionproperties_p.h; path = ../src/mqtt/qmqttconnectionproperties_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872088FA2CE65451008C2C17 /* qmqttcontrolpacket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttcontrolpacket.cpp; path = ../src/mqtt/qmqttcontrolpacket.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872088FB2CE65451008C2C17 /* qmqttcontrolpacket_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttcontrolpacket_p.h; path = ../src/mqtt/qmqttcontrolpacket_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872088FC2CE65451008C2C17 /* qmqttglobal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttglobal.h; path = ../src/mqtt/qmqttglobal.h; sourceTree = SOURCE_ROOT; };
|
||||
872088FD2CE65451008C2C17 /* qmqttmessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttmessage.h; path = ../src/mqtt/qmqttmessage.h; sourceTree = SOURCE_ROOT; };
|
||||
872088FE2CE65451008C2C17 /* qmqttmessage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttmessage.cpp; path = ../src/mqtt/qmqttmessage.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872088FF2CE65451008C2C17 /* qmqttmessage_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttmessage_p.h; path = ../src/mqtt/qmqttmessage_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872089002CE65451008C2C17 /* qmqttpublishproperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttpublishproperties.h; path = ../src/mqtt/qmqttpublishproperties.h; sourceTree = SOURCE_ROOT; };
|
||||
872089012CE65451008C2C17 /* qmqttpublishproperties.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttpublishproperties.cpp; path = ../src/mqtt/qmqttpublishproperties.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872089022CE65451008C2C17 /* qmqttpublishproperties_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttpublishproperties_p.h; path = ../src/mqtt/qmqttpublishproperties_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872089032CE65451008C2C17 /* qmqttsubscription.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttsubscription.h; path = ../src/mqtt/qmqttsubscription.h; sourceTree = SOURCE_ROOT; };
|
||||
872089042CE65451008C2C17 /* qmqttsubscription.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttsubscription.cpp; path = ../src/mqtt/qmqttsubscription.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872089052CE65451008C2C17 /* qmqttsubscription_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttsubscription_p.h; path = ../src/mqtt/qmqttsubscription_p.h; sourceTree = SOURCE_ROOT; };
|
||||
872089062CE65451008C2C17 /* qmqttsubscriptionproperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqttsubscriptionproperties.h; path = ../src/mqtt/qmqttsubscriptionproperties.h; sourceTree = SOURCE_ROOT; };
|
||||
872089072CE65451008C2C17 /* qmqttsubscriptionproperties.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqttsubscriptionproperties.cpp; path = ../src/mqtt/qmqttsubscriptionproperties.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872089082CE65451008C2C17 /* qmqtttopicfilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqtttopicfilter.h; path = ../src/mqtt/qmqtttopicfilter.h; sourceTree = SOURCE_ROOT; };
|
||||
872089092CE65451008C2C17 /* qmqtttopicfilter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqtttopicfilter.cpp; path = ../src/mqtt/qmqtttopicfilter.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8720890A2CE65451008C2C17 /* qmqtttopicname.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqtttopicname.h; path = ../src/mqtt/qmqtttopicname.h; sourceTree = SOURCE_ROOT; };
|
||||
8720890B2CE65451008C2C17 /* qmqtttopicname.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqtttopicname.cpp; path = ../src/mqtt/qmqtttopicname.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8720890C2CE65451008C2C17 /* qmqtttype.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qmqtttype.h; path = ../src/mqtt/qmqtttype.h; sourceTree = SOURCE_ROOT; };
|
||||
8720890D2CE65451008C2C17 /* qmqtttype.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = qmqtttype.cpp; path = ../src/mqtt/qmqtttype.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8720891A2CE6567D008C2C17 /* mqttpublisher.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = mqttpublisher.cpp; path = ../src/mqttpublisher.cpp; sourceTree = SOURCE_ROOT; };
|
||||
872261EC289EA873006A6F75 /* nordictrackelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nordictrackelliptical.cpp; path = ../src/devices/nordictrackelliptical/nordictrackelliptical.cpp; sourceTree = "<group>"; };
|
||||
872261ED289EA873006A6F75 /* nordictrackelliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nordictrackelliptical.h; path = ../src/devices/nordictrackelliptical/nordictrackelliptical.h; sourceTree = "<group>"; };
|
||||
872261EF289EA887006A6F75 /* moc_nordictrackelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_nordictrackelliptical.cpp; sourceTree = "<group>"; };
|
||||
@@ -1117,6 +1190,18 @@
|
||||
87586A3F25B8340D00A243C4 /* proformbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proformbike.h; path = ../src/devices/proformbike/proformbike.h; sourceTree = "<group>"; };
|
||||
87586A4025B8340E00A243C4 /* proformbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = proformbike.cpp; path = ../src/devices/proformbike/proformbike.cpp; sourceTree = "<group>"; };
|
||||
87586A4225B8341B00A243C4 /* moc_proformbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_proformbike.cpp; sourceTree = "<group>"; };
|
||||
875CA9452D0C740000667EE6 /* moc_kineticinroadbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_kineticinroadbike.cpp; sourceTree = "<group>"; };
|
||||
875CA9472D0C742500667EE6 /* kineticinroadbike.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = kineticinroadbike.h; path = ../src/devices/kineticinroadbike/kineticinroadbike.h; sourceTree = SOURCE_ROOT; };
|
||||
875CA9482D0C742500667EE6 /* kineticinroadbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = kineticinroadbike.cpp; path = ../src/devices/kineticinroadbike/kineticinroadbike.cpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA94B2D130F8100667EE6 /* moc_osc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_osc.cpp; sourceTree = "<group>"; };
|
||||
875CA94D2D130FBC00667EE6 /* client.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = client.hpp; path = ../src/oscpp/client.hpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA94E2D130FBC00667EE6 /* error.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = error.hpp; path = ../src/oscpp/error.hpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA94F2D130FBC00667EE6 /* osc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = osc.h; path = ../src/osc.h; sourceTree = SOURCE_ROOT; };
|
||||
875CA9502D130FBC00667EE6 /* osc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = osc.cpp; path = ../src/osc.cpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA9512D130FBC00667EE6 /* print.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = print.hpp; path = ../src/oscpp/print.hpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA9522D130FBC00667EE6 /* server.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = server.hpp; path = ../src/oscpp/server.hpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA9532D130FBC00667EE6 /* types.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = types.hpp; path = ../src/oscpp/types.hpp; sourceTree = SOURCE_ROOT; };
|
||||
875CA9542D130FBC00667EE6 /* util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = util.hpp; path = ../src/oscpp/util.hpp; sourceTree = SOURCE_ROOT; };
|
||||
875F69B726342E8D0009FD78 /* spirittreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = spirittreadmill.h; path = ../src/devices/spirittreadmill/spirittreadmill.h; sourceTree = "<group>"; };
|
||||
875F69B826342E8D0009FD78 /* spirittreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = spirittreadmill.cpp; path = ../src/devices/spirittreadmill/spirittreadmill.cpp; sourceTree = "<group>"; };
|
||||
875F69BA26342E9A0009FD78 /* moc_spirittreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_spirittreadmill.cpp; sourceTree = "<group>"; };
|
||||
@@ -1129,6 +1214,9 @@
|
||||
87646C1E27B5064500F82131 /* bhfitnesselliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bhfitnesselliptical.cpp; path = ../src/devices/bhfitnesselliptical/bhfitnesselliptical.cpp; sourceTree = "<group>"; };
|
||||
87646C1F27B5064500F82131 /* bhfitnesselliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bhfitnesselliptical.h; path = ../src/devices/bhfitnesselliptical/bhfitnesselliptical.h; sourceTree = "<group>"; };
|
||||
87646C2127B5065100F82131 /* moc_bhfitnesselliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_bhfitnesselliptical.cpp; sourceTree = "<group>"; };
|
||||
8767CA522DA3C1FD0003001F /* elitesquarecontroller.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = elitesquarecontroller.h; path = ../src/devices/elitesquarecontroller/elitesquarecontroller.h; sourceTree = SOURCE_ROOT; };
|
||||
8767CA532DA3C1FD0003001F /* elitesquarecontroller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = elitesquarecontroller.cpp; path = ../src/devices/elitesquarecontroller/elitesquarecontroller.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8767CA542DA3C1FD0003001F /* moc_elitesquarecontroller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_elitesquarecontroller.cpp; sourceTree = "<group>"; };
|
||||
8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicwriteprocessor.cpp; path = ../src/characteristics/characteristicwriteprocessor.cpp; sourceTree = "<group>"; };
|
||||
8768C89C2BBC11C70099DBE1 /* file_sync_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = file_sync_client.c; path = ../src/ios/adb/adb/file_sync_client.c; sourceTree = "<group>"; };
|
||||
8768C89D2BBC11C70099DBE1 /* protocol.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = protocol.txt; path = ../src/ios/adb/adb/protocol.txt; sourceTree = "<group>"; };
|
||||
@@ -1239,6 +1327,8 @@
|
||||
8772B7F32CB55E80004AB8E9 /* moc_deerruntreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_deerruntreadmill.cpp; sourceTree = "<group>"; };
|
||||
8772B7F62CB55E98004AB8E9 /* deerruntreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = deerruntreadmill.cpp; sourceTree = "<group>"; };
|
||||
8772B7F92CB5603A004AB8E9 /* deerruntreadmill.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = deerruntreadmill.h; path = ../src/devices/deeruntreadmill/deerruntreadmill.h; sourceTree = SOURCE_ROOT; };
|
||||
877350F52D1C08E50070CBD8 /* SmartControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SmartControl.h; path = ../src/devices/kineticinroadbike/SmartControl.h; sourceTree = SOURCE_ROOT; };
|
||||
877350F62D1C08E50070CBD8 /* SmartControl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SmartControl.cpp; path = ../src/devices/kineticinroadbike/SmartControl.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8775008129E876F7008E48B7 /* iconceptelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iconceptelliptical.cpp; path = ../src/devices/iconceptelliptical/iconceptelliptical.cpp; sourceTree = "<group>"; };
|
||||
8775008229E876F7008E48B7 /* iconceptelliptical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iconceptelliptical.h; path = ../src/devices/iconceptelliptical/iconceptelliptical.h; sourceTree = "<group>"; };
|
||||
8775008429E87712008E48B7 /* moc_iconceptelliptical.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_iconceptelliptical.cpp; sourceTree = "<group>"; };
|
||||
@@ -1272,6 +1362,9 @@
|
||||
8785D5402B3DD0EC005A2EB7 /* PlayerStateWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlayerStateWrapper.h; path = "../src/zwift-api/PlayerStateWrapper.h"; sourceTree = "<group>"; };
|
||||
8785D5412B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_PlayerStateWrapper.cpp; sourceTree = "<group>"; };
|
||||
8785D5422B3DD105005A2EB7 /* moc_zwift_client_auth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_zwift_client_auth.cpp; sourceTree = "<group>"; };
|
||||
87873AED2D09A8AA005F86B4 /* moc_sportsplusrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_sportsplusrower.cpp; sourceTree = "<group>"; };
|
||||
87873AF02D09A8CE005F86B4 /* sportsplusrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sportsplusrower.cpp; sourceTree = "<group>"; };
|
||||
87873AF22D09AADF005F86B4 /* sportsplusrower.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sportsplusrower.h; path = ../src/devices/sportsplusrower/sportsplusrower.h; sourceTree = SOURCE_ROOT; };
|
||||
8789DCDB6A4F681A76DF3F92 /* Qt5Widgets */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = Qt5Widgets; path = "/Users/cagnulein/Qt/5.15.2/ios/lib/libQt5Widgets$(QT_LIBRARY_SUFFIX).a"; sourceTree = "<absolute>"; };
|
||||
878A331725AB4FF800BD13E1 /* yesoulbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = yesoulbike.cpp; path = ../src/devices/yesoulbike/yesoulbike.cpp; sourceTree = "<group>"; };
|
||||
878A331825AB4FF800BD13E1 /* yesoulbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yesoulbike.h; path = ../src/devices/yesoulbike/yesoulbike.h; sourceTree = "<group>"; };
|
||||
@@ -1303,6 +1396,11 @@
|
||||
8798C8852733E103003148B3 /* strydrunpowersensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = strydrunpowersensor.h; path = ../src/devices/strydrunpowersensor/strydrunpowersensor.h; sourceTree = "<group>"; };
|
||||
8798C8862733E103003148B3 /* strydrunpowersensor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = strydrunpowersensor.cpp; path = ../src/devices/strydrunpowersensor/strydrunpowersensor.cpp; sourceTree = "<group>"; };
|
||||
8798C8882733E10E003148B3 /* moc_strydrunpowersensor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_strydrunpowersensor.cpp; sourceTree = "<group>"; };
|
||||
8798FDC02D66075B00CF8EE8 /* osxbluetooth_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = osxbluetooth_p.h; path = ../src/ios/BluetoothPatch/osxbluetooth_p.h; sourceTree = SOURCE_ROOT; };
|
||||
8798FDC12D66075B00CF8EE8 /* osxbtcentralmanager_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = osxbtcentralmanager_p.h; path = ../src/ios/BluetoothPatch/osxbtcentralmanager_p.h; sourceTree = SOURCE_ROOT; };
|
||||
8798FDC22D66075B00CF8EE8 /* osxbtgcdtimer_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = osxbtgcdtimer_p.h; path = ../src/ios/BluetoothPatch/osxbtgcdtimer_p.h; sourceTree = SOURCE_ROOT; };
|
||||
8798FDC32D66075B00CF8EE8 /* OSXBtManagerInternal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = OSXBtManagerInternal.mm; path = ../src/ios/BluetoothPatch/OSXBtManagerInternal.mm; sourceTree = SOURCE_ROOT; };
|
||||
8798FDC42D66075B00CF8EE8 /* osxbtutility_p.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = osxbtutility_p.h; path = ../src/ios/BluetoothPatch/osxbtutility_p.h; sourceTree = SOURCE_ROOT; };
|
||||
879A38C7281BD83300F78B2A /* characteristicnotifier2ad9.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicnotifier2ad9.cpp; path = ../src/characteristics/characteristicnotifier2ad9.cpp; sourceTree = "<group>"; };
|
||||
879E5AA6289C057E00FEA38A /* proformwifitreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = proformwifitreadmill.cpp; path = ../src/devices/proformwifitreadmill/proformwifitreadmill.cpp; sourceTree = "<group>"; };
|
||||
879E5AA7289C057E00FEA38A /* proformwifitreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proformwifitreadmill.h; path = ../src/devices/proformwifitreadmill/proformwifitreadmill.h; sourceTree = "<group>"; };
|
||||
@@ -1335,17 +1433,26 @@
|
||||
87A18F082660D5D9002D7C96 /* moc_ftmsrower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_ftmsrower.cpp; sourceTree = "<group>"; };
|
||||
87A2E0202B2B024200E6168F /* swiftDebug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = swiftDebug.h; path = ../src/ios/swiftDebug.h; sourceTree = "<group>"; };
|
||||
87A2E0212B2B053E00E6168F /* swiftDebug.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = swiftDebug.mm; path = ../src/ios/swiftDebug.mm; sourceTree = "<group>"; };
|
||||
87A33F192D611D8400BFFF29 /* moc_logwriter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_logwriter.cpp; sourceTree = "<group>"; };
|
||||
87A33F1B2D611D9500BFFF29 /* logwriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = logwriter.h; path = ../src/logwriter.h; sourceTree = SOURCE_ROOT; };
|
||||
87A33F1C2D611D9500BFFF29 /* logwriter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = logwriter.cpp; path = ../src/logwriter.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87A3BC1E2656429300D302E3 /* echelonrower.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = echelonrower.h; path = ../src/devices/echelonrower/echelonrower.h; sourceTree = "<group>"; };
|
||||
87A3BC1F2656429400D302E3 /* rower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rower.cpp; path = ../src/devices/rower.cpp; sourceTree = "<group>"; };
|
||||
87A3BC202656429400D302E3 /* echelonrower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = echelonrower.cpp; path = ../src/devices/echelonrower/echelonrower.cpp; sourceTree = "<group>"; };
|
||||
87A3BC212656429400D302E3 /* rower.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rower.h; path = ../src/devices/rower.h; sourceTree = "<group>"; };
|
||||
87A3BC24265642A200D302E3 /* moc_rower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_rower.cpp; sourceTree = "<group>"; };
|
||||
87A3BC25265642A200D302E3 /* moc_echelonrower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_echelonrower.cpp; sourceTree = "<group>"; };
|
||||
87A3DD982D3413790060BAEB /* lifespantreadmill.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = lifespantreadmill.h; path = ../src/devices/lifespantreadmill/lifespantreadmill.h; sourceTree = SOURCE_ROOT; };
|
||||
87A3DD992D3413790060BAEB /* lifespantreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = lifespantreadmill.cpp; path = ../src/devices/lifespantreadmill/lifespantreadmill.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87A3DD9A2D3413790060BAEB /* moc_lifespantreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_lifespantreadmill.cpp; sourceTree = "<group>"; };
|
||||
87A3EBB925D2CFED0040EB4C /* sportstechbike.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sportstechbike.h; path = ../src/devices/sportstechbike/sportstechbike.h; sourceTree = "<group>"; };
|
||||
87A3EBBA25D2CFED0040EB4C /* sportstechbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = sportstechbike.cpp; path = ../src/devices/sportstechbike/sportstechbike.cpp; sourceTree = "<group>"; };
|
||||
87A4B75F25AF27CB0027EF3C /* metric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = metric.cpp; path = ../src/metric.cpp; sourceTree = "<group>"; };
|
||||
87A4B76025AF27CB0027EF3C /* metric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = metric.h; path = ../src/metric.h; sourceTree = "<group>"; };
|
||||
87A659DB8BE7DBAA7B395EF4 /* fit_monitoring_info_mesg_listener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = fit_monitoring_info_mesg_listener.hpp; path = "/Users/cagnulein/qdomyos-zwift/src/fit-sdk/fit_monitoring_info_mesg_listener.hpp"; sourceTree = "<absolute>"; };
|
||||
87A682592CE3AB3100586A2A /* moc_sramAXSController.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_sramAXSController.cpp; sourceTree = "<group>"; };
|
||||
87A6825B2CE3AB4000586A2A /* sramAXSController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sramAXSController.h; path = ../src/devices/sramAXSController/sramAXSController.h; sourceTree = SOURCE_ROOT; };
|
||||
87A6825C2CE3AB4000586A2A /* sramAXSController.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = sramAXSController.cpp; path = ../src/devices/sramAXSController/sramAXSController.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87ADD2B927634C1400B7A0AB /* technogymmyruntreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = technogymmyruntreadmill.cpp; path = ../src/devices/technogymmyruntreadmill/technogymmyruntreadmill.cpp; sourceTree = "<group>"; };
|
||||
87ADD2BA27634C1400B7A0AB /* technogymmyruntreadmill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = technogymmyruntreadmill.h; path = ../src/devices/technogymmyruntreadmill/technogymmyruntreadmill.h; sourceTree = "<group>"; };
|
||||
87ADD2BC27634C2100B7A0AB /* moc_technogymmyruntreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_technogymmyruntreadmill.cpp; sourceTree = "<group>"; };
|
||||
@@ -1362,6 +1469,8 @@
|
||||
87B617EF25F260140094A1CB /* moc_fitshowtreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_fitshowtreadmill.cpp; sourceTree = "<group>"; };
|
||||
87B617F025F260140094A1CB /* moc_snodebike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_snodebike.cpp; sourceTree = "<group>"; };
|
||||
87B617F125F260150094A1CB /* moc_screencapture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_screencapture.cpp; sourceTree = "<group>"; };
|
||||
87B8718F2CE1E837009B06CA /* Zwift hub.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Zwift hub.pb.swift"; path = "../src/devices/zwifthubbike/Zwift hub.pb.swift"; sourceTree = SOURCE_ROOT; };
|
||||
87B871922CE1E94D009B06CA /* zwifthubbike.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = zwifthubbike.swift; path = ../src/devices/zwifthubbike/zwifthubbike.swift; sourceTree = SOURCE_ROOT; };
|
||||
87BAC3BE2BA497160003E925 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../src/ios/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
87BAC3C02BA497350003E925 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
87BAFE472B8CA7AA00065FCD /* moc_focustreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_focustreadmill.cpp; sourceTree = "<group>"; };
|
||||
@@ -1378,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>"; };
|
||||
@@ -1455,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>"; };
|
||||
@@ -1468,6 +1585,15 @@
|
||||
87DAE16626E9FF5000B0527E /* moc_shuaa5treadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_shuaa5treadmill.cpp; sourceTree = "<group>"; };
|
||||
87DAE16726E9FF5000B0527E /* moc_kingsmithr2treadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_kingsmithr2treadmill.cpp; sourceTree = "<group>"; };
|
||||
87DAE16826E9FF5000B0527E /* moc_solef80treadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_solef80treadmill.cpp; sourceTree = "<group>"; };
|
||||
87DC27E62D9BDB53007A1B9D /* echelonstairclimber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = echelonstairclimber.h; path = ../src/devices/echelonstairclimber/echelonstairclimber.h; sourceTree = SOURCE_ROOT; };
|
||||
87DC27E72D9BDB53007A1B9D /* echelonstairclimber.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = echelonstairclimber.cpp; path = ../src/devices/echelonstairclimber/echelonstairclimber.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87DC27E82D9BDB53007A1B9D /* stairclimber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = stairclimber.h; path = ../src/devices/stairclimber.h; sourceTree = SOURCE_ROOT; };
|
||||
87DC27E92D9BDB53007A1B9D /* stairclimber.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = stairclimber.cpp; path = ../src/devices/stairclimber.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87DC27EC2D9BDB8F007A1B9D /* moc_echelonstairclimber.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_echelonstairclimber.cpp; sourceTree = "<group>"; };
|
||||
87DC27ED2D9BDB8F007A1B9D /* moc_stairclimber.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_stairclimber.cpp; sourceTree = "<group>"; };
|
||||
87DC27F02D9BDC43007A1B9D /* moc_moxy5sensor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_moxy5sensor.cpp; sourceTree = "<group>"; };
|
||||
87DC27F12D9BDC43007A1B9D /* moxy5sensor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = moxy5sensor.h; path = ../src/devices/moxy5sensor/moxy5sensor.h; sourceTree = SOURCE_ROOT; };
|
||||
87DC27F22D9BDC43007A1B9D /* moxy5sensor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = moxy5sensor.cpp; path = ../src/devices/moxy5sensor/moxy5sensor.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87DED80427D1273800BE4FBB /* filedownloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = filedownloader.cpp; path = ../src/filedownloader.cpp; sourceTree = "<group>"; };
|
||||
87DED80527D1273900BE4FBB /* filedownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = filedownloader.h; path = ../src/filedownloader.h; sourceTree = "<group>"; };
|
||||
87DED80727D1274500BE4FBB /* moc_filedownloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_filedownloader.cpp; sourceTree = "<group>"; };
|
||||
@@ -1493,6 +1619,9 @@
|
||||
87E6A85725B5C88E00371D28 /* moc_flywheelbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_flywheelbike.cpp; sourceTree = "<group>"; };
|
||||
87E6A85925B5C8B900371D28 /* flywheelbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = flywheelbike.cpp; path = ../src/devices/flywheelbike/flywheelbike.cpp; sourceTree = "<group>"; };
|
||||
87E6A85A25B5C8B900371D28 /* flywheelbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = flywheelbike.h; path = ../src/devices/flywheelbike/flywheelbike.h; sourceTree = "<group>"; };
|
||||
87EAC3D22D1D8D1D004FE975 /* moc_pitpatbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_pitpatbike.cpp; sourceTree = "<group>"; };
|
||||
87EAC3D42D1D8D34004FE975 /* pitpatbike.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = pitpatbike.h; path = ../src/devices/pitpatbike/pitpatbike.h; sourceTree = SOURCE_ROOT; };
|
||||
87EAC3D52D1D8D34004FE975 /* pitpatbike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = pitpatbike.cpp; path = ../src/devices/pitpatbike/pitpatbike.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87EB917427EE5FB3002535E1 /* nautilusbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nautilusbike.cpp; path = ../src/devices/nautilusbike/nautilusbike.cpp; sourceTree = "<group>"; };
|
||||
87EB917527EE5FB3002535E1 /* nautilusbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nautilusbike.h; path = ../src/devices/nautilusbike/nautilusbike.h; sourceTree = "<group>"; };
|
||||
87EB917727EE5FE7002535E1 /* moc_inapptransaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_inapptransaction.cpp; sourceTree = "<group>"; };
|
||||
@@ -1528,7 +1657,13 @@
|
||||
87FA11AC27C5ECE4008AC5D1 /* moc_ultrasportbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_ultrasportbike.cpp; sourceTree = "<group>"; };
|
||||
87FA94662B6B89FD00B6AB9A /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
87FA94682B6B8A5A00B6AB9A /* libSystem.B.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libSystem.B.tbd; path = usr/lib/libSystem.B.tbd; sourceTree = SDKROOT; };
|
||||
87FC40B92D2E74F9008BA736 /* cycleopsphantombike.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = cycleopsphantombike.h; path = ../src/devices/cycleopsphantombike/cycleopsphantombike.h; sourceTree = SOURCE_ROOT; };
|
||||
87FC40BA2D2E74F9008BA736 /* cycleopsphantombike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = cycleopsphantombike.cpp; path = ../src/devices/cycleopsphantombike/cycleopsphantombike.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87FC40BB2D2E74F9008BA736 /* moc_cycleopsphantombike.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_cycleopsphantombike.cpp; sourceTree = "<group>"; };
|
||||
87FD05DDC378E9BAD82A818F /* fit_ohr_settings_mesg_listener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = fit_ohr_settings_mesg_listener.hpp; path = "/Users/cagnulein/qdomyos-zwift/src/fit-sdk/fit_ohr_settings_mesg_listener.hpp"; sourceTree = "<absolute>"; };
|
||||
87FE06802D170D3C00CDAAF6 /* moc_trxappgateusbrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_trxappgateusbrower.cpp; sourceTree = "<group>"; };
|
||||
87FE06822D170D5600CDAAF6 /* trxappgateusbrower.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = trxappgateusbrower.h; path = ../src/devices/trxappgateusbrower/trxappgateusbrower.h; sourceTree = SOURCE_ROOT; };
|
||||
87FE06832D170D5600CDAAF6 /* trxappgateusbrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = trxappgateusbrower.cpp; path = ../src/devices/trxappgateusbrower/trxappgateusbrower.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87FE5BAD2692F3130056EFC8 /* tacxneo2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tacxneo2.cpp; path = ../src/devices/tacxneo2/tacxneo2.cpp; sourceTree = "<group>"; };
|
||||
87FE5BAE2692F3130056EFC8 /* tacxneo2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tacxneo2.h; path = ../src/devices/tacxneo2/tacxneo2.h; sourceTree = "<group>"; };
|
||||
87FE5BB02692F31E0056EFC8 /* moc_tacxneo2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_tacxneo2.cpp; sourceTree = "<group>"; };
|
||||
@@ -2079,6 +2214,92 @@
|
||||
2EB56BE3C2D93CDAB0C52E67 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
87BFEA2D2CEDDEEE00BDD759 /* ios_echelonconnectsport.h */,
|
||||
87BFEA2E2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm */,
|
||||
8767CA522DA3C1FD0003001F /* elitesquarecontroller.h */,
|
||||
8767CA532DA3C1FD0003001F /* elitesquarecontroller.cpp */,
|
||||
8767CA542DA3C1FD0003001F /* moc_elitesquarecontroller.cpp */,
|
||||
87DC27F02D9BDC43007A1B9D /* moc_moxy5sensor.cpp */,
|
||||
87DC27F12D9BDC43007A1B9D /* moxy5sensor.h */,
|
||||
87DC27F22D9BDC43007A1B9D /* moxy5sensor.cpp */,
|
||||
87DC27EC2D9BDB8F007A1B9D /* moc_echelonstairclimber.cpp */,
|
||||
87DC27ED2D9BDB8F007A1B9D /* moc_stairclimber.cpp */,
|
||||
8798FDC02D66075B00CF8EE8 /* osxbluetooth_p.h */,
|
||||
8798FDC12D66075B00CF8EE8 /* osxbtcentralmanager_p.h */,
|
||||
8798FDC22D66075B00CF8EE8 /* osxbtgcdtimer_p.h */,
|
||||
8798FDC32D66075B00CF8EE8 /* OSXBtManagerInternal.mm */,
|
||||
8798FDC42D66075B00CF8EE8 /* osxbtutility_p.h */,
|
||||
87A33F1B2D611D9500BFFF29 /* logwriter.h */,
|
||||
87A33F1C2D611D9500BFFF29 /* logwriter.cpp */,
|
||||
87A33F192D611D8400BFFF29 /* moc_logwriter.cpp */,
|
||||
87A3DD982D3413790060BAEB /* lifespantreadmill.h */,
|
||||
87A3DD992D3413790060BAEB /* lifespantreadmill.cpp */,
|
||||
87A3DD9A2D3413790060BAEB /* moc_lifespantreadmill.cpp */,
|
||||
87FC40B92D2E74F9008BA736 /* cycleopsphantombike.h */,
|
||||
87FC40BA2D2E74F9008BA736 /* cycleopsphantombike.cpp */,
|
||||
87FC40BB2D2E74F9008BA736 /* moc_cycleopsphantombike.cpp */,
|
||||
87EAC3D42D1D8D34004FE975 /* pitpatbike.h */,
|
||||
87EAC3D52D1D8D34004FE975 /* pitpatbike.cpp */,
|
||||
87EAC3D22D1D8D1D004FE975 /* moc_pitpatbike.cpp */,
|
||||
877350F52D1C08E50070CBD8 /* SmartControl.h */,
|
||||
877350F62D1C08E50070CBD8 /* SmartControl.cpp */,
|
||||
875CA94D2D130FBC00667EE6 /* client.hpp */,
|
||||
875CA94E2D130FBC00667EE6 /* error.hpp */,
|
||||
875CA94F2D130FBC00667EE6 /* osc.h */,
|
||||
875CA9502D130FBC00667EE6 /* osc.cpp */,
|
||||
875CA9512D130FBC00667EE6 /* print.hpp */,
|
||||
875CA9522D130FBC00667EE6 /* server.hpp */,
|
||||
875CA9532D130FBC00667EE6 /* types.hpp */,
|
||||
875CA9542D130FBC00667EE6 /* util.hpp */,
|
||||
875CA94B2D130F8100667EE6 /* moc_osc.cpp */,
|
||||
875CA9472D0C742500667EE6 /* kineticinroadbike.h */,
|
||||
875CA9482D0C742500667EE6 /* kineticinroadbike.cpp */,
|
||||
875CA9452D0C740000667EE6 /* moc_kineticinroadbike.cpp */,
|
||||
87873AF22D09AADF005F86B4 /* sportsplusrower.h */,
|
||||
87873AF02D09A8CE005F86B4 /* sportsplusrower.cpp */,
|
||||
87873AED2D09A8AA005F86B4 /* moc_sportsplusrower.cpp */,
|
||||
870A5DB42CEF8FD200839641 /* technogymbike.cpp */,
|
||||
870A5DB22CEF8FB100839641 /* moc_technogymbike.cpp */,
|
||||
8720891A2CE6567D008C2C17 /* mqttpublisher.cpp */,
|
||||
872088F02CE65451008C2C17 /* qmqttauthenticationproperties.h */,
|
||||
872088F12CE65451008C2C17 /* qmqttauthenticationproperties.cpp */,
|
||||
872088F22CE65451008C2C17 /* qmqttclient.h */,
|
||||
872088F32CE65451008C2C17 /* qmqttclient.cpp */,
|
||||
872088F42CE65451008C2C17 /* qmqttclient_p.h */,
|
||||
872088F52CE65451008C2C17 /* qmqttconnection.cpp */,
|
||||
872088F62CE65451008C2C17 /* qmqttconnection_p.h */,
|
||||
872088F72CE65451008C2C17 /* qmqttconnectionproperties.h */,
|
||||
872088F82CE65451008C2C17 /* qmqttconnectionproperties.cpp */,
|
||||
872088F92CE65451008C2C17 /* qmqttconnectionproperties_p.h */,
|
||||
872088FA2CE65451008C2C17 /* qmqttcontrolpacket.cpp */,
|
||||
872088FB2CE65451008C2C17 /* qmqttcontrolpacket_p.h */,
|
||||
872088FC2CE65451008C2C17 /* qmqttglobal.h */,
|
||||
872088FD2CE65451008C2C17 /* qmqttmessage.h */,
|
||||
872088FE2CE65451008C2C17 /* qmqttmessage.cpp */,
|
||||
872088FF2CE65451008C2C17 /* qmqttmessage_p.h */,
|
||||
872089002CE65451008C2C17 /* qmqttpublishproperties.h */,
|
||||
872089012CE65451008C2C17 /* qmqttpublishproperties.cpp */,
|
||||
872089022CE65451008C2C17 /* qmqttpublishproperties_p.h */,
|
||||
872089032CE65451008C2C17 /* qmqttsubscription.h */,
|
||||
872089042CE65451008C2C17 /* qmqttsubscription.cpp */,
|
||||
872089052CE65451008C2C17 /* qmqttsubscription_p.h */,
|
||||
872089062CE65451008C2C17 /* qmqttsubscriptionproperties.h */,
|
||||
872089072CE65451008C2C17 /* qmqttsubscriptionproperties.cpp */,
|
||||
872089082CE65451008C2C17 /* qmqtttopicfilter.h */,
|
||||
872089092CE65451008C2C17 /* qmqtttopicfilter.cpp */,
|
||||
8720890A2CE65451008C2C17 /* qmqtttopicname.h */,
|
||||
8720890B2CE65451008C2C17 /* qmqtttopicname.cpp */,
|
||||
8720890C2CE65451008C2C17 /* qmqtttype.h */,
|
||||
8720890D2CE65451008C2C17 /* qmqtttype.cpp */,
|
||||
872088E62CE6543C008C2C17 /* moc_mqttpublisher.cpp */,
|
||||
872088E72CE6543C008C2C17 /* moc_qmqttclient.cpp */,
|
||||
872088E82CE6543C008C2C17 /* moc_qmqttconnection_p.cpp */,
|
||||
872088E92CE6543C008C2C17 /* moc_qmqttmessage.cpp */,
|
||||
872088EA2CE6543C008C2C17 /* moc_qmqttsubscription.cpp */,
|
||||
87B8718F2CE1E837009B06CA /* Zwift hub.pb.swift */,
|
||||
87A6825B2CE3AB4000586A2A /* sramAXSController.h */,
|
||||
87A6825C2CE3AB4000586A2A /* sramAXSController.cpp */,
|
||||
87A682592CE3AB3100586A2A /* moc_sramAXSController.cpp */,
|
||||
87A083062C73361C00567A4E /* characteristicnotifier2ad9.h */,
|
||||
8772B7F92CB5603A004AB8E9 /* deerruntreadmill.h */,
|
||||
8772B7F62CB55E98004AB8E9 /* deerruntreadmill.cpp */,
|
||||
@@ -2494,6 +2715,14 @@
|
||||
8710707229C4A5E70094D0F3 /* GarminConnect.swift */,
|
||||
87A2E0202B2B024200E6168F /* swiftDebug.h */,
|
||||
8730A3912B404159007E336D /* zwift_protobuf_layer.swift */,
|
||||
87B871922CE1E94D009B06CA /* zwifthubbike.swift */,
|
||||
87FE06822D170D5600CDAAF6 /* trxappgateusbrower.h */,
|
||||
87FE06832D170D5600CDAAF6 /* trxappgateusbrower.cpp */,
|
||||
87FE06802D170D3C00CDAAF6 /* moc_trxappgateusbrower.cpp */,
|
||||
87DC27E62D9BDB53007A1B9D /* echelonstairclimber.h */,
|
||||
87DC27E72D9BDB53007A1B9D /* echelonstairclimber.cpp */,
|
||||
87DC27E82D9BDB53007A1B9D /* stairclimber.h */,
|
||||
87DC27E92D9BDB53007A1B9D /* stairclimber.cpp */,
|
||||
);
|
||||
name = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -2647,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 = (
|
||||
@@ -3065,6 +3307,7 @@
|
||||
AF39DD055C3EF8226FBE929D /* Frameworks */,
|
||||
858FCAB0EB1F29CF8B07677C /* Bundle Data */,
|
||||
FE0A091FDBFB3E9C31B7A1BD /* Products */,
|
||||
8798FDCA2D6F338200CF8EE8 /* Recovered References */,
|
||||
);
|
||||
name = qdomyoszwift;
|
||||
sourceTree = "<group>";
|
||||
@@ -3095,7 +3338,6 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
F8E0E95C27758CAC67EF1CD4 /* PBXTargetDependency */,
|
||||
876E4E312594748100BD5714 /* PBXTargetDependency */,
|
||||
);
|
||||
name = qdomyoszwift;
|
||||
@@ -3165,9 +3407,6 @@
|
||||
DevelopmentTeam = 6335M7T29D;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
E9F0AFC024A6F2D65CE84E08 = {
|
||||
DevelopmentTeam = 6335M7T29D;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "qdomyoszwift" */;
|
||||
@@ -3187,7 +3426,6 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
799833E5566DEFFC37E4BF1E /* qdomyoszwift */,
|
||||
E9F0AFC024A6F2D65CE84E08 /* Qt Preprocess */,
|
||||
876E4E102594747F00BD5714 /* watchkit */,
|
||||
876E4E192594748000BD5714 /* watchkit Extension */,
|
||||
);
|
||||
@@ -3238,31 +3476,6 @@
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
5E618435888B9D49F8540165 /* Qt Qmake */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
name = "Qt Qmake";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "make -C /Users/cagnulein/qdomyos-zwift/build-qdomyos-zwift-Qt_5_15_2_for_iOS-Debug -f qdomyoszwift.xcodeproj/qt_makeqmake.mak";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
7EF0942E79C014DCEC8976BC /* Qt Preprocessors */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
name = "Qt Preprocessors";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "make -C /Users/cagnulein/qdomyos-zwift/build-qdomyos-zwift-Qt_5_15_2_for_iOS-Debug -f qdomyoszwift.xcodeproj/qt_preprocess.mak";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
876E4E162594748000BD5714 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
@@ -3284,12 +3497,15 @@
|
||||
files = (
|
||||
8738249627E646E3004F1B46 /* characteristicnotifier2acd.cpp in Compile Sources */,
|
||||
8738249127E646E3004F1B46 /* dirconpacket.cpp in Compile Sources */,
|
||||
870A5DB52CEF8FD200839641 /* technogymbike.cpp in Compile Sources */,
|
||||
87C5F0BB26285E5F0067A1B5 /* mimetext.cpp in Compile Sources */,
|
||||
8732C17F27353464006DF424 /* iconceptbike.cpp in Compile Sources */,
|
||||
87CF5169293C879800A7CABC /* characteristicwriteprocessore005.cpp in Compile Sources */,
|
||||
873824C027E64707004F1B46 /* moc_dirconmanager.cpp in Compile Sources */,
|
||||
2F0E70F826316F3600E11F3A /* virtualbike_zwift.swift in Compile Sources */,
|
||||
8772A0E825E43AE70080718C /* moc_trxappgateusbbike.cpp in Compile Sources */,
|
||||
87DC27EE2D9BDB8F007A1B9D /* moc_stairclimber.cpp in Compile Sources */,
|
||||
87DC27EF2D9BDB8F007A1B9D /* moc_echelonstairclimber.cpp in Compile Sources */,
|
||||
87062646259480B200D06586 /* ViewController.swift in Compile Sources */,
|
||||
87D269A425F535340076AA48 /* moc_m3ibike.cpp in Compile Sources */,
|
||||
87BAFE482B8CA7AA00065FCD /* moc_focustreadmill.cpp in Compile Sources */,
|
||||
@@ -3310,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 */,
|
||||
@@ -3349,6 +3567,8 @@
|
||||
87917A7328E768D200F8D9AC /* Browser.swift in Compile Sources */,
|
||||
873CD20B27EF8D8A000131BC /* inapptransaction.cpp in Compile Sources */,
|
||||
8727C7D52B3BF1E4005429EB /* moc_proformtelnetbike.cpp in Compile Sources */,
|
||||
8767CA552DA3C1FD0003001F /* elitesquarecontroller.cpp in Compile Sources */,
|
||||
8767CA562DA3C1FD0003001F /* moc_elitesquarecontroller.cpp in Compile Sources */,
|
||||
873824EF27E647A9004F1B46 /* query.cpp in Compile Sources */,
|
||||
876F45FF279350D9003CDA5A /* moc_concept2skierg.cpp in Compile Sources */,
|
||||
BE93C6EF2C2A6BFEEC9EA565 /* fit_buffered_mesg_broadcaster.cpp in Compile Sources */,
|
||||
@@ -3416,6 +3636,7 @@
|
||||
87BE6FDE272D2A3E00C35795 /* moc_horizongr7bike.cpp in Compile Sources */,
|
||||
A4BD6DF51CFFF867B7B5AED4 /* fit_developer_field_definition.cpp in Compile Sources */,
|
||||
87EB918B27EE5FE7002535E1 /* moc_inappproductqmltype.cpp in Compile Sources */,
|
||||
87873AF12D09A8CE005F86B4 /* sportsplusrower.cpp in Compile Sources */,
|
||||
8762D5132601F89500F6F049 /* scanrecordresult.cpp in Compile Sources */,
|
||||
3015F9B9FF4CA6D653D46CCA /* fit_developer_field_description.cpp in Compile Sources */,
|
||||
87310B22266FBB78008BA0D6 /* moc_homefitnessbuddy.cpp in Compile Sources */,
|
||||
@@ -3424,6 +3645,8 @@
|
||||
8772B7F42CB55E80004AB8E9 /* moc_deerruntreadmill.cpp in Compile Sources */,
|
||||
87CC3BA425A0885F001EC5A8 /* elliptical.cpp in Compile Sources */,
|
||||
4AD2C93A2B8FD5855E521630 /* fit_encode.cpp in Compile Sources */,
|
||||
87DC27F32D9BDC43007A1B9D /* moc_moxy5sensor.cpp in Compile Sources */,
|
||||
87DC27F42D9BDC43007A1B9D /* moxy5sensor.cpp in Compile Sources */,
|
||||
87EB918C27EE5FE7002535E1 /* moc_inappproduct.cpp in Compile Sources */,
|
||||
87E34C2D2886F99A00CEDE4B /* moc_octanetreadmill.cpp in Compile Sources */,
|
||||
87D91F9A2800B9970026D43C /* proformwifibike.cpp in Compile Sources */,
|
||||
@@ -3442,6 +3665,7 @@
|
||||
87D269A325F535340076AA48 /* moc_skandikawiribike.cpp in Compile Sources */,
|
||||
87F4FB5C29D550E00061BB4A /* moc_schwinn170bike.cpp in Compile Sources */,
|
||||
87C5F0C226285E5F0067A1B5 /* emailaddress.cpp in Compile Sources */,
|
||||
87B871902CE1E837009B06CA /* Zwift hub.pb.swift in Compile Sources */,
|
||||
873824AF27E64706004F1B46 /* moc_characteristicwriteprocessor2ad9.cpp in Compile Sources */,
|
||||
25F2400F80DAFBD41FE5CC75 /* fit_field.cpp in Compile Sources */,
|
||||
873824E227E647A8004F1B46 /* dns.cpp in Compile Sources */,
|
||||
@@ -3464,17 +3688,35 @@
|
||||
87E6A85B25B5C8B900371D28 /* flywheelbike.cpp in Compile Sources */,
|
||||
87182A09276BBAF600141463 /* virtualrower.cpp in Compile Sources */,
|
||||
87C424262BC1294000503687 /* moc_treadmillErgTable.cpp in Compile Sources */,
|
||||
877350F72D1C08E60070CBD8 /* SmartControl.cpp in Compile Sources */,
|
||||
873824ED27E647A9004F1B46 /* resolver.cpp in Compile Sources */,
|
||||
875CA9552D130FBC00667EE6 /* osc.cpp in Compile Sources */,
|
||||
878531652711A3E1004B153D /* activiotreadmill.cpp in Compile Sources */,
|
||||
87DAE16926E9FF5000B0527E /* moc_shuaa5treadmill.cpp in Compile Sources */,
|
||||
48BA9CE9D6F256A15E8FB25D /* fit_mesg.cpp in Compile Sources */,
|
||||
DD2E0091F3318F053D2995AA /* fit_mesg_broadcaster.cpp in Compile Sources */,
|
||||
FE77C778768741F1A161682E /* fit_mesg_definition.cpp in Compile Sources */,
|
||||
875F69B926342E8D0009FD78 /* spirittreadmill.cpp in Compile Sources */,
|
||||
87A6825D2CE3AB4000586A2A /* sramAXSController.cpp in Compile Sources */,
|
||||
87DF68B825E2673B00FCDA46 /* eslinkertreadmill.cpp in Compile Sources */,
|
||||
8720890E2CE65451008C2C17 /* qmqttsubscriptionproperties.cpp in Compile Sources */,
|
||||
8720890F2CE65451008C2C17 /* qmqttconnectionproperties.cpp in Compile Sources */,
|
||||
872089102CE65451008C2C17 /* qmqttconnection.cpp in Compile Sources */,
|
||||
872089112CE65451008C2C17 /* qmqttsubscription.cpp in Compile Sources */,
|
||||
872089122CE65451008C2C17 /* qmqttclient.cpp in Compile Sources */,
|
||||
872089132CE65451008C2C17 /* qmqtttopicfilter.cpp in Compile Sources */,
|
||||
872089142CE65451008C2C17 /* qmqtttype.cpp in Compile Sources */,
|
||||
872089152CE65451008C2C17 /* qmqttcontrolpacket.cpp in Compile Sources */,
|
||||
872089162CE65451008C2C17 /* qmqttmessage.cpp in Compile Sources */,
|
||||
872089172CE65451008C2C17 /* qmqtttopicname.cpp in Compile Sources */,
|
||||
872089182CE65451008C2C17 /* qmqttpublishproperties.cpp in Compile Sources */,
|
||||
872089192CE65451008C2C17 /* qmqttauthenticationproperties.cpp in Compile Sources */,
|
||||
87FE06842D170D5600CDAAF6 /* trxappgateusbrower.cpp in Compile Sources */,
|
||||
87BCE6BD29F28F72001F70EB /* ypooelliptical.cpp in Compile Sources */,
|
||||
87C7074327E4CF5900E79C46 /* keepbike.cpp in Compile Sources */,
|
||||
87B871932CE1E94D009B06CA /* zwifthubbike.swift in Compile Sources */,
|
||||
878C9E6928B77E7C00669129 /* nordictrackifitadbbike.cpp in Compile Sources */,
|
||||
87A33F1D2D611D9500BFFF29 /* logwriter.cpp in Compile Sources */,
|
||||
8798C8892733E10E003148B3 /* moc_strydrunpowersensor.cpp in Compile Sources */,
|
||||
8781907E2615089D0085E656 /* peloton.cpp in Compile Sources */,
|
||||
2B800DC34C91D8B080DEFBE8 /* fit_mesg_with_event_broadcaster.cpp in Compile Sources */,
|
||||
@@ -3500,6 +3742,7 @@
|
||||
7CF08714869DA569C2EA551C /* keepawakehelper.cpp in Compile Sources */,
|
||||
87EB918727EE5FE7002535E1 /* moc_nautilusbike.cpp in Compile Sources */,
|
||||
873824B827E64707004F1B46 /* moc_cache.cpp in Compile Sources */,
|
||||
87873AEE2D09A8AA005F86B4 /* moc_sportsplusrower.cpp in Compile Sources */,
|
||||
873824E427E647A8004F1B46 /* cache.cpp in Compile Sources */,
|
||||
87A2E0222B2B053E00E6168F /* swiftDebug.mm in Compile Sources */,
|
||||
87310B1E266FBB59008BA0D6 /* smartrowrower.cpp in Compile Sources */,
|
||||
@@ -3535,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 */,
|
||||
@@ -3552,6 +3796,7 @@
|
||||
0317752B0C295CAB82D37E45 /* virtualtreadmill.cpp in Compile Sources */,
|
||||
8742C2B427C92C48007D3FA0 /* moc_wahookickrsnapbike.cpp in Compile Sources */,
|
||||
878531692711A3EC004B153D /* moc_fakebike.cpp in Compile Sources */,
|
||||
870A5DB32CEF8FB100839641 /* moc_technogymbike.cpp in Compile Sources */,
|
||||
873824B027E64706004F1B46 /* moc_cache_p.cpp in Compile Sources */,
|
||||
87EB918627EE5FE7002535E1 /* moc_inapppurchasebackend.cpp in Compile Sources */,
|
||||
87097D31275EA9AF0020EE6F /* moc_sportsplusbike.cpp in Compile Sources */,
|
||||
@@ -3562,6 +3807,12 @@
|
||||
87A0C4BB262329A600121A76 /* npecablebike.cpp in Compile Sources */,
|
||||
873CD22D27EF8E4B000131BC /* iosinapppurchaseproduct.mm in Compile Sources */,
|
||||
873CD20727EF8D8A000131BC /* inappproduct.cpp in Compile Sources */,
|
||||
872088EB2CE6543C008C2C17 /* moc_mqttpublisher.cpp in Compile Sources */,
|
||||
872088EC2CE6543C008C2C17 /* moc_qmqttclient.cpp in Compile Sources */,
|
||||
875CA94C2D130F8100667EE6 /* moc_osc.cpp in Compile Sources */,
|
||||
872088ED2CE6543C008C2C17 /* moc_qmqttmessage.cpp in Compile Sources */,
|
||||
872088EE2CE6543C008C2C17 /* moc_qmqttsubscription.cpp in Compile Sources */,
|
||||
872088EF2CE6543C008C2C17 /* moc_qmqttconnection_p.cpp in Compile Sources */,
|
||||
87A3BC26265642A300D302E3 /* moc_rower.cpp in Compile Sources */,
|
||||
87EFE45B27A51901006EA1C3 /* moc_nautiluselliptical.cpp in Compile Sources */,
|
||||
872DCC392A18D4A800EC9F68 /* virtualdevice.cpp in Compile Sources */,
|
||||
@@ -3586,9 +3837,11 @@
|
||||
873824AE27E64706004F1B46 /* moc_browser.cpp in Compile Sources */,
|
||||
8738249727E646E3004F1B46 /* characteristicnotifier2a53.cpp in Compile Sources */,
|
||||
DF373364C5474D877506CB26 /* FitMesg.mm in Compile Sources */,
|
||||
87FE06812D170D3C00CDAAF6 /* moc_trxappgateusbrower.cpp in Compile Sources */,
|
||||
872261F0289EA887006A6F75 /* moc_nordictrackelliptical.cpp in Compile Sources */,
|
||||
873824E327E647A8004F1B46 /* bitmap.cpp in Compile Sources */,
|
||||
87FE5BB12692F31E0056EFC8 /* moc_tacxneo2.cpp in Compile Sources */,
|
||||
8798FDC52D66075B00CF8EE8 /* OSXBtManagerInternal.mm in Compile Sources */,
|
||||
873824E827E647A8004F1B46 /* provider.cpp in Compile Sources */,
|
||||
8791A8AA25C8603F003B50B2 /* moc_inspirebike.cpp in Compile Sources */,
|
||||
03F49BBCF19B73B18385B13D /* FitMesgDefinition.mm in Compile Sources */,
|
||||
@@ -3616,13 +3869,18 @@
|
||||
8768C9062BBC12B80099DBE1 /* socket_local_client.c in Compile Sources */,
|
||||
8727C7D42B3BF1E4005429EB /* moc_QTelnet.cpp in Compile Sources */,
|
||||
C3D1FD2587BF6F15B58BA675 /* moc_bluetooth.cpp in Compile Sources */,
|
||||
8720891B2CE6567D008C2C17 /* mqttpublisher.cpp in Compile Sources */,
|
||||
87062648259480B700D06586 /* WorkoutTracking.swift in Compile Sources */,
|
||||
8C3422A825EF7ECD78951307 /* moc_bluetoothdevice.cpp in Compile Sources */,
|
||||
8785D5432B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp in Compile Sources */,
|
||||
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 */,
|
||||
87A4B76125AF27CB0027EF3C /* metric.cpp in Compile Sources */,
|
||||
87D10552290996EA00B3935B /* mepanelbike.cpp in Compile Sources */,
|
||||
9D9484EED654597C394345DE /* moc_echelonconnectsport.cpp in Compile Sources */,
|
||||
@@ -3639,6 +3897,7 @@
|
||||
E62DA5FF2436135448C94671 /* moc_toorxtreadmill.cpp in Compile Sources */,
|
||||
87586A4325B8341B00A243C4 /* moc_proformbike.cpp in Compile Sources */,
|
||||
87CC3BA325A0885F001EC5A8 /* domyoselliptical.cpp in Compile Sources */,
|
||||
87A33F1A2D611D8400BFFF29 /* moc_logwriter.cpp in Compile Sources */,
|
||||
87D105542909971100B3935B /* moc_mepanelbike.cpp in Compile Sources */,
|
||||
87B617F325F260150094A1CB /* moc_snodebike.cpp in Compile Sources */,
|
||||
87DA8467284933DE00B550E9 /* moc_fakeelliptical.cpp in Compile Sources */,
|
||||
@@ -3648,6 +3907,7 @@
|
||||
8762D5102601F7EA00F6F049 /* M3iNSQT.cpp in Compile Sources */,
|
||||
872261EE289EA873006A6F75 /* nordictrackelliptical.cpp in Compile Sources */,
|
||||
8718CBA3263063BD004BF4EE /* templateinfosender.cpp in Compile Sources */,
|
||||
87EAC3D62D1D8D34004FE975 /* pitpatbike.cpp in Compile Sources */,
|
||||
E8B499F921FB0AB55C7A8A8B /* moc_gpx.cpp in Compile Sources */,
|
||||
87E6A85825B5C88E00371D28 /* moc_flywheelbike.cpp in Compile Sources */,
|
||||
8754D24C27F786F0003D7054 /* virtualrower.swift in Compile Sources */,
|
||||
@@ -3660,6 +3920,9 @@
|
||||
879E5AA8289C057E00FEA38A /* proformwifitreadmill.cpp in Compile Sources */,
|
||||
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 */,
|
||||
@@ -3672,10 +3935,13 @@
|
||||
8727C7D12B3BF1B8005429EB /* QTelnet.cpp in Compile Sources */,
|
||||
8775008329E876F8008E48B7 /* iconceptelliptical.cpp in Compile Sources */,
|
||||
87B187BD29B8C577007EEF9D /* moc_ziprotreadmill.cpp in Compile Sources */,
|
||||
875CA9492D0C742500667EE6 /* kineticinroadbike.cpp in Compile Sources */,
|
||||
877FBA2B276E684E00F6C0C9 /* moc_bowflextreadmill.cpp in Compile Sources */,
|
||||
874D272229AFA13B0007C079 /* moc_apexbike.cpp in Compile Sources */,
|
||||
8738248127E646C1004F1B46 /* characteristicnotifier2ad2.cpp in Compile Sources */,
|
||||
87A6825A2CE3AB3100586A2A /* moc_sramAXSController.cpp in Compile Sources */,
|
||||
87A0771029B641D600A368BF /* wahookickrheadwind.cpp in Compile Sources */,
|
||||
87EAC3D32D1D8D1D004FE975 /* moc_pitpatbike.cpp in Compile Sources */,
|
||||
8791A8AB25C861BD003B50B2 /* inspirebike.cpp in Compile Sources */,
|
||||
876BFC9D27BE35C5001D7645 /* bowflext216treadmill.cpp in Compile Sources */,
|
||||
871235C126B297720012D0F2 /* moc_kingsmithr1protreadmill.cpp in Compile Sources */,
|
||||
@@ -3714,6 +3980,8 @@
|
||||
87EB918827EE5FE7002535E1 /* moc_inappstoreqmltype.cpp in Compile Sources */,
|
||||
87083D9626678EFA0072410D /* zwiftworkout.cpp in Compile Sources */,
|
||||
87C5F0B826285E5F0067A1B5 /* stagesbike.cpp in Compile Sources */,
|
||||
87FC40BC2D2E74F9008BA736 /* cycleopsphantombike.cpp in Compile Sources */,
|
||||
87FC40BD2D2E74F9008BA736 /* moc_cycleopsphantombike.cpp in Compile Sources */,
|
||||
87C5F0D526285E7E0067A1B5 /* moc_mimehtml.cpp in Compile Sources */,
|
||||
871B9FD4265E6A9A00DB41F4 /* moc_powerzonepack.cpp in Compile Sources */,
|
||||
87A0C4BF262329B500121A76 /* moc_cscbike.cpp in Compile Sources */,
|
||||
@@ -3721,6 +3989,8 @@
|
||||
8790FDE1277B0AC600247550 /* moc_nautilustreadmill.cpp in Compile Sources */,
|
||||
8727A47927849EB200019B5D /* moc_paferstreadmill.cpp in Compile Sources */,
|
||||
8783153B25E8D81E0007817C /* moc_sportstechbike.cpp in Compile Sources */,
|
||||
87A3DD9B2D3413790060BAEB /* moc_lifespantreadmill.cpp in Compile Sources */,
|
||||
87A3DD9C2D3413790060BAEB /* lifespantreadmill.cpp in Compile Sources */,
|
||||
875F69BB26342E9A0009FD78 /* moc_spirittreadmill.cpp in Compile Sources */,
|
||||
8768C8BF2BBC11C80099DBE1 /* transport.c in Compile Sources */,
|
||||
);
|
||||
@@ -3740,11 +4010,6 @@
|
||||
target = 876E4E102594747F00BD5714 /* watchkit */;
|
||||
targetProxy = 876E4E302594748100BD5714 /* PBXContainerItemProxy */;
|
||||
};
|
||||
F8E0E95C27758CAC67EF1CD4 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = E9F0AFC024A6F2D65CE84E08 /* Qt Preprocess */;
|
||||
targetProxy = 876E4E0E2594739400BD5714 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -4068,7 +4333,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 909;
|
||||
CURRENT_PROJECT_VERSION = 1062;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
|
||||
@@ -4101,6 +4366,9 @@
|
||||
../../Qt/5.15.2/ios/include/QtMultimedia,
|
||||
../src/devices,
|
||||
../src/ios/adb/include,
|
||||
../../Qt/5.15.2/ios/include/QtCore/5.15.2/QtCore/private,
|
||||
../../Qt/5.15.2/ios/include/QtCore/5.15.2,
|
||||
../../Qt/5.15.2/ios/include/QtCore/5.15.2/QtCore/,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
/Users/cagnulein/Qt/5.15.2/ios/plugins/platforms,
|
||||
@@ -4259,7 +4527,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 909;
|
||||
CURRENT_PROJECT_VERSION = 1062;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4294,6 +4562,9 @@
|
||||
../../Qt/5.15.2/ios/include/QtMultimedia,
|
||||
../src/devices,
|
||||
../src/ios/adb/include,
|
||||
../../Qt/5.15.2/ios/include/QtCore/5.15.2/QtCore/private,
|
||||
../../Qt/5.15.2/ios/include/QtCore/5.15.2,
|
||||
../../Qt/5.15.2/ios/include/QtCore/5.15.2/QtCore/,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
/Users/cagnulein/Qt/5.15.2/ios/plugins/platforms,
|
||||
@@ -4486,7 +4757,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 909;
|
||||
CURRENT_PROJECT_VERSION = 1062;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -4582,7 +4853,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 909;
|
||||
CURRENT_PROJECT_VERSION = 1062;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -4674,7 +4945,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 909;
|
||||
CURRENT_PROJECT_VERSION = 1062;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -4739,6 +5010,8 @@
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
STRIP_SWIFT_SYMBOLS = NO;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -4788,7 +5061,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 909;
|
||||
CURRENT_PROJECT_VERSION = 1062;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -4849,6 +5122,8 @@
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
STRIP_SWIFT_SYMBOLS = NO;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
@@ -107,6 +107,7 @@ extension MainController: WorkoutTrackingDelegate {
|
||||
WorkoutTracking.speed = WatchKitConnection.speed
|
||||
WorkoutTracking.power = WatchKitConnection.power
|
||||
WorkoutTracking.cadence = WatchKitConnection.cadence
|
||||
WorkoutTracking.steps = WatchKitConnection.steps
|
||||
|
||||
if Locale.current.measurementSystem != "Metric" {
|
||||
self.distanceLabel.setText("Distance \(String(format:"%.2f", WorkoutTracking.distance))")
|
||||
|
||||
@@ -27,6 +27,7 @@ class WatchKitConnection: NSObject {
|
||||
public static var speed = 0.0
|
||||
public static var cadence = 0.0
|
||||
public static var power = 0.0
|
||||
public static var steps = 0
|
||||
weak var delegate: WatchKitConnectionDelegate?
|
||||
|
||||
private override init() {
|
||||
@@ -76,6 +77,10 @@ extension WatchKitConnection: WatchKitConnectionProtocol {
|
||||
WatchKitConnection.power = dPower
|
||||
let dCadence = Double(result["cadence"] as! Double)
|
||||
WatchKitConnection.cadence = dCadence
|
||||
if let stepsDouble = result["steps"] as? Double {
|
||||
let iSteps = Int(stepsDouble)
|
||||
WatchKitConnection.steps = iSteps
|
||||
}
|
||||
}, errorHandler: { (error) in
|
||||
print(error)
|
||||
})
|
||||
|
||||
@@ -33,6 +33,7 @@ class WorkoutTracking: NSObject {
|
||||
public static var cadenceSteps = 0
|
||||
public static var speed = Double()
|
||||
public static var power = Double()
|
||||
public static var steps = Int()
|
||||
public static var cadence = Double()
|
||||
public static var lastDateMetric = Date()
|
||||
var sport: Int = 0
|
||||
@@ -269,6 +270,43 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
}
|
||||
} else {
|
||||
|
||||
// Guard to check if steps quantity type is available
|
||||
guard let quantityTypeSteps = HKQuantityType.quantityType(
|
||||
forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
|
||||
let stepsQuantity = HKQuantity(unit: HKUnit.count(), doubleValue: Double(WorkoutTracking.steps))
|
||||
|
||||
// Create a sample for total steps
|
||||
let sampleSteps = HKCumulativeQuantitySeriesSample(
|
||||
type: quantityTypeSteps,
|
||||
quantity: stepsQuantity, // Use your steps quantity here
|
||||
start: workoutSession.startDate!,
|
||||
end: Date())
|
||||
|
||||
// Add the steps sample to workout builder
|
||||
workoutBuilder.add([sampleSteps]) { (success, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
|
||||
// End the data collection
|
||||
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
|
||||
// Finish the workout and save total steps
|
||||
self.workoutBuilder.finishWorkout { (workout, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
workout?.setValue(stepsQuantity, forKey: "totalSteps")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let quantityTypeDistance = HKQuantityType.quantityType(
|
||||
forIdentifier: .distanceWalkingRunning) else {
|
||||
return
|
||||
@@ -402,7 +440,7 @@ extension WorkoutTracking: HKLiveWorkoutBuilderDelegate {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
} else if(sport == 1) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
let wattPerInterval = HKQuantity(unit: HKUnit.watt(),
|
||||
doubleValue: WorkoutTracking.power)
|
||||
|
||||
@@ -445,7 +483,7 @@ extension WorkoutTracking: HKLiveWorkoutBuilderDelegate {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
} else if(sport == 2) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
let speedPerInterval = HKQuantity(unit: HKUnit.meter().unitDivided(by: HKUnit.second()),
|
||||
doubleValue: WorkoutTracking.speed * 0.277778)
|
||||
|
||||
|
||||
96
docker/linux_gui_vnc/Dockerfile
Normal file
96
docker/linux_gui_vnc/Dockerfile
Normal file
@@ -0,0 +1,96 @@
|
||||
# Define build image
|
||||
FROM ubuntu:latest AS build
|
||||
|
||||
# Install essential build dependencies
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update && apt upgrade -y \
|
||||
&& apt install --no-install-recommends -y \
|
||||
git \
|
||||
ca-certificates \
|
||||
qtquickcontrols2-5-dev \
|
||||
qtconnectivity5-dev \
|
||||
qtbase5-private-dev \
|
||||
qtpositioning5-dev \
|
||||
libqt5charts5-dev \
|
||||
libqt5networkauth5-dev \
|
||||
libqt5websockets5-dev \
|
||||
qml-module* \
|
||||
libqt5texttospeech5-dev \
|
||||
qtlocation5-dev \
|
||||
qtmultimedia5-dev \
|
||||
g++ \
|
||||
make \
|
||||
wget \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Define runtime image
|
||||
FROM ubuntu:latest AS runtime
|
||||
|
||||
# Install essential runtime dependencies
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update && apt upgrade -y \
|
||||
&& apt install --no-install-recommends -y \
|
||||
libqt5bluetooth5 \
|
||||
libqt5widgets5 \
|
||||
libqt5positioning5 \
|
||||
libqt5xml5 \
|
||||
libqt5charts5 \
|
||||
qt5-assistant \
|
||||
libqt5networkauth5 \
|
||||
libqt5websockets5 \
|
||||
qml-module* \
|
||||
libqt5texttospeech5 \
|
||||
libqt5location5-plugins \
|
||||
libqt5multimediawidgets5 \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5multimedia5 \
|
||||
qml-module-qtquick-controls2 \
|
||||
libqt5location5 \
|
||||
bluez \
|
||||
dbus \
|
||||
tigervnc-standalone-server \
|
||||
tigervnc-tools \
|
||||
libgl1-mesa-dri \
|
||||
xfonts-base \
|
||||
x11-xserver-utils \
|
||||
tigervnc-common \
|
||||
net-tools \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Stage 1: Build
|
||||
FROM build AS builder
|
||||
|
||||
# Clone the project and build it
|
||||
WORKDIR /usr/local/src
|
||||
RUN git clone --recursive https://github.com/cagnulein/qdomyos-zwift.git
|
||||
WORKDIR /usr/local/src/qdomyos-zwift
|
||||
RUN git submodule update --init src/smtpclient/ \
|
||||
&& git submodule update --init src/qmdnsengine/ \
|
||||
&& git submodule update --init tst/googletest/
|
||||
WORKDIR /usr/local/src/qdomyos-zwift/src
|
||||
RUN qmake qdomyos-zwift.pro \
|
||||
&& make -j4
|
||||
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM runtime
|
||||
|
||||
# Copy the built binary to /usr/local/bin
|
||||
COPY --from=builder /usr/local/src/qdomyos-zwift/src/qdomyos-zwift /usr/local/bin/qdomyos-zwift
|
||||
|
||||
# VNC configuration
|
||||
RUN mkdir -p ~/.vnc && \
|
||||
echo "securepassword" | vncpasswd -f > ~/.vnc/passwd && \
|
||||
chmod 600 ~/.vnc/passwd
|
||||
|
||||
# .Xauthority configuration
|
||||
RUN touch /root/.Xauthority
|
||||
ENV DISPLAY=:99
|
||||
|
||||
# Start VNC server with authentication
|
||||
CMD vncserver :99 -depth 24 -localhost no -xstartup qdomyos-zwift && \
|
||||
sleep infinity
|
||||
|
||||
2
docker/linux_gui_vnc/build.sh
Executable file
2
docker/linux_gui_vnc/build.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker build -t qdomyos-zwift-vnc .
|
||||
10
docker/linux_gui_vnc/docker-compose.yml
Normal file
10
docker/linux_gui_vnc/docker-compose.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
services:
|
||||
qdomyos-zwift-vnc:
|
||||
image: qdomyos-zwift-vnc
|
||||
container_name: qdomyos-zwift-vnc
|
||||
privileged: true # Required for Bluetooth functionality
|
||||
network_mode: "host" # Used to access host Bluetooth and D-Bus
|
||||
volumes:
|
||||
- /dev:/dev # Forward host devices (for Bluetooth)
|
||||
- /run/dbus:/run/dbus # Forward D-Bus for Bluetooth interaction
|
||||
restart: "no" # Do not restart the container automatically
|
||||
95
docker/linux_webgl/Dockerfile
Normal file
95
docker/linux_webgl/Dockerfile
Normal file
@@ -0,0 +1,95 @@
|
||||
# Define build image
|
||||
FROM ubuntu:latest AS build
|
||||
|
||||
# Install essential build dependencies
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update && apt upgrade -y \
|
||||
&& apt install --no-install-recommends -y \
|
||||
git \
|
||||
ca-certificates \
|
||||
qtquickcontrols2-5-dev \
|
||||
qtconnectivity5-dev \
|
||||
qtbase5-private-dev \
|
||||
qtpositioning5-dev \
|
||||
libqt5charts5-dev \
|
||||
libqt5networkauth5-dev \
|
||||
libqt5websockets5-dev \
|
||||
qml-module* \
|
||||
libqt5texttospeech5-dev \
|
||||
qtlocation5-dev \
|
||||
qtmultimedia5-dev \
|
||||
g++ \
|
||||
make \
|
||||
wget \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Define runtime image
|
||||
FROM ubuntu:latest AS runtime
|
||||
|
||||
# Install essential runtime dependencies
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update && apt upgrade -y \
|
||||
&& apt install --no-install-recommends -y \
|
||||
libqt5bluetooth5 \
|
||||
libqt5widgets5 \
|
||||
libqt5positioning5 \
|
||||
libqt5xml5 \
|
||||
libqt5charts5 \
|
||||
qt5-assistant \
|
||||
libqt5networkauth5 \
|
||||
libqt5websockets5 \
|
||||
qml-module* \
|
||||
libqt5texttospeech5 \
|
||||
libqt5location5-plugins \
|
||||
libqt5multimediawidgets5 \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5multimedia5 \
|
||||
qml-module-qtquick-controls2 \
|
||||
libqt5location5 \
|
||||
bluez \
|
||||
dbus \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Stage 1: Build
|
||||
FROM build AS builder
|
||||
|
||||
# Define variables for Qt versions
|
||||
ARG QT_VERSION=5.15
|
||||
ARG QT_SUBVERSION=5.15.13
|
||||
ARG QT_WEBPLUGIN_NAME=qtwebglplugin-everywhere-opensource-src
|
||||
|
||||
# Build WebGL plugin
|
||||
WORKDIR /usr/local/src
|
||||
RUN wget https://download.qt.io/official_releases/qt/${QT_VERSION}/${QT_SUBVERSION}/submodules/${QT_WEBPLUGIN_NAME}-${QT_SUBVERSION}.zip \
|
||||
&& unzip ${QT_WEBPLUGIN_NAME}-${QT_SUBVERSION}.zip \
|
||||
&& mv *-${QT_SUBVERSION} qtwebglplugin-everywhere \
|
||||
&& cd qtwebglplugin-everywhere \
|
||||
&& qmake \
|
||||
&& make
|
||||
|
||||
# Clone the project and build it
|
||||
WORKDIR /usr/local/src
|
||||
RUN git clone --recursive https://github.com/cagnulein/qdomyos-zwift.git
|
||||
WORKDIR /usr/local/src/qdomyos-zwift
|
||||
RUN git submodule update --init src/smtpclient/ \
|
||||
&& git submodule update --init src/qmdnsengine/ \
|
||||
&& git submodule update --init tst/googletest/
|
||||
WORKDIR /usr/local/src/qdomyos-zwift/src
|
||||
RUN qmake qdomyos-zwift.pro \
|
||||
&& make -j4
|
||||
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM runtime
|
||||
|
||||
# Copy the built binary to /usr/local/bin
|
||||
COPY --from=builder /usr/local/src/qdomyos-zwift/src/qdomyos-zwift /usr/local/bin/qdomyos-zwift
|
||||
|
||||
# Copy WebGL plugin to the appropriate location
|
||||
COPY --from=builder /usr/local/src/qtwebglplugin-everywhere/plugins/platforms/libqwebgl.so /usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqwebgl.so
|
||||
|
||||
# Set the default command to run the application with WebGL
|
||||
CMD ["qdomyos-zwift", "-qml", "-platform", "webgl:port=8080"]
|
||||
2
docker/linux_webgl/build.sh
Executable file
2
docker/linux_webgl/build.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker build -t qdomyos-zwift-webgl .
|
||||
19
docker/linux_webgl/docker-compose.yml
Normal file
19
docker/linux_webgl/docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
qdomyos-zwift-webgl:
|
||||
image: qdomyos-zwift-webgl
|
||||
container_name: qdomyos-zwift-webgl
|
||||
privileged: true
|
||||
network_mode: "host"
|
||||
environment:
|
||||
- DISPLAY=${DISPLAY}
|
||||
volumes:
|
||||
- /dev:/dev
|
||||
- /run/dbus:/run/dbus
|
||||
- ./.config:/root/.config
|
||||
- /tmp/.X11-unix:/tmp/.X11-unix
|
||||
stdin_open: true
|
||||
tty: true
|
||||
restart: "no"
|
||||
# command: qdomyos-zwift -qml -platform webgl:port=8080
|
||||
# command: ["qdomyos-zwift", "-no-gui"]
|
||||
|
||||
@@ -10,7 +10,7 @@ These instructions build the app itself, not the test project.
|
||||
|
||||
```buildoutcfg
|
||||
$ sudo apt update && sudo apt upgrade # this is very important on raspberry pi: you need the bluetooth firmware updated!
|
||||
$ sudo apt install git qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev qml-module* libqt5texttospeech5-dev libqt5texttospeech5 libqt5location5-plugins qtlocation5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 g++ make
|
||||
$ sudo apt install git qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtbase5-private-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev qml-module* libqt5texttospeech5-dev libqt5texttospeech5 libqt5location5-plugins qtlocation5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 g++ make
|
||||
$ git clone https://github.com/cagnulein/qdomyos-zwift.git
|
||||
$ cd qdomyos-zwift
|
||||
$ git submodule update --init src/smtpclient/
|
||||
@@ -106,7 +106,7 @@ This operation takes a moment to complete.
|
||||
#### Install qdomyos-zwift from sources
|
||||
|
||||
```bash
|
||||
sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 qtlocation5-dev qtquickcontrols2-5-dev libqt5texttospeech5-dev libqt5texttospeech5 g++ make
|
||||
sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtbase5-private-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 qtlocation5-dev qtquickcontrols2-5-dev libqt5texttospeech5-dev libqt5texttospeech5 g++ make
|
||||
git clone https://github.com/cagnulein/qdomyos-zwift.git
|
||||
cd qdomyos-zwift
|
||||
git submodule update --init src/smtpclient/
|
||||
@@ -117,6 +117,11 @@ qmake qdomyos-zwift.pro
|
||||
make
|
||||
```
|
||||
|
||||
If you need GUI also do a
|
||||
```
|
||||
apt install qml-module*
|
||||
```
|
||||
|
||||
Please note :
|
||||
- Don't build the application with `-j4` option (this will fail)
|
||||
- Build operation is circa 45 minutes (subsequent builds are faster)
|
||||
@@ -172,6 +177,151 @@ If everything is working as expected, **enable your service at boot time** :
|
||||
|
||||
Then reboot to check operations (`sudo reboot`)
|
||||
|
||||
### (optional) Treadmill Auto-Detection and Service Management
|
||||
This section provides a reliable way to manage the QZ service based on the treadmill's power state. Using a `bluetoothctl`-based Bash script, this solution ensures the QZ service starts when the treadmill is detected and stops when it is not.
|
||||
|
||||
- **Bluetooth Discovery**: Monitors treadmill availability via `bluetoothctl`.
|
||||
- **Service Control**: Automatically starts and stops the QZ service.
|
||||
- **Logging**: Tracks treadmill status and actions in a log file.
|
||||
|
||||
**Notes:**
|
||||
- Ensure `bluetoothctl` is installed and working on your system.
|
||||
- Replace `I_TL` in the script with your treadmill's Bluetooth name. You can find your device name via `bluetoothctl scan on`
|
||||
- Adjust the sleep interval (`sleep 30`) in the script as needed for your use case.
|
||||
|
||||
Step 1: Save the following script as `/root/qz-treadmill-monitor.sh`:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
LOG_FILE="/var/log/qz-treadmill-monitor.log"
|
||||
TARGET_DEVICE="I_TL"
|
||||
SCAN_INTERVAL=30 # Time in seconds between checks
|
||||
SERVICE_NAME="qz"
|
||||
DEBUG_LOG_DIR="/var/log" # Directory where QZ debug logs are stored
|
||||
ERROR_MESSAGE="BTLE stateChanged InvalidService"
|
||||
|
||||
log() {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
is_service_running() {
|
||||
systemctl is-active --quiet "$SERVICE_NAME"
|
||||
return $?
|
||||
}
|
||||
|
||||
scan_for_device() {
|
||||
log "Starting Bluetooth scan for $TARGET_DEVICE..."
|
||||
|
||||
# Run bluetoothctl scan in the background and capture output
|
||||
bluetoothctl scan on &>/dev/null &
|
||||
SCAN_PID=$!
|
||||
|
||||
# Allow some time for devices to appear
|
||||
sleep 5
|
||||
|
||||
# Check if the target device appears in the list
|
||||
bluetoothctl devices | grep -q "$TARGET_DEVICE"
|
||||
DEVICE_FOUND=$?
|
||||
|
||||
# Stop scanning
|
||||
kill "$SCAN_PID"
|
||||
bluetoothctl scan off &>/dev/null
|
||||
|
||||
if [ $DEVICE_FOUND -eq 0 ]; then
|
||||
log "Device '$TARGET_DEVICE' found."
|
||||
return 0
|
||||
else
|
||||
log "Device '$TARGET_DEVICE' not found."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_qz_on_error() {
|
||||
# Get the current date
|
||||
CURRENT_DATE=$(date '+%a_%b_%d')
|
||||
|
||||
# Find the latest QZ debug log file for today
|
||||
LATEST_LOG=$(ls -t "$DEBUG_LOG_DIR"/debug-"$CURRENT_DATE"_*.log 2>/dev/null | head -n 1)
|
||||
|
||||
if [ -z "$LATEST_LOG" ]; then
|
||||
log "No QZ debug log found for today."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Checking latest log file: $LATEST_LOG for errors..."
|
||||
|
||||
# Search the latest log for the error message
|
||||
if grep -q "$ERROR_MESSAGE" "$LATEST_LOG"; then
|
||||
log "***** Error detected in QZ log: $ERROR_MESSAGE *****"
|
||||
log "Restarting QZ service..."
|
||||
systemctl restart "$SERVICE_NAME"
|
||||
else
|
||||
log "No errors detected in $LATEST_LOG."
|
||||
fi
|
||||
}
|
||||
|
||||
manage_service() {
|
||||
local device_found=$1
|
||||
if $device_found; then
|
||||
if ! is_service_running; then
|
||||
log "***** Starting QZ service... *****"
|
||||
systemctl start "$SERVICE_NAME"
|
||||
else
|
||||
log "QZ service is already running."
|
||||
restart_qz_on_error # Check the log for errors when QZ is already running
|
||||
fi
|
||||
else
|
||||
if is_service_running; then
|
||||
log "***** Stopping QZ service... *****"
|
||||
systemctl stop "$SERVICE_NAME"
|
||||
else
|
||||
log "QZ service is already stopped."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
log "Checking for treadmill status..."
|
||||
if scan_for_device; then
|
||||
manage_service true
|
||||
else
|
||||
manage_service false
|
||||
fi
|
||||
log "Waiting for $SCAN_INTERVAL seconds before next check..."
|
||||
sleep "$SCAN_INTERVAL"
|
||||
done
|
||||
```
|
||||
|
||||
Step2: To ensure the script runs continuously, create a systemd service file at `/etc/systemd/system/qz-treadmill-monitor.service`
|
||||
```bash
|
||||
[Unit]
|
||||
Description=QZ Treadmill Monitor Service
|
||||
After=bluetooth.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/root/qz-treadmill-monitor.sh
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Step 3: Enable and Start the Service
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable qz-treadmill-monitor
|
||||
sudo systemctl start qz-treadmill-monitor
|
||||
```
|
||||
|
||||
Monitor logs are written to `/var/log/qz-treadmill-monitor.log`. Use the following command to check logs in real-time:
|
||||
```bash
|
||||
sudo tail -f /var/log/qz-treadmill-monitor.log
|
||||
```
|
||||
|
||||
|
||||
|
||||
### (optional) Enable overlay FS
|
||||
|
||||
|
||||
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])
|
||||
37
qdomyos-zwift.code-workspace
Normal file
37
qdomyos-zwift.code-workspace
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"list": "cpp",
|
||||
"chrono": "cpp",
|
||||
"complex": "cpp",
|
||||
"functional": "cpp",
|
||||
"optional": "cpp",
|
||||
"system_error": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"qhttpserver": "cpp",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"map": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"xstring": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"xutility": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"bitset": "cpp",
|
||||
"iterator": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xtree": "cpp",
|
||||
"ostream": "cpp",
|
||||
"locale": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,59 +10,87 @@ ColumnLayout {
|
||||
property alias textFont: accordionText.font.family
|
||||
property alias textFontSize: accordionText.font.pixelSize
|
||||
property alias indicatRectColor: indicatRect.color
|
||||
default property alias accordionContent: contentPlaceholder.data
|
||||
spacing: 0
|
||||
default property alias accordionContent: contentLoader.sourceComponent
|
||||
|
||||
Layout.fillWidth: true;
|
||||
// Signal emitted when content becomes visible
|
||||
signal contentBecameVisible()
|
||||
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
id: accordionHeader
|
||||
color: "red"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true;
|
||||
Layout.fillWidth: true
|
||||
height: 48
|
||||
|
||||
Rectangle{
|
||||
id:indicatRect
|
||||
x: 16; y: 20
|
||||
width: 8; height: 8
|
||||
radius: 8
|
||||
color: "white"
|
||||
Rectangle {
|
||||
id: indicatRect
|
||||
x: 16; y: 20
|
||||
width: 8; height: 8
|
||||
radius: 8
|
||||
color: "white"
|
||||
}
|
||||
|
||||
Text {
|
||||
id: accordionText
|
||||
x:34;y:13
|
||||
x: 34; y: 13
|
||||
color: "#FFFFFF"
|
||||
text: rootElement.title
|
||||
}
|
||||
|
||||
Image {
|
||||
y:13
|
||||
anchors.right: parent.right
|
||||
y: 13
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 20
|
||||
width: 30; height: 30
|
||||
id: indicatImg
|
||||
source: "qrc:/icons/arrow-collapse-vertical.png"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
rootElement.isOpen = !rootElement.isOpen
|
||||
if(rootElement.isOpen)
|
||||
{
|
||||
if(rootElement.isOpen) {
|
||||
indicatImg.source = "qrc:/icons/arrow-expand-vertical.png"
|
||||
}else{
|
||||
} else {
|
||||
indicatImg.source = "qrc:/icons/arrow-collapse-vertical.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will get filled with the content
|
||||
ColumnLayout {
|
||||
id: contentPlaceholder
|
||||
visible: rootElement.isOpen
|
||||
Layout.fillWidth: true;
|
||||
// Loader with enhanced visibility handling
|
||||
Loader {
|
||||
id: contentLoader
|
||||
active: rootElement.isOpen
|
||||
visible: false // Start invisible
|
||||
Layout.fillWidth: true
|
||||
asynchronous: false
|
||||
|
||||
onLoaded: {
|
||||
if (item) {
|
||||
item.Layout.fillWidth = true
|
||||
visible = true
|
||||
rootElement.contentBecameVisible()
|
||||
}
|
||||
}
|
||||
|
||||
// Handle visibility changes
|
||||
onVisibleChanged: {
|
||||
if (visible && status === Loader.Ready) {
|
||||
rootElement.contentBecameVisible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle accordion closing
|
||||
onIsOpenChanged: {
|
||||
if (!isOpen) {
|
||||
contentLoader.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef CRC16IBM_H
|
||||
#define CRC16IBM_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
|
||||
class CRC16IBM {
|
||||
public:
|
||||
|
||||
// Function to calculate CRC-16 (XMODEM) checksum
|
||||
static quint16 calculateCRC(const QByteArray &data) {
|
||||
quint16 crc = 0xFFFF; // Initial value
|
||||
|
||||
for (char byte : data) {
|
||||
quint8 index = (crc >> 8) ^ static_cast<quint8>(byte);
|
||||
crc = (crc << 8) ^ crc16Table[index];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
private:
|
||||
// Precomputed CRC-16-IBM table
|
||||
static constexpr quint16 crc16Table[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
||||
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
||||
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
||||
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
||||
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
||||
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
||||
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
||||
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
||||
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
||||
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
||||
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
||||
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
||||
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
||||
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
||||
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
||||
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
||||
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
||||
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
||||
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
||||
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif // CRC16IBM_H
|
||||
24
src/Home.qml
24
src/Home.qml
@@ -33,6 +33,7 @@ HomeForm {
|
||||
property bool theme_tile_shadow_enabled: true
|
||||
property string theme_tile_shadow_color: "#9C27B0"
|
||||
property int theme_tile_secondline_textsize: 12
|
||||
property bool skipLocationServicesDialog: false
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
@@ -86,14 +87,31 @@ HomeForm {
|
||||
onTriggered: {if(rootItem.stopRequested) {rootItem.stopRequested = false; inner_stop(); }}
|
||||
}
|
||||
|
||||
property var locationServiceRequsted: false
|
||||
property bool locationServiceRequsted: false
|
||||
|
||||
MessageDialog {
|
||||
id: locationServicesDialog
|
||||
text: "Permissions Required"
|
||||
informativeText: "QZ requires both Bluetooth and Location Services to be enabled.\nLocation Services are necessary on Android to allow the app to find Bluetooth devices.\nThe GPS will not be used.\n\nWould you like to enable them?"
|
||||
buttons: (MessageDialog.Yes | MessageDialog.No)
|
||||
onYesClicked: {locationServiceRequsted = true; rootItem.enableLocationServices()}
|
||||
visible: !rootItem.locationServices() && !locationServiceRequsted
|
||||
onYesClicked: {
|
||||
locationServiceRequsted = true
|
||||
rootItem.enableLocationServices()
|
||||
}
|
||||
onNoClicked: remindLocationServicesDialog.visible = true
|
||||
visible: !rootItem.locationServices() && !locationServiceRequsted && !settings.skipLocationServicesDialog
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: remindLocationServicesDialog
|
||||
text: "Reminder Preference"
|
||||
informativeText: "Would you like to be reminded about enabling Location Services next time?"
|
||||
buttons: (MessageDialog.Yes | MessageDialog.No)
|
||||
onYesClicked: settings.skipLocationServicesDialog = false
|
||||
onNoClicked: settings.skipLocationServicesDialog = true
|
||||
visible: false
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
text: "Restart the app"
|
||||
informativeText: "To apply the changes, you need to restart the app.\nWould you like to do that now?"
|
||||
|
||||
20
src/IndicatorOnlySwitch.qml
Normal file
20
src/IndicatorOnlySwitch.qml
Normal file
@@ -0,0 +1,20 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.0
|
||||
import Qt.labs.settings 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
SwitchDelegate {
|
||||
id: root
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (mouse.x > parent.width - parent.indicator.width) {
|
||||
root.checked = !root.checked
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/OAuth2.h
Normal file
26
src/OAuth2.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef OAUTH2_H
|
||||
#define OAUTH2_H
|
||||
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
struct OAuth2Parameter {
|
||||
QString responseType = QStringLiteral("code");
|
||||
QString approval_prompt = QStringLiteral("force");
|
||||
|
||||
inline bool isEmpty() const { return responseType.isEmpty() && approval_prompt.isEmpty(); }
|
||||
|
||||
QString toString() const {
|
||||
QString msg;
|
||||
QTextStream out(&msg);
|
||||
out << QStringLiteral("OAuth2Parameter{\n") << QStringLiteral("responseType: ") << this->responseType
|
||||
<< QStringLiteral("\n") << QStringLiteral("approval_prompt: ") << this->approval_prompt
|
||||
<< QStringLiteral("\n");
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
#define _STR(x) #x
|
||||
#define STRINGIFY(x) _STR(x)
|
||||
|
||||
#endif // OAUTH2_H
|
||||
@@ -22,7 +22,7 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
AccordionElement {
|
||||
StaticAccordionElement {
|
||||
title: qsTr("Settings folder")
|
||||
indicatRectColor: Material.color(Material.Grey)
|
||||
textColor: Material.color(Material.Grey)
|
||||
|
||||
68
src/StaticAccordionElement.qml
Normal file
68
src/StaticAccordionElement.qml
Normal file
@@ -0,0 +1,68 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
ColumnLayout {
|
||||
id: rootElement
|
||||
property bool isOpen: false
|
||||
property string title: ""
|
||||
property alias color: accordionHeader.color
|
||||
property alias textColor: accordionText.color
|
||||
property alias textFont: accordionText.font.family
|
||||
property alias textFontSize: accordionText.font.pixelSize
|
||||
property alias indicatRectColor: indicatRect.color
|
||||
default property alias accordionContent: contentPlaceholder.data
|
||||
spacing: 0
|
||||
|
||||
Layout.fillWidth: true;
|
||||
|
||||
Rectangle {
|
||||
id: accordionHeader
|
||||
color: "red"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true;
|
||||
height: 48
|
||||
|
||||
Rectangle{
|
||||
id:indicatRect
|
||||
x: 16; y: 20
|
||||
width: 8; height: 8
|
||||
radius: 8
|
||||
color: "white"
|
||||
}
|
||||
|
||||
Text {
|
||||
id: accordionText
|
||||
x:34;y:13
|
||||
color: "#FFFFFF"
|
||||
text: rootElement.title
|
||||
}
|
||||
Image {
|
||||
y:13
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 20
|
||||
width: 30; height: 30
|
||||
id: indicatImg
|
||||
source: "qrc:/icons/arrow-collapse-vertical.png"
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
rootElement.isOpen = !rootElement.isOpen
|
||||
if(rootElement.isOpen)
|
||||
{
|
||||
indicatImg.source = "qrc:/icons/arrow-expand-vertical.png"
|
||||
}else{
|
||||
indicatImg.source = "qrc:/icons/arrow-collapse-vertical.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will get filled with the content
|
||||
ColumnLayout {
|
||||
id: contentPlaceholder
|
||||
visible: rootElement.isOpen
|
||||
Layout.fillWidth: true;
|
||||
}
|
||||
}
|
||||
80
src/WebPelotonAuth.qml
Normal file
80
src/WebPelotonAuth.qml
Normal file
@@ -0,0 +1,80 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Controls.Material 2.12
|
||||
import QtQuick.Dialogs 1.0
|
||||
import QtGraphicalEffects 1.12
|
||||
import Qt.labs.settings 1.0
|
||||
import QtMultimedia 5.15
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtWebView 1.1
|
||||
|
||||
Item {
|
||||
id: pelotonAuthPage
|
||||
anchors.fill: parent
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
visible: true
|
||||
|
||||
// Signal to notify the parent stack when we want to go back
|
||||
signal goBack()
|
||||
|
||||
WebView {
|
||||
anchors.fill: parent
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
visible: !rootItem.pelotonPopupVisible
|
||||
url: rootItem.getPelotonAuthUrl
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: popupPelotonConnectedWeb
|
||||
parent: Overlay.overlay
|
||||
enabled: rootItem.pelotonPopupVisible
|
||||
onEnabledChanged: { if(rootItem.pelotonPopupVisible) popupPelotonConnectedWeb.open() }
|
||||
onClosed: {
|
||||
rootItem.pelotonPopupVisible = false;
|
||||
// Attempt to go back to the previous view after the popup is closed
|
||||
goBack();
|
||||
}
|
||||
|
||||
x: Math.round((parent.width - width) / 2)
|
||||
y: Math.round((parent.height - height) / 2)
|
||||
width: 380
|
||||
height: 120
|
||||
modal: true
|
||||
focus: true
|
||||
palette.text: "white"
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
enter: Transition
|
||||
{
|
||||
NumberAnimation { property: "opacity"; from: 0.0; to: 1.0 }
|
||||
}
|
||||
exit: Transition
|
||||
{
|
||||
NumberAnimation { property: "opacity"; from: 1.0; to: 0.0 }
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 370
|
||||
height: 120
|
||||
text: qsTr("Your Peloton account is now connected!<br><br>Restart the app to apply this!")
|
||||
}
|
||||
}
|
||||
|
||||
// Add a MouseArea to capture clicks anywhere on the popup
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popupPelotonConnectedWeb.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Component is being completed
|
||||
Component.onCompleted: {
|
||||
console.log("WebPelotonAuth loaded")
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import Qt.labs.settings 1.0
|
||||
|
||||
Page {
|
||||
id: wizardPage
|
||||
objectName: "wizardPage"
|
||||
|
||||
property int currentStep: 0
|
||||
property var selectedOptions: ({})
|
||||
@@ -335,7 +336,7 @@ Page {
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Peloton Login")
|
||||
text: qsTr("Connect to Peloton")
|
||||
font.pixelSize: 24
|
||||
font.bold: true
|
||||
color: "white"
|
||||
@@ -343,56 +344,38 @@ Page {
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Username")
|
||||
text: qsTr("Click the button below to connect your Peloton account")
|
||||
font.pixelSize: 20
|
||||
font.bold: true
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
width: stackViewLocal.width * 0.8
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: "white"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: pelotonUsernameTextField
|
||||
text: settings.peloton_username
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillHeight: false
|
||||
onAccepted: settings.peloton_username = text
|
||||
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
|
||||
}
|
||||
source: "icons/icons/Button_Connect_Rect_DarkMode.png"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
width: parent.width * 0.8
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Password")
|
||||
font.pixelSize: 20
|
||||
font.bold: true
|
||||
color: "white"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: pelotonPasswordTextField
|
||||
text: settings.peloton_password
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.fillHeight: false
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
inputMethodHints: Qt.ImhHiddenText
|
||||
echoMode: TextInput.PasswordEchoOnEdit
|
||||
onAccepted: settings.peloton_password = text
|
||||
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
stackViewLocal.push("WebPelotonAuth.qml")
|
||||
stackViewLocal.currentItem.goBack.connect(function() {
|
||||
stackViewLocal.pop();
|
||||
stackViewLocal.push(pelotonDifficultyComponent)
|
||||
})
|
||||
peloton_connect_clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: 50
|
||||
}
|
||||
|
||||
WizardButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
settings.peloton_username = pelotonUsernameTextField.text;
|
||||
settings.peloton_password = pelotonPasswordTextField.text;
|
||||
stackViewLocal.push(pelotonDifficultyComponent)
|
||||
}
|
||||
}
|
||||
|
||||
WizardButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Back")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.18.1" android:versionCode="908" android:installLocation="auto">
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.18.24" android:versionCode="1061" android:installLocation="auto">
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
|
||||
@@ -31,7 +31,7 @@ dependencies {
|
||||
implementation "androidx.core:core-ktx:1.12.0"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0"
|
||||
implementation 'com.google.protobuf:protobuf-javalite:3.25.1'
|
||||
|
||||
|
||||
if(amazon == "1") {
|
||||
// amazon app store
|
||||
implementation 'com.google.mlkit:text-recognition:16.0.0-beta6'
|
||||
@@ -50,7 +50,7 @@ dependencies {
|
||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'com.github.mik3y:usb-serial-for-android:v3.4.6'
|
||||
implementation files('libs/usb-serial-for-android-3.8.1.aar')
|
||||
androidTestImplementation "com.android.support:support-annotations:28.0.0"
|
||||
implementation 'com.google.android.gms:play-services-wearable:+'
|
||||
|
||||
|
||||
BIN
src/android/libs/android-antplus-plugin-lib-release_3.9.0.aar
Normal file
BIN
src/android/libs/android-antplus-plugin-lib-release_3.9.0.aar
Normal file
Binary file not shown.
BIN
src/android/libs/usb-serial-for-android-3.8.1.aar
Normal file
BIN
src/android/libs/usb-serial-for-android-3.8.1.aar
Normal file
Binary file not shown.
@@ -31,7 +31,7 @@ public class Ant {
|
||||
private ChannelService.ChannelServiceComm mChannelService = null;
|
||||
private boolean mChannelServiceBound = false;
|
||||
private final String TAG = "Ant";
|
||||
private Activity activity = null;
|
||||
public static Activity activity = null;
|
||||
static boolean speedRequest = false;
|
||||
static boolean heartRequest = false;
|
||||
static boolean garminKey = false;
|
||||
|
||||
@@ -155,7 +155,7 @@ public class ChannelService extends Service {
|
||||
|
||||
public void openAllChannels() throws ChannelNotAvailableException {
|
||||
if (Ant.heartRequest && heartChannelController == null)
|
||||
heartChannelController = new HeartChannelController(acquireChannel());
|
||||
heartChannelController = new HeartChannelController();
|
||||
|
||||
if (Ant.speedRequest) {
|
||||
if(Ant.treadmill && sdmChannelController == null) {
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012 Dynastream Innovations Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.app.Activity;
|
||||
|
||||
// ANT+ Plugin imports
|
||||
import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc;
|
||||
import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.DataState;
|
||||
import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.IHeartRateDataReceiver;
|
||||
import com.dsi.ant.plugins.antplus.pcc.defines.DeviceState;
|
||||
import com.dsi.ant.plugins.antplus.pcc.defines.EventFlag;
|
||||
import com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult;
|
||||
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.IDeviceStateChangeReceiver;
|
||||
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.IPluginAccessResultReceiver;
|
||||
import com.dsi.ant.plugins.antplus.pccbase.PccReleaseHandle;
|
||||
|
||||
// Basic ANT imports for legacy support
|
||||
import com.dsi.ant.channel.AntChannel;
|
||||
import com.dsi.ant.channel.AntCommandFailedException;
|
||||
import com.dsi.ant.channel.IAntChannelEventHandler;
|
||||
@@ -30,220 +28,103 @@ import com.dsi.ant.message.fromant.ChannelEventMessage;
|
||||
import com.dsi.ant.message.fromant.MessageFromAntType;
|
||||
import com.dsi.ant.message.ipc.AntMessageParcel;
|
||||
|
||||
// Java imports
|
||||
import java.math.BigDecimal;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Random;
|
||||
|
||||
public class HeartChannelController {
|
||||
// The device type and transmission type to be part of the channel ID message
|
||||
private static final int CHANNEL_HEART_DEVICE_TYPE = 0x78;
|
||||
private static final int CHANNEL_HEART_TRANSMISSION_TYPE = 1;
|
||||
private static final String TAG = HeartChannelController.class.getSimpleName();
|
||||
|
||||
private Context context;
|
||||
private AntPlusHeartRatePcc hrPcc = null;
|
||||
private PccReleaseHandle<AntPlusHeartRatePcc> releaseHandle = null;
|
||||
private boolean isConnected = false;
|
||||
public int heart = 0; // Public to be accessible from ChannelService
|
||||
|
||||
// The period and frequency values the channel will be configured to
|
||||
private static final int CHANNEL_HEART_PERIOD = 8118; // 1 Hz
|
||||
private static final int CHANNEL_HEART_FREQUENCY = 57;
|
||||
public HeartChannelController() {
|
||||
this.context = Ant.activity;
|
||||
openChannel();
|
||||
}
|
||||
|
||||
private static final String TAG = HeartChannelController.class.getSimpleName();
|
||||
public boolean openChannel() {
|
||||
// Request access to first available heart rate device
|
||||
releaseHandle = AntPlusHeartRatePcc.requestAccess((Activity)context, 0, 0, // 0 means first available device
|
||||
new IPluginAccessResultReceiver<AntPlusHeartRatePcc>() {
|
||||
@Override
|
||||
public void onResultReceived(AntPlusHeartRatePcc result, RequestAccessResult resultCode, DeviceState initialDeviceState) {
|
||||
switch(resultCode) {
|
||||
case SUCCESS:
|
||||
hrPcc = result;
|
||||
isConnected = true;
|
||||
Log.d(TAG, "Connected to heart rate monitor: " + result.getDeviceName());
|
||||
subscribeToHrEvents();
|
||||
break;
|
||||
case CHANNEL_NOT_AVAILABLE:
|
||||
Log.e(TAG, "Channel Not Available");
|
||||
break;
|
||||
case ADAPTER_NOT_DETECTED:
|
||||
Log.e(TAG, "ANT Adapter Not Available");
|
||||
break;
|
||||
case BAD_PARAMS:
|
||||
Log.e(TAG, "Bad request parameters");
|
||||
break;
|
||||
case OTHER_FAILURE:
|
||||
Log.e(TAG, "RequestAccess failed");
|
||||
break;
|
||||
case DEPENDENCY_NOT_INSTALLED:
|
||||
Log.e(TAG, "Dependency not installed");
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unrecognized result: " + resultCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
new IDeviceStateChangeReceiver() {
|
||||
@Override
|
||||
public void onDeviceStateChange(DeviceState newDeviceState) {
|
||||
Log.d(TAG, "Device State Changed to: " + newDeviceState);
|
||||
if (newDeviceState == DeviceState.DEAD) {
|
||||
isConnected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private static Random randGen = new Random();
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
private AntChannel mAntChannel;
|
||||
private void subscribeToHrEvents() {
|
||||
if (hrPcc != null) {
|
||||
hrPcc.subscribeHeartRateDataEvent(new IHeartRateDataReceiver() {
|
||||
@Override
|
||||
public void onNewHeartRateData(long estTimestamp, EnumSet<EventFlag> eventFlags,
|
||||
int computedHeartRate, long heartBeatCount,
|
||||
BigDecimal heartBeatEventTime, DataState dataState) {
|
||||
|
||||
heart = computedHeartRate;
|
||||
Log.d(TAG, "Heart Rate: " + heart);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private ChannelEventCallback mChannelEventCallback = new ChannelEventCallback();
|
||||
public void close() {
|
||||
if (releaseHandle != null) {
|
||||
releaseHandle.close();
|
||||
releaseHandle = null;
|
||||
}
|
||||
hrPcc = null;
|
||||
isConnected = false;
|
||||
Log.d(TAG, "Channel Closed");
|
||||
}
|
||||
|
||||
public int getHeartRate() {
|
||||
return heart;
|
||||
}
|
||||
|
||||
private boolean mIsOpen;
|
||||
int heart = 0;
|
||||
|
||||
public HeartChannelController(AntChannel antChannel) {
|
||||
mAntChannel = antChannel;
|
||||
openChannel();
|
||||
}
|
||||
|
||||
boolean openChannel() {
|
||||
if (null != mAntChannel) {
|
||||
if (mIsOpen) {
|
||||
Log.w(TAG, "Channel was already open");
|
||||
} else {
|
||||
// Channel ID message contains device number, type and transmission type. In
|
||||
// order for master (TX) channels and slave (RX) channels to connect, they
|
||||
// must have the same channel ID, or wildcard (0) is used.
|
||||
ChannelId channelId = new ChannelId(0,
|
||||
CHANNEL_HEART_DEVICE_TYPE, CHANNEL_HEART_TRANSMISSION_TYPE);
|
||||
|
||||
try {
|
||||
// Setting the channel event handler so that we can receive messages from ANT
|
||||
mAntChannel.setChannelEventHandler(mChannelEventCallback);
|
||||
|
||||
// Performs channel assignment by assigning the type to the channel. Additional
|
||||
// features (such as, background scanning and frequency agility) can be enabled
|
||||
// by passing an ExtendedAssignment object to assign(ChannelType, ExtendedAssignment).
|
||||
mAntChannel.assign(ChannelType.SLAVE_RECEIVE_ONLY);
|
||||
|
||||
/*
|
||||
* Configures the channel ID, messaging period and rf frequency after assigning,
|
||||
* then opening the channel.
|
||||
*
|
||||
* For any additional ANT features such as proximity search or background scanning, refer to
|
||||
* the ANT Protocol Doc found at:
|
||||
* http://www.thisisant.com/resources/ant-message-protocol-and-usage/
|
||||
*/
|
||||
mAntChannel.setChannelId(channelId);
|
||||
mAntChannel.setPeriod(CHANNEL_HEART_PERIOD);
|
||||
mAntChannel.setRfFrequency(CHANNEL_HEART_FREQUENCY);
|
||||
mAntChannel.open();
|
||||
mIsOpen = true;
|
||||
|
||||
Log.d(TAG, "Opened channel with device number");
|
||||
} catch (RemoteException e) {
|
||||
channelError(e);
|
||||
} catch (AntCommandFailedException e) {
|
||||
// This will release, and therefore unassign if required
|
||||
channelError("Open failed", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "No channel available");
|
||||
}
|
||||
|
||||
return mIsOpen;
|
||||
}
|
||||
|
||||
void channelError(RemoteException e) {
|
||||
String logString = "Remote service communication failed.";
|
||||
|
||||
Log.e(TAG, logString);
|
||||
}
|
||||
|
||||
void channelError(String error, AntCommandFailedException e) {
|
||||
StringBuilder logString;
|
||||
|
||||
if (e.getResponseMessage() != null) {
|
||||
String initiatingMessageId = "0x" + Integer.toHexString(
|
||||
e.getResponseMessage().getInitiatingMessageId());
|
||||
String rawResponseCode = "0x" + Integer.toHexString(
|
||||
e.getResponseMessage().getRawResponseCode());
|
||||
|
||||
logString = new StringBuilder(error)
|
||||
.append(". Command ")
|
||||
.append(initiatingMessageId)
|
||||
.append(" failed with code ")
|
||||
.append(rawResponseCode);
|
||||
} else {
|
||||
String attemptedMessageId = "0x" + Integer.toHexString(
|
||||
e.getAttemptedMessageType().getMessageId());
|
||||
String failureReason = e.getFailureReason().toString();
|
||||
|
||||
logString = new StringBuilder(error)
|
||||
.append(". Command ")
|
||||
.append(attemptedMessageId)
|
||||
.append(" failed with reason ")
|
||||
.append(failureReason);
|
||||
}
|
||||
|
||||
Log.e(TAG, logString.toString());
|
||||
|
||||
mAntChannel.release();
|
||||
|
||||
Log.e(TAG, "ANT Command Failed");
|
||||
}
|
||||
|
||||
public void close() {
|
||||
// TODO kill all our resources
|
||||
if (null != mAntChannel) {
|
||||
mIsOpen = false;
|
||||
|
||||
// Releasing the channel to make it available for others.
|
||||
// After releasing, the AntChannel instance cannot be reused.
|
||||
mAntChannel.release();
|
||||
mAntChannel = null;
|
||||
}
|
||||
|
||||
Log.e(TAG, "Channel Closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the Channel Event Handler Interface so that messages can be
|
||||
* received and channel death events can be handled.
|
||||
*/
|
||||
public class ChannelEventCallback implements IAntChannelEventHandler {
|
||||
int revCounts = 0;
|
||||
int ucMessageCount = 0;
|
||||
byte ucPageChange = 0;
|
||||
byte ucExtMesgType = 1;
|
||||
long lastTime = 0;
|
||||
double way;
|
||||
int rev;
|
||||
double remWay;
|
||||
double wheel = 0.1;
|
||||
|
||||
@Override
|
||||
public void onChannelDeath() {
|
||||
// Display channel death message when channel dies
|
||||
Log.e(TAG, "Channel Death");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveMessage(MessageFromAntType messageType, AntMessageParcel antParcel) {
|
||||
Log.d(TAG, "Rx: " + antParcel);
|
||||
Log.d(TAG, "Message Type: " + messageType);
|
||||
|
||||
// Switching on message type to handle different types of messages
|
||||
switch (messageType) {
|
||||
// If data message, construct from parcel and update channel data
|
||||
case BROADCAST_DATA:
|
||||
// Rx Data
|
||||
//updateData(new BroadcastDataMessage(antParcel).getPayload());
|
||||
BroadcastDataMessage m = new BroadcastDataMessage(antParcel);
|
||||
Log.d(TAG, "BROADCAST_DATA: " + m.getPayload());
|
||||
heart = m.getPayload()[7];
|
||||
Log.d(TAG, "BROADCAST_DATA: " + heart);
|
||||
break;
|
||||
case ACKNOWLEDGED_DATA:
|
||||
// Rx Data
|
||||
//updateData(new AcknowledgedDataMessage(antParcel).getPayload());
|
||||
Log.d(TAG, "ACKNOWLEDGED_DATA: " + new AcknowledgedDataMessage(antParcel).getPayload());
|
||||
break;
|
||||
case CHANNEL_EVENT:
|
||||
// Constructing channel event message from parcel
|
||||
ChannelEventMessage eventMessage = new ChannelEventMessage(antParcel);
|
||||
EventCode code = eventMessage.getEventCode();
|
||||
Log.d(TAG, "Event Code: " + code);
|
||||
|
||||
// Switching on event code to handle the different types of channel events
|
||||
switch (code) {
|
||||
case TX:
|
||||
break;
|
||||
case CHANNEL_COLLISION:
|
||||
ucPageChange += 0x20;
|
||||
ucPageChange &= 0xF0;
|
||||
ucMessageCount += 1;
|
||||
break;
|
||||
case RX_SEARCH_TIMEOUT:
|
||||
// TODO May want to keep searching
|
||||
Log.e(TAG, "No Device Found");
|
||||
break;
|
||||
case CHANNEL_CLOSED:
|
||||
case RX_FAIL:
|
||||
case RX_FAIL_GO_TO_SEARCH:
|
||||
case TRANSFER_RX_FAILED:
|
||||
case TRANSFER_TX_COMPLETED:
|
||||
case TRANSFER_TX_FAILED:
|
||||
case TRANSFER_TX_START:
|
||||
case UNKNOWN:
|
||||
// TODO More complex communication will need to handle these events
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ANT_VERSION:
|
||||
case BURST_TRANSFER_DATA:
|
||||
case CAPABILITIES:
|
||||
case CHANNEL_ID:
|
||||
case CHANNEL_RESPONSE:
|
||||
case CHANNEL_STATUS:
|
||||
case SERIAL_NUMBER:
|
||||
case OTHER:
|
||||
// TODO More complex communication will need to handle these message types
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioManager;
|
||||
import android.util.Log;
|
||||
import android.os.Build;
|
||||
|
||||
public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
private static MediaButtonReceiver instance;
|
||||
@@ -28,14 +29,39 @@ public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
private native void nativeOnMediaButtonEvent(int prev, int current, int max);
|
||||
|
||||
public static void registerReceiver(Context context) {
|
||||
if (instance == null) {
|
||||
instance = new MediaButtonReceiver();
|
||||
try {
|
||||
if (instance == null) {
|
||||
instance = new MediaButtonReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter("android.media.VOLUME_CHANGED_ACTION");
|
||||
|
||||
if (context == null) {
|
||||
Log.e("MediaButtonReceiver", "Context is null, cannot register receiver");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
try {
|
||||
context.registerReceiver(instance, filter, Context.RECEIVER_EXPORTED);
|
||||
} catch (SecurityException se) {
|
||||
Log.e("MediaButtonReceiver", "Security exception while registering receiver: " + se.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
context.registerReceiver(instance, filter);
|
||||
} catch (SecurityException se) {
|
||||
Log.e("MediaButtonReceiver", "Security exception while registering receiver: " + se.getMessage());
|
||||
}
|
||||
}
|
||||
Log.d("MediaButtonReceiver", "Receiver registered successfully");
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e("MediaButtonReceiver", "Invalid arguments for receiver registration: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
Log.e("MediaButtonReceiver", "Unexpected error while registering receiver: " + e.getMessage());
|
||||
}
|
||||
IntentFilter filter = new IntentFilter("android.media.VOLUME_CHANGED_ACTION");
|
||||
context.registerReceiver(instance, filter, Context.RECEIVER_EXPORTED);
|
||||
Log.d("MediaButtonReceiver", "registerReceiver");
|
||||
}
|
||||
|
||||
|
||||
public static void unregisterReceiver(Context context) {
|
||||
if (instance != null) {
|
||||
context.unregisterReceiver(instance);
|
||||
|
||||
74
src/android/src/ZwiftHubBike.java
Normal file
74
src/android/src/ZwiftHubBike.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import android.os.Looper;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import com.garmin.android.connectiq.ConnectIQ;
|
||||
import com.garmin.android.connectiq.ConnectIQAdbStrategy;
|
||||
import com.garmin.android.connectiq.IQApp;
|
||||
import com.garmin.android.connectiq.IQDevice;
|
||||
import com.garmin.android.connectiq.exception.InvalidStateException;
|
||||
import com.garmin.android.connectiq.exception.ServiceUnavailableException;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.IntentFilter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ZwiftHubBike {
|
||||
|
||||
private static Context context;
|
||||
|
||||
private static final String TAG = "ZwiftHubBike: ";
|
||||
|
||||
public static byte[] inclinationCommand(double inclination) throws InvalidProtocolBufferException {
|
||||
ZwiftHub.SimulationParam.Builder simulation = ZwiftHub.SimulationParam.newBuilder();
|
||||
simulation.setInclineX100((int)(inclination * 100.0));
|
||||
|
||||
ZwiftHub.HubCommand.Builder command = ZwiftHub.HubCommand.newBuilder();
|
||||
command.setSimulation(simulation.build());
|
||||
|
||||
byte[] data = command.build().toByteArray();
|
||||
byte[] fullData = new byte[data.length + 1];
|
||||
fullData[0] = 0x04;
|
||||
System.arraycopy(data, 0, fullData, 1, data.length);
|
||||
|
||||
return fullData;
|
||||
}
|
||||
|
||||
public static byte[] setGearCommand(int gears) throws InvalidProtocolBufferException {
|
||||
ZwiftHub.PhysicalParam.Builder physical = ZwiftHub.PhysicalParam.newBuilder();
|
||||
physical.setGearRatioX10000(gears);
|
||||
|
||||
ZwiftHub.HubCommand.Builder command = ZwiftHub.HubCommand.newBuilder();
|
||||
command.setPhysical(physical.build());
|
||||
|
||||
byte[] data = command.build().toByteArray();
|
||||
byte[] fullData = new byte[data.length + 1];
|
||||
fullData[0] = 0x04;
|
||||
System.arraycopy(data, 0, fullData, 1, data.length);
|
||||
|
||||
return fullData;
|
||||
}
|
||||
}
|
||||
162
src/android/src/main/proto/zwift_hub.proto
Normal file
162
src/android/src/main/proto/zwift_hub.proto
Normal file
@@ -0,0 +1,162 @@
|
||||
syntax = "proto2";
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
//-------------- Zwift Hub messages
|
||||
// The command code prepending this message is 0x00
|
||||
// This message is sent always following the change of the gear ratio probably to verify it was received properly
|
||||
message HubRequest {
|
||||
optional uint32 DataId = 1; // Value observed 520 and 0, 0 requests general info, 1-7 are the fields# in DeviceInformationContent, 520 requests the gear ratio
|
||||
// 512 to 534 responds unidentifiable data
|
||||
}
|
||||
|
||||
// The command code prepending this message is 0x03
|
||||
message HubRidingData {
|
||||
optional uint32 Power = 1;
|
||||
optional uint32 Cadence = 2;
|
||||
optional uint32 SpeedX100 = 3;
|
||||
optional uint32 HR = 4;
|
||||
optional uint32 Unknown1 = 5; // Values observed 0 when stopped, 2864, 4060, 4636, 6803
|
||||
optional uint32 Unknown2 = 6; // Values observed 25714, 30091 (constant during session)
|
||||
}
|
||||
|
||||
message SimulationParam {
|
||||
optional sint32 Wind = 1; // Wind in m/s * 100. In zwift there is no wind (0). Negative is backwind
|
||||
optional sint32 InclineX100 = 2; // Incline value * 100
|
||||
optional uint32 CWa = 3; // Wind coefficient CW * a * 10000. In zwift this is constant 0.51 (5100)
|
||||
optional uint32 Crr = 4; // Rolling resistance Crr * 100000. In zwift this is constant 0.004 (400)
|
||||
}
|
||||
|
||||
message PhysicalParam {
|
||||
optional uint32 GearRatioX10000 = 2;
|
||||
optional uint32 BikeWeightx100 = 4;
|
||||
optional uint32 RiderWeightx100 = 5;
|
||||
}
|
||||
|
||||
// The command code prepending this message is 0x04
|
||||
message HubCommand {
|
||||
optional uint32 PowerTarget = 3;
|
||||
optional SimulationParam Simulation = 4;
|
||||
optional PhysicalParam Physical = 5;
|
||||
}
|
||||
|
||||
//---------------- Zwift Play messages
|
||||
|
||||
enum PlayButtonStatus {
|
||||
ON = 0;
|
||||
OFF = 1;
|
||||
}
|
||||
// The command code prepending this message is 0x07
|
||||
message PlayKeyPadStatus {
|
||||
optional PlayButtonStatus RightPad = 1;
|
||||
optional PlayButtonStatus Button_Y_Up = 2;
|
||||
optional PlayButtonStatus Button_Z_Left = 3;
|
||||
optional PlayButtonStatus Button_A_Right = 4;
|
||||
optional PlayButtonStatus Button_B_Down = 5;
|
||||
optional PlayButtonStatus Button_On = 6;
|
||||
optional PlayButtonStatus Button_Shift = 7;
|
||||
optional sint32 Analog_LR = 8;
|
||||
optional sint32 Analog_UD = 9;
|
||||
}
|
||||
|
||||
|
||||
message PlayCommandParameters {
|
||||
optional uint32 param1 = 1;
|
||||
optional uint32 param2 = 2;
|
||||
optional uint32 HapticPattern = 3;
|
||||
}
|
||||
|
||||
message PlayCommandContents {
|
||||
optional PlayCommandParameters CommandParameters = 1;
|
||||
}
|
||||
|
||||
// The command code prepending this message is 0x12
|
||||
// This is sent to the control point to configure and make the controller vibrate
|
||||
message PlayCommand {
|
||||
optional PlayCommandContents CommandContents = 2;
|
||||
}
|
||||
|
||||
// The command code prepending this message is 0x19
|
||||
// This is sent periodically when there are no button presses
|
||||
message Idle {
|
||||
optional uint32 Unknown2 = 2;
|
||||
}
|
||||
|
||||
//----------------- Zwift Ride messages
|
||||
enum RideButtonMask {
|
||||
LEFT_BTN = 0x00001;
|
||||
UP_BTN = 0x00002;
|
||||
RIGHT_BTN = 0x00004;
|
||||
DOWN_BTN = 0x00008;
|
||||
A_BTN = 0x00010;
|
||||
B_BTN = 0x00020;
|
||||
Y_BTN = 0x00040;
|
||||
|
||||
Z_BTN = 0x00100;
|
||||
SHFT_UP_L_BTN = 0x00200;
|
||||
SHFT_DN_L_BTN = 0x00400;
|
||||
POWERUP_L_BTN = 0x00800;
|
||||
ONOFF_L_BTN = 0x01000;
|
||||
SHFT_UP_R_BTN = 0x02000;
|
||||
SHFT_DN_R_BTN = 0x04000;
|
||||
|
||||
POWERUP_R_BTN = 0x10000;
|
||||
ONOFF_R_BTN = 0x20000;
|
||||
}
|
||||
|
||||
enum RideAnalogLocation {
|
||||
LEFT = 0;
|
||||
RIGHT = 1;
|
||||
UP = 2;
|
||||
DOWN = 3;
|
||||
}
|
||||
|
||||
message RideAnalogKeyPress {
|
||||
optional RideAnalogLocation Location = 1;
|
||||
optional sint32 AnalogValue = 2;
|
||||
}
|
||||
|
||||
message RideAnalogKeyGroup {
|
||||
repeated RideAnalogKeyPress GroupStatus = 1;
|
||||
}
|
||||
|
||||
// The command code prepending this message is 0x23
|
||||
message RideKeyPadStatus {
|
||||
optional uint32 ButtonMap = 1;
|
||||
optional RideAnalogKeyGroup AnalogButtons = 2;
|
||||
}
|
||||
|
||||
//------------------ Zwift Click messages
|
||||
// The command code prepending this message is 0x37
|
||||
message ClickKeyPadStatus {
|
||||
optional PlayButtonStatus Button_Plus = 1;
|
||||
optional PlayButtonStatus Button_Minus = 2;
|
||||
}
|
||||
|
||||
//------------------ Device Information requested after connection
|
||||
// The command code prepending this message is 0x3c
|
||||
message DeviceInformationContent {
|
||||
optional uint32 Unknown1 = 1;
|
||||
repeated uint32 SoftwareVersion = 2;
|
||||
optional string DeviceName = 3;
|
||||
optional uint32 Unknown4 = 4;
|
||||
optional uint32 Unknown5 =5;
|
||||
optional string SerialNumber = 6;
|
||||
optional string HardwareVersion = 7;
|
||||
repeated uint32 ReplyData = 8;
|
||||
optional uint32 Unknown9 = 9;
|
||||
optional uint32 Unknown10 = 10;
|
||||
optional uint32 Unknown13 = 13;
|
||||
}
|
||||
|
||||
message SubContent {
|
||||
optional DeviceInformationContent Content = 1;
|
||||
optional uint32 Unknown2 = 2;
|
||||
optional uint32 Unknown4 = 4;
|
||||
optional uint32 Unknown5 = 5;
|
||||
optional uint32 Unknown6 = 6;
|
||||
}
|
||||
|
||||
message DeviceInformation {
|
||||
optional uint32 InformationId = 1;
|
||||
optional SubContent SubContent = 2;
|
||||
}
|
||||
1
src/build-qrc-qml.sh
Executable file
1
src/build-qrc-qml.sh
Executable file
@@ -0,0 +1 @@
|
||||
/Users/cagnulein/Qt/5.15.2/ios/bin/rcc qml.qrc -o ../build-qdomyos-zwift-Qt_5_15_2_for_iOS-Debug/qrc_qml.cpp
|
||||
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
|
||||
@@ -31,8 +31,21 @@ int CharacteristicNotifier2ACD::notify(QByteArray &value) {
|
||||
|
||||
|
||||
uint16_t normalizeIncline = 0;
|
||||
|
||||
QSettings settings;
|
||||
bool real_inclination_to_virtual_treamill_bridge = settings.value(QZSettings::real_inclination_to_virtual_treamill_bridge, QZSettings::default_real_inclination_to_virtual_treamill_bridge).toBool();
|
||||
double inclination = ((treadmill *)Bike)->currentInclination().value();
|
||||
if(real_inclination_to_virtual_treamill_bridge) {
|
||||
double offset = settings.value(QZSettings::zwift_inclination_offset,
|
||||
QZSettings::default_zwift_inclination_offset).toDouble();
|
||||
double gain = settings.value(QZSettings::zwift_inclination_gain,
|
||||
QZSettings::default_zwift_inclination_gain).toDouble();
|
||||
inclination -= offset;
|
||||
inclination /= gain;
|
||||
}
|
||||
|
||||
if (dt == bluetoothdevice::TREADMILL)
|
||||
normalizeIncline = (uint32_t)qRound(((treadmill *)Bike)->currentInclination().value() * 10);
|
||||
normalizeIncline = (uint32_t)qRound(inclination * 10);
|
||||
a = (normalizeIncline >> 8) & 0XFF;
|
||||
b = normalizeIncline & 0XFF;
|
||||
QByteArray inclineBytes;
|
||||
@@ -40,7 +53,7 @@ int CharacteristicNotifier2ACD::notify(QByteArray &value) {
|
||||
inclineBytes.append(a);
|
||||
double ramp = 0;
|
||||
if (dt == bluetoothdevice::TREADMILL)
|
||||
ramp = qRadiansToDegrees(qAtan(((treadmill *)Bike)->currentInclination().value() / 100));
|
||||
ramp = qRadiansToDegrees(qAtan(inclination / 100));
|
||||
int16_t normalizeRamp = (int32_t)qRound(ramp * 10);
|
||||
a = (normalizeRamp >> 8) & 0XFF;
|
||||
b = normalizeRamp & 0XFF;
|
||||
|
||||
@@ -25,6 +25,8 @@ void CharacteristicWriteProcessor::changeSlope(int16_t iresistance, uint8_t crr,
|
||||
settings.value(QZSettings::zwift_inclination_gain, QZSettings::default_zwift_inclination_gain).toDouble();
|
||||
double CRRGain = settings.value(QZSettings::CRRGain, QZSettings::default_CRRGain).toDouble();
|
||||
double CWGain = settings.value(QZSettings::CWGain, QZSettings::default_CWGain).toDouble();
|
||||
bool zwift_play_emulator = settings.value(QZSettings::zwift_play_emulator, QZSettings::default_zwift_play_emulator).toBool();
|
||||
double min_inclination = settings.value(QZSettings::min_inclination, QZSettings::default_min_inclination).toDouble();
|
||||
|
||||
qDebug() << QStringLiteral("new requested resistance zwift erg grade ") + QString::number(iresistance) +
|
||||
QStringLiteral(" enabled ") + force_resistance;
|
||||
@@ -38,6 +40,11 @@ void CharacteristicWriteProcessor::changeSlope(int16_t iresistance, uint8_t crr,
|
||||
percentage = (((qTan(qDegreesToRadians(iresistance / 100.0)) * 100.0) * 2.0) * gain) + offset;
|
||||
}
|
||||
|
||||
if(min_inclination > grade) {
|
||||
grade = min_inclination;
|
||||
qDebug() << "grade override due to min_inclination " << min_inclination;
|
||||
}
|
||||
|
||||
/*
|
||||
Surface Road Crr MTB Crr Gravel Crr (Namebrand) Zwift Gravel Crr
|
||||
Pavement .004 .01 .008 .008
|
||||
@@ -60,9 +67,13 @@ void CharacteristicWriteProcessor::changeSlope(int16_t iresistance, uint8_t crr,
|
||||
if (dt == bluetoothdevice::BIKE) {
|
||||
|
||||
// if the bike doesn't have the inclination by hardware, i'm simulating inclination with the value received
|
||||
// form Zwift
|
||||
if (!((bike *)Bike)->inclinationAvailableByHardware())
|
||||
Bike->setInclination(grade + CRR_offset + CW_offset);
|
||||
// from Zwift
|
||||
if (!((bike *)Bike)->inclinationAvailableByHardware()) {
|
||||
if(zwift_play_emulator)
|
||||
Bike->setInclination(grade);
|
||||
else
|
||||
Bike->setInclination(grade + CRR_offset + CW_offset);
|
||||
}
|
||||
|
||||
emit changeInclination(grade, percentage);
|
||||
|
||||
|
||||
308
src/characteristics/characteristicwriteprocessor0003.cpp
Normal file
308
src/characteristics/characteristicwriteprocessor0003.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
#include "bike.h"
|
||||
#include "characteristicwriteprocessor0003.h"
|
||||
#include <QDebug>
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
43
src/characteristics/characteristicwriteprocessor0003.h
Normal file
43
src/characteristics/characteristicwriteprocessor0003.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#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);
|
||||
|
||||
|
||||
private:
|
||||
struct VarintResult {
|
||||
qint64 value;
|
||||
int bytesRead;
|
||||
};
|
||||
|
||||
VarintResult decodeVarint(const QByteArray& bytes, int startIndex);
|
||||
qint32 decodeSInt(const QByteArray& bytes);
|
||||
void handleZwiftGear(const QByteArray &array);
|
||||
int currentZwiftGear = 8;
|
||||
|
||||
signals:
|
||||
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
};
|
||||
|
||||
#endif // CHARACTERISTICWRITEPROCESSOR0003_H
|
||||
@@ -96,21 +96,51 @@ double bike::gears() {
|
||||
}
|
||||
return m_gears + gears_offset;
|
||||
}
|
||||
|
||||
void bike::setGears(double gears) {
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
double gears_offset = settings.value(QZSettings::gears_offset, QZSettings::default_gears_offset).toDouble();
|
||||
gears -= gears_offset;
|
||||
qDebug() << "setGears" << gears;
|
||||
|
||||
// Check for boundaries and emit failure signals
|
||||
if(gears_zwift_ratio && (gears > 24 || gears < 1)) {
|
||||
qDebug() << "new gear value ignored because of gears_zwift_ratio setting!";
|
||||
if(gears > 24) {
|
||||
emit gearFailedUp();
|
||||
} else {
|
||||
emit gearFailedDown();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(gears > maxGears()) {
|
||||
qDebug() << "new gear value ignored because of maxGears" << maxGears();
|
||||
emit gearFailedUp();
|
||||
return;
|
||||
}
|
||||
|
||||
if(gears < minGears()) {
|
||||
qDebug() << "new gear value ignored because of minGears" << minGears();
|
||||
emit gearFailedDown();
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_gears > gears) {
|
||||
emit gearOkDown();
|
||||
} else {
|
||||
emit gearOkUp();
|
||||
}
|
||||
|
||||
m_gears = gears;
|
||||
if(homeform::singleton()) {
|
||||
homeform::singleton()->updateGearsValue();
|
||||
}
|
||||
settings.setValue(QZSettings::gears_current_value, m_gears);
|
||||
|
||||
if (settings.value(QZSettings::gears_restore_value, QZSettings::default_gears_restore_value).toBool())
|
||||
settings.setValue(QZSettings::gears_current_value, m_gears);
|
||||
|
||||
if (lastRawRequestedResistanceValue != -1) {
|
||||
changeResistance(lastRawRequestedResistanceValue);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ class bike : public bluetoothdevice {
|
||||
double currentCrankRevolutions() override;
|
||||
uint16_t lastCrankEventTime() override;
|
||||
bool connected() override;
|
||||
double defaultMaxGears() { return 9999.0; }
|
||||
virtual double maxGears() { return defaultMaxGears(); }
|
||||
virtual double minGears() { return -9999.0; }
|
||||
virtual uint16_t watts();
|
||||
virtual resistance_t pelotonToBikeResistance(int pelotonResistance);
|
||||
virtual resistance_t resistanceFromPowerRequest(uint16_t power);
|
||||
@@ -78,6 +81,10 @@ class bike : public bluetoothdevice {
|
||||
void resistanceChanged(resistance_t resistance);
|
||||
void resistanceRead(resistance_t resistance);
|
||||
void steeringAngleChanged(double angle);
|
||||
void gearOkUp(); // Signal when gear up succeeds
|
||||
void gearOkDown(); // Signal when gear down succeeds
|
||||
void gearFailedUp(); // Signal when gear up hits max
|
||||
void gearFailedDown(); // Signal when gear down hits min
|
||||
|
||||
protected:
|
||||
metric RequestedResistance;
|
||||
|
||||
@@ -170,11 +170,14 @@ void bluetooth::finished() {
|
||||
bool zwiftDeviceFound =
|
||||
!settings.value(QZSettings::zwift_click, QZSettings::default_zwift_click).toBool() && !settings.value(QZSettings::zwift_play, QZSettings::default_zwift_play).toBool();
|
||||
|
||||
bool sramDeviceFound = !settings.value(QZSettings::sram_axs_controller, QZSettings::default_sram_axs_controller).toBool();
|
||||
|
||||
if ((!heartRateBeltFound && !heartRateBeltAvaiable()) || (!ftmsAccessoryFound && !ftmsAccessoryAvaiable()) ||
|
||||
(!cscFound && !cscSensorAvaiable()) || (!powerSensorFound && !powerSensorAvaiable()) ||
|
||||
(!eliteRizerFound && !eliteRizerAvaiable()) || (!eliteSterzoSmartFound && !eliteSterzoSmartAvaiable()) ||
|
||||
(!fitmetriaFanfitFound && !fitmetriaFanfitAvaiable()) ||
|
||||
(!zwiftDeviceFound && !zwiftDeviceAvaiable())) {
|
||||
(!zwiftDeviceFound && !zwiftDeviceAvaiable()) ||
|
||||
(!sramDeviceFound && !sramDeviceAvaiable())) {
|
||||
|
||||
// force heartRateBelt off
|
||||
forceHeartBeltOffForTimeout = true;
|
||||
@@ -294,6 +297,16 @@ bool bluetooth::zwiftDeviceAvaiable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bluetooth::sramDeviceAvaiable() {
|
||||
|
||||
Q_FOREACH (QBluetoothDeviceInfo b, devices) {
|
||||
if (b.name().toUpper().startsWith("SRAM ")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool bluetooth::powerSensorAvaiable() {
|
||||
|
||||
@@ -392,6 +405,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
settings.value(QZSettings::ftms_accessory_name, QZSettings::default_ftms_accessory_name).toString();
|
||||
bool heartRateBeltFound = heartRateBeltName.startsWith(QStringLiteral("Disabled"));
|
||||
bool ftmsAccessoryFound = ftmsAccessoryName.startsWith(QStringLiteral("Disabled"));
|
||||
bool sramDeviceFound = !settings.value(QZSettings::sram_axs_controller, QZSettings::default_sram_axs_controller).toBool();
|
||||
bool zwiftDeviceFound =
|
||||
!settings.value(QZSettings::zwift_click, QZSettings::default_zwift_click).toBool() && !settings.value(QZSettings::zwift_play, QZSettings::default_zwift_play).toBool();
|
||||
bool fitmetriaFanfitFound =
|
||||
@@ -455,6 +469,8 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
QString computrainerSerialPort =
|
||||
settings.value(QZSettings::computrainer_serialport, QZSettings::default_computrainer_serialport).toString();
|
||||
QString csaferowerSerialPort = settings.value(QZSettings::csafe_rower, QZSettings::default_csafe_rower).toString();
|
||||
QString csafeellipticalSerialPort =
|
||||
settings.value(QZSettings::csafe_elliptical_port, QZSettings::default_csafe_elliptical_port).toString();
|
||||
bool manufacturerDeviceFound = false;
|
||||
bool ss2k_peloton = settings.value(QZSettings::ss2k_peloton, QZSettings::default_ss2k_peloton).toBool();
|
||||
bool pafers_treadmill_bh_iboxster_plus =
|
||||
@@ -475,6 +491,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
QString ftms_treadmill = settings.value(QZSettings::ftms_treadmill, QZSettings::default_ftms_treadmill).toString();
|
||||
bool saris_trainer = settings.value(QZSettings::saris_trainer, QZSettings::default_saris_trainer).toBool();
|
||||
bool iconsole_elliptical = settings.value(QZSettings::iconsole_elliptical, QZSettings::default_iconsole_elliptical).toBool();
|
||||
bool iconsole_rower = settings.value(QZSettings::iconsole_rower, QZSettings::default_iconsole_rower).toBool();
|
||||
|
||||
if (!heartRateBeltFound) {
|
||||
|
||||
@@ -488,6 +505,10 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
|
||||
zwiftDeviceFound = zwiftDeviceAvaiable();
|
||||
}
|
||||
if(!sramDeviceFound) {
|
||||
|
||||
sramDeviceFound = sramDeviceAvaiable();
|
||||
}
|
||||
if (!ftmsAccessoryFound) {
|
||||
|
||||
ftmsAccessoryFound = ftmsAccessoryAvaiable();
|
||||
@@ -765,6 +786,23 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(csafeRower);
|
||||
|
||||
} else if (!csafeellipticalSerialPort.isEmpty() && !csafeElliptical) {
|
||||
this->stopDiscovery();
|
||||
// csafeElliptical = new csafeelliptical(noWriteResistance, noHeartService, false);
|
||||
csafeElliptical = new csafeelliptical(noWriteResistance, noHeartService, false,
|
||||
bikeResistanceOffset, bikeResistanceGain);
|
||||
emit deviceConnected(b);
|
||||
connect(csafeElliptical, &bluetoothdevice::connectedAndDiscovered, this, &bluetooth::connectedAndDiscovered);
|
||||
// connect(cscBike, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
connect(csafeElliptical, &csafeelliptical::debug, this, &bluetooth::debug);
|
||||
csafeElliptical->deviceDiscovered(b);
|
||||
// connect(this, SIGNAL(searchingStop()), cscBike, SLOT(searchingStop())); //NOTE: Commented due to #358
|
||||
if (this->discoveryAgent && !this->discoveryAgent->isActive()) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(csafeElliptical);
|
||||
|
||||
#endif
|
||||
} else if (antbike_setting && !antBike) {
|
||||
this->stopDiscovery();
|
||||
@@ -917,7 +955,24 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(domyosBike);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("FAL-SPORTS")) ||
|
||||
} else if (b.name().toUpper().startsWith(QStringLiteral("I-CONSOLE+")) && iconsole_rower &&
|
||||
!trxappgateusbRower && ftms_bike.contains(QZSettings::default_ftms_bike) && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
trxappgateusbRower = new trxappgateusbrower(noWriteResistance, noHeartService,
|
||||
bikeResistanceOffset, bikeResistanceGain);
|
||||
emit deviceConnected(b);
|
||||
connect(trxappgateusbRower, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
// connect(domyosElliptical, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
connect(trxappgateusbRower, &trxappgateusbrower::debug, this, &bluetooth::debug);
|
||||
trxappgateusbRower->deviceDiscovered(b);
|
||||
connect(this, &bluetooth::searchingStop, trxappgateusbRower, &trxappgateusbrower::searchingStop);
|
||||
if (this->discoveryAgent && !this->discoveryAgent->isActive()) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(trxappgateusbRower);
|
||||
} else if (((b.name().toUpper().startsWith(QStringLiteral("FAL-SPORTS")) && !toorx_bike) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("I-CONSOLE+")) && iconsole_elliptical)) &&
|
||||
!trxappgateusbElliptical && ftms_bike.contains(QZSettings::default_ftms_bike) && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -935,7 +990,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(trxappgateusbElliptical);
|
||||
} else if (b.name().startsWith(QStringLiteral("Domyos-EL")) &&
|
||||
} else if (b.name().startsWith(QStringLiteral("Domyos-EL")) && !settings.value(QZSettings::domyos_elliptical_fmts, QZSettings::default_domyos_elliptical_fmts).toBool() &&
|
||||
!b.name().startsWith(QStringLiteral("DomyosBridge")) && !domyosElliptical && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -954,6 +1009,11 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
this->signalBluetoothDeviceConnected(domyosElliptical);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("YPOO-U3-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("SCH_590E")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("KETTLER ")) ||
|
||||
(b.name().startsWith(QStringLiteral("Domyos-EL")) && settings.value(QZSettings::domyos_elliptical_fmts, QZSettings::default_domyos_elliptical_fmts).toBool()) ||
|
||||
(b.name().toUpper().startsWith("SF-") && b.name().midRef(3).toInt() > 0) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("MYELLIPTICAL ")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("CARDIOPOWER EEGO")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("E35")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
(b.name().startsWith(QStringLiteral("FS-")) && iconsole_elliptical)) && !ypooElliptical && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -1065,7 +1125,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
if (this->discoveryAgent && !this->discoveryAgent->isActive())
|
||||
emit searchingStop();
|
||||
this->signalBluetoothDeviceConnected(proformRower);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("B01_"))) && !bhFitnessElliptical && filter) {
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("B01_"))) && ftms_bike.contains(QZSettings::default_ftms_bike) && !bhFitnessElliptical && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
bhFitnessElliptical = new bhfitnesselliptical(noWriteResistance, noHeartService, bikeResistanceOffset,
|
||||
@@ -1249,7 +1309,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
b.name().toUpper().startsWith(QStringLiteral("F63")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("ST90")) && !deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("TRX7.5")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("S77")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("S77")) && sole_inclination) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("F85")) && sole_inclination)) &&
|
||||
!soleF80 && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -1327,18 +1387,24 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith(QStringLiteral("DOMYOS-TC")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826)) && !settings.value(QZSettings::domyostreadmill_notfmts, QZSettings::default_domyostreadmill_notfmts).toBool()) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("XT685")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("XT285")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("FITNESS")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("WELLFIT TM")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("XTERRA TR")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("T118_")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("RUNN ")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("TM4500")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("RUNN ")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("YS_T1MPLUST")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("YPOO-MINI PRO-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("BFX_T9_")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("AB300S-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("TF04-")) || // Sport Synology Z5 Treadmill #2415
|
||||
b.name().toUpper().startsWith(QStringLiteral("FIT-")) || // FIT-1596
|
||||
(b.name().toUpper().startsWith(QStringLiteral("FIT-")) && !b.name().toUpper().startsWith(QStringLiteral("FIT-BK-"))) || // FIT-1596 and sports tech f37s treadmill #2412
|
||||
b.name().toUpper().startsWith(QStringLiteral("LJJ-")) || // LJJ-02351A
|
||||
b.name().toUpper().startsWith(QStringLiteral("WLT-EP-")) || // Flow elliptical
|
||||
(b.name().toUpper().startsWith("SCHWINN 810")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("KS-MC")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("ANPIUS-")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("KS-HD-Z1D"))) || // Kingsmith WalkingPad Z1
|
||||
(b.name().toUpper().startsWith(QStringLiteral("FIT-")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) || // sports tech f37s treadmill #2412
|
||||
(b.name().toUpper().startsWith(QStringLiteral("NOBLEPRO CONNECT")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) || // FTMS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("TT8")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("ST90")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
@@ -1346,11 +1412,14 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
b.name().toUpper().startsWith(QStringLiteral("MOBVOI TM")) || // FTMS
|
||||
b.name().toUpper().startsWith(QStringLiteral("LB600")) || // FTMS
|
||||
b.name().toUpper().startsWith(QStringLiteral("TUNTURI T60-")) || // FTMS
|
||||
b.name().toUpper().startsWith(QStringLiteral("TUNTURI T90-")) || // FTMS
|
||||
b.name().toUpper().startsWith(QStringLiteral("KETTLER TREADMILL")) || // FTMS
|
||||
b.name().toUpper().startsWith(QStringLiteral("ASSAULTRUNNER")) || // FTMS
|
||||
b.name().toUpper().startsWith(QStringLiteral("CITYSPORTS-LINKER")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("TP1")) && b.name().length() == 3) || // FTMS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("CTM")) && b.name().length() >= 15) || // FTMS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("F85")) && !sole_inclination) || // FMTS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("S77")) && !sole_inclination) || // FMTS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("F89")) && !sole_inclination) || // FMTS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("F80")) && !sole_inclination) || // FMTS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("ANPLUS-"))) // FTMS
|
||||
@@ -1446,11 +1515,14 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
this->signalBluetoothDeviceConnected(technogymmyrunrfcommTreadmill);
|
||||
}
|
||||
#endif
|
||||
} else if ((b.name().toUpper().startsWith("TACX ") ||
|
||||
} else if ((b.name().toUpper().startsWith("TACX ") ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("THINK X")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("THINK-")) ||
|
||||
(b.name().toUpper().startsWith("VANRYSEL-HT")) ||
|
||||
b.address() == QBluetoothAddress("C1:14:D9:9C:FB:01") || // specific TACX NEO 2 #1707
|
||||
(b.name().toUpper().startsWith("TACX SMART BIKE"))) &&
|
||||
!b.name().toUpper().startsWith("TACX SATORI") &&
|
||||
ftms_bike.contains(QZSettings::default_ftms_bike) &&
|
||||
!tacxneo2Bike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -1464,6 +1536,20 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// connect(tacxneo2Bike, SIGNAL(inclinationChanged(double)), this, SLOT(inclinationChanged(double)));
|
||||
tacxneo2Bike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(tacxneo2Bike);
|
||||
} else if ((b.name().toUpper().startsWith("INDOORCYCLE")) &&
|
||||
!cycleopsphantomBike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
cycleopsphantomBike = new cycleopsphantombike(noWriteResistance, noHeartService);
|
||||
// stateFileRead();
|
||||
emit(deviceConnected(b));
|
||||
connect(cycleopsphantomBike, SIGNAL(connectedAndDiscovered()), this, SLOT(connectedAndDiscovered()));
|
||||
// connect(cycleopsphantomBike, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
connect(cycleopsphantomBike, SIGNAL(debug(QString)), this, SLOT(debug(QString)));
|
||||
// connect(cycleopsphantomBike, SIGNAL(speedChanged(double)), this, SLOT(speedChanged(double)));
|
||||
// connect(cycleopsphantomBike, SIGNAL(inclinationChanged(double)), this, SLOT(inclinationChanged(double)));
|
||||
cycleopsphantomBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(cycleopsphantomBike);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral(">CABLE")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("MD")) && b.name().length() == 7) ||
|
||||
// BIKE 1, BIKE 2, BIKE 3...
|
||||
@@ -1492,6 +1578,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith("YS_C1_")) || // Yesoul C1H
|
||||
(b.name().toUpper().startsWith("YS_G1_")) || // Yesoul S3
|
||||
(b.name().toUpper().startsWith("YS_G1MPLUS")) || // Yesoul G1M Plus
|
||||
(b.name().toUpper().startsWith("YS_G1MMAX")) || // Yesoul G1M Max
|
||||
(b.name().toUpper().startsWith("DS25-")) || // Bodytone DS25
|
||||
(b.name().toUpper().startsWith("SCHWINN 510T")) ||
|
||||
(b.name().toUpper().startsWith("3G CARDIO ")) ||
|
||||
@@ -1510,10 +1597,10 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith(ftmsAccessoryName.toUpper()) &&
|
||||
settings.value(QZSettings::ss2k_peloton, QZSettings::default_ss2k_peloton)
|
||||
.toBool()) || // ss2k on a peloton bike
|
||||
((b.name().toUpper().startsWith("KICKR CORE")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
(b.name().toUpper().startsWith("MERACH-MR667-")) ||
|
||||
(b.name().toUpper().startsWith("DS60-")) ||
|
||||
(b.name().toUpper().startsWith("BIKE-")) ||
|
||||
(b.name().toUpper().startsWith("M9-")) ||
|
||||
(b.name().toUpper().startsWith("SPAX-BK-")) ||
|
||||
(b.name().toUpper().startsWith("YSV1")) ||
|
||||
(b.name().toUpper().startsWith("VOLT") && b.name().length() == 4) ||
|
||||
@@ -1524,20 +1611,52 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith("WAVEFIT-")) ||
|
||||
(b.name().toUpper().startsWith("KETTLERBLE")) ||
|
||||
(b.name().toUpper().startsWith("JAS_C3")) ||
|
||||
(b.name().toUpper().startsWith("SCH_190U")) ||
|
||||
(b.name().toUpper().startsWith("RAVE WHITE")) ||
|
||||
(b.name().toUpper().startsWith("DOMYOS-BIKING-")) ||
|
||||
(b.name().startsWith(QStringLiteral("Domyos-Bike")) && deviceHasService(b, QBluetoothUuid((quint16)0x1826)) && !settings.value(QZSettings::domyosbike_notfmts, QZSettings::default_domyosbike_notfmts).toBool()) ||
|
||||
(b.name().toUpper().startsWith("F") && b.name().toUpper().endsWith("ARROW")) || // FI9110 Arrow, https://www.fitnessdigital.it/bicicletta-smart-bike-ion-fitness-arrow-connect/p/10022863/ IO Fitness Arrow
|
||||
(b.name().toUpper().startsWith("ICSE") && b.name().length() == 4) ||
|
||||
(b.name().toUpper().startsWith("TUO") && b.name().length() == 3) ||
|
||||
(b.name().toUpper().startsWith("FLX") && b.name().length() == 10) ||
|
||||
(b.name().toUpper().startsWith("CSRB") && b.name().length() == 11) ||
|
||||
(b.name().toUpper().startsWith("DU30-")) || // BodyTone du30
|
||||
(b.name().toUpper().startsWith("BIKZU_")) ||
|
||||
(b.name().toUpper().startsWith("WLT8828")) ||
|
||||
(b.name().toUpper().startsWith("VANRYSEL-HT")) ||
|
||||
(b.name().toUpper().startsWith("WLT8828")) ||
|
||||
(b.name().toUpper().startsWith("HARISON-X15")) ||
|
||||
(b.name().toUpper().startsWith("FEIVON V2")) ||
|
||||
(b.name().toUpper().startsWith("FELVON V2")) ||
|
||||
(b.name().toUpper().startsWith("JUSTO")) ||
|
||||
(b.name().toUpper().startsWith("MYCYCLE ")) ||
|
||||
(b.name().toUpper().startsWith("T2 ")) ||
|
||||
(b.name().toUpper().startsWith("DR") && b.name().length() == 2) ||
|
||||
(b.name().toUpper().startsWith("RC-MAX-")) ||
|
||||
(b.name().toUpper().startsWith("TPS-SPBIKE-2.0")) ||
|
||||
(b.name().toUpper().startsWith("NEO BIKE SMART")) ||
|
||||
(b.name().toUpper().startsWith("ZDRIVE")) ||
|
||||
(b.name().toUpper().startsWith("TUNTURI E60-")) ||
|
||||
(b.name().toUpper().startsWith("TUNTURI F40-")) ||
|
||||
(b.name().toUpper().startsWith("JFBK5.0")) ||
|
||||
(b.name().toUpper().startsWith("NEO 3M ")) ||
|
||||
(b.name().toUpper().startsWith("JFBK7.0")) ||
|
||||
(b.name().toUpper().startsWith("SPEEDRACEX")) ||
|
||||
(b.name().toUpper().startsWith("POOBOO")) ||
|
||||
(b.name().toUpper().startsWith("ZYCLE ZPRO")) ||
|
||||
(b.name().toUpper().startsWith("SM720I")) ||
|
||||
(b.name().toUpper().startsWith("AVANTI")) ||
|
||||
(b.name().toUpper().startsWith("T300P_")) ||
|
||||
(b.name().toUpper().startsWith("T200_")) ||
|
||||
(b.name().toUpper().startsWith("BZ9110 ")) ||
|
||||
(b.name().toUpper().startsWith("TITAN 7000")) ||
|
||||
(b.name().toUpper().startsWith("LYDSTO")) ||
|
||||
(b.name().toUpper().startsWith("CYCLO_")) ||
|
||||
(b.name().toUpper().startsWith("SL010-")) ||
|
||||
(b.name().toUpper().startsWith("LCR")) ||
|
||||
(b.name().toUpper().startsWith("ROBX")) ||
|
||||
(b.name().toUpper().startsWith("XCX-")) ||
|
||||
(b.name().toUpper().startsWith("L-") && b.name().length() == 11) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("FIT-BK-"))) ||
|
||||
(b.name().toUpper().startsWith("VFSPINBIKE")) ||
|
||||
(b.name().toUpper().startsWith("GLT") && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
(b.name().toUpper().startsWith("SPORT01-") && deviceHasService(b, QBluetoothUuid((quint16)0x1826))) || // Labgrey Magnetic Exercise Bike https://www.amazon.co.uk/dp/B0CXMF1NPY?_encoding=UTF8&psc=1&ref=cm_sw_r_cp_ud_dp_PE420HA7RD7WJBZPN075&ref_=cm_sw_r_cp_ud_dp_PE420HA7RD7WJBZPN075&social_share=cm_sw_r_cp_ud_dp_PE420HA7RD7WJBZPN075&skipTwisterOG=1
|
||||
(b.name().toUpper().startsWith("ZUMO")) || (b.name().toUpper().startsWith("XS08-")) ||
|
||||
@@ -1558,6 +1677,8 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
this->signalBluetoothDeviceConnected(ftmsBike);
|
||||
} else if ((b.name().toUpper().startsWith("KICKR SNAP") || b.name().toUpper().startsWith("KICKR BIKE") ||
|
||||
b.name().toUpper().startsWith("KICKR ROLLR") ||
|
||||
b.name().toUpper().startsWith("KICKR CORE") ||
|
||||
(b.name().toUpper().startsWith("KICKR MOVE ")) ||
|
||||
(b.name().toUpper().startsWith("HAMMER ") && saris_trainer) ||
|
||||
(b.name().toUpper().startsWith("WAHOO KICKR"))) &&
|
||||
!wahooKickrSnapBike && !ftmsBike && filter) {
|
||||
@@ -1572,6 +1693,19 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
connect(wahooKickrSnapBike, &wahookickrsnapbike::debug, this, &bluetooth::debug);
|
||||
wahooKickrSnapBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(wahooKickrSnapBike);
|
||||
} else if (b.name().toUpper().startsWith("BIKE ") && b.name().midRef(5).toInt() > 0 &&
|
||||
!technogymBike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
technogymBike =
|
||||
new technogymbike(noWriteResistance, noHeartService, bikeResistanceOffset, bikeResistanceGain);
|
||||
emit deviceConnected(b);
|
||||
connect(technogymBike, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
// connect(wahooKickrSnapBike, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
connect(technogymBike, &technogymbike::debug, this, &bluetooth::debug);
|
||||
technogymBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(technogymBike);
|
||||
} else if (((b.name().toUpper().startsWith("JFIC")) // HORIZON GR7
|
||||
) &&
|
||||
!horizonGr7Bike && filter) {
|
||||
@@ -1586,11 +1720,26 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
connect(horizonGr7Bike, &horizongr7bike::debug, this, &bluetooth::debug);
|
||||
horizonGr7Bike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(horizonGr7Bike);
|
||||
} else if (((b.name().toUpper().startsWith("SMART CONTROL"))
|
||||
) &&
|
||||
!kineticInroadBike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
kineticInroadBike =
|
||||
new kineticinroadbike(noWriteResistance, noHeartService, bikeResistanceOffset, bikeResistanceGain);
|
||||
emit deviceConnected(b);
|
||||
connect(kineticInroadBike, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
// connect(trxappgateusb, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
//connect(kineticInroadBike, &kineticinroadbike::debug, this, &bluetooth::debug);
|
||||
kineticInroadBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(kineticInroadBike);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("STAGES ")) ||
|
||||
(b.name().toUpper().startsWith("TACX SATORI")) ||
|
||||
(b.name().toUpper().startsWith("RACER S")) ||
|
||||
((b.name().toUpper().startsWith("KU")) && b.name().length() == 2) ||
|
||||
(b.name().toUpper().startsWith("ELITETRAINER")) ||
|
||||
((b.name().toUpper().startsWith("KICKR CORE")) && !deviceHasService(b, QBluetoothUuid((quint16)0x1826)) && deviceHasService(b, QBluetoothUuid((quint16)0x1818))) ||
|
||||
(b.name().toUpper().startsWith("TOUR 600")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("QD")) && b.name().length() == 2) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("DFC")) && b.name().length() == 3) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("ASSIOMA")) &&
|
||||
@@ -1646,7 +1795,10 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
b.name().toUpper().startsWith(QStringLiteral("S4 COMMS")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("KS-WLT")) || // KS-WLT-W1
|
||||
b.name().toUpper().startsWith(QStringLiteral("I-ROWER")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("YOROTO-RW-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("SF-RW")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("ROWER ")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("ROGUE CONSOLE ")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("DFIT-L-R")) ||
|
||||
!b.name().compare(ftms_rower, Qt::CaseInsensitive) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("PM5")) &&
|
||||
@@ -1664,13 +1816,28 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// connect(ftmsRower, SIGNAL(inclinationChanged(double)), this, SLOT(inclinationChanged(double)));
|
||||
ftmsRower->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(ftmsRower);
|
||||
} else if ((b.name().toUpper().startsWith(QLatin1String("ECH-EC-SPT"))) &&
|
||||
!echelonStairclimber && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
echelonStairclimber = new echelonstairclimber(this->pollDeviceTime, noConsole, noHeartService);
|
||||
// stateFileRead();
|
||||
emit deviceConnected(b);
|
||||
connect(echelonStairclimber, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
// connect(echelonRower, SIGNAL(disconnected()), this, SLOT(restart())); connect(echelonStride,
|
||||
connect(echelonStairclimber, &echelonstairclimber::debug, this, &bluetooth::debug);
|
||||
connect(echelonStairclimber, &echelonstairclimber::speedChanged, this, &bluetooth::speedChanged);
|
||||
connect(echelonStairclimber, &echelonstairclimber::inclinationChanged, this, &bluetooth::inclinationChanged);
|
||||
echelonStairclimber->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(echelonStairclimber);
|
||||
} else if ((b.name().toUpper().startsWith(QLatin1String("ECH-STRIDE")) ||
|
||||
b.name().toUpper().startsWith(QLatin1String("ECH-UK-")) ||
|
||||
b.name().toUpper().startsWith(QLatin1String("ECH-FR-")) ||
|
||||
b.name().toUpper().startsWith(QLatin1String("STRIDE")) ||
|
||||
b.name().toUpper().startsWith(QLatin1String("STRIDE6S-")) ||
|
||||
b.name().toUpper().startsWith(QLatin1String("ECH-SD-SPT"))) &&
|
||||
!echelonStride && filter) {
|
||||
!echelonStride && !echelonStairclimber && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
echelonStride = new echelonstride(this->pollDeviceTime, noConsole, noHeartService);
|
||||
@@ -1728,8 +1895,22 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
connect(ziproTreadmill, &ziprotreadmill::inclinationChanged, this, &bluetooth::inclinationChanged);
|
||||
ziproTreadmill->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(ziproTreadmill);
|
||||
} else if ((b.name().toUpper().startsWith(QLatin1String("LIFESPAN-TM"))) && !lifespanTreadmill && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
lifespanTreadmill = new lifespantreadmill(this->pollDeviceTime, noConsole, noHeartService);
|
||||
// stateFileRead();
|
||||
emit deviceConnected(b);
|
||||
connect(lifespanTreadmill, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
// connect(ziproTreadmill, SIGNAL(disconnected()), this, SLOT(restart())); connect(echelonStride,
|
||||
connect(lifespanTreadmill, &lifespantreadmill::debug, this, &bluetooth::debug);
|
||||
connect(lifespanTreadmill, &lifespantreadmill::speedChanged, this, &bluetooth::speedChanged);
|
||||
connect(lifespanTreadmill, &lifespantreadmill::inclinationChanged, this, &bluetooth::inclinationChanged);
|
||||
lifespanTreadmill->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(lifespanTreadmill);
|
||||
} else if ((b.name().startsWith(QStringLiteral("ECH-ROW")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("ROWSPORT-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("ROWSPORT")) ||
|
||||
b.name().startsWith(QStringLiteral("ROW-S"))) &&
|
||||
!echelonRower && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -1746,7 +1927,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// connect(echelonRower, SIGNAL(inclinationChanged(double)), this, SLOT(inclinationChanged(double)));
|
||||
echelonRower->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(echelonRower);
|
||||
} else if (b.name().startsWith(QStringLiteral("ECH")) && !echelonRower && !echelonStride &&
|
||||
} else if (b.name().startsWith(QStringLiteral("ECH")) && !echelonRower && !echelonStride && !echelonStairclimber && !ftmsBike &&
|
||||
!echelonConnectSport && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -1878,6 +2059,23 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// SLOT(inclinationChanged(double)));
|
||||
sportsPlusBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(sportsPlusBike);
|
||||
} else if ((b.name().toUpper().contains(QStringLiteral("CARE")) &&
|
||||
b.name().length() >= 13) // CARE968300122
|
||||
&& !sportsPlusRower && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
sportsPlusRower = new sportsplusrower(noWriteResistance, noHeartService);
|
||||
// stateFileRead();
|
||||
emit deviceConnected(b);
|
||||
connect(sportsPlusRower, &bluetoothdevice::connectedAndDiscovered, this,
|
||||
&bluetooth::connectedAndDiscovered);
|
||||
// connect(sportsPlusBike, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
connect(sportsPlusRower, &sportsplusrower::debug, this, &bluetooth::debug);
|
||||
// connect(sportsPlusBike, SIGNAL(speedChanged(double)), this, SLOT(speedChanged(double)));
|
||||
// connect(sportsPlusBike, SIGNAL(inclinationChanged(double)), this,
|
||||
// SLOT(inclinationChanged(double)));
|
||||
sportsPlusRower->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(sportsPlusRower);
|
||||
} else if ((b.name().startsWith(yesoulbike::bluetoothName) ||
|
||||
b.name().toUpper().startsWith("YS_G1M_")) && !yesoulBike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -1911,7 +2109,8 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// connect(proformBike, SIGNAL(inclinationChanged(double)), this, SLOT(inclinationChanged(double)));
|
||||
proformBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(proformBike);
|
||||
} else if ((b.name().startsWith(QStringLiteral("I_TL")) || b.name().startsWith(QStringLiteral("I_IT"))) &&
|
||||
} else if ((b.name().startsWith(QStringLiteral("I_TL")) || b.name().startsWith(QStringLiteral("I_IT")) ||
|
||||
b.name().toUpper().contains(QStringLiteral("_IFIT TREADMILL"))) &&
|
||||
!proformTreadmill && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -1943,7 +2142,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// SLOT(inclinationChanged(double)));
|
||||
eslinkerTreadmill->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(eslinkerTreadmill);
|
||||
} else if (b.name().toUpper().startsWith(QStringLiteral("PITPAT")) && !deerrunTreadmill && filter) {
|
||||
} else if (b.name().toUpper().startsWith(QStringLiteral("PITPAT-T")) && !deerrunTreadmill && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
deerrunTreadmill = new deerruntreadmill(this->pollDeviceTime, noConsole, noHeartService);
|
||||
@@ -1974,6 +2173,14 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
// SLOT(inclinationChanged(double)));
|
||||
pafersTreadmill->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(pafersTreadmill);
|
||||
|
||||
|
||||
} else if (b.name().toUpper().startsWith(QStringLiteral("MOXY6")) && !moxy5Sensor) {
|
||||
// *** SPECIAL DEVICE ****
|
||||
moxy5Sensor = new moxy5sensor();
|
||||
connect(moxy5Sensor, &moxy5sensor::debug, this, &bluetooth::debug);
|
||||
moxy5Sensor->deviceDiscovered(b);
|
||||
|
||||
} else if (b.name().toUpper().startsWith(QStringLiteral("BOWFLEX T")) && !bowflexT216Treadmill && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -2055,6 +2262,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
} else if ((b.name().startsWith(QStringLiteral("TRX ROUTE KEY")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("MASTERT40-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("BH DUALKIT TREAD")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("TF-T")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("BH-TR-"))) && !toorx && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -2095,7 +2303,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith(QStringLiteral("XT485")) && !deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("XT800")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("XT900"))) &&
|
||||
!spiritTreadmill && filter) {
|
||||
!spiritTreadmill && !horizonTreadmill && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
spiritTreadmill = new spirittreadmill();
|
||||
@@ -2127,7 +2335,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith(QStringLiteral("DKN RUN"))) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("ADIDAS "))) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("REEBOK")))) &&
|
||||
!trxappgateusb && !trxappgateusbBike && !toorx_bike && !toorx_ftms && !toorx_ftms_treadmill && !iconsole_elliptical &&
|
||||
!trxappgateusb && !trxappgateusbBike && !toorx_bike && !toorx_ftms && !toorx_ftms_treadmill && !iconsole_elliptical && !iconsole_rower &&
|
||||
filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
@@ -2144,6 +2352,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
b.name().toUpper().startsWith(QStringLiteral("FIT HI WAY")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("BIKZU_")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("PASYOU-")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("VIRTUFIT")) ||
|
||||
((b.name().startsWith(QStringLiteral("TOORX")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("I-CONSOIE+")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("I-CONSOLE+")) ||
|
||||
@@ -2152,9 +2361,10 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
b.name().toUpper().startsWith(QStringLiteral("VIFHTR2.1")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("REEBOK"))) ||
|
||||
b.name().toUpper().contains(QStringLiteral("CR011R")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("FAL-SPORTS")) && toorx_bike) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("DKN MOTION"))) &&
|
||||
(toorx_bike))) &&
|
||||
!trxappgateusb && !toorx_ftms && !toorx_ftms_treadmill && !trxappgateusbBike && filter && !iconsole_elliptical) {
|
||||
!trxappgateusb && !toorx_ftms && !toorx_ftms_treadmill && !trxappgateusbBike && filter && !iconsole_elliptical && !iconsole_rower) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
trxappgateusbBike =
|
||||
@@ -2241,7 +2451,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
pafersBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(pafersBike);
|
||||
} else if (((b.name().startsWith(QStringLiteral("FS-")) && snode_bike) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("TF-")) &&
|
||||
(b.name().toUpper().startsWith(QStringLiteral("TF-")) && !b.name().toUpper().startsWith(QStringLiteral("TF-T")) &&
|
||||
!horizon_treadmill_force_ftms)) && // TF-769DF2
|
||||
!snodeBike &&
|
||||
!ftmsBike && !fitPlusBike && filter) {
|
||||
@@ -2289,6 +2499,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().startsWith(QStringLiteral("SW")) && b.name().length() == 14 &&
|
||||
!b.name().contains('(') && !b.name().contains(')') && !deviceHasService(b, QBluetoothUuid((quint16)0x1826))) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("WINFITA"))) || // also FTMS
|
||||
(b.name().toUpper().startsWith(QStringLiteral("SW-BLE"))) || // FTMS
|
||||
(b.name().startsWith(QStringLiteral("BF70")))) &&
|
||||
!fitshowTreadmill && !iconsole_elliptical && !horizonTreadmill && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -2347,6 +2558,28 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(chronoBike);
|
||||
} else if (b.name().toUpper().startsWith(QStringLiteral("PITPAT-S")) && !pitpatBike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
this->stopDiscovery();
|
||||
pitpatBike = new pitpatbike(noWriteResistance, noHeartService, bikeResistanceOffset,
|
||||
bikeResistanceGain);
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
stateFileRead();
|
||||
#endif
|
||||
emit deviceConnected(b);
|
||||
connect(pitpatBike, &bluetoothdevice::connectedAndDiscovered, this, &bluetooth::connectedAndDiscovered);
|
||||
//connect(pitpatBike, &pitpatbike::debug, this, &bluetooth::debug);
|
||||
// NOTE: Commented due to #358
|
||||
// connect(chronoBike, SIGNAL(speedChanged(double)), this, SLOT(speedChanged(double)));
|
||||
// NOTE: Commented due to #358
|
||||
// connect(chronoBike, SIGNAL(inclinationChanged(double)), this, SLOT(inclinationChanged(double)));
|
||||
pitpatBike->deviceDiscovered(b);
|
||||
// NOTE: Commented due to #358
|
||||
// connect(this, SIGNAL(searchingStop()), chronoBike, SLOT(searchingStop()));
|
||||
if (this->discoveryAgent && !this->discoveryAgent->isActive()) {
|
||||
emit searchingStop();
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(pitpatBike);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2572,6 +2805,8 @@ void bluetooth::connectedAndDiscovered() {
|
||||
&bluetoothdevice::cadenceSensor);
|
||||
connect(powerSensorRun, &bluetoothdevice::speedChanged, this->device(),
|
||||
&bluetoothdevice::speedSensor);
|
||||
connect(powerSensorRun, &bluetoothdevice::inclinationChanged, this->device(),
|
||||
&bluetoothdevice::inclinationSensor);
|
||||
connect(powerSensorRun, &bluetoothdevice::instantaneousStrideLengthChanged, this->device(),
|
||||
&bluetoothdevice::instantaneousStrideLengthSensor);
|
||||
connect(powerSensorRun, &bluetoothdevice::groundContactChanged, this->device(),
|
||||
@@ -2633,6 +2868,25 @@ void bluetooth::connectedAndDiscovered() {
|
||||
}
|
||||
}
|
||||
|
||||
if(settings.value(QZSettings::sram_axs_controller, QZSettings::default_sram_axs_controller).toBool()) {
|
||||
for (const QBluetoothDeviceInfo &b : qAsConst(devices)) {
|
||||
if (((b.name().toUpper().startsWith("SRAM "))) && !sramAXSController && this->device() &&
|
||||
this->device()->deviceType() == bluetoothdevice::BIKE) {
|
||||
|
||||
sramAXSController = new sramaxscontroller();
|
||||
// connect(heartRateBelt, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
|
||||
connect(sramAXSController, &sramaxscontroller::debug, this, &bluetooth::debug);
|
||||
connect(sramAXSController, &sramaxscontroller::plus, (bike*)this->device(), &bike::gearUp);
|
||||
connect(sramAXSController, &sramaxscontroller::minus, (bike*)this->device(), &bike::gearDown);
|
||||
sramAXSController->deviceDiscovered(b);
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested("SRAM Connected!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(settings.value(QZSettings::zwift_click, QZSettings::default_zwift_click).toBool()) {
|
||||
for (const QBluetoothDeviceInfo &b : qAsConst(devices)) {
|
||||
if (((b.name().toUpper().startsWith("ZWIFT CLICK"))) && !zwiftClickRemote && this->device() &&
|
||||
@@ -2659,6 +2913,26 @@ void bluetooth::connectedAndDiscovered() {
|
||||
}
|
||||
|
||||
if(settings.value(QZSettings::zwift_play, QZSettings::default_zwift_play).toBool()) {
|
||||
for (const QBluetoothDeviceInfo &b : qAsConst(devices)) {
|
||||
if (((b.name().toUpper().startsWith("SQUARE"))) && !eliteSquareController && this->device() &&
|
||||
this->device()->deviceType() == bluetoothdevice::BIKE) {
|
||||
|
||||
eliteSquareController = new elitesquarecontroller(this->device());
|
||||
// connect(heartRateBelt, SIGNAL(disconnected()), this, SLOT(restart()));
|
||||
|
||||
connect(eliteSquareController, &elitesquarecontroller::debug, this, &bluetooth::debug);
|
||||
connect(eliteSquareController, &elitesquarecontroller::plus, (bike*)this->device(), &bike::gearUp);
|
||||
connect(eliteSquareController, &elitesquarecontroller::minus, (bike*)this->device(), &bike::gearDown);
|
||||
eliteSquareController->deviceDiscovered(b);
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested("Elite Square Connected!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(settings.value(QZSettings::zwift_play, QZSettings::default_zwift_play).toBool()) {
|
||||
bool zwiftplay_swap = settings.value(QZSettings::zwiftplay_swap, QZSettings::default_zwiftplay_swap).toBool();
|
||||
for (const QBluetoothDeviceInfo &b : qAsConst(devices)) {
|
||||
if ((((b.name().toUpper().startsWith("ZWIFT PLAY"))) || b.name().toUpper().startsWith("ZWIFT RIDE") || b.name().toUpper().startsWith("ZWIFT SF2")) && zwiftPlayDevice.size() < 2 && this->device() &&
|
||||
this->device()->deviceType() == bluetoothdevice::BIKE) {
|
||||
@@ -2666,7 +2940,7 @@ void bluetooth::connectedAndDiscovered() {
|
||||
if(b.manufacturerData(2378).size() > 0) {
|
||||
qDebug() << "this should be 3 or 2. is it? " << int(b.manufacturerData(2378).at(0));
|
||||
zwiftPlayDevice.append(new zwiftclickremote(this->device(),
|
||||
int(b.manufacturerData(2378).at(0)) == 3 ? AbstractZapDevice::ZWIFT_PLAY_TYPE::LEFT : AbstractZapDevice::ZWIFT_PLAY_TYPE::RIGHT));
|
||||
int(b.manufacturerData(2378).at(0)) == 3 || int(b.manufacturerData(2378).at(0)) == 7 ? AbstractZapDevice::ZWIFT_PLAY_TYPE::LEFT : AbstractZapDevice::ZWIFT_PLAY_TYPE::RIGHT));
|
||||
} else {
|
||||
qDebug() << "manufacturer not found for ZWIFT CLICK";
|
||||
zwiftPlayDevice.append(new zwiftclickremote(this->device(),
|
||||
@@ -2678,6 +2952,14 @@ void bluetooth::connectedAndDiscovered() {
|
||||
connect(zwiftPlayDevice.last(), &zwiftclickremote::debug, this, &bluetooth::debug);
|
||||
connect(zwiftPlayDevice.last()->playDevice, &ZwiftPlayDevice::plus, (bike*)this->device(), &bike::gearUp);
|
||||
connect(zwiftPlayDevice.last()->playDevice, &ZwiftPlayDevice::minus, (bike*)this->device(), &bike::gearDown);
|
||||
if((zwiftPlayDevice.last()->typeZap == AbstractZapDevice::LEFT && !zwiftplay_swap) ||
|
||||
(zwiftPlayDevice.last()->typeZap == AbstractZapDevice::RIGHT && zwiftplay_swap)) {
|
||||
connect((bike*)this->device(), &bike::gearOkUp, this, &bluetooth::gearUp);
|
||||
connect((bike*)this->device(), &bike::gearFailedUp, this, &bluetooth::gearFailedUp);
|
||||
} else {
|
||||
connect((bike*)this->device(), &bike::gearOkDown, this, &bluetooth::gearDown);
|
||||
connect((bike*)this->device(), &bike::gearFailedDown, this, &bluetooth::gearFailedDown);
|
||||
}
|
||||
zwiftPlayDevice.last()->deviceDiscovered(b);
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested("Zwift Play/Ride Connected!");
|
||||
@@ -2753,6 +3035,50 @@ void bluetooth::connectedAndDiscovered() {
|
||||
firstConnected = false;
|
||||
}
|
||||
|
||||
void bluetooth::gearUp() {
|
||||
QSettings settings;
|
||||
bool zwiftplay_swap = settings.value(QZSettings::zwiftplay_swap, QZSettings::default_zwiftplay_swap).toBool();
|
||||
foreach(zwiftclickremote* p, zwiftPlayDevice) {
|
||||
if((p->typeZap == AbstractZapDevice::LEFT && !zwiftplay_swap) || (p->typeZap == AbstractZapDevice::RIGHT && zwiftplay_swap)) {
|
||||
p->vibrate(0x20);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bluetooth::gearDown() {
|
||||
QSettings settings;
|
||||
bool zwiftplay_swap = settings.value(QZSettings::zwiftplay_swap, QZSettings::default_zwiftplay_swap).toBool();
|
||||
foreach(zwiftclickremote* p, zwiftPlayDevice) {
|
||||
if((p->typeZap == AbstractZapDevice::RIGHT && !zwiftplay_swap) || (p->typeZap == AbstractZapDevice::LEFT && zwiftplay_swap)) {
|
||||
p->vibrate(0x20);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bluetooth::gearFailedUp() {
|
||||
QSettings settings;
|
||||
bool zwiftplay_swap = settings.value(QZSettings::zwiftplay_swap, QZSettings::default_zwiftplay_swap).toBool();
|
||||
foreach(zwiftclickremote* p, zwiftPlayDevice) {
|
||||
if((p->typeZap == AbstractZapDevice::LEFT && !zwiftplay_swap) || (p->typeZap == AbstractZapDevice::RIGHT && zwiftplay_swap)) {
|
||||
p->vibrate(0x60);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bluetooth::gearFailedDown() {
|
||||
QSettings settings;
|
||||
bool zwiftplay_swap = settings.value(QZSettings::zwiftplay_swap, QZSettings::default_zwiftplay_swap).toBool();
|
||||
foreach(zwiftclickremote* p, zwiftPlayDevice) {
|
||||
if((p->typeZap == AbstractZapDevice::RIGHT && !zwiftplay_swap) || (p->typeZap == AbstractZapDevice::LEFT && zwiftplay_swap)) {
|
||||
p->vibrate(0x60);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bluetooth::heartRate(uint8_t heart) { Q_UNUSED(heart) }
|
||||
|
||||
void bluetooth::restart() {
|
||||
@@ -2969,6 +3295,11 @@ void bluetooth::restart() {
|
||||
delete tacxneo2Bike;
|
||||
tacxneo2Bike = nullptr;
|
||||
}
|
||||
if (cycleopsphantomBike) {
|
||||
|
||||
delete cycleopsphantomBike;
|
||||
cycleopsphantomBike = nullptr;
|
||||
}
|
||||
if (stagesBike) {
|
||||
|
||||
delete stagesBike;
|
||||
@@ -3015,6 +3346,11 @@ void bluetooth::restart() {
|
||||
delete trxappgateusbElliptical;
|
||||
trxappgateusbElliptical = nullptr;
|
||||
}
|
||||
if (trxappgateusbRower) {
|
||||
|
||||
delete trxappgateusbRower;
|
||||
trxappgateusbRower = nullptr;
|
||||
}
|
||||
if (soleBike) {
|
||||
|
||||
delete soleBike;
|
||||
@@ -3050,6 +3386,11 @@ void bluetooth::restart() {
|
||||
delete echelonStride;
|
||||
echelonStride = nullptr;
|
||||
}
|
||||
if (echelonStairclimber) {
|
||||
|
||||
delete echelonStairclimber;
|
||||
echelonStairclimber = nullptr;
|
||||
}
|
||||
if (octaneTreadmill) {
|
||||
|
||||
delete octaneTreadmill;
|
||||
@@ -3060,6 +3401,10 @@ void bluetooth::restart() {
|
||||
delete ziproTreadmill;
|
||||
ziproTreadmill = nullptr;
|
||||
}
|
||||
if (lifespanTreadmill) {
|
||||
delete lifespanTreadmill;
|
||||
lifespanTreadmill = nullptr;
|
||||
}
|
||||
if (octaneElliptical) {
|
||||
|
||||
delete octaneElliptical;
|
||||
@@ -3185,6 +3530,11 @@ void bluetooth::restart() {
|
||||
delete sportsPlusBike;
|
||||
sportsPlusBike = nullptr;
|
||||
}
|
||||
if (sportsPlusRower) {
|
||||
|
||||
delete sportsPlusRower;
|
||||
sportsPlusRower = nullptr;
|
||||
}
|
||||
if (pelotonBike) {
|
||||
|
||||
delete pelotonBike;
|
||||
@@ -3206,12 +3556,22 @@ void bluetooth::restart() {
|
||||
delete csafeRower;
|
||||
csafeRower = nullptr;
|
||||
}
|
||||
if (csafeElliptical) {
|
||||
|
||||
delete csafeElliptical;
|
||||
csafeElliptical= nullptr;
|
||||
}
|
||||
#endif
|
||||
if (chronoBike) {
|
||||
|
||||
delete chronoBike;
|
||||
chronoBike = nullptr;
|
||||
}
|
||||
if (pitpatBike) {
|
||||
|
||||
delete pitpatBike;
|
||||
pitpatBike = nullptr;
|
||||
}
|
||||
if (snodeBike) {
|
||||
|
||||
delete snodeBike;
|
||||
@@ -3227,11 +3587,21 @@ void bluetooth::restart() {
|
||||
delete wahooKickrSnapBike;
|
||||
wahooKickrSnapBike = nullptr;
|
||||
}
|
||||
if (technogymBike) {
|
||||
|
||||
delete technogymBike;
|
||||
technogymBike = nullptr;
|
||||
}
|
||||
if (horizonGr7Bike) {
|
||||
|
||||
delete horizonGr7Bike;
|
||||
horizonGr7Bike = nullptr;
|
||||
}
|
||||
if (kineticInroadBike) {
|
||||
|
||||
delete kineticInroadBike;
|
||||
kineticInroadBike = nullptr;
|
||||
}
|
||||
if (renphoBike) {
|
||||
|
||||
delete renphoBike;
|
||||
@@ -3377,6 +3747,8 @@ bluetoothdevice *bluetooth::device() {
|
||||
return npeCableBike;
|
||||
} else if (tacxneo2Bike) {
|
||||
return tacxneo2Bike;
|
||||
} else if (cycleopsphantomBike) {
|
||||
return cycleopsphantomBike;
|
||||
} else if (stagesBike) {
|
||||
return stagesBike;
|
||||
} else if (toorx) {
|
||||
@@ -3395,6 +3767,8 @@ bluetoothdevice *bluetooth::device() {
|
||||
return trxappgateusbBike;
|
||||
} else if (trxappgateusbElliptical) {
|
||||
return trxappgateusbElliptical;
|
||||
} else if (trxappgateusbRower) {
|
||||
return trxappgateusbRower;
|
||||
} else if (soleBike) {
|
||||
return soleBike;
|
||||
} else if (keepBike) {
|
||||
@@ -3433,10 +3807,14 @@ bluetoothdevice *bluetooth::device() {
|
||||
return echelonRower;
|
||||
} else if (echelonStride) {
|
||||
return echelonStride;
|
||||
} else if (echelonStairclimber) {
|
||||
return echelonStairclimber;
|
||||
} else if (octaneTreadmill) {
|
||||
return octaneTreadmill;
|
||||
} else if (ziproTreadmill) {
|
||||
return ziproTreadmill;
|
||||
} else if (lifespanTreadmill) {
|
||||
return lifespanTreadmill;
|
||||
} else if (octaneElliptical) {
|
||||
return octaneElliptical;
|
||||
} else if (ftmsRower) {
|
||||
@@ -3487,10 +3865,14 @@ bluetoothdevice *bluetooth::device() {
|
||||
return sportsTechElliptical;
|
||||
} else if (sportsPlusBike) {
|
||||
return sportsPlusBike;
|
||||
} else if (sportsPlusRower) {
|
||||
return sportsPlusRower;
|
||||
} else if (inspireBike) {
|
||||
return inspireBike;
|
||||
} else if (chronoBike) {
|
||||
return chronoBike;
|
||||
} else if (pitpatBike) {
|
||||
return pitpatBike;
|
||||
} else if (m3iBike) {
|
||||
return m3iBike;
|
||||
} else if (snodeBike) {
|
||||
@@ -3499,8 +3881,12 @@ bluetoothdevice *bluetooth::device() {
|
||||
return ftmsBike;
|
||||
} else if (wahooKickrSnapBike) {
|
||||
return wahooKickrSnapBike;
|
||||
} else if (technogymBike) {
|
||||
return technogymBike;
|
||||
} else if (horizonGr7Bike) {
|
||||
return horizonGr7Bike;
|
||||
} else if (kineticInroadBike) {
|
||||
return kineticInroadBike;
|
||||
} else if (renphoBike) {
|
||||
return renphoBike;
|
||||
} else if (pafersBike) {
|
||||
@@ -3516,6 +3902,8 @@ bluetoothdevice *bluetooth::device() {
|
||||
return computrainerBike;
|
||||
} else if (csafeRower) {
|
||||
return csafeRower;
|
||||
} else if (csafeElliptical) {
|
||||
return csafeElliptical;
|
||||
#endif
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
#ifndef Q_OS_IOS
|
||||
#include "devices/computrainerbike/computrainerbike.h"
|
||||
#include "devices/csaferower/csaferower.h"
|
||||
#include "devices/csafeelliptical/csafeelliptical.h"
|
||||
#endif
|
||||
#include "devices/concept2skierg/concept2skierg.h"
|
||||
#include "devices/crossrope/crossrope.h"
|
||||
#include "devices/cscbike/cscbike.h"
|
||||
#include "devices/cycleopsphantombike/cycleopsphantombike.h"
|
||||
#include "devices/deeruntreadmill/deerruntreadmill.h"
|
||||
#include "devices/domyosbike/domyosbike.h"
|
||||
#include "devices/domyoselliptical/domyoselliptical.h"
|
||||
@@ -44,8 +46,10 @@
|
||||
|
||||
#include "devices/echelonconnectsport/echelonconnectsport.h"
|
||||
#include "devices/echelonrower/echelonrower.h"
|
||||
#include "devices/echelonstairclimber/echelonstairclimber.h"
|
||||
#include "devices/eliteariafan/eliteariafan.h"
|
||||
#include "devices/eliterizer/eliterizer.h"
|
||||
#include "devices/elitesquarecontroller/elitesquarecontroller.h"
|
||||
#include "devices/elitesterzosmart/elitesterzosmart.h"
|
||||
#include "devices/eslinkertreadmill/eslinkertreadmill.h"
|
||||
#include "devices/fakebike/fakebike.h"
|
||||
@@ -67,12 +71,15 @@
|
||||
#include "devices/iconceptelliptical/iconceptelliptical.h"
|
||||
#include "devices/inspirebike/inspirebike.h"
|
||||
#include "devices/keepbike/keepbike.h"
|
||||
#include "devices/kineticinroadbike/kineticinroadbike.h"
|
||||
#include "devices/kingsmithr1protreadmill/kingsmithr1protreadmill.h"
|
||||
#include "devices/kingsmithr2treadmill/kingsmithr2treadmill.h"
|
||||
#include "devices/lifefitnesstreadmill/lifefitnesstreadmill.h"
|
||||
#include "devices/lifespantreadmill/lifespantreadmill.h"
|
||||
#include "devices/m3ibike/m3ibike.h"
|
||||
#include "devices/mcfbike/mcfbike.h"
|
||||
#include "devices/mepanelbike/mepanelbike.h"
|
||||
#include "devices/moxy5sensor/moxy5sensor.h"
|
||||
#include "devices/nautilusbike/nautilusbike.h"
|
||||
#include "devices/nautiluselliptical/nautiluselliptical.h"
|
||||
#include "devices/nautilustreadmill/nautilustreadmill.h"
|
||||
@@ -86,6 +93,7 @@
|
||||
#include "devices/pafersbike/pafersbike.h"
|
||||
#include "devices/paferstreadmill/paferstreadmill.h"
|
||||
#include "devices/pelotonbike/pelotonbike.h"
|
||||
#include "devices/pitpatbike/pitpatbike.h"
|
||||
#include "devices/proformbike/proformbike.h"
|
||||
#include "devices/proformelliptical/proformelliptical.h"
|
||||
#include "devices/proformellipticaltrainer/proformellipticaltrainer.h"
|
||||
@@ -110,8 +118,10 @@
|
||||
|
||||
#include "devices/spirittreadmill/spirittreadmill.h"
|
||||
#include "devices/sportsplusbike/sportsplusbike.h"
|
||||
#include "devices/sportsplusrower/sportsplusrower.h"
|
||||
#include "devices/sportstechbike/sportstechbike.h"
|
||||
#include "devices/sportstechelliptical/sportstechelliptical.h"
|
||||
#include "devices/sramAXSController/sramAXSController.h"
|
||||
#include "devices/stagesbike/stagesbike.h"
|
||||
|
||||
#include "devices/renphobike/renphobike.h"
|
||||
@@ -122,11 +132,13 @@
|
||||
#include "devices/echelonstride/echelonstride.h"
|
||||
|
||||
#include "templateinfosenderbuilder.h"
|
||||
#include "technogymbike/technogymbike.h"
|
||||
#include "devices/toorxtreadmill/toorxtreadmill.h"
|
||||
#include "devices/treadmill.h"
|
||||
#include "devices/truetreadmill/truetreadmill.h"
|
||||
#include "devices/trxappgateusbbike/trxappgateusbbike.h"
|
||||
#include "devices/trxappgateusbelliptical/trxappgateusbelliptical.h"
|
||||
#include "devices/trxappgateusbrower/trxappgateusbrower.h"
|
||||
#include "devices/trxappgateusbtreadmill/trxappgateusbtreadmill.h"
|
||||
#include "devices/ultrasportbike/ultrasportbike.h"
|
||||
#include "devices/wahookickrheadwind/wahookickrheadwind.h"
|
||||
@@ -175,8 +187,10 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
#ifndef Q_OS_IOS
|
||||
computrainerbike *computrainerBike = nullptr;
|
||||
csaferower *csafeRower = nullptr;
|
||||
csafeelliptical *csafeElliptical = nullptr;
|
||||
#endif
|
||||
concept2skierg *concept2Skierg = nullptr;
|
||||
cycleopsphantombike *cycleopsphantomBike = nullptr;
|
||||
deerruntreadmill *deerrunTreadmill = nullptr;
|
||||
domyostreadmill *domyos = nullptr;
|
||||
domyosbike *domyosBike = nullptr;
|
||||
@@ -192,10 +206,12 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
nautiluselliptical *nautilusElliptical = nullptr;
|
||||
nautilustreadmill *nautilusTreadmill = nullptr;
|
||||
trxappgateusbbike *trxappgateusbBike = nullptr;
|
||||
trxappgateusbrower *trxappgateusbRower = nullptr;
|
||||
trxappgateusbelliptical *trxappgateusbElliptical = nullptr;
|
||||
echelonconnectsport *echelonConnectSport = nullptr;
|
||||
yesoulbike *yesoulBike = nullptr;
|
||||
flywheelbike *flywheelBike = nullptr;
|
||||
moxy5sensor *moxy5Sensor = nullptr;
|
||||
nordictrackelliptical *nordictrackElliptical = nullptr;
|
||||
nordictrackifitadbtreadmill *nordictrackifitadbTreadmill = nullptr;
|
||||
nordictrackifitadbbike *nordictrackifitadbBike = nullptr;
|
||||
@@ -219,9 +235,11 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
truetreadmill *trueTreadmill = nullptr;
|
||||
horizongr7bike *horizonGr7Bike = nullptr;
|
||||
schwinnic4bike *schwinnIC4Bike = nullptr;
|
||||
technogymbike* technogymBike = nullptr;
|
||||
sportstechbike *sportsTechBike = nullptr;
|
||||
sportstechelliptical *sportsTechElliptical = nullptr;
|
||||
sportsplusbike *sportsPlusBike = nullptr;
|
||||
sportsplusrower *sportsPlusRower = nullptr;
|
||||
inspirebike *inspireBike = nullptr;
|
||||
snodebike *snodeBike = nullptr;
|
||||
eslinkertreadmill *eslinkerTreadmill = nullptr;
|
||||
@@ -242,7 +260,9 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
ftmsrower *ftmsRower = nullptr;
|
||||
smartrowrower *smartrowRower = nullptr;
|
||||
echelonstride *echelonStride = nullptr;
|
||||
echelonstairclimber *echelonStairclimber = nullptr;
|
||||
lifefitnesstreadmill *lifefitnessTreadmill = nullptr;
|
||||
lifespantreadmill *lifespanTreadmill = nullptr;
|
||||
keepbike *keepBike = nullptr;
|
||||
kingsmithr1protreadmill *kingsmithR1ProTreadmill = nullptr;
|
||||
kingsmithr2treadmill *kingsmithR2Treadmill = nullptr;
|
||||
@@ -250,6 +270,7 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
pafersbike *pafersBike = nullptr;
|
||||
paferstreadmill *pafersTreadmill = nullptr;
|
||||
tacxneo2 *tacxneo2Bike = nullptr;
|
||||
pitpatbike *pitpatBike = nullptr;
|
||||
renphobike *renphoBike = nullptr;
|
||||
shuaa5treadmill *shuaA5Treadmill = nullptr;
|
||||
heartratebelt *heartRateBelt = nullptr;
|
||||
@@ -262,6 +283,7 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
wahookickrsnapbike *wahooKickrSnapBike = nullptr;
|
||||
ypooelliptical *ypooElliptical = nullptr;
|
||||
ziprotreadmill *ziproTreadmill = nullptr;
|
||||
kineticinroadbike *kineticInroadBike = nullptr;
|
||||
strydrunpowersensor *powerTreadmill = nullptr;
|
||||
eliterizer *eliteRizer = nullptr;
|
||||
elitesterzosmart *eliteSterzoSmart = nullptr;
|
||||
@@ -274,6 +296,8 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
QList<eliteariafan *> eliteAriaFan;
|
||||
QList<zwiftclickremote* > zwiftPlayDevice;
|
||||
zwiftclickremote* zwiftClickRemote = nullptr;
|
||||
sramaxscontroller* sramAXSController = nullptr;
|
||||
elitesquarecontroller* eliteSquareController = nullptr;
|
||||
QString filterDevice = QLatin1String("");
|
||||
|
||||
bool testResistance = false;
|
||||
@@ -308,6 +332,7 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
bool eliteSterzoSmartAvaiable();
|
||||
bool fitmetriaFanfitAvaiable();
|
||||
bool zwiftDeviceAvaiable();
|
||||
bool sramDeviceAvaiable();
|
||||
bool fitmetria_fanfit_isconnected(QString name);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -346,6 +371,10 @@ class bluetooth : public QObject, public SignalHandler {
|
||||
void speedChanged(double);
|
||||
void inclinationChanged(double, double);
|
||||
void connectedAndDiscovered();
|
||||
void gearDown();
|
||||
void gearUp();
|
||||
void gearFailedDown();
|
||||
void gearFailedUp();
|
||||
|
||||
signals:
|
||||
};
|
||||
|
||||
@@ -108,6 +108,7 @@ QTime bluetoothdevice::maxPace() {
|
||||
|
||||
double bluetoothdevice::odometerFromStartup() { return Distance.valueRaw(); }
|
||||
double bluetoothdevice::odometer() { return Distance.value(); }
|
||||
double bluetoothdevice::lapOdometer() { return Distance.lapValue(); }
|
||||
metric bluetoothdevice::calories() { return KCal; }
|
||||
metric bluetoothdevice::jouls() { return m_jouls; }
|
||||
uint8_t bluetoothdevice::fanSpeed() { return FanSpeed; };
|
||||
@@ -149,6 +150,7 @@ double bluetoothdevice::inclinationDifficultOffset() { return m_inclination_diff
|
||||
void bluetoothdevice::cadenceSensor(uint8_t cadence) { Q_UNUSED(cadence) }
|
||||
void bluetoothdevice::powerSensor(uint16_t power) { Q_UNUSED(power) }
|
||||
void bluetoothdevice::speedSensor(double speed) { Q_UNUSED(speed) }
|
||||
void bluetoothdevice::inclinationSensor(double grade, double inclination) { Q_UNUSED(grade); Q_UNUSED(inclination) }
|
||||
void bluetoothdevice::instantaneousStrideLengthSensor(double length) { Q_UNUSED(length); }
|
||||
void bluetoothdevice::groundContactSensor(double groundContact) { Q_UNUSED(groundContact); }
|
||||
void bluetoothdevice::verticalOscillationSensor(double verticalOscillation) { Q_UNUSED(verticalOscillation); }
|
||||
@@ -254,6 +256,7 @@ void bluetoothdevice::update_hr_from_external() {
|
||||
h.setSpeed(Speed.value());
|
||||
h.setPower(m_watt.value());
|
||||
h.setCadence(Cadence.value());
|
||||
h.setSteps(StepCount.value());
|
||||
Heart = appleWatchHeartRate;
|
||||
qDebug() << "Current Heart from Apple Watch: " << QString::number(appleWatchHeartRate);
|
||||
#endif
|
||||
@@ -473,9 +476,9 @@ void bluetoothdevice::setGPXFile(QString filename) {
|
||||
}
|
||||
}
|
||||
|
||||
void bluetoothdevice::setHeartZone(double hz) {
|
||||
void bluetoothdevice::setHeartZone(double hz) {
|
||||
HeartZone = hz;
|
||||
if(isPaused() == false) {
|
||||
if(isPaused() == false && currentHeart().value() > 0) {
|
||||
hz = hz - 1;
|
||||
if(hz >= maxHeartZone() ) {
|
||||
hrZonesSeconds[maxHeartZone() - 1].setValue(hrZonesSeconds[maxHeartZone() - 1].value() + 1);
|
||||
|
||||
@@ -146,6 +146,11 @@ class bluetoothdevice : public QObject {
|
||||
*/
|
||||
virtual QTime lapElapsedTime();
|
||||
|
||||
/**
|
||||
* @brief lapOdometer Gets the distance elapsed on the current lap.
|
||||
*/
|
||||
virtual double lapOdometer();
|
||||
|
||||
/**
|
||||
* @brief connected Gets a value to indicate if the device is connected.
|
||||
*/
|
||||
@@ -216,6 +221,18 @@ class bluetoothdevice : public QObject {
|
||||
*/
|
||||
metric wattsMetric();
|
||||
|
||||
/**
|
||||
* @brief wattsMetricforUi Show the wattage applying averaging in case the user requested this. Units: watts
|
||||
*/
|
||||
double wattsMetricforUI() {
|
||||
QSettings settings;
|
||||
bool power5s = settings.value(QZSettings::power_avg_5s, QZSettings::default_power_avg_5s).toBool();
|
||||
if (power5s)
|
||||
return wattsMetric().average5s();
|
||||
else
|
||||
return wattsMetric().value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief changeFanSpeed Tries to change the fan speed.
|
||||
* @param speed The requested fan speed. Units: depends on device
|
||||
@@ -401,7 +418,7 @@ class bluetoothdevice : public QObject {
|
||||
*/
|
||||
void setTargetPowerZone(double pz) { TargetPowerZone = pz; }
|
||||
|
||||
enum BLUETOOTH_TYPE { UNKNOWN = 0, TREADMILL, BIKE, ROWING, ELLIPTICAL, JUMPROPE };
|
||||
enum BLUETOOTH_TYPE { UNKNOWN = 0, TREADMILL, BIKE, ROWING, ELLIPTICAL, JUMPROPE, STAIRCLIMBER };
|
||||
enum WORKOUT_EVENT_STATE { STARTED = 0, PAUSED = 1, RESUMED = 2, STOPPED = 3 };
|
||||
|
||||
/**
|
||||
@@ -433,6 +450,7 @@ class bluetoothdevice : public QObject {
|
||||
virtual void cadenceSensor(uint8_t cadence);
|
||||
virtual void powerSensor(uint16_t power);
|
||||
virtual void speedSensor(double speed);
|
||||
virtual void inclinationSensor(double grade, double inclination);
|
||||
virtual void changeResistance(resistance_t res);
|
||||
virtual void changePower(int32_t power);
|
||||
virtual void changeInclination(double grade, double percentage);
|
||||
@@ -664,6 +682,11 @@ class bluetoothdevice : public QObject {
|
||||
* @brief _ergTable The current erg table
|
||||
*/
|
||||
ergTable _ergTable;
|
||||
|
||||
/**
|
||||
* @brief StepCount A metric to get and set the step count. Unit: step
|
||||
*/
|
||||
metric StepCount;
|
||||
|
||||
/**
|
||||
* @brief Collect the number of seconds in each zone for the current heart rate
|
||||
|
||||
@@ -64,8 +64,24 @@ csafe::csafe() {
|
||||
cmds["CSAFE_PM_GET_WORKTIME"] = populateCmd(0xa0, QList<int>(), 0x1a);
|
||||
cmds["CSAFE_PM_GET_WORKDISTANCE"] = populateCmd(0xa3, QList<int>(), 0x1a);
|
||||
|
||||
// LIFE FITNESS specific commands
|
||||
cmds["CSAFE_LF_GET_DETAIL"] = populateCmd(0xd0, QList<int>());
|
||||
|
||||
// Generic long commands
|
||||
cmds["CSAFE_SETPROGRAM_CMD"] = populateCmd(0x24, QList<int>() << 1 << 1);
|
||||
cmds["CSAFE_SETUSERINFO_CMD"] = populateCmd(0x2B, QList<int>() << 2 << 1 << 1 << 1);
|
||||
cmds["CSAFE_SETLEVEL_CMD"] = populateCmd(0x2D, QList<int>() << 1);
|
||||
|
||||
// Generic Short Commands
|
||||
cmds["CSAFE_GETSTATUS_CMD"] = populateCmd(0x80, QList<int>());
|
||||
cmds["CSAFE_GOINUSE_CMD"] = populateCmd(0x85, QList<int>());
|
||||
cmds["CSAFE_GETCALORIES_CMD"] = populateCmd(0xa3, QList<int>());
|
||||
cmds["CSAFE_GETPROGRAM_CMD"] = populateCmd(0xA4, QList<int>());
|
||||
cmds["CSAFE_GETPACE_CMD"] = populateCmd(0xa6, QList<int>());
|
||||
cmds["CSAFE_GETCADENCE_CMD"] = populateCmd(0xa7, QList<int>());
|
||||
cmds["CSAFE_GETHORIZONTAL_CMD"] = populateCmd(0xA1, QList<int>());
|
||||
cmds["CSAFE_GETSPEED_CMD"] = populateCmd(0xA5, QList<int>());
|
||||
cmds["CSAFE_GETUSERINFO_CMD"] = populateCmd(0xAB, QList<int>());
|
||||
cmds["CSAFE_GETHRCUR_CMD"] = populateCmd(0xb0, QList<int>());
|
||||
cmds["CSAFE_GETPOWER_CMD"] = populateCmd(0xb4, QList<int>());
|
||||
|
||||
@@ -87,7 +103,8 @@ csafe::csafe() {
|
||||
resp[0xA0] = qMakePair(QString("CSAFE_GETTWORK_CMD"), QList<int>() << 1 << 1 << 1);
|
||||
resp[0xA1] = qMakePair(QString("CSAFE_GETHORIZONTAL_CMD"), QList<int>() << 2 << 1);
|
||||
resp[0xA3] = qMakePair(QString("CSAFE_GETCALORIES_CMD"), QList<int>() << 2);
|
||||
resp[0xA4] = qMakePair(QString("CSAFE_GETPROGRAM_CMD"), QList<int>() << 1);
|
||||
resp[0xA4] = qMakePair(QString("CSAFE_GETPROGRAM_CMD"), QList<int>() << 1 << 1);
|
||||
resp[0xA5] = qMakePair(QString("CSAFE_GETSPEED_CMD"), QList<int>() << 2 << 1);
|
||||
resp[0xA6] = qMakePair(QString("CSAFE_GETPACE_CMD"), QList<int>() << 2 << 1);
|
||||
resp[0xA7] = qMakePair(QString("CSAFE_GETCADENCE_CMD"), QList<int>() << 2 << 1);
|
||||
resp[0xAB] = qMakePair(QString("CSAFE_GETUSERINFO_CMD"), QList<int>() << 2 << 1 << 1 << 1);
|
||||
@@ -105,6 +122,8 @@ csafe::csafe() {
|
||||
resp[0x21] = qMakePair(QString("CSAFE_SETHORIZONTAL_CMD"), QList<int>() << 0);
|
||||
resp[0x23] = qMakePair(QString("CSAFE_SETCALORIES_CMD"), QList<int>() << 0);
|
||||
resp[0x24] = qMakePair(QString("CSAFE_SETPROGRAM_CMD"), QList<int>() << 0);
|
||||
resp[0x2B] = qMakePair(QString("CSAFE_SETUSERINFO_CMD"), QList<int>() << 0);
|
||||
resp[0x2D] = qMakePair(QString("CSAFE_SETLEVEL_CMD"), QList<int>() << 0);
|
||||
resp[0x34] = qMakePair(QString("CSAFE_SETPOWER_CMD"), QList<int>() << 0);
|
||||
resp[0x70] = qMakePair(QString("CSAFE_GETCAPS_CMD"), QList<int>() << 11);
|
||||
|
||||
@@ -129,6 +148,9 @@ csafe::csafe() {
|
||||
resp[0x1A6C] =
|
||||
qMakePair(QString("CSAFE_PM_GET_HEARTBEATDATA"),
|
||||
QList<int>() << 1 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2);
|
||||
|
||||
// LIFE FITNESS specific response
|
||||
resp[0xD0] = qMakePair(QString("CSAFE_LF_GET_DETAIL"), QList<int>() << 0x1c);
|
||||
}
|
||||
|
||||
QList<QList<int>> csafe::populateCmd(int First, QList<int> Second, int Third) {
|
||||
@@ -140,7 +162,9 @@ QList<QList<int>> csafe::populateCmd(int First, QList<int> Second, int Third) {
|
||||
second.clear();
|
||||
third.clear();
|
||||
first.append(First);
|
||||
foreach (int a, Second) { second.append(a); }
|
||||
foreach (int a, Second) {
|
||||
second.append(a);
|
||||
}
|
||||
third.append(Third);
|
||||
ret.append(first);
|
||||
ret.append(second);
|
||||
@@ -155,7 +179,9 @@ QList<QList<int>> csafe::populateCmd(int First, QList<int> Second) {
|
||||
first.clear();
|
||||
second.clear();
|
||||
first.append(First);
|
||||
foreach (int a, Second) { second.append(a); }
|
||||
foreach (int a, Second) {
|
||||
second.append(a);
|
||||
}
|
||||
ret.append(first);
|
||||
ret.append(second);
|
||||
return ret;
|
||||
@@ -195,7 +221,7 @@ QString csafe::bytes2ascii(const QVector<quint8> &raw_bytes) {
|
||||
return word;
|
||||
}
|
||||
|
||||
QByteArray csafe::write(const QStringList &arguments) {
|
||||
QByteArray csafe::write(const QStringList &arguments, bool surround_msg) {
|
||||
int i = 0;
|
||||
QVector<quint8> message;
|
||||
int wrapper = 0;
|
||||
@@ -204,6 +230,10 @@ QByteArray csafe::write(const QStringList &arguments) {
|
||||
|
||||
while (i < arguments.size()) {
|
||||
QString arg = arguments[i];
|
||||
if (!cmds.contains(arg)) {
|
||||
qWarning("CSAFE Command not implemented: %s", qPrintable(arg));
|
||||
return QByteArray();
|
||||
}
|
||||
const auto &cmdprop = cmds[arg];
|
||||
QVector<quint8> command;
|
||||
|
||||
@@ -290,27 +320,31 @@ QByteArray csafe::write(const QStringList &arguments) {
|
||||
qWarning("Message is too long: %d", message.size());
|
||||
}
|
||||
|
||||
int maxmessage = qMax(message.size() + 1, maxresponse); // report IDs
|
||||
if (surround_msg) { // apply non-standard wrapping for PM3 rower
|
||||
int maxmessage = qMax(message.size() + 1, maxresponse); // report IDs
|
||||
|
||||
if (maxmessage <= 21) {
|
||||
message.prepend(0x01);
|
||||
message.append(QVector<quint8>(21 - message.size()));
|
||||
} else if (maxmessage <= 63) {
|
||||
message.prepend(0x04);
|
||||
message.append(QVector<quint8>(63 - message.size()));
|
||||
} else if ((message.size() + 1) <= 121) {
|
||||
message.prepend(0x02);
|
||||
message.append(QVector<quint8>(121 - message.size()));
|
||||
if (maxresponse > 121) {
|
||||
qWarning("Response may be too long to receive. Max possible length: %d", maxresponse);
|
||||
if (maxmessage <= 21) {
|
||||
message.prepend(0x01);
|
||||
message.append(QVector<quint8>(21 - message.size()));
|
||||
} else if (maxmessage <= 63) {
|
||||
message.prepend(0x04);
|
||||
message.append(QVector<quint8>(63 - message.size()));
|
||||
} else if ((message.size() + 1) <= 121) {
|
||||
message.prepend(0x02);
|
||||
message.append(QVector<quint8>(121 - message.size()));
|
||||
if (maxresponse > 121) {
|
||||
qWarning("Response may be too long to receive. Max possible length: %d", maxresponse);
|
||||
}
|
||||
} else {
|
||||
qWarning("Message too long. Message length: %d", message.size());
|
||||
message.clear();
|
||||
}
|
||||
} else {
|
||||
qWarning("Message too long. Message length: %d", message.size());
|
||||
message.clear();
|
||||
}
|
||||
|
||||
QByteArray ret;
|
||||
foreach (int a, message) { ret.append(a); }
|
||||
foreach (int a, message) {
|
||||
ret.append(a);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -325,9 +359,7 @@ QVector<quint8> csafe::check_message(QVector<quint8> message) {
|
||||
quint8 stuffvalue = message.takeAt(i + 1);
|
||||
message[i] = 0xF0 | stuffvalue;
|
||||
}
|
||||
|
||||
checksum ^= message[i]; // calculate checksum
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
@@ -345,15 +377,21 @@ QVector<quint8> csafe::check_message(QVector<quint8> message) {
|
||||
QVariantMap csafe::read(const QVector<quint8> &transmission) {
|
||||
QVector<quint8> message;
|
||||
bool stopfound = false;
|
||||
|
||||
int startflag = transmission[1];
|
||||
|
||||
int j = 0;
|
||||
if (startflag == Extended_Frame_Start_Flag) {
|
||||
j = 4;
|
||||
} else if (startflag == Standard_Frame_Start_Flag) {
|
||||
j = 2;
|
||||
} else {
|
||||
while (j < transmission.size()) {
|
||||
int startflag = transmission[j];
|
||||
if (startflag == Extended_Frame_Start_Flag) {
|
||||
j = j + 3;
|
||||
break;
|
||||
} else if (startflag == Standard_Frame_Start_Flag) {
|
||||
++j;
|
||||
break;
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (j >= transmission.size()) {
|
||||
qWarning("No Start Flag found.");
|
||||
return QVariantMap();
|
||||
}
|
||||
@@ -371,7 +409,6 @@ QVariantMap csafe::read(const QVector<quint8> &transmission) {
|
||||
qWarning("No Stop Flag found.");
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
message = check_message(message);
|
||||
int status = message.takeFirst();
|
||||
|
||||
@@ -34,7 +34,7 @@ class csafe {
|
||||
|
||||
public:
|
||||
csafe();
|
||||
QByteArray write(const QStringList &arguments);
|
||||
QByteArray write(const QStringList &arguments , bool surround_msg = false); //surround_msg is for wrapping the communication in CSAFE non-standard way for some devices like PM3
|
||||
QVector<quint8> check_message(QVector<quint8> message);
|
||||
QVariantMap read(const QVector<quint8> &transmission);
|
||||
};
|
||||
128
src/devices/csafe/csaferunner.cpp
Normal file
128
src/devices/csafe/csaferunner.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "csaferunner.h"
|
||||
|
||||
CsafeRunnerThread::CsafeRunnerThread() {}
|
||||
|
||||
CsafeRunnerThread::CsafeRunnerThread(QString deviceFileName, int sleepTime) {
|
||||
setDevice(deviceFileName);
|
||||
setSleepTime(sleepTime);
|
||||
}
|
||||
|
||||
void CsafeRunnerThread::setDevice(const QString &device) { deviceName = device; }
|
||||
|
||||
void CsafeRunnerThread::setBaudRate(uint32_t _baudRate) { baudRate = _baudRate; }
|
||||
|
||||
void CsafeRunnerThread::setSleepTime(int time) { sleepTime = time; }
|
||||
|
||||
void CsafeRunnerThread::addRefreshCommand(const QStringList &commands) {
|
||||
mutex.lock();
|
||||
refreshCommands.append(commands);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void CsafeRunnerThread::sendCommand(const QStringList &commands) {
|
||||
mutex.lock();
|
||||
if (commandQueue.size() < MAX_QUEUE_SIZE) {
|
||||
commandQueue.enqueue(commands);
|
||||
mutex.unlock();
|
||||
} else {
|
||||
qDebug() << "CSAFE port commands QUEUE FULL. Dropping commands" << commands;
|
||||
}
|
||||
}
|
||||
|
||||
void CsafeRunnerThread::run() {
|
||||
|
||||
int rc = 0;
|
||||
|
||||
SerialHandler *serial = SerialHandler::create(deviceName, baudRate);
|
||||
serial->setEndChar(0xf2); // end of frame for CSAFE
|
||||
serial->setTimeout(1200); // CSAFE spec specifies 1s timeout
|
||||
|
||||
csafe *csafeInstance = new csafe();
|
||||
int connectioncounter = 20; // counts timeouts. If 10 timeouts in a row, then the port is closed and reopened
|
||||
int refresh_nr = -1;
|
||||
QStringList refreshCommand = {};
|
||||
|
||||
while (1) {
|
||||
|
||||
if (connectioncounter > 10 || !serial->isOpen()) {
|
||||
serial->closePort();
|
||||
rc = serial->openPort();
|
||||
if (rc != 0) {
|
||||
emit portAvailable(false);
|
||||
connectioncounter++;
|
||||
qDebug() << "Error opening serial port " << deviceName << "rc=" << rc << " sleeping for "
|
||||
<< "5s";
|
||||
QThread::msleep(5000);
|
||||
continue;
|
||||
} else {
|
||||
emit portAvailable(true);
|
||||
connectioncounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int elapsed = 0;
|
||||
while (elapsed < sleepTime || sleepTime == -1) {
|
||||
QThread::msleep(50);
|
||||
elapsed += 50;
|
||||
// TODO: does not seem to work with netsocket as intended. (no data available)
|
||||
// Needs further testing, maybe because the port is already closed and needs to remain open.
|
||||
// No issue for current implementations as they do not use unsolicited slave data / cmdAutoUpload .
|
||||
if (serial->dataAvailable() > 0 || !commandQueue.isEmpty()) {
|
||||
qDebug() << "CSAFE port data available. " << serial->dataAvailable() << " bytes"
|
||||
<< "commands in queue: " << commandQueue.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray ret;
|
||||
mutex.lock();
|
||||
if (!commandQueue.isEmpty()) {
|
||||
ret = csafeInstance->write(commandQueue.dequeue());
|
||||
qDebug() << "CSAFE port commands processed from queue. Remaining commands in queue: "
|
||||
<< commandQueue.size();
|
||||
} else {
|
||||
if (!(elapsed < sleepTime) || !refreshCommands.isEmpty()) {
|
||||
if (refreshCommands.length() > 0) {
|
||||
refresh_nr++;
|
||||
if (refresh_nr >= refreshCommands.length()) {
|
||||
refresh_nr = 0;
|
||||
}
|
||||
QStringList refreshCommand = refreshCommands[refresh_nr];
|
||||
ret = csafeInstance->write(refreshCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex.unlock();
|
||||
|
||||
if (!ret.isEmpty()) { // we have commands to send
|
||||
qDebug() << "CSAFE >> " << ret.toHex(' ');
|
||||
rc = serial->rawWrite((uint8_t *)ret.data(), ret.length());
|
||||
if (rc < 0) {
|
||||
qDebug() << "Error writing serial port " << deviceName << "rc=" << rc;
|
||||
connectioncounter++;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "CSAFE Slave unsolicited data present.";
|
||||
}
|
||||
|
||||
static uint8_t rx[120];
|
||||
rc = serial->rawRead(rx, 120, true);
|
||||
if (rc > 0) {
|
||||
qDebug() << "CSAFE << " << QByteArray::fromRawData((const char *)rx, rc).toHex(' ') << " (" << rc << ")";
|
||||
connectioncounter = 0;
|
||||
} else {
|
||||
qDebug() << "Error reading serial port " << deviceName << " rc=" << rc;
|
||||
connectioncounter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
QVector<quint8> v;
|
||||
for (int i = 0; i < rc; i++)
|
||||
v.append(rx[i]);
|
||||
QVariantMap frame = csafeInstance->read(v);
|
||||
emit onCsafeFrame(frame);
|
||||
memset(rx, 0x00, sizeof(rx));
|
||||
}
|
||||
serial->closePort();
|
||||
}
|
||||
44
src/devices/csafe/csaferunner.h
Normal file
44
src/devices/csafe/csaferunner.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "csafe.h"
|
||||
#include "qzsettings.h"
|
||||
#include "serialhandler.h"
|
||||
#include <QDebug>
|
||||
#include <QMutex>
|
||||
#include <QQueue>
|
||||
#include <QSettings>
|
||||
#include <QThread>
|
||||
#include <QVariantMap>
|
||||
#include <QVector>
|
||||
|
||||
#define MAX_QUEUE_SIZE 100
|
||||
/**
|
||||
* @brief The CsafeRunnerThread class is a thread that runs the CSAFE protocol interaction.
|
||||
* It periodically sends the refresh commands processes the responses.
|
||||
* It also allows sending additional commands to the device.
|
||||
*/
|
||||
class CsafeRunnerThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CsafeRunnerThread();
|
||||
explicit CsafeRunnerThread(QString deviceFileName, int sleepTime = 200);
|
||||
void setDevice(const QString &device);
|
||||
void setBaudRate(uint32_t baudRate = 9600);
|
||||
void setSleepTime(int time);
|
||||
void addRefreshCommand(const QStringList &commands);
|
||||
void run();
|
||||
|
||||
public slots:
|
||||
void sendCommand(const QStringList &commands);
|
||||
|
||||
signals:
|
||||
void onCsafeFrame(const QVariantMap &frame);
|
||||
void portAvailable(bool available);
|
||||
|
||||
private:
|
||||
QString deviceName;
|
||||
uint32_t baudRate = 9600;
|
||||
int sleepTime = 200;
|
||||
QList<QStringList> refreshCommands;
|
||||
QQueue<QStringList> commandQueue;
|
||||
QMutex mutex;
|
||||
};
|
||||
101
src/devices/csafe/csafeutility.cpp
Normal file
101
src/devices/csafe/csafeutility.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "csafeutility.h"
|
||||
|
||||
// Static data map initialization
|
||||
const QMap<int, QPair<QString, double>> CSafeUtility::unitData = {{1, {"mile", 1609.34}},
|
||||
{2, {"0.1 mile", 160.934}},
|
||||
{3, {"0.01 mile", 16.0934}},
|
||||
{4, {"0.001 mile", 1.60934}},
|
||||
{5, {"ft", 0.3048}},
|
||||
{6, {"inch", 0.0254}},
|
||||
{7, {"lbs.", 0.453592}},
|
||||
{8, {"0.1 lbs.", 0.0453592}},
|
||||
{10, {"10 ft", 3.048}},
|
||||
{16, {"mile/hour", 0.44704}},
|
||||
{17, {"0.1 mile/hour", 0.044704}},
|
||||
{18, {"0.01 mile/hour", 0.0044704}},
|
||||
{19, {"ft/minute", 0.00508}},
|
||||
{33, {"Km", 1000}},
|
||||
{34, {"0.1 km", 100}},
|
||||
{35, {"0.01 km", 10}},
|
||||
{36, {"Meter", 1}},
|
||||
{37, {"0.1 meter", 0.1}},
|
||||
{38, {"Cm", 0.01}},
|
||||
{39, {"Kg", 1}},
|
||||
{40, {"0.1 kg", 0.1}},
|
||||
{48, {"Km/hour", 0.277778}},
|
||||
{49, {"0.1 Km/hour", 0.0277778}},
|
||||
{50, {"0.01 Km/hour", 0.00277778}},
|
||||
{51, {"Meter/minute", 0.0166667}},
|
||||
{55, {"Minutes/mile", 1}},
|
||||
{56, {"Minutes/km", 1}},
|
||||
{57, {"Seconds/km", 1}},
|
||||
{58, {"Seconds/mile", 1}},
|
||||
{65, {"floors", 1}},
|
||||
{66, {"0.1 floors", 0.1}},
|
||||
{67, {"steps", 1}},
|
||||
{68, {"revolutions", 1}},
|
||||
{69, {"strides", 1}},
|
||||
{70, {"strokes", 1}},
|
||||
{71, {"beats", 1}},
|
||||
{72, {"calories", 1}},
|
||||
{73, {"Kp", 1}},
|
||||
{74, {"% grade", 1}},
|
||||
{75, {"0.01 % grade", 0.01}},
|
||||
{76, {"0.1 % grade", 0.1}},
|
||||
{79, {"0.1 floors/minute", 0.1}},
|
||||
{80, {"floors/minute", 1}},
|
||||
{81, {"steps/minute", 1}},
|
||||
{82, {"revs/minute", 1}},
|
||||
{83, {"strides/minute", 1}},
|
||||
{84, {"stokes/minute", 1}},
|
||||
{85, {"beats/minute", 1}},
|
||||
{86, {"calories/minute", 1}},
|
||||
{87, {"calories/hour", 1}},
|
||||
{88, {"Watts", 1}},
|
||||
{89, {"Kpm", 1}},
|
||||
{90, {"Inch-Lb", 1}},
|
||||
{91, {"Foot-Lb", 1}},
|
||||
{92, {"Newton-Meters", 1}},
|
||||
{97, {"Amperes", 1}},
|
||||
{98, {"0.001 Amperes", 0.001}},
|
||||
{99, {"Volts", 1}},
|
||||
{100, {"0.001 Volts", 0.001}}};
|
||||
|
||||
QString CSafeUtility::getUnitName(int unitCode) {
|
||||
if (unitData.contains(unitCode)) {
|
||||
return unitData[unitCode].first; // Return the description
|
||||
}
|
||||
return "Unknown unit";
|
||||
}
|
||||
|
||||
double CSafeUtility::convertToStandard(int unitCode, double value) {
|
||||
if (unitData.contains(unitCode)) {
|
||||
return value * unitData[unitCode].second; // Apply the conversion factor
|
||||
}
|
||||
return value; // Return the original value if no conversion factor is available
|
||||
}
|
||||
|
||||
QString CSafeUtility::statusByteToText(int status) {
|
||||
switch (status) {
|
||||
case 0x00:
|
||||
return "Error";
|
||||
case 0x01:
|
||||
return "Ready";
|
||||
case 0x02:
|
||||
return "Idle";
|
||||
case 0x03:
|
||||
return "Have ID";
|
||||
case 0x05:
|
||||
return "In Use";
|
||||
case 0x06:
|
||||
return "Pause";
|
||||
case 0x07:
|
||||
return "Finish";
|
||||
case 0x08:
|
||||
return "Manual";
|
||||
case 0x09:
|
||||
return "Off line";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
44
src/devices/csafe/csafeutility.h
Normal file
44
src/devices/csafe/csafeutility.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Marcel Verpaalen (marcel@verpaalen.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* This emulates a serial port over a network connection.
|
||||
* e.g. as created by ser2net or hardware serial to ethernet converters
|
||||
*
|
||||
*/
|
||||
#ifndef CSAFEUTILITY_H
|
||||
#define CSAFEUTILITY_H
|
||||
|
||||
#include <cstdint> // For uint8_t
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QPair>
|
||||
|
||||
/**
|
||||
* @brief This class contains some utility functions supporting the CSAFE protocol
|
||||
*/
|
||||
class CSafeUtility {
|
||||
public:
|
||||
static QString getUnitName(int unitCode);
|
||||
static double convertToStandard(int unitCode, double value);
|
||||
static QString statusByteToText(int statusByte);
|
||||
|
||||
private:
|
||||
// Static map to hold unit data
|
||||
static const QMap<int, QPair<QString, double>> unitData;
|
||||
};
|
||||
|
||||
#endif // CSAFEUTILITY_H
|
||||
28
src/devices/csafe/kalmanfilter.cpp
Normal file
28
src/devices/csafe/kalmanfilter.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "kalmanfilter.h"
|
||||
|
||||
KalmanFilter::KalmanFilter(double measure_error, double estimate_error, double process_noise_q, double initial_Value) {
|
||||
setMeasurementError(measure_error);
|
||||
setEstimateError(estimate_error);
|
||||
setProcessNoise(process_noise_q);
|
||||
estimate = initial_Value;
|
||||
}
|
||||
|
||||
KalmanFilter::~KalmanFilter() {};
|
||||
|
||||
double KalmanFilter::updateEstimate(double measurement) {
|
||||
estimate_error += process_noise_q;
|
||||
gain = estimate_error / (estimate_error + measure_error);
|
||||
estimate += gain * (measurement - estimate);
|
||||
estimate_error *= (1 - gain);
|
||||
return estimate;
|
||||
}
|
||||
|
||||
void KalmanFilter::setMeasurementError(double measure_err) { measure_error = measure_err; }
|
||||
|
||||
void KalmanFilter::setEstimateError(double estimate_err) { estimate_error = estimate_err; }
|
||||
|
||||
void KalmanFilter::setProcessNoise(double noise_q) { process_noise_q = noise_q; }
|
||||
|
||||
double KalmanFilter::getGain() { return gain; }
|
||||
|
||||
double KalmanFilter::getEstimateError() { return estimate_error; }
|
||||
52
src/devices/csafe/kalmanfilter.h
Normal file
52
src/devices/csafe/kalmanfilter.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Marcel Verpaalen (marcel@verpaalen.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* This emulates a serial port over a network connection.
|
||||
* e.g. as created by ser2net or hardware serial to ethernet converters
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KALMAN_FILTER_H
|
||||
#define KALMAN_FILTER_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/**
|
||||
* @brief A simple Kalman filter for smoothing noisy data.
|
||||
*/
|
||||
class KalmanFilter {
|
||||
|
||||
public:
|
||||
KalmanFilter(double measure_error, double estimate_error, double process_noise_q = 0, double initial_Value = 0);
|
||||
~KalmanFilter();
|
||||
|
||||
double updateEstimate(double measurement);
|
||||
void setMeasurementError(double measure_err);
|
||||
void setEstimateError(double estimate_err);
|
||||
void setProcessNoise(double noise_q);
|
||||
double getGain();
|
||||
double getEstimateError();
|
||||
|
||||
private:
|
||||
double measure_error;
|
||||
double estimate_error;
|
||||
double process_noise_q;
|
||||
double estimate = 0;
|
||||
double gain = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
118
src/devices/csafe/netserial.cpp
Normal file
118
src/devices/csafe/netserial.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "netserial.h"
|
||||
|
||||
NetSerial::NetSerial(QString deviceFilename) : socket(new QTcpSocket()), _timeout(1000), endChar('\n') {
|
||||
setDevice(deviceFilename);
|
||||
}
|
||||
|
||||
NetSerial::~NetSerial() {
|
||||
closePort();
|
||||
delete socket;
|
||||
}
|
||||
|
||||
void NetSerial::setTimeout(int timeout) { this->_timeout = timeout; }
|
||||
|
||||
void NetSerial::setDevice(const QString &devname) {
|
||||
if (!devname.isEmpty()) {
|
||||
deviceFilename = devname;
|
||||
parseDeviceFilename(devname);
|
||||
}
|
||||
}
|
||||
|
||||
void NetSerial::setEndChar(uint8_t endChar) { this->endChar = endChar; }
|
||||
|
||||
bool NetSerial::isOpen() const { return socket->state() == QAbstractSocket::ConnectedState; }
|
||||
|
||||
int NetSerial::openPort() {
|
||||
if (serverAddress.isEmpty() || serverPort == 0) {
|
||||
qDebug() << "Invalid server address or port";
|
||||
return -1;
|
||||
}
|
||||
|
||||
socket->connectToHost(serverAddress, serverPort);
|
||||
if (!socket->waitForConnected(_timeout)) {
|
||||
qDebug() << "Failed to connect to server:" << socket->errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NetSerial::closePort() {
|
||||
if (isOpen()) {
|
||||
socket->disconnectFromHost();
|
||||
if (socket->state() != QAbstractSocket::UnconnectedState) {
|
||||
socket->waitForDisconnected(_timeout);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NetSerial::dataAvailable() {
|
||||
if (!isOpen()) {
|
||||
qDebug() << "Socket not connected.";
|
||||
return -1;
|
||||
}
|
||||
if (socket->bytesAvailable() > 0) {
|
||||
qDebug() << "Socket data is available!!!!!!!!!!!!!!.";
|
||||
}
|
||||
return socket->bytesAvailable();
|
||||
}
|
||||
|
||||
int NetSerial::rawWrite(uint8_t *bytes, int size) {
|
||||
if (!isOpen()) {
|
||||
qDebug() << "Socket not connected.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QByteArray data(reinterpret_cast<const char *>(bytes), size);
|
||||
qint64 bytesWritten = socket->write(data);
|
||||
if (bytesWritten == -1) {
|
||||
qDebug() << "Failed to write to socket:" << socket->errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!socket->waitForBytesWritten(_timeout)) {
|
||||
qDebug() << "Write operation timed out.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return static_cast<int>(bytesWritten);
|
||||
}
|
||||
|
||||
int NetSerial::rawRead(uint8_t bytes[], int size, bool line) {
|
||||
if (!isOpen()) {
|
||||
qDebug() << "Socket not connected.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QByteArray buffer;
|
||||
while (buffer.size() < size) {
|
||||
if (!socket->waitForReadyRead(_timeout)) {
|
||||
qDebug() << "Read operation timed out.";
|
||||
return buffer.size() > 0 ? buffer.size() : -1;
|
||||
}
|
||||
|
||||
buffer.append(socket->read(size - buffer.size()));
|
||||
if (line && buffer.contains(static_cast<char>(endChar))) {
|
||||
int index = buffer.indexOf(static_cast<char>(endChar)) + 1;
|
||||
memcpy(bytes, buffer.data(), index);
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(bytes, buffer.data(), buffer.size());
|
||||
return buffer.size();
|
||||
}
|
||||
|
||||
bool NetSerial::parseDeviceFilename(const QString &filename) {
|
||||
// Format: "server:port", e.g., "127.0.0.1:12345"
|
||||
QStringList parts = filename.split(':');
|
||||
if (parts.size() == 2) {
|
||||
serverAddress = parts[0];
|
||||
serverPort = parts[1].toUShort();
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Invalid device filename format. Expected 'server:port'.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
61
src/devices/csafe/netserial.h
Normal file
61
src/devices/csafe/netserial.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Marcel Verpaalen (marcel@verpaalen.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NETSERIAL_H
|
||||
#define NETSERIAL_H
|
||||
|
||||
#include "serialhandler.h"
|
||||
#include <QString>
|
||||
#include <QHostAddress>
|
||||
#include <QTcpSocket>
|
||||
#include <QDebug>
|
||||
|
||||
/**
|
||||
* @brief This is a simple implementation of serial port emulation over TCP
|
||||
* It emulates a serial port over a network connection.
|
||||
* e.g. as created by ser2net or hardware serial to ethernet converters
|
||||
*/
|
||||
class NetSerial : public SerialHandler {
|
||||
public:
|
||||
NetSerial(QString deviceFilename);
|
||||
~NetSerial() ;
|
||||
|
||||
int openPort() override;
|
||||
int closePort() override;
|
||||
int dataAvailable() override;
|
||||
int rawWrite(uint8_t *bytes, int size) override;
|
||||
int rawRead(uint8_t bytes[], int size, bool line = false) override;
|
||||
|
||||
bool isOpen() const override;
|
||||
void setTimeout(int timeout) override;
|
||||
void setEndChar(uint8_t endChar) override;
|
||||
void setDevice(const QString &devname) override;
|
||||
|
||||
private:
|
||||
QString deviceFilename;
|
||||
QString serverAddress;
|
||||
quint16 serverPort;
|
||||
QTcpSocket *socket;
|
||||
int _timeout = 1000; // Timeout in milliseconds
|
||||
uint8_t endChar = '\n';
|
||||
bool parseDeviceFilename(const QString &filename);
|
||||
};
|
||||
|
||||
#endif // NETSERIAL_H
|
||||
13
src/devices/csafe/serialhandler.cpp
Normal file
13
src/devices/csafe/serialhandler.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "serialhandler.h"
|
||||
#include "netserial.h"
|
||||
#include "serialport.h"
|
||||
|
||||
SerialHandler *SerialHandler::create(const QString &deviceFilename, uint32_t baudRate) {
|
||||
if (deviceFilename.contains(':')) {
|
||||
qDebug() << "Using NetSerial for device:" << deviceFilename;
|
||||
return new NetSerial(deviceFilename);
|
||||
} else {
|
||||
qDebug() << "Using Serialport for device:" << deviceFilename;
|
||||
return new Serialport(deviceFilename, baudRate);
|
||||
}
|
||||
}
|
||||
60
src/devices/csafe/serialhandler.h
Normal file
60
src/devices/csafe/serialhandler.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Marcel Verpaalen (marcel@verpaalen.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* This emulates a serial port over a network connection.
|
||||
* e.g. as created by ser2net or hardware serial to ethernet converters
|
||||
*
|
||||
*/
|
||||
#ifndef SERIALHANDLER_H
|
||||
#define SERIALHANDLER_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
/**
|
||||
* @brief This is a parent class for serial port handlers
|
||||
* It defines the common interface for serial operations over physical serial port or network
|
||||
*/
|
||||
class SerialHandler {
|
||||
public:
|
||||
virtual ~SerialHandler() = 0; // Pure virtual destructor
|
||||
|
||||
// Factory method to create the appropriate serial handler
|
||||
static SerialHandler *create(const QString &deviceFilename, uint32_t baudRate);
|
||||
|
||||
// Abstract interface for serial operations
|
||||
virtual int openPort() = 0;
|
||||
virtual int closePort() = 0;
|
||||
virtual int rawWrite(uint8_t *bytes, int size) = 0;
|
||||
virtual int rawRead(uint8_t bytes[], int size, bool line = false) = 0;
|
||||
virtual int dataAvailable() = 0;
|
||||
virtual bool isOpen() const = 0;
|
||||
|
||||
// Common configuration methods
|
||||
virtual void setDevice(const QString &devname) = 0;
|
||||
virtual void setTimeout(int timeout) = 0;
|
||||
virtual void setEndChar(uint8_t endChar) = 0;
|
||||
|
||||
protected:
|
||||
// Protected constructor to prevent direct instantiation of this abstract class
|
||||
SerialHandler() = default;
|
||||
};
|
||||
|
||||
inline SerialHandler::~SerialHandler() {} // Definition of the pure virtual destructor
|
||||
|
||||
#endif // SERIALHANDLER_H
|
||||
381
src/devices/csafe/serialport.cpp
Normal file
381
src/devices/csafe/serialport.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
#include "serialport.h"
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* CONSTRUCTOR/DESTRUCTOR
|
||||
* ---------------------------------------------------------------------- */
|
||||
Serialport::Serialport(QString deviceFilename, uint32_t baudRate) {
|
||||
setDevice(deviceFilename);
|
||||
this->baudRate = baudRate;
|
||||
}
|
||||
|
||||
Serialport::~Serialport() {}
|
||||
|
||||
void Serialport::setTimeout(int timeout) { this->_timeout = timeout; }
|
||||
|
||||
void Serialport::setDevice(const QString &devname) {
|
||||
if (!devname.isEmpty()) {
|
||||
deviceFilename = devname;
|
||||
}
|
||||
}
|
||||
|
||||
void Serialport::setEndChar(uint8_t endChar) { this->endChar = endChar; }
|
||||
|
||||
bool Serialport::isOpen() const {
|
||||
#ifdef WIN32
|
||||
return (devicePort != INVALID_HANDLE_VALUE); // Checks if the Windows handle is valid
|
||||
#else
|
||||
return (devicePort != -1); // Checks if the file descriptor is valid on Linux/macOS
|
||||
#endif
|
||||
}
|
||||
|
||||
int Serialport::closePort() {
|
||||
#ifdef WIN32
|
||||
return (int)!CloseHandle(devicePort);
|
||||
#else
|
||||
tcflush(devicePort, TCIOFLUSH); // Clear out the buffer
|
||||
return close(devicePort);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Serialport::openPort() {
|
||||
#ifdef Q_OS_ANDROID
|
||||
QAndroidJniObject::callStaticMethod<void>("org/cagnulen/qdomyoszwift/Usbserial", "open",
|
||||
"(Landroid/content/Context;)V", QtAndroid::androidContext().object());
|
||||
#elif !defined(WIN32)
|
||||
|
||||
// LINUX AND MAC USES TERMIO / IOCTL / STDIO
|
||||
|
||||
#if defined(Q_OS_MACX)
|
||||
int ldisc = TTYDISC;
|
||||
#else
|
||||
int ldisc = N_TTY; // LINUX
|
||||
#endif
|
||||
|
||||
if ((devicePort = open(deviceFilename.toLatin1(), O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1)
|
||||
return errno;
|
||||
|
||||
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
|
||||
|
||||
if (ioctl(devicePort, TIOCSETD, &ldisc) == -1)
|
||||
return errno;
|
||||
|
||||
// get current settings for the port
|
||||
tcgetattr(devicePort, &deviceSettings);
|
||||
|
||||
// set raw mode i.e. ignbrk, brkint, parmrk, istrip, inlcr, igncr, icrnl, ixon
|
||||
// noopost, cs8, noecho, noechonl, noicanon, noisig, noiexn
|
||||
cfmakeraw(&deviceSettings);
|
||||
|
||||
// set baud rate
|
||||
switch (baudRate) {
|
||||
case 2400:
|
||||
cfsetispeed(&deviceSettings, B2400);
|
||||
cfsetospeed(&deviceSettings, B2400);
|
||||
break;
|
||||
case 9600:
|
||||
cfsetispeed(&deviceSettings, B9600);
|
||||
cfsetospeed(&deviceSettings, B9600);
|
||||
break;
|
||||
case 19200:
|
||||
cfsetispeed(&deviceSettings, B19200);
|
||||
cfsetospeed(&deviceSettings, B19200);
|
||||
break;
|
||||
case 38400:
|
||||
cfsetispeed(&deviceSettings, B38400);
|
||||
cfsetospeed(&deviceSettings, B38400);
|
||||
break;
|
||||
case 57600:
|
||||
cfsetispeed(&deviceSettings, B57600);
|
||||
cfsetospeed(&deviceSettings, B57600);
|
||||
break;
|
||||
case 115200:
|
||||
cfsetispeed(&deviceSettings, B115200);
|
||||
cfsetospeed(&deviceSettings, B115200);
|
||||
break;
|
||||
default:
|
||||
qWarning("Invalid baud rate, defaulting to 9600");
|
||||
cfsetispeed(&deviceSettings, B9600);
|
||||
cfsetospeed(&deviceSettings, B9600);
|
||||
break;
|
||||
}
|
||||
|
||||
// further attributes
|
||||
deviceSettings.c_iflag &=
|
||||
~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ICANON | ISTRIP | IXON | IXOFF | IXANY);
|
||||
deviceSettings.c_iflag |= IGNPAR;
|
||||
deviceSettings.c_cflag &= (~CSIZE & ~CSTOPB);
|
||||
deviceSettings.c_oflag = 0;
|
||||
|
||||
#if defined(Q_OS_MACX)
|
||||
deviceSettings.c_cflag &= (~CCTS_OFLOW & ~CRTS_IFLOW); // no hardware flow control
|
||||
deviceSettings.c_cflag |= (CS8 | CLOCAL | CREAD | HUPCL);
|
||||
#else
|
||||
deviceSettings.c_cflag &= (~CRTSCTS); // no hardware flow control
|
||||
deviceSettings.c_cflag |= (CS8 | CLOCAL | CREAD | HUPCL);
|
||||
#endif
|
||||
deviceSettings.c_lflag = 0;
|
||||
deviceSettings.c_cc[VSTART] = 0x11;
|
||||
deviceSettings.c_cc[VSTOP] = 0x13;
|
||||
deviceSettings.c_cc[VEOF] = 0x20;
|
||||
deviceSettings.c_cc[VMIN] = 0;
|
||||
deviceSettings.c_cc[VTIME] = 0;
|
||||
|
||||
// set those attributes
|
||||
if (tcsetattr(devicePort, TCSANOW, &deviceSettings) == -1)
|
||||
return errno;
|
||||
tcgetattr(devicePort, &deviceSettings);
|
||||
|
||||
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
|
||||
#else
|
||||
// WINDOWS USES SET/GETCOMMSTATE AND READ/WRITEFILE
|
||||
|
||||
COMMTIMEOUTS timeouts; // timeout settings on serial ports
|
||||
|
||||
// if deviceFilename references a port above COM9
|
||||
// then we need to open "\\.\COMX" not "COMX"
|
||||
QString portSpec;
|
||||
int portnum = deviceFilename.midRef(3).toString().toInt();
|
||||
if (portnum < 10)
|
||||
portSpec = deviceFilename;
|
||||
else
|
||||
portSpec = "\\\\.\\" + deviceFilename;
|
||||
wchar_t deviceFilenameW[32]; // \\.\COM32 needs 9 characters, 32 should be enough?
|
||||
MultiByteToWideChar(CP_ACP, 0, portSpec.toLatin1(), -1, (LPWSTR)deviceFilenameW, sizeof(deviceFilenameW));
|
||||
|
||||
// win32 commport API
|
||||
devicePort = CreateFile(deviceFilenameW, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (devicePort == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
if (GetCommState(devicePort, &deviceSettings) == false)
|
||||
return -1;
|
||||
|
||||
switch (baudRate) {
|
||||
case 2400:
|
||||
deviceSettings.BaudRate = CBR_2400;
|
||||
break;
|
||||
case 9600:
|
||||
deviceSettings.BaudRate = CBR_9600;
|
||||
break;
|
||||
case 19200:
|
||||
deviceSettings.BaudRate = CBR_19200;
|
||||
break;
|
||||
case 38400: // 38400
|
||||
deviceSettings.BaudRate = CBR_38400;
|
||||
break;
|
||||
case 57600: // 57600
|
||||
deviceSettings.BaudRate = CBR_57600;
|
||||
break;
|
||||
case 115200: // 115200
|
||||
deviceSettings.BaudRate = CBR_115200;
|
||||
break;
|
||||
default:
|
||||
qWarning("Invalid baud rate, defaulting to 9600");
|
||||
deviceSettings.BaudRate = CBR_9600;
|
||||
break;
|
||||
}
|
||||
// so we've opened the comm port lets set it up for
|
||||
deviceSettings.fParity = NOPARITY;
|
||||
deviceSettings.ByteSize = 8;
|
||||
deviceSettings.StopBits = ONESTOPBIT;
|
||||
deviceSettings.XonChar = 11;
|
||||
deviceSettings.XoffChar = 13;
|
||||
deviceSettings.EofChar = 0x0;
|
||||
deviceSettings.ErrorChar = 0x0;
|
||||
deviceSettings.EvtChar = 0x0;
|
||||
deviceSettings.fBinary = true;
|
||||
deviceSettings.fOutX = 0;
|
||||
deviceSettings.fInX = 0;
|
||||
deviceSettings.XonLim = 0;
|
||||
deviceSettings.XoffLim = 0;
|
||||
deviceSettings.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
deviceSettings.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
deviceSettings.fOutxCtsFlow = FALSE; // TRUE;
|
||||
|
||||
if (SetCommState(devicePort, &deviceSettings) == false) {
|
||||
CloseHandle(devicePort);
|
||||
return -1;
|
||||
}
|
||||
|
||||
timeouts.ReadIntervalTimeout = 0;
|
||||
timeouts.ReadTotalTimeoutConstant = 1000;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 50;
|
||||
timeouts.WriteTotalTimeoutConstant = 2000;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts(devicePort, &timeouts);
|
||||
|
||||
#endif
|
||||
|
||||
// success
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Serialport::dataAvailable() {
|
||||
if (!isOpen()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
jint len = QAndroidJniObject::callStaticMethod<jint>("org/cagnulen/qdomyoszwift/Usbserial", "readLen", "()I");
|
||||
static_cast<size_t>(len);
|
||||
|
||||
#elif defined(WIN32)
|
||||
COMSTAT cs;
|
||||
if (!ClearCommError(devicePort, NULL, &cs)) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<size_t>(cs.cbInQue);
|
||||
#else
|
||||
int count = 0;
|
||||
if (-1 == ioctl(devicePort, FIONREAD, &count)) {
|
||||
return 0;
|
||||
} else {
|
||||
return static_cast<size_t>(count);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int Serialport::rawWrite(uint8_t *bytes, int size) {
|
||||
qDebug() << "Writing data:" << QByteArray((const char *)bytes, size).toHex();
|
||||
int rc = 0;
|
||||
if (!isOpen()) {
|
||||
qDebug() << "Port not open";
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
|
||||
QAndroidJniEnvironment env;
|
||||
jbyteArray d = env->NewByteArray(size);
|
||||
jbyte *b = env->GetByteArrayElements(d, 0);
|
||||
for (int i = 0; i < size; i++)
|
||||
b[i] = bytes[i];
|
||||
env->SetByteArrayRegion(d, 0, size, b);
|
||||
QAndroidJniObject::callStaticMethod<void>("org/cagnulen/qdomyoszwift/Usbserial", "write", "([B)V", d);
|
||||
#elif defined(WIN32)
|
||||
DWORD cBytes;
|
||||
rc = WriteFile(devicePort, bytes, size, &cBytes, NULL);
|
||||
if (!rc)
|
||||
return -1;
|
||||
return rc;
|
||||
|
||||
#else
|
||||
int ibytes;
|
||||
ioctl(devicePort, FIONREAD, &ibytes);
|
||||
|
||||
// timeouts are less critical for writing, since vols are low
|
||||
rc = write(devicePort, bytes, size);
|
||||
|
||||
// but it is good to avoid buffer overflow since the
|
||||
// computrainer microcontroller has almost no RAM
|
||||
if (rc != -1)
|
||||
tcdrain(devicePort); // wait till its gone.
|
||||
|
||||
ioctl(devicePort, FIONREAD, &ibytes);
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int Serialport::rawRead(uint8_t bytes[], int size, bool line) {
|
||||
int rc = 0;
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
|
||||
int fullLen = 0;
|
||||
cleanFrame = false;
|
||||
|
||||
// previous buffer?
|
||||
while (bufRX.count()) {
|
||||
bytes[fullLen++] = bufRX.at(0);
|
||||
bufRX.removeFirst();
|
||||
qDebug() << "byte popped from rxBuf";
|
||||
if (fullLen >= size) {
|
||||
qDebug() << size << QByteArray((const char *)bytes, size).toHex(' ');
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
QAndroidJniEnvironment env;
|
||||
while (fullLen < size) {
|
||||
QAndroidJniObject dd =
|
||||
QAndroidJniObject::callStaticObjectMethod("org/cagnulen/qdomyoszwift/Usbserial", "read", "()[B");
|
||||
jint len = QAndroidJniObject::callStaticMethod<jint>("org/cagnulen/qdomyoszwift/Usbserial", "readLen", "()I");
|
||||
jbyteArray d = dd.object<jbyteArray>();
|
||||
jbyte *b = env->GetByteArrayElements(d, 0);
|
||||
if (len + fullLen > size) {
|
||||
QByteArray tmpDebug;
|
||||
qDebug() << "buffer overflow! Truncate from" << len + fullLen << "requested" << size;
|
||||
/*for(int i=0; i<len; i++) {
|
||||
qDebug() << b[i];
|
||||
}*/
|
||||
|
||||
for (int i = fullLen; i < size; i++) {
|
||||
bytes[i] = b[i - fullLen];
|
||||
}
|
||||
for (int i = size; i < len + fullLen; i++) {
|
||||
jbyte bb = b[i - fullLen];
|
||||
bufRX.append(bb);
|
||||
tmpDebug.append(bb);
|
||||
}
|
||||
qDebug() << len + fullLen - size << "bytes to the rxBuf" << tmpDebug.toHex(' ');
|
||||
qDebug() << size << QByteArray((const char *)b, size).toHex(' ');
|
||||
return size;
|
||||
}
|
||||
for (int i = fullLen; i < len + fullLen; i++) {
|
||||
bytes[i] = b[i - fullLen];
|
||||
}
|
||||
qDebug() << len << QByteArray((const char *)b, len).toHex(' ');
|
||||
fullLen += len;
|
||||
}
|
||||
|
||||
qDebug() << "FULL BUFFER RX: << " << fullLen << QByteArray((const char *)bytes, size).toHex(' ');
|
||||
cleanFrame = true;
|
||||
|
||||
return fullLen;
|
||||
#elif defined(WIN32)
|
||||
Q_UNUSED(size);
|
||||
// Readfile deals with timeouts and readyread issues
|
||||
DWORD cBytes;
|
||||
rc = ReadFile(devicePort, bytes, 7, &cBytes, NULL);
|
||||
if (rc)
|
||||
return (int)cBytes;
|
||||
else
|
||||
return (-1);
|
||||
|
||||
#else
|
||||
|
||||
int timeout = 0, i = 0;
|
||||
uint8_t byte;
|
||||
|
||||
// read one byte at a time sleeping when no data ready
|
||||
// until we timeout waiting then return error
|
||||
for (i = 0; i < size; i++) {
|
||||
timeout = 0;
|
||||
rc = 0;
|
||||
while (rc == 0 && timeout < _timeout) {
|
||||
rc = read(devicePort, &byte, 1);
|
||||
if (rc == -1)
|
||||
return -1;
|
||||
else if (rc == 0) {
|
||||
QThread::msleep(50); // sleep for 1/20th of a second
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
timeout += 50;
|
||||
} else {
|
||||
bytes[i] = byte;
|
||||
if (line && endChar == byte) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timeout >= _timeout)
|
||||
return i > 0 ? i : -1;
|
||||
}
|
||||
|
||||
qDebug() << i << QString::fromLocal8Bit((const char *)bytes, i);
|
||||
return i;
|
||||
|
||||
#endif
|
||||
}
|
||||
98
src/devices/csafe/serialport.h
Normal file
98
src/devices/csafe/serialport.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Liversedge (liversedge@gmail.com),
|
||||
2024 Marcel Verpaalen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _SERIALPORT_h
|
||||
#define _SERIALPORT_h
|
||||
|
||||
#include "serialhandler.h"
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "keepawakehelper.h"
|
||||
#include <QAndroidJniObject>
|
||||
#endif
|
||||
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#ifndef N_TTY // for OpenBSD, this is a hack
|
||||
#define N_TTY 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class Serialport : public SerialHandler {
|
||||
public:
|
||||
Serialport(QString deviceFilename, uint32_t baudRate);
|
||||
~Serialport() override;
|
||||
|
||||
// Device management
|
||||
void setDevice(const QString &devname) override;
|
||||
void setTimeout(int timeout) override;
|
||||
void setEndChar(uint8_t endChar) override;
|
||||
|
||||
// Port control
|
||||
int openPort() override;
|
||||
int dataAvailable() override;
|
||||
int closePort() override;
|
||||
|
||||
// Data transfer
|
||||
int rawWrite(uint8_t *bytes, int size) override;
|
||||
int rawRead(uint8_t bytes[], int size, bool line = false) override;
|
||||
|
||||
bool isOpen() const override;
|
||||
|
||||
private:
|
||||
uint32_t baudRate = 9600;
|
||||
uint8_t endChar = 0x0D;
|
||||
int _timeout = 1200;
|
||||
QString deviceFilename;
|
||||
|
||||
// device port
|
||||
#ifdef WIN32
|
||||
HANDLE devicePort; // file descriptor for reading from com3
|
||||
DCB deviceSettings; // serial port settings baud rate et al
|
||||
#else
|
||||
int devicePort; // unix!!
|
||||
struct termios deviceSettings; // unix!!
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
QList<jbyte> bufRX;
|
||||
bool cleanFrame = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // _SERIALPORT_h
|
||||
331
src/devices/csafeelliptical/csafeelliptical.cpp
Normal file
331
src/devices/csafeelliptical/csafeelliptical.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
#include "csafeelliptical.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
csafeelliptical::csafeelliptical(bool noWriteResistance, bool noHeartService, bool noVirtualDevice,
|
||||
int8_t bikeResistanceOffset, double bikeResistanceGain) {
|
||||
m_watt.setType(metric::METRIC_WATT);
|
||||
Speed.setType(metric::METRIC_SPEED);
|
||||
refresh = new QTimer(this);
|
||||
this->noWriteResistance = noWriteResistance;
|
||||
this->noHeartService = noHeartService;
|
||||
this->noVirtualDevice = noVirtualDevice;
|
||||
connect(refresh, &QTimer::timeout, this, &csafeelliptical::update);
|
||||
refresh->start(200ms);
|
||||
QString deviceFilename =
|
||||
settings.value(QZSettings::csafe_elliptical_port, QZSettings::default_csafe_elliptical_port).toString();
|
||||
CsafeRunnerThread *csafeRunner = new CsafeRunnerThread(deviceFilename);
|
||||
setupCommands(csafeRunner);
|
||||
connect(csafeRunner, &CsafeRunnerThread::portAvailable, this, &csafeelliptical::portAvailable);
|
||||
connect(csafeRunner, &CsafeRunnerThread::onCsafeFrame, this, &csafeelliptical::onCsafeFrame);
|
||||
connect(this, &csafeelliptical::sendCsafeCommand, csafeRunner, &CsafeRunnerThread::sendCommand,
|
||||
Qt::QueuedConnection);
|
||||
csafeRunner->start();
|
||||
distanceReceived = 0;
|
||||
kalman = new KalmanFilter(1, .01, .75, 0); // measure error, estimate error, process noise q, initial value
|
||||
}
|
||||
|
||||
void csafeelliptical::setupCommands(CsafeRunnerThread *runner) {
|
||||
QStringList command;
|
||||
command << "CSAFE_GETPOWER_CMD";
|
||||
command << "CSAFE_GETSPEED_CMD";
|
||||
command << "CSAFE_GETCALORIES_CMD";
|
||||
command << "CSAFE_GETHRCUR_CMD";
|
||||
command << "CSAFE_GETHORIZONTAL_CMD";
|
||||
runner->addRefreshCommand(command);
|
||||
runner->addRefreshCommand(QStringList() << "CSAFE_LF_GET_DETAIL");
|
||||
runner->addRefreshCommand(QStringList() << "CSAFE_GETPROGRAM_CMD");
|
||||
}
|
||||
|
||||
void csafeelliptical::setupWorkout() {
|
||||
emit sendCsafeCommand(QStringList() << "CSAFE_GETUSERINFO_CMD");
|
||||
QStringList command = {"CSAFE_SETUSERINFO_CMD",
|
||||
QString::number(settings.value(QZSettings::weight, QZSettings::default_weight).toInt()),
|
||||
"39", QString::number(settings.value(QZSettings::age, QZSettings::default_age).toInt()),
|
||||
"1"}; // weight,weight unit,age,gender
|
||||
emit sendCsafeCommand(command);
|
||||
emit sendCsafeCommand(QStringList() << "CSAFE_SETPROGRAM_CMD" << "4" << "5");
|
||||
emit sendCsafeCommand(QStringList() << "CSAFE_GOINUSE_CMD");
|
||||
}
|
||||
|
||||
void csafeelliptical::onCsafeFrame(const QVariantMap &csafeFrame) {
|
||||
// qDebug() << "Current CSAFE frame received:" << csafeFrame;
|
||||
|
||||
if (csafeFrame["CSAFE_GETCADENCE_CMD"].isValid()) {
|
||||
onCadence(csafeFrame["CSAFE_GETCADENCE_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETSPEED_CMD"].isValid()) {
|
||||
double speed = csafeFrame["CSAFE_GETSPEED_CMD"].value<QVariantList>()[0].toDouble();
|
||||
int unit = csafeFrame["CSAFE_GETSPEED_CMD"].value<QVariantList>()[1].toInt();
|
||||
qDebug() << "Speed value:" << speed << "unit:" << CSafeUtility::getUnitName(unit) << "(" << unit << ")";
|
||||
|
||||
if (unit == 82) { // revs/minute
|
||||
onCadence(speed);
|
||||
} else {
|
||||
onSpeed(CSafeUtility::convertToStandard(unit, speed));
|
||||
}
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETPOWER_CMD"].isValid()) {
|
||||
onPower(csafeFrame["CSAFE_GETPOWER_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETHRCUR_CMD"].isValid()) {
|
||||
onHeart(csafeFrame["CSAFE_GETHRCUR_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETCALORIES_CMD"].isValid()) {
|
||||
onCalories(csafeFrame["CSAFE_GETCALORIES_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETHORIZONTAL_CMD"].isValid()) {
|
||||
double distance = csafeFrame["CSAFE_GETHORIZONTAL_CMD"].value<QVariantList>()[0].toDouble();
|
||||
int unit = csafeFrame["CSAFE_GETHORIZONTAL_CMD"].value<QVariantList>()[1].toInt();
|
||||
qDebug() << "Distance value:" << distance << "unit:" << CSafeUtility::getUnitName(unit) << "(" << unit << ")"
|
||||
<< CSafeUtility::convertToStandard(unit, distance);
|
||||
onDistance(CSafeUtility::convertToStandard(unit, distance));
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETPROGRAM_CMD"].isValid()) {
|
||||
int resistance = csafeFrame["CSAFE_GETPROGRAM_CMD"].value<QVariantList>()[1].toUInt();
|
||||
Resistance = resistance;
|
||||
qDebug() << "Program:" << csafeFrame["CSAFE_GETPROGRAM_CMD"].value<QVariantList>()[0].toUInt()
|
||||
<< "Current level received:" << resistance;
|
||||
}
|
||||
if (csafeFrame["CSAFE_GETSTATUS_CMD"].isValid()) {
|
||||
uint16_t statusvalue = csafeFrame["CSAFE_GETSTATUS_CMD"].value<QVariantList>()[0].toUInt();
|
||||
qDebug() << "Status value:" << statusvalue << " lastStatus:" << lastStatus
|
||||
<< " Machine state from status:" << (statusvalue & 0x0f);
|
||||
if (statusvalue != lastStatus) {
|
||||
lastStatus = statusvalue;
|
||||
char statusChar = static_cast<char>(statusvalue & 0x0f);
|
||||
onStatus(statusChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void csafeelliptical::onSpeed(double speed) {
|
||||
qDebug() << "Current Speed received:" << speed << " updated:" << distanceIsChanging;
|
||||
if (distanceIsChanging)
|
||||
Speed = speed;
|
||||
}
|
||||
|
||||
void csafeelliptical::onPower(double power) {
|
||||
qDebug() << "Current Power received:" << power << " updated:" << distanceIsChanging;
|
||||
if (distanceIsChanging)
|
||||
m_watt = power;
|
||||
}
|
||||
|
||||
void csafeelliptical::onCadence(double cadence) {
|
||||
qDebug() << "Current Cadence received:" << cadence << " updated:" << distanceIsChanging;
|
||||
if (distanceIsChanging)
|
||||
Cadence = cadence;
|
||||
}
|
||||
|
||||
void csafeelliptical::onHeart(double hr) {
|
||||
qDebug() << "Current Heart received:" << hr;
|
||||
QSettings settings;
|
||||
QString heartRateBeltName =
|
||||
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
|
||||
bool disable_hr_frommachinery =
|
||||
settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
|
||||
Heart = (uint8_t)KeepAwakeHelper::heart();
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (heartRateBeltName.startsWith(QStringLiteral("Disabled"))) {
|
||||
uint8_t heart = ((uint8_t)hr);
|
||||
if (heart == 0 || disable_hr_frommachinery) {
|
||||
update_hr_from_external();
|
||||
} else
|
||||
Heart = heart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void csafeelliptical::onCalories(double calories) {
|
||||
qDebug() << "Current Calories received:" << calories;
|
||||
KCal = calories;
|
||||
}
|
||||
|
||||
void csafeelliptical::onDistance(double distance) {
|
||||
qDebug() << "Current Distance received:" << distance << " value tracker:" << distanceReceived.value();
|
||||
Distance = distance / 1000.0;
|
||||
|
||||
if (distance != distanceReceived.value()) {
|
||||
|
||||
double calculated_speed = 3600 * (distance - distanceReceived.value()) /
|
||||
abs(distanceReceived.lastChanged().msecsTo(QDateTime::currentDateTime()));
|
||||
if (distanceIsChanging) { // skip the first distance or after pause otherwise you get enormous speed values
|
||||
// Speed is calculated from the distance and is bit erratic. The kalman filter is used to smooth it out
|
||||
Speed = kalman->updateEstimate(calculated_speed);
|
||||
}
|
||||
|
||||
qDebug() << "Distance received:" << distance << " Previous:" << distanceReceived.value()
|
||||
<< " time(ms):" << abs(distanceReceived.lastChanged().msecsTo(QDateTime::currentDateTime()))
|
||||
<< " Calculated Speed:" << calculated_speed << " New speed:" << Speed.value()
|
||||
<< " updated:" << distanceIsChanging;
|
||||
distanceReceived = distance;
|
||||
distanceIsChanging = true;
|
||||
} else if (abs(distanceReceived.lastChanged().secsTo(QDateTime::currentDateTime())) > 15) {
|
||||
distanceIsChanging = false;
|
||||
m_watt = 0.0;
|
||||
Cadence = 0.0;
|
||||
Speed = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void csafeelliptical::onStatus(char status) {
|
||||
QString statusString = CSafeUtility::statusByteToText(status);
|
||||
qDebug() << "Current Status code:" << status << " status: " << statusString;
|
||||
if (status == 0x06) {
|
||||
// pause
|
||||
paused = true;
|
||||
m_watt = 0.0;
|
||||
Cadence = 0.0;
|
||||
Speed = 0.0;
|
||||
distanceIsChanging = false;
|
||||
} else {
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void csafeelliptical::portAvailable(bool available) {
|
||||
if (available) {
|
||||
qDebug() << "CSAFE port available";
|
||||
_connected = true;
|
||||
setupWorkout();
|
||||
} else {
|
||||
qDebug() << "CSAFE port not available";
|
||||
_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
void csafeelliptical::update() {
|
||||
QSettings settings;
|
||||
QString heartRateBeltName =
|
||||
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
|
||||
|
||||
update_metrics(true, watts());
|
||||
lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
|
||||
|
||||
// ******************************************* virtual treadmill init *************************************
|
||||
if (!firstStateChanged && !this->hasVirtualDevice()
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
&& !h
|
||||
#endif
|
||||
#endif
|
||||
) {
|
||||
QSettings settings;
|
||||
bool virtual_device_enabled =
|
||||
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
bool cadence =
|
||||
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && !virtual_device_rower) {
|
||||
qDebug() << "ios_peloton_workaround activated!";
|
||||
h = new lockscreen();
|
||||
h->virtualbike_ios();
|
||||
} else
|
||||
#endif
|
||||
#endif
|
||||
bool virtual_device_force_bike =
|
||||
settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike)
|
||||
.toBool();
|
||||
if (virtual_device_enabled) {
|
||||
if (!virtual_device_force_bike) {
|
||||
debug("creating virtual treadmill interface...");
|
||||
auto virtualTreadmill = new virtualtreadmill(this, noHeartService);
|
||||
connect(virtualTreadmill, &virtualtreadmill::debug, this, &csafeelliptical::debug);
|
||||
connect(virtualTreadmill, &virtualtreadmill::changeInclination, this,
|
||||
&csafeelliptical::changeInclinationRequested);
|
||||
this->setVirtualDevice(virtualTreadmill, VIRTUAL_DEVICE_MODE::PRIMARY);
|
||||
} else {
|
||||
debug("creating virtual bike interface...");
|
||||
auto virtualBike = new virtualbike(this);
|
||||
// auto virtualBike = new virtualbike(this, noWriteResistance, noHeartService);
|
||||
connect(virtualBike, &virtualbike::changeInclination, this,
|
||||
&csafeelliptical::changeInclinationRequested);
|
||||
connect(virtualBike, &virtualbike::ftmsCharacteristicChanged, this,
|
||||
&csafeelliptical::ftmsCharacteristicChanged);
|
||||
this->setVirtualDevice(virtualBike, VIRTUAL_DEVICE_MODE::ALTERNATIVE);
|
||||
}
|
||||
} else {
|
||||
debug("not creating virtual interface... not enabled");
|
||||
}
|
||||
// ********************************************************************************************************
|
||||
}
|
||||
|
||||
if (!firstStateChanged) {
|
||||
emit connectedAndDiscovered();
|
||||
}
|
||||
|
||||
firstStateChanged = 1;
|
||||
// ********************************************************************************************************
|
||||
|
||||
if (!noVirtualDevice) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool()) {
|
||||
Heart = (uint8_t)KeepAwakeHelper::heart();
|
||||
debug("Current Heart: " + QString::number(Heart.value()));
|
||||
}
|
||||
#endif
|
||||
if (heartRateBeltName.startsWith(QStringLiteral("Disabled"))) {
|
||||
update_hr_from_external();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
bool cadence =
|
||||
settings.value(QZSettings::bike_cadence_sensor, QZSettings::default_bike_cadence_sensor).toBool();
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
if (requestResistance != -1 && requestResistance != currentResistance().value()) {
|
||||
if (!noWriteResistance) {
|
||||
changeResistance(requestResistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void csafeelliptical::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic,
|
||||
const QByteArray &newValue) {
|
||||
QByteArray b = newValue;
|
||||
qDebug() << "Routing FTMS packet to the bike from virtualbike" << characteristic.uuid() << newValue.toHex(' ');
|
||||
}
|
||||
|
||||
void csafeelliptical::changeInclinationRequested(double grade, double percentage) {
|
||||
if (percentage < 0)
|
||||
percentage = 0;
|
||||
changeInclination(grade, percentage);
|
||||
qDebug() << "Requested grade:" << grade << " percentage:" << percentage;
|
||||
}
|
||||
|
||||
void csafeelliptical::changeResistance(resistance_t res) {
|
||||
if (res < 0)
|
||||
res = 0;
|
||||
if (res > 25)
|
||||
res = 25;
|
||||
QStringList resistanceCommand = {"CSAFE_SETLEVEL", QString::number(res)};
|
||||
emit sendCsafeCommand(resistanceCommand);
|
||||
qDebug() << "Send resistance update to device. Requested resitance: " << res;
|
||||
emit sendCsafeCommand(QStringList() << "CSAFE_GETPROGRAM_CMD");
|
||||
}
|
||||
|
||||
bool csafeelliptical::connected() { return _connected; }
|
||||
|
||||
void csafeelliptical::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit debug(QStringLiteral("Found new device: ") + device.name() + " (" + device.address().toString() + ')');
|
||||
}
|
||||
|
||||
uint16_t csafeelliptical::watts() { return m_watt.value(); }
|
||||
142
src/devices/csafeelliptical/csafeelliptical.h
Normal file
142
src/devices/csafeelliptical/csafeelliptical.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Marcel Verpaalen (marcel@verpaalen.com)
|
||||
* based on csaferower
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef CSAFEELLIPTICAL_H
|
||||
#define CSAFEELLIPTICAL_H
|
||||
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/qlowenergyadvertisingdata.h>
|
||||
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristic.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristicdata.h>
|
||||
#include <QtBluetooth/qlowenergycontroller.h>
|
||||
#include <QtBluetooth/qlowenergydescriptordata.h>
|
||||
#include <QtBluetooth/qlowenergyservice.h>
|
||||
#include <QtBluetooth/qlowenergyservicedata.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#else
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#endif
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "keepawakehelper.h"
|
||||
#include <QAndroidJniObject>
|
||||
#endif
|
||||
|
||||
#include "devices/csafe/csafe.h"
|
||||
#include "devices/csafe/csaferunner.h"
|
||||
#include "devices/csafe/csafeutility.h"
|
||||
#include "devices/csafe/kalmanfilter.h"
|
||||
#include "devices/csafe/serialhandler.h"
|
||||
|
||||
#include "devices/elliptical.h"
|
||||
#include "virtualdevices/virtualbike.h"
|
||||
#include "virtualdevices/virtualtreadmill.h"
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QSettings>
|
||||
#include <QThread>
|
||||
|
||||
// #include <stdint.h>
|
||||
// #include <stdio.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <sys/types.h>
|
||||
|
||||
/**
|
||||
* @brief This class is a CSAFE implementation for elliptical devices.
|
||||
* Developed for Life Fitness 95x but most likely also working for other CSAFE devices.
|
||||
*/
|
||||
|
||||
class csafeelliptical : public elliptical {
|
||||
Q_OBJECT
|
||||
public:
|
||||
csafeelliptical(bool noWriteResistance, bool noHeartService, bool noVirtualDevice, int8_t bikeResistanceOffset,
|
||||
double bikeResistanceGain);
|
||||
bool connected() override;
|
||||
|
||||
private:
|
||||
QTimer *refresh;
|
||||
uint8_t sec1Update = 0;
|
||||
QByteArray lastPacket;
|
||||
QDateTime lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
|
||||
uint8_t firstStateChanged = 0;
|
||||
int lastStatus = -1;
|
||||
QSettings settings;
|
||||
|
||||
uint16_t watts() override;
|
||||
void setupCommands(CsafeRunnerThread *runner);
|
||||
void setupWorkout();
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
bool noWriteResistance = false;
|
||||
bool noHeartService = false;
|
||||
bool noVirtualDevice = false;
|
||||
|
||||
bool distanceIsChanging = false;
|
||||
metric distanceReceived;
|
||||
KalmanFilter *kalman;
|
||||
KalmanFilter *kalman1;
|
||||
KalmanFilter *kalman2;
|
||||
KalmanFilter *kalman3;
|
||||
KalmanFilter *kalman4;
|
||||
bool _connected = true;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
void debug(QString string);
|
||||
void sendCsafeCommand(const QStringList &commands);
|
||||
|
||||
private slots:
|
||||
void update();
|
||||
void ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void changeInclinationRequested(double grade, double percentage);
|
||||
void changeResistance(resistance_t res) override;
|
||||
void onPower(double power);
|
||||
void onCadence(double cadence);
|
||||
void onHeart(double hr);
|
||||
void onCalories(double calories);
|
||||
void onDistance(double distance);
|
||||
void onStatus(char status);
|
||||
void onSpeed(double speed);
|
||||
void portAvailable(bool available);
|
||||
void onCsafeFrame(const QVariantMap &frame);
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
};
|
||||
|
||||
#endif // CSAFEELLIPTICAL_H
|
||||
@@ -18,20 +18,33 @@ csaferower::csaferower(bool noWriteResistance, bool noHeartService, bool noVirtu
|
||||
connect(t, &csaferowerThread::onHeart, this, &csaferower::onHeart);
|
||||
connect(t, &csaferowerThread::onCalories, this, &csaferower::onCalories);
|
||||
connect(t, &csaferowerThread::onDistance, this, &csaferower::onDistance);
|
||||
connect(t, &csaferowerThread::onPace, this, &csaferower::onPace);
|
||||
connect(t, &csaferowerThread::onStatus, this, &csaferower::onStatus);
|
||||
t->start();
|
||||
}
|
||||
|
||||
void csaferower::onPower(double power) {
|
||||
qDebug() << "Current Power received:" << power;
|
||||
m_watt = power;
|
||||
|
||||
double pace = (pow((2.8 / power), (1. / 3))) * 1000; // pace to m/km put *500 instead to have a m/500m
|
||||
Speed = (60.0 / (double)(pace)) * 30.0;
|
||||
void csaferower::onPace(double pace) {
|
||||
qDebug() << "Current Pace received:" << pace;
|
||||
if(distanceIsChanging && pace > 0)
|
||||
Speed = (60.0 / (double)(pace)) * 60.0;
|
||||
else
|
||||
Speed = 0;
|
||||
|
||||
qDebug() << "Current Speed calculated:" << Speed.value() << pace;
|
||||
}
|
||||
|
||||
void csaferower::onCadence(double cadence) { qDebug() << "Current Cadence received:" << cadence; }
|
||||
|
||||
void csaferower::onPower(double power) {
|
||||
qDebug() << "Current Power received:" << power;
|
||||
if(distanceIsChanging)
|
||||
m_watt = power;
|
||||
}
|
||||
|
||||
void csaferower::onCadence(double cadence) {
|
||||
qDebug() << "Current Cadence received:" << cadence;
|
||||
if(distanceIsChanging)
|
||||
Cadence = cadence;
|
||||
}
|
||||
|
||||
void csaferower::onHeart(double hr) {
|
||||
qDebug() << "Current Heart received:" << hr;
|
||||
@@ -62,14 +75,30 @@ void csaferower::onCalories(double calories) {
|
||||
KCal = calories;
|
||||
}
|
||||
|
||||
void csaferower::onDistance(double distance) { qDebug() << "Current Distance received:" << distance / 1000.0; }
|
||||
void csaferower::onDistance(double distance) {
|
||||
qDebug() << "Current Distance received:" << distance / 1000.0;
|
||||
|
||||
if(distance != distanceReceived.value()) {
|
||||
distanceIsChanging = true;
|
||||
distanceReceived = distance;
|
||||
} else if(abs(distanceReceived.lastChanged().secsTo(QDateTime::currentDateTime())) > 2) {
|
||||
distanceIsChanging = false;
|
||||
m_watt = 0;
|
||||
Cadence = 0;
|
||||
Speed = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void csaferower::onStatus(char status) {
|
||||
qDebug() << "Current Status received:" << status;
|
||||
}
|
||||
|
||||
csaferowerThread::csaferowerThread() {}
|
||||
|
||||
void csaferowerThread::run() {
|
||||
QSettings settings;
|
||||
/*devicePort =
|
||||
settings.value(QZSettings::computrainer_serialport, QZSettings::default_computrainer_serialport).toString();*/
|
||||
deviceFilename = settings.value(QZSettings::csafe_rower, QZSettings::default_csafe_rower).toString();
|
||||
|
||||
openPort();
|
||||
csafe *aa = new csafe();
|
||||
@@ -81,7 +110,9 @@ void csaferowerThread::run() {
|
||||
command << "CSAFE_GETPOWER_CMD";
|
||||
command << "CSAFE_GETCALORIES_CMD";
|
||||
command << "CSAFE_GETHRCUR_CMD";
|
||||
QByteArray ret = aa->write(command);
|
||||
command << "CSAFE_GETPACE_CMD";
|
||||
command << "CSAFE_GETSTATUS_CMD";
|
||||
QByteArray ret = aa->write(command,true);
|
||||
|
||||
qDebug() << " >> " << ret.toHex(' ');
|
||||
rawWrite((uint8_t *)ret.data(), ret.length());
|
||||
@@ -96,6 +127,9 @@ void csaferowerThread::run() {
|
||||
if (f["CSAFE_GETCADENCE_CMD"].isValid()) {
|
||||
emit onCadence(f["CSAFE_GETCADENCE_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (f["CSAFE_GETPACE_CMD"].isValid()) {
|
||||
emit onPace(f["CSAFE_GETPACE_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (f["CSAFE_GETPOWER_CMD"].isValid()) {
|
||||
emit onPower(f["CSAFE_GETPOWER_CMD"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
@@ -108,6 +142,10 @@ void csaferowerThread::run() {
|
||||
if (f["CSAFE_PM_GET_WORKDISTANCE"].isValid()) {
|
||||
emit onDistance(f["CSAFE_PM_GET_WORKDISTANCE"].value<QVariantList>()[0].toDouble());
|
||||
}
|
||||
if (f["CSAFE_GETSTATUS_CMD"].isValid()) {
|
||||
char statusChar = static_cast<char>(f["CSAFE_GETSTATUS_CMD"].value<QVariantList>()[0].toUInt() & 0x0f);
|
||||
emit onStatus(statusChar);
|
||||
}
|
||||
|
||||
memset(rx, 0x00, sizeof(rx));
|
||||
QThread::msleep(50);
|
||||
@@ -125,6 +163,9 @@ int csaferowerThread::closePort() {
|
||||
}
|
||||
|
||||
int csaferowerThread::openPort() {
|
||||
|
||||
qDebug() << "Opening serial port " << deviceFilename.toLatin1();
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
QAndroidJniObject::callStaticMethod<void>("org/cagnulen/qdomyoszwift/CSafeRowerUSBHID", "open",
|
||||
"(Landroid/content/Context;)V", QtAndroid::androidContext().object());
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "csafe.h"
|
||||
#include "devices/csafe/csafe.h"
|
||||
#include "devices/rower.h"
|
||||
#include "virtualdevices/virtualbike.h"
|
||||
#include "virtualdevices/virtualrower.h"
|
||||
@@ -86,6 +86,8 @@ class csaferowerThread : public QThread {
|
||||
void onHeart(double hr);
|
||||
void onCalories(double calories);
|
||||
void onDistance(double distance);
|
||||
void onPace(double pace);
|
||||
void onStatus(char status);
|
||||
|
||||
private:
|
||||
// Utility and BG Thread functions
|
||||
@@ -141,6 +143,10 @@ class csaferower : public rower {
|
||||
uint16_t oldLastCrankEventTime = 0;
|
||||
uint16_t oldCrankRevs = 0;
|
||||
|
||||
bool distanceIsChanging = false;
|
||||
metric distanceReceived;
|
||||
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
#endif
|
||||
@@ -158,6 +164,8 @@ class csaferower : public rower {
|
||||
void onHeart(double hr);
|
||||
void onCalories(double calories);
|
||||
void onDistance(double distance);
|
||||
void onPace(double pace);
|
||||
void onStatus(char status);
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <QLowEnergyConnectionParameters>
|
||||
#endif
|
||||
#include <chrono>
|
||||
#include "homeform.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -89,7 +90,7 @@ void cscbike::update() {
|
||||
/*initDone*/) {
|
||||
bool cadence_sensor_as_bike =
|
||||
settings.value(QZSettings::cadence_sensor_as_bike, QZSettings::default_cadence_sensor_as_bike).toBool();
|
||||
update_metrics(true, watts(), !cadence_sensor_as_bike);
|
||||
update_metrics(false, watts(), !cadence_sensor_as_bike);
|
||||
|
||||
if(lastGoodCadence.secsTo(QDateTime::currentDateTime()) > 5 && !charNotified) {
|
||||
readMethod = true;
|
||||
@@ -141,7 +142,7 @@ void cscbike::serviceDiscovered(const QBluetoothUuid &gatt) {
|
||||
|
||||
void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
|
||||
qDebug() << "characteristicChanged << " << characteristic.uuid() << newValue.toHex(' ') << newValue.length();
|
||||
Q_UNUSED(characteristic);
|
||||
QSettings settings;
|
||||
// QString heartRateBeltName = //unused QString
|
||||
@@ -157,6 +158,10 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
|
||||
|
||||
if (characteristic.uuid() == QBluetoothUuid((quint16)0x2A19)) {
|
||||
battery = newValue.at(0);
|
||||
if(battery != battery_level)
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested(bluetoothDevice.name() + QStringLiteral(" Battery Level ") + QString::number(battery) + " %");
|
||||
battery_level = battery;
|
||||
qDebug() << QStringLiteral("battery: ") << battery;
|
||||
return;
|
||||
}
|
||||
@@ -210,7 +215,7 @@ void cscbike::characteristicChanged(const QLowEnergyCharacteristic &characterist
|
||||
|
||||
if (CrankRevs != oldCrankRevs && deltaT) {
|
||||
double cadence = ((CrankRevs - oldCrankRevs) / deltaT) * 1024 * 60;
|
||||
if (cadence >= 0 && cadence < 256)
|
||||
if ((cadence >= 0 && cadence < 256 && CrankPresent) || (!CrankPresent && WheelPresent))
|
||||
Cadence = cadence;
|
||||
lastGoodCadence = now;
|
||||
} else if (lastGoodCadence.msecsTo(now) > 2000) {
|
||||
|
||||
@@ -57,6 +57,8 @@ class cscbike : public bike {
|
||||
uint8_t firstStateChanged = 0;
|
||||
bool charNotified = false;
|
||||
|
||||
uint8_t battery_level = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
|
||||
1013
src/devices/cycleopsphantombike/cycleopsphantombike.cpp
Normal file
1013
src/devices/cycleopsphantombike/cycleopsphantombike.cpp
Normal file
File diff suppressed because it is too large
Load Diff
134
src/devices/cycleopsphantombike/cycleopsphantombike.h
Normal file
134
src/devices/cycleopsphantombike/cycleopsphantombike.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef CYCLEOPSPHANTOMBIKE_H
|
||||
#define CYCLEOPSPHANTOMBIKE_H
|
||||
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/qlowenergyadvertisingdata.h>
|
||||
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristic.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristicdata.h>
|
||||
#include <QtBluetooth/qlowenergycontroller.h>
|
||||
#include <QtBluetooth/qlowenergydescriptordata.h>
|
||||
#include <QtBluetooth/qlowenergyservice.h>
|
||||
#include <QtBluetooth/qlowenergyservicedata.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#else
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#endif
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "devices/bike.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
|
||||
class cycleopsphantombike : public bike {
|
||||
Q_OBJECT
|
||||
public:
|
||||
cycleopsphantombike(bool noWriteResistance, bool noHeartService);
|
||||
void changePower(int32_t power) override;
|
||||
bool connected() override;
|
||||
resistance_t pelotonToBikeResistance(int pelotonResistance) override;
|
||||
|
||||
private:
|
||||
enum class ControlMode : uint8_t {
|
||||
Headless = 0x00,
|
||||
ManualPower = 0x01,
|
||||
ManualSlope = 0x02,
|
||||
PowerRange = 0x03,
|
||||
WarmUp = 0x04,
|
||||
RollDown = 0x05
|
||||
};
|
||||
|
||||
enum class ControlStatus : uint8_t {
|
||||
SpeedOkay = 0x00,
|
||||
SpeedUp = 0x01,
|
||||
SpeedDown = 0x02,
|
||||
RollDownInitializing = 0x03,
|
||||
RollDownInProcess = 0x04,
|
||||
RollDownPassed = 0x05,
|
||||
RollDownFailed = 0x06
|
||||
};
|
||||
void setControlMode(ControlMode mode, int16_t parameter1 = 0, int16_t parameter2 = 0);
|
||||
|
||||
void writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
|
||||
bool wait_for_response = false);
|
||||
void startDiscover();
|
||||
void forceInclination(double inclination);
|
||||
uint16_t watts() override;
|
||||
double bikeResistanceToPeloton(double resistance);
|
||||
void setUserConfiguration(double wheelDiameter, double gearRatio);
|
||||
|
||||
QTimer *refresh;
|
||||
|
||||
const int max_resistance = 100;
|
||||
|
||||
QList<QLowEnergyService *> gattCommunicationChannelService;
|
||||
QLowEnergyCharacteristic gattWriteCharControlPointId;
|
||||
QLowEnergyCharacteristic gattWriteCharCustomId;
|
||||
QLowEnergyService *gattPowerService = nullptr;
|
||||
QLowEnergyService *gattCustomService = nullptr;
|
||||
// QLowEnergyCharacteristic gattNotify1Characteristic;
|
||||
|
||||
uint8_t sec1Update = 0;
|
||||
QByteArray lastPacket;
|
||||
QDateTime lastRefreshCharacteristicChanged2A5B = QDateTime::currentDateTime();
|
||||
QDateTime lastRefreshCharacteristicChanged2AD2 = QDateTime::currentDateTime();
|
||||
QDateTime lastRefreshCharacteristicChangedPower = QDateTime::currentDateTime();
|
||||
QDateTime lastGoodCadence = QDateTime::currentDateTime();
|
||||
uint8_t firstStateChanged = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
bool noWriteResistance = false;
|
||||
bool noHeartService = false;
|
||||
|
||||
uint16_t oldLastCrankEventTime = 0;
|
||||
uint16_t oldCrankRevs = 0;
|
||||
uint16_t CrankRevsRead = 0;
|
||||
|
||||
double lastGearValue = -1;
|
||||
bool resistance_received = false;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
void debug(QString string);
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
|
||||
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 characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue);
|
||||
void stateChanged(QLowEnergyService::ServiceState state);
|
||||
void controllerStateChanged(QLowEnergyController::ControllerState state);
|
||||
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void serviceScanDone(void);
|
||||
void update();
|
||||
void error(QLowEnergyController::Error err);
|
||||
void errorService(QLowEnergyService::ServiceError);
|
||||
|
||||
void powerPacketReceived(const QByteArray &b);
|
||||
};
|
||||
|
||||
#endif // CYCLEOPSPHANTOMBIKE_H
|
||||
@@ -14,7 +14,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 +23,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 +73,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,
|
||||
|
||||
@@ -160,6 +172,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,11 +180,14 @@ 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);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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,6 +30,7 @@ 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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -368,6 +368,12 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
|
||||
else
|
||||
kcal = KCal.value();
|
||||
}
|
||||
|
||||
if(!firstCharacteristicChanged) {
|
||||
Distance += ((speed / (double)3600.0) /
|
||||
((double)1000.0 / (double)(lastRefreshCharacteristicChanged.msecsTo(now))));
|
||||
}
|
||||
|
||||
double distance = GetDistanceFromPacket(value);
|
||||
|
||||
double ucadence = ((uint8_t)value.at(9));
|
||||
@@ -451,7 +457,7 @@ void domyosbike::characteristicChanged(const QLowEnergyCharacteristic &character
|
||||
fabs(now.msecsTo(Speed.lastChanged()) / 1000.0), this->speedLimit());
|
||||
}
|
||||
KCal = kcal;
|
||||
Distance = distance;
|
||||
firstCharacteristicChanged = false;
|
||||
}
|
||||
|
||||
double domyosbike::GetSpeedFromPacket(const QByteArray &packet) {
|
||||
|
||||
@@ -80,6 +80,7 @@ class domyosbike : public bike {
|
||||
uint8_t sec1Update = 0;
|
||||
QByteArray lastPacket;
|
||||
QDateTime lastRefreshCharacteristicChanged = QDateTime::currentDateTime();
|
||||
bool firstCharacteristicChanged = true;
|
||||
|
||||
enum _BIKE_TYPE {
|
||||
CHANG_YOW,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "keepawakehelper.h"
|
||||
#endif
|
||||
#include "homeform.h"
|
||||
#include "virtualdevices/virtualbike.h"
|
||||
#include "virtualdevices/virtualtreadmill.h"
|
||||
#include <QBluetoothLocalDevice>
|
||||
@@ -367,7 +368,7 @@ void domyoselliptical::characteristicChanged(const QLowEnergyCharacteristic &cha
|
||||
|
||||
double domyoselliptical::GetSpeedFromPacket(const QByteArray &packet) {
|
||||
|
||||
uint16_t convertedData = (packet.at(6) << 8) | packet.at(7);
|
||||
uint16_t convertedData = (packet.at(6) << 8) | ((uint8_t)packet.at(7));
|
||||
double data = (double)convertedData / 10.0f;
|
||||
return data;
|
||||
}
|
||||
@@ -513,6 +514,14 @@ void domyoselliptical::serviceScanDone(void) {
|
||||
|
||||
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::stateChanged, this, &domyoselliptical::stateChanged);
|
||||
if(!gattCommunicationChannelService) {
|
||||
QSettings settings;
|
||||
settings.setValue(QZSettings::domyos_elliptical_fmts, true);
|
||||
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested("Domyos Elliptial it's a FTMS. Restart QZ to apply the fix, thanks.");
|
||||
return;
|
||||
}
|
||||
gattCommunicationChannelService->discoverDetails();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
474
src/devices/echelonstairclimber/echelonstairclimber.cpp
Normal file
474
src/devices/echelonstairclimber/echelonstairclimber.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
#include "echelonstairclimber.h"
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "keepawakehelper.h"
|
||||
#endif
|
||||
#include "virtualdevices/virtualbike.h"
|
||||
#include "virtualdevices/virtualtreadmill.h"
|
||||
#include <QBluetoothLocalDevice>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QMetaEnum>
|
||||
#include <QSettings>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
echelonstairclimber::echelonstairclimber(uint32_t pollDeviceTime, bool noConsole, bool noHeartService, double forceInitSpeed,
|
||||
double forceInitInclination) {
|
||||
m_watt.setType(metric::METRIC_WATT);
|
||||
Speed.setType(metric::METRIC_SPEED);
|
||||
this->noConsole = noConsole;
|
||||
this->noHeartService = noHeartService;
|
||||
|
||||
refresh = new QTimer(this);
|
||||
initDone = false;
|
||||
connect(refresh, &QTimer::timeout, this, &echelonstairclimber::update);
|
||||
refresh->start(pollDeviceTime);
|
||||
}
|
||||
|
||||
void echelonstairclimber::writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
|
||||
bool wait_for_response) {
|
||||
QEventLoop loop;
|
||||
QTimer timeout;
|
||||
|
||||
if (wait_for_response) {
|
||||
connect(this, &echelonstairclimber::packetReceived, &loop, &QEventLoop::quit);
|
||||
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
|
||||
} else {
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::characteristicWritten, &loop, &QEventLoop::quit);
|
||||
timeout.singleShot(300ms, &loop, &QEventLoop::quit);
|
||||
}
|
||||
|
||||
if (gattCommunicationChannelService->state() != QLowEnergyService::ServiceState::ServiceDiscovered ||
|
||||
m_control->state() == QLowEnergyController::UnconnectedState) {
|
||||
emit debug(QStringLiteral("writeCharacteristic error because the connection is closed"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (writeBuffer) {
|
||||
delete writeBuffer;
|
||||
}
|
||||
writeBuffer = new QByteArray((const char *)data, data_len);
|
||||
|
||||
gattCommunicationChannelService->writeCharacteristic(gattWriteCharacteristic, *writeBuffer);
|
||||
|
||||
if (!disable_log) {
|
||||
emit debug(QStringLiteral(" >> ") + writeBuffer->toHex(' ') +
|
||||
QStringLiteral(" // ") + info);
|
||||
}
|
||||
|
||||
loop.exec();
|
||||
|
||||
if (timeout.isActive() == false) {
|
||||
emit debug(QStringLiteral(" exit for timeout"));
|
||||
}
|
||||
}
|
||||
|
||||
void echelonstairclimber::updateDisplay(uint16_t elapsed) {}
|
||||
|
||||
void echelonstairclimber::forceSpeed(double requestSpeed) {
|
||||
uint8_t noOpData[] = {0xf0, 0xb2, 0x02, 0x00, 0x00, 0x00};
|
||||
|
||||
noOpData[3] = (uint8_t)(((uint16_t)(requestSpeed * 1000.0)) >> 8);
|
||||
noOpData[4] = ((uint8_t)(requestSpeed * 1000.0));
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(noOpData) - 1; i++) {
|
||||
noOpData[5] += noOpData[i]; // the last byte is a sort of a checksum
|
||||
}
|
||||
|
||||
writeCharacteristic(noOpData, sizeof(noOpData), QStringLiteral("force speed"), false, true);
|
||||
}
|
||||
|
||||
void echelonstairclimber::forceIncline(double requestIncline) {
|
||||
uint8_t noOpData[] = {0xf0, 0xb1, 0x01, 0x00, 0x00};
|
||||
|
||||
noOpData[3] = requestIncline;
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(noOpData) - 1; i++) {
|
||||
noOpData[4] += noOpData[i]; // the last byte is a sort of a checksum
|
||||
}
|
||||
|
||||
writeCharacteristic(noOpData, sizeof(noOpData), QStringLiteral("force incline"), false, true);
|
||||
}
|
||||
|
||||
void echelonstairclimber::sendPoll() {
|
||||
uint8_t noOpData[] = {0xf0, 0xa0, 0x01, 0x00, 0x00};
|
||||
|
||||
noOpData[3] = counterPoll;
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(noOpData) - 1; i++) {
|
||||
noOpData[4] += noOpData[i]; // the last byte is a sort of a checksum
|
||||
}
|
||||
|
||||
writeCharacteristic(noOpData, sizeof(noOpData), QStringLiteral("noOp"), false, true);
|
||||
|
||||
counterPoll++;
|
||||
if (!counterPoll) {
|
||||
counterPoll = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void echelonstairclimber::changeInclinationRequested(double grade, double percentage) {
|
||||
if (percentage < 0)
|
||||
percentage = 0;
|
||||
changeInclination(grade, percentage);
|
||||
}
|
||||
|
||||
void echelonstairclimber::update() {
|
||||
if (m_control->state() == QLowEnergyController::UnconnectedState) {
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (initRequest) {
|
||||
initRequest = false;
|
||||
btinit();
|
||||
} else if (/*bluetoothDevice.isValid() &&*/
|
||||
m_control->state() == QLowEnergyController::DiscoveredState && gattCommunicationChannelService &&
|
||||
gattWriteCharacteristic.isValid() && gattNotify1Characteristic.isValid() &&
|
||||
gattNotify2Characteristic.isValid() && initDone) {
|
||||
QSettings settings;
|
||||
// ******************************************* virtual treadmill init *************************************
|
||||
if (!firstInit && !this->hasVirtualDevice()) {
|
||||
bool virtual_device_enabled =
|
||||
settings.value(QZSettings::virtual_device_enabled, QZSettings::default_virtual_device_enabled).toBool();
|
||||
bool virtual_device_force_bike =
|
||||
settings.value(QZSettings::virtual_device_force_bike, QZSettings::default_virtual_device_force_bike)
|
||||
.toBool();
|
||||
if (virtual_device_enabled) {
|
||||
if (!virtual_device_force_bike) {
|
||||
debug("creating virtual treadmill interface...");
|
||||
auto virtualTreadMill = new virtualtreadmill(this, noHeartService);
|
||||
connect(virtualTreadMill, &virtualtreadmill::debug, this, &echelonstairclimber::debug);
|
||||
connect(virtualTreadMill, &virtualtreadmill::changeInclination, this,
|
||||
&echelonstairclimber::changeInclinationRequested);
|
||||
this->setVirtualDevice(virtualTreadMill, VIRTUAL_DEVICE_MODE::PRIMARY);
|
||||
} else {
|
||||
debug("creating virtual bike interface...");
|
||||
auto virtualBike = new virtualbike(this);
|
||||
connect(virtualBike, &virtualbike::changeInclination, this,
|
||||
&echelonstairclimber::changeInclinationRequested);
|
||||
this->setVirtualDevice(virtualBike, VIRTUAL_DEVICE_MODE::ALTERNATIVE);
|
||||
}
|
||||
firstInit = 1;
|
||||
}
|
||||
}
|
||||
// ********************************************************************************************************
|
||||
|
||||
update_metrics(true, watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
|
||||
|
||||
// updating the treadmill console every second
|
||||
if (sec1Update++ >= (2000 / refresh->interval())) {
|
||||
sec1Update = 0;
|
||||
sendPoll();
|
||||
}
|
||||
/*
|
||||
if (requestSpeed != -1) {
|
||||
if (requestSpeed != currentSpeed().value() && requestSpeed >= 0 && requestSpeed <= 22) {
|
||||
emit debug(QStringLiteral("writing speed ") + QString::number(requestSpeed));
|
||||
forceSpeed(requestSpeed);
|
||||
}
|
||||
requestSpeed = -1;
|
||||
}
|
||||
if (requestInclination != -100) {
|
||||
if (requestInclination < 0)
|
||||
requestInclination = 0;
|
||||
if (requestInclination != currentInclination().value() && requestInclination >= 0 &&
|
||||
requestInclination <= 15) {
|
||||
emit debug(QStringLiteral("writing incline ") + QString::number(requestInclination));
|
||||
forceIncline(requestInclination);
|
||||
}
|
||||
requestInclination = -100;
|
||||
}
|
||||
if (requestStart != -1) {
|
||||
emit debug(QStringLiteral("starting..."));
|
||||
if (lastSpeed == 0.0) {
|
||||
lastSpeed = 0.5;
|
||||
}
|
||||
uint8_t initData3[] = {0xf0, 0xb0, 0x01, 0x01, 0xa2};
|
||||
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("start"), false, true);
|
||||
|
||||
if(stride4) {
|
||||
uint8_t initData0[] = {0xf0, 0xa5, 0x00, 0x95};
|
||||
writeCharacteristic(initData0, sizeof(initData0), QStringLiteral("start"), false, false);
|
||||
}
|
||||
|
||||
uint8_t initData4[] = {0xf0, 0xd0, 0x01, 0x00, 0xc1};
|
||||
writeCharacteristic(initData4, sizeof(initData4), QStringLiteral("start"), false, false);
|
||||
|
||||
uint8_t initData5[] = {0xf0, 0xd0, 0x01, 0x11, 0xd2};
|
||||
writeCharacteristic(initData5, sizeof(initData5), QStringLiteral("start"), false, false);
|
||||
|
||||
if(stride4) {
|
||||
uint8_t initData0[] = {0xf0, 0xd3, 0x02, 0x01, 0xf4, 0xba};
|
||||
writeCharacteristic(initData0, sizeof(initData0), QStringLiteral("start"), false, false);
|
||||
}
|
||||
|
||||
lastStart = QDateTime::currentMSecsSinceEpoch();
|
||||
requestStart = -1;
|
||||
emit tapeStarted();
|
||||
}
|
||||
if (requestStop != -1) {
|
||||
emit debug(QStringLiteral("stopping..."));
|
||||
uint8_t initData3[] = {0xf0, 0xb0, 0x01, 0x00, 0xa1};
|
||||
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("stop"), false, true);
|
||||
requestStop = -1;
|
||||
lastStop = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
if (requestFanSpeed != -1) {
|
||||
emit debug(QStringLiteral("changing fan speed..."));
|
||||
// sendChangeFanSpeed(requestFanSpeed);
|
||||
requestFanSpeed = -1;
|
||||
}
|
||||
if (requestIncreaseFan != -1) {
|
||||
emit debug(QStringLiteral("increasing fan speed..."));
|
||||
// sendChangeFanSpeed(FanSpeed + 1);
|
||||
requestIncreaseFan = -1;
|
||||
} else if (requestDecreaseFan != -1) {
|
||||
emit debug(QStringLiteral("decreasing fan speed..."));
|
||||
// sendChangeFanSpeed(FanSpeed - 1);
|
||||
requestDecreaseFan = -1;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void echelonstairclimber::serviceDiscovered(const QBluetoothUuid &gatt) {
|
||||
emit debug(QStringLiteral("serviceDiscovered ") + gatt.toString());
|
||||
}
|
||||
|
||||
void echelonstairclimber::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
// qDebug() << "characteristicChanged" << characteristic.uuid() << newValue << newValue.length();
|
||||
QSettings settings;
|
||||
QString heartRateBeltName =
|
||||
settings.value(QZSettings::heart_rate_belt_name, QZSettings::default_heart_rate_belt_name).toString();
|
||||
Q_UNUSED(characteristic);
|
||||
QByteArray value = newValue;
|
||||
|
||||
qDebug() << QStringLiteral(" << ") + newValue.toHex(' ');
|
||||
|
||||
lastPacket = newValue;
|
||||
|
||||
if (((unsigned char)newValue.at(0)) == 0xf0 && ((unsigned char)newValue.at(1)) == 0xd1) {
|
||||
|
||||
uint16_t convertedData = (newValue.at(5) << 8) | ((uint8_t)newValue.at(6));
|
||||
StepCount = convertedData;
|
||||
convertedData = (newValue.at(7) << 8) | ((uint8_t)newValue.at(8));
|
||||
elevationAcc = convertedData;
|
||||
Cadence = (uint8_t)newValue.at(9);
|
||||
Speed = Cadence.value() / 3.2;
|
||||
|
||||
qDebug() << QStringLiteral("Current Floors: ") + QString::number(StepCount.value() / 10.0);
|
||||
qDebug() << QStringLiteral("Current Cadence: ") + QString::number(Cadence.value());
|
||||
qDebug() << QStringLiteral("Current StepCount: ") + QString::number(StepCount.value());
|
||||
qDebug() << QStringLiteral("Current elevationAcc: ") + QString::number(elevationAcc.value());
|
||||
return;
|
||||
}
|
||||
|
||||
/*if (newValue.length() != 21)
|
||||
return;*/
|
||||
|
||||
/*if ((uint8_t)(newValue.at(0)) != 0xf0 && (uint8_t)(newValue.at(1)) != 0xd1)
|
||||
return;*/
|
||||
|
||||
if (!firstCharacteristicChanged) {
|
||||
if (watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()))
|
||||
KCal +=
|
||||
((((0.048 * ((double)watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat())) +
|
||||
1.19) *
|
||||
settings.value(QZSettings::weight, QZSettings::default_weight).toFloat() * 3.5) /
|
||||
200.0) /
|
||||
(60000.0 / ((double)lastTimeCharacteristicChanged.msecsTo(
|
||||
now)))); //(( (0.048* Output in watts +1.19) * body weight in
|
||||
// kg * 3.5) / 200 ) / 60
|
||||
Distance += ((Speed.value() / 3600.0) /
|
||||
(1000.0 / (lastTimeCharacteristicChanged.msecsTo(now))));
|
||||
}
|
||||
|
||||
if ((uint8_t)newValue.at(1) == 0xD1 && newValue.length() > 11)
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (settings.value(QZSettings::ant_heart, QZSettings::default_ant_heart).toBool())
|
||||
Heart = (uint8_t)KeepAwakeHelper::heart();
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (heartRateBeltName.startsWith(QStringLiteral("Disabled"))) {
|
||||
|
||||
uint8_t heart = ((uint8_t)newValue.at(11));
|
||||
if (heart == 0) {
|
||||
update_hr_from_external();
|
||||
} else {
|
||||
Heart = heart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << QStringLiteral("Current Heart: ") + QString::number(Heart.value());
|
||||
qDebug() << QStringLiteral("Current Calculate Distance: ") + QString::number(Distance.value());
|
||||
qDebug() << QStringLiteral("Current Watt: ") +
|
||||
QString::number(watts(settings.value(QZSettings::weight, QZSettings::default_weight).toFloat()));
|
||||
|
||||
if (m_control->error() != QLowEnergyController::NoError)
|
||||
qDebug() << QStringLiteral("QLowEnergyController ERROR!!") << m_control->errorString();
|
||||
|
||||
lastTimeCharacteristicChanged = now;
|
||||
firstCharacteristicChanged = false;
|
||||
}
|
||||
|
||||
void echelonstairclimber::btinit() {
|
||||
uint8_t initData0[] = {0xf0, 0xa4, 0x00, 0x94};
|
||||
uint8_t initData1[] = {0xf0, 0xe0, 0x85, 0xf8, 0x03, 0x68, 0xd2, 0x8a};
|
||||
uint8_t initData2[] = {0xf0, 0xe0, 0x38, 0x04, 0x03, 0xb5, 0x14, 0xd8};
|
||||
uint8_t initData3[] = {0xf0, 0xa1, 0x00, 0x91};
|
||||
uint8_t initData4[] = {0xf0, 0xa3, 0x00, 0x93};
|
||||
uint8_t initData5[] = {0xf0, 0xb0, 0x01, 0x01, 0xa2};
|
||||
uint8_t initData6[] = {0xf0, 0xa5, 0x00, 0x95};
|
||||
|
||||
writeCharacteristic(initData0, sizeof(initData0), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData1, sizeof(initData1), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData0, sizeof(initData0), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData2, sizeof(initData2), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData3, sizeof(initData3), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData4, sizeof(initData4), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData5, sizeof(initData5), QStringLiteral("init"), false, true);
|
||||
writeCharacteristic(initData6, sizeof(initData6), QStringLiteral("init"), false, true);
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void echelonstairclimber::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
QBluetoothUuid _gattWriteCharacteristicId(QStringLiteral("0bf669f2-45f2-11e7-9598-0800200c9a66"));
|
||||
QBluetoothUuid _gattNotify1CharacteristicId(QStringLiteral("0bf669f3-45f2-11e7-9598-0800200c9a66"));
|
||||
QBluetoothUuid _gattNotify2CharacteristicId(QStringLiteral("0bf669f4-45f2-11e7-9598-0800200c9a66"));
|
||||
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceState>();
|
||||
qDebug() << QStringLiteral("BTLE stateChanged ") + QString::fromLocal8Bit(metaEnum.valueToKey(state));
|
||||
|
||||
if (state == QLowEnergyService::ServiceDiscovered) {
|
||||
// qDebug() << gattCommunicationChannelService->characteristics();
|
||||
|
||||
gattWriteCharacteristic = gattCommunicationChannelService->characteristic(_gattWriteCharacteristicId);
|
||||
gattNotify1Characteristic = gattCommunicationChannelService->characteristic(_gattNotify1CharacteristicId);
|
||||
gattNotify2Characteristic = gattCommunicationChannelService->characteristic(_gattNotify2CharacteristicId);
|
||||
Q_ASSERT(gattWriteCharacteristic.isValid());
|
||||
Q_ASSERT(gattNotify1Characteristic.isValid());
|
||||
Q_ASSERT(gattNotify2Characteristic.isValid());
|
||||
|
||||
// establish hook into notifications
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::characteristicChanged, this,
|
||||
&echelonstairclimber::characteristicChanged);
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::characteristicWritten, this,
|
||||
&echelonstairclimber::characteristicWritten);
|
||||
connect(gattCommunicationChannelService, SIGNAL(error(QLowEnergyService::ServiceError)), this,
|
||||
SLOT(errorService(QLowEnergyService::ServiceError)));
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::descriptorWritten, this,
|
||||
&echelonstairclimber::descriptorWritten);
|
||||
|
||||
QByteArray descriptor;
|
||||
descriptor.append((char)0x01);
|
||||
descriptor.append((char)0x00);
|
||||
gattCommunicationChannelService->writeDescriptor(
|
||||
gattNotify1Characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), descriptor);
|
||||
gattCommunicationChannelService->writeDescriptor(
|
||||
gattNotify2Characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
void echelonstairclimber::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue) {
|
||||
emit debug(QStringLiteral("descriptorWritten ") + descriptor.name() + " " + newValue.toHex(' '));
|
||||
|
||||
initRequest = true;
|
||||
emit connectedAndDiscovered();
|
||||
}
|
||||
|
||||
void echelonstairclimber::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
|
||||
Q_UNUSED(characteristic);
|
||||
emit debug(QStringLiteral("characteristicWritten ") + newValue.toHex(' '));
|
||||
}
|
||||
|
||||
void echelonstairclimber::serviceScanDone(void) {
|
||||
qDebug() << QStringLiteral("serviceScanDone");
|
||||
|
||||
auto services_list = m_control->services();
|
||||
for (const QBluetoothUuid &s : qAsConst(services_list)) {
|
||||
qDebug() << s << "service found!";
|
||||
}
|
||||
|
||||
QBluetoothUuid _gattCommunicationChannelServiceId(QStringLiteral("0bf669f1-45f2-11e7-9598-0800200c9a66"));
|
||||
|
||||
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
|
||||
|
||||
if(gattCommunicationChannelService == nullptr) {
|
||||
qDebug() << "invalid service";
|
||||
return;
|
||||
}
|
||||
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::stateChanged, this, &echelonstairclimber::stateChanged);
|
||||
gattCommunicationChannelService->discoverDetails();
|
||||
}
|
||||
|
||||
void echelonstairclimber::errorService(QLowEnergyService::ServiceError err) {
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceError>();
|
||||
emit debug(QStringLiteral("echelonstairclimber::errorService ") + QString::fromLocal8Bit(metaEnum.valueToKey(err)) +
|
||||
m_control->errorString());
|
||||
}
|
||||
|
||||
void echelonstairclimber::error(QLowEnergyController::Error err) {
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyController::Error>();
|
||||
emit debug(QStringLiteral("echelonstairclimber::error ") + QString::fromLocal8Bit(metaEnum.valueToKey(err)) +
|
||||
m_control->errorString());
|
||||
}
|
||||
|
||||
void echelonstairclimber::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
{
|
||||
bluetoothDevice = device;
|
||||
|
||||
m_control = QLowEnergyController::createCentral(bluetoothDevice, this);
|
||||
connect(m_control, &QLowEnergyController::serviceDiscovered, this, &echelonstairclimber::serviceDiscovered);
|
||||
connect(m_control, &QLowEnergyController::discoveryFinished, this, &echelonstairclimber::serviceScanDone);
|
||||
connect(m_control, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(error(QLowEnergyController::Error)));
|
||||
connect(m_control, &QLowEnergyController::stateChanged, this, &echelonstairclimber::controllerStateChanged);
|
||||
|
||||
connect(m_control,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
|
||||
this, [this](QLowEnergyController::Error error) {
|
||||
Q_UNUSED(error);
|
||||
Q_UNUSED(this);
|
||||
emit debug(QStringLiteral("Cannot connect to remote device."));
|
||||
emit disconnected();
|
||||
});
|
||||
connect(m_control, &QLowEnergyController::connected, this, [this]() {
|
||||
Q_UNUSED(this);
|
||||
emit debug(QStringLiteral("Controller connected. Search services..."));
|
||||
m_control->discoverServices();
|
||||
});
|
||||
connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
|
||||
Q_UNUSED(this);
|
||||
emit debug(QStringLiteral("LowEnergy controller disconnected"));
|
||||
emit disconnected();
|
||||
});
|
||||
|
||||
// Connect
|
||||
m_control->connectToDevice();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void echelonstairclimber::controllerStateChanged(QLowEnergyController::ControllerState state) {
|
||||
qDebug() << QStringLiteral("controllerStateChanged") << state;
|
||||
if (state == QLowEnergyController::UnconnectedState && m_control) {
|
||||
qDebug() << QStringLiteral("trying to connect back again...");
|
||||
initDone = false;
|
||||
m_control->connectToDevice();
|
||||
}
|
||||
}
|
||||
|
||||
bool echelonstairclimber::connected() {
|
||||
if (!m_control) {
|
||||
return false;
|
||||
}
|
||||
return m_control->state() == QLowEnergyController::DiscoveredState;
|
||||
}
|
||||
104
src/devices/echelonstairclimber/echelonstairclimber.h
Normal file
104
src/devices/echelonstairclimber/echelonstairclimber.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef ECHELONSTAIRCLIMBER_H
|
||||
#define ECHELONSTAIRCLIMBER_H
|
||||
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/qlowenergyadvertisingdata.h>
|
||||
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristic.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristicdata.h>
|
||||
#include <QtBluetooth/qlowenergycontroller.h>
|
||||
#include <QtBluetooth/qlowenergydescriptordata.h>
|
||||
#include <QtBluetooth/qlowenergyservice.h>
|
||||
#include <QtBluetooth/qlowenergyservicedata.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#else
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#endif
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "stairclimber.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
|
||||
class echelonstairclimber : public stairclimber {
|
||||
Q_OBJECT
|
||||
public:
|
||||
echelonstairclimber(uint32_t poolDeviceTime = 200, bool noConsole = false, bool noHeartService = false,
|
||||
double forceInitSpeed = 0.0, double forceInitInclination = 0.0);
|
||||
bool connected() override;
|
||||
|
||||
private:
|
||||
double GetSpeedFromPacket(QByteArray packet);
|
||||
double GetInclinationFromPacket(QByteArray packet);
|
||||
double GetKcalFromPacket(QByteArray packet);
|
||||
double GetDistanceFromPacket(QByteArray packet);
|
||||
void forceSpeed(double requestSpeed);
|
||||
void forceIncline(double requestIncline);
|
||||
void updateDisplay(uint16_t elapsed);
|
||||
void btinit();
|
||||
void sendPoll();
|
||||
void writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
|
||||
bool wait_for_response = false);
|
||||
void startDiscover();
|
||||
bool noConsole = false;
|
||||
bool noHeartService = false;
|
||||
uint32_t pollDeviceTime = 200;
|
||||
uint8_t sec1Update = 0;
|
||||
uint8_t firstInit = 0;
|
||||
uint8_t counterPoll = 1;
|
||||
QByteArray lastPacket;
|
||||
QDateTime lastTimeCharacteristicChanged;
|
||||
bool firstCharacteristicChanged = true;
|
||||
|
||||
QTimer *refresh;
|
||||
|
||||
QLowEnergyService *gattCommunicationChannelService = nullptr;
|
||||
QLowEnergyCharacteristic gattWriteCharacteristic;
|
||||
QLowEnergyCharacteristic gattNotify1Characteristic;
|
||||
QLowEnergyCharacteristic gattNotify2Characteristic;
|
||||
|
||||
bool initDone = false;
|
||||
bool initRequest = false;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = 0;
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
void debug(QString string);
|
||||
void speedChanged(double speed);
|
||||
void packetReceived();
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
|
||||
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 changeInclinationRequested(double grade, double percentage);
|
||||
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void serviceScanDone(void);
|
||||
void update();
|
||||
void error(QLowEnergyController::Error err);
|
||||
void errorService(QLowEnergyService::ServiceError);
|
||||
};
|
||||
|
||||
#endif // ECHELONSTAIRCLIMBER_H
|
||||
300
src/devices/elitesquarecontroller/elitesquarecontroller.cpp
Normal file
300
src/devices/elitesquarecontroller/elitesquarecontroller.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
#include "homeform.h"
|
||||
#include "elitesquarecontroller.h"
|
||||
#include <QBluetoothLocalDevice>
|
||||
#include <QDateTime>
|
||||
#include <QEventLoop>
|
||||
#include <QFile>
|
||||
#include <QMetaEnum>
|
||||
#include <QSettings>
|
||||
#include <QThread>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// Define static constants
|
||||
const QString elitesquarecontroller::DEVICE_NAME = "SQUARE";
|
||||
const QBluetoothUuid elitesquarecontroller::SERVICE_UUID = QBluetoothUuid(QStringLiteral("347b0001-7635-408b-8918-8ff3949ce592"));
|
||||
const QBluetoothUuid elitesquarecontroller::CHARACTERISTIC_UUID = QBluetoothUuid(QStringLiteral("347b0045-7635-408b-8918-8ff3949ce592"));
|
||||
|
||||
elitesquarecontroller::elitesquarecontroller(bluetoothdevice *parentDevice) {
|
||||
#ifdef Q_OS_IOS
|
||||
QZ_EnableDiscoveryCharsAndDescripttors = true;
|
||||
#endif
|
||||
this->parentDevice = parentDevice;
|
||||
|
||||
// Initialize button state vector (24 buttons from 0-23)
|
||||
buttonState.resize(24);
|
||||
buttonState.fill(0);
|
||||
|
||||
// Set up refresh timer
|
||||
refresh = new QTimer(this);
|
||||
connect(refresh, &QTimer::timeout, this, &elitesquarecontroller::update);
|
||||
refresh->start(1000ms);
|
||||
}
|
||||
|
||||
void elitesquarecontroller::update() {
|
||||
// Just a simple heartbeat check - no handshake needed for Elite Square
|
||||
if (m_control && m_control->state() == QLowEnergyController::UnconnectedState) {
|
||||
// Try to reconnect if disconnected
|
||||
qDebug() << QStringLiteral("Elite Square disconnected, attempting to reconnect...");
|
||||
m_control->connectToDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::serviceDiscovered(const QBluetoothUuid &gatt) {
|
||||
emit debug(QStringLiteral("serviceDiscovered ") + gatt.toString());
|
||||
|
||||
// Check if the discovered service is the Elite Square service
|
||||
if (gatt == SERVICE_UUID) {
|
||||
qDebug() << QStringLiteral("Elite Square service discovered");
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::disconnectBluetooth() {
|
||||
qDebug() << QStringLiteral("elitesquarecontroller::disconnect") << m_control;
|
||||
|
||||
if (m_control) {
|
||||
m_control->disconnectFromDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::characteristicChanged(const QLowEnergyCharacteristic &characteristic,
|
||||
const QByteArray &newValue) {
|
||||
Q_UNUSED(characteristic);
|
||||
emit packetReceived();
|
||||
|
||||
qDebug() << QStringLiteral(" << ") << newValue.toHex(' ') << QString(newValue);
|
||||
|
||||
// Process the Elite Square button data
|
||||
if (characteristic.uuid() == CHARACTERISTIC_UUID) {
|
||||
// Process the raw bytes directly
|
||||
parseButtonData(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::parseButtonData(const QByteArray &data) {
|
||||
// The data comes as raw bytes, with 11 bytes total
|
||||
if (data.size() < 11) {
|
||||
qDebug() << QStringLiteral("Invalid button data size: ") << data.size();
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the hex data for better debugging
|
||||
qDebug() << QStringLiteral("Processing button data: ") << data.toHex(' ');
|
||||
|
||||
// Extract the important bytes (based on the provided Android code)
|
||||
uint8_t leftByte = data[5];
|
||||
uint8_t rightByte = data[10];
|
||||
|
||||
// Extract the nibbles from these bytes
|
||||
uint8_t leftLargeValue = (leftByte >> 4) & 0xF; // Left Shift Button 1
|
||||
uint8_t leftSmallValue = leftByte & 0xF; // Left Shift Button 2
|
||||
uint8_t rightLargeValue = (rightByte >> 4) & 0xF; // Right Shift Button 1
|
||||
uint8_t rightSmallValue = rightByte & 0xF; // Right Shift Button 2
|
||||
|
||||
qDebug() << QStringLiteral("Button values: leftLarge=") << leftLargeValue
|
||||
<< QStringLiteral(" leftSmall=") << leftSmallValue
|
||||
<< QStringLiteral(" rightLarge=") << rightLargeValue
|
||||
<< QStringLiteral(" rightSmall=") << rightSmallValue;
|
||||
|
||||
// Check if the left large shift button (Left Shift Button 1) is pressed
|
||||
// According to the code, odd values indicate press events
|
||||
if (leftLargeValue != buttonState[BUTTON_LEFT_SHIFT_1] && leftLargeValue % 2 == 1) {
|
||||
qDebug() << QStringLiteral("Left Shift Button 1 pressed (shift down 1)");
|
||||
emit minus();
|
||||
}
|
||||
buttonState[BUTTON_LEFT_SHIFT_1] = leftLargeValue;
|
||||
|
||||
// Check if the left small shift button (Left Shift Button 2) is pressed
|
||||
if (leftSmallValue != buttonState[BUTTON_LEFT_SHIFT_2] && leftSmallValue % 2 == 1) {
|
||||
qDebug() << QStringLiteral("Left Shift Button 2 pressed (shift down 3)");
|
||||
// Emit minus three times for triple downshift
|
||||
emit minus();
|
||||
emit minus();
|
||||
emit minus();
|
||||
}
|
||||
buttonState[BUTTON_LEFT_SHIFT_2] = leftSmallValue;
|
||||
|
||||
// Check if the right large shift button (Right Shift Button 1) is pressed
|
||||
if (rightLargeValue != buttonState[BUTTON_RIGHT_SHIFT_1] && rightLargeValue % 2 == 1) {
|
||||
qDebug() << QStringLiteral("Right Shift Button 1 pressed (shift up 1)");
|
||||
emit plus();
|
||||
}
|
||||
buttonState[BUTTON_RIGHT_SHIFT_1] = rightLargeValue;
|
||||
|
||||
// Check if the right small shift button (Right Shift Button 2) is pressed
|
||||
if (rightSmallValue != buttonState[BUTTON_RIGHT_SHIFT_2] && rightSmallValue % 2 == 1) {
|
||||
qDebug() << QStringLiteral("Right Shift Button 2 pressed (shift up 3)");
|
||||
// Emit plus three times for triple upshift
|
||||
emit plus();
|
||||
emit plus();
|
||||
emit plus();
|
||||
}
|
||||
buttonState[BUTTON_RIGHT_SHIFT_2] = rightSmallValue;
|
||||
|
||||
// Check for steering buttons and other controls
|
||||
// Assuming byte 3 might contain steering information as in our previous implementation
|
||||
uint8_t controlByte = data[3];
|
||||
|
||||
// These conditions would need to be adjusted based on actual behavior
|
||||
if (controlByte == 0x60 && data[3] != buttonState[BUTTON_X]) {
|
||||
emit steeringLeft(true);
|
||||
buttonState[BUTTON_X] = controlByte;
|
||||
} else if (controlByte == 0x20 && data[3] != buttonState[BUTTON_CIRCLE]) {
|
||||
// Need to make sure this doesn't conflict with shift button detection
|
||||
emit steeringRight(true);
|
||||
buttonState[BUTTON_CIRCLE] = controlByte;
|
||||
} else if (controlByte == 0x00) {
|
||||
emit steeringLeft(false);
|
||||
emit steeringRight(false);
|
||||
buttonState[BUTTON_X] = 0;
|
||||
buttonState[BUTTON_CIRCLE] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceState>();
|
||||
emit debug(QStringLiteral("BTLE stateChanged ") + QString::fromLocal8Bit(metaEnum.valueToKey(state)));
|
||||
|
||||
for (QLowEnergyService *s : qAsConst(gattCommunicationChannelService)) {
|
||||
qDebug() << QStringLiteral("stateChanged") << s->serviceUuid() << s->state();
|
||||
if (s->state() != QLowEnergyService::ServiceDiscovered && s->state() != QLowEnergyService::InvalidService) {
|
||||
qDebug() << QStringLiteral("not all services discovered");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (state != QLowEnergyService::ServiceState::ServiceDiscovered) {
|
||||
qDebug() << QStringLiteral("ignoring this state");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << QStringLiteral("all services discovered!");
|
||||
|
||||
for (QLowEnergyService *s : qAsConst(gattCommunicationChannelService)) {
|
||||
if (s->state() == QLowEnergyService::ServiceDiscovered) {
|
||||
// establish hook into notifications
|
||||
connect(s, &QLowEnergyService::characteristicChanged, this, &elitesquarecontroller::characteristicChanged);
|
||||
connect(
|
||||
s, static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error),
|
||||
this, &elitesquarecontroller::errorService);
|
||||
connect(s, &QLowEnergyService::descriptorWritten, this, &elitesquarecontroller::descriptorWritten);
|
||||
|
||||
qDebug() << s->serviceUuid() << QStringLiteral("connected!");
|
||||
|
||||
// Check if this is the Elite Square service
|
||||
if (s->serviceUuid() == SERVICE_UUID) {
|
||||
gattService = s;
|
||||
|
||||
// Find the notification characteristic
|
||||
auto characteristics_list = s->characteristics();
|
||||
for (const QLowEnergyCharacteristic &c : qAsConst(characteristics_list)) {
|
||||
if (c.uuid() == CHARACTERISTIC_UUID) {
|
||||
gattNotifyCharacteristic = c;
|
||||
|
||||
// Subscribe to notifications
|
||||
if ((c.properties() & QLowEnergyCharacteristic::Notify) == QLowEnergyCharacteristic::Notify) {
|
||||
QByteArray descriptor;
|
||||
descriptor.append((char)0x01);
|
||||
descriptor.append((char)0x00);
|
||||
if (c.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration).isValid()) {
|
||||
s->writeDescriptor(c.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), descriptor);
|
||||
connectionEstablished = true;
|
||||
qDebug() << QStringLiteral("Elite Square notification subscribed!");
|
||||
} else {
|
||||
qDebug() << QStringLiteral("ClientCharacteristicConfiguration is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue) {
|
||||
emit debug(QStringLiteral("descriptorWritten ") + descriptor.name() + " " + newValue.toHex(' '));
|
||||
}
|
||||
|
||||
void elitesquarecontroller::serviceScanDone(void) {
|
||||
emit debug(QStringLiteral("serviceScanDone"));
|
||||
|
||||
auto services_list = m_control->services();
|
||||
for (const QBluetoothUuid &s : qAsConst(services_list)) {
|
||||
gattCommunicationChannelService.append(m_control->createServiceObject(s));
|
||||
if (gattCommunicationChannelService.constLast()) {
|
||||
connect(gattCommunicationChannelService.constLast(), &QLowEnergyService::stateChanged, this,
|
||||
&elitesquarecontroller::stateChanged);
|
||||
gattCommunicationChannelService.constLast()->discoverDetails();
|
||||
} else {
|
||||
m_control->disconnectFromDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void elitesquarecontroller::errorService(QLowEnergyService::ServiceError err) {
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyService::ServiceError>();
|
||||
emit debug(QStringLiteral("elitesquarecontroller::errorService") + QString::fromLocal8Bit(metaEnum.valueToKey(err)) +
|
||||
m_control->errorString());
|
||||
}
|
||||
|
||||
void elitesquarecontroller::error(QLowEnergyController::Error err) {
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<QLowEnergyController::Error>();
|
||||
emit debug(QStringLiteral("elitesquarecontroller::error") + QString::fromLocal8Bit(metaEnum.valueToKey(err)) +
|
||||
m_control->errorString());
|
||||
}
|
||||
|
||||
void elitesquarecontroller::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit debug(QStringLiteral("Found new device: ") + device.name() + QStringLiteral(" (") +
|
||||
device.address().toString() + ')');
|
||||
|
||||
// Check if this is the Elite Square device
|
||||
if (device.name() == DEVICE_NAME) {
|
||||
bluetoothDevice = device;
|
||||
m_control = QLowEnergyController::createCentral(bluetoothDevice, this);
|
||||
connect(m_control, &QLowEnergyController::serviceDiscovered, this, &elitesquarecontroller::serviceDiscovered);
|
||||
connect(m_control, &QLowEnergyController::discoveryFinished, this, &elitesquarecontroller::serviceScanDone);
|
||||
connect(m_control,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
|
||||
this, &elitesquarecontroller::error);
|
||||
connect(m_control, &QLowEnergyController::stateChanged, this, &elitesquarecontroller::controllerStateChanged);
|
||||
|
||||
connect(m_control,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
|
||||
this, [this](QLowEnergyController::Error error) {
|
||||
Q_UNUSED(error);
|
||||
Q_UNUSED(this);
|
||||
emit debug(QStringLiteral("Cannot connect to Elite Square device."));
|
||||
emit disconnected();
|
||||
});
|
||||
connect(m_control, &QLowEnergyController::connected, this, [this]() {
|
||||
Q_UNUSED(this);
|
||||
emit debug(QStringLiteral("Elite Square controller connected. Searching services..."));
|
||||
m_control->discoverServices();
|
||||
});
|
||||
connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
|
||||
Q_UNUSED(this);
|
||||
emit debug(QStringLiteral("Elite Square controller disconnected"));
|
||||
connectionEstablished = false;
|
||||
emit disconnected();
|
||||
});
|
||||
|
||||
// Connect to the device
|
||||
m_control->connectToDevice();
|
||||
}
|
||||
}
|
||||
|
||||
bool elitesquarecontroller::connected() {
|
||||
if (!m_control) {
|
||||
return false;
|
||||
}
|
||||
return connectionEstablished && (m_control->state() == QLowEnergyController::DiscoveredState);
|
||||
}
|
||||
|
||||
void elitesquarecontroller::controllerStateChanged(QLowEnergyController::ControllerState state) {
|
||||
qDebug() << QStringLiteral("controllerStateChanged") << state;
|
||||
if (state == QLowEnergyController::UnconnectedState && m_control) {
|
||||
qDebug() << QStringLiteral("trying to connect back again...");
|
||||
connectionEstablished = false;
|
||||
m_control->connectToDevice();
|
||||
}
|
||||
}
|
||||
120
src/devices/elitesquarecontroller/elitesquarecontroller.h
Normal file
120
src/devices/elitesquarecontroller/elitesquarecontroller.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef ELITESQUARECONTROLLER_H
|
||||
#define ELITESQUARECONTROLLER_H
|
||||
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/qlowenergyadvertisingdata.h>
|
||||
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristic.h>
|
||||
#include <QtBluetooth/qlowenergycharacteristicdata.h>
|
||||
#include <QtBluetooth/qlowenergycontroller.h>
|
||||
#include <QtBluetooth/qlowenergydescriptordata.h>
|
||||
#include <QtBluetooth/qlowenergyservice.h>
|
||||
#include <QtBluetooth/qlowenergyservicedata.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#else
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#endif
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QTime>
|
||||
|
||||
#include "devices/bluetoothdevice.h"
|
||||
#include "zwift_play/abstractZapDevice.h"
|
||||
|
||||
// Button positions on Elite Square controller
|
||||
enum EliteSquareButton {
|
||||
BUTTON_NONE = 0,
|
||||
// 1 and 2 are N/A
|
||||
BUTTON_UP = 3,
|
||||
BUTTON_LEFT = 4,
|
||||
BUTTON_DOWN = 5,
|
||||
BUTTON_RIGHT = 6,
|
||||
BUTTON_X = 7,
|
||||
BUTTON_SQUARE = 8,
|
||||
BUTTON_LEFT_CAMPAGNOLO = 9,
|
||||
BUTTON_LEFT_BRAKE = 10,
|
||||
BUTTON_LEFT_SHIFT_1 = 11,
|
||||
BUTTON_LEFT_SHIFT_2 = 12,
|
||||
BUTTON_Y = 13,
|
||||
BUTTON_A = 14,
|
||||
BUTTON_B = 15,
|
||||
BUTTON_Z = 16,
|
||||
BUTTON_CIRCLE = 17,
|
||||
BUTTON_TRIANGLE = 18,
|
||||
// 19 is N/A
|
||||
BUTTON_RIGHT_CAMPAGNOLO = 20,
|
||||
BUTTON_RIGHT_BRAKE = 21,
|
||||
BUTTON_RIGHT_SHIFT_1 = 22,
|
||||
BUTTON_RIGHT_SHIFT_2 = 23
|
||||
};
|
||||
|
||||
// Button state
|
||||
enum EliteSquareButtonState {
|
||||
RELEASED = 0,
|
||||
PRESSED = 1
|
||||
};
|
||||
|
||||
class elitesquarecontroller : public bluetoothdevice {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Constants for Elite Square device
|
||||
static const QString DEVICE_NAME;
|
||||
static const QBluetoothUuid SERVICE_UUID;
|
||||
static const QBluetoothUuid CHARACTERISTIC_UUID;
|
||||
|
||||
elitesquarecontroller(bluetoothdevice *parentDevice);
|
||||
bool connected() override;
|
||||
|
||||
private:
|
||||
QList<QLowEnergyService *> gattCommunicationChannelService;
|
||||
QLowEnergyCharacteristic gattNotifyCharacteristic;
|
||||
QLowEnergyService *gattService;
|
||||
|
||||
bluetoothdevice *parentDevice = nullptr;
|
||||
|
||||
bool connectionEstablished = false;
|
||||
QTimer *refresh;
|
||||
|
||||
// Last known state of each button (0-23)
|
||||
QVector<int> buttonState;
|
||||
|
||||
// Helper function to parse the button data
|
||||
void parseButtonData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void disconnected();
|
||||
void debug(QString string);
|
||||
void packetReceived();
|
||||
|
||||
// Match AbstractZapDevice signals
|
||||
void plus(); // Gear up/increase
|
||||
void minus(); // Gear down/decrease
|
||||
void steeringLeft(bool active); // X button and Left Campagnolo
|
||||
void steeringRight(bool active); // Circle button and Right Campagnolo
|
||||
void buttonActivated(int buttonId, bool pressed); // General button signal
|
||||
|
||||
public slots:
|
||||
void deviceDiscovered(const QBluetoothDeviceInfo &device);
|
||||
void disconnectBluetooth();
|
||||
|
||||
private slots:
|
||||
void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue);
|
||||
void stateChanged(QLowEnergyService::ServiceState state);
|
||||
void controllerStateChanged(QLowEnergyController::ControllerState state);
|
||||
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void serviceScanDone(void);
|
||||
void update();
|
||||
void error(QLowEnergyController::Error err);
|
||||
void errorService(QLowEnergyService::ServiceError);
|
||||
void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue);
|
||||
};
|
||||
|
||||
#endif // ELITESQUARECONTROLLER_H
|
||||
@@ -118,7 +118,8 @@ void elliptical::setGears(double gears) {
|
||||
QSettings settings;
|
||||
qDebug() << "setGears" << gears;
|
||||
m_gears = gears;
|
||||
settings.setValue(QZSettings::gears_current_value, m_gears);
|
||||
if (settings.value(QZSettings::gears_restore_value, QZSettings::default_gears_restore_value).toBool())
|
||||
settings.setValue(QZSettings::gears_current_value, m_gears);
|
||||
if (lastRawRequestedResistanceValue != -1) {
|
||||
changeResistance(lastRawRequestedResistanceValue);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ void eslinkertreadmill::forceSpeed(double requestSpeed) {
|
||||
QStringLiteral("forceSpeed speed=") + QString::number(requestSpeed), false, true);
|
||||
} else if(treadmill_type == ESANGLINKER) {
|
||||
uint8_t display[] = {0xa9, 0x01, 0x01, 0x0b, 0x00};
|
||||
display[3] = (int)(requestSpeed * 10 * 0.621371);
|
||||
display[3] = (int)qRound(requestSpeed * 10 * 0.621371);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
display[4] = display[4] ^ display[i];
|
||||
}
|
||||
@@ -461,12 +461,11 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
|
||||
}
|
||||
}
|
||||
|
||||
if ((newValue.length() != 17 && (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE)))
|
||||
return;
|
||||
else if (newValue.length() != 5 && (treadmill_type == COSTAWAY || treadmill_type == TYPE::ESANGLINKER))
|
||||
return;
|
||||
if ((newValue.length() != 17 && (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE))) {
|
||||
|
||||
if (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE) {
|
||||
} else if (newValue.length() != 5 && (treadmill_type == COSTAWAY || treadmill_type == TYPE::ESANGLINKER)) {
|
||||
|
||||
} else if (treadmill_type == RHYTHM_FUN || treadmill_type == YPOO_MINI_CHANGE) {
|
||||
double speed = GetSpeedFromPacket(value);
|
||||
double incline = GetInclinationFromPacket(value);
|
||||
double kcal = GetKcalFromPacket(value);
|
||||
@@ -502,12 +501,7 @@ void eslinkertreadmill::characteristicChanged(const QLowEnergyCharacteristic &ch
|
||||
lastSpeed = speed;
|
||||
lastInclination = incline;
|
||||
}
|
||||
} else if (treadmill_type == COSTAWAY || treadmill_type == TYPE::ESANGLINKER) {
|
||||
if(treadmill_type == TYPE::ESANGLINKER) {
|
||||
if((uint8_t)newValue.at(1) != 0xe0)
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (treadmill_type == COSTAWAY || (treadmill_type == TYPE::ESANGLINKER && (uint8_t)newValue.at(1) == 0xe0)) {
|
||||
const double miles = 1.60934;
|
||||
if(((uint8_t)newValue.at(3)) == 0xFF && treadmill_type == COSTAWAY)
|
||||
Speed = 0;
|
||||
@@ -914,3 +908,10 @@ void eslinkertreadmill::waitForHandshakePacket() {
|
||||
timeout.singleShot(3000, &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
double eslinkertreadmill::minStepSpeed() {
|
||||
if(treadmill_type == ESANGLINKER)
|
||||
return 0.160934; // 0.1 mi
|
||||
else
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class eslinkertreadmill : public treadmill {
|
||||
double minStepInclination() override;
|
||||
bool autoPauseWhenSpeedIsZero() override;
|
||||
bool autoStartWhenSpeedIsGreaterThenZero() override;
|
||||
double minStepSpeed() override;
|
||||
|
||||
private:
|
||||
double GetSpeedFromPacket(const QByteArray &packet);
|
||||
|
||||
@@ -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.
|
||||
@@ -195,3 +198,11 @@ resistance_t fakebike::resistanceFromPowerRequest(uint16_t power) {
|
||||
|
||||
uint16_t fakebike::watts() { return m_watt.value(); }
|
||||
bool fakebike::connected() { return true; }
|
||||
|
||||
double fakebike::maxGears() {
|
||||
return 24;
|
||||
}
|
||||
|
||||
double fakebike::minGears() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ class fakebike : public bike {
|
||||
uint16_t watts() override;
|
||||
resistance_t maxResistance() override { return 100; }
|
||||
resistance_t resistanceFromPowerRequest(uint16_t power) override;
|
||||
double maxGears() override;
|
||||
double minGears() override;
|
||||
|
||||
private:
|
||||
QTimer *refresh;
|
||||
|
||||
@@ -52,6 +52,8 @@ void faketreadmill::update() {
|
||||
emit debug(QStringLiteral("writing incline ") + QString::number(requestInclination));
|
||||
requestInclination = -100;
|
||||
}
|
||||
|
||||
StepCount = StepCount.value() + 0.5;
|
||||
|
||||
_ergTable.collectTreadmillData(Speed.value(), _watts, Inclination.value());
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "fitplusbike.h"
|
||||
#include "homeform.h"
|
||||
#include "virtualdevices/virtualbike.h"
|
||||
#include <QBluetoothLocalDevice>
|
||||
#include <QDateTime>
|
||||
@@ -511,7 +512,7 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
Resistance = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
|
||||
(uint16_t)((uint8_t)newValue.at(index)))) / 10.0;
|
||||
emit resistanceRead(Resistance.value());
|
||||
m_pelotonResistance = Resistance.value();
|
||||
m_pelotonResistance = bikeResistanceToPeloton(Resistance.value());
|
||||
index += 2;
|
||||
qDebug() << QStringLiteral("Current Resistance: ") + QString::number(Resistance.value());
|
||||
}
|
||||
@@ -632,14 +633,18 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
|
||||
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
|
||||
} else {
|
||||
m_pelotonResistance = (100 * Resistance.value()) / max_resistance;
|
||||
m_pelotonResistance = bikeResistanceToPeloton(Resistance.value());
|
||||
}
|
||||
|
||||
if (settings.value(QZSettings::cadence_sensor_name, QZSettings::default_cadence_sensor_name)
|
||||
.toString()
|
||||
.startsWith(QStringLiteral("Disabled")))
|
||||
Cadence = ((uint8_t)newValue.at(6));
|
||||
m_watt = (double)((((uint8_t)newValue.at(10)) << 8) | ((uint8_t)newValue.at(9))) / 10.0;
|
||||
if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
|
||||
.toString()
|
||||
.startsWith(QStringLiteral("Disabled"))) {
|
||||
m_watt = (double)((((uint8_t)newValue.at(10)) << 8) | ((uint8_t)newValue.at(9))) / 10.0;
|
||||
}
|
||||
|
||||
/*if (!settings.value(QZSettings::speed_power_based, QZSettings::default_speed_power_based).toBool())
|
||||
Speed = (double)((((uint8_t)newValue.at(4)) << 10) | ((uint8_t)newValue.at(9))) / 100.0;
|
||||
@@ -731,6 +736,19 @@ void fitplusbike::characteristicChanged(const QLowEnergyCharacteristic &characte
|
||||
}
|
||||
}
|
||||
|
||||
resistance_t fitplusbike::pelotonToBikeResistance(int pelotonResistance) {
|
||||
QSettings settings;
|
||||
double adjustedPelotonResistance = (pelotonResistance - settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble()) /
|
||||
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble();
|
||||
return (adjustedPelotonResistance * max_resistance) / 100;
|
||||
}
|
||||
|
||||
double fitplusbike::bikeResistanceToPeloton(double resistance) {
|
||||
QSettings settings;
|
||||
return (((resistance * 100) / max_resistance) * settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
|
||||
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
|
||||
}
|
||||
|
||||
void fitplusbike::btinit() {
|
||||
|
||||
QSettings settings;
|
||||
@@ -950,16 +968,28 @@ void fitplusbike::serviceScanDone(void) {
|
||||
QBluetoothUuid _gattCommunicationChannelServiceId((quint16)0xfff0);
|
||||
|
||||
gattCommunicationChannelService = m_control->createServiceObject(_gattCommunicationChannelServiceId);
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::stateChanged, this, &fitplusbike::stateChanged);
|
||||
gattCommunicationChannelService->discoverDetails();
|
||||
if(gattCommunicationChannelService) {
|
||||
connect(gattCommunicationChannelService, &QLowEnergyService::stateChanged, this, &fitplusbike::stateChanged);
|
||||
gattCommunicationChannelService->discoverDetails();
|
||||
|
||||
if (sportstech_sx600) {
|
||||
if (sportstech_sx600) {
|
||||
gattCommunicationChannelServiceFTMS = m_control->createServiceObject(QBluetoothUuid((quint16)0x1826));
|
||||
if (gattCommunicationChannelServiceFTMS) {
|
||||
qDebug() << "FTMS found!";
|
||||
connect(gattCommunicationChannelServiceFTMS, &QLowEnergyService::stateChanged, this,
|
||||
&fitplusbike::stateChanged);
|
||||
gattCommunicationChannelServiceFTMS->discoverDetails();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qDebug() << _gattCommunicationChannelServiceId << "not found!";
|
||||
gattCommunicationChannelServiceFTMS = m_control->createServiceObject(QBluetoothUuid((quint16)0x1826));
|
||||
if (gattCommunicationChannelServiceFTMS) {
|
||||
qDebug() << "FTMS found!";
|
||||
connect(gattCommunicationChannelServiceFTMS, &QLowEnergyService::stateChanged, this,
|
||||
&fitplusbike::stateChanged);
|
||||
gattCommunicationChannelServiceFTMS->discoverDetails();
|
||||
if(gattCommunicationChannelServiceFTMS) {
|
||||
QSettings settings;
|
||||
settings.setValue(QZSettings::ftms_bike, bluetoothDevice.name());
|
||||
qDebug() << "forcing FTMS bike since it has FTMS";
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested("FTMS bike found, restart the app to apply the change!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -986,6 +1016,7 @@ void fitplusbike::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
merach_MRK = true;
|
||||
} else if (device.name().toUpper().startsWith("H9110 OSAKA")) {
|
||||
qDebug() << QStringLiteral("H9110 OSAKA workaround enabled!");
|
||||
max_resistance = 32;
|
||||
H9110_OSAKA = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class fitplusbike : public bike {
|
||||
public:
|
||||
fitplusbike(bool noWriteResistance, bool noHeartService, int8_t bikeResistanceOffset, double bikeResistanceGain);
|
||||
resistance_t maxResistance() override { return max_resistance; }
|
||||
resistance_t pelotonToBikeResistance(int pelotonResistance) override;
|
||||
bool connected() override;
|
||||
resistance_t resistanceFromPowerRequest(uint16_t power) override;
|
||||
|
||||
@@ -49,6 +50,7 @@ class fitplusbike : public bike {
|
||||
void startDiscover();
|
||||
void forceResistance(resistance_t requestResistance);
|
||||
void sendPoll();
|
||||
double bikeResistanceToPeloton(double resistance);
|
||||
uint16_t watts() override;
|
||||
uint16_t wattsFromResistance(double resistance);
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ void fitshowtreadmill::update() {
|
||||
if (requestInclination != inc) {
|
||||
emit debug(QStringLiteral("writing incline ") + QString::number(requestInclination));
|
||||
inc = requestInclination;
|
||||
double speed = currentSpeed().value();
|
||||
double speed = currentSpeed().valueRaw();
|
||||
if (requestSpeed != -1) {
|
||||
speed = requestSpeed;
|
||||
requestSpeed = -1;
|
||||
|
||||
@@ -111,7 +111,7 @@ class fitshowtreadmill : public treadmill {
|
||||
bool firstCharacteristicChanged = true;
|
||||
int MAX_INCLINE = 30;
|
||||
int COUNTDOWN_VALUE = 0;
|
||||
int MAX_SPEED = 30;
|
||||
int MAX_SPEED = 300;
|
||||
int MIN_INCLINE = 0;
|
||||
int MIN_SPEED = 0;
|
||||
int UNIT = -100;
|
||||
|
||||
@@ -21,7 +21,7 @@ focustreadmill::focustreadmill(uint32_t pollDeviceTime, bool noConsole, bool noH
|
||||
double forceInitInclination) {
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
QZ_EnableDiscoveryCharsAndDescripttors = true;
|
||||
QZ_EnableDiscoveryCharsAndDescripttors = false;
|
||||
#endif
|
||||
|
||||
m_watt.setType(metric::METRIC_WATT);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "keepawakehelper.h"
|
||||
#endif
|
||||
#include <chrono>
|
||||
#include "wheelcircumference.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
extern quint8 QZ_EnableDiscoveryCharsAndDescripttors;
|
||||
@@ -35,14 +36,18 @@ ftmsbike::ftmsbike(bool noWriteResistance, bool noHeartService, int8_t bikeResis
|
||||
initDone = false;
|
||||
connect(refresh, &QTimer::timeout, this, &ftmsbike::update);
|
||||
refresh->start(settings.value(QZSettings::poll_device_time, QZSettings::default_poll_device_time).toInt());
|
||||
wheelCircumference::GearTable g;
|
||||
g.printTable();
|
||||
}
|
||||
|
||||
void ftmsbike::writeCharacteristicZwiftPlay(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
|
||||
bool wait_for_response) {
|
||||
QEventLoop loop;
|
||||
QTimer timeout;
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
|
||||
if(!zwiftPlayService) {
|
||||
if(!zwiftPlayService || !gears_zwift_ratio) {
|
||||
qDebug() << QStringLiteral("zwiftPlayService is null!");
|
||||
return;
|
||||
}
|
||||
@@ -74,14 +79,21 @@ void ftmsbike::writeCharacteristicZwiftPlay(uint8_t *data, uint8_t data_len, con
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
void ftmsbike::writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
|
||||
bool ftmsbike::writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log,
|
||||
bool wait_for_response) {
|
||||
QEventLoop loop;
|
||||
QTimer timeout;
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
|
||||
if(!gattFTMSService) {
|
||||
qDebug() << QStringLiteral("gattFTMSService is null!");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(zwiftPlayService && gears_zwift_ratio) {
|
||||
qDebug() << QStringLiteral("zwiftPlayService is present!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wait_for_response) {
|
||||
@@ -97,7 +109,7 @@ void ftmsbike::writeCharacteristic(uint8_t *data, uint8_t data_len, const QStrin
|
||||
}
|
||||
writeBuffer = new QByteArray((const char *)data, data_len);
|
||||
|
||||
if (gattWriteCharControlPointId.properties() & QLowEnergyCharacteristic::WriteNoResponse) {
|
||||
if (gattWriteCharControlPointId.properties() & QLowEnergyCharacteristic::WriteNoResponse && !DOMYOS) {
|
||||
gattFTMSService->writeCharacteristic(gattWriteCharControlPointId, *writeBuffer,
|
||||
QLowEnergyService::WriteWithoutResponse);
|
||||
} else {
|
||||
@@ -109,19 +121,33 @@ void ftmsbike::writeCharacteristic(uint8_t *data, uint8_t data_len, const QStrin
|
||||
}
|
||||
|
||||
loop.exec();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ftmsbike::init() {
|
||||
if (initDone)
|
||||
return;
|
||||
|
||||
if(ICSE) {
|
||||
uint8_t write[] = {FTMS_REQUEST_CONTROL};
|
||||
bool ret = writeCharacteristic(write, sizeof(write), "requestControl", false, true);
|
||||
write[0] = {FTMS_RESET};
|
||||
ret = writeCharacteristic(write, sizeof(write), "reset", false, true);
|
||||
}
|
||||
|
||||
uint8_t write[] = {FTMS_REQUEST_CONTROL};
|
||||
writeCharacteristic(write, sizeof(write), "requestControl", false, true);
|
||||
bool ret = writeCharacteristic(write, sizeof(write), "requestControl", false, true);
|
||||
write[0] = {FTMS_START_RESUME};
|
||||
writeCharacteristic(write, sizeof(write), "start simulation", false, true);
|
||||
ret = writeCharacteristic(write, sizeof(write), "start simulation", false, true);
|
||||
|
||||
initDone = true;
|
||||
initRequest = false;
|
||||
if(ret) {
|
||||
initDone = true;
|
||||
initRequest = false;
|
||||
}
|
||||
}
|
||||
|
||||
ftmsbike::~ftmsbike() {
|
||||
}
|
||||
|
||||
void ftmsbike::zwiftPlayInit() {
|
||||
@@ -212,8 +238,8 @@ void ftmsbike::forceResistance(resistance_t requestResistance) {
|
||||
|
||||
QSettings settings;
|
||||
if (!settings.value(QZSettings::ss2k_peloton, QZSettings::default_ss2k_peloton).toBool() &&
|
||||
resistance_lvl_mode == false && _3G_Cardio_RB == false) {
|
||||
uint8_t write[] = {FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
resistance_lvl_mode == false && _3G_Cardio_RB == false && JFBK5_0 == false) {
|
||||
uint8_t write[] = {FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS, 0x00, 0x00, 0x00, 0x00, 0x28, 0x19};
|
||||
|
||||
double fr = (((double)requestResistance) * bikeResistanceGain) + ((double)bikeResistanceOffset);
|
||||
requestResistance = fr;
|
||||
@@ -224,16 +250,30 @@ void ftmsbike::forceResistance(resistance_t requestResistance) {
|
||||
writeCharacteristic(write, sizeof(write),
|
||||
QStringLiteral("forceResistance ") + QString::number(requestResistance));
|
||||
} else {
|
||||
uint8_t write[] = {FTMS_SET_TARGET_RESISTANCE_LEVEL, 0x00};
|
||||
if(_3G_Cardio_RB)
|
||||
requestResistance = requestResistance * 10;
|
||||
write[1] = ((uint8_t)(requestResistance));
|
||||
writeCharacteristic(write, sizeof(write),
|
||||
QStringLiteral("forceResistance ") + QString::number(requestResistance));
|
||||
if(SL010)
|
||||
Resistance = requestResistance;
|
||||
|
||||
if(JFBK5_0 || DIRETO_XR) {
|
||||
uint8_t write[] = {FTMS_SET_TARGET_RESISTANCE_LEVEL, 0x00, 0x00};
|
||||
write[1] = ((uint16_t)requestResistance * 10) & 0xFF;
|
||||
write[2] = ((uint16_t)requestResistance * 10) >> 8;
|
||||
writeCharacteristic(write, sizeof(write),
|
||||
QStringLiteral("forceResistance ") + QString::number(requestResistance));
|
||||
} else {
|
||||
uint8_t write[] = {FTMS_SET_TARGET_RESISTANCE_LEVEL, 0x00};
|
||||
if(_3G_Cardio_RB || SL010)
|
||||
requestResistance = requestResistance * 10;
|
||||
write[1] = ((uint8_t)(requestResistance));
|
||||
writeCharacteristic(write, sizeof(write),
|
||||
QStringLiteral("forceResistance ") + QString::number(requestResistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ftmsbike::update() {
|
||||
|
||||
QSettings settings;
|
||||
|
||||
if (m_control->state() == QLowEnergyController::UnconnectedState) {
|
||||
emit disconnected();
|
||||
return;
|
||||
@@ -241,6 +281,13 @@ void ftmsbike::update() {
|
||||
|
||||
if (initRequest) {
|
||||
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 //&&
|
||||
@@ -264,7 +311,8 @@ void ftmsbike::update() {
|
||||
forceResistance(currentResistance().value());
|
||||
}
|
||||
|
||||
auto virtualBike = this->VirtualBike();
|
||||
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()) {
|
||||
if (requestResistance > 100) {
|
||||
@@ -274,120 +322,76 @@ void ftmsbike::update() {
|
||||
requestResistance = 1;
|
||||
}
|
||||
|
||||
if (requestResistance != currentResistance().value() || lastGearValue != gears()) {
|
||||
double gearMultiplier = 5;
|
||||
if(REEBOK)
|
||||
gearMultiplier = 1;
|
||||
resistance_t rR = requestResistance + (gears() * gearMultiplier);
|
||||
|
||||
if (rR != currentResistance().value() || lastGearValue != gears()) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
|
||||
// if the FTMS is connected, the ftmsCharacteristicChanged event will do all the stuff because it's a
|
||||
// FTMS bike. This condition handles the peloton requests
|
||||
if (((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike) &&
|
||||
(requestPower == 0 || requestPower == -1)) {
|
||||
if (((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike || resistance_lvl_mode) &&
|
||||
(requestPower == 0 || requestPower == -1 || resistance_lvl_mode)) {
|
||||
init();
|
||||
forceResistance(requestResistance + (gears() * 5));
|
||||
|
||||
forceResistance(rR);
|
||||
}
|
||||
}
|
||||
requestResistance = -1;
|
||||
if(!ICSE)
|
||||
requestResistance = -1;
|
||||
}
|
||||
|
||||
if((virtualBike && virtualBike->ftmsDeviceConnected()) && lastGearValue != gears() && lastRawRequestedInclinationValue != -100 && lastPacketFromFTMS.length() >= 7) {
|
||||
qDebug() << "injecting fake ftms frame in order to send the new gear value ASAP" << lastPacketFromFTMS.toHex(' ');
|
||||
ftmsCharacteristicChanged(QLowEnergyCharacteristic(), lastPacketFromFTMS);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
if(zwiftPlayService && gears_zwift_ratio && lastGearValue != gears()) {
|
||||
uint8_t gear1[] = {0x04, 0x2a, 0x03, 0x10, 0xdc, 0xec};
|
||||
uint8_t gear2[] = {0x04, 0x2a, 0x04, 0x10, 0xdc, 0xec, 0x01};
|
||||
uint32_t gear_value = 0;
|
||||
QSettings settings;
|
||||
wheelCircumference::GearTable table;
|
||||
wheelCircumference::GearTable::GearInfo g = table.getGear((int)gears());
|
||||
double original_ratio = ((double)settings.value(QZSettings::gear_crankset_size, QZSettings::default_gear_crankset_size).toDouble()) /
|
||||
((double)settings.value(QZSettings::gear_cog_size, QZSettings::default_gear_cog_size).toDouble());
|
||||
|
||||
double current_ratio = ((double)g.crankset / (double)g.rearCog);
|
||||
|
||||
uint32_t gear_value = static_cast<uint32_t>(10000.0 * (current_ratio/original_ratio) * (42.0/14.0));
|
||||
|
||||
qDebug() << "zwift hub gear current ratio" << current_ratio << g.crankset << g.rearCog << "gear_value" << gear_value << "original_ratio" << original_ratio;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
QByteArray proto = lockscreen::zwift_hub_setGearsCommand(gear_value);
|
||||
#else
|
||||
QByteArray proto;
|
||||
#endif
|
||||
#elif defined Q_OS_ANDROID
|
||||
QAndroidJniObject result = QAndroidJniObject::callStaticObjectMethod(
|
||||
"org/cagnulen/qdomyoszwift/ZwiftHubBike",
|
||||
"setGearCommand",
|
||||
"(I)[B",
|
||||
gear_value);
|
||||
|
||||
switch((int)gears()) {
|
||||
case 1:
|
||||
gear_value = 0x3acc;
|
||||
break;
|
||||
case 2:
|
||||
gear_value = 0x43fc;
|
||||
break;
|
||||
case 3:
|
||||
gear_value = 0x4dac;
|
||||
break;
|
||||
case 4:
|
||||
gear_value = 0x56d5;
|
||||
break;
|
||||
case 5:
|
||||
gear_value = 0x608c;
|
||||
break;
|
||||
case 6:
|
||||
gear_value = 0x6be8;
|
||||
break;
|
||||
case 7:
|
||||
gear_value = 0x77c4;
|
||||
break;
|
||||
case 8:
|
||||
gear_value = 0x183a0;
|
||||
break;
|
||||
case 9:
|
||||
gear_value = 0x191a8;
|
||||
break;
|
||||
case 10:
|
||||
gear_value = 0x19fb0;
|
||||
break;
|
||||
case 11:
|
||||
gear_value = 0x1adb8;
|
||||
break;
|
||||
case 12:
|
||||
gear_value = 0x1bbc0;
|
||||
break;
|
||||
case 13:
|
||||
gear_value = 0x1cbf3;
|
||||
break;
|
||||
case 14:
|
||||
gear_value = 0x1dca8;
|
||||
break;
|
||||
case 15:
|
||||
gear_value = 0x1ecdc;
|
||||
break;
|
||||
case 16:
|
||||
gear_value = 0x1fd90;
|
||||
break;
|
||||
case 17:
|
||||
gear_value = 0x290d4;
|
||||
break;
|
||||
case 18:
|
||||
gear_value = 0x2a498;
|
||||
break;
|
||||
case 19:
|
||||
gear_value = 0x2b7dc;
|
||||
break;
|
||||
case 20:
|
||||
gear_value = 0x2cb9f;
|
||||
break;
|
||||
case 21:
|
||||
gear_value = 0x2e2d8;
|
||||
break;
|
||||
case 22:
|
||||
gear_value = 0x2fa90;
|
||||
break;
|
||||
case 23:
|
||||
gear_value = 0x391c8;
|
||||
break;
|
||||
case 24:
|
||||
gear_value = 0x3acf3;
|
||||
break;
|
||||
default:
|
||||
// Gestione del caso di default
|
||||
break;
|
||||
if (!result.isValid()) {
|
||||
qDebug() << "setGearCommand returned invalid value";
|
||||
return;
|
||||
}
|
||||
|
||||
gear_value = gear_value * settings.value(QZSettings::gears_gain, QZSettings::default_gears_gain).toDouble();
|
||||
jbyteArray array = result.object<jbyteArray>();
|
||||
QAndroidJniEnvironment env;
|
||||
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
|
||||
jsize length = env->GetArrayLength(array);
|
||||
|
||||
if(gear_value < 0x10000) {
|
||||
gear1[4] = gear_value & 0xFF;
|
||||
gear1[5] = ((gear_value & 0xFF00) >> 8) & 0xFF;
|
||||
writeCharacteristicZwiftPlay(gear1, sizeof(gear1), "gear", false, true);
|
||||
} else {
|
||||
gear2[4] = gear_value & 0xFF;
|
||||
gear2[5] = ((gear_value & 0xFF00) >> 8) & 0xFF;
|
||||
gear2[6] = ((gear_value & 0xFF0000) >> 16) & 0xFF;
|
||||
writeCharacteristicZwiftPlay(gear2, sizeof(gear2), "gear", false, true);
|
||||
}
|
||||
QByteArray proto((char*)bytes, length);
|
||||
|
||||
env->ReleaseByteArrayElements(array, bytes, JNI_ABORT);
|
||||
#else
|
||||
QByteArray proto;
|
||||
qDebug() << "ERROR: gear message not handled!";
|
||||
return;
|
||||
#endif
|
||||
writeCharacteristicZwiftPlay((uint8_t*)proto.data(), proto.length(), "gear", false, true);
|
||||
|
||||
uint8_t gearApply[] = {0x00, 0x08, 0x88, 0x04};
|
||||
writeCharacteristicZwiftPlay(gearApply, sizeof(gearApply), "gearApply", false, true);
|
||||
@@ -395,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);
|
||||
@@ -416,7 +421,7 @@ void ftmsbike::update() {
|
||||
|
||||
QSettings settings;
|
||||
if (settings.value(QZSettings::ss2k_peloton, QZSettings::default_ss2k_peloton).toBool()) {
|
||||
uint8_t write[] = {FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t write[] = {FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS, 0x00, 0x00, 0x00, 0x00, 0x28, 0x19};
|
||||
|
||||
writeCharacteristic(write, sizeof(write), QStringLiteral("init SS2K"));
|
||||
}
|
||||
@@ -438,6 +443,8 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
bool disable_hr_frommachinery =
|
||||
settings.value(QZSettings::heart_ignore_builtin, QZSettings::default_heart_ignore_builtin).toBool();
|
||||
bool heart = false;
|
||||
bool watt_ignore_builtin =
|
||||
settings.value(QZSettings::watt_ignore_builtin, QZSettings::default_watt_ignore_builtin).toBool();
|
||||
|
||||
qDebug() << characteristic.uuid() << newValue.length() << QStringLiteral(" << ") << newValue.toHex(' ');
|
||||
|
||||
@@ -451,16 +458,57 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
return;
|
||||
}
|
||||
|
||||
if (characteristic.uuid() == QBluetoothUuid((quint16)0x2A19)) { // Battery Service
|
||||
if (characteristic.uuid() == QBluetoothUuid((quint16)0x2A19) && !D2RIDE) { // Battery Service
|
||||
if(newValue.length() > 0) {
|
||||
uint8_t b = (uint8_t)newValue.at(0);
|
||||
if(b != battery_level)
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested(QStringLiteral("Battery Level ") + QString::number(b) + " %");
|
||||
homeform::singleton()->setToastRequested(bluetoothDevice.name() + QStringLiteral(" Battery Level ") + QString::number(b) + " %");
|
||||
battery_level = b;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(characteristic.uuid() == QBluetoothUuid(QStringLiteral("00000002-19ca-4651-86e5-fa29dcdd09d1")) && newValue.at(0) == 0x03) {
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
m_watt = lockscreen::zwift_hub_getPowerFromBuffer(newValue.mid(1));
|
||||
qDebug() << "Current power: " << m_watt.value();
|
||||
|
||||
Cadence = lockscreen::zwift_hub_getCadenceFromBuffer(newValue.mid(1));
|
||||
qDebug() << "Current cadence: " << Cadence.value();
|
||||
#endif
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if(T2 && characteristic.uuid() == QBluetoothUuid(QStringLiteral("6e400003-b5a3-f393-e0a9-e50e24dcca9e")) && newValue.length() == 62) {
|
||||
int16_t gears = ((int16_t)(((int16_t)((uint8_t)newValue.at(55)) << 8) |
|
||||
(int16_t)((uint8_t)newValue.at(54))));
|
||||
|
||||
qDebug() << QStringLiteral("T2 gears event, actual gear") << gears << QStringLiteral("previous value") << T2_lastGear;
|
||||
|
||||
if (gears < T2_lastGear) {
|
||||
for (int i = 0; i < T2_lastGear - gears; ++i) {
|
||||
gearDown();
|
||||
}
|
||||
} else if (gears > T2_lastGear) {
|
||||
for (int i = 0; i < gears - T2_lastGear; ++i) {
|
||||
gearUp();
|
||||
}
|
||||
}
|
||||
|
||||
T2_lastGear = gears;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wattbike Atom First Generation - Display Gears
|
||||
if(WATTBIKE && characteristic.uuid() == QBluetoothUuid(QStringLiteral("b4cc1224-bc02-4cae-adb9-1217ad2860d1")) &&
|
||||
newValue.length() > 3 && newValue.at(1) == 0x03 && (uint8_t)newValue.at(2) == 0xb6) {
|
||||
uint8_t gear = newValue.at(3);
|
||||
qDebug() << "watt bike gears" << gear;
|
||||
setGears(gear);
|
||||
}
|
||||
|
||||
if (characteristic.uuid() == QBluetoothUuid((quint16)0x2AD2)) {
|
||||
|
||||
@@ -485,6 +533,12 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
uint16_t word_flags;
|
||||
};
|
||||
|
||||
// clean time in case for a long period we don't receive values
|
||||
if(lastRefreshCharacteristicChanged2AD2.secsTo(now) > 5) {
|
||||
qDebug() << "clearing lastRefreshCharacteristicChanged2AD2" << lastRefreshCharacteristicChanged2AD2 << now;
|
||||
lastRefreshCharacteristicChanged2AD2 = now;
|
||||
}
|
||||
|
||||
flags Flags;
|
||||
int index = 0;
|
||||
Flags.word_flags = (newValue.at(1) << 8) | newValue.at(0);
|
||||
@@ -552,12 +606,19 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
|
||||
|
||||
if (Flags.resistanceLvl) {
|
||||
Resistance = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
|
||||
(uint16_t)((uint8_t)newValue.at(index))));
|
||||
emit resistanceRead(Resistance.value());
|
||||
index += 2;
|
||||
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
|
||||
resistance_received = true;
|
||||
double d = ((double)(((uint16_t)((uint8_t)newValue.at(index + 1)) << 8) |
|
||||
(uint16_t)((uint8_t)newValue.at(index))));
|
||||
index += 2;
|
||||
if(Resistance.value() > 0) {
|
||||
if(BIKE_)
|
||||
d = d / 10.0;
|
||||
// for this bike, i will use the resistance that I set directly because the bike sends a different ratio.
|
||||
if(!SL010)
|
||||
Resistance = d;
|
||||
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
|
||||
emit resistanceRead(Resistance.value());
|
||||
resistance_received = true;
|
||||
}
|
||||
}
|
||||
double ac = 0.01243107769;
|
||||
double bc = 1.145964912;
|
||||
@@ -575,8 +636,8 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
br) /
|
||||
(2.0 * ar)) *
|
||||
settings.value(QZSettings::peloton_gain, QZSettings::default_peloton_gain).toDouble()) +
|
||||
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
|
||||
if (!resistance_received && !DU30_bike) {
|
||||
settings.value(QZSettings::peloton_offset, QZSettings::default_peloton_offset).toDouble();
|
||||
if (!resistance_received && !DU30_bike && !SL010) {
|
||||
Resistance = m_pelotonResistance;
|
||||
emit resistanceRead(Resistance.value());
|
||||
emit debug(QStringLiteral("Current Resistance: ") + QString::number(Resistance.value()));
|
||||
@@ -588,6 +649,10 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
// power table from an user
|
||||
if(DU30_bike) {
|
||||
m_watt = wattsFromResistance(Resistance.value());
|
||||
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
|
||||
} else if (LYDSTO && watt_ignore_builtin) {
|
||||
m_watt = wattFromHR(true);
|
||||
emit debug(QStringLiteral("Current Watt: ") + QString::number(m_watt.value()));
|
||||
} else if (settings.value(QZSettings::power_sensor_name, QZSettings::default_power_sensor_name)
|
||||
.toString()
|
||||
.startsWith(QStringLiteral("Disabled")))
|
||||
@@ -921,7 +986,18 @@ void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
|
||||
qDebug() << s->serviceUuid() << QStringLiteral("connected!");
|
||||
|
||||
if (settings.value(QZSettings::hammer_racer_s, QZSettings::default_hammer_racer_s).toBool() || ICSE) {
|
||||
if (ICSE) {
|
||||
QBluetoothUuid ftmsService((quint16)0x1826);
|
||||
QBluetoothUuid CSCService((quint16)0x1816);
|
||||
if (s->serviceUuid() != ftmsService && s->serviceUuid() != CSCService) {
|
||||
qDebug() << QStringLiteral("ICSE bike wants to be subscribed only to FTMS and CSC services in order "
|
||||
"to send metrics")
|
||||
<< s->serviceUuid();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.value(QZSettings::hammer_racer_s, QZSettings::default_hammer_racer_s).toBool() || SCH_190U || DOMYOS || SMB1) {
|
||||
QBluetoothUuid ftmsService((quint16)0x1826);
|
||||
if (s->serviceUuid() != ftmsService) {
|
||||
qDebug() << QStringLiteral("hammer racer bike wants to be subscribed only to FTMS service in order "
|
||||
@@ -997,7 +1073,7 @@ void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
}
|
||||
|
||||
if (gattFTMSService && gattWriteCharControlPointId.isValid() &&
|
||||
settings.value(QZSettings::hammer_racer_s, QZSettings::default_hammer_racer_s).toBool()) {
|
||||
(settings.value(QZSettings::hammer_racer_s, QZSettings::default_hammer_racer_s).toBool() || SMB1)) {
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -1041,8 +1117,8 @@ void ftmsbike::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
|
||||
void ftmsbike::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
|
||||
|
||||
if (!autoResistance()) {
|
||||
qDebug() << "ignoring routing FTMS packet to the bike from virtualbike because of auto resistance OFF"
|
||||
if (!autoResistance() || resistance_lvl_mode) {
|
||||
qDebug() << "ignoring routing FTMS packet to the bike from virtualbike because of auto resistance OFF or resistance lvl mode is on"
|
||||
<< characteristic.uuid() << newValue.toHex(' ');
|
||||
return;
|
||||
}
|
||||
@@ -1055,29 +1131,78 @@ void ftmsbike::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &charact
|
||||
qDebug() << "routing FTMS packet to the bike from virtualbike" << characteristic.uuid() << newValue.toHex(' ');
|
||||
|
||||
// handling gears
|
||||
if (b.at(0) == FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS && ((zwiftPlayService == nullptr && gears_zwift_ratio) || !gears_zwift_ratio)) {
|
||||
if (b.at(0) == FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS && (zwiftPlayService == nullptr || !gears_zwift_ratio)) {
|
||||
double min_inclination = settings.value(QZSettings::min_inclination, QZSettings::default_min_inclination).toDouble();
|
||||
double offset =
|
||||
settings.value(QZSettings::zwift_inclination_offset, QZSettings::default_zwift_inclination_offset).toDouble();
|
||||
double gain =
|
||||
settings.value(QZSettings::zwift_inclination_gain, QZSettings::default_zwift_inclination_gain).toDouble();
|
||||
|
||||
lastPacketFromFTMS.clear();
|
||||
for(int i=0; i<b.length(); i++)
|
||||
lastPacketFromFTMS.append(b.at(i));
|
||||
qDebug() << "lastPacketFromFTMS" << lastPacketFromFTMS.toHex(' ');
|
||||
int16_t slope = (((uint8_t)b.at(3)) + (b.at(4) << 8));
|
||||
|
||||
if (gears() != 0) {
|
||||
slope += (gears() * 50);
|
||||
}
|
||||
|
||||
if(min_inclination > (((double)slope) / 100.0)) {
|
||||
slope = min_inclination * 100;
|
||||
qDebug() << "grade override due to min_inclination " << min_inclination;
|
||||
}
|
||||
|
||||
slope *= gain;
|
||||
slope += (offset * 100);
|
||||
|
||||
b[3] = slope & 0xFF;
|
||||
b[4] = slope >> 8;
|
||||
|
||||
|
||||
qDebug() << "applying gears mod" << gears() << slope;
|
||||
/*} else if(b.at(0) == FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS && zwiftPlayService != nullptr && gears_zwift_ratio) {
|
||||
|
||||
} else if(b.at(0) == FTMS_SET_INDOOR_BIKE_SIMULATION_PARAMS && zwiftPlayService != nullptr && gears_zwift_ratio) {
|
||||
int16_t slope = (((uint8_t)b.at(3)) + (b.at(4) << 8));
|
||||
uint8_t gear2[] = {0x04, 0x22, 0x02, 0x10, 0x00};
|
||||
int g = (int)(((double)slope / 100.0) + settings.value(QZSettings::gears_offset, QZSettings::default_gears_offset).toDouble());
|
||||
if(g < 0) {
|
||||
g = 0;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
QByteArray message = lockscreen::zwift_hub_inclinationCommand(((double)slope) / 100.0);
|
||||
#else
|
||||
QByteArray message;
|
||||
#endif
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
QAndroidJniObject result = QAndroidJniObject::callStaticObjectMethod(
|
||||
"org/cagnulen/qdomyoszwift/ZwiftHubBike",
|
||||
"inclinationCommand",
|
||||
"(D)[B",
|
||||
((double)slope) / 100.0);
|
||||
|
||||
if(!result.isValid()) {
|
||||
qDebug() << "inclinationCommand returned invalid value";
|
||||
return;
|
||||
}
|
||||
gear2[4] = g;
|
||||
writeCharacteristicZwiftPlay(gear2, sizeof(gear2), "gearInclination", false, false);*/
|
||||
} else if(b.at(0) == FTMS_SET_TARGET_POWER && b.length() > 2) {
|
||||
|
||||
jbyteArray array = result.object<jbyteArray>();
|
||||
QAndroidJniEnvironment env;
|
||||
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
|
||||
jsize length = env->GetArrayLength(array);
|
||||
|
||||
QByteArray message((char*)bytes, length);
|
||||
|
||||
env->ReleaseByteArrayElements(array, bytes, JNI_ABORT);
|
||||
#else
|
||||
QByteArray message;
|
||||
qDebug() << "implement zwift hub protobuf!";
|
||||
return;
|
||||
#endif
|
||||
writeCharacteristicZwiftPlay((uint8_t*)message.data(), message.length(), "gearInclination", false, false);
|
||||
return;
|
||||
} else if(b.at(0) == FTMS_SET_TARGET_POWER && zwiftPlayService != nullptr && gears_zwift_ratio) {
|
||||
qDebug() << "discarding";
|
||||
return;
|
||||
}
|
||||
// gears on erg mode is quite useless and it's confusing
|
||||
/* else if(b.at(0) == FTMS_SET_TARGET_POWER && b.length() > 2) {
|
||||
lastPacketFromFTMS.clear();
|
||||
for(int i=0; i<b.length(); i++)
|
||||
lastPacketFromFTMS.append(b.at(i));
|
||||
@@ -1089,7 +1214,7 @@ void ftmsbike::ftmsCharacteristicChanged(const QLowEnergyCharacteristic &charact
|
||||
b[1] = power & 0xFF;
|
||||
b[2] = power >> 8;
|
||||
qDebug() << "applying gears mod" << gears() << gearsZwiftRatio() << power;
|
||||
}
|
||||
}*/
|
||||
|
||||
writeCharacteristic((uint8_t*)b.data(), b.length(), "injectWrite ", false, true);
|
||||
}
|
||||
@@ -1136,6 +1261,13 @@ void ftmsbike::serviceScanDone(void) {
|
||||
connect(gattCommunicationChannelService.constLast(), &QLowEnergyService::stateChanged, this,
|
||||
&ftmsbike::stateChanged);
|
||||
gattCommunicationChannelService.constLast()->discoverDetails();
|
||||
|
||||
// watt bikes has the 6 as default gear value
|
||||
if(s == QBluetoothUuid(QStringLiteral("b4cc1223-bc02-4cae-adb9-1217ad2860d1")) && SS2K == false) {
|
||||
WATTBIKE = true;
|
||||
qDebug() << QStringLiteral("restoring gear 6 to watt bikes");
|
||||
setGears(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1177,10 +1309,55 @@ void ftmsbike::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
ICSE = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("DOMYOS"))) {
|
||||
qDebug() << QStringLiteral("DOMYOS found");
|
||||
resistance_lvl_mode = true;
|
||||
DOMYOS = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("3G Cardio RB"))) {
|
||||
qDebug() << QStringLiteral("_3G_Cardio_RB found");
|
||||
_3G_Cardio_RB = true;
|
||||
} else if((bluetoothDevice.name().toUpper().startsWith("SCH_190U"))) {
|
||||
qDebug() << QStringLiteral("SCH_190U found");
|
||||
SCH_190U = true;
|
||||
} else if(bluetoothDevice.name().toUpper().startsWith("D2RIDE")) {
|
||||
qDebug() << QStringLiteral("D2RIDE found");
|
||||
D2RIDE = true;
|
||||
} else if(bluetoothDevice.name().toUpper().startsWith("VFSPINBIKE")) {
|
||||
qDebug() << QStringLiteral("VFSPINBIKE found");
|
||||
VFSPINBIKE = true;
|
||||
} else if(bluetoothDevice.name().toUpper().startsWith("SMARTSPIN2K")) {
|
||||
qDebug() << QStringLiteral("SS2K found");
|
||||
SS2K = true;
|
||||
} else if(bluetoothDevice.name().toUpper().startsWith("DIRETO XR")) {
|
||||
qDebug() << QStringLiteral("DIRETO XR found");
|
||||
DIRETO_XR = true;
|
||||
} else if(bluetoothDevice.name().toUpper().startsWith("JFBK5.0") || bluetoothDevice.name().toUpper().startsWith("JFBK7.0")) {
|
||||
qDebug() << QStringLiteral("JFBK5.0 found");
|
||||
resistance_lvl_mode = true;
|
||||
JFBK5_0 = true;
|
||||
} else if((bluetoothDevice.name().toUpper().startsWith("BIKE-"))) {
|
||||
qDebug() << QStringLiteral("BIKE- found");
|
||||
BIKE_ = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("SMB1"))) {
|
||||
qDebug() << QStringLiteral("SMB1 found");
|
||||
SMB1 = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("SPAX-BK-"))) {
|
||||
qDebug() << QStringLiteral("SPAX-BK found");
|
||||
resistance_lvl_mode = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("LYDSTO"))) {
|
||||
qDebug() << QStringLiteral("LYDSTO found");
|
||||
LYDSTO = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("SL010-"))) {
|
||||
qDebug() << QStringLiteral("SL010 found");
|
||||
SL010 = true;
|
||||
max_resistance = 25;
|
||||
resistance_lvl_mode = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("REEBOK"))) {
|
||||
qDebug() << QStringLiteral("REEBOK found");
|
||||
REEBOK = true;
|
||||
max_resistance = 32;
|
||||
resistance_lvl_mode = true;
|
||||
} else if ((bluetoothDevice.name().toUpper().startsWith("T2 "))) {
|
||||
qDebug() << QStringLiteral("T2 found");
|
||||
T2 = true;
|
||||
}
|
||||
|
||||
if(settings.value(QZSettings::force_resistance_instead_inclination, QZSettings::default_force_resistance_instead_inclination).toBool()) {
|
||||
@@ -1227,6 +1404,17 @@ bool ftmsbike::connected() {
|
||||
return m_control->state() == QLowEnergyController::DiscoveredState;
|
||||
}
|
||||
|
||||
void ftmsbike::setWheelDiameter(double diameter) {
|
||||
uint8_t write[] = {FTMS_SET_WHEEL_CIRCUMFERENCE, 0x00, 0x00};
|
||||
|
||||
diameter = diameter * 10.0;
|
||||
|
||||
write[1] = ((uint16_t)diameter) & 0xFF;
|
||||
write[2] = ((uint16_t)diameter) >> 8;
|
||||
|
||||
writeCharacteristic(write, sizeof(write), QStringLiteral("setWheelCircumference ") + QString::number(diameter));
|
||||
}
|
||||
|
||||
uint16_t ftmsbike::watts() {
|
||||
if (currentCadence().value() == 0) {
|
||||
return 0;
|
||||
@@ -1243,3 +1431,30 @@ void ftmsbike::controllerStateChanged(QLowEnergyController::ControllerState stat
|
||||
m_control->connectToDevice();
|
||||
}
|
||||
}
|
||||
|
||||
double ftmsbike::maxGears() {
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
|
||||
if((zwiftPlayService != nullptr) && gears_zwift_ratio) {
|
||||
wheelCircumference::GearTable g;
|
||||
return g.maxGears;
|
||||
} else if(WATTBIKE) {
|
||||
return 22;
|
||||
} else {
|
||||
return 9999.0;
|
||||
}
|
||||
}
|
||||
|
||||
double ftmsbike::minGears() {
|
||||
QSettings settings;
|
||||
bool gears_zwift_ratio = settings.value(QZSettings::gears_zwift_ratio, QZSettings::default_gears_zwift_ratio).toBool();
|
||||
|
||||
if((zwiftPlayService != nullptr) && gears_zwift_ratio ) {
|
||||
return 1;
|
||||
} else if(WATTBIKE) {
|
||||
return 1;
|
||||
} else {
|
||||
return -9999.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "wheelcircumference.h"
|
||||
#include "devices/bike.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
@@ -69,18 +70,22 @@ class ftmsbike : public bike {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ftmsbike(bool noWriteResistance, bool noHeartService, int8_t bikeResistanceOffset, double bikeResistanceGain);
|
||||
~ftmsbike();
|
||||
bool connected() override;
|
||||
resistance_t pelotonToBikeResistance(int pelotonResistance) override;
|
||||
resistance_t maxResistance() override { return max_resistance; }
|
||||
resistance_t resistanceFromPowerRequest(uint16_t power) override;
|
||||
double maxGears() override;
|
||||
double minGears() override;
|
||||
|
||||
private:
|
||||
void writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
|
||||
bool writeCharacteristic(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
|
||||
bool wait_for_response = false);
|
||||
void writeCharacteristicZwiftPlay(uint8_t *data, uint8_t data_len, const QString &info, bool disable_log = false,
|
||||
bool wait_for_response = false);
|
||||
void zwiftPlayInit();
|
||||
void startDiscover();
|
||||
void setWheelDiameter(double diameter);
|
||||
uint16_t watts() override;
|
||||
void init();
|
||||
void forceResistance(resistance_t requestResistance);
|
||||
@@ -122,6 +127,21 @@ class ftmsbike : public bike {
|
||||
bool ICSE = false;
|
||||
bool DOMYOS = false;
|
||||
bool _3G_Cardio_RB = false;
|
||||
bool SCH_190U = false;
|
||||
bool D2RIDE = false;
|
||||
bool WATTBIKE = false;
|
||||
bool VFSPINBIKE = false;
|
||||
bool SS2K = false;
|
||||
bool DIRETO_XR = false;
|
||||
bool JFBK5_0 = false;
|
||||
bool BIKE_ = false;
|
||||
bool SMB1 = false;
|
||||
bool LYDSTO = false;
|
||||
bool SL010 = false;
|
||||
bool REEBOK = false;
|
||||
bool T2 = false;
|
||||
|
||||
int16_t T2_lastGear = 0;
|
||||
|
||||
uint8_t battery_level = 0;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user