Skip to content

Commit

Permalink
Merge branch 'master' into peloton_raspberry_treadmill
Browse files Browse the repository at this point in the history
  • Loading branch information
cagnulein committed Jan 3, 2024
2 parents 39fc5b7 + 9065ef0 commit 0095c73
Show file tree
Hide file tree
Showing 33 changed files with 15,515 additions and 166 deletions.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.16.29" android:versionCode="693" android:installLocation="auto">
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.16.30" android:versionCode="696" android:installLocation="auto">
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
Expand Down
27 changes: 27 additions & 0 deletions src/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ buildscript {

dependencies {
classpath 'com.android.tools.build:gradle:3.6.0'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.4'
}
}

plugins {
id "com.google.protobuf" version "0.9.4"
}

repositories {
google()
jcenter()
Expand All @@ -17,6 +22,7 @@ repositories {
}

apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'

def amazon = System.getenv('AMAZON')
println(amazon)
Expand All @@ -25,6 +31,7 @@ dependencies {
compile 'com.rvalerio:fgchecker:1.1.0'
implementation "androidx.core:core-ktx:1.12.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0"
implementation 'com.google.protobuf:protobuf-javalite:3.25.1'

if(amazon == "1") {
// amazon app store
Expand All @@ -49,6 +56,26 @@ dependencies {
implementation 'com.google.android.gms:play-services-wearable:+'
}

import org.apache.tools.ant.taskdefs.condition.Os

// Compatible with macOS on Apple Silicon
def archSuffix = Os.isFamily(Os.FAMILY_MAC) ? ':osx-x86_64' : ''

protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.1$archSuffix"
}
generateProtoTasks {
all().configureEach { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}

android {
/*******************************************************
* The following variables:
Expand Down
1 change: 1 addition & 0 deletions src/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
android.useAndroidX=true
android.enableJetifier=true
protoc_platform=osx-x86_64
Empty file modified src/android/gradlew
100644 → 100755
Empty file.
65 changes: 65 additions & 0 deletions src/android/src/ZwiftAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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 ZwiftAPI {

private static Context context;

private static final String TAG = "ZwiftAPI: ";
private static ZwiftMessages.PlayerState playerState;

public static void zwift_api_decodemessage_player(byte[] value) {
try {
playerState = ZwiftMessages.PlayerState.parseFrom(value);
// Ora puoi usare 'message' come un oggetto normale
} catch (InvalidProtocolBufferException e) {
// Gestisci l'eccezione se il messaggio non può essere parsato
Log.e(TAG, e.toString());
}
}

public static float getAltitude() {
Log.d(TAG, "getAltitude " + playerState.getAltitude());
return playerState.getAltitude();
}

public static float getDistance() {
Log.d(TAG, "getDistance " + playerState.getDistance());
return playerState.getDistance();
}
}
155 changes: 155 additions & 0 deletions src/android/src/main/proto/zwift_messages.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
syntax="proto3";

package org.cagnulen.qdomyoszwift;

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;
}
3 changes: 2 additions & 1 deletion src/bluetooth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
!toorx_ftms && toorx_ftms_treadmill) ||
!b.name().compare(ftms_treadmill, Qt::CaseInsensitive) ||
b.name().toUpper().startsWith(QStringLiteral("MOBVOI TM")) || // FTMS
b.name().toUpper().startsWith(QStringLiteral("TUNTURI T60-")) || // FTMS
b.name().toUpper().startsWith(QStringLiteral("KETTLER TREADMILL")) || // FTMS
b.name().toUpper().startsWith(QStringLiteral("ASSAULTRUNNER")) || // FTMS
(b.name().toUpper().startsWith(QStringLiteral("CTM")) && b.name().length() >= 15) || // FTMS
Expand Down Expand Up @@ -1355,7 +1356,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
(b.name().toUpper().startsWith("ZUMO")) || (b.name().toUpper().startsWith("XS08-")) ||
(b.name().toUpper().startsWith("B94")) || (b.name().toUpper().startsWith("STAGES BIKE")) ||
(b.name().toUpper().startsWith("SUITO")) || (b.name().toUpper().startsWith("D2RIDE")) ||
(b.name().toUpper().startsWith("DIRETO XR")) ||
(b.name().toUpper().startsWith("DIRETO XR")) || (b.name().toUpper().startsWith("MERACH-667-")) ||
!b.name().compare(ftms_bike, Qt::CaseInsensitive) || (b.name().toUpper().startsWith("SMB1")) ||
(b.name().toUpper().startsWith("UBIKE FTMS")) || (b.name().toUpper().startsWith("INRIDE"))) &&
!ftmsBike && !snodeBike && !fitPlusBike && !stagesBike && filter) {
Expand Down
49 changes: 48 additions & 1 deletion src/characteristicnotifier2a63.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,57 @@ int CharacteristicNotifier2A63::notify(QByteArray &value) {
normalizeWattage = 0;

if (Bike->deviceType() == bluetoothdevice::BIKE) {
value.append((char)0x20); // crank data present
/*
// set measurement
measurement[2] = power & 0xFF;
measurement[3] = (power >> 8) & 0xFF;
measurement[4] = wheelrev & 0xFF;
measurement[5] = (wheelrev >> 8) & 0xFF;
measurement[6] = (wheelrev >> 16) & 0xFF;
measurement[7] = (wheelrev >> 24) & 0xFF;
measurement[8] = lastwheel & 0xFF;
measurement[9] = (lastwheel >> 8) & 0xFF;
measurement[10] = crankrev & 0xFF;
measurement[11] = (crankrev >> 8) & 0xFF;
measurement[12] = lastcrank & 0xFF;
measurement[13] = (lastcrank >> 8) & 0xFF;
// speed & distance
// NOTE : based on Apple Watch default wheel dimension 700c x 2.5mm
// NOTE : 3 is theoretical crank:wheel gear ratio
// NOTE : 2.13 is circumference of 700c in meters
wheelCount = crankCount * 3;
speed = cadence * 3 * 2.13 * 60 / 1000;
distance = wheelCount * 2.13 / 1000;
#if defined(USEPOWER)
lastWheelK = lastCrankK * 2; // 1/2048 s granularity
#else
lastWheelK = lastCrankK * 1; // 1/1024 s granularity
#endif
*/

uint32_t wheelCount = (uint32_t)Bike->currentCrankRevolutions() * 3;
uint16_t lastWheelK = Bike->lastCrankEventTime() * 2;

value.append((char)0x30); // crank data present and wheel for apple watch
value.append((char)0x00);
value.append((char)(((uint16_t)normalizeWattage) & 0xFF)); // watt
value.append((char)(((uint16_t)normalizeWattage) >> 8) & 0xFF); // watt

value.append((char)(((uint32_t)wheelCount) & 0xFF)); // revs count
value.append((char)(((uint32_t)wheelCount) >> 8) & 0xFF); // revs count
value.append((char)(((uint32_t)wheelCount) >> 16) & 0xFF); // revs count
value.append((char)(((uint32_t)wheelCount) >> 24) & 0xFF); // revs count
value.append((char)(lastWheelK & 0xff)); // eventtime
value.append((char)(lastWheelK >> 8) & 0xFF); // eventtime

value.append((char)(((uint16_t)Bike->currentCrankRevolutions()) & 0xFF)); // revs count
value.append((char)(((uint16_t)Bike->currentCrankRevolutions()) >> 8) & 0xFF); // revs count
value.append((char)(Bike->lastCrankEventTime() & 0xff)); // eventtime
Expand Down
6 changes: 6 additions & 0 deletions src/fakebike.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ void fakebike::update() {
if (requestPower != -1) {
// bepo70: don't know if this conversion is really needed, i would do it anyway.
m_watt = (double)requestPower;
Cadence = requestPower;
emit debug(QStringLiteral("writing power ") + QString::number(requestPower));
requestPower = -1;
// bepo70: Disregard the current inclination for calculating speed. When the video
Expand All @@ -60,6 +61,11 @@ void fakebike::update() {
m_watt.value(), 0, Speed.value(), fabs(QDateTime::currentDateTime().msecsTo(Speed.lastChanged()) / 1000.0),
speedLimit());
}

if (Cadence.value() > 0) {
CrankRevs++;
LastCrankEventTime += (uint16_t)(1024.0 / (((double)(Cadence.value())) / 60.0));
}

if (requestInclination != -100) {
Inclination = requestInclination;
Expand Down
Loading

0 comments on commit 0095c73

Please sign in to comment.