Compare commits

...

14 Commits

Author SHA1 Message Date
Roberto Viola
f2d8bd9dc3 Update PlayerStateWrapper.h 2023-12-30 18:54:37 +00:00
Roberto Viola
f03068189b final build for ios 2023-12-30 19:28:04 +01:00
Roberto Viola
8e166ff6cf works perfectly with zwift! 2023-12-30 18:57:36 +01:00
Roberto Viola
f1d2b5ec40 works on ios! 2023-12-30 18:14:34 +01:00
Roberto Viola
d858eff4cc Create zwift_messages.pb.swift 2023-12-29 18:01:13 +01:00
Roberto Viola
ca4ce00743 getting protodata 2023-12-28 18:49:01 +01:00
Roberto Viola
534d39053e Update PlayerStateWrapper.h 2023-12-28 17:21:31 +01:00
Roberto Viola
8fb3030933 Update main.cpp 2023-12-28 17:03:39 +01:00
Roberto Viola
f43db4fe31 Update project.pbxproj 2023-12-28 16:55:15 +01:00
Roberto Viola
98603ba8b8 fixing build 2023-12-28 16:49:34 +01:00
Roberto Viola
ec85319987 trying without the protobuffer 2023-12-28 16:45:57 +01:00
Roberto Viola
5b9122e337 Create zwift_messages.proto 2023-12-28 15:43:28 +01:00
Roberto Viola
84e27e6db1 protobuf added 2023-12-28 15:43:23 +01:00
Roberto Viola
18543d5d71 adding files 2023-12-28 15:41:48 +01:00
17 changed files with 14722 additions and 4 deletions

View File

