mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
7 Commits
master
...
build-1118
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d0c5a5cb8 | ||
|
|
f4d161867b | ||
|
|
a3654095f7 | ||
|
|
f2bfb0e7b6 | ||
|
|
dbb4b57ee9 | ||
|
|
05e64940c3 | ||
|
|
c4c380bc4a |
@@ -371,4 +371,9 @@ The ProForm 995i implementation serves as the reference example:
|
||||
## Additional Memories
|
||||
|
||||
- When adding a new setting in QML (setting-tiles.qml), you must:
|
||||
* Add the property at the END of the properties list
|
||||
* Add the property at the END of the properties list
|
||||
|
||||
## Code Style Rules
|
||||
|
||||
- All comments MUST be written in English only
|
||||
- Avoid code duplication - unify repeated logic into shared functions or sections when possible
|
||||
@@ -4381,7 +4381,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1117;
|
||||
CURRENT_PROJECT_VERSION = 1118;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
|
||||
@@ -4575,7 +4575,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1117;
|
||||
CURRENT_PROJECT_VERSION = 1118;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4805,7 +4805,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1117;
|
||||
CURRENT_PROJECT_VERSION = 1118;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -4901,7 +4901,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1117;
|
||||
CURRENT_PROJECT_VERSION = 1118;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -4993,7 +4993,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1117;
|
||||
CURRENT_PROJECT_VERSION = 1118;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -5109,7 +5109,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1117;
|
||||
CURRENT_PROJECT_VERSION = 1118;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
|
||||
@@ -260,6 +260,7 @@ void bluetoothdevice::update_hr_from_external() {
|
||||
h.setPower(m_watt.value());
|
||||
h.setCadence(Cadence.value());
|
||||
h.setSteps(StepCount.value());
|
||||
h.setInclination(Inclination.value());
|
||||
Heart = appleWatchHeartRate;
|
||||
qDebug() << "Current Heart from Apple Watch: " << QString::number(appleWatchHeartRate);
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "keepawakehelper.h"
|
||||
#include <QLowEnergyConnectionParameters>
|
||||
#endif
|
||||
#ifdef Q_OS_IOS
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -85,6 +88,15 @@ void faketreadmill::update() {
|
||||
this->setVirtualDevice(virtualBike, VIRTUAL_DEVICE_MODE::ALTERNATIVE);
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
// Initialize lockscreen for iOS TCP data access
|
||||
if (!h) {
|
||||
h = new lockscreen();
|
||||
h->virtualtreadmill_zwift_ios(false);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
if (!firstStateChanged)
|
||||
emit connectedAndDiscovered();
|
||||
firstStateChanged = 1;
|
||||
@@ -112,6 +124,19 @@ void faketreadmill::update() {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
|
||||
// Update from TCP data if available
|
||||
if (h && firstStateChanged) {
|
||||
double tcpSpeed = h->getTcpSpeed();
|
||||
double tcpInclination = h->getTcpInclination();
|
||||
|
||||
if (tcpSpeed > -100) {
|
||||
Speed = tcpSpeed;
|
||||
}
|
||||
if (tcpInclination > -100) {
|
||||
Inclination = tcpInclination;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QTime>
|
||||
#include <QUrlQuery>
|
||||
#include <QUuid>
|
||||
#include <chrono>
|
||||
|
||||
homeform *homeform::m_singleton = 0;
|
||||
@@ -682,6 +683,7 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
|
||||
}
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
deviceUUID = QUuid::createUuid().toString();
|
||||
iphone_browser = new QMdnsEngine::Browser(&iphone_server, "_qz_iphone._tcp.local.", &iphone_cache);
|
||||
|
||||
QObject::connect(iphone_browser, &QMdnsEngine::Browser::serviceAdded, [](const QMdnsEngine::Service &service) {
|
||||
@@ -705,16 +707,7 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
|
||||
[]() { qDebug() << "iphone_socket connected!"; });
|
||||
QObject::connect(homeform::singleton()->iphone_socket, &QTcpSocket::readyRead, []() {
|
||||
QString rec = homeform::singleton()->iphone_socket->readAll();
|
||||
qDebug() << "iphone_socket received << " << rec;
|
||||
QStringList fields = rec.split("#");
|
||||
foreach (QString f, fields) {
|
||||
if (f.contains("HR")) {
|
||||
QStringList values = f.split("=");
|
||||
if (values.length() > 1) {
|
||||
emit homeform::singleton()->heartRate(values[1].toDouble());
|
||||
}
|
||||
}
|
||||
}
|
||||
homeform::singleton()->processTcpMessage(rec);
|
||||
});
|
||||
|
||||
homeform::singleton()->iphone_address = address;
|
||||
@@ -745,16 +738,7 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
|
||||
[]() { qDebug() << "iphone_socket connected!"; });
|
||||
QObject::connect(homeform::singleton()->iphone_socket, &QTcpSocket::readyRead, []() {
|
||||
QString rec = homeform::singleton()->iphone_socket->readAll();
|
||||
qDebug() << "iphone_socket received << " << rec;
|
||||
QStringList fields = rec.split("#");
|
||||
foreach (QString f, fields) {
|
||||
if (f.contains("HR")) {
|
||||
QStringList values = f.split("=");
|
||||
if (values.length() > 1) {
|
||||
emit homeform::singleton()->heartRate(values[1].toDouble());
|
||||
}
|
||||
}
|
||||
}
|
||||
homeform::singleton()->processTcpMessage(rec);
|
||||
});
|
||||
homeform::singleton()->iphone_address = address;
|
||||
homeform::singleton()->iphone_socket->connectToHost(
|
||||
@@ -6967,13 +6951,24 @@ void homeform::update() {
|
||||
#ifndef Q_OS_IOS
|
||||
if (iphone_socket && iphone_socket->state() == QAbstractSocket::ConnectedState) {
|
||||
QString toSend =
|
||||
"SENDER=PAD#HR=" + QString::number(bluetoothManager->device()->currentHeart().value()) +
|
||||
"SENDER=PAD#UUID=" + deviceUUID +
|
||||
"#HR=" + QString::number(bluetoothManager->device()->currentHeart().value()) +
|
||||
"#KCAL=" + QString::number(bluetoothManager->device()->calories().value()) +
|
||||
"#BCAD=" + QString::number(bluetoothManager->device()->currentCadence().value()) +
|
||||
"#SPD=" + QString::number(bluetoothManager->device()->currentSpeed().value()) +
|
||||
"#PWR=" + QString::number(bluetoothManager->device()->wattsMetric().value()) +
|
||||
"#CAD=" + QString::number(bluetoothManager->device()->currentCadence().value()) +
|
||||
"#ODO=" + QString::number(bluetoothManager->device()->odometer()) + "#";
|
||||
"#ODO=" + QString::number(bluetoothManager->device()->odometer());
|
||||
|
||||
// Aggiungi inclinazione se il device connesso è un treadmill
|
||||
if (bluetoothManager->device()->deviceType() == bluetoothdevice::TREADMILL) {
|
||||
treadmill* t = qobject_cast<treadmill*>(bluetoothManager->device());
|
||||
if (t) {
|
||||
toSend += "#INCL=" + QString::number(t->currentInclination().value());
|
||||
}
|
||||
}
|
||||
|
||||
toSend += "#";
|
||||
int write = iphone_socket->write(toSend.toLocal8Bit(), toSend.length());
|
||||
qDebug() << "iphone_socket send " << write << toSend;
|
||||
}
|
||||
@@ -6998,6 +6993,59 @@ bool homeform::getDevice() {
|
||||
return this->bluetoothManager->device()->connected();
|
||||
}
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
void homeform::processTcpMessage(const QString& message) {
|
||||
qDebug() << "iphone_socket received << " << message;
|
||||
QStringList fields = message.split("#");
|
||||
bool hasTreadmillData = false;
|
||||
double speed = 0.0;
|
||||
double incline = 0.0;
|
||||
QString remoteUUID;
|
||||
|
||||
foreach (QString f, fields) {
|
||||
if (f.contains("HR")) {
|
||||
QStringList values = f.split("=");
|
||||
if (values.length() > 1) {
|
||||
emit heartRate(values[1].toDouble());
|
||||
}
|
||||
} else if (f.contains("UUID=")) {
|
||||
QStringList values = f.split("=");
|
||||
if (values.length() > 1) {
|
||||
remoteUUID = values[1];
|
||||
}
|
||||
} else if (f.contains("SPD=")) {
|
||||
QStringList values = f.split("=");
|
||||
if (values.length() > 1) {
|
||||
speed = values[1].toDouble();
|
||||
}
|
||||
} else if (f.contains("INCL=")) {
|
||||
QStringList values = f.split("=");
|
||||
if (values.length() > 1) {
|
||||
incline = values[1].toDouble();
|
||||
hasTreadmillData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process treadmill data only if UUID is different (avoid echo)
|
||||
if (hasTreadmillData && !remoteUUID.isEmpty() && remoteUUID != deviceUUID && bluetoothManager) {
|
||||
bluetooth* bt = bluetoothManager;
|
||||
if (bt->device()) {
|
||||
faketreadmill* ft = qobject_cast<faketreadmill*>(bt->device());
|
||||
if (ft) {
|
||||
// Update speed and incline of fake treadmill
|
||||
if (speed > -100) {
|
||||
ft->changeSpeed(speed);
|
||||
}
|
||||
if (incline > -100) {
|
||||
ft->changeInclination(incline, incline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool homeform::getLap() {
|
||||
if (!this->bluetoothManager->device()) {
|
||||
|
||||
|
||||
@@ -823,6 +823,8 @@ class homeform : public QObject {
|
||||
QTcpSocket *iphone_socket = nullptr;
|
||||
QMdnsEngine::Service iphone_service;
|
||||
QHostAddress iphone_address;
|
||||
void processTcpMessage(const QString& message);
|
||||
QString deviceUUID;
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -55,6 +55,16 @@ var pedometer = CMPedometer()
|
||||
{
|
||||
return WatchKitConnection.stepCadence;
|
||||
}
|
||||
|
||||
@objc public func getTcpSpeed() -> Double
|
||||
{
|
||||
return WatchKitConnection.tcpSpeed;
|
||||
}
|
||||
|
||||
@objc public func getTcpInclination() -> Double
|
||||
{
|
||||
return WatchKitConnection.tcpInclination;
|
||||
}
|
||||
|
||||
@objc public func setSteps(steps: Int) -> Void
|
||||
{
|
||||
@@ -128,8 +138,20 @@ var pedometer = CMPedometer()
|
||||
Server.server?.send(createString(sender: sender))
|
||||
}
|
||||
|
||||
@objc public func setInclination(inclination: Double) -> Void
|
||||
{
|
||||
var sender: String
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
sender = "PAD"
|
||||
} else {
|
||||
sender = "PHONE"
|
||||
}
|
||||
WatchKitConnection.inclination = inclination;
|
||||
Server.server?.send(createString(sender: sender))
|
||||
}
|
||||
|
||||
func createString(sender: String) -> String {
|
||||
return "SENDER=\(sender)#HR=\(WatchKitConnection.currentHeartRate)#KCAL=\(WatchKitConnection.kcal)#BCAD=\(WatchKitConnection.cadence)#SPD=\(WatchKitConnection.speed)#PWR=\(WatchKitConnection.power)#CAD=\(WatchKitConnection.stepCadence)#ODO=\(WatchKitConnection.distance)#";
|
||||
return "SENDER=\(sender)#UUID=\(WatchKitConnection.deviceUUID)#HR=\(WatchKitConnection.currentHeartRate)#KCAL=\(WatchKitConnection.kcal)#BCAD=\(WatchKitConnection.cadence)#SPD=\(WatchKitConnection.speed)#PWR=\(WatchKitConnection.power)#CAD=\(WatchKitConnection.stepCadence)#ODO=\(WatchKitConnection.distance)#INCL=\(WatchKitConnection.inclination)#";
|
||||
}
|
||||
|
||||
@objc func updateHeartRate() {
|
||||
|
||||
@@ -112,6 +112,27 @@ class Connection {
|
||||
if sender?.contains("PAD") ?? false && message.contains("PWR=") {
|
||||
let pwr : String = message.slice(from: "PWR=", to: "#") ?? ""
|
||||
WatchKitConnection.power = (Double(pwr) ?? WatchKitConnection.power)
|
||||
}
|
||||
if sender?.contains("PAD") ?? false && message.contains("INCL=") {
|
||||
let incl : String = message.slice(from: "INCL=", to: "#") ?? ""
|
||||
WatchKitConnection.inclination = (Double(incl) ?? WatchKitConnection.inclination)
|
||||
}
|
||||
|
||||
// Handle treadmill data from any sender except self (avoid echo)
|
||||
if message.contains("UUID=") {
|
||||
let remoteUUID = message.slice(from: "UUID=", to: "#") ?? ""
|
||||
if remoteUUID != WatchKitConnection.deviceUUID {
|
||||
// Process speed data from remote device
|
||||
if message.contains("SPD=") {
|
||||
let spd : String = message.slice(from: "SPD=", to: "#") ?? ""
|
||||
WatchKitConnection.tcpSpeed = (Double(spd) ?? WatchKitConnection.tcpSpeed)
|
||||
}
|
||||
// Process inclination data from remote device
|
||||
if message.contains("INCL=") {
|
||||
let incl : String = message.slice(from: "INCL=", to: "#") ?? ""
|
||||
WatchKitConnection.tcpInclination = (Double(incl) ?? WatchKitConnection.tcpInclination)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,14 @@ class WatchKitConnection: NSObject {
|
||||
static var distance = 0.0
|
||||
static var stepCadence = 0
|
||||
static var kcal = 0.0
|
||||
static var speed = 0.0
|
||||
static var speed = -100.0
|
||||
static var power = 0.0
|
||||
static var cadence = 0.0
|
||||
static var steps = 0
|
||||
static var inclination = -100.0
|
||||
static var deviceUUID = UUID().uuidString
|
||||
static var tcpSpeed = -100.0
|
||||
static var tcpInclination = -100.0
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
|
||||
@@ -15,6 +15,7 @@ class lockscreen {
|
||||
void setSpeed(double speed);
|
||||
void setPower(double power);
|
||||
void setCadence(double cadence);
|
||||
void setInclination(double inclination);
|
||||
|
||||
// virtualbike
|
||||
void virtualbike_ios();
|
||||
@@ -97,6 +98,10 @@ class lockscreen {
|
||||
|
||||
// Zwift Hub Protobuf
|
||||
static QByteArray zwift_hub_inclinationCommand(double inclination);
|
||||
|
||||
// TCP Data Access
|
||||
double getTcpSpeed();
|
||||
double getTcpInclination();
|
||||
static QByteArray zwift_hub_setGearsCommand(unsigned int gears);
|
||||
static uint32_t zwift_hub_getPowerFromBuffer(const QByteArray& buffer);
|
||||
static uint32_t zwift_hub_getCadenceFromBuffer(const QByteArray& buffer);
|
||||
|
||||
@@ -89,6 +89,10 @@ void lockscreen::setSpeed(double speed)
|
||||
{
|
||||
[h setSpeedWithSpeed:speed];
|
||||
}
|
||||
void lockscreen::setInclination(double inclination)
|
||||
{
|
||||
[h setInclinationWithInclination:inclination];
|
||||
}
|
||||
|
||||
|
||||
void lockscreen::virtualbike_ios()
|
||||
@@ -385,6 +389,14 @@ QByteArray lockscreen::zwift_hub_inclinationCommand(double inclination) {
|
||||
}
|
||||
}
|
||||
|
||||
double lockscreen::getTcpSpeed() {
|
||||
return [h getTcpSpeed];
|
||||
}
|
||||
|
||||
double lockscreen::getTcpInclination() {
|
||||
return [h getTcpInclination];
|
||||
}
|
||||
|
||||
QByteArray lockscreen::zwift_hub_setGearsCommand(unsigned int gears) {
|
||||
NSError *error = nil;
|
||||
NSData *command = [ZwiftHubBike setGearCommandWithGears:gears error:&error];
|
||||
|
||||
Reference in New Issue
Block a user