1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-22 06:41:06 +02:00

Binary sensor: initial support for devices using Binary Sensor Service

This commit is contained in:
Daniel Dakhno 2022-06-19 02:55:18 +02:00 committed by vanous
parent efbafedfd6
commit b29a8cefd2
25 changed files with 994 additions and 0 deletions

View File

@ -687,6 +687,10 @@
android:name=".devices.um25.Activity.DataActivity"
android:exported="true" />
<activity
android:name=".devices.binary_sensor.activity.DataActivity"
android:exported="true" />
<activity
android:name=".activities.GpxReceiverActivity"
android:label="@string/gpx_receiver_activity_title"

View File

@ -0,0 +1,70 @@
package nodomain.freeyourgadget.gadgetbridge.devices.binary_sensor.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.TextView;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.BinarySensorSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState;
public class DataActivity extends AbstractGBActivity {
TextView stateView, countView;
BroadcastReceiver stateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (stateView == null) {
return;
}
if (countView == null) {
return;
}
boolean is_closed = intent.getBooleanExtra("EXTRA_SENSOR_CLOSED", false);
int count = intent.getIntExtra("EXTRA_SENSOR_COUNT", -1);
stateView.setText(is_closed ? "CLOSED" : "OPEN");
stateView.setBackgroundResource(is_closed ? android.R.color.holo_green_light : android.R.color.holo_red_light);
countView.setText("Count: " + count);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binary_sensor_data);
stateView = findViewById(R.id.text_sensor_state);
countView = findViewById(R.id.text_sensor_count);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(BinarySensorSupport.ACTION_SENSOR_STATE_CHANGED);
LocalBroadcastManager.getInstance(getApplicationContext())
.registerReceiver(
stateReceiver,
filter
);
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(new Intent(BinarySensorSupport.ACTION_SENSOR_STATE_REQUEST));
}
@Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(getApplicationContext())
.unregisterReceiver(
stateReceiver
);
}
}

View File

@ -0,0 +1,157 @@
package nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator;
import android.app.Activity;
import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelUuid;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.binary_sensor.activity.DataActivity;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.BinarySensorSupport;
public class BinarySensorCoordinator extends AbstractBLEDeviceCoordinator {
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
}
@NonNull
@Override
public Collection<? extends ScanFilter> createBLEScanFilters() {
return Collections.singletonList(
new ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString(BinarySensorSupport.BINARY_SENSOR_SERVICE_UUID))
.build()
);
}
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
Log.d("coordinator", "candidate name: " + candidate.getName());
for(ParcelUuid service : candidate.getServiceUuids()){
if(service.getUuid().toString().equals(BinarySensorSupport.BINARY_SENSOR_SERVICE_UUID)){
return getDeviceType();
};
}
return DeviceType.UNKNOWN;
}
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return new int[0];
}
@Override
public DeviceType getDeviceType() {
return DeviceType.BINARY_SENSOR;
}
@Nullable
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return false;
}
@Override
public boolean supportsActivityTracking() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;
}
@Override
public boolean supportsFindDevice() {
return false;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return false;
}
@Override
public String getManufacturer() {
return "DIY";
}
@Override
public boolean supportsAppsManagement() {
return true;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return DataActivity.class;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return false;
}
@Override
public boolean supportsWeather() {
return false;
}
@Override
public int getBatteryCount() {
return 0;
}
@Override
public int getBondingStyle() {
return BONDING_STYLE_NONE;
}
}

View File

@ -113,6 +113,7 @@ public enum DeviceType {
BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35),
VESC_NRF(500, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc),
VESC_HM10(501, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc),
BINARY_SENSOR(510, R.drawable.ic_device_unknown, R.drawable.ic_device_unknown_disabled, R.string.devicetype_binary_sensor),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
private final int key;

View File

@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.BinarySensorSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.fitpro.FitProDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.banglejs.BangleJSDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGB6900DeviceSupport;
@ -318,6 +319,8 @@ public class DeviceSupportFactory {
return new ServiceDeviceSupport(new VescDeviceSupport(device.getType()));
case BOSE_QC35:
return new ServiceDeviceSupport(new QC35BaseSupport());
case BINARY_SENSOR:
return new ServiceDeviceSupport(new BinarySensorSupport());
}
return null;
}

View File

@ -0,0 +1,179 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor;
import android.net.Uri;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
public class BinarySensorBaseSupport extends AbstractBTLEDeviceSupport {
public BinarySensorBaseSupport(Logger logger) {
super(logger);
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
}
@Override
public void onDeleteNotification(int id) {
}
@Override
public void onSetTime() {
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
}
@Override
public void onSetCallState(CallSpec callSpec) {
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
}
@Override
public void onEnableRealtimeSteps(boolean enable) {
}
@Override
public void onInstallApp(Uri uri) {
}
@Override
public void onAppInfoReq() {
}
@Override
public void onAppStart(UUID uuid, boolean start) {
}
@Override
public void onAppDelete(UUID uuid) {
}
@Override
public void onAppConfiguration(UUID appUuid, String config, Integer id) {
}
@Override
public void onAppReorder(UUID[] uuids) {
}
@Override
public void onFetchRecordedData(int dataTypes) {
}
@Override
public void onReset(int flags) {
}
@Override
public void onHeartRateTest() {
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
}
@Override
public void onFindDevice(boolean start) {
}
@Override
public void onSetConstantVibration(int integer) {
}
@Override
public void onScreenshotReq() {
}
@Override
public void onEnableHeartRateSleepSupport(boolean enable) {
}
@Override
public void onSetHeartRateMeasurementInterval(int seconds) {
}
@Override
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
}
@Override
public void onDeleteCalendarEvent(byte type, long id) {
}
@Override
public void onSendConfiguration(String config) {
}
@Override
public void onReadConfiguration(String config) {
}
@Override
public void onTestNewFunction() {
}
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
}
@Override
public boolean useAutoConnect() {
return false;
}
}

