wip: here
parent
e34917ef8b
commit
cc9a1bcfc6
|
@ -0,0 +1,173 @@
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.here;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Nicolò Balzarotti <anothersms@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class HereConstants {
|
||||||
|
|
||||||
|
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 ARG_WRIST_LEFT = 0; //Guess...
|
||||||
|
public static final byte ARG_WRIST_RIGHT = 1; //Guess...
|
||||||
|
|
||||||
|
public static final byte ARG_LANGUAGE_CN = 1;
|
||||||
|
public static final byte ARG_LANGUAGE_EN = 2;
|
||||||
|
|
||||||
|
public static final byte ARG_TIMEMODE_24H = 1;
|
||||||
|
public static final byte ARG_TIMEMODE_12H = 0;
|
||||||
|
|
||||||
|
public static final byte ARG_UNIT_METRIC = 0;
|
||||||
|
public static final byte ARG_UNIT_IMPERIAL = 1;
|
||||||
|
|
||||||
|
public static final byte ARG_GENDER_MALE = 0;
|
||||||
|
public static final byte ARG_GENDER_FEMALE = 1;
|
||||||
|
|
||||||
|
public static final byte ARG_HEARTRATE_MEASURE_ON = 11;
|
||||||
|
public static final byte ARG_HEARTRATE_MEASURE_OFF = 22;
|
||||||
|
|
||||||
|
public static final byte ARG_HEARTRATE_ALLDAY_ON = 0x0A;
|
||||||
|
public static final byte ARG_HEARTRATE_ALLDAY_OFF = (byte) 0xff;
|
||||||
|
|
||||||
|
public static final byte INCOMING_CALL_STATE_DISABLED_THRESHOLD = 0x7B;
|
||||||
|
public static final byte INCOMING_CALL_STATE_ENABLED = (byte) 0xAA;
|
||||||
|
|
||||||
|
public static final byte ARG_ALARM_DISABLE = (byte) -1;
|
||||||
|
|
||||||
|
public static final byte[] CMD_SET_PREF_START = new byte[]{0x4f, 0x5a};
|
||||||
|
public static final byte[] CMD_SET_PREF_START1 = new byte[]{0x4d};
|
||||||
|
//public static final byte CMD_SET_ALARM = 0x4c; Unknown
|
||||||
|
public static final byte CMD_SET_ALARM = 0x0c;
|
||||||
|
public static final byte CMD_SET_LANGUAGE = 0x22;
|
||||||
|
public static final byte CMD_SET_TIMEMODE = 0x47;
|
||||||
|
public static final byte CMD_SET_UNITS = 0x48;
|
||||||
|
public static final byte CMD_SET_GENDER = 0x2d;
|
||||||
|
public static final byte CMD_SET_DATE = 0x08;
|
||||||
|
public static final byte CMD_SET_TIME = 0x09;
|
||||||
|
public static final byte CMD_SET_WEEK = 0x2a;
|
||||||
|
public static final byte CMD_SET_PREF_SIT = 0x1e;
|
||||||
|
public static final byte CMD_SET_WEIGHT = 0x05;
|
||||||
|
public static final byte CMD_HEIGHT = 0x04;
|
||||||
|
public static final byte CMD_SET_AGE = 0x2c;
|
||||||
|
public static final byte CMD_SET_GOAL = 0x26;
|
||||||
|
public static final byte CMD_SET_SCREENTIME = 0x0b;
|
||||||
|
public static final byte CMD_SET_BLOOD = 0x4e; //??
|
||||||
|
|
||||||
|
public static final byte CMD_SET_FINDME = 0x0a;
|
||||||
|
public static final byte ARG_FINDME_ON = 0x01;
|
||||||
|
public static final byte ARG_FINDME_OFF = 0x02;
|
||||||
|
|
||||||
|
public static final byte CMD_GET_VERSION = 0x17;
|
||||||
|
public static final byte CMD_SET_END = 0x4f;
|
||||||
|
public static final byte CMD_SET_INCOMING_CALL_NUMBER = 0x23;
|
||||||
|
public static final byte CMD_SET_ALLDAY_HRM = 0x35;
|
||||||
|
public static final byte CMD_ACTION_INCOMING_CALL = 0x41;
|
||||||
|
public static final byte CMD_SET_CONF_END = 0x4f;
|
||||||
|
public static final byte CMD_SET_PREFS = 0x50;
|
||||||
|
public static final byte CMD_SET_SIT_INTERVAL = 0x51;
|
||||||
|
public static final byte CMD_SET_HEARTRATE_STATE = 0x32;
|
||||||
|
|
||||||
|
//Actions to device
|
||||||
|
public static final byte CMD_GET_ACTIVE_DAY = 0x27;
|
||||||
|
public static final byte CMD_GET_DAY_DATA = 0x15;
|
||||||
|
public static final byte CMD_GET_SLEEP = 0x19;
|
||||||
|
public static final byte CMD_GET_CURR_DATA = 0x16;
|
||||||
|
public static final byte CMD_GET_DEVICE_ID = 0x24;
|
||||||
|
|
||||||
|
public static final byte CMD_ACTION_INCOMING_SOCIAL = 0x31;
|
||||||
|
//public static final byte COMMAND_ACTION_INCOMING_SMS = 0x40; //Unknown
|
||||||
|
public static final byte CMD_ACTION_DISPLAY_TEXT = 0x43;
|
||||||
|
|
||||||
|
public static final byte CMD_ACTION_DISPLAY_TEXT_NAME = 0x3F;
|
||||||
|
public static final byte CMD_ACTION_DISPLAY_TEXT_NAME_CN = 0x3E; //Text in GB2312?
|
||||||
|
public static final byte[] CMD_ACTION_HELLO = new byte[]{0x01, 0x00};
|
||||||
|
public static final byte CMD_SHUTDOWN = 0x5B;
|
||||||
|
public static final byte ARG_SHUTDOWN_EN = 0x5A;
|
||||||
|
|
||||||
|
public static final byte CMD_FACTORY_RESET = -74;
|
||||||
|
public static final byte ARG_FACTORY_RESET_EN = 0x5A;
|
||||||
|
|
||||||
|
public static final byte CMD_SET_INCOMING_MESSAGE = 0x07;
|
||||||
|
public static final byte CMD_SET_INCOMING_CALL = 0x06;
|
||||||
|
public static final byte ARG_INCOMING_CALL = (byte) -86;
|
||||||
|
public static final byte ARG_INCOMING_MESSAGE = (byte) -86;
|
||||||
|
|
||||||
|
//Incoming Messages
|
||||||
|
public static final byte DATA_STATS = 0x33;
|
||||||
|
public static final byte DATA_STEPS = 0x36;
|
||||||
|
public static final byte DATA_DAY_SUMMARY = 0x38;
|
||||||
|
public static final byte DATA_DAY_SUMMARY_ALT = 0x39;
|
||||||
|
public static final byte DATA_SLEEP = 0x1A;
|
||||||
|
public static final byte DATA_VERSION = 0x18;
|
||||||
|
|
||||||
|
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_TIMEFORMAT = "hplus_timeformat";
|
||||||
|
public static final String PREF_HPLUS_WRIST = "hplus_wrist";
|
||||||
|
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 Map<Character, Byte> transliterateMap = new HashMap<Character, Byte>(){
|
||||||
|
{
|
||||||
|
//These are missing
|
||||||
|
put('ó', new Byte((byte) 111));
|
||||||
|
put('Ó', new Byte((byte) 79));
|
||||||
|
put('í', new Byte((byte) 105));
|
||||||
|
put('Í', new Byte((byte) 73));
|
||||||
|
put('ú', new Byte((byte) 117));
|
||||||
|
put('Ú', new Byte((byte) 85));
|
||||||
|
|
||||||
|
//These mostly belong to the extended ASCII table
|
||||||
|
put('Ç', new Byte((byte) 128));
|
||||||
|
put('ü', new Byte((byte) 129));
|
||||||
|
put('é', new Byte((byte) 130));
|
||||||
|
put('â', new Byte((byte) 131));
|
||||||
|
put('ä', new Byte((byte) 132));
|
||||||
|
put('à', new Byte((byte) 133));
|
||||||
|
put('ã', new Byte((byte) 134));
|
||||||
|
put('ç', new Byte((byte) 135));
|
||||||
|
put('ê', new Byte((byte) 136));
|
||||||
|
put('ë', new Byte((byte) 137));
|
||||||
|
put('è', new Byte((byte) 138));
|
||||||
|
put('Ï', new Byte((byte) 139));
|
||||||
|
put('Î', new Byte((byte) 140));
|
||||||
|
put('Ì', new Byte((byte) 141));
|
||||||
|
put('Ã', new Byte((byte) 142));
|
||||||
|
put('Ä', new Byte((byte) 143));
|
||||||
|
put('É', new Byte((byte) 144));
|
||||||
|
put('æ', new Byte((byte) 145));
|
||||||
|
put('Æ', new Byte((byte) 146));
|
||||||
|
put('ô', new Byte((byte) 147));
|
||||||
|
put('ö', new Byte((byte) 148));
|
||||||
|
put('ò', new Byte((byte) 149));
|
||||||
|
put('û', new Byte((byte) 150));
|
||||||
|
put('ù', new Byte((byte) 151));
|
||||||
|
put('ÿ', new Byte((byte) 152));
|
||||||
|
put('Ö', new Byte((byte) 153));
|
||||||
|
put('Ü', new Byte((byte) 154));
|
||||||
|
put('¢', new Byte((byte) 155));
|
||||||
|
put('£', new Byte((byte) 156));
|
||||||
|
put('¥', new Byte((byte) 157));
|
||||||
|
put('ƒ', new Byte((byte) 159));
|
||||||
|
put('á', new Byte((byte) 160));
|
||||||
|
put('ñ', new Byte((byte) 164));
|
||||||
|
put('Ñ', new Byte((byte) 165));
|
||||||
|
put('ª', new Byte((byte) 166));
|
||||||
|
put('º', new Byte((byte) 167));
|
||||||
|
put('¿', new Byte((byte) 168));
|
||||||
|
put('¬', new Byte((byte) 170));
|
||||||
|
put('½', new Byte((byte) 171));
|
||||||
|
put('¼', new Byte((byte) 172));
|
||||||
|
put('¡', new Byte((byte) 173));
|
||||||
|
put('«', new Byte((byte) 174));
|
||||||
|
put('»', new Byte((byte) 175));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,261 @@
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.here;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Nicolò Balzarotti <anothersms@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 de.greenrobot.dao.query.QueryBuilder;
|
||||||
|
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.entities.DaoSession;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||||
|
//import nodomain.freeyourgadget.gadgetbridge.entities.HereHealthActivitySampleDao;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Pattern; // Regex match name
|
||||||
|
import java.util.regex.Matcher; // Regex match name
|
||||||
|
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext;
|
||||||
|
|
||||||
|
public class HereCoordinator extends AbstractDeviceCoordinator {
|
||||||
|
protected static final Logger LOG = LoggerFactory.getLogger(HereCoordinator.class);
|
||||||
|
protected static Prefs prefs = GBApplication.getPrefs();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public Collection<? extends ScanFilter> createBLEScanFilters() {
|
||||||
|
ParcelUuid hpService = new ParcelUuid(HereConstants.UUID_SERVICE_HP);
|
||||||
|
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(hpService).build();
|
||||||
|
return Collections.singletonList(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||||
|
String name = candidate.getDevice().getName();
|
||||||
|
Pattern pat = Pattern.compile("H([LR])[A-F0-9]+|HERE-([LR])-[A-F0-9]+");
|
||||||
|
Matcher mat = pat.matcher(name);
|
||||||
|
// Identify if device is Left or Right
|
||||||
|
if (mat.matches()) {
|
||||||
|
if (mat.group(1) == "L") {
|
||||||
|
return DeviceType.HEREL;
|
||||||
|
} else if (mat.group(1) == "R") {
|
||||||
|
return DeviceType.HERER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DeviceType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeviceType getDeviceType() {
|
||||||
|
return DeviceType.HERER; //FIXME: SAVE device type
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Activity> getPairingActivity() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Activity> 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 boolean supportsScreenshots() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAlarmConfiguration() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSmartWakeup(GBDevice device) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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<? extends Activity> getAppsManagementActivity() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||||
|
Long deviceId = device.getId();
|
||||||
|
QueryBuilder<?> qb = session.getHereHealthActivitySampleDao().queryBuilder();
|
||||||
|
qb.where(HereHealthActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getLanguage(String address) {
|
||||||
|
String language = prefs.getString("language", "default");
|
||||||
|
Locale locale;
|
||||||
|
|
||||||
|
if (language.equals("default")) {
|
||||||
|
locale = Locale.getDefault();
|
||||||
|
} else {
|
||||||
|
locale = new Locale(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locale.getLanguage().equals(new Locale("cn").getLanguage())){
|
||||||
|
return HereConstants.ARG_LANGUAGE_CN;
|
||||||
|
}else{
|
||||||
|
return HereConstants.ARG_LANGUAGE_EN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getTimeMode(String address) {
|
||||||
|
String tmode = prefs.getString(HereConstants.PREF_HPLUS_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h));
|
||||||
|
|
||||||
|
if(tmode.equals(getContext().getString(R.string.p_timeformat_24h))) {
|
||||||
|
return HereConstants.ARG_TIMEMODE_24H;
|
||||||
|
}else{
|
||||||
|
return HereConstants.ARG_TIMEMODE_12H;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getUnit(String address) {
|
||||||
|
String units = prefs.getString(HereConstants.PREF_HPLUS_UNIT, getContext().getString(R.string.p_unit_metric));
|
||||||
|
|
||||||
|
if(units.equals(getContext().getString(R.string.p_unit_metric))){
|
||||||
|
return HereConstants.ARG_UNIT_METRIC;
|
||||||
|
}else{
|
||||||
|
return HereConstants.ARG_UNIT_IMPERIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getUserWeight(String address) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
|
||||||
|
return (byte) (activityUser.getWeightKg() & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getUserHeight(String address) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
|
||||||
|
return (byte) (activityUser.getHeightCm() & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getUserAge(String address) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
|
||||||
|
return (byte) (activityUser.getAge() & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getUserGender(String address) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
|
||||||
|
if (activityUser.getGender() == ActivityUser.GENDER_MALE)
|
||||||
|
return HereConstants.ARG_GENDER_MALE;
|
||||||
|
|
||||||
|
return HereConstants.ARG_GENDER_FEMALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getGoal(String address) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
|
||||||
|
return activityUser.getStepsGoal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getScreenTime(String address) {
|
||||||
|
return (byte) (prefs.getInt(HereConstants.PREF_HPLUS_SCREENTIME, 5) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getAllDayHR(String address) {
|
||||||
|
Boolean value = (prefs.getBoolean(HereConstants.PREF_HPLUS_ALLDAYHR, true));
|
||||||
|
|
||||||
|
if(value){
|
||||||
|
return HereConstants.ARG_HEARTRATE_ALLDAY_ON;
|
||||||
|
}else{
|
||||||
|
return HereConstants.ARG_HEARTRATE_ALLDAY_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getSocial(String address) {
|
||||||
|
//TODO: Figure what this is. Returning the default value
|
||||||
|
|
||||||
|
return (byte) 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte getUserWrist(String address) {
|
||||||
|
String value = prefs.getString(HereConstants.PREF_HPLUS_WRIST, getContext().getString(R.string.left));
|
||||||
|
|
||||||
|
if(value.equals(getContext().getString(R.string.left))){
|
||||||
|
return HereConstants.ARG_WRIST_LEFT;
|
||||||
|
}else{
|
||||||
|
return HereConstants.ARG_WRIST_RIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSITStartTime(String address) {
|
||||||
|
return prefs.getInt(HereConstants.PREF_HPLUS_SIT_START_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSITEndTime(String address) {
|
||||||
|
return prefs.getInt(HereConstants.PREF_HPLUS_SIT_END_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,223 @@
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.here;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Nicolò Balzarotti <anothersms@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.greenrobot.dao.AbstractDao;
|
||||||
|
import de.greenrobot.dao.Property;
|
||||||
|
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.HPlusHealthActivityOverlay;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivityOverlayDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.HPlusHealthActivitySampleDao;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusDataRecord;
|
||||||
|
|
||||||
|
public class HereHealthSampleProvider extends AbstractSampleProvider<HereHealthActivitySample> {
|
||||||
|
|
||||||
|
private GBDevice mDevice;
|
||||||
|
private DaoSession mSession;
|
||||||
|
|
||||||
|
public HereHealthSampleProvider(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 HPlusDataRecord.TYPE_DAY_SLOT:
|
||||||
|
case HPlusDataRecord.TYPE_DAY_SUMMARY:
|
||||||
|
case HPlusDataRecord.TYPE_REALTIME:
|
||||||
|
case HPlusDataRecord.TYPE_SLEEP:
|
||||||
|
case HPlusDataRecord.TYPE_UNKNOWN:
|
||||||
|
return ActivityKind.TYPE_UNKNOWN;
|
||||||
|
default:
|
||||||
|
return rawType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int toRawActivityKind(int activityKind) {
|
||||||
|
switch (activityKind){
|
||||||
|
case ActivityKind.TYPE_DEEP_SLEEP:
|
||||||
|
return ActivityKind.TYPE_DEEP_SLEEP;
|
||||||
|
case ActivityKind.TYPE_LIGHT_SLEEP:
|
||||||
|
return ActivityKind.TYPE_LIGHT_SLEEP;
|
||||||
|
default:
|
||||||
|
return HPlusDataRecord.TYPE_DAY_SLOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getTimestampSampleProperty() {
|
||||||
|
return HPlusHealthActivitySampleDao.Properties.Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HereHealthActivitySample createActivitySample() {
|
||||||
|
return new HPlusHealthActivitySample();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Property getRawKindSampleProperty() {
|
||||||
|
return null; // HPlusHealthActivitySampleDao.Properties.RawKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float normalizeIntensity(int rawIntensity) {
|
||||||
|
return rawIntensity / (float) 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Property getDeviceIdentifierSampleProperty() {
|
||||||
|
return HPlusHealthActivitySampleDao.Properties.DeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractDao<HereHealthActivitySample, ?> getSampleDao() {
|
||||||
|
return getSession().getHPlusHealthActivitySampleDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<HPlusHealthActivitySample> getActivityamples(int timestamp_from, int timestamp_to) {
|
||||||
|
return getAllActivitySamples(timestamp_from, timestamp_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HPlusHealthActivitySample> getSleepSamples(int timestamp_from, int timestamp_to) {
|
||||||
|
return getAllActivitySamples(timestamp_from, timestamp_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public List<HPlusHealthActivitySample> getAllActivitySamples(int timestamp_from, int timestamp_to) {
|
||||||
|
List<HPlusHealthActivitySample> samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL);
|
||||||
|
|
||||||
|
Device dbDevice = DBHelper.findDevice(getDevice(), getSession());
|
||||||
|
if (dbDevice == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<HPlusHealthActivityOverlay> qb = getSession().getHPlusHealthActivityOverlayDao().queryBuilder();
|
||||||
|
|
||||||
|
qb.where(HPlusHealthActivityOverlayDao.Properties.DeviceId.eq(dbDevice.getId()),
|
||||||
|
HPlusHealthActivityOverlayDao.Properties.TimestampFrom.ge(timestamp_from - 3600 * 24),
|
||||||
|
HPlusHealthActivityOverlayDao.Properties.TimestampTo.le(timestamp_to),
|
||||||
|
HPlusHealthActivityOverlayDao.Properties.TimestampTo.ge(timestamp_from));
|
||||||
|
|
||||||
|
List<HPlusHealthActivityOverlay> overlayRecords = qb.build().list();
|
||||||
|
|
||||||
|
//Todays sample steps will come from the Day Slots messages
|
||||||
|
//Historical steps will be provided by Day Summaries messages
|
||||||
|
//This will allow both week and current day results to be consistent
|
||||||
|
Calendar today = GregorianCalendar.getInstance();
|
||||||
|
today.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
today.set(Calendar.MINUTE, 0);
|
||||||
|
today.set(Calendar.SECOND, 0);
|
||||||
|
today.set(Calendar.MILLISECOND, 0);
|
||||||
|
|
||||||
|
int stepsTodayMax = 0;
|
||||||
|
int stepsTodayCount = 0;
|
||||||
|
HPlusHealthActivitySample lastSample = null;
|
||||||
|
|
||||||
|
for(HPlusHealthActivitySample sample: samples){
|
||||||
|
if(sample.getTimestamp() >= today.getTimeInMillis() / 1000){
|
||||||
|
|
||||||
|
/**Strategy is:
|
||||||
|
* Calculate max steps from realtime messages
|
||||||
|
* Calculate sum of steps from day 10 minute slot summaries
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(sample.getRawKind() == HPlusDataRecord.TYPE_REALTIME) {
|
||||||
|
stepsTodayMax = Math.max(stepsTodayMax, sample.getSteps());
|
||||||
|
}else if(sample.getRawKind() == HPlusDataRecord.TYPE_DAY_SLOT) {
|
||||||
|
stepsTodayCount += sample.getSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.setSteps(ActivitySample.NOT_MEASURED);
|
||||||
|
lastSample = sample;
|
||||||
|
}else{
|
||||||
|
if (sample.getRawKind() != HPlusDataRecord.TYPE_DAY_SUMMARY) {
|
||||||
|
sample.setSteps(ActivitySample.NOT_MEASURED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastSample != null)
|
||||||
|
lastSample.setSteps(Math.max(stepsTodayCount, stepsTodayMax));
|
||||||
|
|
||||||
|
for (HPlusHealthActivityOverlay overlay : overlayRecords) {
|
||||||
|
|
||||||
|
//Create fake events to improve activity counters if there are no events around the overlay
|
||||||
|
//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.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) {
|
||||||
|
|
||||||
|
if (sample.getTimestamp() >= overlay.getTimestampFrom() && sample.getTimestamp() < overlay.getTimestampTo()) {
|
||||||
|
sample.setRawKind(overlay.getRawKind());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detachFromSession();
|
||||||
|
|
||||||
|
Collections.sort(samples, new Comparator<HPlusHealthActivitySample>() {
|
||||||
|
public int compare(HPlusHealthActivitySample one, HPlusHealthActivitySample other) {
|
||||||
|
return one.getTimestamp() - other.getTimestamp();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<HPlusHealthActivitySample> insertVirtualItem(List<HPlusHealthActivitySample> samples, int timestamp, long deviceId, long userId) {
|
||||||
|
HPlusHealthActivitySample sample = new HPlusHealthActivitySample(
|
||||||
|
timestamp, // ts
|
||||||
|
deviceId,
|
||||||
|
userId, // User id
|
||||||
|
null, // Raw Data
|
||||||
|
ActivityKind.TYPE_UNKNOWN,
|
||||||
|
1, // Intensity
|
||||||
|
ActivitySample.NOT_MEASURED, // Steps
|
||||||
|
ActivitySample.NOT_MEASURED, // HR
|
||||||
|
ActivitySample.NOT_MEASURED, // Distance
|
||||||
|
ActivitySample.NOT_MEASURED // Calories
|
||||||
|
);
|
||||||
|
|
||||||
|
sample.setProvider(this);
|
||||||
|
samples.add(sample);
|
||||||
|
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.here;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Nicolò Balzarotti <anothersms@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.here.HereCoordinator;
|
||||||
|
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 HereCoordinator {
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturer() {
|
||||||
|
return "Makibes";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ public enum DeviceType {
|
||||||
LIVEVIEW(30),
|
LIVEVIEW(30),
|
||||||
HPLUS(40),
|
HPLUS(40),
|
||||||
MAKIBESF68(41),
|
MAKIBESF68(41),
|
||||||
|
HEREL(50), // Single device made of L + R
|
||||||
|
HERER(51), // But can be controlled independently
|
||||||
TEST(1000);
|
TEST(1000);
|
||||||
|
|
||||||
private final int key;
|
private final int key;
|
||||||
|
|
Loading…
Reference in New Issue