mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-09-01 20:15:52 +02:00
Xiaomi: Refactor workout summary parsing
This commit is contained in:
parent
05d8f99312
commit
f7258034cc
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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<XiaomiSimpleDataEntry> dataEntries;
|
||||
|
||||
public XiaomiSimpleActivityParser(final int headerSize, final List<XiaomiSimpleDataEntry> 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<XiaomiSimpleDataEntry> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user