mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 02:46:50 +01:00
Garmin: Manual HR measurements and realtime HR/steps
This commit is contained in:
parent
e5b91d3ac3
commit
5bc114b062
@ -225,9 +225,8 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsManualHeartRateMeasurement(final GBDevice device) {
|
public boolean supportsRealtimeData() {
|
||||||
// TODO: It should be supported, but not yet implemented
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -827,6 +827,21 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeartRateTest() {
|
||||||
|
communicator.onHeartRateTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableRealtimeHeartRateMeasurement(final boolean enable) {
|
||||||
|
communicator.onEnableRealtimeHeartRateMeasurement(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableRealtimeSteps(final boolean enable) {
|
||||||
|
communicator.onEnableRealtimeSteps(enable);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTestNewFunction() {
|
public void onTestNewFunction() {
|
||||||
parseAllFitFilesFromStorage();
|
parseAllFitFilesFromStorage();
|
||||||
|
@ -14,6 +14,12 @@ public interface ICommunicator {
|
|||||||
|
|
||||||
boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
|
boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
|
||||||
|
|
||||||
|
void onHeartRateTest();
|
||||||
|
|
||||||
|
void onEnableRealtimeHeartRateMeasurement(final boolean enable);
|
||||||
|
|
||||||
|
void onEnableRealtimeSteps(final boolean enable);
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
void onMessage(byte[] message);
|
void onMessage(byte[] message);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator
|
|||||||
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGatt;
|
||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
@ -10,6 +13,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator;
|
||||||
|
|
||||||
public class CommunicatorV1 implements ICommunicator {
|
public class CommunicatorV1 implements ICommunicator {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CommunicatorV1.class);
|
||||||
|
|
||||||
public static final UUID UUID_SERVICE_GARMIN_GFDI = UUID.fromString("6A4E2401-667B-11E3-949A-0800200C9A66");
|
public static final UUID UUID_SERVICE_GARMIN_GFDI = UUID.fromString("6A4E2401-667B-11E3-949A-0800200C9A66");
|
||||||
|
|
||||||
private final GarminSupport mSupport;
|
private final GarminSupport mSupport;
|
||||||
@ -25,7 +30,7 @@ public class CommunicatorV1 implements ICommunicator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeDevice(final TransactionBuilder builder) {
|
public void initializeDevice(final TransactionBuilder builder) {
|
||||||
|
LOG.error("Initialization is not implemented for V1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -37,4 +42,19 @@ public class CommunicatorV1 implements ICommunicator {
|
|||||||
public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeartRateTest() {
|
||||||
|
LOG.error("onHeartRateTest is not implemented for V1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableRealtimeHeartRateMeasurement(final boolean enable) {
|
||||||
|
LOG.error("onEnableRealtimeHeartRateMeasurement is not implemented for V1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableRealtimeSteps(final boolean enable) {
|
||||||
|
LOG.error("onEnableRealtimeSteps is not implemented for V1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,10 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGatt;
|
||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -12,7 +16,18 @@ import java.nio.ByteOrder;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminActivitySampleProvider;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
|
||||||
@ -21,19 +36,29 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
public class CommunicatorV2 implements ICommunicator {
|
public class CommunicatorV2 implements ICommunicator {
|
||||||
public static final String BASE_UUID = "6A4E%04X-667B-11E3-949A-0800200C9A66";
|
private static final Logger LOG = LoggerFactory.getLogger(CommunicatorV2.class);
|
||||||
|
|
||||||
|
public static final String BASE_UUID = "6A4E%04X-667B-11E3-949A-0800200C9A66";
|
||||||
public static final UUID UUID_SERVICE_GARMIN_ML_GFDI = UUID.fromString(String.format(BASE_UUID, 0x2800));
|
public static final UUID UUID_SERVICE_GARMIN_ML_GFDI = UUID.fromString(String.format(BASE_UUID, 0x2800));
|
||||||
|
|
||||||
|
private static final long GADGETBRIDGE_CLIENT_ID = 2L;
|
||||||
|
|
||||||
private BluetoothGattCharacteristic characteristicSend;
|
private BluetoothGattCharacteristic characteristicSend;
|
||||||
private BluetoothGattCharacteristic characteristicReceive;
|
private BluetoothGattCharacteristic characteristicReceive;
|
||||||
|
|
||||||
public int maxWriteSize = 20;
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CommunicatorV2.class);
|
|
||||||
public final CobsCoDec cobsCoDec;
|
|
||||||
private final GarminSupport mSupport;
|
private final GarminSupport mSupport;
|
||||||
private final long gadgetBridgeClientID = 2L;
|
|
||||||
private int gfdiHandle = 0;
|
private int gfdiHandle = 0;
|
||||||
|
public int maxWriteSize = 20;
|
||||||
|
public final CobsCoDec cobsCoDec;
|
||||||
|
|
||||||
|
private int realtimeHrHandle = 0;
|
||||||
|
private boolean realtimeHrOneShot = false;
|
||||||
|
|
||||||
|
private int realtimeStepsHandle = 0;
|
||||||
|
private int previousSteps = -1;
|
||||||
|
|
||||||
|
private int realtimeAccelHandle = 0;
|
||||||
|
|
||||||
public CommunicatorV2(final GarminSupport garminSupport) {
|
public CommunicatorV2(final GarminSupport garminSupport) {
|
||||||
this.mSupport = garminSupport;
|
this.mSupport = garminSupport;
|
||||||
@ -101,85 +126,266 @@ public class CommunicatorV2 implements ICommunicator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer message = ByteBuffer.wrap(characteristic.getValue()).order(ByteOrder.LITTLE_ENDIAN);
|
final ByteBuffer message = ByteBuffer.wrap(characteristic.getValue()).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
// LOG.debug("RECEIVED: {}", GB.hexdump(message.array()));
|
|
||||||
final byte handle = message.get();
|
final byte handle = message.get();
|
||||||
if (0x00 == handle) { //handle management message
|
|
||||||
|
|
||||||
final byte type = message.get();
|
if (0x00 == handle) {
|
||||||
final long incomingClientID = message.getLong();
|
processHandleManagement(message);
|
||||||
|
|
||||||
if (incomingClientID != this.gadgetBridgeClientID) {
|
|
||||||
LOG.debug("Ignoring incoming message, client ID is not ours. Message: {}", GB.hexdump(message.array()));
|
|
||||||
}
|
|
||||||
RequestType requestType = RequestType.fromCode(type);
|
|
||||||
if (null == requestType) {
|
|
||||||
LOG.error("Unknown request type. Message: {}", message.array());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
switch (requestType) {
|
|
||||||
case REGISTER_ML_REQ: //register service request
|
|
||||||
case CLOSE_HANDLE_REQ: //close handle request
|
|
||||||
case CLOSE_ALL_REQ: //close all handles request
|
|
||||||
case UNK_REQ: //unknown request
|
|
||||||
LOG.warn("Received handle request, expecting responses. Message: {}", message.array());
|
|
||||||
case REGISTER_ML_RESP: //register service response
|
|
||||||
LOG.debug("Received register response. Message: {}", message.array());
|
|
||||||
final short registeredService = message.getShort();
|
|
||||||
final byte status = message.get();
|
|
||||||
if (0 == status && 1 == registeredService) { //success
|
|
||||||
this.gfdiHandle = message.get();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CLOSE_HANDLE_RESP: //close handle response
|
|
||||||
LOG.debug("Received close handle response. Message: {}", message.array());
|
|
||||||
break;
|
|
||||||
case CLOSE_ALL_RESP: //close all handles response
|
|
||||||
LOG.debug("Received close all handles response. Message: {}", message.array());
|
|
||||||
new TransactionBuilder("open GFDI")
|
|
||||||
.write(characteristicSend, registerGFDI())
|
|
||||||
.queue(this.mSupport.getQueue());
|
|
||||||
break;
|
|
||||||
case UNK_RESP: //unknown response
|
|
||||||
LOG.debug("Received unknown. Message: {}", message.array());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else if (this.gfdiHandle == handle) {
|
} else if (this.gfdiHandle == handle) {
|
||||||
|
processGfdi(message);
|
||||||
|
} else if (this.realtimeHrHandle == handle) {
|
||||||
|
processRealtimeHeartRate(message);
|
||||||
|
} else if (this.realtimeStepsHandle == handle) {
|
||||||
|
processRealtimeSteps(message);
|
||||||
|
} else if (this.realtimeAccelHandle == handle) {
|
||||||
|
processRealtimeAccelerometer(message);
|
||||||
|
} else {
|
||||||
|
LOG.warn("Got message for unknown handle {}: {}", handle, GB.hexdump(characteristic.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
byte[] partial = new byte[message.remaining()];
|
return true;
|
||||||
message.get(partial);
|
}
|
||||||
this.cobsCoDec.receivedBytes(partial);
|
|
||||||
|
|
||||||
this.mSupport.onMessage(this.cobsCoDec.retrieveMessage());
|
@Override
|
||||||
|
public void onHeartRateTest() {
|
||||||
|
realtimeHrOneShot = true;
|
||||||
|
if (realtimeHrHandle == 0) {
|
||||||
|
new TransactionBuilder("heart rate test")
|
||||||
|
.write(characteristicSend, registerService(Service.REALTIME_HR, false))
|
||||||
|
.queue(this.mSupport.getQueue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableRealtimeHeartRateMeasurement(final boolean enable) {
|
||||||
|
toggleService(Service.REALTIME_HR, realtimeHrHandle, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnableRealtimeSteps(final boolean enable) {
|
||||||
|
if (toggleService(Service.REALTIME_STEPS, realtimeStepsHandle, enable)) {
|
||||||
|
previousSteps = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean toggleService(final Service service, final int currentHandle, final boolean enable) {
|
||||||
|
if (enable && currentHandle == 0) {
|
||||||
|
new TransactionBuilder(service + " = true")
|
||||||
|
.write(characteristicSend, registerService(service, false))
|
||||||
|
.queue(this.mSupport.getQueue());
|
||||||
|
return true;
|
||||||
|
} else if (!enable && currentHandle != 0) {
|
||||||
|
new TransactionBuilder(service + " = false")
|
||||||
|
.write(characteristicSend, closeService(service, currentHandle))
|
||||||
|
.queue(this.mSupport.getQueue());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] closeAllServices() {
|
private void processHandleManagement(final ByteBuffer message) {
|
||||||
ByteBuffer toSend = ByteBuffer.allocate(13);
|
final byte type = message.get();
|
||||||
toSend.order(ByteOrder.BIG_ENDIAN);
|
final long incomingClientID = message.getLong();
|
||||||
toSend.putShort((short) RequestType.CLOSE_ALL_REQ.ordinal()); //close all services
|
|
||||||
toSend.order(ByteOrder.LITTLE_ENDIAN);
|
if (incomingClientID != GADGETBRIDGE_CLIENT_ID) {
|
||||||
toSend.putLong(this.gadgetBridgeClientID);
|
LOG.warn("Ignoring incoming message, client ID {} is not ours. Message: {}", incomingClientID, GB.hexdump(message.array()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final RequestType requestType = RequestType.fromCode(type);
|
||||||
|
if (null == requestType) {
|
||||||
|
LOG.error("Unknown request type {}. Message: {}", type, message.array());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (requestType) {
|
||||||
|
case REGISTER_ML_REQ:
|
||||||
|
case CLOSE_HANDLE_REQ:
|
||||||
|
case CLOSE_ALL_REQ:
|
||||||
|
case UNK_REQ:
|
||||||
|
LOG.warn("Received handle request, expecting responses. Message: {}", message.array());
|
||||||
|
return;
|
||||||
|
case REGISTER_ML_RESP: {
|
||||||
|
final short registeredServiceCode = message.getShort();
|
||||||
|
final Service registeredService = Service.fromCode(registeredServiceCode);
|
||||||
|
final byte status = message.get();
|
||||||
|
if (registeredService == null) {
|
||||||
|
LOG.error("Got register response status={} for unknown service {}", status, registeredServiceCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status != 0) {
|
||||||
|
LOG.warn("Failed to register {}, status={}", registeredService, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int handle = message.get();
|
||||||
|
final int reliable = message.get();
|
||||||
|
LOG.debug("Got register response for {}, reliable={}", registeredService, reliable);
|
||||||
|
|
||||||
|
switch (registeredService) {
|
||||||
|
case GFDI:
|
||||||
|
this.gfdiHandle = handle;
|
||||||
|
break;
|
||||||
|
case REALTIME_HR:
|
||||||
|
this.realtimeHrHandle = handle;
|
||||||
|
break;
|
||||||
|
case REALTIME_STEPS:
|
||||||
|
this.realtimeStepsHandle = handle;
|
||||||
|
break;
|
||||||
|
case REALTIME_ACCELEROMETER:
|
||||||
|
this.realtimeAccelHandle = handle;
|
||||||
|
new TransactionBuilder("start realtime accel")
|
||||||
|
.write(characteristicSend, new byte[]{(byte) handle, 0x01})
|
||||||
|
.queue(this.mSupport.getQueue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSE_HANDLE_RESP: {
|
||||||
|
final short serviceCode = message.getShort();
|
||||||
|
final Service service = Service.fromCode(serviceCode);
|
||||||
|
final int handle = message.get();
|
||||||
|
final byte status = message.get();
|
||||||
|
LOG.debug("Received close handle response: service={}, handle={}, status={}", service, handle, status);
|
||||||
|
if (service != null) {
|
||||||
|
switch (service) {
|
||||||
|
case GFDI:
|
||||||
|
this.gfdiHandle = 0;
|
||||||
|
break;
|
||||||
|
case REALTIME_HR:
|
||||||
|
this.realtimeHrHandle = 0;
|
||||||
|
break;
|
||||||
|
case REALTIME_STEPS:
|
||||||
|
this.realtimeStepsHandle = 0;
|
||||||
|
break;
|
||||||
|
case REALTIME_ACCELEROMETER:
|
||||||
|
this.realtimeAccelHandle = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSE_ALL_RESP:
|
||||||
|
LOG.debug("Received close all handles response. Message: {}", message.array());
|
||||||
|
this.gfdiHandle = 0;
|
||||||
|
this.realtimeHrHandle = 0;
|
||||||
|
this.realtimeStepsHandle = 0;
|
||||||
|
this.realtimeAccelHandle = 0;
|
||||||
|
new TransactionBuilder("open GFDI")
|
||||||
|
.write(characteristicSend, registerService(Service.GFDI, false))
|
||||||
|
.queue(this.mSupport.getQueue());
|
||||||
|
break;
|
||||||
|
case UNK_RESP:
|
||||||
|
LOG.debug("Received unknown. Message: {}", message.array());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processGfdi(final ByteBuffer message) {
|
||||||
|
final byte[] partial = new byte[message.remaining()];
|
||||||
|
message.get(partial);
|
||||||
|
this.cobsCoDec.receivedBytes(partial);
|
||||||
|
|
||||||
|
this.mSupport.onMessage(this.cobsCoDec.retrieveMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRealtimeHeartRate(final ByteBuffer buf) {
|
||||||
|
final byte type = buf.get(); // 0/2/3? 3 == realtime?
|
||||||
|
final int hr = buf.get();
|
||||||
|
final int resting = buf.get();
|
||||||
|
// ff ff after
|
||||||
|
LOG.debug("Got realtime HR: type={} hr={} resting={}", type, hr, resting);
|
||||||
|
|
||||||
|
if (hr > 0) {
|
||||||
|
broadcastRealtimeActivity(hr, -1);
|
||||||
|
|
||||||
|
if (realtimeHrOneShot && realtimeHrHandle != 0) {
|
||||||
|
onEnableRealtimeHeartRateMeasurement(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRealtimeSteps(final ByteBuffer buf) {
|
||||||
|
final int steps = buf.getInt();
|
||||||
|
final int goal = buf.getInt();
|
||||||
|
LOG.debug("Got realtime steps: steps={} goal={}", steps, goal);
|
||||||
|
|
||||||
|
if (previousSteps == -1) {
|
||||||
|
previousSteps = steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastRealtimeActivity(-1, steps - previousSteps);
|
||||||
|
|
||||||
|
previousSteps = steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRealtimeAccelerometer(final ByteBuffer message) {
|
||||||
|
final byte[] partial = new byte[message.remaining()];
|
||||||
|
message.get(partial);
|
||||||
|
|
||||||
|
LOG.debug("Got realtime accel: {}", GB.hexdump(partial));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] closeAllServices() {
|
||||||
|
final ByteBuffer toSend = ByteBuffer.allocate(13).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
toSend.put((byte) 0); // handle
|
||||||
|
toSend.put((byte) RequestType.CLOSE_ALL_REQ.ordinal());
|
||||||
|
toSend.putLong(GADGETBRIDGE_CLIENT_ID);
|
||||||
toSend.putShort((short) 0);
|
toSend.putShort((short) 0);
|
||||||
return toSend.array();
|
return toSend.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] registerGFDI() {
|
private byte[] registerService(final Service service, final boolean reliable) {
|
||||||
ByteBuffer toSend = ByteBuffer.allocate(13);
|
final ByteBuffer toSend = ByteBuffer.allocate(13).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
toSend.order(ByteOrder.BIG_ENDIAN);
|
toSend.put((byte) 0);
|
||||||
toSend.putShort((short) RequestType.REGISTER_ML_REQ.ordinal()); //register service request
|
toSend.put((byte) RequestType.REGISTER_ML_REQ.ordinal());
|
||||||
toSend.order(ByteOrder.LITTLE_ENDIAN);
|
toSend.putLong(GADGETBRIDGE_CLIENT_ID);
|
||||||
toSend.putLong(this.gadgetBridgeClientID);
|
toSend.putShort(service.getCode());
|
||||||
toSend.putShort((short) 1); //service GFDI
|
toSend.put((byte) (reliable ? 2 : 0));
|
||||||
return toSend.array();
|
return toSend.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RequestType {
|
private byte[] closeService(final Service service, final int handle) {
|
||||||
|
final ByteBuffer toSend = ByteBuffer.allocate(13).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
toSend.put((byte) 0);
|
||||||
|
toSend.put((byte) RequestType.CLOSE_HANDLE_REQ.ordinal());
|
||||||
|
toSend.putLong(GADGETBRIDGE_CLIENT_ID);
|
||||||
|
toSend.putShort(service.getCode());
|
||||||
|
toSend.put((byte) handle);
|
||||||
|
return toSend.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcastRealtimeActivity(final int hr, final int steps) {
|
||||||
|
final GarminActivitySample sample;
|
||||||
|
try (final DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||||
|
final DaoSession session = dbHandler.getDaoSession();
|
||||||
|
|
||||||
|
final GBDevice gbDevice = mSupport.getDevice();
|
||||||
|
final Device device = DBHelper.getDevice(gbDevice, session);
|
||||||
|
final User user = DBHelper.getUser(session);
|
||||||
|
final GarminActivitySampleProvider provider = new GarminActivitySampleProvider(gbDevice, session);
|
||||||
|
sample = provider.createActivitySample();
|
||||||
|
|
||||||
|
sample.setDeviceId(device.getId());
|
||||||
|
sample.setUserId(user.getId());
|
||||||
|
sample.setTimestamp((int) (System.currentTimeMillis() / 1000));
|
||||||
|
sample.setHeartRate(hr);
|
||||||
|
sample.setSteps(steps);
|
||||||
|
sample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
||||||
|
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||||
|
sample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
||||||
|
} catch (final Exception e) {
|
||||||
|
LOG.error("Error creating activity sample", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
|
||||||
|
.putExtra(GBDevice.EXTRA_DEVICE, mSupport.getDevice())
|
||||||
|
.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample);
|
||||||
|
LocalBroadcastManager.getInstance(mSupport.getContext()).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum RequestType {
|
||||||
REGISTER_ML_REQ,
|
REGISTER_ML_REQ,
|
||||||
REGISTER_ML_RESP,
|
REGISTER_ML_RESP,
|
||||||
CLOSE_HANDLE_REQ,
|
CLOSE_HANDLE_REQ,
|
||||||
@ -190,6 +396,7 @@ public class CommunicatorV2 implements ICommunicator {
|
|||||||
UNK_REQ,
|
UNK_REQ,
|
||||||
UNK_RESP;
|
UNK_RESP;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static RequestType fromCode(final int code) {
|
public static RequestType fromCode(final int code) {
|
||||||
for (final RequestType requestType : RequestType.values()) {
|
for (final RequestType requestType : RequestType.values()) {
|
||||||
if (requestType.ordinal() == code) {
|
if (requestType.ordinal() == code) {
|
||||||
@ -200,4 +407,41 @@ public class CommunicatorV2 implements ICommunicator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum Service {
|
||||||
|
GFDI(1),
|
||||||
|
REGISTRATION(4),
|
||||||
|
REALTIME_HR(6),
|
||||||
|
REALTIME_STEPS(7),
|
||||||
|
REALTIME_CALORIES(8),
|
||||||
|
REALTIME_INTENSITY(10),
|
||||||
|
REALTIME_HRV(12),
|
||||||
|
REALTIME_STRESS(13),
|
||||||
|
REALTIME_ACCELEROMETER(16),
|
||||||
|
REALTIME_SPO2(19),
|
||||||
|
REALTIME_BODY_BATTERY(20),
|
||||||
|
REALTIME_RESPIRATION(21),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final short code;
|
||||||
|
|
||||||
|
Service(final int code) {
|
||||||
|
this.code = (short) code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Service fromCode(final int code) {
|
||||||
|
for (final Service service : Service.values()) {
|
||||||
|
if (service.code == code) {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -933,9 +931,7 @@ public class XiaomiHealthService extends AbstractXiaomiService {
|
|||||||
sample.setHeartRate(realTimeStats.getHeartRate());
|
sample.setHeartRate(realTimeStats.getHeartRate());
|
||||||
sample.setSteps(realTimeStats.getSteps() - previousSteps);
|
sample.setSteps(realTimeStats.getSteps() - previousSteps);
|
||||||
sample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
sample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
||||||
sample.setHeartRate(realTimeStats.getHeartRate());
|
|
||||||
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||||
sample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
LOG.error("Error creating activity sample", e);
|
LOG.error("Error creating activity sample", e);
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user