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 3cc46111e..19c0d45ea 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
@@ -459,6 +459,13 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_MISCALE_WEIGHT_UNIT = "pref_miscale_weight_unit";
public static final String PREF_MISCALE_SMALL_OBJECTS = "pref_miscale_small_objects";
+ public static final String PREF_MIJIA_LYWSD_COMFORT_CHARACTERISTIC_LENGTH = "pref_mijia_lywsd_comfort_characteristic_length";
+ public static final String PREF_MIJIA_LYWSD_COMFORT_LEVEL = "pref_mijia_lywsd_comfort_level";
+ public static final String PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER = "pref_mijia_lywsd_comfort_temperature_lower";
+ public static final String PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER = "pref_mijia_lywsd_comfort_temperature_upper";
+ public static final String PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER = "pref_mijia_lywsd_comfort_humidity_lower";
+ public static final String PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER = "pref_mijia_lywsd_comfort_humidity_upper";
+
public static final String PREF_QC35_NOISE_CANCELLING_LEVEL = "qc35_noise_cancelling_level";
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD = "prefs_activity_in_device_card";
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 997ca36da..4c62a7152 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
@@ -764,6 +764,11 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
addPreferenceHandlerFor(PREF_MISCALE_WEIGHT_UNIT);
addPreferenceHandlerFor(PREF_MISCALE_SMALL_OBJECTS);
+ addPreferenceHandlerFor(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER);
+ addPreferenceHandlerFor(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER);
+ addPreferenceHandlerFor(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER);
+ addPreferenceHandlerFor(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER);
+
addPreferenceHandlerFor(PREF_FEMOMETER_MEASUREMENT_MODE);
addPreferenceHandlerFor(PREF_QC35_NOISE_CANCELLING_LEVEL);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/AbstractMijiaLywsdCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/AbstractMijiaLywsdCoordinator.java
index 448ad590a..0cad25382 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/AbstractMijiaLywsdCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/AbstractMijiaLywsdCoordinator.java
@@ -22,6 +22,7 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@@ -51,6 +52,11 @@ public abstract class AbstractMijiaLywsdCoordinator extends AbstractBLEDeviceCoo
return "Xiaomi";
}
+ @Override
+ public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
+ return new MijiaLywsdSettingsCustomizer();
+ }
+
@NonNull
@Override
public Class extends DeviceSupport> getDeviceSupportClass() {
@@ -60,6 +66,7 @@ public abstract class AbstractMijiaLywsdCoordinator extends AbstractBLEDeviceCoo
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return new int[]{
+ R.xml.devicesettings_mijia_lywsd,
R.xml.devicesettings_temperature_scale_cf,
};
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdSettingsCustomizer.java
new file mode 100644
index 000000000..ce2017557
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd/MijiaLywsdSettingsCustomizer.java
@@ -0,0 +1,95 @@
+/* Copyright (C) 2024 Severin von Wnuck-Lipinski
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd;
+
+import android.os.Parcel;
+import androidx.preference.Preference;
+import androidx.preference.SeekBarPreference;
+
+import java.util.Collections;
+import java.util.Set;
+
+import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
+import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.mijia_lywsd.MijiaLywsdSupport;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
+
+public class MijiaLywsdSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public MijiaLywsdSettingsCustomizer createFromParcel(final Parcel in) {
+ return new MijiaLywsdSettingsCustomizer();
+ }
+
+ @Override
+ public MijiaLywsdSettingsCustomizer[] newArray(final int size) {
+ return new MijiaLywsdSettingsCustomizer[size];
+ }
+ };
+
+ @Override
+ public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
+ String key = preference.getKey();
+
+ if (!key.equals(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER) && !key.equals(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER) &&
+ !key.equals(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER) && !key.equals(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER))
+ return;
+
+ SeekBarPreference temperatureLower = handler.findPreference(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER);
+ SeekBarPreference temperatureUpper = handler.findPreference(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER);
+ SeekBarPreference humidityLower = handler.findPreference(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER);
+ SeekBarPreference humidityUpper = handler.findPreference(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER);
+
+ if (temperatureLower == null || temperatureUpper == null || humidityLower == null || humidityUpper == null)
+ return;
+
+ // Clamp minimum value of upper limit to lower limit
+ if (temperatureLower.getValue() > temperatureUpper.getValue())
+ temperatureUpper.setValue(temperatureLower.getValue());
+
+ if (humidityLower.getValue() > humidityUpper.getValue())
+ humidityUpper.setValue(humidityLower.getValue());
+ }
+
+ @Override
+ public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs, final String rootKey) {
+ Preference comfortLevel = handler.findPreference(PREF_MIJIA_LYWSD_COMFORT_LEVEL);
+
+ if (comfortLevel != null) {
+ int length = prefs.getInt(PREF_MIJIA_LYWSD_COMFORT_CHARACTERISTIC_LENGTH, 0);
+
+ // Hide comfort level for unknown characteristic length
+ comfortLevel.setVisible(length == MijiaLywsdSupport.COMFORT_LEVEL_LENGTH_LYWSD03 ||
+ length == MijiaLywsdSupport.COMFORT_LEVEL_LENGTH_XMWSDJ04);
+ }
+ }
+
+ @Override
+ public Set getPreferenceKeysWithSummary() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {}
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd/MijiaLywsdSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd/MijiaLywsdSupport.java
index 5bac80878..f89aefdb5 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd/MijiaLywsdSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd/MijiaLywsdSupport.java
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023-2024 José Rebelo
+/* Copyright (C) 2023-2024 José Rebelo, Severin von Wnuck-Lipinski
This file is part of Gadgetbridge.
@@ -19,18 +19,20 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.mijia_lywsd;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.Objects;
import java.util.SimpleTimeZone;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
-import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd.AbstractMijiaLywsdCoordinator;
@@ -44,12 +46,16 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
+import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
+
public class MijiaLywsdSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(MijiaLywsdSupport.class);
private static final UUID UUID_TIME = UUID.fromString("ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6");
private static final UUID UUID_BATTERY = UUID.fromString("ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6");
private static final UUID UUID_SCALE = UUID.fromString("ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6");
+ private static final UUID UUID_COMFORT_LEVEL = UUID.fromString("ebe0ccd7-7a0a-4b0c-8a1a-6ff2997da3a6");
private static final UUID UUID_CONN_INTERVAL = UUID.fromString("ebe0ccd8-7a0a-4b0c-8a1a-6ff2997da3a6");
private final DeviceInfoProfile deviceInfoProfile;
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
@@ -64,6 +70,10 @@ public class MijiaLywsdSupport extends AbstractBTLEDeviceSupport {
}
};
+ // Length of comfort level characteristic for different devices
+ public static final int COMFORT_LEVEL_LENGTH_LYWSD03 = 6;
+ public static final int COMFORT_LEVEL_LENGTH_XMWSDJ04 = 8;
+
public MijiaLywsdSupport() {
super(LOG);
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
@@ -86,6 +96,7 @@ public class MijiaLywsdSupport extends AbstractBTLEDeviceSupport {
}
getBatteryInfo(builder);
+ getComfortLevel(builder);
setConnectionInterval(builder);
setInitialized(builder);
return builder;
@@ -118,11 +129,53 @@ public class MijiaLywsdSupport extends AbstractBTLEDeviceSupport {
builder.read(batteryCharacteristc);
}
- private void setTemperatureScale(TransactionBuilder builder, String scale) {
+ private void getComfortLevel(TransactionBuilder builder) {
+ BluetoothGattCharacteristic comfortCharacteristc = getCharacteristic(MijiaLywsdSupport.UUID_COMFORT_LEVEL);
+ builder.read(comfortCharacteristc);
+ }
+
+ private void setTemperatureScale(TransactionBuilder builder, SharedPreferences prefs) {
+ String scale = prefs.getString(PREF_TEMPERATURE_SCALE_CF, "");
BluetoothGattCharacteristic scaleCharacteristc = getCharacteristic(MijiaLywsdSupport.UUID_SCALE);
builder.write(scaleCharacteristc, new byte[]{(byte) ("f".equals(scale) ? 0x01 : 0xff)});
}
+ private void setComfortLevel(TransactionBuilder builder, SharedPreferences prefs) {
+ int length = prefs.getInt(PREF_MIJIA_LYWSD_COMFORT_CHARACTERISTIC_LENGTH, 0);
+ int temperatureLower = prefs.getInt(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER, 19);
+ int temperatureUpper = prefs.getInt(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER, 27);
+ int humidityLower = prefs.getInt(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER, 20);
+ int humidityUpper = prefs.getInt(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER, 85);
+
+ // Ignore invalid values
+ if (temperatureLower > temperatureUpper || humidityLower > humidityUpper)
+ return;
+
+ BluetoothGattCharacteristic comfortCharacteristc = getCharacteristic(MijiaLywsdSupport.UUID_COMFORT_LEVEL);
+ ByteBuffer buf = ByteBuffer.allocate(length);
+
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ switch (length) {
+ case COMFORT_LEVEL_LENGTH_LYWSD03:
+ buf.putShort((short)(temperatureUpper * 100));
+ buf.putShort((short)(temperatureLower * 100));
+ buf.put((byte)humidityUpper);
+ buf.put((byte)humidityLower);
+ break;
+ case COMFORT_LEVEL_LENGTH_XMWSDJ04:
+ buf.putShort((short)(temperatureUpper * 10));
+ buf.putShort((short)(temperatureLower * 10));
+ buf.putShort((short)(humidityUpper * 10));
+ buf.putShort((short)(humidityLower * 10));
+ break;
+ default:
+ return;
+ }
+
+ builder.write(comfortCharacteristc, buf.array());
+ }
+
private void handleBatteryInfo(byte[] value, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
batteryCmd.level = ((short) value[0]);
@@ -131,6 +184,45 @@ public class MijiaLywsdSupport extends AbstractBTLEDeviceSupport {
}
}
+ private void handleComfortLevel(byte[] value, int status) {
+ if (status != BluetoothGatt.GATT_SUCCESS)
+ return;
+
+ ByteBuffer buf = ByteBuffer.wrap(value);
+ int temperatureLower, temperatureUpper;
+ int humidityLower, humidityUpper;
+
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ switch (value.length) {
+ case COMFORT_LEVEL_LENGTH_LYWSD03:
+ temperatureUpper = buf.getShort() / 100;
+ temperatureLower = buf.getShort() / 100;
+ humidityUpper = buf.get();
+ humidityLower = buf.get();
+ break;
+ case COMFORT_LEVEL_LENGTH_XMWSDJ04:
+ temperatureUpper = buf.getShort() / 10;
+ temperatureLower = buf.getShort() / 10;
+ humidityUpper = buf.getShort() / 10;
+ humidityLower = buf.getShort() / 10;
+ break;
+ default:
+ LOG.error("Unknown comfort level characteristic: {}", hexdump(value));
+ return;
+ }
+
+ SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
+
+ prefs.edit()
+ .putInt(PREF_MIJIA_LYWSD_COMFORT_CHARACTERISTIC_LENGTH, value.length)
+ .putInt(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER, temperatureLower)
+ .putInt(PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER, temperatureUpper)
+ .putInt(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER, humidityLower)
+ .putInt(PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER, humidityUpper)
+ .apply();
+ }
+
private void requestDeviceInfo(TransactionBuilder builder) {
LOG.debug("Requesting Device Info!");
deviceInfoProfile.requestDeviceInfo(builder);
@@ -189,22 +281,36 @@ public class MijiaLywsdSupport extends AbstractBTLEDeviceSupport {
handleBatteryInfo(characteristic.getValue(), status);
return true;
}
+
+ if (MijiaLywsdSupport.UUID_COMFORT_LEVEL.equals(characteristicUUID)) {
+ handleComfortLevel(characteristic.getValue(), status);
+ return true;
+ }
+
LOG.info("Unhandled characteristic read: " + characteristicUUID);
return false;
}
@Override
public void onSendConfiguration(String config) {
- TransactionBuilder builder;
+ SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
+
try {
+ TransactionBuilder builder = performInitialized("Sending configuration for option: " + config);
+
switch (config) {
- case DeviceSettingsPreferenceConst.PREF_TEMPERATURE_SCALE_CF:
- String temperatureScale = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getString(DeviceSettingsPreferenceConst.PREF_TEMPERATURE_SCALE_CF, "");
- builder = performInitialized("Sending configuration for option: " + config);
- setTemperatureScale(builder, temperatureScale);
- builder.queue(getQueue());
+ case PREF_TEMPERATURE_SCALE_CF:
+ setTemperatureScale(builder, prefs);
+ break;
+ case PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_LOWER:
+ case PREF_MIJIA_LYWSD_COMFORT_TEMPERATURE_UPPER:
+ case PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_LOWER:
+ case PREF_MIJIA_LYWSD_COMFORT_HUMIDITY_UPPER:
+ setComfortLevel(builder, prefs);
break;
}
+
+ builder.queue(getQueue());
} catch (IOException e) {
LOG.error("Error setting configuration on LYWSD02", e);
GB.toast("Error setting configuration", Toast.LENGTH_LONG, GB.ERROR, e);
diff --git a/app/src/main/res/drawable/ic_humidity_mid.xml b/app/src/main/res/drawable/ic_humidity_mid.xml
new file mode 100644
index 000000000..fcfd6980a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_humidity_mid.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 493f9738c..4ad4e11f3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2725,6 +2725,14 @@
Chinese (jin)
Small Objects
Store weight of objects lighter than 10 kg
+ Comfort Level
+ Configure the temperature and humidity limits for the displayed emoji
+ Temperature (°C)
+ Recommended range: 19 - 27
+ Humidity (%)
+ Recommended range: 20 - 85
+ Lower Limit
+ Upper Limit
Protocol Version
Auto Brightness
Adjust screen brightness according to ambient light
diff --git a/app/src/main/res/xml/devicesettings_mijia_lywsd.xml b/app/src/main/res/xml/devicesettings_mijia_lywsd.xml
new file mode 100644
index 000000000..8f894361f
--- /dev/null
+++ b/app/src/main/res/xml/devicesettings_mijia_lywsd.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+