Garmin: Rename LocalMessage to PredefinedLocalMessage and clarify its usage

PredefinedLocalMessage are only useful for FIT messages and should not interfere with FIT files. The only impact of using the local message in fit files was in the textual output, but it was confusing.

Add an explicit constructor to RecordHeader if PredefinedLocalMessage should be taken into account, and use this only in fit messages leaving the default constructor for fit files.

Also adjusts the test case as textual output comparison needs to be fixed.
This commit is contained in:
Daniele Gobbetti 2024-04-23 16:39:59 +02:00 committed by José Rebelo
parent 53e8b811f9
commit c44258310e
8 changed files with 53 additions and 49 deletions

View File

@ -45,7 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.
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;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.LocalMessage;
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.messages.ConfigurationMessage;
@ -276,14 +276,14 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
List<RecordData> weatherData = new ArrayList<>();
List<RecordDefinition> weatherDefinitions = new ArrayList<>(3);
weatherDefinitions.add(LocalMessage.TODAY_WEATHER_CONDITIONS.getRecordDefinition());
weatherDefinitions.add(LocalMessage.HOURLY_WEATHER_FORECAST.getRecordDefinition());
weatherDefinitions.add(LocalMessage.DAILY_WEATHER_FORECAST.getRecordDefinition());
weatherDefinitions.add(PredefinedLocalMessage.TODAY_WEATHER_CONDITIONS.getRecordDefinition());
weatherDefinitions.add(PredefinedLocalMessage.HOURLY_WEATHER_FORECAST.getRecordDefinition());
weatherDefinitions.add(PredefinedLocalMessage.DAILY_WEATHER_FORECAST.getRecordDefinition());
sendOutgoingMessage(new nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.FitDefinitionMessage(weatherDefinitions));
try {
RecordData today = new RecordData(LocalMessage.TODAY_WEATHER_CONDITIONS.getRecordDefinition());
RecordData today = new RecordData(PredefinedLocalMessage.TODAY_WEATHER_CONDITIONS.getRecordDefinition());
today.setFieldByName("weather_report", 0); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
today.setFieldByName("timestamp", weather.timestamp);
today.setFieldByName("observed_at_time", weather.timestamp);
@ -304,7 +304,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
for (int hour = 0; hour <= 11; hour++) {
if (hour < weather.hourly.size()) {
WeatherSpec.Hourly hourly = weather.hourly.get(hour);
RecordData weatherHourlyForecast = new RecordData(LocalMessage.HOURLY_WEATHER_FORECAST.getRecordDefinition());
RecordData weatherHourlyForecast = new RecordData(PredefinedLocalMessage.HOURLY_WEATHER_FORECAST.getRecordDefinition());
weatherHourlyForecast.setFieldByName("weather_report", 1); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp);
weatherHourlyForecast.setFieldByName("temperature", hourly.temp);
@ -320,7 +320,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
}
}
//
RecordData todayDailyForecast = new RecordData(LocalMessage.DAILY_WEATHER_FORECAST.getRecordDefinition());
RecordData todayDailyForecast = new RecordData(PredefinedLocalMessage.DAILY_WEATHER_FORECAST.getRecordDefinition());
todayDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
todayDailyForecast.setFieldByName("timestamp", weather.timestamp);
todayDailyForecast.setFieldByName("low_temperature", weather.todayMinTemp);
@ -335,7 +335,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
if (day < weather.forecasts.size()) {
WeatherSpec.Daily daily = weather.forecasts.get(day);
int ts = weather.timestamp + (day + 1) * 24 * 60 * 60; //TODO: is this needed?
RecordData weatherDailyForecast = new RecordData(LocalMessage.DAILY_WEATHER_FORECAST.getRecordDefinition());
RecordData weatherDailyForecast = new RecordData(PredefinedLocalMessage.DAILY_WEATHER_FORECAST.getRecordDefinition());
weatherDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
weatherDailyForecast.setFieldByName("timestamp", weather.timestamp);
weatherDailyForecast.setFieldByName("low_temperature", daily.minTemp);

View File

@ -5,7 +5,7 @@ import androidx.annotation.Nullable;
import java.nio.ByteOrder;
import java.util.List;
public enum LocalMessage {
public enum PredefinedLocalMessage {
TODAY_WEATHER_CONDITIONS(6, GlobalFITMessage.WEATHER,
new int[]{0, 253, 9, 1, 14, 13, 2, 3, 5, 4, 6, 7, 10, 11, 8}
),
@ -20,17 +20,17 @@ public enum LocalMessage {
private final GlobalFITMessage globalFITMessage;
private final int[] globalDefinitionIds;
LocalMessage(int type, GlobalFITMessage globalFITMessage, int[] globalDefinitionIds) {
PredefinedLocalMessage(int type, GlobalFITMessage globalFITMessage, int[] globalDefinitionIds) {
this.type = type;
this.globalFITMessage = globalFITMessage;
this.globalDefinitionIds = globalDefinitionIds;
}
@Nullable
public static LocalMessage fromType(int type) {
for (final LocalMessage localMessage : LocalMessage.values()) {
if (localMessage.getType() == type) {
return localMessage;
public static PredefinedLocalMessage fromType(int type) {
for (final PredefinedLocalMessage predefinedLocalMessage : PredefinedLocalMessage.values()) {
if (predefinedLocalMessage.getType() == type) {
return predefinedLocalMessage;
}
}
return null;

View File

@ -180,8 +180,8 @@ public class RecordData {
return oBuilder.toString();
}
public LocalMessage getLocalMessage() {
return recordHeader.getLocalMessage();
public PredefinedLocalMessage getPredefinedLocalMessage() {
return recordHeader.getPredefinedLocalMessage();
}
private class FieldData {

View File

@ -14,26 +14,22 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.Mess
public class RecordDefinition {
private final RecordHeader recordHeader;
private final GlobalFITMessage globalFITMessage;
private final LocalMessage localMessage;
private final PredefinedLocalMessage predefinedLocalMessage;
private final java.nio.ByteOrder byteOrder;
private List<FieldDefinition> fieldDefinitions;
private List<DevFieldDefinition> devFieldDefinitions;
public RecordDefinition(RecordHeader recordHeader, ByteOrder byteOrder, LocalMessage localMessage, GlobalFITMessage globalFITMessage, List<FieldDefinition> fieldDefinitions, List<DevFieldDefinition> devFieldDefinitions) {
public RecordDefinition(RecordHeader recordHeader, ByteOrder byteOrder, PredefinedLocalMessage predefinedLocalMessage, GlobalFITMessage globalFITMessage, List<FieldDefinition> fieldDefinitions, List<DevFieldDefinition> devFieldDefinitions) {
this.recordHeader = recordHeader;
this.byteOrder = byteOrder;
this.localMessage = localMessage;
this.predefinedLocalMessage = predefinedLocalMessage;
this.globalFITMessage = globalFITMessage;
this.fieldDefinitions = fieldDefinitions;
this.devFieldDefinitions = devFieldDefinitions;
}
public RecordDefinition(ByteOrder byteOrder, LocalMessage localMessage, List<FieldDefinition> fieldDefinitions) {
this(new RecordHeader(true, false, localMessage, null), byteOrder, localMessage, localMessage.getGlobalFITMessage(), fieldDefinitions, null);
}
public RecordDefinition(ByteOrder byteOrder, LocalMessage localMessage) {
this(new RecordHeader(true, false, localMessage, null), byteOrder, localMessage, localMessage.getGlobalFITMessage(), localMessage.getLocalFieldDefinitions(), null);
public RecordDefinition(ByteOrder byteOrder, PredefinedLocalMessage predefinedLocalMessage) {
this(new RecordHeader(true, false, predefinedLocalMessage, null), byteOrder, predefinedLocalMessage, predefinedLocalMessage.getGlobalFITMessage(), predefinedLocalMessage.getLocalFieldDefinitions(), null);
}
public RecordDefinition(ByteOrder byteOrder, RecordHeader recordHeader, GlobalFITMessage globalFITMessage, List<FieldDefinition> fieldDefinitions) {
@ -118,7 +114,7 @@ public class RecordDefinition {
}
public String getName() {
return localMessage != null ? localMessage.name() : "unknown_" + globalFITMessage;
return predefinedLocalMessage != null ? predefinedLocalMessage.name() : "unknown_" + globalFITMessage;
}
@NonNull

View File

@ -7,21 +7,25 @@ import androidx.annotation.Nullable;
public class RecordHeader {
private final boolean definition;
private final boolean developerData;
private final LocalMessage localMessage;
private final PredefinedLocalMessage predefinedLocalMessage;
private final int rawLocalMessageType;
private final Integer timeOffset;
private long referenceTimestamp;
public RecordHeader(boolean definition, boolean developerData, LocalMessage localMessage, Integer timeOffset) {
public RecordHeader(boolean definition, boolean developerData, PredefinedLocalMessage predefinedLocalMessage, Integer timeOffset) {
this.definition = definition;
this.developerData = developerData;
this.localMessage = localMessage;
this.rawLocalMessageType = localMessage.getType();
this.predefinedLocalMessage = predefinedLocalMessage;
this.rawLocalMessageType = predefinedLocalMessage.getType();
this.timeOffset = timeOffset;
}
//see https://github.com/polyvertex/fitdecode/blob/master/fitdecode/reader.py#L512
public RecordHeader(byte header) {
this(header, false);
}
public RecordHeader(byte header, boolean inferLocalMessage) {
if ((header & 0x80) == 0x80) { //compressed timestamp TODO add support
definition = false;
developerData = false;
@ -33,9 +37,13 @@ public class RecordHeader {
rawLocalMessageType = header & 0xf;
timeOffset = null;
}
localMessage = LocalMessage.fromType(rawLocalMessageType);
if (inferLocalMessage)
predefinedLocalMessage = PredefinedLocalMessage.fromType(rawLocalMessageType);
else
predefinedLocalMessage = null;
}
public void setReferenceTimestamp(long referenceTimestamp) {
this.referenceTimestamp = referenceTimestamp;
}
@ -61,14 +69,14 @@ public class RecordHeader {
}
@Nullable
public LocalMessage getLocalMessage() {
return localMessage;
public PredefinedLocalMessage getPredefinedLocalMessage() {
return predefinedLocalMessage;
}
public byte generateOutgoingDefinitionPayload() {
if (!definition && !developerData)
return (byte) (timeOffset | (((byte) localMessage.getType()) << 5));
byte base = (byte) (null == localMessage ? rawLocalMessageType : localMessage.getType());
return (byte) (timeOffset | (((byte) predefinedLocalMessage.getType()) << 5));
byte base = (byte) (null == predefinedLocalMessage ? rawLocalMessageType : predefinedLocalMessage.getType());
if (definition)
base = (byte) (base | 0x40);
if (developerData)
@ -79,8 +87,8 @@ public class RecordHeader {
public byte generateOutgoingDataPayload() { //TODO: unclear if correct
if (!definition && !developerData)
return (byte) (timeOffset | (((byte) localMessage.getType()) << 5));
byte base = (byte) (null == localMessage ? rawLocalMessageType : localMessage.getType());
return (byte) (timeOffset | (((byte) predefinedLocalMessage.getType()) << 5));
byte base = (byte) (null == predefinedLocalMessage ? rawLocalMessageType : predefinedLocalMessage.getType());
if (developerData)
base = (byte) (base | 0x20);
@ -90,7 +98,7 @@ public class RecordHeader {
@NonNull
@Override
public String toString() {
return "Local Message: " + (null == localMessage ? "raw: " + rawLocalMessageType : "type: " + localMessage.name());
return "Local Message: " + (null == predefinedLocalMessage ? "raw: " + rawLocalMessageType : "type: " + predefinedLocalMessage.name());
}
@Override
@ -101,12 +109,12 @@ public class RecordHeader {
RecordHeader that = (RecordHeader) o;
if (rawLocalMessageType != that.rawLocalMessageType) return false;
return localMessage == that.localMessage;
return predefinedLocalMessage == that.predefinedLocalMessage;
}
@Override
public int hashCode() {
int result = (localMessage != null ? localMessage.hashCode() : 0);
int result = (predefinedLocalMessage != null ? predefinedLocalMessage.hashCode() : 0);
result = 31 * result + rawLocalMessageType;
return result;
}

View File

@ -3,7 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.LocalMessage;
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.RecordHeader;
@ -24,16 +24,16 @@ public class FitDataMessage extends GFDIMessage {
final List<RecordData> recordDataList = new ArrayList<>();
while (reader.remaining() > 0) {
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte());
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte(), true);
if (recordHeader.isDefinition())
return null;
LocalMessage localMessage = recordHeader.getLocalMessage();
if (localMessage == null) {
PredefinedLocalMessage predefinedLocalMessage = recordHeader.getPredefinedLocalMessage();
if (predefinedLocalMessage == null) {
LOG.warn("Local message is null");
return null;
}
RecordData recordData = new RecordData(localMessage.getRecordDefinition());
RecordData recordData = new RecordData(predefinedLocalMessage.getRecordDefinition());
recordData.parseDataMessage(reader);
recordDataList.add(recordData);
}

View File

@ -25,7 +25,7 @@ public class FitDefinitionMessage extends GFDIMessage {
List<RecordDefinition> recordDefinitions = new ArrayList<>();
while (reader.remaining() > 0) {
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte());
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte(), true);
recordDefinitions.add(RecordDefinition.parseIncoming(reader, recordHeader));
}

View File

@ -434,19 +434,19 @@ public class GarminSupportTest {
"unknown_254(UINT16/2): 0 unknown_1(UINT16Z/2): 50008 unknown_0(UINT8/1): 1 unknown_3(UINT8Z/1): 1 ], \n" +
"Local Message: raw: 5 Global Message Number: UNK_6=[\n" +
"unknown_0(STRING/4): EVO unknown_3(UINT32/4): 45719172 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 0 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): 47617 unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 80 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 1 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 1 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): 5 unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 ], \n" +
"Local Message: type: TODAY_WEATHER_CONDITIONS Global Message Number: UNK_6=[\n" +
"Local Message: raw: 6 Global Message Number: UNK_6=[\n" +
"unknown_0(STRING/5): P2SL unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 1 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): 28209 unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 90 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 1 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 1 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): 5 unknown_35(UINT8/3): [0,118,190] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 ], \n" +
"Local Message: raw: 7 Global Message Number: UNK_6=[\n" +
"unknown_0(STRING/9): LANGSTER unknown_3(UINT32/4): 1231891 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 2 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): 10851 unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 1 unknown_19(UINT8/1): 0 unknown_20(UINT8/1): 1 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): 5 unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 ], \n" +
"Local Message: raw: 8 Global Message Number: UNK_6=[\n" +
"unknown_0(STRING/2): M unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [53,39,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 3 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): 31337 unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 1 unknown_19(UINT8/1): 0 unknown_20(UINT8/1): 1 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): 5 unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 ], \n" +
"Local Message: type: HOURLY_WEATHER_FORECAST Global Message Number: UNK_6=[\n" +
"Local Message: raw: 9 Global Message Number: UNK_6=[\n" +
"unknown_0(STRING/7): Bike 5 unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 4 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): null unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 0 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 0 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): null unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 , \n" +
"unknown_0(STRING/7): Bike 6 unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 5 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): null unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 0 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 0 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): null unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 , \n" +
"unknown_0(STRING/7): Bike 7 unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 6 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): null unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 0 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 0 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): null unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 , \n" +
"unknown_0(STRING/7): Bike 8 unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 7 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): null unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 0 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 0 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): null unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 , \n" +
"unknown_0(STRING/7): Bike 9 unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 8 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): null unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 0 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 0 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): null unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 ], \n" +
"Local Message: type: DAILY_WEATHER_FORECAST Global Message Number: UNK_6=[\n" +
"Local Message: raw: 10 Global Message Number: UNK_6=[\n" +
"unknown_0(STRING/8): Bike 10 unknown_3(UINT32/4): 0 unknown_39(UINT8Z/4): [39,53,,] unknown_41(UINT8Z/12): [23,21,19,18,17,16,15,14,13,12,11,] unknown_254(UINT16/2): 9 unknown_4(UINT16Z/2): null unknown_5(UINT16Z/2): null unknown_6(UINT16Z/2): null unknown_7(UINT16Z/2): null unknown_8(UINT16/2): 2096 unknown_9(UINT16/2): 0 unknown_10(UINT16/2): 95 unknown_11(UINT16/2): 500 unknown_42(UINT16Z/2): null unknown_1(ENUM/1): null unknown_2(ENUM/1): null unknown_12(UINT8/1): 1 unknown_13(UINT8/1): 1 unknown_14(UINT8/1): 0 unknown_15(UINT8/1): 0 unknown_16(UINT8/1): 0 unknown_17(UINT8/1): 0 unknown_18(UINT8/1): 0 unknown_19(UINT8/1): 254 unknown_20(UINT8/1): 0 unknown_21(UINT8Z/1): null unknown_22(UINT8Z/1): null unknown_23(UINT8Z/1): null unknown_24(UINT8Z/1): null unknown_35(UINT8/3): [0,50,] unknown_36(ENUM/1): 4 unknown_37(UINT8/1): null unknown_38(UINT8Z/1): 2 unknown_40(UINT8Z/1): 11 unknown_43(UINT8Z/1): null unknown_44(ENUM/1): 0 ], \n" +
"Local Message: raw: 11 Global Message Number: CONNECTIVITY=[\n" +
"name(STRING/9): Edge 510 bluetooth_enabled(ENUM/1): 0 live_tracking_enabled(ENUM/1): null weather_conditions_enabled(ENUM/1): null weather_alerts_enabled(ENUM/1): null auto_activity_upload_enabled(ENUM/1): null course_download_enabled(ENUM/1): null workout_download_enabled(ENUM/1): null gps_ephemeris_download_enabled(ENUM/1): null ]}";