diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miwatch/MiWatchLiteCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miwatch/MiWatchLiteCoordinator.java
new file mode 100644
index 000000000..8327d9b90
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miwatch/MiWatchLiteCoordinator.java
@@ -0,0 +1,67 @@
+/* Copyright (C) 2023 Andreas Shimokawa
+
+ 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.miwatch;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.regex.Pattern;
+
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
+import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.miwatch.MiWatchLiteSupport;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
+
+public class MiWatchLiteCoordinator extends XiaomiCoordinator {
+ @Override
+ protected Pattern getSupportedDeviceName() {
+ return Pattern.compile("^Mi Watch Lite_[A-Z0-9]{4}$");
+ }
+
+ @Nullable
+ @Override
+ public InstallHandler findInstallHandler(final Uri uri, final Context context) {
+ // TODO implement this
+ return super.findInstallHandler(uri, context);
+ }
+
+ @Override
+ public int getDeviceNameResource() {
+ return R.string.devicetype_xiaomi_watch_lite;
+ }
+
+ @Override
+ public int getDefaultIconResource() {
+ return R.drawable.ic_device_amazfit_bip;
+ }
+
+ @Override
+ public int getDisabledIconResource() {
+ return R.drawable.ic_device_amazfit_bip_disabled;
+ }
+
+ @NonNull
+ @Override
+ public Class extends DeviceSupport> getDeviceSupportClass() {
+ return MiWatchLiteSupport.class;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
index b55711ab3..3d89a8c8d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
@@ -143,6 +143,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.withingssteelhr.WithingsStee
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miband8.MiBand8Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.devices.miwatch.MiWatchLiteCoordinator;
/**
* For every supported device, a device type constant must exist.
@@ -194,6 +195,7 @@ public enum DeviceType {
AMAZFITPOPPRO(AmazfitPopProCoordinator.class),
MIBAND7(MiBand7Coordinator.class),
MIBAND8(MiBand8Coordinator.class),
+ MIWATCHLITE(MiWatchLiteCoordinator.class),
AMAZFITGTS3(AmazfitGTS3Coordinator.class),
AMAZFITGTR3(AmazfitGTR3Coordinator.class),
AMAZFITGTR4(AmazfitGTR4Coordinator.class),
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
index d6edb830b..32d01343b 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
@@ -22,6 +22,7 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.service;
+import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.util.Log;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miwatch/MiWatchLiteSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miwatch/MiWatchLiteSupport.java
new file mode 100644
index 000000000..ee0817c0e
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miwatch/MiWatchLiteSupport.java
@@ -0,0 +1,165 @@
+/* Copyright (C) 2023 Andreas Shimokawa
+
+ 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.service.devices.miwatch;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.content.SharedPreferences;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
+import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+public class MiWatchLiteSupport extends XiaomiSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MiWatchLiteSupport.class);
+
+ private static final UUID UUID_CHARACTERISTIC_MAIN = UUID.fromString("16186f02-0000-1000-8000-00807f9b34fb");
+ private static final UUID UUID_CHARACTERISTIC_UNK1 = UUID.fromString("16186f01-0000-1000-8000-00807f9b34fb");
+ private static final UUID UUID_CHARACTERISTIC_UNK2 = UUID.fromString("16186f03-0000-1000-8000-00807f9b34fb");
+ private static final UUID UUID_CHARACTERISTIC_UNK3 = UUID.fromString("16186f04-0000-1000-8000-00807f9b34fb");
+ private static final UUID UUID_CHARACTERISTIC_UNK4 = UUID.fromString("16186f05-0000-1000-8000-00807f9b34fb");
+ private static boolean sendUserIdDone = false;
+
+ public MiWatchLiteSupport() {
+ super(); // FIXME: no we do not want to do this!! This adds supported characteristics which we do not have - but we have to.
+ addSupportedService(UUID.fromString("16186f00-0000-1000-8000-00807f9b34fb"));
+ }
+
+ @Override
+ protected TransactionBuilder initializeDevice(final TransactionBuilder builder) {
+
+ // FIXME why is this needed?
+ getDevice().setFirmwareVersion("...");
+ //getDevice().setFirmwareVersion2("...");
+ enableNotifications(builder, true);
+ builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
+ builder.requestMtu(247);
+ byte[] command = new byte[]{0, 0, 0, 0, 1, 0};
+ BluetoothGattCharacteristic characteristic = getCharacteristic(UUID_CHARACTERISTIC_MAIN);
+ builder.write(characteristic, command);
+
+ builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
+
+
+ return builder;
+ }
+
+ private void enableNotifications(TransactionBuilder builder, boolean enable) {
+ builder.notify(getCharacteristic(UUID_CHARACTERISTIC_MAIN), enable);
+ builder.notify(getCharacteristic(UUID_CHARACTERISTIC_UNK1), enable);
+ builder.notify(getCharacteristic(UUID_CHARACTERISTIC_UNK2), enable);
+ builder.notify(getCharacteristic(UUID_CHARACTERISTIC_UNK3), enable);
+ builder.notify(getCharacteristic(UUID_CHARACTERISTIC_UNK4), enable);
+ }
+
+ protected static byte[] getUserId(final GBDevice device) {
+ final byte[] userId = new byte[10];
+
+ final SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress());
+
+ final String authKey = sharedPrefs.getString("authkey", null);
+ if (StringUtils.isNotBlank(authKey)) {
+ final byte[] srcBytes;
+ // Allow both with and without 0x, to avoid user mistakes
+ if (authKey.length() == 22 && authKey.startsWith("0x")) {
+ srcBytes = GB.hexStringToByteArray(authKey.trim().substring(2));
+ } else if (authKey.length() == 10) {
+ srcBytes = authKey.getBytes();
+ } else {
+ return null;
+ }
+ System.arraycopy(srcBytes, 0, userId, 0, 10);
+ }
+
+ return userId;
+ }
+
+ @Override
+ public void sendCommand(final TransactionBuilder builder, final nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto.Command command) {
+ final byte[] commandBytes = command.toByteArray();
+ final int commandLength = 2 + commandBytes.length;
+ if (commandLength > getMTU()) {
+ LOG.warn("Command with {} bytes is too large for MTU of {}", commandLength, getMTU());
+ }
+ builder.write(getCharacteristic(UUID_CHARACTERISTIC_MAIN), new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00});
+ builder.wait(500);
+
+ final ByteBuffer buf = ByteBuffer.allocate(commandLength).order(ByteOrder.LITTLE_ENDIAN);
+ buf.put((byte) 1);
+ buf.put((byte) 0);
+ buf.put(commandBytes);
+ LOG.debug("Sending command {} as {}", GB.hexdump(commandBytes), GB.hexdump(buf.array()));
+ builder.write(getCharacteristic(UUID_CHARACTERISTIC_MAIN), buf.array());
+ }
+
+ @Override
+ public boolean onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ if (super.onCharacteristicChanged(gatt, characteristic)) {
+ return true;
+ }
+
+ UUID characteristicUUID = characteristic.getUuid();
+
+ final byte[] success_bytes = new byte[]{0x00, 0x00, 0x01, 0x01, 0x00, 0x00};
+ final byte[] ping_request = new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
+ if (characteristicUUID.equals(UUID_CHARACTERISTIC_MAIN)) {
+ if (Arrays.equals(success_bytes, characteristic.getValue()) && !sendUserIdDone) {
+ byte[] userId = getUserId(gbDevice);
+ if (userId == null) {
+ LOG.warn("no user id, sending 0000000000, this won't work");
+ userId = new byte[] {0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30};
+ }
+ sendUserIdDone = true;
+ TransactionBuilder builder = new TransactionBuilder("send user id");
+ builder.write(getCharacteristic(UUID_CHARACTERISTIC_MAIN), org.apache.commons.lang3.ArrayUtils.addAll(new byte[]{0x01, 0x00, 0x08, 0x01, 0x10, 0x05, 0x1a, 0x0c, 0x3a, 0x0a}, userId));
+ builder.queue(getQueue());
+ return true;
+ }
+ } else if (characteristicUUID.equals(UUID_CHARACTERISTIC_UNK1)) {
+ if (Arrays.equals(ping_request, characteristic.getValue())) {
+ TransactionBuilder builder = new TransactionBuilder("reply ping");
+ builder.write(getCharacteristic(UUID_CHARACTERISTIC_UNK1), new byte[]{0x00, 0x00, 0x01, 0x01});
+ builder.queue(getQueue());
+ return true;
+ }
+ if (ArrayUtils.startsWith(characteristic.getValue(), new byte[]{1, 0, 8})) {
+ TransactionBuilder builder = new TransactionBuilder("ack whatever");
+ builder.write(getCharacteristic(UUID_CHARACTERISTIC_UNK1), new byte[]{0x00, 0x00, 0x01, 0x00});
+ builder.queue(getQueue());
+ return true;
+ }
+
+ }
+ LOG.info("Unhandled characteristic changed: " + characteristicUUID);
+ return false;
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c27511017..515925d6a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1386,6 +1386,7 @@
Sony LinkBuds S
Binary sensor
Femometer Vinca II
+ Xiaomi Watch Lite
Choose export location
General
High-priority
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a2e01c0df..99b91f965 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
+#Sun Oct 08 20:08:12 CEST 2023
distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
-zipStoreBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME