mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
68 Commits
android36
...
build-1044
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38a3a1e124 | ||
|
|
8896154fc1 | ||
|
|
ff6b9bf780 | ||
|
|
05da4fafc8 | ||
|
|
de591bb650 | ||
|
|
12eb8abfd0 | ||
|
|
707c1d81ff | ||
|
|
f02228f9be | ||
|
|
db842417c4 | ||
|
|
f0c5a41e48 | ||
|
|
498ad13c15 | ||
|
|
6183101e2f | ||
|
|
fbdd5bd177 | ||
|
|
e65a209543 | ||
|
|
458642abab | ||
|
|
045a0d0c7d | ||
|
|
93713ae19d | ||
|
|
c2c581b6d0 | ||
|
|
3711e94026 | ||
|
|
eccaa89d77 | ||
|
|
97a67340a6 | ||
|
|
e24b4d0a55 | ||
|
|
3f58080193 | ||
|
|
deed740ac9 | ||
|
|
d19db65cf6 | ||
|
|
c5942d1d93 | ||
|
|
13851564d0 | ||
|
|
df3620f83a | ||
|
|
614957819f | ||
|
|
69d8bab7c8 | ||
|
|
7686ca48d0 | ||
|
|
9685a00b8a | ||
|
|
c69714bfad | ||
|
|
9da3d28995 | ||
|
|
90c8c167bc | ||
|
|
9962776f02 | ||
|
|
ef9ce3cffd | ||
|
|
0a717865ef | ||
|
|
329cf8beaa | ||
|
|
a7777f4185 | ||
|
|
fd200f43f6 | ||
|
|
7e9024a36e | ||
|
|
26c261957e | ||
|
|
5d38be8a5c | ||
|
|
a5ef8dfc21 | ||
|
|
39599dc7ad | ||
|
|
d765f13a13 | ||
|
|
1173f8f8bc | ||
|
|
6959993a40 | ||
|
|
45ca7b3b5b | ||
|
|
a84bfac840 | ||
|
|
c6c83085af | ||
|
|
f1c2414ad6 | ||
|
|
942d213216 | ||
|
|
0ad2f62e6c | ||
|
|
37f45b8439 | ||
|
|
821cddb342 | ||
|
|
f378bc3fff | ||
|
|
6ea8c002ac | ||
|
|
a95ab376c5 | ||
|
|
7d30088c7b | ||
|
|
fb3777b7ff | ||
|
|
d28acf3e96 | ||
|
|
553e83a075 | ||
|
|
857839b80f | ||
|
|
6e28f8a797 | ||
|
|
ddafafaba6 | ||
|
|
a746373894 |
@@ -4255,7 +4255,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1037;
|
||||
CURRENT_PROJECT_VERSION = 1044;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
|
||||
@@ -4449,7 +4449,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1037;
|
||||
CURRENT_PROJECT_VERSION = 1044;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4679,7 +4679,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1037;
|
||||
CURRENT_PROJECT_VERSION = 1044;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -4775,7 +4775,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1037;
|
||||
CURRENT_PROJECT_VERSION = 1044;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -4867,7 +4867,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1037;
|
||||
CURRENT_PROJECT_VERSION = 1044;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -4983,7 +4983,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1037;
|
||||
CURRENT_PROJECT_VERSION = 1044;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
|
||||
@@ -227,6 +227,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
let quantity = HKQuantity(unit: unit,
|
||||
doubleValue: totalEnergyBurned)
|
||||
|
||||
if workoutSession.startDate == nil {
|
||||
return
|
||||
}
|
||||
|
||||
let sample = HKCumulativeQuantitySeriesSample(type: quantityType,
|
||||
quantity: quantity,
|
||||
start: workoutSession.startDate!,
|
||||
|
||||
@@ -132,7 +132,7 @@ void antbike::update() {
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -214,7 +214,7 @@ void apexbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,10 @@ bluetooth::bluetooth(bool logs, const QString &deviceName, bool noWriteResistanc
|
||||
|
||||
QString nordictrack_2950_ip =
|
||||
settings.value(QZSettings::nordictrack_2950_ip, QZSettings::default_nordictrack_2950_ip).toString();
|
||||
bool fake_bike =
|
||||
settings.value(QZSettings::applewatch_fakedevice, QZSettings::default_applewatch_fakedevice).toBool();
|
||||
bool fake_treadmill =
|
||||
settings.value(QZSettings::fakedevice_treadmill, QZSettings::default_fakedevice_treadmill).toBool();
|
||||
|
||||
if (settings.value(QZSettings::peloton_bike_ocr, QZSettings::default_peloton_bike_ocr).toBool() && !pelotonBike) {
|
||||
pelotonBike = new pelotonbike(noWriteResistance, noHeartService);
|
||||
@@ -47,6 +51,28 @@ bluetooth::bluetooth(bool logs, const QString &deviceName, bool noWriteResistanc
|
||||
}
|
||||
// this signal is not associated to anything in this moment, since the homeform is not loaded yet
|
||||
this->signalBluetoothDeviceConnected(pelotonBike);
|
||||
} else if (fake_bike) {
|
||||
fakeBike = new fakebike(noWriteResistance, noHeartService, false);
|
||||
emit deviceConnected(QBluetoothDeviceInfo());
|
||||
connect(fakeBike, &bluetoothdevice::connectedAndDiscovered, this, &bluetooth::connectedAndDiscovered);
|
||||
connect(fakeBike, &fakebike::debug, this, &bluetooth::debug);
|
||||
if (this->discoveryAgent && !this->discoveryAgent->isActive()) {
|
||||
emit searchingStop();
|
||||
}
|
||||
// this signal is not associated to anything in this moment, since the homeform is not loaded yet
|
||||
this->signalBluetoothDeviceConnected(fakeBike);
|
||||
return;
|
||||
} else if (fake_treadmill) {
|
||||
fakeTreadmill = new faketreadmill(noWriteResistance, noHeartService, false);
|
||||
emit deviceConnected(QBluetoothDeviceInfo());
|
||||
connect(fakeTreadmill, &bluetoothdevice::connectedAndDiscovered, this, &bluetooth::connectedAndDiscovered);
|
||||
connect(fakeTreadmill, &faketreadmill::debug, this, &bluetooth::debug);
|
||||
if (this->discoveryAgent && !this->discoveryAgent->isActive()) {
|
||||
emit searchingStop();
|
||||
}
|
||||
// this signal is not associated to anything in this moment, since the homeform is not loaded yet
|
||||
this->signalBluetoothDeviceConnected(fakeBike);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
@@ -267,6 +267,12 @@ void bluetoothdevice::update_hr_from_external() {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
lockscreen h;
|
||||
h.workoutTrackingUpdate(Speed.value(), Cadence.value(), (uint16_t)m_watt.value(), calories().value(), StepCount.value(), deviceType(), odometer() * 1000.0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void bluetoothdevice::clearStats() {
|
||||
|
||||
@@ -284,7 +284,7 @@ void csafeelliptical::update() {
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -738,7 +738,7 @@ void cycleopsphantombike::characteristicChanged(const QLowEnergyCharacteristic &
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)currentHeart().value());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -62,6 +62,14 @@ void fakebike::update() {
|
||||
speedLimit());
|
||||
}
|
||||
|
||||
double weight = settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
|
||||
if (watts())
|
||||
KCal +=
|
||||
((((0.048 * ((double)watts()) + 1.19) * weight * 3.5) / 200.0) /
|
||||
(60000.0 / ((double)lastRefreshCharacteristicChanged.msecsTo(
|
||||
QDateTime::currentDateTime())))); //(( (0.048* Output in watts +1.19) * body weight in
|
||||
// kg * 3.5) / 200 ) / 60
|
||||
|
||||
if (Cadence.value() > 0) {
|
||||
CrankRevs++;
|
||||
LastCrankEventTime += (uint16_t)(1024.0 / (((double)(Cadence.value())) / 60.0));
|
||||
|
||||
@@ -900,7 +900,7 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -264,7 +264,7 @@ void kineticinroadbike::characteristicChanged(const QLowEnergyCharacteristic &ch
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -458,7 +458,7 @@ void nordictrackifitadbelliptical::processPendingDatagrams() {
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadencep && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -240,7 +240,7 @@ void pitpatbike::characteristicChanged(const QLowEnergyCharacteristic &character
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -424,7 +424,7 @@ void technogymbike::characteristicChanged(const QLowEnergyCharacteristic &charac
|
||||
bool ios_peloton_workaround =
|
||||
settings.value(QZSettings::ios_peloton_workaround, QZSettings::default_ios_peloton_workaround).toBool();
|
||||
if (ios_peloton_workaround && cadence && h && firstStateChanged) {
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setCadence(currentCrankRevolutions(), lastCrankEventTime());
|
||||
h->virtualbike_setHeartRate((uint8_t)metrics_override_heartrate());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -746,6 +746,11 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
|
||||
}
|
||||
});
|
||||
});
|
||||
#else
|
||||
#ifndef IO_UNDER_QT
|
||||
h = new lockscreen();
|
||||
h->appleWatchAppInstalled();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (QSslSocket::supportsSsl()) {
|
||||
@@ -4023,7 +4028,12 @@ void homeform::Start_inner(bool send_event_to_device) {
|
||||
videoPlaybackHalfPlayer->pause();
|
||||
}
|
||||
} else {
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
if(h && !h->appleWatchAppInstalled())
|
||||
h->startWorkout(bluetoothManager->device()->deviceType());
|
||||
#endif
|
||||
#endif
|
||||
if (bluetoothManager->device() && send_event_to_device) {
|
||||
bluetoothManager->device()->start();
|
||||
}
|
||||
@@ -4117,6 +4127,13 @@ void homeform::Stop() {
|
||||
|
||||
m_startRequested = false;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#ifndef IO_UNDER_QT
|
||||
if(h && !h->appleWatchAppInstalled())
|
||||
h->stopWorkout();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
qDebug() << QStringLiteral("Stop pressed - paused") << paused << QStringLiteral("stopped") << stopped;
|
||||
|
||||
if (stopped) {
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
#include <QQuickItemGrabResult>
|
||||
#include <QTextToSpeech>
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#include "ios/lockscreen.h"
|
||||
#endif
|
||||
#ifdef Q_OS_ANDROID
|
||||
|
||||
#include <QAndroidJniEnvironment>
|
||||
#include <QtAndroid>
|
||||
#endif
|
||||
@@ -808,6 +810,9 @@ class homeform : public QObject {
|
||||
bool floating_open = false;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
lockscreen *h = nullptr;
|
||||
#endif
|
||||
bool m_locationServices = true;
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
|
||||
@@ -17,8 +17,11 @@ class ViewController: UIViewController {
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
WorkoutTracking.shared.authorizeHealthKit()
|
||||
WorkoutTracking.shared.observerHeartRateSamples()
|
||||
if #available(iOS 17.0, *) {
|
||||
WorkoutTracking.authorizeHealthKit()
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
WatchKitConnection.shared.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,88 @@
|
||||
//
|
||||
// WorkoutTracking.swift
|
||||
// ElecDemo
|
||||
// WatchWorkoutTracking.swift
|
||||
// ElecDemo WatchKit Extension
|
||||
//
|
||||
// Created by NhatHM on 8/12/19.
|
||||
// Copyright © 2019 GST.PID. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import HealthKit
|
||||
|
||||
protocol WorkoutTrackingProtocol {
|
||||
func authorizeHealthKit()
|
||||
func observerHeartRateSamples()
|
||||
let SwiftDebug = swiftDebug()
|
||||
|
||||
protocol WorkoutTrackingDelegate: class {
|
||||
func didReceiveHealthKitHeartRate(_ heartRate: Double)
|
||||
func didReceiveHealthKitStepCounts(_ stepCounts: Double)
|
||||
func didReceiveHealthKitStepCadence(_ stepCadence: Double)
|
||||
func didReceiveHealthKitDistanceCycling(_ distanceCycling: Double)
|
||||
func didReceiveHealthKitActiveEnergyBurned(_ activeEnergyBurned: Double)
|
||||
}
|
||||
|
||||
class WorkoutTracking {
|
||||
protocol WorkoutTrackingProtocol {
|
||||
static func authorizeHealthKit()
|
||||
func startWorkOut(deviceType: UInt16)
|
||||
func stopWorkOut()
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@objc class WorkoutTracking: NSObject {
|
||||
static let shared = WorkoutTracking()
|
||||
public static var lastDateMetric = Date()
|
||||
public static var lastStepCount = Double()
|
||||
public static var distance = Double()
|
||||
public static var kcal = Double()
|
||||
public static var steps = Double()
|
||||
var sport: Int = 0
|
||||
let healthStore = HKHealthStore()
|
||||
var observerQuery: HKObserverQuery!
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
var workoutBuilder: HKWorkoutBuilder!
|
||||
var workoutInProgress: Bool = false
|
||||
|
||||
init() {
|
||||
weak var delegate: WorkoutTrackingDelegate?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
extension WorkoutTracking {
|
||||
func setSport(_ sport: Int) {
|
||||
self.sport = sport
|
||||
}
|
||||
|
||||
private func configWorkout() {
|
||||
var activityType = HKWorkoutActivityType.cycling
|
||||
if self.sport == 1 {
|
||||
activityType = HKWorkoutActivityType.running
|
||||
} else if self.sport == 2 {
|
||||
activityType = HKWorkoutActivityType.cycling
|
||||
} else if self.sport == 3 {
|
||||
activityType = HKWorkoutActivityType.rowing
|
||||
} else if self.sport == 4 {
|
||||
activityType = HKWorkoutActivityType.elliptical
|
||||
}
|
||||
|
||||
configuration.activityType = activityType
|
||||
configuration.locationType = .indoor
|
||||
|
||||
do {
|
||||
workoutBuilder = try HKWorkoutBuilder(healthStore: healthStore, configuration: configuration, device: .local())
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
func authorizeHealthKit() {
|
||||
|
||||
@objc static func requestAuth() {
|
||||
authorizeHealthKit()
|
||||
}
|
||||
|
||||
@objc public static func authorizeHealthKit() {
|
||||
if HKHealthStore.isHealthDataAvailable() {
|
||||
let infoToRead = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
@@ -33,86 +92,365 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
|
||||
let infoToShare = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
var infoToShare: Set<HKSampleType> = []
|
||||
|
||||
healthStore.requestAuthorization(toShare: infoToShare, read: infoToRead) { (success, error) in
|
||||
if success {
|
||||
print("Authorization healthkit success")
|
||||
} else if let error = error {
|
||||
print(error)
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
infoToShare = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingPower)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingSpeed)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingCadence)!,
|
||||
HKSampleType.quantityType(forIdentifier: .runningPower)!,
|
||||
HKSampleType.quantityType(forIdentifier: .runningSpeed)!,
|
||||
HKSampleType.quantityType(forIdentifier: .runningStrideLength)!,
|
||||
HKSampleType.quantityType(forIdentifier: .runningVerticalOscillation)!,
|
||||
HKSampleType.quantityType(forIdentifier: .walkingSpeed)!,
|
||||
HKSampleType.quantityType(forIdentifier: .walkingStepLength)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
infoToShare = Set([
|
||||
HKSampleType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKSampleType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
HKHealthStore().requestAuthorization(toShare: infoToShare, read: infoToRead) { (success, error) in
|
||||
if success {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: Authorization healthkit success")
|
||||
} else if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
print("HealthKit not available")
|
||||
SwiftDebug.qtDebug("WorkoutTracking: HealthKit not avaiable")
|
||||
}
|
||||
}
|
||||
|
||||
func observerHeartRateSamples() {
|
||||
guard let heartRateSampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
|
||||
return
|
||||
@objc func startWorkOut(deviceType: UInt16) {
|
||||
if(workoutInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let observerQuery = observerQuery {
|
||||
healthStore.stop(observerQuery)
|
||||
}
|
||||
|
||||
observerQuery = HKObserverQuery(sampleType: heartRateSampleType, predicate: nil) { [unowned self] (_, _, error) in
|
||||
WorkoutTracking.authorizeHealthKit()
|
||||
workoutInProgress = true;
|
||||
WorkoutTracking.lastDateMetric = Date()
|
||||
SwiftDebug.qtDebug("WorkoutTracking: Start workout")
|
||||
setSport(Int(deviceType))
|
||||
configWorkout()
|
||||
workoutBuilder.beginCollection(withStart: Date()) { (success, error) in
|
||||
SwiftDebug.qtDebug(success.description)
|
||||
if let error = error {
|
||||
print("Error: \(error.localizedDescription)")
|
||||
return
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func stopWorkOut() {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: Stop workout")
|
||||
|
||||
guard let quantityType = HKQuantityType.quantityType(
|
||||
forIdentifier: .activeEnergyBurned) else {
|
||||
return
|
||||
}
|
||||
|
||||
let unit = HKUnit.kilocalorie()
|
||||
let totalEnergyBurned = WorkoutTracking.kcal
|
||||
let quantity = HKQuantity(unit: unit,
|
||||
doubleValue: totalEnergyBurned)
|
||||
|
||||
let sample = HKCumulativeQuantitySeriesSample(type: quantityType,
|
||||
quantity: quantity,
|
||||
start: workoutBuilder.startDate!,
|
||||
end: Date())
|
||||
|
||||
workoutBuilder.add([sample]) {(success, error) in}
|
||||
|
||||
let unitDistance = HKUnit.mile()
|
||||
let miles = WorkoutTracking.distance * 0.000621371
|
||||
let quantityMiles = HKQuantity(unit: unitDistance,
|
||||
doubleValue: miles)
|
||||
|
||||
if(sport == 2) {
|
||||
|
||||
guard let quantityTypeDistance = HKQuantityType.quantityType(
|
||||
forIdentifier: .distanceCycling) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.fetchLatestHeartRateSample { (sample) in
|
||||
guard let sample = sample else {
|
||||
|
||||
let sampleDistance = HKCumulativeQuantitySeriesSample(type: quantityTypeDistance,
|
||||
quantity: quantityMiles,
|
||||
start: workoutBuilder.startDate!,
|
||||
end: Date())
|
||||
|
||||
workoutBuilder.add([sampleDistance]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
self.workoutBuilder.finishWorkout{ (workout, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
workout?.setValue(quantityMiles, forKey: "totalDistance")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Guard to check if steps quantity type is available
|
||||
guard let quantityTypeSteps = HKQuantityType.quantityType(
|
||||
forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
|
||||
let stepsQuantity = HKQuantity(unit: HKUnit.count(), doubleValue: Double(WorkoutTracking.steps))
|
||||
|
||||
// Create a sample for total steps
|
||||
let sampleSteps = HKCumulativeQuantitySeriesSample(
|
||||
type: quantityTypeSteps,
|
||||
quantity: stepsQuantity, // Use your steps quantity here
|
||||
start: workoutBuilder.startDate!,
|
||||
end: Date())
|
||||
|
||||
// Guard to check if distance quantity type is available
|
||||
guard let quantityTypeDistance = HKQuantityType.quantityType(
|
||||
forIdentifier: .distanceWalkingRunning) else {
|
||||
return
|
||||
}
|
||||
|
||||
let sampleDistance = HKCumulativeQuantitySeriesSample(
|
||||
type: quantityTypeDistance,
|
||||
quantity: quantityMiles,
|
||||
start: workoutBuilder.startDate!,
|
||||
end: Date())
|
||||
|
||||
// Add both samples to the workout builder
|
||||
workoutBuilder.add([sampleSteps, sampleDistance]) { (success, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
|
||||
let heartRate = sample.quantity.doubleValue(for: heartRateUnit)
|
||||
print("Heart Rate Sample: \(heartRate)")
|
||||
//LocalNotificationHelper.fireHeartRate(heartRate)
|
||||
// End the data collection - only do this once
|
||||
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
// Finish the workout - only do this once
|
||||
self.workoutBuilder.finishWorkout { (workout, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
// No need to manually set values - the builder has added the samples
|
||||
// and the workout now has steps and distance metrics built in
|
||||
|
||||
// You can access the data if needed:
|
||||
if let workout = workout {
|
||||
// Here you can use the workout as needed
|
||||
// The steps and distance are now part of the workout's statistics
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
healthStore.execute(observerQuery)
|
||||
healthStore.enableBackgroundDelivery(for: heartRateSampleType, frequency: .immediate) { (success, error) in
|
||||
print(success)
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
workoutInProgress = false;
|
||||
}
|
||||
|
||||
@objc func addMetrics(power: Double, cadence: Double, speed: Double, kcal: Double, steps: Double, deviceType: UInt8, distance: Double) {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: GET DATA: \(Date())")
|
||||
|
||||
if(workoutInProgress == false && power > 0) {
|
||||
startWorkOut(deviceType: UInt16(deviceType))
|
||||
} else if(workoutInProgress == false && power == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Speed = speed / 100;
|
||||
|
||||
WorkoutTracking.kcal = kcal
|
||||
WorkoutTracking.steps = steps
|
||||
WorkoutTracking.distance = distance
|
||||
|
||||
if(sport == 2) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
let wattPerInterval = HKQuantity(unit: HKUnit.watt(),
|
||||
doubleValue: power)
|
||||
|
||||
if(WorkoutTracking.lastDateMetric.distance(to: Date()) < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
guard let powerType = HKQuantityType.quantityType(
|
||||
forIdentifier: .cyclingPower) else {
|
||||
return
|
||||
}
|
||||
let wattPerIntervalSample = HKQuantitySample(type: powerType,
|
||||
quantity: wattPerInterval,
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
workoutBuilder.add([wattPerIntervalSample]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
let cadencePerInterval = HKQuantity(unit: HKUnit.count().unitDivided(by: HKUnit.second()),
|
||||
doubleValue: cadence / 60.0)
|
||||
|
||||
guard let cadenceType = HKQuantityType.quantityType(
|
||||
forIdentifier: .cyclingCadence) else {
|
||||
return
|
||||
}
|
||||
let cadencePerIntervalSample = HKQuantitySample(type: cadenceType,
|
||||
quantity: cadencePerInterval,
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
workoutBuilder.add([cadencePerIntervalSample]) {(success, error) in
|
||||
if success {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: OK")
|
||||
}
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
let speedPerInterval = HKQuantity(unit: HKUnit.meter().unitDivided(by: HKUnit.second()),
|
||||
doubleValue: (Speed / 3.6))
|
||||
|
||||
guard let speedType = HKQuantityType.quantityType(
|
||||
forIdentifier: .cyclingSpeed) else {
|
||||
return
|
||||
}
|
||||
let speedPerIntervalSample = HKQuantitySample(type: speedType,
|
||||
quantity: speedPerInterval,
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
workoutBuilder.add([speedPerIntervalSample]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
} else if(sport == 1) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
// Guard to check if steps quantity type is available
|
||||
guard let quantityTypeSteps = HKQuantityType.quantityType(
|
||||
forIdentifier: .stepCount) else {
|
||||
return
|
||||
}
|
||||
|
||||
let stepsQuantity = HKQuantity(unit: HKUnit.count(), doubleValue: Double(WorkoutTracking.steps - WorkoutTracking.lastStepCount))
|
||||
|
||||
// Create a sample for total steps
|
||||
let sampleSteps = HKCumulativeQuantitySeriesSample(
|
||||
type: quantityTypeSteps,
|
||||
quantity: stepsQuantity, // Use your steps quantity here
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
|
||||
// Add the steps sample to workout builder
|
||||
workoutBuilder.add([sampleSteps]) { (success, error) in
|
||||
if let error = error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
let wattPerInterval = HKQuantity(unit: HKUnit.watt(),
|
||||
doubleValue: power)
|
||||
|
||||
if(WorkoutTracking.lastDateMetric.distance(to: Date()) < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
guard let powerType = HKQuantityType.quantityType(
|
||||
forIdentifier: .runningPower) else {
|
||||
return
|
||||
}
|
||||
let wattPerIntervalSample = HKQuantitySample(type: powerType,
|
||||
quantity: wattPerInterval,
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
workoutBuilder.add([wattPerIntervalSample]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
let speedPerInterval = HKQuantity(unit: HKUnit.meter().unitDivided(by: HKUnit.second()),
|
||||
doubleValue: Speed * 0.277778)
|
||||
|
||||
guard let speedType = HKQuantityType.quantityType(
|
||||
forIdentifier: .runningSpeed) else {
|
||||
return
|
||||
}
|
||||
let speedPerIntervalSample = HKQuantitySample(type: speedType,
|
||||
quantity: speedPerInterval,
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
workoutBuilder.add([speedPerIntervalSample]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}/* else if(sport == 2) {
|
||||
if #available(watchOSApplicationExtension 10.0, *) {
|
||||
let speedPerInterval = HKQuantity(unit: HKUnit.meter().unitDivided(by: HKUnit.second()),
|
||||
doubleValue: Speed * 0.277778)
|
||||
|
||||
guard let speedType = HKQuantityType.quantityType(
|
||||
forIdentifier: .walkingSpeed) else {
|
||||
return
|
||||
}
|
||||
let speedPerIntervalSample = HKQuantitySample(type: speedType,
|
||||
quantity: speedPerInterval,
|
||||
start: WorkoutTracking.lastDateMetric,
|
||||
end: Date())
|
||||
workoutBuilder.add([speedPerIntervalSample]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}*/
|
||||
|
||||
// TODO HANDLE WALKING, ROWING AND ELLIPTICAL
|
||||
|
||||
WorkoutTracking.lastDateMetric = Date()
|
||||
WorkoutTracking.lastStepCount = steps
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutTracking {
|
||||
private func fetchLatestHeartRateSample(completionHandler: @escaping (_ sample: HKQuantitySample?) -> Void) {
|
||||
guard let sampleType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
let predicate = HKQuery.predicateForSamples(withStart: Date.distantPast, end: Date(), options: .strictEndDate)
|
||||
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
|
||||
let query = HKSampleQuery(sampleType: sampleType,
|
||||
predicate: predicate,
|
||||
limit: Int(HKObjectQueryNoLimit),
|
||||
sortDescriptors: [sortDescriptor]) { (_, results, error) in
|
||||
if let error = error {
|
||||
print("Error: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
completionHandler(results?[0] as? HKQuantitySample)
|
||||
}
|
||||
|
||||
healthStore.execute(query)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
class lockscreen {
|
||||
public:
|
||||
bool appleWatchAppInstalled();
|
||||
void setTimerDisabled();
|
||||
void request();
|
||||
long heartRate();
|
||||
@@ -15,6 +16,10 @@ class lockscreen {
|
||||
void setSpeed(double speed);
|
||||
void setPower(double power);
|
||||
void setCadence(double cadence);
|
||||
void startWorkout(unsigned short deviceType);
|
||||
void stopWorkout();
|
||||
|
||||
void workoutTrackingUpdate(double speed, unsigned short cadence, unsigned short watt, unsigned short currentCalories, unsigned long long currentSteps, unsigned char deviceType, double distance);
|
||||
|
||||
// virtualbike
|
||||
void virtualbike_ios();
|
||||
@@ -28,7 +33,7 @@ class lockscreen {
|
||||
double virtualbike_getPowerRequested();
|
||||
bool virtualbike_updateFTMS(unsigned short normalizeSpeed, unsigned char currentResistance,
|
||||
unsigned short currentCadence, unsigned short currentWatt,
|
||||
unsigned short CrankRevolutions, unsigned short LastCrankEventTime, signed short Gears);
|
||||
unsigned short CrankRevolutions, unsigned short LastCrankEventTime, signed short Gears, unsigned short currentCalories, unsigned int Distance);
|
||||
int virtualbike_getLastFTMSMessage(unsigned char *message);
|
||||
|
||||
// virtualrower
|
||||
@@ -38,7 +43,7 @@ class lockscreen {
|
||||
unsigned short currentCadence, unsigned short currentWatt,
|
||||
unsigned short CrankRevolutions, unsigned short LastCrankEventTime,
|
||||
unsigned short StrokesCount, unsigned int Distance, unsigned short KCal,
|
||||
unsigned short Pace);
|
||||
unsigned short Pace, unsigned short currentCalories);
|
||||
int virtualrower_getLastFTMSMessage(unsigned char *message);
|
||||
|
||||
// virtualtreadmill
|
||||
@@ -49,7 +54,8 @@ class lockscreen {
|
||||
double virtualtreadmill_getPowerRequested();
|
||||
bool virtualtreadmill_updateFTMS(unsigned short normalizeSpeed, unsigned char currentResistance,
|
||||
unsigned short currentCadence, unsigned short currentWatt,
|
||||
unsigned short currentInclination, unsigned long long currentDistance);
|
||||
unsigned short currentInclination, unsigned long long currentDistance, unsigned short currentCalories,
|
||||
qint32 currentSteps);
|
||||
|
||||
// volume
|
||||
double getVolume();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#import <ConnectIQ/ConnectIQ.h>
|
||||
#import "qdomyoszwift-Swift2.h"
|
||||
#include "ios/lockscreen.h"
|
||||
#include "devices/bluetoothdevice.h"
|
||||
#include <QDebug>
|
||||
#include "ios/AdbClient.h"
|
||||
#include "ios/ios_eliteariafan.h"
|
||||
@@ -22,6 +23,7 @@ static virtualbike_ios_swift* _virtualbike = nil;
|
||||
static virtualbike_zwift* _virtualbike_zwift = nil;
|
||||
static virtualrower* _virtualrower = nil;
|
||||
static virtualtreadmill_zwift* _virtualtreadmill_zwift = nil;
|
||||
static WorkoutTracking* workoutTracking = nil;
|
||||
|
||||
static GarminConnect* Garmin = 0;
|
||||
|
||||
@@ -33,6 +35,36 @@ static zwift_protobuf_layer* zwiftProtobufLayer = nil;
|
||||
|
||||
static NSString* profile_selected;
|
||||
|
||||
bool lockscreen::appleWatchAppInstalled() {
|
||||
if ([WCSession isSupported]) {
|
||||
// Get the default session
|
||||
WCSession *session = [WCSession defaultSession];
|
||||
|
||||
// Activate the session
|
||||
[session activateSession];
|
||||
|
||||
// Check if a watch is paired and the app is installed
|
||||
if (session.isPaired && session.isWatchAppInstalled) {
|
||||
// An Apple Watch is paired and has the companion app installed
|
||||
qDebug() << "Apple Watch is paired and app is installed";
|
||||
return true;
|
||||
} else if (session.isPaired) {
|
||||
// An Apple Watch is paired but doesn't have the companion app
|
||||
qDebug() << "Apple Watch is paired but app is not installed";
|
||||
return false;
|
||||
} else {
|
||||
// No Apple Watch is paired
|
||||
qDebug() << "No Apple Watch is paired";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// This device doesn't support Watch connectivity
|
||||
qDebug() << "Watch connectivity is not supported on this device";
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void lockscreen::setTimerDisabled() {
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
|
||||
}
|
||||
@@ -45,7 +77,22 @@ void lockscreen::request()
|
||||
if (@available(iOS 13, *)) {
|
||||
Garmin = [[GarminConnect alloc] init];
|
||||
}
|
||||
_adb = [[AdbClient alloc] initWithVerbose:YES];
|
||||
// just to be sure, I built the library for iOS17 only but theorically we can use any iOS version
|
||||
if (@available(iOS 17, *)) {
|
||||
workoutTracking = [[WorkoutTracking alloc] init];
|
||||
[WorkoutTracking requestAuth];
|
||||
_adb = [[AdbClient alloc] initWithVerbose:YES];
|
||||
}
|
||||
}
|
||||
|
||||
void lockscreen::startWorkout(unsigned short deviceType) {
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking startWorkOutWithDeviceType:deviceType];
|
||||
}
|
||||
|
||||
void lockscreen::stopWorkout() {
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking stopWorkOut];
|
||||
}
|
||||
|
||||
long lockscreen::heartRate()
|
||||
@@ -106,6 +153,11 @@ void lockscreen::virtualbike_setCadence(unsigned short crankRevolutions, unsigne
|
||||
[_virtualbike updateCadenceWithCrankRevolutions:crankRevolutions LastCrankEventTime:lastCrankEventTime];
|
||||
}
|
||||
|
||||
void lockscreen::workoutTrackingUpdate(double speed, unsigned short cadence, unsigned short watt, unsigned short currentCalories, unsigned long long currentSteps, unsigned char deviceType, double currentDistance) {
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:watt cadence:cadence*2 speed:speed * 100 kcal:currentCalories steps:currentSteps deviceType:deviceType distance:currentDistance];
|
||||
}
|
||||
|
||||
void lockscreen::virtualbike_zwift_ios(bool disable_hr, bool garmin_bluetooth_compatibility, bool zwift_play_emulator, bool watt_bike_emulator)
|
||||
{
|
||||
_virtualbike_zwift = [[virtualbike_zwift alloc] initWithDisable_hr:disable_hr garmin_bluetooth_compatibility:garmin_bluetooth_compatibility zwift_play_emulator:zwift_play_emulator watt_bike_emulator:watt_bike_emulator];
|
||||
@@ -152,15 +204,21 @@ double lockscreen::virtualbike_getPowerRequested()
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool lockscreen::virtualbike_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 CrankRevolutions, UInt16 LastCrankEventTime, signed short Gears)
|
||||
bool lockscreen::virtualbike_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 CrankRevolutions, UInt16 LastCrankEventTime, signed short Gears, UInt16 currentCalories, UInt32 Distance)
|
||||
{
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:currentCalories steps:0 deviceType: bluetoothdevice::BLUETOOTH_TYPE::BIKE distance:Distance];
|
||||
|
||||
if(_virtualbike_zwift != nil)
|
||||
return [_virtualbike_zwift updateFTMSWithNormalizeSpeed:normalizeSpeed currentCadence:currentCadence currentResistance:currentResistance currentWatt:currentWatt CrankRevolutions:CrankRevolutions LastCrankEventTime:LastCrankEventTime Gears:Gears];
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool lockscreen::virtualrower_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 CrankRevolutions, UInt16 LastCrankEventTime, UInt16 StrokesCount, UInt32 Distance, UInt16 KCal, UInt16 Pace)
|
||||
bool lockscreen::virtualrower_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 CrankRevolutions, UInt16 LastCrankEventTime, UInt16 StrokesCount, UInt32 Distance, UInt16 KCal, UInt16 Pace, UInt16 currentCalories)
|
||||
{
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:currentCalories steps:0 deviceType: bluetoothdevice::BLUETOOTH_TYPE::ROWING distance:Distance];
|
||||
|
||||
if(_virtualrower != nil)
|
||||
return [_virtualrower updateFTMSWithNormalizeSpeed:normalizeSpeed currentCadence:currentCadence currentResistance:currentResistance currentWatt:currentWatt CrankRevolutions:CrankRevolutions LastCrankEventTime:LastCrankEventTime StrokesCount:StrokesCount Distance:Distance KCal:KCal Pace:Pace];
|
||||
return 0;
|
||||
@@ -212,8 +270,11 @@ double lockscreen::virtualtreadmill_getPowerRequested()
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool lockscreen::virtualtreadmill_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 currentInclination, UInt64 currentDistance)
|
||||
bool lockscreen::virtualtreadmill_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 currentInclination, UInt64 currentDistance, UInt16 currentCalories, qint32 currentSteps)
|
||||
{
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:currentCalories steps:currentSteps deviceType:bluetoothdevice::BLUETOOTH_TYPE::TREADMILL distance:currentDistance];
|
||||
|
||||
if(_virtualtreadmill_zwift != nil)
|
||||
return [_virtualtreadmill_zwift updateFTMSWithNormalizeSpeed:normalizeSpeed currentCadence:currentCadence currentResistance:currentResistance currentWatt:currentWatt currentInclination:currentInclination currentDistance:currentDistance];
|
||||
return 0;
|
||||
|
||||
@@ -1379,7 +1379,7 @@ void virtualbike::bikeProvider() {
|
||||
// really connected to a device
|
||||
if (h->virtualbike_updateFTMS(normalizeSpeed, (char)Bike->currentResistance().value(),
|
||||
(uint16_t)Bike->currentCadence().value() * 2, (uint16_t)normalizeWattage,
|
||||
Bike->currentCrankRevolutions(), Bike->lastCrankEventTime(), ((bike*)Bike)->gears())) {
|
||||
Bike->currentCrankRevolutions(), Bike->lastCrankEventTime(), ((bike*)Bike)->gears(), Bike->calories().value(), Bike->odometer() * 1000.0)) {
|
||||
h->virtualbike_setHeartRate(Bike->currentHeart().value());
|
||||
|
||||
uint8_t ftms_message[255];
|
||||
|
||||
@@ -347,7 +347,7 @@ void virtualrower::rowerProvider() {
|
||||
normalizeSpeed, (char)Rower->currentResistance().value(), (uint16_t)Rower->currentCadence().value() * 2,
|
||||
(uint16_t)normalizeWattage, Rower->currentCrankRevolutions(), Rower->lastCrankEventTime(),
|
||||
((rower *)Rower)->currentStrokesCount().value(), Rower->odometer() * 1000, Rower->calories().value(),
|
||||
QTime(0, 0, 0).secsTo(((rower *)Rower)->currentPace()))) {
|
||||
QTime(0, 0, 0).secsTo(((rower *)Rower)->currentPace()), Rower->calories().value())) {
|
||||
h->virtualrower_setHeartRate(Rower->currentHeart().value());
|
||||
|
||||
uint8_t ftms_message[255];
|
||||
|
||||
@@ -546,7 +546,8 @@ void virtualtreadmill::treadmillProvider() {
|
||||
if (h->virtualtreadmill_updateFTMS(
|
||||
normalizeSpeed, 0, (uint16_t)((treadmill *)treadMill)->currentCadence().value() * cadence_multiplier,
|
||||
(uint16_t)((treadmill *)treadMill)->wattsMetric().value(),
|
||||
inclination * 10, (uint64_t)(((treadmill *)treadMill)->odometer() * 1000.0))) {
|
||||
inclination * 10, (uint64_t)(((treadmill *)treadMill)->odometer() * 1000.0), ((treadmill *)treadMill)->calories().value(),
|
||||
((treadmill *)treadMill)->currentStepCount().value())) {
|
||||
h->virtualtreadmill_setHeartRate(((treadmill *)treadMill)->currentHeart().value());
|
||||
lastSlopeChanged = h->virtualtreadmill_lastChangeCurrentSlope();
|
||||
if ((uint64_t)QDateTime::currentSecsSinceEpoch() < lastSlopeChanged + slopeTimeoutSecs)
|
||||
|
||||
Reference in New Issue
Block a user