1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-17 11:46:48 +02:00

Merge pull request #523 from jpbarraca/Makibes-F68

HPlus: Support for Makibes F68
This commit is contained in:
Andreas Shimokawa 2017-01-26 16:07:26 +01:00 committed by GitHub
commit 896eb19b3e
11 changed files with 232 additions and 153 deletions

View File

@ -15,8 +15,8 @@ public final class HPlusConstants {
public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd"); public static final UUID UUID_SERVICE_HP = UUID.fromString("14701820-620a-3973-7c78-9cfff0876abd");
public static final byte ARG_COUNTRY_CN = 1; public static final byte ARG_LANGUAGE_CN = 1;
public static final byte ARG_COUNTRY_OTHER = 2; public static final byte ARG_LANGUAGE_EN = 2;
public static final byte ARG_TIMEMODE_24H = 0; public static final byte ARG_TIMEMODE_24H = 0;
public static final byte ARG_TIMEMODE_12H = 1; public static final byte ARG_TIMEMODE_12H = 1;
@ -111,7 +111,7 @@ public final class HPlusConstants {
public static final String PREF_HPLUS_ALERT_TIME = "hplus_alert_time"; 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_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_SIT_END_TIME = "hplus_sit_end_time";
public static final String PREF_HPLUS_COUNTRY = "hplus_country"; public static final String PREF_HPLUS_LANGUAGE = "hplus_language";
public static final Map<Character, Byte> transliterateMap = new HashMap<Character, Byte>(){ public static final Map<Character, Byte> transliterateMap = new HashMap<Character, Byte>(){
{ {

View File

@ -38,8 +38,8 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
public class HPlusCoordinator extends AbstractDeviceCoordinator { public class HPlusCoordinator extends AbstractDeviceCoordinator {
private static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class); protected static final Logger LOG = LoggerFactory.getLogger(HPlusCoordinator.class);
private static Prefs prefs = GBApplication.getPrefs(); protected static Prefs prefs = GBApplication.getPrefs();
@NonNull @NonNull
@Override @Override
@ -144,8 +144,8 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
return activityUser.getStepsGoal(); return activityUser.getStepsGoal();
} }
public static byte getCountry(String address) { public static byte getLanguage(String address) {
return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_COUNTRY + "_" + address, 10); return (byte) prefs.getInt(HPlusConstants.PREF_HPLUS_LANGUAGE + "_" + address, HPlusConstants.ARG_LANGUAGE_EN);
} }

View File

@ -141,30 +141,46 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider<HPlusHealt
today.set(Calendar.SECOND, 0); today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0); today.set(Calendar.MILLISECOND, 0);
int stepsToday = 0; int stepsTodayMax = 0;
int stepsTodayCount = 0;
HPlusHealthActivitySample lastSample = null;
for(HPlusHealthActivitySample sample: samples){ for(HPlusHealthActivitySample sample: samples){
if(sample.getTimestamp() >= today.getTimeInMillis() / 1000){ if(sample.getTimestamp() >= today.getTimeInMillis() / 1000){
//Only consider these for the current day as a single message is enough for steps
//HR and Overlays will still benefit from the full set of samples /**Strategy is:
* Calculate max steps from realtime messages
* Calculate sum of steps from day 10 minute slot summaries
*/
if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME) { if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME) {
int aux = sample.getSteps(); stepsTodayMax = Math.max(stepsTodayMax, sample.getSteps());
sample.setSteps(sample.getSteps() - stepsToday); }else if(sample.getRawKind() == HPlusDataRecord.TYPE_DAY_SLOT) {
stepsToday = aux; stepsTodayCount += sample.getSteps();
}else }
sample.setSteps(ActivitySample.NOT_MEASURED);
sample.setSteps(ActivitySample.NOT_MEASURED);
lastSample = sample;
}else{ }else{
if (sample.getRawKind() != HPlusDataRecord.TYPE_DAY_SUMMARY) { if (sample.getRawKind() != HPlusDataRecord.TYPE_DAY_SUMMARY) {
sample.setSteps(ActivityKind.TYPE_NOT_MEASURED); sample.setSteps(ActivitySample.NOT_MEASURED);
} }
} }
} }
if(lastSample != null)
lastSample.setSteps(Math.max(stepsTodayCount, stepsTodayMax));
for (HPlusHealthActivityOverlay overlay : overlayRecords) { for (HPlusHealthActivityOverlay overlay : overlayRecords) {
//Create fake events to improve activity counters if there are no events around the overlay //Create fake events to improve activity counters if there are no events around the overlay
//timestamp boundaries //timestamp boundaries
//Insert one before, one at the beginning, one at the end, and one 1s after.
insertVirtualItem(samples, Math.max(overlay.getTimestampFrom() - 1, timestamp_from), overlay.getDeviceId(), overlay.getUserId());
insertVirtualItem(samples, Math.max(overlay.getTimestampFrom(), timestamp_from), overlay.getDeviceId(), overlay.getUserId()); insertVirtualItem(samples, Math.max(overlay.getTimestampFrom(), timestamp_from), overlay.getDeviceId(), overlay.getUserId());
insertVirtualItem(samples, Math.min(overlay.getTimestampTo() - 1, timestamp_to - 1), overlay.getDeviceId(), overlay.getUserId()); insertVirtualItem(samples, Math.min(overlay.getTimestampTo() - 1, timestamp_to - 1), overlay.getDeviceId(), overlay.getUserId());
insertVirtualItem(samples, Math.min(overlay.getTimestampTo(), timestamp_to), overlay.getDeviceId(), overlay.getUserId());
for (HPlusHealthActivitySample sample : samples) { for (HPlusHealthActivitySample sample : samples) {
if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) { if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) {
@ -191,7 +207,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider<HPlusHealt
userId, // User id userId, // User id
null, // Raw Data null, // Raw Data
ActivityKind.TYPE_UNKNOWN, ActivityKind.TYPE_UNKNOWN,
0, // Intensity 1, // Intensity
ActivitySample.NOT_MEASURED, // Steps ActivitySample.NOT_MEASURED, // Steps
ActivitySample.NOT_MEASURED, // HR ActivitySample.NOT_MEASURED, // HR
ActivitySample.NOT_MEASURED, // Distance ActivitySample.NOT_MEASURED, // Distance

View File

@ -0,0 +1,33 @@
package nodomain.freeyourgadget.gadgetbridge.devices.hplus;
/*
* @author João Paulo Barraca &lt;jpbarraca@gmail.com&gt;
*/
import android.support.annotation.NonNull;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
/**
* Pseudo Coordinator for the Makibes F68, a sub type of the HPLUS devices
*/
public class MakibesF68Coordinator extends HPlusCoordinator {
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
String name = candidate.getDevice().getName();
if(name != null && name.startsWith("SPORT")){
return DeviceType.MAKIBESF68;
}
return DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.MAKIBESF68;
}
}

View File

@ -14,6 +14,7 @@ public enum DeviceType {
VIBRATISSIMO(20), VIBRATISSIMO(20),
LIVEVIEW(30), LIVEVIEW(30),
HPLUS(40), HPLUS(40),
MAKIBESF68(50),
TEST(1000); TEST(1000);
private final int key; private final int key;

View File

@ -10,6 +10,7 @@ import java.util.EnumSet;
import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
@ -98,7 +99,10 @@ public class DeviceSupportFactory {
deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break; break;
case HPLUS: case HPLUS:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.HPLUS), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MAKIBESF68:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break; break;
} }
if (deviceSupport != null) { if (deviceSupport != null) {

View File

@ -8,7 +8,7 @@ import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
public class HPlusDataRecordDaySlot extends HPlusDataRecord { public class HPlusDataRecordDaySlot extends HPlusDataRecord {
@ -47,9 +47,9 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord {
heartRate = data[1] & 0xFF; heartRate = data[1] & 0xFF;
if(heartRate == 255 || heartRate == 0) if(heartRate == 255 || heartRate == 0)
heartRate = ActivityKind.TYPE_NOT_MEASURED; heartRate = ActivitySample.NOT_MEASURED;
steps = (data[2] & 0xFF) * 256 + data[3] & 0xFF; steps = (data[2] & 0xFF) * 256 + (data[3] & 0xFF);
//?? data[6]; atemp?? always 0 //?? data[6]; atemp?? always 0
secondsInactive = data[7] & 0xFF; // ? secondsInactive = data[7] & 0xFF; // ?
@ -69,16 +69,21 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord {
return String.format(Locale.US, "Slot: %d, Time: %s, Steps: %d, InactiveSeconds: %d, HeartRate: %d", slot, slotTime.getTime(), steps, secondsInactive, heartRate); return String.format(Locale.US, "Slot: %d, Time: %s, Steps: %d, InactiveSeconds: %d, HeartRate: %d", slot, slotTime.getTime(), steps, secondsInactive, heartRate);
} }
public void add(HPlusDataRecordDaySlot other){ public void accumulate(HPlusDataRecordDaySlot other){
if(other == null) if(other == null)
return; return;
steps += other.steps; if(steps == ActivitySample.NOT_MEASURED)
secondsInactive += other.secondsInactive; steps = other.steps;
if(heartRate == -1) else if(other.steps != ActivitySample.NOT_MEASURED)
steps += other.steps;
if(heartRate == ActivitySample.NOT_MEASURED)
heartRate = other.heartRate; heartRate = other.heartRate;
else if(other.heartRate != -1) { else if(other.heartRate != ActivitySample.NOT_MEASURED) {
heartRate = (heartRate + other.heartRate) / 2; heartRate = (heartRate + other.heartRate) / 2;
} }
secondsInactive += other.secondsInactive;
} }
} }

View File

@ -60,8 +60,8 @@ class HPlusDataRecordRealtime extends HPlusDataRecord {
timestamp = (int) (GregorianCalendar.getInstance().getTimeInMillis() / 1000); timestamp = (int) (GregorianCalendar.getInstance().getTimeInMillis() / 1000);
distance = 10 * ((data[4] & 0xFF) * 256 + (data[3] & 0xFF)); // meters distance = 10 * ((data[4] & 0xFF) * 256 + (data[3] & 0xFF)); // meters
steps = (data[2] & 0xFF) * 256 + (data[1] & 0xFF); steps = (data[2] & 0xFF) * 256 + (data[1] & 0xFF);
int x = (data[6] & 0xFF) * 256 + data[5] & 0xFF; int x = (data[6] & 0xFF) * 256 + (data[5] & 0xFF);
int y = (data[8] & 0xFF) * 256 + data[7] & 0xFF; int y = (data[8] & 0xFF) * 256 + (data[7] & 0xFF);
battery = data[9]; battery = data[9];

View File

@ -8,7 +8,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -19,8 +18,6 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.GBException;
@ -52,8 +49,6 @@ class HPlusHandlerThread extends GBDeviceIoThread {
private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60; private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60;
private int DAY_SUMMARY_SYNC_RETRY_PERIOD = 30; private int DAY_SUMMARY_SYNC_RETRY_PERIOD = 30;
private int HELLO_INTERVAL = 60;
private boolean mQuit = false; private boolean mQuit = false;
private HPlusSupport mHPlusSupport; private HPlusSupport mHPlusSupport;
@ -61,7 +56,6 @@ class HPlusHandlerThread extends GBDeviceIoThread {
private int mLastSlotRequested = 0; private int mLastSlotRequested = 0;
private Calendar mLastSleepDayReceived = GregorianCalendar.getInstance(); private Calendar mLastSleepDayReceived = GregorianCalendar.getInstance();
private Calendar mHelloTime = GregorianCalendar.getInstance();
private Calendar mGetDaySlotsTime = GregorianCalendar.getInstance(); private Calendar mGetDaySlotsTime = GregorianCalendar.getInstance();
private Calendar mGetSleepTime = GregorianCalendar.getInstance(); private Calendar mGetSleepTime = GregorianCalendar.getInstance();
private Calendar mGetDaySummaryTime = GregorianCalendar.getInstance(); private Calendar mGetDaySummaryTime = GregorianCalendar.getInstance();
@ -72,7 +66,9 @@ class HPlusHandlerThread extends GBDeviceIoThread {
private final Object waitObject = new Object(); private final Object waitObject = new Object();
List<HPlusDataRecordDaySlot> mDaySlotSamples = new ArrayList<>(); List<HPlusDataRecordDaySlot> mDaySlotRecords = new ArrayList<>();
private HPlusDataRecordDaySlot mCurrentDaySlot = null;
public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) { public HPlusHandlerThread(GBDevice gbDevice, Context context, HPlusSupport hplusSupport) {
super(gbDevice, context); super(gbDevice, context);
@ -113,10 +109,6 @@ class HPlusHandlerThread extends GBDeviceIoThread {
Calendar now = GregorianCalendar.getInstance(); Calendar now = GregorianCalendar.getInstance();
if (now.compareTo(mHelloTime) > 0) {
sendHello();
}
if (now.compareTo(mGetDaySlotsTime) > 0) { if (now.compareTo(mGetDaySlotsTime) > 0) {
requestNextDaySlots(); requestNextDaySlots();
} }
@ -130,7 +122,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
} }
now = GregorianCalendar.getInstance(); now = GregorianCalendar.getInstance();
waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()), mHelloTime.getTimeInMillis())) - now.getTimeInMillis(); waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis())) - now.getTimeInMillis();
} }
} }
@ -152,54 +144,22 @@ class HPlusHandlerThread extends GBDeviceIoThread {
mSlotsInitialSync = true; mSlotsInitialSync = true;
mLastSlotReceived = -1; mLastSlotReceived = -1;
mLastSlotRequested = 0; mLastSlotRequested = 0;
mCurrentDaySlot = null;
mDaySlotRecords.clear();
TransactionBuilder builder = new TransactionBuilder("startSyncDayStats"); TransactionBuilder builder = new TransactionBuilder("startSyncDayStats");
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID}); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DEVICE_ID});
builder.wait(400);
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION}); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION});
builder.wait(400);
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP});
builder.wait(400);
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA});
builder.wait(400);
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY});
builder.wait(400);
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA}); builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA});
builder.queue(mHPlusSupport.getQueue()); builder.queue(mHPlusSupport.getQueue());
scheduleHello();
synchronized (waitObject) { synchronized (waitObject) {
waitObject.notify(); waitObject.notify();
} }
} }
/**
* Send an Hello/Null Packet to keep connection
*/
private void sendHello() {
TransactionBuilder builder = new TransactionBuilder("hello");
builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO);
builder.queue(mHPlusSupport.getQueue());
scheduleHello();
synchronized (waitObject) {
waitObject.notify();
}
}
/**
* Schedule an Hello Packet in the future
*/
public void scheduleHello(){
mHelloTime = GregorianCalendar.getInstance();
mHelloTime.add(Calendar.SECOND, HELLO_INTERVAL);
}
/** /**
* Process a message containing information regarding a day slot * Process a message containing information regarding a day slot
* A slot summarizes 10 minutes of data * A slot summarizes 10 minutes of data
@ -218,32 +178,56 @@ class HPlusHandlerThread extends GBDeviceIoThread {
return false; return false;
} }
//Ignore real time messages as they are still not understood
if(!mSlotsInitialSync){
mGetDaySlotsTime.set(Calendar.SECOND, CURRENT_DAY_SYNC_PERIOD);
return true;
}
Calendar now = GregorianCalendar.getInstance(); Calendar now = GregorianCalendar.getInstance();
int nowSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + (now.get(Calendar.MINUTE) / 10); int nowSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + (now.get(Calendar.MINUTE) / 10);
if(record.slot == nowSlot){
//If the slot is in the future, actually it is from the previous day if(mCurrentDaySlot != null && mCurrentDaySlot != record){
//Subtract a day of seconds mCurrentDaySlot.accumulate(record);
if(record.slot >= nowSlot){ mDaySlotRecords.add(mCurrentDaySlot);
record.timestamp -= 3600 * 24; mCurrentDaySlot = null;
}else{
//Store it to a temp variable as this is an intermediate value
mCurrentDaySlot = record;
if(!mSlotsInitialSync)
return true;
}
} }
//Ignore out of order messages if(mSlotsInitialSync) {
if(record.slot == mLastSlotReceived + 1) {
mLastSlotReceived = record.slot; //If the slot is in the future, actually it is from the previous day
//Subtract a day of seconds
if(record.slot > nowSlot){
record.timestamp -= 3600 * 24;
}
if (record.slot == mLastSlotReceived + 1) {
mLastSlotReceived = record.slot;
}
//Ignore the current slot as it is incomplete
if(record.slot != nowSlot)
mDaySlotRecords.add(record);
//Still fetching ring buffer. Request the next slots
if (record.slot == mLastSlotRequested) {
mGetDaySlotsTime.clear();
synchronized (waitObject) {
waitObject.notify();
}
}
//Keep buffering
if(record.slot != 143)
return true;
} else {
mGetDaySlotsTime = GregorianCalendar.getInstance();
mGetDaySlotsTime.add(Calendar.DAY_OF_MONTH, 1);
} }
if(record.slot < 143){ if(mDaySlotRecords.size() > 0) {
mDaySlotSamples.add(record);
}else {
//Sort the samples //Sort the samples
Collections.sort(mDaySlotSamples, new Comparator<HPlusDataRecordDaySlot>() { Collections.sort(mDaySlotRecords, new Comparator<HPlusDataRecordDaySlot>() {
public int compare(HPlusDataRecordDaySlot one, HPlusDataRecordDaySlot other) { public int compare(HPlusDataRecordDaySlot one, HPlusDataRecordDaySlot other) {
return one.timestamp - other.timestamp; return one.timestamp - other.timestamp;
} }
@ -253,20 +237,20 @@ class HPlusHandlerThread extends GBDeviceIoThread {
HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession()); HPlusHealthSampleProvider provider = new HPlusHealthSampleProvider(getDevice(), dbHandler.getDaoSession());
List<HPlusHealthActivitySample> samples = new ArrayList<>(); List<HPlusHealthActivitySample> samples = new ArrayList<>();
for(HPlusDataRecordDaySlot storedRecord : mDaySlotSamples) { for (HPlusDataRecordDaySlot storedRecord : mDaySlotRecords) {
HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp); HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp);
sample.setRawHPlusHealthData(record.getRawData()); sample.setRawHPlusHealthData(storedRecord.getRawData());
sample.setSteps(record.steps); sample.setSteps(storedRecord.steps);
sample.setHeartRate(record.heartRate); sample.setHeartRate(storedRecord.heartRate);
sample.setRawKind(record.type); sample.setRawKind(storedRecord.type);
sample.setProvider(provider); sample.setProvider(provider);
samples.add(sample); samples.add(sample);
} }
provider.getSampleDao().insertOrReplaceInTx(samples); provider.getSampleDao().insertOrReplaceInTx(samples);
mDaySlotSamples.clear(); mDaySlotRecords.clear();
} catch (GBException ex) { } catch (GBException ex) {
LOG.debug((ex.getMessage())); LOG.debug((ex.getMessage()));
@ -274,13 +258,6 @@ class HPlusHandlerThread extends GBDeviceIoThread {
LOG.debug(ex.getMessage()); LOG.debug(ex.getMessage());
} }
} }
//Still fetching ring buffer. Request the next slots
if (record.slot == mLastSlotRequested) {
mGetDaySlotsTime.clear();
synchronized (waitObject) {
waitObject.notify();
}
}
return true; return true;
} }
@ -357,6 +334,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
LOG.debug((e.getMessage())); LOG.debug((e.getMessage()));
return false; return false;
} }
//Skip duplicated messages as the device seems to send the same record multiple times //Skip duplicated messages as the device seems to send the same record multiple times
//This can be used to detect the user is moving (not sleeping) //This can be used to detect the user is moving (not sleeping)
if(prevRealTimeRecord != null && record.same(prevRealTimeRecord)) if(prevRealTimeRecord != null && record.same(prevRealTimeRecord))
@ -490,7 +468,6 @@ class HPlusHandlerThread extends GBDeviceIoThread {
* Messages will be provided every 10 minutes after they are available * Messages will be provided every 10 minutes after they are available
*/ */
private void requestNextDaySlots() { private void requestNextDaySlots() {
Calendar now = GregorianCalendar.getInstance(); Calendar now = GregorianCalendar.getInstance();
int currentSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + now.get(Calendar.MINUTE) / 10; int currentSlot = now.get(Calendar.HOUR_OF_DAY) * 6 + now.get(Calendar.MINUTE) / 10;

View File

@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@ -54,6 +55,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
public BluetoothGattCharacteristic measureCharacteristic = null; public BluetoothGattCharacteristic measureCharacteristic = null;
private HPlusHandlerThread syncHelper; private HPlusHandlerThread syncHelper;
private DeviceType deviceType = DeviceType.UNKNOWN;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override @Override
@ -65,8 +67,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
} }
}; };
public HPlusSupport() { public HPlusSupport(DeviceType type) {
super(LOG); super(LOG);
deviceType = type;
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE);
addSupportedService(HPlusConstants.UUID_SERVICE_HP); addSupportedService(HPlusConstants.UUID_SERVICE_HP);
@ -75,7 +80,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
IntentFilter intentFilter = new IntentFilter(); IntentFilter intentFilter = new IntentFilter();
broadcastManager.registerReceiver(mReceiver, intentFilter); broadcastManager.registerReceiver(mReceiver, intentFilter);
} }
@Override @Override
@ -107,7 +111,9 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
sendUserInfo(builder); //Sync preferences sendUserInfo(builder); //Sync preferences
setSIT(builder); //Sync SIT Interval setSIT(builder); //Sync SIT Interval
setCurrentDate(builder); // Sync Current Date setCurrentDate(builder); // Sync Current Date
setDayOfWeek(builder);
setCurrentTime(builder); // Sync Current Time setCurrentTime(builder); // Sync Current Time
setLanguage(builder);
requestDeviceInfo(builder); requestDeviceInfo(builder);
@ -133,51 +139,69 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
} }
private HPlusSupport syncPreferences(TransactionBuilder transaction) { private HPlusSupport syncPreferences(TransactionBuilder transaction) {
byte gender = HPlusCoordinator.getUserGender(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())) { if(deviceType == DeviceType.HPLUS) {
int t = HPlusCoordinator.getAlertTime(getDevice().getAddress()); byte gender = HPlusCoordinator.getUserGender(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.getLanguage(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;
alertTimeHour = (byte) ((t / 256) & 0xff); if (HPlusCoordinator.getSWAlertTime(getDevice().getAddress())) {
alertTimeMinute = (byte) (t % 256); int t = HPlusCoordinator.getAlertTime(getDevice().getAddress());
alertTimeHour = (byte) ((t / 256) & 0xff);
alertTimeMinute = (byte) (t % 256);
}
byte unit = HPlusCoordinator.getUnit(getDevice().getAddress());
byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress()));
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_PREFS,
gender,
age,
bodyHeight,
bodyWeight,
0,
0,
(byte) ((goal / 256) & 0xff),
(byte) (goal % 256),
displayTime,
country,
0,
social,
allDayHeart,
wrist,
0,
alertTimeHour,
alertTimeMinute,
unit,
timemode
});
}else if(deviceType == DeviceType.MAKIBESF68){
//Makibes doesn't support setting everything at once.
setGender(transaction);
setAge(transaction);
setWeight(transaction);
setHeight(transaction);
setGoal(transaction);
setLanguage(transaction);
setScreenTime(transaction);
//setAlarm(transaction, t);
setUnit(transaction);
setTimeMode(transaction);
} }
byte unit = HPlusCoordinator.getUnit(getDevice().getAddress());
byte timemode = HPlusCoordinator.getTimeMode((getDevice().getAddress()));
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_PREFS,
gender,
age,
bodyHeight,
bodyWeight,
0,
0,
(byte) ((goal / 256) & 0xff),
(byte) (goal % 256),
displayTime,
country,
0,
social,
allDayHeart,
wrist,
0,
alertTimeHour,
alertTimeMinute,
unit,
timemode
});
setAllDayHeart(transaction); setAllDayHeart(transaction);
@ -185,7 +209,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
} }
private HPlusSupport setLanguage(TransactionBuilder transaction) { private HPlusSupport setLanguage(TransactionBuilder transaction) {
byte value = HPlusCoordinator.getCountry(getDevice().getAddress()); byte value = HPlusCoordinator.getLanguage(getDevice().getAddress());
transaction.write(ctrlCharacteristic, new byte[]{ transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_LANGUAGE, HPlusConstants.CMD_SET_LANGUAGE,
value value
@ -248,13 +272,20 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
transaction.write(ctrlCharacteristic, new byte[]{ transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_WEEK, HPlusConstants.CMD_SET_WEEK,
(byte) c.get(Calendar.DAY_OF_WEEK) (byte) (c.get(Calendar.DAY_OF_WEEK) - 1)
}); });
return this; return this;
} }
private HPlusSupport setSIT(TransactionBuilder transaction) { private HPlusSupport setSIT(TransactionBuilder transaction) {
//Makibes F68 doesn't like this command.
//Just ignore.
if(deviceType == DeviceType.MAKIBESF68){
return this;
}
int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress()); int startTime = HPlusCoordinator.getSITStartTime(getDevice().getAddress());
int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress()); int endTime = HPlusCoordinator.getSITEndTime(getDevice().getAddress());
@ -646,8 +677,17 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
} }
private void showIncomingCall(String name, String number) { private void showIncomingCall(String name, String rawNumber) {
try { try {
StringBuilder number = new StringBuilder();
//Clean up number as the device only accepts digits
for(char c : rawNumber.toCharArray()){
if(Character.isDigit(c)){
number.append(c);
}
}
TransactionBuilder builder = performInitialized("incomingCall"); TransactionBuilder builder = performInitialized("incomingCall");
//Enable call notifications //Enable call notifications

View File

@ -23,6 +23,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
@ -172,6 +173,8 @@ public class DeviceHelper {
result.add(new VibratissimoCoordinator()); result.add(new VibratissimoCoordinator());
result.add(new LiveviewCoordinator()); result.add(new LiveviewCoordinator());
result.add(new HPlusCoordinator()); result.add(new HPlusCoordinator());
result.add(new MakibesF68Coordinator());
return result; return result;
} }