ble: enable BtLEQueue to log to-be-written values

This commit is contained in:
Thomas Kuehne
2025-06-28 19:28:22 +02:00
committed by José Rebelo
parent 95d163e8c3
commit e40608dcf6
3 changed files with 56 additions and 33 deletions
@@ -1,4 +1,4 @@
/* Copyright (C) 2016-2024 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2016-2025 Andreas Shimokawa, Carsten Pfeiffer, Thomas Kuehne
This file is part of Gadgetbridge.
@@ -20,24 +20,36 @@ import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
public abstract class ConditionalWriteAction extends WriteAction {
private boolean actualGenerated;
private byte[] actual;
public ConditionalWriteAction(BluetoothGattCharacteristic characteristic) {
super(characteristic, null);
}
@Override
protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
byte[] conditionalValue = checkCondition();
if (conditionalValue != null) {
return super.writeValue(gatt, characteristic, conditionalValue);
public boolean run(BluetoothGatt gatt) {
byte[] value = getValue();
if (value != null) {
return super.run(gatt);
}
return true;
}
@Override
public final byte[] getValue() {
if (!actualGenerated) {
actual = checkCondition();
actualGenerated = true;
}
return actual;
}
/**
* Checks the condition whether the write shall happen or not.
* Returns the actual value to be written or null in case nothing shall be written.
* <p/>
* Note that returning null will not cause run() to return false, in other words,
* Note that returning null will not cause {@link #run()} to return false, in other words,
* the rest of the queue will still be executed.
*
* @return the value to be written or null to not write anything
@@ -1,4 +1,4 @@
/* Copyright (C) 2015-2024 Andreas Shimokawa, Carsten Pfeiffer, Daniele
/* Copyright (C) 2015-2025 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, José Rebelo, Uwe Hermann, Thomas Kuehne
This file is part of Gadgetbridge.
@@ -19,7 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothStatusCodes;
@@ -30,11 +29,12 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BleNamesResolver;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
/**
* Invokes a write operation on a given GATT characteristic.
* Invokes a write operation on a given {@link BluetoothGattCharacteristic}.
* The result status will be made available asynchronously through the
* {@link BluetoothGattCallback}
* {@link GattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)}
*/
public class WriteAction extends BtLEAction {
private static final Logger LOG = LoggerFactory.getLogger(WriteAction.class);
@@ -52,24 +52,23 @@ public class WriteAction extends BtLEAction {
int properties = characteristic.getProperties();
//TODO: expectsResult should return false if PROPERTY_WRITE_NO_RESPONSE is true, but this leads to timing issues
if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0)) {
return writeValue(gatt, characteristic, value);
return writeCharacteristicImp(gatt, characteristic, getValue());
}
LOG.error("WriteAction for non-writeable characteristic {}", characteristic.getUuid());
return false;
}
protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
return writeCharacteristic(gatt,characteristic,value);
}
/// shared write implementation that can be used without a BtLEAction
@SuppressLint("MissingPermission")
public static boolean writeCharacteristic(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
if (LOG.isDebugEnabled()) {
LOG.debug("writing to characteristic: {} : {}", characteristic.getUuid(), Logging.formatBytes(value));
LOG.debug("writing to characteristic: {} - {}", characteristic.getUuid(), Logging.formatBytes(value));
}
return writeCharacteristicImp(gatt, characteristic, value);
}
@SuppressLint("MissingPermission")
private static boolean writeCharacteristicImp(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
if (GBApplication.isRunningTiramisuOrLater()) {
// use API introduced in SDK level 33 to catch exceptions and more specific errors
try {
@@ -90,7 +89,7 @@ public class WriteAction extends BtLEAction {
return false;
}
public final byte[] getValue() {
public byte[] getValue() {
return value;
}
@@ -98,4 +97,12 @@ public class WriteAction extends BtLEAction {
public boolean expectsResult() {
return true;
}
@Override
public String toString() {
BluetoothGattCharacteristic characteristic = getCharacteristic();
String uuid = characteristic == null ? "(null)" : characteristic.getUuid().toString();
return getCreationTime() + ": " + getClass().getSimpleName() + " " + uuid + " - "
+ Logging.formatBytes(getValue());
}
}
@@ -730,9 +730,9 @@ public class UltrahumanDeviceSupport extends AbstractBTLESingleDeviceSupport {
}
@Override
protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
LOG.debug("GB>>UH write {} {}", Characteristic, StringUtils.bytesToHex(value));
return super.writeValue(gatt, characteristic, value);
public boolean run(BluetoothGatt gatt) {
LOG.debug("GB>>UH write {} {}", Characteristic, StringUtils.bytesToHex(getValue()));
return super.run(gatt);
}
}
@@ -754,24 +754,28 @@ public class UltrahumanDeviceSupport extends AbstractBTLESingleDeviceSupport {
/// calculate date/time on the fly to avoid setting an outdated value
private class UHSetTime extends UHWrite {
private byte[] actual;
UHSetTime(UltrahumanCharacteristic chara) {
super(chara);
}
@Override
protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
final Calendar calendar = DateTimeUtils.getCalendarUTC();
final long millis = calendar.getTimeInMillis();
final long epoc = Math.round(millis / 1000.0d);
public byte[] getValue(){
if (actual == null){
final Calendar calendar = DateTimeUtils.getCalendarUTC();
final long millis = calendar.getTimeInMillis();
final long epoc = Math.round(millis / 1000.0d);
byte[] command = new byte[]{
OPERATION_SETTIME,
(byte) (epoc & 0xff),
(byte) ((epoc >> 8) & 0xff),
(byte) ((epoc >> 16) & 0xff),
(byte) ((epoc >> 24) & 0xff)
};
return super.writeValue(gatt, characteristic, command);
actual = new byte[]{
OPERATION_SETTIME,
(byte) (epoc & 0xff),
(byte) ((epoc >> 8) & 0xff),
(byte) ((epoc >> 16) & 0xff),
(byte) ((epoc >> 24) & 0xff)
};
}
return actual;
}
}
}