1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-03 17:02:13 +01:00

Zepp OS: Refactor config, fix health on GTR 3 and GTS 3

The config refactor in addf7ff6a broke health settings on GTR3 and GTS3

- GTS 3 and GTR 3 health configs use protocol v1. The only difference
  seems to be that the steps goal is a SHORT instead of an INT.
- It needs a refactoring from the ground up to better handle different
  versions, but this is enough to get the GTR 3 and GTS 3 working.
This commit is contained in:
José Rebelo 2022-10-29 12:03:25 +01:00
parent 6cc3579e9c
commit cd59511aad
10 changed files with 333 additions and 282 deletions

View File

@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiLanguageType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType;
@ -318,14 +318,14 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
}
public boolean hasGps(final GBDevice device) {
return supportsConfig(device, Huami2021Config.ConfigArg.WORKOUT_GPS_PRESET);
return supportsConfig(device, ZeppOsConfigService.ConfigArg.WORKOUT_GPS_PRESET);
}
public boolean supportsAutoBrightness(final GBDevice device) {
return supportsConfig(device, Huami2021Config.ConfigArg.SCREEN_AUTO_BRIGHTNESS);
return supportsConfig(device, ZeppOsConfigService.ConfigArg.SCREEN_AUTO_BRIGHTNESS);
}
private boolean supportsConfig(final GBDevice device, final Huami2021Config.ConfigArg config) {
return Huami2021Config.deviceHasConfig(getPrefs(device), config);
private boolean supportsConfig(final GBDevice device, final ZeppOsConfigService.ConfigArg config) {
return ZeppOsConfigService.deviceHasConfig(getPrefs(device), config);
}
}

View File

