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 90a7fb3d6..ffa240279 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 @@ -13,11 +13,51 @@ public final class BFH16Constants { public static final UUID BFH16_MAIN_SERVICE = UUID.fromString(String.format(BASE_UUID, "33F4") ); - + //Known Services public static final UUID BFH16_SERVICE_1 = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"); public static final UUID BFH16_SERVICE_2 = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb"); //Attribute public static final UUID BFH16_SERVICE_3 = UUID.fromString("000056ff-0000-1000-8000-00805f9b34fb"); //Service public static final UUID BFH16_SERVICE_4 = UUID.fromString("0000fee7-0000-1000-8000-00805f9b34fb"); //Service + + + //I Think i discovered that this device (BFH16) uses communication similar to JYOU + //Therefore i copied the following vars: + public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("000033f3-0000-1000-8000-00805f9b34fb"); + public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("000033f4-0000-1000-8000-00805f9b34fb"); + public static final UUID UUID_SERVICE_JYOU = UUID.fromString("000056ff-0000-1000-8000-00805f9b34fb"); + + public static final byte CMD_SET_DATE_AND_TIME = 0x08; + public static final byte CMD_SET_HEARTRATE_AUTO = 0x38; + public static final byte CMD_SET_HEARTRATE_WARNING_VALUE = 0x01; + public static final byte CMD_SET_TARGET_STEPS = 0x03; + public static final byte CMD_SET_ALARM_1 = 0x09; + public static final byte CMD_SET_ALARM_2 = 0x22; + public static final byte CMD_SET_ALARM_3 = 0x23; + public static final byte CMD_GET_STEP_COUNT = 0x1D; + public static final byte CMD_GET_SLEEP_TIME = 0x32; + public static final byte CMD_SET_NOON_TIME = 0x26; + public static final byte CMD_SET_SLEEP_TIME = 0x27; + public static final byte CMD_SET_DND_SETTINGS = 0x39; + public static final byte CMD_SET_INACTIVITY_WARNING_TIME = 0x24; + 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_BATTERY_LEVEL = (byte)0xF7; + public static final byte RECEIVE_DEVICE_INFO = (byte)0xF6; + public static final byte RECEIVE_STEPS_DATA = (byte)0xF9; + public static final byte RECEIVE_HEARTRATE = (byte)0xE8; + + 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/devices/bfh16/BFH16DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16DeviceCoordinator.java index 836f61f0d..474344728 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bfh16/BFH16DeviceCoordinator.java @@ -69,7 +69,7 @@ public class BFH16DeviceCoordinator extends AbstractDeviceCoordinator @Override public int getBondingStyle(GBDevice deviceCandidate){ - return BONDING_STYLE_NONE; //Might be wrong? + return BONDING_STYLE_NONE; } @Override @@ -77,187 +77,116 @@ public class BFH16DeviceCoordinator extends AbstractDeviceCoordinator return null; } - //Additional required functions + //Additional required functions ______________________________________ - /** - * Hook for subclasses to perform device-specific deletion logic, e.g. db cleanup. - * @param gbDevice the GBDevice - * @param device the corresponding database Device - * @param session the session to use - * @throws GBException - */ - protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException - { + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { } - /** - * Returns the sample provider for the device being supported. - * - * @return - */ - public SampleProvider getSampleProvider(GBDevice device, DaoSession session) - { - return null; //FIXME + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; } - /** - * Finds an install handler for the given uri that can install the given - * uri on the device being managed. - * - * @param uri - * @param context - * @return the install handler or null if that uri cannot be installed on the device - */ - public InstallHandler findInstallHandler(Uri uri, Context context) - { - return null; //FIXME + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; } + @Override + public Class getAppsManagementActivity() + { + return null; + } - //SupportsXXX + //SupportsXXX ________________________________________________________ - /** - * Returns true if activity data fetching is supported by the device - * (with this coordinator). - * - * @return - */ + @Override + public int getAlarmSlotCount() + { + return 3; + } + + @Override public boolean supportsActivityDataFetching(){ return false; } - /** - * Returns true if activity tracking is supported by the device - * (with this coordinator). - * - * @return - */ + @Override public boolean supportsActivityTracking() - { - return true; - } - - /** - * Returns true if this device/coordinator supports taking screenshots. - * - * @return - */ - public boolean supportsScreenshots() { return false; } - /** - * Returns the number of alarms this device/coordinator supports - * Shall return 0 also if it is not possible to set alarms via - * protocol, but only on the smart device itself. - * - * @return - */ - public int getAlarmSlotCount() - { - return 0; //FIXME - } - - /** - * Returns true if this device/coordinator supports alarms with smart wakeup - * @return - */ - public boolean supportsSmartWakeup(GBDevice device) - { - return false; - } - - /** - * Returns true if the given device supports heart rate measurements. - * @return - */ - public boolean supportsHeartRateMeasurement(GBDevice device) - { - return true; - } - - /** - * Returns true if this device/coordinator supports managing device apps. - * - * @return - */ + @Override public boolean supportsAppsManagement() { return false; } - /** - * Returns the Activity class that will be used to manage device apps. - * - * @return - */ - public Class getAppsManagementActivity() - { - return null; //FIXME - } - - /** - * Indicates whether the device has some kind of calender we can sync to. - * Also used for generated sunrise/sunset events - */ + @Override public boolean supportsCalendarEvents() { return false; } - /** - * Indicates whether the device supports getting a stream of live data. - * This can be live HR, steps etc. - */ - public boolean supportsRealtimeData() - { - return false; - } - - /** - * Indicates whether the device supports current weather and/or weather - * forecast display. - */ - public boolean supportsWeather() - { - return false; - } - - /** - * Indicates whether the device supports being found by vibrating, - * making some sound or lighting up - */ + @Override public boolean supportsFindDevice() { return false; } - /** - * Indicates whether the device supports displaying music information - * like artist, title, album, play state etc. - */ - public boolean supportsMusicInfo() + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { - return false; + return true; } - /** - * Indicates whether the device has an led which supports custom colors - */ + @Override public boolean supportsLedColor() { return false; } - /** - * Indicates whether the device's led supports any RGB color, - * or only preset colors - */ + @Override + public boolean supportsMusicInfo() + { + return false; + } + + @Override + public boolean supportsRealtimeData() + { + return true; + } + + @Override public boolean supportsRgbLedColor() { return false; } + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) + { + return false; + } + + @Override + public boolean supportsWeather() + { + return false; + } + + + + + + } 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 d16a29cc1..08528f761 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 @@ -1,14 +1,24 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.bfh16; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; import android.net.Uri; +import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Calendar; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; import nodomain.freeyourgadget.gadgetbridge.devices.bfh16.BFH16Constants; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; @@ -18,14 +28,183 @@ 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; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { private static final Logger LOG = LoggerFactory.getLogger(BFH16DeviceSupport.class); + public BluetoothGattCharacteristic ctrlCharacteristic = null; + public BluetoothGattCharacteristic measureCharacteristic = null; + + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + public BFH16DeviceSupport() { super(LOG); - addSupportedService(BFH16Constants.BFH16_MAIN_SERVICE); + addSupportedService(BFH16Constants.UUID_SERVICE_JYOU); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + LOG.info("Initializing"); + + gbDevice.setState(GBDevice.State.INITIALIZING); + gbDevice.sendDeviceUpdateIntent(getContext()); + + measureCharacteristic = getCharacteristic(BFH16Constants.UUID_CHARACTERISTIC_MEASURE); + ctrlCharacteristic = getCharacteristic(BFH16Constants.UUID_CHARACTERISTIC_CONTROL); + + builder.setGattCallback(this); + builder.notify(measureCharacteristic, true); + + syncSettings(builder); + + gbDevice.setState(GBDevice.State.INITIALIZED); + gbDevice.sendDeviceUpdateIntent(getContext()); + + LOG.info("Initialization Done"); + + return builder; + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + + UUID characteristicUUID = characteristic.getUuid(); + byte[] data = characteristic.getValue(); + if (data.length == 0) + return true; + + switch (data[0]) { + case BFH16Constants.RECEIVE_DEVICE_INFO: + int fwVerNum = data[4] & 0xFF; + versionCmd.fwVersion = (fwVerNum / 100) + "." + ((fwVerNum % 100) / 10) + "." + ((fwVerNum % 100) % 10); + handleGBDeviceEvent(versionCmd); + LOG.info("Firmware version is: " + versionCmd.fwVersion); + return true; + case BFH16Constants.RECEIVE_BATTERY_LEVEL: + batteryCmd.level = data[8]; + handleGBDeviceEvent(batteryCmd); + LOG.info("Battery level is: " + batteryCmd.level); + return true; + case BFH16Constants.RECEIVE_STEPS_DATA: + int steps = ByteBuffer.wrap(data, 5, 4).getInt(); + LOG.info("Number of walked steps: " + steps); + return true; + case BFH16Constants.RECEIVE_HEARTRATE: + LOG.info("Current heart rate: " + data[1]); + LOG.info("Current blood presure: " + data[3] + "/" + data[2]); + LOG.info("Current satiation: " + data[4]); + return true; + default: + LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0])); + return true; + } + } + + 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 + )); + } + + 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_GET_STEP_COUNT, 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) + )); + } + + 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 @@ -38,63 +217,24 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title); byte icon; switch (notificationSpec.type) { -// case GENERIC_SMS: -// icon = JYouConstants.ICON_SMS; -// break; -// case FACEBOOK: -// case FACEBOOK_MESSENGER: -// icon = JYouConstants.ICON_FACEBOOK; -// break; -// case TWITTER: -// icon = JYouConstants.ICON_TWITTER; -// break; -// case WHATSAPP: -// icon = JYouConstants.ICON_WHATSAPP; -// break; -// default: -// icon = JYouConstants.ICON_LINE; -// break; + 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); //TODO FIXME - } - - private void showNotification(byte icon, String title, String message) { - //TODO implement -// 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] = JYouConstants.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()); -// } + showNotification(icon, notificationTitle, notificationSpec.body); } @Override @@ -103,58 +243,58 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { } @Override - public void onSetTime() { -// try { -// TransactionBuilder builder = performInitialized("SetTime"); -// syncDateAndTime(builder); -// builder.queue(getQueue()); -// } catch(IOException e) { -// LOG.warn(e.getMessage()); -// } + public void onSetAlarms(ArrayList alarms) { + try { + TransactionBuilder builder = performInitialized("SetAlarms"); + + for (int i = 0; i < alarms.size(); i++) + { + byte cmd; + switch (i) { + case 0: + cmd = BFH16Constants.CMD_SET_ALARM_1; + break; + case 1: + cmd = BFH16Constants.CMD_SET_ALARM_2; + break; + case 2: + cmd = BFH16Constants.CMD_SET_ALARM_3; + break; + default: + return; + } + Calendar cal = AlarmUtils.toCalendar(alarms.get(i)); + builder.write(ctrlCharacteristic, commandWithChecksum( + cmd, + alarms.get(i).getEnabled() ? cal.get(Calendar.HOUR_OF_DAY) : -1, + alarms.get(i).getEnabled() ? cal.get(Calendar.MINUTE) : -1 + )); + } + builder.queue(getQueue()); + GB.toast(getContext(), "Alarm settings applied - do note that the current device does not support day specification", Toast.LENGTH_LONG, GB.INFO); + } catch(IOException e) { + LOG.warn(e.getMessage()); + } } @Override - public void onSetAlarms(ArrayList alarms) { -// try { -// TransactionBuilder builder = performInitialized("SetAlarms"); -// -// for (int i = 0; i < alarms.size(); i++) -// { -// byte cmd; -// switch (i) { -// case 0: -// cmd = JYouConstants.CMD_SET_ALARM_1; -// break; -// case 1: -// cmd = JYouConstants.CMD_SET_ALARM_2; -// break; -// case 2: -// cmd = JYouConstants.CMD_SET_ALARM_3; -// break; -// default: -// return; -// } -// Calendar cal = AlarmUtils.toCalendar(alarms.get(i)); -// builder.write(ctrlCharacteristic, commandWithChecksum( -// cmd, -// alarms.get(i).getEnabled() ? cal.get(Calendar.HOUR_OF_DAY) : -1, -// alarms.get(i).getEnabled() ? cal.get(Calendar.MINUTE) : -1 -// )); -// } -// builder.queue(getQueue()); -// GB.toast(getContext(), "Alarm settings applied - do note that the current device does not support day specification", Toast.LENGTH_LONG, GB.INFO); -// } catch(IOException e) { -// LOG.warn(e.getMessage()); -// } + public void onSetTime() { + try { + TransactionBuilder builder = performInitialized("SetTime"); + syncDateAndTime(builder); + builder.queue(getQueue()); + } catch(IOException e) { + LOG.warn(e.getMessage()); + } } @Override public void onSetCallState(CallSpec callSpec) { -// switch (callSpec.command) { -// case CallSpec.CALL_INCOMING: -// showNotification(JYouConstants.ICON_CALL, callSpec.name, callSpec.number); -// break; -// } + switch (callSpec.command) { + case CallSpec.CALL_INCOMING: + showNotification(BFH16Constants.ICON_CALL, callSpec.name, callSpec.number); + break; + } } @Override @@ -174,7 +314,7 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onEnableRealtimeSteps(boolean enable) { - //onEnableRealtimeHeartRateMeasurement(enable); + onEnableRealtimeHeartRateMeasurement(enable); } @Override @@ -214,50 +354,50 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onReset(int flags) { -// try { -// TransactionBuilder builder = performInitialized("Reboot"); -// builder.write(ctrlCharacteristic, commandWithChecksum( -// JYouConstants.CMD_ACTION_REBOOT_DEVICE, 0, 0 -// )); -// builder.queue(getQueue()); -// } catch(Exception e) { -// LOG.warn(e.getMessage()); -// } + try { + TransactionBuilder builder = performInitialized("Reboot"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_ACTION_REBOOT_DEVICE, 0, 0 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } } @Override public void onHeartRateTest() { -// try { -// TransactionBuilder builder = performInitialized("HeartRateTest"); -// builder.write(ctrlCharacteristic, commandWithChecksum( -// JYouConstants.CMD_ACTION_HEARTRATE_SWITCH, 0, 1 -// )); -// builder.queue(getQueue()); -// } catch(Exception e) { -// LOG.warn(e.getMessage()); -// } + try { + TransactionBuilder builder = performInitialized("HeartRateTest"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_ACTION_HEARTRATE_SWITCH, 0, 1 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } } @Override public void onEnableRealtimeHeartRateMeasurement(boolean enable) { -// // TODO: test -// try { -// TransactionBuilder builder = performInitialized("RealTimeHeartMeasurement"); -// builder.write(ctrlCharacteristic, commandWithChecksum( -// JYouConstants.CMD_SET_HEARTRATE_AUTO, 0, enable ? 1 : 0 -// )); -// builder.queue(getQueue()); -// } catch(Exception e) { -// LOG.warn(e.getMessage()); -// } + // TODO: test + try { + TransactionBuilder builder = performInitialized("RealTimeHeartMeasurement"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_HEARTRATE_AUTO, 0, enable ? 1 : 0 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } } @Override public void onFindDevice(boolean start) { -// if (start) { -// showNotification(JYouConstants.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); -// } + 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); + } } @Override @@ -310,4 +450,47 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { } + 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 { + if (src == null) + return null; + + for (int i = src.length(); i > 0; i--) { + String sub = src.substring(0, i); + byte[] subUTF8 = sub.getBytes("UTF-8"); + + if (subUTF8.length == byteCount) { + return subUTF8; + } + + if (subUTF8.length < byteCount) { + byte[] largerSubUTF8 = new byte[byteCount]; + System.arraycopy(subUTF8, 0, largerSubUTF8, 0, subUTF8.length); + return largerSubUTF8; + } + } + } catch (UnsupportedEncodingException e) { + LOG.warn(e.getMessage()); + } + return null; + } }