1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-25 14:31:05 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorSupport.java
2024-01-10 19:01:48 +01:00

204 lines
8.5 KiB
Java

/* Copyright (C) 2022-2024 Daniel Dakhno
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.binary_sensor;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.widget.Toast;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.MessageId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ParameterId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ReportState;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message.GetSensorRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message.Response;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message.SetSensorRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.Parameter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.SensorState;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class BinarySensorSupport extends BinarySensorBaseSupport {
final public static String BINARY_SENSOR_SERVICE_UUID = "0000183b-0000-1000-8000-00805f9b34fb";
final public static String BINARY_SENSOR_CONTROL_CHARACTERISTIC_UUID = "00002b2b-0000-1000-8000-00805f9b34fb";
final public static String BINARY_SENSOR_RESPONSE_CHARACTERISTIC_UUID = "00002b2c-0000-1000-8000-00805f9b34fb";
final public static String ACTION_SENSOR_STATE_CHANGED = "nodomain.freeyourgadget.gadgetbridge.binary_sensor.STATE_CHANGED";
final public static String ACTION_SENSOR_STATE_REQUEST = "nodomain.freeyourgadget.gadgetbridge.binary_sensor.STATE_REQUEST";
private static final Logger logger = LoggerFactory.getLogger(BinarySensorSupport.class);
private nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState sensorState = null;
private int sensorCount = -1;
public BinarySensorSupport() {
super(logger);
addSupportedService(UUID.fromString(BINARY_SENSOR_SERVICE_UUID));
LocalBroadcastManager.getInstance(getContext())
.registerReceiver(
stateRequestReceiver,
new IntentFilter(ACTION_SENSOR_STATE_REQUEST)
);
}
@Override
public void dispose() {
super.dispose();
LocalBroadcastManager.getInstance(getContext())
.unregisterReceiver(stateRequestReceiver);
}
BroadcastReceiver stateRequestReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
sendStateChangeIntent(false);
}
};
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().toString().equals(BINARY_SENSOR_RESPONSE_CHARACTERISTIC_UUID)) {
handleResponseValue(characteristic.getValue());
return true;
}
return false;
}
Response decodeResponse(byte[] value) {
ByteBuffer buffer = ByteBuffer.wrap(value);
buffer.get(); // split packet header
buffer.get(); // RFU
byte messageIdByte = buffer.get();
buffer.get(); // RFU
int parameterCount = buffer.get();
Parameter[] parameters = new Parameter[parameterCount];
MessageId messageId = MessageId.fromMessageIdByte(messageIdByte);
for (int i = 0; i < parameterCount; i++) {
byte parameterIdByte = buffer.get();
byte payloadLength = buffer.get();
buffer.get(); // RFU
buffer.get(); // RFU
ParameterId parameterId = ParameterId.fromParameterIdByte(parameterIdByte);
byte[] payload = new byte[payloadLength];
buffer.get(payload);
parameters[i] = Parameter.decode(parameterId, payload);
}
return new Response(
messageId,
parameters
);
}
void sendStateChangeIntent(boolean sendGlobally){
Intent intent = new Intent(ACTION_SENSOR_STATE_CHANGED);
intent.putExtra("EXTRA_SENSOR_CLOSED", sensorState == nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState.SENSOR_STATE_CLOSED);
intent.putExtra("EXTRA_SENSOR_COUNT", sensorCount);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
if(sendGlobally) {
getContext().sendBroadcast(intent);
}
}
void handleResponseValue(byte[] value) {
Response response = decodeResponse(value);
for (Parameter parameter : response.getParameters()) {
if (parameter instanceof SensorState) {
if(getDevice().getState() != GBDevice.State.INITIALIZED){
new TransactionBuilder("set device state")
.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()))
.queue(getQueue());
}
SensorState stateParameter = (SensorState) parameter;
logger.debug("sensor state: " + stateParameter.getSensorState() + " count: " + stateParameter.getCount());
this.sensorState = stateParameter.getSensorState();
this.sensorCount = stateParameter.getCount();
sendStateChangeIntent(true);
}
}
}
private void sendPacketToDevice(byte[] data, TransactionBuilder builder) {
byte[] fullData = new byte[data.length + 1];
fullData[0] = 0x00;
System.arraycopy(data, 0, fullData, 1, data.length);
builder.write(getCharacteristic(UUID.fromString(BINARY_SENSOR_CONTROL_CHARACTERISTIC_UUID)), fullData);
}
private void sendPacketToDevice(byte[] data) {
TransactionBuilder builder = new TransactionBuilder("BSS control");
sendPacketToDevice(data, builder);
builder.queue(getQueue());
}
@Override
public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
GB.toast("error setting indication", Toast.LENGTH_LONG, GB.ERROR);
}
return true;
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
logger.debug("initializing device");
builder
.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()))
.notify(getCharacteristic(UUID.fromString(BINARY_SENSOR_RESPONSE_CHARACTERISTIC_UUID)), true)
;
SetSensorRequest setSensorRequest = new SetSensorRequest(SensorType.SENSOR_TYPE_OPENING_CLOSING, ReportState.REPORT_STATUS_ENABLED);
GetSensorRequest getSensorRequest = new GetSensorRequest(SensorType.SENSOR_TYPE_OPENING_CLOSING);
sendPacketToDevice(getSensorRequest.encode(), builder);
sendPacketToDevice(setSensorRequest.encode(), builder);
return builder;
}
}