diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
index 365fce34c..2d45cb09e 100644
--- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
+++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
@@ -43,7 +43,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
- final Schema schema = new Schema(42, MAIN_PACKAGE + ".entities");
+ final Schema schema = new Schema(43, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@@ -60,6 +60,7 @@ public class GBDaoGenerator {
addMakibesHR3ActivitySample(schema, user, device);
addMiBandActivitySample(schema, user, device);
+ addHuamiExtendedActivitySample(schema, user, device);
addPebbleHealthActivitySample(schema, user, device);
addPebbleHealthActivityKindOverlay(schema, user, device);
addPebbleMisfitActivitySample(schema, user, device);
@@ -223,6 +224,20 @@ public class GBDaoGenerator {
return activitySample;
}
+ private static Entity addHuamiExtendedActivitySample(Schema schema, Entity user, Entity device) {
+ Entity activitySample = addEntity(schema, "HuamiExtendedActivitySample");
+ addCommonActivitySampleProperties("MiBandActivitySample", activitySample, user, device);
+ activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
+ activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
+ activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
+ addHeartRateProperties(activitySample);
+ activitySample.addIntProperty("unknown1");
+ activitySample.addIntProperty("sleep");
+ activitySample.addIntProperty("deepSleep");
+ activitySample.addIntProperty("remSleep");
+ return activitySample;
+ }
+
private static void addHeartRateProperties(Entity activitySample) {
activitySample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetterAndSetter(OVERRIDE);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java
index 6cc36e918..f92bf0eb4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java
@@ -367,7 +367,7 @@ public class GBDeviceAdapterv2 extends ListAdapter. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami;
-import android.content.Context;
-import android.net.Uri;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import androidx.annotation.NonNull;
import java.util.Arrays;
import java.util.List;
@@ -29,19 +25,24 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability;
import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl;
-import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
+import de.greenrobot.dao.query.QueryBuilder;
+import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.Device;
+import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
-import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public abstract class Huami2021Coordinator extends HuamiCoordinator {
- private static final Logger LOG = LoggerFactory.getLogger(Huami2021Coordinator.class);
-
@Override
public boolean supportsHeartRateMeasurement(final GBDevice device) {
- // TODO: One-shot HR measures are not working, so let's disable this for now
+ return true;
+ }
+
+ @Override
+ public boolean supportsManualHeartRateMeasurement(final GBDevice device) {
+ // TODO: It should be supported, but not yet properly implemented
return false;
}
@@ -61,15 +62,8 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
}
@Override
- public boolean supportsActivityTracking() {
- // TODO: It's supported by the devices, but not yet implemented
- return false;
- }
-
- @Override
- public boolean supportsActivityDataFetching() {
- // TODO: It's supported by the devices, but not yet implemented
- return false;
+ public boolean supportsRemSleep() {
+ return true;
}
@Override
@@ -94,10 +88,18 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
return true;
}
+ @Override
+ protected void deleteDevice(@NonNull final GBDevice gbDevice,
+ @NonNull final Device device,
+ @NonNull final DaoSession session) throws GBException {
+ final Long deviceId = device.getId();
+ final QueryBuilder> qb = session.getHuamiExtendedActivitySampleDao().queryBuilder();
+ qb.where(HuamiExtendedActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
+ }
+
@Override
public SampleProvider extends AbstractActivitySample> getSampleProvider(final GBDevice device, final DaoSession session) {
- // TODO: It's supported by the devices, but not yet implemented
- return null;
+ return new HuamiExtendedSampleProvider(device, session);
}
@Override
@@ -117,7 +119,7 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
}
@Override
- public String[] getSupportedLanguageSettings(GBDevice device) {
+ public String[] getSupportedLanguageSettings(final GBDevice device) {
return new String[]{
"auto",
"de_DE",
@@ -148,7 +150,7 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
}
@Override
- public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
+ public int[] getSupportedDeviceSpecificSettings(final GBDevice device) {
return new int[]{
R.xml.devicesettings_header_time,
//R.xml.devicesettings_timeformat,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiExtendedSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiExtendedSampleProvider.java
new file mode 100644
index 000000000..d1ee0438e
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiExtendedSampleProvider.java
@@ -0,0 +1,144 @@
+/* Copyright (C) 2022 José Rebelo
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices.huami;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+import de.greenrobot.dao.AbstractDao;
+import de.greenrobot.dao.Property;
+import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
+
+public class HuamiExtendedSampleProvider extends AbstractSampleProvider {
+ public static final int TYPE_CUSTOM_UNSET = -1;
+ public static final int TYPE_OUTDOOR_RUNNING = 64;
+ public static final int TYPE_NOT_WORN = 115;
+ public static final int TYPE_CHARGING = 118;
+ public static final int TYPE_SLEEP = 120;
+ public static final int TYPE_CUSTOM_DEEP_SLEEP = TYPE_SLEEP + 1;
+ public static final int TYPE_CUSTOM_REM_SLEEP = TYPE_SLEEP + 2;
+
+ public HuamiExtendedSampleProvider(final GBDevice device, final DaoSession session) {
+ super(device, session);
+ }
+
+ @Override
+ public float normalizeIntensity(int rawIntensity) {
+ return rawIntensity / 256.0f;
+ }
+
+ @Override
+ public AbstractDao getSampleDao() {
+ return getSession().getHuamiExtendedActivitySampleDao();
+ }
+
+ @NonNull
+ @Override
+ protected Property getTimestampSampleProperty() {
+ return HuamiExtendedActivitySampleDao.Properties.Timestamp;
+ }
+
+ @NonNull
+ @Override
+ protected Property getDeviceIdentifierSampleProperty() {
+ return HuamiExtendedActivitySampleDao.Properties.DeviceId;
+ }
+
+ @Override
+ protected Property getRawKindSampleProperty() {
+ return HuamiExtendedActivitySampleDao.Properties.RawKind;
+ }
+
+ @Override
+ public HuamiExtendedActivitySample createActivitySample() {
+ return new HuamiExtendedActivitySample();
+ }
+
+ @Override
+ protected List getGBActivitySamples(final int timestamp_from, final int timestamp_to, final int activityType) {
+ final List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, activityType);
+ postProcess(samples);
+ return samples;
+ }
+
+ private void postProcess(final List samples) {
+ if (samples.isEmpty()) {
+ return;
+ }
+
+ for (final HuamiExtendedActivitySample sample : samples) {
+ if (sample.getRawKind() == TYPE_SLEEP) {
+ // Band reports type sleep regardless of sleep type, so we map it to custom raw types
+ // These thresholds are arbitrary, but seem to somewhat match the data that's displayed on the band
+
+ sample.setDeepSleep(sample.getDeepSleep() & 127);
+ sample.setRemSleep(sample.getRemSleep() & 127);
+
+ if (sample.getRemSleep() > 55) {
+ sample.setRawKind(TYPE_CUSTOM_REM_SLEEP);
+ sample.setRawIntensity(sample.getRemSleep());
+ } else if (sample.getDeepSleep() > 42) {
+ sample.setRawKind(TYPE_CUSTOM_DEEP_SLEEP);
+ sample.setRawIntensity(sample.getDeepSleep());
+ } else {
+ sample.setRawIntensity(sample.getSleep());
+ }
+ }
+ }
+ }
+
+ @Override
+ public int normalizeType(final int rawType) {
+ switch (rawType) {
+ case TYPE_OUTDOOR_RUNNING:
+ return ActivityKind.TYPE_RUNNING;
+ case TYPE_NOT_WORN:
+ case TYPE_CHARGING:
+ return ActivityKind.TYPE_NOT_WORN;
+ case TYPE_SLEEP:
+ return ActivityKind.TYPE_LIGHT_SLEEP;
+ case TYPE_CUSTOM_DEEP_SLEEP:
+ return ActivityKind.TYPE_DEEP_SLEEP;
+ case TYPE_CUSTOM_REM_SLEEP:
+ return ActivityKind.TYPE_REM_SLEEP;
+ }
+
+ return ActivityKind.TYPE_UNKNOWN;
+ }
+
+ @Override
+ public int toRawActivityKind(final int activityKind) {
+ switch (activityKind) {
+ case ActivityKind.TYPE_RUNNING:
+ return TYPE_OUTDOOR_RUNNING;
+ case ActivityKind.TYPE_NOT_WORN:
+ return TYPE_NOT_WORN;
+ case ActivityKind.TYPE_LIGHT_SLEEP:
+ case ActivityKind.TYPE_DEEP_SLEEP:
+ case ActivityKind.TYPE_REM_SLEEP:
+ return TYPE_SLEEP;
+ }
+
+ return TYPE_CUSTOM_UNSET;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java
index 472571659..669ea22f4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java
@@ -115,7 +115,9 @@ public class HuamiService {
// maybe not really activity data, but steps?
public static final byte COMMAND_FETCH_DATA = 0x02;
- public static final byte COMMAND_XXXX_ACTIVITY_DATA = 0x03; // maybe delete/drop activity data?
+ // maybe delete/drop activity data?
+ // on Huami it's just 03 / on Huami 2021 it's 03:09
+ public static final byte COMMAND_ACK_ACTIVITY_DATA = 0x03;
public static final byte[] COMMAND_SET_FITNESS_GOAL_START = new byte[] { 0x10, 0x0, 0x0 };
public static final byte[] COMMAND_SET_FITNESS_GOAL_END = new byte[] { 0, 0 };
@@ -229,6 +231,7 @@ public class HuamiService {
public static final byte COMMAND_FIRMWARE_REBOOT = 0x05; // to UUID_CHARACTERISTIC_FIRMWARE
public static final byte[] RESPONSE_FINISH_SUCCESS = new byte[] {RESPONSE, 2, SUCCESS };
+ public static final byte[] RESPONSE_ACK_SUCCESS = new byte[] {RESPONSE, 3, SUCCESS };
public static final byte[] RESPONSE_FIRMWARE_DATA_SUCCESS = new byte[] {RESPONSE, COMMAND_FIRMWARE_START_DATA, SUCCESS };
/**
* Received in response to any dateformat configuration request (byte 0 in the byte[] value.
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/AbstractHuamiOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/AbstractHuamiOperation.java
index 12fea4ad0..81c27c9d2 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/AbstractHuamiOperation.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/AbstractHuamiOperation.java
@@ -16,6 +16,7 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
+import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.AbstractMiBandOperation;
@@ -25,9 +26,13 @@ public abstract class AbstractHuamiOperation extends AbstractMiBandOperation System.currentTimeMillis()) {
- LOG.warn("Not doing another fetch since last synced timestamp is in the future: " + DateTimeUtils.formatDateTime(lastSyncTimestamp.getTime()));
+ LOG.warn("Not doing another fetch since last synced timestamp is in the future: {}", DateTimeUtils.formatDateTime(lastSyncTimestamp.getTime()));
return false;
}
- LOG.info("Doing another fetch since last sync timestamp is still too old: " + DateTimeUtils.formatDateTime(lastSyncTimestamp.getTime()));
+ LOG.info("Doing another fetch since last sync timestamp is still too old: {}", DateTimeUtils.formatDateTime(lastSyncTimestamp.getTime()));
return true;
}
private GregorianCalendar saveSamples() {
- if (samples.size() > 0) {
- // save all the samples that we got
- try (DBHandler handler = GBApplication.acquireDB()) {
- DaoSession session = handler.getDaoSession();
- SampleProvider sampleProvider = new MiBandSampleProvider(getDevice(), session);
- Device device = DBHelper.getDevice(getDevice(), session);
- User user = DBHelper.getUser(session);
-
- GregorianCalendar timestamp = (GregorianCalendar) startTimestamp.clone();
- for (MiBandActivitySample sample : samples) {
- sample.setDevice(device);
- sample.setUser(user);
- sample.setTimestamp((int) (timestamp.getTimeInMillis() / 1000));
- sample.setProvider(sampleProvider);
-
- if (LOG.isDebugEnabled()) {
-// LOG.debug("sample: " + sample);
- }
-
- timestamp.add(Calendar.MINUTE, 1);
- }
- sampleProvider.addGBActivitySamples(samples.toArray(new MiBandActivitySample[0]));
-
- saveLastSyncTimestamp(timestamp);
- LOG.info("Mi2 activity data: last sample timestamp: " + DateTimeUtils.formatDateTime(timestamp.getTime()));
- return timestamp;
-
- } catch (Exception ex) {
- GB.toast(getContext(), "Error saving activity samples", Toast.LENGTH_LONG, GB.ERROR);
- } finally {
- samples.clear();
- }
+ if (samples.isEmpty()) {
+ LOG.info("No samples to save");
+ return null;
+ }
+
+ LOG.info("Saving {} samples", samples.size());
+
+ // save all the samples that we got
+ try (DBHandler handler = GBApplication.acquireDB()) {
+ DaoSession session = handler.getDaoSession();
+
+ DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(getDevice());
+ SampleProvider sampleProvider = coordinator.getSampleProvider(getDevice(), session);
+ Device device = DBHelper.getDevice(getDevice(), session);
+ User user = DBHelper.getUser(session);
+
+ GregorianCalendar timestamp = (GregorianCalendar) startTimestamp.clone();
+ for (MiBandActivitySample sample : samples) {
+ sample.setDevice(device);
+ sample.setUser(user);
+ sample.setTimestamp((int) (timestamp.getTimeInMillis() / 1000));
+ sample.setProvider(sampleProvider);
+
+ //LOG.debug(sampleToString(sample));
+
+ timestamp.add(Calendar.MINUTE, 1);
+ }
+ sampleProvider.addGBActivitySamples(samples.toArray(new MiBandActivitySample[0]));
+
+ saveLastSyncTimestamp(timestamp);
+ LOG.info("Huami activity data: last sample timestamp: {}", DateTimeUtils.formatDateTime(timestamp.getTime()));
+ return timestamp;
+ } catch (Exception ex) {
+ GB.toast(getContext(), "Error saving activity samples", Toast.LENGTH_LONG, GB.ERROR);
+ LOG.error("Error saving activity samples", ex);
+ return null;
+ } finally {
+ samples.clear();
}
- return null;
}
/**
@@ -189,21 +199,74 @@ public class FetchActivityOperation extends AbstractFetchOperation {
}
for (int i = 1; i < len; i += sampleSize) {
- MiBandActivitySample sample = createSample(value[i], value[i + 1], value[i + 2], value[i + 3]); // lgtm [java/index-out-of-bounds]
+ final MiBandActivitySample sample;
+
+ switch (sampleSize) {
+ case 4:
+ sample = createSample(value, i);
+ break;
+ case 8:
+ sample = createExtendedSample(value, i);
+ break;
+ default:
+ throw new IllegalStateException("Unsupported sample size " + sampleSize);
+ }
+
samples.add(sample);
}
}
- private MiBandActivitySample createSample(byte category, byte intensity, byte steps, byte heartrate) {
+ private MiBandActivitySample createSample(byte[] value, int i) {
MiBandActivitySample sample = new MiBandActivitySample();
- sample.setRawKind(category & 0xff);
- sample.setRawIntensity(intensity & 0xff);
- sample.setSteps(steps & 0xff);
- sample.setHeartRate(heartrate & 0xff);
+ sample.setRawKind(value[i] & 0xff);
+ sample.setRawIntensity(value[i + 1] & 0xff);
+ sample.setSteps(value[i + 2] & 0xff);
+ sample.setHeartRate(value[i + 3] & 0xff);
return sample;
}
+ private MiBandActivitySample createExtendedSample(byte[] value, int i) {
+ final HuamiExtendedActivitySample huamiExtendedActivitySample = new HuamiExtendedActivitySample();
+ huamiExtendedActivitySample.setRawKind(value[i] & 0xff);
+ huamiExtendedActivitySample.setRawIntensity(value[i + 1] & 0xff);
+ huamiExtendedActivitySample.setSteps(value[i + 2] & 0xff);
+ huamiExtendedActivitySample.setHeartRate(value[i + 3] & 0xff);
+ huamiExtendedActivitySample.setUnknown1(value[i + 4] & 0xff);
+ huamiExtendedActivitySample.setSleep(value[i + 5] & 0xff);
+ huamiExtendedActivitySample.setDeepSleep(value[i + 6] & 0xff);
+ huamiExtendedActivitySample.setRemSleep(value[i + 7] & 0xff);
+
+ return huamiExtendedActivitySample;
+ }
+
+ private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ROOT);
+
+ public static String sampleToString(final MiBandActivitySample sample) {
+ final StringBuilder builder = new StringBuilder(160);
+
+ builder.append(sample.getClass().getSimpleName());
+ builder.append("{");
+ builder.append(sdf.format(new Date(sample.getTimestamp() * 1000L)));
+ builder.append(",rawKind=").append(sample.getRawKind());
+ builder.append(",rawIntensity=").append(sample.getRawIntensity());
+ builder.append(",steps=").append(sample.getSteps());
+ builder.append(",heartRate=").append(sample.getHeartRate());
+
+ if (sample instanceof HuamiExtendedActivitySample) {
+ final HuamiExtendedActivitySample huamiExtendedSample = (HuamiExtendedActivitySample) sample;
+
+ builder.append(",unknown1=").append(huamiExtendedSample.getUnknown1());
+ builder.append(",sleep=").append(huamiExtendedSample.getSleep());
+ builder.append(",deepSleep=").append(huamiExtendedSample.getDeepSleep());
+ builder.append(",remSleep=").append(huamiExtendedSample.getRemSleep());
+ }
+
+ builder.append("}");
+
+ return builder.toString();
+ }
+
@Override
protected String getLastSyncTimeKey() {
return "lastSyncTimeMillis";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation2021.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation2021.java
index 6640f1324..7faf353a5 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation2021.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation2021.java
@@ -39,12 +39,6 @@ public class UpdateFirmwareOperation2021 extends UpdateFirmwareOperation2020 {
super(uri, support);
}
- @Override
- protected void enableOtherNotifications(final TransactionBuilder builder, final boolean enable) {
- // Disable 2021 chunked reads, otherwise firmware upgrades get interrupted
- builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ), enable);
- }
-
@Override
AbstractHuamiFirmwareInfo createFwInfo(final Uri uri, final Context context) throws IOException {
return super.createFwInfo(uri, context);