@@ -182,6 +182,11 @@
872DCC3B2A18D4C000EC9F68 /* moc_virtualdevice.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 872DCC3A2A18D4C000EC9F68 /* moc_virtualdevice.cpp */; };
873063BE259DF20000DA0F44 /* heartratebelt.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 873063BC259DF20000DA0F44 /* heartratebelt.cpp */; };
873063C0259DF2C500DA0F44 /* moc_heartratebelt.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 873063BF259DF2C500DA0F44 /* moc_heartratebelt.cpp */; };
8730A38B2B404081007E336D /* SwiftProtobuf in Link Binary With Libraries */ = {isa = PBXBuildFile; productRef = 8730A38A2B404081007E336D /* SwiftProtobuf */; };
8730A38D2B404081007E336D /* SwiftProtobufPluginLibrary in Link Binary With Libraries */ = {isa = PBXBuildFile; productRef = 8730A38C2B404081007E336D /* SwiftProtobufPluginLibrary */; };
8730A38F2B404081007E336D /* protoc-gen-swift in Link Binary With Libraries */ = {isa = PBXBuildFile; productRef = 8730A38E2B404081007E336D /* protoc-gen-swift */; };
8730A3922B404159007E336D /* zwift_protobuf_layer.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8730A3912B404159007E336D /* zwift_protobuf_layer.swift */; };
8730A3932B4078E6007E336D /* zwift_messages.pb.swift in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8730A3902B4040AF007E336D /* zwift_messages.pb.swift */; };
87310B1E266FBB59008BA0D6 /* smartrowrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87310B1B266FBB54008BA0D6 /* smartrowrower.cpp */; };
87310B1F266FBB59008BA0D6 /* homefitnessbuddy.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87310B1C266FBB57008BA0D6 /* homefitnessbuddy.cpp */; };
87310B22266FBB78008BA0D6 /* moc_homefitnessbuddy.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87310B20266FBB6E008BA0D6 /* moc_homefitnessbuddy.cpp */; };
@@ -329,6 +334,8 @@
878531652711A3E1004B153D /* activiotreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878531612711A3E1004B153D /* activiotreadmill.cpp */; };
878531682711A3EC004B153D /* moc_activiotreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878531662711A3EB004B153D /* moc_activiotreadmill.cpp */; };
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 */; };
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 */; };
@@ -909,6 +916,8 @@
873063BC259DF20000DA0F44 /* heartratebelt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = heartratebelt.cpp; path = ../src/heartratebelt.cpp; sourceTree = "<group>"; };
873063BD259DF20000DA0F44 /* heartratebelt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = heartratebelt.h; path = ../src/heartratebelt.h; sourceTree = "<group>"; };
873063BF259DF2C500DA0F44 /* moc_heartratebelt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_heartratebelt.cpp; sourceTree = "<group>"; };
8730A3902B4040AF007E336D /* zwift_messages.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = zwift_messages.pb.swift; path = "../src/zwift-api/zwift_messages.pb.swift"; sourceTree = "<group>"; };
8730A3912B404159007E336D /* zwift_protobuf_layer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = zwift_protobuf_layer.swift; path = "../src/zwift-api/zwift_protobuf_layer.swift"; sourceTree = "<group>"; };
87310B1A266FBB54008BA0D6 /* homefitnessbuddy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = homefitnessbuddy.h; path = ../src/homefitnessbuddy.h; sourceTree = "<group>"; };
87310B1B266FBB54008BA0D6 /* smartrowrower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = smartrowrower.cpp; path = ../src/smartrowrower.cpp; sourceTree = "<group>"; };
87310B1C266FBB57008BA0D6 /* homefitnessbuddy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = homefitnessbuddy.cpp; path = ../src/homefitnessbuddy.cpp; sourceTree = "<group>"; };
@@ -1133,6 +1142,10 @@
878531632711A3E1004B153D /* fakebike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fakebike.h; path = ../src/fakebike.h; sourceTree = "<group>"; };
878531662711A3EB004B153D /* moc_activiotreadmill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_activiotreadmill.cpp; sourceTree = "<group>"; };
878531672711A3EB004B153D /* moc_fakebike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_fakebike.cpp; sourceTree = "<group>"; };
8785D53F2B3DD0EC005A2EB7 /* zwift_client_auth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zwift_client_auth.h; path = "../src/zwift-api/zwift_client_auth.h"; sourceTree = "<group>"; };
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>"; };
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/yesoulbike.cpp; sourceTree = "<group>"; };
878A331825AB4FF800BD13E1 /* yesoulbike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yesoulbike.h; path = ../src/yesoulbike.h; sourceTree = "<group>"; };
@@ -1672,6 +1685,7 @@
4FA524144B680D741D33EACB /* qtgraphicaleffectsprivate in Link Binary With Libraries */,
879F74152893D732009A64C8 /* CoreMedia.framework in Link Binary With Libraries */,
1C823E40F377B93A664EAC1B /* modelsplugin in Link Binary With Libraries */,
8730A38D2B404081007E336D /* SwiftProtobufPluginLibrary in Link Binary With Libraries */,
B9DED9CC16B0F3339F363FBF /* workerscriptplugin in Link Binary With Libraries */,
023642106C14651D2E1F4D5D /* dialogplugin in Link Binary With Libraries */,
133CA0345CD2BFB03079A655 /* qmlfolderlistmodelplugin in Link Binary With Libraries */,
@@ -1680,6 +1694,7 @@
3BD5A5F95DF5239184791B58 /* dialogsprivateplugin in Link Binary With Libraries */,
EF98F8C34BE322582E9B73D7 /* qtquickcontrolsplugin in Link Binary With Libraries */,
7C8D236C48F2964061C3457C /* widgetsplugin in Link Binary With Libraries */,
8730A38B2B404081007E336D /* SwiftProtobuf in Link Binary With Libraries */,
401B341C04019FFA2146E79D /* Qt5Widgets in Link Binary With Libraries */,
61EC5BE7EEC8D905C63FF628 /* qmlplugin in Link Binary With Libraries */,
877A080D2893DC4300C0F0AB /* CoreVideo.framework in Link Binary With Libraries */,
@@ -1693,6 +1708,7 @@
5A4A6C1B12D4D769431E876E /* qtquickcontrols2materialstyleplugin in Link Binary With Libraries */,
A044AC393BA2327284BB63B4 /* qtquickcontrols2fusionstyleplugin in Link Binary With Libraries */,
B413AFE7A08F2D63D57F683E /* qtquickcontrols2universalstyleplugin in Link Binary With Libraries */,
8730A38F2B404081007E336D /* protoc-gen-swift in Link Binary With Libraries */,
7BA3E396471B90F086588B5C /* qtgraphicaleffectsplugin in Link Binary With Libraries */,
F020E5470020A5BF3EB828A3 /* qtquickcontrols2imaginestyleplugin in Link Binary With Libraries */,
964DFEF4056724121ED9A98D /* Qt5QuickControls2 in Link Binary With Libraries */,
@@ -1917,6 +1933,11 @@
2EB56BE3C2D93CDAB0C52E67 /* Sources */ = {
isa = PBXGroup;
children = (
8730A3902B4040AF007E336D /* zwift_messages.pb.swift */,
8785D5412B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp */,
8785D5422B3DD105005A2EB7 /* moc_zwift_client_auth.cpp */,
8785D5402B3DD0EC005A2EB7 /* PlayerStateWrapper.h */,
8785D53F2B3DD0EC005A2EB7 /* zwift_client_auth.h */,
8727C7D32B3BF1E4005429EB /* moc_proformtelnetbike.cpp */,
8727C7D22B3BF1E4005429EB /* moc_QTelnet.cpp */,
8727C7CC2B3BF1B8005429EB /* proformtelnetbike.cpp */,
@@ -2289,6 +2310,7 @@
C8CE72E7B224D8B886614E3F /* domyosbike.h */,
8710707229C4A5E70094D0F3 /* GarminConnect.swift */,
87A2E0202B2B024200E6168F /* swiftDebug.h */,
8730A3912B404159007E336D /* zwift_protobuf_layer.swift */,
);
name = Sources;
sourceTree = "<group>";
@@ -2816,6 +2838,11 @@
876E4E312594748100BD5714 /* PBXTargetDependency */,
);
name = qdomyoszwift;
packageProductDependencies = (
8730A38A2B404081007E336D /* SwiftProtobuf */,
8730A38C2B404081007E336D /* SwiftProtobufPluginLibrary */,
8730A38E2B404081007E336D /* protoc-gen-swift */,
);
productName = qdomyoszwift;
productReference = 040B10E2EF2CEF79F2205FE2 /* qdomyoszwift.app */;
productType = "com.apple.product-type.application";
@@ -2891,6 +2918,9 @@
Base,
);
mainGroup = E8C543AB96796ECAA2E65C57 /* qdomyoszwift */;
packageReferences = (
8730A3892B404081007E336D /* XCRemoteSwiftPackageReference "swift-protobuf" */,
);
productRefGroup = FE0A091FDBFB3E9C31B7A1BD /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -3036,6 +3066,7 @@
87C5F0D326285E7E0067A1B5 /* moc_mimecontentformatter.cpp in Compile Sources */,
8718CBAB263063CE004BF4EE /* moc_templateinfosenderbuilder.cpp in Compile Sources */,
C6B3CD471768392E18F85819 /* fit_accumulated_field.cpp in Compile Sources */,
8730A3932B4078E6007E336D /* zwift_messages.pb.swift in Compile Sources */,
3D7395B0A17915A06361C7F3 /* fit_accumulator.cpp in Compile Sources */,
2A61806454201575EDB3F94F /* fit_buffer_encode.cpp in Compile Sources */,
87F02E4229178545000DB52C /* moc_octaneelliptical.cpp in Compile Sources */,
@@ -3071,6 +3102,7 @@
87CC3B9D25A08812001EC5A8 /* moc_domyoselliptical.cpp in Compile Sources */,
87900DC6268B672E000CB351 /* renphobike.cpp in Compile Sources */,
879F16462847E55C00CE4945 /* proformellipticaltrainer.cpp in Compile Sources */,
8730A3922B404159007E336D /* zwift_protobuf_layer.swift in Compile Sources */,
87917A7728E768D200F8D9AC /* Client.swift in Compile Sources */,
873824B927E64707004F1B46 /* moc_provider.cpp in Compile Sources */,
8727A47727849EA600019B5D /* paferstreadmill.cpp in Compile Sources */,
@@ -3136,6 +3168,7 @@
87097D2F275EA9A30020EE6F /* sportsplusbike.cpp in Compile Sources */,
333C629F93DB3941862924F7 /* fit_field_base.cpp in Compile Sources */,
87473A9827ECAA0500C203F5 /* moc_proformrower.cpp in Compile Sources */,
8785D5442B3DD105005A2EB7 /* moc_zwift_client_auth.cpp in Compile Sources */,
EEC10275DF646075E08DDC9B /* fit_field_definition.cpp in Compile Sources */,
87062645259480AB00D06586 /* LocalNotificationHelper.swift in Compile Sources */,
8718CBA4263063BD004BF4EE /* templateinfosenderbuilder.cpp in Compile Sources */,
@@ -3283,6 +3316,7 @@
C3D1FD2587BF6F15B58BA675 /* moc_bluetooth.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 */,
@@ -4508,6 +4542,35 @@
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
8730A3892B404081007E336D /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-protobuf.git";
requirement = {
kind = exactVersion;
version = 1.25.2;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
8730A38A2B404081007E336D /* SwiftProtobuf */ = {
isa = XCSwiftPackageProductDependency;
package = 8730A3892B404081007E336D /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = SwiftProtobuf;
};
8730A38C2B404081007E336D /* SwiftProtobufPluginLibrary */ = {
isa = XCSwiftPackageProductDependency;
package = 8730A3892B404081007E336D /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = SwiftProtobufPluginLibrary;
};
8730A38E2B404081007E336D /* protoc-gen-swift */ = {
isa = XCSwiftPackageProductDependency;
package = 8730A3892B404081007E336D /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = "protoc-gen-swift";
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 6DB9C3763D02B1415CD9D565 /* Project object */;
}

View File

@@ -67,6 +67,13 @@ class lockscreen {
// Elite Aria Fan
void eliteAriaFan();
void eliteAriaFan_fanSpeedRequest(unsigned char speed);
// Zwift API
void zwift_api_decodemessage_player(const char* data, int len);
float zwift_api_getaltitude();
int zwift_api_getdistance();
float zwift_api_getlatitude();
float zwift_api_getlongitude();
};
#endif // LOCKSCREEN_H

View File

@@ -29,6 +29,8 @@ static AdbClient *_adb = 0;
static ios_eliteariafan* ios_eliteAriaFan = nil;
static zwift_protobuf_layer* zwiftProtobufLayer = nil;
void lockscreen::setTimerDisabled() {
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
}
@@ -37,6 +39,7 @@ void lockscreen::request()
{
h = [[healthkit alloc] init];
[h request];
zwiftProtobufLayer = [[zwift_protobuf_layer alloc] init];
if (@available(iOS 13, *)) {
Garmin = [[GarminConnect alloc] init];
}
@@ -298,4 +301,25 @@ void lockscreen::eliteAriaFan_fanSpeedRequest(unsigned char speed) {
[ios_eliteAriaFan fanSpeedRequest:speed];
}
}
void lockscreen::zwift_api_decodemessage_player(const char* data, int len) {
NSData *d = [NSData dataWithBytes:data length:len];
[zwiftProtobufLayer getPlayerStateWithValue:d];
}
float lockscreen::zwift_api_getaltitude() {
return [zwiftProtobufLayer getAltitude];
}
int lockscreen::zwift_api_getdistance() {
return [zwiftProtobufLayer getDistance];
}
float lockscreen::zwift_api_getlatitude() {
return [zwiftProtobufLayer getLatitude];
}
float lockscreen::zwift_api_getlongitude() {
return [zwiftProtobufLayer getLongitude];
}
#endif

View File

@@ -282,7 +282,6 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS
}
int main(int argc, char *argv[]) {
#ifdef Q_OS_WIN32
qputenv("QT_MULTIMEDIA_PREFERRED_PLUGINS", "windowsmediafoundation");
#endif

View File

@@ -291,6 +291,8 @@ HEADERS += \
$$PWD/proformtelnetbike.h \
$$PWD/windows_zwift_workout_paddleocr_thread.h \
$$PWD/fakerower.h \
$$PWD/zwift-api/PlayerStateWrapper.h \
$$PWD/zwift-api/zwift_client_auth.h \
virtualdevice.h \
$$PWD/androidactivityresultreceiver.h \
$$PWD/androidadblog.h \

View File

@@ -690,8 +690,12 @@ const QString QZSettings::proformtdf1ip = QStringLiteral("proformtdf1ip");
const QString QZSettings::default_proformtdf1ip = QStringLiteral("");
const QString QZSettings::proform_bike_225_csx = QStringLiteral("proform_bike_225_csx");
const QString QZSettings::proform_treadmill_l6_0s = QStringLiteral("proform_treadmill_l6_0s");
const QString QZSettings::zwift_username = QStringLiteral("zwift_username");
const QString QZSettings::default_zwift_username = QStringLiteral("username");
const QString QZSettings::zwift_password = QStringLiteral("zwift_password");
const QString QZSettings::default_zwift_password = QStringLiteral("password");
const uint32_t allSettingsCount = 579;
const uint32_t allSettingsCount = 581;
QVariant allSettings[allSettingsCount][2] = {
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
@@ -1277,6 +1281,9 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::proform_bike_225_csx, QZSettings::default_proform_bike_225_csx},
{QZSettings::proform_treadmill_l6_0s, QZSettings::default_proform_treadmill_l6_0s},
{QZSettings::proformtdf1ip, QZSettings::default_proformtdf1ip},
{QZSettings::zwift_username, QZSettings::default_zwift_username},
{QZSettings::zwift_password, QZSettings::default_zwift_password},
};
void QZSettings::qDebugAllSettings(bool showDefaults) {

View File

@@ -1938,6 +1938,13 @@ class QZSettings {
static const QString proformtdf1ip;
static const QString default_proformtdf1ip;
static const QString zwift_username;
static const QString default_zwift_username;
static const QString zwift_password;
static const QString default_zwift_password;
/**
* @brief Write the QSettings values using the constants from this namespace.
* @param showDefaults Optionally indicates if the default should be shown with the key.

View File

@@ -859,6 +859,8 @@ import QtQuick.Dialogs 1.0
// from version 2.16.30
property bool proform_treadmill_l6_0s: false
property string proformtdf1ip: ""
property string zwift_username: ""
property string zwift_password: ""
}
function paddingZeros(text, limit) {
@@ -4416,7 +4418,6 @@ import QtQuick.Dialogs 1.0
}
AccordionElement {
visible: OS_VERSION === "Other"
title: qsTr("Zwift Options") + "\uD83E\uDD47"
indicatRectColor: Material.color(Material.Grey)
textColor: Material.color(Material.Grey)
@@ -4424,6 +4425,84 @@ import QtQuick.Dialogs 1.0
accordionContent: ColumnLayout {
spacing: 0
RowLayout {
spacing: 10
Label {
text: qsTr("Username:")
Layout.fillWidth: true
}
TextField {
id: zwiftUsernameTextField
text: settings.zwift_username
horizontalAlignment: Text.AlignRight
Layout.fillHeight: false
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onAccepted: settings.zwift_username = text
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
}
Button {
id: okZwiftUsernameButton
text: "OK"
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: { settings.zwift_username = zwiftUsernameTextField.text; window.settings_restart_to_apply = true; toast.show("Setting saved!"); }
}
}
Label {
text: qsTr("Enter the email address you use to login to Zwift. Ensure there are no spaces before or after your email. Click OK.")
font.bold: true
font.italic: true
font.pixelSize: 9
textFormat: Text.PlainText
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
color: Material.color(Material.Lime)
}
RowLayout {
spacing: 10
Label {
id: labelZwiftPassword
//text: qsTr("Password:") + ((rootItem.zwiftLogin===-1)?"":(rootItem.zwiftLogin===1?"\u2705":"\u274c"))
text: qsTr("Password:")
Layout.fillWidth: true
}
TextField {
id: zwiftPasswordTextField
text: settings.zwift_password
horizontalAlignment: Text.AlignRight
Layout.fillHeight: false
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
inputMethodHints: Qt.ImhHiddenText
echoMode: TextInput.PasswordEchoOnEdit
onAccepted: settings.zwift_password = text
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
}
Button {
id: okZwiftPasswordButton
text: "OK"
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: { settings.zwift_password = zwiftPasswordTextField.text; window.settings_restart_to_apply = true; toast.show("Setting saved!"); }
}
}
Label {
//text: qsTr("Enter the password you use to login to Zwift. Click OK. If you have entered the correct login credentials and the QZ is able to access your account, you will see a when you reopen QZ. This is a secure login, not accessible by anyone but you.")
text: qsTr("Enter the password you use to login to Zwift. Click OK.")
font.bold: true
font.italic: true
font.pixelSize: 9
textFormat: Text.PlainText
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
color: Material.color(Material.Lime)
}
SwitchDelegate {
text: qsTr("Zwift Treadmill Auto Inclination")
spacing: 0

View File

@@ -29,6 +29,12 @@ trainprogram::trainprogram(const QList<trainrow> &rows, bluetooth *b, QString *d
this->description = *description;
if (tags)
this->tags = *tags;
if(settings.value(QZSettings::zwift_username, QZSettings::default_zwift_username).toString().length() > 0) {
zwift_auth_token = new AuthToken(settings.value(QZSettings::zwift_username, QZSettings::default_zwift_username).toString(), settings.value(QZSettings::zwift_password, QZSettings::default_zwift_password).toString());
zwift_auth_token->getAccessToken();
}
/*
int c = 0;
for (c = 0; c < rows.length(); c++) {
@@ -572,7 +578,6 @@ void trainprogram::scheduler() {
QMutexLocker(&this->schedulerMutex);
QSettings settings;
// outside the if case about a valid train program because the information for the floating window url should be
// sent anyway
if (settings.value(QZSettings::peloton_companion_workout_ocr, QZSettings::default_companion_peloton_workout_ocr)
@@ -590,6 +595,54 @@ void trainprogram::scheduler() {
(bluetoothManager->device()->currentSpeed().value() <= 0 &&
!settings.value(QZSettings::continuous_moving, QZSettings::default_continuous_moving).toBool()) ||
bluetoothManager->device()->isPaused()) {
if(bluetoothManager->device() && bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL &&
settings.value(QZSettings::zwift_username, QZSettings::default_zwift_username).toString().length() > 0 &&
zwift_auth_token->access_token.length() > 0) {
if(!zwift_world) {
zwift_world = new World(1, zwift_auth_token->getAccessToken());
qDebug() << "creating zwift api world";
}
else {
#ifdef Q_OS_IOS
#ifndef IO_UNDER_QT
if(!h)
h = new lockscreen();
if(zwift_player_id == -1) {
QString id = zwift_world->player_id();
QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(id.toLocal8Bit(), &parseError);
QJsonObject ride = document.object();
qDebug() << "zwift api player" << ride;
zwift_player_id = ride[QStringLiteral("id")].toInt();
} else {
static int zwift_counter = 5;
if(zwift_counter++ >= 4) {
zwift_counter = 0;
QByteArray b = zwift_world->playerStatus(zwift_player_id);
h->zwift_api_decodemessage_player(b.data(), b.length());
float alt = h->zwift_api_getaltitude();
float distance = h->zwift_api_getdistance();
static float old_distance = 0;
static float old_alt = 0;
if(old_distance > 0) {
float delta = distance - old_distance;
float deltaA = alt - old_alt;
float incline = (deltaA / delta) / 2.0;
if(delta > 1) {
qDebug() << "zwift api incline" << incline << delta << deltaA;
bluetoothManager->device()->changeInclination(incline, incline);
}
}
old_distance = distance;
old_alt = alt;
}
}
#endif
#endif
}
}
// in case no workout has been selected
// Zwift OCR

View File

@@ -8,6 +8,13 @@
#include <QTime>
#include <QTimer>
#ifdef Q_OS_IOS
#include "ios/lockscreen.h"
#endif
#include "zwift-api/PlayerStateWrapper.h"
#include "zwift-api/zwift_client_auth.h"
class trainrow {
public:
QTime duration = QTime(0, 0, 0, 0);
@@ -155,6 +162,15 @@ private slots:
QUdpSocket* pelotonOCRsocket = nullptr;
void pelotonOCRcomputeTime(QString t);
AuthToken* zwift_auth_token = nullptr;
World* zwift_world = nullptr;
int zwift_player_id = -1;
#ifdef Q_OS_IOS
lockscreen *h = 0;
#endif
};
#endif // TRAINPROGRAM_H

View File

@@ -0,0 +1,178 @@
#ifndef PLAYERSTATEWRAPPER_H
#define PLAYERSTATEWRAPPER_H
#include <QMap>
#include <QString>
#include <QJsonObject>
#include <QJsonDocument>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>
#include <QDebug>
class ZwiftRequest: public QObject {
Q_OBJECT
public:
ZwiftRequest(const QString& getAccessToken) : getAccessToken(getAccessToken) {}
QString json(const QString& url) {
QNetworkRequest request(QUrl(BASE_URL + url));
request.setRawHeader("Accept", "application/json");
request.setRawHeader("Authorization", "Bearer " + getAccessToken.toUtf8());
QNetworkReply* reply = manager.get(request);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Error: " << reply->errorString();
return "";
}
return reply->readAll();
}
QByteArray protobuf(const QString& url) {
QNetworkRequest request(QUrl(BASE_URL + url));
request.setRawHeader("Accept", "application/x-protobuf-lite");
request.setRawHeader("Authorization", "Bearer " + getAccessToken.toUtf8());
QNetworkReply* reply = manager.get(request);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Error: " << reply->errorString();
return QByteArray();
}
return reply->readAll();
}
private:
QNetworkAccessManager manager;
const QString BASE_URL = "https://us-or-rly101.zwift.com";
const QString getAccessToken;
};
class World {
public:
World(int worldId, const QString& getAccessToken) : worldId(worldId), request(getAccessToken) {}
QString getPlayers() {
return request.json("/relay/worlds/" + QString::number(worldId));
}
QByteArray playerStatus(int playerId) {
QByteArray buffer = request.protobuf("/relay/worlds/" + QString::number(worldId) + "/players/" + QString::number(playerId));
return buffer;
}
QString player_id() {
return request.json("/api/profiles/me");
}
private:
int worldId;
ZwiftRequest request;
};
class PlayerStateWrapper {
public:
enum TURN_SIGNALS {
RIGHT = 0,
LEFT = 1,
STRAIGHT = 2
};
//PlayerStateWrapper(const zwift_messages::PlayerState& playerState) : playerState(playerState) {}
int getRideOns() {
return (playerState.at(19) >> 24) & 0xfff;
}
bool isTurning() {
return (playerState.at(19) & 8) != 0;
}
bool isForward() {
return (playerState.at(19) & 4) != 0;
}
int getCourse() {
return (playerState.at(19) & 0xff0000) >> 16;
}
int getWorld() {
return COURSE_TO_WORLD[getCourse()];
}
int getRoadId() {
return (playerState.at(20) & 0xff00) >> 8;
}
int getRoadDirection() {
return (playerState.at(20) & 0xffff000000) >> 24;
}
TURN_SIGNALS getTurnSignal() {
int signalCode = playerState.at(20) & 0x70;
if (signalCode == 0x10) {
return RIGHT;
} else if (signalCode == 0x20) {
return LEFT;
} else if (signalCode == 0x40) {
return STRAIGHT;
} else {
return STRAIGHT;
}
}
int getPowerUp() {
return playerState.at(20) & 0xf;
}
bool hasFeatherBoost() {
return getPowerUp() == 0;
}
bool hasDraftBoost() {
return getPowerUp() == 1;
}
bool hasAeroBoost() {
return getPowerUp() == 5;
}
int getCadence() {
//return static_cast<int>((playerState.cadenceuhz() * 60) / 1000000);
}
std::string operator()(const std::string& item) {
try {
//return playerState.GetReflection()->GetString(playerState, playerState.GetDescriptor()->FindFieldByName(item));
} catch (const std::exception& e) {
qDebug() << "Error: " << e.what();
return "";
}
}
private:
QMap<int, int> COURSE_TO_WORLD = {{3, 1}, {4, 2}, {5, 3}, {6, 1}};
enum COURSES {
WATOPIA = 3,
RICHMOND = 4,
LONDON = 5
};
//zwift_messages::PlayerState playerState;
QByteArray playerState;
};
#endif // PLAYERSTATEWRAPPER_H

View File

@@ -0,0 +1,116 @@
#ifndef ZWIFT_CLIENT_AUTH_H
#define ZWIFT_CLIENT_AUTH_H
#include <QCoreApplication>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDateTime>
class AuthToken : public QObject {
Q_OBJECT
public:
QString username;
QString password;
QString access_token;
qint64 expires_in;
QString id_token;
qint64 not_before_policy;
QString refresh_token;
qint64 refresh_expires_in;
QString session_state;
QString token_type;
qint64 access_token_expiration;
qint64 refresh_token_expiration;
AuthToken(const QString& username, const QString& password, QObject* parent = nullptr)
: QObject(parent), username(username), password(password) {}
bool haveValidAccessToken() const {
return !access_token.isEmpty() && QDateTime::currentMSecsSinceEpoch() < access_token_expiration;
}
bool haveValidRefreshToken() const {
return !refresh_token.isEmpty() && QDateTime::currentMSecsSinceEpoch() < refresh_token_expiration;
}
Q_INVOKABLE QString getAccessToken() {
if (haveValidAccessToken()) {
return access_token;
} else {
updateTokenData();
return access_token;
}
}
public slots:
void updateTokenData() {
if (haveValidRefreshToken()) {
QUrl url("https://secure.zwift.com/auth/realms/zwift/tokens/access/codes");
QUrlQuery query;
query.addQueryItem("refresh_token", refresh_token);
query.addQueryItem("grant_type", "refresh_token");
query.addQueryItem("client_id", "Zwift_Mobile_Link");
url.setQuery(query);
QNetworkRequest request(url);
QNetworkReply* reply = networkManager.get(request);
connect(reply, &QNetworkReply::finished, [=]() {
handleTokenResponse(reply);
});
} else {
QUrl url("https://secure.zwift.com/auth/realms/zwift/tokens/access/codes");
QUrlQuery postData;
postData.addQueryItem("username", username);
postData.addQueryItem("password", password);
postData.addQueryItem("grant_type", "password");
postData.addQueryItem("client_id", "Zwift_Mobile_Link");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* reply = networkManager.post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
connect(reply, &QNetworkReply::finished, [=]() {
handleTokenResponse(reply);
});
}
}
private slots:
void handleTokenResponse(QNetworkReply* reply) {
if (reply->error() == QNetworkReply::NoError) {
QByteArray responseData = reply->readAll();
QJsonDocument jsonDocument = QJsonDocument::fromJson(responseData);
QJsonObject tokenData = jsonDocument.object();
QDateTime now = QDateTime::currentDateTime();
access_token = tokenData["access_token"].toString();
expires_in = tokenData["expires_in"].toInt();
id_token = tokenData["id_token"].toString();
not_before_policy = tokenData["not-before-policy"].toInt();
refresh_token = tokenData["refresh_token"].toString();
refresh_expires_in = tokenData["refresh_expires_in"].toInt();
session_state = tokenData["session_state"].toString();
token_type = tokenData["token_type"].toString();
access_token_expiration = now.toMSecsSinceEpoch() + (expires_in - 5) * 1000;
refresh_token_expiration = now.toMSecsSinceEpoch() + (refresh_expires_in - 5) * 1000;
qDebug() << "Access Token: " << access_token;
} else {
qDebug() << "Error fetching token: " << reply->errorString();
}
reply->deleteLater();
}
private:
QNetworkAccessManager networkManager;
};
#endif // ZWIFT_CLIENT_AUTH_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
syntax="proto3";
message PlayerState {
int32 id = 1;
int64 worldTime = 2;
int32 distance = 3;
int32 roadTime = 4;
int32 laps = 5;
int32 speed = 6;
int32 roadPosition = 8;
int32 cadenceUHz = 9;
int32 heartrate = 11;
int32 power = 12;
int64 heading = 13;
int32 lean = 14;
int32 climbing = 15;
int32 time = 16;
int32 f19 = 19;
int32 f20 = 20;
int32 progress = 21;
int64 customisationId = 22;
int32 justWatching = 23;
int32 calories = 24;
float x = 25;
float altitude = 26;
float y = 27;
int32 watchingRiderId = 28;
int32 groupId = 29;
int64 sport = 31;
}
message ClientToServer {
int32 connected = 1;
int32 rider_id = 2;
int64 world_time = 3;
PlayerState state = 7;
int32 seqno = 4;
int64 tag8 = 8;
int64 tag9 = 9;
int64 last_update = 10;
int64 tag11 = 11;
int64 last_player_update = 12;
}
message SegmentResult {
int64 id = 1;
int64 rider_id = 2;
int64 event_subgroup_id = 6;
string first_name = 7;
string last_name = 8;
string finish_time_str = 10;
int64 elapsed_ms = 11;
int32 powermeter = 12;
int32 weight = 13;
int32 power = 15;
int32 heartrate = 19;
}
message SegmentResults {
int64 world_id = 1;
int64 segment_id = 2;
int64 event_subgroup_id = 3;
repeated SegmentResult segment_results = 4;
}
message UnknownMessage1 {
// string firstName=7;
// string lastName=8;
// string timestamp=17;
}
message UnknownMessage {
// int64 tag1=1;
// UnknownMessage1 tag4=4;
}
message ServerToClient {
int32 tag1 = 1;
int32 rider_id = 2;
int64 world_time = 3;
int32 seqno = 4;
repeated PlayerState player_states = 8;
repeated UnknownMessage player_updates = 9;
int64 tag11 = 11;
int64 tag17 = 17;
int32 num_msgs = 18;
int32 msgnum = 19;
}
message WorldAttributes {
int32 world_id = 1;
string name = 2;
int64 tag3 = 3;
int64 tag5 = 4;
int64 world_time = 6;
int64 clock_time = 7;
}
message WorldAttribute {
int64 world_time = 2;
}
message EventSubgroupProtobuf {
int32 id = 1;
string name = 2;
int32 rules = 8;
int32 route = 22;
int32 laps = 25;
int32 startLocation = 29;
int32 label = 30;
int32 paceType = 31;
int32 jerseyHash = 36;
}
message RiderAttributes {
int32 f2 = 2;
int32 f3 = 3;
message AttributeMessage {
int32 myId = 1;
int32 theirId = 2;
string firstName = 3;
string lastName = 4;
int32 countryCode = 5;
}
AttributeMessage attributeMessage = 4;
int32 theirId = 10;
int32 f13 = 13;
}
message Profiles {
repeated Profile profiles = 1;
}
message Profile {
int32 id = 1;
string firstName = 4;
string lastName = 5;
int32 male = 6;
int32 weight = 9;
int32 bodyType = 12;
int32 countryCode = 34;
int32 totalDistance = 35;
int32 totalDistanceClimbed = 36;
int32 totalTimeInMinutes = 37;
int32 totalWattHours = 41;
int32 height = 42;
int32 totalExperiencePoints = 46;
int32 achievementLevel = 49;
int32 powerSource = 52;
int32 age = 55;
string launchedGameClient = 108;
int32 currentActivityId = 109;
}

View File

@@ -0,0 +1,47 @@
//
// zwift_protobuf_layer.swift
// qdomyoszwift
//
// Created by Roberto Viola on 30/12/23.
//
import Foundation
import SwiftProtobuf
@objc public class zwift_protobuf_layer : NSObject {
var player: PlayerState!
let SwiftDebug = swiftDebug()
@objc public func getPlayerState(value: Data) {
do {
//let hexString = value.map { String(format: "%02x", $0) }.joined()
//SwiftDebug.qtDebug("HEX \(hexString)")
let decodedInfo = try PlayerState(serializedData: value)
player = decodedInfo
} catch {
SwiftDebug.qtDebug("An error occurred: \(error)")
}
}
@objc public func getAltitude() -> Float {
return player.altitude
}
@objc public func getDistance() -> Int32 {
return player.distance
}
@objc public func getLatitude() -> Float {
return player.x
}
@objc public func getLongitude() -> Float {
return player.y
}
/*
@objc public init() {
super.init()
}*/
}