mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Garmin: Initial attempt at supporting incoming FitDefinition/FitData Messages
Add the code to generate outgoing FitDefinition- and FitDataStatusMessage. Add support for incoming FitDefinitionMessage and register handler via deviceEvent (commented for now to avoid leaking). Refactor FitLocalMessageHandler to support both outgoing and incoming handling of FitDefinition/FitData. Most of the changes were not tested for the lack of hardware, but at least they show no regressions.
This commit is contained in:
committed by
José Rebelo
parent
a3d732f11a
commit
157c9c2237
+20
-4
@@ -3,7 +3,12 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitLocalMessageBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.FitDataMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.FitDefinitionMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
||||
@@ -13,21 +18,29 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.stat
|
||||
public class FitLocalMessageHandler implements MessageHandler{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FitLocalMessageHandler.class);
|
||||
private final GarminSupport deviceSupport;
|
||||
private final FitLocalMessageBuilder localMessageBuilder;
|
||||
private final List<RecordDefinition> recordDefinitionList;
|
||||
private List<RecordData> recordDataList;
|
||||
|
||||
public FitLocalMessageHandler(GarminSupport deviceSupport, FitLocalMessageBuilder localMessageBuilder) {
|
||||
this.deviceSupport = deviceSupport;
|
||||
this.localMessageBuilder = localMessageBuilder;
|
||||
this.recordDefinitionList = localMessageBuilder.getDefinitions();
|
||||
this.recordDataList = localMessageBuilder.getRecordDataList();
|
||||
}
|
||||
|
||||
public FitLocalMessageHandler(GarminSupport deviceSupport, List<RecordDefinition> recordDefinitionList) {
|
||||
this.deviceSupport = deviceSupport;
|
||||
this.recordDefinitionList = recordDefinitionList;
|
||||
this.recordDataList = new ArrayList<>();
|
||||
}
|
||||
|
||||
public FitDefinitionMessage init() {
|
||||
return new FitDefinitionMessage(localMessageBuilder.getDefinitions());
|
||||
return new FitDefinitionMessage(this.recordDefinitionList);
|
||||
}
|
||||
|
||||
private FitDataMessage sendFollowUp(FitDefinitionStatusMessage fitDefinitionStatusMessage) {
|
||||
if (fitDefinitionStatusMessage.getFitDefinitionStatusCode() != FitDefinitionStatusMessage.FitDefinitionStatusCode.APPLIED)
|
||||
LOG.warn("FitDefinition was not applied, will send FitData anyway.");
|
||||
return new FitDataMessage(localMessageBuilder.getRecordDataList());
|
||||
return new FitDataMessage(this.recordDataList);
|
||||
}
|
||||
|
||||
private void unregisterSelf() {
|
||||
@@ -40,6 +53,9 @@ public class FitLocalMessageHandler implements MessageHandler{
|
||||
return sendFollowUp((FitDefinitionStatusMessage) message);
|
||||
else if (message instanceof FitDataStatusMessage) {
|
||||
unregisterSelf();
|
||||
} else if (message instanceof FitDataMessage) {
|
||||
recordDataList = ((FitDataMessage) message).applyDefinitions(recordDefinitionList);
|
||||
//TODO: do something
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
-1
@@ -83,6 +83,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v2.CommunicatorV2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.CapabilitiesDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.FileDownloadedDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.IncomingFitDefinitionDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.NotificationSubscriptionDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.SupportedFileTypesDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.WeatherRequestDeviceEvent;
|
||||
@@ -446,6 +447,10 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC
|
||||
}
|
||||
|
||||
currentlyDownloading = null;
|
||||
} else if (deviceEvent instanceof IncomingFitDefinitionDeviceEvent) {
|
||||
//TODO: commented for now to avoid leaking
|
||||
// final FitLocalMessageHandler fitLocalMessageHandler = new FitLocalMessageHandler(this, ((IncomingFitDefinitionDeviceEvent) deviceEvent).getRecordDefinitions());
|
||||
// messageHandlers.add(fitLocalMessageHandler);
|
||||
} else {
|
||||
super.evaluateGBDeviceEvent(deviceEvent);
|
||||
}
|
||||
@@ -608,7 +613,7 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC
|
||||
|
||||
protected void unregisterHandler(MessageHandler registered) {
|
||||
messageHandlers.remove(registered);
|
||||
LOG.warn("handlers: {}", messageHandlers.stream().count());
|
||||
LOG.warn("handlers left: {}", messageHandlers.stream().count());
|
||||
}
|
||||
|
||||
public static FitLocalMessageBuilder encodeWeather(final WeatherSpec weather) {
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||
|
||||
|
||||
public class IncomingFitDefinitionDeviceEvent extends GBDeviceEvent {
|
||||
public List<RecordDefinition> getRecordDefinitions() {
|
||||
return recordDefinitions;
|
||||
}
|
||||
|
||||
private final List<RecordDefinition> recordDefinitions;
|
||||
|
||||
public IncomingFitDefinitionDeviceEvent(List<RecordDefinition> recordDefinitions) {
|
||||
this.recordDefinitions = recordDefinitions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(final Context context, final GBDevice device) {
|
||||
// Handled in support class
|
||||
}
|
||||
}
|
||||
+9
-21
@@ -14,13 +14,12 @@ public class FitDataMessage extends GFDIMessage {
|
||||
private List<RecordData> recordDataList;
|
||||
private GarminByteBufferReader reader;
|
||||
private final boolean generateOutgoing;
|
||||
private boolean isProcessed = false;
|
||||
|
||||
public FitDataMessage(GarminByteBufferReader messageReader, GarminMessage garminMessage) {
|
||||
this.reader = messageReader;
|
||||
this.recordDataList = new ArrayList<>();
|
||||
this.recordDataList = null;
|
||||
this.garminMessage = garminMessage;
|
||||
this.statusMessage = new FitDataStatusMessage(garminMessage, Status.ACK, FitDataStatusMessage.FitDataStatusCode.APPLIED);
|
||||
this.statusMessage = new FitDataStatusMessage(garminMessage, Status.ACK, FitDataStatusMessage.FitDataStatusCode.APPLIED, true);
|
||||
this.generateOutgoing = false;
|
||||
}
|
||||
|
||||
@@ -29,7 +28,6 @@ public class FitDataMessage extends GFDIMessage {
|
||||
this.recordDataList = recordDataList;
|
||||
this.garminMessage = GarminMessage.FIT_DATA;
|
||||
this.statusMessage = null;
|
||||
this.isProcessed = true;
|
||||
this.generateOutgoing = true;
|
||||
}
|
||||
|
||||
@@ -37,13 +35,14 @@ public class FitDataMessage extends GFDIMessage {
|
||||
return new FitDataMessage(new GarminByteBufferReader(reader.readBytes(reader.remaining())), garminMessage);
|
||||
}
|
||||
|
||||
public void applyDefinitions(List<RecordDefinition> recordDefinitions) {
|
||||
public List<RecordData> applyDefinitions(List<RecordDefinition> recordDefinitions) {
|
||||
final List<RecordData> recordDataList = new ArrayList<>();;
|
||||
Objects.requireNonNull(reader, "reader cannot be null");
|
||||
try {
|
||||
while (reader.remaining() > 0) {
|
||||
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte());
|
||||
if (recordHeader.isDefinition())
|
||||
return;
|
||||
return null;
|
||||
|
||||
RecordDefinition localMessageDefinition = recordDefinitions.stream()
|
||||
.filter(d -> d.getRecordHeader().getLocalMessageType() == recordHeader.getLocalMessageType())
|
||||
@@ -52,7 +51,7 @@ public class FitDataMessage extends GFDIMessage {
|
||||
|
||||
if (localMessageDefinition == null) {
|
||||
LOG.warn("Cannot find a local message definition for type: {}", recordHeader.getLocalMessageType());
|
||||
return ;
|
||||
return null;
|
||||
}
|
||||
RecordData recordData = new RecordData(
|
||||
localMessageDefinition,
|
||||
@@ -61,30 +60,19 @@ public class FitDataMessage extends GFDIMessage {
|
||||
recordData.parseDataMessage(reader, null);
|
||||
recordDataList.add(recordData);
|
||||
}
|
||||
this.isProcessed = true;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while applying definitions", e);
|
||||
throw new IllegalStateException("Failed to apply definitions to FIT data", e);
|
||||
} finally {
|
||||
reader = null;
|
||||
reader.setPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<RecordData> getRecordDataList() {
|
||||
if(!isProcessed())
|
||||
throw new IllegalStateException(
|
||||
"Message has not been processed yet. Call applyDefinitions first."
|
||||
);
|
||||
return recordDataList;
|
||||
}
|
||||
|
||||
public boolean isProcessed() {
|
||||
return isProcessed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean generateOutgoing() {
|
||||
if(this.recordDataList == null)
|
||||
return false;
|
||||
final MessageWriter writer = new MessageWriter(response);
|
||||
writer.writeShort(0); // packet size will be filled below
|
||||
writer.writeShort(this.garminMessage.getId());
|
||||
|
||||
+22
-5
@@ -1,24 +1,36 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.IncomingFitDefinitionDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.FitDefinitionStatusMessage;
|
||||
|
||||
public class FitDefinitionMessage extends GFDIMessage {
|
||||
|
||||
private final IncomingFitDefinitionDeviceEvent incomingFitDefinitionDeviceEvent;
|
||||
private final List<RecordDefinition> recordDefinitions;
|
||||
private final boolean generateOutgoing;
|
||||
|
||||
public FitDefinitionMessage(List<RecordDefinition> recordDefinitions, GarminMessage garminMessage) {
|
||||
public FitDefinitionMessage(List<RecordDefinition> recordDefinitions, GarminMessage garminMessage) {//incoming
|
||||
this.recordDefinitions = recordDefinitions;
|
||||
this.garminMessage = garminMessage;
|
||||
this.statusMessage = this.getStatusMessage();
|
||||
this.statusMessage = new FitDefinitionStatusMessage(garminMessage, Status.ACK, FitDefinitionStatusMessage.FitDefinitionStatusCode.APPLIED, true);
|
||||
this.generateOutgoing = false;
|
||||
|
||||
this.incomingFitDefinitionDeviceEvent = new IncomingFitDefinitionDeviceEvent(recordDefinitions);
|
||||
}
|
||||
|
||||
public FitDefinitionMessage(List<RecordDefinition> recordDefinitions) {
|
||||
public FitDefinitionMessage(List<RecordDefinition> recordDefinitions) { //outgoing
|
||||
this.recordDefinitions = recordDefinitions;
|
||||
this.garminMessage = GarminMessage.FIT_DEFINITION;
|
||||
this.statusMessage = null;
|
||||
this.generateOutgoing = true;
|
||||
|
||||
this.incomingFitDefinitionDeviceEvent = null;
|
||||
}
|
||||
|
||||
public static FitDefinitionMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
|
||||
@@ -36,6 +48,11 @@ public class FitDefinitionMessage extends GFDIMessage {
|
||||
return recordDefinitions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GBDeviceEvent> getGBDeviceEvent() {
|
||||
return Collections.singletonList(incomingFitDefinitionDeviceEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean generateOutgoing() {
|
||||
final MessageWriter writer = new MessageWriter(response);
|
||||
@@ -44,7 +61,7 @@ public class FitDefinitionMessage extends GFDIMessage {
|
||||
for (RecordDefinition recordDefinition : recordDefinitions) {
|
||||
recordDefinition.generateOutgoingPayload(writer);
|
||||
}
|
||||
return true;
|
||||
return this.generateOutgoing;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
-9
@@ -2,22 +2,19 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.sta
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
|
||||
|
||||
public class FitDataStatusMessage extends GFDIStatusMessage {
|
||||
|
||||
private final Status status;
|
||||
private final FitDataStatusCode fitDataStatusCode;
|
||||
private final boolean sendOutgoing;
|
||||
|
||||
public FitDataStatusMessage(GarminMessage garminMessage, Status status, FitDataStatusCode fitDataStatusCode) {
|
||||
public FitDataStatusMessage(GarminMessage garminMessage, Status status, FitDataStatusCode fitDataStatusCode, boolean sendOutgoing) {
|
||||
this.garminMessage = garminMessage;
|
||||
this.status = status;
|
||||
this.fitDataStatusCode = fitDataStatusCode;
|
||||
switch (fitDataStatusCode) {
|
||||
case APPLIED:
|
||||
LOG.info("FIT DATA RETURNED STATUS: {}", fitDataStatusCode.name());
|
||||
break;
|
||||
default:
|
||||
LOG.warn("FIT DATA RETURNED STATUS: {}", fitDataStatusCode.name());
|
||||
}
|
||||
this.sendOutgoing = sendOutgoing;
|
||||
}
|
||||
|
||||
public static FitDataStatusMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
|
||||
@@ -28,7 +25,25 @@ public class FitDataStatusMessage extends GFDIStatusMessage {
|
||||
LOG.warn("Unknown fit data status code {}", fitDataStatusCodeByte);
|
||||
return null;
|
||||
}
|
||||
return new FitDataStatusMessage(garminMessage, status, fitDataStatusCode);
|
||||
switch (fitDataStatusCode) {
|
||||
case APPLIED:
|
||||
LOG.info("FIT DATA RETURNED STATUS: {}", fitDataStatusCode.name());
|
||||
break;
|
||||
default:
|
||||
LOG.warn("FIT DATA RETURNED STATUS: {}", fitDataStatusCode.name());
|
||||
}
|
||||
return new FitDataStatusMessage(garminMessage, status, fitDataStatusCode, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean generateOutgoing() {
|
||||
final MessageWriter writer = new MessageWriter(response);
|
||||
writer.writeShort(0); // packet size will be filled below
|
||||
writer.writeShort(GarminMessage.RESPONSE.getId());
|
||||
writer.writeShort(garminMessage.getId());
|
||||
writer.writeByte(status.ordinal());
|
||||
writer.writeByte(fitDataStatusCode.ordinal());
|
||||
return sendOutgoing;
|
||||
}
|
||||
|
||||
public enum FitDataStatusCode {
|
||||
|
||||
+24
-9
@@ -2,22 +2,19 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.sta
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
|
||||
|
||||
public class FitDefinitionStatusMessage extends GFDIStatusMessage {
|
||||
|
||||
private final Status status;
|
||||
private final FitDefinitionStatusCode fitDefinitionStatusCode;
|
||||
private final boolean sendOutgoing;
|
||||
|
||||
public FitDefinitionStatusMessage(GarminMessage garminMessage, Status status, FitDefinitionStatusCode fitDefinitionStatusCode) {
|
||||
public FitDefinitionStatusMessage(GarminMessage garminMessage, Status status, FitDefinitionStatusCode fitDefinitionStatusCode, boolean sendOutgoing) {
|
||||
this.garminMessage = garminMessage;
|
||||
this.status = status;
|
||||
this.fitDefinitionStatusCode = fitDefinitionStatusCode;
|
||||
switch (fitDefinitionStatusCode) {
|
||||
case APPLIED:
|
||||
LOG.info("FIT DEFINITION RETURNED STATUS: {}", fitDefinitionStatusCode.name());
|
||||
break;
|
||||
default:
|
||||
LOG.warn("FIT DEFINITION RETURNED STATUS: {}", fitDefinitionStatusCode.name());
|
||||
}
|
||||
this.sendOutgoing = sendOutgoing;
|
||||
}
|
||||
|
||||
public static FitDefinitionStatusMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
|
||||
@@ -28,13 +25,31 @@ public class FitDefinitionStatusMessage extends GFDIStatusMessage {
|
||||
LOG.warn("Unknown fit definition status code {}", fitDefinitionStatusCodeByte);
|
||||
return null;
|
||||
}
|
||||
return new FitDefinitionStatusMessage(garminMessage, status, fitDefinitionStatusCode);
|
||||
switch (fitDefinitionStatusCode) {
|
||||
case APPLIED:
|
||||
LOG.info("FIT DEFINITION RETURNED STATUS: {}", fitDefinitionStatusCode.name());
|
||||
break;
|
||||
default:
|
||||
LOG.warn("FIT DEFINITION RETURNED STATUS: {}", fitDefinitionStatusCode.name());
|
||||
}
|
||||
return new FitDefinitionStatusMessage(garminMessage, status, fitDefinitionStatusCode, false);
|
||||
}
|
||||
|
||||
public FitDefinitionStatusCode getFitDefinitionStatusCode() {
|
||||
return fitDefinitionStatusCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean generateOutgoing() {
|
||||
final MessageWriter writer = new MessageWriter(response);
|
||||
writer.writeShort(0); // packet size will be filled below
|
||||
writer.writeShort(GarminMessage.RESPONSE.getId());
|
||||
writer.writeShort(garminMessage.getId());
|
||||
writer.writeByte(status.ordinal());
|
||||
writer.writeByte(fitDefinitionStatusCode.ordinal());
|
||||
return sendOutgoing;
|
||||
}
|
||||
|
||||
public enum FitDefinitionStatusCode {
|
||||
APPLIED,
|
||||
NOT_UNIQUE,
|
||||
|
||||
+115
@@ -16,6 +16,10 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -31,12 +35,24 @@ import java.lang.reflect.Parameter;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v2.CommunicatorV2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.IncomingFitDefinitionDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitFile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitLocalMessageBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalFITMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||
@@ -47,6 +63,12 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitFileCreator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitFileId;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitLocation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.FitDataMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.FitDefinitionMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.FitDataStatusMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.FitDefinitionStatusMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
@@ -57,6 +79,89 @@ public class GarminSupportTest extends TestBase {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWeatherDataMessage() {
|
||||
byte[] weatherDataMessage = GB.hexStringToByteArray("5901941306003f3672953f3672951a0e1b0200b4000a7a1a2b211bba1707fe6f1d426f6c7a616e6f2c204974616c790009013f367a1018020144037e0a360e3ecac083ff09013f36882017020144037e0a3b0e00000000ff09013f36963015020144037e0a3f0e00000000ff09013f36a44013010015037e0a430d00000000ff09013f36b25012010015037e00470d00000000ff09013f36c06012010015037e004a0d00000000ff09013f36ce7011010017037e004d0d00000000ff09013f36dc8010010017037e00500d00000000ff09013f36ea9010010017037e00510d00000000ff09013f36f8a00f000029037e00530c00000000ff09013f3706b00e000029037e00540c00000000ff09013f3714c00f000029037e00500c00000000ff0a023f3672950e1b0200030a023f3672950e1d0000040a023f3672950f1e0114050a023f36729511200114060a023f367295122100140052E3");
|
||||
// byte[] weatherDataMessage = GB.hexStringToByteArray("59019413060040684550406845500A060A010095640254095200000000000000004D79204C6F636174696F6E000000000901406848000A160087012A62547F3E99999AFF0901406856100A010168025460577F3DCCCCCDFF0901406864200901000504A85E557F00000000FF09014068723009010027025458517F00000000FF09014068804009010015037E53517F00000000FF090140688E5009010161037E4D4E7F00000000FF090140689C6008010162037E334E7F00000000FF09014068AA700701015A012A1A4F7F00000000FF09014068B8800701008D0254004E7F00000000FF09014068C690070100AC037E004D7F00000000FF09014068D4A007010099012A004E7F00000000FF09014068E2B0061600B4012A004F7F00000000FF0A0240684550060A0164040A0240684550060E1064050A02406845500A121061060A02406845500A0D0361000A0240684550090F106401C8DA");
|
||||
// byte[] weatherDataMessage = GB.hexStringToByteArray("590194130600405D7050405D70501007130000C20001920E3000000000000000004D79204C6F636174696F6E000000000901405D75B0100100D30245002D0040933333000901405D83C0120100B40245002A004099999A000901405D91D0120100A0048A002A0040900000000901405D9FE01301009E0649002B00406CCCCD000901405DADF013010096056A002F0040133333000901405DBC001201008703510036003FA00000000901405DCA101001003B048A003B003F000000000901405DD8200D01003B02450041003D4CCCCD000901405DE6300D16005A019200410000000000000901405DF4400C16007501BF00450000000000000901405E02500C16005A045E00450000000000000901405E10600B01007204B700490000000000000A02405D705007130000030A02405D70500814163A040A02405D705008141605050A02405D70500611035E060A02405D7050050C1640006324");
|
||||
FitDataMessage fitDataMessage = (FitDataMessage) GFDIMessage.parseIncoming(weatherDataMessage);
|
||||
|
||||
byte[] weatherDefinitionMessage = GB.hexStringToByteArray("7b00931346000100800f000100fd04860904860101010e01010d01010201000302840501020402840601010701020a04850b0485080f0749000100800b000100fd04860101010201000302840402840501020701020f01011004881101004a0001008007000100fd04860e01010d01010201000501020c01003ae3");
|
||||
FitDefinitionMessage fitDefinitionMessage = (FitDefinitionMessage) GFDIMessage.parseIncoming(weatherDefinitionMessage);
|
||||
|
||||
List<RecordData> recordDataList = fitDataMessage.applyDefinitions(fitDefinitionMessage.getRecordDefinitions());
|
||||
|
||||
List<RecordData> weatherData = new ArrayList<>();
|
||||
|
||||
for (RecordData rec : recordDataList) {
|
||||
|
||||
|
||||
RecordData out = new RecordData(rec.getRecordDefinition(), rec.getRecordDefinition().getRecordHeader());
|
||||
|
||||
for (int fieldNumber :
|
||||
rec.getFieldsNumbers()) {
|
||||
Object o = rec.getFieldByNumber(fieldNumber);
|
||||
out.setFieldByNumber(fieldNumber, o);
|
||||
Assert.assertEquals(o, out.getFieldByNumber(fieldNumber));
|
||||
}
|
||||
weatherData.add(out);
|
||||
System.out.println(rec.toString());
|
||||
System.out.println(out.toString());
|
||||
|
||||
Assert.assertEquals(rec.toString(), out.toString());
|
||||
|
||||
}
|
||||
|
||||
FitDataMessage recostructed = new FitDataMessage(weatherData);
|
||||
|
||||
byte[] output = recostructed.getOutgoingMessage();
|
||||
|
||||
Assert.assertArrayEquals(weatherDataMessage, output);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incomingFitDefinition() {
|
||||
GarminSupport support = createSupport();
|
||||
|
||||
final CobsCoDec cobsCoDec = new CobsCoDec();
|
||||
cobsCoDec.receivedBytes(GB.hexStringToByteArray("00021703AA13082088236905011303021F03033F0507045BC600"));//notificationcontrol message from log
|
||||
final byte[] deCobs = cobsCoDec.retrieveMessage();
|
||||
|
||||
GFDIMessage pippo = GFDIMessage.parseIncoming(GB.hexStringToByteArray("1700AA13002088236905011300021F00033F0007045BC6"));//notificationcontrol message from log
|
||||
|
||||
final byte[] incomingDefinition = GB.hexStringToByteArray("5700931340000000000603048C0404860102840202840502840001004100000C000404028400010001010005010042000001000317048C1A048C01010A4300007F00060318070101000401000501000701000A010054CE");
|
||||
FitDefinitionMessage fitDefinitionMessage = (FitDefinitionMessage) GFDIMessage.parseIncoming(incomingDefinition);
|
||||
FitDefinitionStatusMessage ok_applied = new FitDefinitionStatusMessage(GFDIMessage.GarminMessage.FIT_DEFINITION, GFDIMessage.Status.ACK, FitDefinitionStatusMessage.FitDefinitionStatusCode.APPLIED, true);
|
||||
|
||||
Assert.assertArrayEquals(ok_applied.getOutgoingMessage(), fitDefinitionMessage.getAckBytestream());
|
||||
|
||||
final List<GBDeviceEvent> events = fitDefinitionMessage.getGBDeviceEvent();
|
||||
Assert.assertEquals(1, events.size());
|
||||
Assert.assertTrue((events.getFirst() instanceof IncomingFitDefinitionDeviceEvent));
|
||||
|
||||
final FitLocalMessageHandler fitLocalMessageHandler = new FitLocalMessageHandler(support, ((IncomingFitDefinitionDeviceEvent) events.getFirst()).getRecordDefinitions());
|
||||
|
||||
final byte[] incomingData = GB.hexStringToByteArray("2E00941302CA960015070000000703466F726572756E6E65722033350000000000000000000000000101010197F3");
|
||||
FitDataMessage fitDataMessage = (FitDataMessage) GFDIMessage.parseIncoming(incomingData);
|
||||
|
||||
fitLocalMessageHandler.handle(fitDataMessage);
|
||||
|
||||
FitDataStatusMessage ok_applied_data = new FitDataStatusMessage(GFDIMessage.GarminMessage.FIT_DATA, GFDIMessage.Status.ACK, FitDataStatusMessage.FitDataStatusCode.APPLIED, true);
|
||||
Assert.assertArrayEquals(ok_applied_data.getOutgoingMessage(), fitDataMessage.getAckBytestream());
|
||||
|
||||
//RecordData{CAPABILITIES, sports=7, connectivity_supported=352360138, unknown_26(UINT32Z/4)=7}
|
||||
//RecordData{CONNECTIVITY, bluetooth_le_enabled=0, name=Forerunner 35, live_tracking_enabled=1, weather_conditions_enabled=1, auto_activity_upload_enabled=1, gps_ephemeris_download_enabled=1}
|
||||
|
||||
//forse va in conflitto con ``private void enableWeather() {``???
|
||||
|
||||
|
||||
|
||||
// final FitLocalMessageBuilder pippo = new FitLocalMessageBuilder(((IncomingFitDefinitionDeviceEvent) events.getFirst()).getRecordDefinitions());
|
||||
// pippo.getDefinitions();
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testBaseFields() {
|
||||
|
||||
@@ -538,4 +643,14 @@ public class GarminSupportTest extends TestBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private GarminSupport createSupport() {
|
||||
return new GarminSupport() {
|
||||
@Override
|
||||
public BluetoothGattCharacteristic getCharacteristic(final UUID uuid) {
|
||||
return new BluetoothGattCharacteristic(null, 0, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
-1
@@ -23,7 +23,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.weather.WeatherMapper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitLocalMessageBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.PredefinedLocalMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherAqi;
|
||||
|
||||
Reference in New Issue
Block a user