forked from upstream-mirrors/OpenTracks
88 lines
3.2 KiB
Java
88 lines
3.2 KiB
Java
package de.dennisguse.opentracks.sensors;
|
|
|
|
import android.bluetooth.BluetoothGattCharacteristic;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.VisibleForTesting;
|
|
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
import de.dennisguse.opentracks.data.models.Power;
|
|
import de.dennisguse.opentracks.sensors.driver.DriverObserver0;
|
|
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
|
|
import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
|
|
|
|
public class BluetoothHandlerCyclingPower implements SensorHandlerInterface {
|
|
|
|
public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID(
|
|
new UUID(0x181800001000L, 0x800000805f9b34fbL),
|
|
new UUID(0x2A6300001000L, 0x800000805f9b34fbL)
|
|
);
|
|
|
|
@Override
|
|
public List<ServiceMeasurementUUID> getServices() {
|
|
return List.of(CYCLING_POWER);
|
|
}
|
|
|
|
@Override
|
|
public AggregatorCyclingPower createEmptySensorData(String address, String name) {
|
|
return new AggregatorCyclingPower(address, name);
|
|
}
|
|
|
|
@Override
|
|
public void handlePayload(DriverObserver0 observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
|
Data cyclingPower = parseCyclingPower(characteristic);
|
|
|
|
if (cyclingPower == null) return;
|
|
|
|
observer.onChange(cyclingPower);
|
|
}
|
|
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
|
public static Data parseCyclingPower(BluetoothGattCharacteristic characteristic) {
|
|
// DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml
|
|
int valueLength = characteristic.getValue().length;
|
|
if (valueLength == 0) {
|
|
return null;
|
|
}
|
|
|
|
int index = 0;
|
|
int flags1 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++);
|
|
int flags2 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++);
|
|
boolean hasPedalPowerBalance = (flags1 & 0x01) > 0;
|
|
boolean hasAccumulatedTorque = (flags1 & 0x04) > 0;
|
|
boolean hasWheel = (flags1 & 16) > 0;
|
|
boolean hasCrank = (flags1 & 32) > 0;
|
|
|
|
Integer instantaneousPower = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, index);
|
|
index += 2;
|
|
|
|
if (hasPedalPowerBalance) {
|
|
index += 1;
|
|
}
|
|
if (hasAccumulatedTorque) {
|
|
index += 2;
|
|
}
|
|
if (hasWheel) {
|
|
index += 2 + 2;
|
|
}
|
|
|
|
BluetoothHandlerCyclingCadence.CrankData crankData = null;
|
|
if (hasCrank && valueLength - index >= 4) {
|
|
long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index);
|
|
index += 2;
|
|
|
|
int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s
|
|
|
|
crankData = new BluetoothHandlerCyclingCadence.CrankData(crankCount, crankTime);
|
|
}
|
|
|
|
return new Data(Power.of(instantaneousPower), crankData);
|
|
}
|
|
|
|
public record Data(Power power, BluetoothHandlerCyclingCadence.CrankData crank) {}
|
|
|
|
}
|