@ -22,7 +22,6 @@ public class Huami2021Service {
*/
public static final short CHUNKED2021_ENDPOINT_HTTP = 0x0001;
public static final short CHUNKED2021_ENDPOINT_CALENDAR = 0x0007;
public static final short CHUNKED2021_ENDPOINT_CONFIG = 0x000a;
public static final short CHUNKED2021_ENDPOINT_WEATHER = 0x000e;
public static final short CHUNKED2021_ENDPOINT_ALARMS = 0x000f;
public static final short CHUNKED2021_ENDPOINT_CANNED_MESSAGES = 0x0013;
@ -221,16 +220,6 @@ public class Huami2021Service {
public static final byte MUSIC_BUTTON_VOLUME_UP = 0x05;
public static final byte MUSIC_BUTTON_VOLUME_DOWN = 0x06;
/**
* Config, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_CONFIG}.
*/
public static final byte CONFIG_CMD_CAPABILITIES_REQUEST = 0x01;
public static final byte CONFIG_CMD_CAPABILITIES_RESPONSE = 0x02;
public static final byte CONFIG_CMD_REQUEST = 0x03;
public static final byte CONFIG_CMD_RESPONSE = 0x04;
public static final byte CONFIG_CMD_SET = 0x05;
public static final byte CONFIG_CMD_ACK = 0x06;
/**
* Reminders, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_REMINDERS}.
*/

View File

@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -57,11 +57,11 @@ public class Huami2021SettingsCustomizer extends HuamiSettingsCustomizer {
removeUnsupportedElementsFromListPreference(HuamiConst.PREF_SHORTCUTS_SORTABLE, handler, prefs);
removeUnsupportedElementsFromListPreference(HuamiConst.PREF_CONTROL_CENTER_SORTABLE, handler, prefs);
for (final Huami2021Config.ConfigArg config : Huami2021Config.ConfigArg.values()) {
for (final ZeppOsConfigService.ConfigArg config : ZeppOsConfigService.ConfigArg.values()) {
if (config.getPrefKey() == null) {
continue;
}
switch (config.getConfigType()) {
switch (config.getConfigType(null)) {
case BYTE:
case BYTE_LIST:
case STRING_LIST:
@ -79,96 +79,96 @@ public class Huami2021SettingsCustomizer extends HuamiSettingsCustomizer {
}
// Hide all config groups that may not be mapped directly to a preference
final Map<String, List<Huami2021Config.ConfigArg>> configScreens = new HashMap<String, List<Huami2021Config.ConfigArg>>() {{
final Map<String, List<ZeppOsConfigService.ConfigArg>> configScreens = new HashMap<String, List<ZeppOsConfigService.ConfigArg>>() {{
put(DeviceSettingsPreferenceConst.PREF_SCREEN_NIGHT_MODE, Arrays.asList(
Huami2021Config.ConfigArg.NIGHT_MODE_MODE,
Huami2021Config.ConfigArg.NIGHT_MODE_SCHEDULED_START,
Huami2021Config.ConfigArg.NIGHT_MODE_SCHEDULED_END
ZeppOsConfigService.ConfigArg.NIGHT_MODE_MODE,
ZeppOsConfigService.ConfigArg.NIGHT_MODE_SCHEDULED_START,
ZeppOsConfigService.ConfigArg.NIGHT_MODE_SCHEDULED_END
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_SLEEP_MODE, Arrays.asList(
Huami2021Config.ConfigArg.SLEEP_MODE_SLEEP_SCREEN,
Huami2021Config.ConfigArg.SLEEP_MODE_SMART_ENABLE
ZeppOsConfigService.ConfigArg.SLEEP_MODE_SLEEP_SCREEN,
ZeppOsConfigService.ConfigArg.SLEEP_MODE_SMART_ENABLE
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_LIFT_WRIST, Arrays.asList(
Huami2021Config.ConfigArg.LIFT_WRIST_MODE,
Huami2021Config.ConfigArg.LIFT_WRIST_SCHEDULED_START,
Huami2021Config.ConfigArg.LIFT_WRIST_SCHEDULED_END,
Huami2021Config.ConfigArg.LIFT_WRIST_RESPONSE_SENSITIVITY
ZeppOsConfigService.ConfigArg.LIFT_WRIST_MODE,
ZeppOsConfigService.ConfigArg.LIFT_WRIST_SCHEDULED_START,
ZeppOsConfigService.ConfigArg.LIFT_WRIST_SCHEDULED_END,
ZeppOsConfigService.ConfigArg.LIFT_WRIST_RESPONSE_SENSITIVITY
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_PASSWORD, Arrays.asList(
Huami2021Config.ConfigArg.PASSWORD_ENABLED,
Huami2021Config.ConfigArg.PASSWORD_TEXT
ZeppOsConfigService.ConfigArg.PASSWORD_ENABLED,
ZeppOsConfigService.ConfigArg.PASSWORD_TEXT
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_ALWAYS_ON_DISPLAY, Arrays.asList(
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_MODE,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_START,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_END,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_STYLE
ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_MODE,
ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_START,
ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_END,
ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE,
ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_STYLE
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_AUTO_BRIGHTNESS, Arrays.asList(
Huami2021Config.ConfigArg.SCREEN_AUTO_BRIGHTNESS
ZeppOsConfigService.ConfigArg.SCREEN_AUTO_BRIGHTNESS
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_HEARTRATE_MONITORING, Arrays.asList(
Huami2021Config.ConfigArg.HEART_RATE_ALL_DAY_MONITORING,
Huami2021Config.ConfigArg.HEART_RATE_HIGH_ALERTS,
Huami2021Config.ConfigArg.HEART_RATE_LOW_ALERTS,
Huami2021Config.ConfigArg.HEART_RATE_ACTIVITY_MONITORING,
Huami2021Config.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING,
Huami2021Config.ConfigArg.SLEEP_BREATHING_QUALITY_MONITORING,
Huami2021Config.ConfigArg.STRESS_MONITORING,
Huami2021Config.ConfigArg.STRESS_RELAXATION_REMINDER,
Huami2021Config.ConfigArg.SPO2_ALL_DAY_MONITORING,
Huami2021Config.ConfigArg.SPO2_LOW_ALERT
ZeppOsConfigService.ConfigArg.HEART_RATE_ALL_DAY_MONITORING,
ZeppOsConfigService.ConfigArg.HEART_RATE_HIGH_ALERTS,
ZeppOsConfigService.ConfigArg.HEART_RATE_LOW_ALERTS,
ZeppOsConfigService.ConfigArg.HEART_RATE_ACTIVITY_MONITORING,
ZeppOsConfigService.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING,
ZeppOsConfigService.ConfigArg.SLEEP_BREATHING_QUALITY_MONITORING,
ZeppOsConfigService.ConfigArg.STRESS_MONITORING,
ZeppOsConfigService.ConfigArg.STRESS_RELAXATION_REMINDER,
ZeppOsConfigService.ConfigArg.SPO2_ALL_DAY_MONITORING,
ZeppOsConfigService.ConfigArg.SPO2_LOW_ALERT
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_INACTIVITY_EXTENDED, Arrays.asList(
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_ENABLED,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_START,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_END,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_ENABLED,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_START,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_END
ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_ENABLED,
ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_START,
ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_END,
ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_DND_ENABLED,
ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_START,
ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_END
));
put(DeviceSettingsPreferenceConst.PREF_HEADER_GPS, Arrays.asList(
Huami2021Config.ConfigArg.WORKOUT_GPS_PRESET,
Huami2021Config.ConfigArg.WORKOUT_GPS_BAND,
Huami2021Config.ConfigArg.WORKOUT_GPS_COMBINATION,
Huami2021Config.ConfigArg.WORKOUT_GPS_SATELLITE_SEARCH,
Huami2021Config.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_ENABLED,
Huami2021Config.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_TIME
ZeppOsConfigService.ConfigArg.WORKOUT_GPS_PRESET,
ZeppOsConfigService.ConfigArg.WORKOUT_GPS_BAND,
ZeppOsConfigService.ConfigArg.WORKOUT_GPS_COMBINATION,
ZeppOsConfigService.ConfigArg.WORKOUT_GPS_SATELLITE_SEARCH,
ZeppOsConfigService.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_ENABLED,
ZeppOsConfigService.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_TIME
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_SOUND_AND_VIBRATION, Arrays.asList(
Huami2021Config.ConfigArg.VOLUME,
Huami2021Config.ConfigArg.CROWN_VIBRATION,
Huami2021Config.ConfigArg.ALERT_TONE,
Huami2021Config.ConfigArg.COVER_TO_MUTE,
Huami2021Config.ConfigArg.VIBRATE_FOR_ALERT,
Huami2021Config.ConfigArg.TEXT_TO_SPEECH
ZeppOsConfigService.ConfigArg.VOLUME,
ZeppOsConfigService.ConfigArg.CROWN_VIBRATION,
ZeppOsConfigService.ConfigArg.ALERT_TONE,
ZeppOsConfigService.ConfigArg.COVER_TO_MUTE,
ZeppOsConfigService.ConfigArg.VIBRATE_FOR_ALERT,
ZeppOsConfigService.ConfigArg.TEXT_TO_SPEECH
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_DO_NOT_DISTURB, Arrays.asList(
Huami2021Config.ConfigArg.DND_MODE,
Huami2021Config.ConfigArg.DND_SCHEDULED_START,
Huami2021Config.ConfigArg.DND_SCHEDULED_END
ZeppOsConfigService.ConfigArg.DND_MODE,
ZeppOsConfigService.ConfigArg.DND_SCHEDULED_START,
ZeppOsConfigService.ConfigArg.DND_SCHEDULED_END
));
put(DeviceSettingsPreferenceConst.PREF_HEADER_WORKOUT_DETECTION, Arrays.asList(
Huami2021Config.ConfigArg.WORKOUT_DETECTION_CATEGORY,
Huami2021Config.ConfigArg.WORKOUT_DETECTION_ALERT,
Huami2021Config.ConfigArg.WORKOUT_DETECTION_SENSITIVITY
ZeppOsConfigService.ConfigArg.WORKOUT_DETECTION_CATEGORY,
ZeppOsConfigService.ConfigArg.WORKOUT_DETECTION_ALERT,
ZeppOsConfigService.ConfigArg.WORKOUT_DETECTION_SENSITIVITY
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_OFFLINE_VOICE, Arrays.asList(
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPOND_TURN_WRIST,
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPOND_SCREEN_ON,
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING,
Huami2021Config.ConfigArg.OFFLINE_VOICE_LANGUAGE
ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_RESPOND_TURN_WRIST,
ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_RESPOND_SCREEN_ON,
ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING,
ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_LANGUAGE
));
}};
for (final Map.Entry<String, List<Huami2021Config.ConfigArg>> configScreen : configScreens.entrySet()) {
for (final Map.Entry<String, List<ZeppOsConfigService.ConfigArg>> configScreen : configScreens.entrySet()) {
hidePrefIfNoConfigSupported(
handler,
prefs,
configScreen.getKey(),
configScreen.getValue().toArray(new Huami2021Config.ConfigArg[0])
configScreen.getValue().toArray(new ZeppOsConfigService.ConfigArg[0])
);
}
@ -303,7 +303,7 @@ public class Huami2021SettingsCustomizer extends HuamiSettingsCustomizer {
}
// Get the list of possible values for this preference, as reported by the band
final List<String> possibleValues = prefs.getList(Huami2021Config.getPrefPossibleValuesKey(prefKey), null);
final List<String> possibleValues = prefs.getList(ZeppOsConfigService.getPrefPossibleValuesKey(prefKey), null);
if (possibleValues == null || possibleValues.isEmpty()) {
// The band hasn't reported this setting, so we don't know the possible values.
// Hide it
@ -360,14 +360,14 @@ public class Huami2021SettingsCustomizer extends HuamiSettingsCustomizer {
private void hidePrefIfNoConfigSupported(final DeviceSpecificSettingsHandler handler,
final Prefs prefs,
final String prefToHide,
final Huami2021Config.ConfigArg... configs) {
final ZeppOsConfigService.ConfigArg... configs) {
final Preference pref = handler.findPreference(prefToHide);
if (pref == null) {
return;
}
for (final Huami2021Config.ConfigArg config : configs) {
if (Huami2021Config.deviceHasConfig(prefs, config)) {
for (final ZeppOsConfigService.ConfigArg config : configs) {
if (ZeppOsConfigService.deviceHasConfig(prefs, config)) {
// This preference is supported, don't hide
return;
}

View File

@ -17,5 +17,5 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
public interface Huami2021Handler {
void handle2021Payload(int type, byte[] payload);
void handle2021Payload(short type, byte[] payload);
}

View File

@ -17,32 +17,27 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
import static org.apache.commons.lang3.ArrayUtils.subarray;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LANGUAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LANGUAGE_AUTO;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT_AUTO;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Service.*;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.SUCCESS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_NAME;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint16;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint8;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.mapTimeZone;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.FITNESS_GOAL_CALORIES;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.FITNESS_GOAL_FAT_BURN_TIME;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.FITNESS_GOAL_SLEEP;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.FITNESS_GOAL_STANDING_TIME;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.FITNESS_GOAL_STEPS;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.FITNESS_GOAL_WEIGHT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.HEART_RATE_ALL_DAY_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.LANGUAGE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.LANGUAGE_FOLLOW_PHONE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.PASSWORD_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.PASSWORD_TEXT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.TEMPERATURE_UNIT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.TIME_FORMAT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigGroup;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigSetter;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.FITNESS_GOAL_CALORIES;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.FITNESS_GOAL_FAT_BURN_TIME;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.FITNESS_GOAL_SLEEP;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.FITNESS_GOAL_STANDING_TIME;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.FITNESS_GOAL_STEPS;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.FITNESS_GOAL_WEIGHT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.HEART_RATE_ALL_DAY_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.LANGUAGE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.LANGUAGE_FOLLOW_PHONE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.PASSWORD_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.PASSWORD_TEXT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.TEMPERATURE_UNIT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.TIME_FORMAT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigGroup;
import android.Manifest;
import android.content.Intent;
@ -120,6 +115,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.Fet
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.HuamiFetchDebugLogsOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation2021;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsFileUploadService;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
@ -144,6 +141,11 @@ public abstract class Huami2021Support extends HuamiSupport {
// Services
private final ZeppOsFileUploadService fileUploadService = new ZeppOsFileUploadService(this);
private final ZeppOsConfigService configService = new ZeppOsConfigService(this);
private final Map<Short, AbstractZeppOsService> mServiceMap = new HashMap<Short, AbstractZeppOsService>() {{
put(fileUploadService.getEndpoint(), fileUploadService);
put(configService.getEndpoint(), configService);
}};
public Huami2021Support() {
this(LOG);
@ -174,15 +176,15 @@ public abstract class Huami2021Support extends HuamiSupport {
@Override
public void onSendConfiguration(final String config) {
final ConfigSetter configSetter = new ConfigSetter();
final ZeppOsConfigService.ConfigSetter configSetter = configService.newSetter();
final Prefs prefs = getDevicePrefs();
try {
if (Huami2021Config.setConfig(prefs, config, configSetter)) {
if (configService.setConfig(prefs, config, configSetter)) {
// If the ConfigSetter was able to set the config, just write it and return
final TransactionBuilder builder;
builder = performInitialized("Sending configuration for option: " + config);
configSetter.write(this, builder);
configSetter.write(builder);
builder.queue(getQueue());
return;
@ -453,14 +455,14 @@ public abstract class Huami2021Support extends HuamiSupport {
final int goalFatBurnTime = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_GOAL_FAT_BURN_TIME_MINUTES, ActivityUser.defaultUserFatBurnTimeMinutes);
LOG.info("Setting Fitness Goals to steps={}, calories={}, sleep={}, weight={}, standingTime={}, fatBurn={}", goalSteps, goalCalories, goalSleep, goalWeight, goalStandingTime, goalFatBurnTime);
new ConfigSetter()
configService.newSetter()
.setInt(FITNESS_GOAL_STEPS, goalSteps)
.setShort(FITNESS_GOAL_CALORIES, (short) goalCalories)
.setShort(FITNESS_GOAL_SLEEP, (short) (goalSleep * 60))
.setShort(FITNESS_GOAL_WEIGHT, (short) goalWeight)
.setShort(FITNESS_GOAL_STANDING_TIME, (short) (goalStandingTime))
.setShort(FITNESS_GOAL_FAT_BURN_TIME, (short) goalFatBurnTime)
.write(this, builder);
.write(builder);
return this;
}
@ -529,10 +531,10 @@ public abstract class Huami2021Support extends HuamiSupport {
return this;
}
new ConfigSetter()
configService.newSetter()
.setBoolean(PASSWORD_ENABLED, passwordEnabled)
.setString(PASSWORD_TEXT, password)
.write(this, builder);
.write(builder);
return this;
}
@ -897,9 +899,9 @@ public abstract class Huami2021Support extends HuamiSupport {
protected Huami2021Support setHeartrateSleepSupport(final TransactionBuilder builder) {
final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(gbDevice.getAddress());
new ConfigSetter()
configService.newSetter()
.setBoolean(SLEEP_HIGH_ACCURACY_MONITORING, enableHrSleepSupport)
.write(this, builder);
.write(builder);
return this;
}
@ -955,10 +957,10 @@ public abstract class Huami2021Support extends HuamiSupport {
}
@Override
protected HuamiSupport setHeartrateMeasurementInterval(TransactionBuilder builder, int minutes) {
new ConfigSetter()
protected HuamiSupport setHeartrateMeasurementInterval(final TransactionBuilder builder, final int minutes) {
configService.newSetter()
.setByte(HEART_RATE_ALL_DAY_MONITORING, (byte) minutes)
.write(this, builder);
.write(builder);
return this;
}
@ -1043,9 +1045,9 @@ public abstract class Huami2021Support extends HuamiSupport {
timeFormatByte = 0x00;
}
new ConfigSetter()
configService.newSetter()
.setByte(TIME_FORMAT, timeFormatByte)
.write(this, builder);
.write(builder);
return this;
}
@ -1057,7 +1059,7 @@ public abstract class Huami2021Support extends HuamiSupport {
setDisplayItems2021(
builder,
DISPLAY_ITEMS_MENU,
new ArrayList<>(prefs.getList(Huami2021Config.getPrefPossibleValuesKey(HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE), Collections.emptyList())),
new ArrayList<>(prefs.getList(ZeppOsConfigService.getPrefPossibleValuesKey(HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE), Collections.emptyList())),
new ArrayList<>(prefs.getList(HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE, Collections.emptyList()))
);
return this;
@ -1070,7 +1072,7 @@ public abstract class Huami2021Support extends HuamiSupport {
setDisplayItems2021(
builder,
DISPLAY_ITEMS_SHORTCUTS,
new ArrayList<>(prefs.getList(Huami2021Config.getPrefPossibleValuesKey(HuamiConst.PREF_SHORTCUTS_SORTABLE), Collections.emptyList())),
new ArrayList<>(prefs.getList(ZeppOsConfigService.getPrefPossibleValuesKey(HuamiConst.PREF_SHORTCUTS_SORTABLE), Collections.emptyList())),
new ArrayList<>(prefs.getList(HuamiConst.PREF_SHORTCUTS_SORTABLE, Collections.emptyList()))
);
return this;
@ -1082,7 +1084,7 @@ public abstract class Huami2021Support extends HuamiSupport {
setDisplayItems2021(
builder,
DISPLAY_ITEMS_CONTROL_CENTER,
new ArrayList<>(prefs.getList(Huami2021Config.getPrefPossibleValuesKey(HuamiConst.PREF_CONTROL_CENTER_SORTABLE), Collections.emptyList())),
new ArrayList<>(prefs.getList(ZeppOsConfigService.getPrefPossibleValuesKey(HuamiConst.PREF_CONTROL_CENTER_SORTABLE), Collections.emptyList())),
new ArrayList<>(prefs.getList(HuamiConst.PREF_CONTROL_CENTER_SORTABLE, Collections.emptyList()))
);
return this;
@ -1226,9 +1228,9 @@ public abstract class Huami2021Support extends HuamiSupport {
break;
}
new ConfigSetter()
configService.newSetter()
.setByte(TEMPERATURE_UNIT, unitByte)
.write(this, builder);
.write(builder);
return this;
}
@ -1240,10 +1242,10 @@ public abstract class Huami2021Support extends HuamiSupport {
LOG.info("Setting device language to {}", localeString);
new ConfigSetter()
configService.newSetter()
.setByte(LANGUAGE, getLanguageId())
.setBoolean(LANGUAGE_FOLLOW_PHONE, localeString.equals("auto"))
.write(this, builder);
.write(builder);
return this;
}
@ -1361,12 +1363,8 @@ public abstract class Huami2021Support extends HuamiSupport {
LOG.info("2021 phase3Initialize...");
setUserInfo(builder);
setFitnessGoal(builder);
for (final ConfigGroup configGroup : ConfigGroup.values()) {
requestConfig(builder, configGroup);
}
configService.requestAllConfigs(builder);
requestCapabilityReminders(builder);
fileUploadService.requestCapability(builder);
@ -1416,7 +1414,7 @@ public abstract class Huami2021Support extends HuamiSupport {
}
@Override
public void handle2021Payload(final int type, final byte[] payload) {
public void handle2021Payload(final short type, final byte[] payload) {
if (payload == null || payload.length == 0) {
LOG.warn("Empty or null payload for {}", String.format("0x%04x", type));
return;
@ -1424,6 +1422,11 @@ public abstract class Huami2021Support extends HuamiSupport {
LOG.debug("Got 2021 payload for {}: {}", String.format("0x%04x", type), GB.hexdump(payload));
if (mServiceMap.containsKey(type)) {
mServiceMap.get(type).handlePayload(payload);
return;
}
switch (type) {
case CHUNKED2021_ENDPOINT_ALARMS:
handle2021Alarms(payload);
@ -1437,12 +1440,6 @@ public abstract class Huami2021Support extends HuamiSupport {
case CHUNKED2021_ENDPOINT_COMPAT:
LOG.warn("Unexpected compat payload {}", GB.hexdump(payload));
return;
case CHUNKED2021_ENDPOINT_CONFIG:
handle2021Config(payload);
return;
case ZeppOsFileUploadService.ENDPOINT:
fileUploadService.handlePayload(payload);
return;
case CHUNKED2021_ENDPOINT_WEATHER:
handle2021Weather(payload);
return;
@ -1660,102 +1657,6 @@ public abstract class Huami2021Support extends HuamiSupport {
// TODO update database?
}
private void requestConfig(final TransactionBuilder builder,
final ConfigGroup config,
final boolean includeConstraints,
final List<Huami2021Config.ConfigArg> args) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(CONFIG_CMD_REQUEST);
baos.write(bool(includeConstraints));
baos.write(config.getValue());
baos.write(args.size());
for (final Huami2021Config.ConfigArg arg : args) {
baos.write(arg.getCode());
}
writeToChunked2021(builder, CHUNKED2021_ENDPOINT_CONFIG, baos.toByteArray(), true);
}
private void requestConfig(final TransactionBuilder builder, final ConfigGroup config) {
requestConfig(builder, config, true, Huami2021Config.ConfigArg.getAllArgsForConfigGroup(config));
}
protected void handle2021Config(final byte[] payload) {
switch (payload[0]) {
case CONFIG_CMD_ACK:
LOG.info("Configuration ACK, status = {}", payload[1]);
return;
case CONFIG_CMD_RESPONSE:
if (payload[1] != 1) {
LOG.warn("Configuration response not success: {}", payload[1]);
return;
}
handle2021ConfigResponse(payload);
return;
default:
LOG.warn("Unexpected configuration payload byte {}", String.format("0x%02x", payload[0]));
}
}
private void handle2021ConfigResponse(final byte[] payload) {
final ConfigGroup configGroup = ConfigGroup.fromValue(payload[2]);
if (configGroup == null) {
LOG.warn("Unknown config type {}", String.format("0x%02x", payload[2]));
return;
}
if (configGroup.getVersion() != payload[3]) {
LOG.warn("Unexpected next byte {} for {}", String.format("0x%02x", payload[3]), configGroup);
return;
}
final boolean includesConstraints = payload[4] == 0x01;
int numConfigs = payload[5] & 0xff;
LOG.info("Got {} configs for {}", numConfigs, configGroup);
final Map<String, Object> prefs = new Huami2021Config.ConfigParser(configGroup, includesConstraints)
.parse(numConfigs, subarray(payload, 6, payload.length));
if (prefs == null) {
return;
}
final GBDeviceEventUpdatePreferences eventUpdatePreferences = new GBDeviceEventUpdatePreferences(prefs);
evaluateGBDeviceEvent(eventUpdatePreferences);
if (isInitialized()) {
final TransactionBuilder builder;
boolean hasAutoConfigsToSend = false;
try {
builder = performInitialized("set auto band configs");
} catch (final Exception e) {
LOG.error("Failed to set auto band configs", e);
return;
}
if (prefs.containsKey(PREF_LANGUAGE) && prefs.get(PREF_LANGUAGE).equals(PREF_LANGUAGE_AUTO)) {
// Band is reporting automatic language, we need to send the actual language
setLanguage(builder);
hasAutoConfigsToSend = true;
}
if (prefs.containsKey(PREF_TIMEFORMAT) && prefs.get(PREF_TIMEFORMAT).equals(PREF_TIMEFORMAT_AUTO)) {
// Band is reporting automatic time format, we need to send the actual time format
setTimeFormat(builder);
hasAutoConfigsToSend = true;
}
if (hasAutoConfigsToSend) {
builder.queue(getQueue());
}
}
}
protected void handle2021Workout(final byte[] payload) {
switch (payload[0]) {
case WORKOUT_CMD_APP_OPEN:
@ -1838,7 +1739,7 @@ public abstract class Huami2021Support extends HuamiSupport {
LOG.error("Unknown display items type {}", String.format("0x%x", payload[1]));
return;
}
final String allScreensPrefKey = Huami2021Config.getPrefPossibleValuesKey(prefKey);
final String allScreensPrefKey = ZeppOsConfigService.getPrefPossibleValuesKey(prefKey);
final boolean menuHasMoreSection;

View File

@ -4111,7 +4111,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
@Override
public void handle2021Payload(int type, byte[] payload) {
public void handle2021Payload(short type, byte[] payload) {
if (type == Huami2021Service.CHUNKED2021_ENDPOINT_COMPAT) {
LOG.info("got configuration data");
type = 0;

View File

@ -132,7 +132,7 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler
}
@Override
public void handle2021Payload(final int type, final byte[] payload) {
public void handle2021Payload(final short type, final byte[] payload) {
if (type != Huami2021Service.CHUNKED2021_ENDPOINT_AUTH) {
this.huamiSupport.handle2021Payload(type, payload);
return;

View File

@ -30,6 +30,10 @@ public abstract class AbstractZeppOsService {
public abstract boolean isEncrypted();
public abstract void handlePayload(final byte[] payload);
protected Huami2021Support getSupport() {
return mSupport;
}
protected void write(final String taskName, final byte[] data) {
this.mSupport.writeToChunked2021(taskName, getEndpoint(), data, isEncrypted());
}

View File

@ -14,13 +14,12 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services;
import static org.apache.commons.lang3.ArrayUtils.subarray;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
import static nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl.PREF_PASSWORD;
import static nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl.PREF_PASSWORD_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Service.CHUNKED2021_ENDPOINT_CONFIG;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Service.CONFIG_CMD_SET;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_EXPOSE_HR_THIRDPARTY;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE_END;
@ -59,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability;
import nodomain.freeyourgadget.gadgetbridge.capabilities.WorkoutDetectionCapability;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLift;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLiftSensitivity;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.AlwaysOnDisplay;
@ -66,13 +66,143 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.DoNotDisturb;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021MenuType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiLanguageType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.MapUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class Huami2021Config {
private static final Logger LOG = LoggerFactory.getLogger(Huami2021Config.class);
public class ZeppOsConfigService extends AbstractZeppOsService {
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsConfigService.class);
private static final short ENDPOINT = 0x000a;
private static final byte CMD_CAPABILITIES_REQUEST = 0x01;
private static final byte CMD_CAPABILITIES_RESPONSE = 0x02;
private static final byte CMD_REQUEST = 0x03;
private static final byte CMD_RESPONSE = 0x04;
private static final byte CMD_SET = 0x05;
private static final byte CMD_ACK = 0x06;
private final Map<ConfigGroup, Byte> mGroupVersions = new HashMap<>();
public ZeppOsConfigService(final Huami2021Support support) {
super(support);
}
@Override
public short getEndpoint() {
return ENDPOINT;
}
@Override
public boolean isEncrypted() {
return true;
}
@Override
public void handlePayload(final byte[] payload) {
switch (payload[0]) {
case CMD_ACK:
LOG.info("Configuration ACK, status = {}", payload[1]);
return;
case CMD_RESPONSE:
if (payload[1] != 1) {
LOG.warn("Configuration response not success: {}", payload[1]);
return;
}
handle2021ConfigResponse(payload);
return;
default:
LOG.warn("Unexpected configuration payload byte {}", String.format("0x%02x", payload[0]));
}
}
private boolean sentFitnessGoal = false;
private void handle2021ConfigResponse(final byte[] payload) {
final ConfigGroup configGroup = ConfigGroup.fromValue(payload[2]);
if (configGroup == null) {
LOG.warn("Unknown config type {}", String.format("0x%02x", payload[2]));
return;
}
final byte version = payload[3];
if (configGroup.getVersion() != version) {
// Special case for HEALTH, where we actually support version 1 as well
// TODO: Support multiple versions in a cleaner way...
if (!(configGroup == ConfigGroup.HEALTH && configGroup.getVersion() == 1)) {
LOG.warn("Unexpected version {} for {}", String.format("0x%02x", version), configGroup);
return;
}
}
mGroupVersions.put(configGroup, version);
final boolean includesConstraints = payload[4] == 0x01;
int numConfigs = payload[5] & 0xff;
LOG.info("Got {} configs for {} version {}", numConfigs, configGroup, version);
final Map<String, Object> prefs = new ZeppOsConfigService.ConfigParser(configGroup, includesConstraints)
.parse(numConfigs, subarray(payload, 6, payload.length));
if (prefs == null) {
return;
}
final GBDeviceEventUpdatePreferences eventUpdatePreferences = new GBDeviceEventUpdatePreferences(prefs);
getSupport().evaluateGBDeviceEvent(eventUpdatePreferences);
if (getSupport().getDevice().isInitialized()) {
if (prefs.containsKey(PREF_LANGUAGE) && prefs.get(PREF_LANGUAGE).equals(PREF_LANGUAGE_AUTO)) {
// Band is reporting automatic language, we need to send the actual language
getSupport().onSendConfiguration(PREF_LANGUAGE);
}
if (prefs.containsKey(PREF_TIMEFORMAT) && prefs.get(PREF_TIMEFORMAT).equals(PREF_TIMEFORMAT_AUTO)) {
// Band is reporting automatic time format, we need to send the actual time format
getSupport().onSendConfiguration(PREF_TIMEFORMAT);
}
}
if (configGroup == ConfigGroup.HEALTH && !sentFitnessGoal) {
// We need to send the fitness goal after we got the protocol version
getSupport().onSendConfiguration(PREF_USER_FITNESS_GOAL);
sentFitnessGoal = true;
}
}
public void requestAllConfigs(final TransactionBuilder builder) {
for (final ConfigGroup configGroup : ConfigGroup.values()) {
requestConfig(builder, configGroup);
}
}
public void requestConfig(final TransactionBuilder builder, final ConfigGroup config) {
requestConfig(builder, config, true, ZeppOsConfigService.ConfigArg.getAllArgsForConfigGroup(config));
}
public void requestConfig(final TransactionBuilder builder,
final ConfigGroup config,
final boolean includeConstraints,
final List<ZeppOsConfigService.ConfigArg> args) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(CMD_REQUEST);
baos.write((byte) (includeConstraints ? 1 : 0));
baos.write(config.getValue());
baos.write(args.size());
for (final ZeppOsConfigService.ConfigArg arg : args) {
baos.write(arg.getCode());
}
write(builder, baos.toByteArray());
}
public enum ConfigGroup {
DISPLAY(0x01, 0x02),
@ -201,7 +331,7 @@ public class Huami2021Config {
SPO2_ALL_DAY_MONITORING(ConfigGroup.HEALTH, ConfigType.BOOL, 0x31, PREF_SPO2_ALL_DAY_MONITORING),
SPO2_LOW_ALERT(ConfigGroup.HEALTH, ConfigType.BYTE, 0x32, PREF_SPO2_LOW_ALERT_THRESHOLD),
FITNESS_GOAL_NOTIFICATION(ConfigGroup.HEALTH, ConfigType.BOOL, 0x51, PREF_USER_FITNESS_GOAL_NOTIFICATION),
FITNESS_GOAL_STEPS(ConfigGroup.HEALTH, ConfigType.INT, 0x52, null), // TODO needs to be handled globally
FITNESS_GOAL_STEPS(ConfigGroup.HEALTH, null /* Special case, handled below */, 0x52, null), // TODO needs to be handled globally
FITNESS_GOAL_CALORIES(ConfigGroup.HEALTH, ConfigType.SHORT, 0x53, null), // TODO needs to be handled globally
FITNESS_GOAL_WEIGHT(ConfigGroup.HEALTH, ConfigType.SHORT, 0x54, null), // TODO needs to be handled globally
FITNESS_GOAL_SLEEP(ConfigGroup.HEALTH, ConfigType.SHORT, 0x55, null), // TODO needs to be handled globally
@ -263,7 +393,27 @@ public class Huami2021Config {
return configGroup;
}
public ConfigType getConfigType() {
public ConfigType getConfigType(@Nullable final Map<ConfigGroup, Byte> groupVersions) {
if (this == FITNESS_GOAL_STEPS) {
if (groupVersions == null) {
return ConfigType.INT;
}
final Byte groupVersion = groupVersions.get(getConfigGroup());
if (groupVersion == null) {
LOG.error("Version for {} is not known", getConfigGroup());
return null;
}
switch (groupVersion) {
case 0x01:
return ConfigType.SHORT;
case 0x02:
default:
return ConfigType.INT;
}
}
return configType;
}
@ -276,7 +426,7 @@ public class Huami2021Config {
}
public static ConfigArg fromCode(final ConfigGroup configGroup, final byte code) {
for (final Huami2021Config.ConfigArg arg : values()) {
for (final ZeppOsConfigService.ConfigArg arg : values()) {
if (arg.getConfigGroup().equals(configGroup) && arg.getCode() == code) {
return arg;
}
@ -285,8 +435,8 @@ public class Huami2021Config {
}
public static List<ConfigArg> getAllArgsForConfigGroup(final ConfigGroup configGroup) {
final List<Huami2021Config.ConfigArg> configArgs = new ArrayList<>();
for (final Huami2021Config.ConfigArg arg : values()) {
final List<ZeppOsConfigService.ConfigArg> configArgs = new ArrayList<>();
for (final ZeppOsConfigService.ConfigArg arg : values()) {
if (arg.getConfigGroup().equals(configGroup)) {
configArgs.add(arg);
}
@ -317,14 +467,14 @@ public class Huami2021Config {
*
* @return true if the {@link ConfigSetter} was updated for this preference key
*/
public static boolean setConfig(final Prefs prefs, final String key, final ConfigSetter setter) {
public boolean setConfig(final Prefs prefs, final String key, final ConfigSetter setter) {
final ConfigArg configArg = PREF_TO_CONFIG.get(key);
if (configArg == null) {
LOG.error("Unknown pref key {}", key);
return false;
}
switch (configArg.getConfigType()) {
switch (configArg.getConfigType(mGroupVersions)) {
case BOOL:
setter.setBoolean(configArg, prefs.getBoolean(key, false));
return true;
@ -470,11 +620,15 @@ public class Huami2021Config {
return String.format(Locale.ROOT, "huami_2021_known_config_%s", pref.name());
}
public static boolean deviceHasConfig(final Prefs devicePrefs, final Huami2021Config.ConfigArg config) {
public static boolean deviceHasConfig(final Prefs devicePrefs, final ZeppOsConfigService.ConfigArg config) {
return devicePrefs.getBoolean(getPrefKnownConfig(config), false);
}
public static class ConfigSetter {
public ConfigSetter newSetter() {
return new ConfigSetter();
}
public class ConfigSetter {
private final Map<ConfigGroup, Map<ConfigArg, byte[]>> arguments = new LinkedHashMap<>();
public ConfigSetter() {
@ -568,13 +722,13 @@ public class Huami2021Config {
final Map<ConfigArg, byte[]> configArgMap = arguments.get(configGroup);
try {
baos.write(CONFIG_CMD_SET);
baos.write(CMD_SET);
baos.write(configGroup.getValue());
baos.write(configGroup.getVersion());
baos.write(0x00); // ?
baos.write(configArgMap.size());
for (final Map.Entry<ConfigArg, byte[]> arg : configArgMap.entrySet()) {
final ConfigType configType = arg.getKey().getConfigType();
final ConfigType configType = arg.getKey().getConfigType(mGroupVersions);
baos.write(arg.getKey().getCode());
baos.write(configType.getValue());
baos.write(arg.getValue());
@ -586,22 +740,27 @@ public class Huami2021Config {
return baos.toByteArray();
}
public void write(final Huami2021Support support, final TransactionBuilder builder) {
public void write(final TransactionBuilder builder) {
// Write one command per config group
for (final ConfigGroup configGroup : arguments.keySet()) {
support.writeToChunked2021(builder, CHUNKED2021_ENDPOINT_CONFIG, encode(configGroup), true);
ZeppOsConfigService.this.write(builder, encode(configGroup));
}
}
private void checkArg(final ConfigArg arg, final ConfigType expectedConfigType) {
if (arg.getConfigType(mGroupVersions) == null) {
// Some special cases (STEPS goal) do not have a config type
return;
}
try {
if (!expectedConfigType.equals(arg.getConfigType())) {
if (!expectedConfigType.equals(arg.getConfigType(mGroupVersions))) {
throw new IllegalArgumentException(
String.format(
"Invalid arg type %s for %s, expected %s",
expectedConfigType,
arg,
arg.getConfigType()
arg.getConfigType(mGroupVersions)
)
);
}
@ -616,9 +775,7 @@ public class Huami2021Config {
}
}
public static class ConfigParser {
private static final Logger LOG = LoggerFactory.getLogger(ConfigParser.class);
public class ConfigParser {
private final ConfigGroup configGroup;
private final boolean includesConstraints;
@ -640,7 +797,7 @@ public class Huami2021Config {
}
final byte configArgByte = buf.get();
final Huami2021Config.ConfigArg configArg = Huami2021Config.ConfigArg.fromCode(configGroup, configArgByte);
final ZeppOsConfigService.ConfigArg configArg = ZeppOsConfigService.ConfigArg.fromCode(configGroup, configArgByte);
if (configArg == null) {
LOG.error("Unknown config {} for {}", String.format("0x%02x", configArgByte), configGroup);
}
@ -654,8 +811,8 @@ public class Huami2021Config {
return prefs;
}
if (configArg != null) {
if (configType != configArg.getConfigType()) {
LOG.warn("Unexpected arg type {} for {}, expected {}", configType, configArg, configArg.getConfigType());
if (configType != configArg.getConfigType(mGroupVersions)) {
LOG.warn("Unexpected arg type {} for {}, expected {}", configType, configArg, configArg.getConfigType(mGroupVersions));
}
}
@ -762,7 +919,7 @@ public class Huami2021Config {
LOG.warn("Unhandled {} pref of type {}", configType, configArg);
}
if (configArg != null && argPrefs != null && configType == configArg.getConfigType()) {
if (configArg != null && argPrefs != null && configType == configArg.getConfigType(mGroupVersions)) {
prefs.put(getPrefKnownConfig(configArg), true);
// Special cases for "follow phone" preferences. We need to ensure that "auto"
@ -787,7 +944,7 @@ public class Huami2021Config {
return prefs;
}
private static Map<String, Object> convertBooleanToPrefs(final ConfigArg configArg, final ConfigBoolean value) {
private Map<String, Object> convertBooleanToPrefs(final ConfigArg configArg, final ConfigBoolean value) {
// Special cases
switch (configArg) {
case LANGUAGE_FOLLOW_PHONE:
@ -816,7 +973,7 @@ public class Huami2021Config {
return null;
}
private static Map<String, Object> convertStringToPrefs(final ConfigArg configArg, final ConfigString str) {
private Map<String, Object> convertStringToPrefs(final ConfigArg configArg, final ConfigString str) {
// Special cases
switch (configArg) {
case DATE_FORMAT:
@ -833,7 +990,7 @@ public class Huami2021Config {
return null;
}
private static Map<String, Object> convertStringListToPrefs(final ConfigArg configArg, final ConfigStringList str) {
private Map<String, Object> convertStringListToPrefs(final ConfigArg configArg, final ConfigStringList str) {
final List<String> possibleValues = str.getPossibleValues();
final boolean includesConstraints = !possibleValues.isEmpty();
Map<String, Object> prefs = null;
@ -861,7 +1018,7 @@ public class Huami2021Config {
return prefs;
}
private static Map<String, Object> convertShortToPrefs(final ConfigArg configArg, final ConfigShort value) {
private Map<String, Object> convertShortToPrefs(final ConfigArg configArg, final ConfigShort value) {
if (configArg.getPrefKey() != null) {
// The arg maps to a number pref directly
final Map<String, Object> prefs = singletonMap(configArg.getPrefKey(), value.getValue());
@ -877,7 +1034,7 @@ public class Huami2021Config {
return null;
}
private static Map<String, Object> convertIntToPrefs(final ConfigArg configArg, final ConfigInt value) {
private Map<String, Object> convertIntToPrefs(final ConfigArg configArg, final ConfigInt value) {
if (configArg.getPrefKey() != null) {
// The arg maps to a number pref directly
final Map<String, Object> prefs = singletonMap(configArg.getPrefKey(), value.getValue());
@ -893,7 +1050,7 @@ public class Huami2021Config {
return null;
}
private static Map<String, Object> convertDatetimeHhMmToPrefs(final ConfigArg configArg, final ConfigDatetimeHhMm hhmm) {
private Map<String, Object> convertDatetimeHhMmToPrefs(final ConfigArg configArg, final ConfigDatetimeHhMm hhmm) {
if (configArg.getPrefKey() != null) {
// The arg maps to a hhmm pref directly
return singletonMap(configArg.getPrefKey(), hhmm.getValue());
@ -902,7 +1059,7 @@ public class Huami2021Config {
return null;
}
private static Map<String, Object> convertByteListToPrefs(final ConfigArg configArg, final ConfigByteList value) {
private Map<String, Object> convertByteListToPrefs(final ConfigArg configArg, final ConfigByteList value) {
final byte[] possibleValues = value.getPossibleValues();
final boolean includesConstraints = possibleValues != null && possibleValues.length > 0;
Map<String, Object> prefs = null;
@ -931,7 +1088,7 @@ public class Huami2021Config {
return prefs;
}
private static Map<String, Object> convertByteToPrefs(final ConfigArg configArg, final ConfigByte value) {
private Map<String, Object> convertByteToPrefs(final ConfigArg configArg, final ConfigByte value) {
final byte[] possibleValues = value.getPossibleValues();
final boolean includesConstraints = value.getPossibleValues().length > 0;
Map<String, Object> prefs = null;
@ -964,7 +1121,7 @@ public class Huami2021Config {
decoder = null;
break;
case HEART_RATE_ALL_DAY_MONITORING:
decoder = Huami2021Config::decodeHeartRateAllDayMonitoring;
decoder = ZeppOsConfigService::decodeHeartRateAllDayMonitoring;
break;
case SCREEN_TIMEOUT:
case HEART_RATE_HIGH_ALERTS:
@ -1023,7 +1180,7 @@ public class Huami2021Config {
return prefs;
}
private static List<String> decodeByteValues(final byte[] values, final ValueDecoder<Byte> decoder) {
private List<String> decodeByteValues(final byte[] values, final ValueDecoder<Byte> decoder) {
final List<String> decoded = new ArrayList<>(values.length);
for (final byte b : values) {
final String decodedByte = decoder.decode(b);
@ -1037,7 +1194,7 @@ public class Huami2021Config {
return decoded;
}
private static String decodeStringValues(final List<String> values, final ValueDecoder<String> decoder) {
private String decodeStringValues(final List<String> values, final ValueDecoder<String> decoder) {
final List<String> decoded = new ArrayList<>(values.size());
for (final String str : values) {
final String decodedStr = decoder.decode(str);
@ -1053,7 +1210,7 @@ public class Huami2021Config {
return String.join(",", decoded);
}
private static Map<String, Object> singletonMap(final String key, final Object value) {
private Map<String, Object> singletonMap(final String key, final Object value) {
if (key == null) {
LOG.error("Null key in prefs update");
if (BuildConfig.DEBUG) {

View File

@ -35,17 +35,17 @@ import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
public class ZeppOsFileUploadService extends AbstractZeppOsService {
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsFileUploadService.class);
public static final short ENDPOINT = 0x000d;
private static final short ENDPOINT = 0x000d;
public static final byte CMD_CAPABILITIES_REQUEST = 0x01;
public static final byte CMD_CAPABILITIES_RESPONSE = 0x02;
public static final byte CMD_UPLOAD_REQUEST = 0x03;
public static final byte CMD_UPLOAD_RESPONSE = 0x04;
public static final byte CMD_DATA_SEND = 0x10;
public static final byte CMD_DATA_ACK = 0x11;
private static final byte CMD_CAPABILITIES_REQUEST = 0x01;
private static final byte CMD_CAPABILITIES_RESPONSE = 0x02;
private static final byte CMD_UPLOAD_REQUEST = 0x03;
private static final byte CMD_UPLOAD_RESPONSE = 0x04;
private static final byte CMD_DATA_SEND = 0x10;
private static final byte CMD_DATA_ACK = 0x11;
public static final byte FLAG_FIRST_CHUNK = 0x01;
public static final byte FLAG_LAST_CHUNK = 0x02;
private static final byte FLAG_FIRST_CHUNK = 0x01;
private static final byte FLAG_LAST_CHUNK = 0x02;
private final Map<Byte, FileSendRequest> mSessionRequests = new HashMap<>();