@@ -110,9 +110,26 @@ void strydrunpowersensor::update() {
110110 sec1Update = 0 ;
111111 // updateDisplay(elapsed);
112112 }
113+
114+ if (strydIndoorModeRequested && !strydIndoorModeConfirmed && strydIndoorModeRetries < 3 &&
115+ strydIndoorModeRequestedAt.msecsTo (QDateTime::currentDateTime ()) > 1000 ) {
116+ qDebug () << QStringLiteral (" Stryd5: no ack for Indoor mode switch, retrying" );
117+ requestStrydIndoorMode ();
118+ }
113119 }
114120}
115121
122+ void strydrunpowersensor::requestStrydIndoorMode () {
123+ if (!strydIndoorModeService || !strydIndoorModeChar.isValid ())
124+ return ;
125+
126+ qDebug () << QStringLiteral (" Stryd5: switching power mode to Indoor" );
127+ strydIndoorModeService->writeCharacteristic (strydIndoorModeChar, QByteArray::fromHex (" 1500" ));
128+ strydIndoorModeRequested = true ;
129+ strydIndoorModeRequestedAt = QDateTime::currentDateTime ();
130+ strydIndoorModeRetries++;
131+ }
132+
116133void strydrunpowersensor::serviceDiscovered (const QBluetoothUuid &gatt) {
117134 emit debug (QStringLiteral (" serviceDiscovered " ) + gatt.toString ());
118135}
@@ -506,6 +523,18 @@ void strydrunpowersensor::characteristicChanged(const QLowEnergyCharacteristic &
506523 // emit verticalOscillationChanged(VerticalOscillationMM.value());
507524 qDebug () << QStringLiteral (" Current GroundContactMS:" ) << GroundContactMS.value ();
508525 qDebug () << QStringLiteral (" Current VerticalOscillationMM:" ) << VerticalOscillationMM.value ();
526+ } else if (strydIndoorModeRequested && newValue.length () >= 3 && (uint8_t )newValue.at (0 ) == 0x15 &&
527+ (uint8_t )newValue.at (1 ) == 0x00 ) {
528+ // ack of the 15 00/15 01 mode switch written to 7e78aa20: byte 2 echoes back the confirmed
529+ // mode (0x00 = Indoor, 0x01 = Outdoor)
530+ uint8_t confirmedMode = (uint8_t )newValue.at (2 );
531+ if (confirmedMode == 0x00 ) {
532+ strydIndoorModeConfirmed = true ;
533+ qDebug () << QStringLiteral (" Stryd5: Indoor mode confirmed by the pod" );
534+ } else {
535+ qDebug () << QStringLiteral (" Stryd5: pod acked mode " ) << confirmedMode
536+ << QStringLiteral (" instead of Indoor, will retry" );
537+ }
509538 }
510539 }
511540
@@ -585,6 +614,19 @@ void strydrunpowersensor::stateChanged(QLowEnergyService::ServiceState state) {
585614 if (c.uuid () == QBluetoothUuid::CSCMeasurement) {
586615 cadenceChar = c;
587616 }
617+
618+ // Stryd 5 broadcasts a power value that already reacts to the incline it detects on its own
619+ // (confirmed via BLE sniffing, see #4480), which conflicts with QZ's own inclination gain
620+ // formula. Switching the pod to Indoor mode makes it stop compensating for incline on its
621+ // own, leaving QZ's formula as the only source of the incline adjustment.
622+ if (bluetoothDevice.name ().startsWith (QStringLiteral (" Stryd5" ), Qt::CaseInsensitive) &&
623+ c.uuid () == QBluetoothUuid (QStringLiteral (" 7e78aa20-72cd-d3b8-a81f-5b7e589bea0f" )) &&
624+ (c.properties () & QLowEnergyCharacteristic::Write)) {
625+ strydIndoorModeService = s;
626+ strydIndoorModeChar = c;
627+ requestStrydIndoorMode ();
628+ }
629+
588630 qDebug () << QStringLiteral (" char uuid" ) << c.uuid () << QStringLiteral (" handle" ) << c.handle ();
589631 auto descriptors_list = c.descriptors ();
590632 for (const QLowEnergyDescriptor &d : qAsConst (descriptors_list)) {
0 commit comments