From b29a8cefd266be8748f4e40b109bd6ab6955835a Mon Sep 17 00:00:00 2001 From: Daniel Dakhno Date: Sun, 19 Jun 2022 02:55:18 +0200 Subject: [PATCH] Binary sensor: initial support for devices using Binary Sensor Service --- app/src/main/AndroidManifest.xml | 4 + .../binary_sensor/activity/DataActivity.java | 70 +++++++ .../coordinator/BinarySensorCoordinator.java | 157 +++++++++++++++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 3 + .../BinarySensorBaseSupport.java | 179 +++++++++++++++++ .../binary_sensor/BinarySensorSupport.java | 187 ++++++++++++++++++ .../protocol/constants/MessageId.java | 22 +++ .../protocol/constants/ParameterId.java | 30 +++ .../protocol/constants/ReportState.java | 19 ++ .../protocol/constants/ResultCode.java | 19 ++ .../protocol/constants/SensorState.java | 19 ++ .../protocol/constants/SensorType.java | 20 ++ .../protocol/message/GetSensorRequest.java | 16 ++ .../protocol/message/Message.java | 33 ++++ .../protocol/message/Response.java | 22 +++ .../protocol/message/SetSensorRequest.java | 18 ++ .../protocol/parameter/Parameter.java | 49 +++++ .../protocol/parameter/ReportStatus.java | 18 ++ .../protocol/parameter/ResultCode.java | 17 ++ .../protocol/parameter/SensorState.java | 32 +++ .../protocol/parameter/SensorType.java | 18 ++ .../gadgetbridge/util/DeviceHelper.java | 2 + .../layout/activity_binary_sensor_data.xml | 38 ++++ app/src/main/res/values/strings.xml | 1 + 25 files changed, 994 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/binary_sensor/activity/DataActivity.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/binary_sensor/coordinator/BinarySensorCoordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorBaseSupport.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorSupport.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/MessageId.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ParameterId.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ReportState.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ResultCode.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorState.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorType.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/GetSensorRequest.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Message.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Response.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/SetSensorRequest.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/Parameter.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ReportStatus.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ResultCode.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorState.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorType.java create mode 100644 app/src/main/res/layout/activity_binary_sensor_data.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96a65c05b..c776c1e1c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -687,6 +687,10 @@ android:name=".devices.um25.Activity.DataActivity" android:exported="true" /> + + 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 getPairingActivity() { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider 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 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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index fed1569e4..e35906031 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -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; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 164669ec3..a9eb794a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -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; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorBaseSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorBaseSupport.java new file mode 100644 index 000000000..df2e80405 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorBaseSupport.java @@ -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 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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorSupport.java new file mode 100644 index 000000000..bd176451a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/BinarySensorSupport.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/MessageId.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/MessageId.java new file mode 100644 index 000000000..f3e1ef104 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/MessageId.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ParameterId.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ParameterId.java new file mode 100644 index 000000000..7559b2b9e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ParameterId.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ReportState.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ReportState.java new file mode 100644 index 000000000..cb3af1a5b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ReportState.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ResultCode.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ResultCode.java new file mode 100644 index 000000000..f59d4525a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/ResultCode.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorState.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorState.java new file mode 100644 index 000000000..5bf67c1a3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorState.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorType.java new file mode 100644 index 000000000..0e66a545e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/constants/SensorType.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/GetSensorRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/GetSensorRequest.java new file mode 100644 index 000000000..6d79c1bd9 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/GetSensorRequest.java @@ -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) + } + ); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Message.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Message.java new file mode 100644 index 000000000..a75d25ae4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Message.java @@ -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(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Response.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Response.java new file mode 100644 index 000000000..074663740 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/Response.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/SetSensorRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/SetSensorRequest.java new file mode 100644 index 000000000..676760a4f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/message/SetSensorRequest.java @@ -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) + } + ); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/Parameter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/Parameter.java new file mode 100644 index 000000000..2cd9fd445 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/Parameter.java @@ -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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ReportStatus.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ReportStatus.java new file mode 100644 index 000000000..bcae3994d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ReportStatus.java @@ -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]) + ); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ResultCode.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ResultCode.java new file mode 100644 index 000000000..66630249f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/ResultCode.java @@ -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]) + ); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorState.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorState.java new file mode 100644 index 000000000..fcf36f9e8 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorState.java @@ -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 + ); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorType.java new file mode 100644 index 000000000..62b1d7ef1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/binary_sensor/protocol/parameter/SensorType.java @@ -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]) + ); + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index af571ba89..d7d2fe3f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -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; } diff --git a/app/src/main/res/layout/activity_binary_sensor_data.xml b/app/src/main/res/layout/activity_binary_sensor_data.xml new file mode 100644 index 000000000..5c690b85e --- /dev/null +++ b/app/src/main/res/layout/activity_binary_sensor_data.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b979cd076..9898c2567 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1754,4 +1754,5 @@ Total number of steps in the whole streak Total average %d steps per day + Binary sensor