1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-01 06:22:55 +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.entities.HuamiExtendedActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser; 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.HuamiLanguageType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType;
@ -318,14 +318,14 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
} }
public boolean hasGps(final GBDevice device) { 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) { 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) { private boolean supportsConfig(final GBDevice device, final ZeppOsConfigService.ConfigArg config) {
return Huami2021Config.deviceHasConfig(getPrefs(device), 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_HTTP = 0x0001;
public static final short CHUNKED2021_ENDPOINT_CALENDAR = 0x0007; 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_WEATHER = 0x000e;
public static final short CHUNKED2021_ENDPOINT_ALARMS = 0x000f; public static final short CHUNKED2021_ENDPOINT_ALARMS = 0x000f;
public static final short CHUNKED2021_ENDPOINT_CANNED_MESSAGES = 0x0013; 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_UP = 0x05;
public static final byte MUSIC_BUTTON_VOLUME_DOWN = 0x06; 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}. * 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.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; 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.service.devices.huami.HuamiVibrationPatternNotificationType;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; 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_SHORTCUTS_SORTABLE, handler, prefs);
removeUnsupportedElementsFromListPreference(HuamiConst.PREF_CONTROL_CENTER_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) { if (config.getPrefKey() == null) {
continue; continue;
} }
switch (config.getConfigType()) { switch (config.getConfigType(null)) {
case BYTE: case BYTE:
case BYTE_LIST: case BYTE_LIST:
case STRING_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 // 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( put(DeviceSettingsPreferenceConst.PREF_SCREEN_NIGHT_MODE, Arrays.asList(
Huami2021Config.ConfigArg.NIGHT_MODE_MODE, ZeppOsConfigService.ConfigArg.NIGHT_MODE_MODE,
Huami2021Config.ConfigArg.NIGHT_MODE_SCHEDULED_START, ZeppOsConfigService.ConfigArg.NIGHT_MODE_SCHEDULED_START,
Huami2021Config.ConfigArg.NIGHT_MODE_SCHEDULED_END ZeppOsConfigService.ConfigArg.NIGHT_MODE_SCHEDULED_END
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_SLEEP_MODE, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_SLEEP_MODE, Arrays.asList(
Huami2021Config.ConfigArg.SLEEP_MODE_SLEEP_SCREEN, ZeppOsConfigService.ConfigArg.SLEEP_MODE_SLEEP_SCREEN,
Huami2021Config.ConfigArg.SLEEP_MODE_SMART_ENABLE ZeppOsConfigService.ConfigArg.SLEEP_MODE_SMART_ENABLE
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_LIFT_WRIST, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_LIFT_WRIST, Arrays.asList(
Huami2021Config.ConfigArg.LIFT_WRIST_MODE, ZeppOsConfigService.ConfigArg.LIFT_WRIST_MODE,
Huami2021Config.ConfigArg.LIFT_WRIST_SCHEDULED_START, ZeppOsConfigService.ConfigArg.LIFT_WRIST_SCHEDULED_START,
Huami2021Config.ConfigArg.LIFT_WRIST_SCHEDULED_END, ZeppOsConfigService.ConfigArg.LIFT_WRIST_SCHEDULED_END,
Huami2021Config.ConfigArg.LIFT_WRIST_RESPONSE_SENSITIVITY ZeppOsConfigService.ConfigArg.LIFT_WRIST_RESPONSE_SENSITIVITY
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_PASSWORD, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_PASSWORD, Arrays.asList(
Huami2021Config.ConfigArg.PASSWORD_ENABLED, ZeppOsConfigService.ConfigArg.PASSWORD_ENABLED,
Huami2021Config.ConfigArg.PASSWORD_TEXT ZeppOsConfigService.ConfigArg.PASSWORD_TEXT
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_ALWAYS_ON_DISPLAY, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_ALWAYS_ON_DISPLAY, Arrays.asList(
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_MODE, ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_MODE,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_START, ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_START,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_END, ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_END,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE, ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_STYLE ZeppOsConfigService.ConfigArg.ALWAYS_ON_DISPLAY_STYLE
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_AUTO_BRIGHTNESS, Arrays.asList( 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( put(DeviceSettingsPreferenceConst.PREF_SCREEN_HEARTRATE_MONITORING, Arrays.asList(
Huami2021Config.ConfigArg.HEART_RATE_ALL_DAY_MONITORING, ZeppOsConfigService.ConfigArg.HEART_RATE_ALL_DAY_MONITORING,
Huami2021Config.ConfigArg.HEART_RATE_HIGH_ALERTS, ZeppOsConfigService.ConfigArg.HEART_RATE_HIGH_ALERTS,
Huami2021Config.ConfigArg.HEART_RATE_LOW_ALERTS, ZeppOsConfigService.ConfigArg.HEART_RATE_LOW_ALERTS,
Huami2021Config.ConfigArg.HEART_RATE_ACTIVITY_MONITORING, ZeppOsConfigService.ConfigArg.HEART_RATE_ACTIVITY_MONITORING,
Huami2021Config.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING, ZeppOsConfigService.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING,
Huami2021Config.ConfigArg.SLEEP_BREATHING_QUALITY_MONITORING, ZeppOsConfigService.ConfigArg.SLEEP_BREATHING_QUALITY_MONITORING,
Huami2021Config.ConfigArg.STRESS_MONITORING, ZeppOsConfigService.ConfigArg.STRESS_MONITORING,
Huami2021Config.ConfigArg.STRESS_RELAXATION_REMINDER, ZeppOsConfigService.ConfigArg.STRESS_RELAXATION_REMINDER,
Huami2021Config.ConfigArg.SPO2_ALL_DAY_MONITORING, ZeppOsConfigService.ConfigArg.SPO2_ALL_DAY_MONITORING,
Huami2021Config.ConfigArg.SPO2_LOW_ALERT ZeppOsConfigService.ConfigArg.SPO2_LOW_ALERT
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_INACTIVITY_EXTENDED, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_INACTIVITY_EXTENDED, Arrays.asList(
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_ENABLED, ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_ENABLED,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_START, ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_START,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_END, ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_END,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_ENABLED, ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_DND_ENABLED,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_START, ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_START,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_END ZeppOsConfigService.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_END
)); ));
put(DeviceSettingsPreferenceConst.PREF_HEADER_GPS, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_HEADER_GPS, Arrays.asList(
Huami2021Config.ConfigArg.WORKOUT_GPS_PRESET, ZeppOsConfigService.ConfigArg.WORKOUT_GPS_PRESET,
Huami2021Config.ConfigArg.WORKOUT_GPS_BAND, ZeppOsConfigService.ConfigArg.WORKOUT_GPS_BAND,
Huami2021Config.ConfigArg.WORKOUT_GPS_COMBINATION, ZeppOsConfigService.ConfigArg.WORKOUT_GPS_COMBINATION,
Huami2021Config.ConfigArg.WORKOUT_GPS_SATELLITE_SEARCH, ZeppOsConfigService.ConfigArg.WORKOUT_GPS_SATELLITE_SEARCH,
Huami2021Config.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_ENABLED, ZeppOsConfigService.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_ENABLED,
Huami2021Config.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_TIME ZeppOsConfigService.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_TIME
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_SOUND_AND_VIBRATION, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_SOUND_AND_VIBRATION, Arrays.asList(
Huami2021Config.ConfigArg.VOLUME, ZeppOsConfigService.ConfigArg.VOLUME,
Huami2021Config.ConfigArg.CROWN_VIBRATION, ZeppOsConfigService.ConfigArg.CROWN_VIBRATION,
Huami2021Config.ConfigArg.ALERT_TONE, ZeppOsConfigService.ConfigArg.ALERT_TONE,
Huami2021Config.ConfigArg.COVER_TO_MUTE, ZeppOsConfigService.ConfigArg.COVER_TO_MUTE,
Huami2021Config.ConfigArg.VIBRATE_FOR_ALERT, ZeppOsConfigService.ConfigArg.VIBRATE_FOR_ALERT,
Huami2021Config.ConfigArg.TEXT_TO_SPEECH ZeppOsConfigService.ConfigArg.TEXT_TO_SPEECH
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_DO_NOT_DISTURB, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_DO_NOT_DISTURB, Arrays.asList(
Huami2021Config.ConfigArg.DND_MODE, ZeppOsConfigService.ConfigArg.DND_MODE,
Huami2021Config.ConfigArg.DND_SCHEDULED_START, ZeppOsConfigService.ConfigArg.DND_SCHEDULED_START,
Huami2021Config.ConfigArg.DND_SCHEDULED_END ZeppOsConfigService.ConfigArg.DND_SCHEDULED_END
)); ));
put(DeviceSettingsPreferenceConst.PREF_HEADER_WORKOUT_DETECTION, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_HEADER_WORKOUT_DETECTION, Arrays.asList(
Huami2021Config.ConfigArg.WORKOUT_DETECTION_CATEGORY, ZeppOsConfigService.ConfigArg.WORKOUT_DETECTION_CATEGORY,
Huami2021Config.ConfigArg.WORKOUT_DETECTION_ALERT, ZeppOsConfigService.ConfigArg.WORKOUT_DETECTION_ALERT,
Huami2021Config.ConfigArg.WORKOUT_DETECTION_SENSITIVITY ZeppOsConfigService.ConfigArg.WORKOUT_DETECTION_SENSITIVITY
)); ));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_OFFLINE_VOICE, Arrays.asList( put(DeviceSettingsPreferenceConst.PREF_SCREEN_OFFLINE_VOICE, Arrays.asList(
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPOND_TURN_WRIST, ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_RESPOND_TURN_WRIST,
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPOND_SCREEN_ON, ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_RESPOND_SCREEN_ON,
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING, ZeppOsConfigService.ConfigArg.OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING,
Huami2021Config.ConfigArg.OFFLINE_VOICE_LANGUAGE 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( hidePrefIfNoConfigSupported(
handler, handler,
prefs, prefs,
configScreen.getKey(), 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 // 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()) { if (possibleValues == null || possibleValues.isEmpty()) {
// The band hasn't reported this setting, so we don't know the possible values. // The band hasn't reported this setting, so we don't know the possible values.
// Hide it // Hide it
@ -360,14 +360,14 @@ public class Huami2021SettingsCustomizer extends HuamiSettingsCustomizer {
private void hidePrefIfNoConfigSupported(final DeviceSpecificSettingsHandler handler, private void hidePrefIfNoConfigSupported(final DeviceSpecificSettingsHandler handler,
final Prefs prefs, final Prefs prefs,
final String prefToHide, final String prefToHide,
final Huami2021Config.ConfigArg... configs) { final ZeppOsConfigService.ConfigArg... configs) {
final Preference pref = handler.findPreference(prefToHide); final Preference pref = handler.findPreference(prefToHide);
if (pref == null) { if (pref == null) {
return; return;
} }
for (final Huami2021Config.ConfigArg config : configs) { for (final ZeppOsConfigService.ConfigArg config : configs) {
if (Huami2021Config.deviceHasConfig(prefs, config)) { if (ZeppOsConfigService.deviceHasConfig(prefs, config)) {
// This preference is supported, don't hide // This preference is supported, don't hide
return; return;
} }

View File

@ -17,5 +17,5 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
public interface Huami2021Handler { 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; package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
import static org.apache.commons.lang3.ArrayUtils.subarray; 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.Huami2021Service.*;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.SUCCESS; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.SUCCESS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_NAME; 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.fromUint16;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint8; import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint8;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.mapTimeZone; 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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.ConfigArg.LANGUAGE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.LANGUAGE_FOLLOW_PHONE; import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.LANGUAGE_FOLLOW_PHONE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.PASSWORD_ENABLED; import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.PASSWORD_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.PASSWORD_TEXT; import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.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.zeppos.services.ZeppOsConfigService.ConfigArg.TEMPERATURE_UNIT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigArg.TIME_FORMAT; import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigArg.TIME_FORMAT;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigGroup; import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsConfigService.ConfigGroup;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config.ConfigSetter;
import android.Manifest; import android.Manifest;
import android.content.Intent; 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.HuamiFetchDebugLogsOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation; 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.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.service.devices.huami.zeppos.services.ZeppOsFileUploadService;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils; import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil; import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
@ -144,6 +141,11 @@ public abstract class Huami2021Support extends HuamiSupport {
// Services // Services
private final ZeppOsFileUploadService fileUploadService = new ZeppOsFileUploadService(this); 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() { public Huami2021Support() {
this(LOG); this(LOG);
@ -174,15 +176,15 @@ public abstract class Huami2021Support extends HuamiSupport {
@Override @Override
public void onSendConfiguration(final String config) { public void onSendConfiguration(final String config) {
final ConfigSetter configSetter = new ConfigSetter(); final ZeppOsConfigService.ConfigSetter configSetter = configService.newSetter();
final Prefs prefs = getDevicePrefs(); final Prefs prefs = getDevicePrefs();
try { 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 // If the ConfigSetter was able to set the config, just write it and return
final TransactionBuilder builder; final TransactionBuilder builder;
builder = performInitialized("Sending configuration for option: " + config); builder = performInitialized("Sending configuration for option: " + config);
configSetter.write(this, builder); configSetter.write(builder);
builder.queue(getQueue()); builder.queue(getQueue());
return; 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); 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); 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) .setInt(FITNESS_GOAL_STEPS, goalSteps)
.setShort(FITNESS_GOAL_CALORIES, (short) goalCalories) .setShort(FITNESS_GOAL_CALORIES, (short) goalCalories)
.setShort(FITNESS_GOAL_SLEEP, (short) (goalSleep * 60)) .setShort(FITNESS_GOAL_SLEEP, (short) (goalSleep * 60))
.setShort(FITNESS_GOAL_WEIGHT, (short) goalWeight) .setShort(FITNESS_GOAL_WEIGHT, (short) goalWeight)
.setShort(FITNESS_GOAL_STANDING_TIME, (short) (goalStandingTime)) .setShort(FITNESS_GOAL_STANDING_TIME, (short) (goalStandingTime))
.setShort(FITNESS_GOAL_FAT_BURN_TIME, (short) goalFatBurnTime) .setShort(FITNESS_GOAL_FAT_BURN_TIME, (short) goalFatBurnTime)
.write(this, builder); .write(builder);
return this; return this;
} }
@ -529,10 +531,10 @@ public abstract class Huami2021Support extends HuamiSupport {
return this; return this;
} }
new ConfigSetter() configService.newSetter()
.setBoolean(PASSWORD_ENABLED, passwordEnabled) .setBoolean(PASSWORD_ENABLED, passwordEnabled)
.setString(PASSWORD_TEXT, password) .setString(PASSWORD_TEXT, password)
.write(this, builder); .write(builder);
return this; return this;
} }
@ -897,9 +899,9 @@ public abstract class Huami2021Support extends HuamiSupport {
protected Huami2021Support setHeartrateSleepSupport(final TransactionBuilder builder) { protected Huami2021Support setHeartrateSleepSupport(final TransactionBuilder builder) {
final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(gbDevice.getAddress()); final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(gbDevice.getAddress());
new ConfigSetter() configService.newSetter()
.setBoolean(SLEEP_HIGH_ACCURACY_MONITORING, enableHrSleepSupport) .setBoolean(SLEEP_HIGH_ACCURACY_MONITORING, enableHrSleepSupport)
.write(this, builder); .write(builder);
return this; return this;
} }
@ -955,10 +957,10 @@ public abstract class Huami2021Support extends HuamiSupport {
} }
@Override @Override
protected HuamiSupport setHeartrateMeasurementInterval(TransactionBuilder builder, int minutes) { protected HuamiSupport setHeartrateMeasurementInterval(final TransactionBuilder builder, final int minutes) {
new ConfigSetter() configService.newSetter()
.setByte(HEART_RATE_ALL_DAY_MONITORING, (byte) minutes) .setByte(HEART_RATE_ALL_DAY_MONITORING, (byte) minutes)
.write(this, builder); .write(builder);
return this; return this;
} }
@ -1043,9 +1045,9 @@ public abstract class Huami2021Support extends HuamiSupport {
timeFormatByte = 0x00; timeFormatByte = 0x00;
} }
new ConfigSetter() configService.newSetter()
.setByte(TIME_FORMAT, timeFormatByte) .setByte(TIME_FORMAT, timeFormatByte)
.write(this, builder); .write(builder);
return this; return this;
} }
@ -1057,7 +1059,7 @@ public abstract class Huami2021Support extends HuamiSupport {
setDisplayItems2021( setDisplayItems2021(
builder, builder,
DISPLAY_ITEMS_MENU, 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())) new ArrayList<>(prefs.getList(HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE, Collections.emptyList()))
); );
return this; return this;
@ -1070,7 +1072,7 @@ public abstract class Huami2021Support extends HuamiSupport {
setDisplayItems2021( setDisplayItems2021(
builder, builder,
DISPLAY_ITEMS_SHORTCUTS, 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())) new ArrayList<>(prefs.getList(HuamiConst.PREF_SHORTCUTS_SORTABLE, Collections.emptyList()))
); );
return this; return this;
@ -1082,7 +1084,7 @@ public abstract class Huami2021Support extends HuamiSupport {
setDisplayItems2021( setDisplayItems2021(
builder, builder,
DISPLAY_ITEMS_CONTROL_CENTER, 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())) new ArrayList<>(prefs.getList(HuamiConst.PREF_CONTROL_CENTER_SORTABLE, Collections.emptyList()))
); );
return this; return this;
@ -1226,9 +1228,9 @@ public abstract class Huami2021Support extends HuamiSupport {
break; break;
} }
new ConfigSetter() configService.newSetter()
.setByte(TEMPERATURE_UNIT, unitByte) .setByte(TEMPERATURE_UNIT, unitByte)
.write(this, builder); .write(builder);
return this; return this;
} }
@ -1240,10 +1242,10 @@ public abstract class Huami2021Support extends HuamiSupport {
LOG.info("Setting device language to {}", localeString); LOG.info("Setting device language to {}", localeString);
new ConfigSetter() configService.newSetter()
.setByte(LANGUAGE, getLanguageId()) .setByte(LANGUAGE, getLanguageId())
.setBoolean(LANGUAGE_FOLLOW_PHONE, localeString.equals("auto")) .setBoolean(LANGUAGE_FOLLOW_PHONE, localeString.equals("auto"))
.write(this, builder); .write(builder);
return this; return this;
} }
@ -1361,12 +1363,8 @@ public abstract class Huami2021Support extends HuamiSupport {
LOG.info("2021 phase3Initialize..."); LOG.info("2021 phase3Initialize...");
setUserInfo(builder); setUserInfo(builder);
setFitnessGoal(builder);
for (final ConfigGroup configGroup : ConfigGroup.values()) {
requestConfig(builder, configGroup);
}
configService.requestAllConfigs(builder);
requestCapabilityReminders(builder); requestCapabilityReminders(builder);
fileUploadService.requestCapability(builder); fileUploadService.requestCapability(builder);
@ -1416,7 +1414,7 @@ public abstract class Huami2021Support extends HuamiSupport {
} }
@Override @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) { if (payload == null || payload.length == 0) {
LOG.warn("Empty or null payload for {}", String.format("0x%04x", type)); LOG.warn("Empty or null payload for {}", String.format("0x%04x", type));
return; 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)); 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) { switch (type) {
case CHUNKED2021_ENDPOINT_ALARMS: case CHUNKED2021_ENDPOINT_ALARMS:
handle2021Alarms(payload); handle2021Alarms(payload);
@ -1437,12 +1440,6 @@ public abstract class Huami2021Support extends HuamiSupport {
case CHUNKED2021_ENDPOINT_COMPAT: case CHUNKED2021_ENDPOINT_COMPAT:
LOG.warn("Unexpected compat payload {}", GB.hexdump(payload)); LOG.warn("Unexpected compat payload {}", GB.hexdump(payload));
return; return;
case CHUNKED2021_ENDPOINT_CONFIG:
handle2021Config(payload);
return;
case ZeppOsFileUploadService.ENDPOINT:
fileUploadService.handlePayload(payload);
return;
case CHUNKED2021_ENDPOINT_WEATHER: case CHUNKED2021_ENDPOINT_WEATHER:
handle2021Weather(payload); handle2021Weather(payload);
return; return;
@ -1660,102 +1657,6 @@ public abstract class Huami2021Support extends HuamiSupport {
// TODO update database? // 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) { protected void handle2021Workout(final byte[] payload) {
switch (payload[0]) { switch (payload[0]) {
case WORKOUT_CMD_APP_OPEN: 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])); LOG.error("Unknown display items type {}", String.format("0x%x", payload[1]));
return; return;
} }
final String allScreensPrefKey = Huami2021Config.getPrefPossibleValuesKey(prefKey); final String allScreensPrefKey = ZeppOsConfigService.getPrefPossibleValuesKey(prefKey);
final boolean menuHasMoreSection; final boolean menuHasMoreSection;

View File

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

View File

@ -132,7 +132,7 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler
} }
@Override @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) { if (type != Huami2021Service.CHUNKED2021_ENDPOINT_AUTH) {
this.huamiSupport.handle2021Payload(type, payload); this.huamiSupport.handle2021Payload(type, payload);
return; return;

View File

@ -30,6 +30,10 @@ public abstract class AbstractZeppOsService {
public abstract boolean isEncrypted(); public abstract boolean isEncrypted();
public abstract void handlePayload(final byte[] payload); public abstract void handlePayload(final byte[] payload);
protected Huami2021Support getSupport() {
return mSupport;
}
protected void write(final String taskName, final byte[] data) { protected void write(final String taskName, final byte[] data) {
this.mSupport.writeToChunked2021(taskName, getEndpoint(), data, isEncrypted()); 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 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/>. */ 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.activities.devicesettings.DeviceSettingsPreferenceConst.*;
import static nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl.PREF_PASSWORD; 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.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.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;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE_END; 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.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability;
import nodomain.freeyourgadget.gadgetbridge.capabilities.WorkoutDetectionCapability; 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.ActivateDisplayOnLift;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLiftSensitivity; import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLiftSensitivity;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.AlwaysOnDisplay; 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.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; 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.GB;
import nodomain.freeyourgadget.gadgetbridge.util.MapUtils; import nodomain.freeyourgadget.gadgetbridge.util.MapUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class Huami2021Config { public class ZeppOsConfigService extends AbstractZeppOsService {
private static final Logger LOG = LoggerFactory.getLogger(Huami2021Config.class); 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 { public enum ConfigGroup {
DISPLAY(0x01, 0x02), 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_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), 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_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_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_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 FITNESS_GOAL_SLEEP(ConfigGroup.HEALTH, ConfigType.SHORT, 0x55, null), // TODO needs to be handled globally
@ -263,7 +393,27 @@ public class Huami2021Config {
return configGroup; 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; return configType;
} }
@ -276,7 +426,7 @@ public class Huami2021Config {
} }
public static ConfigArg fromCode(final ConfigGroup configGroup, final byte code) { 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) { if (arg.getConfigGroup().equals(configGroup) && arg.getCode() == code) {
return arg; return arg;
} }
@ -285,8 +435,8 @@ public class Huami2021Config {
} }
public static List<ConfigArg> getAllArgsForConfigGroup(final ConfigGroup configGroup) { public static List<ConfigArg> getAllArgsForConfigGroup(final ConfigGroup configGroup) {
final List<Huami2021Config.ConfigArg> configArgs = new ArrayList<>(); final List<ZeppOsConfigService.ConfigArg> configArgs = new ArrayList<>();
for (final Huami2021Config.ConfigArg arg : values()) { for (final ZeppOsConfigService.ConfigArg arg : values()) {
if (arg.getConfigGroup().equals(configGroup)) { if (arg.getConfigGroup().equals(configGroup)) {
configArgs.add(arg); configArgs.add(arg);
} }
@ -317,14 +467,14 @@ public class Huami2021Config {
* *
* @return true if the {@link ConfigSetter} was updated for this preference key * @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); final ConfigArg configArg = PREF_TO_CONFIG.get(key);
if (configArg == null) { if (configArg == null) {
LOG.error("Unknown pref key {}", key); LOG.error("Unknown pref key {}", key);
return false; return false;
} }
switch (configArg.getConfigType()) { switch (configArg.getConfigType(mGroupVersions)) {
case BOOL: case BOOL:
setter.setBoolean(configArg, prefs.getBoolean(key, false)); setter.setBoolean(configArg, prefs.getBoolean(key, false));
return true; return true;
@ -470,11 +620,15 @@ public class Huami2021Config {
return String.format(Locale.ROOT, "huami_2021_known_config_%s", pref.name()); 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); 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<>(); private final Map<ConfigGroup, Map<ConfigArg, byte[]>> arguments = new LinkedHashMap<>();
public ConfigSetter() { public ConfigSetter() {
@ -568,13 +722,13 @@ public class Huami2021Config {
final Map<ConfigArg, byte[]> configArgMap = arguments.get(configGroup); final Map<ConfigArg, byte[]> configArgMap = arguments.get(configGroup);
try { try {
baos.write(CONFIG_CMD_SET); baos.write(CMD_SET);
baos.write(configGroup.getValue()); baos.write(configGroup.getValue());
baos.write(configGroup.getVersion()); baos.write(configGroup.getVersion());
baos.write(0x00); // ? baos.write(0x00); // ?
baos.write(configArgMap.size()); baos.write(configArgMap.size());
for (final Map.Entry<ConfigArg, byte[]> arg : configArgMap.entrySet()) { 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(arg.getKey().getCode());
baos.write(configType.getValue()); baos.write(configType.getValue());
baos.write(arg.getValue()); baos.write(arg.getValue());
@ -586,22 +740,27 @@ public class Huami2021Config {
return baos.toByteArray(); return baos.toByteArray();
} }
public void write(final Huami2021Support support, final TransactionBuilder builder) { public void write(final TransactionBuilder builder) {
// Write one command per config group // Write one command per config group
for (final ConfigGroup configGroup : arguments.keySet()) { 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) { 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 { try {
if (!expectedConfigType.equals(arg.getConfigType())) { if (!expectedConfigType.equals(arg.getConfigType(mGroupVersions))) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Invalid arg type %s for %s, expected %s", "Invalid arg type %s for %s, expected %s",
expectedConfigType, expectedConfigType,
arg, arg,
arg.getConfigType() arg.getConfigType(mGroupVersions)
) )
); );
} }
@ -616,9 +775,7 @@ public class Huami2021Config {
} }
} }
public static class ConfigParser { public class ConfigParser {
private static final Logger LOG = LoggerFactory.getLogger(ConfigParser.class);
private final ConfigGroup configGroup; private final ConfigGroup configGroup;
private final boolean includesConstraints; private final boolean includesConstraints;
@ -640,7 +797,7 @@ public class Huami2021Config {
} }
final byte configArgByte = buf.get(); 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) { if (configArg == null) {
LOG.error("Unknown config {} for {}", String.format("0x%02x", configArgByte), configGroup); LOG.error("Unknown config {} for {}", String.format("0x%02x", configArgByte), configGroup);
} }
@ -654,8 +811,8 @@ public class Huami2021Config {
return prefs; return prefs;
} }
if (configArg != null) { if (configArg != null) {
if (configType != configArg.getConfigType()) { if (configType != configArg.getConfigType(mGroupVersions)) {
LOG.warn("Unexpected arg type {} for {}, expected {}", configType, configArg, configArg.getConfigType()); 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); 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); prefs.put(getPrefKnownConfig(configArg), true);
// Special cases for "follow phone" preferences. We need to ensure that "auto" // Special cases for "follow phone" preferences. We need to ensure that "auto"
@ -787,7 +944,7 @@ public class Huami2021Config {
return prefs; 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 // Special cases
switch (configArg) { switch (configArg) {
case LANGUAGE_FOLLOW_PHONE: case LANGUAGE_FOLLOW_PHONE:
@ -816,7 +973,7 @@ public class Huami2021Config {
return null; 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 // Special cases
switch (configArg) { switch (configArg) {
case DATE_FORMAT: case DATE_FORMAT:
@ -833,7 +990,7 @@ public class Huami2021Config {
return null; 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 List<String> possibleValues = str.getPossibleValues();
final boolean includesConstraints = !possibleValues.isEmpty(); final boolean includesConstraints = !possibleValues.isEmpty();
Map<String, Object> prefs = null; Map<String, Object> prefs = null;
@ -861,7 +1018,7 @@ public class Huami2021Config {
return prefs; 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) { if (configArg.getPrefKey() != null) {
// The arg maps to a number pref directly // The arg maps to a number pref directly
final Map<String, Object> prefs = singletonMap(configArg.getPrefKey(), value.getValue()); final Map<String, Object> prefs = singletonMap(configArg.getPrefKey(), value.getValue());
@ -877,7 +1034,7 @@ public class Huami2021Config {
return null; 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) { if (configArg.getPrefKey() != null) {
// The arg maps to a number pref directly // The arg maps to a number pref directly
final Map<String, Object> prefs = singletonMap(configArg.getPrefKey(), value.getValue()); final Map<String, Object> prefs = singletonMap(configArg.getPrefKey(), value.getValue());
@ -893,7 +1050,7 @@ public class Huami2021Config {
return null; 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) { if (configArg.getPrefKey() != null) {
// The arg maps to a hhmm pref directly // The arg maps to a hhmm pref directly
return singletonMap(configArg.getPrefKey(), hhmm.getValue()); return singletonMap(configArg.getPrefKey(), hhmm.getValue());
@ -902,7 +1059,7 @@ public class Huami2021Config {
return null; 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 byte[] possibleValues = value.getPossibleValues();
final boolean includesConstraints = possibleValues != null && possibleValues.length > 0; final boolean includesConstraints = possibleValues != null && possibleValues.length > 0;
Map<String, Object> prefs = null; Map<String, Object> prefs = null;
@ -931,7 +1088,7 @@ public class Huami2021Config {
return prefs; 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 byte[] possibleValues = value.getPossibleValues();
final boolean includesConstraints = value.getPossibleValues().length > 0; final boolean includesConstraints = value.getPossibleValues().length > 0;
Map<String, Object> prefs = null; Map<String, Object> prefs = null;
@ -964,7 +1121,7 @@ public class Huami2021Config {
decoder = null; decoder = null;
break; break;
case HEART_RATE_ALL_DAY_MONITORING: case HEART_RATE_ALL_DAY_MONITORING:
decoder = Huami2021Config::decodeHeartRateAllDayMonitoring; decoder = ZeppOsConfigService::decodeHeartRateAllDayMonitoring;
break; break;
case SCREEN_TIMEOUT: case SCREEN_TIMEOUT:
case HEART_RATE_HIGH_ALERTS: case HEART_RATE_HIGH_ALERTS:
@ -1023,7 +1180,7 @@ public class Huami2021Config {
return prefs; 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); final List<String> decoded = new ArrayList<>(values.length);
for (final byte b : values) { for (final byte b : values) {
final String decodedByte = decoder.decode(b); final String decodedByte = decoder.decode(b);
@ -1037,7 +1194,7 @@ public class Huami2021Config {
return decoded; 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()); final List<String> decoded = new ArrayList<>(values.size());
for (final String str : values) { for (final String str : values) {
final String decodedStr = decoder.decode(str); final String decodedStr = decoder.decode(str);
@ -1053,7 +1210,7 @@ public class Huami2021Config {
return String.join(",", decoded); 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) { if (key == null) {
LOG.error("Null key in prefs update"); LOG.error("Null key in prefs update");
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {

View File

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