mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-11 17:41:57 +01:00
Merge branch 'master' into background-javascript
This commit is contained in:
commit
942984dec2
@ -152,6 +152,7 @@ Feel free to open an issue on our issue tracker, but please:
|
||||
|
||||
## Having problems?
|
||||
|
||||
0. Phone crashing during device discovery? Disable Privacy Guard (or similarly named functionality) during discovery.
|
||||
1. Open Gadgetbridge's settings and check the option to write log files
|
||||
2. Reproduce the problem you encountered
|
||||
3. Check the logfile at /sdcard/Android/data/nodomain.freeyourgadget.gadgetbridge/files/gadgetbridge.log
|
||||
|
@ -60,12 +60,12 @@ dependencies {
|
||||
// testCompile 'ch.qos.logback:logback-core:1.1.3'
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
testCompile "org.robolectric:robolectric:3.1.2"
|
||||
testCompile "org.robolectric:robolectric:3.2.2"
|
||||
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:25.1.1'
|
||||
compile 'com.android.support:support-v4:25.1.1'
|
||||
compile 'com.android.support:design:25.1.1'
|
||||
compile 'com.android.support:appcompat-v7:25.2.0'
|
||||
compile 'com.android.support:support-v4:25.2.0'
|
||||
compile 'com.android.support:design:25.2.0'
|
||||
compile 'com.github.tony19:logback-android-classic:1.1.1-4'
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
compile 'com.github.PhilJay:MPAndroidChart:v3.0.1'
|
||||
|
@ -89,6 +89,26 @@
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
|
||||
<data android:pathPattern="/.*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\.pbw" />
|
||||
<data android:pathPattern="/.*\\..*\\.pbw" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\.pbw" />
|
||||
@ -142,6 +162,26 @@
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.fw" />
|
||||
<data android:pathPattern="/.*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft.en" />
|
||||
<data android:pathPattern="/.*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.ft" />
|
||||
<data android:pathPattern="/.*\\.pbw" />
|
||||
<data android:pathPattern="/.*\\..*\\.pbw" />
|
||||
<data android:pathPattern="/.*\\..*\\..*\\.pbw" />
|
||||
|
@ -109,8 +109,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
|
||||
if (device != null && device.getAddress().equals(bondingAddress)) {
|
||||
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||
GB.toast(DiscoveryActivity.this, "Successfully bonded with: " + bondingAddress, Toast.LENGTH_SHORT, GB.INFO);
|
||||
finish();
|
||||
handleDeviceBonded();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,6 +117,11 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
|
||||
}
|
||||
};
|
||||
|
||||
private void handleDeviceBonded() {
|
||||
GB.toast(DiscoveryActivity.this, "Successfully bonded with: " + bondingAddress, Toast.LENGTH_SHORT, GB.INFO);
|
||||
finish();
|
||||
}
|
||||
|
||||
private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
|
||||
@Override
|
||||
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
||||
@ -516,9 +520,21 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
|
||||
} else {
|
||||
try {
|
||||
BluetoothDevice btDevice = adapter.getRemoteDevice(deviceCandidate.getMacAddress());
|
||||
if (btDevice.createBond()) {
|
||||
// async, wait for bonding event to finish this activity
|
||||
bondingAddress = btDevice.getAddress();
|
||||
switch (btDevice.getBondState()) {
|
||||
case BluetoothDevice.BOND_NONE: {
|
||||
if (btDevice.createBond()) {
|
||||
// async, wait for bonding event to finish this activity
|
||||
bondingAddress = btDevice.getAddress();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BluetoothDevice.BOND_BONDING:
|
||||
// async, wait for bonding event to finish this activity
|
||||
bondingAddress = btDevice.getAddress();
|
||||
break;
|
||||
case BluetoothDevice.BOND_BONDED:
|
||||
handleDeviceBonded();
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error pairing device: " + deviceCandidate.getMacAddress());
|
||||
|
@ -94,12 +94,16 @@ public class MiBand2Service {
|
||||
public static final byte[] COMMAND_SET_FITNESS_GOAL_END = new byte[] { 0, 0 };
|
||||
|
||||
|
||||
public static byte COMMAND_DATEFORMAT = 0x06;
|
||||
public static byte ENDPOINT_DISPLAY = 0x06;
|
||||
|
||||
public static final byte[] DATEFORMAT_DATE_TIME = new byte[] { COMMAND_DATEFORMAT, 0x0a, 0x0, 0x03 };
|
||||
public static final byte[] DATEFORMAT_TIME = new byte[] { COMMAND_DATEFORMAT, 0x0a, 0x0, 0x0 };
|
||||
public static final byte[] DATEFORMAT_TIME_12_HOURS = new byte[] { COMMAND_DATEFORMAT, 0x02, 0x0, 0x0 };
|
||||
public static final byte[] DATEFORMAT_TIME_24_HOURS = new byte[] { COMMAND_DATEFORMAT, 0x02, 0x0, 0x1 };
|
||||
public static final byte[] DATEFORMAT_DATE_TIME = new byte[] {ENDPOINT_DISPLAY, 0x0a, 0x0, 0x03 };
|
||||
public static final byte[] DATEFORMAT_TIME = new byte[] {ENDPOINT_DISPLAY, 0x0a, 0x0, 0x0 };
|
||||
public static final byte[] DATEFORMAT_TIME_12_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x0 };
|
||||
public static final byte[] DATEFORMAT_TIME_24_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x1 };
|
||||
public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01};
|
||||
public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x00};
|
||||
public static final byte[] DISPLAY_XXX = new byte[] {ENDPOINT_DISPLAY, 0x03, 0x0, 0x0 };
|
||||
public static final byte[] DISPLAY_YYY = new byte[] {ENDPOINT_DISPLAY, 0x10, 0x0, 0x1, 0x0 };
|
||||
|
||||
public static final byte RESPONSE = 0x10;
|
||||
|
||||
@ -111,14 +115,14 @@ public class MiBand2Service {
|
||||
public static final byte COMMAND_FIRMWARE_START_DATA = 0x03; // to UUID_CHARACTERISTIC_FIRMWARE
|
||||
public static final byte COMMAND_FIRMWARE_UPDATE_SYNC = 0x00; // to UUID_CHARACTERISTIC_FIRMWARE
|
||||
public static final byte COMMAND_FIRMWARE_CHECKSUM = 0x04; // to UUID_CHARACTERISTIC_FIRMWARE
|
||||
public static final byte COMMAND_FIRMWARE_APPLY_REBOOT = 0x05; // or is it REBOOT? to UUID_CHARACTERISTIC_FIRMWARE
|
||||
public static final byte COMMAND_FIRMWARE_REBOOT = 0x05; // to UUID_CHARACTERISTIC_FIRMWARE
|
||||
|
||||
public static final byte[] RESPONSE_FINISH_SUCCESS = new byte[] {RESPONSE, 2, SUCCESS };
|
||||
public static final byte[] RESPONSE_FIRMWARE_DATA_SUCCESS = new byte[] {RESPONSE, COMMAND_FIRMWARE_START_DATA, SUCCESS };
|
||||
/**
|
||||
* Received in response to any dateformat configuration request (byte 0 in the byte[] value.
|
||||
*/
|
||||
public static final byte[] RESPONSE_DATEFORMAT_SUCCESS = new byte[] { RESPONSE, COMMAND_DATEFORMAT, 0x0a, 0x0, 0x01 };
|
||||
public static final byte[] RESPONSE_DATEFORMAT_SUCCESS = new byte[] { RESPONSE, ENDPOINT_DISPLAY, 0x0a, 0x0, 0x01 };
|
||||
public static final byte[] RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS = new byte[] { RESPONSE, COMMAND_ACTIVITY_DATA_START_DATE, SUCCESS};
|
||||
|
||||
public static final byte[] WEAR_LOCATION_LEFT_WRIST = new byte[] { 0x20, 0x00, 0x00, 0x02 };
|
||||
@ -127,34 +131,13 @@ public class MiBand2Service {
|
||||
public static final byte[] COMMAND_ENABLE_HR_SLEEP_MEASUREMENT = new byte[]{0x15, 0x00, 0x01};
|
||||
public static final byte[] COMMAND_DISABLE_HR_SLEEP_MEASUREMENT = new byte[]{0x15, 0x00, 0x00};
|
||||
|
||||
public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{0x06, 0x05, 0x00, 0x01};
|
||||
public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{0x06, 0x05, 0x00, 0x00};
|
||||
|
||||
public static final byte[] COMMAND_TEXT_NOTIFICATION = new byte[] {0x05, 0x01};
|
||||
public static final byte[] COMMAND_TEXT_NOTIFICATION_CONTINUATION = new byte[] {(byte) 0xfa, 0x01, 0x00};
|
||||
public static final byte COMMAND_ALERT_CATEGORY_CHAT = (byte) 0xfa;
|
||||
|
||||
static {
|
||||
MIBAND_DEBUG = new HashMap<>();
|
||||
MIBAND_DEBUG.put(UUID_SERVICE_MIBAND_SERVICE, "MiBand Service");
|
||||
MIBAND_DEBUG.put(UUID_SERVICE_HEART_RATE, "MiBand HR Service");
|
||||
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_INFO, "Device Info");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_NAME, "Device Name");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_NOTIFICATION, "Notification");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_USER_INFO, "User Info");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_CONTROL_POINT, "Control Point");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_REALTIME_STEPS, "Realtime Steps");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_ACTIVITY_DATA, "Activity Data");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_FIRMWARE_DATA, "Firmware Data");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_LE_PARAMS, "LE Params");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DATE_TIME, "Date/Time");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_STATISTICS, "Statistics");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_BATTERY, "Battery");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_TEST, "Test");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_SENSOR_DATA, "Sensor Data");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_PAIR, "Pair");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT, "Heart Rate Control Point");
|
||||
// MIBAND_DEBUG.put(UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT, "Heart Rate Measure");
|
||||
}
|
||||
|
||||
public static String lookup(UUID uuid, String fallback) {
|
||||
|
@ -4,6 +4,9 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
|
||||
|
||||
public class MiBandService {
|
||||
@ -14,7 +17,7 @@ public class MiBandService {
|
||||
|
||||
public static final UUID UUID_SERVICE_MIBAND_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE0"));
|
||||
public static final UUID UUID_SERVICE_MIBAND2_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE1"));
|
||||
public static final UUID UUID_SERVICE_HEART_RATE = UUID.fromString(String.format(BASE_UUID, "180D"));
|
||||
public static final UUID UUID_SERVICE_HEART_RATE = GattService.UUID_SERVICE_HEART_RATE;
|
||||
public static final String UUID_SERVICE_WEIGHT_SERVICE = "00001530-0000-3512-2118-0009af100700";
|
||||
|
||||
public static final UUID UUID_CHARACTERISTIC_DEVICE_INFO = UUID.fromString(String.format(BASE_UUID, "FF01"));
|
||||
@ -47,8 +50,8 @@ public class MiBandService {
|
||||
|
||||
public static final UUID UUID_CHARACTERISTIC_PAIR = UUID.fromString(String.format(BASE_UUID, "FF0F"));
|
||||
|
||||
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT = UUID.fromString(String.format(BASE_UUID, "2A39"));
|
||||
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT = UUID.fromString(String.format(BASE_UUID, "2A37"));
|
||||
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT = GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT;
|
||||
public static final UUID UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT = GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT;
|
||||
|
||||
|
||||
|
||||
|
@ -53,7 +53,7 @@ public class V2NotificationStrategy implements NotificationStrategy {
|
||||
}
|
||||
}
|
||||
}
|
||||
sendAlert(simpleNotification, builder);
|
||||
// sendAlert(simpleNotification, builder);
|
||||
}
|
||||
|
||||
protected void sendAlert(SimpleNotification simpleNotification, TransactionBuilder builder) {
|
||||
|
@ -0,0 +1,19 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2;
|
||||
|
||||
public enum FirmwareType {
|
||||
FIRMWARE((byte) 0),
|
||||
FONT((byte) 1),
|
||||
UNKNOWN1((byte) 2),
|
||||
UNKNOWN2((byte) 3),
|
||||
INVALID(Byte.MIN_VALUE);
|
||||
|
||||
private final byte value;
|
||||
|
||||
FirmwareType(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
@ -27,13 +27,23 @@ public class Mi2FirmwareInfo {
|
||||
(byte) 0xf3,
|
||||
(byte) 0xe7,
|
||||
};
|
||||
|
||||
private static final int FW_HEADER_OFFSET = 0x150;
|
||||
|
||||
private static final byte[] FT_HEADER = new byte[] { // HMZK font file (*.ft, *.ft.xx)
|
||||
0x48,
|
||||
0x4d,
|
||||
0x5a,
|
||||
0x4b
|
||||
};
|
||||
|
||||
private static Map<Integer,String> crcToVersion = new HashMap<>();
|
||||
static {
|
||||
crcToVersion.put(41899, "1.0.0.39");
|
||||
}
|
||||
|
||||
private FirmwareType firmwareType = FirmwareType.FIRMWARE;
|
||||
|
||||
public static String toVersion(int crc16) {
|
||||
return crcToVersion.get(crc16);
|
||||
}
|
||||
@ -51,6 +61,18 @@ public class Mi2FirmwareInfo {
|
||||
this.bytes = bytes;
|
||||
crc16 = CheckSums.getCRC16(bytes);
|
||||
firmwareVersion = crcToVersion.get(crc16);
|
||||
firmwareType = determineFirmwareType(bytes);
|
||||
}
|
||||
|
||||
private FirmwareType determineFirmwareType(byte[] bytes) {
|
||||
if (ArrayUtils.startsWith(bytes, FT_HEADER)) {
|
||||
return FirmwareType.FONT;
|
||||
}
|
||||
if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET)) {
|
||||
// TODO: this is certainly not a correct validation, but it works for now
|
||||
return FirmwareType.FIRMWARE;
|
||||
}
|
||||
return FirmwareType.INVALID;
|
||||
}
|
||||
|
||||
public boolean isGenerallyCompatibleWith(GBDevice device) {
|
||||
@ -58,8 +80,7 @@ public class Mi2FirmwareInfo {
|
||||
}
|
||||
|
||||
public boolean isHeaderValid() {
|
||||
// TODO: this is certainly not a correct validation, but it works for now
|
||||
return ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET);
|
||||
return getFirmwareType() != FirmwareType.INVALID;
|
||||
}
|
||||
|
||||
public void checkValid() throws IllegalArgumentException {
|
||||
@ -84,4 +105,8 @@ public class Mi2FirmwareInfo {
|
||||
public int getFirmwareVersion() {
|
||||
return getCrc16(); // HACK until we know how to determine the version from the fw bytes
|
||||
}
|
||||
|
||||
public FirmwareType getFirmwareType() {
|
||||
return firmwareType;
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class Mi2NotificationStrategy extends V2NotificationStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sendAlert(simpleNotification, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
@ -37,7 +36,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2SampleProvider
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
@ -45,7 +43,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
@ -67,20 +64,16 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.CheckAuthenticationNeededAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions.StopNotificationAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
@ -123,8 +116,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
private volatile boolean telephoneRinging;
|
||||
private volatile boolean isLocatingDevice;
|
||||
|
||||
private DeviceInfo mDeviceInfo;
|
||||
|
||||
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
|
||||
private RealtimeSamplesSupport realtimeSamplesSupport;
|
||||
@ -171,26 +162,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
} catch (IOException e) {
|
||||
GB.toast(getContext(), "Initializing Mi Band 2 failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
}
|
||||
|
||||
// builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
|
||||
// enableNotifications(builder, true)
|
||||
// .setLowLatency(builder)
|
||||
// .readDate(builder) // without reading the data, we get sporadic connection problems, especially directly after turning on BT
|
||||
// this is apparently not needed anymore, and actually causes problems when bonding is not used/does not work
|
||||
// so we simply not use the UUID_PAIR characteristic.
|
||||
// .pair(builder)
|
||||
//.requestDeviceInfo(builder)
|
||||
//.requestBatteryInfo(builder);
|
||||
// .sendUserInfo(builder)
|
||||
// .checkAuthenticationNeeded(builder, getDevice())
|
||||
// .setWearLocation(builder)
|
||||
// .setHeartrateSleepSupport(builder)
|
||||
// .setFitnessGoal(builder)
|
||||
// .enableFurtherNotifications(builder, true)
|
||||
// .setCurrentTime(builder)
|
||||
// .requestBatteryInfo(builder)
|
||||
// .setHighLatency(builder)
|
||||
// .setInitialized(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -226,27 +197,13 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support readDate(TransactionBuilder builder) {
|
||||
// NAVL
|
||||
// builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_DATE_TIME));
|
||||
// TODO: handle result
|
||||
builder.read(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME));
|
||||
return this;
|
||||
}
|
||||
|
||||
// NAVL
|
||||
public MiBand2Support setLowLatency(TransactionBuilder builder) {
|
||||
// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency());
|
||||
return this;
|
||||
}
|
||||
// NAVL
|
||||
public MiBand2Support setHighLatency(TransactionBuilder builder) {
|
||||
// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getHighLatency());
|
||||
// TODO: low latency?
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support checkAuthenticationNeeded(TransactionBuilder builder, GBDevice device) {
|
||||
builder.add(new CheckAuthenticationNeededAction(device));
|
||||
public MiBand2Support setHighLatency(TransactionBuilder builder) {
|
||||
// TODO: high latency?
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -272,13 +229,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
public MiBand2Support enableFurtherNotifications(TransactionBuilder builder, boolean enable) {
|
||||
// builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable)
|
||||
// .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable)
|
||||
// .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable);
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), enable);
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO), enable);
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON), enable);
|
||||
BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
|
||||
BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT);
|
||||
if (heartrateCharacteristic != null) {
|
||||
builder.notify(heartrateCharacteristic, enable);
|
||||
}
|
||||
@ -301,10 +255,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public DeviceInfo getDeviceInfo() {
|
||||
return mDeviceInfo;
|
||||
}
|
||||
|
||||
private MiBand2Support sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
|
||||
LOG.info("Sending notification to MiBand: (" + repeat + " times)");
|
||||
NotificationStrategy strategy = getNotificationStrategy();
|
||||
@ -335,44 +285,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return new Mi2NotificationStrategy(this);
|
||||
}
|
||||
|
||||
static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT};
|
||||
|
||||
static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1};
|
||||
static final byte[] stopHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 0};
|
||||
static final byte[] startHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 1};
|
||||
static final byte[] stopHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 0};
|
||||
static final byte[] startHeartMeasurementSleep = new byte[]{0x15, MiBandService.COMMAND_SET_HR_SLEEP, 1};
|
||||
static final byte[] stopHeartMeasurementSleep = new byte[]{0x15, MiBandService.COMMAND_SET_HR_SLEEP, 0};
|
||||
|
||||
static final byte[] startRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 1};
|
||||
static final byte[] stopRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 0};
|
||||
|
||||
/**
|
||||
* Part of device initialization process. Do not call manually.
|
||||
*
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
private MiBand2Support sendUserInfo(TransactionBuilder builder) {
|
||||
LOG.debug("Writing User Info!");
|
||||
// Use a custom action instead of just builder.write() because mDeviceInfo
|
||||
// is set by handleDeviceInfo *after* this action is created.
|
||||
builder.add(new BtLEAction(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_USER_INFO)) {
|
||||
@Override
|
||||
public boolean expectsResult() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(BluetoothGatt gatt) {
|
||||
// at this point, mDeviceInfo should be set
|
||||
return new WriteAction(getCharacteristic(),
|
||||
MiBandCoordinator.getAnyUserInfo(getDevice().getAddress()).getData(mDeviceInfo)
|
||||
).run(gatt);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
private static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1};
|
||||
private static final byte[] stopHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 0};
|
||||
private static final byte[] startHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 1};
|
||||
private static final byte[] stopHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 0};
|
||||
|
||||
private MiBand2Support requestBatteryInfo(TransactionBuilder builder) {
|
||||
LOG.debug("Requesting Battery Info!");
|
||||
@ -387,31 +303,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return this;
|
||||
}
|
||||
|
||||
/* private MiBandSupport requestHRInfo(TransactionBuilder builder) {
|
||||
LOG.debug("Requesting HR Info!");
|
||||
BluetoothGattCharacteristic HRInfo = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT);
|
||||
builder.read(HRInfo);
|
||||
BluetoothGattCharacteristic HR_Point = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT);
|
||||
builder.read(HR_Point);
|
||||
return this;
|
||||
}
|
||||
*//**
|
||||
* Part of HR test. Do not call manually.
|
||||
*
|
||||
* @param transaction
|
||||
* @return
|
||||
*//*
|
||||
private MiBandSupport heartrate(TransactionBuilder transaction) {
|
||||
LOG.info("Attempting to read HR ...");
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT);
|
||||
if (characteristic != null) {
|
||||
transaction.write(characteristic, new byte[]{MiBandService.COMMAND_SET__HR_CONTINUOUS});
|
||||
} else {
|
||||
LOG.info("Unable to read HR from MI device -- characteristic not available");
|
||||
}
|
||||
return this;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Part of device initialization process. Do not call manually.
|
||||
*
|
||||
@ -684,7 +575,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
public void onReboot() {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("Reboot");
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), reboot);
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_FIRMWARE), new byte[] { MiBand2Service.COMMAND_FIRMWARE_REBOOT});
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to reboot MI", ex);
|
||||
@ -695,9 +586,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
public void onHeartRateTest() {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("HeartRateTest");
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to read HearRate with MI2", ex);
|
||||
@ -709,8 +600,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("Enable realtime heart rateM measurement");
|
||||
if (enable) {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous);
|
||||
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual);
|
||||
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous);
|
||||
}
|
||||
@ -753,19 +644,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeSteps(boolean enable) {
|
||||
// try {
|
||||
// BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
|
||||
// if (enable) {
|
||||
// TransactionBuilder builder = performInitialized("Read realtime steps");
|
||||
// builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS)).queue(getQueue());
|
||||
// }
|
||||
// performInitialized(enable ? "Enabling realtime steps notifications" : "Disabling realtime steps notifications")
|
||||
// .write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), enable ? getLowLatency() : getHighLatency())
|
||||
// .write(controlPoint, enable ? startRealTimeStepsNotifications : stopRealTimeStepsNotifications).queue(getQueue());
|
||||
// enableRealtimeSamplesTimer(enable);
|
||||
// } catch (IOException e) {
|
||||
// LOG.error("Unable to change realtime steps notification to: " + enable, e);
|
||||
// }
|
||||
}
|
||||
|
||||
private byte[] getHighLatency() {
|
||||
@ -854,13 +732,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
if (MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
|
||||
handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS);
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) {
|
||||
handleNotificationNotif(characteristic.getValue());
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
handleRealtimeSteps(characteristic.getValue());
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
} else if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
handleHeartrate(characteristic.getValue());
|
||||
return true;
|
||||
// } else if (MiBand2Service.UUID_UNKNOQN_CHARACTERISTIC0.equals(characteristicUUID)) {
|
||||
@ -901,12 +776,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
|
||||
handleBatteryInfo(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
} else if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
logHeartrate(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) {
|
||||
logDate(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON.equals(characteristicUUID)) {
|
||||
handleButtonPressed(characteristic.getValue());
|
||||
return true;
|
||||
@ -921,16 +793,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
public boolean onCharacteristicWrite(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic, int status) {
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (MiBandService.UUID_CHARACTERISTIC_PAIR.equals(characteristicUUID)) {
|
||||
handlePairResult(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_USER_INFO.equals(characteristicUUID)) {
|
||||
handleUserInfoResult(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT.equals(characteristicUUID)) {
|
||||
handleControlPointResult(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
|
||||
if (MiBand2Service.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
|
||||
LOG.info("KEY AES SEND");
|
||||
logMessageContent(characteristic.getValue());
|
||||
return true;
|
||||
@ -938,15 +801,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void logDate(byte[] value, int status) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value);
|
||||
LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime()));
|
||||
} else {
|
||||
logMessageContent(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void logHeartrate(byte[] value, int status) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS && value != null) {
|
||||
LOG.info("Got heartrate:");
|
||||
@ -1046,66 +900,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return realtimeSamplesSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* React to unsolicited messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION
|
||||
* characteristic,
|
||||
* These messages appear to be always 1 byte long, with values that are listed in MiBandService.
|
||||
* It is not excluded that there are further values which are still unknown.
|
||||
* <p/>
|
||||
* Upon receiving known values that request further action by GB, the appropriate method is called.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
private void handleNotificationNotif(byte[] value) {
|
||||
if (value.length != 1) {
|
||||
LOG.error("Notifications should be 1 byte long.");
|
||||
LOG.info("RECEIVED DATA WITH LENGTH: " + value.length);
|
||||
for (byte b : value) {
|
||||
LOG.warn("DATA: " + String.format("0x%2x", b));
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (value[0]) {
|
||||
case MiBandService.NOTIFY_AUTHENTICATION_FAILED:
|
||||
// we get first FAILED, then NOTIFY_STATUS_MOTOR_AUTH (0x13)
|
||||
// which means, we need to authenticate by tapping
|
||||
getDevice().setState(State.AUTHENTICATION_REQUIRED);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
GB.toast(getContext(), "Band needs pairing", Toast.LENGTH_LONG, GB.ERROR);
|
||||
break;
|
||||
case MiBandService.NOTIFY_AUTHENTICATION_SUCCESS: // fall through -- not sure which one we get
|
||||
case MiBandService.NOTIFY_RESET_AUTHENTICATION_SUCCESS: // for Mi 1A
|
||||
case MiBandService.NOTIFY_STATUS_MOTOR_AUTH_SUCCESS:
|
||||
LOG.info("Band successfully authenticated");
|
||||
// maybe we can perform the rest of the initialization from here
|
||||
doInitialize();
|
||||
break;
|
||||
|
||||
case MiBandService.NOTIFY_STATUS_MOTOR_AUTH:
|
||||
LOG.info("Band needs authentication (MOTOR_AUTH)");
|
||||
getDevice().setState(State.AUTHENTICATING);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
break;
|
||||
|
||||
case MiBandService.NOTIFY_SET_LATENCY_SUCCESS:
|
||||
LOG.info("Setting latency succeeded.");
|
||||
break;
|
||||
default:
|
||||
for (byte b : value) {
|
||||
LOG.warn("DATA: " + String.format("0x%2x", b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doInitialize() {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("just initializing after authentication");
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to initialize device after authentication", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeviceName(byte[] value, int status) {
|
||||
// if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// versionCmd.hwVersion = new String(value);
|
||||
@ -1151,21 +945,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
// TODO: react on 0x10, 0x02, 0x01 on notification (success)
|
||||
}
|
||||
|
||||
private void handleControlPointResult(byte[] value, int status) {
|
||||
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||
LOG.warn("Could not write to the control point.");
|
||||
}
|
||||
LOG.info("handleControlPoint write status:" + status + "; length: " + (value != null ? value.length : "(null)"));
|
||||
|
||||
if (value != null) {
|
||||
for (byte b : value) {
|
||||
LOG.info("handleControlPoint WROTE DATA:" + String.format("0x%8x", b));
|
||||
}
|
||||
} else {
|
||||
LOG.warn("handleControlPoint WROTE null");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) {
|
||||
// if (getDeviceInfo().supportsHeartrate()) {
|
||||
// getDevice().addDeviceInfo(new GenericItem(
|
||||
@ -1190,44 +969,6 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUserInfoResult(byte[] value, int status) {
|
||||
// successfully transferred user info means we're initialized
|
||||
// commented out, because we have SetDeviceStateAction which sets initialized
|
||||
// state on every successful initialization.
|
||||
// if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// setConnectionState(State.INITIALIZED);
|
||||
// }
|
||||
}
|
||||
|
||||
private void setConnectionState(State newState) {
|
||||
getDevice().setState(newState);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
}
|
||||
|
||||
private void handlePairResult(byte[] pairResult, int status) {
|
||||
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||
LOG.info("Pairing MI device failed: " + status);
|
||||
return;
|
||||
}
|
||||
|
||||
String value = null;
|
||||
if (pairResult != null) {
|
||||
if (pairResult.length == 1) {
|
||||
try {
|
||||
if (pairResult[0] == 2) {
|
||||
LOG.info("Successfully paired MI device");
|
||||
return;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Error identifying pairing result", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
value = Arrays.toString(pairResult);
|
||||
}
|
||||
LOG.info("MI Band pairing result: " + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the events from the android device calendars and set the alarms on the miband.
|
||||
* @param builder
|
||||
|
@ -17,14 +17,15 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband2.MiBand2FWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.FirmwareType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2FirmwareInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband2.MiBand2FWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
@ -113,10 +114,15 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
|
||||
break;
|
||||
}
|
||||
case MiBand2Service.COMMAND_FIRMWARE_CHECKSUM: {
|
||||
sendApplyReboot(getFirmwareInfo());
|
||||
if (getFirmwareInfo().getFirmwareType() == FirmwareType.FIRMWARE) {
|
||||
getSupport().onReboot();
|
||||
} else {
|
||||
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
|
||||
done();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MiBand2Service.COMMAND_FIRMWARE_APPLY_REBOOT: {
|
||||
case MiBand2Service.COMMAND_FIRMWARE_REBOOT: {
|
||||
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
|
||||
// getSupport().onReboot();
|
||||
done();
|
||||
@ -152,12 +158,20 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
|
||||
builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext()));
|
||||
int fwSize = getFirmwareInfo().getSize();
|
||||
byte[] sizeBytes = BLETypeConversions.fromUint24(fwSize);
|
||||
byte[] bytes = new byte[]{
|
||||
MiBand2Service.COMMAND_FIRMWARE_INIT,
|
||||
sizeBytes[0],
|
||||
sizeBytes[1],
|
||||
sizeBytes[2],
|
||||
};
|
||||
int arraySize = 4;
|
||||
boolean isFirmwareCode = getFirmwareInfo().getFirmwareType() == FirmwareType.FIRMWARE;
|
||||
if (!isFirmwareCode) {
|
||||
arraySize++;
|
||||
}
|
||||
byte[] bytes = new byte[arraySize];
|
||||
int i = 0;
|
||||
bytes[i++] = MiBand2Service.COMMAND_FIRMWARE_INIT;
|
||||
bytes[i++] = sizeBytes[0];
|
||||
bytes[i++] = sizeBytes[1];
|
||||
bytes[i++] = sizeBytes[2];
|
||||
if (!isFirmwareCode) {
|
||||
bytes[i++] = getFirmwareInfo().getFirmwareType().getValue();
|
||||
}
|
||||
|
||||
builder.write(fwCControlChar, bytes);
|
||||
builder.queue(getQueue());
|
||||
@ -176,7 +190,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
|
||||
*
|
||||
* @param info
|
||||
* @return whether the transfer succeeded or not. Only a BT layer exception will cause the transmission to fail.
|
||||
* @see MiBand2Support#handleNotificationNotif
|
||||
* @see #handleNotificationNotif
|
||||
*/
|
||||
private boolean sendFirmwareData(Mi2FirmwareInfo info) {
|
||||
byte[] fwbytes = info.getBytes();
|
||||
@ -237,21 +251,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand2Operation {
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
private void sendApplyReboot(Mi2FirmwareInfo firmwareInfo) throws IOException {
|
||||
TransactionBuilder builder = performInitialized("send firmware apply/reboot");
|
||||
builder.write(fwCControlChar, new byte[] { MiBand2Service.COMMAND_FIRMWARE_APPLY_REBOOT });
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
private Mi2FirmwareInfo getFirmwareInfo() {
|
||||
return firmwareInfo;
|
||||
}
|
||||
|
||||
enum State {
|
||||
INITIAL,
|
||||
SEND_FW2,
|
||||
SEND_FW1,
|
||||
FINISHED,
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
||||
|
@ -52,4 +52,14 @@ public class ArrayUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given byte array starts with the given values
|
||||
* @param array the array to check
|
||||
* @param values the values which the other array is checked to start with
|
||||
* @return
|
||||
*/
|
||||
public static boolean startsWith(byte[] array, byte[] values) {
|
||||
return equals(array, values, 0);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,12 @@ public class LanguageUtils {
|
||||
put('п', "p"); put('р', "r"); put('с', "s"); put('т', "t"); put('у', "u"); put('ф', "f"); put('х', "kh"); put('ц', "c");
|
||||
put('ч', "ch");put('ш', "sh");put('щ', "shh");put('ъ', "\"");put('ы', "y"); put('ь', "'"); put('э', "eh"); put('ю', "ju");
|
||||
put('я', "ja");
|
||||
|
||||
|
||||
//hebrew chars
|
||||
put('א', "a"); put('ב', "b"); put('ג', "g"); put('ד', "d"); put('ה', "h"); put('ו', "u"); put('ז', "z"); put('ח', "kh");
|
||||
put('ט', "t"); put('י', "y"); put('כ', "c"); put('ל', "l"); put('מ', "m"); put('נ', "n"); put('ס', "s"); put('ע', "'");
|
||||
put('פ', "p"); put('צ', "ts"); put('ק', "k"); put('ר', "r"); put('ש', "sh"); put('ת', "th"); put('ף', "f"); put('ץ', "ts");
|
||||
put('ך', "ch");put('ם', "m");put('ן', "n");
|
||||
//continue for other languages...
|
||||
}
|
||||
};
|
||||
|
@ -213,7 +213,7 @@
|
||||
<string name="miband_pairing_using_dummy_userdata">No valid user data given, using dummy user data for now.</string>
|
||||
<string name="miband_pairing_tap_hint">When your Mi Band vibrates and blinks, tap it a few times in a row.</string>
|
||||
<string name="appinstaller_install">Install</string>
|
||||
<string name="discovery_connected_devices_hint">Make your device discoverable. Currently connected devices will likely not be discovered. On Android 6 or later, you need to activate location (e.g. GPS). If your device does not show up after two minutes, try again after rebooting your mobile device.</string>
|
||||
<string name="discovery_connected_devices_hint">Make your device discoverable. Currently connected devices will likely not be discovered. Activate location (e.g. GPS) on Android 6+. Disable Privacy Guard for Gadgetbridge, because it may crash and reboot your phone. If no device is found after a few minutes, try again after rebooting your mobile device.</string>
|
||||
<string name="discovery_note">Note:</string>
|
||||
<string name="candidate_item_device_image">Device Image</string>
|
||||
<string name="miband_prefs_alias">Name/Alias</string>
|
||||
|
@ -112,6 +112,6 @@ public class DeviceCommunicationServiceTestCase extends TestBase {
|
||||
mDeviceService.invokeService(intent);
|
||||
String result = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
|
||||
|
||||
assertTrue("Transliteration support fail!", result.equals("Prosto tekct"));
|
||||
assertEquals("Transliteration support fail!", "Prosto tekct", result);
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,46 @@ public class ArrayUtilsTest extends TestBase {
|
||||
assertFalse(ArrayUtils.equals(DATA_5, new byte[] {3, 4, 6}, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWith1() throws Exception {
|
||||
assertTrue(ArrayUtils.startsWith(DATA_5, new byte[] {1}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWith2() throws Exception {
|
||||
assertTrue(ArrayUtils.startsWith(DATA_5, new byte[] {1, 2}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWithAll() throws Exception {
|
||||
assertTrue(ArrayUtils.startsWith(DATA_5, DATA_5.clone()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWithEmpty() throws Exception {
|
||||
assertTrue(ArrayUtils.startsWith(DATA_5, EMPTY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWithFail1() throws Exception {
|
||||
try {
|
||||
ArrayUtils.startsWith(DATA_5, null);
|
||||
fail("should have thrown an exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWithFail3() throws Exception {
|
||||
assertFalse(ArrayUtils.startsWith(DATA_5, new byte[] {2, 3}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWithFail4() throws Exception {
|
||||
assertFalse(ArrayUtils.startsWith(DATA_5, new byte[] {1, 2, 3, 4, 5, 6}));
|
||||
}
|
||||
|
||||
private byte[] b(int b) {
|
||||
return new byte[] {(byte) b};
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import org.junit.Test;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@ -15,13 +16,23 @@ import static org.junit.Assert.assertTrue;
|
||||
*/
|
||||
public class LanguageUtilsTest extends TestBase {
|
||||
@Test
|
||||
public void testStringTransliterate() throws Exception {
|
||||
public void testStringTransliterateCyrillic() throws Exception {
|
||||
//input with cyrillic and diacritic letters
|
||||
String input = "Прõсто текčт";
|
||||
String output = LanguageUtils.transliterate(input);
|
||||
String result = "Prosto tekct";
|
||||
|
||||
assertTrue(String.format("Transliteration fail! Expected '%s', but found '%s'}", result, output), output.equals(result));
|
||||
assertEquals("Transliteration failed", result, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringTransliterateHebrew() throws Exception {
|
||||
//input with cyrillic and diacritic letters
|
||||
String input = "בדיקה עברית";
|
||||
String output = LanguageUtils.transliterate(input);
|
||||
String result = "bdykh 'bryth";
|
||||
|
||||
assertEquals("Transliteration failed", result, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -31,7 +42,7 @@ public class LanguageUtilsTest extends TestBase {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("transliteration", true);
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
|
||||
assertTrue("Transliteration option fail! Expected 'On', but result is 'Off'", LanguageUtils.transliterate());
|
||||
}
|
||||
|
@ -26,6 +26,13 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* Base class for all testcases in Gadgetbridge that are supposed to run locally
|
||||
* with robolectric.
|
||||
*
|
||||
* Important: To run them, create a run configuration and execute them in the Gadgetbridge/app/
|
||||
* directory.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, sdk = 19)
|
||||
// need sdk 19 because "WITHOUT ROWID" is not supported in robolectric/sqlite4java
|
||||
|
Loading…
Reference in New Issue
Block a user