diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/FitLocalMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/FitLocalMessageHandler.java index 023a8d33fd..4df55b52e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/FitLocalMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/FitLocalMessageHandler.java @@ -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 recordDefinitionList; + private List 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 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; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java index c9cb40379b..bdd6219824 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java @@ -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) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/deviceevents/IncomingFitDefinitionDeviceEvent.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/deviceevents/IncomingFitDefinitionDeviceEvent.java new file mode 100644 index 0000000000..8a83273e7d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/deviceevents/IncomingFitDefinitionDeviceEvent.java @@ -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 getRecordDefinitions() { + return recordDefinitions; + } + + private final List recordDefinitions; + + public IncomingFitDefinitionDeviceEvent(List recordDefinitions) { + this.recordDefinitions = recordDefinitions; + } + + @Override + public void evaluate(final Context context, final GBDevice device) { + // Handled in support class + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java index 925c949e50..187417535c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java @@ -14,13 +14,12 @@ public class FitDataMessage extends GFDIMessage { private List 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 recordDefinitions) { + public List applyDefinitions(List recordDefinitions) { + final List 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 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()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDefinitionMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDefinitionMessage.java index 4a48d9535e..402c547f20 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDefinitionMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDefinitionMessage.java @@ -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 recordDefinitions; + private final boolean generateOutgoing; - public FitDefinitionMessage(List recordDefinitions, GarminMessage garminMessage) { + public FitDefinitionMessage(List 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 recordDefinitions) { + public FitDefinitionMessage(List 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 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; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDataStatusMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDataStatusMessage.java index f01566222c..6b324ea54b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDataStatusMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDataStatusMessage.java @@ -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 { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDefinitionStatusMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDefinitionStatusMessage.java index 927a7c3681..413143e874 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDefinitionStatusMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/status/FitDefinitionStatusMessage.java @@ -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, diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupportTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupportTest.java index e46dfd6dfa..09df669db5 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupportTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupportTest.java @@ -16,6 +16,10 @@ along with this program. If not, see . */ 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 recordDataList = fitDataMessage.applyDefinitions(fitDefinitionMessage.getRecordDefinitions()); + + List 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 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); + } + }; + } } \ No newline at end of file diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitWeatherTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitWeatherTest.java index a331bd7565..b93acb6fea 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitWeatherTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/messages/FitWeatherTest.java @@ -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;