mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
13 Commits
ant-remote
...
build-1148
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e14cc9912 | ||
|
|
d510d44657 | ||
|
|
c8a76ab9c7 | ||
|
|
aa5241e3d1 | ||
|
|
db22ee0d9b | ||
|
|
2321838f46 | ||
|
|
37aab5ab8d | ||
|
|
fdc83d812d | ||
|
|
4284dcb6b4 | ||
|
|
542245696d | ||
|
|
97bd9f03e5 | ||
|
|
9e3b307d01 | ||
|
|
a1b313081e |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
src/qdomyos-zwift.pro.user
|
||||
|
||||
.idea/
|
||||
|
||||
src/Makefile
|
||||
|
||||
@@ -4445,7 +4445,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1147;
|
||||
CURRENT_PROJECT_VERSION = 1148;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
|
||||
@@ -4641,7 +4641,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1147;
|
||||
CURRENT_PROJECT_VERSION = 1148;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4873,7 +4873,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1147;
|
||||
CURRENT_PROJECT_VERSION = 1148;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -4969,7 +4969,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1147;
|
||||
CURRENT_PROJECT_VERSION = 1148;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -5061,7 +5061,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1147;
|
||||
CURRENT_PROJECT_VERSION = 1148;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -5177,7 +5177,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1147;
|
||||
CURRENT_PROJECT_VERSION = 1148;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
|
||||
@@ -23,6 +23,7 @@ class WatchKitConnection: NSObject {
|
||||
static let shared = WatchKitConnection()
|
||||
public static var distance = 0.0
|
||||
public static var kcal = 0.0
|
||||
public static var totalKcal = 0.0
|
||||
public static var stepCadence = 0
|
||||
public static var speed = 0.0
|
||||
public static var cadence = 0.0
|
||||
@@ -70,6 +71,9 @@ extension WatchKitConnection: WatchKitConnectionProtocol {
|
||||
WatchKitConnection.distance = dDistance
|
||||
let dKcal = Double(result["kcal"] as! Double)
|
||||
WatchKitConnection.kcal = dKcal
|
||||
if let totalKcalDouble = result["totalKcal"] as? Double {
|
||||
WatchKitConnection.totalKcal = totalKcalDouble
|
||||
}
|
||||
|
||||
let dSpeed = Double(result["speed"] as! Double)
|
||||
WatchKitConnection.speed = dSpeed
|
||||
|
||||
@@ -28,6 +28,7 @@ class WorkoutTracking: NSObject {
|
||||
static let shared = WorkoutTracking()
|
||||
public static var distance = Double()
|
||||
public static var kcal = Double()
|
||||
public static var totalKcal = Double()
|
||||
public static var cadenceTimeStamp = NSDate().timeIntervalSince1970
|
||||
public static var cadenceLastSteps = Double()
|
||||
public static var cadenceSteps = 0
|
||||
@@ -166,6 +167,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .basalEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingPower)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingSpeed)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingCadence)!,
|
||||
@@ -185,6 +187,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .basalEnergyBurned)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
}
|
||||
@@ -223,25 +226,30 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
workoutSession.stopActivity(with: Date())
|
||||
workoutSession.end()
|
||||
|
||||
guard let quantityType = HKQuantityType.quantityType(
|
||||
// Write active calories
|
||||
guard let activeQuantityType = HKQuantityType.quantityType(
|
||||
forIdentifier: .activeEnergyBurned) else {
|
||||
return
|
||||
}
|
||||
|
||||
let unit = HKUnit.kilocalorie()
|
||||
let totalEnergyBurned = WorkoutTracking.kcal
|
||||
let quantity = HKQuantity(unit: unit,
|
||||
doubleValue: totalEnergyBurned)
|
||||
let activeEnergyBurned = WorkoutTracking.kcal
|
||||
let activeQuantity = HKQuantity(unit: unit,
|
||||
doubleValue: activeEnergyBurned)
|
||||
|
||||
let startDate = workoutSession.startDate ?? WorkoutTracking.lastDateMetric
|
||||
|
||||
let sample = HKCumulativeQuantitySeriesSample(type: quantityType,
|
||||
quantity: quantity,
|
||||
start: startDate,
|
||||
end: Date())
|
||||
let activeSample = HKCumulativeQuantitySeriesSample(type: activeQuantityType,
|
||||
quantity: activeQuantity,
|
||||
start: startDate,
|
||||
end: Date())
|
||||
|
||||
workoutBuilder.add([sample]) {(success, error) in}
|
||||
|
||||
workoutBuilder.add([activeSample]) {(success, error) in
|
||||
if let error = error {
|
||||
print("WatchWorkoutTracking active calories: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
let unitDistance = HKUnit.mile()
|
||||
let miles = WorkoutTracking.distance
|
||||
let quantityMiles = HKQuantity(unit: unitDistance,
|
||||
@@ -273,6 +281,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
print(error)
|
||||
}
|
||||
workout?.setValue(quantityMiles, forKey: "totalDistance")
|
||||
// Set total energy burned on the workout
|
||||
let totalEnergy = WorkoutTracking.totalKcal > 0 ? WorkoutTracking.totalKcal : activeEnergyBurned
|
||||
let totalEnergyQuantity = HKQuantity(unit: unit, doubleValue: totalEnergy)
|
||||
workout?.setValue(totalEnergyQuantity, forKey: "totalEnergyBurned")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,6 +346,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
print(error)
|
||||
}
|
||||
workout?.setValue(quantityMiles, forKey: "totalDistance")
|
||||
// Set total energy burned on the workout
|
||||
let totalEnergy = WorkoutTracking.totalKcal > 0 ? WorkoutTracking.totalKcal : activeEnergyBurned
|
||||
let totalEnergyQuantity = HKQuantity(unit: unit, doubleValue: totalEnergy)
|
||||
workout?.setValue(totalEnergyQuantity, forKey: "totalEnergyBurned")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,6 +415,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
print(error)
|
||||
}
|
||||
workout?.setValue(quantityMiles, forKey: "totalDistance")
|
||||
// Set total energy burned on the workout
|
||||
let totalEnergy = WorkoutTracking.totalKcal > 0 ? WorkoutTracking.totalKcal : activeEnergyBurned
|
||||
let totalEnergyQuantity = HKQuantity(unit: unit, doubleValue: totalEnergy)
|
||||
workout?.setValue(totalEnergyQuantity, forKey: "totalEnergyBurned")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,27 @@ QTime bluetoothdevice::maxPace() {
|
||||
double bluetoothdevice::odometerFromStartup() { return Distance.valueRaw(); }
|
||||
double bluetoothdevice::odometer() { return Distance.value(); }
|
||||
double bluetoothdevice::lapOdometer() { return Distance.lapValue(); }
|
||||
metric bluetoothdevice::calories() { return KCal; }
|
||||
metric bluetoothdevice::calories() {
|
||||
QSettings settings;
|
||||
bool activeOnly = settings.value(QZSettings::calories_active_only, QZSettings::default_calories_active_only).toBool();
|
||||
|
||||
if (activeOnly) {
|
||||
activeKCal.setValue(metric::calculateActiveKCal(KCal.value(), elapsed.value()));
|
||||
return activeKCal;
|
||||
} else {
|
||||
// Return total calories
|
||||
return KCal;
|
||||
}
|
||||
}
|
||||
|
||||
metric bluetoothdevice::totalCalories() {
|
||||
return KCal;
|
||||
}
|
||||
|
||||
metric bluetoothdevice::activeCalories() {
|
||||
return activeKCal;
|
||||
}
|
||||
|
||||
metric bluetoothdevice::jouls() { return m_jouls; }
|
||||
uint8_t bluetoothdevice::fanSpeed() { return FanSpeed; };
|
||||
bool bluetoothdevice::changeFanSpeed(uint8_t speed) {
|
||||
@@ -254,7 +274,17 @@ void bluetoothdevice::update_hr_from_external() {
|
||||
#ifndef IO_UNDER_QT
|
||||
lockscreen h;
|
||||
long appleWatchHeartRate = h.heartRate();
|
||||
h.setKcal(KCal.value());
|
||||
QSettings settings;
|
||||
bool activeOnly = settings.value(QZSettings::calories_active_only, QZSettings::default_calories_active_only).toBool();
|
||||
|
||||
if (activeOnly) {
|
||||
// When active calories setting is enabled, send both total and active calories
|
||||
h.setKcal(calories().value()); // This will be active calories
|
||||
h.setTotalKcal(totalCalories().value()); // This will be total calories
|
||||
} else {
|
||||
// When disabled, send total calories as before
|
||||
h.setKcal(calories().value()); // This will be total calories
|
||||
}
|
||||
h.setDistance(Distance.value());
|
||||
h.setSpeed(Speed.value());
|
||||
h.setPower(m_watt.value());
|
||||
@@ -277,7 +307,7 @@ void bluetoothdevice::update_hr_from_external() {
|
||||
double kcal = calories().value();
|
||||
if(kcal < 0)
|
||||
kcal = 0;
|
||||
h.workoutTrackingUpdate(Speed.value(), Cadence.value(), (uint16_t)m_watt.value(), kcal, StepCount.value(), deviceType(), odometer() * 1000.0);
|
||||
h.workoutTrackingUpdate(Speed.value(), Cadence.value(), (uint16_t)m_watt.value(), kcal, StepCount.value(), deviceType(), odometer() * 1000.0, totalCalories().value());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -108,11 +108,18 @@ class bluetoothdevice : public QObject {
|
||||
|
||||
/**
|
||||
* @brief calories Gets a metric object to get and set the amount of energy expended.
|
||||
* Default implementation returns the protected KCal property. Units: kcal
|
||||
* Default implementation returns the protected KCal property, potentially adjusted for active calories. Units: kcal
|
||||
* Other implementations could have different units.
|
||||
* @return
|
||||
*/
|
||||
virtual metric calories();
|
||||
virtual metric activeCalories();
|
||||
|
||||
/**
|
||||
* @brief totalCalories Gets total calories (including BMR) regardless of active calories setting.
|
||||
* @return Total calories metric
|
||||
*/
|
||||
virtual metric totalCalories();
|
||||
|
||||
/**
|
||||
* @brief jouls Gets a metric object to get and set the number of joules expended. Units: joules
|
||||
@@ -548,6 +555,7 @@ class bluetoothdevice : public QObject {
|
||||
* @brief KCal The number of kilocalories expended in the session. Units: kcal
|
||||
*/
|
||||
metric KCal;
|
||||
metric activeKCal;
|
||||
|
||||
/**
|
||||
* @brief Speed The simulated speed of the device. Units: km/h
|
||||
|
||||
@@ -5204,8 +5204,9 @@ void homeform::update() {
|
||||
QString::number((bluetoothManager->device())->currentSpeed().max() * unit_conversion, 'f', 1));
|
||||
heart->setValue(QString::number(bluetoothManager->device()->currentHeart().value(), 'f', 0));
|
||||
|
||||
bool activeOnly = settings.value(QZSettings::calories_active_only, QZSettings::default_calories_active_only).toBool();
|
||||
calories->setValue(QString::number(bluetoothManager->device()->calories().value(), 'f', 0));
|
||||
calories->setSecondLine(QString::number(bluetoothManager->device()->calories().rate1s() * 60.0, 'f', 1) +
|
||||
calories->setSecondLine(QString::number((activeOnly ? bluetoothManager->device()->activeCalories().rate1s() : bluetoothManager->device()->calories().rate1s()) * 60.0, 'f', 1) +
|
||||
" /min");
|
||||
if (!settings.value(QZSettings::fitmetria_fanfit_enable, QZSettings::default_fitmetria_fanfit_enable).toBool())
|
||||
fan->setValue(QString::number(bluetoothManager->device()->fanSpeed()));
|
||||
@@ -7207,9 +7208,13 @@ void homeform::update() {
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
if (iphone_socket && iphone_socket->state() == QAbstractSocket::ConnectedState) {
|
||||
QSettings mdns_settings;
|
||||
bool activeOnly = mdns_settings.value(QZSettings::calories_active_only, QZSettings::default_calories_active_only).toBool();
|
||||
|
||||
QString toSend =
|
||||
"SENDER=PAD#HR=" + QString::number(bluetoothManager->device()->currentHeart().value()) +
|
||||
"#KCAL=" + QString::number(bluetoothManager->device()->calories().value()) +
|
||||
(activeOnly ? "#TOTALKCAL=" + QString::number(bluetoothManager->device()->totalCalories().value()) : "") +
|
||||
"#BCAD=" + QString::number(bluetoothManager->device()->currentCadence().value()) +
|
||||
"#SPD=" + QString::number(bluetoothManager->device()->currentSpeed().value()) +
|
||||
"#PWR=" + QString::number(bluetoothManager->device()->wattsMetric().value()) +
|
||||
|
||||
@@ -92,6 +92,18 @@ var pedometer = CMPedometer()
|
||||
Server.server?.send(createString(sender: sender))
|
||||
}
|
||||
|
||||
@objc public func setTotalKcal(totalKcal: Double) -> Void
|
||||
{
|
||||
var sender: String
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
sender = "PAD"
|
||||
} else {
|
||||
sender = "PHONE"
|
||||
}
|
||||
WatchKitConnection.totalKcal = totalKcal;
|
||||
Server.server?.send(createString(sender: sender))
|
||||
}
|
||||
|
||||
@objc public func setCadence(cadence: Double) -> Void
|
||||
{
|
||||
var sender: String
|
||||
@@ -129,7 +141,7 @@ var pedometer = CMPedometer()
|
||||
}
|
||||
|
||||
func createString(sender: String) -> String {
|
||||
return "SENDER=\(sender)#HR=\(WatchKitConnection.currentHeartRate)#KCAL=\(WatchKitConnection.kcal)#BCAD=\(WatchKitConnection.cadence)#SPD=\(WatchKitConnection.speed)#PWR=\(WatchKitConnection.power)#CAD=\(WatchKitConnection.stepCadence)#ODO=\(WatchKitConnection.distance)#";
|
||||
return "SENDER=\(sender)#HR=\(WatchKitConnection.currentHeartRate)#KCAL=\(WatchKitConnection.kcal)#TOTALKCAL=\(WatchKitConnection.totalKcal)#BCAD=\(WatchKitConnection.cadence)#SPD=\(WatchKitConnection.speed)#PWR=\(WatchKitConnection.power)#CAD=\(WatchKitConnection.stepCadence)#ODO=\(WatchKitConnection.distance)#";
|
||||
}
|
||||
|
||||
@objc func updateHeartRate() {
|
||||
|
||||
@@ -26,6 +26,7 @@ class WatchKitConnection: NSObject {
|
||||
static var distance = 0.0
|
||||
static var stepCadence = 0
|
||||
static var kcal = 0.0
|
||||
static var totalKcal = 0.0
|
||||
static var speed = 0.0
|
||||
static var power = 0.0
|
||||
static var cadence = 0.0
|
||||
@@ -55,6 +56,11 @@ class WatchKitConnection: NSObject {
|
||||
WatchKitConnection.kcal = Kcal;
|
||||
}
|
||||
|
||||
public func setTotalKCal(TotalKcal: Double) -> Void
|
||||
{
|
||||
WatchKitConnection.totalKcal = TotalKcal;
|
||||
}
|
||||
|
||||
public func setDistance(Distance: Double) -> Void
|
||||
{
|
||||
WatchKitConnection.distance = Distance;
|
||||
|
||||
@@ -31,6 +31,7 @@ protocol WorkoutTrackingProtocol {
|
||||
public static var lastDateMetric = Date()
|
||||
public static var distance = Double()
|
||||
public static var kcal = Double()
|
||||
public static var totalKcal = Double()
|
||||
public static var steps = Double()
|
||||
var sport: Int = 0
|
||||
let healthStore = HKHealthStore()
|
||||
@@ -100,6 +101,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .basalEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingPower)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingSpeed)!,
|
||||
HKSampleType.quantityType(forIdentifier: .cyclingCadence)!,
|
||||
@@ -119,6 +121,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
|
||||
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
|
||||
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
|
||||
HKSampleType.quantityType(forIdentifier: .basalEnergyBurned)!,
|
||||
HKSampleType.workoutType()
|
||||
])
|
||||
}
|
||||
@@ -166,22 +169,27 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
guard let quantityType = HKQuantityType.quantityType(
|
||||
// Write active calories
|
||||
guard let activeQuantityType = HKQuantityType.quantityType(
|
||||
forIdentifier: .activeEnergyBurned) else {
|
||||
return
|
||||
}
|
||||
|
||||
let unit = HKUnit.kilocalorie()
|
||||
let totalEnergyBurned = WorkoutTracking.kcal
|
||||
let quantity = HKQuantity(unit: unit,
|
||||
doubleValue: totalEnergyBurned)
|
||||
let activeEnergyBurned = WorkoutTracking.kcal
|
||||
let activeQuantity = HKQuantity(unit: unit,
|
||||
doubleValue: activeEnergyBurned)
|
||||
|
||||
let sample = HKCumulativeQuantitySeriesSample(type: quantityType,
|
||||
quantity: quantity,
|
||||
start: startDate,
|
||||
end: Date())
|
||||
let activeSample = HKCumulativeQuantitySeriesSample(type: activeQuantityType,
|
||||
quantity: activeQuantity,
|
||||
start: startDate,
|
||||
end: Date())
|
||||
|
||||
workoutBuilder.add([sample]) {(success, error) in}
|
||||
workoutBuilder.add([activeSample]) {(success, error) in
|
||||
if let error = error {
|
||||
SwiftDebug.qtDebug("WorkoutTracking active calories: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
let unitDistance = HKUnit.mile()
|
||||
let miles = WorkoutTracking.distance * 0.000621371
|
||||
@@ -215,6 +223,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: " + error.localizedDescription)
|
||||
}
|
||||
workout?.setValue(quantityMiles, forKey: "totalDistance")
|
||||
// Set total energy burned on the workout
|
||||
let totalEnergy = WorkoutTracking.totalKcal > 0 ? WorkoutTracking.totalKcal : activeEnergyBurned
|
||||
let totalEnergyQuantity = HKQuantity(unit: unit, doubleValue: totalEnergy)
|
||||
workout?.setValue(totalEnergyQuantity, forKey: "totalEnergyBurned")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -270,14 +282,10 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
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
|
||||
}
|
||||
// Set total energy burned on the workout
|
||||
let totalEnergy = WorkoutTracking.totalKcal > 0 ? WorkoutTracking.totalKcal : activeEnergyBurned
|
||||
let totalEnergyQuantity = HKQuantity(unit: unit, doubleValue: totalEnergy)
|
||||
workout?.setValue(totalEnergyQuantity, forKey: "totalEnergyBurned")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +294,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
workoutInProgress = false;
|
||||
}
|
||||
|
||||
@objc func addMetrics(power: Double, cadence: Double, speed: Double, kcal: Double, steps: Double, deviceType: UInt8, distance: Double) {
|
||||
@objc func addMetrics(power: Double, cadence: Double, speed: Double, kcal: Double, steps: Double, deviceType: UInt8, distance: Double, totalKcal: Double) {
|
||||
SwiftDebug.qtDebug("WorkoutTracking: GET DATA: \(Date())")
|
||||
|
||||
if(workoutInProgress == false && power > 0) {
|
||||
@@ -298,6 +306,7 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
|
||||
let Speed = speed / 100;
|
||||
|
||||
WorkoutTracking.kcal = kcal
|
||||
WorkoutTracking.totalKcal = totalKcal
|
||||
WorkoutTracking.steps = steps
|
||||
WorkoutTracking.distance = distance
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ class lockscreen {
|
||||
long heartRate();
|
||||
long stepCadence();
|
||||
void setKcal(double kcal);
|
||||
void setTotalKcal(double totalKcal);
|
||||
void setDistance(double distance);
|
||||
void setSteps(double steps);
|
||||
void setSpeed(double speed);
|
||||
@@ -17,7 +18,7 @@ class lockscreen {
|
||||
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 currentDistance);
|
||||
void workoutTrackingUpdate(double speed, unsigned short cadence, unsigned short watt, unsigned short currentCalories, unsigned long long currentSteps, unsigned char deviceType, double currentDistance, double totalKcal);
|
||||
bool appleWatchAppInstalled();
|
||||
|
||||
// virtualbike
|
||||
|
||||
@@ -129,6 +129,11 @@ void lockscreen::setKcal(double kcal)
|
||||
[h setKcalWithKcal:kcal];
|
||||
}
|
||||
|
||||
void lockscreen::setTotalKcal(double totalKcal)
|
||||
{
|
||||
[h setTotalKcalWithTotalKcal:totalKcal];
|
||||
}
|
||||
|
||||
void lockscreen::setDistance(double distance)
|
||||
{
|
||||
[h setDistanceWithDistance:distance * 0.621371];
|
||||
@@ -172,9 +177,9 @@ 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) {
|
||||
void lockscreen::workoutTrackingUpdate(double speed, unsigned short cadence, unsigned short watt, unsigned short currentCalories, unsigned long long currentSteps, unsigned char deviceType, double currentDistance, double totalKcal) {
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:watt cadence:cadence*2 speed:speed * 100 kcal:currentCalories steps:currentSteps deviceType:deviceType distance:currentDistance];
|
||||
[workoutTracking addMetricsWithPower:watt cadence:cadence*2 speed:speed * 100 kcal:currentCalories steps:currentSteps deviceType:deviceType distance:currentDistance totalKcal:totalKcal];
|
||||
}
|
||||
|
||||
void lockscreen::virtualbike_zwift_ios(bool disable_hr, bool garmin_bluetooth_compatibility, bool zwift_play_emulator, bool watt_bike_emulator)
|
||||
@@ -226,7 +231,7 @@ double lockscreen::virtualbike_getPowerRequested()
|
||||
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];
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:currentCalories steps:0 deviceType: bluetoothdevice::BLUETOOTH_TYPE::BIKE distance:Distance totalKcal:0];
|
||||
|
||||
if(_virtualbike_zwift != nil)
|
||||
return [_virtualbike_zwift updateFTMSWithNormalizeSpeed:normalizeSpeed currentCadence:currentCadence currentResistance:currentResistance currentWatt:currentWatt CrankRevolutions:CrankRevolutions LastCrankEventTime:LastCrankEventTime Gears:Gears];
|
||||
@@ -236,7 +241,7 @@ bool lockscreen::virtualbike_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResi
|
||||
bool lockscreen::virtualrower_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 CrankRevolutions, UInt16 LastCrankEventTime, UInt16 StrokesCount, UInt32 Distance, UInt16 KCal, UInt16 Pace)
|
||||
{
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:KCal steps:0 deviceType: bluetoothdevice::BLUETOOTH_TYPE::ROWING distance:Distance];
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:KCal steps:0 deviceType: bluetoothdevice::BLUETOOTH_TYPE::ROWING distance:Distance totalKcal:0];
|
||||
|
||||
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];
|
||||
@@ -301,7 +306,7 @@ double lockscreen::virtualtreadmill_getRequestedSpeed()
|
||||
bool lockscreen::virtualtreadmill_updateFTMS(UInt16 normalizeSpeed, UInt8 currentResistance, UInt16 currentCadence, UInt16 currentWatt, UInt16 currentInclination, UInt64 currentDistance, unsigned short currentCalories, qint32 currentSteps, unsigned short elapsedSeconds)
|
||||
{
|
||||
if(workoutTracking != nil && !appleWatchAppInstalled())
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:currentCalories steps:currentSteps deviceType:bluetoothdevice::BLUETOOTH_TYPE::TREADMILL distance:currentDistance];
|
||||
[workoutTracking addMetricsWithPower:currentWatt cadence:currentCadence speed:normalizeSpeed kcal:currentCalories steps:currentSteps deviceType:bluetoothdevice::BLUETOOTH_TYPE::TREADMILL distance:currentDistance totalKcal:0];
|
||||
|
||||
if(_virtualtreadmill_zwift != nil)
|
||||
return [_virtualtreadmill_zwift updateFTMSWithNormalizeSpeed:normalizeSpeed currentCadence:currentCadence currentResistance:currentResistance currentWatt:currentWatt currentInclination:currentInclination currentDistance:currentDistance elapsedTimeSeconds:elapsedSeconds];
|
||||
|
||||
@@ -423,3 +423,44 @@ Women:
|
||||
return (T * ((0.6309 * H) + (0.1988 * W) + (0.2017 * A) - 55.0969) / 4.184);
|
||||
}
|
||||
}
|
||||
|
||||
double metric::calculateBMR() {
|
||||
// Calculate Basal Metabolic Rate using Mifflin-St Jeor equation
|
||||
// BMR (kcal/day) for males: 10 * weight(kg) + 6.25 * height(cm) - 5 * age + 5
|
||||
// BMR (kcal/day) for females: 10 * weight(kg) + 6.25 * height(cm) - 5 * age - 161
|
||||
|
||||
QSettings settings;
|
||||
QString sex = settings.value(QZSettings::sex, QZSettings::default_sex).toString();
|
||||
double weight = settings.value(QZSettings::weight, QZSettings::default_weight).toFloat();
|
||||
double age = settings.value(QZSettings::age, QZSettings::default_age).toDouble();
|
||||
double height = settings.value(QZSettings::height, QZSettings::default_height).toDouble();
|
||||
|
||||
// Full Mifflin-St Jeor equation with height
|
||||
if (sex.toLower().contains("female")) {
|
||||
return (10 * weight) + (6.25 * height) - (5 * age) - 161;
|
||||
} else {
|
||||
return (10 * weight) + (6.25 * height) - (5 * age) + 5;
|
||||
}
|
||||
}
|
||||
|
||||
double metric::calculateActiveKCal(double totalKCal, double elapsed) {
|
||||
QSettings settings;
|
||||
bool activeOnly = settings.value(QZSettings::calories_active_only, QZSettings::default_calories_active_only).toBool();
|
||||
|
||||
if (!activeOnly) {
|
||||
return totalKCal; // Return total calories if active-only mode is disabled
|
||||
}
|
||||
|
||||
// Calculate BMR in calories per second
|
||||
double bmrPerDay = calculateBMR();
|
||||
double bmrPerSecond = bmrPerDay / (24.0 * 60.0 * 60.0); // Convert from calories/day to calories/second
|
||||
|
||||
// Calculate BMR calories for the elapsed time
|
||||
double bmrForElapsed = bmrPerSecond * elapsed;
|
||||
|
||||
// Active calories = Total calories - BMR calories for the elapsed time
|
||||
double activeKCal = totalKCal - bmrForElapsed;
|
||||
|
||||
// Ensure we don't return negative calories
|
||||
return activeKCal > 0 ? activeKCal : 0;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ class metric {
|
||||
static double calculateWeightLoss(double kcal);
|
||||
static double calculateVO2Max(QList<SessionLine> *session);
|
||||
static double calculateKCalfromHR(double HR_AVG, double elapsed);
|
||||
static double calculateBMR();
|
||||
static double calculateActiveKCal(double totalKCal, double elapsed);
|
||||
|
||||
static double powerPeak(QList<SessionLine> *session, int seconds);
|
||||
|
||||
|
||||
@@ -974,9 +974,11 @@ const QString QZSettings::tile_auto_virtual_shifting_climb_enabled = QStringLite
|
||||
const QString QZSettings::tile_auto_virtual_shifting_climb_order = QStringLiteral("tile_auto_virtual_shifting_climb_order");
|
||||
const QString QZSettings::tile_auto_virtual_shifting_sprint_enabled = QStringLiteral("tile_auto_virtual_shifting_sprint_enabled");
|
||||
const QString QZSettings::tile_auto_virtual_shifting_sprint_order = QStringLiteral("tile_auto_virtual_shifting_sprint_order");
|
||||
const QString QZSettings::calories_active_only = QStringLiteral("calories_active_only");
|
||||
const QString QZSettings::height = QStringLiteral("height");
|
||||
|
||||
|
||||
const uint32_t allSettingsCount = 798;
|
||||
const uint32_t allSettingsCount = 800;
|
||||
|
||||
QVariant allSettings[allSettingsCount][2] = {
|
||||
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
|
||||
@@ -1796,6 +1798,8 @@ QVariant allSettings[allSettingsCount][2] = {
|
||||
{QZSettings::tile_auto_virtual_shifting_sprint_enabled, QZSettings::default_tile_auto_virtual_shifting_sprint_enabled},
|
||||
{QZSettings::tile_auto_virtual_shifting_sprint_order, QZSettings::default_tile_auto_virtual_shifting_sprint_order},
|
||||
{QZSettings::rogue_echo_bike, QZSettings::default_rogue_echo_bike},
|
||||
{QZSettings::calories_active_only, QZSettings::default_calories_active_only},
|
||||
{QZSettings::height, QZSettings::default_height},
|
||||
};
|
||||
|
||||
void QZSettings::qDebugAllSettings(bool showDefaults) {
|
||||
|
||||
@@ -2660,6 +2660,18 @@ class QZSettings {
|
||||
static const QString tile_auto_virtual_shifting_sprint_order;
|
||||
static constexpr int default_tile_auto_virtual_shifting_sprint_order = 57;
|
||||
|
||||
/**
|
||||
* @brief Calculate only active calories (exclude basal metabolic rate)
|
||||
*/
|
||||
static const QString calories_active_only;
|
||||
static constexpr bool default_calories_active_only = false;
|
||||
|
||||
/**
|
||||
* @brief User height in centimeters for BMR calculation
|
||||
*/
|
||||
static const QString height;
|
||||
static constexpr double default_height = 175.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.
|
||||
|
||||
@@ -1198,6 +1198,8 @@ import Qt.labs.platform 1.1
|
||||
property int tile_auto_virtual_shifting_sprint_order: 57
|
||||
property string proform_rower_ip: ""
|
||||
property string ftms_elliptical: "Disabled"
|
||||
property bool calories_active_only: false
|
||||
property real height: 175.0
|
||||
}
|
||||
|
||||
|
||||
@@ -1317,6 +1319,62 @@ import Qt.labs.platform 1.1
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
Label {
|
||||
id: labelHeight
|
||||
text: qsTr("Player Height") + "(" + (settings.miles_unit?"ft/in":"cm") + ")"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: heightTextField
|
||||
text: settings.miles_unit ? Math.floor(settings.height / 30.48) + "'" + Math.round((settings.height % 30.48) / 2.54) + '"' : settings.height
|
||||
horizontalAlignment: Text.AlignRight
|
||||
Layout.fillHeight: false
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
//inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
onAccepted: {
|
||||
if (settings.miles_unit) {
|
||||
var parts = text.match(/(\d+)'(\d+)"/);
|
||||
if (parts) {
|
||||
settings.height = parseInt(parts[1]) * 30.48 + parseInt(parts[2]) * 2.54;
|
||||
}
|
||||
} else {
|
||||
settings.height = text;
|
||||
}
|
||||
}
|
||||
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
|
||||
}
|
||||
Button {
|
||||
id: okHeightButton
|
||||
text: "OK"
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
onClicked: {
|
||||
if (settings.miles_unit) {
|
||||
var parts = heightTextField.text.match(/(\d+)'(\d+)"/);
|
||||
if (parts) {
|
||||
settings.height = parseInt(parts[1]) * 30.48 + parseInt(parts[2]) * 2.54;
|
||||
}
|
||||
} else {
|
||||
settings.height = heightTextField.text;
|
||||
}
|
||||
toast.show("Setting saved!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Enter your height for more accurate BMR and active calories calculation. Use centimeters for metric or feet'inches\" format (e.g., 5'10\") for imperial units.")
|
||||
font.bold: true
|
||||
font.italic: true
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
Label {
|
||||
@@ -1721,7 +1779,35 @@ import Qt.labs.platform 1.1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("This prevents your bike or treadmill from sending its calories-burned calculation to QZ and defaults to QZ’s more accurate calculation.")
|
||||
text: qsTr("This prevents your bike or treadmill from sending its calories-burned calculation to QZ and defaults to QZ's more accurate calculation.")
|
||||
font.bold: true
|
||||
font.italic: true
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
IndicatorOnlySwitch {
|
||||
id: switchActiveCaloriesOnlyDelegate
|
||||
text: qsTr("Calculate Active Calories Only")
|
||||
spacing: 0
|
||||
bottomPadding: 0
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
clip: false
|
||||
checked: settings.calories_active_only
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
onClicked: settings.calories_active_only = checked
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Enable to calculate only active calories (excluding basal metabolic rate) similar to Apple Watch. When disabled, total calories including BMR are calculated. This affects both display and Apple Health integration.")
|
||||
font.bold: true
|
||||
font.italic: true
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
|
||||
Reference in New Issue
Block a user