mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-14 14:09:28 +01:00
Fix pairing with MI1A
This commit is contained in:
parent
7591d4a8af
commit
d57c6166b9
@ -1,7 +1,10 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.devices.miband;
|
package nodomain.freeyourgadget.gadgetbridge.devices.miband;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by UgoRaffaele on 30/01/2015.
|
* Created by UgoRaffaele on 30/01/2015.
|
||||||
*/
|
*/
|
||||||
@ -56,46 +59,6 @@ public class UserInfo {
|
|||||||
this.height = height;
|
this.height = height;
|
||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
byte[] sequence = new byte[20];
|
|
||||||
|
|
||||||
int uid = calculateUidFrom(alias);
|
|
||||||
String normalizedAlias = ensureTenCharacters(alias);
|
|
||||||
sequence[0] = (byte) uid;
|
|
||||||
sequence[1] = (byte) (uid >>> 8);
|
|
||||||
sequence[2] = (byte) (uid >>> 16);
|
|
||||||
sequence[3] = (byte) (uid >>> 24);
|
|
||||||
|
|
||||||
sequence[4] = (byte) (gender & 0xff);
|
|
||||||
sequence[5] = (byte) (age & 0xff);
|
|
||||||
sequence[6] = (byte) (height & 0xff);
|
|
||||||
sequence[7] = (byte) (weight & 0xff);
|
|
||||||
sequence[8] = (byte) (type & 0xff);
|
|
||||||
|
|
||||||
for (int u = 9; u < 19; u++)
|
|
||||||
sequence[u] = normalizedAlias.getBytes()[u - 9];
|
|
||||||
|
|
||||||
byte[] crcSequence = new byte[19];
|
|
||||||
System.arraycopy(sequence, 0, crcSequence, 0, crcSequence.length);
|
|
||||||
|
|
||||||
sequence[19] = (byte) ((CheckSums.getCRC8(crcSequence) ^ Integer.parseInt(address.substring(address.length() - 2), 16)) & 0xff);
|
|
||||||
|
|
||||||
this.data = sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String ensureTenCharacters(String alias) {
|
|
||||||
char[] result = new char[10];
|
|
||||||
int aliasLen = alias.length();
|
|
||||||
int maxLen = Math.min(10, alias.length());
|
|
||||||
int diff = 10 - maxLen;
|
|
||||||
for (int i = 0; i < maxLen; i++) {
|
|
||||||
result[i + diff] = alias.charAt(i);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < diff; i++) {
|
|
||||||
result[i] = '0';
|
|
||||||
}
|
|
||||||
return new String(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculateUidFrom(String alias) {
|
private int calculateUidFrom(String alias) {
|
||||||
@ -108,7 +71,34 @@ public class UserInfo {
|
|||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getData() {
|
public byte[] getData(DeviceInfo mDeviceInfo) {
|
||||||
return this.data;
|
byte[] sequence = new byte[20];
|
||||||
|
int uid = calculateUidFrom(alias);
|
||||||
|
|
||||||
|
sequence[0] = (byte) uid;
|
||||||
|
sequence[1] = (byte) (uid >>> 8);
|
||||||
|
sequence[2] = (byte) (uid >>> 16);
|
||||||
|
sequence[3] = (byte) (uid >>> 24);
|
||||||
|
|
||||||
|
sequence[4] = (byte) (gender & 0xff);
|
||||||
|
sequence[5] = (byte) (age & 0xff);
|
||||||
|
sequence[6] = (byte) (height & 0xff);
|
||||||
|
sequence[7] = (byte) (weight & 0xff);
|
||||||
|
sequence[8] = (byte) (type & 0xff);
|
||||||
|
|
||||||
|
int aliasFrom = 9;
|
||||||
|
if (mDeviceInfo.isMili1A()) {
|
||||||
|
sequence[9] = (byte) (mDeviceInfo.feature & 255);
|
||||||
|
sequence[10] = (byte) (mDeviceInfo.appearance & 255);
|
||||||
|
aliasFrom = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] aliasBytes = alias.substring(0, Math.min(alias.length(), 19-aliasFrom)).getBytes();
|
||||||
|
System.arraycopy(aliasBytes, 0, sequence, aliasFrom, aliasBytes.length);
|
||||||
|
|
||||||
|
byte[] crcSequence = Arrays.copyOf(sequence, 19);
|
||||||
|
sequence[19] = (byte) ((CheckSums.getCRC8(crcSequence) ^ Integer.parseInt(this.btAddress.substring(this.btAddress.length() - 2), 16)) & 0xff);
|
||||||
|
|
||||||
|
return sequence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,84 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class DeviceInfo extends AbstractInfo {
|
public class DeviceInfo extends AbstractInfo {
|
||||||
|
public final String deviceId;
|
||||||
|
public final int profileVersion;
|
||||||
|
public final int fwVersion;
|
||||||
|
public final int hwVersion;
|
||||||
|
public final int feature;
|
||||||
|
public final int appearance;
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isChecksumCorrect(byte[] data) {
|
||||||
|
int crc8 = CheckSums.getCRC8(new byte[]{data[0], data[1], data[2], data[3], data[4], data[5], data[6]});
|
||||||
|
return data[7] == (crc8 ^ data[3] & 255);
|
||||||
|
}
|
||||||
|
|
||||||
public DeviceInfo(byte[] data) {
|
public DeviceInfo(byte[] data) {
|
||||||
super(data);
|
super(data);
|
||||||
|
|
||||||
|
if ((data.length == 16 || data.length == 20) && isChecksumCorrect(data)) {
|
||||||
|
deviceId = String.format("%02X%02X%02X%02X%02X%02X%02X%02X", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
|
||||||
|
profileVersion = getInt(data, 8);
|
||||||
|
fwVersion = getInt(data, 12);
|
||||||
|
hwVersion = Integer.decode("0x" + deviceId.substring(12, 14)).intValue();
|
||||||
|
feature = Integer.decode("0x" + deviceId.substring(8, 10)).intValue();
|
||||||
|
appearance = Integer.decode("0x" + deviceId.substring(10, 12)).intValue();
|
||||||
|
} else {
|
||||||
|
deviceId = "crc error";
|
||||||
|
profileVersion = -1;
|
||||||
|
fwVersion = -1;
|
||||||
|
hwVersion = -1;
|
||||||
|
feature = -1;
|
||||||
|
appearance = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt(byte[] data, int from, int len) {
|
||||||
|
int ret = 0;
|
||||||
|
for(int i = 0; i < len; ++i) {
|
||||||
|
ret |= (data[from + i] & 255) << i * 8;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getInt(byte[] data, int from) {
|
||||||
|
return getInt(data, from, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHumanFirmwareVersion() {
|
public String getHumanFirmwareVersion() {
|
||||||
if (mData.length == 16) {
|
if (fwVersion == -1)
|
||||||
int last = 15;
|
return GBApplication.getContext().getString(R.string._unknown_);
|
||||||
return String.format(Locale.US, "%d.%d.%d.%d", mData[last], mData[last - 1], mData[last - 2], mData[last - 3]);
|
|
||||||
}
|
return String.format(Locale.US, "%d.%d.%d.%d",
|
||||||
return GBApplication.getContext().getString(R.string._unknown_);
|
fwVersion >> 24 & 255,
|
||||||
|
fwVersion >> 16 & 255,
|
||||||
|
fwVersion >> 8 & 255,
|
||||||
|
fwVersion & 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFirmwareVersion() {
|
public int getFirmwareVersion() {
|
||||||
if (mData.length == 16) {
|
return fwVersion;
|
||||||
int last = 15;
|
}
|
||||||
return (mData[last] << 24) | (mData[last - 1] << 16) | (mData[last - 2] << 8) | mData[last - 3];
|
|
||||||
}
|
@Override
|
||||||
return -1;
|
public String toString() {
|
||||||
|
return "DeviceInfo{" +
|
||||||
|
"deviceId='" + deviceId + '\'' +
|
||||||
|
", profileVersion=" + profileVersion +
|
||||||
|
", fwVersion=" + fwVersion +
|
||||||
|
", hwVersion=" + hwVersion +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMili1A() {
|
||||||
|
return (this.feature & 255) == 5 && (this.appearance & 255) == 0 || (this.feature & 255) == 0 && (this.hwVersion & 255) == 208;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import android.preference.PreferenceManager;
|
|||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -91,13 +92,13 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
|
||||||
pair(builder)
|
pair(builder)
|
||||||
|
.requestDeviceInfo(builder)
|
||||||
.sendUserInfo(builder)
|
.sendUserInfo(builder)
|
||||||
.setWearLocation(builder)
|
.setWearLocation(builder)
|
||||||
.setFitnessGoal(builder)
|
.setFitnessGoal(builder)
|
||||||
.enableNotifications(builder, true)
|
.enableNotifications(builder, true)
|
||||||
.setCurrentTime(builder)
|
.setCurrentTime(builder)
|
||||||
.requestBatteryInfo(builder)
|
.requestBatteryInfo(builder)
|
||||||
.requestDeviceInfo(builder)
|
|
||||||
.setInitialized(builder);
|
.setInitialized(builder);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
@ -268,8 +269,19 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
*/
|
*/
|
||||||
private MiBandSupport sendUserInfo(TransactionBuilder builder) {
|
private MiBandSupport sendUserInfo(TransactionBuilder builder) {
|
||||||
LOG.debug("Writing User Info!");
|
LOG.debug("Writing User Info!");
|
||||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO);
|
builder.add(new BtLEAction(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO)) {
|
||||||
builder.write(characteristic, MiBandCoordinator.getAnyUserInfo(getDevice().getAddress()).getData());
|
@Override
|
||||||
|
public boolean expectsResult() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothGatt gatt) {
|
||||||
|
return new WriteAction(getCharacteristic(),
|
||||||
|
MiBandCoordinator.getAnyUserInfo(getDevice().getAddress()).getData(mDeviceInfo)
|
||||||
|
).run(gatt);
|
||||||
|
}
|
||||||
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,6 +761,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
|||||||
private void handleDeviceInfo(byte[] value, int status) {
|
private void handleDeviceInfo(byte[] value, int status) {
|
||||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
mDeviceInfo = new DeviceInfo(value);
|
mDeviceInfo = new DeviceInfo(value);
|
||||||
|
LOG.warn(mDeviceInfo.toString());
|
||||||
versionCmd.fwVersion = mDeviceInfo.getHumanFirmwareVersion();
|
versionCmd.fwVersion = mDeviceInfo.getHumanFirmwareVersion();
|
||||||
handleGBDeviceEvent(versionCmd);
|
handleGBDeviceEvent(versionCmd);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user