From a87ddbb66a29e814b3e5abc9d083ad8a7d83322d Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 30 Dec 2025 15:21:33 +0100 Subject: [PATCH] Garmin: add support for devices not sending ConfigurationMessage Infer capabilities from the incoming FitData message (as test/workaround, we're not sure if the field we're using is the correct one). Use the CapabilitiesDeviceEvent instead of the hardcoded ConfigurationMessage instanceof check to call completeInitialization() in GarminSupport. Also centralize the generation of the capabilities-related DeviceEvent in GarminCapability as they are used both in ConfigurationMessage as in FitLocalMessageHandler now. --- .../devices/vivomovehr/GarminCapability.java | 34 +++++++++++++++++++ .../garmin/FitLocalMessageHandler.java | 23 ++++++++++++- .../service/devices/garmin/GarminSupport.java | 16 +++++---- .../garmin/messages/ConfigurationMessage.java | 9 +---- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/GarminCapability.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/GarminCapability.java index 93a7f419bd..06d258fab1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/GarminCapability.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/GarminCapability.java @@ -16,11 +16,18 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences; +import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminPreferences; +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.deviceevents.CapabilitiesDeviceEvent; + public enum GarminCapability { CONNECT_MOBILE_FIT_LINK, GOLF_FIT_LINK, @@ -187,6 +194,22 @@ public enum GarminCapability { return result; } + public static Set setFromLong(final long capabilitiesMask) { + final Set result = + new HashSet<>(GarminCapability.values().length); + + for (int i = 0; i < Long.SIZE; i++) { + if ((capabilitiesMask & (1L << i)) != 0) { + GarminCapability cap = FROM_ORDINAL.get(i); + if (cap != null) { + result.add(cap); + } + } + } + return result; + } + + public static byte[] setToBinary(final Set capabilities) { final GarminCapability[] values = values(); final byte[] result = new byte[(values.length + 7) / 8]; @@ -213,4 +236,15 @@ public enum GarminCapability { } return result.toString(); } + + public static List getGBDeviceEvent(final Set capabilities) { + final Set capabilitiesPref = new HashSet<>(); + for (final GarminCapability capability : capabilities) { + capabilitiesPref.add(capability.name()); + } + return Arrays.asList( + new CapabilitiesDeviceEvent(capabilities), + new GBDeviceEventUpdatePreferences(GarminPreferences.PREF_GARMIN_CAPABILITIES, capabilitiesPref) + ); + } } 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 7a60ff2f52..248ac391ca 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 @@ -6,7 +6,10 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.GarminCapability; 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; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.FitDataMessage; @@ -44,14 +47,32 @@ public class FitLocalMessageHandler implements MessageHandler{ } private void parseIncomingFitDataMessage(FitDataMessage incoming) { + final List deviceEventList = new ArrayList<>(); recordDataList = (incoming).applyDefinitions(recordDefinitionList); for(RecordData d: recordDataList){ LOG.info("Incoming FitDataMessage: {}", d); + List processed = processRecordData(d); + if(processed!=null) { + deviceEventList.addAll(processed); + } + } + LOG.info("Some incoming FitDataMessages are not processed any further, just logged."); + for (final GBDeviceEvent event : deviceEventList) { + deviceSupport.evaluateGBDeviceEvent(event); } - LOG.info("Incoming FitDataMessages are not processed any further, just logged."); unregisterSelf(); } + private List processRecordData(RecordData d) { + if (d.getRecordDefinition().getGlobalFITMessage() == GlobalFITMessage.CAPABILITIES) { + //TODO: we are not sure this is correct! + return GarminCapability.getGBDeviceEvent( + GarminCapability.setFromLong((Long) d.getFieldByName("connectivity_supported")) + ); + } + return null; + } + private void unregisterSelf() { deviceSupport.unregisterHandler(this); } 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 60e44dfab2..c4ba0ab4fd 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 @@ -312,19 +312,15 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC } } - final List events = parsedMessage.getGBDeviceEvent(); - for (final GBDeviceEvent event : events) { - evaluateGBDeviceEvent(event); - } - sendAck("send status", parsedMessage); //send status message sendOutgoingMessage("send reply", parsedMessage); //send reply if any sendOutgoingMessage("send followup", followup); //send followup message if any - if (parsedMessage instanceof GenericStatusMessage && ((GenericStatusMessage) parsedMessage).getGarminMessage() == GFDIMessage.GarminMessage.CONFIGURATION) { //the last forced message exchange - completeInitialization(); + final List events = parsedMessage.getGBDeviceEvent(); + for (final GBDeviceEvent event : events) { + evaluateGBDeviceEvent(event); } processDownloadQueue(); @@ -358,6 +354,7 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC } } else if (deviceEvent instanceof CapabilitiesDeviceEvent) { final Set capabilities = ((CapabilitiesDeviceEvent) deviceEvent).capabilities; + completeInitialization(); if (capabilities.contains(GarminCapability.REALTIME_SETTINGS)) { final String language = Locale.getDefault().getLanguage(); final String country = Locale.getDefault().getCountry(); @@ -722,6 +719,11 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC } private void completeInitialization() { + if (gbDevice.getState() == GBDevice.State.INITIALIZED) { + LOG.error("completeInitialization() was called, but the device is already initialized. This should never happen! Please report this to the project."); + LOG.warn("preventing double initialization"); + return; + } sendOutgoingMessage("request supported file types", new SupportedFileTypesMessage()); sendDeviceSettings(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/ConfigurationMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/ConfigurationMessage.java index c936da09d9..e996c6b1a3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/ConfigurationMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/ConfigurationMessage.java @@ -35,14 +35,7 @@ public class ConfigurationMessage extends GFDIMessage { @Override public List getGBDeviceEvent() { - final Set capabilitiesPref = new HashSet<>(); - for (final GarminCapability capability : capabilities) { - capabilitiesPref.add(capability.name()); - } - return Arrays.asList( - new CapabilitiesDeviceEvent(capabilities), - new GBDeviceEventUpdatePreferences(GarminPreferences.PREF_GARMIN_CAPABILITIES, capabilitiesPref) - ); + return GarminCapability.getGBDeviceEvent(capabilities); } @Override