diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/WorkoutSummaryParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/WorkoutSummaryParser.java
index 30c9c0c71..fbd93b712 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/WorkoutSummaryParser.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/WorkoutSummaryParser.java
@@ -18,6 +18,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.imp
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import org.apache.commons.lang3.ArrayUtils;
import org.json.JSONException;
import org.json.JSONObject;
@@ -26,7 +28,6 @@ import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.util.Date;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
@@ -96,45 +97,48 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
final XiaomiActivityFileId fileId = XiaomiActivityFileId.from(buf);
+ XiaomiSimpleActivityParser parser = null;
+
switch (fileId.getSubtype()) {
case SPORTS_OUTDOOR_WALKING_V1:
- parseOutdoorWalkingV1(summary, fileId, buf);
+ summary.setActivityKind(ActivityKind.TYPE_WALKING);
+ parser = getOutdoorWalkingV1Parser(fileId);
break;
case SPORTS_OUTDOOR_RUNNING:
- parseOutdoorRunning(summary, fileId, buf);
+ summary.setActivityKind(ActivityKind.TYPE_RUNNING);
+ // TODO
break;
case SPORTS_INDOOR_CYCLING:
- parseIndoorCycling(summary, fileId, buf);
+ parser = getIndoorCyclingParser(fileId);
break;
case SPORTS_FREESTYLE:
- parseFreestyle(summary, fileId, buf);
+ summary.setActivityKind(ActivityKind.TYPE_STRENGTH_TRAINING);
+ // TODO
break;
case SPORTS_ELLIPTICAL:
- parseElliptical(summary, fileId, buf);
+ summary.setActivityKind(ActivityKind.TYPE_ELLIPTICAL_TRAINER);
+ // TODO
break;
case SPORTS_OUTDOOR_WALKING_V2:
- parseOutdoorWalkingV2(summary, fileId, buf);
+ parser = getOutdoorWalkingV2Parser(fileId);
break;
case SPORTS_OUTDOOR_CYCLING:
- parseOutdoorCycling(summary, fileId, buf);
+ parser = getOutdoorCyclingParser(fileId);
break;
default:
LOG.warn("No workout summary parser for {}", fileId.getSubtypeCode());
break;
}
+ if (parser != null) {
+ parser.parse(summary, buf);
+ }
+
return summary;
}
- private void parseOutdoorRunning(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- summary.setActivityKind(ActivityKind.TYPE_RUNNING);
-
- // TODO
- }
-
- private void parseIndoorCycling(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- final JSONObject summaryData = new JSONObject();
-
+ @Nullable
+ private XiaomiSimpleActivityParser getIndoorCyclingParser(final XiaomiActivityFileId fileId) {
final int version = fileId.getVersion();
final int headerSize;
switch (version) {
@@ -143,58 +147,26 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
break;
default:
LOG.warn("Unable to parse workout summary version {}", fileId.getVersion());
- return;
+ return null;
}
- final byte[] header = new byte[headerSize];
- buf.get(header);
+ final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
+ builder.setHeaderSize(headerSize);
+ builder.addInt("startTime", "seconds");
+ builder.addInt("endTime", "seconds");
+ builder.addInt("activeSeconds", "seconds");
+ builder.addInt("distanceMeters", "meters");
+ builder.addShort("caloriesBurnt", "calories_unit");
+ builder.addUnknown(4);
+ builder.addByte("averageHR", "bpm");
+ builder.addByte("maxHR", "bpm");
+ builder.addByte("minHR", "bpm");
- summary.setActivityKind(ActivityKind.TYPE_INDOOR_CYCLING);
-
- final int startTime = buf.getInt();
- final int endTime = buf.getInt();
-
- // We don't set the start time, since we need it to match the fileId for the WorkoutGpsParser
- // to find it. They also seem to match.
- //summary.setStartTime(new Date(startTime * 1000L));
- summary.setEndTime(new Date(endTime * 1000L));
-
- final int duration = buf.getInt();
- addSummaryData(summaryData, "activeSeconds", duration, "seconds");
-
- final int distance = buf.getInt();
- addSummaryData(summaryData, "distanceMeters", distance, "meters");
-
- final int calories = buf.getShort();
- addSummaryData(summaryData, "caloriesBurnt", calories, "calories_unit");
-
- final int unknown1 = buf.getInt();
-
- final float avgHr = buf.get() & 0xff;
- final float maxHr = buf.get() & 0xff;
- final float minHr = buf.get() & 0xff;
- addSummaryData(summaryData, "averageHR", avgHr, "bpm");
- addSummaryData(summaryData, "maxHR", maxHr, "bpm");
- addSummaryData(summaryData, "minHR", minHr, "bpm");
-
- summary.setSummaryData(summaryData.toString());
+ return builder.build();
}
- private void parseFreestyle(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- summary.setActivityKind(ActivityKind.TYPE_STRENGTH_TRAINING);
-
- // TODO
- }
-
- private void parseElliptical(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- summary.setActivityKind(ActivityKind.TYPE_ELLIPTICAL_TRAINER);
-
- // TODO
- }
-
- private void parseOutdoorWalkingV1(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- final JSONObject summaryData = new JSONObject();
-
+ @Nullable
+ private XiaomiSimpleActivityParser getOutdoorWalkingV1Parser(final XiaomiActivityFileId fileId) {
final int version = fileId.getVersion();
final int headerSize;
switch (version) {
@@ -203,55 +175,41 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
break;
default:
LOG.warn("Unable to parse workout summary version {}", fileId.getVersion());
- return;
+ return null;
}
- final byte[] header = new byte[headerSize];
- buf.get(header);
+ final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
+ builder.setHeaderSize(headerSize);
+ builder.addInt("startTime", "seconds");
+ builder.addInt("endTime", "seconds");
+ builder.addInt("activeSeconds", "seconds");
+ builder.addInt("distanceMeters", "meters");
+ builder.addInt("caloriesBurnt", "calories_unit");
+ builder.addInt("maxPace", "seconds_m");
+ builder.addInt("minPace", "seconds_m");
+ builder.addUnknown(4);
+ builder.addInt("steps", "steps_unit");
+ builder.addUnknown(2); // pace?
+ builder.addByte("averageHR", "bpm");
+ builder.addByte("maxHR", "bpm");
+ builder.addByte("minHR", "bpm");
+ builder.addUnknown(20);
+ builder.addFloat("recoveryValue", "recoveryValue");
+ builder.addUnknown(9);
+ builder.addByte("recoveryTime", "seconds");
+ builder.addUnknown(2);
+ builder.addInt("vo2max", "seconds");
+ builder.addInt("hrZoneAnaerobic", "seconds");
+ builder.addInt("hrZoneAerobic", "seconds");
+ builder.addInt("hrZoneFatBurn", "seconds");
+ builder.addInt("hrZoneWarmUp", "seconds");
+ builder.addInt("configured_time_goal", "seconds");
- summary.setActivityKind(ActivityKind.TYPE_WALKING);
-
- final int startTime = buf.getInt();
- final int endTime = buf.getInt();
-
- // We don't set the start time, since we need it to match the fileId for the WorkoutGpsParser
- // to find it. They also seem to match.
- //summary.setStartTime(new Date(startTime * 1000L));
- summary.setEndTime(new Date(endTime * 1000L));
-
- final int duration = buf.getInt();
- addSummaryData(summaryData, "activeSeconds", duration, "seconds");
-
- final int distance = buf.getInt();
- addSummaryData(summaryData, "distanceMeters", distance, "meters");
-
- final int calories = buf.getShort();
- addSummaryData(summaryData, "caloriesBurnt", calories, "calories_unit");
-
-
- final int maxPace = buf.getInt();
- addSummaryData(summaryData, "maxPace", maxPace, "seconds_m");
- final int minPace = buf.getInt();
- addSummaryData(summaryData, "minPace", minPace, "seconds_m");
- final int unknown1 = buf.getInt(); // ?
- final int steps = buf.getInt();
- addSummaryData(summaryData, "steps", steps, "steps_unit");
- final int unknown6 = buf.getShort(); // pace?
-
- final int averageHR = buf.get() & 0xff;
- final int maxHR = buf.get() & 0xff;
- final int minHR = buf.get() & 0xff;
-
- addSummaryData(summaryData, "averageHR", averageHR, "bpm");
- addSummaryData(summaryData, "maxHR", maxHR, "bpm");
- addSummaryData(summaryData, "minHR", minHR, "bpm");
-
- summary.setSummaryData(summaryData.toString());
+ return builder.build();
}
- private void parseOutdoorWalkingV2(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- final JSONObject summaryData = new JSONObject();
-
+ @Nullable
+ private XiaomiSimpleActivityParser getOutdoorWalkingV2Parser(final XiaomiActivityFileId fileId) {
final int version = fileId.getVersion();
final int headerSize;
switch (version) {
@@ -260,63 +218,42 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
break;
default:
LOG.warn("Unable to parse workout summary version {}", fileId.getVersion());
- return;
+ return null;
}
- final byte[] header = new byte[headerSize];
- buf.get(header);
+ final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
+ builder.setHeaderSize(headerSize);
+ builder.addShort("xiaomiActivityType", "xiaomiActivityType");
+ builder.addInt("startTime", "seconds");
+ builder.addInt("endTime", "seconds");
+ builder.addInt("activeSeconds", "seconds");
+ builder.addUnknown(4);
+ builder.addInt("distanceMeters", "meters");
+ builder.addUnknown(2);
+ builder.addShort("caloriesBurnt", "calories_unit");
+ builder.addUnknown(12);
+ builder.addInt("steps", "steps_unit");
+ builder.addUnknown(2);
+ builder.addByte("averageHR", "bpm");
+ builder.addByte("maxHR", "bpm");
+ builder.addByte("minHR", "bpm");
+ builder.addUnknown(20);
+ builder.addFloat("recoveryValue", "?");
+ builder.addUnknown(9);
+ builder.addByte("recoveryTime", "seconds");
+ builder.addUnknown(2);
+ builder.addInt("vo2max", "ml/kg/min");
+ builder.addInt("hrZoneAnaerobic", "seconds");
+ builder.addInt("hrZoneAerobic", "seconds");
+ builder.addInt("hrZoneFatBurn", "seconds");
+ builder.addInt("hrZoneWarmUp", "seconds");
+ builder.addInt("configured_time_goal", "seconds");
- final short workoutType = buf.getShort();
-
- switch (workoutType) {
- case 2:
- summary.setActivityKind(ActivityKind.TYPE_WALKING);
- break;
- default:
- summary.setActivityKind(ActivityKind.TYPE_UNKNOWN);
- }
-
- final int startTime = buf.getInt();
- final int endTime = buf.getInt();
-
- // We don't set the start time, since we need it to match the fileId for the WorkoutGpsParser
- // to find it. They also seem to match.
- //summary.setStartTime(new Date(startTime * 1000L));
- summary.setEndTime(new Date(endTime * 1000L));
-
- final int duration = buf.getInt();
- addSummaryData(summaryData, "activeSeconds", duration, "seconds");
-
- final int unknown1 = buf.getInt();
- final int distance = buf.getInt();
- addSummaryData(summaryData, "distanceMeters", distance, "meters");
-
- final int unknown2 = buf.getShort();
-
- final int calories = buf.getShort();
- addSummaryData(summaryData, "caloriesBurnt", calories, "calories_unit");
-
- final int unknown3 = buf.getInt(); // pace?
- final int unknown4 = buf.getInt(); // pace?
- final int unknown5 = buf.getInt(); // pace?
- final int steps = buf.getInt();
- addSummaryData(summaryData, "steps", steps, "steps_unit");
- final int unknown6 = buf.getShort(); // pace?
-
- final int averageHR = buf.get() & 0xff;
- final int maxHR = buf.get() & 0xff;
- final int minHR = buf.get() & 0xff;
-
- addSummaryData(summaryData, "averageHR", averageHR, "bpm");
- addSummaryData(summaryData, "maxHR", maxHR, "bpm");
- addSummaryData(summaryData, "minHR", minHR, "bpm");
-
- summary.setSummaryData(summaryData.toString());
+ return builder.build();
}
- private void parseOutdoorCycling(final BaseActivitySummary summary, final XiaomiActivityFileId fileId, final ByteBuffer buf) {
- final JSONObject summaryData = new JSONObject();
-
+ @Nullable
+ private XiaomiSimpleActivityParser getOutdoorCyclingParser(final XiaomiActivityFileId fileId) {
final int version = fileId.getVersion();
final int headerSize;
switch (version) {
@@ -325,77 +262,25 @@ public class WorkoutSummaryParser extends XiaomiActivityParser implements Activi
break;
default:
LOG.warn("Unable to parse workout summary version {}", fileId.getVersion());
- return;
+ return null;
}
- final byte[] header = new byte[headerSize];
- buf.get(header);
+ final XiaomiSimpleActivityParser.Builder builder = new XiaomiSimpleActivityParser.Builder();
+ builder.setHeaderSize(headerSize);
+ builder.addShort("xiaomiWorkoutType", "xiaomiWorkoutType");
+ builder.addInt("startTime", "seconds");
+ builder.addInt("endTime", "seconds");
+ builder.addInt("activeSeconds", "seconds");
+ builder.addUnknown(4);
+ builder.addInt("distanceMeters", "meters");
+ builder.addUnknown(2);
+ builder.addShort("caloriesBurnt", "calories_unit");
+ builder.addUnknown(8);
+ builder.addFloat("maxSpeed", "km_h");
+ builder.addByte("averageHR", "bpm");
+ builder.addByte("maxHR", "bpm");
+ builder.addByte("minHR", "bpm");
- final short workoutType = buf.getShort();
-
- switch (workoutType) {
- case 6:
- summary.setActivityKind(ActivityKind.TYPE_CYCLING);
- break;
- default:
- summary.setActivityKind(ActivityKind.TYPE_UNKNOWN);
- }
-
- final int startTime = buf.getInt();
- final int endTime = buf.getInt();
-
- // We don't set the start time, since we need it to match the fileId for the WorkoutGpsParser
- // to find it. They also seem to match.
- //summary.setStartTime(new Date(startTime * 1000L));
- summary.setEndTime(new Date(endTime * 1000L));
-
- final int duration = buf.getInt();
- addSummaryData(summaryData, "activeSeconds", duration, "seconds");
-
- final int unknown1 = buf.getInt();
- final int distance = buf.getInt();
- addSummaryData(summaryData, "distanceMeters", distance, "meters");
-
- final int unknown2 = buf.getShort();
-
- final int calories = buf.getShort();
- addSummaryData(summaryData, "caloriesBurnt", calories, "calories_unit");
-
- final int unknown3 = buf.getInt();
- final int unknown4 = buf.getInt();
- final float maxSpeed = buf.getFloat();
-
- final float avgHr = buf.get() & 0xff;
- final float maxHr = buf.get() & 0xff;
- final float minHr = buf.get() & 0xff;
- addSummaryData(summaryData, "averageHR", avgHr, "bpm");
- addSummaryData(summaryData, "maxHR", maxHr, "bpm");
- addSummaryData(summaryData, "minHR", minHr, "bpm");
-
- summary.setSummaryData(summaryData.toString());
- }
-
- protected void addSummaryData(final JSONObject summaryData, final String key, final float value, final String unit) {
- if (value > 0) {
- try {
- final JSONObject innerData = new JSONObject();
- innerData.put("value", value);
- innerData.put("unit", unit);
- summaryData.put(key, innerData);
- } catch (final JSONException ignore) {
- }
- }
- }
-
- protected void addSummaryData(final JSONObject summaryData, final String key, final String value) {
- if (key != null && !key.equals("") && value != null && !value.equals("")) {
- try {
- final JSONObject innerData = new JSONObject();
- innerData.put("value", value);
- innerData.put("unit", "string");
- summaryData.put(key, innerData);
- } catch (final JSONException ignore) {
- }
- }
+ return builder.build();
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/XiaomiSimpleActivityParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/XiaomiSimpleActivityParser.java
new file mode 100644
index 000000000..203171ede
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/XiaomiSimpleActivityParser.java
@@ -0,0 +1,144 @@
+/* Copyright (C) 2023 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.service.devices.xiaomi.activity.impl;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
+
+public class XiaomiSimpleActivityParser {
+ private final int headerSize;
+ private final List dataEntries;
+
+ public XiaomiSimpleActivityParser(final int headerSize, final List dataEntries) {
+ this.headerSize = headerSize;
+ this.dataEntries = dataEntries;
+ }
+
+ public void parse(final BaseActivitySummary summary, final ByteBuffer buf) {
+ final JSONObject summaryData = new JSONObject();
+
+ final byte[] header = new byte[headerSize];
+ buf.get(header);
+
+ for (final XiaomiSimpleDataEntry dataEntry : dataEntries) {
+ final Number value = dataEntry.get(buf);
+ if (value == null) {
+ continue;
+ }
+
+ if (dataEntry.getKey().equals("endTime")) {
+ if (dataEntry.getUnit().equals("seconds")) {
+ summary.setEndTime(new Date(value.intValue() * 1000L));
+ } else {
+ throw new IllegalArgumentException("endTime should be in seconds");
+ }
+ } if (dataEntry.getKey().equals("xiaomiWorkoutType")) {
+ // TODO use XiaomiWorkoutType
+ switch (value.intValue()) {
+ case 2:
+ summary.setActivityKind(ActivityKind.TYPE_WALKING);
+ break;
+ case 6:
+ summary.setActivityKind(ActivityKind.TYPE_CYCLING);
+ break;
+ default:
+ summary.setActivityKind(ActivityKind.TYPE_UNKNOWN);
+ }
+ } else {
+ addSummaryData(summaryData, dataEntry.getKey(), value.floatValue(), dataEntry.getUnit());
+ }
+ }
+
+ summary.setSummaryData(summaryData.toString());
+ }
+
+ protected void addSummaryData(final JSONObject summaryData, final String key, final float value, final String unit) {
+ if (value > 0) {
+ try {
+ final JSONObject innerData = new JSONObject();
+ innerData.put("value", value);
+ innerData.put("unit", unit);
+ summaryData.put(key, innerData);
+ } catch (final JSONException ignore) {
+ }
+ }
+ }
+
+ protected void addSummaryData(final JSONObject summaryData, final String key, final String value) {
+ if (key != null && !key.equals("") && value != null && !value.equals("")) {
+ try {
+ final JSONObject innerData = new JSONObject();
+ innerData.put("value", value);
+ innerData.put("unit", "string");
+ summaryData.put(key, innerData);
+ } catch (final JSONException ignore) {
+ }
+ }
+ }
+
+ public static class Builder {
+ private int headerSize;
+ private List dataEntries = new ArrayList<>();
+
+ public Builder setHeaderSize(final int headerSize) {
+ this.headerSize = headerSize;
+ return this;
+ }
+
+ public Builder addByte(final String key, final String unit) {
+ dataEntries.add(new XiaomiSimpleDataEntry(key, unit, buf -> buf.get() & 0xff));
+ return this;
+ }
+
+ public Builder addShort(final String key, final String unit) {
+ dataEntries.add(new XiaomiSimpleDataEntry(key, unit, ByteBuffer::getShort));
+ return this;
+ }
+
+ public Builder addInt(final String key, final String unit) {
+ dataEntries.add(new XiaomiSimpleDataEntry(key, unit, ByteBuffer::getInt));
+ return this;
+ }
+
+ public Builder addFloat(final String key, final String unit) {
+ dataEntries.add(new XiaomiSimpleDataEntry(key, unit, ByteBuffer::getFloat));
+ return this;
+ }
+
+ public Builder addUnknown(final int sizeBytes) {
+ dataEntries.add(new XiaomiSimpleDataEntry(null, null, buf -> {
+ for (int i = 0; i < sizeBytes; i++) {
+ buf.get();
+ }
+ return null;
+ }));
+ return this;
+ }
+
+ public XiaomiSimpleActivityParser build() {
+ return new XiaomiSimpleActivityParser(headerSize, dataEntries);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/XiaomiSimpleDataEntry.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/XiaomiSimpleDataEntry.java
new file mode 100644
index 000000000..3ec131cdf
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/impl/XiaomiSimpleDataEntry.java
@@ -0,0 +1,47 @@
+/* Copyright (C) 2023 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.service.devices.xiaomi.activity.impl;
+
+import java.nio.ByteBuffer;
+
+public class XiaomiSimpleDataEntry {
+ private final String key;
+ private final String unit;
+ private final Getter getter;
+
+ public XiaomiSimpleDataEntry(final String key, final String unit, final Getter getter) {
+ this.key = key;
+ this.unit = unit;
+ this.getter = getter;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public Number get(final ByteBuffer buf) {
+ return getter.get(buf);
+ }
+
+ public interface Getter {
+ Number get(ByteBuffer buf);
+ }
+}