Merge pull request #145 from sarg/master

Pairing support for MI1A
here
Carsten Pfeiffer 2015-10-18 21:28:41 +02:00
commit e0289f63ce
4 changed files with 116 additions and 58 deletions

View File

@ -1,7 +1,10 @@
package nodomain.freeyourgadget.gadgetbridge.devices.miband;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
import java.util.Arrays;
/**
* Created by UgoRaffaele on 30/01/2015.
*/
@ -56,46 +59,6 @@ public class UserInfo {
this.height = height;
this.weight = weight;
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) {
@ -108,7 +71,34 @@ public class UserInfo {
return uid;
}
public byte[] getData() {
return this.data;
public byte[] getData(DeviceInfo mDeviceInfo) {
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;
}
}

View File

@ -368,7 +368,7 @@ public final class BtLEQueue {
try {
getCallbackToUse().onCharacteristicRead(gatt, characteristic, status);
} catch (Throwable ex) {
LOG.error("onCharaceristicRead: " + ex.getMessage(), ex);
LOG.error("onCharacteristicRead: " + ex.getMessage(), ex);
}
}
checkWaitingCharacteristic(characteristic, status);

View File

@ -1,29 +1,84 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
import java.util.Locale;
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) {
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() {
if (mData.length == 16) {
int last = 15;
return String.format(Locale.US, "%d.%d.%d.%d", mData[last], mData[last - 1], mData[last - 2], mData[last - 3]);
}
return GBApplication.getContext().getString(R.string._unknown_);
if (fwVersion == -1)
return GBApplication.getContext().getString(R.string._unknown_);
return String.format(Locale.US, "%d.%d.%d.%d",
fwVersion >> 24 & 255,
fwVersion >> 16 & 255,
fwVersion >> 8 & 255,
fwVersion & 255);
}
public int getFirmwareVersion() {
if (mData.length == 16) {
int last = 15;
return (mData[last] << 24) | (mData[last - 1] << 16) | (mData[last - 2] << 8) | mData[last - 3];
}
return -1;
return fwVersion;
}
@Override
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;
}
}

View File

@ -9,6 +9,7 @@ import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -91,13 +92,13 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
pair(builder)
.requestDeviceInfo(builder)
.sendUserInfo(builder)
.setWearLocation(builder)
.setFitnessGoal(builder)
.enableNotifications(builder, true)
.setCurrentTime(builder)
.requestBatteryInfo(builder)
.requestDeviceInfo(builder)
.setInitialized(builder);
return builder;
@ -268,8 +269,19 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
*/
private MiBandSupport sendUserInfo(TransactionBuilder builder) {
LOG.debug("Writing User Info!");
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO);
builder.write(characteristic, MiBandCoordinator.getAnyUserInfo(getDevice().getAddress()).getData());
builder.add(new BtLEAction(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO)) {
@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;
}
@ -749,6 +761,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
private void handleDeviceInfo(byte[] value, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
mDeviceInfo = new DeviceInfo(value);
LOG.warn(mDeviceInfo.toString());
versionCmd.fwVersion = mDeviceInfo.getHumanFirmwareVersion();
handleGBDeviceEvent(versionCmd);
}