Compare commits

...

21 Commits

Author SHA1 Message Date
Roberto Viola
82a6afe6b6 kickr core to wahookickr class 2024-10-29 12:28:13 +01:00
Roberto Viola
a7b8e63235 fixing UI and settings 2024-10-29 08:50:33 +01:00
Roberto Viola
d7c3a84adb adding max and minGears 2024-10-29 08:21:59 +01:00
Roberto Viola
64b9ec9d72 fixing gear conversion 2024-10-29 08:17:38 +01:00
Roberto Viola
379cf8d7de Update project.pbxproj 2024-10-29 08:02:08 +01:00
Roberto Viola
2f2989f90d completed? 2024-10-28 15:40:00 +01:00
Roberto Viola
6fac9770be qml finally saves the settings correctly 2024-10-28 15:09:13 +01:00
Roberto Viola
8d07d7c3f7 Merge branch 'master' into Custom-gearing-ranges/ratios-(Discussion-#2671) 2024-10-28 13:44:31 +01:00
Roberto Viola
f85f743499 i need to save the first 3 static objects and use it in the wahoo module 2024-10-28 11:11:05 +01:00
Roberto Viola
74c37f5624 Update gears.qml 2024-10-28 09:46:58 +01:00
Roberto Viola
527396eafc Revert "Update gears.qml"
This reverts commit 0f149448b3.
2024-10-28 09:18:54 +01:00
Roberto Viola
0f149448b3 Update gears.qml 2024-10-25 16:28:33 +02:00
Roberto Viola
ed1599ca8e need to center the values in the table 2024-10-24 15:08:25 +02:00
Roberto Viola
89808ae65b fixing casting to double 2024-10-24 11:15:51 +02:00
Roberto Viola
2da194f073 Merge branch 'master' into Custom-gearing-ranges/ratios-(Discussion-#2671) 2024-10-24 11:00:46 +02:00
Roberto Viola
d712621b7b fixing formula 2024-10-23 11:47:05 +02:00
Roberto Viola
1c06260036 Merge branch 'master' into Custom-gearing-ranges/ratios-(Discussion-#2671) 2024-10-23 11:44:26 +02:00
Roberto Viola
2d1364497e Update virtualbike_zwift.swift 2024-10-22 15:40:04 +02:00
Roberto Viola
cc7757bfcd Update project.pbxproj 2024-10-22 15:38:44 +02:00
Roberto Viola
49c7a96c81 Merge branch 'master' into Custom-gearing-ranges/ratios-(Discussion-#2671) 2024-10-22 15:32:50 +02:00
Roberto Viola
05d598ffcf first raw version 2024-10-21 14:06:48 +02:00
12 changed files with 998 additions and 15 deletions

View File

@@ -4068,7 +4068,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 919;
CURRENT_PROJECT_VERSION = 922;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
@@ -4259,7 +4259,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 919;
CURRENT_PROJECT_VERSION = 922;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = NO;
@@ -4486,7 +4486,7 @@
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 919;
CURRENT_PROJECT_VERSION = 922;
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -4582,7 +4582,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 919;
CURRENT_PROJECT_VERSION = 922;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 6335M7T29D;
ENABLE_BITCODE = YES;
@@ -4674,7 +4674,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 919;
CURRENT_PROJECT_VERSION = 922;
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
ENABLE_BITCODE = YES;
ENABLE_PREVIEWS = YES;
@@ -4788,7 +4788,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 919;
CURRENT_PROJECT_VERSION = 922;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
ENABLE_BITCODE = YES;

View File

@@ -106,6 +106,14 @@ void bike::setGears(double gears) {
qDebug() << "new gear value ignored because of gears_zwift_ratio setting!";
return;
}
if(gears > maxGears()) {
qDebug() << "new gear value ignored because of maxGears" << maxGears();
return;
}
if(gears < minGears()) {
qDebug() << "new gear value ignored because of minGears" << minGears();
return;
}
m_gears = gears;
if(homeform::singleton()) {
homeform::singleton()->updateGearsValue();

View File

@@ -23,6 +23,8 @@ class bike : public bluetoothdevice {
double currentCrankRevolutions() override;
uint16_t lastCrankEventTime() override;
bool connected() override;
virtual double maxGears() { return 9999.0; }
virtual double minGears() { return -9999.0; }
virtual uint16_t watts();
virtual resistance_t pelotonToBikeResistance(int pelotonResistance);
virtual resistance_t resistanceFromPowerRequest(uint16_t power);

View File

@@ -1511,7 +1511,6 @@ 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-")) ||
@@ -1562,6 +1561,7 @@ 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("HAMMER ") && saris_trainer) ||
(b.name().toUpper().startsWith("WAHOO KICKR"))) &&
!wahooKickrSnapBike && !ftmsBike && filter) {

View File

@@ -31,6 +31,8 @@ wahookickrsnapbike::wahookickrsnapbike(bool noWriteResistance, bool noHeartServi
initDone = false;
connect(refresh, &QTimer::timeout, this, &wahookickrsnapbike::update);
refresh->start(200ms);
GearTable g;
g.printTable();
}
bool wahookickrsnapbike::writeCharacteristic(uint8_t *data, uint8_t data_len, QString info, bool disable_log,
@@ -192,10 +194,16 @@ void wahookickrsnapbike::update() {
}
QThread::msleep(700);
QByteArray d = setWheelCircumference(gearsToWheelDiameter(gears()));
uint8_t e[20];
setGears(1);
memcpy(e, d.constData(), d.length());
writeCharacteristic(e, d.length(), "setWheelCircumference", false, true);
// required to the SS2K only one time
Resistance = 0;
emit resistanceRead(Resistance.value());
initRequest = false;
initRequest = false;
} else if (bluetoothDevice.isValid() &&
m_control->state() == QLowEnergyController::DiscoveredState //&&
// gattCommunicationChannelService &&
@@ -259,7 +267,10 @@ void wahookickrsnapbike::update() {
memcpy(b, a.constData(), a.length());
writeCharacteristic(b, a.length(), "setResistance", false, true);
} else if (virtualBike && virtualBike->ftmsDeviceConnected() && lastGearValue != gears()) {
inclinationChanged(lastGrade, lastGrade);
QByteArray a = setWheelCircumference(gearsToWheelDiameter(gears()));
uint8_t b[20];
memcpy(b, a.constData(), a.length());
writeCharacteristic(b, a.length(), "setWheelCircumference", false, true);
}
lastGearValue = gears();
requestResistance = -1;
@@ -280,6 +291,17 @@ void wahookickrsnapbike::update() {
}
}
double wahookickrsnapbike::gearsToWheelDiameter(double gear) {
QSettings settings;
GearTable table;
if(gear < 1) gear = 1;
else if(gear > table.maxGears) gear = table.maxGears;
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());
GearTable::GearInfo g = table.getGear((int)gear);
double current_ratio = ((double)g.crankset / (double)g.rearCog);
return (((double)settings.value(QZSettings::gear_circumference, QZSettings::default_gear_circumference).toDouble()) / original_ratio) * ((double)current_ratio);
}
void wahookickrsnapbike::serviceDiscovered(const QBluetoothUuid &gatt) {
emit debug(QStringLiteral("serviceDiscovered ") + gatt.toString());
}
@@ -823,7 +845,6 @@ void wahookickrsnapbike::inclinationChanged(double grade, double percentage) {
emit debug(QStringLiteral("writing inclination ") + QString::number(grade));
QSettings settings;
double g = grade;
g += gears();
QByteArray a = setSimGrade(g);
uint8_t b[20];
memcpy(b, a.constData(), a.length());
@@ -833,3 +854,12 @@ void wahookickrsnapbike::inclinationChanged(double grade, double percentage) {
bool wahookickrsnapbike::inclinationAvailableByHardware() {
return KICKR_BIKE;
}
double wahookickrsnapbike::maxGears() {
GearTable g;
return g.maxGears;
}
double wahookickrsnapbike::minGears() {
return 1;
}

View File

@@ -42,6 +42,8 @@ class wahookickrsnapbike : public bike {
bool connected() override;
resistance_t maxResistance() override { return 100; }
bool inclinationAvailableByHardware() override;
double maxGears() override;
double minGears() override;
enum OperationCode : uint8_t {
_unlock = 32,
@@ -56,7 +58,7 @@ class wahookickrsnapbike : public bike {
_setWheelCircumference = 72,
};
private:
private:
QByteArray unlockCommand();
QByteArray setResistanceMode(double resistance);
QByteArray setStandardMode(uint8_t level);
@@ -107,7 +109,88 @@ class wahookickrsnapbike : public bike {
volatile int notificationSubscribed = 0;
resistance_t lastForcedResistance = -1;
resistance_t lastForcedResistance = -1;
double gearsToWheelDiameter(double gear);
class GearTable {
public:
int maxGears = 12;
struct GearInfo {
int gear;
int crankset;
int rearCog;
};
void loadGearSettings() {
QSettings settings;
QString gearConfig = settings.value("gear_configuration").toString();
if (gearConfig.isEmpty()) {
gearConfig = "1|38|44|true\n2|38|38|true\n3|38|32|true\n4|38|28|true\n"
"5|38|24|true\n6|38|21|true\n7|38|19|true\n8|38|17|true\n"
"9|38|15|true\n10|38|13|true\n11|38|11|true\n12|38|10|true";
}
gears.clear();
maxGears = 0;
// Parsa la configurazione
QStringList rows = gearConfig.split('\n');
for (const QString& row : rows) {
QStringList parts = row.split('|');
if (parts.size() >= 4 && (parts[3] == "true")) {
GearInfo config;
config.gear = parts[0].toInt();
config.crankset = parts[1].toInt();
config.rearCog = parts[2].toInt();
gears.push_back(config);
maxGears = qMax(maxGears, config.gear);
}
}
}
void addGear(int gear, int crankset, int rearCog) {
gears.push_back({gear, crankset, rearCog});
}
void removeGear(int gear) {
gears.erase(std::remove_if(gears.begin(), gears.end(),
[gear](const GearInfo& info) { return info.gear == gear; }),
gears.end());
}
void printTable() const {
qDebug() << "| Gear | Crankset | Rear Cog |\n";
qDebug() << "|------|----------|----------|\n";
for (const auto& gear : gears) {
qDebug() << "| " << gear.gear << " | " << gear.crankset
<< " | " << gear.rearCog << " |\n";
}
}
GearInfo getGear(int gearNumber) const {
auto it = std::find_if(gears.begin(), gears.end(),
[gearNumber](const GearInfo& info) { return info.gear == gearNumber; });
if (it != gears.end()) {
return *it;
}
return GearInfo();
}
GearTable() {
loadGearSettings();
}
private:
std::vector<GearInfo> gears;
};
#ifdef Q_OS_IOS
lockscreen *h = 0;

817
src/gears.qml Normal file
View File

@@ -0,0 +1,817 @@
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
ScrollView {
contentWidth: -1
focus: true
anchors.horizontalCenter: parent.horizontalCenter
anchors.fill: parent
id: gearSettingsWindow
visible: true
clip: true
// Properties
Settings {
id: settings
property string gear_configuration: "1|38|44|true\n2|38|38|true\n3|38|32|true\n4|38|28|true\n5|38|24|true\n6|38|21|true\n7|38|19|true\n8|38|17|true\n9|38|15|true\n10|38|13|true\n11|38|11|true\n12|38|10|true"
property int gear_crankset_size: 42
property int gear_cog_size: 14
property string gear_wheel_size: "700 x 18C"
property real gear_circumference: 2070
}
property int selectedCranksetSize: settings.gear_crankset_size
property int selectedCogSize: settings.gear_cog_size
property string selectedWheelSize: settings.gear_wheel_size
property real selectedCircumference: settings.gear_circumference
property bool inited: false
property int initialWheelSizeIndex: {
// Trova l'indice corretto basato sul valore salvato
for (let i = 0; i < wheelSizes.count; i++) {
if (wheelSizes.get(i).text === settings.gear_wheel_size) {
return i;
}
}
return 0; // default se non trovato
}
Component.onCompleted: {
if (settings.gear_configuration) {
gearRows = stringToGearRows(settings.gear_configuration)
}
console.log("Component.onCompleted " + settings.gear_crankset_size + " " + settings.gear_cog_size + " " + settings.gear_wheel_size + " " + settings.gear_circumference)
wheelSizeCombo.currentIndex = initialWheelSizeIndex
selectedCranksetSize = settings.gear_crankset_size
selectedCogSize = settings.gear_cog_size
inited = true
}
function updateSettings() {
if(!inited)
return;
settings.gear_crankset_size = selectedCranksetSize
settings.gear_cog_size = selectedCogSize
settings.gear_wheel_size = selectedWheelSize
settings.gear_circumference = selectedCircumference
}
function stringToGearRows(gearString) {
if (!gearString) return []
return gearString.split("\n").map(function(row) {
const parts = row.split("|")
return {
gear: parseInt(parts[0]),
crankset: parseInt(parts[1]),
cog: parseInt(parts[2]),
active: parts[3] === "true"
}
})
}
function gearRowsToString(gearRows) {
return gearRows.map(function(row) {
return row.gear + "|" + row.crankset + "|" + row.cog + "|" + row.active
}).join("\n")
}
// Monitora i cambiamenti nelle gear e salva automaticamente
onGearConfigurationChanged: {
settings.gear_configuration = gearRowsToString(gearRows)
}
onSettingsChanged: {
console.log("onSettingsChanged")
updateSettings()
}
Connections {
target: gearSettingsWindow
function onGearConfigurationChanged() {
gearTable.updateGearListModel()
}
}
function loadGearProfile(profileName) {
if (profileName in gearProfiles) {
const profile = gearProfiles[profileName]
// Create new array with copied objects
var newGears = []
for (var i = 0; i < profile.gears.length; i++) {
newGears.push({
gear: profile.gears[i].gear,
crankset: profile.gears[i].crankset,
cog: profile.gears[i].cog,
active: profile.gears[i].active
})
}
gearRows = newGears
// Force update
var temp = gearRows
gearRows = []
gearRows = temp
gearConfigurationChanged(gearRows)
}
}
property var gearProfiles: {
"Time Trial": {
name: "Time Trial (52/36, 10 - 28)",
gears: [
{ gear: 1, crankset: 36, cog: 28, active: true },
{ gear: 2, crankset: 36, cog: 24, active: true },
{ gear: 3, crankset: 36, cog: 21, active: true },
{ gear: 4, crankset: 36, cog: 19, active: true },
{ gear: 5, crankset: 36, cog: 18, active: true },
{ gear: 6, crankset: 36, cog: 17, active: true },
{ gear: 7, crankset: 36, cog: 16, active: true },
{ gear: 8, crankset: 36, cog: 15, active: true },
{ gear: 9, crankset: 36, cog: 14, active: true },
{ gear: 10, crankset: 52, cog: 19, active: true },
{ gear: 11, crankset: 52, cog: 18, active: true },
{ gear: 12, crankset: 52, cog: 17, active: true },
{ gear: 13, crankset: 52, cog: 16, active: true },
{ gear: 14, crankset: 52, cog: 15, active: true },
{ gear: 15, crankset: 52, cog: 14, active: true },
{ gear: 16, crankset: 52, cog: 13, active: true },
{ gear: 17, crankset: 52, cog: 12, active: true },
{ gear: 18, crankset: 52, cog: 11, active: true },
{ gear: 19, crankset: 52, cog: 10, active: true }
]
},
"Rolling Hills": {
name: "Rolling Hills (46/33, 10 - 33)",
gears: [
{ gear: 1, crankset: 33, cog: 33, active: true },
{ gear: 2, crankset: 33, cog: 28, active: true },
{ gear: 3, crankset: 33, cog: 24, active: true },
{ gear: 4, crankset: 33, cog: 21, active: true },
{ gear: 5, crankset: 33, cog: 19, active: true },
{ gear: 6, crankset: 33, cog: 17, active: true },
{ gear: 7, crankset: 33, cog: 15, active: true },
{ gear: 8, crankset: 46, cog: 19, active: true },
{ gear: 9, crankset: 46, cog: 17, active: true },
{ gear: 10, crankset: 46, cog: 15, active: true },
{ gear: 11, crankset: 46, cog: 14, active: true },
{ gear: 12, crankset: 46, cog: 13, active: true },
{ gear: 13, crankset: 46, cog: 12, active: true },
{ gear: 14, crankset: 46, cog: 11, active: true },
{ gear: 15, crankset: 46, cog: 10, active: true }
]
},
"Alpine": {
name: "Alpine (43/30, 10 - 36)",
gears: [
{ gear: 1, crankset: 30, cog: 36, active: true },
{ gear: 2, crankset: 30, cog: 32, active: true },
{ gear: 3, crankset: 30, cog: 28, active: true },
{ gear: 4, crankset: 30, cog: 24, active: true },
{ gear: 5, crankset: 30, cog: 21, active: true },
{ gear: 6, crankset: 30, cog: 19, active: true },
{ gear: 7, crankset: 30, cog: 17, active: true },
{ gear: 8, crankset: 30, cog: 15, active: true },
{ gear: 9, crankset: 43, cog: 19, active: true },
{ gear: 10, crankset: 43, cog: 17, active: true },
{ gear: 11, crankset: 43, cog: 15, active: true },
{ gear: 12, crankset: 43, cog: 13, active: true },
{ gear: 13, crankset: 43, cog: 12, active: true },
{ gear: 14, crankset: 43, cog: 11, active: true },
{ gear: 15, crankset: 43, cog: 10, active: true }
]
},
"Reality Bender": {
name: "Reality Bender (24 even spaced)",
gears: [
{ gear: 1, crankset: 30, cog: 40, active: true },
{ gear: 2, crankset: 30, cog: 36, active: true },
{ gear: 3, crankset: 30, cog: 33, active: true },
{ gear: 4, crankset: 30, cog: 30, active: true },
{ gear: 5, crankset: 30, cog: 27, active: true },
{ gear: 6, crankset: 34, cog: 28, active: true },
{ gear: 7, crankset: 34, cog: 26, active: true },
{ gear: 8, crankset: 34, cog: 24, active: true },
{ gear: 9, crankset: 34, cog: 22, active: true },
{ gear: 10, crankset: 44, cog: 26, active: true },
{ gear: 11, crankset: 44, cog: 24, active: true },
{ gear: 12, crankset: 44, cog: 22, active: true },
{ gear: 13, crankset: 44, cog: 20, active: true },
{ gear: 14, crankset: 44, cog: 18, active: true },
{ gear: 15, crankset: 56, cog: 21, active: true },
{ gear: 16, crankset: 56, cog: 19, active: true },
{ gear: 17, crankset: 58, cog: 18, active: true },
{ gear: 18, crankset: 60, cog: 17, active: true },
{ gear: 19, crankset: 62, cog: 16, active: true },
{ gear: 20, crankset: 63, cog: 15, active: true },
{ gear: 21, crankset: 64, cog: 14, active: true },
{ gear: 22, crankset: 66, cog: 13, active: true },
{ gear: 23, crankset: 67, cog: 12, active: true }
]
},
"Explorer": {
name: "Explorer (40, 10 - 46)",
gears: [
{ gear: 1, crankset: 40, cog: 46, active: true },
{ gear: 2, crankset: 40, cog: 38, active: true },
{ gear: 3, crankset: 40, cog: 32, active: true },
{ gear: 4, crankset: 40, cog: 28, active: true },
{ gear: 5, crankset: 40, cog: 24, active: true },
{ gear: 6, crankset: 40, cog: 21, active: true },
{ gear: 7, crankset: 40, cog: 19, active: true },
{ gear: 8, crankset: 40, cog: 17, active: true },
{ gear: 9, crankset: 40, cog: 15, active: true },
{ gear: 10, crankset: 40, cog: 13, active: true },
{ gear: 11, crankset: 40, cog: 12, active: true },
{ gear: 12, crankset: 40, cog: 11, active: true },
{ gear: 13, crankset: 40, cog: 10, active: true }
]
}
}
// Initial gear data
property var gearRows: [
{ gear: 1, crankset: 38, cog: 44, active: true },
{ gear: 2, crankset: 38, cog: 38, active: true },
{ gear: 3, crankset: 38, cog: 32, active: true },
{ gear: 4, crankset: 38, cog: 28, active: true },
{ gear: 5, crankset: 38, cog: 24, active: true },
{ gear: 6, crankset: 38, cog: 21, active: true },
{ gear: 7, crankset: 38, cog: 19, active: true },
{ gear: 8, crankset: 38, cog: 17, active: true },
{ gear: 9, crankset: 38, cog: 15, active: true },
{ gear: 10, crankset: 38, cog: 13, active: true },
{ gear: 11, crankset: 38, cog: 11, active: true },
{ gear: 12, crankset: 38, cog: 10, active: true }
]
function addNewGear() {
// Find the first inactive gear or add at the end
let newGearIndex = gearRows.findIndex(row => !row.active);
if (newGearIndex === -1) {
newGearIndex = gearRows.length;
}
// Create new gear with default values
const newGear = {
gear: newGearIndex + 1,
crankset: selectedCranksetSize,
cog: selectedCogSize,
active: true
};
if (newGearIndex < gearRows.length) {
gearRows[newGearIndex] = newGear;
} else {
gearRows.push(newGear);
}
// Force update
var temp = gearRows;
gearRows = [];
gearRows = temp;
gearConfigurationChanged(gearRows);
}
function clearGearsFromIndex(startIndex) {
for (let i = startIndex; i < gearRows.length; i++) {
gearRows[i].active = false
}
// Force update
var temp = gearRows
gearRows = []
gearRows = temp
gearConfigurationChanged(gearRows)
}
function initializeGearRows() {
gearRows = [
{ gear: 1, crankset: 38, cog: 44, active: true },
{ gear: 2, crankset: 38, cog: 38, active: true },
{ gear: 3, crankset: 38, cog: 32, active: true },
{ gear: 4, crankset: 38, cog: 28, active: true },
{ gear: 5, crankset: 38, cog: 24, active: true },
{ gear: 6, crankset: 38, cog: 21, active: true },
{ gear: 7, crankset: 38, cog: 19, active: true },
{ gear: 8, crankset: 38, cog: 17, active: true },
{ gear: 9, crankset: 38, cog: 15, active: true },
{ gear: 10, crankset: 38, cog: 13, active: true },
{ gear: 11, crankset: 38, cog: 11, active: true },
{ gear: 12, crankset: 38, cog: 10, active: true }
]
// Force update
var temp = gearRows
gearRows = []
gearRows = temp
}
// Signals to notify when values change
signal settingsChanged()
signal gearConfigurationChanged(var gearRows)
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 20
id: chainringColumn
// Crankset Size
GroupBox {
title: "Chainring Size"
Layout.fillWidth: true
ColumnLayout {
Label {
text: "Tooth count of your chainring on the bike you are currently riding on your trainer - enter 42 for Zwift Ride"
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.maximumWidth: chainringColumn.width - 20
}
SpinBox {
from: 1
to: 60
value: selectedCranksetSize
onValueChanged: {
selectedCranksetSize = value
console.log("Crankset Size changed");
settingsChanged()
}
}
}
}
// Cog Size
GroupBox {
title: "Cog Size"
Layout.fillWidth: true
ColumnLayout {
Label {
text: "Tooth count of your rear cog on your trainer - enter 14 if you have the Zwift Cog"
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.maximumWidth: chainringColumn.width - 20
}
SpinBox {
from: 1
to: 50
value: selectedCogSize
onValueChanged: {
selectedCogSize = value
console.log("Cog Size changed");
settingsChanged()
}
}
}
}
// Wheel Size
GroupBox {
title: "Virtual Wheel Size"
Layout.fillWidth: true
ComboBox {
id: wheelSizeCombo
width: parent.width
currentIndex: initialWheelSizeIndex
textRole: "text"
model: ListModel {
id: wheelSizes
ListElement { text: "700 x 18C"; circumference: 2070 }
ListElement { text: "700 x 19C"; circumference: 2080 }
ListElement { text: "700 x 20C"; circumference: 2086 }
ListElement { text: "700 x 23C"; circumference: 2096 }
ListElement { text: "700 x 25C"; circumference: 2109 }
ListElement { text: "700 x 28C"; circumference: 2127 }
ListElement { text: "700 x 30C"; circumference: 2140 }
ListElement { text: "700 x 32C"; circumference: 2152 }
ListElement { text: "700 x 35C"; circumference: 2171 }
ListElement { text: "700 x 38C"; circumference: 2190 }
ListElement { text: "700 x 40C"; circumference: 2203 }
ListElement { text: "700 x 44C"; circumference: 2230 }
ListElement { text: "700 x 45C"; circumference: 2234 }
ListElement { text: "700 x 47C"; circumference: 2247 }
ListElement { text: "700 x 50C"; circumference: 2265 }
ListElement { text: "650 x 20C"; circumference: 1938 }
ListElement { text: "650 x 23C"; circumference: 1944 }
ListElement { text: "650 x 35A"; circumference: 2090 }
ListElement { text: "650 x 38B"; circumference: 2105 }
ListElement { text: "650 x 38A"; circumference: 2125 }
ListElement { text: "12\" x 1.75\""; circumference: 935 }
ListElement { text: "12\" x 1.95\""; circumference: 940 }
ListElement { text: "14\" x 1.50\""; circumference: 1020 }
ListElement { text: "14\" x 1.75\""; circumference: 1055 }
ListElement { text: "16\" x 1.50\""; circumference: 1185 }
ListElement { text: "16\" x 1.75\""; circumference: 1195 }
ListElement { text: "16\" x 2.00\""; circumference: 1245 }
ListElement { text: "16\" x 1-1/8\""; circumference: 1290 }
ListElement { text: "16\" x 1-3/8\""; circumference: 1300 }
ListElement { text: "18\" x 1.50\""; circumference: 1340 }
ListElement { text: "18\" x 1.75\""; circumference: 1350 }
ListElement { text: "20\" x 1.25\""; circumference: 1450 }
ListElement { text: "20\" x 1.35\""; circumference: 1460 }
ListElement { text: "20\" x 1.50\""; circumference: 1490 }
ListElement { text: "20\" x 1.75\""; circumference: 1515 }
ListElement { text: "20\" x 1.95\""; circumference: 1565 }
ListElement { text: "20\" x 1-1/8\""; circumference: 1545 }
ListElement { text: "20\" x 1-3/8\""; circumference: 1615 }
ListElement { text: "22\" x 1-3/8\""; circumference: 1770 }
ListElement { text: "22\" x 1-1/2\""; circumference: 1785 }
ListElement { text: "24\" x 3/4\" Tubular"; circumference: 1785 }
ListElement { text: "24\" x 1\""; circumference: 1753 }
ListElement { text: "24\" x 1-1/8\""; circumference: 1795 }
ListElement { text: "24\" x 1-1/4\""; circumference: 1905 }
ListElement { text: "24\" x 1.75\""; circumference: 1890 }
ListElement { text: "24\" x 2.00\""; circumference: 1925 }
ListElement { text: "24\" x 2.125\""; circumference: 1965 }
ListElement { text: "26\" x 7/8\" Tubular"; circumference: 1920 }
ListElement { text: "26\" x 1.25\""; circumference: 1950 }
ListElement { text: "26\" x 1.40\""; circumference: 2005 }
ListElement { text: "26\" x 1.50\""; circumference: 2010 }
ListElement { text: "26\" x 1.75\""; circumference: 2023 }
ListElement { text: "26\" x 1.95\""; circumference: 2050 }
ListElement { text: "26\" x 2.00\""; circumference: 2055 }
ListElement { text: "26\" x 2.10\""; circumference: 2068 }
ListElement { text: "26\" x 2.125\""; circumference: 2070 }
ListElement { text: "26\" x 2.35\""; circumference: 2083 }
ListElement { text: "26\" x 3.00\""; circumference: 2170 }
ListElement { text: "26\" x 1-1.0\""; circumference: 1913 }
ListElement { text: "26\" x 1\""; circumference: 1952 }
ListElement { text: "26\" x 1-1/8\""; circumference: 1970 }
ListElement { text: "26\" x 1-3/8\""; circumference: 2068 }
ListElement { text: "26\" x 1-1/2\""; circumference: 2100 }
ListElement { text: "27\" x 1\""; circumference: 2145 }
ListElement { text: "27\" x 1-1/8\""; circumference: 2155 }
ListElement { text: "27\" x 1-1/4\""; circumference: 2161 }
ListElement { text: "27\" x 1-3/8\""; circumference: 2169 }
ListElement { text: "27.5\" / 650B x 1.50\""; circumference: 2079 }
ListElement { text: "27.5\" / 650B x 1.95\""; circumference: 2090 }
ListElement { text: "27.5\" / 650B x 2.10\""; circumference: 2148 }
ListElement { text: "27.5\" / 650B x 2.25\""; circumference: 2182 }
ListElement { text: "27.5\" / 650B x 2.3\""; circumference: 2199 }
ListElement { text: "27.5\" / 650B x 2.35\""; circumference: 2207 }
ListElement { text: "27.5\" / 650B x 2.4\""; circumference: 2213 }
ListElement { text: "27.5\" / 650B x 2.5\""; circumference: 2231 }
ListElement { text: "27.5\" / 650B x 2.6\""; circumference: 2247 }
ListElement { text: "27.5\" / 650B x 2.8\""; circumference: 2279 }
ListElement { text: "29\" x 2.1\""; circumference: 2286 }
ListElement { text: "29\" x 2.2\""; circumference: 2302 }
ListElement { text: "29\" x 2.25\""; circumference: 2310 }
ListElement { text: "29\" x 2.3\""; circumference: 2326 }
ListElement { text: "29\" x 2.35\""; circumference: 2326 }
ListElement { text: "29\" x 2.4\""; circumference: 2333 }
ListElement { text: "29\" x 2.5\""; circumference: 2350 }
ListElement { text: "29\" x 2.6\""; circumference: 2366 }
}
onCurrentIndexChanged: {
if (currentIndex >= 0) {
selectedWheelSize = model.get(currentIndex).text
selectedCircumference = model.get(currentIndex).circumference
console.log("wheelSizeCombo changed");
settingsChanged()
}
}
}
}
GroupBox {
title: "Preset Gear Profiles"
Layout.fillWidth: true
ComboBox {
id: profileCombo
width: parent.width
textRole: "text"
displayText: currentIndex < 0 ? "Select a profile..." : model.get(currentIndex).text
model: ListModel {
id: profileModel
}
Component.onCompleted: {
for (var key in gearProfiles) {
profileModel.append({
text: gearProfiles[key].name,
value: key
})
}
}
onCurrentIndexChanged: {
if (currentIndex >= 0) {
loadGearProfile(profileModel.get(currentIndex).value)
}
}
}
}
// Gear Table GroupBox
GroupBox {
title: "Virtual Gear Table"
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: parent.height
ColumnLayout {
anchors.fill: parent
spacing: 10
// Updated Buttons Row
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 40
spacing: 10
Button {
text: "Add Gear"
Layout.fillWidth: true
Layout.preferredHeight: 40
onClicked: addNewGear()
}
Button {
text: "Clear Selected Gear and Following"
Layout.fillWidth: true
Layout.preferredHeight: 40
onClicked: {
if (gearTable.currentRow >= 0) {
clearGearsFromIndex(gearTable.currentRow)
}
}
}
Button {
text: "Reset All Gears"
Layout.fillWidth: true
Layout.preferredHeight: 40
onClicked: initializeGearRows()
}
}
// Table Header (same as before)
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 40
color: "#f0f0f0"
border.width: 1
border.color: "#cccccc"
Row {
anchors.fill: parent
Rectangle {
width: parent.width / 3
height: parent.height
border.width: 1
border.color: "#cccccc"
color: "transparent"
Text {
anchors.centerIn: parent
text: "Gear"
font.bold: true
color: "black"
}
}
Rectangle {
width: parent.width / 3
height: parent.height
border.width: 1
border.color: "#cccccc"
color: "transparent"
Text {
anchors.centerIn: parent
text: "Chainring"
font.bold: true
color: "black"
}
}
Rectangle {
width: parent.width / 3
height: parent.height
border.width: 1
border.color: "#cccccc"
color: "transparent"
Text {
anchors.centerIn: parent
text: "Rear Cog"
font.bold: true
color: "black"
}
}
}
}
// Table Content
ListView {
id: gearTable
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
property int currentRow: -1
model: ListModel {
id: gearListModel
}
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AlwaysOff
}
Component.onCompleted: {
updateGearListModel()
}
function updateGearListModel() {
gearListModel.clear()
for (var i = 0; i < gearRows.length; i++) {
if (gearRows[i].active) {
gearListModel.append(gearRows[i])
}
}
}
delegate: Rectangle {
width: gearTable.width
height: 40
color: gearTable.currentRow === index ? "#e0e0e0" : "white"
MouseArea {
anchors.fill: parent
onClicked: gearTable.currentRow = index
}
Row {
anchors.fill: parent
// Gear Number (non-editable)
Rectangle {
width: parent.width / 3
height: parent.height
border.width: 1
border.color: "#cccccc"
color: "transparent"
Text {
anchors.centerIn: parent
text: gear
color: "black"
}
}
// Crankset (editable)
Rectangle {
width: parent.width / 3
height: parent.height
border.width: 1
border.color: "#cccccc"
color: "transparent"
SpinBox {
id: cranksetSpinBox
anchors.centerIn: parent
width: parent.width * 0.8
height: 30
from: 1
to: 60
value: crankset
onValueModified: {
gearRows[index].crankset = value
gearConfigurationChanged(gearRows)
}
// Style the SpinBox
contentItem: TextInput {
z: 2
text: cranksetSpinBox.textFromValue(cranksetSpinBox.value, cranksetSpinBox.locale)
font: cranksetSpinBox.font
color: "black"
selectionColor: "#21be2b"
selectedTextColor: "#ffffff"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
up.indicator: Rectangle {
x: parent.width - width
height: parent.height
width: height
color: parent.up.pressed ? "#e4e4e4" : "#f6f6f6"
border.color: "#cccccc"
Text {
text: "+"
color: "black"
anchors.centerIn: parent
font.pixelSize: 12
}
}
down.indicator: Rectangle {
x: 0
height: parent.height
width: height
color: parent.down.pressed ? "#e4e4e4" : "#f6f6f6"
border.color: "#cccccc"
Text {
text: "-"
color: "black"
anchors.centerIn: parent
font.pixelSize: 12
}
}
background: Rectangle {
color: "white"
border.color: "#cccccc"
}
}
}
// Rear Cog (editable)
Rectangle {
width: parent.width / 3
height: parent.height
border.width: 1
border.color: "#cccccc"
color: "transparent"
SpinBox {
id: cogSpinBox
anchors.centerIn: parent
width: parent.width * 0.8
height: 30
from: 1
to: 50
value: cog
onValueModified: {
gearRows[index].cog = value
gearConfigurationChanged(gearRows)
}
// Style the SpinBox (same as cranksetSpinBox)
contentItem: TextInput {
z: 2
text: cogSpinBox.textFromValue(cogSpinBox.value, cogSpinBox.locale)
font: cogSpinBox.font
color: "black"
selectionColor: "#21be2b"
selectedTextColor: "#ffffff"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
up.indicator: Rectangle {
x: parent.width - width
height: parent.height
width: height
color: parent.up.pressed ? "#e4e4e4" : "#f6f6f6"
border.color: "#cccccc"
Text {
text: "+"
color: "black"
anchors.centerIn: parent
font.pixelSize: 12
}
}
down.indicator: Rectangle {
x: 0
height: parent.height
width: height
color: parent.down.pressed ? "#e4e4e4" : "#f6f6f6"
border.color: "#cccccc"
Text {
text: "-"
color: "black"
anchors.centerIn: parent
font.pixelSize: 12
}
}
background: Rectangle {
color: "white"
border.color: "#cccccc"
}
}
}
}
}
}
}
}
}
}

View File

@@ -109,5 +109,6 @@
<file>ChartFooterInnerNoJS.qml</file>
<file>inner_templates/chartjs/dochartliveheart.js</file>
<file>Wizard.qml</file>
<file>gears.qml</file>
</qresource>
</RCC>

View File

@@ -769,8 +769,15 @@ const QString QZSettings::default_peloton_date_format = QStringLiteral("MM/dd/yy
const QString QZSettings::force_resistance_instead_inclination = QStringLiteral("force_resistance_instead_inclination");
const QString QZSettings::proform_treadmill_575i = QStringLiteral("proform_treadmill_575i");
const QString QZSettings::zwift_play_emulator = QStringLiteral("zwift_play_emulator");
const QString QZSettings::gear_configuration = QStringLiteral("gear_configuration");
const QString QZSettings::default_gear_configuration = QStringLiteral("1|38|44|true\n2|38|38|true\n3|38|32|true\n4|38|28|true\n5|38|24|true\n6|38|21|true\n7|38|19|true\n8|38|17|true\n9|38|15|true\n10|38|13|true\n11|38|11|true\n12|38|10|true");
const QString QZSettings::gear_crankset_size = QStringLiteral("gear_crankset_size");
const QString QZSettings::gear_cog_size = QStringLiteral("gear_cog_size");
const QString QZSettings::gear_wheel_size = QStringLiteral("gear_wheel_size");
const QString QZSettings::default_gear_wheel_size = QStringLiteral("700 x 18C");
const QString QZSettings::gear_circumference = QStringLiteral("gear_circumference");
const uint32_t allSettingsCount = 651;
const uint32_t allSettingsCount = 656;
QVariant allSettings[allSettingsCount][2] = {
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
@@ -1428,6 +1435,11 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::force_resistance_instead_inclination, QZSettings::default_force_resistance_instead_inclination},
{QZSettings::proform_treadmill_575i, QZSettings::default_proform_treadmill_575i},
{QZSettings::zwift_play_emulator, QZSettings::default_zwift_play_emulator},
{QZSettings::gear_configuration, QZSettings::default_gear_configuration},
{QZSettings::gear_crankset_size, QZSettings::default_gear_crankset_size},
{QZSettings::gear_cog_size, QZSettings::default_gear_cog_size},
{QZSettings::gear_wheel_size, QZSettings::default_gear_wheel_size},
{QZSettings::gear_circumference, QZSettings::default_gear_circumference},
};
void QZSettings::qDebugAllSettings(bool showDefaults) {

View File

@@ -2153,6 +2153,21 @@ class QZSettings {
static const QString zwift_play_emulator;
static constexpr bool default_zwift_play_emulator = false;
static const QString gear_configuration;
static const QString default_gear_configuration;
static const QString gear_crankset_size;
static constexpr int default_gear_crankset_size = 42;
static const QString gear_cog_size;
static constexpr int default_gear_cog_size = 14;
static const QString gear_wheel_size;
static const QString default_gear_wheel_size;
static const QString gear_circumference;
static constexpr double default_gear_circumference = 2070.0;
/**
* @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

@@ -11,7 +11,7 @@ ScrollView {
anchors.fill: parent
//anchors.bottom: footerSettings.top
//anchors.bottomMargin: footerSettings.height + 10
id: settingsTTLPane
id: settingsInclinationPane
Settings {
id: settings

View File

@@ -987,6 +987,13 @@ import QtQuick.Dialogs 1.0
// from version 2.18.1
property bool zwift_play_emulator: false
// from version 2.18.2
property string gear_configuration: "1|38|44|true\n2|38|38|true\n3|38|32|true\n4|38|28|true\n5|38|24|true\n6|38|21|true\n7|38|19|true\n8|38|17|true\n9|38|15|true\n10|38|13|true\n11|38|11|true\n12|38|10|true"
property int gear_crankset_size: 42
property int gear_cog_size: 14
property string gear_wheel_size: "700 x 18C"
property real gear_circumference: 2070
}
function paddingZeros(text, limit) {
@@ -2493,6 +2500,14 @@ import QtQuick.Dialogs 1.0
color: Material.color(Material.Lime)
}
NewPageElement {
title: qsTr("Wahoo Options")
indicatRectColor: Material.color(Material.Grey)
textColor: Material.color(Material.Yellow)
color: Material.backgroundColor
accordionContent: "gears.qml"
}
AccordionElement {
id: schwinnBikeAccordion
title: qsTr("Schwinn Bike Options")
@@ -9800,7 +9815,7 @@ import QtQuick.Dialogs 1.0
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
color: Material.color(Material.Lime)
}
}
}
}