mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
55 Commits
424ed85179
...
windows-11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8aa63d0d8 | ||
|
|
b573442800 | ||
|
|
721f6d460e | ||
|
|
e6efe0f074 | ||
|
|
df3e64e57d | ||
|
|
e8faa3e671 | ||
|
|
cc4a5be817 | ||
|
|
99394713f8 | ||
|
|
c76195a271 | ||
|
|
79ba8ea874 | ||
|
|
f602563789 | ||
|
|
12f77be4a4 | ||
|
|
c5cfb1c936 | ||
|
|
a2e721812f | ||
|
|
f737e1be83 | ||
|
|
eafe75e65f | ||
|
|
7c52e9263f | ||
|
|
9bb016f4b9 | ||
|
|
797ae5f102 | ||
|
|
190011a365 | ||
|
|
159f96dd8d | ||
|
|
839213e917 | ||
|
|
e349ca9bad | ||
|
|
9ca3fca817 | ||
|
|
095d8bc1a3 | ||
|
|
07606facba | ||
|
|
bd0e7033c2 | ||
|
|
0337c08252 | ||
|
|
8b60b56dc2 | ||
|
|
f3d8b3964c | ||
|
|
06253998ee | ||
|
|
dbb9ba3510 | ||
|
|
ad802f7d2d | ||
|
|
9cc02dede9 | ||
|
|
82a6afe6b6 | ||
|
|
a7b8e63235 | ||
|
|
d7c3a84adb | ||
|
|
64b9ec9d72 | ||
|
|
379cf8d7de | ||
|
|
2f2989f90d | ||
|
|
6fac9770be | ||
|
|
8d07d7c3f7 | ||
|
|
f85f743499 | ||
|
|
74c37f5624 | ||
|
|
527396eafc | ||
|
|
0f149448b3 | ||
|
|
ed1599ca8e | ||
|
|
89808ae65b | ||
|
|
2da194f073 | ||
|
|
d712621b7b | ||
|
|
1c06260036 | ||
|
|
2d1364497e | ||
|
|
cc7757bfcd | ||
|
|
49c7a96c81 | ||
|
|
05d598ffcf |
74
src/android/src/ZwiftHub.java
Normal file
74
src/android/src/ZwiftHub.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 ZwiftHub {
|
||||
|
||||
private static Context context;
|
||||
|
||||
private static final String TAG = "ZwiftHub: ";
|
||||
|
||||
public static byte[] inclinationCommand(double inclination) throws InvalidProtocolBufferException {
|
||||
SimulationParam.Builder simulation = SimulationParam.newBuilder();
|
||||
simulation.setInclineX100((int)(inclination * 100.0));
|
||||
|
||||
HubCommand.Builder command = 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 {
|
||||
PhysicalParam.Builder physical = PhysicalParam.newBuilder();
|
||||
physical.setGearRatioX10000(gears);
|
||||
|
||||
HubCommand.Builder command = 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;
|
||||
}
|
||||
}
|
||||
@@ -2311,6 +2311,82 @@ void horizontreadmill::characteristicRead(const QLowEnergyCharacteristic &charac
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
bool horizontreadmill::discoverServicesWin11(const QBluetoothAddress& address) {
|
||||
BLUETOOTH_DEVICE_INFO deviceInfo = { sizeof(BLUETOOTH_DEVICE_INFO) };
|
||||
deviceInfo.Address.ullLong = address.toUInt64();
|
||||
|
||||
DWORD error = BluetoothGetDeviceInfo(nullptr, &deviceInfo);
|
||||
if (error != ERROR_SUCCESS) {
|
||||
qDebug() << "Failed to get device info. Error code:" << error;
|
||||
DWORD lastError = GetLastError();
|
||||
qDebug() << "Last error:" << lastError;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
DWORD serviceCount = 0;
|
||||
GUID* serviceGuids = nullptr;
|
||||
|
||||
// Prima chiamata per ottenere il numero di servizi
|
||||
DWORD result = BluetoothEnumerateInstalledServices(
|
||||
nullptr,
|
||||
&deviceInfo,
|
||||
&serviceCount,
|
||||
nullptr);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
qDebug() << "Failed to enumerate services count";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (serviceCount == 0) {
|
||||
qDebug() << "No services found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocare memoria per i GUID dei servizi
|
||||
serviceGuids = new GUID[serviceCount];
|
||||
|
||||
// Seconda chiamata per ottenere i GUID effettivi
|
||||
result = BluetoothEnumerateInstalledServices(
|
||||
nullptr,
|
||||
&deviceInfo,
|
||||
&serviceCount,
|
||||
serviceGuids);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
delete[] serviceGuids;
|
||||
qDebug() << "Failed to enumerate services";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Processare ogni servizio trovato
|
||||
for (DWORD i = 0; i < serviceCount; i++) {
|
||||
handleWin11Service(serviceGuids[i]);
|
||||
}
|
||||
|
||||
delete[] serviceGuids;
|
||||
return true;
|
||||
}
|
||||
|
||||
void horizontreadmill::handleWin11Service(const GUID& serviceGuid) {
|
||||
wchar_t guidString[39];
|
||||
StringFromGUID2(serviceGuid, guidString, sizeof(guidString)/sizeof(wchar_t));
|
||||
|
||||
QString qtGuidString = QString::fromWCharArray(guidString);
|
||||
QBluetoothUuid qtUuid(qtGuidString);
|
||||
|
||||
QLowEnergyService* service = m_control->createServiceObject(qtUuid);
|
||||
if (service) {
|
||||
gattCommunicationChannelService.append(service);
|
||||
connect(service, &QLowEnergyService::stateChanged, this, &horizontreadmill::stateChanged);
|
||||
service->discoverDetails();
|
||||
qDebug() << "Discovered service:" << qtUuid;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void horizontreadmill::serviceScanDone(void) {
|
||||
emit debug(QStringLiteral("serviceScanDone"));
|
||||
|
||||
@@ -2318,12 +2394,18 @@ void horizontreadmill::serviceScanDone(void) {
|
||||
firstStateChanged = 0;
|
||||
auto services_list = m_control->services();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (discoverServicesWin11(m_control->remoteAddress())) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (const QBluetoothUuid &s : qAsConst(services_list)) {
|
||||
qDebug() << s << "discovering...";
|
||||
gattCommunicationChannelService.append(m_control->createServiceObject(s));
|
||||
connect(gattCommunicationChannelService.constLast(), &QLowEnergyService::stateChanged, this,
|
||||
&horizontreadmill::stateChanged);
|
||||
gattCommunicationChannelService.constLast()->discoverDetails();
|
||||
qDebug() << s << "discovering...";
|
||||
gattCommunicationChannelService.append(m_control->createServiceObject(s));
|
||||
connect(gattCommunicationChannelService.constLast(), &QLowEnergyService::stateChanged, this,
|
||||
&horizontreadmill::stateChanged);
|
||||
gattCommunicationChannelService.constLast()->discoverDetails();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <bthsdpdef.h>
|
||||
#include <bluetoothapis.h>
|
||||
#include <VersionHelpers.h>
|
||||
#endif
|
||||
|
||||
class horizontreadmill : public treadmill {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -48,6 +55,11 @@ class horizontreadmill : public treadmill {
|
||||
bool autoStartWhenSpeedIsGreaterThenZero() override;
|
||||
|
||||
private:
|
||||
#ifdef Q_OS_WIN
|
||||
bool discoverServicesWin11(const QBluetoothAddress& address);
|
||||
void handleWin11Service(const GUID& serviceGuid);
|
||||
#endif
|
||||
|
||||
void writeCharacteristic(QLowEnergyService *service, QLowEnergyCharacteristic characteristic, uint8_t *data,
|
||||
uint8_t data_len, QString info, bool disable_log = false, bool wait_for_response = false);
|
||||
void waitForAPacket();
|
||||
|
||||
@@ -990,14 +990,15 @@ void homeform::backup() {
|
||||
|
||||
static uint8_t index = 0;
|
||||
qDebug() << QStringLiteral("saving fit file backup...");
|
||||
|
||||
QString path = getWritableAppDir();
|
||||
bluetoothdevice *dev = bluetoothManager->device();
|
||||
if (dev) {
|
||||
|
||||
QString filename = path + QString::number(index) + backupFitFileName;
|
||||
QFile::remove(filename);
|
||||
qfit::save(filename, Session, dev->deviceType(),
|
||||
qDebug() << QStringLiteral("writable dir") << path;
|
||||
bluetoothdevice *dev = bluetoothManager->device();
|
||||
if (dev) {
|
||||
QString filename = path + QString::number(index) + backupFitFileName;
|
||||
qDebug() << QStringLiteral("filename") << filename;
|
||||
QFile::remove(filename);
|
||||
qDebug() << QStringLiteral("filename removed") << filename;
|
||||
qfit::save(filename, Session, dev->deviceType(),
|
||||
qobject_cast<m3ibike *>(dev) ? QFIT_PROCESS_DISTANCENOISE : QFIT_PROCESS_NONE,
|
||||
stravaPelotonWorkoutType, dev->bluetoothDevice.name());
|
||||
|
||||
|
||||
44
src/main.cpp
44
src/main.cpp
@@ -331,6 +331,50 @@ int main(int argc, char *argv[]) {
|
||||
app->setOrganizationDomain(QStringLiteral("robertoviola.cloud"));
|
||||
app->setApplicationName(QStringLiteral("qDomyos-Zwift"));
|
||||
|
||||
/* TEST ZWIFT HUB */
|
||||
#ifdef Q_OS_ANDROID
|
||||
{
|
||||
QAndroidJniObject rrr = QAndroidJniObject::callStaticObjectMethod(
|
||||
"org/cagnulen/qdomyoszwift/ZwiftHub",
|
||||
"inclinationCommand",
|
||||
"(D)[B",
|
||||
8.0);
|
||||
|
||||
if(!rrr.isValid()) {
|
||||
qDebug() << "inclinationCommand returned invalid value";
|
||||
}
|
||||
|
||||
jbyteArray array = rrr.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);
|
||||
qDebug() << "inclination command" << message.toHex(' ');
|
||||
|
||||
QAndroidJniObject rr = QAndroidJniObject::callStaticObjectMethod(
|
||||
"org/cagnulen/qdomyoszwift/ZwiftHub",
|
||||
"setGearCommand",
|
||||
"(I)[B",
|
||||
32608);
|
||||
|
||||
if (!rr.isValid()) {
|
||||
qDebug() << "setGearCommand returned invalid value";
|
||||
}
|
||||
|
||||
array = rr.object<jbyteArray>();
|
||||
bytes = env->GetByteArrayElements(array, nullptr);
|
||||
length = env->GetArrayLength(array);
|
||||
|
||||
QByteArray proto((char*)bytes, length);
|
||||
|
||||
env->ReleaseByteArrayElements(array, bytes, JNI_ABORT);
|
||||
qDebug() << "gear command" << proto.toHex(' ');
|
||||
}
|
||||
#endif
|
||||
|
||||
QSettings settings;
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
|
||||
@@ -31,7 +31,14 @@ CONFIG += qmltypes
|
||||
#unix:!android: CONFIG += webengine
|
||||
|
||||
win32:DEFINES += _ITERATOR_DEBUG_LEVEL=0
|
||||
win32:!mingw:LIBS += -llibprotobuf -llibprotoc -labseil_dll -llibprotobuf-lite -L$$PWD
|
||||
win32:!mingw:LIBS += -llibprotobuf -llibprotoc -labseil_dll -llibprotobuf-lite -lBthprops -lVersion -lRpcrt4 -lole32 -L$$PWD
|
||||
|
||||
win32 {
|
||||
LIBS += -lBthprops
|
||||
LIBS += -lVersion
|
||||
LIBS += -lRpcrt4 # Per StringFromGUID2
|
||||
LIBS += -lole32 # Altra possibile dipendenza per GUID
|
||||
}
|
||||
|
||||
QML_IMPORT_NAME = org.cagnulein.qdomyoszwift
|
||||
QML_IMPORT_MAJOR_VERSION = 1
|
||||
|
||||
Reference in New Issue
Block a user