diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
index 733434b4fd..96e9073bd0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
@@ -575,6 +575,11 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_SONY_ADAPTIVE_VOLUME_CONTROL = "pref_adaptive_volume_control";
public static final String PREF_SONY_WIDE_AREA_TAP = "pref_wide_area_tap";
+ public static final String PREF_GYMLINK_ENABLED = "gymlink_enabled";
+ public static final String PREF_ANTPLUS_ENABLED = "antplus_enabled";
+ public static final String PREF_HR_BROADCAST = "hr_broadcast";
+ public static final String PREF_DUAL_CONNECTION = "dual_connection";
+
public static final String PREF_MEDIA_SOURCE = "pref_media_source";
public static final String PREF_MEDIA_PLAYBACK_MODE = "pref_media_playback_mode";
public static final String PREF_SHOKZ_EQUALIZER_BLUETOOTH = "pref_shokz_equalizer_bluetooth";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
index 4073e56266..2ac30b8959 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
@@ -857,6 +857,11 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
addPreferenceHandlerFor(PREF_SONY_ADAPTIVE_VOLUME_CONTROL);
addPreferenceHandlerFor(PREF_SONY_WIDE_AREA_TAP);
+ addPreferenceHandlerFor(PREF_GYMLINK_ENABLED);
+ addPreferenceHandlerFor(PREF_ANTPLUS_ENABLED);
+ addPreferenceHandlerFor(PREF_HR_BROADCAST);
+ addPreferenceHandlerFor(PREF_DUAL_CONNECTION);
+
addPreferenceHandlerFor(PREF_MEDIA_SOURCE);
addPreferenceHandlerFor(PREF_MEDIA_PLAYBACK_MODE);
addPreferenceHandlerFor(PREF_SHOKZ_EQUALIZER_BLUETOOTH);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/polar/PolarH10DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/polar/PolarH10DeviceCoordinator.java
index c364a5679c..057b070dbb 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/polar/PolarH10DeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/polar/PolarH10DeviceCoordinator.java
@@ -3,6 +3,8 @@ package nodomain.freeyourgadget.gadgetbridge.devices.polar;
import java.util.regex.Pattern;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public class PolarH10DeviceCoordinator extends AbstractPolarDeviceCoordinator {
@Override
@@ -14,4 +16,11 @@ public class PolarH10DeviceCoordinator extends AbstractPolarDeviceCoordinator {
protected Pattern getSupportedDeviceName() {
return Pattern.compile("^Polar H10.*");
}
+
+ @Override
+ public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
+ final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
+ deviceSpecificSettings.addRootScreen(R.xml.devicesettings_polar);
+ return deviceSpecificSettings;
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java
index dec98a3b40..89e967a6a6 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java
@@ -597,6 +597,7 @@ public class BleNamesResolver {
mServices.put("000056ff-0000-1000-8000-00805f9b34fb", "(Propr: GloryFit Data");
mServices.put("af9df7a1-e595-11e3-96b4-0002a5d5c51b", "(Propr: OneTouch");
mServices.put("9b012401-bc30-ce9a-e111-0f67e491abde", "(Propr: Garmin GFDI V0)");
+ mServices.put("6217ff4b-fb31-1140-ad5a-a45545d7ecf3", "(Propr: Polar Settings)");
mServices.put("6a4e2401-667b-11e3-949a-0800200c9a66", "(Propr: Garmin GFDI V1)");
mServices.put("6a4e2800-667b-11e3-949a-0800200c9a66", "(Propr: Garmin ML)");
mServices.put("86f61000-f706-58a0-95b2-1fb9261e4dc7", "(Propr: Ultrahuman State)");
@@ -1194,6 +1195,7 @@ public class BleNamesResolver {
mCharacteristics.put("86f65002-f706-58a0-95b2-1fb9261e4dc7", "(Propr: Ultrahuman Response)");
mCharacteristics.put("86f66001-f706-58a0-95b2-1fb9261e4dc7", "(Propr: Ultrahuman Data)");
mCharacteristics.put("da2e7828-fbce-4e01-ae9e-261174997c48", "(Propr: SMP - Simple Management Protocol)");
+ mCharacteristics.put("6217ff4d-91bb-91d0-7e2a-7cd3bda8a1f3", "(Propr: Polar Settings)");
mCharacteristics.put("6e400002-b5a3-f393-e0a9-e50e24dcca9e", "(Propr: Nordic UART TX)");
mCharacteristics.put("6e400003-b5a3-f393-e0a9-e50e24dcca9e", "(Propr: Nordic UART RX)");
mCharacteristics.put("de5bf729-d711-4e47-af26-65e3012a5dc7", "(Propr: Yawell Notify)");
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/polar/PolarH10DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/polar/PolarH10DeviceSupport.java
index 8de05742d2..4dec42c187 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/polar/PolarH10DeviceSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/polar/PolarH10DeviceSupport.java
@@ -1,5 +1,10 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.polar;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ANTPLUS_ENABLED;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DUAL_CONNECTION;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GYMLINK_ENABLED;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HR_BROADCAST;
+
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
@@ -18,6 +23,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
+import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.HeartRrIntervalSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.polar.PolarH10ActivitySampleProvider;
@@ -50,12 +56,26 @@ public class PolarH10DeviceSupport extends AbstractBTLESingleDeviceSupport {
public static final UUID UUID_SERVICE_HEART_RATE = GattService.UUID_SERVICE_HEART_RATE;
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT = GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT;
+ public static final UUID UUID_SERVICE_POLAR_SETTINGS = UUID.fromString("6217ff4b-fb31-1140-ad5a-a45545d7ecf3");
+ public static final UUID UUID_CHARACTERISTIC_POLAR_SETTINGS = UUID.fromString("6217ff4d-91bb-91d0-7e2a-7cd3bda8a1f3");
+
+ public static final byte CMD_HR_BROADCAST_SET = (byte) 0x01;
+ public static final byte CMD_HR_BROADCAST_GET = (byte) 0x02;
+ public static final byte CMD_GYM_LINK_SET = (byte) 0x03;
+ public static final byte CMD_GYM_LINK_GET = (byte) 0x04;
+ public static final byte CMD_DUAL_CONNECTION_SET = (byte) 0x08;
+ public static final byte CMD_DUAL_CONNECTION_GET = (byte) 0x09;
+ public static final byte CMD_ANT_SET = (byte) 0x0a;
+ public static final byte CMD_ANT_GET = (byte) 0x0b;
+ public static final byte CMD_RESPONSE = (byte) 0xf0;
+
public PolarH10DeviceSupport() {
super(LOG);
addSupportedService(UUID_SERVICE_DEVICE_INFORMATION);
addSupportedService(UUID_SERVICE_BATTERY_SERVICE);
addSupportedService(UUID_SERVICE_HEART_RATE);
+ addSupportedService(UUID_SERVICE_POLAR_SETTINGS);
IntentListener mListener = intent -> {
String action = intent.getAction();
@@ -101,6 +121,12 @@ public class PolarH10DeviceSupport extends AbstractBTLESingleDeviceSupport {
heartRateProfile.enableNotify(builder, true);
+ builder.notify(UUID_CHARACTERISTIC_POLAR_SETTINGS, true);
+ builder.write(UUID_CHARACTERISTIC_POLAR_SETTINGS, CMD_HR_BROADCAST_GET);
+ builder.write(UUID_CHARACTERISTIC_POLAR_SETTINGS, CMD_GYM_LINK_GET);
+ builder.write(UUID_CHARACTERISTIC_POLAR_SETTINGS, CMD_DUAL_CONNECTION_GET);
+ builder.write(UUID_CHARACTERISTIC_POLAR_SETTINGS, CMD_ANT_GET);
+
// Set defaults
getDevice().setFirmwareVersion("N/A");
getDevice().setFirmwareVersion2("N/A");
@@ -142,11 +168,106 @@ public class PolarH10DeviceSupport extends AbstractBTLESingleDeviceSupport {
return true;
}
+ if (UUID_CHARACTERISTIC_POLAR_SETTINGS.equals(characteristic.getUuid())) {
+ handlePolarSetting(value);
+ return true;
+ }
+
LOG.warn("Unhandled characteristic change: {} = {}", characteristic.getUuid(), GB.hexdump(value));
return false;
}
+ @Override
+ public void onSendConfiguration(final String config) {
+ final byte preferenceCommand;
+ final boolean preferenceValue;
+
+ switch (config) {
+ case PREF_GYMLINK_ENABLED:
+ preferenceCommand = CMD_GYM_LINK_SET;
+ preferenceValue = getDevicePrefs().getBoolean(config, false);
+ break;
+ case PREF_ANTPLUS_ENABLED:
+ preferenceCommand = CMD_ANT_SET;
+ preferenceValue = getDevicePrefs().getBoolean(config, false);
+ break;
+ case PREF_HR_BROADCAST:
+ preferenceCommand = CMD_HR_BROADCAST_SET;
+ preferenceValue = getDevicePrefs().getBoolean(config, false);
+ break;
+ case PREF_DUAL_CONNECTION:
+ preferenceCommand = CMD_DUAL_CONNECTION_SET;
+ preferenceValue = getDevicePrefs().getBoolean(config, false);
+ break;
+ default:
+ super.onSendConfiguration(config);
+ return;
+ }
+
+ LOG.debug("Setting {} = {}", config, preferenceValue);
+
+ final TransactionBuilder builder = createTransactionBuilder("set " + config + " = " + preferenceValue);
+ builder.write(UUID_CHARACTERISTIC_POLAR_SETTINGS, preferenceCommand, (byte) (preferenceValue ? 1 : 0));
+ builder.queue();
+ }
+
+ private void handlePolarSetting(final byte[] value) {
+ if (value[0] != CMD_RESPONSE) {
+ LOG.warn("Unexpected first byte {}", String.format("0x%02x", value[0]));
+ return;
+ }
+
+ switch (value[1]) {
+ case CMD_HR_BROADCAST_SET:
+ LOG.debug("HR broadcast set response, success = {}", value[2]);
+ break;
+ case CMD_HR_BROADCAST_GET:
+ if (value[2] != 0x01) {
+ LOG.warn("HR broadcast get failed, status = {}", String.format("0x%02x", value[2]));
+ return;
+ }
+ LOG.debug("Got HR broadcast setting, value = {}", value[3] != 0);
+ evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(PREF_HR_BROADCAST, value[3] != 0));
+ break;
+ case CMD_GYM_LINK_SET:
+ LOG.debug("GymLink set response, success = {}", value[2]);
+ break;
+ case CMD_GYM_LINK_GET:
+ if (value[2] != 0x01) {
+ LOG.warn("GymLink get failed, status = {}", String.format("0x%02x", value[2]));
+ return;
+ }
+ LOG.debug("Got GymLink setting, value = {}", value[3] != 0);
+ evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(PREF_GYMLINK_ENABLED, value[3] != 0));
+ break;
+ case CMD_DUAL_CONNECTION_SET:
+ LOG.debug("Dual connection set response, success = {}", value[2]);
+ break;
+ case CMD_DUAL_CONNECTION_GET:
+ if (value[2] != 0x01) {
+ LOG.warn("Dual connection get failed, status = {}", String.format("0x%02x", value[2]));
+ return;
+ }
+ LOG.debug("Got dual connection setting, value = {}", value[3] != 0);
+ evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(PREF_DUAL_CONNECTION, value[3] != 0));
+ break;
+ case CMD_ANT_SET:
+ LOG.debug("ANT+ set response, success = {}", value[2]);
+ break;
+ case CMD_ANT_GET:
+ if (value[2] != 0x01) {
+ LOG.warn("ANT+ get failed, status = {}", String.format("0x%02x", value[2]));
+ return;
+ }
+ LOG.debug("Got ANT+ setting, value = {}", value[3] != 0);
+ evaluateGBDeviceEvent(new GBDeviceEventUpdatePreferences(PREF_ANTPLUS_ENABLED, value[3] != 0));
+ break;
+ default:
+ LOG.warn("Unknown response byte: {}", String.format("0x%02x", value[1]));
+ }
+ }
+
private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) {
LOG.warn("Device info: {}", info);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index eccb32d42b..afb3e45f85 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4769,4 +4769,12 @@
Step goal progress
Control music player
Ring phone
+ GymLink
+ Allow GymLink-compatible equipment to connect to the device.
+ ANT+
+ Nearby compatible ANT+ devices can pick up your heart rate.
+ Broadcast heart rate
+ Any nearby device or app can pick up your heart rate.
+ Dual connection
+ Allow two apps or devices to connect simultaneously.
diff --git a/app/src/main/res/xml/devicesettings_polar.xml b/app/src/main/res/xml/devicesettings_polar.xml
new file mode 100644
index 0000000000..eb2fb74f1f
--- /dev/null
+++ b/app/src/main/res/xml/devicesettings_polar.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+