1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-24 09:35:50 +01:00

Garmin: Add support for V1

This commit is contained in:
José Rebelo 2024-12-03 20:03:29 +00:00
parent ce126b805e
commit a6de977d50
4 changed files with 71 additions and 17 deletions

View File

@ -143,20 +143,26 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
protected TransactionBuilder initializeDevice(final TransactionBuilder builder) {
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
if (getSupportedServices().contains(CommunicatorV2.UUID_SERVICE_GARMIN_ML_GFDI)) {
communicator = new CommunicatorV2(this);
} else if (getSupportedServices().contains(CommunicatorV1.UUID_SERVICE_GARMIN_GFDI)) {
communicator = new CommunicatorV1(this);
} else {
LOG.warn("Failed to find a known Garmin service");
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.NOT_CONNECTED, getContext()));
return builder;
}
if (getDevicePrefs().getBoolean(PREF_ALLOW_HIGH_MTU, true)) {
builder.requestMtu(515);
}
final CommunicatorV2 communicatorV2 = new CommunicatorV2(this);
if (communicatorV2.initializeDevice(builder)) {
communicator = communicatorV2;
} else {
// V2 did not manage to initialize, attempt V1
final CommunicatorV1 communicatorV1 = new CommunicatorV1(this);
if (!communicatorV1.initializeDevice(builder)) {
// Neither V1 nor V2 worked, not a Garmin device?
LOG.warn("Failed to find a known Garmin service");
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.NOT_CONNECTED, getContext()));
return builder;
}
communicator = communicatorV1;
}
communicator.initializeDevice(builder);
return builder;

View File

@ -10,7 +10,7 @@ public interface ICommunicator {
void onMtuChanged(final int mtu);
void initializeDevice(TransactionBuilder builder);
boolean initializeDevice(TransactionBuilder builder);
boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);

View File

@ -6,40 +6,88 @@ import android.bluetooth.BluetoothGattCharacteristic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.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_CHARACTERISTIC_GARMIN_GFDI_SEND = UUID.fromString("6A4E4C80-667B-11E3-949A-0800200C9A66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE = UUID.fromString("6A4ECD28-667B-11E3-949A-0800200C9A66");
private BluetoothGattCharacteristic characteristicSend;
private BluetoothGattCharacteristic characteristicReceive;
private final GarminSupport mSupport;
public final CobsCoDec cobsCoDec;
public int maxWriteSize = 20;
public CommunicatorV1(final GarminSupport garminSupport) {
this.mSupport = garminSupport;
this.cobsCoDec = new CobsCoDec();
}
@Override
public void onMtuChanged(final int mtu) {
maxWriteSize = mtu - 3;
}
@Override
public void initializeDevice(final TransactionBuilder builder) {
LOG.error("Initialization is not implemented for V1");
public boolean initializeDevice(final TransactionBuilder builder) {
characteristicSend = mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_GFDI_SEND);
characteristicReceive = mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE);
if (characteristicSend == null || characteristicReceive == null) {
LOG.warn("Failed to find V1 GFDI characteristics");
return false;
}
builder.notify(characteristicReceive, true);
LOG.debug("Initializing as Garmin V1");
return true;
}
@Override
public void sendMessage(final String taskName, final byte[] message) {
if (null == message)
return;
final byte[] payload = cobsCoDec.encode(message);
final TransactionBuilder builder = new TransactionBuilder(taskName);
int remainingBytes = payload.length;
if (remainingBytes > maxWriteSize - 1) {
int position = 0;
while (remainingBytes > 0) {
final byte[] fragment = Arrays.copyOfRange(payload, position, position + Math.min(remainingBytes, maxWriteSize - 1));
builder.write(characteristicSend, fragment);
position += fragment.length;
remainingBytes -= fragment.length;
}
} else {
builder.write(characteristicSend, payload);
}
builder.queue(this.mSupport.getQueue());
}
@Override
public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().equals(characteristicReceive.getUuid())) {
this.cobsCoDec.receivedBytes(characteristic.getValue());
this.mSupport.onMessage(this.cobsCoDec.retrieveMessage());
return true;
}
return false;
}

View File

@ -77,7 +77,7 @@ public class CommunicatorV2 implements ICommunicator {
}
@Override
public void initializeDevice(final TransactionBuilder builder) {
public boolean initializeDevice(final TransactionBuilder builder) {
// Iterate through the known ML characteristics until we find a known pair
// send characteristic = read characteristic + 0x10 (eg. 2810 / 2820)
for (int i = 0x2810; i <= 0x2814; i++) {
@ -90,13 +90,13 @@ public class CommunicatorV2 implements ICommunicator {
builder.notify(characteristicReceive, true);
builder.write(characteristicSend, closeAllServices());
return;
return true;
}
}
LOG.warn("Failed to find any known ML characteristics");
builder.add(new SetDeviceStateAction(mSupport.getDevice(), GBDevice.State.NOT_CONNECTED, mSupport.getContext()));
return false;
}
@Override