mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
ble: standardize server actions (#5157)
This commit is contained in:
committed by
José Rebelo
parent
5526e9528e
commit
f03ac358fa
+6
-2
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2019-2024 Andreas Böhler
|
/* Copyright (C) 2019-2025 Andreas Böhler, Thomas Kuehne
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
|||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothGattServer;
|
import android.bluetooth.BluetoothGattServer;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,7 +66,9 @@ public abstract class BtLEServerAction {
|
|||||||
return DateTimeUtils.formatLocalTime(creationTimestamp);
|
return DateTimeUtils.formatLocalTime(creationTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getCreationTime() + ":" + getClass().getSimpleName() + " on device: " + getDevice().getAddress();
|
return getCreationTime() + " " + getClass().getSimpleName() + " on " + getDevice().getAddress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-5
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2019-2024 Andreas Böhler, Damien Gaignon
|
/* Copyright (C) 2019-2025 Andreas Böhler, Damien Gaignon, Thomas Kuehne
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@@ -17,11 +17,15 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.NotifyCharacteristicChangedAction;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ServerResponseAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ServerResponseAction;
|
||||||
|
|
||||||
public class ServerTransactionBuilder {
|
public class ServerTransactionBuilder {
|
||||||
@@ -34,6 +38,7 @@ public class ServerTransactionBuilder {
|
|||||||
mTransaction = new ServerTransaction(taskName);
|
mTransaction = new ServerTransaction(taskName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public ServerTransactionBuilder writeServerResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] data) {
|
public ServerTransactionBuilder writeServerResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] data) {
|
||||||
if(device == null) {
|
if(device == null) {
|
||||||
LOG.warn("Unable to write to device: null");
|
LOG.warn("Unable to write to device: null");
|
||||||
@@ -43,7 +48,20 @@ public class ServerTransactionBuilder {
|
|||||||
return add(action);
|
return add(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerTransactionBuilder add(BtLEServerAction action) {
|
@NonNull
|
||||||
|
public ServerTransactionBuilder notifyCharacteristicChanged(@NonNull BluetoothDevice device,
|
||||||
|
@NonNull BluetoothGattCharacteristic characteristic,
|
||||||
|
@NonNull byte[] value) {
|
||||||
|
if (device == null) {
|
||||||
|
LOG.warn("Unable to write to device: null");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
BtLEServerAction action = new NotifyCharacteristicChangedAction(device, characteristic, value);
|
||||||
|
return add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ServerTransactionBuilder add(@NonNull BtLEServerAction action) {
|
||||||
mTransaction.add(action);
|
mTransaction.add(action);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -66,10 +84,8 @@ public class ServerTransactionBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* To be used as the final step to execute the transaction by the given queue.
|
* To be used as the final step to execute the transaction by the given queue.
|
||||||
*
|
|
||||||
* @param queue
|
|
||||||
*/
|
*/
|
||||||
public void queue(BtLEQueue queue) {
|
public void queue(@NonNull BtLEQueue queue) {
|
||||||
if (mQueued) {
|
if (mQueued) {
|
||||||
throw new IllegalStateException("This builder had already been queued. You must not reuse it.");
|
throw new IllegalStateException("This builder had already been queued. You must not reuse it.");
|
||||||
}
|
}
|
||||||
|
|||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/* Copyright (C) 2023-2025 Frank Ertl, Thomas Kuehne
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.bluetooth.BluetoothGattServer;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BleNamesResolver;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEServerAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
|
public class NotifyCharacteristicChangedAction extends BtLEServerAction {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(NotifyCharacteristicChangedAction.class);
|
||||||
|
|
||||||
|
private final BluetoothGattCharacteristic characteristic;
|
||||||
|
private final byte[] value;
|
||||||
|
|
||||||
|
public NotifyCharacteristicChangedAction(@NonNull BluetoothDevice device,
|
||||||
|
@NonNull BluetoothGattCharacteristic characteristic,
|
||||||
|
byte[] value) {
|
||||||
|
super(device);
|
||||||
|
this.characteristic = characteristic;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean expectsResult() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothGattServer server) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
int status = server.notifyCharacteristicChanged(getDevice(), characteristic, false, value);
|
||||||
|
if (status == BluetoothStatusCodes.SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG.error("notifyCharacteristicChanged {} failed: {}",
|
||||||
|
characteristic.getUuid(), BleNamesResolver.getBluetoothStatusString(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characteristic.setValue(value)) {
|
||||||
|
if (server.notifyCharacteristicChanged(getDevice(), characteristic, false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG.error("notifyCharacteristicChanged {} failed", characteristic.getUuid());
|
||||||
|
} else {
|
||||||
|
LOG.error("setting value of characteristic {} failed", characteristic.getUuid());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String uuid = characteristic == null ? "(null)" : characteristic.getUuid().toString();
|
||||||
|
return super.toString() + " " + uuid + " - " + GB.hexdump(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-18
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2019-2024 Andreas Böhler
|
/* Copyright (C) 2019-2025 Andreas Böhler, Thomas Kuehne
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@@ -16,24 +16,19 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothGattCallback;
|
|
||||||
import android.bluetooth.BluetoothGattServer;
|
import android.bluetooth.BluetoothGattServer;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import androidx.annotation.NonNull;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEServerAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEServerAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a response on a given GATT characteristic read.
|
* Invokes a response on a given GATT characteristic read.
|
||||||
* The result status will be made available asynchronously through the
|
|
||||||
* {@link BluetoothGattCallback}
|
|
||||||
*/
|
*/
|
||||||
public class ServerResponseAction extends BtLEServerAction {
|
public class ServerResponseAction extends BtLEServerAction {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ServerResponseAction.class);
|
|
||||||
|
|
||||||
private final byte[] value;
|
private final byte[] value;
|
||||||
private final int requestId;
|
private final int requestId;
|
||||||
private final int status;
|
private final int status;
|
||||||
@@ -47,17 +42,10 @@ public class ServerResponseAction extends BtLEServerAction {
|
|||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
@Override
|
@Override
|
||||||
public boolean run(BluetoothGattServer server) {
|
public boolean run(BluetoothGattServer server) {
|
||||||
return writeValue(server, getDevice(), requestId, status, offset, value);
|
return server.sendResponse(getDevice(), requestId, status, offset, getValue());
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean writeValue(BluetoothGattServer gattServer, BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("writing to server: " + device.getAddress() + ": " + Logging.formatBytes(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return gattServer.sendResponse(device, requestId, status, offset, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final byte[] getValue() {
|
protected final byte[] getValue() {
|
||||||
@@ -68,4 +56,10 @@ public class ServerResponseAction extends BtLEServerAction {
|
|||||||
public boolean expectsResult() {
|
public boolean expectsResult() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " #" + requestId + " - " + GB.hexdump(getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -58,7 +58,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.ServerTransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.ServerTransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.activity.WithingsActivityType;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.activity.WithingsActivityType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.WithingsServerAction;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.WithingsUUID;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.WithingsUUID;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.conversation.ActivitySampleHandler;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.conversation.ActivitySampleHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.conversation.BatteryStateHandler;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.conversation.BatteryStateHandler;
|
||||||
@@ -441,8 +440,8 @@ public class WithingsSteelHRDeviceSupport extends AbstractBTLESingleDeviceSuppor
|
|||||||
public void sendAncsNotificationSourceNotification(NotificationSource notificationSource) {
|
public void sendAncsNotificationSourceNotification(NotificationSource notificationSource) {
|
||||||
try {
|
try {
|
||||||
ServerTransactionBuilder builder = performServer("notificationSourceNotification");
|
ServerTransactionBuilder builder = performServer("notificationSourceNotification");
|
||||||
notificationSourceCharacteristic.setValue(notificationSource.serialize());
|
byte[] data = notificationSource.serialize();
|
||||||
builder.add(new WithingsServerAction(device, notificationSourceCharacteristic));
|
builder.notifyCharacteristicChanged(device, notificationSourceCharacteristic, data);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Could not send notification.", e);
|
logger.error("Could not send notification.", e);
|
||||||
@@ -454,8 +453,7 @@ public class WithingsSteelHRDeviceSupport extends AbstractBTLESingleDeviceSuppor
|
|||||||
try {
|
try {
|
||||||
ServerTransactionBuilder builder = performServer("dataSourceNotification");
|
ServerTransactionBuilder builder = performServer("dataSourceNotification");
|
||||||
byte[] data = response.serialize();
|
byte[] data = response.serialize();
|
||||||
dataSourceCharacteristic.setValue(response.serialize());
|
builder.notifyCharacteristicChanged(device, dataSourceCharacteristic, data);
|
||||||
builder.add(new WithingsServerAction(device, dataSourceCharacteristic));
|
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Could not send notification.", e);
|
logger.error("Could not send notification.", e);
|
||||||
|
|||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/* Copyright (C) 2023-2024 Frank Ertl
|
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
|
||||||
|
|
||||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Gadgetbridge is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
|
||||||
import android.bluetooth.BluetoothGattCharacteristic;
|
|
||||||
import android.bluetooth.BluetoothGattServer;
|
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEServerAction;
|
|
||||||
|
|
||||||
public class WithingsServerAction extends BtLEServerAction
|
|
||||||
{
|
|
||||||
private BluetoothGattCharacteristic characteristic;
|
|
||||||
|
|
||||||
public WithingsServerAction(BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
|
|
||||||
super(device);
|
|
||||||
this.characteristic = characteristic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean expectsResult() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean run(BluetoothGattServer server) {
|
|
||||||
return server.notifyCharacteristicChanged(getDevice(), characteristic, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user