From c7a6dc4b01c0122c75239f4e06a5e370797d55d1 Mon Sep 17 00:00:00 2001 From: Sophanimus Date: Fri, 5 Apr 2019 21:52:04 +0200 Subject: [PATCH] improvements --- .../devices/bfh16/BFH16Constants.java | 29 +- .../devices/bfh16/BFH16DeviceSupport.java | 414 ++++++++++-------- 2 files changed, 244 insertions(+), 199 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16Constants.java index b3437843a..5a4724690 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16Constants.java @@ -59,11 +59,25 @@ public final class BFH16Constants { //Verified receive bytes + public static final byte RECEIVE_DEVICE_INFO = (byte)0xF6; public static final byte RECEIVE_BATTERY_LEVEL = (byte)0xF7; public static final byte RECEIVE_STEPS_DATA = (byte)0xF9; public static final byte RECEIVE_HEART_DATA = (byte)0xE8; public static final byte RECEIVE_PHOTO_TRIGGER = (byte)0xF3; + //Verified icon bytes + public static final byte ICON_CALL = (byte)0x00; + public static final byte ICON_SMS = (byte)0x01; + public static final byte ICON_WECHAT = (byte)0x02; + public static final byte ICON_QQ = (byte)0x03; + public static final byte ICON_FACEBOOK = (byte)0x04; + public static final byte ICON_SKYPE = (byte)0x05; + public static final byte ICON_TWITTER = (byte)0x06; + public static final byte ICON_WHATSAPP = (byte)0x07; + public static final byte ICON_LINE = (byte)0x08; + public static final byte ICON_TALK = (byte)0x09; + public static final byte ICON_RUNNER = (byte)0x0A; + //Most probably correct command bytes @@ -110,19 +124,4 @@ public final class BFH16Constants { public static final byte CMD_ACTION_HEARTRATE_SWITCH = 0x0D; public static final byte CMD_ACTION_SHOW_NOTIFICATION = 0x2C; public static final byte CMD_ACTION_REBOOT_DEVICE = 0x0E; - - - public static final byte RECEIVE_DEVICE_INFO = (byte)0xF6; - - - public static final byte ICON_CALL = 0; - public static final byte ICON_SMS = 1; - public static final byte ICON_WECHAT = 2; - public static final byte ICON_QQ = 3; - public static final byte ICON_FACEBOOK = 4; - public static final byte ICON_SKYPE = 5; - public static final byte ICON_TWITTER = 6; - public static final byte ICON_WHATSAPP = 7; - public static final byte ICON_LINE = 8; - } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bfh16/BFH16DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bfh16/BFH16DeviceSupport.java index e0fd9152b..e535acd11 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bfh16/BFH16DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bfh16/BFH16DeviceSupport.java @@ -15,6 +15,38 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +/* + Features: + Working + work in progress + possible + not supported + Get firmware version (X) () (X) () + Get battery level (X) () (X) () + + Set alarms (1-3) (X) () (X) () + Sync date and time (X) () (X) () + Find device (X) () (X) () + + Switch 12/24 hour mode () () (X) () + Set step goal () () (X) () + Set sitting reminder () () (X) () + Trigger a photo () () (X) () + + Switch automated heartbeat detection () () (X) () + Switch display illumination () () (X) () + Switch vibration () () (X) () + Switch notifications () () (X) () + Set do not distract time () () (X) () + + Get Steps () () (X) () + Get Heart Rate () () (X) () + Get Blood Pressure () () (x) () + Get Blood Satiation () () (X) () + + Send Notification () () (X) () + + */ package nodomain.freeyourgadget.gadgetbridge.service.devices.bfh16; @@ -36,6 +68,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.pebble.GBDeviceEventDataLogging; import nodomain.freeyourgadget.gadgetbridge.devices.bfh16.BFH16Constants; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; @@ -67,6 +100,11 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { addSupportedService(BFH16Constants.BFH16_SERVICE2); } + @Override + public boolean useAutoConnect() { + return true; + } + @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { LOG.info("Initializing BFH16"); @@ -90,6 +128,9 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { return builder; } + //onXYZ + //______________________________________________________________________________________________ + @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { @@ -116,160 +157,28 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { return true; case BFH16Constants.RECEIVE_STEPS_DATA: int steps = ByteBuffer.wrap(data, 5, 4).getInt(); + //TODO handle step data LOG.info("Number of walked steps: " + steps); return true; case BFH16Constants.RECEIVE_HEART_DATA: + //TODO handle heart data LOG.info("Current heart rate: " + data[1]); LOG.info("Current blood pressure: " + data[2] + "/" + data[3]); LOG.info("Current satiation: " + data[4]); return true; case BFH16Constants.RECEIVE_PHOTO_TRIGGER: + //TODO handle photo trigger LOG.info("Received photo trigger: " + data[8]); return true; default: LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0])); + LOG.info("Unhandled characteristic data: "+ data[0]+" "+data[1]+" "+data[2]+" "+data[3]+" "+data[4]+" "+data[5]+" "+data[6]+" "+data[7]+" "+data[8]); return true; } } - //Fully working - private void syncDateAndTime(TransactionBuilder builder) { - Calendar cal = Calendar.getInstance(); - String strYear = String.valueOf(cal.get(Calendar.YEAR)); - byte year1 = (byte)Integer.parseInt(strYear.substring(0, 2)); - byte year2 = (byte)Integer.parseInt(strYear.substring(2, 4)); - byte month = (byte)cal.get(Calendar.MONTH); - byte day = (byte)cal.get(Calendar.DAY_OF_MONTH); - byte hour = (byte)cal.get(Calendar.HOUR_OF_DAY); - byte minute = (byte)cal.get(Calendar.MINUTE); - byte second = (byte)cal.get(Calendar.SECOND); - byte weekDay = (byte)cal.get(Calendar.DAY_OF_WEEK); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_DATE_AND_TIME, - (year1 << 24) | (year2 << 16) | (month << 8) | day, - (hour << 24) | (minute << 16) | (second << 8) | weekDay - )); - } - - //TODO: not checked yet - private void syncSettings(TransactionBuilder builder) { - syncDateAndTime(builder); - - // TODO: unhardcode and separate stuff - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_HEARTRATE_WARNING_VALUE, 0, 152 - )); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_TARGET_STEPS, 0, 10000 - )); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SWITCH_METRIC_IMPERIAL, 0, 0 - )); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_GET_SLEEP_TIME, 0, 0 - )); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_NOON_TIME, 12 * 60 * 60, 14 * 60 * 60 // 12:00 - 14:00 - )); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_SLEEP_TIME, 21 * 60 * 60, 8 * 60 * 60 // 21:00 - 08:00 - )); - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_INACTIVITY_WARNING_TIME, 0, 0 - )); - - // do not disturb and a couple more features - byte dndStartHour = 22; - byte dndStartMin = 0; - byte dndEndHour = 8; - byte dndEndMin = 0; - boolean dndToggle = false; - boolean vibrationToggle = true; - boolean wakeOnRaiseToggle = true; - builder.write(ctrlCharacteristic, commandWithChecksum( - BFH16Constants.CMD_SET_DND_SETTINGS, - (dndStartHour << 24) | (dndStartMin << 16) | (dndEndHour << 8) | dndEndMin, - ((dndToggle ? 0 : 1) << 2) | ((vibrationToggle ? 1 : 0) << 1) | (wakeOnRaiseToggle ? 1 : 0) - )); - } - - //TODO: not checked yet + needs rework - private void showNotification(byte icon, String title, String message) { - try { - TransactionBuilder builder = performInitialized("ShowNotification"); - - byte[] titleBytes = stringToUTF8Bytes(title, 16); - byte[] messageBytes = stringToUTF8Bytes(message, 80); - - for (int i = 1; i <= 7; i++) - { - byte[] currentPacket = new byte[20]; - currentPacket[0] = BFH16Constants.CMD_ACTION_SHOW_NOTIFICATION; - currentPacket[1] = 7; - currentPacket[2] = (byte)i; - switch(i) { - case 1: - currentPacket[4] = icon; - break; - case 2: - if (titleBytes != null) { - System.arraycopy(titleBytes, 0, currentPacket, 3, 6); - System.arraycopy(titleBytes, 6, currentPacket, 10, 10); - } - break; - default: - if (messageBytes != null) { - System.arraycopy(messageBytes, 16 * (i - 3), currentPacket, 3, 6); - System.arraycopy(messageBytes, 6 + 16 * (i - 3), currentPacket, 10, 10); - } - break; - } - builder.write(ctrlCharacteristic, currentPacket); - } - builder.queue(getQueue()); - } catch (IOException e) { - LOG.warn(e.getMessage()); - } - } - - @Override - public boolean useAutoConnect() { - return true; - } - - //TODO: not checked yet + needs rework - @Override - public void onNotification(NotificationSpec notificationSpec) { - String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title); - byte icon; - switch (notificationSpec.type) { - case GENERIC_SMS: - icon = BFH16Constants.ICON_SMS; - break; - case FACEBOOK: - case FACEBOOK_MESSENGER: - icon = BFH16Constants.ICON_FACEBOOK; - break; - case TWITTER: - icon = BFH16Constants.ICON_TWITTER; - break; - case WHATSAPP: - icon = BFH16Constants.ICON_WHATSAPP; - break; - default: - icon = BFH16Constants.ICON_LINE; - break; - } - showNotification(icon, notificationTitle, notificationSpec.body); - } - - @Override - public void onDeleteNotification(int id) { - - } - - //fully working + //working @Override public void onSetAlarms(ArrayList alarms) { try { @@ -305,6 +214,38 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { } } + + //TODO: not checked yet + needs rework + @Override + public void onNotification(NotificationSpec notificationSpec) { + String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title); + byte icon; + switch (notificationSpec.type) { + case GENERIC_SMS: + icon = BFH16Constants.ICON_SMS; + break; + case FACEBOOK: + case FACEBOOK_MESSENGER: + icon = BFH16Constants.ICON_FACEBOOK; + break; + case TWITTER: + icon = BFH16Constants.ICON_TWITTER; + break; + case WHATSAPP: + icon = BFH16Constants.ICON_WHATSAPP; + break; + default: + icon = BFH16Constants.ICON_LINE; + break; + } + showNotification(icon, notificationTitle, notificationSpec.body); + } + + @Override + public void onDeleteNotification(int id) { + + } + @Override public void onSetTime() { try { @@ -424,10 +365,16 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { //working @Override public void onFindDevice(boolean start) { - if (start) { - showNotification(BFH16Constants.ICON_QQ, "Gadgetbridge", "Bzzt! Bzzt!"); - GB.toast(getContext(), "As your device doesn't have sound, it will only vibrate 3 times consecutively", Toast.LENGTH_LONG, GB.INFO); + try { + TransactionBuilder builder = performInitialized("FindDevice"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_VIBRATE, 0, start ? 1 : 0 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); } + GB.toast(getContext(), "Your device will vibrate 3 times!", Toast.LENGTH_LONG, GB.INFO); } @Override @@ -481,33 +428,20 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { - try { - TransactionBuilder builder = performInitialized("TestNewFunction"); + showNotification((byte)0xFF, "", ""); - //byte cmd = BFH16Constants.CMD_GET_STEP_COUNT; - - //Wakeup? Photo? - //builder.write(ctrlCharacteristic, commandWithChecksum((byte) 0x25, 0, 0)); - //builder.write(ctrlCharacteristic, commandWithChecksum((byte) 0x25, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00)); - - builder.write(ctrlCharacteristic, commandWithChecksum( (byte)1, 0, 200)); - -// builder.write(ctrlCharacteristic, commandWithChecksum((byte) 0xF, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00)); - - -// builder.write(ctrlCharacteristic, commandWithChecksum( -// (byte) 39, (byte) 0, (byte) 0, -// (byte) 189, (byte) 216, (byte) 0, -// (byte) 0, (byte) 196, (byte) 224)); - - - builder.queue(getQueue()); - - GB.toast(getContext(), "TestNewFunction executed!", Toast.LENGTH_LONG, GB.INFO); - - } catch(IOException e) { - LOG.warn(e.getMessage()); - } +// try { +// TransactionBuilder builder = performInitialized("TestNewFunction"); +// +// //Test get sleep time +// builder.write(ctrlCharacteristic, commandWithChecksum( (byte)0x32, 0, 0)); +// builder.queue(getQueue()); +// +// GB.toast(getContext(), "TestNewFunction executed!", Toast.LENGTH_LONG, GB.INFO); +// +// } catch(IOException e) { +// LOG.warn(e.getMessage()); +// } } @@ -516,6 +450,136 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { } + + + //FUNCTIONS + //______________________________________________________________________________________________ + + //TODO: check + private void showNotification(byte icon, String title, String message) { + try { + TransactionBuilder builder = performInitialized("ShowNotification"); + + byte[] titleBytes = stringToUTF8Bytes(title, 16); + byte[] messageBytes = stringToUTF8Bytes(message, 80); + + for (int i = 1; i <= 7; i++) + { + byte[] currentPacket = new byte[20]; + currentPacket[0] = BFH16Constants.CMD_ACTION_SHOW_NOTIFICATION; + currentPacket[1] = 7; + currentPacket[2] = (byte)i; + switch(i) { + case 1: + currentPacket[4] = icon; + break; + case 2: + if (titleBytes != null) { + System.arraycopy(titleBytes, 0, currentPacket, 3, 6); + System.arraycopy(titleBytes, 6, currentPacket, 10, 10); + } + break; + default: + if (messageBytes != null) { + System.arraycopy(messageBytes, 16 * (i - 3), currentPacket, 3, 6); + System.arraycopy(messageBytes, 6 + 16 * (i - 3), currentPacket, 10, 10); + } + break; + } + builder.write(ctrlCharacteristic, currentPacket); + } + builder.queue(getQueue()); + } catch (IOException e) { + LOG.warn(e.getMessage()); + } + } + + + //working + private void syncDateAndTime(TransactionBuilder builder) { + Calendar cal = Calendar.getInstance(); + String strYear = String.valueOf(cal.get(Calendar.YEAR)); + byte year1 = (byte)Integer.parseInt(strYear.substring(0, 2)); + byte year2 = (byte)Integer.parseInt(strYear.substring(2, 4)); + byte month = (byte)cal.get(Calendar.MONTH); + byte day = (byte)cal.get(Calendar.DAY_OF_MONTH); + byte hour = (byte)cal.get(Calendar.HOUR_OF_DAY); + byte minute = (byte)cal.get(Calendar.MINUTE); + byte second = (byte)cal.get(Calendar.SECOND); + byte weekDay = (byte)cal.get(Calendar.DAY_OF_WEEK); + + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_DATE_AND_TIME, + (year1 << 24) | (year2 << 16) | (month << 8) | day, + (hour << 24) | (minute << 16) | (second << 8) | weekDay + )); + } + + + //TODO: check + private void syncSettings(TransactionBuilder builder) { + syncDateAndTime(builder); + + // TODO: unhardcode and separate stuff + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_HEARTRATE_WARNING_VALUE, 0, 152 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_TARGET_STEPS, 0, 10000 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SWITCH_METRIC_IMPERIAL, 0, 0 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_GET_SLEEP_TIME, 0, 0 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_NOON_TIME, 12 * 60 * 60, 14 * 60 * 60 // 12:00 - 14:00 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_SLEEP_TIME, 21 * 60 * 60, 8 * 60 * 60 // 21:00 - 08:00 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_INACTIVITY_WARNING_TIME, 0, 0 + )); + + // do not disturb and a couple more features + byte dndStartHour = 22; + byte dndStartMin = 0; + byte dndEndHour = 8; + byte dndEndMin = 0; + boolean dndToggle = false; + boolean vibrationToggle = true; + boolean wakeOnRaiseToggle = true; + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_DND_SETTINGS, + (dndStartHour << 24) | (dndStartMin << 16) | (dndEndHour << 8) | dndEndMin, + ((dndToggle ? 0 : 1) << 2) | ((vibrationToggle ? 1 : 0) << 1) | (wakeOnRaiseToggle ? 1 : 0) + )); + } + + + //working + private byte[] commandWithChecksum(byte cmd, int argSlot1, int argSlot2) + { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(cmd); + buf.putInt(argSlot1); + buf.putInt(argSlot2); + + byte[] bytesToWrite = buf.array(); + + byte checksum = 0; + for (byte b : bytesToWrite) { + checksum += b; + } + + bytesToWrite[9] = checksum; + + return bytesToWrite; + } + + /** * Checksum is calculated by the sum of bytes 0 to 8 and send as byte 9 */ @@ -548,24 +612,6 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { return bytesToWrite; } - private byte[] commandWithChecksum(byte cmd, int argSlot1, int argSlot2) - { - ByteBuffer buf = ByteBuffer.allocate(10); - buf.put(cmd); - buf.putInt(argSlot1); - buf.putInt(argSlot2); - - byte[] bytesToWrite = buf.array(); - - byte checksum = 0; - for (byte b : bytesToWrite) { - checksum += b; - } - - bytesToWrite[9] = checksum; - - return bytesToWrite; - } private byte[] stringToUTF8Bytes(String src, int byteCount) { try {