View File

@ -0,0 +1,187 @@
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;
}
}

View File

@ -0,0 +1,22 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants;
public enum MessageId {
MESSAGE_ID_GET_SENSOR_REQUEST,
MESSAGE_ID_GET_SENSOR_RESPONSE,
MESSAGE_ID_SET_SENSOR_REQUEST,
MESSAGE_ID_SET_SENSOR_RESPONSE,
MESSAGE_ID_SENSOR_STATUS_EVENT;
public byte getMessageIdByte(){
return (byte) ordinal();
}
public static MessageId fromMessageIdByte(byte messageIdByte){
for(MessageId value:MessageId.values()){
if(value.getMessageIdByte() == messageIdByte){
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,30 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants;
public enum ParameterId {
PARAMETER_ID_RESULT_CODE((byte) 0x00),
PARAMETER_ID_CANCEL((byte) 0x01),
PARAMETER_ID_SENSOR_TYPE((byte) 0x02),
PARAMETER_ID_REPORT_STATUS((byte) 0x03),
PARAMETER_ID_SENSOR_STATUS((byte) 0x0A),
PARAMETER_ID_MULTIPLE_SENSOR_STATUS((byte) 0x0B),
PARAMETER_ID_NAME((byte) 0x0C);
private byte id;
ParameterId(byte id){
this.id = id;
}
public byte getParameterIdByte(){
return this.id;
}
public static ParameterId fromParameterIdByte(byte parameterId){
for(ParameterId id:ParameterId.values()){
if(id.getParameterIdByte() == parameterId){
return id;
}
}
return null;
}
}

View File

@ -0,0 +1,19 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants;
public enum ReportState {
REPORT_STATUS_DISABLED,
REPORT_STATUS_ENABLED;
public byte getReportStateByte(){
return (byte) ordinal();
}
public static ReportState fromReportStateByte(byte reportState){
for(ReportState value:ReportState.values()){
if(value.getReportStateByte() == reportState){
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,19 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants;
public enum ResultCode {
RESULT_CODE_SUCCESS,
RESULT_CODE_FAILURE;
public byte getResultCodeByte(){
return (byte) ordinal();
}
public static ResultCode fromResultCodeByte(byte resultCode){
for(ResultCode value:ResultCode.values()){
if(value.getResultCodeByte() == resultCode){
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,19 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants;
public enum SensorState {
SENSOR_STATE_CLOSED,
SENSOR_STATE_OPEN;
public byte getSensorStateByte(){
return (byte) ordinal();
}
public static SensorState fromSensorStateByte(byte sensorState){
for(SensorState value:SensorState.values()){
if(value.getSensorStateByte() == sensorState){
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,20 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants;
public enum SensorType {
SENSOR_TYPE_OPENING_CLOSING,
SENSOR_TYPE_VIBRATION_DETECTION,
SENSOR_TYPE_HUMAN_DETECTION;
public byte getSensorTypeByte(){
return (byte) ordinal();
}
public static SensorType fromSensorTypeByte(byte sensorType){
for(SensorType value:SensorType.values()){
if(value.getSensorTypeByte() == sensorType){
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,16 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.MessageId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.Parameter;
public class GetSensorRequest extends Message{
public GetSensorRequest(SensorType sensorType) {
super(
MessageId.MESSAGE_ID_GET_SENSOR_REQUEST,
new Parameter[]{
new nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.SensorType(sensorType)
}
);
}
}

View File

@ -0,0 +1,33 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.MessageId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.Parameter;
public class Message {
MessageId messageId;
Parameter[] parameters;
public Message(MessageId messageId, Parameter[] parameters) {
this.messageId = messageId;
this.parameters = parameters;
}
public byte[] encode(){
int dataLength = 4;
for(Parameter parameter : parameters){
dataLength += parameter.getPayloadLength() + 4;
}
ByteBuffer buffer = ByteBuffer.allocate(dataLength);
buffer
.put((byte) 0x00) // RFU
.put(messageId.getMessageIdByte()) // RFU
.put((byte) 0x00) // RFU
.put((byte) parameters.length);
for(Parameter parameter : parameters){
buffer.put(parameter.encode());
}
return buffer.array();
}
}

View File

@ -0,0 +1,22 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.MessageId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.Parameter;
public class Response {
MessageId messageId;
Parameter[] parameters;
public MessageId getMessageId() {
return messageId;
}
public Parameter[] getParameters() {
return parameters;
}
public Response(MessageId messageId, Parameter[] parameters) {
this.messageId = messageId;
this.parameters = parameters;
}
}

View File

@ -0,0 +1,18 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.message;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.MessageId;
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.parameter.Parameter;
public class SetSensorRequest extends Message{
public SetSensorRequest(SensorType sensorType, ReportState reportState) {
super(
MessageId.MESSAGE_ID_SET_SENSOR_REQUEST,
new Parameter[]{
new nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.SensorType(sensorType),
new nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter.ReportStatus(reportState)
}
);
}
}

View File

@ -0,0 +1,49 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ParameterId;
public class Parameter {
ParameterId parameterId;
byte[] payload;
public Parameter(ParameterId parameterId, byte... payload) {
this.parameterId = parameterId;
this.payload = payload;
}
public ParameterId getParameterId() {
return parameterId;
}
public int getPayloadLength(){
return payload.length;
}
public byte[] encode(){
ByteBuffer buffer = ByteBuffer.allocate(payload.length + 4);
buffer
.put(parameterId.getParameterIdByte())
.put((byte) payload.length)
.put((byte) 0x00) // RFU
.put((byte) 0x00) // RFU
.put(payload);
return buffer.array();
}
public static Parameter decode(ParameterId parameterId, byte[] payload){
if(parameterId == ParameterId.PARAMETER_ID_RESULT_CODE){
return ResultCode.decode(payload);
}else if(parameterId == ParameterId.PARAMETER_ID_REPORT_STATUS){
return ReportStatus.decode(payload);
}else if(parameterId == ParameterId.PARAMETER_ID_SENSOR_STATUS){
return SensorState.decode(payload);
}else if(parameterId == ParameterId.PARAMETER_ID_SENSOR_TYPE){
return SensorType.decode(payload);
}
return null;
}
}

View File

@ -0,0 +1,18 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ParameterId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ReportState;
public class ReportStatus extends Parameter{
ReportState reportState;
public ReportStatus(nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ReportState reportState) {
super(ParameterId.PARAMETER_ID_REPORT_STATUS, reportState.getReportStateByte());
this.reportState = reportState;
}
public static ReportStatus decode(byte[] data){
return new ReportStatus(
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ReportState.fromReportStateByte(data[0])
);
}
}

View File

@ -0,0 +1,17 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ParameterId;
public class ResultCode extends Parameter{
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ResultCode resultCode;
public ResultCode(nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ResultCode resultCode) {
super(ParameterId.PARAMETER_ID_RESULT_CODE, resultCode.getResultCodeByte());
this.resultCode = resultCode;
}
public static ResultCode decode(byte[] data){
return new ResultCode(
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ResultCode.fromResultCodeByte(data[0])
);
}
}

View File

@ -0,0 +1,32 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ParameterId;
public class SensorState extends Parameter{
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState sensorState;
int count;
public SensorState(nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState sensorState, int count) {
super(ParameterId.PARAMETER_ID_SENSOR_STATUS, sensorState.getSensorStateByte());
this.sensorState = sensorState;
this.count = count;
}
public nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState getSensorState() {
return sensorState;
}
public int getCount() {
return count;
}
public static SensorState decode(byte[] data){
int dataInt = (data[1] << 8) | data[0];
byte stateByte = (byte)((dataInt >> 11) & 0x01);
int count = dataInt & 0b11111111111;
return new SensorState(
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorState.fromSensorStateByte(stateByte),
count
);
}
}

View File

@ -0,0 +1,18 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.parameter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.ParameterId;
public class SensorType extends Parameter{
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorType sensorType;
public SensorType(nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorType sensorType) {
super(ParameterId.PARAMETER_ID_SENSOR_TYPE, sensorType.getSensorTypeByte());
this.sensorType = sensorType;
}
public static SensorType decode(byte[] data){
return new SensorType(
nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.protocol.constants.SensorType.fromSensorTypeByte(data[0])
);
}
}

View File

@ -114,6 +114,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss.SMAQ2OSSCoordinator
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyWH1000XM4Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.BinarySensorCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.vesc.VescCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator;
@ -331,6 +332,7 @@ public class DeviceHelper {
result.add(new SonyWFSP800NCoordinator());
result.add(new SonyWF1000XM3Coordinator());
result.add(new QC35Coordinator());
result.add(new BinarySensorCoordinator());
return result;
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal">
<TextView
android:id="@+id/text_sensor_state"
android:layout_width="250dp"
android:layout_height="250dp"
android:background="@android:color/holo_red_light"
android:text="unknown"
android:textSize="50dp"
android:gravity="center"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="unknown"
android:textSize="50dp"
android:id="@+id/text_sensor_count"/>
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1754,4 +1754,5 @@
<string name="steps_streaks_total_steps_hint">Total number of steps in the whole streak</string>
<string name="steps_streaks_total_steps_average_hint">Total average %d steps per day</string>
<string name="devicetype_binary_sensor">Binary sensor</string>
</resources>