First piece of Garmin Vivomove HR support basics

- `DeviceType` definition
- Definition of activity sample table
- Coordinator and sample provider
This commit is contained in:
Mormegil 2023-06-15 15:38:25 +02:00 committed by José Rebelo
parent bdb904faf9
commit 114f6fcbf0
9 changed files with 593 additions and 2 deletions

View File

@ -90,8 +90,9 @@ public class GBDaoGenerator {
addCasioGBX100Sample(schema, user, device);
addFitProActivitySample(schema, user, device);
addPineTimeActivitySample(schema, user, device);
addHybridHRActivitySample(schema, user, device);
addVivomoveHrActivitySample(schema, user, device);
addCalendarSyncState(schema, device);
addAlarms(schema, user, device);
addReminders(schema, user, device);
@ -461,6 +462,19 @@ public class GBDaoGenerator {
return activitySample;
}
private static Entity addVivomoveHrActivitySample(Schema schema, Entity user, Entity device) {
final Entity activitySample = addEntity(schema, "VivomoveHrActivitySample");
activitySample.implementsSerializable();
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
activitySample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetterAndSetter(OVERRIDE);
activitySample.addIntProperty("caloriesBurnt");
activitySample.addIntProperty("floorsClimbed");
return activitySample;
}
private static Entity addWatchXPlusHealthActivitySample(Schema schema, Entity user, Entity device) {
Entity activitySample = addEntity(schema, "WatchXPlusActivitySample");
activitySample.implementsSerializable();

View File

@ -66,7 +66,8 @@ vendor's servers.
- [Galaxy Buds Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#galaxy-buds-pro)
- [Galaxy Buds2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#galaxy-buds2)
- [Galaxy Buds2 Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#galaxy-buds2-pro)
- Garmin
- Vívomove HR
- [HPlus Devices (e.g. ZeBand)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/HPlus)
- ID115
- [iTag](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/iTag)

View File

@ -0,0 +1,142 @@
package nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public enum GarminCapability {
CONNECT_MOBILE_FIT_LINK,
GOLF_FIT_LINK,
VIVOKID_JR_FIT_LINK,
SYNC,
DEVICE_INITIATES_SYNC,
HOST_INITIATED_SYNC_REQUESTS,
GNCS,
ADVANCED_MUSIC_CONTROLS,
FIND_MY_PHONE,
FIND_MY_WATCH,
CONNECTIQ_HTTP,
CONNECTIQ_SETTINGS,
CONNECTIQ_WATCH_APP_DOWNLOAD,
CONNECTIQ_WIDGET_DOWNLOAD,
CONNECTIQ_WATCH_FACE_DOWNLOAD,
CONNECTIQ_DATA_FIELD_DOWNLOAD,
CONNECTIQ_APP_MANAGEMENT,
COURSE_DOWNLOAD,
WORKOUT_DOWNLOAD,
GOLF_COURSE_DOWNLOAD,
DELTA_SOFTWARE_UPDATE_FILES,
FITPAY,
LIVETRACK,
LIVETRACK_AUTO_START,
LIVETRACK_MESSAGING,
GROUP_LIVETRACK,
WEATHER_CONDITIONS,
WEATHER_ALERTS,
GPS_EPHEMERIS_DOWNLOAD,
EXPLICIT_ARCHIVE,
SWING_SENSOR,
SWING_SENSOR_REMOTE,
INCIDENT_DETECTION,
TRUEUP,
INSTANT_INPUT,
SEGMENTS,
AUDIO_PROMPT_LAP,
AUDIO_PROMPT_PACE_SPEED,
AUDIO_PROMPT_HEART_RATE,
AUDIO_PROMPT_POWER,
AUDIO_PROMPT_NAVIGATION,
AUDIO_PROMPT_CADENCE,
SPORT_GENERIC,
SPORT_RUNNING,
SPORT_CYCLING,
SPORT_TRANSITION,
SPORT_FITNESS_EQUIPMENT,
SPORT_SWIMMING,
STOP_SYNC_AFTER_SOFTWARE_UPDATE,
CALENDAR,
WIFI_SETUP,
SMS_NOTIFICATIONS,
BASIC_MUSIC_CONTROLS,
AUDIO_PROMPTS_SPEECH,
DELTA_SOFTWARE_UPDATES,
GARMIN_DEVICE_INFO_FILE_TYPE,
SPORT_PROFILE_SETUP,
HSA_SUPPORT,
SPORT_STRENGTH,
SPORT_CARDIO,
UNION_PAY,
IPASS,
CIQ_AUDIO_CONTENT_PROVIDER,
UNION_PAY_INTERNATIONAL,
REQUEST_PAIR_FLOW,
LOCATION_UPDATE,
LTE_SUPPORT,
DEVICE_DRIVEN_LIVETRACK_SUPPORT,
CUSTOM_CANNED_TEXT_LIST_SUPPORT,
EXPLORE_SYNC,
INCIDENT_DETECT_AND_ASSISTANCE,
CURRENT_TIME_REQUEST_SUPPORT,
CONTACTS_SUPPORT,
LAUNCH_REMOTE_CIQ_APP_SUPPORT,
DEVICE_MESSAGES,
WAYPOINT_TRANSFER,
MULTI_LINK_SERVICE,
OAUTH_CREDENTIALS,
GOLF_9_PLUS_9,
ANTI_THEFT_ALARM,
INREACH,
EVENT_SHARING;
public static final Set<GarminCapability> ALL_CAPABILITIES = new HashSet<>(values().length);
private static final Map<Integer, GarminCapability> FROM_ORDINAL = new HashMap<>(values().length);
static {
for (GarminCapability cap : values()) {
FROM_ORDINAL.put(cap.ordinal(), cap);
ALL_CAPABILITIES.add(cap);
}
}
public static Set<GarminCapability> setFromBinary(byte[] bytes) {
final Set<GarminCapability> result = new HashSet<>(GarminCapability.values().length);
int current = 0;
for (int b : bytes) {
for (int curr = 1; curr < 0x100; curr <<= 1) {
if ((b & curr) != 0) {
result.add(FROM_ORDINAL.get(current));
}
++current;
}
}
return result;
}
public static byte[] setToBinary(Set<GarminCapability> capabilities) {
final GarminCapability[] values = values();
final byte[] result = new byte[(values.length + 7) / 8];
int bytePos = 0;
int bitPos = 0;
for (int i = 0; i < values.length; ++i) {
if (capabilities.contains(FROM_ORDINAL.get(i))) {
result[bytePos] |= (1 << bitPos);
}
++bitPos;
if (bitPos >= 8) {
bitPos = 0;
++bytePos;
}
}
return result;
}
public static String setToString(Set<GarminCapability> capabilities) {
final StringBuilder result = new StringBuilder();
for (GarminCapability cap : capabilities) {
if (result.length() > 0) result.append(", ");
result.append(cap.name());
}
return result.toString();
}
}

View File

@ -0,0 +1,82 @@
package nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
public class VivomoveConstants {
public static final UUID UUID_SERVICE_GARMIN_1 = UUID.fromString("6A4E2401-667B-11E3-949A-0800200C9A66");
public static final UUID UUID_SERVICE_GARMIN_2 = UUID.fromString("6A4E2500-667B-11E3-949A-0800200C9A66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_SEND = UUID.fromString("6a4e4c80-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE = UUID.fromString("6a4ecd28-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_HEART_RATE = UUID.fromString("6a4e2501-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_STEPS = UUID.fromString("6a4e2502-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_CALORIES = UUID.fromString("6a4e2503-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_STAIRS = UUID.fromString("6a4e2504-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_INTENSITY = UUID.fromString("6a4e2505-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_HEART_RATE_VARIATION = UUID.fromString("6a4e2507-667b-11e3-949a-0800200c9a66");
// public static final UUID UUID_CHARACTERISTIC_GARMIN_STRESS = UUID.fromString("6a4e2508-667b-11e3-949a-0800200c9a66");
public static final UUID UUID_CHARACTERISTIC_GARMIN_2_9 = UUID.fromString("6a4e2509-667b-11e3-949a-0800200c9a66");
// public static final UUID UUID_CHARACTERISTIC_GARMIN_SPO2 = UUID.fromString("6a4e250c-667b-11e3-949a-0800200c9a66");
// public static final UUID UUID_CHARACTERISTIC_GARMIN_RESPIRATION = UUID.fromString("6a4e250e-667b-11e3-949a-0800200c9a66");
// public static final UUID UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public static final int STATUS_ACK = 0;
public static final int STATUS_NAK = 1;
public static final int STATUS_UNSUPPORTED = 2;
public static final int STATUS_DECODE_ERROR = 3;
public static final int STATUS_CRC_ERROR = 4;
public static final int STATUS_LENGTH_ERROR = 5;
public static final int GADGETBRIDGE_UNIT_NUMBER = 22222;
public static final int GARMIN_DEVICE_XML_FILE_INDEX = 65533;
// TODO: Better capability management/configuration
// public static final Set<GarminCapability> OUR_CAPABILITIES = new HashSet<>(Arrays.asList(GarminCapability.SYNC, GarminCapability.GNCS, GarminCapability.ADVANCED_MUSIC_CONTROLS, GarminCapability.FIND_MY_PHONE, GarminCapability.WEATHER_CONDITIONS, GarminCapability.WEATHER_ALERTS, GarminCapability.DEVICE_MESSAGES, GarminCapability.SMS_NOTIFICATIONS, GarminCapability.SYNC, GarminCapability.DEVICE_INITIATES_SYNC, GarminCapability.HOST_INITIATED_SYNC_REQUESTS, GarminCapability.CALENDAR, GarminCapability.CURRENT_TIME_REQUEST_SUPPORT));
public static final Set<GarminCapability> OUR_CAPABILITIES = GarminCapability.ALL_CAPABILITIES;
public static final int MAX_WRITE_SIZE = 20;
/**
* Garmin zero time in seconds since Epoch: 1989-12-31T00:00:00Z
*/
public static final int GARMIN_TIME_EPOCH = 631065600;
public static final SimpleDateFormat ANCS_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ROOT);
public static final int MESSAGE_RESPONSE = 5000;
public static final int MESSAGE_REQUEST = 5001;
public static final int MESSAGE_DOWNLOAD_REQUEST = 5002;
public static final int MESSAGE_UPLOAD_REQUEST = 5003;
public static final int MESSAGE_FILE_TRANSFER_DATA = 5004;
public static final int MESSAGE_CREATE_FILE_REQUEST = 5005;
public static final int MESSAGE_DIRECTORY_FILE_FILTER_REQUEST = 5007;
public static final int MESSAGE_FILE_READY = 5009;
public static final int MESSAGE_FIT_DEFINITION = 5011;
public static final int MESSAGE_FIT_DATA = 5012;
public static final int MESSAGE_WEATHER_REQUEST = 5014;
public static final int MESSAGE_BATTERY_STATUS = 5023;
public static final int MESSAGE_DEVICE_INFORMATION = 5024;
public static final int MESSAGE_DEVICE_SETTINGS = 5026;
public static final int MESSAGE_SYSTEM_EVENT = 5030;
public static final int MESSAGE_SUPPORTED_FILE_TYPES_REQUEST = 5031;
public static final int MESSAGE_NOTIFICATION_SOURCE = 5033;
public static final int MESSAGE_GNCS_CONTROL_POINT_REQUEST = 5034;
public static final int MESSAGE_GNCS_DATA_SOURCE = 5035;
public static final int MESSAGE_NOTIFICATION_SERVICE_SUBSCRIPTION = 5036;
public static final int MESSAGE_SYNC_REQUEST = 5037;
public static final int MESSAGE_FIND_MY_PHONE = 5039;
public static final int MESSAGE_CANCEL_FIND_MY_PHONE = 5040;
public static final int MESSAGE_MUSIC_CONTROL_CAPABILITIES = 5042;
public static final int MESSAGE_PROTOBUF_REQUEST = 5043;
public static final int MESSAGE_PROTOBUF_RESPONSE = 5044;
public static final int MESSAGE_MUSIC_CONTROL_ENTITY_UPDATE = 5049;
public static final int MESSAGE_CONFIGURATION = 5050;
public static final int MESSAGE_CURRENT_TIME_REQUEST = 5052;
public static final int MESSAGE_AUTH_NEGOTIATION = 5101;
}

View File

@ -0,0 +1,119 @@
package nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.GBException;
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.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class VivomoveHrCoordinator extends AbstractDeviceCoordinator {
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
if ("vívomove HR".equals(candidate.getName())) return DeviceType.VIVOMOVE_HR;
final boolean hasServiceUuids = candidate.getServiceUuids().length > 0;
return hasServiceUuids && candidate.supportsService(VivomoveConstants.UUID_SERVICE_GARMIN_2) ? DeviceType.VIVOMOVE_HR : DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.VIVOMOVE_HR;
}
@Nullable
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return true;
}
@Override
public boolean supportsActivityTracking() {
return true;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return new VivomoveHrSampleProvider(device, session);
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return true;
}
@Override
public String getManufacturer() {
return "Garmin";
}
@Override
public boolean supportsAppsManagement(GBDevice device) {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return true;
}
@Override
public boolean supportsWeather() {
return true;
}
@Override
public boolean supportsFindDevice() {
return true;
}
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
// nothing to delete, yet
}
}

View File

@ -0,0 +1,229 @@
package nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.Property;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.VivomoveHrActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.VivomoveHrActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
public class VivomoveHrSampleProvider extends AbstractSampleProvider<VivomoveHrActivitySample> {
public static final int RAW_TYPE_KIND_MASK = 0x0F000000;
public static final int RAW_TYPE_KIND_ACTIVITY = 0x00000000;
public static final int RAW_TYPE_KIND_SLEEP = 0x01000000;
// public static final int RAW_TYPE_KIND_NOT_WORN = 0x0F000000;
public static final int RAW_NOT_WORN = 0x0F000000;
public VivomoveHrSampleProvider(GBDevice device, DaoSession session) {
super(device, session);
}
@Override
public int normalizeType(int rawType) {
if (rawType == RAW_NOT_WORN) {
return ActivityKind.TYPE_NOT_WORN;
}
switch (rawType & RAW_TYPE_KIND_MASK) {
case RAW_TYPE_KIND_ACTIVITY:
return normalizeActivityType(rawType & ~RAW_TYPE_KIND_MASK);
case RAW_TYPE_KIND_SLEEP:
return normalizeSleepType(rawType & ~RAW_TYPE_KIND_MASK);
default:
// ???
return ActivityKind.TYPE_UNKNOWN;
}
}
private static int normalizeActivityType(int rawType) {
switch (rawType) {
case 0:
// generic
return ActivityKind.TYPE_ACTIVITY;
case 1:
// running
return ActivityKind.TYPE_RUNNING;
case 2:
// cycling
return ActivityKind.TYPE_CYCLING;
case 3:
// transition
return ActivityKind.TYPE_ACTIVITY | ActivityKind.TYPE_RUNNING | ActivityKind.TYPE_WALKING | ActivityKind.TYPE_EXERCISE | ActivityKind.TYPE_SWIMMING;
case 4:
// fitness_equipment
return ActivityKind.TYPE_EXERCISE;
case 5:
// swimming
return ActivityKind.TYPE_SWIMMING;
case 6:
// walking
return ActivityKind.TYPE_WALKING;
case 8:
// sedentary
// TODO?
return ActivityKind.TYPE_ACTIVITY;
default:
return ActivityKind.TYPE_UNKNOWN;
}
}
private static int normalizeSleepType(int rawType) {
switch (rawType) {
case 0:
// deep_sleep
return ActivityKind.TYPE_DEEP_SLEEP;
case 1:
// light_sleep
return ActivityKind.TYPE_LIGHT_SLEEP;
case 2:
// awake
case 3:
// more_awake
return ActivityKind.TYPE_ACTIVITY;
default:
// ?
return ActivityKind.TYPE_UNKNOWN;
}
}
@Override
public int toRawActivityKind(int activityKind) {
switch (activityKind) {
case ActivityKind.TYPE_NOT_WORN:
return RAW_NOT_WORN;
case ActivityKind.TYPE_ACTIVITY:
// generic
//noinspection PointlessBitwiseExpression
return RAW_TYPE_KIND_ACTIVITY | 0;
case ActivityKind.TYPE_RUNNING:
// running
return RAW_TYPE_KIND_ACTIVITY | 1;
case ActivityKind.TYPE_CYCLING:
// cycling
return RAW_TYPE_KIND_ACTIVITY | 2;
case ActivityKind.TYPE_ACTIVITY | ActivityKind.TYPE_RUNNING | ActivityKind.TYPE_WALKING | ActivityKind.TYPE_EXERCISE | ActivityKind.TYPE_SWIMMING:
return RAW_TYPE_KIND_ACTIVITY | 3;
case ActivityKind.TYPE_EXERCISE:
// fitness_equipment
return RAW_TYPE_KIND_ACTIVITY | 4;
case ActivityKind.TYPE_SWIMMING:
// swimming
return RAW_TYPE_KIND_ACTIVITY | 5;
case ActivityKind.TYPE_WALKING:
// walking
return RAW_TYPE_KIND_ACTIVITY | 6;
case ActivityKind.TYPE_LIGHT_SLEEP:
return RAW_TYPE_KIND_SLEEP | 1;
case ActivityKind.TYPE_DEEP_SLEEP:
//noinspection PointlessBitwiseExpression
return RAW_TYPE_KIND_SLEEP | 0;
default:
if ((activityKind & ActivityKind.TYPE_SWIMMING) != 0) return RAW_TYPE_KIND_ACTIVITY | 5;
if ((activityKind & ActivityKind.TYPE_CYCLING) != 0) return RAW_TYPE_KIND_ACTIVITY | 2;
if ((activityKind & ActivityKind.TYPE_RUNNING) != 0) return RAW_TYPE_KIND_ACTIVITY | 1;
if ((activityKind & ActivityKind.TYPE_EXERCISE) != 0) return RAW_TYPE_KIND_ACTIVITY | 4;
if ((activityKind & ActivityKind.TYPE_WALKING) != 0) return RAW_TYPE_KIND_ACTIVITY | 6;
if ((activityKind & ActivityKind.TYPE_SLEEP) != 0) return RAW_TYPE_KIND_SLEEP | 1;
if ((activityKind & ActivityKind.TYPE_ACTIVITY) != 0) {
//noinspection PointlessBitwiseExpression
return RAW_TYPE_KIND_ACTIVITY | 0;
}
return 0;
}
}
@Override
public float normalizeIntensity(int rawIntensity) {
return rawIntensity / 255.0f;
}
@Override
public VivomoveHrActivitySample createActivitySample() {
return new VivomoveHrActivitySample();
}
@Override
public AbstractDao<VivomoveHrActivitySample, ?> getSampleDao() {
return getSession().getVivomoveHrActivitySampleDao();
}
@Nullable
@Override
protected Property getRawKindSampleProperty() {
return VivomoveHrActivitySampleDao.Properties.RawKind;
}
@NonNull
@Override
protected Property getTimestampSampleProperty() {
return VivomoveHrActivitySampleDao.Properties.Timestamp;
}
@NonNull
@Override
protected Property getDeviceIdentifierSampleProperty() {
return VivomoveHrActivitySampleDao.Properties.DeviceId;
}
public static String rawKindToString(int rawType) {
if (rawType == RAW_NOT_WORN) {
return "not worn";
}
switch (rawType & RAW_TYPE_KIND_MASK) {
case RAW_TYPE_KIND_ACTIVITY:
return activityTypeToString(rawType & ~RAW_TYPE_KIND_MASK);
case RAW_TYPE_KIND_SLEEP:
return sleepTypeToString(rawType & ~RAW_TYPE_KIND_MASK);
default:
// ???
return "unknown " + rawType;
}
}
private static String activityTypeToString(int rawType) {
switch (rawType) {
case 0:
return "generic";
case 1:
return "running";
case 2:
return "cycling";
case 3:
return "transition";
case 4:
return "fitness equipment";
case 5:
return "swimming";
case 6:
return "walking";
case 8:
return "sedentary";
default:
return "unknown activity " + rawType;
}
}
private static String sleepTypeToString(int rawType) {
switch (rawType) {
case 0:
return "deep sleep";
case 1:
return "light sleep";
case 2:
// awake
return "awake";
case 3:
// more_awake
return "more awake";
default:
return "unknown sleep " + rawType;
}
}
}

View File

@ -106,6 +106,7 @@ public enum DeviceType {
FITPRO(230, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_fitpro),
ITAG(250, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_itag),
NUTMINI(251, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_nut_mini),
VIVOMOVE_HR(260, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_vivomove_hr),
VIBRATISSIMO(300, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo),
SONY_SWR12(310, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_sonyswr12),
LIVEVIEW(320, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview),

View File

@ -27,6 +27,7 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.widget.Toast;
import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveHrCoordinator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -373,6 +374,7 @@ public class DeviceHelper {
result.add(new SuperCarsCoordinator());
result.add(new AsteroidOSDeviceCoordinator());
result.add(new SoFlowCoordinator());
result.add(new VivomoveHrCoordinator());
return result;
}

View File

@ -1269,6 +1269,7 @@
<string name="devicetype_amazfit_gts2e">Amazfit GTS 2e</string>
<string name="devicetype_amazfit_x">Amazfit X</string>
<string name="devicetype_zepp_e">Zepp E</string>
<string name="devicetype_vivomove_hr">Garmin Vivomove HR</string>
<string name="devicetype_vibratissimo">Vibratissimo</string>
<string name="devicetype_um25">UM-25</string>
<string name="devicetype_liveview">LiveView</string>