From 5b3ef8999f5270dd576aab9ea406a0ce52082251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 21 Dec 2016 12:51:25 +0000 Subject: [PATCH 01/10] Add preliminary support for HPlus devices, such as the Zeblaze Zeband (and many others) Working: Text and call notifications, setting most user data, date and time, heart rate monitoring, sleep monitoring (alfa) --- .../gadgetbridge/daogen/GBDaoGenerator.java | 12 + .../gadgetbridge/devices/SampleProvider.java | 1 + .../devices/hplus/HPlusConstants.java | 82 ++ .../devices/hplus/HPlusCoordinator.java | 202 +++++ .../devices/hplus/HPlusSampleProvider.java | 140 +++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 4 + .../devices/hplus/HPlusSleepRecord.java | 86 ++ .../service/devices/hplus/HPlusSupport.java | 802 ++++++++++++++++++ .../gadgetbridge/util/DeviceHelper.java | 2 + 10 files changed, 1332 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 63e5d7139..223404ce5 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -60,6 +60,7 @@ public class GBDaoGenerator { addPebbleHealthActivityKindOverlay(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); addPebbleMorpheuzActivitySample(schema, user, device); + addHPlusHealthActivitySample(schema, user, device); new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -221,6 +222,17 @@ public class GBDaoGenerator { return activitySample; } + private static Entity addHPlusHealthActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addByteArrayProperty("rawHPlusHealthData"); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + return activitySample; + } + private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { activitySample.setSuperclass(superClass); activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index fca1710ec..3dafa35af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -24,6 +24,7 @@ public interface SampleProvider { int PROVIDER_PEBBLE_MISFIT = 3; int PROVIDER_PEBBLE_HEALTH = 4; int PROVIDER_MIBAND2 = 5; + int PROVIDER_HPLUS = 6; int PROVIDER_UNKNOWN = 100; // TODO: can also be removed diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java new file mode 100644 index 000000000..a24b1e862 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -0,0 +1,82 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + +import java.util.UUID; + +/** + * Message constants reverse-engineered by João Paulo Barraca, jpbarraca@gmail.com. + * + * @author João Paulo Barraca <jpbarraca@gmail.com> + */ +public final class HPlusConstants { + + public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("14702856-620a-3973-7c78-9cfff0876abd"); + public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("14702853-620a-3973-7c78-9cfff0876abd"); + public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); + + + public static final byte COUNTRY_CN = 1; + public static final byte COUNTRY_OTHER = 2; + + public static final byte CLOCK_24H = 0; + public static final byte CLOCK_12H = 1; + + public static final byte UNIT_METRIC = 0; + public static final byte UNIT_IMPERIAL = 1; + + public static final byte SEX_MALE = 0; + public static final byte SEX_FEMALE = 1; + + public static final byte HEARTRATE_MEASURE_ON = 11; + public static final byte HEARTRATE_MEASURE_OFF = 22; + + public static final byte HEARTRATE_ALLDAY_ON = 10; + public static final byte HEARTRATE_ALLDAY_OFF = -1; + + public static final byte[] COMMAND_SET_INIT1 = new byte[]{0x50,0x00,0x25,(byte) 0xb1,0x4a,0x00,0x00,0x27,0x10,0x05,0x02,0x00,(byte) 0xff,0x0a,(byte) 0xff,0x00,(byte) 0xff,(byte) 0xff,0x00,0x01}; + public static final byte[] COMMAND_SET_INIT2 = new byte[]{0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,(byte) 0xe0,0x0c,0x12,0x16,0x0a,0x10,0x00,0x00,0x00,0x00}; + + public static final byte[] COMMAND_SET_PREF_START = new byte[]{0x4f, 0x5a}; + public static final byte[] COMMAND_SET_PREF_START1 = new byte[]{0x4d}; + + public static final byte COMMAND_SET_PREF_COUNTRY = 0x22; + public static final byte COMMAND_SET_PREF_TIMEMODE = 0x47; + public static final byte COMMAND_SET_PREF_UNIT = 0x48; + public static final byte COMMAND_SET_PREF_SEX = 0x2d; + + public static final byte COMMAND_SET_PREF_DATE = 0x08; + public static final byte COMMAND_SET_PREF_TIME = 0x09; + public static final byte COMMAND_SET_PREF_WEEK = 0x2a; + public static final byte COMMAND_SET_PREF_SIT = 0x1e; + public static final byte COMMAND_SET_PREF_WEIGHT = 0x05; + public static final byte COMMAND_SET_PREF_HEIGHT = 0x04; + public static final byte COMMAND_SET_PREF_AGE = 0x2c; + public static final byte COMMAND_SET_PREF_GOAL = 0x26; + public static final byte COMMAND_SET_PREF_SCREENTIME = 0x0b; + public static final byte COMMAND_SET_PREF_BLOOD = 0x4e; //?? + public static final byte COMMAND_SET_PREF_FINDME = 0x0a; + public static final byte COMMAND_SET_PREF_SAVE = 0x17; + public static final byte COMMAND_SET_PREF_END = 0x4f; + public static final byte COMMAND_SET_INCOMMING_SOCIAL = 0x31; + public static final byte COMMAND_SET_INCOMMING_SMS = 0x40; + public static final byte COMMAND_SET_DISPLAY_TEXT = 0x43; + public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; + public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; + + public static final byte COMMAND_SET_INCOMMING_CALL = 65; + public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; + + public static final byte COMMAND_SET_CONF_SAVE = 0x17; + public static final byte COMMAND_SET_CONF_END = 0x4f; + + + public static final byte DATA_STATS = 0x33; + public static final byte DATA_SLEEP = 0x1A; + + + public static final String PREF_HPLUS_USER_ALIAS = "hplus_user_alias"; + public static final String PREF_HPLUS_FITNESS_GOAL = "hplus_fitness_goal"; + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; + public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; + public static final String PREF_HPLUS_UNIT = "hplus_unit"; + public static final String PREF_HPLUS_TIMEMODE = "hplus_timemode"; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java new file mode 100644 index 000000000..4eceef13c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -0,0 +1,202 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.UserInfo; +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.ActivityUser; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HPlusCoordinator extends AbstractDeviceCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); + private static Prefs prefs = GBApplication.getPrefs(); + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + LOG.debug("Looking for: " + name); + if (name != null && name.startsWith("HPLUS")) { + return DeviceType.HPLUS; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.HPLUS; + } + + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public Class getPrimaryActivity() { + return ChartsActivity.class; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return true; + } + + @Override + public boolean supportsActivityTracking() { + return true; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return new HPlusSampleProvider(device, session); + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsAlarmConfiguration() { + return true; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return true; + } + + @Override + public int getTapString() { + return R.string.tap_connected_device_for_activity; + } + + @Override + public String getManufacturer() { + return "Zeblaze"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + // nothing to delete, yet + } + + public static int getFitnessGoal(String address) throws IllegalArgumentException { + Prefs prefs = GBApplication.getPrefs(); + return prefs.getInt(HPlusConstants.PREF_HPLUS_FITNESS_GOAL + "_" + address, 10000); + } + + /** + * Returns the user info from the user configured data in the preferences. + * + * @param hplusAddress + * @throws IllegalArgumentException when the user info can not be created + */ + public static UserInfo getConfiguredUserInfo(String hplusAddress) throws IllegalArgumentException { + ActivityUser activityUser = new ActivityUser(); + + UserInfo info = UserInfo.create( + hplusAddress, + prefs.getString(HPlusConstants.PREF_HPLUS_USER_ALIAS, null), + activityUser.getGender(), + activityUser.getAge(), + activityUser.getHeightCm(), + activityUser.getWeightKg(), + 0 + ); + return info; + } + + public static byte getCountry(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); + + } + + public static byte getTimeMode(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_TIMEMODE + "_" + address, 0); + } + + public static byte getUnit(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_UNIT + "_" + address, 0); + } + + public static byte getUserWeight(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) activityUser.getWeightKg(); + } + + public static byte getUserHeight(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) activityUser.getHeightCm(); + } + + public static byte getUserAge(String address) { + ActivityUser activityUser = new ActivityUser(); + + return (byte) activityUser.getAge(); + } + + public static byte getUserSex(String address) { + ActivityUser activityUser = new ActivityUser(); + + int gender = activityUser.getGender(); + + return (byte) gender; + + } + + public static int getGoal(String address) { + ActivityUser activityUser = new ActivityUser(); + + return activityUser.getStepsGoal(); + } + + public static byte getScreenTime(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5); + + } + + public static byte getAllDayHR(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java new file mode 100644 index 000000000..67b535a3b --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java @@ -0,0 +1,140 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.content.Context; +import android.support.annotation.NonNull; + +import java.util.Collections; +import java.util.List; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.query.QueryBuilder; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public class HPlusSampleProvider extends AbstractSampleProvider { + + public static final int TYPE_DEEP_SLEEP = 4; + public static final int TYPE_LIGHT_SLEEP = 5; + public static final int TYPE_ACTIVITY = -1; + public static final int TYPE_UNKNOWN = -1; + public static final int TYPE_NONWEAR = 3; + public static final int TYPE_CHARGING = 6; + + private GBDevice mDevice; + private DaoSession mSession; + + public HPlusSampleProvider(GBDevice device, DaoSession session) { + super(device, session); + + mSession = session; + mDevice = device;; + } + + public int getID() { + return SampleProvider.PROVIDER_HPLUS; + } + + public int normalizeType(int rawType) { + switch (rawType) { + case TYPE_DEEP_SLEEP: + return ActivityKind.TYPE_DEEP_SLEEP; + case TYPE_LIGHT_SLEEP: + return ActivityKind.TYPE_LIGHT_SLEEP; + case TYPE_ACTIVITY: + return ActivityKind.TYPE_ACTIVITY; + case TYPE_NONWEAR: + return ActivityKind.TYPE_NOT_WORN; + case TYPE_CHARGING: + return ActivityKind.TYPE_NOT_WORN; //I believe it's a safe assumption + default: +// case TYPE_UNKNOWN: // fall through + return ActivityKind.TYPE_UNKNOWN; + } + } + + public int toRawActivityKind(int activityKind) { + switch (activityKind) { + case ActivityKind.TYPE_ACTIVITY: + return TYPE_ACTIVITY; + case ActivityKind.TYPE_DEEP_SLEEP: + return TYPE_DEEP_SLEEP; + case ActivityKind.TYPE_LIGHT_SLEEP: + return TYPE_LIGHT_SLEEP; + case ActivityKind.TYPE_NOT_WORN: + return TYPE_NONWEAR; + case ActivityKind.TYPE_UNKNOWN: // fall through + default: + return TYPE_UNKNOWN; + } + } + + + @Override + public List getAllActivitySamples(int timestamp_from, int timestamp_to) { + List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); + + Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); + if (dbDevice == null) { + // no device, no samples + return Collections.emptyList(); + } + + QueryBuilder qb = getSession().getHPlusHealthActivitySampleDao().queryBuilder(); + + qb.where(HPlusHealthActivitySampleDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivitySampleDao.Properties.Timestamp.ge(timestamp_from)) + .where(HPlusHealthActivitySampleDao.Properties.Timestamp.le(timestamp_to)); + + List sampleList = qb.build().list(); + + for (HPlusHealthActivitySample sample : sampleList) { + if (timestamp_from <= sample.getTimestamp() && sample.getTimestamp() < timestamp_to) { + sample.setRawKind(sample.getRawKind()); + } + } + detachFromSession(); + return samples; + } + @NonNull + @Override + protected de.greenrobot.dao.Property getTimestampSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.Timestamp; + } + + @Override + public HPlusHealthActivitySample createActivitySample() { + return new HPlusHealthActivitySample(); + } + + @Override + protected de.greenrobot.dao.Property getRawKindSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.RawKind; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity; //TODO: Calculate actual value + } + + @NonNull + @Override + protected de.greenrobot.dao.Property getDeviceIdentifierSampleProperty() { + return HPlusHealthActivitySampleDao.Properties.DeviceId; + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getHPlusHealthActivitySampleDao(); + } +} 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 5c9d26636..882a82053 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -13,6 +13,7 @@ public enum DeviceType { MIBAND2(11), VIBRATISSIMO(20), LIVEVIEW(30), + HPLUS(40), TEST(1000); 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 71c3dc8d9..af25c8a73 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -15,6 +15,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Suppo import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DeviceSupportFactory { @@ -96,6 +97,9 @@ public class DeviceSupportFactory { case LIVEVIEW: deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case HPLUS: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java new file mode 100644 index 000000000..bed5d581e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSleepRecord.java @@ -0,0 +1,86 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; + + + +public class HPlusSleepRecord { + private long bedTimeStart; + private long bedTimeEnd; + private int deepSleepSeconds; + private int spindleSeconds; + private int remSleepSeconds; + private int wakeupTime; + private int wakeupCount; + private int enterSleepSeconds; + private byte[] rawData; + + HPlusSleepRecord(byte[] data) { + rawData = data; + int year = data[2] * 256 + data[1]; + int month = data[3]; + int day = data[4]; + + enterSleepSeconds = data[6] * 256 + data[5]; + spindleSeconds = data[8] * 256 + data[7]; + deepSleepSeconds = data[10] * 256 + data[9]; + remSleepSeconds = data[12] * 256 + data[11]; + wakeupTime = data[14] * 256 + data[13]; + wakeupCount = data[16] * 256 + data[15]; + int hour = data[17]; + int minute = data[18]; + + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month); + c.set(Calendar.DAY_OF_MONTH, day); + c.set(Calendar.HOUR, hour); + c.set(Calendar.MINUTE, minute); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + bedTimeStart = (c.getTimeInMillis() / 1000L); + bedTimeEnd = bedTimeStart + enterSleepSeconds + spindleSeconds + deepSleepSeconds + remSleepSeconds + wakeupTime; + } + + byte[] getRawData() { + + return rawData; + } + + public long getBedTimeStart() { + return bedTimeStart; + } + + public long getBedTimeEnd() { + return bedTimeEnd; + } + + public int getDeepSleepSeconds() { + return deepSleepSeconds; + } + + public int getSpindleSeconds() { + return spindleSeconds; + } + + public int getRemSleepSeconds() { + return remSleepSeconds; + } + + public int getWakeupTime() { + return wakeupTime; + } + + public int getWakeupCount() { + return wakeupCount; + } + + public int getEnterSleepSeconds() { + return enterSleepSeconds; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java new file mode 100644 index 000000000..7737b5198 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -0,0 +1,802 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; + +/* +* @author João Paulo Barraca <jpbarraca@gmail.com> +*/ + +import android.bluetooth.BluetoothDevice; +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.net.Uri; +import android.support.v4.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusConstants; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +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.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; + + +public class HPlusSupport extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(HPlusSupport.class); + + private BluetoothGattCharacteristic ctrlCharacteristic = null; + private BluetoothGattCharacteristic measureCharacteristic = null; + + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String s = intent.getAction(); + if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) { + handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO)); + } + } + }; + + public HPlusSupport() { + super(LOG); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); + addSupportedService(HPlusConstants.UUID_SERVICE_HP); + + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + IntentFilter intentFilter = new IntentFilter(); + + broadcastManager.registerReceiver(mReceiver, intentFilter); + } + + @Override + public void dispose() { + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext()); + broadcastManager.unregisterReceiver(mReceiver); + super.dispose(); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + + measureCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE); + ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + + + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); + + //Fill device info + requestDeviceInfo(builder); + + getDevice().setFirmwareVersion("0"); + getDevice().setFirmwareVersion2("0"); + + //Initialize device + setInitValues(builder); + + builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); + + UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + BluetoothGattDescriptor descriptor = measureCharacteristic.getDescriptor(uuid); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + + builder.setGattCallback(this); + builder.notify(measureCharacteristic, true); + + setInitialized(builder); + return builder; + } + + private HPlusSupport setInitValues(TransactionBuilder builder){ + LOG.debug("Set Init Values"); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); + return this; + } + + private HPlusSupport sendUserInfo(TransactionBuilder builder){ + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START1); + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_SAVE}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_END}); + return this; + } + + + private HPlusSupport setCountry(TransactionBuilder transaction) { + LOG.info("Attempting to set country..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setTimeMode(TransactionBuilder transaction) { + LOG.info("Attempting to set Time Mode..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIMEMODE, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setUnit(TransactionBuilder transaction) { + LOG.info("Attempting to set Units..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_UNIT, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setCurrentDate(TransactionBuilder transaction) { + LOG.info("Attempting to set Current Date..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + Calendar c = Calendar.getInstance(); + int year = c.get(Calendar.YEAR) - 1900; + int month = c.get(Calendar.MONTH); + int day = c.get(Calendar.DAY_OF_MONTH); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_DATE, + (byte) (year / 256), + (byte) (year % 256), + (byte) (month), + (byte) (day) + }; + } + }); + return this; + } + + private HPlusSupport setCurrentTime(TransactionBuilder transaction) { + LOG.info("Attempting to set Current Time..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + Calendar c = Calendar.getInstance(); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIME, + (byte) c.get(Calendar.HOUR_OF_DAY), + (byte) c.get(Calendar.MINUTE), + (byte) c.get(Calendar.SECOND) + }; + } + }); + return this; + } + + + private HPlusSupport setDayOfWeek(TransactionBuilder transaction) { + LOG.info("Attempting to set Day Of Week..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + Calendar c = Calendar.getInstance(); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEEK, + (byte) c.get(Calendar.DAY_OF_WEEK) + }; + } + }); + return this; + } + + + private HPlusSupport setSIT(TransactionBuilder transaction) { + LOG.info("Attempting to set SIT..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + Calendar c = Calendar.getInstance(); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SIT, + 0, 0, 0, 0, 0 + }; + } + }); + return this; + } + + private HPlusSupport setWeight(TransactionBuilder transaction) { + LOG.info("Attempting to set Weight..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEIGHT, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setHeight(TransactionBuilder transaction) { + LOG.info("Attempting to set Height..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_HEIGHT, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setAge(TransactionBuilder transaction) { + LOG.info("Attempting to set Age..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_AGE, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setSex(TransactionBuilder transaction) { + LOG.info("Attempting to set Sex..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SEX, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setGoal(TransactionBuilder transaction) { + LOG.info("Attempting to set Sex..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + int value = HPlusCoordinator.getGoal(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_GOAL, + (byte) (value / 256), + (byte) (value % 256) + }; + } + }); + return this; + } + + + private HPlusSupport setScreenTime(TransactionBuilder transaction) { + LOG.info("Attempting to set Screentime..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SCREENTIME, + (byte) value + }; + } + }); + return this; + } + + private HPlusSupport setAllDayHeart(TransactionBuilder transaction) { + LOG.info("Attempting to set All Day HR..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, + (byte) value + }; + } + }); + return this; + } + + + private HPlusSupport setAlarm(TransactionBuilder transaction) { + LOG.info("Attempting to set Alarm..."); + return this; + } + + private HPlusSupport setBlood(TransactionBuilder transaction) { + LOG.info("Attempting to set Blood..."); + return this; + } + + + private HPlusSupport setFindMe(TransactionBuilder transaction) { + LOG.info("Attempting to set Findme..."); + return this; + } + + private HPlusSupport requestDeviceInfo(TransactionBuilder builder) { + LOG.debug("Requesting Device Info!"); + BluetoothGattCharacteristic deviceName = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME); + builder.read(deviceName); + return this; + } + + private void setInitialized(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + } + + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + public void pair() { + LOG.debug("Pair"); + } + + private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { + LOG.warn("Device info: " + info); + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + LOG.debug("Got Notification"); + showText(notificationSpec.body); + } + + + @Override + public void onSetTime() { + TransactionBuilder builder = new TransactionBuilder("vibration"); + setCurrentDate(builder); + setCurrentTime(builder); + + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onSetCallState(CallSpec callSpec) { + switch(callSpec.command){ + case CallSpec.CALL_INCOMING: { + showText(callSpec.name, callSpec.number); + break; + } + } + + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + LOG.debug("Canned Messages: "+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) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchActivityData() { + + } + + @Override + public void onReboot() { + + } + + @Override + public void onHeartRateTest() { + LOG.debug("On HeartRateTest"); + + getQueue().clear(); + + TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); + byte state = 0; + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, 0x10}); //Set Real Time... ? + builder.queue(getQueue()); + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + LOG.debug("Set Real Time HR Measurement: " + enable); + + getQueue().clear(); + + TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); + byte state = 0; + + if(enable) + state = HPlusConstants.HEARTRATE_ALLDAY_ON; + else + state = HPlusConstants.HEARTRATE_ALLDAY_OFF; + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, state}); + builder.queue(getQueue()); + } + + @Override + public void onFindDevice(boolean start) { + LOG.debug("Find Me"); + + getQueue().clear(); + ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + + TransactionBuilder builder = new TransactionBuilder("findMe"); + + byte[] msg = new byte[2]; + msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; + + if(start) + msg[1] = 1; + else + msg[1] = 0; + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } + + @Override + public void onSetConstantVibration(int intensity) { + LOG.debug("Vibration Trigger"); + + getQueue().clear(); + + ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + + TransactionBuilder builder = new TransactionBuilder("vibration"); + + byte[] msg = new byte[15]; + msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; + + for(int i = 0;i 12) { + message = title.substring(0, 12); + }else { + message = title; + for(int i = message.length(); i < 12; i++) + message += ""; + } + } + message += body; + + int length = message.length() / 17; + + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_INCOMMING_SOCIAL, (byte) length}); + + int remaining = 0; + + if(message.length() % 17 > 0) + remaining = length + 1; + else + remaining = length; + + msg[1] = (byte) remaining; + int message_index = 0; + int i = 3; + + for(int j=0; j < message.length(); j++){ + msg[i++] = (byte) message.charAt(j); + + if(i == msg.length){ + message_index ++; + msg[2] = (byte) message_index; + builder.write(ctrlCharacteristic, msg); + + msg = msg.clone(); + for(i=3; i < msg.length; i++) + msg[i] = 32; + + if(message_index < remaining) + i = 3; + else + break; + } + } + + msg[2] = (byte) remaining; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } + + public boolean isExpectedDevice(BluetoothDevice device) { + return true; + } + + public void close() { + } + + @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 HPlusConstants.DATA_STATS: + return processDataStats(data); + case HPlusConstants.DATA_SLEEP: + return processSleepStats(data); + default: + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + + } + return false; + } + + private boolean processSleepStats(byte[] data){ + LOG.debug("Process Sleep Stats"); + + if(data.length < 19) { + LOG.error("Invalid Sleep Message Length " + data.length); + return false; + } + HPlusSleepRecord record = new HPlusSleepRecord(data); + + try (DBHandler handler = GBApplication.acquireDB()) { + DaoSession session = handler.getDaoSession(); + + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + int ts = (int) (System.currentTimeMillis() / 1000); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) + .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, record.getRawData() ) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + + }catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + + private boolean processDataStats(byte[]data){ + LOG.debug("Process Data Stats"); + + if(data.length < 15) { + LOG.error("Invalid Stats Message Length " + data.length); + return false; + } + double distance = ( (int) data[4] * 256 + data[3]) / 100.0; + + int x = (int) data[6] * 256 + data[5]; + int y = (int) data[8] * 256 + data[7]; + int calories = x + y; + + int bpm = (data[11] == -1) ? HPlusHealthActivitySample.NOT_MEASURED : data[11]; + + try (DBHandler handler = GBApplication.acquireDB()) { + DaoSession session = handler.getDaoSession(); + + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + int ts = (int) (System.currentTimeMillis() / 1000); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + + if (bpm != HPlusHealthActivitySample.NOT_MEASURED) { + HPlusHealthActivitySample sample = createActivitySample(device, user, ts, provider); + sample.setHeartRate(bpm); + provider.addGBActivitySample(sample); + } + }catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + + return true; + } + + public HPlusHealthActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { + HPlusHealthActivitySample sample = new HPlusHealthActivitySample(); + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp(timestampInSeconds); + sample.setProvider(provider); + + return sample; + } +} 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 0b0c6a8d9..c57abb934 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -22,6 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; @@ -167,6 +168,7 @@ public class DeviceHelper { result.add(new PebbleCoordinator()); result.add(new VibratissimoCoordinator()); result.add(new LiveviewCoordinator()); + result.add(new HPlusCoordinator()); return result; } From ae9ebc1be8782841752da1e20895de1d31a1291a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Wed, 21 Dec 2016 23:57:57 +0000 Subject: [PATCH 02/10] Refactoring some parts. Added support for param synchronisation with band --- .../gadgetbridge/daogen/GBDaoGenerator.java | 2 + .../devices/hplus/HPlusConstants.java | 18 +- .../devices/hplus/HPlusCoordinator.java | 36 ++- .../service/devices/hplus/HPlusSupport.java | 237 ++++++++++++------ 4 files changed, 210 insertions(+), 83 deletions(-) diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 223404ce5..e6cb8ddf8 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -226,6 +226,8 @@ public class GBDaoGenerator { Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); activitySample.addByteArrayProperty("rawHPlusHealthData"); + activitySample.addIntProperty("rawHPlusCalories").notNull(); + activitySample.addIntProperty("rawHPlusDistance").notNull(); activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index a24b1e862..17882caa4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -56,9 +56,6 @@ public final class HPlusConstants { public static final byte COMMAND_SET_PREF_FINDME = 0x0a; public static final byte COMMAND_SET_PREF_SAVE = 0x17; public static final byte COMMAND_SET_PREF_END = 0x4f; - public static final byte COMMAND_SET_INCOMMING_SOCIAL = 0x31; - public static final byte COMMAND_SET_INCOMMING_SMS = 0x40; - public static final byte COMMAND_SET_DISPLAY_TEXT = 0x43; public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; @@ -68,10 +65,18 @@ public final class HPlusConstants { public static final byte COMMAND_SET_CONF_SAVE = 0x17; public static final byte COMMAND_SET_CONF_END = 0x4f; + public static final byte COMMAND_SET_PREFS = 0x50; + public static final byte COMMAND_SET_SIT_INTERVAL = 0x51; + + public static final byte DATA_STATS = 0x33; public static final byte DATA_SLEEP = 0x1A; + public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; + public static final byte COMMAND_ACTION_INCOMMING_SMS = 0x40; + public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; + public static final String PREF_HPLUS_USER_ALIAS = "hplus_user_alias"; public static final String PREF_HPLUS_FITNESS_GOAL = "hplus_fitness_goal"; @@ -79,4 +84,11 @@ public final class HPlusConstants { public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; public static final String PREF_HPLUS_UNIT = "hplus_unit"; public static final String PREF_HPLUS_TIMEMODE = "hplus_timemode"; + public static final String PREF_HPLUS_WRIST = "hplus_wrist"; + public static final String PREF_HPLUS_SWALERT = "hplus_sw_alert"; + public static final String PREF_HPLUS_ALERT_TIME = "hplus_alert_time"; + public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time"; + public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time"; + public static final String PREF_HPLUS_COUNTRY = "hplus_country"; + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 4eceef13c..b90e20c50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -145,7 +145,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getCountry(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_COUNTRY + "_" + address, 10); } @@ -199,4 +199,38 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); } + + public static byte getSocial(String address) { + + //TODO: Figure what this is. Returning the default value + + return (byte) 255; + } + + public static byte getUserWrist(String address) { + return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10); + + } + + public static boolean getSWAlertTime(String address) { + return (boolean) prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); + + + } + + public static int getAlertTime(String address) { + return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); + + } + + public static int getSITStartTime(String address) { + return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); + + } + + public static int getSITEndTime(String address) { + return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); + + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 7737b5198..eca31a311 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -35,7 +35,9 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; @@ -99,14 +101,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); - //Fill device info - requestDeviceInfo(builder); - getDevice().setFirmwareVersion("0"); getDevice().setFirmwareVersion2("0"); //Initialize device setInitValues(builder); + syncPreferences(builder); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -121,22 +121,78 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return builder; } - private HPlusSupport setInitValues(TransactionBuilder builder){ + private HPlusSupport setInitValues(TransactionBuilder builder) { LOG.debug("Set Init Values"); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); return this; } - private HPlusSupport sendUserInfo(TransactionBuilder builder){ + private HPlusSupport sendUserInfo(TransactionBuilder builder) { builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_PREF_START1); + syncPreferences(builder); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_SAVE}); builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_CONF_END}); return this; } + private HPlusSupport syncPreferences(TransactionBuilder transaction) { + LOG.info("Attempting to sync preferences..."); + transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { + @Override + protected byte[] checkCondition() { + + byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); + byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); + byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); + byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); + byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? + byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); + byte alertTimeHour = 0; + byte alertTimeMinute = 0; + + if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { + int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); + + alertTimeHour = (byte) (t / 256); + alertTimeMinute = (byte) (t % 256); + } + + byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); + byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); + + return new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + sex, + age, + bodyHeight, + bodyWeight, + 0, + 0, + (byte) (goal / 256), + (byte) (goal % 256), + displayTime, + country, + 0, + social, + allDayHeart, + wrist, + alertTimeHour, + alertTimeMinute, + unit, + timemode + }; + } + }); + return this; + } private HPlusSupport setCountry(TransactionBuilder transaction) { LOG.info("Attempting to set country..."); @@ -200,10 +256,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return new byte[]{ HPlusConstants.COMMAND_SET_PREF_DATE, - (byte) (year / 256), - (byte) (year % 256), - (byte) (month), - (byte) (day) + (byte) (year / 256), + (byte) (year % 256), + (byte) (month), + (byte) (day) }; } }); @@ -251,11 +307,30 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { @Override protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); + int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); + int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); + + Calendar now = Calendar.getInstance(); return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SIT, - 0, 0, 0, 0, 0 + HPlusConstants.COMMAND_SET_SIT_INTERVAL, + (byte) (startTime / 256), + (byte) (startTime % 256), + (byte) (endTime / 256), + (byte) (endTime % 256), + 0, + 0, + (byte) (now.get(Calendar.YEAR) / 256), + (byte) (now.get(Calendar.YEAR) % 256), + (byte) (now.get(Calendar.MONTH) + 1), + (byte) (now.get(Calendar.DAY_OF_MONTH)), + (byte) (now.get(Calendar.HOUR)), + (byte) (now.get(Calendar.MINUTE)), + (byte) (now.get(Calendar.SECOND)), + 0, + 0, + 0, + 0 }; } }); @@ -443,7 +518,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetCallState(CallSpec callSpec) { - switch(callSpec.command){ + switch (callSpec.command) { case CallSpec.CALL_INCOMING: { showText(callSpec.name, callSpec.number); break; @@ -454,7 +529,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { - LOG.debug("Canned Messages: "+cannedMessagesSpec); + LOG.debug("Canned Messages: " + cannedMessagesSpec); } @Override @@ -535,7 +610,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); byte state = 0; - if(enable) + if (enable) state = HPlusConstants.HEARTRATE_ALLDAY_ON; else state = HPlusConstants.HEARTRATE_ALLDAY_OFF; @@ -556,7 +631,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte[] msg = new byte[2]; msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; - if(start) + if (start) msg[1] = 1; else msg[1] = 0; @@ -577,7 +652,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte[] msg = new byte[15]; msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; - for(int i = 0;i 12) { - message = title.substring(0, 12); - }else { - message = title; - for(int i = message.length(); i < 12; i++) - message += ""; - } + if (title != null) { + if (title.length() > 12) { + message = title.substring(0, 12); + } else { + message = title; + for (int i = message.length(); i < 12; i++) + message += " "; } - message += body; + } + message += body; - int length = message.length() / 17; + int length = message.length() / 17; - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_INCOMMING_SOCIAL, (byte) length}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); - int remaining = 0; + int remaining = 0; - if(message.length() % 17 > 0) - remaining = length + 1; - else - remaining = length; + if (message.length() % 17 > 0) + remaining = length + 1; + else + remaining = length; - msg[1] = (byte) remaining; - int message_index = 0; - int i = 3; + msg[1] = (byte) remaining; + int message_index = 0; + int i = 3; - for(int j=0; j < message.length(); j++){ - msg[i++] = (byte) message.charAt(j); + for (int j = 0; j < message.length(); j++) { + msg[i++] = (byte) message.charAt(j); - if(i == msg.length){ - message_index ++; - msg[2] = (byte) message_index; - builder.write(ctrlCharacteristic, msg); + if (i == msg.length) { + message_index++; + msg[2] = (byte) message_index; + builder.write(ctrlCharacteristic, msg); - msg = msg.clone(); - for(i=3; i < msg.length; i++) - msg[i] = 32; + msg = msg.clone(); + for (i = 3; i < msg.length; i++) + msg[i] = 32; - if(message_index < remaining) - i = 3; - else - break; - } + if (message_index < remaining) + i = 3; + else + break; } + } - msg[2] = (byte) remaining; + msg[2] = (byte) remaining; - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); } public boolean isExpectedDevice(BluetoothDevice device) { @@ -703,14 +778,14 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { UUID characteristicUUID = characteristic.getUuid(); byte[] data = characteristic.getValue(); - if(data.length == 0) + if (data.length == 0) return true; - switch(data[0]){ + switch (data[0]) { case HPlusConstants.DATA_STATS: - return processDataStats(data); + return processDataStats(data); case HPlusConstants.DATA_SLEEP: - return processSleepStats(data); + return processSleepStats(data); default: LOG.info("Unhandled characteristic changed: " + characteristicUUID); @@ -718,10 +793,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return false; } - private boolean processSleepStats(byte[] data){ + private boolean processSleepStats(byte[] data) { LOG.debug("Process Sleep Stats"); - if(data.length < 19) { + if (data.length < 19) { LOG.error("Invalid Sleep Message Length " + data.length); return false; } @@ -735,13 +810,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { int ts = (int) (System.currentTimeMillis() / 1000); HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); - Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) - .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, record.getRawData() ) - .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + //TODO: Store Sample. How? - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + //provider.addGBActivitySample(record); - }catch (GBException e) { + + } catch (GBException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); @@ -751,14 +825,16 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } - private boolean processDataStats(byte[]data){ + private boolean processDataStats(byte[] data) { + //TODO: Store Calories and Distance. How? + LOG.debug("Process Data Stats"); - if(data.length < 15) { + if (data.length < 15) { LOG.error("Invalid Stats Message Length " + data.length); return false; } - double distance = ( (int) data[4] * 256 + data[3]) / 100.0; + double distance = ((int) data[4] * 256 + data[3]) / 100.0; int x = (int) data[6] * 256 + data[5]; int y = (int) data[8] * 256 + data[7]; @@ -778,9 +854,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { if (bpm != HPlusHealthActivitySample.NOT_MEASURED) { HPlusHealthActivitySample sample = createActivitySample(device, user, ts, provider); sample.setHeartRate(bpm); + sample.setSteps(0); + sample.setRawIntensity(ActivitySample.NOT_MEASURED); + sample.setRawKind(HPlusSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? provider.addGBActivitySample(sample); } - }catch (GBException e) { + } catch (GBException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); From 6c186329dff933323500ec4e550a8a7364286b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 00:08:14 +0000 Subject: [PATCH 03/10] Cleanup HPlusSampleProvider --- .../devices/hplus/HPlusSampleProvider.java | 36 ++----------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java index 67b535a3b..018c56a60 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java @@ -25,12 +25,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class HPlusSampleProvider extends AbstractSampleProvider { - public static final int TYPE_DEEP_SLEEP = 4; - public static final int TYPE_LIGHT_SLEEP = 5; - public static final int TYPE_ACTIVITY = -1; - public static final int TYPE_UNKNOWN = -1; - public static final int TYPE_NONWEAR = 3; - public static final int TYPE_CHARGING = 6; private GBDevice mDevice; private DaoSession mSession; @@ -47,37 +41,11 @@ public class HPlusSampleProvider extends AbstractSampleProvider Date: Fri, 23 Dec 2016 00:10:38 +0000 Subject: [PATCH 04/10] Improved device filter by considering the existence of a service UUID --- .../devices/hplus/HPlusCoordinator.java | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index b90e20c50..76da9c20d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -4,9 +4,13 @@ package nodomain.freeyourgadget.gadgetbridge.devices.hplus; * @author João Paulo Barraca <jpbarraca@gmail.com> */ +import android.annotation.TargetApi; import android.app.Activity; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; import android.support.annotation.NonNull; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -29,17 +33,35 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + public class HPlusCoordinator extends AbstractDeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); private static Prefs prefs = GBApplication.getPrefs(); + @NonNull + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid mi2Service = new ParcelUuid(HPlusConstants.UUID_SERVICE_HP); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi2Service).build(); + return Collections.singletonList(filter); + } + + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if (candidate.supportsService(HPlusConstants.UUID_SERVICE_HP)) { + return DeviceType.HPLUS; + } + String name = candidate.getDevice().getName(); LOG.debug("Looking for: " + name); if (name != null && name.startsWith("HPLUS")) { return DeviceType.HPLUS; } + return DeviceType.UNKNOWN; } @@ -123,27 +145,6 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { return prefs.getInt(HPlusConstants.PREF_HPLUS_FITNESS_GOAL + "_" + address, 10000); } - /** - * Returns the user info from the user configured data in the preferences. - * - * @param hplusAddress - * @throws IllegalArgumentException when the user info can not be created - */ - public static UserInfo getConfiguredUserInfo(String hplusAddress) throws IllegalArgumentException { - ActivityUser activityUser = new ActivityUser(); - - UserInfo info = UserInfo.create( - hplusAddress, - prefs.getString(HPlusConstants.PREF_HPLUS_USER_ALIAS, null), - activityUser.getGender(), - activityUser.getAge(), - activityUser.getHeightCm(), - activityUser.getWeightKg(), - 0 - ); - return info; - } - public static byte getCountry(String address) { return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_COUNTRY + "_" + address, 10); @@ -213,23 +214,23 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static boolean getSWAlertTime(String address) { - return (boolean) prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); + return prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); } public static int getAlertTime(String address) { - return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); } public static int getSITStartTime(String address) { - return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); } public static int getSITEndTime(String address) { - return (int) prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); + return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); } From 2b78f2708f9182d153c51097cd4690977b38bd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 01:21:05 +0000 Subject: [PATCH 05/10] Cleanup according to PR Review --- .../devices/hplus/HPlusConstants.java | 3 +- .../devices/hplus/HPlusCoordinator.java | 31 +- .../devices/hplus/HPlusSampleProvider.java | 26 - .../service/devices/hplus/HPlusSupport.java | 595 ++++++++++-------- 4 files changed, 329 insertions(+), 326 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 17882caa4..a22990b85 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -71,6 +71,7 @@ public final class HPlusConstants { public static final byte DATA_STATS = 0x33; + public static final byte DATA_STEPS = 0x36; public static final byte DATA_SLEEP = 0x1A; public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; @@ -78,8 +79,6 @@ public final class HPlusConstants { public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; - public static final String PREF_HPLUS_USER_ALIAS = "hplus_user_alias"; - public static final String PREF_HPLUS_FITNESS_GOAL = "hplus_fitness_goal"; public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr"; public static final String PREF_HPLUS_UNIT = "hplus_unit"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 76da9c20d..d1cf8e251 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -141,8 +141,9 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static int getFitnessGoal(String address) throws IllegalArgumentException { - Prefs prefs = GBApplication.getPrefs(); - return prefs.getInt(HPlusConstants.PREF_HPLUS_FITNESS_GOAL + "_" + address, 10000); + ActivityUser activityUser = new ActivityUser(); + + return activityUser.getStepsGoal(); } public static byte getCountry(String address) { @@ -161,28 +162,25 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { public static byte getUserWeight(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) activityUser.getWeightKg(); + return (byte) (activityUser.getWeightKg() & 0xFF); } public static byte getUserHeight(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) activityUser.getHeightCm(); + return (byte) (activityUser.getHeightCm() & 0xFF); } public static byte getUserAge(String address) { ActivityUser activityUser = new ActivityUser(); - return (byte) activityUser.getAge(); + return (byte) (activityUser.getAge() & 0xFF); } public static byte getUserSex(String address) { ActivityUser activityUser = new ActivityUser(); - int gender = activityUser.getGender(); - - return (byte) gender; - + return (byte) (activityUser.getGender() & 0xFF); } public static int getGoal(String address) { @@ -192,46 +190,37 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator { } public static byte getScreenTime(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5); - + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_SCREENTIME + "_" + address, 5) & 0xFF); } public static byte getAllDayHR(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10); - + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_ALLDAYHR + "_" + address, 10) & 0xFF); } public static byte getSocial(String address) { - //TODO: Figure what this is. Returning the default value return (byte) 255; } public static byte getUserWrist(String address) { - return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10); - + return (byte) (prefs.getInt(HPlusConstants.PREF_HPLUS_WRIST + "_" + address, 10) & 0xFF); } public static boolean getSWAlertTime(String address) { return prefs.getBoolean(HPlusConstants.PREF_HPLUS_SWALERT + "_" + address, false); - - } public static int getAlertTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_ALERT_TIME + "_" + address, 0); - } public static int getSITStartTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_START_TIME + "_" + address, 0); - } public static int getSITEndTime(String address) { return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME + "_" + address, 0); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java index 018c56a60..a26f6be2c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusSampleProvider.java @@ -48,32 +48,6 @@ public class HPlusSampleProvider extends AbstractSampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { - List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); - - Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); - if (dbDevice == null) { - // no device, no samples - return Collections.emptyList(); - } - - QueryBuilder qb = getSession().getHPlusHealthActivitySampleDao().queryBuilder(); - - qb.where(HPlusHealthActivitySampleDao.Properties.DeviceId.eq(dbDevice.getId()), HPlusHealthActivitySampleDao.Properties.Timestamp.ge(timestamp_from)) - .where(HPlusHealthActivitySampleDao.Properties.Timestamp.le(timestamp_to)); - - List sampleList = qb.build().list(); - - for (HPlusHealthActivitySample sample : sampleList) { - if (timestamp_from <= sample.getTimestamp() && sample.getTimestamp() < timestamp_to) { - sample.setRawKind(sample.getRawKind()); - } - } - detachFromSession(); - return samples; - } @NonNull @Override protected de.greenrobot.dao.Property getTimestampSampleProperty() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index eca31a311..8914f1986 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -7,17 +7,18 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus; import android.bluetooth.BluetoothDevice; 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.net.Uri; import android.support.v4.content.LocalBroadcastManager; +import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.UUID; @@ -35,14 +36,13 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.entities.UserAttributes; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; 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.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -50,9 +50,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; -import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.util.GB; public class HPlusSupport extends AbstractBTLEDeviceSupport { @@ -110,10 +110,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); - UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); - BluetoothGattDescriptor descriptor = measureCharacteristic.getDescriptor(uuid); - descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - builder.setGattCallback(this); builder.notify(measureCharacteristic, true); @@ -123,6 +119,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setInitValues(TransactionBuilder builder) { LOG.debug("Set Init Values"); + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT1); builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_SET_INIT2); return this; @@ -141,71 +138,61 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport syncPreferences(TransactionBuilder transaction) { LOG.info("Attempting to sync preferences..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); - byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); - byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); - byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); - int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); - byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); - byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); - byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? - byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); - byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); - byte alertTimeHour = 0; - byte alertTimeMinute = 0; + byte sex = HPlusCoordinator.getUserSex(getDevice().getAddress()); + byte age = HPlusCoordinator.getUserAge(getDevice().getAddress()); + byte bodyHeight = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + byte bodyWeight = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + int goal = HPlusCoordinator.getGoal(getDevice().getAddress()); + byte displayTime = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + byte country = HPlusCoordinator.getCountry(getDevice().getAddress()); + byte social = HPlusCoordinator.getSocial(getDevice().getAddress()); // ?? + byte allDayHeart = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + byte wrist = HPlusCoordinator.getUserWrist(getDevice().getAddress()); + byte alertTimeHour = 0; + byte alertTimeMinute = 0; - if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { - int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); + if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) { + int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); - alertTimeHour = (byte) (t / 256); - alertTimeMinute = (byte) (t % 256); - } + alertTimeHour = (byte) ((t / 256) & 0xff); + alertTimeMinute = (byte) (t % 256); + } - byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); - byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); + byte unit = HPlusCoordinator.getUnit(getDevice().getAddress()); + byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress())); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_COUNTRY, - sex, - age, - bodyHeight, - bodyWeight, - 0, - 0, - (byte) (goal / 256), - (byte) (goal % 256), - displayTime, - country, - 0, - social, - allDayHeart, - wrist, - alertTimeHour, - alertTimeMinute, - unit, - timemode - }; - } + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + sex, + age, + bodyHeight, + bodyWeight, + 0, + 0, + (byte) ((goal / 256) & 0xff), + (byte) (goal % 256), + displayTime, + country, + 0, + social, + allDayHeart, + wrist, + alertTimeHour, + alertTimeMinute, + unit, + timemode }); return this; } private HPlusSupport setCountry(TransactionBuilder transaction) { LOG.info("Attempting to set country..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_COUNTRY, - (byte) value - }; - } + byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_COUNTRY, + value }); return this; } @@ -213,73 +200,57 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setTimeMode(TransactionBuilder transaction) { LOG.info("Attempting to set Time Mode..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_TIMEMODE, - (byte) value - }; - } + byte value = HPlusCoordinator.getTimeMode(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIMEMODE, + value }); return this; } private HPlusSupport setUnit(TransactionBuilder transaction) { LOG.info("Attempting to set Units..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_UNIT, - (byte) value - }; - } + + byte value = HPlusCoordinator.getUnit(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_UNIT, + value }); return this; } private HPlusSupport setCurrentDate(TransactionBuilder transaction) { LOG.info("Attempting to set Current Date..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); - int year = c.get(Calendar.YEAR) - 1900; - int month = c.get(Calendar.MONTH); - int day = c.get(Calendar.DAY_OF_MONTH); + Calendar c = Calendar.getInstance(); + int year = c.get(Calendar.YEAR) - 1900; + int month = c.get(Calendar.MONTH); + int day = c.get(Calendar.DAY_OF_MONTH); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_DATE, + (byte) ((year / 256) & 0xff), + (byte) (year % 256), + (byte) (month), + (byte) (day) - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_DATE, - (byte) (year / 256), - (byte) (year % 256), - (byte) (month), - (byte) (day) - }; - } }); return this; } private HPlusSupport setCurrentTime(TransactionBuilder transaction) { LOG.info("Attempting to set Current Time..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_TIME, - (byte) c.get(Calendar.HOUR_OF_DAY), - (byte) c.get(Calendar.MINUTE), - (byte) c.get(Calendar.SECOND) - }; - } + Calendar c = Calendar.getInstance(); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_TIME, + (byte) c.get(Calendar.HOUR_OF_DAY), + (byte) c.get(Calendar.MINUTE), + (byte) c.get(Calendar.SECOND) + }); return this; } @@ -287,16 +258,12 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setDayOfWeek(TransactionBuilder transaction) { LOG.info("Attempting to set Day Of Week..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - Calendar c = Calendar.getInstance(); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_WEEK, - (byte) c.get(Calendar.DAY_OF_WEEK) - }; - } + Calendar c = Calendar.getInstance(); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEEK, + (byte) c.get(Calendar.DAY_OF_WEEK) }); return this; } @@ -304,67 +271,56 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setSIT(TransactionBuilder transaction) { LOG.info("Attempting to set SIT..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); - int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); - Calendar now = Calendar.getInstance(); + int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); + int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); + + Calendar now = Calendar.getInstance(); + + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_SIT_INTERVAL, + (byte) ((startTime / 256) & 0xff), + (byte) (startTime % 256), + (byte) ((endTime / 256) & 0xff), + (byte) (endTime % 256), + 0, + 0, + (byte) ((now.get(Calendar.YEAR) / 256) & 0xff), + (byte) (now.get(Calendar.YEAR) % 256), + (byte) (now.get(Calendar.MONTH) + 1), + (byte) (now.get(Calendar.DAY_OF_MONTH)), + (byte) (now.get(Calendar.HOUR)), + (byte) (now.get(Calendar.MINUTE)), + (byte) (now.get(Calendar.SECOND)), + 0, + 0, + 0, + 0 - return new byte[]{ - HPlusConstants.COMMAND_SET_SIT_INTERVAL, - (byte) (startTime / 256), - (byte) (startTime % 256), - (byte) (endTime / 256), - (byte) (endTime % 256), - 0, - 0, - (byte) (now.get(Calendar.YEAR) / 256), - (byte) (now.get(Calendar.YEAR) % 256), - (byte) (now.get(Calendar.MONTH) + 1), - (byte) (now.get(Calendar.DAY_OF_MONTH)), - (byte) (now.get(Calendar.HOUR)), - (byte) (now.get(Calendar.MINUTE)), - (byte) (now.get(Calendar.SECOND)), - 0, - 0, - 0, - 0 - }; - } }); return this; } private HPlusSupport setWeight(TransactionBuilder transaction) { LOG.info("Attempting to set Weight..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_WEIGHT, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_WEIGHT, + value + }); return this; } private HPlusSupport setHeight(TransactionBuilder transaction) { LOG.info("Attempting to set Height..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_HEIGHT, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_HEIGHT, + value + }); return this; } @@ -372,32 +328,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAge(TransactionBuilder transaction) { LOG.info("Attempting to set Age..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_AGE, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserAge(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_AGE, + value + }); return this; } private HPlusSupport setSex(TransactionBuilder transaction) { LOG.info("Attempting to set Sex..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SEX, - (byte) value - }; - } + byte value = HPlusCoordinator.getUserSex(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SEX, + value + }); return this; } @@ -405,17 +353,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setGoal(TransactionBuilder transaction) { LOG.info("Attempting to set Sex..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - int value = HPlusCoordinator.getGoal(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_GOAL, - (byte) (value / 256), - (byte) (value % 256) - }; - } + int value = HPlusCoordinator.getGoal(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_GOAL, + (byte) ((value / 256) & 0xff), + (byte) (value % 256) + }); return this; } @@ -423,32 +367,24 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setScreenTime(TransactionBuilder transaction) { LOG.info("Attempting to set Screentime..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_SCREENTIME, - (byte) value - }; - } + byte value = HPlusCoordinator.getScreenTime(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_SCREENTIME, + value + }); return this; } private HPlusSupport setAllDayHeart(TransactionBuilder transaction) { LOG.info("Attempting to set All Day HR..."); - transaction.add(new ConditionalWriteAction(ctrlCharacteristic) { - @Override - protected byte[] checkCondition() { - byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); - return new byte[]{ - HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, - (byte) value - }; - } + byte value = HPlusCoordinator.getAllDayHR(getDevice().getAddress()); + transaction.write(ctrlCharacteristic, new byte[]{ + HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, + value + }); return this; } @@ -456,17 +392,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private HPlusSupport setAlarm(TransactionBuilder transaction) { LOG.info("Attempting to set Alarm..."); + //TODO: Find how to set alarms return this; } private HPlusSupport setBlood(TransactionBuilder transaction) { LOG.info("Attempting to set Blood..."); + //TODO: Find what blood means for the band return this; } private HPlusSupport setFindMe(TransactionBuilder transaction) { LOG.info("Attempting to set Findme..."); + //TODO: Find how this works return this; } @@ -499,16 +438,16 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { LOG.debug("Got Notification"); + //TODO: Show different notifications acccording to source as Band supports this showText(notificationSpec.body); } @Override public void onSetTime() { - TransactionBuilder builder = new TransactionBuilder("vibration"); + TransactionBuilder builder = new TransactionBuilder("time"); setCurrentDate(builder); setCurrentTime(builder); - } @Override @@ -594,11 +533,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getQueue().clear(); TransactionBuilder builder = new TransactionBuilder("HeartRateTest"); - byte state = 0; builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_SET_PREF_ALLDAYHR, 0x10}); //Set Real Time... ? builder.queue(getQueue()); - } @Override @@ -608,7 +545,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getQueue().clear(); TransactionBuilder builder = new TransactionBuilder("realTimeHeartMeasurement"); - byte state = 0; + byte state; if (enable) state = HPlusConstants.HEARTRATE_ALLDAY_ON; @@ -623,20 +560,23 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public void onFindDevice(boolean start) { LOG.debug("Find Me"); - getQueue().clear(); - ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + try { + TransactionBuilder builder = performInitialized("findMe"); - TransactionBuilder builder = new TransactionBuilder("findMe"); + byte[] msg = new byte[2]; + msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; - byte[] msg = new byte[2]; - msg[0] = HPlusConstants.COMMAND_SET_PREF_FINDME; + if (start) + msg[1] = 1; + else + msg[1] = 0; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast(getContext(), "Error toogling Find Me: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } - if (start) - msg[1] = 1; - else - msg[1] = 0; - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); } @Override @@ -645,18 +585,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { getQueue().clear(); - ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + try { + TransactionBuilder builder = performInitialized("vibration"); - TransactionBuilder builder = new TransactionBuilder("vibration"); + byte[] msg = new byte[15]; + msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; - byte[] msg = new byte[15]; - msg[0] = HPlusConstants.COMMAND_SET_DISPLAY_ALERT; + for (int i = 0; i < msg.length - 1; i++) + msg[i + 1] = (byte) "GadgetBridge".charAt(i); - for (int i = 0; i < msg.length - 1; i++) - msg[i + 1] = (byte) "GadgetBridge".charAt(i); - - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast(getContext(), "Error setting Vibration: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } } @Override @@ -688,7 +630,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { LOG.debug("Test New Function"); - } @@ -699,67 +640,71 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private void showText(String title, String body) { LOG.debug("Show Notification"); - TransactionBuilder builder = new TransactionBuilder("showText"); - if (ctrlCharacteristic == null) - ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL); + try { + TransactionBuilder builder = performInitialized("notification"); - byte[] msg = new byte[20]; - for (int i = 0; i < msg.length; i++) - msg[i] = 32; - msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; + byte[] msg = new byte[20]; + for (int i = 0; i < msg.length; i++) + msg[i] = 32; - String message = ""; + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; - if (title != null) { - if (title.length() > 12) { - message = title.substring(0, 12); - } else { - message = title; - for (int i = message.length(); i < 12; i++) - message += " "; + String message = ""; + + if (title != null) { + if (title.length() > 12) { + message = title.substring(0, 12); + } else { + message = title; + for (int i = message.length(); i < 12; i++) + message += " "; + } } - } - message += body; + message += body; - int length = message.length() / 17; + int length = message.length() / 17; - builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); + builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.COMMAND_ACTION_INCOMING_SOCIAL, (byte) 255}); - int remaining = 0; + int remaining; - if (message.length() % 17 > 0) - remaining = length + 1; - else - remaining = length; + if (message.length() % 17 > 0) + remaining = length + 1; + else + remaining = length; - msg[1] = (byte) remaining; - int message_index = 0; - int i = 3; + msg[1] = (byte) remaining; + int message_index = 0; + int i = 3; - for (int j = 0; j < message.length(); j++) { - msg[i++] = (byte) message.charAt(j); + for (int j = 0; j < message.length(); j++) { + msg[i++] = (byte) message.charAt(j); - if (i == msg.length) { - message_index++; - msg[2] = (byte) message_index; - builder.write(ctrlCharacteristic, msg); + if (i == msg.length) { + message_index++; + msg[2] = (byte) message_index; + builder.write(ctrlCharacteristic, msg); - msg = msg.clone(); - for (i = 3; i < msg.length; i++) - msg[i] = 32; + msg = msg.clone(); + for (i = 3; i < msg.length; i++) + msg[i] = 32; - if (message_index < remaining) - i = 3; - else - break; + if (message_index < remaining) + i = 3; + else + break; + } } + + msg[2] = (byte) remaining; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + }catch(IOException e){ + GB.toast(getContext(), "Error showing device Notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } - - msg[2] = (byte) remaining; - - builder.write(ctrlCharacteristic, msg); - builder.queue(getQueue()); } public boolean isExpectedDevice(BluetoothDevice device) { @@ -786,6 +731,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return processDataStats(data); case HPlusConstants.DATA_SLEEP: return processSleepStats(data); + case HPlusConstants.DATA_STEPS: + return processStepStats(data); + default: LOG.info("Unhandled characteristic changed: " + characteristicUUID); @@ -793,6 +741,99 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { return false; } + /* + Receives a message containing the status of the day. + */ + private boolean processDayStats(byte[] data) { + int a = data[4] * 256 + data[5]; + if (a < 144) { + int slot = a * 2; // 10 minute slots as an offset from 0:00 AM + int avgHR = data[1]; //Average Heart Rate + int steps = data[2] * 256 + data[3]; // Steps in this period + + //?? data[6]; + int timeInactive = data[7]; + + LOG.debug("Day Stats: Slot: " + slot + " HR: " + avgHR + " Steps: " + steps + " TimeInactive: " + timeInactive); + + try (DBHandler handler = GBApplication.acquireDB()) { + DaoSession session = handler.getDaoSession(); + + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + int ts = (int) (System.currentTimeMillis() / 1000); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + + //TODO: Store Sample. How? + + //provider.addGBActivitySample(record); + + + } catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + } else + LOG.error("Invalid day stats"); + + return true; + } + + private boolean processStepStats(byte[] data) { + LOG.debug("Process Step Stats"); + + if (data.length < 19) { + LOG.error("Invalid Steps Message Length " + data.length); + return false; + } + /* + This is a dump of the entire day. + */ + int year = data[9] + data[10] * 256; + short month = data[11]; + short day = data[12]; + int steps = data[2] * 256 + data[1]; + + float distance = ((float) (data[3] + data[4] * 256) / 100.0f); + + /* + unknown fields + short s12 = (short)(data[5] + data[6] * 256); + short s13 = (short)(data[7] + data[8] * 256); + short s16 = (short)(data[13]) + data[14] * 256); + short s17 = data[15]; + short s18 = data[16]; + */ + + + LOG.debug("Step Stats: Year: " + year + " Month: " + month + " Day:"); + try (DBHandler handler = GBApplication.acquireDB()) { + DaoSession session = handler.getDaoSession(); + + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + int ts = (int) (System.currentTimeMillis() / 1000); + HPlusSampleProvider provider = new HPlusSampleProvider(gbDevice, session); + + + //TODO: Store Sample. How? + + //provider.addGBActivitySample(record); + + + } catch (GBException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + private boolean processSleepStats(byte[] data) { LOG.debug("Process Sleep Stats"); @@ -856,7 +897,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { sample.setHeartRate(bpm); sample.setSteps(0); sample.setRawIntensity(ActivitySample.NOT_MEASURED); - sample.setRawKind(HPlusSampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? + sample.setRawKind(ActivityKind.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? provider.addGBActivitySample(sample); } } catch (GBException e) { From 9a338c9baefae05bc66e58bc8d5e009d6835d05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 01:35:18 +0000 Subject: [PATCH 06/10] HPlus: Fix text notification length --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 8914f1986..181b81230 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -653,11 +653,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { String message = ""; if (title != null) { - if (title.length() > 12) { + if (title.length() > 17) { message = title.substring(0, 12); } else { message = title; - for (int i = message.length(); i < 12; i++) + for (int i = message.length(); i < 17; i++) message += " "; } } From 9dd5967f4e558e40bb77a79915ecc08150ccc8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 10:14:03 +0000 Subject: [PATCH 07/10] HPlus: Set date and time on connect --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 181b81230..c3669729f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -106,6 +106,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //Initialize device setInitValues(builder); + setCurrentDate(builder); + setCurrentTime(builder); + syncPreferences(builder); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -233,7 +236,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { HPlusConstants.COMMAND_SET_PREF_DATE, (byte) ((year / 256) & 0xff), (byte) (year % 256), - (byte) (month), + (byte) (month + 1), (byte) (day) }); From cd915598b03f728953568be416754fa3758f673c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 11:46:20 +0000 Subject: [PATCH 08/10] HPlus: Improved handling of incomming calls --- .../devices/hplus/HPlusConstants.java | 9 ++- .../service/devices/hplus/HPlusSupport.java | 75 ++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index a22990b85..7f8ac3ba7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -59,7 +59,7 @@ public final class HPlusConstants { public static final byte COMMAND_SET_DISPLAY_ALERT = 0x23; public static final byte COMMAND_SET_PREF_ALLDAYHR = 53; - public static final byte COMMAND_SET_INCOMMING_CALL = 65; + public static final byte COMMAND_SET_INCOMING_CALL = 0x41; public static final byte[] COMMAND_FACTORY_RESET = new byte[] {-74, 90}; public static final byte COMMAND_SET_CONF_SAVE = 0x17; @@ -75,8 +75,13 @@ public final class HPlusConstants { public static final byte DATA_SLEEP = 0x1A; public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; - public static final byte COMMAND_ACTION_INCOMMING_SMS = 0x40; + public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; public static final byte COMMAND_ACTION_DISPLAY_TEXT = 0x43; + public static final byte[] COMMAND_ACTION_INCOMING_CALL = new byte[] {6, -86}; + public static final byte COMMAND_ACTION_DISPLAY_TEXT_CENTER = 0x23; + public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME = 0x3F; + public static final byte COMMAND_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312? + public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index c3669729f..4797ce5ac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -462,7 +462,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { public void onSetCallState(CallSpec callSpec) { switch (callSpec.command) { case CallSpec.CALL_INCOMING: { - showText(callSpec.name, callSpec.number); + showIncomingCall(callSpec.name, callSpec.number); break; } } @@ -636,6 +636,79 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } + private void showIncomingCall(String name, String number){ + LOG.debug("Show Incoming Call"); + + try { + TransactionBuilder builder = performInitialized("incomingCallIcon"); + + //Enable call notifications + builder.write(ctrlCharacteristic, new byte[] {HPlusConstants.COMMAND_SET_INCOMING_CALL, 1 }); + + //Show Call Icon + builder.write(ctrlCharacteristic, HPlusConstants.COMMAND_ACTION_INCOMING_CALL); + + //builder = performInitialized("incomingCallText"); + builder.queue(getQueue()); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + byte[] msg = new byte[13]; + + builder = performInitialized("incomingCallNumber"); + + //Show call number + for (int i = 0; i < msg.length; i++) + msg[i] = ' '; + + for(int i = 0; i < number.length() && i < (msg.length - 1); i++) + msg[i + 1] = (byte) number.charAt(i); + + + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_CENTER; + + builder.write(ctrlCharacteristic, msg); + builder.queue(getQueue()); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + builder = performInitialized("incomingCallText"); + + //Show call name + //Must call twice, otherwise nothing happens + for (int i = 0; i < msg.length; i++) + msg[i] = ' '; + + for(int i = 0; i < name.length() && i < (msg.length - 1); i++) + msg[i + 1] = (byte) name.charAt(i); + + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_NAME; + builder.write(ctrlCharacteristic, msg); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT_NAME_CN; + builder.write(ctrlCharacteristic, msg); + + builder.queue(getQueue()); + }catch(IOException e){ + GB.toast(getContext(), "Error showing incoming call: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + + } + } + private void showText(String message) { showText(null, message); } From 88f2d2ee4f440d64870a2d9036f023a053b0665b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Fri, 23 Dec 2016 12:20:06 +0000 Subject: [PATCH 09/10] HPlus: Fixed notification title size --- .../gadgetbridge/service/devices/hplus/HPlusSupport.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index 4797ce5ac..ece3eb066 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -722,7 +722,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { byte[] msg = new byte[20]; for (int i = 0; i < msg.length; i++) - msg[i] = 32; + msg[i] = ' '; msg[0] = HPlusConstants.COMMAND_ACTION_DISPLAY_TEXT; @@ -730,7 +730,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { if (title != null) { if (title.length() > 17) { - message = title.substring(0, 12); + message = title.substring(0, 17); } else { message = title; for (int i = message.length(); i < 17; i++) @@ -764,7 +764,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { msg = msg.clone(); for (i = 3; i < msg.length; i++) - msg[i] = 32; + msg[i] = ' '; if (message_index < remaining) i = 3; From 649e20ad04fc038303fe1c15ab2f7ab524364553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Paulo=20Barraca?= Date: Sat, 24 Dec 2016 00:05:51 +0000 Subject: [PATCH 10/10] HPlus: Ignore duplicated messages from band --- .../gadgetbridge/devices/hplus/HPlusConstants.java | 1 + .../service/devices/hplus/HPlusSupport.java | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index 7f8ac3ba7..f69bb4536 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -72,6 +72,7 @@ public final class HPlusConstants { public static final byte DATA_STATS = 0x33; public static final byte DATA_STEPS = 0x36; + public static final byte DATA_SLEEP = 0x1A; public static final byte COMMAND_ACTION_INCOMING_SOCIAL = 0x31; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index ece3eb066..c3704ffec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -61,6 +61,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { private BluetoothGattCharacteristic ctrlCharacteristic = null; private BluetoothGattCharacteristic measureCharacteristic = null; + private byte[] lastDataStats = null; + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -108,7 +110,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { setInitValues(builder); setCurrentDate(builder); setCurrentTime(builder); - syncPreferences(builder); builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true); @@ -449,6 +450,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { @Override public void onSetTime() { TransactionBuilder builder = new TransactionBuilder("time"); + setCurrentDate(builder); setCurrentTime(builder); } @@ -651,6 +653,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { //builder = performInitialized("incomingCallText"); builder.queue(getQueue()); + //TODO: Use WaitAction try { Thread.sleep(200); } catch (InterruptedException e) { @@ -728,6 +731,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { String message = ""; + //TODO: Create StringUtils.pad and StringUtils.truncate if (title != null) { if (title.length() > 17) { message = title.substring(0, 17); @@ -951,6 +955,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { LOG.error("Invalid Stats Message Length " + data.length); return false; } + + //Ignore duplicate packets + if(data.equals(lastDataStats)) + return true; + + lastDataStats = data.clone(); + double distance = ((int) data[4] * 256 + data[3]) / 100.0; int x = (int) data[6] * 256 + data[5];